ampsci
c++ program for high-precision atomic structure calculations of single-valence systems
QED.hpp
1 #pragma once
2 #include "DiracOperator/GenerateOperator.hpp"
3 #include "DiracOperator/Operators/hfs.hpp"
4 #include "DiracOperator/TensorOperator.hpp"
5 #include "IO/InputBlock.hpp"
6 #include "Physics/FGRadPot.hpp"
7 #include "Physics/PhysConst_constants.hpp"
8 #include "Wavefunction/Wavefunction.hpp"
9 #include "qip/Vector.hpp"
10 #include <cmath>
11 
12 namespace DiracOperator {
13 
14 //==============================================================================
16 class Vrad final : public ScalarOperator {
17 public:
18  Vrad(QED::RadPot rad_pot)
19  : ScalarOperator(Parity::even, 1.0), m_Vrad(std::move(rad_pot)) {}
20  std::string name() const override final { return "Vrad"; }
21  std::string units() const override final { return "au"; }
22 
23  virtual DiracSpinor radial_rhs(const int kappa_a,
24  const DiracSpinor &Fb) const override final {
25  auto dF = m_Vrad.Vel(Fb.l()) * Fb;
26  using namespace qip::overloads;
27  const auto &Hmag = m_Vrad.Hmag(Fb.l());
28  dF.f() -= Hmag * Fb.g();
29  dF.g() -= Hmag * Fb.f();
30  if (kappa_a != Fb.kappa())
31  return 0.0 * dF;
32  return dF;
33  }
34 
35  virtual double radialIntegral(const DiracSpinor &Fa,
36  const DiracSpinor &Fb) const override final {
37  // nb: faster not to do this, but nicer this way
38  return Fa * radial_rhs(Fa.kappa(), Fb);
39  }
40 
41  const QED::RadPot &RadPot() const { return m_Vrad; }
42 
43 private:
44  QED::RadPot m_Vrad;
45 };
46 
47 //==============================================================================
48 inline std::unique_ptr<DiracOperator::TensorOperator>
49 generate_Vrad(const IO::InputBlock &input, const Wavefunction &wf) {
50  using namespace DiracOperator;
51  input.check(
52  {{{"Ueh", " Uehling (vacuum pol). [1.0]"},
53  {"SE_h", " self-energy high-freq electric. [1.0]"},
54  {"SE_l", " self-energy low-freq electric. [1.0]"},
55  {"SE_m", " self-energy magnetic. [1.0]"},
56  {"WK", " Wickman-Kroll. [0.0]"},
57  {"rcut", "Maximum radius (au) to calculate Rad Pot for [5.0]"},
58  {"scale_rN", "Scale factor for Nuclear size. 0 for pointlike, 1 for "
59  "typical [1.0]"},
60  {"scale_l", "List of doubles. Extra scaling factor for each l e.g., "
61  "1,0,1 => include for s and d, but not for p [1.0]"},
62  {"readwrite", "Read/write potential? [true]"}}});
63  if (input.has_option("help")) {
64  return nullptr;
65  }
66 
67  const auto x_Ueh = input.get("Ueh", 1.0);
68  const auto x_SEe_h = input.get("SE_h", 1.0);
69  const auto x_SEe_l = input.get("SE_l", 1.0);
70  const auto x_SEm = input.get("SE_m", 1.0);
71  const auto x_wk = input.get("WK", 0.0);
72  const auto rcut = input.get("rcut", 5.0);
73  const auto scale_rN = input.get("scale_rN", 1.0);
74  const auto x_spd = input.get("scale_l", std::vector{1.0});
75  const auto readwrite = input.get("readwrite", true);
76 
77  const auto r_N_au =
78  std::sqrt(5.0 / 3.0) * scale_rN * wf.nucleus().r_rms() / PhysConst::aB_fm;
79 
80  auto qed = QED::RadPot(wf.grid().r(), wf.Znuc(), r_N_au, rcut,
81  {x_Ueh, x_SEe_h, x_SEe_l, x_SEm, x_wk}, x_spd, true,
82  readwrite);
83 
84  return std::make_unique<Vrad>(std::move(qed));
85 }
86 
87 //==============================================================================
89 
103 class VertexQED final : public TensorOperator {
104 
105 public: // constructor
106  VertexQED(const TensorOperator *const h0, const Grid &rgrid, double a = 1.0,
107  double b = 1.0)
108  : TensorOperator(
109  h0->rank(), h0->parity() == 1 ? Parity::even : Parity::odd,
110  h0->getc(), vertex_func(rgrid, a, b, h0->getv()), h0->get_d_order(),
111  h0->imaginaryQ() ? Realness::imaginary : Realness::real,
112  h0->freqDependantQ()),
113  m_h0(h0) {}
114 
115  std::string name() const override final {
116  return m_h0->name() + "_vertexQED";
117  }
118  std::string units() const override final { return m_h0->units(); }
119 
120  double angularF(const int ka, const int kb) const override final {
121  return m_h0->angularF(ka, kb);
122  }
123 
124  double angularCff(int ka, int kb) const override final {
125  return m_h0->angularCff(ka, kb);
126  }
127  double angularCgg(int ka, int kb) const override final {
128  return m_h0->angularCgg(ka, kb);
129  }
130  double angularCfg(int ka, int kb) const override final {
131  return m_h0->angularCfg(ka, kb);
132  }
133  double angularCgf(int ka, int kb) const override final {
134  return m_h0->angularCgf(ka, kb);
135  }
136 
137  // Have m_h0 pointer, so delete copy/asign constructors
138  VertexQED(const DiracOperator::VertexQED &) = delete;
139  VertexQED &operator=(const DiracOperator::VertexQED &) = delete;
140 
141 private:
142  const TensorOperator *const m_h0;
143 
144 public:
146  static double a(double z) { return 1.0 + 28.5 / z; }
147 
155  static std::vector<double> vertex_func(const Grid &rgrid, double a, double b,
156  std::vector<double> v = {}) {
157 
158  const double a0 = PhysConst::alpha;
159  if (v.empty()) {
160  // If v is empty, means it should be {1,1,1,1,...}
161  v.resize(rgrid.num_points(), 1.0);
162  }
163 
164  for (auto i = 0ul; i < rgrid.num_points(); ++i) {
165  auto exp = a * a0 * std::exp(-b * rgrid.r(i) / a0);
166  v[i] *= exp;
167  }
168  return v;
169  }
170 };
171 
172 //==============================================================================
174 class MLVP final : public TensorOperator {
175 
176 public:
178  MLVP(const DiracOperator::hfs *const h0, const Grid &rgrid, double rN)
179  : TensorOperator(
180  h0->rank(), h0->parity() == 1 ? Parity::even : Parity::odd,
181  h0->getc(), MLVP_func(rgrid, rN, h0->getv()), h0->get_d_order(),
182  h0->imaginaryQ() ? Realness::imaginary : Realness::real,
183  h0->freqDependantQ()),
184  m_h0(*h0) {}
185 
186  std::string name() const override final { return "MLVP"; }
187  std::string units() const override final { return m_h0.units(); }
188 
189  double angularF(const int ka, const int kb) const override final {
190  return m_h0.angularF(ka, kb);
191  }
192  double angularCff(int ka, int kb) const override final {
193  return m_h0.angularCff(ka, kb);
194  }
195  double angularCgg(int ka, int kb) const override final {
196  return m_h0.angularCgg(ka, kb);
197  }
198  double angularCfg(int ka, int kb) const override final {
199  return m_h0.angularCfg(ka, kb);
200  }
201  double angularCgf(int ka, int kb) const override final {
202  return m_h0.angularCgf(ka, kb);
203  }
204 
205 public:
206  // Store a copy?
207  DiracOperator::hfs m_h0;
208 
209 public:
210  // public since may as well be
211  // This multiplies the original operator by Z(r), which is the MLVP correction
212  static std::vector<double> MLVP_func(const Grid &rgrid, double rN,
213  std::vector<double> v = {}) {
214  // rN must be in atomic units
215 
216  if (v.empty()) {
217  // If v is empty, means it should be {1,1,1,1,...}
218  v.resize(rgrid.num_points(), 1.0);
219  }
220 
221  // compute the integral at each radial grid point
222  for (auto i = 0ul; i < rgrid.num_points(); ++i) {
223  const auto Z_mvlp = FGRP::Q_MLVP(rgrid.r(i), rN);
224  // multiply the operator
225  v[i] *= Z_mvlp;
226  }
227 
228  return v;
229  }
230 };
231 
232 //==============================================================================
233 inline std::unique_ptr<DiracOperator::TensorOperator>
234 generate_MLVP(const IO::InputBlock &input, const Wavefunction &wf) {
235  using namespace DiracOperator;
236  input.check(
237  {{"rN",
238  "Nuclear radius (in fm), for finite-nuclear size "
239  "correction to Uehling loop. If not given, taken from wavefunction."},
240  {"hfs_options{}",
241  "Options for hyperfine operator that sits inside the MLVP operator. "
242  "Note: should use pointlike F(r)! [see `ampsci -o hfs`]."}});
243  if (input.has_option("help")) {
244  return nullptr;
245  }
246 
247  // 1. generate regular hfs operator
248  const auto t_options = input.getBlock("hfs_options");
249  auto oper_options = t_options ? *t_options : IO::InputBlock{};
250 
251  if (!oper_options.get("nuc_mag")) {
252  // Typically, default is Ball. But in our case, should be pointlike
253  oper_options.add(IO::Option{"nuc_mag", "pointlike"});
254  }
255 
256  // 2. MLVP
257  const auto rN_fm =
258  input.get("rN", std::sqrt(5.0 / 3.0) * wf.nucleus().r_rms());
259 
260  if (oper_options.get("print", true)) {
261  std::cout << "\nGenerate MLVP operator for hfs, with parameters:\n";
262  if (rN_fm != 0.0)
263  std::cout << "Using finite nuclear charge in Uehling loop, with rN="
264  << rN_fm << " fm.\n";
265  else
266  std::cout << "Using pointlike Uehling loop.\n";
267  }
268 
269  const auto h = generate_hfs(oper_options, wf);
270 
271  const auto r_N_au = rN_fm / PhysConst::aB_fm;
272 
273  return std::make_unique<MLVP>(dynamic_cast<DiracOperator::hfs *>(h.get()),
274  wf.grid(), r_N_au);
275 }
276 
277 } // namespace DiracOperator
Magnetic loop vacuum polarisation (Uehling vertex)
Definition: QED.hpp:174
std::string units() const override final
Returns units of operator (usually au, may be MHz, etc.)
Definition: QED.hpp:187
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition: QED.hpp:186
MLVP(const DiracOperator::hfs *const h0, const Grid &rgrid, double rN)
rN is nuclear (charge) radius, in atomic units
Definition: QED.hpp:178
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition: QED.hpp:189
Speacial case for scalar operator.
Definition: TensorOperator.hpp:233
General operator (virtual base class); operators derive from this.
Definition: TensorOperator.hpp:110
bool imaginaryQ() const
returns true if operator is imaginary (has imag MEs)
Definition: TensorOperator.hpp:171
virtual std::string units() const
Returns units of operator (usually au, may be MHz, etc.)
Definition: TensorOperator.hpp:186
int parity() const
returns parity, as integer (+1 or -1)
Definition: TensorOperator.hpp:174
virtual double angularF(const int, const int) const =0
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
const std::vector< double > & getv() const
Returns a const ref to vector v.
Definition: TensorOperator.hpp:165
double getc() const
Returns a const ref to constant c.
Definition: TensorOperator.hpp:167
Effective VertexQED operator.
Definition: QED.hpp:103
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:155
std::string units() const override final
Returns units of operator (usually au, may be MHz, etc.)
Definition: QED.hpp:118
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition: QED.hpp:115
static double a(double z)
Fitting factor for hyperfine. Default a(Z)
Definition: QED.hpp:146
double angularF(const int ka, const int kb) const override final
angularF: links radiation integral to RME. RME = <a||h||b> = angularF(a,b) * radial_int(a,...
Definition: QED.hpp:120
Flambaum-ginges radiative potential operator.
Definition: QED.hpp:16
virtual double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const override final
Defined via <a||h||b> = angularF(a,b) * radialIntegral(a,b) (Note: if radial_rhs is overridden,...
Definition: QED.hpp:35
std::string units() const override final
Returns units of operator (usually au, may be MHz, etc.)
Definition: QED.hpp:21
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition: QED.hpp:20
virtual DiracSpinor radial_rhs(const int kappa_a, const DiracSpinor &Fb) const override final
radial_int = Fa * radial_rhs(a, Fb) (a needed for angular factor)
Definition: QED.hpp:23
Units: Assumes g in nuc. magneton units (magnetic), and Q in barns (electric), and rN is atomic units...
Definition: hfs.hpp:208
Stores radial Dirac spinor: F_nk = (f, g)
Definition: DiracSpinor.hpp:41
Holds grid, including type + Jacobian (dr/du)
Definition: Grid.hpp:31
auto num_points() const
Number of grid points.
Definition: Grid.hpp:64
const std::vector< double > & r() const
Grid points, r.
Definition: Grid.hpp:75
Holds list of Options, and a list of other InputBlocks. Can be initialised with a list of options,...
Definition: InputBlock.hpp:142
void add(InputBlock block, bool merge=false)
Add a new InputBlock (merge: will be merged with existing if names match)
Definition: InputBlock.hpp:296
bool check(std::initializer_list< std::string > blocks, const std::vector< std::pair< std::string, std::string >> &list, bool print=false) const
Check all the options and blocks in this; if any of them are not present in 'list',...
Definition: InputBlock.hpp:594
std::optional< InputBlock > getBlock(std::string_view name) const
Returns optional InputBlock. Contains InputBlock if block of given name exists; empty otherwise.
Definition: InputBlock.hpp:443
bool has_option(std::string_view key) const
Check is option is present (even if not set) in current block.
Definition: InputBlock.hpp:201
T get(std::string_view key, T default_value) const
If 'key' exists in the options, returns value. Else, returns default_value. Note: If two keys with sa...
Definition: InputBlock.hpp:417
Class holds Flambaum-Ginges QED Radiative Potential.
Definition: RadPot.hpp:15
std::vector< double > Vel(int l=0) const
Returns entire electric part of potential.
Definition: RadPot.cpp:151
Stores Wavefunction (set of valence orbitals, grid, HF etc.)
Definition: Wavefunction.hpp:36
int Znuc() const
Nuclear charge, Z.
Definition: Wavefunction.hpp:98
const Grid & grid() const
Returns a const reference to the radial grid.
Definition: Wavefunction.hpp:81
const Nuclear::Nucleus & nucleus() const
Returns Nuclear::nucleus object (contains nuc. parameters)
Definition: Wavefunction.hpp:96
Dirac Operators: General + derived.
Definition: GenerateOperator.cpp:12
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
Definition: PhysConst_constants.hpp:13
namespace qip::overloads provides operator overloads for std::vector
Definition: Vector.hpp:450
Simple struct; holds key-value pair, both strings. == compares key.
Definition: InputBlock.hpp:105