ampsci
High-precision calculations for one- and two-valence atomic systems
BoundState.hpp
1#pragma once
2#include "AdamsMoulton.hpp"
3#include "Physics/PhysConst_constants.hpp"
4#include "Wavefunction/DiracSpinor.hpp"
5#include <memory>
6#include <utility>
7#include <vector>
8class Grid;
9
10//! Functions and classes used to solve the Dirac equation.
11namespace DiracODE {
12
13//==============================================================================
14/*!
15 @brief Solves bound-state problem for local potential (en < 0).
16 @details
17 Solves \f$ (H_0 + v - \epsilon_a)F_a = 0 \f$ for the bound state.
18 en0 is the initial energy guess (must be reasonably good).
19 eps is the convergence target for the energy.
20 - @p v is the local potential (e.g., v = v_dir + v_nuc)
21 - @p H_off_diag is an optional off-diagonal potential
22 - @p alpha: \f$ \alpha = \lambda\alpha_0 \f$ is the effective fine-structure constant
23*/
24void boundState(DiracSpinor &Fa, const double en0, const std::vector<double> &v,
25 const std::vector<double> &H_off_diag = {},
26 const double alpha = PhysConst::alpha, double eps = 1.0e-14,
27 const DiracSpinor *const VxFa = nullptr,
28 const DiracSpinor *const Fa0 = nullptr, double zion = 1,
29 double mass = 1.0);
30
31//! Factory overload: constructs a DiracSpinor(n, kappa, gr) and calls boundState().
33 int n, int kappa, const double en0, const std::shared_ptr<const Grid> &gr,
34 const std::vector<double> &v, const std::vector<double> &H_off_diag = {},
35 const double alpha = PhysConst::alpha, double eps = 1.0e-14,
36 const DiracSpinor *const VxFa = nullptr,
37 const DiracSpinor *const Fa0 = nullptr, double zion = 1, double mass = 1.0) {
38 DiracSpinor Fnk = DiracSpinor(n, kappa, gr);
39 boundState(Fnk, en0, v, H_off_diag, alpha, eps, VxFa, Fa0, zion, mass);
40 return Fnk;
41}
42
43//! For given energy en, solves DE with correct boundary conditions at the origin
44void regularAtOrigin(DiracSpinor &Fa, const double en,
45 const std::vector<double> &v,
46 const std::vector<double> &H_off_diag, const double alpha,
47 const DiracSpinor *const VxFa = nullptr,
48 const DiracSpinor *const Fa0 = nullptr, double zion = 1,
49 double mass = 1.0);
50
51//! For given energy en, solves (local) DE with correct boundary conditions at infinity
52void regularAtInfinity(DiracSpinor &Fa, const double en,
53 const std::vector<double> &v,
54 const std::vector<double> &H_off_diag,
55 const double alpha,
56 const DiracSpinor *const VxFa = nullptr,
57 const DiracSpinor *const Fa0 = nullptr, double zion = 1,
58 double mass = 1.0);
59
60namespace Internal {
61
62//==============================================================================
63//! Parameters used for Adams-Moulton bound-state solver.
64namespace Param {
65
66//! K (# steps) for Adams-Moulton method (between 1 and 12).
67constexpr std::size_t K_Adams = 7;
68//! Parameter to determine 'asymptotically large r'.
69constexpr double cALR = 550.0;
70//! Max # attempts at converging bound-state energy.
71constexpr int max_its = 99;
72//! Fractional size of 'large' energy update steps (~12%).
73constexpr double lfrac_de = 0.12;
74//! Number of grid points either side of the classical turning point.
75constexpr int d_ctp = 4;
76
77//! Order of coefficients in the large-r asymptotic expansion.
78constexpr int nx = 15;
79//! Convergence threshold for the asymptotic expansion.
80constexpr double nx_eps = 1.e-12;
81
82//! Weighting function for meshing inward/outward solutions at the turning point.
83//! Must be positive; index i may be negative [ctp - d_ctp].
84constexpr auto weight = [](std::size_t i) {
85 return 1.0 / static_cast<double>(i * i + 1);
86};
87
88static_assert(
89 Param::K_Adams >= 1 && Param::K_Adams <= AdamsMoulton::K_max,
90 "\nFAIL in DiracODE: parameter K_Adams must be between 5 and 8\n");
91
92} // namespace Param
93
94//==============================================================================
95/*!
96 @brief Derivative matrix for the radial Dirac equation, dF/du = D(u)*F(u) + S(u).
97 @details
98 Implements AdamsMoulton::DerivativeMatrix<std::size_t, double>, using the
99 grid index i as the argument type (T = std::size_t). The ODE is solved in
100 terms of the grid parameter u (where r = r(u)), so all matrix elements
101 include a dr/du Jacobian factor.
102
103 The radial Dirac equation for a central potential v(r) is:
104
105 \f[
106 \frac{d}{du}\begin{pmatrix}f\\g\end{pmatrix}
107 = \frac{dr}{du}
108 \begin{pmatrix}
109 -\kappa/r + \alpha H_\text{mag} & \alpha(\varepsilon - v) + 2mc \\
110 \alpha(v - \varepsilon) & \kappa/r - \alpha H_\text{mag}
111 \end{pmatrix}
112 \begin{pmatrix}f\\g\end{pmatrix}
113 + S
114 \f]
115
116 where \f$ c = 1/\alpha \f$ is the speed of light in atomic units.
117 The optional inhomogeneous source S encodes the exchange interaction:
118
119 \f[
120 S_f = -\alpha \, [V_x F_a]_g \, \frac{dr}{du}, \quad
121 S_g = +\alpha \, [V_x F_a]_f \, \frac{dr}{du}.
122 \f]
123
124 @note Non-copyable; the constructor stores raw pointers to the grid and
125 potential arrays, which must outlive this object.
126*/
127struct DiracDerivative : AdamsMoulton::DerivativeMatrix<std::size_t, double> {
128
129 /*!
130 @brief Constructs the Dirac derivative matrix for a given orbital and potential.
131 @param in_grid Radial grid.
132 @param in_v Local potential v(r).
133 @param in_k Orbital kappa quantum number.
134 @param in_en Orbital energy.
135 @param in_alpha Fine-structure constant (alpha).
136 @param V_off_diag Optional off-diagonal (magnetic) potential H_mag(r).
137 If empty, treated as zero.
138 @param VxFa Optional exchange potential VxFa. If nullptr, ignored.
139 @param iFa0 Optional inhomogeneous source spinor. If nullptr, ignored.
140 @param zion Effective ionic charge (used for boundary conditions).
141 @param in_mass Effective particle mass in atomic units (default 1).
142 */
143 DiracDerivative(const Grid &in_grid, const std::vector<double> &in_v,
144 const int in_k, const double in_en, const double in_alpha,
145 const std::vector<double> &V_off_diag = {},
146 const DiracSpinor *const VxFa = nullptr,
147 const DiracSpinor *const iFa0 = nullptr, double zion = 1,
148 double in_mass = 1.0);
149
150 const Grid *const pgr;
151 const std::vector<double> *const v;
152 const std::vector<double> *const Hmag;
153 const DiracSpinor *const VxFa;
154 const DiracSpinor *const Fa0;
155 const double zion = 1.0;
156 const int k;
157 const double en, alpha, cc;
158 double mass;
159
160 //! D matrix elements (see @ref DiracDerivative for definitions); index i is grid point.
161 double a(std::size_t i) const final;
162 double b(std::size_t i) const final;
163 double c(std::size_t i) const final;
164 double d(std::size_t i) const final;
165 //! Inhomogeneous source terms from exchange potential VxFa.
166 double Sf(std::size_t i) const final;
167 double Sg(std::size_t i) const final;
168
169 DiracDerivative(const DiracDerivative &) = delete;
170 void operator=(const DiracDerivative &) = delete;
171};
172
173//==============================================================================
174// Tracks energy guesses and node counts during bound-state iteration.
175struct TrackEnGuess {
176 int count_toomany = 0;
177 int count_toofew = 0;
178 double high_en = 0.0;
179 double low_en = 0.0;
180};
181
182//==============================================================================
183//! Returns grid index of "practical infinity": the point where f(r) drops effectively to zero.
184std::size_t findPracticalInfinity(const double en, const std::vector<double> &v,
185 const std::vector<double> &r,
186 const double alr);
187
188//! Returns grid index of the classical turning point, where |V(r)| = |E|.
189std::size_t findClassicalTurningPoint(const double en,
190 const std::vector<double> &v,
191 std::size_t pinf, std::size_t d_ctp);
192
193/*!
194 @brief Constructs a trial Dirac solution with correct boundary conditions at both ends.
195 @details
196 Integrates outwards from the origin and inwards from practical infinity, then
197 joins the two solutions at the classical turning point (ctp). If the energy is
198 not an eigenvalue, there will be a discontinuity ('kink') in g at ctp. The
199 difference dg = g_out - g_in at the joining region is stored and used for the
200 perturbation-theory energy update.
201*/
202void trialDiracSolution(std::vector<double> &f, std::vector<double> &g,
203 std::vector<double> &dg, const double en, const int ka,
204 const std::vector<double> &v,
205 const std::vector<double> &H_off_diag, const Grid &gr,
206 std::size_t ctp, std::size_t d_ctp, std::size_t pinf,
207 const double alpha,
208 const DiracSpinor *const VxFa = nullptr,
209 const DiracSpinor *const Fa0 = nullptr, double zion = 1,
210 double mass = 1.0);
211
212//! Returns the number of nodes (sign changes) in f up to index maxi.
213int countNodes(const std::vector<double> &f, const std::size_t maxi);
214
215//! Makes a large (bisection-style) energy update; updates TrackEnGuess accordingly.
216void largeEnergyChange(double *en, TrackEnGuess *sofar, double frac_de,
217 bool toomany_nodes);
218
219//! Returns an updated energy using first-order perturbation theory, given f and dg at ctp.
220double smallEnergyChangePT(const double en, const double anorm,
221 const std::vector<double> &f,
222 const std::vector<double> &dg, std::size_t ctp,
223 std::size_t d_ctp, const double alpha,
224 const TrackEnGuess &sofar);
225
226/*!
227 @brief Integrates the Dirac equation outwards from the origin.
228 @details
229 Integrates up to index @p final (not inclusive); if final=0, integrates to
230 f.size(). The solution satisfies the boundary condition at r=0 but not at large r.
231*/
232void solve_Dirac_outwards(std::vector<double> &f, std::vector<double> &g,
233 const DiracDerivative &Hd, std::size_t final = 0);
234
235/*!
236 @brief Integrates the Dirac equation inwards from pinf to ctp.
237 @details
238 The solution satisfies the boundary condition at large r (practical infinity)
239 but not at the origin.
240*/
241void solve_Dirac_inwards(std::vector<double> &f, std::vector<double> &g,
242 const DiracDerivative &Hd, std::size_t ctp,
243 std::size_t pinf, double mass = 1.0);
244
245/*!
246 @brief Joins the inward and outward solutions into a single wavefunction.
247 @details
248 Produces a solution with correct boundary conditions at both r=0 and large r,
249 matched at ctp using a weighted average over [ctp-d_ctp, ctp+d_ctp].
250 The resulting wavefunction may not be smooth at the joining point if the
251 energy is not an eigenvalue; the discontinuity in g is stored in dg.
252*/
253void joinInOutSolutions(std::vector<double> &f, std::vector<double> &g,
254 std::vector<double> &dg,
255 const std::vector<double> &f_in,
256 const std::vector<double> &g_in, std::size_t ctp,
257 std::size_t d_ctp, std::size_t pinf);
258
259} // namespace Internal
260} // namespace DiracODE
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:42
Holds grid, including type + Jacobian (dr/du)
Definition Grid.hpp:31
constexpr double lfrac_de
Fractional size of 'large' energy update steps (~12%).
Definition BoundState.hpp:73
constexpr int max_its
Max # attempts at converging bound-state energy.
Definition BoundState.hpp:71
constexpr double cALR
Parameter to determine 'asymptotically large r'.
Definition BoundState.hpp:69
constexpr std::size_t K_Adams
K (# steps) for Adams-Moulton method (between 1 and 12).
Definition BoundState.hpp:67
constexpr int nx
Order of coefficients in the large-r asymptotic expansion.
Definition BoundState.hpp:78
constexpr auto weight
Weighting function for meshing inward/outward solutions at the turning point. Must be positive; index...
Definition BoundState.hpp:84
constexpr double nx_eps
Convergence threshold for the asymptotic expansion.
Definition BoundState.hpp:80
constexpr int d_ctp
Number of grid points either side of the classical turning point.
Definition BoundState.hpp:75
Functions and classes used to solve the Dirac equation.
Definition AsymptoticSpinor.hpp:8
void regularAtInfinity(DiracSpinor &Fa, const double en, const std::vector< double > &v, const std::vector< double > &H_mag, const double alpha, const DiracSpinor *const VxFa, const DiracSpinor *const Fa0, double zion, double mass)
For given energy en, solves (local) DE with correct boundary conditions at infinity.
Definition BoundState.cpp:170
void regularAtOrigin(DiracSpinor &Fa, const double en, const std::vector< double > &v, const std::vector< double > &H_mag, const double alpha, const DiracSpinor *const VxFa, const DiracSpinor *const Fa0, double zion, double mass)
For given energy en, solves DE with correct boundary conditions at the origin.
Definition BoundState.cpp:149
void boundState(DiracSpinor &Fn, const double en0, const std::vector< double > &v, const std::vector< double > &H_mag, const double alpha, double eps_goal, const DiracSpinor *const VxFa, const DiracSpinor *const Fa0, double zion, double mass)
Solves bound-state problem for local potential (en < 0).
Definition BoundState.cpp:23
constexpr double alpha
Fine-structure constant: alpha = 1/137.035 999 177(21) [CODATA 2022].
Definition PhysConst_constants.hpp:24
Pure-virtual struct defining the derivative matrix for a 2x2 ODE system.
Definition AdamsMoulton.hpp:47
Derivative matrix for the radial Dirac equation, dF/du = D(u)*F(u) + S(u).
Definition BoundState.hpp:127
double a(std::size_t i) const final
D matrix elements (see DiracDerivative for definitions); index i is grid point.
Definition BoundState.cpp:501
double Sf(std::size_t i) const final
Inhomogeneous source terms from exchange potential VxFa.
Definition BoundState.cpp:513