LCOV - code coverage report
Current view: top level - src/basis - BasisAtomCreator.test.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 144 144 100.0 %
Date: 2026-08-17 11:38:34 Functions: 6 6 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/basis/BasisAtomCreator.hpp"
       5             : 
       6             : #include "pairinteraction/basis/BasisAtom.hpp"
       7             : #include "pairinteraction/database/Database.hpp"
       8             : #include "pairinteraction/diagonalize/DiagonalizerEigen.hpp"
       9             : #include "pairinteraction/enums/OperatorType.hpp"
      10             : #include "pairinteraction/enums/Parity.hpp"
      11             : #include "pairinteraction/enums/TransformationType.hpp"
      12             : #include "pairinteraction/ket/KetAtom.hpp"
      13             : #include "pairinteraction/ket/KetAtomCreator.hpp"
      14             : #include "pairinteraction/system/SystemAtom.hpp"
      15             : 
      16             : #include <doctest/doctest.h>
      17             : 
      18             : namespace pairinteraction {
      19             : 
      20             : constexpr double VOLT_PER_CM_IN_ATOMIC_UNITS = 1 / 5.14220675112e9;
      21             : 
      22           1 : DOCTEST_TEST_CASE("create a basis for strontium 88") {
      23           1 :     Database &database = Database::get_global_instance();
      24           1 :     auto basis = BasisAtomCreator<double>()
      25           2 :                      .set_species("Sr88_sqdt")
      26           2 :                      .restrict_quantum_number("n", 60, 60)
      27           2 :                      .restrict_quantum_number("l", 0, 2)
      28           2 :                      .restrict_quantum_number("s", 0, 0)
      29           1 :                      .create(database);
      30          19 :     for (const auto &ket : *basis) {
      31           9 :         DOCTEST_CHECK(ket->get_species() == "Sr88_sqdt");
      32           9 :     }
      33           1 : }
      34             : 
      35           1 : DOCTEST_TEST_CASE("create a basis for strontium 87") {
      36           1 :     Database &database = Database::get_global_instance();
      37           1 :     auto basis = BasisAtomCreator<double>()
      38           2 :                      .set_species("Sr87_mqdt")
      39           2 :                      .restrict_quantum_number("nu", 59, 61)
      40           2 :                      .restrict_quantum_number("l", 0, 0)
      41           1 :                      .create(database);
      42         161 :     for (const auto &ket : *basis) {
      43          80 :         DOCTEST_CHECK(ket->get_species() == "Sr87_mqdt");
      44          80 :     }
      45           1 : }
      46             : 
      47           1 : DOCTEST_TEST_CASE("create a basis from kets") {
      48           1 :     Database &database = Database::get_global_instance();
      49           1 :     auto ket1 = KetAtomCreator("Sr88_sqdt", 59, 0, 0, 0).create(database);
      50           1 :     auto ket2 = KetAtomCreator("Sr88_sqdt", 60, 0, 0, 0).create(database);
      51           1 :     auto ket3 = KetAtomCreator("Sr88_sqdt", 61, 0, 0, 0).create(database);
      52             :     auto basis =
      53           1 :         BasisAtomCreator<double>().add_ket(ket1).add_ket(ket2).add_ket(ket3).create(database);
      54           7 :     for (const auto &ket : *basis) {
      55           3 :         DOCTEST_CHECK(ket->get_species() == "Sr88_sqdt");
      56           3 :     }
      57           1 : }
      58             : 
      59           1 : DOCTEST_TEST_CASE("create a basis and sort it according to parity and m") {
      60           1 :     Database &database = Database::get_global_instance();
      61           1 :     auto basis_unsorted = BasisAtomCreator<double>()
      62           2 :                               .set_species("Rb")
      63           2 :                               .restrict_quantum_number("n", 60, 60)
      64           2 :                               .restrict_quantum_number("l", 0, 3)
      65           2 :                               .restrict_quantum_number("m", -0.5, 0.5)
      66           1 :                               .create(database);
      67             : 
      68             :     // Sort the basis by parity and the m quantum number
      69           1 :     auto sorter = basis_unsorted->get_sorter(
      70           1 :         {TransformationType::SORT_BY_PARITY, TransformationType::SORT_BY_QUANTUM_NUMBER_M});
      71           1 :     auto basis = basis_unsorted->transformed(sorter);
      72             : 
      73             :     // Check if the basis is properly sorted
      74           1 :     auto parity = Parity::ODD;
      75           1 :     auto quantum_number_m = std::numeric_limits<double>::lowest();
      76          15 :     for (size_t i = 0; i < basis->get_number_of_states(); ++i) {
      77          14 :         DOCTEST_MESSAGE("State ", i, ": Parity = ", basis->get_parity(i),
      78             :                         ", M = ", basis->get_quantum_number_m(i));
      79          14 :         DOCTEST_CHECK(basis->get_parity(i) >= parity);
      80          14 :         if (basis->get_parity(i) != parity) {
      81           1 :             parity = basis->get_parity(i);
      82           1 :             quantum_number_m = std::numeric_limits<double>::lowest();
      83             :         }
      84          14 :         DOCTEST_CHECK(basis->get_quantum_number_m(i) >= quantum_number_m);
      85          14 :         quantum_number_m = basis->get_quantum_number_m(i);
      86             :     }
      87             : 
      88             :     // Check that the blocks are correctly determined
      89           1 :     auto blocks = basis->get_indices_of_blocks(
      90           1 :         {TransformationType::SORT_BY_PARITY, TransformationType::SORT_BY_QUANTUM_NUMBER_M});
      91           1 :     std::vector<size_t> expected_start = {0, 4, 8, 11};
      92             : 
      93           1 :     DOCTEST_CHECK(blocks.size() == expected_start.size());
      94             : 
      95           1 :     size_t idx = 0;
      96           5 :     for (const auto &block : blocks) {
      97           4 :         DOCTEST_MESSAGE("Block ", idx, " starts at ", block.start);
      98           4 :         DOCTEST_CHECK(block.start == expected_start[idx]);
      99           4 :         idx++;
     100             :     }
     101             : 
     102             :     // Test implicit conversion of an eigen matrix to a transformator
     103           1 :     size_t dim = basis->get_number_of_states();
     104             :     Eigen::SparseMatrix<double, Eigen::RowMajor> matrix(static_cast<long>(dim),
     105           1 :                                                         static_cast<long>(dim));
     106           1 :     matrix.setIdentity();
     107           1 :     auto transformed = basis->transformed(matrix);
     108           1 :     auto transformation = transformed->get_transformation();
     109           1 :     DOCTEST_CHECK(transformation.transformation_type.back() == TransformationType::ARBITRARY);
     110           1 : }
     111             : 
     112           3 : DOCTEST_TEST_CASE("calculation of matrix elements") {
     113           3 :     auto &database = Database::get_global_instance();
     114             : 
     115           3 :     auto ket_s = KetAtomCreator()
     116           6 :                      .set_species("Rb")
     117           6 :                      .set_quantum_number("n", 60)
     118           6 :                      .set_quantum_number("l", 0)
     119           6 :                      .set_quantum_number("j", 0.5)
     120           6 :                      .set_quantum_number("m", 0.5)
     121           3 :                      .create(database);
     122             : 
     123           3 :     auto ket_p = KetAtomCreator()
     124           6 :                      .set_species("Rb")
     125           6 :                      .set_quantum_number("n", 60)
     126           6 :                      .set_quantum_number("l", 1)
     127           6 :                      .set_quantum_number("j", 0.5)
     128           6 :                      .set_quantum_number("m", 0.5)
     129           3 :                      .create(database);
     130             : 
     131           3 :     auto basis = BasisAtomCreator<double>()
     132           6 :                      .set_species("Rb")
     133           6 :                      .restrict_quantum_number("n", 59, 61)
     134           6 :                      .restrict_quantum_number("l", 0, 1)
     135           6 :                      .restrict_quantum_number("m", 0.5, 0.5)
     136           3 :                      .create(database);
     137             : 
     138           3 :     SystemAtom<double> system(basis);
     139             : 
     140           4 :     auto get_corresponding_state_index = [&database](const auto &b,
     141           4 :                                                      const std::shared_ptr<const KetAtom> &ket) {
     142           4 :         auto basis_ket = BasisAtomCreator<double>().add_ket(ket).create(database);
     143           4 :         Eigen::MatrixXd overlaps =
     144           8 :             Eigen::MatrixXd(b->get_matrix_elements(basis_ket, OperatorType::IDENTITY, 0))
     145             :                 .cwiseAbs();
     146           4 :         Eigen::Index idx = 0;
     147           4 :         overlaps.row(0).maxCoeff(&idx);
     148           4 :         return idx;
     149           4 :     };
     150             : 
     151           3 :     DOCTEST_SUBCASE("calculate energy") {
     152           1 :         auto basis_ket_s = BasisAtomCreator<double>().add_ket(ket_s).create(database);
     153             : 
     154           1 :         auto m1 = basis_ket_s->get_matrix_elements(basis_ket_s, OperatorType::ENERGY, 0);
     155           1 :         DOCTEST_CHECK(m1.rows() == 1);
     156           1 :         DOCTEST_CHECK(m1.cols() == 1);
     157           1 :         double energy1 = m1.coeff(0, 0);
     158             : 
     159           1 :         auto m2 = basis->get_matrix_elements(basis_ket_s, OperatorType::ENERGY, 0);
     160           1 :         DOCTEST_CHECK(m2.rows() == 1);
     161           1 :         DOCTEST_CHECK(m2.cols() == basis->get_number_of_states());
     162           1 :         double energy2 = m2.coeff(0, static_cast<int>(get_corresponding_state_index(basis, ket_s)));
     163             : 
     164           1 :         double reference = ket_s->get_energy();
     165           1 :         DOCTEST_CHECK(std::abs(energy1 - reference) < 1e-11);
     166           1 :         DOCTEST_CHECK(std::abs(energy2 - reference) < 1e-11);
     167           4 :     }
     168             : 
     169           3 :     DOCTEST_SUBCASE("calculate electric dipole matrix element") {
     170           1 :         auto basis_ket_p = BasisAtomCreator<double>().add_ket(ket_p).create(database);
     171             : 
     172           1 :         auto m = basis->get_matrix_elements(basis_ket_p, OperatorType::ELECTRIC_DIPOLE, 0);
     173           1 :         DOCTEST_CHECK(m.rows() == 1);
     174           1 :         DOCTEST_CHECK(m.cols() == basis->get_number_of_states());
     175           1 :         double dipole = m.coeff(0, static_cast<int>(get_corresponding_state_index(basis, ket_s)));
     176             : 
     177           1 :         DOCTEST_CHECK(std::abs(dipole - 1247.6043831131365) < 1e-6);
     178           4 :     }
     179             : 
     180           3 :     DOCTEST_SUBCASE("calculate electric dipole matrix element with and without an induced dipole") {
     181             :         {
     182           1 :             auto state = basis->get_state(get_corresponding_state_index(basis, ket_s));
     183             : 
     184           1 :             auto m = state->get_matrix_elements(state, OperatorType::ELECTRIC_DIPOLE, 0);
     185           1 :             DOCTEST_CHECK(m.rows() == 1);
     186           1 :             DOCTEST_CHECK(m.cols() == 1);
     187           1 :             double dipole = m.coeff(0, 0);
     188             : 
     189           1 :             DOCTEST_CHECK(std::abs(dipole - 0) < 1e-6);
     190           1 :         }
     191             : 
     192             :         {
     193           1 :             system.set_electric_field({0, 0, VOLT_PER_CM_IN_ATOMIC_UNITS});
     194           1 :             system.diagonalize(DiagonalizerEigen<double>());
     195           1 :             auto eigenbasis = system.get_eigenbasis();
     196           1 :             auto state = eigenbasis->get_state(get_corresponding_state_index(eigenbasis, ket_s));
     197             : 
     198           1 :             auto m = state->get_matrix_elements(state, OperatorType::ELECTRIC_DIPOLE, 0);
     199           1 :             DOCTEST_CHECK(m.rows() == 1);
     200           1 :             DOCTEST_CHECK(m.cols() == 1);
     201           1 :             double dipole = m.coeff(0, 0);
     202             : 
     203           1 :             DOCTEST_CHECK(std::abs(dipole - 135.04130863117354) < 1e-6);
     204           1 :         }
     205           3 :     }
     206           3 : }
     207             : 
     208             : } // namespace pairinteraction

Generated by: LCOV version 1.16