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