High-precision calculations for one- and two-valence atomic systems
CorePolarisation.hpp
1#pragma once
2#include "DiracOperator/TensorOperator.hpp"
3#include "qip/String.hpp"
4#include <cassert>
5#include <memory>
6#include <string>
7#include <vector>
8class DiracSpinor;
9namespace DiracOperator {
10class TensorOperator;
11}
12namespace HF {
13class HartreeFock;
14}
15
16/*!
17 @brief Core-polarisation (RPA) corrections to matrix elements of an external field.
18 @details
19 Provides classes and functions for computing all-order core-polarisation
20 corrections to matrix elements of an external field operator.
21
22 In the presence of an external field of frequency \f$ \omega \f$, the
23 time-dependent operator is
24 \f[
25 T(t) = t_+ e^{-i\omega t} + t_- e^{+i\omega t},
26 \f]
27 where \f$ t_+ = t^k_q(\omega) \f$ is an irreducible tensor operator of rank \f$ k \f$
28 and \f$ t_- = t_+^\dag(-\omega) \f$.
29 Each orbital, including those in the core, acquires a first-order perturbation,
30 \f[
31 \delta\phi_a(t) = \varphi^a_+ e^{-i\omega t} + \varphi^a_- e^{+i\omega t}.
32 \f]
33 Since the core orbitals are perturbed, the Hartree-Fock potential is also perturbed:
34 \f[
35 \delta V_\pm \phi_i =
36 \sum_a^{\rm core} \left[
37 \matel{\phi_a}{Q}{\varphi^a_+}\phi_i - \matel{\phi_a}{Q}{\phi_i}\varphi^a_+
38 + \matel{\varphi^a_-}{Q}{\phi_a}\phi_i - \matel{\varphi^a_-}{Q}{\phi_i}\phi_a
39 \right].
40 \f]
41
42 The resulting
43 core-polarisation corrections to matrix elements are given by
44 \f[
45 \matel{b}{t_\pm}{a} \to \matel{b}{t_\pm + \delta V_\pm}{a},
46 \f]
47 where \f$ \delta V_\pm \f$ is the correction to the HF potential arising
48 from the perturbed core orbitals \f$ \{\varphi^a_\pm\} \f$.
49
50 Since \f$ \delta V \f$ is solved self-consistently, this accounts for core
51 polarisation to all orders in the Coulomb interaction.
52
53 Two equivalent methods are implemented:
54
55 - TDHF (@ref TDHF, @ref TDHFbasis): solves the TDHF equations
56 \f[
57 (h_{\rm HF} - \en_a \mp \omega)\varphi^a_\pm
58 = -(t_\pm + \delta V_\pm - \delta\en^a_\pm)\phi_a
59 \f]
60 self-consistently for all core orbitals. The corrections can also be
61 found via the basis expansion
62 \f[
63 \varphi^a_\pm = \sum_n \frac{\ket{n}\matel{n}{t_\pm + \delta V_\pm}{a}}
64 {\en_a - \en_n \pm \omega}.
65 \f]
66 See Dzuba et al. (1984).
67
68 - Diagram/Goldstone (@ref DiagramRPA): evaluates the four core-polarisation
69 diagrams directly,
70 \f[
71 \redmatel{w}{\delta V_\pm}{v} =
72 \sum_{na} \frac{(-1)^{k+w-n}}{[k]} \left(
73 \frac{T_{na}\,W^k_{wavn}}{\en_a - \en_n \pm \omega}
74 + \frac{(-1)^{a-n} T_{an}\,W^k_{wnva}}{\en_a - \en_n \mp \omega}
75 \right),
76 \f]
77 with \f$ T_{ij} = t_{ij} + \redmatel{i}{\delta V_\pm}{j} \f$ iterated to
78 convergence. See Johnson et al., Phys. Rev. A 21, 409 (1980).
79
80 Use @ref make_rpa to construct the appropriate object from a method string,
81 and Amplitudes::matrix_elements to compute matrix elements including the
82 correction.
83*/
84namespace ExternalField {
85
86//! Available RPA/core-polarisation methods
87enum class Method { TDHF, basis, diagram, cntm, none, Error };
88
89//! Parses method string to Method enum (case-insensitive)
90inline Method ParseMethod(std::string_view str) {
91 return qip::ci_compare(str, "TDHF") ? Method::TDHF :
92 qip::ci_compare(str, "true") ? Method::TDHF :
93 qip::ci_compare(str, "default") ? Method::TDHF :
94 qip::ci_compare(str, "basis") ? Method::basis :
95 qip::ci_compare(str, "tdhf_basis") ? Method::basis :
96 qip::ci_compare(str, "tdhfbasis") ? Method::basis :
97 qip::ci_compare(str, "diagram") ? Method::diagram :
98 qip::ci_compare(str, "diagramRPA") ? Method::diagram :
99 qip::ci_compare(str, "rpad") ? Method::diagram :
100 qip::ci_compare(str, "rpa(d)") ? Method::diagram :
101 qip::ci_compare(str, "cntm") ? Method::cntm :
102 qip::ci_compare(str, "TDHFcntm") ? Method::cntm :
103 qip::ci_compare(str, "none") ? Method::none :
104 qip::ci_compare(str, "false") ? Method::none :
105 qip::ci_compare(str, "") ? Method::none :
106 Method::Error;
107}
108
109/*!
110 @brief Selects the perturbed orbital: X = varphi_+, Y = varphi_-.
111 @details
112 Corresponds to the two first-order corrections to a core orbital,
113 \f[ \delta\phi_a(t) = \varphi^a_+ e^{-i\omega t} + \varphi^a_- e^{+i\omega t}. \f]
114 X selects \f$ \varphi^a_+ \f$ (absorption/forward), Y selects
115 \f$ \varphi^a_- \f$ (emission/backward).
116*/
117enum class dPsiType { X, Y };
118//! Whether the state is a bra or ket
119enum class StateType { bra, ket }; // lhs, rhs
120
121/*!
122 @brief Virtual base class for core-polarisation (RPA); computes dV corrections.
123 @details
124 Defines the interface for all RPA/core-polarisation methods. Concrete
125 implementations are @ref TDHF, @ref TDHFbasis, and @ref DiagramRPA.
126 See the @ref ExternalField namespace documentation for notation and physics.
127
128 @note
129 Stores a raw pointer to the external-field operator @p h passed at
130 construction. That operator must remain alive for the lifetime of this object.
131
132 ---
133
134 @note
135 For frequency-dependent operators, updating the operator frequency externally
136 will affect results. @ref solve_core() should be re-called after any such update.
137
138 ---
139
140 @note
141 Calling @ref solve_core() with a different freuqnecy will _only_ chnage the frequency used
142 to solve the TDHF/RPA equations. It wil **not** change the frequency of the operator itself.
143 For frequency-dependent operators, you must update the freuqency of the operator first.
144 See @ref DiracOperator::TensorOperator .
145 Since this class stores just a pointer to the operator, that's all you need to do.
146*/
148
149protected:
151 : m_h(h), m_rank(h->rank()), m_pi(h->parity()), m_imag(h->imaginaryQ()) {}
152
153protected:
155 double m_core_eps{1.0};
156 int m_core_its{0};
157 double m_core_omega{0.0};
158 int m_rank;
159 int m_pi;
160 bool m_imag;
161
162 double m_eta{0.35};
163 double m_eps{1.0e-15};
164
165public:
166 //! Returns eps (convergance) of last solve_core run
167 double last_eps() const { return m_core_eps; }
168 //! Returns its (# of iterations) of last solve_core run
169 double last_its() const { return m_core_its; }
170 //! Returns omega (frequency) of last solve_core run
171 double last_omega() const { return m_core_omega; }
172 //! Rank of the operator
173 int rank() const { return m_rank; }
174 //! Parity of the operator
175 int parity() const { return m_pi; }
176 //! Returns true if the operator is imaginary
177 bool imagQ() const { return m_imag; }
178
179 //! Convergance target
180 double &eps_target() { return m_eps; }
181 //! Convergance target
182 double eps_target() const { return m_eps; }
183
184 //! Damping factor; 0 means no damping. Must have 0 <= eta < 1
185 double eta() const { return m_eta; }
186 //! Set/update damping factor; 0 means no damping. Must have 0 <= eta < 1
187 void set_eta(double eta) {
188 assert(eta >= 0.0 && eta < 1 && "Must have 0 <= eta < 1");
189 m_eta = eta;
190 }
191
192 //! Returns RPA method
193 virtual Method method() const = 0;
194
195 /*!
196 @brief Solve for delta_V_pm self-consistently for all core orbitals at frequency omega.
197 @details
198 Iterates the RPA equations (TDHF or diagram, depending on the implementation)
199 until the correction \f$ \delta V_\pm(\omega) \f$ converges to within
200 eps_target(), or @p max_its iterations are reached.
201
202 @param omega External-field frequency \f$ \omega \f$ in atomic units.
203 Solved at \f$ |\omega| \f$: both components,
204 \f$ \delta V(\pm|\omega|) \f$, are found together,
205 and selected in dV().
206 @param max_its Maximum number of iterations.
207 - 0: no iterations; dV() returns 0.
208 - 1: single iteration; dV() returns lowest-order correction.
209 @param print If true, print convergence progress to stdout.
210
211 @note Does not update the frequency of the operator itself; for frequency-dependent
212 operators, update the operator frequency externally before calling.
213 */
214 virtual void solve_core(double omega, int max_its = 100,
215 bool print = true) = 0;
216
217 //! @brief Clears the internal state back to pre solve_core()
218 virtual void clear() = 0;
219
220 /*!
221 @brief Returns the reduced matrix element <n||dV||m> (see namespace doc).
222 @details
223
224 * conj = false: dV at +|omega|, the absorption (e^{-i omega t}) component;
225 * conj = true: dV at -|omega|, the emission component, which is the
226 Hermitian conjugate dV^dag.
227
228 Both are solved by solve_core, which takes
229 |omega|. The caller chooses: for a matrix element at the transition
230 frequency omega = e_n - e_m, conj = (omega < 0). For a sum over states or
231 a mixed state, conj follows the sign of the frequency the operator was
232 set at, not the energies of the pair of orbitals.
233
234 For a static solve the two coincide (exactly, including imaginary
235 operators), so conj does nothing there.
236 */
237 virtual double dV(const DiracSpinor &Fn, const DiracSpinor &Fm,
238 bool conj = false) const = 0;
239
240 //! @brief Returns [dV_pm * phi_m]_kappa: RHS of TDHF eq., projected onto kappa (see namespace doc)
241 virtual DiracSpinor dV_rhs(int kappa, const DiracSpinor &Fm,
242 bool conj = false) const {
243 // XXX Remove this implementation (make pure virtual) once j_L killed
244 (void)kappa;
245 (void)Fm;
246 (void)conj;
247 assert(false && "This should be made pure virtual");
248 return Fm;
249 }
250
251public:
252 CorePolarisation &operator=(const CorePolarisation &) = delete;
253 CorePolarisation(const CorePolarisation &) = default;
254 virtual ~CorePolarisation() = default;
255};
256
257//==============================================================================
258/*!
259 @brief Factory function to construct a core-polarisation (RPA) object.
260 @details
261 Parses @p method and returns a `std::unique_ptr<CorePolarisation>` of the
262 appropriate type. Returns nullptr if @p method is "none" or "false".
263
264 Supported methods (case-insensitive):
265 - `"TDHF"`: time-dependent Hartree-Fock (TDHF).
266 - `"basis"`: TDHF solved in a basis set.
267 - `"diagram"`: diagram RPA.
268 - `"none"`, `"false"`, `""`: no RPA; returns nullptr.
269
270 If the method string is not recognised, prints an error and defaults to none.
271
272 @param method String specifying the RPA method (see above).
273 @param h Pointer to the forward operator (\f$ t_+ \f$).
274 @param vhf Pointer to the Hartree-Fock object (provides core potential).
275 @param print If true, print a brief description of the chosen method.
276 @param basis Basis set for basis/diagram methods (ignored for TDHF).
277 @param identity Identifier string passed to DiagramRPA (e.g. for caching).
278
279 @return Unique pointer to the constructed CorePolarisation object, or
280 nullptr if RPA is disabled.
281
282 @warning An unrecognised @p method string triggers an error message and
283 falls through to no RPA rather than throwing.
284*/
285[[nodiscard]] std::unique_ptr<CorePolarisation>
286make_rpa(const std::string &method, const DiracOperator::TensorOperator *h,
287 const HF::HartreeFock *vhf, bool print = false,
288 const std::vector<DiracSpinor> &basis = {},
289 const std::string &identity = "");
290
291} // namespace ExternalField
General tensor operator (virtual base class); all single-particle (one-body) tenosor operators derive...
Definition TensorOperator.hpp:198
bool imaginaryQ() const
returns true if operator is imaginary (has imag MEs)
Definition TensorOperator.hpp:344
int parity() const
returns parity, as integer (+1 or -1)
Definition TensorOperator.hpp:350
int rank() const
Rank k of operator.
Definition TensorOperator.hpp:347
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:55
Virtual base class for core-polarisation (RPA); computes dV corrections.
Definition CorePolarisation.hpp:147
virtual void clear()=0
Clears the internal state back to pre solve_core()
virtual DiracSpinor dV_rhs(int kappa, const DiracSpinor &Fm, bool conj=false) const
Returns [dV_pm * phi_m]_kappa: RHS of TDHF eq., projected onto kappa (see namespace doc)
Definition CorePolarisation.hpp:241
int rank() const
Rank of the operator.
Definition CorePolarisation.hpp:173
double last_eps() const
Returns eps (convergance) of last solve_core run.
Definition CorePolarisation.hpp:167
double last_omega() const
Returns omega (frequency) of last solve_core run.
Definition CorePolarisation.hpp:171
void set_eta(double eta)
Set/update damping factor; 0 means no damping. Must have 0 <= eta < 1.
Definition CorePolarisation.hpp:187
double eps_target() const
Convergance target.
Definition CorePolarisation.hpp:182
bool imagQ() const
Returns true if the operator is imaginary.
Definition CorePolarisation.hpp:177
virtual Method method() const =0
Returns RPA method.
virtual double dV(const DiracSpinor &Fn, const DiracSpinor &Fm, bool conj=false) const =0
Returns the reduced matrix element <n||dV||m> (see namespace doc).
double eta() const
Damping factor; 0 means no damping. Must have 0 <= eta < 1.
Definition CorePolarisation.hpp:185
double last_its() const
Returns its (# of iterations) of last solve_core run.
Definition CorePolarisation.hpp:169
double & eps_target()
Convergance target.
Definition CorePolarisation.hpp:180
virtual void solve_core(double omega, int max_its=100, bool print=true)=0
Solve for delta_V_pm self-consistently for all core orbitals at frequency omega.
int parity() const
Parity of the operator.
Definition CorePolarisation.hpp:175
Uses TDHF to include core-polarisation (RPA) corrections to matrix elements of an external field oper...
Definition TDHF.hpp:58
Solves relativistic Hartree-Fock equations for core and valence. Optionally includes Breit and QED ef...
Definition HartreeFock.hpp:114
Dirac operators: TensorOperator base class and derived implementations for single-particle (one-body)...
Definition SecondOrder.hpp:7
Core-polarisation (RPA) corrections to matrix elements of an external field.
Definition MatrixElements.hpp:9
Method ParseMethod(std::string_view str)
Parses method string to Method enum (case-insensitive)
Definition CorePolarisation.hpp:90
StateType
Whether the state is a bra or ket.
Definition CorePolarisation.hpp:119
dPsiType
Selects the perturbed orbital: X = varphi_+, Y = varphi_-.
Definition CorePolarisation.hpp:117
Method
Available RPA/core-polarisation methods.
Definition CorePolarisation.hpp:87
std::unique_ptr< CorePolarisation > make_rpa(const std::string &method, const DiracOperator::TensorOperator *h, const HF::HartreeFock *vhf, bool print, const std::vector< DiracSpinor > &basis, const std::string &identity)
Factory function to construct a core-polarisation (RPA) object.
Definition CorePolarisation.cpp:19
Functions and classes for Hartree-Fock.
Definition CI_Integrals.hpp:16
bool ci_compare(std::string_view s1, std::string_view s2)
Case-insensitive string comparison; equivalent to tolower(s1) == tolower(s2).
Definition String.hpp:144