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//! @brief Solves bound-state problem for local potential (en < 0)
15/*! @details
16\f[ (H_0 + v - \epsilon_a)F_a = 0\f]
17en0 is initial energy guess (must be reasonably good).
18log_eps: log10(eps); eps is convergence target for energy.
19 - v is local potential (e.g., v = v_dir + v_nuc)
20 - H_off_diag is optional off-diagonal potential.
21 - alpha: \f$\alpha = \lambda\alpha_0\f$ is the effective value of
22fine-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
32 int n, int kappa, const double en0, const std::shared_ptr<const Grid> &gr,
33 const std::vector<double> &v, const std::vector<double> &H_off_diag = {},
34 const double alpha = PhysConst::alpha, double eps = 1.0e-14,
35 const DiracSpinor *const VxFa = nullptr,
36 const DiracSpinor *const Fa0 = nullptr, double zion = 1, double mass = 1.0) {
37 DiracSpinor Fnk = DiracSpinor(n, kappa, gr);
38 boundState(Fnk, en0, v, H_off_diag, alpha, eps, VxFa, Fa0, zion, mass);
39 return Fnk;
40}
41
42//! For given energy en, solves DE with correct boundary conditions at the origin
43void regularAtOrigin(DiracSpinor &Fa, const double en,
44 const std::vector<double> &v,
45 const std::vector<double> &H_off_diag, const double alpha,
46 const DiracSpinor *const VxFa = nullptr,
47 const DiracSpinor *const Fa0 = nullptr, double zion = 1,
48 double mass = 1.0);
49
50//! For given energy en, solves (local) DE with correct boundary conditions at infinity
51void regularAtInfinity(DiracSpinor &Fa, const double en,
52 const std::vector<double> &v,
53 const std::vector<double> &H_off_diag,
54 const double alpha,
55 const DiracSpinor *const VxFa = nullptr,
56 const DiracSpinor *const Fa0 = nullptr, double zion = 1,
57 double mass = 1.0);
58
59namespace Internal {
60
61//==============================================================================
62// Parameters used for Adams-Moulton mehtod:
63namespace Param {
64
65// K (# steps) for Adams-Moulton method (between 1 and 12)
66constexpr std::size_t K_Adams = 7;
67// Parameter to determine 'assymptotically large r [kinda..]' (=800)
68constexpr double cALR = 550.0;
69// Max # attempts at converging [sove bs] (30)
70constexpr int max_its = 99;
71// 'large' energy variations (0.1 => 10%)
72constexpr double lfrac_de = 0.12;
73// Num points past ctp +/- d_ctp.
74constexpr int d_ctp = 4;
75
76// order of the expansion coeficients in large-r asymptotic expansion (15 orig.)
77constexpr int nx = 15;
78// convergance for expansion in asymptotic expansion (10^-8)
79constexpr double nx_eps = 1.e-12;
80
81// weighting function for meshing in/out solutions
82// nb: must be positive, but i may be negative [ctp - d_ctp]
83constexpr auto weight = [](std::size_t i) {
84 return 1.0 / static_cast<double>(i * i + 1);
85};
86
87static_assert(
88 Param::K_Adams >= 1 && Param::K_Adams <= AdamsMoulton::K_max,
89 "\nFAIL in DiracODE: parameter K_Adams must be between 5 and 8\n");
90
91} // namespace Param
92
93//==============================================================================
94//! Matrix which defines Dirac derivative: (dF/dr) = D*F
95struct DiracDerivative : AdamsMoulton::DerivativeMatrix<std::size_t, double> {
96
97 DiracDerivative(const Grid &in_grid, const std::vector<double> &in_v,
98 const int in_k, const double in_en, const double in_alpha,
99 const std::vector<double> &V_off_diag = {},
100 const DiracSpinor *const VxFa = nullptr,
101 const DiracSpinor *const iFa0 = nullptr, double zion = 1,
102 double in_mass = 1.0);
103 const Grid *const pgr;
104 const std::vector<double> *const v;
105 const std::vector<double> *const Hmag;
106 const DiracSpinor *const VxFa;
107 const DiracSpinor *const Fa0;
108 const double zion = 1.0;
109 const int k;
110 const double en, alpha, cc;
111 double mass;
112
113 double a(std::size_t i) const final;
114 double b(std::size_t i) const final;
115 double c(std::size_t i) const final;
116 double d(std::size_t i) const final;
117 double Sf(std::size_t i) const final;
118 double Sg(std::size_t i) const final;
119
120 DiracDerivative(const DiracDerivative &) = delete;
121 void operator=(const DiracDerivative &) = delete;
122};
123
124//==============================================================================
125// To keep track of current/previous energy guesses
126struct TrackEnGuess {
127 // Number of times there was too many/too few nodes:
128 int count_toomany = 0;
129 int count_toofew = 0;
130 // Upper and lower energy window before correct # nodes
131 double high_en = 0.0;
132 double low_en = 0.0;
133};
134
135//==============================================================================
136// Finds "practical infinity" index, where f(r) drops effectively to zero
137std::size_t findPracticalInfinity(const double en, const std::vector<double> &v,
138 const std::vector<double> &r,
139 const double alr);
140
141// Finds "classical turning point" index, where |V(r)| = |E|
142std::size_t findClassicalTurningPoint(const double en,
143 const std::vector<double> &v,
144 std::size_t pinf, std::size_t d_ctp);
145
146// Finds a trial Dirac solution for given energy that has correct boundary conditions.
147/*
148If it's not an exact solution, there will be a 'kink' in the wavefunction
149around ctp (classical turning point), where the inward and outward solutons
150were joind. The difference between g for these, dg=(gout-gin), is stored, and
151is used for PT
152*/
153void trialDiracSolution(std::vector<double> &f, std::vector<double> &g,
154 std::vector<double> &dg, const double en, const int ka,
155 const std::vector<double> &v,
156 const std::vector<double> &H_off_diag, const Grid &gr,
157 std::size_t ctp, std::size_t d_ctp, std::size_t pinf,
158 const double alpha,
159 const DiracSpinor *const VxFa = nullptr,
160 const DiracSpinor *const Fa0 = nullptr, double zion = 1,
161 double mass = 1.0);
162
163// Counts the nodes a given function f
164int countNodes(const std::vector<double> &f, const std::size_t maxi);
165
166// Makes a large update to energy; updates 'TrackEnGuess'
167void largeEnergyChange(double *en, TrackEnGuess *sofar, double frac_de,
168 bool toomany_nodes);
169
170// Makes a small update to energy using perturbation theory, given f and dg
171double smallEnergyChangePT(const double en, const double anorm,
172 const std::vector<double> &f,
173 const std::vector<double> &dg, std::size_t ctp,
174 std::size_t d_ctp, const double alpha,
175 const TrackEnGuess &sofar);
176
177// Solves Dirac equation by integrating outwards from zero.
178// Integrates only to 'final' (not inclusive). If final=0, goes to f.size()
179// Solution has correct boundary condition at r=0, but not at large r.
180void solve_Dirac_outwards(std::vector<double> &f, std::vector<double> &g,
181 const DiracDerivative &Hd, std::size_t final = 0);
182
183// Solves Dirac equation by integrating inwards from 'pinf' to 'ctp'
184// Solution has correct boundary condition at large r, but not at small r.
185void solve_Dirac_inwards(std::vector<double> &f, std::vector<double> &g,
186 const DiracDerivative &Hd, std::size_t ctp,
187 std::size_t pinf, double mass = 1.0);
188
189// Meshes the two solutions from inwards/outwards integration.
190// Produces solution that has correct boundary conditions at 0 and infinity,
191// but may not be smooth at the joining point.
192void joinInOutSolutions(std::vector<double> &f, std::vector<double> &g,
193 std::vector<double> &dg,
194 const std::vector<double> &f_in,
195 const std::vector<double> &g_in, std::size_t ctp,
196 std::size_t d_ctp, std::size_t pinf);
197
198} // namespace Internal
199} // namespace DiracODE
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:41
Holds grid, including type + Jacobian (dr/du)
Definition Grid.hpp:31
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, holds the derivative matrix for 2x2 system of ODEs. Derive from this,...
Definition AdamsMoulton.hpp:79
Matrix which defines Dirac derivative: (dF/dr) = D*F.
Definition BoundState.hpp:95
double a(std::size_t i) const final
a,b,c,d are derivative matrix functions; all must be user implemented
Definition BoundState.cpp:501
double Sf(std::size_t i) const final
Sf and Sg are optional inhomogenous terms.
Definition BoundState.cpp:513