ampsci
c++ program for high-precision atomic structure calculations of single-valence systems
Loading...
Searching...
No Matches
EM_multipole.hpp
1#pragma once
2#include "DiracOperator/TensorOperator.hpp"
3#include "IO/InputBlock.hpp"
4#include "Wavefunction/Wavefunction.hpp"
5#include "qip/Maths.hpp"
6
7namespace DiracOperator {
8
9//==============================================================================
11
16class Ek_omega final : public TensorOperator {
17public:
18 Ek_omega(const Grid &gr, int K, double alpha, double omega,
19 bool transition_form)
20 : TensorOperator(K, Angular::evenQ(K) ? Parity::even : Parity::odd, 0.0,
21 gr.r(), 0, Realness::real, true),
22 m_alpha(alpha),
23 m_K(K),
24 m_transition_form(transition_form) {
25 updateFrequency(omega);
26 }
27 std::string name() const override final {
28 return m_transition_form ? std::string("t^E_") + std::to_string(m_K) :
29 std::string("E(") + std::to_string(m_K) + ")";
30 }
31 std::string units() const override final {
32 return m_transition_form ? std::string("") : std::string("aB^k");
33 }
34
35 double angularF(const int ka, const int kb) const override final {
36 return m_constant * Angular::Ck_kk(m_K, ka, kb);
37 }
38
39 //--------------
40 DiracSpinor radial_rhs(const int kappa_a,
41 const DiracSpinor &Fb) const override final {
42
43 DiracSpinor dF(0, kappa_a, Fb.grid_sptr());
44 dF.min_pt() = Fb.min_pt();
45 dF.max_pt() = Fb.max_pt();
46
47 if (isZero(kappa_a, Fb.kappa())) {
48 dF.min_pt() = 0;
49 dF.max_pt() = 0;
50 return dF;
51 }
52
53 const auto c1 = double(kappa_a - Fb.kappa()) / (m_K + 1) + 1;
54 const auto c2 = c1 - 2;
55 for (auto i = Fb.min_pt(); i < Fb.max_pt(); i++) {
56 dF.f(i) = j1[i] * Fb.f(i) - j2[i] * c1 * Fb.g(i);
57 dF.g(i) = j1[i] * Fb.g(i) - j2[i] * c2 * Fb.g(i);
58 }
59 return dF;
60 }
61
62 //--------------
63 double radialIntegral(const DiracSpinor &Fa,
64 const DiracSpinor &Fb) const override final {
65
66 if (isZero(Fa.kappa(), Fb.kappa())) {
67 return 0.0;
68 }
69
70 const auto cc = double(Fa.kappa() - Fb.kappa()) / (m_K + 1);
71 // const auto c2 = c1 - 2;
72
73 const auto pi = std::max(Fa.min_pt(), Fb.min_pt());
74 const auto pf = std::min(Fa.max_pt(), Fb.max_pt());
75
76 const auto &drdu = Fb.grid().drdu();
77
78 const auto ff = NumCalc::integrate(1.0, pi, pf, j1, Fa.f(), Fb.f(), drdu);
79 const auto gg = NumCalc::integrate(1.0, pi, pf, j1, Fa.g(), Fb.g(), drdu);
80
81 // fg only enters if states not the same. Compare memory address, not quantum numbers, may be spline/non-spline?
82 const auto fg_not_gf = &Fa != &Fb;
83 const auto fg =
84 fg_not_gf ? NumCalc::integrate(1.0, pi, pf, j2, Fa.f(), Fb.g(), drdu) :
85 0.0;
86 const auto gf =
87 fg_not_gf ? NumCalc::integrate(1.0, pi, pf, j2, Fa.g(), Fb.f(), drdu) :
88 fg;
89
90 return (ff + gg - cc * (fg + gf) - (fg - gf)) * Fb.grid().du();
91 }
92
94 void updateFrequency(const double omega) override final {
95 const auto q = std::abs(m_alpha * omega);
96
97 // should be 1 for "transition" operator, otherwise factor for the "moment" form
98 m_constant = m_transition_form ?
99 1.0 :
100 qip::double_factorial(2 * m_K + 1) / qip::pow(q, m_K);
101
102 SphericalBessel::fillBesselVec_kr(m_K, q, m_vec, &j1);
103 SphericalBessel::fillBesselVec_kr(m_K + 1, q, m_vec, &j2);
104 }
105
106private:
107 double m_alpha; // (including var-alpha)
108 int m_K;
109 bool m_transition_form = true;
110 std::vector<double> j1{};
111 std::vector<double> j2{};
112};
113
114//==============================================================================
116
121class Ekv_omega final : public TensorOperator {
122public:
123 Ekv_omega(const Grid &gr, int K, double alpha, double omega,
124 bool transition_form)
125 : TensorOperator(K, Angular::evenQ(K) ? Parity::even : Parity::odd, 0.0,
126 gr.r(), 0, Realness::real, true),
127 m_alpha(alpha),
128 m_K(K),
129 m_transition_form(transition_form) {
130 updateFrequency(omega);
131 }
132 std::string name() const override final {
133 return m_transition_form ? std::string("tv^E_") + std::to_string(m_K) :
134 std::string("Ev(") + std::to_string(m_K) + ")";
135 }
136 std::string units() const override final {
137 return m_transition_form ? std::string("") : std::string("aB^k");
138 }
139
140 double angularF(const int ka, const int kb) const override final {
141 return m_constant * Angular::Ck_kk(m_K, ka, kb);
142 }
143
144 //--------------
145 DiracSpinor radial_rhs(const int kappa_a,
146 const DiracSpinor &Fb) const override final {
147
148 DiracSpinor dF(0, kappa_a, Fb.grid_sptr());
149 dF.min_pt() = Fb.min_pt();
150 dF.max_pt() = Fb.max_pt();
151
152 if (isZero(kappa_a, Fb.kappa())) {
153 dF.min_pt() = 0;
154 dF.max_pt() = 0;
155 return dF;
156 }
157
158 const auto c1 = double(kappa_a - Fb.kappa()) / (m_K + 1);
159 const auto c2 = -double(m_K);
160 const auto c3 = c1 * (m_K + 1);
161 for (auto i = Fb.min_pt(); i < Fb.max_pt(); i++) {
162 dF.f(i) = ((c3 + c2) * j1_on_qr[i] - c1 * j2[i]) * Fb.g(i);
163 dF.g(i) = ((c3 - c2) * j1_on_qr[i] - c1 * j2[i]) * Fb.f(i);
164 }
165 return dF;
166 }
167
168 //--------------
169 double radialIntegral(const DiracSpinor &Fa,
170 const DiracSpinor &Fb) const override final {
171
172 if (isZero(Fa.kappa(), Fb.kappa())) {
173 return 0.0;
174 }
175
176 const auto c1 = double(Fa.kappa() - Fb.kappa()) / (m_K + 1);
177 const auto c2 = -double(m_K);
178 const auto c3 = c1 * (m_K + 1);
179
180 const auto pi = std::max(Fa.min_pt(), Fb.min_pt());
181 const auto pf = std::min(Fa.max_pt(), Fb.max_pt());
182
183 const auto &drdu = Fb.grid().drdu();
184
185 const auto same = &Fa == &Fb;
186
187 const auto fg1 =
188 NumCalc::integrate(1.0, pi, pf, j1_on_qr, Fa.f(), Fb.g(), drdu);
189 const auto fg2 = NumCalc::integrate(1.0, pi, pf, j2, Fa.f(), Fb.g(), drdu);
190
191 const auto gf1 =
192 same ? fg1 :
193 NumCalc::integrate(1.0, pi, pf, j1_on_qr, Fa.g(), Fb.f(), drdu);
194 const auto gf2 =
195 same ? fg2 : NumCalc::integrate(1.0, pi, pf, j2, Fa.g(), Fb.f(), drdu);
196
197 return ((c3 + c2) * fg1 + (c3 - c2) * gf1 - c1 * (fg2 + gf2)) *
198 Fb.grid().du();
199 }
200
202 void updateFrequency(const double omega) override final {
203 const auto q = std::abs(m_alpha * omega);
204 m_q = q;
205
206 // should be 1 for "transition" operator, otherwise factor for the "moment" form
207 m_constant = m_transition_form ?
208 1.0 :
209 qip::double_factorial(2 * m_K + 1) / qip::pow(q, m_K);
210
211 SphericalBessel::fillBesselVec_kr(m_K, q, m_vec, &j1_on_qr);
212 SphericalBessel::fillBesselVec_kr(m_K + 1, q, m_vec, &j2);
213 for (std::size_t i = 0; i < m_vec.size(); ++i) {
214 j1_on_qr[i] /= (q * m_vec[i]);
215 }
216 }
217
218private:
219 double m_alpha; // (including var-alpha)
220 int m_K;
221 double m_q{0.0};
222 bool m_transition_form = true;
223 std::vector<double> j1_on_qr{};
224 std::vector<double> j2{};
225};
226
227//==============================================================================
229class Mk_omega final : public TensorOperator {
230public:
231 Mk_omega(const Grid &gr, int K, double alpha, double omega,
232 bool transition_form)
233 : TensorOperator(K, Angular::evenQ(K) ? Parity::odd : Parity::even, 0.0,
234 gr.r(), 0, Realness::real, true),
235 m_alpha(alpha),
236 m_K(K),
237 m_transition_form(transition_form) {
238 updateFrequency(omega);
239 }
240 std::string name() const override final {
241 return m_transition_form ? std::string("t^M_") + std::to_string(m_K) :
242 std::string("M(") + std::to_string(m_K) + ")";
243 }
244 std::string units() const override final {
245 return m_transition_form ? std::string("") : std::string("mu_B*aB^k");
246 }
247
248 double angularF(const int ka, const int kb) const override final {
249 return -m_constant * (ka + kb) * Angular::Ck_kk(m_K, -ka, kb) / (m_K + 1);
250 }
251
252 //--------------
253 DiracSpinor radial_rhs(const int kappa_a,
254 const DiracSpinor &Fb) const override final {
255
256 DiracSpinor dF(0, kappa_a, Fb.grid_sptr());
257 dF.min_pt() = Fb.min_pt();
258 dF.max_pt() = Fb.max_pt();
259
260 if (isZero(kappa_a, Fb.kappa()) || (kappa_a == -Fb.kappa())) {
261 dF.min_pt() = 0;
262 dF.max_pt() = 0;
263 return dF;
264 }
265
266 for (auto i = Fb.min_pt(); i < Fb.max_pt(); i++) {
267 dF.f(i) = j1[i] * Fb.g(i);
268 dF.g(i) = j1[i] * Fb.f(i);
269 }
270 return dF;
271 }
272
273 //--------------
274 double radialIntegral(const DiracSpinor &Fa,
275 const DiracSpinor &Fb) const override final {
276
277 if (isZero(Fa.kappa(), Fb.kappa()) || (Fa.kappa() == -Fb.kappa())) {
278 return 0.0;
279 }
280
281 const auto pi = std::max(Fa.min_pt(), Fb.min_pt());
282 const auto pf = std::min(Fa.max_pt(), Fb.max_pt());
283
284 const auto &drdu = Fb.grid().drdu();
285
286 const auto fg = NumCalc::integrate(1.0, pi, pf, j1, Fa.f(), Fb.g(), drdu);
287 const auto gf = NumCalc::integrate(1.0, pi, pf, j1, Fa.g(), Fb.f(), drdu);
288
289 return (fg + gf) * Fb.grid().du();
290 }
291
293 void updateFrequency(const double omega) override final {
294 const auto q = std::abs(m_alpha * omega);
295
296 // should be 1 for "transition" operator, otherwise factor for the "moment" form
297 m_constant = m_transition_form ?
298 1.0 :
299 (2.0 / m_alpha) * qip::double_factorial(2 * m_K + 1) /
300 qip::pow(q, m_K);
301
302 SphericalBessel::fillBesselVec_kr(m_K, q, m_vec, &j1);
303 }
304
305private:
306 double m_alpha;
307 int m_K;
308 bool m_transition_form = true;
309 std::vector<double> j1{};
310};
311
312//------------------------------------------------------------------------------
313
314inline std::unique_ptr<DiracOperator::TensorOperator>
315generate_Ek_omega(const IO::InputBlock &input, const Wavefunction &wf) {
316 using namespace DiracOperator;
317 input.check({{"k", "Rank: k=1 for E1, =2 for E2 etc. [1]"},
318 {"omega", "Frequency: nb: q = alpha*omega [0]"},
319 {"transition_form",
320 "Use transition form (true), or moment form (fasle). nb: use "
321 "moment form to compare to E1/E2 etc, transition form for "
322 "alpha*e^{iqr} expansion [true]"}});
323 if (input.has_option("help")) {
324 return nullptr;
325 }
326 const auto k = input.get("k", 1);
327 const auto omega = input.get("omega", 0.0);
328 const auto transition_form = input.get("transition_form", true);
329 return std::make_unique<Ek_omega>(wf.grid(), k, wf.alpha(), omega,
330 transition_form);
331}
332
333inline std::unique_ptr<DiracOperator::TensorOperator>
334generate_Mk_omega(const IO::InputBlock &input, const Wavefunction &wf) {
335 using namespace DiracOperator;
336 input.check({{"k", "Rank: k=1 for E1, =2 for E2 etc. [1]"},
337 {"omega", "Frequency: nb: q = alpha*omega [0]"},
338 {"transition_form",
339 "Use transition form (true), or moment form (fasle). nb: use "
340 "moment form to compare to E1/E2 etc, transition form for "
341 "alpha*e^{iqr} expansion [true]"}});
342 if (input.has_option("help")) {
343 return nullptr;
344 }
345 const auto k = input.get("k", 1);
346 const auto omega = input.get("omega", 0.0);
347 const auto transition_form = input.get("transition_form", true);
348 return std::make_unique<Mk_omega>(wf.grid(), k, wf.alpha(), omega,
349 transition_form);
350}
351
352inline std::unique_ptr<DiracOperator::TensorOperator>
353generate_Ekv_omega(const IO::InputBlock &input, const Wavefunction &wf) {
354 using namespace DiracOperator;
355 input.check({{"k", "Rank: k=1 for E1, =2 for E2 etc. [1]"},
356 {"omega", "Frequency: nb: q = alpha*omega [0.001]"},
357 {"transition_form",
358 "Use transition form (true), or moment form (fasle). nb: use "
359 "moment form to compare to E1/E2 etc, transition form for "
360 "alpha*e^{iqr} expansion [true]"}});
361 if (input.has_option("help")) {
362 return nullptr;
363 }
364 const auto k = input.get("k", 1);
365 const auto omega = input.get("omega", 0.001);
366 const auto transition_form = input.get("transition_form", true);
367 return std::make_unique<Ekv_omega>(wf.grid(), k, wf.alpha(), omega,
368 transition_form);
369}
370
371} // namespace DiracOperator
Electric multipole operator, L-form, including frequency-dependence.
Definition EM_multipole.hpp:16
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 EM_multipole.hpp:40
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.hpp:94
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 EM_multipole.hpp:63
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:27
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 EM_multipole.hpp:35
std::string units() const override final
Returns units of operator (usually au, may be MHz, etc.)
Definition EM_multipole.hpp:31
Electric multipole operator, V-form, including frequency-dependence.
Definition EM_multipole.hpp:121
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:132
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 EM_multipole.hpp:140
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 EM_multipole.hpp:145
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 EM_multipole.hpp:169
std::string units() const override final
Returns units of operator (usually au, may be MHz, etc.)
Definition EM_multipole.hpp:136
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.hpp:202
Magnetic multipole operator, including frequency-dependence.
Definition EM_multipole.hpp:229
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 EM_multipole.hpp:253
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 EM_multipole.hpp:248
std::string name() const override final
Returns "name" of operator (e.g., 'E1')
Definition EM_multipole.hpp:240
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 EM_multipole.hpp:274
std::string units() const override final
Returns units of operator (usually au, may be MHz, etc.)
Definition EM_multipole.hpp:244
void updateFrequency(const double omega) override final
nb: q = alpha*omega!
Definition EM_multipole.hpp:293
General operator (virtual base class); operators derive from this.
Definition TensorOperator.hpp:110
bool isZero(const int ka, int kb) const
If matrix element <a|h|b> is zero, returns true.
Definition TensorOperator.cpp:15
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:41
auto max_pt() const
Effective size(); index after last non-zero point (index for f[i])
Definition DiracSpinor.hpp:144
const std::vector< double > & f() const
Upper (large) radial component function, f.
Definition DiracSpinor.hpp:125
auto min_pt() const
First non-zero point (index for f[i])
Definition DiracSpinor.hpp:140
const std::vector< double > & g() const
Lower (small) radial component function, g.
Definition DiracSpinor.hpp:132
Holds grid, including type + Jacobian (dr/du)
Definition Grid.hpp:31
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
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:37
const Grid & grid() const
Returns a const reference to the radial grid.
Definition Wavefunction.hpp:82
double alpha() const
Local value of fine-structure constant.
Definition Wavefunction.hpp:88
constexpr bool evenQ(int a)
Returns true if a is even - for integer values.
Definition Wigner369j.hpp:146
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
Dirac Operators: General + derived.
Definition GenerateOperator.cpp:3
constexpr T double_factorial(T x)
Double factorial x!! - nb: does not check for overflow. Max x is 20 for uint64_t.
Definition Maths.hpp:131
constexpr auto pow(T x)
x^n for integer n (n compile-time template parameter), x any arithmetic type (T). Returns double for ...
Definition Maths.hpp:88