High-precision calculations for one- and two-valence atomic systems
TensorOperator.hpp
1#pragma once
2#include "Angular/Wigner369j.hpp"
3#include "Maths/Grid.hpp"
4#include "Maths/NumCalc_quadIntegrate.hpp"
5#include "Maths/SphericalBessel.hpp"
6#include "Physics/PhysConst_constants.hpp"
7#include "Potentials/NuclearPotentials.hpp"
8#include "Wavefunction/DiracSpinor.hpp"
9#include "qip/Vector.hpp"
10#include <cmath>
11#include <memory>
12#include <string>
13#include <vector>
14
15/*!
16 @brief Dirac operators: TensorOperator base class and derived implementations for single-particle (one-body) spherical tensor operators.
17
18 @details
19 This namespace contains the TensorOperator base class and all derived
20 operator classes. Operators act on DiracSpinor objects and compute reduced
21 matrix elements (RMEs) of the form:
22
23 \f[
24 \langle a \| \hat{h} \| b \rangle = A_{ab} \cdot R_{ab}
25 \f]
26
27 where \f$ A_{ab} \f$ is a purely angular factor and \f$ R_{ab} \f$ is a radial
28 integral. New operators are implemented by deriving from TensorOperator (or
29 ScalarOperator for rank-0 operators) and overriding the relevant virtual
30 functions; see TensorOperator for details.
31
32 Operators are typically constructed via @ref generate(), which takes a name
33 string and an InputBlock; see GenerateOperator.hpp for the full list of
34 available operators.
35
36 See @ref TensorOperator class documentation for main descriptions.
37 All usable tensor operators dervive from that virtual class.
38
39 From the command line, use
40 ```shell
41 ampsci -o
42 ```
43 to get a list of available operators.
44 For a specific operator 'OperatorName', use:
45 ```shell
46 ampsci -o OperatorName
47 ```
48 to see available run-time options for that operator.
49 These options are passed to @ref generate().
50
51 @note New operator classes must be registered in the @ref operator_list in
52 GenerateOperator.hpp to be accessible at runtime.
53*/
54namespace DiracOperator {
55
56//! Parity of operator
57enum class Parity { even, odd, Error };
58
59/*!
60 @brief Specifies whether an operator's matrix elements are real or imaginary.
61 @details
62 Operators must have purely real or purely imaginary reduced matrix elements.
63 For imaginary operators, the imaginary part is computed and stored; the real
64 part is identically zero. This distinction affects the Hermitian symmetry
65 relation
66 \f[
67 \langle b \| \hat{h} \| a \rangle = \pm \langle a \| \hat{h} \| b \rangle
68 \f]
69*/
70enum class Realness { real, imaginary, Error };
71
72//! Type of matrix element returned
73/*! @details
74 Specifies which form of matrix element is used or returned.
75
76 @value Reduced : Reduced matrix element
77 @value Stretched : Matrix element for stretched states (m = j)
78 @value HFConstant : Hyperfine (A,B,C...) constant
79
80 For off-diagonal, j:=min(ja,jb)
81*/
82enum class MatrixElementType { Reduced, Stretched, HFConstant, Error };
83
84//! Convert string to Parity
85Parity parse_Parity(const std::string &s);
86
87//! Convert Parity to string
88std::string parse_Parity(Parity p);
89
90//! Convert string to Realness
91Realness parse_Realness(const std::string &s);
92
93//! Convert Realness to string
94std::string parse_Realness(Realness r);
95
96//! Convert string to MatrixElementType
97MatrixElementType parse_MatrixElementType(const std::string &s);
98
99//! Convert MatrixElementType to string
101
102//==============================================================================
103/*!
104 @brief General tensor operator (virtual base class);
105 all single-particle (one-body) tenosor operators derive from this.
106
107 @details
108
109 Represents a single-particle (one-body) tensor operator
110 \f$ \hat{h} \f$ of rank-\f$ k \f$ and parity \f$ \pi \f$,
111 thats acts on Dirac spinors.
112 The core quantity is the _reduced matrix element_ (RME):
113
114 \f[
115 \langle a \| \hat{h} \| b \rangle \equiv A_{ab} \cdot R_{ab}
116 \f]
117
118 where \f$ A_{ab} \f$ is the angular factor (see angularF()) and \f$ R_{ab} \f$
119 is the radial integral (see radialIntegral()).
120
121 The default radial integral is:
122
123 \f[
124 R_{ab} = c \int_0^\infty v(r)\left(
125 C_{ff}\,f_a f_b + C_{fg}\,f_a g_b +
126 C_{gf}\,g_a f_b + C_{gg}\,g_a g_b
127 \right)\,{\rm d}r
128 \f]
129
130 where \f$ c \f$ is an overall multiplicative constant (1 by default),
131 \f$ v(r) \f$ is an optional radial function (stored in m_vec),
132 and the \f$ C_{xy} \f$ are angular coefficients returned by @ref angularCff() etc.
133
134 - Operators must be spherical tensor operators of defined rank and parity
135 - Operators must have pure real, or pure imaginary matrix elements
136 - If matrix elements are imaginary, class returns the imaginary part
137 - For operators with imaginary matrix elements, the @ref Realness member variable (m_Realness) must be set to Realness::imaginary via the virtual base class constructor @ref TensorOperator() (impacts symmetry)
138 - (For brevity, we may refer to operators whose matrix elements are imaginary as 'imaginary operators'. This is sometimes confusing.)
139
140 ## Implementing a derived operator
141
142 TensorOperator is a virtual base class and cannot be constructed directly.
143
144 @note All derived operators must implement angularF()
145
146 Beyond that, there are two levels of customisation:
147
148 ### Standard case -- override angular factors only:
149
150 Implement angularF() (required), and optionally
151 @ref angularCff(), angularCgg(), angularCfg(), angularCgf().
152 The radial integral is then handled
153 automatically by the default @ref radial_rhs() and @ref radialIntegral().
154 The \f$ v(r) \f$ radial function, and overall constant \f$ c \f$ are set
155 within the constructor @ref TensorOperator::TensorOperator(), which should
156 be called by any deriving class.
157 Then, the default radial integral as above will be used.
158
159 ### Non-standard case -- override the radial functions:
160
161 If the radial integral cannot be expressed in the above standard/default form
162 (e.g., it involves derivatives, non-local terms, or more complicated radial structure),
163 you shuold override radial_rhs() **and** radialIntegral() directly.
164 In that case both should be consistent with each other:
165
166 - radial_rhs(ka, Fb) returns the spinor \f$ \delta F_b \f$ such that
167
168 \f[ F_a \cdot \delta F_b = R_{ab} \f]
169
170 - radialIntegral(Fa, Fb) returns \f$ R_{ab} \f$ directly
171 - Generally, this should be directly checked with a unit test
172
173 @note
174 - If radialIntegral() is overridden then radial_rhs() _must_ be overridden
175 (and vice versa), or results will be inconsistent.
176 It's OK (but inefficient) to define radialIntegral(a,b) = a*radial_rhs(b).
177 But that is not the default.
178
179 @warning
180 - Frequency-dependent operators: if the operator depends on the
181 transition frequency, or energy/momentum transfer,
182 (e.g., dynamic multipole operators), pass
183 freq_dep=true to the constructor and override updateFrequency(). This
184 function must be called with the current frequency before computing
185 any matrix elements. Failing to override it will abort at runtime.
186
187 @note
188 - You may not construct a TensorOperator directly. Construct one of the
189 derived classes; see [Operators/include.hpp](Operators/include.hpp) for the list.
190
191 --
192
193 @note
194 - To expose a new operator at runtime (e.g., via `ampsci -o`), add it to
195 the @ref operator_list in GenerateOperator.hpp and provide a corresponding
196 `generate_XYZ()` factory function.
197*/
199protected:
200 /*!
201 @brief Constructs a specific tensor operator. Called by derived classes.
202 @details
203 Initialises the basic properties of a tensor operator.
204
205 @param rank_k Tensor rank k of the operator.
206 @param pi Parity of the operator (Parity::even or Parity::odd).
207 @param constant Multiplicative constant c, included in the radial integral.
208 @param vec Overall v=f(r) radial function, as std::vector.
209 May be impty, in which case it is not used (equivilant to vector of 1)
210 @param RorI Specifies whether the matrix element is real or imaginary
211 (Realness::real or Realness::imaginary).
212 @param freq_dep Indicates whether the operator depends on frequency
213 (e.g., dynamic external fields).
214
215 @note TensorOperator is a virtual base class and may not be constructed
216 directly. It is intended to be initialised by derived operator classes.
217 */
218 TensorOperator(int rank_k, Parity pi, double constant = 1.0,
219 const std::vector<double> &vec = {},
220 Realness RorI = Realness::real, bool freq_dep = false,
221 bool odd_in_omega = false)
222 : m_rank(rank_k),
223 m_parity(pi),
224 m_Realness(RorI),
225 m_freqDependantQ(freq_dep),
226 m_oddInOmegaQ(odd_in_omega),
227 m_constant(constant),
228 m_vec(vec) {};
229
230public:
231 //! Used to pass generic parameters to update() function
232 /*! @details
233 Override in derived class, then use as:
234
235 auto const* params = dynamic_cast<const Params*>(&p);
236
237 Rarely used.
238 */
239 struct Params {
240 virtual ~Params() = default;
241 };
242
243public:
244 virtual ~TensorOperator() = default;
245 TensorOperator(const TensorOperator &) = default;
246 TensorOperator &operator=(const TensorOperator &) = default;
247 TensorOperator(TensorOperator &&) = default;
248 TensorOperator &operator=(TensorOperator &&) = default;
249
250protected:
251 int m_rank;
252 Parity m_parity;
253 Realness m_Realness;
254 bool m_freqDependantQ{false};
255 bool m_oddInOmegaQ{false};
256
257protected:
258 // these may be updated for frequency-dependant operators
259 double m_constant; // included in radial integral
260 std::vector<double> m_vec;
261 double m_omega{0.0};
262
263public:
264 //! Returns true if the operator is frequency-dependent (requires updateFrequency() calls).
265 bool freqDependantQ() const { return m_freqDependantQ; }
266
267 //! Sign of the operator at -|omega| relative to that at +|omega|:
268 //! h(-|omega|) = h_minus_sign() * h(+|omega|): -1 for an operator odd in
269 //! the frequency (e.g. E1v), +1 otherwise (depends on |omega| only). So
270 //! the operator need only ever be set at +|omega|; the emission
271 //! (e^{+i omega t}) side carries this sign
272 double h_minus_sign() const { return m_oddInOmegaQ ? -1.0 : 1.0; }
273
274 //! Returns the current frequency set by the last updateFrequency() call.
275 //! Zero for frequency-independent operators.
276 double omega() const { return m_omega; }
277
278public:
279 //! Returns true if <a|h|b> = 0 by rank/parity selection rules.
280 bool isZero(int ka, int kb) const;
281 //! Overload taking DiracSpinors; forwards to isZero(ka, kb).
282 bool isZero(const DiracSpinor &Fa, const DiracSpinor &Fb) const;
283
284 //! Returns true if the matrix element is non-zero by angular momentum and parity selection rules (arguments are 2j and pi as integers).
285 bool selectrion_rule(int twoJA, int piA, int twoJB, int piB) const {
286 if (twoJA == twoJB && twoJA == 0.0)
287 return false;
288
289 if (Angular::triangle(twoJA, twoJB, 2 * m_rank) == 0)
290 return false;
291
292 return (m_parity == Parity::even) == (piA == piB);
293 }
294
295 /*!
296 @brief Updates the operator for a new frequency omega.
297
298 @details
299 Must be overridden by any frequency-dependent operator (i.e., where
300 freqDependantQ() returns true). Called before computing matrix elements
301 whenever the frequency changes. Does nothing for a frequency-independent
302 operator, so may be called on any operator.
303
304 The base class implementation aborts for a frequency-dependent operator:
305 if freq_dep=true is set but this function is not overridden, it will
306 abort at runtime when called.
307
308 @param omega Frequency in atomic units.
309
310 @note Derived implementations must set @p m_omega = omega so that omega()
311 reflects the current frequency.
312 */
313 virtual void updateFrequency(const double) {
314 if (!m_freqDependantQ)
315 return;
316 std::cout
317 << "Must reimplement updateFrequency() for freq. dep operators!\n";
318 std::cout << this->name() << "\n";
319 std::abort();
320 };
321
322 /*!
323 Updates the rank of operator (rarely used). Generally also updates parity
324
325 @details
326 Use with caution.
327 @warning Will usually have to call updateFrequency() after this!
328 Updating rank often breaks frequency-dependence of radial functions
329 (e.g., spherical Bessel functions)
330 */
331 virtual void updateRank(int) {
332 std::cout << "Must reimplement updateRank() is needed\n";
333 std::cout << this->name() << "\n";
334 std::abort();
335 }
336
337 //! Returns a const ref to the stored vector v
338 const std::vector<double> &getv() const { return m_vec; }
339
340 //! Returns the "overall" constant c
341 double getc() const { return m_constant; }
342
343 //! returns true if operator is imaginary (has imag MEs)
344 bool imaginaryQ() const { return (m_Realness == Realness::imaginary); }
345
346 //! Rank k of operator
347 int rank() const { return m_rank; }
348
349 //! returns parity, as integer (+1 or -1)
350 int parity() const { return (m_parity == Parity::even) ? 1 : -1; }
351
352 //! returns relative sign between <a||x||b> and <b||x||a>
353 int symm_sign(const DiracSpinor &Fa, const DiracSpinor &Fb) const {
354 const auto sra_i = imaginaryQ() ? -1 : 1;
355 const auto sra = Angular::neg1pow_2(Fa.twoj() - Fb.twoj());
356 return sra_i * sra;
357 }
358
359 //! Returns "name" of operator (e.g., 'E1')
360 virtual std::string name() const { return "Operator"; };
361 //! Returns units of operator as a string (usually au, may be MHz, etc.)
362 virtual std::string units() const { return "au"; };
363
364public:
365 // These are needed for radial integrals
366 // Usually just constants, but can also be functions of kappa
367
368 /*!
369 @brief Angular coefficient C_ff for the f_a*f_b term of the radial integral.
370
371 @details
372 The default radial integral is structured as:
373
374 \f[
375 R_{ab} = c\int_0^\infty v(r)\left(
376 C_{ff}\,f_a f_b + C_{fg}\,f_a g_b +
377 C_{gf}\,g_a f_b + C_{gg}\,g_a g_b
378 \right)\,{\rm d}r
379 \f]
380
381 These coefficients are often constants, but may depend on
382 \f$ \kappa_a, \kappa_b \f$ for operators with angular-momentum-dependent
383 coupling between large and small components (e.g., spin-dependent
384 operators). Override in derived classes as needed.
385
386 @param kappa_a kappa \f$ \kappa_a \f$ for left-hand-side (bra)
387 @param kappa_b kappa \f$ \kappa_b \f$ for right-hand-side (ket)
388
389 @note Only relevant when using the default radial_rhs()/radialIntegral().
390 If those are overridden, these are not called.
391 */
392 virtual double angularCff(int kappa_a, int kappa_b) const {
393 (void)kappa_a, (void)kappa_b;
394 return 1.0;
395 }
396 //! Angular coefficient C_gg for the g_a*g_b term of the radial integral.
397 virtual double angularCgg(int, int) const { return 1.0; }
398 //! Angular coefficient C_fg for the f_a*g_b term of the radial integral.
399 virtual double angularCfg(int, int) const { return 0.0; }
400 //! Angular coefficient C_gf for the g_a*f_b term of the radial integral.
401 virtual double angularCgf(int, int) const { return 0.0; }
402
403 /*!
404 @brief Dispatches to angularCff/fg/gf/gg based on component indices x, y.
405 @details
406 Convenience dispatcher: x and y index the spinor component (0 = large/f,
407 1 = small/g) of the bra and ket respectively, and returns the
408 corresponding angular coefficient C_xy(ka, kb).
409
410 See @ref angularCff()
411
412 | x | y | returns |
413 |---|---|----------------|
414 | 0 | 0 | angularCff() |
415 | 0 | 1 | angularCfg() |
416 | 1 | 0 | angularCgf() |
417 | 1 | 1 | angularCgg() |
418
419 @param x Bra component index: 0=f (large), 1=g (small).
420 @param y Ket component index: 0=f (large), 1=g (small).
421 @param kappa_a kappa \f$ \kappa_a \f$ for left-hand-side (bra)
422 @param kappa_b kappa \f$ \kappa_b \f$ for right-hand-side (ket)
423
424 @note Only relevant when using the default radial_rhs()/radialIntegral().
425 If those are overridden, these are not called.
426 */
427 double angularCxy(uint8_t x, uint8_t y, int kappa_a, int kappa_b) const {
428 assert((x <= 1 && y <= 1) && "x and y must be 0 or 1 in angularCxy");
429 return x == 0 ? (y == 0 ? angularCff(kappa_a, kappa_b) :
430 angularCfg(kappa_a, kappa_b)) :
431 (y == 0 ? angularCgf(kappa_a, kappa_b) :
432 angularCgg(kappa_a, kappa_b));
433 }
434
435public:
436 /*!
437 @brief Angular factor A_ab linking the radial integral to the RME.
438
439 @details
440 All derived operators must implement this. It gives the purely angular
441 part of the reduced matrix element:
442
443 \f[
444 \langle a \| \hat{h} \| b \rangle \equiv A_{ab} \cdot R_{ab}
445 \f]
446
447 where \f$ R_{ab} \f$ is returned by radialIntegral(). For most operators,
448 \f$ A_{ab} \f$ is a product of Clebsch-Gordan / 3j coefficients and
449 depends only on \f$ \kappa_a, \kappa_b \f$
450 (and the rank \f$ k \f$ and parity \f$ \pi \f$ of the operator).
451
452 @note This is a pure virtual function -- every derived operator must
453 provide an implementation.
454 */
455 virtual double angularF(const int, const int) const = 0;
456
457 /*!
458 @brief Creates a polymorphic copy of the operator at its current state,
459 or nullptr if cloning is not supported by the derived class.
460 @details
461 Frequency-dependent operators must override this so the TDHF solver
462 can construct an independent \f$ t_- = t_+^\dagger(-\omega) \f$ operator.
463
464 Most operators can implement this as a one-liner using the copy constructor:
465 \code
466 return std::make_unique<DerivedClass>(*this);
467 \endcode
468 This works whenever the copy constructor correctly preserves all state,
469 including m_vec, m_constant, and m_omega.
470
471 @return Owning pointer to a copy, or nullptr if cloning is unsupported.
472 @note The base class returns nullptr. Operators whose copy constructor is
473 deleted or otherwise unavailable cannot use this pattern and must either
474 implement a manual clone or leave the default nullptr.
475 */
476 virtual std::unique_ptr<TensorOperator> clone() const { return nullptr; }
477
478 //! @brief Computes the right-hand spinor dF_b for the radial integral.
479 /*!
480 @details
481 Returns \f$ \delta F_b \f$ such that the radial integral satisfies:
482
483 \f[
484 R_{ab} = F_a \cdot \delta F_b
485 = \int_0^\infty \left(f_a\,\delta f_b + g_a\,\delta g_b\right)\,{\rm d}r
486 \f]
487
488 The default implementation constructs \f$ \delta F_b \f$ using the stored
489 radial function \f$ v(r) \f$ and the angular coefficients:
490
491 \f[
492 \delta F_b(r) = c\,v(r)
493 \begin{pmatrix}
494 C_{ff}\,f_b(r) + C_{fg}\,g_b(r) \\
495 C_{gf}\,f_b(r) + C_{gg}\,g_b(r)
496 \end{pmatrix}
497 \f]
498
499 This is used by reduced_rhs() to build \f$ \langle a \| \hat{h} \| b \rangle \f$
500 as a spinor-valued quantity, enabling perturbation theory and TDHF.
501 Override this for operators whose radial structure cannot be expressed in
502 this standard form.
503
504 @param kappa_a Relativistic quantum number \f$ \kappa_a \f$ of the bra state
505 (needed to evaluate the angular coefficients).
506 @param Fb Ket DiracSpinor \f$ F_b \f$ .
507
508 @return DiracSpinor \f$ \delta F_b \f$ .
509
510 @warning If this is overridden, radialIntegral() should also be overridden
511 consistently (and vice versa), so that reducedME() and reduced_rhs() remain
512 consistent.
513 */
514 virtual DiracSpinor radial_rhs(const int kappa_a,
515 const DiracSpinor &Fb) const;
516
517 //! @brief Radial integral R_ab, defined by RME = angularF(a,b) * radialIntegral(a,b).
518 /*!
519 @details
520 Returns the radial part \f$ R_{ab} \f$ of the reduced matrix element:
521
522 \f[
523 \langle a \| \hat{h} \| b \rangle = A_{ab} \cdot R_{ab}
524 \f]
525
526 where \f$ A_{ab} \f$ is angularF().
527
528 The default implementation evaluates \f$ R_{ab} = F_a \cdot \delta F_b \f$ ,
529 using the default radial structure:
530
531 \f[
532 R_{ab} = c\int_0^\infty v(r)\left(
533 C_{ff}\,f_a f_b + C_{fg}\,f_a g_b +
534 C_{gf}\,g_a f_b + C_{gg}\,g_a g_b
535 \right)\,{\rm d}r
536 \f]
537
538 Override this for operators that do not fit this standard form. If
539 radial_rhs() is also overridden, both must remain mutually consistent.
540
541 @warning If radial_rhs() is overridden but radialIntegral() is not (or
542 vice versa), reducedME() and reduced_rhs() will give inconsistent results.
543 */
544 virtual double radialIntegral(const DiracSpinor &Fa,
545 const DiracSpinor &Fb) const;
546
547 /*!
548 @brief 3j-symbol factor linking the full ME to the RME.
549 @details
550 \f[ (-1)^{j_a - m_a} \begin{pmatrix} j_a & k & j_b \\ -m_a & q & m_b \end{pmatrix} \f]
551 such that \f$\langle a | \hat{h} | b \rangle = {\rm rme3js} \times \langle a \| \hat{h} \| b \rangle\f$.
552 All arguments are twice the actual value (2*j, 2*m, 2*q).
553 */
554 double rme3js(int twoja, int twojb, int two_mb = 1, int two_q = 0) const;
555
556 //! Overload of rme3js taking DiracSpinors.
557 double rme3js(const DiracSpinor &Fa, const DiracSpinor &Fb, int two_mb = 1,
558 int two_q = 0) const {
559 return rme3js(Fa.twoj(), Fb.twoj(), two_mb, two_q);
560 }
561
562 //! Returns angularF(ka,kb) * radial_rhs(ka,Fb); spinor-valued RME action on Fb, used in perturbation theory/TDHF.
563 DiracSpinor reduced_rhs(const int ka, const DiracSpinor &Fb) const;
564
565 //! As reduced_rhs but for the conjugate direction; Fb * reduced_lhs(ka, Fb) = <b||h||a>.
566 DiracSpinor reduced_lhs(const int ka, const DiracSpinor &Fb) const;
567
568 //! Returns the reduced matrix element <a||h||b> = A_ab * R_ab.
569 double reducedME(const DiracSpinor &Fa, const DiracSpinor &Fb) const;
570
571 //! Returns "full" matrix element, for optional (ma, mb, q) [taken as int 2*].
572 //! If not specified, returns z-component (q=0), with ma=mb=min(ja,jb)
573 double fullME(const DiracSpinor &Fa, const DiracSpinor &Fb,
574 std::optional<int> two_ma = std::nullopt,
575 std::optional<int> two_mb = std::nullopt,
576 std::optional<int> two_q = std::nullopt) const;
577
578 //! Returns the factor to convert a reduced ME to a different form (Reduced, Stretched, or HFConstant); see MatrixElementType.
579 double matel_factor(MatrixElementType type, int twoJa, int twoJb) const;
580
581 //! Overload of matel_factor taking DiracSpinors.
583 const DiracSpinor &Fb) const {
584 return matel_factor(type, Fa.twoj(), Fb.twoj());
585 }
586};
587
588//============================================================================
589//============================================================================
590/*!
591 @brief Rank-0 (scalar) tensor operator; derives from TensorOperator with k=0.
592 @details
593 Convenience base for scalar operators. angularF() returns \f$ \sqrt{2|\kappa|} \f$
594 when \f$ |\kappa_a|=|\kappa_b| \f$ , and zero otherwise. The four C_xy coefficients
595 default to (1,0,0,1) (diagonal ff+gg), but can be set via in_g for operators
596 with large/small component mixing (e.g., PNC operators).
597*/
599public:
600 //! General scalar operator constructor. in_g = {C_ff, C_fg, C_gf, C_gg}.
601 ScalarOperator(Parity pi, double in_coef,
602 const std::vector<double> &in_v = {},
603 const std::array<int, 4> &in_g = {1, 0, 0, 1},
604 Realness rori = Realness::real)
605 : TensorOperator(0, pi, in_coef, in_v, rori),
606 c_ff(in_g[0]),
607 c_fg(in_g[1]),
608 c_gf(in_g[2]),
609 c_gg(in_g[3]) {}
610
611public:
612 virtual double angularF(const int ka, const int kb) const override {
613 // For scalar operators, <a||h||b> = RadInt / 3js
614 // 3js:= 1/(Sqrt[2j+1]) ... depends on m???
615 // |k| = j+1/2
616 return (std::abs(ka) == std::abs(kb)) ? std::sqrt(2.0 * std::abs(ka)) : 0.0;
617 }
618
619private:
620 const double c_ff, c_fg, c_gf, c_gg;
621
622protected:
623 double virtual angularCff(int, int) const override { return c_ff; }
624 double virtual angularCgg(int, int) const override { return c_gg; }
625 double virtual angularCfg(int, int) const override { return c_fg; }
626 double virtual angularCgf(int, int) const override { return c_gf; }
627};
628
629//------------------------------------------------------------------------------
630//! Speacial operator: 0
631class NullOperator final : public ScalarOperator {
632public:
633 NullOperator() : ScalarOperator(Parity::even, 0, {}) {}
634
635protected:
636 double angularCff(int, int) const override final { return 0.0; }
637 double angularCgg(int, int) const override final { return 0.0; }
638 double angularCfg(int, int) const override final { return 0.0; }
639 double angularCgf(int, int) const override final { return 0.0; }
640};
641
642//******************************************************************************
643// Helper functions: Useful for several operators
644//******************************************************************************
645
646//! Pab function: Int[ (fa*gb + pm*ga*fb) * t(r) , dr]. pm = +/-1 (usually)
647/*! @details
648Note: does not know selection rules, so only evaluate if non-zero
649*/
650double Pab(double pm, const std::vector<double> &t, const DiracSpinor &Fa,
651 const DiracSpinor &Fb);
652
653//! Rab function: Int[ (fa*fb + pm*ga*gb) * t(r) , dr]. pm = +/-1 (usually)
654/*! @details
655Note: does not know selection rules, so only evaluate if non-zero
656*/
657double Rab(double pm, const std::vector<double> &t, const DiracSpinor &Fa,
658 const DiracSpinor &Fb);
659
660//! Pab_rhs function: dF_ab += A * t(r) * (g, pm*f) , pm=+/-1 (usually).
661//! NOTE: uses +=, so can combine. Ensure empty to begin.
662/*! @details
663Note: does not know selection rules, so only evaluate if non-zero.
664Should have Fa*Pab_rhs = A * Pab.
665*/
666void Pab_rhs(double pm, const std::vector<double> &t, DiracSpinor *dF,
667 const DiracSpinor &Fb, double A = 1.0);
668
669//! Rab_rhs function: dF_ab += A * t(r) * (f, pm*g) , pm=+/-1 (usually).
670//! NOTE: uses +=, so can combine. Ensure empty to begin.
671/*! @details
672Note: does not know selection rules, so only evaluate if non-zero.
673Should have Fa*Rab_rhs = A * Rab.
674*/
675void Rab_rhs(double pm, const std::vector<double> &t, DiracSpinor *dF,
676 const DiracSpinor &Fb, double A = 1.0);
677
678//! Vab function: Int[ (fa*gb ) * t(r) , dr].
679double Vab(const std::vector<double> &t, const DiracSpinor &Fa,
680 const DiracSpinor &Fb);
681//! Wab function: Int[ (ga*fb ) * t(r) , dr].
682double Wab(const std::vector<double> &t, const DiracSpinor &Fa,
683 const DiracSpinor &Fb);
684
685//! Gab function: Int[ (ga*gb ) * t(r) , dr].
686double Gab(const std::vector<double> &t, const DiracSpinor &Fa,
687 const DiracSpinor &Fb);
688
689//! Gab_rhs function: dF += a * t(r) * (0, g_b). NOTE: uses +=, so can combine.
690void Gab_rhs(const std::vector<double> &t, DiracSpinor *dF,
691 const DiracSpinor &Fb, double a);
692
693// Same - for constant t(r)=c
694
695//! Pab[1] function: Int[ (fa*gb + pm*ga*fb) , dr]. pm = +/-1 (usually)
696double Pab(double pm, const DiracSpinor &Fa, const DiracSpinor &Fb);
697
698//! Rab[1] function: Int[ (fa*fb + pm*ga*gb) , dr] = Int[ (pm-1)*ga*gb) , dr].
699//! NOTE: assumes NOT diagonal, using orthogonality condition.
700double Rab(double pm, const DiracSpinor &Fa, const DiracSpinor &Fb);
701
702//! Pab_rhs[1] function: dF_ab += A * (g, pm*f) , pm=+/-1 (usually).
703void Pab_rhs(double pm, DiracSpinor *dF, const DiracSpinor &Fb, double A = 1.0);
704
705//! Rab_rhs[1] function: dF_ab += A * (f, pm*g) = dF_ab += A * (0, (pm-1)*g).
706//! NOTE: assumes NOT diagonal, using orthogonality condition.
707void Rab_rhs(double pm, DiracSpinor *dF, const DiracSpinor &Fb, double A = 1.0);
708
709//! Gab = Int[ ga*gb , dr] - (just relativistic correction part of integral)
710double Gab(const DiracSpinor &Fa, const DiracSpinor &Fb);
711
712//! Gab_rhs(r) += a*g_b(r). Note: uses += so may be sumulative
713void Gab_rhs(DiracSpinor *dF, const DiracSpinor &Fb, double a);
714
715} // namespace DiracOperator
Speacial operator: 0.
Definition TensorOperator.hpp:631
double angularCgg(int, int) const override final
Angular coefficient C_gg for the g_a*g_b term of the radial integral.
Definition TensorOperator.hpp:637
double angularCff(int, int) const override final
Angular coefficient C_ff for the f_a*f_b term of the radial integral.
Definition TensorOperator.hpp:636
double angularCfg(int, int) const override final
Angular coefficient C_fg for the f_a*g_b term of the radial integral.
Definition TensorOperator.hpp:638
double angularCgf(int, int) const override final
Angular coefficient C_gf for the g_a*f_b term of the radial integral.
Definition TensorOperator.hpp:639
Rank-0 (scalar) tensor operator; derives from TensorOperator with k=0.
Definition TensorOperator.hpp:598
ScalarOperator(Parity pi, double in_coef, const std::vector< double > &in_v={}, const std::array< int, 4 > &in_g={1, 0, 0, 1}, Realness rori=Realness::real)
General scalar operator constructor. in_g = {C_ff, C_fg, C_gf, C_gg}.
Definition TensorOperator.hpp:601
virtual double angularCgf(int, int) const override
Angular coefficient C_gf for the g_a*f_b term of the radial integral.
Definition TensorOperator.hpp:626
virtual double angularCfg(int, int) const override
Angular coefficient C_fg for the f_a*g_b term of the radial integral.
Definition TensorOperator.hpp:625
virtual double angularCgg(int, int) const override
Angular coefficient C_gg for the g_a*g_b term of the radial integral.
Definition TensorOperator.hpp:624
virtual double angularF(const int ka, const int kb) const override
Angular factor A_ab linking the radial integral to the RME.
Definition TensorOperator.hpp:612
virtual double angularCff(int, int) const override
Angular coefficient C_ff for the f_a*f_b term of the radial integral.
Definition TensorOperator.hpp:623
General tensor operator (virtual base class); all single-particle (one-body) tenosor operators derive...
Definition TensorOperator.hpp:198
double omega() const
Returns the current frequency set by the last updateFrequency() call. Zero for frequency-independent ...
Definition TensorOperator.hpp:276
double fullME(const DiracSpinor &Fa, const DiracSpinor &Fb, std::optional< int > two_ma=std::nullopt, std::optional< int > two_mb=std::nullopt, std::optional< int > two_q=std::nullopt) const
Returns "full" matrix element, for optional (ma, mb, q) [taken as int 2*]. If not specified,...
Definition TensorOperator.cpp:67
virtual double angularCgf(int, int) const
Angular coefficient C_gf for the g_a*f_b term of the radial integral.
Definition TensorOperator.hpp:401
bool freqDependantQ() const
Returns true if the operator is frequency-dependent (requires updateFrequency() calls).
Definition TensorOperator.hpp:265
virtual std::unique_ptr< TensorOperator > clone() const
Creates a polymorphic copy of the operator at its current state, or nullptr if cloning is not support...
Definition TensorOperator.hpp:476
int symm_sign(const DiracSpinor &Fa, const DiracSpinor &Fb) const
returns relative sign between <a||x||b> and <b||x||a>
Definition TensorOperator.hpp:353
bool imaginaryQ() const
returns true if operator is imaginary (has imag MEs)
Definition TensorOperator.hpp:344
virtual std::string units() const
Returns units of operator as a string (usually au, may be MHz, etc.)
Definition TensorOperator.hpp:362
int parity() const
returns parity, as integer (+1 or -1)
Definition TensorOperator.hpp:350
double h_minus_sign() const
Sign of the operator at -|omega| relative to that at +|omega|: h(-|omega|) = h_minus_sign() * h(+|ome...
Definition TensorOperator.hpp:272
double rme3js(const DiracSpinor &Fa, const DiracSpinor &Fb, int two_mb=1, int two_q=0) const
Overload of rme3js taking DiracSpinors.
Definition TensorOperator.hpp:557
virtual double angularF(const int, const int) const =0
Angular factor A_ab linking the radial integral to the RME.
virtual double angularCfg(int, int) const
Angular coefficient C_fg for the f_a*g_b term of the radial integral.
Definition TensorOperator.hpp:399
double matel_factor(MatrixElementType type, int twoJa, int twoJb) const
Returns the factor to convert a reduced ME to a different form (Reduced, Stretched,...
Definition TensorOperator.cpp:84
virtual std::string name() const
Returns "name" of operator (e.g., 'E1')
Definition TensorOperator.hpp:360
double getc() const
Returns the "overall" constant c.
Definition TensorOperator.hpp:341
double reducedME(const DiracSpinor &Fa, const DiracSpinor &Fb) const
Returns the reduced matrix element <a||h||b> = A_ab * R_ab.
Definition TensorOperator.cpp:62
virtual double angularCgg(int, int) const
Angular coefficient C_gg for the g_a*g_b term of the radial integral.
Definition TensorOperator.hpp:397
bool selectrion_rule(int twoJA, int piA, int twoJB, int piB) const
Returns true if the matrix element is non-zero by angular momentum and parity selection rules (argume...
Definition TensorOperator.hpp:285
DiracSpinor reduced_lhs(const int ka, const DiracSpinor &Fb) const
As reduced_rhs but for the conjugate direction; Fb * reduced_lhs(ka, Fb) = <b||h||a>.
Definition TensorOperator.cpp:55
double angularCxy(uint8_t x, uint8_t y, int kappa_a, int kappa_b) const
Dispatches to angularCff/fg/gf/gg based on component indices x, y.
Definition TensorOperator.hpp:427
double matel_factor(MatrixElementType type, const DiracSpinor &Fa, const DiracSpinor &Fb) const
Overload of matel_factor taking DiracSpinors.
Definition TensorOperator.hpp:582
double rme3js(int twoja, int twojb, int two_mb=1, int two_q=0) const
3j-symbol factor linking the full ME to the RME.
Definition TensorOperator.cpp:40
bool isZero(int ka, int kb) const
Returns true if <a|h|b> = 0 by rank/parity selection rules.
Definition TensorOperator.cpp:18
virtual DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const
Computes the right-hand spinor dF_b for the radial integral.
Definition TensorOperator.cpp:104
virtual void updateRank(int)
Definition TensorOperator.hpp:331
TensorOperator(int rank_k, Parity pi, double constant=1.0, const std::vector< double > &vec={}, Realness RorI=Realness::real, bool freq_dep=false, bool odd_in_omega=false)
Constructs a specific tensor operator. Called by derived classes.
Definition TensorOperator.hpp:218
virtual double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const
Radial integral R_ab, defined by RME = angularF(a,b) * radialIntegral(a,b).
Definition TensorOperator.cpp:143
virtual double angularCff(int kappa_a, int kappa_b) const
Angular coefficient C_ff for the f_a*f_b term of the radial integral.
Definition TensorOperator.hpp:392
int rank() const
Rank k of operator.
Definition TensorOperator.hpp:347
DiracSpinor reduced_rhs(const int ka, const DiracSpinor &Fb) const
Returns angularF(ka,kb) * radial_rhs(ka,Fb); spinor-valued RME action on Fb, used in perturbation the...
Definition TensorOperator.cpp:50
virtual void updateFrequency(const double)
Updates the operator for a new frequency omega.
Definition TensorOperator.hpp:313
const std::vector< double > & getv() const
Returns a const ref to the stored vector v.
Definition TensorOperator.hpp:338
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:55
int twoj() const
2j (twice the total angular momentum)
Definition DiracSpinor.hpp:121
constexpr int neg1pow_2(int two_a)
Evaluates (-1)^{two_a/2} (for integer a; two_a is even)
Definition Wigner369j.hpp:239
constexpr int triangle(int j1, int j2, int J)
Returns 1 if triangle rule is satisfied. nb: works with j OR twoj!
Definition Wigner369j.hpp:249
Dirac operators: TensorOperator base class and derived implementations for single-particle (one-body)...
Definition SecondOrder.hpp:7
void Pab_rhs(double pm, const std::vector< double > &t, DiracSpinor *dF, const DiracSpinor &Fb, double a)
Pab_rhs function: dF_ab += A * t(r) * (g, pm*f) , pm=+/-1 (usually). NOTE: uses +=,...
Definition TensorOperator.cpp:223
double Vab(const std::vector< double > &t, const DiracSpinor &Fa, const DiracSpinor &Fb)
Vab function: Int[ (fa*gb ) * t(r) , dr].
Definition TensorOperator.cpp:242
double Gab(const std::vector< double > &t, const DiracSpinor &Fa, const DiracSpinor &Fb)
Gab function: Int[ (ga*gb ) * t(r) , dr].
Definition TensorOperator.cpp:261
double Rab(double pm, const std::vector< double > &t, const DiracSpinor &Fa, const DiracSpinor &Fb)
Rab function: Int[ (fa*fb + pm*ga*gb) * t(r) , dr]. pm = +/-1 (usually)
Definition TensorOperator.cpp:211
double Wab(const std::vector< double > &t, const DiracSpinor &Fa, const DiracSpinor &Fb)
Wab function: Int[ (ga*fb ) * t(r) , dr].
Definition TensorOperator.cpp:252
Realness parse_Realness(const std::string &s)
Convert string to Realness.
Definition TensorOperator.cpp:385
MatrixElementType
Type of matrix element returned.
Definition TensorOperator.hpp:82
void Rab_rhs(double pm, const std::vector< double > &t, DiracSpinor *dF, const DiracSpinor &Fb, double a)
Rab_rhs function: dF_ab += A * t(r) * (f, pm*g) , pm=+/-1 (usually). NOTE: uses +=,...
Definition TensorOperator.cpp:233
Parity
Parity of operator.
Definition TensorOperator.hpp:57
Parity parse_Parity(const std::string &s)
Convert string to Parity.
Definition TensorOperator.cpp:362
void Gab_rhs(const std::vector< double > &t, DiracSpinor *dF, const DiracSpinor &Fb, double a)
Gab_rhs function: dF += a * t(r) * (0, g_b). NOTE: uses +=, so can combine.
Definition TensorOperator.cpp:270
Realness
Specifies whether an operator's matrix elements are real or imaginary.
Definition TensorOperator.hpp:70
double Pab(double pm, const std::vector< double > &t, const DiracSpinor &Fa, const DiracSpinor &Fb)
Pab function: Int[ (fa*gb + pm*ga*fb) * t(r) , dr]. pm = +/-1 (usually)
Definition TensorOperator.cpp:197
MatrixElementType parse_MatrixElementType(const std::string &s)
Convert string to MatrixElementType.
Definition TensorOperator.cpp:408
Used to pass generic parameters to update() function.
Definition TensorOperator.hpp:239