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"
15using Func_R2_R = std::function<double(
double,
double)>;
20 const Func_R2_R &hfs_F) {
21 std::vector<double> rfunc;
23 for (
const auto r : rgrid) {
24 rfunc.push_back(hfs_F(r, rN) / std::pow(r, k + 1));
32 return [=](
double r,
double rN) {
33 return (r > rN) ? 1.0 : std::pow(r / rN, 2 * k + 1);
39 return [=](
double r,
double rN) {
return (r > rN) ? 1.0 : 0.0; };
44 return [=](double, double) {
return 1.0; };
49inline auto VolotkaSP_F(
double mu,
double I_nuc,
double l_pn,
int gl,
50 bool print =
true) -> Func_R2_R
55 const auto two_I = int(2 * I_nuc + 0.0001);
56 const auto two_l = int(2 * l_pn + 0.0001);
57 const auto g_l = double(gl);
58 const auto gI = mu / I_nuc;
60 const auto K = (l_pn * (l_pn + 1.0) - (3. / 4.)) / (I_nuc * (I_nuc + 1.0));
61 const double g_s = (2.0 * gI - g_l * (K + 1.0)) / (1.0 - K);
63 std::cout <<
"SingleParticle using: gl=" << g_l <<
", gs=" << g_s
64 <<
", l=" << l_pn <<
", gI=" << gI <<
" (I=" << I_nuc <<
")\n";
66 (two_I == two_l + 1) ?
67 g_s * (1 - two_I) / (4.0 * (two_I + 2)) + g_l * 0.5 * (two_I - 1) :
68 g_s * (3 + two_I) / (4.0 * (two_I + 2)) +
69 g_l * 0.5 * two_I * (two_I + 3) / (two_I + 2);
70 if (two_I != two_l + 1 && two_I != two_l - 1) {
71 std::cerr <<
"\nFAIL:59 in Hyperfine (VolotkaSP_F):\n "
72 "we must have I = l +/- 1/2, but we have: I,l = "
73 << I_nuc <<
"," << l_pn <<
"\n";
74 return [](double, double) {
return 0.0; };
76 return [=](
double r,
double rN) {
77 return (r > rN) ? 1.0 :
78 ((r * r * r) / (rN * rN * rN)) *
79 (1.0 - (3.0 / mu) * std::log(r / rN) * factor);
85inline auto uSP(
double mu,
double I_nuc,
double l_pn,
int gl,
double n,
86 double R,
bool u_option,
bool print =
true) -> Func_R2_R {
87 const auto two_I = int(2 * I_nuc + 0.0001);
88 const auto two_l = int(2 * l_pn + 0.0001);
89 const auto g_l = double(gl);
90 const auto gI = mu / I_nuc;
92 const auto K = (l_pn * (l_pn + 1.0) - (3. / 4.)) / (I_nuc * (I_nuc + 1.0));
93 const double g_s = (2.0 * gI - g_l * (K + 1.0)) / (1.0 - K);
95 std::cout <<
"uSP using: gl=" << g_l <<
", gs=" << g_s <<
", l=" << l_pn
96 <<
", gI=" << gI <<
" (I=" << I_nuc <<
")\n";
98 std::cout <<
"u(r) = u1(r) = u0 (R-r)^n ";
100 std::cout <<
"u(r) = u2(r) = u0 r^n ";
102 std::cout <<
"with n = " << n <<
"\n";
104 if (two_I != two_l + 1 && two_I != two_l - 1) {
105 std::cerr <<
"\nFAIL:59 in Hyperfine (uSP):\n "
106 "we must have I = l +/- 1/2, but we have: I,l = "
107 << I_nuc <<
"," << l_pn <<
"\n";
108 return [](double, double) {
return 0.0; };
113 const auto r2u2 = [=](
double r) {
114 return u_option ? r * r * std::pow(R - r, 2.0 * n) :
115 r * r * std::pow(r, 2.0 * n);
120 1.0 / NumCalc::num_integrate(r2u2, 0.0, R, 100, NumCalc::linear);
122 const auto F0r = [=](
double r) {
123 const auto f = [=](
double x) {
return u02 * r2u2(x); };
124 return NumCalc::num_integrate(f, 0.0, r, 100, NumCalc::linear);
127 const auto FrR = [=](
double r) {
128 const auto f = [=](
double x) {
129 return u02 * r2u2(x) * r * r * r / x / x / x;
131 return NumCalc::num_integrate(f, r, R, 100, NumCalc::linear);
134 const auto two_Ip1 = 2.0 * (I_nuc + 1.0);
135 const auto twoI_p3 = 2.0 * I_nuc + 3.0;
136 const auto twoI_m1 = 2.0 * I_nuc - 1.0;
138 double f1 = (two_I == two_l + 1) ?
139 0.5 * g_s + (I_nuc - 0.5) * g_l :
140 -I_nuc / two_Ip1 * g_s + I_nuc * twoI_p3 / two_Ip1 * g_l;
143 (two_I == two_l + 1) ?
144 -twoI_m1 / (4.0 * two_Ip1) * g_s + (I_nuc - 0.5) * g_l :
145 twoI_p3 / (4.0 * two_Ip1) * g_s + I_nuc * twoI_p3 / two_Ip1 * g_l;
147 return [=](
double r,
double rN) {
148 return (r > rN) ? 1.0 : (1.0 / mu) * (f1 * F0r(r) + f2 * FrR(r));
155 double l1,
int gl1,
double I2,
double l2,
156 bool print =
true) -> Func_R2_R
163 const auto K = (I1 * (I1 + 1.0) - I2 * (I2 + 1.0)) / (It * (It + 1.0));
164 const auto gt = mut / It;
165 const auto g1 = mu1 / I1;
166 const auto g2 = (g1 * (K + 1.0) - 2.0 * gt) / (K - 1.0);
167 const auto mu2 = g2 * I2;
168 const auto gl2 = (gl1 == 0) ? 1 : 0;
169 const auto F1 =
VolotkaSP_F(mu1, I1, l1, gl1, print);
170 const auto F2 =
VolotkaSP_F(mu2, I2, l2, gl2, print);
171 return [=](
double r,
double rN) {
172 return (0.5 / gt) * (g1 * F1(r, rN) + g2 * F2(r, rN) +
173 K * (g1 * F1(r, rN) - g2 * F2(r, rN)));
181 const auto tjz = std::min(tja, tjb);
186 const auto f = [&]() {
190 const double J = 0.5 * tjz;
204 std::cout <<
"Warning: k=" << k
205 <<
" has no standard A/B/C/D hyperfine-constant definition; "
225inline double hfsB(
const TensorOperator *h,
const DiracSpinor &Fa) {
226 auto Raa = h->radialIntegral(Fa, Fa);
227 return Raa * double(Fa.twoj() - 1) / double(Fa.twoj() + 2) *
240 using Func_R2_R = std::function<double(
double,
double)>;
243 hfs(
int in_k,
double in_GQ,
double rN_au,
const Grid &rgrid,
248 magnetic(k % 2 != 0),
249 cfg(magnetic ? 1.0 : 0.0),
250 cff(magnetic ? 0.0 : 1.0),
253 const auto power = magnetic ? (k - 1) / 2 : k / 2;
259 std::pow(PhysConst::barn_au, power);
260 m_unit = mMHzQ ? unit_au * PhysConst::Hartree_MHz : unit_au;
263 std::string
name() const override final {
264 return "hfs" + std::to_string(k) +
"";
266 std::string
units() const override final {
return mMHzQ ?
"MHz" :
"au"; }
268 double angularF(
const int ka,
const int kb)
const override final {
269 return magnetic ? -double(ka + kb) / double(k) *
274 double angularCff(
int,
int)
const override final {
return cff; }
275 double angularCgg(
int,
int)
const override final {
return cff; }
276 double angularCfg(
int,
int)
const override final {
return cfg; }
277 double angularCgf(
int,
int)
const override final {
return cfg; }
290inline std::unique_ptr<DiracOperator::TensorOperator>
295 {{
"",
"Most following will be taken from the default nucleus if "
296 "not explicitely given"},
297 {
"mu",
"Magnetic moment in mu_N"},
298 {
"Q",
"Nuclear quadrupole moment, in barns. Also used as overall "
299 "constant for any higher-order moments [1.0]"},
300 {
"k",
"Multipolarity. 1=mag. dipole, 2=elec. quad, etc. [1]"},
302 "nuclear (magnetic) rms radius, in Fermi (fm) (defult is charge rms)"},
303 {
"units",
"Units for output (only for k=1,k=2). MHz or au [MHz]"},
304 {
"F",
"F(r): Nuclear moment distribution: ball, point, shell, "
305 "SingleParticle, or doublyOddSP [ball]"},
306 {
"F(r)",
"Obselete; use 'F' from now - will be removed"},
307 {
"nuc_mag",
"Obselete; use 'F' from now - will be removed"},
308 {
"printF",
"Writes F(r) to a text file [false]"},
309 {
"print",
"Write F(r) info to screen [true]"},
310 {
"",
"The following are only for F=SingleParticle or doublyOddSP"},
311 {
"I",
"Nuclear spin. Taken from nucleus"},
312 {
"parity",
"Nulcear parity: +/-1"},
313 {
"l",
"l for unpaired nucleon (automatically derived from I and "
314 "parity; best to leave as default)"},
315 {
"gl",
"=1 for proton, =0 for neutron"},
316 {
"",
"The following are only used if F=doublyOddSP"},
317 {
"mu1",
"mag moment of 'first' unpaired nucleon"},
318 {
"gl1",
"gl of 'first' unpaired nucleon"},
319 {
"l1",
"l of 'first' unpaired nucleon"},
320 {
"l2",
"l of 'second' unpaired nucleon"},
321 {
"I1",
"total spin (J) of 'first' unpaired nucleon"},
322 {
"I2",
"total spin (J) of 'second' unpaired nucleon"},
323 {
"",
"The following are only for u(r) function"},
324 {
"u",
"u1 or u2 : u1(r) = (R-r)^n, u2(r) = r^n [u1]"},
325 {
"n",
"n that appears above. Should be between 0 and 2 [0]"}});
332 auto mu = input.
get(
"mu", isotope.mu ? *isotope.mu : 1.0);
333 auto I_nuc = input.
get(
"I", isotope.I_N ? *isotope.I_N : 1.0);
334 const auto print = input.
get(
"print",
true);
335 const auto k = input.
get(
"k", 1);
341 fmt2::styled_print(fg(fmt::color::red),
"\nError 246:\n");
342 std::cout <<
"In hyperfine: invalid K=" << k <<
"! meaningless results\n";
344 if (I_nuc <= 0 && k == 1) {
345 fmt2::styled_print(fg(fmt::color::orange),
"\nWarning 253:\n");
346 std::cout <<
"In hyperfine: invalid I_nuc=" << I_nuc
347 <<
"! meaningless results\nSetting I=1\n";
350 if (mu == 0.0 && k == 1) {
351 fmt2::styled_print(fg(fmt::color::orange),
"\nWarning 352:\n");
352 std::cout <<
"Setting mu=1\n";
356 const auto g_or_Q = (k == 1) ? (mu / I_nuc) :
357 (k == 2) ? input.
get(
"Q", isotope.q ? *isotope.q : 1.0) :
360 enum class DistroType {
370 std::string default_distribution =
"ball";
375 input.
get<std::string>(
"F", default_distribution) :
376 input.has_option(
"nuc_mag") ?
377 input.get<std::string>(
"nuc_mag", default_distribution) :
378 input.get<std::string>(
"F(r)", default_distribution);
380 const auto distro_type =
389 if (distro_type == DistroType::Error) {
390 fmt2::styled_print(fg(fmt::color::red),
"\nError 271:\n");
391 std::cout <<
"\nIn hyperfine. Unkown F(r) - " << Fr_str <<
"\n";
392 std::cout <<
"Defaulting to pointlike!\n";
396 distro_type == DistroType::point ? 0.0 : input.
get(
"rrms", nuc.r_rms());
397 const auto r_nucfm = std::sqrt(5.0 / 3) * r_rmsfm;
398 const auto r_nucau = r_nucfm / PhysConst::aB_fm;
401 std::cout <<
"\nHyperfine structure: " << wf.
atom() <<
"\n";
402 std::cout <<
"K=" << k <<
" ("
403 << (k == 1 ?
"magnetic dipole" :
404 k == 2 ?
"electric quadrupole" :
405 k % 2 == 0 ?
"electric multipole" :
406 "magnetic multipole")
408 std::cout <<
"Using " << Fr_str <<
" nuclear distro for F(r)\n"
409 <<
"w/ r_N = " << r_nucfm <<
"fm = " << r_nucau
410 <<
"au (r_rms=" << r_rmsfm <<
"fm)\n";
411 std::cout <<
"Points inside nucleus: " << wf.
grid().
getIndex(r_nucau)
414 std::cout <<
"mu = " << mu <<
", I = " << I_nuc <<
", g = " << g_or_Q
417 std::cout <<
"Q = " << g_or_Q <<
"\n";
423 if (distro_type == DistroType::ball) {
425 }
else if (distro_type == DistroType::shell) {
427 }
else if (distro_type == DistroType::SingleParticle) {
428 const auto pi = input.
get(
"parity", isotope.parity ? *isotope.parity : 0);
429 const auto l_tmp = int(I_nuc + 0.5 + 0.0001);
430 auto l = ((l_tmp % 2 == 0) == (pi == 1)) ? l_tmp : l_tmp - 1;
431 l = input.
get(
"l",
l);
432 const auto gl_default = wf.
Znuc() % 2 == 0 ? 0 : 1;
433 const auto gl = input.
get<
int>(
"gl", gl_default);
435 std::cout <<
"Single-Particle (Volotka formula) for unpaired";
437 std::cout <<
" proton ";
439 std::cout <<
" neturon ";
441 std::cout <<
" gl=" << gl <<
"??? program will run, but prob wrong!\n";
442 std::cout <<
"with l=" <<
l <<
" (pi=" << pi <<
")\n";
445 }
else if (distro_type == DistroType::spu) {
446 const auto pi = input.
get(
"parity", isotope.parity ? *isotope.parity : 0);
447 const auto u_func = input.
get(
"u", std::string{
"u1"});
448 const bool u_option = u_func == std::string{
"u1"};
449 const auto n = input.
get(
"n", 0.0);
450 const auto l_tmp = int(I_nuc + 0.5 + 0.0001);
451 auto l = ((l_tmp % 2 == 0) == (pi == 1)) ? l_tmp : l_tmp - 1;
452 l = input.
get(
"l",
l);
453 const auto gl_default = wf.
Znuc() % 2 == 0 ? 0 : 1;
454 const auto gl = input.
get<
int>(
"gl", gl_default);
456 std::cout <<
"Single-Particle (Volotka formula) with u(r) for unpaired";
458 std::cout <<
" proton ";
460 std::cout <<
" neturon ";
462 std::cout <<
" gl=" << gl <<
"??? program will run, but prob wrong!\n";
463 std::cout <<
"with l=" <<
l <<
" (pi=" << pi <<
")\n";
466 }
else if (distro_type == DistroType::doublyOddSP) {
467 const auto mu1 = input.
get<
double>(
"mu1", 1.0);
468 const auto gl1 = input.
get<
int>(
"gl1", -1);
469 if (gl1 != 0 && gl1 != 1) {
470 fmt2::styled_print(fg(fmt::color::red),
"\nError 324:\n");
471 std::cout <<
"In " << input.name() <<
" " << Fr_str
472 <<
"; have gl1=" << gl1 <<
" but need 1 or 0\n";
475 const auto l1 = input.
get<
double>(
"l1", -1.0);
476 const auto l2 = input.
get<
double>(
"l2", -1.0);
477 const auto I1 = input.
get<
double>(
"I1", -1.0);
478 const auto I2 = input.
get<
double>(
"I2", -1.0);
484 if (input.
get<
bool>(
"printF",
false)) {
485 std::ofstream of(wf.
identity() +
"_" + Fr_str +
".txt");
487 for (
auto r : wf.grid()) {
488 of << r * PhysConst::aB_fm <<
" "
489 << Fr(r * PhysConst::aB_fm, r_nucau * PhysConst::aB_fm) <<
"\n";
493 return std::make_unique<hfs>(k, g_or_Q, r_nucau, wf.
grid(), Fr, use_MHz);
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 nuclear moment in units of powers of nuclear magnetons and/or barns - muN*b^(k-1)/2 fo...
Definition hfs.hpp:238
std::string units() const override final
Returns units of operator (usually au, may be MHz, etc.)
Definition hfs.hpp:266
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition hfs.hpp:263
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:268
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:87
double jjp1() const
j(j+1)
Definition DiracSpinor.hpp:91
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
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
std::string identity() const
Atomic symbol, including core ionisation degree and run_label.
Definition Wavefunction.cpp:685
std::string atom() const
String of atom info (e.g., "Cs, Z=55, A=133")
Definition Wavefunction.hpp:190
const Nuclear::Nucleus & nucleus() const
Returns Nuclear::nucleus object (contains nuc. parameters)
Definition Wavefunction.hpp:97
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:177
constexpr int neg1pow_2(int two_a)
Evaluates (-1)^{two_a/2} (for integer a; two_a is even)
Definition Wigner369j.hpp:152
constexpr int twoj_k(int ka)
returns 2j given kappa
Definition Wigner369j.hpp:45
double Ck_kk(int k, int ka, int kb)
Reduced (relativistic) angular ME: <ka||C^k||kb> [takes k and kappa].
Definition Wigner369j.hpp:298
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:154
auto sphericalBall_F(int k=1) -> Func_R2_R
Spherical ball F(r): (r/rN)^3 for r<rN, 1 for r>rN.
Definition hfs.hpp:31
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:85
auto pointlike_F() -> Func_R2_R
Pointlike F(r): 1.
Definition hfs.hpp:43
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:214
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:179
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:49
auto sphericalShell_F() -> Func_R2_R
Spherical shell F(r): 0 for r<rN, 1 for r>rN.
Definition hfs.hpp:38
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:19
Dirac Operators: General + derived.
Definition GenerateOperator.cpp:3
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
constexpr double muN_CGS
Nulcear magneton (in Gaussian CGS-derived atomic units):
Definition PhysConst_constants.hpp:101
qip library: A collection of useful functions
Definition Array.hpp:9
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:139
bool ci_compare(std::string_view s1, std::string_view s2)
Case insensitive string compare. Essentially: LowerCase(s1)==LowerCase(s2)
Definition String.hpp:131