pairinteraction
A Rydberg Interaction Calculator
test_pair_potential.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2024 Pairinteraction Developers
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
6
7#include <Eigen/Dense>
8#include <filesystem>
9#include <fstream>
10#include <spdlog/spdlog.h>
11#include <vector>
12
13constexpr double HARTREE_IN_GHZ = 6579683.920501762;
14constexpr double UM_IN_ATOMIC_UNITS = 1 / 5.29177210544e-5;
15
16int main(int argc, char **argv) {
17 // Call the setup function to configure logging
19
20 // Create a database instance
21 std::filesystem::path database_dir;
22 std::filesystem::path data_dir;
23 bool download_missing = false;
24
25 for (int i = 1; i < argc; ++i) {
26 bool found = pairinteraction::args::parse_download_missing(i, argc, argv, download_missing);
27 if (!found) {
28 found = pairinteraction::args::parse_database_dir(i, argc, argv, database_dir);
29 }
30 if (!found) {
31 pairinteraction::args::parse_data_dir(i, argc, argv, data_dir);
32 }
33 }
34
35 pairinteraction::Database database(download_missing, true, database_dir);
36
37 // Create and diagonalize systems for two atoms
39 .set_species("Rb")
40 .restrict_quantum_number_n(58, 62)
41 .restrict_quantum_number_l(0, 2)
42 .create(database);
43 SPDLOG_INFO("Number of single-atom basis states: {}", basis->get_number_of_states());
44
46
47 // Create two-atom systems for different interatomic distances
49 .set_species("Rb")
53 .create(database);
54 double min_energy = 2 * ket->get_energy() - 3 / HARTREE_IN_GHZ;
55 double max_energy = 2 * ket->get_energy() + 3 / HARTREE_IN_GHZ;
56
58 .add(system)
59 .add(system)
60 .restrict_energy(min_energy, max_energy)
61 .restrict_quantum_number_m(1, 1)
62 .create();
63 SPDLOG_INFO("Number of two-atom basis states: {}", basis_pair->get_number_of_states());
64
65 std::vector<pairinteraction::SystemPair<double>> system_pairs;
66 system_pairs.reserve(5);
67 for (int i = 1; i < 6; ++i) {
68 pairinteraction::SystemPair<double> system(basis_pair);
69 system.set_distance_vector({0, 0, i * UM_IN_ATOMIC_UNITS});
70 system_pairs.push_back(std::move(system));
71 }
72
73 // Diagonalize the systems in parallel
75 pairinteraction::diagonalize(system_pairs, diagonalizer);
76
77 // Sort by the eigenenergies
78 for (auto &system : system_pairs) {
80 system.transform(sorter);
81 }
82
83 // Extract results
84 std::vector<std::string> kets;
85 Eigen::MatrixX<double> eigenenergies(system_pairs.size(), basis_pair->get_number_of_states());
86 Eigen::MatrixX<double> eigenstates(system_pairs.size(),
87 basis_pair->get_number_of_states() *
88 basis_pair->get_number_of_states());
89 Eigen::MatrixX<double> overlaps(system_pairs.size(), basis_pair->get_number_of_states());
90
91 kets.reserve(basis->get_number_of_states());
92 for (const auto &ket : *basis_pair) {
93 std::stringstream ss;
94 ss << *ket;
95 kets.push_back(ss.str());
96 }
97
98 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(system_pairs.size()); ++i) {
99 eigenenergies.row(i) = system_pairs[i].get_eigenenergies() * HARTREE_IN_GHZ;
100
102 system_pairs[i].get_eigenbasis()->get_coefficients().toDense().transpose();
103 eigenstates.row(i) = Eigen::Map<Eigen::VectorXd>(tmp.data(), tmp.size());
104
105 overlaps.row(i) = system_pairs[i].get_eigenbasis()->get_overlaps(ket, ket);
106 }
107
108 // Compare with reference data
109 bool success = true;
110
111 // Check kets
112 const std::filesystem::path reference_kets_file =
113 data_dir / "reference_pair_potential/kets.txt";
114 SPDLOG_INFO("Reference kets: {}", reference_kets_file.string());
115
116 std::vector<std::string> reference_kets;
117 std::string line;
118 std::ifstream stream(reference_kets_file);
119 if (!stream) {
120 SPDLOG_ERROR("Could not open reference kets file");
121 success = false;
122 } else {
123 reference_kets.reserve(basis_pair->get_number_of_states());
124 while (std::getline(stream, line)) {
125 reference_kets.push_back(line);
126 }
127 stream.close();
128 if (kets.size() != reference_kets.size()) {
129 SPDLOG_ERROR("Number of kets does not match reference data");
130 success = false;
131 } else if (kets != reference_kets) {
132 for (size_t i = 0; i < kets.size(); ++i) {
133 SPDLOG_DEBUG("Ket: {} vs {}, match: {}", kets[i], reference_kets[i],
134 kets[i] == reference_kets[i]);
135 }
136 SPDLOG_ERROR("Kets do not match reference data");
137 success = false;
138 }
139 }
140
141 // Check eigenenergies
142 const std::filesystem::path reference_eigenenergies_file =
143 data_dir / "reference_pair_potential/eigenenergies.txt";
144 SPDLOG_INFO("Reference eigenenergies: {}", reference_eigenenergies_file.string());
145
146 Eigen::MatrixXd reference_eigenenergies(system_pairs.size(),
147 basis_pair->get_number_of_states());
148 stream = std::ifstream(reference_eigenenergies_file);
149 if (!stream) {
150 SPDLOG_ERROR("Could not open reference eigenenergies file");
151 success = false;
152 } else {
153 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(system_pairs.size()); ++i) {
154 for (Eigen::Index j = 0;
155 j < static_cast<Eigen::Index>(basis_pair->get_number_of_states()); ++j) {
156 stream >> reference_eigenenergies(i, j);
157 }
158 }
159 stream.close();
160 if ((eigenenergies - reference_eigenenergies).array().abs().maxCoeff() >
161 100e-6) { // 100 kHz precision
162 for (Eigen::Index i = 0; i < eigenenergies.size(); ++i) {
163 SPDLOG_DEBUG("Eigenenergy: {} vs {}, delta: {}", eigenenergies(i),
164 reference_eigenenergies(i),
165 std::abs(eigenenergies(i) - reference_eigenenergies(i)));
166 }
167 SPDLOG_ERROR("Eigenenergies do not match reference data");
168 success = false;
169 }
170 }
171
172 // Check overlaps
173 const std::filesystem::path reference_overlaps_file =
174 data_dir / "reference_pair_potential/overlaps.txt";
175 SPDLOG_INFO("Reference overlaps: {}", reference_overlaps_file.string());
176
177 Eigen::MatrixXd reference_overlaps(system_pairs.size(), basis_pair->get_number_of_states());
178 stream = std::ifstream(reference_overlaps_file);
179 if (!stream) {
180 SPDLOG_ERROR("Could not open reference overlap file");
181 success = false;
182 } else {
183 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(system_pairs.size()); ++i) {
184 for (Eigen::Index j = 0;
185 j < static_cast<Eigen::Index>(basis_pair->get_number_of_states()); ++j) {
186 stream >> reference_overlaps(i, j);
187 }
188 }
189 stream.close();
190 if ((overlaps - reference_overlaps).array().abs().maxCoeff() > 1e-5) {
191 for (Eigen::Index i = 0; i < overlaps.size(); ++i) {
192 SPDLOG_DEBUG("Overlap: {} vs {}, delta: {}", overlaps(i), reference_overlaps(i),
193 std::abs(overlaps(i) - reference_overlaps(i)));
194 }
195 SPDLOG_ERROR("Overlaps do not match reference data");
196 success = false;
197 }
198 }
199
200 // Check eigenstates
201 // Because of degeneracies, checking the eigenstates against reference data is complicated.
202 // Thus, we only check their normalization and orthogonality.
203 Eigen::VectorXd cumulative_norm =
204 (eigenstates.adjoint().array() * eigenstates.transpose().array()).colwise().sum();
205 if (!cumulative_norm.isApprox(Eigen::VectorXd::Constant(5, 19), 1e-5)) {
206 SPDLOG_ERROR("Eigenvectors are not orthonormal.");
207 success = false;
208 }
209
210 return success ? 0 : 1;
211}
Builder class for creating BasisAtom objects.
BasisAtomCreator< Scalar > & set_species(const std::string &value)
BasisPairCreator< Scalar > & add(const SystemAtom< Scalar > &system_atom)
Builder class for creating KetAtom objects.
KetAtomCreator & set_quantum_number_n(int value)
std::shared_ptr< const KetAtom > create(Database &database) const
KetAtomCreator & set_quantum_number_l(double value)
KetAtomCreator & set_species(const std::string &value)
KetAtomCreator & set_quantum_number_m(double value)
Type & set_distance_vector(const std::array< real_t, 3 > &vector)
Definition: SystemPair.cpp:255
Sorting get_sorter(const std::vector< TransformationType > &labels) const override
Definition: System.cpp:139
System< Derived > & transform(const Transformation< scalar_t > &transformation)
Definition: System.cpp:158
Matrix< Type, Dynamic, Dynamic > MatrixX
bool parse_data_dir(int &i, int argc, char **const argv, std::filesystem::path &data_dir)
Definition: args.hpp:45
bool parse_database_dir(int &i, int argc, char **const argv, std::filesystem::path &database_dir)
Definition: args.hpp:21
bool parse_download_missing(int &i, int argc, char **const argv, bool &download_missing)
Definition: args.hpp:11
void setup()
Definition: setup.cpp:18
void diagonalize(std::initializer_list< std::reference_wrapper< Derived > > systems, const DiagonalizerInterface< typename Derived::scalar_t > &diagonalizer, std::optional< typename Derived::real_t > min_eigenenergy={}, std::optional< typename Derived::real_t > max_eigenenergy={}, double rtol=1e-6)
Definition: diagonalize.cpp:17
constexpr double HARTREE_IN_GHZ
int main(int argc, char **argv)
constexpr double UM_IN_ATOMIC_UNITS