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"
15 using Func_R2_R = std::function<double(
double,
double)>;
19 return [=](
double r,
double rN) {
20 return (r > rN) ? 1.0 : (r * r * r) / (rN * rN * rN);
26 return [=](
double r,
double rN) {
return (r > rN) ? 1.0 : 0.0; };
31 return [=](double, double) {
return 1.0; };
36 inline auto VolotkaSP_F(
double mu,
double I_nuc,
double l_pn,
int gl,
37 bool print =
true) -> Func_R2_R
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);
45 const auto gI = mu / I_nuc;
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);
50 std::cout <<
"SingleParticle using: gl=" << g_l <<
", gs=" << g_s
51 <<
", l=" << l_pn <<
", gI=" << gI <<
" (I=" << I_nuc <<
")\n";
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; };
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);
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);
77 const auto gI = mu / I_nuc;
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);
82 std::cout <<
"uSP using: gl=" << g_l <<
", gs=" << g_s <<
", l=" << l_pn
83 <<
", gI=" << gI <<
" (I=" << I_nuc <<
")\n";
85 std::cout <<
"u(r) = u1(r) = u0 (R-r)^n ";
87 std::cout <<
"u(r) = u2(r) = u0 r^n ";
89 std::cout <<
"with n = " << n <<
"\n";
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; };
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);
107 1.0 / NumCalc::num_integrate(r2u2, 0.0, R, 100, NumCalc::linear);
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);
114 const auto FrR = [=](
double r) {
115 const auto f = [=](
double x) {
116 return u02 * r2u2(x) * r * r * r / x / x / x;
118 return NumCalc::num_integrate(f, r, R, 100, NumCalc::linear);
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;
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;
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;
134 return [=](
double r,
double rN) {
135 return (r > rN) ? 1.0 : (1.0 / mu) * (f1 * F0r(r) + f2 * FrR(r));
142 double l1,
int gl1,
double I2,
double l2,
143 bool print =
true) -> Func_R2_R
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)));
167 const auto tjz = std::min(tja, tjb);
170 const auto f = k % 2 == 0 ? 2.0 : 1 / (0.5 * tjz);
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) *
195 const Func_R2_R &hfs_F) {
196 std::vector<double> rfunc;
198 for (
const auto r : rgrid)
199 rfunc.push_back(hfs_F(r, rN) / pow(r, k + 1));
210 using Func_R2_R = std::function<double(
double,
double)>;
213 hfs(
int in_k,
double in_GQ,
double rN_au,
const Grid &rgrid,
218 magnetic(k % 2 != 0),
219 cfg(magnetic ? 1.0 : 0.0),
220 cff(magnetic ? 0.0 : 1.0),
223 std::string
name() const override final {
224 return "hfs" + std::to_string(k) +
"";
226 std::string
units() const override final {
227 return (k <= 2 && mMHzQ) ?
"MHz" :
"au";
230 double angularF(
const int ka,
const int kb)
const override final {
234 (mMHzQ && k == 2) ? PhysConst::barn_MHz :
236 return magnetic ? -(ka + kb) *
Angular::Ck_kk(k, -ka, kb) * unit :
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; }
255 inline std::unique_ptr<DiracOperator::TensorOperator>
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]"},
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]"}});
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);
305 fmt2::styled_print(fg(fmt::color::red),
"\nError 246:\n");
306 std::cout <<
"In hyperfine: invalid K=" << k <<
"! meaningless results\n";
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";
314 const auto g_or_Q = (k == 1) ? (mu / I_nuc) :
315 (k == 2) ? input.
get(
"Q", isotope.q ? *isotope.q : 1.0) :
318 enum class DistroType {
328 std::string default_distribution =
"ball";
333 input.
get<std::string>(
"nuc_mag", default_distribution) :
334 input.
get<std::string>(
"F(r)", default_distribution);
336 const auto distro_type =
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";
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;
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")
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)
370 std::cout <<
"mu = " << mu <<
", I = " << I_nuc <<
", g = " << g_or_Q
373 std::cout <<
"Q = " << g_or_Q <<
"\n";
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);
388 const auto gl_default = wf.
Znuc() % 2 == 0 ? 0 : 1;
389 const auto gl = input.
get<
int>(
"gl", gl_default);
391 std::cout <<
"Single-Particle (Volotka formula) for unpaired";
393 std::cout <<
" proton ";
395 std::cout <<
" neturon ";
397 std::cout <<
" gl=" << gl <<
"??? program will run, but prob wrong!\n";
398 std::cout <<
"with l=" <<
l <<
" (pi=" << pi <<
")\n";
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"});
404 const bool u_option = u_func == std::string{
"u1"};
405 const auto n = input.
get(
"n", 0.0);
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);
409 const auto gl_default = wf.
Znuc() % 2 == 0 ? 0 : 1;
410 const auto gl = input.
get<
int>(
"gl", gl_default);
412 std::cout <<
"Single-Particle (Volotka formula) with u(r) for unpaired";
414 std::cout <<
" proton ";
416 std::cout <<
" neturon ";
418 std::cout <<
" gl=" << gl <<
"??? program will run, but prob wrong!\n";
419 std::cout <<
"with l=" <<
l <<
" (pi=" << pi <<
")\n";
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);
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";
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);
440 if (input.
get<
bool>(
"printF",
false)) {
441 std::ofstream of(wf.
identity() +
"_" + Fr_str +
".txt");
443 for (
auto r : wf.
grid()) {
444 of << r * PhysConst::aB_fm <<
" "
445 << Fr(r * PhysConst::aB_fm, r_nucau * PhysConst::aB_fm) <<
"\n";
449 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 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
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