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, 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, "none") ? Method::none :
102 qip::ci_compare(str, "false") ? Method::none :
103 qip::ci_compare(str, "") ? Method::none :
104 Method::Error;
105}
106
107/*!
108 @brief Selects the perturbed orbital: X = varphi_+, Y = varphi_-.
109 @details
110 Corresponds to the two first-order corrections to a core orbital,
111 \f[ \delta\phi_a(t) = \varphi^a_+ e^{-i\omega t} + \varphi^a_- e^{+i\omega t}. \f]
112 X selects \f$ \varphi^a_+ \f$ (absorption/forward), Y selects
113 \f$ \varphi^a_- \f$ (emission/backward).
114*/
115enum class dPsiType { X, Y };
116//! Whether the state is a bra or ket
117enum class StateType { bra, ket }; // lhs, rhs
118
119/*!
120 @brief Virtual base class for core-polarisation (RPA); computes dV corrections.
121 @details
122 Defines the interface for all RPA/core-polarisation methods. Concrete
123 implementations are @ref TDHF, @ref TDHFbasis, and @ref DiagramRPA.
124 See the @ref ExternalField namespace documentation for notation and physics.
125
126 @note
127 Stores a raw pointer to the external-field operator @p h passed at
128 construction. That operator must remain alive for the lifetime of this object.
129
130 ---
131
132 @note
133 For frequency-dependent operators, updating the operator frequency externally
134 will affect results. @ref solve_core() should be re-called after any such update.
135
136 ---
137
138 @note
139 Calling @ref solve_core() with a different freuqnecy will _only_ chnage the frequency used
140 to solve the TDHF/RPA equations. It wil **not** change the frequency of the operator itself.
141 For frequency-dependent operators, you must update the freuqency of the operator first.
142 See @ref DiracOperator::TensorOperator .
143 Since this class stores just a pointer to the operator, that's all you need to do.
144*/
146
147protected:
149 : m_h(h), m_rank(h->rank()), m_pi(h->parity()), m_imag(h->imaginaryQ()) {}
150
151protected:
153 double m_core_eps{1.0};
154 int m_core_its{0};
155 double m_core_omega{0.0};
156 int m_rank;
157 int m_pi;
158 bool m_imag;
159
160 double m_eta{0.4};
161 double m_eps{1.0e-10};
162
163public:
164 //! Returns eps (convergance) of last solve_core run
165 double last_eps() const { return m_core_eps; }
166 //! Returns its (# of iterations) of last solve_core run
167 double last_its() const { return m_core_its; }
168 //! Returns omega (frequency) of last solve_core run
169 double last_omega() const { return m_core_omega; }
170 //! Rank of the operator
171 int rank() const { return m_rank; }
172 //! Parity of the operator
173 int parity() const { return m_pi; }
174 //! Returns true if the operator is imaginary
175 bool imagQ() const { return m_imag; }
176
177 //! Convergance target
178 double &eps_target() { return m_eps; }
179 //! Convergance target
180 double eps_target() const { return m_eps; }
181
182 //! Damping factor; 0 means no damping. Must have 0 <= eta < 1
183 double eta() const { return m_eta; }
184 //! Set/update damping factor; 0 means no damping. Must have 0 <= eta < 1
185 void set_eta(double eta) {
186 assert(eta >= 0.0 && eta < 1 && "Must have 0 <= eta < 1");
187 m_eta = eta;
188 }
189
190 //! Returns RPA method
191 virtual Method method() const = 0;
192
193 /*!
194 @brief Solve for delta_V_pm self-consistently for all core orbitals at frequency omega.
195 @details
196 Iterates the RPA equations (TDHF or diagram, depending on the implementation)
197 until the correction \f$ \delta V_\pm(\omega) \f$ converges to within
198 eps_target(), or @p max_its iterations are reached.
199
200 @param omega External-field frequency \f$ \omega \f$ in atomic units.
201 May be zero (static limit) or negative.
202 @param max_its Maximum number of iterations.
203 - 0: no iterations; dV() returns 0.
204 - 1: single iteration; dV() returns lowest-order correction.
205 @param print If true, print convergence progress to stdout.
206
207 @note Does not update the frequency of the operator itself; for frequency-dependent
208 operators, update the operator frequency externally before calling.
209 */
210 virtual void solve_core(double omega, int max_its = 100,
211 bool print = true) = 0;
212
213 //! @brief Clears the internal state back to pre solve_core()
214 virtual void clear() = 0;
215
216 //! @brief Returns reduced matrix element <n||dV_pm||m> (see namespace doc for dV_pm)
217 virtual double dV(const DiracSpinor &Fn, const DiracSpinor &Fm) const = 0;
218
219 //! @brief Returns [dV_pm * phi_m]_kappa: RHS of TDHF eq., projected onto kappa (see namespace doc)
220 virtual DiracSpinor dV_rhs(int kappa, const DiracSpinor &Fm,
221 bool conj = false) const {
222 // XXX Remove this implementation (make pure virtual) once j_L killed
223 (void)kappa;
224 (void)Fm;
225 (void)conj;
226 assert(false && "This should be made pure virtual");
227 return Fm;
228 }
229
230public:
231 CorePolarisation &operator=(const CorePolarisation &) = delete;
232 CorePolarisation(const CorePolarisation &) = default;
233 virtual ~CorePolarisation() = default;
234};
235
236//==============================================================================
237/*!
238 @brief Factory function to construct a core-polarisation (RPA) object.
239 @details
240 Parses @p method and returns a `std::unique_ptr<CorePolarisation>` of the
241 appropriate type. Returns nullptr if @p method is "none" or "false".
242
243 Supported methods (case-insensitive):
244 - `"TDHF"`: time-dependent Hartree-Fock (TDHF).
245 - `"basis"`: TDHF solved in a basis set.
246 - `"diagram"`: diagram RPA.
247 - `"none"`, `"false"`, `""`: no RPA; returns nullptr.
248
249 If the method string is not recognised, prints an error and defaults to none.
250
251 @param method String specifying the RPA method (see above).
252 @param h Pointer to the forward operator (\f$ t_+ \f$).
253 @param vhf Pointer to the Hartree-Fock object (provides core potential).
254 @param print If true, print a brief description of the chosen method.
255 @param basis Basis set for basis/diagram methods (ignored for TDHF).
256 @param identity Identifier string passed to DiagramRPA (e.g. for caching).
257 @param h_minus Pointer to the backward operator (\f$ t_- \f$); if nullptr
258 (default), @p h is used for both. See @ref TDHF constructor
259 for when this is needed.
260
261 @return Unique pointer to the constructed CorePolarisation object, or
262 nullptr if RPA is disabled.
263
264 @warning An unrecognised @p method string triggers an error message and
265 falls through to no RPA rather than throwing.
266*/
267[[nodiscard]] std::unique_ptr<CorePolarisation>
268make_rpa(const std::string &method, const DiracOperator::TensorOperator *h,
269 const HF::HartreeFock *vhf, bool print = false,
270 const std::vector<DiracSpinor> &basis = {},
271 const std::string &identity = "",
272 const DiracOperator::TensorOperator *h_minus = nullptr);
273
274} // 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:333
int parity() const
returns parity, as integer (+1 or -1)
Definition TensorOperator.hpp:339
int rank() const
Rank k of operator.
Definition TensorOperator.hpp:336
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:44
Virtual base class for core-polarisation (RPA); computes dV corrections.
Definition CorePolarisation.hpp:145
virtual void clear()=0
Clears the internal state back to pre solve_core()
virtual double dV(const DiracSpinor &Fn, const DiracSpinor &Fm) const =0
Returns reduced matrix element <n||dV_pm||m> (see namespace doc for dV_pm)
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:220
int rank() const
Rank of the operator.
Definition CorePolarisation.hpp:171
double last_eps() const
Returns eps (convergance) of last solve_core run.
Definition CorePolarisation.hpp:165
double last_omega() const
Returns omega (frequency) of last solve_core run.
Definition CorePolarisation.hpp:169
void set_eta(double eta)
Set/update damping factor; 0 means no damping. Must have 0 <= eta < 1.
Definition CorePolarisation.hpp:185
double eps_target() const
Convergance target.
Definition CorePolarisation.hpp:180
bool imagQ() const
Returns true if the operator is imaginary.
Definition CorePolarisation.hpp:175
virtual Method method() const =0
Returns RPA method.
double eta() const
Damping factor; 0 means no damping. Must have 0 <= eta < 1.
Definition CorePolarisation.hpp:183
double last_its() const
Returns its (# of iterations) of last solve_core run.
Definition CorePolarisation.hpp:167
double & eps_target()
Convergance target.
Definition CorePolarisation.hpp:178
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:173
Uses TDHF to include core-polarisation (RPA) corrections to matrix elements of an external field oper...
Definition TDHF.hpp:59
Solves relativistic Hartree-Fock equations for core and valence. Optionally includes Breit and QED ef...
Definition HartreeFock.hpp:111
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:117
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, const DiracOperator::TensorOperator *h_minus)
Factory function to construct a core-polarisation (RPA) object.
Definition CorePolarisation.cpp:18
dPsiType
Selects the perturbed orbital: X = varphi_+, Y = varphi_-.
Definition CorePolarisation.hpp:115
Method
Available RPA/core-polarisation methods.
Definition CorePolarisation.hpp:87
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