ampsci
High-precision calculations for one- and two-valence atomic systems
QED.hpp
1#pragma once
2#include "DiracOperator/Operators/hfs.hpp"
3#include "DiracOperator/TensorOperator.hpp"
4#include "IO/InputBlock.hpp"
5#include "Physics/PhysConst_constants.hpp"
6#include "Potentials/FGRadPot.hpp"
7#include "Wavefunction/Wavefunction.hpp"
8#include "qip/Vector.hpp"
9#include <cmath>
10
11namespace DiracOperator {
12
13//==============================================================================
14//! Flambaum-Ginges radiative potential operator
15class Vrad final : public ScalarOperator {
16public:
17 Vrad(QED::RadPot rad_pot)
18 : ScalarOperator(Parity::even, 1.0), m_Vrad(std::move(rad_pot)) {}
19 std::string name() const override final { return "Vrad"; }
20 std::string units() const override final { return "au"; }
21
22 virtual DiracSpinor radial_rhs(const int kappa_a,
23 const DiracSpinor &Fb) const override final {
24 auto dF = m_Vrad.Vel(Fb.l()) * Fb;
25 using namespace qip::overloads;
26 const auto &Hmag = m_Vrad.Hmag(Fb.l());
27 dF.f() -= Hmag * Fb.g();
28 dF.g() -= Hmag * Fb.f();
29 if (kappa_a != Fb.kappa())
30 return 0.0 * dF;
31 return dF;
32 }
33
34 virtual double radialIntegral(const DiracSpinor &Fa,
35 const DiracSpinor &Fb) const override final {
36 // nb: faster not to do this, but nicer this way
37 return Fa * radial_rhs(Fa.kappa(), Fb);
38 }
39
40 const QED::RadPot &RadPot() const { return m_Vrad; }
41
42 std::unique_ptr<TensorOperator> clone() const override final {
43 return std::make_unique<Vrad>(*this);
44 }
45
46 static std::unique_ptr<TensorOperator> generate(const IO::InputBlock &input,
47 const Wavefunction &wf) {
48 input.check(
49 {{{"Ueh", " Uehling (vacuum pol). [1.0]"},
50 {"SE_h", " self-energy high-freq electric. [1.0]"},
51 {"SE_l", " self-energy low-freq electric. [1.0]"},
52 {"SE_m", " self-energy magnetic. [1.0]"},
53 {"WK", " Wickman-Kroll. [0.0]"},
54 {"rcut", "Maximum radius (au) to calculate Rad Pot for [5.0]"},
55 {"scale_rN", "Scale factor for Nuclear size. 0 for pointlike, 1 for "
56 "typical [1.0]"},
57 {"scale_l", "List of doubles. Extra scaling factor for each l e.g., "
58 "1,0,1 => include for s and d, but not for p [1.0]"},
59 {"readwrite", "Read/write potential? [true]"}}});
60 if (input.has_option("help"))
61 return nullptr;
62 const auto x_Ueh = input.get("Ueh", 1.0);
63 const auto x_SEe_h = input.get("SE_h", 1.0);
64 const auto x_SEe_l = input.get("SE_l", 1.0);
65 const auto x_SEm = input.get("SE_m", 1.0);
66 const auto x_wk = input.get("WK", 0.0);
67 const auto rcut = input.get("rcut", 5.0);
68 const auto scale_rN = input.get("scale_rN", 1.0);
69 const auto x_spd = input.get("scale_l", std::vector{1.0});
70 const auto readwrite = input.get("readwrite", true);
71 const auto r_N_au =
72 std::sqrt(5.0 / 3.0) * scale_rN * wf.nucleus().r_rms() / PhysConst::aB_fm;
73 auto qed = QED::RadPot(wf.grid().r(), wf.Znuc(), r_N_au, rcut,
74 {x_Ueh, x_SEe_h, x_SEe_l, x_SEm, x_wk}, x_spd, true,
75 readwrite, wf.run_label());
76 return std::make_unique<Vrad>(std::move(qed));
77 }
78
79private:
80 QED::RadPot m_Vrad;
81};
82
83//==============================================================================
84//! @brief Effective VertexQED operator
85/*! @details
86Takes in any TensorOperator (DiracOperator) h, and forms the corresponding
87effective QED vertex operator, defined:
88
89\f[
90\hat h_{\rm vertex} = A \alpha \exp(-b r / \lambda_c)
91\f]
92
93where
94
95\f[ \lambda_c = 1/ \alpha \approx 137 \f]
96
97A and b are fitting factors; typically b=1
98 */
99class VertexQED final : public TensorOperator {
100
101public: // constructor
102 VertexQED(const TensorOperator *const h0, const Grid &rgrid, double a = 1.0,
103 double b = 1.0)
104 : TensorOperator(h0->rank(), h0->parity() == 1 ? Parity::even : Parity::odd,
105 h0->getc(), vertex_func(rgrid, a, b, h0->getv()),
106 h0->imaginaryQ() ? Realness::imaginary : Realness::real,
107 h0->freqDependantQ()),
108 m_h0(h0) {}
109
110 std::string name() const override final {
111 return m_h0->name() + "_vertexQED";
112 }
113 std::string units() const override final { return m_h0->units(); }
114
115 double angularF(const int ka, const int kb) const override final {
116 return m_h0->angularF(ka, kb);
117 }
118
119 double angularCff(int ka, int kb) const override final {
120 return m_h0->angularCff(ka, kb);
121 }
122 double angularCgg(int ka, int kb) const override final {
123 return m_h0->angularCgg(ka, kb);
124 }
125 double angularCfg(int ka, int kb) const override final {
126 return m_h0->angularCfg(ka, kb);
127 }
128 double angularCgf(int ka, int kb) const override final {
129 return m_h0->angularCgf(ka, kb);
130 }
131
132 // Have m_h0 pointer, so delete copy/asign constructors
133 VertexQED(const DiracOperator::VertexQED &) = delete;
134 VertexQED &operator=(const DiracOperator::VertexQED &) = delete;
135
136private:
137 const TensorOperator *const m_h0;
138
139public:
140 //! Fitting factor for hyperfine. Default a(Z)
141 static double a(double z) { return 1.0 + 28.5 / z; }
142
143 //! Takes existing radial vector, multiplies by:
144 //! @details
145 //! a(Z) * a0 * exp( - b * r / a0).
146 //! a0 = alpha = 1/137.
147 //! b=1 by default. A should be fitted.
148 //! a(Z) = 1.0 + 28.5/Z
149 //! nb: can give it an empty vector, to just get the exponential function
150 static std::vector<double> vertex_func(const Grid &rgrid, double a, double b,
151 std::vector<double> v = {}) {
152
153 const double a0 = PhysConst::alpha;
154 if (v.empty()) {
155 // If v is empty, means it should be {1,1,1,1,...}
156 v.resize(rgrid.num_points(), 1.0);
157 }
158
159 for (auto i = 0ul; i < rgrid.num_points(); ++i) {
160 auto exp = a * a0 * std::exp(-b * rgrid.r(i) / a0);
161 v[i] *= exp;
162 }
163 return v;
164 }
165};
166
167//==============================================================================
168//! Magnetic loop vacuum polarisation (Uehling vertex)
169class MLVP final : public TensorOperator {
170
171public:
172 //! rN is nuclear (charge) radius, in atomic units
173 MLVP(const DiracOperator::hfs *const h0, const Grid &rgrid, double rN)
174 : TensorOperator(h0->rank(), h0->parity() == 1 ? Parity::even : Parity::odd,
175 h0->getc(), MLVP_func(rgrid, rN, h0->getv()),
176 h0->imaginaryQ() ? Realness::imaginary : Realness::real,
177 h0->freqDependantQ()),
178 m_h0(*h0) {}
179
180 std::string name() const override final { return "MLVP"; }
181 std::string units() const override final { return m_h0.units(); }
182
183 double angularF(const int ka, const int kb) const override final {
184 return m_h0.angularF(ka, kb);
185 }
186 double angularCff(int ka, int kb) const override final {
187 return m_h0.angularCff(ka, kb);
188 }
189 double angularCgg(int ka, int kb) const override final {
190 return m_h0.angularCgg(ka, kb);
191 }
192 double angularCfg(int ka, int kb) const override final {
193 return m_h0.angularCfg(ka, kb);
194 }
195 double angularCgf(int ka, int kb) const override final {
196 return m_h0.angularCgf(ka, kb);
197 }
198
199 std::unique_ptr<TensorOperator> clone() const override final {
200 return std::make_unique<MLVP>(*this);
201 }
202
203public:
204 // Store a copy?
206
207public:
208 // public since may as well be
209 // This multiplies the original operator by Z(r), which is the MLVP correction
210 static std::vector<double> MLVP_func(const Grid &rgrid, double rN,
211 std::vector<double> v = {}) {
212 // rN must be in atomic units
213
214 if (v.empty()) {
215 // If v is empty, means it should be {1,1,1,1,...}
216 v.resize(rgrid.num_points(), 1.0);
217 }
218
219 // compute the integral at each radial grid point
220 for (auto i = 0ul; i < rgrid.num_points(); ++i) {
221 const auto Z_mvlp = FGRP::Q_MLVP(rgrid.r(i), rN);
222 // multiply the operator
223 v[i] *= Z_mvlp;
224 }
225
226 return v;
227 }
228
229 static std::unique_ptr<TensorOperator> generate(const IO::InputBlock &input,
230 const Wavefunction &wf) {
231 input.check(
232 {{"rN",
233 "Nuclear radius (in fm), for finite-nuclear size "
234 "correction to Uehling loop. If not given, taken from wavefunction."},
235 {"hfs_options{}",
236 "Options for hyperfine operator that sits inside the MLVP operator. "
237 " [see `ampsci -o hfs`]."}});
238 if (input.has_option("help"))
239 return nullptr;
240 // 1. generate regular hfs operator
241 const auto t_options = input.getBlock("hfs_options");
242 auto oper_options = t_options ? *t_options : IO::InputBlock{};
243 // 2. MLVP
244 const auto rN_fm =
245 input.get("rN", std::sqrt(5.0 / 3.0) * wf.nucleus().r_rms());
246 if (oper_options.get("print", true)) {
247 std::cout << "\nGenerate MLVP operator for hfs, with parameters:\n";
248 if (rN_fm != 0.0)
249 std::cout << "Using finite nuclear charge in Uehling loop, with rN="
250 << rN_fm << " fm.\n";
251 else
252 std::cout << "Using pointlike Uehling loop.\n";
253 }
254 const auto h = hfs::generate(oper_options, wf);
255 const auto r_N_au = rN_fm / PhysConst::aB_fm;
256 return std::make_unique<MLVP>(dynamic_cast<DiracOperator::hfs *>(h.get()),
257 wf.grid(), r_N_au);
258 }
259};
260
261} // namespace DiracOperator
Magnetic loop vacuum polarisation (Uehling vertex)
Definition QED.hpp:169
std::string units() const override final
Returns units of operator as a string (usually au, may be MHz, etc.)
Definition QED.hpp:181
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition QED.hpp:180
double angularCgf(int ka, int kb) const override final
Angular coefficient C_gf for the g_a*f_b term of the radial integral.
Definition QED.hpp:195
double angularCff(int ka, int kb) const override final
Angular coefficient C_ff for the f_a*f_b term of the radial integral.
Definition QED.hpp:186
std::unique_ptr< TensorOperator > clone() const override final
Creates a polymorphic copy of the operator at its current state, or nullptr if cloning is not support...
Definition QED.hpp:199
double angularCgg(int ka, int kb) const override final
Angular coefficient C_gg for the g_a*g_b term of the radial integral.
Definition QED.hpp:189
MLVP(const DiracOperator::hfs *const h0, const Grid &rgrid, double rN)
rN is nuclear (charge) radius, in atomic units
Definition QED.hpp:173
double angularCfg(int ka, int kb) const override final
Angular coefficient C_fg for the f_a*g_b term of the radial integral.
Definition QED.hpp:192
double angularF(const int ka, const int kb) const override final
Angular factor A_ab linking the radial integral to the RME.
Definition QED.hpp:183
Rank-0 (scalar) tensor operator; derives from TensorOperator with k=0.
Definition TensorOperator.hpp:587
General tensor operator (virtual base class); all single-particle (one-body) tenosor operators derive...
Definition TensorOperator.hpp:198
virtual double angularCgf(int, int) const
Angular coefficient C_gf for the g_a*f_b term of the radial integral.
Definition TensorOperator.hpp:390
bool freqDependantQ() const
Returns true if the operator is frequency-dependent (requires updateFrequency() calls).
Definition TensorOperator.hpp:262
bool imaginaryQ() const
returns true if operator is imaginary (has imag MEs)
Definition TensorOperator.hpp:333
virtual std::string units() const
Returns units of operator as a string (usually au, may be MHz, etc.)
Definition TensorOperator.hpp:351
int parity() const
returns parity, as integer (+1 or -1)
Definition TensorOperator.hpp:339
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:388
virtual std::string name() const
Returns "name" of operator (e.g., 'E1')
Definition TensorOperator.hpp:349
double getc() const
Returns the "overall" constant c.
Definition TensorOperator.hpp:330
virtual double angularCgg(int, int) const
Angular coefficient C_gg for the g_a*g_b term of the radial integral.
Definition TensorOperator.hpp:386
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:381
int rank() const
Rank k of operator.
Definition TensorOperator.hpp:336
const std::vector< double > & getv() const
Returns a const ref to the stored vector v.
Definition TensorOperator.hpp:327
Effective VertexQED operator.
Definition QED.hpp:99
double angularCff(int ka, int kb) const override final
Angular coefficient C_ff for the f_a*f_b term of the radial integral.
Definition QED.hpp:119
static std::vector< double > vertex_func(const Grid &rgrid, double a, double b, std::vector< double > v={})
Takes existing radial vector, multiplies by:
Definition QED.hpp:150
double angularCgg(int ka, int kb) const override final
Angular coefficient C_gg for the g_a*g_b term of the radial integral.
Definition QED.hpp:122
std::string units() const override final
Returns units of operator as a string (usually au, may be MHz, etc.)
Definition QED.hpp:113
double angularCfg(int ka, int kb) const override final
Angular coefficient C_fg for the f_a*g_b term of the radial integral.
Definition QED.hpp:125
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition QED.hpp:110
static double a(double z)
Fitting factor for hyperfine. Default a(Z)
Definition QED.hpp:141
double angularCgf(int ka, int kb) const override final
Angular coefficient C_gf for the g_a*f_b term of the radial integral.
Definition QED.hpp:128
double angularF(const int ka, const int kb) const override final
Angular factor A_ab linking the radial integral to the RME.
Definition QED.hpp:115
Flambaum-Ginges radiative potential operator.
Definition QED.hpp:15
virtual 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 QED.hpp:34
std::string units() const override final
Returns units of operator as a string (usually au, may be MHz, etc.)
Definition QED.hpp:20
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition QED.hpp:19
std::unique_ptr< TensorOperator > clone() const override final
Creates a polymorphic copy of the operator at its current state, or nullptr if cloning is not support...
Definition QED.hpp:42
virtual 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 QED.hpp:22
Generalised hyperfine-structure operator, including relevant nuclear moment.
Definition hfs.hpp:270
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:42
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
auto num_points() const
Number of grid points.
Definition Grid.hpp:120
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
std::optional< InputBlock > getBlock(std::string_view name) const
Returns an optional copy of the child block named name; empty if not found.
Definition InputBlock.hpp:497
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
Constructs and stores the Flambaum-Ginges QED Radiative Potential.
Definition RadPot.hpp:16
std::vector< double > Vel(int l=0) const
Returns entire electric part of potential.
Definition RadPot.cpp:155
std::vector< double > Hmag(int) const
Returns H_mag (magnetic self-energy form vactor)
Definition RadPot.cpp:164
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
int Znuc() const
Nuclear charge, Z.
Definition Wavefunction.hpp:99
const std::string & run_label() const
Atomic symbol, including core ionisation degree and run_label.
Definition Wavefunction.hpp:205
const Nuclear::Nucleus & nucleus() const
Returns Nuclear::nucleus object (contains nuc. parameters)
Definition Wavefunction.hpp:97
Dirac operators: TensorOperator base class and derived implementations for single-particle (one-body)...
Definition GenerateOperator.cpp:6
Parity
Parity of operator.
Definition TensorOperator.hpp:57
Realness
Specifies whether an operator's matrix elements are real or imaginary.
Definition TensorOperator.hpp:70
double Q_MLVP(double r, double rN)
Magnetic-loop vacuum polarisation, includes finite-nuclear size.
Definition FGRadPot.cpp:280
constexpr double alpha
Fine-structure constant: alpha = 1/137.035 999 177(21) [CODATA 2022].
Definition PhysConst_constants.hpp:24
Operator overloads for std::vector.
Definition Vector.hpp:503