19#include <cpptrace/cpptrace.hpp>
22#include <fmt/ranges.h>
24#include <nlohmann/json.hpp>
25#include <oneapi/tbb.h>
26#include <spdlog/spdlog.h>
27#include <system_error>
33 :
Database(download_missing, default_use_cache, default_database_dir) {}
36 :
Database(default_download_missing, default_use_cache, std::move(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)) {
43 if (database_dir_.empty()) {
44 database_dir_ = default_database_dir;
48 if (!std::filesystem::exists(database_dir_)) {
49 std::filesystem::create_directories(database_dir_);
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));
56 SPDLOG_INFO(
"Using database directory: {}", database_dir_.string());
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 ",
65 std::make_error_code(std::errc::not_a_directory));
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);
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>>();
85 if (database_repo_host != default_database_repo_host ||
86 database_repo_paths != default_database_repo_paths) {
90 if (seed == doc[
"hash"].get<std::size_t>()) {
91 database_repo_host.clear();
92 database_repo_paths.clear();
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 '{}'.",
104 if (database_repo_host.empty() || database_repo_paths.empty()) {
105 SPDLOG_INFO(
"Updating the database repository host and paths:");
107 database_repo_host = default_database_repo_host;
108 database_repo_paths = default_database_repo_paths;
109 std::ofstream file(configfile);
112 SPDLOG_INFO(
"* New host: {}", default_database_repo_host);
113 SPDLOG_INFO(
"* New paths: {}", fmt::join(default_database_repo_paths,
", "));
115 doc[
"database_repo_host"] = default_database_repo_host;
116 doc[
"database_repo_paths"] = database_repo_paths;
118 std::size_t seed = 0;
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());
136 if (!download_missing_) {
137 database_repo_paths.clear();
139 downloader = std::make_unique<GitHubDownloader>();
140 manager = std::make_unique<ParquetManager>(database_dir_, *downloader, database_repo_paths,
142 manager->scan_local();
143 manager->scan_remote();
146 std::istringstream iss(manager->get_versions_info());
147 for (std::string line; std::getline(iss, line);) {
158 throw std::invalid_argument(
"The quantum number m must be specified.");
163 throw std::invalid_argument(
"The quantum number f must be an integer or half-integer.");
166 throw std::invalid_argument(
"The quantum number f must be positive.");
171 throw std::invalid_argument(
"The quantum number j must be an integer or half-integer.");
174 throw std::invalid_argument(
"The quantum number j must be positive.");
179 throw std::invalid_argument(
"The quantum number m must be an integer or half-integer.");
184 std::string separator;
185 if (description.
energy.has_value()) {
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);
196 where += separator + fmt::format(
"f = {}", description.
quantum_number_f.value());
204 where += separator + fmt::format(
"n = {}", description.
quantum_number_n.value());
215 fmt::format(
"exp_nui BETWEEN {} AND {}", description.
quantum_number_nui.value() - 0.5,
221 fmt::format(
"exp_l BETWEEN {} AND {}", description.
quantum_number_l.value() - 0.5,
227 fmt::format(
"exp_s BETWEEN {} AND {}", description.
quantum_number_s.value() - 0.5,
233 fmt::format(
"exp_j BETWEEN {} AND {}", description.
quantum_number_j.value() - 0.5,
239 fmt::format(
"exp_l_ryd BETWEEN {} AND {}",
246 fmt::format(
"exp_j_ryd BETWEEN {} AND {}",
251 if (separator.empty()) {
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())));
264 orderby += separator + fmt::format(
"(nu - {})^2", description.
quantum_number_nu.value());
273 orderby += separator + fmt::format(
"(exp_l - {})^2", description.
quantum_number_l.value());
277 orderby += separator + fmt::format(
"(exp_s - {})^2", description.
quantum_number_s.value());
281 orderby += separator + fmt::format(
"(exp_j - {})^2", description.
quantum_number_j.value());
294 if (separator.empty()) {
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));
304 if (result->HasError()) {
305 throw cpptrace::runtime_error(
"Error querying the database: " + result->GetError());
308 if (result->RowCount() == 0) {
309 throw std::invalid_argument(
"No state found.");
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};
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());
335 auto chunk = result->Fetch();
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];
342 if (order_val_1 - order_val_0 <= order_val_0) {
344 std::vector<KetAtom> kets;
346 for (
size_t i = 0; i < 2; ++i) {
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);
404 throw std::invalid_argument(
405 fmt::format(
"The ket is not uniquely specified. Possible kets are:\n{}\n{}",
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];
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.");
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.");
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.");
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);
466template <
typename Scalar>
467std::shared_ptr<const BasisAtom<Scalar>>
469 std::vector<size_t> additional_ket_ids) {
471 std::string where =
"(";
472 std::string separator;
479 fmt::format(
"energy BETWEEN {} AND {}", description.
range_energy.
min(),
509 fmt::format(
"exp_nui BETWEEN {}-2*std_nui AND {}+2*std_nui",
516 fmt::format(
"exp_l BETWEEN {}-2*std_l AND {}+2*std_l",
523 fmt::format(
"exp_s BETWEEN {}-2*std_s AND {}+2*std_s",
530 fmt::format(
"exp_j BETWEEN {}-2*std_j AND {}+2*std_j",
537 fmt::format(
"exp_l_ryd BETWEEN {}-2*std_l_ryd AND {}+2*std_l_ryd",
544 fmt::format(
"exp_j_ryd BETWEEN {}-2*std_j_ryd AND {}+2*std_j_ryd",
549 if (separator.empty()) {
553 if (!additional_ket_ids.empty()) {
555 fmt::join(additional_ket_ids,
","));
559 std::string id_of_kets;
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());
566 duckdb::FlatVector::GetData<duckdb::string_t>(result->Fetch()->data[0])[0].GetString();
569 auto result = con->Query(fmt::format(
570 R
"(CREATE TEMP TABLE '{}' AS SELECT *, {} AS ketid FROM (
572 UNNEST(list_transform(generate_series(0,(2*f)::bigint),
573 x -> x::double-f)) AS m FROM '{}'
576 manager->get_path(species, "states"), where));
578 if (result->HasError()) {
579 throw cpptrace::runtime_error(
"Error creating table: " + result->GetError());
586 std::string separator;
588 select += separator +
"MIN(energy) AS min_energy, MAX(energy) AS max_energy";
592 select += separator +
"MIN(f) AS min_f, MAX(f) AS max_f";
596 select += separator +
"MIN(m) AS min_m, MAX(m) AS max_m";
600 select += separator +
"MIN(n) AS min_n, MAX(n) AS max_n";
604 select += separator +
"MIN(nu) AS min_nu, MAX(nu) AS max_nu";
608 select += separator +
"MIN(exp_nui) AS min_nui, MAX(exp_nui) AS max_nui";
612 select += separator +
"MIN(exp_l) AS min_l, MAX(exp_l) AS max_l";
616 select += separator +
"MIN(exp_s) AS min_s, MAX(exp_s) AS max_s";
620 select += separator +
"MIN(exp_j) AS min_j, MAX(exp_j) AS max_j";
624 select += separator +
"MIN(exp_l_ryd) AS min_l_ryd, MAX(exp_l_ryd) AS max_l_ryd";
628 select += separator +
"MIN(exp_j_ryd) AS min_j_ryd, MAX(exp_j_ryd) AS max_j_ryd";
632 if (!separator.empty()) {
633 auto result = con->Query(fmt::format(R
"(SELECT {} FROM '{}')", select, id_of_kets));
635 if (result->HasError()) {
636 throw cpptrace::runtime_error(
"Error querying the database: " + result->GetError());
639 auto chunk = result->Fetch();
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.");
649 auto min_energy = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
650 if (std::sqrt(-1 / (2 * min_energy)) - 1 >
652 SPDLOG_DEBUG(
"No state found with the requested minimum energy. Requested: {}, "
656 auto max_energy = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
657 if (std::sqrt(-1 / (2 * max_energy)) + 1 <
659 SPDLOG_DEBUG(
"No state found with the requested maximum energy. Requested: {}, "
665 auto min_f = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
667 SPDLOG_DEBUG(
"No state found with the requested minimum quantum number f. "
668 "Requested: {}, found: {}.",
671 auto max_f = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
673 SPDLOG_DEBUG(
"No state found with the requested maximum quantum number f. "
674 "Requested: {}, found: {}.",
679 auto min_m = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
681 SPDLOG_DEBUG(
"No state found with the requested minimum quantum number m. "
682 "Requested: {}, found: {}.",
685 auto max_m = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
687 SPDLOG_DEBUG(
"No state found with the requested maximum quantum number m. "
688 "Requested: {}, found: {}.",
693 auto min_n = duckdb::FlatVector::GetData<int64_t>(chunk->data[idx++])[0];
695 SPDLOG_DEBUG(
"No state found with the requested minimum quantum number n. "
696 "Requested: {}, found: {}.",
699 auto max_n = duckdb::FlatVector::GetData<int64_t>(chunk->data[idx++])[0];
701 SPDLOG_DEBUG(
"No state found with the requested maximum quantum number n. "
702 "Requested: {}, found: {}.",
707 auto min_nu = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
709 SPDLOG_DEBUG(
"No state found with the requested minimum quantum number nu. "
710 "Requested: {}, found: {}.",
713 auto max_nu = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
715 SPDLOG_DEBUG(
"No state found with the requested maximum quantum number nu. "
716 "Requested: {}, found: {}.",
721 auto min_nui = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
723 SPDLOG_DEBUG(
"No state found with the requested minimum quantum number nui. "
724 "Requested: {}, found: {}.",
727 auto max_nui = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
729 SPDLOG_DEBUG(
"No state found with the requested maximum quantum number nui. "
730 "Requested: {}, found: {}.",
735 auto min_l = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
737 SPDLOG_DEBUG(
"No state found with the requested minimum quantum number l. "
738 "Requested: {}, found: {}.",
741 auto max_l = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
743 SPDLOG_DEBUG(
"No state found with the requested maximum quantum number l. "
744 "Requested: {}, found: {}.",
749 auto min_s = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
751 SPDLOG_DEBUG(
"No state found with the requested minimum quantum number s. "
752 "Requested: {}, found: {}.",
755 auto max_s = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
757 SPDLOG_DEBUG(
"No state found with the requested maximum quantum number s. "
758 "Requested: {}, found: {}.",
763 auto min_j = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
765 SPDLOG_DEBUG(
"No state found with the requested minimum quantum number j. "
766 "Requested: {}, found: {}.",
769 auto max_j = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
771 SPDLOG_DEBUG(
"No state found with the requested maximum quantum number j. "
772 "Requested: {}, found: {}.",
777 auto min_l_ryd = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
779 SPDLOG_DEBUG(
"No state found with the requested minimum quantum number l_ryd. "
780 "Requested: {}, found: {}.",
783 auto max_l_ryd = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
785 SPDLOG_DEBUG(
"No state found with the requested maximum quantum number l_ryd. "
786 "Requested: {}, found: {}.",
791 auto min_j_ryd = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
793 SPDLOG_DEBUG(
"No state found with the requested minimum quantum number j_ryd. "
794 "Requested: {}, found: {}.",
797 auto max_j_ryd = duckdb::FlatVector::GetData<double>(chunk->data[idx++])[0];
799 SPDLOG_DEBUG(
"No state found with the requested maximum quantum number j_ryd. "
800 "Requested: {}, found: {}.",
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)",
813 if (result->HasError()) {
814 throw cpptrace::runtime_error(
"Error querying the database: " + result->GetError());
817 if (result->RowCount() == 0) {
818 throw std::invalid_argument(
"No state found.");
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};
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());
843 std::vector<std::shared_ptr<const KetAtom>> kets;
844 kets.reserve(result->RowCount());
846 double last_energy = std::numeric_limits<double>::lowest();
849 for (
auto chunk = result->Fetch(); chunk; chunk = result->Fetch()) {
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]);
875 for (
size_t i = 0; i < chunk->size(); i++) {
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.");
883 if (chunk_energy[i] < last_energy) {
884 throw std::runtime_error(
"The states are not sorted by energy.");
886 last_energy = chunk_energy[i];
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]));
906 std::move(kets), std::move(id_of_kets), *
this);
909template <
typename Scalar>
910Eigen::SparseMatrix<Scalar, Eigen::RowMajor>
916 std::string specifier;
920 specifier =
"matrix_elements_d";
924 specifier =
"matrix_elements_q";
928 specifier =
"matrix_elements_q0";
932 specifier =
"matrix_elements_o";
936 specifier =
"matrix_elements_mu";
940 specifier =
"energy";
944 specifier =
"identity";
948 throw std::invalid_argument(
"Unknown operator type.");
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.");
955 std::string id_of_kets = initial_basis->get_id_of_kets();
956 std::string cache_key = fmt::format(
"{}_{}_{}", specifier, q, id_of_kets);
958 if (!get_matrix_elements_cache().contains(cache_key)) {
959 Eigen::Index dim = initial_basis->get_number_of_kets();
961 std::vector<int> outerIndexPtr;
962 std::vector<int> innerIndices;
963 std::vector<real_t> values;
965 if (specifier ==
"identity") {
966 outerIndexPtr.reserve(dim + 1);
967 innerIndices.reserve(dim);
970 for (
int i = 0; i < dim; i++) {
971 outerIndexPtr.push_back(
static_cast<int>(innerIndices.size()));
972 innerIndices.push_back(i);
975 outerIndexPtr.push_back(
static_cast<int>(innerIndices.size()));
979 if (std::abs(q) > kappa) {
980 throw std::invalid_argument(
"Invalid q.");
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(
989 SELECT id, f, m, ketid FROM '{}'
992 SELECT MIN(f) AS min_f, MAX(f) AS max_f,
993 MIN(id) AS min_id, MAX(id) AS max_id
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)
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)
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)));
1024 result = con->Query(fmt::format(
1025 R
"(SELECT ketid as row, ketid as col, energy as val FROM '{}' ORDER BY row ASC)",
1029 if (result->HasError()) {
1030 throw cpptrace::runtime_error(
"Error querying the database: " + result->GetError());
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] +
"'.");
1046 int num_entries =
static_cast<int>(result->RowCount());
1047 outerIndexPtr.reserve(dim + 1);
1048 innerIndices.reserve(num_entries);
1049 values.reserve(num_entries);
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.");
1065 for (; last_row < row; last_row++) {
1066 outerIndexPtr.push_back(
static_cast<int>(innerIndices.size()));
1069 innerIndices.push_back(initial_basis->get_ket_index_from_id(chunk_col[i]));
1070 values.push_back(chunk_val[i]);
1074 for (; last_row < dim + 1; last_row++) {
1075 outerIndexPtr.push_back(
static_cast<int>(innerIndices.size()));
1079 Eigen::Map<const Eigen::SparseMatrix<real_t, Eigen::RowMajor>> matrix_map(
1080 dim, dim, values.size(), outerIndexPtr.data(), innerIndices.data(), values.data());
1083 get_matrix_elements_cache()[cache_key] = matrix_map;
1087 return final_basis->get_coefficients().adjoint() *
1088 get_matrix_elements_cache()[cache_key].template cast<Scalar>() *
1089 initial_basis->get_coefficients();
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;
1107 return get_global_instance_without_checks(default_download_missing, default_use_cache,
1108 default_database_dir);
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.");
1122 if (database_dir.empty()) {
1123 database_dir = default_database_dir;
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.");
1137 if (database_dir.empty()) {
1138 database_dir = default_database_dir;
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.");
1152Database &Database::get_global_instance_without_checks(
bool download_missing,
bool use_cache,
1154 static Database database(download_missing, use_cache, std::move(database_dir));
1162 SPDLOG_ERROR(
"Error getting the pairinteraction cache directory.");
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);
1183#undef INSTANTIATE_GETTERS
#define INSTANTIATE_GETTERS(SCALAR)
Class for creating a basis of atomic kets.
std::shared_ptr< const KetAtom > get_ket(const std::string &species, const AtomDescriptionByParameters &description)
static Database & get_global_instance()
bool get_download_missing() const
std::filesystem::path get_database_dir() const
std::shared_ptr< const BasisAtom< Scalar > > get_basis(const std::string &species, const AtomDescriptionByRanges &description, std::vector< size_t > additional_ket_ids)
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)
bool get_use_cache() const
std::filesystem::path get_config_directory()
std::filesystem::path get_cache_directory()
constexpr std::string_view SQL_TERM_FOR_LINEARIZED_ID_IN_DATABASE
void hash_combine(std::size_t &seed, T const &v)
Combine hashes.
size_t get_linearized_id_in_database(size_t id, double quantum_number_m)
@ ELECTRIC_QUADRUPOLE_ZERO
std::optional< double > quantum_number_f
std::optional< double > quantum_number_l
std::optional< double > quantum_number_nui
std::optional< int > quantum_number_n
std::optional< double > quantum_number_j_ryd
std::optional< double > energy
std::optional< double > quantum_number_m
std::optional< double > quantum_number_l_ryd
std::optional< double > quantum_number_j
std::optional< double > quantum_number_s
std::optional< double > quantum_number_nu
Range< double > range_quantum_number_nui
Range< double > range_quantum_number_f
Range< double > range_quantum_number_l
Range< int > range_quantum_number_n
Range< double > range_quantum_number_l_ryd
Range< double > range_quantum_number_s
Range< double > range_quantum_number_j_ryd
Range< double > range_quantum_number_j
Range< double > range_energy
Range< double > range_quantum_number_nu
Range< double > range_quantum_number_m
database_dir_noexcept() noexcept