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