LCOV - code coverage report
Current view: top level - tests - test_starkmap.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 90 112 80.4 %
Date: 2025-04-29 15:56:08 Functions: 1 1 100.0 %

          Line data    Source code
       1             : // SPDX-FileCopyrightText: 2024 Pairinteraction Developers
       2             : // SPDX-License-Identifier: LGPL-3.0-or-later
       3             : 
       4             : #include "pairinteraction/pairinteraction.hpp"
       5             : #include "pairinteraction/utils/args.hpp"
       6             : 
       7             : #include <Eigen/Dense>
       8             : #include <filesystem>
       9             : #include <fstream>
      10             : #include <spdlog/spdlog.h>
      11             : #include <vector>
      12             : 
      13             : constexpr double HARTREE_IN_GHZ = 6579683.920501762;
      14             : constexpr double VOLT_PER_CM_IN_ATOMIC_UNITS = 1 / 5.14220675112e9;
      15             : 
      16           1 : int main(int argc, char **argv) {
      17             :     // Call the setup function to configure logging
      18           1 :     pairinteraction::setup();
      19             : 
      20             :     // Create a database instance
      21           1 :     std::filesystem::path database_dir;
      22           1 :     std::filesystem::path data_dir;
      23           1 :     bool download_missing = false;
      24             : 
      25           3 :     for (int i = 1; i < argc; ++i) {
      26           2 :         bool found = pairinteraction::args::parse_download_missing(i, argc, argv, download_missing);
      27           2 :         if (!found) {
      28           2 :             found = pairinteraction::args::parse_database_dir(i, argc, argv, database_dir);
      29             :         }
      30           2 :         if (!found) {
      31           1 :             pairinteraction::args::parse_data_dir(i, argc, argv, data_dir);
      32             :         }
      33             :     }
      34             : 
      35           1 :     pairinteraction::Database database(download_missing, true, database_dir);
      36             : 
      37             :     // Create a basis
      38           1 :     auto ket = pairinteraction::KetAtomCreator()
      39           2 :                    .set_species("Rb")
      40           1 :                    .set_quantum_number_n(60)
      41           1 :                    .set_quantum_number_l(0)
      42           1 :                    .set_quantum_number_m(0.5)
      43           1 :                    .create(database);
      44             : 
      45           1 :     auto basis = pairinteraction::BasisAtomCreator<double>()
      46           2 :                      .set_species("Rb")
      47           1 :                      .restrict_quantum_number_n(58, 62)
      48           1 :                      .restrict_quantum_number_l(0, 2)
      49           1 :                      .create(database);
      50             : 
      51           2 :     SPDLOG_INFO("Number of basis states: {}", basis->get_number_of_states());
      52             : 
      53             :     // Create systems for different values of the electric field
      54           1 :     std::vector<pairinteraction::SystemAtom<double>> systems;
      55           1 :     systems.reserve(11);
      56          12 :     for (int i = 0; i < 11; ++i) {
      57          11 :         pairinteraction::SystemAtom<double> system(basis);
      58          11 :         system.set_electric_field({0, 0, i * VOLT_PER_CM_IN_ATOMIC_UNITS});
      59          11 :         systems.push_back(std::move(system));
      60          11 :     }
      61             : 
      62             :     // Diagonalize the systems in parallel
      63           1 :     pairinteraction::DiagonalizerEigen<double> diagonalizer(pairinteraction::FloatType::FLOAT32);
      64           1 :     pairinteraction::diagonalize(systems, diagonalizer);
      65             : 
      66             :     // Sort by the eigenenergies
      67          12 :     for (auto &system : systems) {
      68          11 :         auto sorter = system.get_sorter({pairinteraction::TransformationType::SORT_BY_ENERGY});
      69          11 :         system.transform(sorter);
      70          11 :     }
      71             : 
      72             :     // Extract results
      73           1 :     std::vector<std::string> kets;
      74           1 :     Eigen::MatrixX<double> eigenenergies(systems.size(), basis->get_number_of_states());
      75             :     Eigen::MatrixX<double> eigenstates(
      76           1 :         systems.size(), basis->get_number_of_states() * basis->get_number_of_states());
      77           1 :     Eigen::MatrixX<double> overlaps(systems.size(), basis->get_number_of_states());
      78             : 
      79           1 :     kets.reserve(basis->get_number_of_states());
      80          91 :     for (const auto &ket : *basis) {
      81          90 :         std::stringstream ss;
      82          90 :         ss << *ket;
      83          90 :         kets.push_back(ss.str());
      84          90 :     }
      85             : 
      86          12 :     for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(systems.size()); ++i) {
      87          11 :         eigenenergies.row(i) = systems[i].get_eigenenergies() * HARTREE_IN_GHZ;
      88             : 
      89             :         Eigen::MatrixX<double> tmp =
      90          11 :             systems[i].get_eigenbasis()->get_coefficients().toDense().transpose();
      91          11 :         eigenstates.row(i) = Eigen::Map<Eigen::VectorXd>(tmp.data(), tmp.size());
      92             : 
      93          11 :         overlaps.row(i) = systems[i].get_eigenbasis()->get_overlaps(ket);
      94          11 :     }
      95             : 
      96             :     // Compare with reference data
      97           1 :     bool success = true;
      98             : 
      99             :     // Check kets
     100           1 :     const std::filesystem::path reference_kets_file = data_dir / "reference_stark_map/kets.txt";
     101           2 :     SPDLOG_INFO("Reference kets: {}", reference_kets_file.string());
     102             : 
     103           1 :     std::vector<std::string> reference_kets;
     104           1 :     std::string line;
     105           1 :     std::ifstream stream(reference_kets_file);
     106           1 :     if (!stream) {
     107           0 :         SPDLOG_ERROR("Could not open reference kets file");
     108           0 :         success = false;
     109             :     } else {
     110           1 :         reference_kets.reserve(basis->get_number_of_states());
     111          91 :         while (std::getline(stream, line)) {
     112          90 :             reference_kets.push_back(line);
     113             :         }
     114           1 :         stream.close();
     115           1 :         if (kets.size() != reference_kets.size()) {
     116           0 :             SPDLOG_ERROR("Number of kets does not match reference data");
     117           0 :             success = false;
     118           1 :         } else if (kets != reference_kets) {
     119           0 :             for (size_t i = 0; i < kets.size(); ++i) {
     120           0 :                 SPDLOG_DEBUG("Ket: {} vs {}, match: {}", kets[i], reference_kets[i],
     121             :                              kets[i] == reference_kets[i]);
     122             :             }
     123           0 :             SPDLOG_ERROR("Kets do not match reference data");
     124           0 :             success = false;
     125             :         }
     126             :     }
     127             : 
     128             :     // Check eigenenergies
     129             :     const std::filesystem::path reference_eigenenergies_file =
     130           1 :         data_dir / "reference_stark_map/eigenenergies.txt";
     131           2 :     SPDLOG_INFO("Reference eigenenergies: {}", reference_eigenenergies_file.string());
     132             : 
     133           1 :     Eigen::MatrixXd reference_eigenenergies(systems.size(), basis->get_number_of_states());
     134           1 :     stream = std::ifstream(reference_eigenenergies_file);
     135           1 :     if (!stream) {
     136           0 :         SPDLOG_ERROR("Could not open reference eigenenergies file");
     137           0 :         success = false;
     138             :     } else {
     139          12 :         for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(systems.size()); ++i) {
     140        1001 :             for (Eigen::Index j = 0; j < static_cast<Eigen::Index>(basis->get_number_of_states());
     141             :                  ++j) {
     142         990 :                 stream >> reference_eigenenergies(i, j);
     143             :             }
     144             :         }
     145           1 :         stream.close();
     146           1 :         if ((eigenenergies - reference_eigenenergies).array().abs().maxCoeff() >
     147             :             500e-6) { // 500 kHz precision
     148           0 :             for (Eigen::Index i = 0; i < eigenenergies.size(); ++i) {
     149           0 :                 SPDLOG_DEBUG("Eigenenergy: {} vs {}, delta: {}", eigenenergies(i),
     150             :                              reference_eigenenergies(i),
     151             :                              std::abs(eigenenergies(i) - reference_eigenenergies(i)));
     152             :             }
     153           0 :             SPDLOG_ERROR("Eigenenergies do not match reference data");
     154           0 :             success = false;
     155             :         }
     156             :     }
     157             : 
     158             :     // Check overlaps
     159             :     const std::filesystem::path reference_overlaps_file =
     160           1 :         data_dir / "reference_stark_map/overlaps.txt";
     161           2 :     SPDLOG_INFO("Reference overlaps: {}", reference_overlaps_file.string());
     162             : 
     163           1 :     Eigen::MatrixXd reference_overlaps(systems.size(), basis->get_number_of_states());
     164           1 :     stream = std::ifstream(reference_overlaps_file);
     165           1 :     if (!stream) {
     166           0 :         SPDLOG_ERROR("Could not open reference overlap file");
     167           0 :         success = false;
     168             :     } else {
     169          12 :         for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(systems.size()); ++i) {
     170        1001 :             for (Eigen::Index j = 0; j < static_cast<Eigen::Index>(basis->get_number_of_states());
     171             :                  ++j) {
     172         990 :                 stream >> reference_overlaps(i, j);
     173             :             }
     174             :         }
     175           1 :         stream.close();
     176           1 :         if ((overlaps - reference_overlaps).array().abs().maxCoeff() > 5e-5) {
     177           0 :             for (Eigen::Index i = 0; i < overlaps.size(); ++i) {
     178           0 :                 SPDLOG_DEBUG("Overlap: {} vs {}, delta: {}", overlaps(i), reference_overlaps(i),
     179             :                              std::abs(overlaps(i) - reference_overlaps(i)));
     180             :             }
     181           0 :             SPDLOG_ERROR("Overlaps do not match reference data");
     182           0 :             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           1 :         (eigenstates.adjoint().array() * eigenstates.transpose().array()).colwise().sum();
     191           1 :     if (!cumulative_norm.isApprox(Eigen::VectorXd::Constant(11, 90), 5e-5)) {
     192           0 :         SPDLOG_ERROR("Eigenvectors are not orthonormal.");
     193           0 :         success = false;
     194             :     }
     195             : 
     196           1 :     return success ? 0 : 1;
     197           1 : }

Generated by: LCOV version 1.16