ampsci
High-precision calculations for one- and two-valence atomic systems
hfs.hpp
1#pragma once
2#include "DiracOperator/TensorOperator.hpp"
3#include "IO/InputBlock.hpp"
4#include "Physics/NuclearData.hpp"
5#include "Potentials/NuclearPotentials.hpp"
6#include "Wavefunction/Wavefunction.hpp"
7#include "fmt/color.hpp"
8#include "qip/Maths.hpp"
9#include <functional>
10
11namespace DiracOperator {
12
13//==============================================================================
14//! Auxiliary functions for hyperfine operators; F(r) [nuclear distribution] and similar
15//! @details See \ref DiracOperator::hfs for operator
16namespace Hyperfine {
17
18/*!
19 @brief Type for radial function F(r, r_Nuc) -- nuclear magnetisation/charge distribution
20 @details
21 - F(r,rN) contains only finite nuclear size correction, not HFS radial function.
22 - F(r,rN) -> 1 for r > rN
23 - r and rN always in atomic units
24
25 The full HFS radial operator is t^k(r) = F(r, r_N) / r^{k+1}, formed by
26 \ref tk_radial. F(r) itself is defined by the chosen nuclear model;
27 see \ref sphericalBall_F, \ref VolotkaSP_F, \ref uSP, \ref doublyOddSP_F,
28 \ref generic_F.
29*/
30using RadialFunction = std::function<double(double r, double r_Nuc)>;
31
32//==============================================================================
33/*!
34 @brief Forms the hyperfine radial operator: F(r, r_N) / r^{k+1}
35 @details
36 \f[
37 t^k_{\rm radial}[r_i] = \frac{F(r_i, r_N)}{r_i^{k+1}}
38 \f]
39
40 See \ref RadialFunction for the contract on F(r, r_N).
41 Angular factors and signs are handled elsewhere.
42
43 @param k Multipole rank (1 = M1, 2 = E2, ...)
44 @param rN Nuclear radius (a.u.); passed to @p hfs_F as r_N
45 @param r Radial grid points (a.u.)
46 @param hfs_F Nuclear magnetisation/charge distribution F(r, r_N);
47 see \ref RadialFunction and \ref sphericalBall_F
48 @return Radial values of t^k(r)
49*/
50std::vector<double> tk_radial(int k, double rN, const std::vector<double> &r,
51 const RadialFunction &hfs_F);
52
53//==============================================================================
54/*!
55 @brief Spherical ball model for F(r, r_N) [default]. Uniformly distributed point k-poles.
56 @details
57 Based on a simple multipole expansion, assuming the nucleus is composed of
58 uniformly distributed point k-poles. Note: not everyone defines this the same way.
59
60 \f[
61 F(r, r_N) =
62 \begin{cases}
63 (r/r_N)^{2k+1} & r < r_N \\
64 1 & r > r_N
65 \end{cases}
66 \f]
67*/
69
70//! Spherical shell F(r, r_N): 0 for r < r_N, 1 for r > r_N
72
73//! Pointlike F(r): F = 1 everywhere
75
76/*!
77 @brief Volotka single-particle nuclear model: F(r, rN)
78 @details
79 Calculates the Bohr-Weisskopf magnetisation distribution using the Volotka
80 single-particle model of Phys. Rev. Lett. 125, 063002 (2020).
81 Assumes radial nuclear density is a step function.
82 F goes to 1 as r_N -> 0. See \ref RadialFunction for F(r) contract.
83
84 @param mu Nuclear magnetic moment (in nuclear magnetons)
85 @param I_nuc Nuclear spin
86 @param l_pn Orbital angular momentum of valence nucleon
87 @param gl Orbital g-factor (1 = proton, 0 = neutron)
88 @param print Print model details
89
90 @return Radial function \f$ F_{\mathrm{BW}}(r, r_N) \f$
91*/
92RadialFunction VolotkaSP_F(double mu, double I_nuc, double l_pn, int gl,
93 bool print = true);
94
95/*!
96 @brief Elizarov single-particle magnetisation model [extended Volotka]
97 @details
98 Returns the radial magnetisation function F(r, r_N) for the model of
99 Elizarov, A. A. et al., Opt. Spectrosc. 100, 361 (2006).
100 Uses nuclear radial density u(r), with:
101
102 \f[
103 u(r) =
104 \begin{cases}
105 u_0 (R-r)^n & \text{u1(r)} \\
106 u_0 r^n & \text{u2(r)}
107 \end{cases}
108 \f]
109
110 n=0 should correspond to the Volotka model (see \ref VolotkaSP_F).
111 See \ref RadialFunction for F(r) contract.
112
113 @param mu Nuclear magnetic moment (in nuclear magnetons)
114 @param I_nuc Nuclear spin
115 @param l_pn Orbital angular momentum of valence nucleon
116 @param gl Orbital g-factor (1 = proton, 0 = neutron)
117 @param n Power; usually 0, 1, or 2. 0 => Volotka
118 @param R Nuclear radius
119 @param u_option Selects radial form: true for u1(r), false for u2(r)
120 @param print Print model details
121
122 @return Radial magnetisation function F(r, r_N)
123
124 @warning Normalisation is done by numerical integration; may be unstable.
125 Check that result returns to \ref VolotkaSP_F as n -> 0.
126*/
127RadialFunction uSP(double mu, double I_nuc, double l_pn, int gl, double n,
128 double R, bool u_option, bool print = true);
129
130//! Volotka single-particle model for doubly-odd nuclei
131/*! @details
132 From: [Phys. Rev. Lett. 125, 063002 (2020).](http://arxiv.org/abs/2001.01907)
133
134 Total magnetisation:
135 \f[
136 g F(r) = 0.5 \left[ g_1 F_1(r) + g_2 F_2(r)
137 + (g_1 F_1(r) - g_2 F_2(r)) K \right]
138 \f]
139
140 with
141
142 \f[
143 K = \frac{[ I_1(I_1+1) - I_2(I_2+1) ]}{[ I(I+1) ]}
144 \f]
145
146 Returns F(r) (i.e., divided by total g).
147
148 g2 is obtained from
149 g = 0.5 [ g1 + g2 + (g1 - g2) K ].
150
151 @note F1, F2 are the Volotka single-particle functions (see \ref VolotkaSP_F).
152
153 @param mut Total nuclear magnetic moment (in nuclear magnetons)
154 @param It Total nuclear spin
155 @param mu1 Magnetic moment of nucleon 1
156 @param I1 Spin of nucleon 1
157 @param l1 Orbital angular momentum of nucleon 1
158 @param gl1 Orbital g-factor of nucleon 1 (1 = proton, 0 = neutron)
159 @param I2 Spin of nucleon 2
160 @param l2 Orbital angular momentum of nucleon 2
161 @param print Print model details
162
163 @return Radial function F(r)
164*/
165RadialFunction doublyOddSP_F(double mut, double It, double mu1, double I1,
166 double l1, int gl1, double I2, double l2,
167 bool print = true);
168
169/*!
170 @brief Constructs F(r) from a numerical density rho(r) given on the radial grid.
171 @details
172 Computes the cumulative radial integral with rank-k weighting:
173
174 \f[
175 F(r) = N \int_0^r x^{2k} \rho(x)\, \d x, \qquad
176 N = \frac{1}{\int_0^\infty x^{2k} \rho(x)\, \d x}
177 \f]
178
179 so that \f$ F(r) \to 1 \f$ as \f$ r \to \infty \f$.
180
181 The x^{2k} weighting matches the radial scaling of the multipole operator of rank k
182 (consistent with \ref sphericalBall_F, where F ~ r^{2k+1} inside the nucleus).
183
184 The density @p rho need not be pre-normalised.
185 The returned function ignores its second argument (r_Nuc), since the
186 distribution is already encoded in @p rho. Assumes @p r is always a
187 grid point (no interpolation).
188
189 @param grid Radial grid on which @p rho is defined.
190 @param rho Nuclear density values on the grid points.
191 @param k Multipole rank (1 = M1, 2 = E2, ...)
192 @return Radial distribution function F(r, r_Nuc).
193*/
194RadialFunction generic_F(const Grid &grid, const std::vector<double> &rho,
195 int k);
196
197//------------------------------------------------------------------------------
198//! Converts reduced matrix element to A/B coeficients (takes k, 2J, 2J)
199double convert_RME_to_HFSconstant_2J(int k, int tja, int tjb);
200
201//! Converts reduced matrix element to A/B coeficients (takes k, kappa, kappa)
202double convert_RME_to_HFSconstant(int k, int ka, int kb);
203
204} // namespace Hyperfine
205
206//==============================================================================
207//==============================================================================
208//==============================================================================
209
210//! Generalised hyperfine-structure operator, including relevant nuclear moment
211/*! @details
212
213 Implements the nuclear multipole hyperfine operator of rank @p k using a
214 specified finite-nucleus radial model.
215
216 By default, includes the nuclear moment, and relevant factors.
217 Input parameter @p GQ is the g-factor (k=1) or nuclear moment (k>1),
218 in units of nuclear magnetons * barns^power.
219 If 'units' set to 'au', usually should set mu=I=Q=1 then to get raw t^k matrix elements.
220
221 That is, it calculates:
222
223 \f[
224 GQ * t^k
225 \f]
226
227 with:
228
229 \f[
230 t^k =
231 \frac{-1}{r^{k+1}}\,F(r)
232 \begin{cases}
233 C^k & \text{electric (even k)} \\
234 \sqrt{\frac{k+1}{k}}
235 \mathbf{\alpha}\!\cdot\!\mathbf{C}^{(0)}_k
236 & \text{magnetic (odd k)}
237 \end{cases}
238 \f]
239
240 where F(r) is nuclear k-pole distribution (=1 for pointlike).
241
242 Convert to hyperfine constant by taking stretched state, and multiply by:
243
244 \f[
245 M =
246 \begin{cases}
247 1/J & k = 1 \\
248 2 & k = 2 \\
249 -1 & k = 3 \\
250 1 & k \geq 4 \\
251 \end{cases}
252 \f]
253
254 This factor (including the steched 3j symbol) is returned by \ref Hyperfine::convert_RME_to_HFSconstant()
255
256 Units:
257 - Assumes nuclear moments in units of:
258 - magnetic moments: @f$\mu_N\, b^{(k-1)/2}@f$
259 - electric moments: @f$b^{k/2}@f$
260 - Matrix element are in MHz by default, otherwise in atomic units.
261
262 Here \f$ \mu_N \f$ is the nuclear magneton and \f$ b \f$ is the barn.
263
264 See, e.g., Xiao _et al._, [Phys. Rev. A 102, 022810 (2020).](http://arxiv.org/abs/2007.06798)
265
266 The radial part is constructed from \ref Hyperfine::tk_radial().
267
268 @note See @ref DiracOperator::Hyperfine for details
269*/
270class hfs final : public TensorOperator {
271 using RadialFunction = std::function<double(double, double)>;
272
273public:
274 //! @brief Constructs hyperfine operator of multipolarity k.
275 /*! @details
276 Initialises the hyperfine interaction operator.
277
278 @param in_k Tensor rank k (multipolarity) of the hyperfine interaction
279 (e.g., 1,2,3,... for M1, E2, M3,... etc.)
280 @param GQ Nuclear g-factor for k=1, general moment for k>1
281 @param rN_au Nuclear radius (a.u.), used for finite-size magnetisation models.
282 @param rgrid Radial grid used to define the radial functions.
283 @param hfs_F Radial magnetisation distribution function
284 (see @ref DiracOperator::Hyperfine).
285 @param MHzQ If true outputs in MHz units (assuming input g etc. in muN.barns); otherwise in raw atomic units (pure t^k matrix elements).
286
287 @see See also: \ref DiracOperator::Hyperfine
288 */
289 hfs(int in_k, double GQ, double rN_au, const Grid &rgrid,
290 const RadialFunction &hfs_F = Hyperfine::pointlike_F(), bool MHzQ = true)
291 : TensorOperator(in_k, Parity::even, GQ,
292 Hyperfine::tk_radial(in_k, rN_au, rgrid.r(), hfs_F)),
293 k(in_k),
294 magnetic(k % 2 != 0),
295 cfg(magnetic ? 1.0 : 0.0),
296 cff(magnetic ? 0.0 : 1.0),
297 mMHzQ(MHzQ) {
298
299 const auto power = magnetic ? (k - 1) / 2 : k / 2;
300
301 // Assumes nuclear moment in muN*b^(k-1)/2 for magnetic,
302 // and b^k/2 for electric
303 const auto unit_au =
304 magnetic ? PhysConst::muN_CGS * std::pow(PhysConst::barn_au, power) :
305 std::pow(PhysConst::barn_au, power);
306 m_unit = mMHzQ ? unit_au * PhysConst::Hartree_MHz : 1.0;
307 }
308
309 std::string name() const override final {
310 return "hfs" + std::to_string(k) + "";
311 }
312 std::string units() const override final { return mMHzQ ? "MHz" : "au"; }
313
314 double angularF(const int ka, const int kb) const override final {
315 return magnetic ? -double(ka + kb) / double(k) *
316 Angular::Ck_kk(k, ka, -kb) * m_unit :
317 -Angular::Ck_kk(k, ka, kb) * m_unit;
318 }
319
320 double angularCff(int, int) const override final { return cff; }
321 double angularCgg(int, int) const override final { return cff; }
322 double angularCfg(int, int) const override final { return cfg; }
323 double angularCgf(int, int) const override final { return cfg; }
324
325 std::unique_ptr<TensorOperator> clone() const override final {
326 return std::make_unique<hfs>(*this);
327 }
328
329 static std::unique_ptr<TensorOperator> generate(const IO::InputBlock &input,
330 const Wavefunction &wf) {
331
332 input.check(
333 {{"", "Most following will be taken from the default nucleus if "
334 "not explicitely given"},
335 {"mu", "Magnetic moment in mu_N"},
336 {"Q", "Nuclear quadrupole moment, in barns. Also used as overall "
337 "constant for any higher-order moments [1.0]"},
338 {"k", "Multipolarity. 1=mag. dipole, 2=elec. quad, etc. [1]"},
339 {"rrms",
340 "nuclear (magnetic) rms radius, in Fermi (fm) (defult is charge rms)"},
341 {"units",
342 "Units for output (only for k=1,k=2). MHz or au. For MHz, "
343 "assumes nuclear moments g/Q are in mu_N*barns^p. For atomic "
344 "units, best to set g=Q=1 to get raw t^k matrix elements [MHz]"},
345 {"F", "F(r): Nuclear moment distribution: ball, point, shell, "
346 "SingleParticle, doublyOddSP, uSP, Fermi, or Gaussian [ball]"},
347 {"", "The following options are all for F(r) details. See "
348 "https://ampsci.dev for full details (search `hyperfine`)"},
349 {"printF", "Writes F(r) to a text file [false]"},
350 {"print", "Write F(r) info to screen [true]"},
351 {"", "The following are only for F=SingleParticle or doublyOddSP"},
352 {"I", "Nuclear spin. Taken from nucleus"},
353 {"parity", "Nulcear parity: +/-1"},
354 {"l", "l for unpaired nucleon (automatically derived from I and "
355 "parity; best to leave as default)"},
356 {"gl", "=1 for proton, =0 for neutron"},
357 {"", "The following are only used if F=doublyOddSP"},
358 {"mu1", "mag moment of 'first' unpaired nucleon"},
359 {"gl1", "gl of 'first' unpaired nucleon"},
360 {"l1", "l of 'first' unpaired nucleon"},
361 {"l2", "l of 'second' unpaired nucleon"},
362 {"I1", "total spin (J) of 'first' unpaired nucleon"},
363 {"I2", "total spin (J) of 'second' unpaired nucleon"},
364 {"", "The following are only for u(r) function (F=uSP)"},
365 {"u", "u1 or u2 : u1(r) = (R-r)^n, u2(r) = r^n [u1]"},
366 {"n", "n that appears above. Should be between 0 and 2 [0]"},
367 {"", "The following are only for Fermi function (F=Fermi)"},
368 {"t",
369 "skin thickness (normally 2.3). note: c taken from wavefunction, or "
370 "from rrms above [2.3]"}});
371 if (input.has_option("help")) {
372 return nullptr;
373 }
374
375 const auto nuc = wf.nucleus();
376 const auto isotope = Nuclear::findIsotopeData(nuc.z(), nuc.a());
377
378 auto mu = input.get("mu", isotope.mu ? *isotope.mu : 1.0);
379 auto I_nuc = input.get("I", isotope.I_N ? *isotope.I_N : 1.0);
380 const auto print = input.get("print", true);
381 const auto k = input.get("k", 1);
382
383 const auto use_MHz =
384 qip::ci_compare(input.get<std::string>("units", "MHz"), "MHz");
385
386 if (k <= 0) {
387 fmt2::styled_print(fg(fmt::color::red), "\nError 246:\n");
388 std::cout << "In hyperfine: invalid K=" << k << "! meaningless results\n";
389 }
390 if (I_nuc <= 0 && k == 1) {
391 fmt2::styled_print(fg(fmt::color::orange), "\nWarning 253:\n");
392 std::cout << "In hyperfine: invalid I_nuc=" << I_nuc
393 << "! meaningless results\nSetting I=1\n";
394 I_nuc = 1;
395 }
396 if (mu == 0.0 && k == 1) {
397 fmt2::styled_print(fg(fmt::color::orange), "\nWarning 352:\n");
398 std::cout << "Setting mu=1\n";
399 mu = 1;
400 }
401
402 const auto g_or_Q =
403 (k == 1) ? (mu / I_nuc) :
404 (k == 2) ? input.get("Q", isotope.q ? *isotope.q : 1.0) :
405 input.get("Q", 1.0);
406
407 enum class DistroType {
408 point,
409 ball,
410 shell,
411 SingleParticle,
412 doublyOddSP,
413 uSP,
414 Fermi,
415 Gaussian,
416 Error
417 };
418
419 const std::string default_distribution = "ball";
420
421 // For compatability with old notation of 'F(r)' input option
422 const auto Fr_str =
423 input.has_option("F") ?
424 input.get<std::string>("F", default_distribution) :
425 input.has_option("nuc_mag") ?
426 input.get<std::string>("nuc_mag", default_distribution) :
427 input.get<std::string>("F(r)", default_distribution);
428
429 const auto distro_type =
430 (qip::ci_wc_compare(Fr_str, "point*") || qip::ci_compare(Fr_str, "1")) ?
431 DistroType::point :
432 qip::ci_compare(Fr_str, "ball") ? DistroType::ball :
433 qip::ci_compare(Fr_str, "shell") ? DistroType::shell :
434 qip::ci_wc_compare(Fr_str, "Single*") ? DistroType::SingleParticle :
435 qip::ci_wc_compare(Fr_str, "doublyOdd*") ? DistroType::doublyOddSP :
436 (qip::ci_compare(Fr_str, "spu") || qip::ci_compare(Fr_str, "uSP")) ?
437 DistroType::uSP :
438 qip::ci_wc_compare(Fr_str, "Fermi*") ? DistroType::Fermi :
439 qip::ci_wc_compare(Fr_str, "Gauss*") ? DistroType::Gaussian :
440 DistroType::Error;
441 if (distro_type == DistroType::Error) {
442 fmt2::styled_print(fg(fmt::color::red), "\nError 271:\n");
443 std::cout << "\nIn hyperfine. Unkown F(r) - " << Fr_str << "\n";
444 std::cout << "Defaulting to pointlike!\n";
445 }
446
447 const auto r_rmsfm =
448 distro_type == DistroType::point ? 0.0 : input.get("rrms", nuc.r_rms());
449 const auto r_nucfm = std::sqrt(5.0 / 3) * r_rmsfm;
450 const auto r_nucau = r_nucfm / PhysConst::aB_fm;
451
452 if (print) {
453 std::cout << "\nHyperfine structure: " << wf.atom() << "\n";
454 std::cout << "K=" << k << " ("
455 << (k == 1 ? "magnetic dipole" :
456 k == 2 ? "electric quadrupole" :
457 k % 2 == 0 ? "electric multipole" :
458 "magnetic multipole")
459 << ")\n";
460 std::cout << "Using " << Fr_str << " nuclear distro for F(r)\n"
461 << "w/ r_N = " << r_nucfm << "fm = " << r_nucau
462 << "au (r_rms=" << r_rmsfm << "fm)\n";
463 std::cout << "Points inside nucleus: " << wf.grid().getIndex(r_nucau)
464 << "\n";
465 if (k == 1) {
466 std::cout << "mu = " << mu << ", I = " << I_nuc << ", g = " << g_or_Q
467 << "\n";
468 } else {
469 std::cout << "Q = " << g_or_Q << "\n";
470 }
471 }
472
473 // default is BALL:
474 auto Fr = Hyperfine::sphericalBall_F(k); // should never be used
475
476 if (distro_type == DistroType::ball) {
478
479 } else if (distro_type == DistroType::shell) {
481
482 } else if (distro_type == DistroType::SingleParticle) {
483 const auto pi = input.get("parity", isotope.parity ? *isotope.parity : 0);
484 const auto l_tmp = int(I_nuc + 0.5 + 0.0001);
485 auto l = ((l_tmp % 2 == 0) == (pi == 1)) ? l_tmp : l_tmp - 1;
486 l = input.get("l", l); // can override derived 'l' (not recommended)
487 const auto gl_default = wf.Znuc() % 2 == 0 ? 0 : 1; // unparied proton?
488 const auto gl = input.get<int>("gl", gl_default);
489 if (print) {
490 std::cout << "Single-Particle (Volotka formula) for unpaired";
491 if (gl == 1)
492 std::cout << " proton ";
493 else if (gl == 0)
494 std::cout << " neturon ";
495 else
496 std::cout << " gl=" << gl
497 << "??? program will run, but prob wrong!\n";
498 std::cout << "with l=" << l << " (pi=" << pi << ")\n";
499 }
500 Fr = Hyperfine::VolotkaSP_F(mu, I_nuc, l, gl, print);
501
502 } else if (distro_type == DistroType::uSP) {
503 const auto pi = input.get("parity", isotope.parity ? *isotope.parity : 0);
504 const auto u_func =
505 input.get("u", std::string{"u1"}); // u1=(R-r)^n, u2=r^n
506 const bool u_option = u_func == std::string{"u1"};
507 const auto n = input.get("n", 1.0); // u1=(R-r)^n, u2=r^n
508 const auto l_tmp = int(I_nuc + 0.5 + 0.0001);
509 auto l = ((l_tmp % 2 == 0) == (pi == 1)) ? l_tmp : l_tmp - 1;
510 l = input.get("l", l); // can override derived 'l' (not recommended)
511 const auto gl_default = wf.Znuc() % 2 == 0 ? 0 : 1; // unparied proton?
512 const auto gl = input.get<int>("gl", gl_default);
513 if (print) {
514 std::cout << "Single-Particle (Volotka formula) with u(r) for unpaired";
515 if (gl == 1)
516 std::cout << " proton ";
517 else if (gl == 0)
518 std::cout << " neturon ";
519 else
520 std::cout << " gl=" << gl
521 << "??? program will run, but prob wrong!\n";
522 std::cout << "with l=" << l << " (pi=" << pi << ")\n";
523 }
524 if (print) {
525 std::cout << "u(r) = " << u_func << ": "
526 << (u_option ? "(R-r)^n" : "r^n") << ", n=" << n << "\n";
527 }
528 Fr = Hyperfine::uSP(mu, I_nuc, l, gl, n, r_nucau, u_option, print);
529
530 } else if (distro_type == DistroType::doublyOddSP) {
531 const auto mu1 = input.get<double>("mu1", 1.0);
532 const auto gl1 = input.get<int>("gl1", -1); // 1 or 0 (p or n)
533 if (gl1 != 0 && gl1 != 1) {
534 fmt2::styled_print(fg(fmt::color::red), "\nError 324:\n");
535 std::cout << "In " << input.name() << " " << Fr_str
536 << "; have gl1=" << gl1 << " but need 1 or 0\n";
537 return std::make_unique<NullOperator>(NullOperator());
538 }
539 const auto l1 = input.get<double>("l1", -1.0);
540 const auto l2 = input.get<double>("l2", -1.0);
541 const auto I1 = input.get<double>("I1", -1.0);
542 const auto I2 = input.get<double>("I2", -1.0);
543
544 Fr = Hyperfine::doublyOddSP_F(mu, I_nuc, mu1, I1, l1, gl1, I2, l2, print);
545
546 } else if (distro_type == DistroType::Fermi) {
547 const auto t_fm = input.get("t", nuc.t());
548 const auto c_fm = Nuclear::c_hdr_formula_rrms_t(r_rmsfm, t_fm);
549 if (print)
550 std::cout << "Fermi distro: c=" << c_fm << " fm, t=" << t_fm << " fm\n";
551 const auto rho =
552 Nuclear::fermiNuclearDensity_tcN(t_fm, c_fm, 1.0, wf.grid());
553 Fr = Hyperfine::generic_F(wf.grid(), rho, k);
554
555 } else if (distro_type == DistroType::Gaussian) {
556 const auto rN_au = r_rmsfm / PhysConst::aB_fm;
557 if (print)
558 std::cout << "Gaussian distro: r_rms=" << r_rmsfm << " fm\n";
559 std::vector<double> rho;
560 rho.reserve(wf.grid().num_points());
561 for (const auto ri : wf.grid().r()) {
562 rho.push_back(std::exp(-1.5 * ri * ri / (rN_au * rN_au)));
563 }
564 Fr = Hyperfine::generic_F(wf.grid(), rho, k);
565 } else if (distro_type == DistroType::Error) {
566 std::cout << "Using pointlike F(r):\n";
568 }
569
570 // Optionally print F(r) function to file
571 if (input.get<bool>("printF", false)) {
572 std::ofstream of(wf.identity() + "_" + Fr_str + ".txt");
573 of << "r/fm F(r)\n";
574 for (auto r : wf.grid()) {
575 of << r * PhysConst::aB_fm << " "
576 << Fr(r * PhysConst::aB_fm, r_nucau * PhysConst::aB_fm) << "\n";
577 }
578 }
579
580 return std::make_unique<hfs>(k, g_or_Q, r_nucau, wf.grid(), Fr, use_MHz);
581 }
582
583private:
584 int k;
585 bool magnetic;
586 double cfg;
587 double cff;
588 bool mMHzQ;
589 double m_unit{1.0};
590};
591
592} // namespace DiracOperator
General tensor operator (virtual base class); all single-particle (one-body) tenosor operators derive...
Definition TensorOperator.hpp:198
Generalised hyperfine-structure operator, including relevant nuclear moment.
Definition hfs.hpp:270
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 hfs.hpp:325
double angularCgg(int, int) const override final
Angular coefficient C_gg for the g_a*g_b term of the radial integral.
Definition hfs.hpp:321
double angularCgf(int, int) const override final
Angular coefficient C_gf for the g_a*f_b term of the radial integral.
Definition hfs.hpp:323
double angularCfg(int, int) const override final
Angular coefficient C_fg for the f_a*g_b term of the radial integral.
Definition hfs.hpp:322
std::string units() const override final
Returns units of operator as a string (usually au, may be MHz, etc.)
Definition hfs.hpp:312
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition hfs.hpp:309
hfs(int in_k, double GQ, double rN_au, const Grid &rgrid, const RadialFunction &hfs_F=Hyperfine::pointlike_F(), bool MHzQ=true)
Constructs hyperfine operator of multipolarity k.
Definition hfs.hpp:289
double angularF(const int ka, const int kb) const override final
Angular factor A_ab linking the radial integral to the RME.
Definition hfs.hpp:314
double angularCff(int, int) const override final
Angular coefficient C_ff for the f_a*f_b term of the radial integral.
Definition hfs.hpp:320
Non-uniform radial grid with Jacobian, suitable for atomic structure calculations.
Definition Grid.hpp:85
auto num_points() const
Number of grid points.
Definition Grid.hpp:120
std::size_t getIndex(double x, bool require_nearest=false) const
Returns the grid index closest to x. If require_nearest is false, returns the index of the largest gr...
Definition Grid.cpp:76
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::string_view name() const
Returns the name of this block.
Definition InputBlock.hpp:210
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
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:690
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 Ck_kk(int k, int ka, int kb)
Reduced relativistic angular ME: <ka||C^k||kb>. Takes kappa values.
Definition Wigner369j.hpp:486
double convert_RME_to_HFSconstant_2J(int k, int tja, int tjb)
Converts reduced matrix element to A/B coeficients (takes k, 2J, 2J)
Definition hfs.cpp:206
RadialFunction doublyOddSP_F(double mut, double It, double mu1, double I1, double l1, int gl1, double I2, double l2, bool print)
Volotka single-particle model for doubly-odd nuclei.
Definition hfs.cpp:150
std::vector< double > tk_radial(int k, double rN, const std::vector< double > &r, const RadialFunction &hfs_F)
Forms the hyperfine radial operator: F(r, r_N) / r^{k+1}.
Definition hfs.cpp:14
double convert_RME_to_HFSconstant(int k, int ka, int kb)
Converts reduced matrix element to A/B coeficients (takes k, kappa, kappa)
Definition hfs.cpp:240
RadialFunction pointlike_F()
Pointlike F(r): F = 1 everywhere.
Definition hfs.cpp:38
RadialFunction sphericalBall_F(int k)
Spherical ball model for F(r, r_N) [default]. Uniformly distributed point k-poles.
Definition hfs.cpp:26
RadialFunction sphericalShell_F()
Spherical shell F(r, r_N): 0 for r < r_N, 1 for r > r_N.
Definition hfs.cpp:33
RadialFunction generic_F(const Grid &grid, const std::vector< double > &rho, int k)
Constructs F(r) from a numerical density rho(r) given on the radial grid.
Definition hfs.cpp:168
RadialFunction uSP(double mu, double I_nuc, double l_pn, int gl, double n, double R, bool u_option, bool print)
Elizarov single-particle magnetisation model [extended Volotka].
Definition hfs.cpp:81
std::function< double(double r, double r_Nuc)> RadialFunction
Type for radial function F(r, r_Nuc) – nuclear magnetisation/charge distribution.
Definition hfs.hpp:30
RadialFunction VolotkaSP_F(double mu, double I_nuc, double l_pn, int gl, bool print)
Volotka single-particle nuclear model: F(r, rN)
Definition hfs.cpp:44
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
double c_hdr_formula_rrms_t(double rrms, double t)
Calculates c from rrms and t.
Definition NuclearData.cpp:73
Isotope findIsotopeData(int z, int a)
Looks up + returns an isotope from the list. If not in list, partially blank.
Definition NuclearData.cpp:6
std::vector< double > fermiNuclearDensity_tcN(double t, double c, double Z_norm, const Grid &grid)
Fermi charge distribution, rho(r) - normalised to Z_norm.
Definition NuclearPotentials.cpp:319
constexpr double muN_CGS
Nulcear magneton (in Gaussian CGS-derived atomic units):
Definition PhysConst_constants.hpp:101
General-purpose utility library.
Definition Array.hpp:23
bool ci_wc_compare(std::string_view s1, std::string_view s2)
Case-insensitive version of wildcard_compare.
Definition String.hpp:155
bool ci_compare(std::string_view s1, std::string_view s2)
Case-insensitive string comparison; equivalent to tolower(s1) == tolower(s2).
Definition String.hpp:143