ampsci
c++ program for high-precision atomic structure calculations of single-valence systems
hfs.hpp
1 #pragma once
2 #include "DiracOperator/TensorOperator.hpp"
3 #include "IO/InputBlock.hpp"
4 #include "Wavefunction/Wavefunction.hpp"
5 #include "fmt/color.hpp"
6 #include "qip/Maths.hpp"
7 #include <functional>
8 
9 namespace DiracOperator {
10 
11 //==============================================================================
12 
14 namespace Hyperfine {
15 using Func_R2_R = std::function<double(double, double)>; // save typing
16 
18 inline auto sphericalBall_F() -> Func_R2_R {
19  return [=](double r, double rN) {
20  return (r > rN) ? 1.0 : (r * r * r) / (rN * rN * rN);
21  };
22 }
23 
25 inline auto sphericalShell_F() -> Func_R2_R {
26  return [=](double r, double rN) { return (r > rN) ? 1.0 : 0.0; };
27 }
28 
30 inline auto pointlike_F() -> Func_R2_R {
31  return [=](double, double) { return 1.0; };
32 }
33 
34 //------------------------------------------------------------------------------
36 inline auto VolotkaSP_F(double mu, double I_nuc, double l_pn, int gl,
37  bool print = true) -> Func_R2_R
38 // Function that returns generates + returns F_BW Bohr-Weiskopf
39 // gl = 1 for proton, =0 for neutron. Double allowed for testing..
40 // mu is in units of Nuclear Magneton!
41 {
42  const auto two_I = int(2 * I_nuc + 0.0001);
43  const auto two_l = int(2 * l_pn + 0.0001);
44  const auto g_l = double(gl); // just safety
45  const auto gI = mu / I_nuc;
46 
47  const auto K = (l_pn * (l_pn + 1.0) - (3. / 4.)) / (I_nuc * (I_nuc + 1.0));
48  const double g_s = (2.0 * gI - g_l * (K + 1.0)) / (1.0 - K);
49  if (print)
50  std::cout << "SingleParticle using: gl=" << g_l << ", gs=" << g_s
51  << ", l=" << l_pn << ", gI=" << gI << " (I=" << I_nuc << ")\n";
52  const double factor =
53  (two_I == two_l + 1) ?
54  g_s * (1 - two_I) / (4.0 * (two_I + 2)) + g_l * 0.5 * (two_I - 1) :
55  g_s * (3 + two_I) / (4.0 * (two_I + 2)) +
56  g_l * 0.5 * two_I * (two_I + 3) / (two_I + 2);
57  if (two_I != two_l + 1 && two_I != two_l - 1) {
58  std::cerr << "\nFAIL:59 in Hyperfine (VolotkaSP_F):\n "
59  "we must have I = l +/- 1/2, but we have: I,l = "
60  << I_nuc << "," << l_pn << "\n";
61  return [](double, double) { return 0.0; };
62  }
63  return [=](double r, double rN) {
64  return (r > rN) ? 1.0 :
65  ((r * r * r) / (rN * rN * rN)) *
66  (1.0 - (3.0 / mu) * std::log(r / rN) * factor);
67  };
68 }
69 
70 //------------------------------------------------------------------------------
72 inline auto uSP(double mu, double I_nuc, double l_pn, int gl, double n,
73  double R, bool u_option, bool print = true) -> Func_R2_R {
74  const auto two_I = int(2 * I_nuc + 0.0001);
75  const auto two_l = int(2 * l_pn + 0.0001);
76  const auto g_l = double(gl); // just safety
77  const auto gI = mu / I_nuc;
78 
79  const auto K = (l_pn * (l_pn + 1.0) - (3. / 4.)) / (I_nuc * (I_nuc + 1.0));
80  const double g_s = (2.0 * gI - g_l * (K + 1.0)) / (1.0 - K);
81  if (print) {
82  std::cout << "uSP using: gl=" << g_l << ", gs=" << g_s << ", l=" << l_pn
83  << ", gI=" << gI << " (I=" << I_nuc << ")\n";
84  if (u_option) {
85  std::cout << "u(r) = u1(r) = u0 (R-r)^n ";
86  } else {
87  std::cout << "u(r) = u2(r) = u0 r^n ";
88  }
89  std::cout << "with n = " << n << "\n";
90  }
91  if (two_I != two_l + 1 && two_I != two_l - 1) {
92  std::cerr << "\nFAIL:59 in Hyperfine (uSP):\n "
93  "we must have I = l +/- 1/2, but we have: I,l = "
94  << I_nuc << "," << l_pn << "\n";
95  return [](double, double) { return 0.0; };
96  }
97 
98  // nb: in theory, can read u(r) in from file!
99  // r^2 * u(r)^2
100  const auto r2u2 = [=](double r) {
101  return u_option ? r * r * std::pow(R - r, 2.0 * n) :
102  r * r * std::pow(r, 2.0 * n);
103  };
104 
105  // normalisation
106  const auto u02 =
107  1.0 / NumCalc::num_integrate(r2u2, 0.0, R, 100, NumCalc::linear);
108 
109  const auto F0r = [=](double r) {
110  const auto f = [=](double x) { return u02 * r2u2(x); };
111  return NumCalc::num_integrate(f, 0.0, r, 100, NumCalc::linear);
112  };
113 
114  const auto FrR = [=](double r) {
115  const auto f = [=](double x) {
116  return u02 * r2u2(x) * r * r * r / x / x / x;
117  };
118  return NumCalc::num_integrate(f, r, R, 100, NumCalc::linear);
119  };
120 
121  const auto two_Ip1 = 2.0 * (I_nuc + 1.0);
122  const auto twoI_p3 = 2.0 * I_nuc + 3.0;
123  const auto twoI_m1 = 2.0 * I_nuc - 1.0;
124 
125  double f1 = (two_I == two_l + 1) ?
126  0.5 * g_s + (I_nuc - 0.5) * g_l :
127  -I_nuc / two_Ip1 * g_s + I_nuc * twoI_p3 / two_Ip1 * g_l;
128 
129  double f2 =
130  (two_I == two_l + 1) ?
131  -twoI_m1 / (4.0 * two_Ip1) * g_s + (I_nuc - 0.5) * g_l :
132  twoI_p3 / (4.0 * two_Ip1) * g_s + I_nuc * twoI_p3 / two_Ip1 * g_l;
133 
134  return [=](double r, double rN) {
135  return (r > rN) ? 1.0 : (1.0 / mu) * (f1 * F0r(r) + f2 * FrR(r));
136  };
137 }
138 
139 //------------------------------------------------------------------------------
141 inline auto doublyOddSP_F(double mut, double It, double mu1, double I1,
142  double l1, int gl1, double I2, double l2,
143  bool print = true) -> Func_R2_R
144 // F(r) * g = 0.5 [ g1F1 + g2F2 + (g1F1 - g2F2) * K]
145 // K = [I1(I1+1) - I2(I2+1)] / [I(I+1)]
146 // return F(r) [divide by g]
147 // VolotkaSP_F(mu, I, l, g_l); //gl is 1 or 0
148 // g2 : from: g = 0.5 [ g1 + g2 + (g1 - g2) * K]
149 {
150  const auto K = (I1 * (I1 + 1.0) - I2 * (I2 + 1.0)) / (It * (It + 1.0));
151  const auto gt = mut / It;
152  const auto g1 = mu1 / I1;
153  const auto g2 = (g1 * (K + 1.0) - 2.0 * gt) / (K - 1.0);
154  const auto mu2 = g2 * I2;
155  const auto gl2 = (gl1 == 0) ? 1 : 0;
156  const auto F1 = VolotkaSP_F(mu1, I1, l1, gl1, print);
157  const auto F2 = VolotkaSP_F(mu2, I2, l2, gl2, print);
158  return [=](double r, double rN) {
159  return (0.5 / gt) * (g1 * F1(r, rN) + g2 * F2(r, rN) +
160  K * (g1 * F1(r, rN) - g2 * F2(r, rN)));
161  };
162 }
163 
164 //------------------------------------------------------------------------------
166 inline double convert_RME_to_AB_2J(int k, int tja, int tjb) {
167  const auto tjz = std::min(tja, tjb); // arbitrary choice
168  const auto s = Angular::neg1pow_2(tja - tjb);
169  const auto tjs = Angular::threej_2(tja, 2 * k, tjb, -tjz, 0, tjz);
170  const auto f = k % 2 == 0 ? 2.0 : 1 / (0.5 * tjz);
171  return s * f * tjs;
172 }
173 
175 inline double convert_RME_to_AB(int k, int ka, int kb) {
176  const auto tja = Angular::twoj_k(ka);
177  const auto tjb = Angular::twoj_k(kb);
178  return convert_RME_to_AB_2J(k, tja, tjb);
179 }
180 
181 inline double hfsA(const TensorOperator *h, const DiracSpinor &Fa) {
182  auto Raa = h->radialIntegral(Fa, Fa); //?
183  return Raa * Fa.kappa() / (Fa.jjp1()) * PhysConst::muN_CGS_MHz;
184 }
185 
186 inline double hfsB(const TensorOperator *h, const DiracSpinor &Fa) {
187  auto Raa = h->radialIntegral(Fa, Fa); //?
188  return Raa * double(Fa.twoj() - 1) / double(Fa.twoj() + 2) *
189  PhysConst::barn_MHz;
190 }
191 
192 //==============================================================================
194 inline std::vector<double> RadialFunc(int k, double rN, const Grid &rgrid,
195  const Func_R2_R &hfs_F) {
196  std::vector<double> rfunc;
197  rfunc.reserve(rgrid.num_points());
198  for (const auto r : rgrid)
199  rfunc.push_back(hfs_F(r, rN) / pow(r, k + 1));
200  return rfunc;
201 }
202 
203 } // namespace Hyperfine
204 
205 //==============================================================================
208 class hfs final : public TensorOperator {
209  // see Xiao, ..., Derevianko, Phys. Rev. A 102, 022810 (2020).
210  using Func_R2_R = std::function<double(double, double)>;
211 
212 public:
213  hfs(int in_k, double in_GQ, double rN_au, const Grid &rgrid,
214  const Func_R2_R &hfs_F = Hyperfine::pointlike_F(), bool MHzQ = true)
215  : TensorOperator(in_k, Parity::even, in_GQ,
216  Hyperfine::RadialFunc(in_k, rN_au, rgrid, hfs_F), 0),
217  k(in_k),
218  magnetic(k % 2 != 0),
219  cfg(magnetic ? 1.0 : 0.0),
220  cff(magnetic ? 0.0 : 1.0),
221  mMHzQ(MHzQ) {}
222 
223  std::string name() const override final {
224  return "hfs" + std::to_string(k) + "";
225  }
226  std::string units() const override final {
227  return (k <= 2 && mMHzQ) ? "MHz" : "au";
228  }
229 
230  double angularF(const int ka, const int kb) const override final {
231  // inludes unit: Assumes g in nuc. magneton units, and/or Q in barns
232  // This only for k=1 (mag dipole) and k=2 E. quad.
233  const auto unit = (mMHzQ && k == 1) ? PhysConst::muN_CGS_MHz :
234  (mMHzQ && k == 2) ? PhysConst::barn_MHz :
235  1.0;
236  return magnetic ? -(ka + kb) * Angular::Ck_kk(k, -ka, kb) * unit :
237  -Angular::Ck_kk(k, ka, kb) * unit;
238  }
239 
240  double angularCff(int, int) const override final { return cff; }
241  double angularCgg(int, int) const override final { return cff; }
242  double angularCfg(int, int) const override final { return cfg; }
243  double angularCgf(int, int) const override final { return cfg; }
244 
245 private:
246  int k;
247  bool magnetic;
248  double cfg;
249  double cff;
250  bool mMHzQ;
251 };
252 
253 //==============================================================================
254 //==============================================================================
255 inline std::unique_ptr<DiracOperator::TensorOperator>
256 generate_hfs(const IO::InputBlock &input, const Wavefunction &wf) {
257  using namespace DiracOperator;
258 
259  input.check(
260  {{"", "Most following will be taken from the default nucleus if "
261  "not explicitely given"},
262  {"mu", "Magnetic moment in mu_N"},
263  {"Q", "Nuclear quadrupole moment, in barns. Also used as overall "
264  "constant for any higher-order moments [1.0]"},
265  {"k", "Multipolarity. 1=mag. dipole, 2=elec. quad, etc. [1]"},
266  {"rrms",
267  "nuclear (magnetic) rms radius, in Fermi (fm) (defult is charge rms)"},
268  {"units", "Units for output (only for k=1,k=2). MHz or au [MHz]"},
269  {"nuc_mag", "Nuclear magnetisation: ball, point, shell, SingleParticle, "
270  "or doublyOddSP [ball]"},
271  {"printF", "Writes F(r) [nuc_mag] to a text file [false]"},
272  {"print", "Write F(r) [nuc_mag] info to screen [true]"},
273  {"", "The following are only for SingleParticle or doublyOddSP"},
274  {"I", "Nuclear spin. Taken from nucleus"},
275  {"parity", "Nulcear parity: +/-1"},
276  {"l", "l for unpaired nucleon (automatically derived from I and "
277  "parity; best to leave as default)"},
278  {"gl", "=1 for proton, =0 for neutron"},
279  {"", "The following are only for doublyOddSP"},
280  {"mu1", "mag moment of 'first' unpaired nucleon"},
281  {"gl1", "gl of 'first' unpaired nucleon"},
282  {"l1", "l of 'first' unpaired nucleon"},
283  {"l2", "l of 'second' unpaired nucleon"},
284  {"I1", "total spin (J) of 'first' unpaired nucleon"},
285  {"I2", "total spin (J) of 'second' unpaired nucleon"},
286  {"", "The following are only for u(r) function"},
287  {"u", "u1 or u2 : u1(r) = (R-r)^n, u2(r) = r^n [u1]"},
288  {"n", "n that appears above. Should be between 0 and 2 [0]"}});
289  if (input.has_option("help")) {
290  return nullptr;
291  }
292 
293  const auto nuc = wf.nucleus();
294  const auto isotope = Nuclear::findIsotopeData(nuc.z(), nuc.a());
295  const auto mu = input.get("mu", isotope.mu ? *isotope.mu : 0.0);
296  const auto I_nuc = input.get("I", isotope.I_N ? *isotope.I_N : 0.0);
297  const auto print = input.get("print", true);
298  const auto k = input.get("k", 1);
299 
300  const auto use_MHz =
301  (k <= 2 &&
302  qip::ci_compare(input.get<std::string>("units", "MHz"), "MHz"));
303 
304  if (k <= 0) {
305  fmt2::styled_print(fg(fmt::color::red), "\nError 246:\n");
306  std::cout << "In hyperfine: invalid K=" << k << "! meaningless results\n";
307  }
308  if (I_nuc <= 0) {
309  fmt2::styled_print(fg(fmt::color::orange), "\nWarning 253:\n");
310  std::cout << "In hyperfine: invalid I_nuc=" << I_nuc
311  << "! meaningless results\n";
312  }
313 
314  const auto g_or_Q = (k == 1) ? (mu / I_nuc) :
315  (k == 2) ? input.get("Q", isotope.q ? *isotope.q : 1.0) :
316  input.get("Q", 1.0);
317 
318  enum class DistroType {
319  point,
320  ball,
321  shell,
322  SingleParticle,
323  doublyOddSP,
324  spu,
325  Error
326  };
327 
328  std::string default_distribution = "ball";
329 
330  // For compatability with old notation of 'F(r)' input option
331  const auto Fr_str =
332  input.has_option("nuc_mag") ?
333  input.get<std::string>("nuc_mag", default_distribution) :
334  input.get<std::string>("F(r)", default_distribution);
335 
336  const auto distro_type =
337  (qip::ci_wc_compare(Fr_str, "point*") || qip::ci_compare(Fr_str, "1")) ?
338  DistroType::point :
339  qip::ci_compare(Fr_str, "ball") ? DistroType::ball :
340  qip::ci_compare(Fr_str, "shell") ? DistroType::shell :
341  qip::ci_wc_compare(Fr_str, "Single*") ? DistroType::SingleParticle :
342  qip::ci_compare(Fr_str, "doublyOdd*") ? DistroType::doublyOddSP :
343  qip::ci_compare(Fr_str, "spu") ? DistroType::spu :
344  DistroType::Error;
345  if (distro_type == DistroType::Error) {
346  fmt2::styled_print(fg(fmt::color::red), "\nError 271:\n");
347  std::cout << "\nIn hyperfine. Unkown F(r) - " << Fr_str << "\n";
348  std::cout << "Defaulting to pointlike!\n";
349  }
350 
351  const auto r_rmsfm =
352  distro_type == DistroType::point ? 0.0 : input.get("rrms", nuc.r_rms());
353  const auto r_nucfm = std::sqrt(5.0 / 3) * r_rmsfm;
354  const auto r_nucau = r_nucfm / PhysConst::aB_fm;
355 
356  if (print) {
357  std::cout << "\nHyperfine structure: " << wf.atom() << "\n";
358  std::cout << "K=" << k << " ("
359  << (k == 1 ? "magnetic dipole" :
360  k == 2 ? "electric quadrupole" :
361  k % 2 == 0 ? "electric multipole" :
362  "magnetic multipole")
363  << ")\n";
364  std::cout << "Using " << Fr_str << " nuclear distro for F(r)\n"
365  << "w/ r_N = " << r_nucfm << "fm = " << r_nucau
366  << "au (r_rms=" << r_rmsfm << "fm)\n";
367  std::cout << "Points inside nucleus: " << wf.grid().getIndex(r_nucau)
368  << "\n";
369  if (k == 1) {
370  std::cout << "mu = " << mu << ", I = " << I_nuc << ", g = " << g_or_Q
371  << "\n";
372  } else {
373  std::cout << "Q = " << g_or_Q << "\n";
374  }
375  }
376 
377  // default is BALL:
378  auto Fr = Hyperfine::sphericalBall_F();
379  if (distro_type == DistroType::ball) {
381  } else if (distro_type == DistroType::shell) {
383  } else if (distro_type == DistroType::SingleParticle) {
384  const auto pi = input.get("parity", isotope.parity ? *isotope.parity : 0);
385  const auto l_tmp = int(I_nuc + 0.5 + 0.0001);
386  auto l = ((l_tmp % 2 == 0) == (pi == 1)) ? l_tmp : l_tmp - 1;
387  l = input.get("l", l); // can override derived 'l' (not recommended)
388  const auto gl_default = wf.Znuc() % 2 == 0 ? 0 : 1; // unparied proton?
389  const auto gl = input.get<int>("gl", gl_default);
390  if (print) {
391  std::cout << "Single-Particle (Volotka formula) for unpaired";
392  if (gl == 1)
393  std::cout << " proton ";
394  else if (gl == 0)
395  std::cout << " neturon ";
396  else
397  std::cout << " gl=" << gl << "??? program will run, but prob wrong!\n";
398  std::cout << "with l=" << l << " (pi=" << pi << ")\n";
399  }
400  Fr = Hyperfine::VolotkaSP_F(mu, I_nuc, l, gl, print);
401  } else if (distro_type == DistroType::spu) {
402  const auto pi = input.get("parity", isotope.parity ? *isotope.parity : 0);
403  const auto u_func = input.get("u", std::string{"u1"}); // u1=(R-r)^n, u2=r^n
404  const bool u_option = u_func == std::string{"u1"};
405  const auto n = input.get("n", 0.0); // u1=(R-r)^n, u2=r^n
406  const auto l_tmp = int(I_nuc + 0.5 + 0.0001);
407  auto l = ((l_tmp % 2 == 0) == (pi == 1)) ? l_tmp : l_tmp - 1;
408  l = input.get("l", l); // can override derived 'l' (not recommended)
409  const auto gl_default = wf.Znuc() % 2 == 0 ? 0 : 1; // unparied proton?
410  const auto gl = input.get<int>("gl", gl_default);
411  if (print) {
412  std::cout << "Single-Particle (Volotka formula) with u(r) for unpaired";
413  if (gl == 1)
414  std::cout << " proton ";
415  else if (gl == 0)
416  std::cout << " neturon ";
417  else
418  std::cout << " gl=" << gl << "??? program will run, but prob wrong!\n";
419  std::cout << "with l=" << l << " (pi=" << pi << ")\n";
420  }
421  Fr = Hyperfine::uSP(mu, I_nuc, l, gl, n, r_nucau, u_option, print);
422  } else if (distro_type == DistroType::doublyOddSP) {
423  const auto mu1 = input.get<double>("mu1", 1.0);
424  const auto gl1 = input.get<int>("gl1", -1); // 1 or 0 (p or n)
425  if (gl1 != 0 && gl1 != 1) {
426  fmt2::styled_print(fg(fmt::color::red), "\nError 324:\n");
427  std::cout << "In " << input.name() << " " << Fr_str
428  << "; have gl1=" << gl1 << " but need 1 or 0\n";
429  return std::make_unique<NullOperator>(NullOperator());
430  }
431  const auto l1 = input.get<double>("l1", -1.0);
432  const auto l2 = input.get<double>("l2", -1.0);
433  const auto I1 = input.get<double>("I1", -1.0);
434  const auto I2 = input.get<double>("I2", -1.0);
435 
436  Fr = Hyperfine::doublyOddSP_F(mu, I_nuc, mu1, I1, l1, gl1, I2, l2, print);
437  }
438 
439  // Optionally print F(r) function to file
440  if (input.get<bool>("printF", false)) {
441  std::ofstream of(wf.identity() + "_" + Fr_str + ".txt");
442  of << "r/fm F(r)\n";
443  for (auto r : wf.grid()) {
444  of << r * PhysConst::aB_fm << " "
445  << Fr(r * PhysConst::aB_fm, r_nucau * PhysConst::aB_fm) << "\n";
446  }
447  }
448 
449  return std::make_unique<hfs>(k, g_or_Q, r_nucau, wf.grid(), Fr, use_MHz);
450 }
451 
452 } // namespace DiracOperator
Speacial operator: 0.
Definition: TensorOperator.hpp:279
General operator (virtual base class); operators derive from this.
Definition: TensorOperator.hpp:110
virtual double radialIntegral(const DiracSpinor &Fa, const DiracSpinor &Fb) const
Defined via <a||h||b> = angularF(a,b) * radialIntegral(a,b) (Note: if radial_rhs is overridden,...
Definition: TensorOperator.cpp:133
Units: Assumes g in nuc. magneton units (magnetic), and Q in barns (electric), and rN is atomic units...
Definition: hfs.hpp:208
std::string units() const override final
Returns units of operator (usually au, may be MHz, etc.)
Definition: hfs.hpp:226
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition: hfs.hpp:223
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: hfs.hpp:230
l (orbital angular momentum) operator
Definition: jls.hpp:27
s (spin) operator
Definition: jls.hpp:50
Stores radial Dirac spinor: F_nk = (f, g)
Definition: DiracSpinor.hpp:41
int kappa() const
Dirac quantum number, kappa.
Definition: DiracSpinor.hpp:84
double jjp1() const
j(j+1)
Definition: DiracSpinor.hpp:88
Holds grid, including type + Jacobian (dr/du)
Definition: Grid.hpp:31
auto num_points() const
Number of grid points.
Definition: Grid.hpp:64
std::size_t getIndex(double x, bool require_nearest=false) const
Given value, returns grid index. if not require_nearest, returns lower.
Definition: Grid.cpp:76
Holds list of Options, and a list of other InputBlocks. Can be initialised with a list of options,...
Definition: InputBlock.hpp:142
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
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
Stores Wavefunction (set of valence orbitals, grid, HF etc.)
Definition: Wavefunction.hpp:36
int Znuc() const
Nuclear charge, Z.
Definition: Wavefunction.hpp:98
std::string identity() const
Atomic symbol, including core ionisation degree and run_label.
Definition: Wavefunction.cpp:687
const Grid & grid() const
Returns a const reference to the radial grid.
Definition: Wavefunction.hpp:81
std::string atom() const
String of atom info (e.g., "Cs, Z=55, A=133")
Definition: Wavefunction.hpp:189
const Nuclear::Nucleus & nucleus() const
Returns Nuclear::nucleus object (contains nuc. parameters)
Definition: Wavefunction.hpp:96
double threej_2(int two_j1, int two_j2, int two_j3, int two_m1, int two_m2, int two_m3)
Calculates wigner 3j symbol. Takes in 2*j (or 2*l) - intput is integer.
Definition: Wigner369j.hpp:146
constexpr int neg1pow_2(int two_a)
Evaluates (-1)^{two_a/2} (for integer a; two_a is even)
Definition: Wigner369j.hpp:121
constexpr int twoj_k(int ka)
returns 2j given kappa
Definition: Wigner369j.hpp:14
double Ck_kk(int k, int ka, int kb)
Reduced (relativistic) angular ME: <ka||C^k||kb> [takes k and kappa].
Definition: Wigner369j.hpp:267
auto doublyOddSP_F(double mut, double It, double mu1, double I1, double l1, int gl1, double I2, double l2, bool print=true) -> Func_R2_R
'Volotka' SP model, for doubly-odd nuclei: Phys. Rev. Lett. 125, 063002 (2020)
Definition: hfs.hpp:141
std::vector< double > RadialFunc(int k, double rN, const Grid &rgrid, const Func_R2_R &hfs_F)
Takes in F(r) and k, and forms hyperfine radial function: F(r,rN)/r^{k+1}.
Definition: hfs.hpp:194
auto uSP(double mu, double I_nuc, double l_pn, int gl, double n, double R, bool u_option, bool print=true) -> Func_R2_R
Elizarov et al., Optics and Spectroscopy (2006): u(r) = u0(R-r)^n.
Definition: hfs.hpp:72
auto pointlike_F() -> Func_R2_R
Pointlike F(r): 1.
Definition: hfs.hpp:30
auto sphericalBall_F() -> Func_R2_R
Spherical ball F(r): (r/rN)^3 for r<rN, 1 for r>rN.
Definition: hfs.hpp:18
double convert_RME_to_AB(int k, int ka, int kb)
Converts reduced matrix element to A/B coeficients (takes k, kappa, kappa)
Definition: hfs.hpp:175
double convert_RME_to_AB_2J(int k, int tja, int tjb)
Converts reduced matrix element to A/B coeficients (takes k, 2J, 2J)
Definition: hfs.hpp:166
auto VolotkaSP_F(double mu, double I_nuc, double l_pn, int gl, bool print=true) -> Func_R2_R
'Volotka' single-particle model: see Phys. Rev. Lett. 125, 063002 (2020)
Definition: hfs.hpp:36
auto sphericalShell_F() -> Func_R2_R
Spherical shell F(r): 0 for r<rN, 1 for r>rN.
Definition: hfs.hpp:25
Dirac Operators: General + derived.
Definition: GenerateOperator.cpp:12
Isotope findIsotopeData(int z, int a)
Looks up + returns an isotope from the list. If not in list, partially blank.
Definition: NuclearData.cpp:6
constexpr double muN_CGS_MHz
Nulcear magneton in MHz (via Gaussian CGS-derived atomic units):
Definition: PhysConst_constants.hpp:103
bool ci_wc_compare(std::string_view s1, std::string_view s2)
Compares two strings, s1 and s2. s2 may contain ONE wildcard ('*') which will match anything....
Definition: String.hpp:100
bool ci_compare(std::string_view s1, std::string_view s2)
Case insensitive string compare. Essentially: LowerCase(s1)==LowerCase(s2)
Definition: String.hpp:92