ampsci
High-precision calculations for one- and two-valence atomic systems
EM_multipole.hpp
1#pragma once
2#include "DiracOperator/TensorOperator.hpp"
3#include "IO/InputBlock.hpp"
4#include "Maths/SphericalBessel.hpp"
5#include "Wavefunction/Wavefunction.hpp"
6#include "qip/Maths.hpp"
7
8// include the 'low qr' form here, simply for convenience
9#include "EM_multipole_lowqr.hpp"
10
11namespace DiracOperator {
12
13//==============================================================================
14//! @brief Vector electric multipole (transition) operator, Length-form: ( \f$ T^{(+1),{\rm Len}}_K \f$ ).
15/*!
16 @details
17 - nb: q = alpha*omega, so "omega" = c*q
18 - Electric multipole operator in the L-form (transition form
19 convention): t^E_L. The radial dependence is expressed in terms of
20 spherical Bessel functions j_L(q*r) with q = alpha * omega.
21 - The operator supports using either on-the-fly Bessel evaluation or a
22 precomputed `SphericalBessel::JL_table` supplied via the constructor
23 (pointer `jl`). When `jl` is non-null the table is used to look up the
24 closest j_L(q) vector for performance.
25 - Note: The q value for jL(qr) will be the _nearest_ to the requested q
26 - you should ensure the lookup table is close enough, or this can lead to errors.
27*/
28class VEk_Len final : public TensorOperator {
29public:
30 VEk_Len(const Grid &gr, int K, double omega,
31 const SphericalBessel::JL_table *jl = nullptr)
32 : TensorOperator(K, Angular::evenQ(K) ? Parity::even : Parity::odd, 1.0,
33 gr.r(), 0, Realness::imaginary, true),
34 m_K(K),
35 m_jl(jl) {
36 if (omega != 0.0)
37 updateFrequency(omega);
38 }
39 std::string name() const override final {
40 return std::string("V^E(Len)_") + std::to_string(m_K);
41 }
42
43 double angularF(const int ka, const int kb) const override final {
44 return Angular::Ck_kk(m_K, ka, kb);
45 }
46
47 DiracSpinor radial_rhs(const int kappa_a,
48 const DiracSpinor &Fb) const override final;
49
50 double radialIntegral(const DiracSpinor &Fa,
51 const DiracSpinor &Fb) const override final;
52
53 //! nb: q = alpha*omega!
54 void updateFrequency(const double omega) override final;
55
56private:
57 int m_K;
58 const SphericalBessel::JL_table *m_jl{nullptr};
59
60 // This is to avoid copy if not required:
61 std::vector<double> m_jK{};
62 std::vector<double> m_jKp1{};
63 const std::vector<double> *p_jK{nullptr};
64 const std::vector<double> *p_jKp1{nullptr};
65
66public:
67 // Default shallow copy semantics
68 VEk_Len(const VEk_Len &) = default;
69 VEk_Len &operator=(const VEk_Len &) = default;
70};
71
72//==============================================================================
73//! @brief Vector electric multipole (V-form) operator: \f$ V^E_K = T^{(+1)}_K(q) \f$
74/*!
75 @details
76 - nb: q = alpha*omega, so "omega" = c*q
77 - Vector (spatial) electric multipole operator used in the V-form
78 (transition convention). Radial dependence uses spherical
79 Bessel functions j_L(q*r) and derived combinations like j_L(q*r)/(q*r)
80 where needed; q = alpha * omega.
81 - The constructor takes an optional `const SphericalBessel::JL_table *jl` to
82 enable lookup from a precomputed table for improved performance.
83 - Note: The q value for jL(qr) will be the _nearest_ to the requested q
84 - you should ensure the lookup table is close enough, or this can lead to errors.
85*/
86class VEk final : public TensorOperator {
87public:
88 VEk(const Grid &gr, int K, double omega,
89 const SphericalBessel::JL_table *jl = nullptr)
90 : TensorOperator(K, Angular::evenQ(K) ? Parity::even : Parity::odd, 1.0,
91 gr.r(), 0, Realness::imaginary, true),
92 m_K(K),
93 m_jl(jl) {
94 if (omega != 0.0)
95 updateFrequency(omega);
96 }
97 std::string name() const override final {
98 return std::string("V^E_") + std::to_string(m_K);
99 }
100
101 double angularF(const int ka, const int kb) const override final {
102 return Angular::Ck_kk(m_K, ka, kb);
103 }
104
105 DiracSpinor radial_rhs(const int kappa_a,
106 const DiracSpinor &Fb) const override final;
107
108 double radialIntegral(const DiracSpinor &Fa,
109 const DiracSpinor &Fb) const override final;
110
111 //! nb: q = alpha*omega!
112 void updateFrequency(const double omega) override final;
113
114private:
115 int m_K;
116 const SphericalBessel::JL_table *m_jl{nullptr};
117
118 // This is to avoid copy if not required:
119 std::vector<double> m_jK_on_qr{};
120 std::vector<double> m_jKp1{};
121 const std::vector<double> *p_jK_on_qr{nullptr};
122 const std::vector<double> *p_jKp1{nullptr};
123
124public:
125 // Default shallow copy semantics (pointer member is shallow-copied)
126 VEk(const VEk &) = default;
127 VEk &operator=(const VEk &) = default;
128};
129
130//==============================================================================
131//! @brief Vector longitudinal multipole operator (V-form): \f$ V^L_K = T^{(-1)}_K(q) \f$
132/*!
133 @details
134 - nb: q = alpha*omega, so "omega" = c*q
135 - Implements the longitudinal (scalar-like) component of the vector
136 multipole operator. Radial dependence uses spherical Bessel functions
137 j_L(q*r) and, when required, the combination j_L(q*r)/(q*r).
138 - The constructor takes an optional `const SphericalBessel::JL_table *jl` to
139 enable lookup from a precomputed table for improved performance.
140 - Note: The q value for jL(qr) will be the _nearest_ to the requested q
141 - you should ensure the lookup table is close enough, or this can lead to errors.
142*/
143class VLk final : public TensorOperator {
144public:
145 VLk(const Grid &gr, int K, double omega,
146 const SphericalBessel::JL_table *jl = nullptr)
147 : TensorOperator(K, Angular::evenQ(K) ? Parity::even : Parity::odd, 1.0,
148 gr.r(), 0, Realness::imaginary, true),
149 m_K(K),
150 m_jl(jl) {
151 if (omega != 0.0)
152 updateFrequency(omega);
153 }
154 std::string name() const override final {
155 return std::string("V^L_") + std::to_string(m_K);
156 }
157
158 double angularF(const int ka, const int kb) const override final {
159 return Angular::Ck_kk(m_K, ka, kb);
160 }
161
162 DiracSpinor radial_rhs(const int kappa_a,
163 const DiracSpinor &Fb) const override final;
164
165 double radialIntegral(const DiracSpinor &Fa,
166 const DiracSpinor &Fb) const override final;
167
168 //! nb: q = alpha*omega!
169 void updateFrequency(const double omega) override final;
170
171private:
172 int m_K;
173 const SphericalBessel::JL_table *m_jl{nullptr};
174
175 // This is to avoid copy if not required:
176 std::vector<double> m_jK_on_qr{};
177 std::vector<double> m_jKp1{};
178 const std::vector<double> *p_jK_on_qr{nullptr};
179 const std::vector<double> *p_jKp1{nullptr};
180
181public:
182 // Default shallow copy semantics (pointer member is shallow-copied)
183 VLk(const VLk &) = default;
184 VLk &operator=(const VLk &) = default;
185};
186
187//==============================================================================
188//! @brief Vector magnetic multipole operator: \f$ V^M_K = T^{(0)}_K(q) \f$
189/*!
190 @details
191 - nb: q = alpha*omega, so "omega" = c*q
192 - Implements the magnetic multipole operator. The radial dependence is
193 expressed via spherical Bessel functions j_L(q*r) with q = alpha * omega.
194 - The constructor takes an optional `const SphericalBessel::JL_table *jl` to
195 enable lookup from a precomputed table for improved performance.
196 - Note: The q value for jL(qr) will be the _nearest_ to the requested q
197 - you should ensure the lookup table is close enough, or this can lead to errors.
198*/
199class VMk final : public TensorOperator {
200public:
201 VMk(const Grid &gr, int K, double omega,
202 const SphericalBessel::JL_table *jl = nullptr)
203 : TensorOperator(K, Angular::evenQ(K) ? Parity::odd : Parity::even, 1.0,
204 gr.r(), 0, Realness::imaginary, true),
205 m_K(K),
206 m_jl(jl) {
207 if (omega != 0.0)
208 updateFrequency(omega);
209 }
210
211 std::string name() const override final {
212 return std::string("V^M_") + std::to_string(m_K);
213 }
214
215 double angularF(const int ka, const int kb) const override final {
216 return Angular::Ck_kk(m_K, -ka, kb);
217 }
218
219 DiracSpinor radial_rhs(const int kappa_a,
220 const DiracSpinor &Fb) const override final;
221
222 double radialIntegral(const DiracSpinor &Fa,
223 const DiracSpinor &Fb) const override final;
224
225 //! nb: q = alpha*omega!
226 void updateFrequency(const double omega) override final;
227
228private:
229 int m_K;
230 const SphericalBessel::JL_table *m_jl{nullptr};
231
232 // This is to avoid copy if not required:
233 std::vector<double> m_jK{};
234 const std::vector<double> *p_jK{nullptr};
235
236public:
237 // Default shallow copy semantics (pointer member is shallow-copied)
238 VMk(const VMk &) = default;
239 VMk &operator=(const VMk &) = default;
240};
241
242//==============================================================================
243//! @brief Temporal component of the vector multipole operator: \f$ \Phi_K = t^K(q) \f$
244/*!
245 @details
246 - nb: q = alpha*omega, so "omega" = c*q
247 - Implements the time-like (temporal) component of the vector multipole
248 operator with explicit frequency dependence via j_L(q*r).
249 - The constructor takes an optional `const SphericalBessel::JL_table *jl` to
250 enable lookup from a precomputed table for improved performance.
251 - Note: The q value for jL(qr) will be the _nearest_ to the requested q
252 - you should ensure the lookup table is close enough, or this can lead to errors.
253*/
254class Phik final : public TensorOperator {
255public:
256 Phik(const Grid &gr, int K, double omega,
257 const SphericalBessel::JL_table *jl = nullptr)
258 : TensorOperator(K, Angular::evenQ(K) ? Parity::even : Parity::odd, 1.0,
259 gr.r(), 0, Realness::real, true),
260 m_K(K),
261 m_jl(jl) {
262 if (omega != 0.0)
263 updateFrequency(omega);
264 }
265 std::string name() const override final {
266 return std::string("Phi_") + std::to_string(m_K);
267 }
268
269 double angularF(const int ka, const int kb) const override final {
270 return Angular::Ck_kk(m_K, ka, kb);
271 }
272
273 DiracSpinor radial_rhs(const int kappa_a,
274 const DiracSpinor &Fb) const override final;
275
276 double radialIntegral(const DiracSpinor &Fa,
277 const DiracSpinor &Fb) const override final;
278
279 //! nb: q = alpha*omega!
280 void updateFrequency(const double omega) override final;
281
282private:
283 int m_K;
284 const SphericalBessel::JL_table *m_jl{nullptr};
285
286 // This is to avoid copy if not required:
287 std::vector<double> m_jK{};
288 const std::vector<double> *p_jK{nullptr};
289
290public:
291 // Default shallow copy semantics (pointer member is shallow-copied)
292 Phik(const Phik &) = default;
293 Phik &operator=(const Phik &) = default;
294};
295
296//==============================================================================
297//! @brief Scalar multipole operator: \f$ S_K = t^K(q)\gamma^0 \f$
298/*!
299 @details
300 - nb: q = alpha*omega, so "omega" = c*q
301 - Implements the scalar multipole operator whose radial factor is
302 e^{i q r} (represented via spherical Bessel functions j_L(q*r)). The
303 operator includes the gamma^0 Dirac matrix structure.
304 - The constructor takes an optional `const SphericalBessel::JL_table *jl` to
305 enable lookup from a precomputed table for improved performance.
306 - Note: The q value for jL(qr) will be the _nearest_ to the requested q
307 - you should ensure the lookup table is close enough, or this can lead to errors.
308*/
309class Sk final : public TensorOperator {
310public:
311 Sk(const Grid &gr, int K, double omega,
312 const SphericalBessel::JL_table *jl = nullptr)
313 : TensorOperator(K, Angular::evenQ(K) ? Parity::even : Parity::odd, 1.0,
314 gr.r(), 0, Realness::real, true),
315 m_K(K),
316 m_jl(jl) {
317 if (omega != 0.0)
318 updateFrequency(omega);
319 }
320 std::string name() const override final {
321 return std::string("t^S_") + std::to_string(m_K);
322 }
323
324 double angularF(const int ka, const int kb) const override final {
325 return Angular::Ck_kk(m_K, ka, kb);
326 }
327
328 DiracSpinor radial_rhs(const int kappa_a,
329 const DiracSpinor &Fb) const override final;
330
331 double radialIntegral(const DiracSpinor &Fa,
332 const DiracSpinor &Fb) const override final;
333
334 //! nb: q = alpha*omega!
335 void updateFrequency(const double omega) override final;
336
337private:
338 int m_K;
339 const SphericalBessel::JL_table *m_jl{nullptr};
340
341 // This is to avoid copy if not required:
342 std::vector<double> m_jK{};
343 const std::vector<double> *p_jK{nullptr};
344
345public:
346 // Default shallow copy semantics (pointer member is shallow-copied)
347 Sk(const Sk &) = default;
348 Sk &operator=(const Sk &) = default;
349};
350
351//==============================================================================
352//==============================================================================
353// Gamma^5 versions!
354
355//==============================================================================
356//! @brief Axial electric multipole operator: \f$ A^E_K = T^{(+1)}_K(q)\gamma^5 \f$
357/*!
358 @details
359 - Gamma^5 variant of the electric multipole (vector) operator. Functions
360 analogously to `VEk` but with the gamma^5 Dirac structure applied to the
361 angular part.
362 - Radial functions use spherical Bessel combinations j_L(q*r) or
363 j_L(q*r)/(q*r) with q = alpha * omega.
364 - Accepts an optional `const SphericalBessel::JL_table *jl` to use precomputed
365 Bessel vectors; otherwise computes them on demand.
366*/
367class AEk final : public TensorOperator {
368public:
369 AEk(const Grid &gr, int K, double omega,
370 const SphericalBessel::JL_table *jl = nullptr)
371 : TensorOperator(K, Angular::evenQ(K) ? Parity::odd : Parity::even, 1.0,
372 gr.r(), 0, Realness::real),
373 m_K(K),
374 m_jl(jl) {
375 if (omega != 0.0)
376 updateFrequency(omega);
377 }
378 std::string name() const override final {
379 return std::string("T^E5_") + std::to_string(m_K);
380 }
381
382 double angularF(const int ka, const int kb) const override final {
383 return Angular::Ck_kk(m_K, ka, -kb);
384 }
385
386 DiracSpinor radial_rhs(const int kappa_a,
387 const DiracSpinor &Fb) const override final;
388
389 double radialIntegral(const DiracSpinor &Fa,
390 const DiracSpinor &Fb) const override final;
391
392 //! nb: q = alpha*omega!
393 void updateFrequency(const double omega) override final;
394
395private:
396 int m_K;
397 const SphericalBessel::JL_table *m_jl{nullptr};
398 std::vector<double> m_jK_on_qr{};
399 std::vector<double> m_jKp1{};
400 const std::vector<double> *p_jK_on_qr{nullptr};
401 const std::vector<double> *p_jKp1{nullptr};
402
403public:
404 // Default shallow copy semantics (pointer member is shallow-copied)
405 AEk(const AEk &) = default;
406 AEk &operator=(const AEk &) = default;
407};
408
409//==============================================================================
410//! @brief Axial longitudinal multipole operator: \f$ A^L_K = T^{(-1)}_K(q)\gamma^5 \f$
411/*!
412 @details
413 - Gamma^5 variant of the longitudinal multipole operator. Works like
414 `VLk` but with the gamma^5 Dirac structure applied to the angular part.
415 - Supports optional `const SphericalBessel::JL_table *jl` for lookup-table
416 acceleration; otherwise uses on-the-fly Bessel evaluation.
417*/
418class ALk final : public TensorOperator {
419public:
420 ALk(const Grid &gr, int K, double omega,
421 const SphericalBessel::JL_table *jl = nullptr)
422 : TensorOperator(K, Angular::evenQ(K) ? Parity::odd : Parity::even, 1.0,
423 gr.r(), 0, Realness::real, true),
424 m_K(K),
425 m_jl(jl) {
426 if (omega != 0.0)
427 updateFrequency(omega);
428 }
429 std::string name() const override final {
430 return std::string("T^L5_") + std::to_string(m_K);
431 }
432
433 double angularF(const int ka, const int kb) const override final {
434 return Angular::Ck_kk(m_K, ka, -kb);
435 }
436
437 DiracSpinor radial_rhs(const int kappa_a,
438 const DiracSpinor &Fb) const override final;
439
440 double radialIntegral(const DiracSpinor &Fa,
441 const DiracSpinor &Fb) const override final;
442
443 //! nb: q = alpha*omega!
444 void updateFrequency(const double omega) override final;
445
446private:
447 int m_K;
448 const SphericalBessel::JL_table *m_jl{nullptr};
449 std::vector<double> m_jK_on_qr{};
450 std::vector<double> m_jKp1{};
451 const std::vector<double> *p_jK_on_qr{nullptr};
452 const std::vector<double> *p_jKp1{nullptr};
453
454public:
455 // Default shallow copy semantics (pointer member is shallow-copied)
456 ALk(const ALk &) = default;
457 ALk &operator=(const ALk &) = default;
458};
459
460//==============================================================================
461//! @brief Axial magnetic multipole operator: \f$ A^M_K = T^{(0)}_K(q)\gamma^5 \f$
462/*!
463 @details
464 - Gamma^5 variant of the magnetic multipole operator. Analogous to
465 `VMk` but with the gamma^5 Dirac structure applied where appropriate.
466 - Uses spherical Bessel functions j_L(q*r) for radial dependence and
467 accepts an optional `const SphericalBessel::JL_table *jl`.
468*/
469class AMk final : public TensorOperator {
470public:
471 AMk(const Grid &gr, int K, double omega,
472 const SphericalBessel::JL_table *jl = nullptr)
473 : TensorOperator(K, Angular::evenQ(K) ? Parity::even : Parity::odd, 1.0,
474 gr.r(), 0, Realness::real, true),
475 m_K(K),
476 m_jl(jl) {
477 if (omega != 0.0)
478 updateFrequency(omega);
479 }
480
481 std::string name() const override final {
482 return std::string("T^M5_") + std::to_string(m_K);
483 }
484
485 double angularF(const int ka, const int kb) const override final {
486 return Angular::Ck_kk(m_K, ka, kb);
487 }
488
489 DiracSpinor radial_rhs(const int kappa_a,
490 const DiracSpinor &Fb) const override final;
491
492 double radialIntegral(const DiracSpinor &Fa,
493 const DiracSpinor &Fb) const override final;
494
495 //! nb: q = alpha*omega!
496 void updateFrequency(const double omega) override final;
497
498private:
499 int m_K;
500 const SphericalBessel::JL_table *m_jl{nullptr};
501 std::vector<double> m_jK{};
502 const std::vector<double> *p_jK{nullptr};
503
504public:
505 // Default shallow copy semantics (pointer member is shallow-copied)
506 AMk(const AMk &) = default;
507 AMk &operator=(const AMk &) = default;
508};
509
510//==============================================================================
511//! @brief Temporal component of the axial vector multipole operator: \f$ \Theta_K = \Phi^5_K = t^K(q)\gamma^5 \f$
512/*!
513 @details
514 - Gamma^5 variant of the temporal (time-like) component of the vector
515 multipole operator. Functions like `Phik` but with gamma^5 applied to
516 the appropriate spin-angular structure.
517 - Radial dependence uses j_L(q*r) and the constructor accepts an
518 optional `const SphericalBessel::JL_table *jl`.
519*/
520class Phi5k final : public TensorOperator {
521public:
522 Phi5k(const Grid &gr, int K, double omega,
523 const SphericalBessel::JL_table *jl = nullptr)
524 : TensorOperator(K, Angular::evenQ(K) ? Parity::odd : Parity::even, 1.0,
525 gr.r(), 0, Realness::real, true),
526 m_K(K),
527 m_jl(jl) {
528 if (omega != 0.0)
529 updateFrequency(omega);
530 }
531 std::string name() const override final {
532 return std::string("Phi5_") + std::to_string(m_K);
533 }
534
535 double angularF(const int ka, const int kb) const override final {
536 return Angular::Ck_kk(m_K, ka, -kb);
537 }
538
539 DiracSpinor radial_rhs(const int kappa_a,
540 const DiracSpinor &Fb) const override final;
541
542 double radialIntegral(const DiracSpinor &Fa,
543 const DiracSpinor &Fb) const override final;
544
545 //! nb: q = alpha*omega!
546 void updateFrequency(const double omega) override final;
547
548private:
549 int m_K;
550 const SphericalBessel::JL_table *m_jl{nullptr};
551 std::vector<double> m_jK{};
552 const std::vector<double> *p_jK{nullptr};
553
554public:
555 // Default shallow copy semantics (pointer member is shallow-copied)
556 Phi5k(const Phi5k &) = default;
557 Phi5k &operator=(const Phi5k &) = default;
558};
559
560//==============================================================================
561//! @brief Pseudoscalar multipole operator:
562//! \f$ P_K = S^5_K = t^K(q)(i\gamma^0\gamma^5) \f$
563/*!
564 @details
565 - Implements the pseudoscalar multipole operator ~ e^{i q r} i gamma^0 gamma^5.
566 - Radial dependence is provided via spherical Bessel functions j_L(q*r).
567 - Supports an optional `const SphericalBessel::JL_table *jl` for precomputed
568 Bessel lookup; otherwise computes on demand.
569*/
570class S5k final : public TensorOperator {
571public:
572 S5k(const Grid &gr, int K, double omega,
573 const SphericalBessel::JL_table *jl = nullptr)
574 : TensorOperator(K, Angular::evenQ(K) ? Parity::odd : Parity::even, 1.0,
575 gr.r(), 0, Realness::real, true),
576 m_K(K),
577 m_jl(jl) {
578 if (omega != 0.0)
579 updateFrequency(omega);
580 }
581 std::string name() const override final {
582 return std::string("S5_") + std::to_string(m_K);
583 }
584
585 double angularF(const int ka, const int kb) const override final {
586 return Angular::Ck_kk(m_K, ka, -kb);
587 }
588
589 DiracSpinor radial_rhs(const int kappa_a,
590 const DiracSpinor &Fb) const override final;
591
592 double radialIntegral(const DiracSpinor &Fa,
593 const DiracSpinor &Fb) const override final;
594
595 //! nb: q = alpha*omega!
596 void updateFrequency(const double omega) override final;
597
598private:
599 int m_K;
600 const SphericalBessel::JL_table *m_jl{nullptr};
601 std::vector<double> m_jK{};
602 const std::vector<double> *p_jK{nullptr};
603
604public:
605 // Default shallow copy semantics (pointer member is shallow-copied)
606 S5k(const S5k &) = default;
607 S5k &operator=(const S5k &) = default;
608};
609
610//==============================================================================
611//==============================================================================
612
613//! Helper functions for the multipole operators
614namespace multipole {
615
616//! Convert from "transition form" to "moment form" [check sign?]
617inline double moment_factor(int K, double omega) {
618 const auto q = std::abs(PhysConst::alpha * omega);
619 // sign?
620 return qip::double_factorial(2 * K + 1) / qip::pow(q, K) *
621 std::sqrt(K / (K + 1.0));
622}
623
624} // namespace multipole
625
626//==============================================================================
627//==============================================================================
628
629inline std::unique_ptr<DiracOperator::TensorOperator>
630generate_Multipole(const IO::InputBlock &input, const Wavefunction &wf) {
631 using namespace DiracOperator;
632 input.check({
633 {"", "Note: This function cannot use the Spherical Bessel looup table. If "
634 "require efficiency for large number of q values, construct directly"},
635 {"k", "Rank: k=1 for E1, =2 for E2 etc. [1]"},
636 {"omega", "Frequency: nb: q := alpha*omega [1.0e-4]"},
637 {"type", "V,A,S,P (Vector, Axial, Scalar, Pseudoscalar) [V]"},
638 {"low_q", "bool. Use low-q formulas (K=0 and 1 only, no L-form) [false]"},
639 {"component", "E,M,L,T (electric, magnetic, longitudanel, temporal). "
640 "Temporal is forced if type = S or P. [E]"},
641 {"form", "L,V (Length, Velocity); only for electric vector [L]"},
642 });
643 if (input.has_option("help")) {
644 return nullptr;
645 }
646 const auto k = input.get("k", 1);
647 const auto omega = input.get("omega", 1.0e-4);
648
649 const auto low_q = input.get("low_q", false);
650
651 using namespace std::string_literals;
652 const auto type = input.get("type", "V"s);
653 const auto component = input.get("component", "E"s);
654 const auto form = input.get("form", "V"s);
655
656 const bool Vector = qip::ci_wc_compare(type, "V*");
657 const bool AxialVector = qip::ci_wc_compare(type, "A*");
658 const bool Scalar = qip::ci_wc_compare(type, "S*");
659 const bool PseudoScalar = qip::ci_wc_compare(type, "P*");
660
661 const bool Electric = qip::ci_wc_compare(component, "E*");
662 const bool Magnetic = qip::ci_wc_compare(component, "M*");
663 const bool Longitudinal = qip::ci_wc_compare(component, "L*");
664 const bool Temporal = qip::ci_wc_compare(component, "T*");
665
666 const bool LengthForm = qip::ci_wc_compare(form, "L*");
667
668 if (LengthForm && !(Electric && Vector)) {
669 std::cout << "Fail; Length form only valid for Electric Vector\n";
670 }
671
672 if (low_q) {
673 if (Electric && Vector)
674 return std::make_unique<VEk_lowq>(wf.grid(), k, omega);
675 if (Electric && AxialVector)
676 return std::make_unique<AEk_lowq>(wf.grid(), k, omega);
677
678 // Longitudinal
679 if (Longitudinal && Vector)
680 return std::make_unique<VLk_lowq>(wf.grid(), k, omega);
681 if (Longitudinal && AxialVector)
682 return std::make_unique<ALk_lowq>(wf.grid(), k, omega);
683
684 // Magnetic
685 if (Magnetic && Vector)
686 return std::make_unique<VMk_lowq>(wf.grid(), k, omega);
687 if (Magnetic && AxialVector)
688 return std::make_unique<AMk_lowq>(wf.grid(), k, omega);
689
690 // Temporal
691 if (Temporal && Vector)
692 return std::make_unique<Phik_lowq>(wf.grid(), k, omega);
693 if (Temporal && AxialVector)
694 return std::make_unique<Phi5k_lowq>(wf.grid(), k, omega);
695
696 if (Scalar)
697 return std::make_unique<Sk_lowq>(wf.grid(), k, omega);
698 if (PseudoScalar)
699 return std::make_unique<S5k_lowq>(wf.grid(), k, omega);
700 }
701
702 // Electric:
703 if (Electric && LengthForm && Vector)
704 return std::make_unique<VEk_Len>(wf.grid(), k, omega);
705 if (Electric && Vector)
706 return std::make_unique<VEk>(wf.grid(), k, omega);
707 if (Electric && AxialVector)
708 return std::make_unique<AEk>(wf.grid(), k, omega);
709
710 // Longitudinal
711 if (Longitudinal && Vector)
712 return std::make_unique<VLk>(wf.grid(), k, omega);
713 if (Longitudinal && AxialVector)
714 return std::make_unique<ALk>(wf.grid(), k, omega);
715
716 // Magnetic
717 if (Magnetic && Vector)
718 return std::make_unique<VMk>(wf.grid(), k, omega);
719 if (Magnetic && AxialVector)
720 return std::make_unique<AMk>(wf.grid(), k, omega);
721
722 // Temporal
723 if (Temporal && Vector)
724 return std::make_unique<Phik>(wf.grid(), k, omega);
725 if (Temporal && AxialVector)
726 return std::make_unique<Phi5k>(wf.grid(), k, omega);
727
728 if (Scalar)
729 return std::make_unique<Sk>(wf.grid(), k, omega);
730 if (PseudoScalar)
731 return std::make_unique<S5k>(wf.grid(), k, omega);
732
733 std::cout << "Fail; Invalid Combination\n";
734 return std::make_unique<NullOperator>();
735}
736
737//------------------------------------------------------------------------------
738//! @brief Factory for relativistic multipole operators.
739/*!
740 @details
741 Constructs and returns a specific multipole operator derived from
742 DiracOperator::TensorOperator, based on the requested Lorentz structure,
743 multipole component, and momentum-transfer regime.
744
745 The operator corresponds to a spherical multipole of rank @p k with
746 frequency/energy transfer @p omega. The type and component determine
747 the Lorentz structure and spatial character of the interaction.
748
749 @param grid Radial grid on which the operator acts.
750 @param k Multipole rank (total angular momentum of the operator).
751 @param omega Energy (frequency) transfer.
752 @param type Interaction type:
753 - 'V' : Vector
754 - 'A' : Axial-vector
755 - 'S' : Scalar
756 - 'P' : Pseudoscalar
757 @param comp Multipole component (for V/A types):
758 - 'E' : Electric
759 - 'M' : Magnetic
760 - 'L' : Longitudinal
761 - 'T' : Temporal [caution - NOT transverse!]
762 (Ignored for scalar and pseudoscalar operators.)
763 @param low_q If true, construct the low-momentum (long-wavelength)
764 approximation of the operator.
765 @param jl Optional pointer to a precomputed spherical Bessel table.
766 If provided, radial Bessel functions are taken from this
767 table to avoid recomputation. If nullptr, they are generated
768 internally as needed.
769
770 @return A std::unique_ptr to the requested TensorOperator.
771
772 @note Length-form electric operators (if implemented separately) are
773 only valid for vector-electric ('V','E') combinations.
774
775 @warning Invalid combinations of @p type and @p comp may result in
776 a nullptr being returned.
777 */
778std::unique_ptr<DiracOperator::TensorOperator>
779MultipoleOperator(const Grid &grid, int k, double omega, char type, char comp,
780 bool low_q, const SphericalBessel::JL_table *jl = nullptr);
781
782} // namespace DiracOperator
Axial electric multipole operator: .
Definition EM_multipole.hpp:367
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:382
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:378
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:329
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:392
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:362
Axial longitudinal multipole operator: .
Definition EM_multipole.hpp:418
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:410
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:433
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:446
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:429
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:432
Axial magnetic multipole operator: .
Definition EM_multipole.hpp:469
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:485
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:467
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:481
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:506
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:489
Temporal component of the axial vector multipole operator: .
Definition EM_multipole.hpp:520
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:535
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:522
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:531
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:550
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:539
Temporal component of the vector multipole operator: .
Definition EM_multipole.hpp:254
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:244
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:261
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:269
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:272
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:265
Pseudoscalar multipole operator: .
Definition EM_multipole.hpp:570
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:581
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:582
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:565
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:592
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:585
Scalar multipole operator: .
Definition EM_multipole.hpp:309
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:320
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:287
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:324
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:314
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:304
General operator (virtual base class); operators derive from this.
Definition TensorOperator.hpp:65
Vector electric multipole (transition) operator, Length-form: ( ).
Definition EM_multipole.hpp:28
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:9
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:43
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:39
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:31
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:47
Vector electric multipole (V-form) operator: .
Definition EM_multipole.hpp:86
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:97
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:111
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:101
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:92
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:66
Vector longitudinal multipole operator (V-form): .
Definition EM_multipole.hpp:143
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:158
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:154
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:132
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:170
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:156
Vector magnetic multipole operator: .
Definition EM_multipole.hpp:199
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.cpp:229
DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition EM_multipole.cpp:191
double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Radial part of integral R_ab = (Fa|t|Fb).
Definition EM_multipole.cpp:213
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:211
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition EM_multipole.hpp:215
s (spin) operator
Definition jls.hpp:50
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:41
Holds grid, including type + Jacobian (dr/du)
Definition Grid.hpp:31
const std::vector< double > & r() const
Grid points, r.
Definition Grid.hpp:75
Holds list of Options, and a list of other InputBlocks. Can be initialised with a list of options,...
Definition InputBlock.hpp:153
bool check(std::initializer_list< std::string > blocks, const std::vector< std::pair< std::string, std::string > > &list, bool print=false) const
Check all the options and blocks in this; if any of them are not present in 'list',...
Definition InputBlock.hpp:606
bool has_option(std::string_view key) const
Check is option is present (even if not set) in current block.
Definition InputBlock.hpp:212
T get(std::string_view key, T default_value) const
If 'key' exists in the options, returns value. Else, returns default_value. Note: If two keys with sa...
Definition InputBlock.hpp:428
Spherical Bessel Lookup table; in the form j_L(qr) = J[L][q][r].
Definition SphericalBessel.hpp:120
Stores Wavefunction (set of valence orbitals, grid, HF etc.)
Definition Wavefunction.hpp:37
const Grid & grid() const
Returns a const reference to the radial grid.
Definition Wavefunction.hpp:82
constexpr bool evenQ(int a)
Returns true if a is even - for integer values.
Definition Wigner369j.hpp:154
double Ck_kk(int k, int ka, int kb)
Reduced (relativistic) angular ME: <ka||C^k||kb> [takes k and kappa].
Definition Wigner369j.hpp:306
double moment_factor(int K, double omega)
Convert from "transition form" to "moment form" [check sign?].
Definition EM_multipole.hpp:617
Dirac Operators: General + derived.
Definition GenerateOperator.cpp:3
std::unique_ptr< DiracOperator::TensorOperator > MultipoleOperator(const Grid &grid, int k, double omega, char type, char comp, bool low_q, const SphericalBessel::JL_table *jl)
Factory for relativistic multipole operators.
Definition EM_multipole.cpp:607
constexpr double alpha
Fine-structure constant: alpha = 1/137.035 999 177(21) [CODATA 2022].
Definition PhysConst_constants.hpp:24
constexpr T double_factorial(T x)
Double factorial x!! - nb: does not check for overflow. Max x is 20 for uint64_t.
Definition Maths.hpp:131
bool ci_wc_compare(std::string_view s1, std::string_view s2)
Compares two strings, s1 and s2. s2 may contain ONE wildcard ('*') which will match anything....
Definition String.hpp:140
constexpr auto pow(T x)
x^n for integer n (n compile-time template parameter), x any arithmetic type (T). Returns double for ...
Definition Maths.hpp:88