pairinteraction
A Rydberg Interaction Calculator
test_starkmap.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 VOLT_PER_CM_IN_ATOMIC_UNITS = 1 / 5.14220675112e9;
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 a basis
39 .set_species("Rb")
43 .create(database);
44
46 .set_species("Rb")
47 .restrict_quantum_number_n(58, 62)
48 .restrict_quantum_number_l(0, 2)
49 .create(database);
50
51 SPDLOG_INFO("Number of basis states: {}", basis->get_number_of_states());
52
53 // Create systems for different values of the electric field
54 std::vector<pairinteraction::SystemAtom<double>> systems;
55 systems.reserve(11);
56 for (int i = 0; i < 11; ++i) {
59 systems.push_back(std::move(system));
60 }
61
62 // Diagonalize the systems in parallel
64 pairinteraction::diagonalize(systems, diagonalizer);
65
66 // Sort by the eigenenergies
67 for (auto &system : systems) {
68 auto sorter = system.get_sorter({pairinteraction::TransformationType::SORT_BY_ENERGY});
69 system.transform(sorter);
70 }
71
72 // Extract results
73 std::vector<std::string> kets;
74 Eigen::MatrixX<double> eigenenergies(systems.size(), basis->get_number_of_states());
75 Eigen::MatrixX<double> eigenstates(
76 systems.size(), basis->get_number_of_states() * basis->get_number_of_states());
77 Eigen::MatrixX<double> overlaps(systems.size(), basis->get_number_of_states());
78
79 kets.reserve(basis->get_number_of_states());
80 for (const auto &ket : *basis) {
81 std::stringstream ss;
82 ss << *ket;
83 kets.push_back(ss.str());
84 }
85
86 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(systems.size()); ++i) {
87 eigenenergies.row(i) = systems[i].get_eigenenergies() * HARTREE_IN_GHZ;
88
90 systems[i].get_eigenbasis()->get_coefficients().toDense().transpose();
91 eigenstates.row(i) = Eigen::Map<Eigen::VectorXd>(tmp.data(), tmp.size());
92
93 overlaps.row(i) = systems[i].get_eigenbasis()->get_overlaps(ket);
94 }
95
96 // Compare with reference data
97 bool success = true;
98
99 // Check kets
100 const std::filesystem::path reference_kets_file = data_dir / "reference_stark_map/kets.txt";
101 SPDLOG_INFO("Reference kets: {}", reference_kets_file.string());
102
103 std::vector<std::string> reference_kets;
104 std::string line;
105 std::ifstream stream(reference_kets_file);
106 if (!stream) {
107 SPDLOG_ERROR("Could not open reference kets file");
108 success = false;
109 } else {
110 reference_kets.reserve(basis->get_number_of_states());
111 while (std::getline(stream, line)) {
112 reference_kets.push_back(line);
113 }
114 stream.close();
115 if (kets.size() != reference_kets.size()) {
116 SPDLOG_ERROR("Number of kets does not match reference data");
117 success = false;
118 } else if (kets != reference_kets) {
119 for (size_t i = 0; i < kets.size(); ++i) {
120 SPDLOG_DEBUG("Ket: {} vs {}, match: {}", kets[i], reference_kets[i],
121 kets[i] == reference_kets[i]);
122 }
123 SPDLOG_ERROR("Kets do not match reference data");
124 success = false;
125 }
126 }
127
128 // Check eigenenergies
129 const std::filesystem::path reference_eigenenergies_file =
130 data_dir / "reference_stark_map/eigenenergies.txt";
131 SPDLOG_INFO("Reference eigenenergies: {}", reference_eigenenergies_file.string());
132
133 Eigen::MatrixXd reference_eigenenergies(systems.size(), basis->get_number_of_states());
134 stream = std::ifstream(reference_eigenenergies_file);
135 if (!stream) {
136 SPDLOG_ERROR("Could not open reference eigenenergies file");
137 success = false;
138 } else {
139 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(systems.size()); ++i) {
140 for (Eigen::Index j = 0; j < static_cast<Eigen::Index>(basis->get_number_of_states());
141 ++j) {
142 stream >> reference_eigenenergies(i, j);
143 }
144 }
145 stream.close();
146 if ((eigenenergies - reference_eigenenergies).array().abs().maxCoeff() >
147 500e-6) { // 500 kHz precision
148 for (Eigen::Index i = 0; i < eigenenergies.size(); ++i) {
149 SPDLOG_DEBUG("Eigenenergy: {} vs {}, delta: {}", eigenenergies(i),
150 reference_eigenenergies(i),
151 std::abs(eigenenergies(i) - reference_eigenenergies(i)));
152 }
153 SPDLOG_ERROR("Eigenenergies do not match reference data");
154 success = false;
155 }
156 }
157
158 // Check overlaps
159 const std::filesystem::path reference_overlaps_file =
160 data_dir / "reference_stark_map/overlaps.txt";
161 SPDLOG_INFO("Reference overlaps: {}", reference_overlaps_file.string());
162
163 Eigen::MatrixXd reference_overlaps(systems.size(), basis->get_number_of_states());
164 stream = std::ifstream(reference_overlaps_file);
165 if (!stream) {
166 SPDLOG_ERROR("Could not open reference overlap file");
167 success = false;
168 } else {
169 for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(systems.size()); ++i) {
170 for (Eigen::Index j = 0; j < static_cast<Eigen::Index>(basis->get_number_of_states());
171 ++j) {
172 stream >> reference_overlaps(i, j);
173 }
174 }
175 stream.close();
176 if ((overlaps - reference_overlaps).array().abs().maxCoeff() > 5e-5) {
177 for (Eigen::Index i = 0; i < overlaps.size(); ++i) {
178 SPDLOG_DEBUG("Overlap: {} vs {}, delta: {}", overlaps(i), reference_overlaps(i),
179 std::abs(overlaps(i) - reference_overlaps(i)));
180 }
181 SPDLOG_ERROR("Overlaps do not match reference data");
182 success = false;
183 }
184 }
185
186 // Check eigenstates
187 // Because of degeneracies, checking the eigenstates against reference data is complicated.
188 // Thus, we only check their normalization and orthogonality.
189 Eigen::VectorXd cumulative_norm =
190 (eigenstates.adjoint().array() * eigenstates.transpose().array()).colwise().sum();
191 if (!cumulative_norm.isApprox(Eigen::VectorXd::Constant(11, 90), 5e-5)) {
192 SPDLOG_ERROR("Eigenvectors are not orthonormal.");
193 success = false;
194 }
195
196 return success ? 0 : 1;
197}
Builder class for creating BasisAtom objects.
BasisAtomCreator< Scalar > & set_species(const std::string &value)
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_electric_field(const std::array< real_t, 3 > &field)
Definition: SystemAtom.cpp:26
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 VOLT_PER_CM_IN_ATOMIC_UNITS