pairinteraction
A Rydberg Interaction Calculator
Database.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2024 Pairinteraction Developers
2// SPDX-License-Identifier: LGPL-3.0-or-later
3
5
18
19#include <cpptrace/cpptrace.hpp>
20#include <duckdb.hpp>
21#include <fmt/core.h>
22#include <fmt/ranges.h>
23#include <fstream>
24#include <nlohmann/json.hpp>
25#include <oneapi/tbb.h>
26#include <spdlog/spdlog.h>
27#include <system_error>
28
29namespace pairinteraction {
30Database::Database() : Database(default_download_missing) {}
31
32Database::Database(bool download_missing)
33 : Database(download_missing, default_use_cache, default_database_dir) {}
34
36 : Database(default_download_missing, default_use_cache, std::move(database_dir)) {}
37
38Database::Database(bool download_missing, bool use_cache, std::filesystem::path database_dir)
39 : download_missing_(download_missing), use_cache_(use_cache),
40 database_dir_(std::move(database_dir)), db(std::make_unique<duckdb::DuckDB>(nullptr)),
41 con(std::make_unique<duckdb::Connection>(*db)) {
42
43 if (database_dir_.empty()) {
44 database_dir_ = default_database_dir;
45 }
46
47 // Ensure the database directory exists
48 if (!std::filesystem::exists(database_dir_)) {
49 std::filesystem::create_directories(database_dir_);
50 }
51 database_dir_ = std::filesystem::canonical(database_dir_);
52 if (!std::filesystem::is_directory(database_dir_)) {
53 throw std::filesystem::filesystem_error("Cannot access database", database_dir_.string(),
54 std::make_error_code(std::errc::not_a_directory));
55 }
56 SPDLOG_INFO("Using database directory: {}", database_dir_.string());
57
58 // Ensure that the config directory exists
60 if (!std::filesystem::exists(configdir)) {
61 std::filesystem::create_directories(configdir);
62 } else if (!std::filesystem::is_directory(configdir)) {
63 throw std::filesystem::filesystem_error("Cannot access config directory ",
64 configdir.string(),
65 std::make_error_code(std::errc::not_a_directory));
66 }
67
68 // Read in the database_repo_paths if a config file exists, otherwise use the default and
69 // write it to the config file
70 std::filesystem::path configfile = configdir / "database.json";
71 std::string database_repo_host;
72 std::vector<std::string> database_repo_paths;
73 if (std::filesystem::exists(configfile)) {
74 std::ifstream file(configfile);
75 nlohmann::json doc = nlohmann::json::parse(file, nullptr, false);
76
77 if (!doc.is_discarded() && doc.contains("hash") && doc.contains("database_repo_host") &&
78 doc.contains("database_repo_paths")) {
79 database_repo_host = doc["database_repo_host"].get<std::string>();
80 database_repo_paths = doc["database_repo_paths"].get<std::vector<std::string>>();
81
82 // If the values are not equal to the default values but the hash is consistent (i.e.,
83 // the user has not changed anything manually), clear the values so that they can be
84 // updated
85 if (database_repo_host != default_database_repo_host ||
86 database_repo_paths != default_database_repo_paths) {
87 std::size_t seed = 0;
88 utils::hash_combine(seed, database_repo_paths);
89 utils::hash_combine(seed, database_repo_host);
90 if (seed == doc["hash"].get<std::size_t>()) {
91 database_repo_host.clear();
92 database_repo_paths.clear();
93 } else {
94 SPDLOG_INFO("The database repository host and paths have been changed "
95 "manually. Thus, they will not be updated automatically. To reset "
96 "them, delete the file '{}'.",
97 configfile.string());
98 }
99 }
100 }
101 }
102
103 // Read in and store the default values if necessary
104 if (database_repo_host.empty() || database_repo_paths.empty()) {
105 SPDLOG_INFO("Updating the database repository host and paths:");
106
107 database_repo_host = default_database_repo_host;
108 database_repo_paths = default_database_repo_paths;
109 std::ofstream file(configfile);
110 nlohmann::json doc;
111
112 SPDLOG_INFO("* New host: {}", default_database_repo_host);
113 SPDLOG_INFO("* New paths: {}", fmt::join(default_database_repo_paths, ", "));
114
115 doc["database_repo_host"] = default_database_repo_host;
116 doc["database_repo_paths"] = database_repo_paths;
117
118 std::size_t seed = 0;
119 utils::hash_combine(seed, default_database_repo_paths);
120 utils::hash_combine(seed, default_database_repo_host);
121 doc["hash"] = seed;
122
123 file << doc.dump(4);
124 }
125
126 // Limit the memory usage of duckdb's buffer manager
127 {
128 auto result = con->Query("PRAGMA max_memory = '8GB';");
129 if (result->HasError()) {
130 throw cpptrace::runtime_error("Error setting the memory limit: " + result->GetError());
131 }
132 }
133
134 // Instantiate a database manager that provides access to database tables. If a table
135 // is outdated/not available locally, it will be downloaded if download_missing_ is true.
136 if (!download_missing_) {
137 database_repo_paths.clear();
138 }
139 downloader = std::make_unique<GitHubDownloader>();
140 manager = std::make_unique<ParquetManager>(database_dir_, *downloader, database_repo_paths,
141 *con, use_cache_);
142 manager->scan_local();
143 manager->scan_remote();
144
145 // Print versions of tables
146 std::istringstream iss(manager->get_versions_info());
147 for (std::string line; std::getline(iss, line);) {
148 SPDLOG_INFO(line);
149 }
150}
151
152Database::~Database() = default;
153
154std::shared_ptr<const KetAtom> Database::get_ket(const std::string &species,
155 const AtomDescriptionByParameters &description) {
156 // Check that the specifications are valid
157 if (!description.quantum_number_m.has_value()) {
158 throw std::invalid_argument("The quantum number m must be specified.");
159 }
160 if (description.quantum_number_f.has_value() &&
161 2 * description.quantum_number_f.value() !=
162 std::rint(2 * description.quantum_number_f.value())) {
163 throw std::invalid_argument("The quantum number f must be an integer or half-integer.");
164 }
165 if (description.quantum_number_f.has_value() && description.quantum_number_f.value() < 0) {
166 throw std::invalid_argument("The quantum number f must be positive.");
167 }
168 if (description.quantum_number_j.has_value() &&
169 2 * description.quantum_number_j.value() !=
170 std::rint(2 * description.quantum_number_j.value())) {
171 throw std::invalid_argument("The quantum number j must be an integer or half-integer.");
172 }
173 if (description.quantum_number_j.has_value() && description.quantum_number_j.value() < 0) {
174 throw std::invalid_argument("The quantum number j must be positive.");
175 }
176 if (description.quantum_number_m.has_value() &&
177 2 * description.quantum_number_m.value() !=
178 std::rint(2 * description.quantum_number_m.value())) {
179 throw std::invalid_argument("The quantum number m must be an integer or half-integer.");
180 }
181
182 // Describe the state
183 std::string where;
184 std::string separator;
185 if (description.energy.has_value()) {
186 // The following condition derives from demanding that quantum number n that corresponds to
187 // the energy "E_n = -1/(2*n^2)" is not off by more than 1 from the actual quantum number n,
188 // i.e., "sqrt(-1/(2*E_n)) - sqrt(-1/(2*E_{n-1})) = 1"
189 where += separator +
190 fmt::format("SQRT(-1/(2*energy)) BETWEEN {} AND {}",
191 std::sqrt(-1 / (2 * description.energy.value())) - 0.5,
192 std::sqrt(-1 / (2 * description.energy.value())) + 0.5);
193 separator = " AND ";
194 }
195 if (description.quantum_number_f.has_value()) {
196 where += separator + fmt::format("f = {}", description.quantum_number_f.value());
197 separator = " AND ";
198 }
199 if (description.parity != Parity::UNKNOWN) {
200 where += separator + fmt::format("parity = {}", fmt::streamed(description.parity));
201 separator = " AND ";
202 }
203 if (description.quantum_number_n.has_value()) {
204 where += separator + fmt::format("n = {}", description.quantum_number_n.value());
205 separator = " AND ";
206 }
207 if (description.quantum_number_nu.has_value()) {
208 where += separator +
209 fmt::format("nu BETWEEN {} AND {}", description.quantum_number_nu.value() - 0.5,
210 description.quantum_number_nu.value() + 0.5);
211 separator = " AND ";
212 }
213 if (description.quantum_number_nui.has_value()) {
214 where += separator +
215 fmt::format("exp_nui BETWEEN {} AND {}", description.quantum_number_nui.value() - 0.5,
216 description.quantum_number_nui.value() + 0.5);
217 separator = " AND ";
218 }
219 if (description.quantum_number_l.has_value()) {
220 where += separator +
221 fmt::format("exp_l BETWEEN {} AND {}", description.quantum_number_l.value() - 0.5,
222 description.quantum_number_l.value() + 0.5);
223 separator = " AND ";
224 }
225 if (description.quantum_number_s.has_value()) {
226 where += separator +
227 fmt::format("exp_s BETWEEN {} AND {}", description.quantum_number_s.value() - 0.5,
228 description.quantum_number_s.value() + 0.5);
229 separator = " AND ";
230 }
231 if (description.quantum_number_j.has_value()) {
232 where += separator +
233 fmt::format("exp_j BETWEEN {} AND {}", description.quantum_number_j.value() - 0.5,
234 description.quantum_number_j.value() + 0.5);
235 separator = " AND ";
236 }
237 if (description.quantum_number_l_ryd.has_value()) {
238 where += separator +
239 fmt::format("exp_l_ryd BETWEEN {} AND {}",
240 description.quantum_number_l_ryd.value() - 0.5,
241 description.quantum_number_l_ryd.value() + 0.5);
242 separator = " AND ";
243 }
244 if (description.quantum_number_j_ryd.has_value()) {
245 where += separator +
246 fmt::format("exp_j_ryd BETWEEN {} AND {}",
247 description.quantum_number_j_ryd.value() - 0.5,
248 description.quantum_number_j_ryd.value() + 0.5);
249 separator = " AND ";
250 }
251 if (separator.empty()) {
252 where += "FALSE";
253 }
254
255 std::string orderby;
256 separator = "";
257 if (description.energy.has_value()) {
258 orderby += separator +
259 fmt::format("(SQRT(-1/(2*energy)) - {})^2",
260 std::sqrt(-1 / (2 * description.energy.value())));
261 separator = " + ";
262 }
263 if (description.quantum_number_nu.has_value()) {
264 orderby += separator + fmt::format("(nu - {})^2", description.quantum_number_nu.value());
265 separator = " + ";
266 }
267 if (description.quantum_number_nui.has_value()) {
268 orderby +=
269 separator + fmt::format("(exp_nui - {})^2", description.quantum_number_nui.value());
270 separator = " + ";
271 }
272 if (description.quantum_number_l.has_value()) {
273 orderby += separator + fmt::format("(exp_l - {})^2", description.quantum_number_l.value());
274 separator = " + ";
275 }
276 if (description.quantum_number_s.has_value()) {
277 orderby += separator + fmt::format("(exp_s - {})^2", description.quantum_number_s.value());
278 separator = " + ";
279 }
280 if (description.quantum_number_j.has_value()) {
281 orderby += separator + fmt::format("(exp_j - {})^2", description.quantum_number_j.value());
282 separator = " + ";
283 }
284 if (description.quantum_number_l_ryd.has_value()) {
285 orderby +=
286 separator + fmt::format("(exp_l_ryd - {})^2", description.quantum_number_l_ryd.value());
287 separator = " + ";
288 }
289 if (description.quantum_number_j_ryd.has_value()) {
290 orderby +=
291 separator + fmt::format("(exp_j_ryd - {})^2", description.quantum_number_j_ryd.value());
292 separator = " + ";
293 }
294 if (separator.empty()) {
295 orderby += "id";
296 }
297
298 // Ask the database for the described state
299 auto result = con->Query(fmt::format(
300 R"(SELECT energy, f, parity, id, n, nu, exp_nui, std_nui, exp_l, std_l, exp_s, std_s,
301 exp_j, std_j, exp_l_ryd, std_l_ryd, exp_j_ryd, std_j_ryd, is_j_total_momentum, is_calculated_with_mqdt, underspecified_channel_contribution, {} AS order_val FROM '{}' WHERE {} ORDER BY order_val ASC LIMIT 2)",
302 orderby, manager->get_path(species, "states"), where));
303
304 if (result->HasError()) {
305 throw cpptrace::runtime_error("Error querying the database: " + result->GetError());
306 }
307
308 if (result->RowCount() == 0) {
309 throw std::invalid_argument("No state found.");
310 }
311
312 // Check the types of the columns
313 const auto &types = result->types;
314 const auto &labels = result->names;
315 const std::vector<duckdb::LogicalType> ref_types = {
316 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE, duckdb::LogicalType::BIGINT,
317 duckdb::LogicalType::BIGINT, duckdb::LogicalType::BIGINT, duckdb::LogicalType::DOUBLE,
318 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE,
319 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE,
320 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE,
321 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE,
322 duckdb::LogicalType::BOOLEAN, duckdb::LogicalType::BOOLEAN, duckdb::LogicalType::DOUBLE,
323 duckdb::LogicalType::DOUBLE};
324
325 for (size_t i = 0; i < types.size(); i++) {
326 if (types[i] != ref_types[i]) {
327 throw std::runtime_error("Wrong type for '" + labels[i] + "'. Got " +
328 types[i].ToString() + " but expected " +
329 ref_types[i].ToString());
330 }
331 }
332
333 // Get the first chunk of the results (the first chunk is sufficient as we need two rows at
334 // most)
335 auto chunk = result->Fetch();
336
337 // Check that the ket is uniquely specified
338 if (chunk->size() > 1) {
339 auto order_val_0 = duckdb::FlatVector::GetData<double>(chunk->data[21])[0];
340 auto order_val_1 = duckdb::FlatVector::GetData<double>(chunk->data[21])[1];
341
342 if (order_val_1 - order_val_0 <= order_val_0) {
343 // Get a list of possible kets
344 std::vector<KetAtom> kets;
345 kets.reserve(2);
346 for (size_t i = 0; i < 2; ++i) {
347 auto result_quantum_number_m = description.quantum_number_m.value();
348 auto result_energy = duckdb::FlatVector::GetData<double>(chunk->data[0])[i];
349 auto result_quantum_number_f =
350 duckdb::FlatVector::GetData<double>(chunk->data[1])[i];
351 auto result_parity = duckdb::FlatVector::GetData<int64_t>(chunk->data[2])[i];
353 duckdb::FlatVector::GetData<int64_t>(chunk->data[3])[i],
354 result_quantum_number_m);
355 auto result_quantum_number_n =
356 duckdb::FlatVector::GetData<int64_t>(chunk->data[4])[i];
357 auto result_quantum_number_nu =
358 duckdb::FlatVector::GetData<double>(chunk->data[5])[i];
359 auto result_quantum_number_nui_exp =
360 duckdb::FlatVector::GetData<double>(chunk->data[6])[i];
361 auto result_quantum_number_nui_std =
362 duckdb::FlatVector::GetData<double>(chunk->data[7])[i];
363 auto result_quantum_number_l_exp =
364 duckdb::FlatVector::GetData<double>(chunk->data[8])[i];
365 auto result_quantum_number_l_std =
366 duckdb::FlatVector::GetData<double>(chunk->data[9])[i];
367 auto result_quantum_number_s_exp =
368 duckdb::FlatVector::GetData<double>(chunk->data[10])[i];
369 auto result_quantum_number_s_std =
370 duckdb::FlatVector::GetData<double>(chunk->data[11])[i];
371 auto result_quantum_number_j_exp =
372 duckdb::FlatVector::GetData<double>(chunk->data[12])[i];
373 auto result_quantum_number_j_std =
374 duckdb::FlatVector::GetData<double>(chunk->data[13])[i];
375 auto result_quantum_number_l_ryd_exp =
376 duckdb::FlatVector::GetData<double>(chunk->data[14])[i];
377 auto result_quantum_number_l_ryd_std =
378 duckdb::FlatVector::GetData<double>(chunk->data[15])[i];
379 auto result_quantum_number_j_ryd_exp =
380 duckdb::FlatVector::GetData<double>(chunk->data[16])[i];
381 auto result_quantum_number_j_ryd_std =
382 duckdb::FlatVector::GetData<double>(chunk->data[17])[i];
383 auto result_is_j_total_momentum =
384 duckdb::FlatVector::GetData<bool>(chunk->data[18])[i];
385 auto result_is_calculated_with_mqdt =
386 duckdb::FlatVector::GetData<bool>(chunk->data[19])[i];
387 auto result_underspecified_channel_contribution =
388 duckdb::FlatVector::GetData<double>(chunk->data[20])[i];
389 kets.emplace_back(typename KetAtom::Private(), result_energy,
390 result_quantum_number_f, result_quantum_number_m,
391 static_cast<Parity>(result_parity), species,
392 result_quantum_number_n, result_quantum_number_nu,
393 result_quantum_number_nui_exp, result_quantum_number_nui_std,
394 result_quantum_number_l_exp, result_quantum_number_l_std,
395 result_quantum_number_s_exp, result_quantum_number_s_std,
396 result_quantum_number_j_exp, result_quantum_number_j_std,
397 result_quantum_number_l_ryd_exp, result_quantum_number_l_ryd_std,
398 result_quantum_number_j_ryd_exp, result_quantum_number_j_ryd_std,
399 result_is_j_total_momentum, result_is_calculated_with_mqdt,
400 result_underspecified_channel_contribution, *this, result_id);
401 }
402
403 // Throw an error with the possible kets
404 throw std::invalid_argument(
405 fmt::format("The ket is not uniquely specified. Possible kets are:\n{}\n{}",
406 fmt::streamed(kets[0]), fmt::streamed(kets[1])));
407 }
408 }
409
410 // Construct the state
411 auto result_quantum_number_m = description.quantum_number_m.value();
412 auto result_energy = duckdb::FlatVector::GetData<double>(chunk->data[0])[0];
413 auto result_quantum_number_f = duckdb::FlatVector::GetData<double>(chunk->data[1])[0];
414 auto result_parity = duckdb::FlatVector::GetData<int64_t>(chunk->data[2])[0];
416 duckdb::FlatVector::GetData<int64_t>(chunk->data[3])[0], result_quantum_number_m);
417 auto result_quantum_number_n = duckdb::FlatVector::GetData<int64_t>(chunk->data[4])[0];
418 auto result_quantum_number_nu = duckdb::FlatVector::GetData<double>(chunk->data[5])[0];
419 auto result_quantum_number_nui_exp = duckdb::FlatVector::GetData<double>(chunk->data[6])[0];
420 auto result_quantum_number_nui_std = duckdb::FlatVector::GetData<double>(chunk->data[7])[0];
421 auto result_quantum_number_l_exp = duckdb::FlatVector::GetData<double>(chunk->data[8])[0];
422 auto result_quantum_number_l_std = duckdb::FlatVector::GetData<double>(chunk->data[9])[0];
423 auto result_quantum_number_s_exp = duckdb::FlatVector::GetData<double>(chunk->data[10])[0];
424 auto result_quantum_number_s_std = duckdb::FlatVector::GetData<double>(chunk->data[11])[0];
425 auto result_quantum_number_j_exp = duckdb::FlatVector::GetData<double>(chunk->data[12])[0];
426 auto result_quantum_number_j_std = duckdb::FlatVector::GetData<double>(chunk->data[13])[0];
427 auto result_quantum_number_l_ryd_exp = duckdb::FlatVector::GetData<double>(chunk->data[14])[0];
428 auto result_quantum_number_l_ryd_std = duckdb::FlatVector::GetData<double>(chunk->data[15])[0];
429 auto result_quantum_number_j_ryd_exp = duckdb::FlatVector::GetData<double>(chunk->data[16])[0];
430 auto result_quantum_number_j_ryd_std = duckdb::FlatVector::GetData<double>(chunk->data[17])[0];
431 auto result_is_j_total_momentum = duckdb::FlatVector::GetData<bool>(chunk->data[18])[0];
432 auto result_is_calculated_with_mqdt = duckdb::FlatVector::GetData<bool>(chunk->data[19])[0];
433 auto result_underspecified_channel_contribution =
434 duckdb::FlatVector::GetData<double>(chunk->data[20])[0];
435
436 // Check the quantum number m
437 if (std::abs(result_quantum_number_m) > result_quantum_number_f) {
438 throw std::invalid_argument(
439 "The absolute value of the quantum number m must be less than or equal to f.");
440 }
441 if (result_quantum_number_f + result_quantum_number_m !=
442 std::rint(result_quantum_number_f + result_quantum_number_m)) {
443 throw std::invalid_argument(
444 "The quantum numbers f and m must be both either integers or half-integers.");
445 }
446
447#ifndef NDEBUG
448 // Check database consistency
449 if (result_is_j_total_momentum && result_quantum_number_f != result_quantum_number_j_exp) {
450 throw std::runtime_error("If j is the total momentum, f must be equal to j.");
451 }
452#endif
453
454 return std::make_shared<const KetAtom>(
455 typename KetAtom::Private(), result_energy, result_quantum_number_f,
456 result_quantum_number_m, static_cast<Parity>(result_parity), species,
457 result_quantum_number_n, result_quantum_number_nu, result_quantum_number_nui_exp,
458 result_quantum_number_nui_std, result_quantum_number_l_exp, result_quantum_number_l_std,
459 result_quantum_number_s_exp, result_quantum_number_s_std, result_quantum_number_j_exp,
460 result_quantum_number_j_std, result_quantum_number_l_ryd_exp,
461 result_quantum_number_l_ryd_std, result_quantum_number_j_ryd_exp,
462 result_quantum_number_j_ryd_std, result_is_j_total_momentum, result_is_calculated_with_mqdt,
463 result_underspecified_channel_contribution, *this, result_id);
464}
465
466template <typename Scalar>
467std::shared_ptr<const BasisAtom<Scalar>>
468Database::get_basis(const std::string &species, const AtomDescriptionByRanges &description,
469 std::vector<size_t> additional_ket_ids) {
470 // Describe the states
471 std::string where = "(";
472 std::string separator;
473 if (description.parity != Parity::UNKNOWN) {
474 where += separator + fmt::format("parity = {}", fmt::streamed(description.parity));
475 separator = " AND ";
476 }
477 if (description.range_energy.is_finite()) {
478 where += separator +
479 fmt::format("energy BETWEEN {} AND {}", description.range_energy.min(),
480 description.range_energy.max());
481 separator = " AND ";
482 }
483 if (description.range_quantum_number_f.is_finite()) {
484 where += separator +
485 fmt::format("f BETWEEN {} AND {}", description.range_quantum_number_f.min(),
486 description.range_quantum_number_f.max());
487 separator = " AND ";
488 }
489 if (description.range_quantum_number_m.is_finite()) {
490 where += separator +
491 fmt::format("m BETWEEN {} AND {}", description.range_quantum_number_m.min(),
492 description.range_quantum_number_m.max());
493 separator = " AND ";
494 }
495 if (description.range_quantum_number_n.is_finite()) {
496 where += separator +
497 fmt::format("n BETWEEN {} AND {}", description.range_quantum_number_n.min(),
498 description.range_quantum_number_n.max());
499 separator = " AND ";
500 }
501 if (description.range_quantum_number_nu.is_finite()) {
502 where += separator +
503 fmt::format("nu BETWEEN {} AND {}", description.range_quantum_number_nu.min(),
504 description.range_quantum_number_nu.max());
505 separator = " AND ";
506 }
507 if (description.range_quantum_number_nui.is_finite()) {
508 where += separator +
509 fmt::format("exp_nui BETWEEN {}-2*std_nui AND {}+2*std_nui",
510 description.range_quantum_number_nui.min(),
511 description.range_quantum_number_nui.max());
512 separator = " AND ";
513 }
514 if (description.range_quantum_number_l.is_finite()) {
515 where += separator +
516 fmt::format("exp_l BETWEEN {}-2*std_l AND {}+2*std_l",
517 description.range_quantum_number_l.min(),
518 description.range_quantum_number_l.max());
519 separator = " AND ";
520 }
521 if (description.range_quantum_number_s.is_finite()) {
522 where += separator +
523 fmt::format("exp_s BETWEEN {}-2*std_s AND {}+2*std_s",
524 description.range_quantum_number_s.min(),
525 description.range_quantum_number_s.max());
526 separator = " AND ";
527 }
528 if (description.range_quantum_number_j.is_finite()) {
529 where += separator +
530 fmt::format("exp_j BETWEEN {}-2*std_j AND {}+2*std_j",
531 description.range_quantum_number_j.min(),
532 description.range_quantum_number_j.max());
533 separator = " AND ";
534 }
535 if (description.range_quantum_number_l_ryd.is_finite()) {
536 where += separator +
537 fmt::format("exp_l_ryd BETWEEN {}-2*std_l_ryd AND {}+2*std_l_ryd",
538 description.range_quantum_number_l_ryd.min(),
539 description.range_quantum_number_l_ryd.max());
540 separator = " AND ";
541 }
542 if (description.range_quantum_number_j_ryd.is_finite()) {
543 where += separator +
544 fmt::format("exp_j_ryd BETWEEN {}-2*std_j_ryd AND {}+2*std_j_ryd",
545 description.range_quantum_number_j_ryd.min(),
546 description.range_quantum_number_j_ryd.max());
547 separator = " AND ";
548 }
549 if (separator.empty()) {
550 where += "FALSE";
551 }
552 where += ")";
553 if (!additional_ket_ids.empty()) {
554 where += fmt::format(" OR {} IN ({})", utils::SQL_TERM_FOR_LINEARIZED_ID_IN_DATABASE,
555 fmt::join(additional_ket_ids, ","));
556 }
557
558 // Create a table containing the described states
559 std::string id_of_kets;
560 {
561 auto result = con->Query(R"(SELECT UUID()::varchar)");
562 if (result->HasError()) {
563 throw cpptrace::runtime_error("Error selecting id_of_kets: " + result->GetError());
564 }
565 id_of_kets =
566 duckdb::FlatVector::GetData<duckdb::string_t>(result->Fetch()->data[0])[0].GetString();
567 }
568 {
569 auto result = con->Query(fmt::format(
570 R"(CREATE TEMP TABLE '{}' AS SELECT *, {} AS ketid FROM (
571 SELECT *,
572 UNNEST(list_transform(generate_series(0,(2*f)::bigint),
573 x -> x::double-f)) AS m FROM '{}'
574 ) WHERE {})",
576 manager->get_path(species, "states"), where));
577
578 if (result->HasError()) {
579 throw cpptrace::runtime_error("Error creating table: " + result->GetError());
580 }
581 }
582
583 // Ask the table for the extreme values of the quantum numbers
584 {
585 std::string select;
586 std::string separator;
587 if (description.range_energy.is_finite()) {
588 select += separator + "MIN(energy) AS min_energy, MAX(energy) AS max_energy";
589 separator = ", ";
590 }
591 if (description.range_quantum_number_f.is_finite()) {
592 select += separator + "MIN(f) AS min_f, MAX(f) AS max_f";
593 separator = ", ";
594 }
595 if (description.range_quantum_number_m.is_finite()) {
596 select += separator + "MIN(m) AS min_m, MAX(m) AS max_m";
597 separator = ", ";
598 }
599 if (description.range_quantum_number_n.is_finite()) {
600 select += separator + "MIN(n) AS min_n, MAX(n) AS max_n";
601 separator = ", ";
602 }
603 if (description.range_quantum_number_nu.is_finite()) {
604 select += separator + "MIN(nu) AS min_nu, MAX(nu) AS max_nu";
605 separator = ", ";
606 }
607 if (description.range_quantum_number_nui.is_finite()) {
608 select += separator + "MIN(exp_nui) AS min_nui, MAX(exp_nui) AS max_nui";
609 separator = ", ";
610 }
611 if (description.range_quantum_number_l.is_finite()) {
612 select += separator + "MIN(exp_l) AS min_l, MAX(exp_l) AS max_l";
613 separator = ", ";
614 }
615 if (description.range_quantum_number_s.is_finite()) {
616 select += separator + "MIN(exp_s) AS min_s, MAX(exp_s) AS max_s";
617 separator = ", ";
618 }
619 if (description.range_quantum_number_j.is_finite()) {
620 select += separator + "MIN(exp_j) AS min_j, MAX(exp_j) AS max_j";
621 separator = ", ";
622 }
623 if (description.range_quantum_number_l_ryd.is_finite()) {
624 select += separator + "MIN(exp_l_ryd) AS min_l_ryd, MAX(exp_l_ryd) AS max_l_ryd";
625 separator = ", ";
626 }
627 if (description.range_quantum_number_j_ryd.is_finite()) {
628 select += separator + "MIN(exp_j_ryd) AS min_j_ryd, MAX(exp_j_ryd) AS max_j_ryd";
629 separator = ", ";
630 }
631
632 if (!separator.empty()) {
633 auto result = con->Query(fmt::format(R"(SELECT {} FROM '{}')", select, id_of_kets));
634
635 if (result->HasError()) {
636 throw cpptrace::runtime_error("Error querying the database: " + result->GetError());
637 }
638
639 auto chunk = result->Fetch();
640
641 for (size_t i = 0; i < chunk->ColumnCount(); i++) {
642 if (duckdb::FlatVector::IsNull(chunk->data[i], 0)) {
643 throw std::invalid_argument("No state found.");
644 }
645 }
646
647 size_t idx = 0;
648 if (description.range_energy.is_finite()) {
649 auto min_energy = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
650 if (std::sqrt(-1 / (2 * min_energy)) - 1 >
651 std::sqrt(-1 / (2 * description.range_energy.min()))) {
652 SPDLOG_DEBUG("No state found with the requested minimum energy. Requested: {}, "
653 "found: {}.",
654 description.range_energy.min(), min_energy);
655 }
656 auto max_energy = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
657 if (std::sqrt(-1 / (2 * max_energy)) + 1 <
658 std::sqrt(-1 / (2 * description.range_energy.max()))) {
659 SPDLOG_DEBUG("No state found with the requested maximum energy. Requested: {}, "
660 "found: {}.",
661 description.range_energy.max(), max_energy);
662 }
663 }
664 if (description.range_quantum_number_f.is_finite()) {
665 auto min_f = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
666 if (min_f > description.range_quantum_number_f.min()) {
667 SPDLOG_DEBUG("No state found with the requested minimum quantum number f. "
668 "Requested: {}, found: {}.",
669 description.range_quantum_number_f.min(), min_f);
670 }
671 auto max_f = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
672 if (max_f < description.range_quantum_number_f.max()) {
673 SPDLOG_DEBUG("No state found with the requested maximum quantum number f. "
674 "Requested: {}, found: {}.",
675 description.range_quantum_number_f.max(), max_f);
676 }
677 }
678 if (description.range_quantum_number_m.is_finite()) {
679 auto min_m = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
680 if (min_m > description.range_quantum_number_m.min()) {
681 SPDLOG_DEBUG("No state found with the requested minimum quantum number m. "
682 "Requested: {}, found: {}.",
683 description.range_quantum_number_m.min(), min_m);
684 }
685 auto max_m = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
686 if (max_m < description.range_quantum_number_m.max()) {
687 SPDLOG_DEBUG("No state found with the requested maximum quantum number m. "
688 "Requested: {}, found: {}.",
689 description.range_quantum_number_m.max(), max_m);
690 }
691 }
692 if (description.range_quantum_number_n.is_finite()) {
693 auto min_n = duckdb::FlatVector::GetData<int64_t>(chunk->data[idx++])[0];
694 if (min_n > description.range_quantum_number_n.min()) {
695 SPDLOG_DEBUG("No state found with the requested minimum quantum number n. "
696 "Requested: {}, found: {}.",
697 description.range_quantum_number_n.min(), min_n);
698 }
699 auto max_n = duckdb::FlatVector::GetData<int64_t>(chunk->data[idx++])[0];
700 if (max_n < description.range_quantum_number_n.max()) {
701 SPDLOG_DEBUG("No state found with the requested maximum quantum number n. "
702 "Requested: {}, found: {}.",
703 description.range_quantum_number_n.max(), max_n);
704 }
705 }
706 if (description.range_quantum_number_nu.is_finite()) {
707 auto min_nu = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
708 if (min_nu - 1 > description.range_quantum_number_nu.min()) {
709 SPDLOG_DEBUG("No state found with the requested minimum quantum number nu. "
710 "Requested: {}, found: {}.",
711 description.range_quantum_number_nu.min(), min_nu);
712 }
713 auto max_nu = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
714 if (max_nu + 1 < description.range_quantum_number_nu.max()) {
715 SPDLOG_DEBUG("No state found with the requested maximum quantum number nu. "
716 "Requested: {}, found: {}.",
717 description.range_quantum_number_nu.max(), max_nu);
718 }
719 }
720 if (description.range_quantum_number_nui.is_finite()) {
721 auto min_nui = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
722 if (min_nui - 1 > description.range_quantum_number_nui.min()) {
723 SPDLOG_DEBUG("No state found with the requested minimum quantum number nui. "
724 "Requested: {}, found: {}.",
725 description.range_quantum_number_nui.min(), min_nui);
726 }
727 auto max_nui = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
728 if (max_nui + 1 < description.range_quantum_number_nui.max()) {
729 SPDLOG_DEBUG("No state found with the requested maximum quantum number nui. "
730 "Requested: {}, found: {}.",
731 description.range_quantum_number_nui.max(), max_nui);
732 }
733 }
734 if (description.range_quantum_number_l.is_finite()) {
735 auto min_l = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
736 if (min_l - 1 > description.range_quantum_number_l.min()) {
737 SPDLOG_DEBUG("No state found with the requested minimum quantum number l. "
738 "Requested: {}, found: {}.",
739 description.range_quantum_number_l.min(), min_l);
740 }
741 auto max_l = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
742 if (max_l + 1 < description.range_quantum_number_l.max()) {
743 SPDLOG_DEBUG("No state found with the requested maximum quantum number l. "
744 "Requested: {}, found: {}.",
745 description.range_quantum_number_l.max(), max_l);
746 }
747 }
748 if (description.range_quantum_number_s.is_finite()) {
749 auto min_s = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
750 if (min_s - 1 > description.range_quantum_number_s.min()) {
751 SPDLOG_DEBUG("No state found with the requested minimum quantum number s. "
752 "Requested: {}, found: {}.",
753 description.range_quantum_number_s.min(), min_s);
754 }
755 auto max_s = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
756 if (max_s + 1 < description.range_quantum_number_s.max()) {
757 SPDLOG_DEBUG("No state found with the requested maximum quantum number s. "
758 "Requested: {}, found: {}.",
759 description.range_quantum_number_s.max(), max_s);
760 }
761 }
762 if (description.range_quantum_number_j.is_finite()) {
763 auto min_j = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
764 if (min_j - 1 > description.range_quantum_number_j.min()) {
765 SPDLOG_DEBUG("No state found with the requested minimum quantum number j. "
766 "Requested: {}, found: {}.",
767 description.range_quantum_number_j.min(), min_j);
768 }
769 auto max_j = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
770 if (max_j + 1 < description.range_quantum_number_j.max()) {
771 SPDLOG_DEBUG("No state found with the requested maximum quantum number j. "
772 "Requested: {}, found: {}.",
773 description.range_quantum_number_j.max(), max_j);
774 }
775 }
776 if (description.range_quantum_number_l_ryd.is_finite()) {
777 auto min_l_ryd = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
778 if (min_l_ryd - 1 > description.range_quantum_number_l_ryd.min()) {
779 SPDLOG_DEBUG("No state found with the requested minimum quantum number l_ryd. "
780 "Requested: {}, found: {}.",
781 description.range_quantum_number_l_ryd.min(), min_l_ryd);
782 }
783 auto max_l_ryd = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
784 if (max_l_ryd + 1 < description.range_quantum_number_l_ryd.max()) {
785 SPDLOG_DEBUG("No state found with the requested maximum quantum number l_ryd. "
786 "Requested: {}, found: {}.",
787 description.range_quantum_number_l_ryd.max(), max_l_ryd);
788 }
789 }
790 if (description.range_quantum_number_j_ryd.is_finite()) {
791 auto min_j_ryd = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
792 if (min_j_ryd - 1 > description.range_quantum_number_j_ryd.min()) {
793 SPDLOG_DEBUG("No state found with the requested minimum quantum number j_ryd. "
794 "Requested: {}, found: {}.",
795 description.range_quantum_number_j_ryd.min(), min_j_ryd);
796 }
797 auto max_j_ryd = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
798 if (max_j_ryd + 1 < description.range_quantum_number_j_ryd.max()) {
799 SPDLOG_DEBUG("No state found with the requested maximum quantum number j_ryd. "
800 "Requested: {}, found: {}.",
801 description.range_quantum_number_j_ryd.max(), max_j_ryd);
802 }
803 }
804 }
805 }
806
807 // Ask the table for the described states
808 auto result = con->Query(fmt::format(
809 R"(SELECT energy, f, m, parity, ketid, n, nu, exp_nui, std_nui, exp_l, std_l,
810 exp_s, std_s, exp_j, std_j, exp_l_ryd, std_l_ryd, exp_j_ryd, std_j_ryd, is_j_total_momentum, is_calculated_with_mqdt, underspecified_channel_contribution FROM '{}' ORDER BY ketid ASC)",
811 id_of_kets));
812
813 if (result->HasError()) {
814 throw cpptrace::runtime_error("Error querying the database: " + result->GetError());
815 }
816
817 if (result->RowCount() == 0) {
818 throw std::invalid_argument("No state found.");
819 }
820
821 // Check the types of the columns
822 const auto &types = result->types;
823 const auto &labels = result->names;
824 const std::vector<duckdb::LogicalType> ref_types = {
825 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE,
826 duckdb::LogicalType::BIGINT, duckdb::LogicalType::BIGINT, duckdb::LogicalType::BIGINT,
827 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE,
828 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE,
829 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE,
830 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE, duckdb::LogicalType::DOUBLE,
831 duckdb::LogicalType::DOUBLE, duckdb::LogicalType::BOOLEAN, duckdb::LogicalType::BOOLEAN,
832 duckdb::LogicalType::DOUBLE};
833
834 for (size_t i = 0; i < types.size(); i++) {
835 if (types[i] != ref_types[i]) {
836 throw std::runtime_error("Wrong type for '" + labels[i] + "'. Got " +
837 types[i].ToString() + " but expected " +
838 ref_types[i].ToString());
839 }
840 }
841
842 // Construct the states
843 std::vector<std::shared_ptr<const KetAtom>> kets;
844 kets.reserve(result->RowCount());
845#ifndef NDEBUG
846 double last_energy = std::numeric_limits<double>::lowest();
847#endif
848
849 for (auto chunk = result->Fetch(); chunk; chunk = result->Fetch()) {
850
851 auto *chunk_energy = duckdb::FlatVector::GetData<double>(chunk->data[0]);
852 auto *chunk_quantum_number_f = duckdb::FlatVector::GetData<double>(chunk->data[1]);
853 auto *chunk_quantum_number_m = duckdb::FlatVector::GetData<double>(chunk->data[2]);
854 auto *chunk_parity = duckdb::FlatVector::GetData<int64_t>(chunk->data[3]);
855 auto *chunk_id = duckdb::FlatVector::GetData<int64_t>(chunk->data[4]);
856 auto *chunk_quantum_number_n = duckdb::FlatVector::GetData<int64_t>(chunk->data[5]);
857 auto *chunk_quantum_number_nu = duckdb::FlatVector::GetData<double>(chunk->data[6]);
858 auto *chunk_quantum_number_nui_exp = duckdb::FlatVector::GetData<double>(chunk->data[7]);
859 auto *chunk_quantum_number_nui_std = duckdb::FlatVector::GetData<double>(chunk->data[8]);
860 auto *chunk_quantum_number_l_exp = duckdb::FlatVector::GetData<double>(chunk->data[9]);
861 auto *chunk_quantum_number_l_std = duckdb::FlatVector::GetData<double>(chunk->data[10]);
862 auto *chunk_quantum_number_s_exp = duckdb::FlatVector::GetData<double>(chunk->data[11]);
863 auto *chunk_quantum_number_s_std = duckdb::FlatVector::GetData<double>(chunk->data[12]);
864 auto *chunk_quantum_number_j_exp = duckdb::FlatVector::GetData<double>(chunk->data[13]);
865 auto *chunk_quantum_number_j_std = duckdb::FlatVector::GetData<double>(chunk->data[14]);
866 auto *chunk_quantum_number_l_ryd_exp = duckdb::FlatVector::GetData<double>(chunk->data[15]);
867 auto *chunk_quantum_number_l_ryd_std = duckdb::FlatVector::GetData<double>(chunk->data[16]);
868 auto *chunk_quantum_number_j_ryd_exp = duckdb::FlatVector::GetData<double>(chunk->data[17]);
869 auto *chunk_quantum_number_j_ryd_std = duckdb::FlatVector::GetData<double>(chunk->data[18]);
870 auto *chunk_is_j_total_momentum = duckdb::FlatVector::GetData<bool>(chunk->data[19]);
871 auto *chunk_is_calculated_with_mqdt = duckdb::FlatVector::GetData<bool>(chunk->data[20]);
872 auto *chunk_underspecified_channel_contribution =
873 duckdb::FlatVector::GetData<double>(chunk->data[21]);
874
875 for (size_t i = 0; i < chunk->size(); i++) {
876
877#ifndef NDEBUG
878 // Check database consistency
879 if (chunk_is_j_total_momentum[i] &&
880 chunk_quantum_number_f[i] != chunk_quantum_number_j_exp[i]) {
881 throw std::runtime_error("If j is the total momentum, f must be equal to j.");
882 }
883 if (chunk_energy[i] < last_energy) {
884 throw std::runtime_error("The states are not sorted by energy.");
885 }
886 last_energy = chunk_energy[i];
887#endif
888
889 // Append a new state
890 kets.push_back(std::make_shared<const KetAtom>(
891 typename KetAtom::Private(), chunk_energy[i], chunk_quantum_number_f[i],
892 chunk_quantum_number_m[i], static_cast<Parity>(chunk_parity[i]), species,
893 chunk_quantum_number_n[i], chunk_quantum_number_nu[i],
894 chunk_quantum_number_nui_exp[i], chunk_quantum_number_nui_std[i],
895 chunk_quantum_number_l_exp[i], chunk_quantum_number_l_std[i],
896 chunk_quantum_number_s_exp[i], chunk_quantum_number_s_std[i],
897 chunk_quantum_number_j_exp[i], chunk_quantum_number_j_std[i],
898 chunk_quantum_number_l_ryd_exp[i], chunk_quantum_number_l_ryd_std[i],
899 chunk_quantum_number_j_ryd_exp[i], chunk_quantum_number_j_ryd_std[i],
900 chunk_is_j_total_momentum[i], chunk_is_calculated_with_mqdt[i],
901 chunk_underspecified_channel_contribution[i], *this, chunk_id[i]));
902 }
903 }
904
905 return std::make_shared<const BasisAtom<Scalar>>(typename BasisAtom<Scalar>::Private(),
906 std::move(kets), std::move(id_of_kets), *this);
907}
908
909template <typename Scalar>
910Eigen::SparseMatrix<Scalar, Eigen::RowMajor>
911Database::get_matrix_elements(std::shared_ptr<const BasisAtom<Scalar>> initial_basis,
912 std::shared_ptr<const BasisAtom<Scalar>> final_basis,
913 OperatorType type, int q) {
915
916 std::string specifier;
917 int kappa{};
918 switch (type) {
920 specifier = "matrix_elements_d";
921 kappa = 1;
922 break;
924 specifier = "matrix_elements_q";
925 kappa = 2;
926 break;
928 specifier = "matrix_elements_q0";
929 kappa = 0;
930 break;
932 specifier = "matrix_elements_o";
933 kappa = 3;
934 break;
936 specifier = "matrix_elements_mu";
937 kappa = 1;
938 break;
940 specifier = "energy";
941 kappa = 0;
942 break;
944 specifier = "identity";
945 kappa = 0;
946 break;
947 default:
948 throw std::invalid_argument("Unknown operator type.");
949 }
950
951 if (initial_basis->get_id_of_kets() != final_basis->get_id_of_kets()) {
952 throw std::invalid_argument(
953 "The initial and final basis must be expressed using the same kets.");
954 }
955 std::string id_of_kets = initial_basis->get_id_of_kets();
956 std::string cache_key = fmt::format("{}_{}_{}", specifier, q, id_of_kets);
957
958 if (!get_matrix_elements_cache().contains(cache_key)) {
959 Eigen::Index dim = initial_basis->get_number_of_kets();
960
961 std::vector<int> outerIndexPtr;
962 std::vector<int> innerIndices;
963 std::vector<real_t> values;
964
965 if (specifier == "identity") {
966 outerIndexPtr.reserve(dim + 1);
967 innerIndices.reserve(dim);
968 values.reserve(dim);
969
970 for (int i = 0; i < dim; i++) {
971 outerIndexPtr.push_back(static_cast<int>(innerIndices.size()));
972 innerIndices.push_back(i);
973 values.push_back(1);
974 }
975 outerIndexPtr.push_back(static_cast<int>(innerIndices.size()));
976
977 } else {
978 // Check that the specifications are valid
979 if (std::abs(q) > kappa) {
980 throw std::invalid_argument("Invalid q.");
981 }
982
983 // Ask the database for the operator
984 std::string species = initial_basis->get_species();
985 duckdb::unique_ptr<duckdb::MaterializedQueryResult> result;
986 if (specifier != "energy") {
987 result = con->Query(fmt::format(
988 R"(WITH s AS (
989 SELECT id, f, m, ketid FROM '{}'
990 ),
991 b AS (
992 SELECT MIN(f) AS min_f, MAX(f) AS max_f,
993 MIN(id) AS min_id, MAX(id) AS max_id
994 FROM s
995 ),
996 w_filtered AS (
997 SELECT *
998 FROM '{}'
999 WHERE kappa = {} AND q = {} AND
1000 f_initial BETWEEN (SELECT min_f FROM b) AND (SELECT max_f FROM b) AND
1001 f_final BETWEEN (SELECT min_f FROM b) AND (SELECT max_f FROM b)
1002 ),
1003 e_filtered AS (
1004 SELECT *
1005 FROM '{}'
1006 WHERE
1007 id_initial BETWEEN (SELECT min_id FROM b) AND (SELECT max_id FROM b) AND
1008 id_final BETWEEN (SELECT min_id FROM b) AND (SELECT max_id FROM b)
1009 )
1010 SELECT
1011 s2.ketid AS row,
1012 s1.ketid AS col,
1013 e.val*w.val AS val
1014 FROM e_filtered AS e
1015 JOIN s AS s1 ON e.id_initial = s1.id
1016 JOIN s AS s2 ON e.id_final = s2.id
1017 JOIN w_filtered AS w ON
1018 w.f_initial = s1.f AND w.m_initial = s1.m AND
1019 w.f_final = s2.f AND w.m_final = s2.m
1020 ORDER BY row ASC, col ASC)",
1021 id_of_kets, manager->get_path("misc", "wigner"), kappa, q,
1022 manager->get_path(species, specifier)));
1023 } else {
1024 result = con->Query(fmt::format(
1025 R"(SELECT ketid as row, ketid as col, energy as val FROM '{}' ORDER BY row ASC)",
1026 id_of_kets));
1027 }
1028
1029 if (result->HasError()) {
1030 throw cpptrace::runtime_error("Error querying the database: " + result->GetError());
1031 }
1032
1033 // Check the types of the columns
1034 const auto &types = result->types;
1035 const auto &labels = result->names;
1036 const std::vector<duckdb::LogicalType> ref_types = {duckdb::LogicalType::BIGINT,
1037 duckdb::LogicalType::BIGINT,
1038 duckdb::LogicalType::DOUBLE};
1039 for (size_t i = 0; i < types.size(); i++) {
1040 if (types[i] != ref_types[i]) {
1041 throw std::runtime_error("Wrong type for '" + labels[i] + "'.");
1042 }
1043 }
1044
1045 // Construct the matrix
1046 int num_entries = static_cast<int>(result->RowCount());
1047 outerIndexPtr.reserve(dim + 1);
1048 innerIndices.reserve(num_entries);
1049 values.reserve(num_entries);
1050
1051 int last_row = -1;
1052
1053 for (auto chunk = result->Fetch(); chunk; chunk = result->Fetch()) {
1055 auto *chunk_row = duckdb::FlatVector::GetData<int64_t>(chunk->data[0]);
1056 auto *chunk_col = duckdb::FlatVector::GetData<int64_t>(chunk->data[1]);
1057 auto *chunk_val = duckdb::FlatVector::GetData<double>(chunk->data[2]);
1059 for (size_t i = 0; i < chunk->size(); i++) {
1060 int row = final_basis->get_ket_index_from_id(chunk_row[i]);
1061 if (row != last_row) {
1062 if (row < last_row) {
1063 throw std::runtime_error("The rows are not sorted.");
1064 }
1065 for (; last_row < row; last_row++) {
1066 outerIndexPtr.push_back(static_cast<int>(innerIndices.size()));
1067 }
1069 innerIndices.push_back(initial_basis->get_ket_index_from_id(chunk_col[i]));
1070 values.push_back(chunk_val[i]);
1071 }
1072 }
1074 for (; last_row < dim + 1; last_row++) {
1075 outerIndexPtr.push_back(static_cast<int>(innerIndices.size()));
1076 }
1077 }
1078
1079 Eigen::Map<const Eigen::SparseMatrix<real_t, Eigen::RowMajor>> matrix_map(
1080 dim, dim, values.size(), outerIndexPtr.data(), innerIndices.data(), values.data());
1081
1082 // Cache the matrix
1083 get_matrix_elements_cache()[cache_key] = matrix_map;
1084 }
1085
1086 // Construct the operator and return it
1087 return final_basis->get_coefficients().adjoint() *
1088 get_matrix_elements_cache()[cache_key].template cast<Scalar>() *
1089 initial_basis->get_coefficients();
1090}
1091
1092bool Database::get_download_missing() const { return download_missing_; }
1093
1094bool Database::get_use_cache() const { return use_cache_; }
1095
1096std::filesystem::path Database::get_database_dir() const { return database_dir_; }
1098oneapi::tbb::concurrent_unordered_map<std::string, Eigen::SparseMatrix<double, Eigen::RowMajor>> &
1099Database::get_matrix_elements_cache() {
1100 static oneapi::tbb::concurrent_unordered_map<std::string,
1101 Eigen::SparseMatrix<double, Eigen::RowMajor>>
1102 matrix_elements_cache;
1103 return matrix_elements_cache;
1104}
1105
1107 return get_global_instance_without_checks(default_download_missing, default_use_cache,
1108 default_database_dir);
1109}
1110
1111Database &Database::get_global_instance(bool download_missing) {
1112 Database &database = get_global_instance_without_checks(download_missing, default_use_cache,
1113 default_database_dir);
1114 if (download_missing != database.download_missing_) {
1115 throw std::invalid_argument(
1116 "The 'download_missing' argument must not change between calls to the method.");
1117 }
1118 return database;
1119}
1122 if (database_dir.empty()) {
1123 database_dir = default_database_dir;
1124 }
1125 Database &database = get_global_instance_without_checks(default_download_missing,
1126 default_use_cache, database_dir);
1127 if (!std::filesystem::exists(database_dir) ||
1128 std::filesystem::canonical(database_dir) != database.database_dir_) {
1129 throw std::invalid_argument(
1130 "The 'database_dir' argument must not change between calls to the method.");
1131 }
1132 return database;
1134
1135Database &Database::get_global_instance(bool download_missing, bool use_cache,
1136 std::filesystem::path database_dir) {
1137 if (database_dir.empty()) {
1138 database_dir = default_database_dir;
1139 }
1140 Database &database =
1141 get_global_instance_without_checks(download_missing, use_cache, database_dir);
1142 if (download_missing != database.download_missing_ || use_cache != database.use_cache_ ||
1143 !std::filesystem::exists(database_dir) ||
1144 std::filesystem::canonical(database_dir) != database.database_dir_) {
1145 throw std::invalid_argument(
1146 "The 'download_missing', 'use_cache' and 'database_dir' arguments must not "
1147 "change between calls to the method.");
1148 }
1149 return database;
1150}
1151
1152Database &Database::get_global_instance_without_checks(bool download_missing, bool use_cache,
1153 std::filesystem::path database_dir) {
1154 static Database database(download_missing, use_cache, std::move(database_dir));
1155 return database;
1156}
1157
1158struct database_dir_noexcept : std::filesystem::path {
1159 explicit database_dir_noexcept() noexcept try : std
1160 ::filesystem::path(paths::get_cache_directory() / "database") {}
1161 catch (...) {
1162 SPDLOG_ERROR("Error getting the pairinteraction cache directory.");
1163 std::terminate();
1164 }
1165};
1166
1167const std::filesystem::path Database::default_database_dir = database_dir_noexcept();
1168
1169// Explicit instantiations
1170// NOLINTBEGIN(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
1171#define INSTANTIATE_GETTERS(SCALAR) \
1172 template std::shared_ptr<const BasisAtom<SCALAR>> Database::get_basis<SCALAR>( \
1173 const std::string &species, const AtomDescriptionByRanges &description, \
1174 std::vector<size_t> additional_ket_ids); \
1175 template Eigen::SparseMatrix<SCALAR, Eigen::RowMajor> Database::get_matrix_elements<SCALAR>( \
1176 std::shared_ptr<const BasisAtom<SCALAR>> initial_basis, \
1177 std::shared_ptr<const BasisAtom<SCALAR>> final_basis, OperatorType type, int q);
1178// NOLINTEND(bugprone-macro-parentheses, cppcoreguidelines-macro-usage)
1179
1180INSTANTIATE_GETTERS(double)
1181INSTANTIATE_GETTERS(std::complex<double>)
1182
1183#undef INSTANTIATE_GETTERS
1184} // namespace pairinteraction
#define INSTANTIATE_GETTERS(SCALAR)
Definition: Database.cpp:1133
nlohmann::json json
Class for creating a basis of atomic kets.
Definition: BasisAtom.hpp:40
std::shared_ptr< const KetAtom > get_ket(const std::string &species, const AtomDescriptionByParameters &description)
Definition: Database.cpp:154
static Database & get_global_instance()
Definition: Database.cpp:1068
bool get_download_missing() const
Definition: Database.cpp:1054
std::filesystem::path get_database_dir() const
Definition: Database.cpp:1058
std::shared_ptr< const BasisAtom< Scalar > > get_basis(const std::string &species, const AtomDescriptionByRanges &description, std::vector< size_t > additional_ket_ids)
Definition: Database.cpp:467
Eigen::SparseMatrix< Scalar, Eigen::RowMajor > get_matrix_elements(std::shared_ptr< const BasisAtom< Scalar > > initial_basis, std::shared_ptr< const BasisAtom< Scalar > > final_basis, OperatorType type, int q)
Definition: Database.cpp:905
bool get_use_cache() const
Definition: Database.cpp:1056
bool is_finite() const
Definition: Range.hpp:32
Sortable min() const
Definition: Range.hpp:20
Sortable max() const
Definition: Range.hpp:26
auto streamed(T &&v)
Definition: streamed.hpp:13
std::filesystem::path get_config_directory()
Definition: paths.hpp:61
std::filesystem::path get_cache_directory()
Definition: paths.hpp:29
constexpr std::string_view SQL_TERM_FOR_LINEARIZED_ID_IN_DATABASE
void hash_combine(std::size_t &seed, T const &v)
Combine hashes.
Definition: hash.hpp:43
size_t get_linearized_id_in_database(size_t id, double quantum_number_m)