LCOV - code coverage report
Current view: top level - tests - test_pair_potential.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 103 127 81.1 %
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 UM_IN_ATOMIC_UNITS = 1 / 5.29177210544e-5;
      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 and diagonalize systems for two atoms
      38           1 :     auto basis = pairinteraction::BasisAtomCreator<double>()
      39           2 :                      .set_species("Rb")
      40           1 :                      .restrict_quantum_number_n(58, 62)
      41           1 :                      .restrict_quantum_number_l(0, 2)
      42           1 :                      .create(database);
      43           2 :     SPDLOG_INFO("Number of single-atom basis states: {}", basis->get_number_of_states());
      44             : 
      45           1 :     pairinteraction::SystemAtom<double> system(basis);
      46             : 
      47             :     // Create two-atom systems for different interatomic distances
      48           1 :     auto ket = pairinteraction::KetAtomCreator()
      49           2 :                    .set_species("Rb")
      50           1 :                    .set_quantum_number_n(60)
      51           1 :                    .set_quantum_number_l(0)
      52           1 :                    .set_quantum_number_m(0.5)
      53           1 :                    .create(database);
      54           1 :     double min_energy = 2 * ket->get_energy() - 3 / HARTREE_IN_GHZ;
      55           1 :     double max_energy = 2 * ket->get_energy() + 3 / HARTREE_IN_GHZ;
      56             : 
      57           2 :     auto basis_pair = pairinteraction::BasisPairCreator<double>()
      58           1 :                           .add(system)
      59           1 :                           .add(system)
      60           1 :                           .restrict_energy(min_energy, max_energy)
      61           1 :                           .restrict_quantum_number_m(1, 1)
      62           1 :                           .create();
      63           2 :     SPDLOG_INFO("Number of two-atom basis states: {}", basis_pair->get_number_of_states());
      64             : 
      65           1 :     std::vector<pairinteraction::SystemPair<double>> system_pairs;
      66           1 :     system_pairs.reserve(5);
      67           6 :     for (int i = 1; i < 6; ++i) {
      68           5 :         pairinteraction::SystemPair<double> system(basis_pair);
      69           5 :         system.set_distance_vector({0, 0, i * UM_IN_ATOMIC_UNITS});
      70           5 :         system_pairs.push_back(std::move(system));
      71           5 :     }
      72             : 
      73             :     // Diagonalize the systems in parallel
      74           1 :     pairinteraction::DiagonalizerEigen<double> diagonalizer(pairinteraction::FloatType::FLOAT32);
      75           1 :     pairinteraction::diagonalize(system_pairs, diagonalizer);
      76             : 
      77             :     // Sort by the eigenenergies
      78           6 :     for (auto &system : system_pairs) {
      79           5 :         auto sorter = system.get_sorter({pairinteraction::TransformationType::SORT_BY_ENERGY});
      80           5 :         system.transform(sorter);
      81           5 :     }
      82             : 
      83             :     // Extract results
      84           1 :     std::vector<std::string> kets;
      85           1 :     Eigen::MatrixX<double> eigenenergies(system_pairs.size(), basis_pair->get_number_of_states());
      86           0 :     Eigen::MatrixX<double> eigenstates(system_pairs.size(),
      87           1 :                                        basis_pair->get_number_of_states() *
      88           1 :                                            basis_pair->get_number_of_states());
      89           1 :     Eigen::MatrixX<double> overlaps(system_pairs.size(), basis_pair->get_number_of_states());
      90             : 
      91           1 :     kets.reserve(basis->get_number_of_states());
      92          20 :     for (const auto &ket : *basis_pair) {
      93          19 :         std::stringstream ss;
      94          19 :         ss << *ket;
      95          19 :         kets.push_back(ss.str());
      96          19 :     }
      97             : 
      98           6 :     for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(system_pairs.size()); ++i) {
      99           5 :         eigenenergies.row(i) = system_pairs[i].get_eigenenergies() * HARTREE_IN_GHZ;
     100             : 
     101             :         Eigen::MatrixX<double> tmp =
     102           5 :             system_pairs[i].get_eigenbasis()->get_coefficients().toDense().transpose();
     103           5 :         eigenstates.row(i) = Eigen::Map<Eigen::VectorXd>(tmp.data(), tmp.size());
     104             : 
     105           5 :         overlaps.row(i) = system_pairs[i].get_eigenbasis()->get_overlaps(ket, ket);
     106           5 :     }
     107             : 
     108             :     // Compare with reference data
     109           1 :     bool success = true;
     110             : 
     111             :     // Check kets
     112             :     const std::filesystem::path reference_kets_file =
     113           1 :         data_dir / "reference_pair_potential/kets.txt";
     114           2 :     SPDLOG_INFO("Reference kets: {}", reference_kets_file.string());
     115             : 
     116           1 :     std::vector<std::string> reference_kets;
     117           1 :     std::string line;
     118           1 :     std::ifstream stream(reference_kets_file);
     119           1 :     if (!stream) {
     120           0 :         SPDLOG_ERROR("Could not open reference kets file");
     121           0 :         success = false;
     122             :     } else {
     123           1 :         reference_kets.reserve(basis_pair->get_number_of_states());
     124          20 :         while (std::getline(stream, line)) {
     125          19 :             reference_kets.push_back(line);
     126             :         }
     127           1 :         stream.close();
     128           1 :         if (kets.size() != reference_kets.size()) {
     129           0 :             SPDLOG_ERROR("Number of kets does not match reference data");
     130           0 :             success = false;
     131           1 :         } else if (kets != reference_kets) {
     132           0 :             for (size_t i = 0; i < kets.size(); ++i) {
     133           0 :                 SPDLOG_DEBUG("Ket: {} vs {}, match: {}", kets[i], reference_kets[i],
     134             :                              kets[i] == reference_kets[i]);
     135             :             }
     136           0 :             SPDLOG_ERROR("Kets do not match reference data");
     137           0 :             success = false;
     138             :         }
     139             :     }
     140             : 
     141             :     // Check eigenenergies
     142             :     const std::filesystem::path reference_eigenenergies_file =
     143           1 :         data_dir / "reference_pair_potential/eigenenergies.txt";
     144           2 :     SPDLOG_INFO("Reference eigenenergies: {}", reference_eigenenergies_file.string());
     145             : 
     146           0 :     Eigen::MatrixXd reference_eigenenergies(system_pairs.size(),
     147           1 :                                             basis_pair->get_number_of_states());
     148           1 :     stream = std::ifstream(reference_eigenenergies_file);
     149           1 :     if (!stream) {
     150           0 :         SPDLOG_ERROR("Could not open reference eigenenergies file");
     151           0 :         success = false;
     152             :     } else {
     153           6 :         for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(system_pairs.size()); ++i) {
     154         100 :             for (Eigen::Index j = 0;
     155         100 :                  j < static_cast<Eigen::Index>(basis_pair->get_number_of_states()); ++j) {
     156          95 :                 stream >> reference_eigenenergies(i, j);
     157             :             }
     158             :         }
     159           1 :         stream.close();
     160           1 :         if ((eigenenergies - reference_eigenenergies).array().abs().maxCoeff() >
     161             :             100e-6) { // 100 kHz precision
     162           0 :             for (Eigen::Index i = 0; i < eigenenergies.size(); ++i) {
     163           0 :                 SPDLOG_DEBUG("Eigenenergy: {} vs {}, delta: {}", eigenenergies(i),
     164             :                              reference_eigenenergies(i),
     165             :                              std::abs(eigenenergies(i) - reference_eigenenergies(i)));
     166             :             }
     167           0 :             SPDLOG_ERROR("Eigenenergies do not match reference data");
     168           0 :             success = false;
     169             :         }
     170             :     }
     171             : 
     172             :     // Check overlaps
     173             :     const std::filesystem::path reference_overlaps_file =
     174           1 :         data_dir / "reference_pair_potential/overlaps.txt";
     175           2 :     SPDLOG_INFO("Reference overlaps: {}", reference_overlaps_file.string());
     176             : 
     177           1 :     Eigen::MatrixXd reference_overlaps(system_pairs.size(), basis_pair->get_number_of_states());
     178           1 :     stream = std::ifstream(reference_overlaps_file);
     179           1 :     if (!stream) {
     180           0 :         SPDLOG_ERROR("Could not open reference overlap file");
     181           0 :         success = false;
     182             :     } else {
     183           6 :         for (Eigen::Index i = 0; i < static_cast<Eigen::Index>(system_pairs.size()); ++i) {
     184         100 :             for (Eigen::Index j = 0;
     185         100 :                  j < static_cast<Eigen::Index>(basis_pair->get_number_of_states()); ++j) {
     186          95 :                 stream >> reference_overlaps(i, j);
     187             :             }
     188             :         }
     189           1 :         stream.close();
     190           1 :         if ((overlaps - reference_overlaps).array().abs().maxCoeff() > 1e-5) {
     191           0 :             for (Eigen::Index i = 0; i < overlaps.size(); ++i) {
     192           0 :                 SPDLOG_DEBUG("Overlap: {} vs {}, delta: {}", overlaps(i), reference_overlaps(i),
     193             :                              std::abs(overlaps(i) - reference_overlaps(i)));
     194             :             }
     195           0 :             SPDLOG_ERROR("Overlaps do not match reference data");
     196           0 :             success = false;
     197             :         }
     198             :     }
     199             : 
     200             :     // Check eigenstates
     201             :     // Because of degeneracies, checking the eigenstates against reference data is complicated.
     202             :     // Thus, we only check their normalization and orthogonality.
     203             :     Eigen::VectorXd cumulative_norm =
     204           1 :         (eigenstates.adjoint().array() * eigenstates.transpose().array()).colwise().sum();
     205           1 :     if (!cumulative_norm.isApprox(Eigen::VectorXd::Constant(5, 19), 1e-5)) {
     206           0 :         SPDLOG_ERROR("Eigenvectors are not orthonormal.");
     207           0 :         success = false;
     208             :     }
     209             : 
     210           1 :     return success ? 0 : 1;
     211           1 : }

Generated by: LCOV version 1.16