ampsci
High-precision calculations for one- and two-valence atomic systems
CSF.hpp
1#pragma once
2#include "LinAlg/include.hpp"
3#include "Wavefunction/DiracSpinor.hpp"
4#include <array>
5#include <optional>
6#include <utility>
7#include <vector>
8
9namespace CI {
10
11//==============================================================================
12/*!
13 @brief Two-electron configuration state function (CSF).
14 @details
15 A CSF is an antisymmetrised two-electron basis state with definite total
16 angular momentum (\f$ J^2 \f$, \f$ J_z \f$) and parity,
17 built from a pair of single-particle
18 relativistic orbitals. Only two-electron CSFs are implemented.
19
20 Each CSF2 stores the indices of its two constituent orbitals (always sorted
21 to avoid double-counting) and the total parity, which is the product of the
22 parities of the two single-particle states.
23
24 @note The orbital pair is stored as a sorted array of DiracSpinor::Index
25 (uint16_t) rather than DiracSpinor references, so CSF2 objects are
26 cheap to copy and store. There is a limit to maximum n<=256 - see @ref Angular::nk_to_index
27*/
28class CSF2 {
29 int m_parity;
30
31public:
32 // nb: array of states is always sorted
33 std::array<DiracSpinor::Index, 2> states;
34
35 CSF2(const DiracSpinor &a, const DiracSpinor &b);
36
37 //! Index (nk_index) of the ith constituent orbital (i = 0 or 1)
38 DiracSpinor::Index state(std::size_t i) const;
39
40 friend bool operator==(const CSF2 &A, const CSF2 &B);
41 friend bool operator!=(const CSF2 &A, const CSF2 &B);
42
43 /*!
44 @brief Returns the number of orbitals that differ between two CSFs (0, 1,
45 or 2).
46 @details
47 Used to select the appropriate Slater-Condon rule when evaluating CI matrix
48 elements: 0 -- diagonal; 1 -- single substitution; 2 -- double
49 substitution; >2 -- zero by orthogonality.
50 */
51 static int num_different(const CSF2 &A, const CSF2 &B);
52
53 /*!
54 @brief For two CSFs differing by exactly one orbital, returns {n, a} where
55 @p V contains orbital n and @p X contains orbital a.
56 @details
57 Identifies the "particle" index n (in @p V but not @p X) and the "hole"
58 index a (in @p X but not @p V), as needed to apply the single-substitution
59 Slater-Condon rule: \f$ \langle V | \hat{O} | X \rangle \f$ where
60 \f$ |V\rangle = \hat{a}^\dag_n \hat{a}_a |X\rangle \f$.
61
62 @warning Result is undefined if @p V and @p X do not differ by exactly one
63 orbital; check with num_different() first.
64 */
65 static std::array<DiracSpinor::Index, 2> diff_1_na(const CSF2 &V,
66 const CSF2 &X);
67
68 /*!
69 @brief Returns the orbital index shared by two CSFs that differ by exactly
70 one orbital.
71 @details
72 Extracts the common (spectator) orbital needed for single-substitution
73 matrix elements.
74
75 @warning Assumes @p A and @p B differ by exactly one orbital.
76 */
77 static DiracSpinor::Index same_1_j(const CSF2 &A, const CSF2 &B);
78
79 //! Parity of the CSF, +/-1
80 int parity() const;
81
82 //! Single-particle configuration as a string, in relativistic or non-rel form
83 std::string config(bool relativistic = false) const;
84};
85
86//==============================================================================
87/*!
88 @brief Forms all two-electron CSFs with given total J and parity.
89 @details
90 Iterates over all pairs of single-particle states in @p cisp_basis and
91 retains those whose angular momenta can be coupled to total \f$ J = \f$
92 @p twoJ /2 and whose combined parity equals @p parity. Duplicate pairs are
93 excluded by construction.
94
95 @param twoJ Twice the total angular momentum 2J.
96 @param parity Total parity: +1 (even) or -1 (odd).
97 @param cisp_basis Single-particle basis from which CSFs are constructed.
98 @return Sorted list of all valid two-electron CSFs for the given J and parity.
99*/
100std::vector<CSF2> form_CSFs(int twoJ, int parity,
101 const std::vector<DiracSpinor> &cisp_basis);
102
103//==============================================================================
104/*!
105 @brief Configuration metadata for a single CI level.
106 @details
107 Stores identifying information derived after solving the CI eigenvalue
108 problem: the dominant non-relativistic configuration label, the squared CI
109 coefficient of that configuration, and approximate good quantum numbers
110 (g_J factor, L, S) where they can be assigned.
111
112 Fields are left at their default (empty/negative) values if not yet computed;
113 call @ref PsiJPi::update_config_info() to populate them.
114*/
116 //! Dominant configuration label (typically non-relativistic notation)
117 std::string config{};
118 //! Squared CI coefficient of the dominant configuration (or sum over non-rel degenerates)
119 double ci2{0.0};
120 double gJ{0.0};
121 //! Approximate orbital angular momentum L (-1 if not assigned)
122 double L{-1.0};
123 //! Twice the approximate spin S (-1 if not assigned)
124 double twoS{-1.0};
125};
126
127//==============================================================================
128/*!
129 @brief Container for CI solutions in a single (J, parity) sector.
130 @details
131 Holds the complete set of configuration state functions and the results of
132 the CI diagonalisation for a fixed total angular momentum J and parity.
133
134 Construction builds the CSF basis via form_CSFs() but does not solve the
135 eigenvalue problem; call solve() separately after constructing the CI
136 Hamiltonian matrix. Configuration labels are not set automatically -- call
137 update_config_info() for each solution after solving.
138
139 @note Only two-electron (two-particle) systems are supported.
140*/
141class PsiJPi {
142
143 int m_twoj{-1};
144 int m_pi{0};
145
146 // Number of solutions stored:
147 std::size_t m_num_solutions{0};
148 // List of CSFs
149 std::vector<CSF2> m_CSFs{};
150 // Energy, and CI expansion coeficients
151 std::pair<LinAlg::Vector<double>, LinAlg::Matrix<double>> m_Solution{};
152 std::vector<ConfigInfo> m_Info{};
153
154public:
155 /*!
156 @brief Constructs the CSF basis for the given J and parity; does not solve.
157 @details
158 Calls form_CSFs() to build the list of two-electron CSFs. The eigenvalue
159 problem is not solved until solve() is called with the CI Hamiltonian.
160
161 @param twoJ Twice the total angular momentum 2J.
162 @param pi Total parity: +1 or -1.
163 @param cisp_basis Single-particle basis used to construct the CSFs.
164 */
165 PsiJPi(int twoJ, int pi, const std::vector<DiracSpinor> &cisp_basis)
166 : m_twoj(twoJ), m_pi(pi), m_CSFs(form_CSFs(twoJ, pi, cisp_basis)) {}
167
168 PsiJPi() {}
169
170 /*!
171 @brief Solves the CI eigenvalue problem for the given Hamiltonian matrix.
172 @details
173 Diagonalises @p Hci and stores the resulting eigenvalues and eigenvectors.
174 Does not populate ConfigInfo; call update_config_info() separately.
175
176 - If @p num_solutions > 0, finds only the lowest @p num_solutions eigenpairs.
177 - If @p all_below is set, finds all eigenpairs with energy below that value
178 (in cm^-1); @p num_solutions is then ignored.
179 - If both are unset (or @p num_solutions <= 0), all eigenpairs are computed.
180
181 @param Hci CI Hamiltonian matrix in the CSF basis.
182 @param num_solutions Number of lowest solutions to find [0 = all].
183 @param all_below If set, find all solutions below this energy (cm^-1).
184 */
185 void solve(const LinAlg::Matrix<double> &Hci, int num_solutions = 0,
186 std::optional<double> all_below = {});
187
188 //! Set configuration info for the ith solution (must be called manually after solve())
189 void update_config_info(std::size_t i, const ConfigInfo &info);
190
191 //! Full list of CSFs spanning this (J, parity) sector
192 const std::vector<CSF2> &CSFs() const;
193
194 //! Returns reference to the ith CSF
195 const CSF2 &CSF(std::size_t i) const;
196
197 //! Energy of the ith CI solution (atomic units)
198 double energy(std::size_t i) const;
199
200 //! CI expansion coefficients for the ith solution (one per CSF)
201 LinAlg::View<const double> coefs(std::size_t i) const;
202
203 //! CI coefficient for the ith solution corresponding to the jth CSF
204 double coef(std::size_t i, std::size_t j) const;
205
206 //! Parity of the sector (+/-1)
207 int parity() const;
208
209 //! Twice the total angular momentum 2J for this sector
210 int twoJ() const;
211
212 //! Number of CI solutions currently stored
213 std::size_t num_solutions() const;
214
215 //! Configuration info for the ith solution (must have been set via update_config_info())
216 const ConfigInfo &info(std::size_t i) const;
217};
218
219} // namespace CI
Two-electron configuration state function (CSF).
Definition CSF.hpp:28
static DiracSpinor::Index same_1_j(const CSF2 &A, const CSF2 &B)
Returns the orbital index shared by two CSFs that differ by exactly one orbital.
Definition CSF.cpp:55
DiracSpinor::Index state(std::size_t i) const
Index (nk_index) of the ith constituent orbital (i = 0 or 1)
Definition CSF.cpp:17
std::string config(bool relativistic=false) const
Single-particle configuration as a string, in relativistic or non-rel form.
Definition CSF.cpp:70
int parity() const
Parity of the CSF, +/-1.
Definition CSF.cpp:68
static int num_different(const CSF2 &A, const CSF2 &B)
Returns the number of orbitals that differ between two CSFs (0, 1, or 2).
Definition CSF.cpp:30
static std::array< DiracSpinor::Index, 2 > diff_1_na(const CSF2 &V, const CSF2 &X)
For two CSFs differing by exactly one orbital, returns {n, a} where V contains orbital n and X contai...
Definition CSF.cpp:41
Container for CI solutions in a single (J, parity) sector.
Definition CSF.hpp:141
void solve(const LinAlg::Matrix< double > &Hci, int num_solutions=0, std::optional< double > all_below={})
Solves the CI eigenvalue problem for the given Hamiltonian matrix.
Definition CSF.cpp:128
double energy(std::size_t i) const
Energy of the ith CI solution (atomic units)
Definition CSF.cpp:166
PsiJPi(int twoJ, int pi, const std::vector< DiracSpinor > &cisp_basis)
Constructs the CSF basis for the given J and parity; does not solve.
Definition CSF.hpp:165
LinAlg::View< const double > coefs(std::size_t i) const
CI expansion coefficients for the ith solution (one per CSF)
Definition CSF.cpp:172
const ConfigInfo & info(std::size_t i) const
Configuration info for the ith solution (must have been set via update_config_info())
Definition CSF.cpp:193
std::size_t num_solutions() const
Number of CI solutions currently stored.
Definition CSF.cpp:190
const CSF2 & CSF(std::size_t i) const
Returns reference to the ith CSF.
Definition CSF.cpp:163
double coef(std::size_t i, std::size_t j) const
CI coefficient for the ith solution corresponding to the jth CSF.
Definition CSF.cpp:178
const std::vector< CSF2 > & CSFs() const
Full list of CSFs spanning this (J, parity) sector.
Definition CSF.cpp:160
int twoJ() const
Twice the total angular momentum 2J for this sector.
Definition CSF.cpp:187
void update_config_info(std::size_t i, const ConfigInfo &info)
Set configuration info for the ith solution (must be called manually after solve())
Definition CSF.cpp:154
int parity() const
Parity of the sector (+/-1)
Definition CSF.cpp:184
Stores radial Dirac spinor: F_nk = (f, g)
Definition DiracSpinor.hpp:42
Matrix class; row-major.
Definition Matrix.hpp:39
Proved a "view" onto an array.
Definition Matrix.ipp:7
Functions and classes for Configuration Interaction calculations.
Definition CI_Integrals.cpp:11
double L
Approximate orbital angular momentum L (-1 if not assigned)
Definition CSF.hpp:122
double ci2
Squared CI coefficient of the dominant configuration (or sum over non-rel degenerates)
Definition CSF.hpp:119
double twoS
Twice the approximate spin S (-1 if not assigned)
Definition CSF.hpp:124
std::string config
Dominant configuration label (typically non-relativistic notation)
Definition CSF.hpp:117
std::vector< CSF2 > form_CSFs(int twoJ, int parity, const std::vector< DiracSpinor > &cisp_basis)
Forms all two-electron CSFs with given total J and parity.
Definition CSF.cpp:86
Configuration metadata for a single CI level.
Definition CSF.hpp:115