LCOV - code coverage report
Current view: top level - src/basis - BasisPairCreator.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 62 72 86.1 %
Date: 2025-04-29 15:56:08 Functions: 5 12 41.7 %

          Line data    Source code
       1             : // SPDX-FileCopyrightText: 2024 Pairinteraction Developers
       2             : // SPDX-License-Identifier: LGPL-3.0-or-later
       3             : 
       4             : #include "pairinteraction/basis/BasisPairCreator.hpp"
       5             : 
       6             : #include "pairinteraction/basis/BasisAtom.hpp"
       7             : #include "pairinteraction/basis/BasisPair.hpp"
       8             : #include "pairinteraction/enums/Parity.hpp"
       9             : #include "pairinteraction/enums/TransformationType.hpp"
      10             : #include "pairinteraction/ket/KetAtom.hpp"
      11             : #include "pairinteraction/ket/KetPair.hpp"
      12             : #include "pairinteraction/system/SystemAtom.hpp"
      13             : 
      14             : #include <algorithm>
      15             : #include <limits>
      16             : #include <memory>
      17             : 
      18             : namespace pairinteraction {
      19             : template <typename Scalar>
      20          13 : BasisPairCreator<Scalar>::BasisPairCreator() : product_of_parities(Parity::UNKNOWN) {}
      21             : 
      22             : template <typename Scalar>
      23          26 : BasisPairCreator<Scalar> &BasisPairCreator<Scalar>::add(const SystemAtom<Scalar> &system_atom) {
      24          26 :     if (!system_atom.is_diagonal()) {
      25           0 :         throw std::invalid_argument("The system must be diagonalized before it can be added.");
      26             :     }
      27          26 :     systems_atom.push_back(system_atom);
      28          26 :     return *this;
      29             : }
      30             : 
      31             : template <typename Scalar>
      32          10 : BasisPairCreator<Scalar> &BasisPairCreator<Scalar>::restrict_energy(real_t min, real_t max) {
      33          10 :     range_energy = {min, max};
      34          10 :     return *this;
      35             : }
      36             : 
      37             : template <typename Scalar>
      38           9 : BasisPairCreator<Scalar> &BasisPairCreator<Scalar>::restrict_quantum_number_m(real_t min,
      39             :                                                                               real_t max) {
      40           9 :     range_quantum_number_m = {min, max};
      41           9 :     return *this;
      42             : }
      43             : 
      44             : template <typename Scalar>
      45           0 : BasisPairCreator<Scalar> &BasisPairCreator<Scalar>::restrict_product_of_parities(Parity value) {
      46           0 :     product_of_parities = value;
      47           0 :     return *this;
      48             : }
      49             : 
      50             : template <typename Scalar>
      51          13 : std::shared_ptr<const BasisPair<Scalar>> BasisPairCreator<Scalar>::create() const {
      52          13 :     if (systems_atom.size() != 2) {
      53           0 :         throw std::invalid_argument("Two SystemAtom must be added before creating the BasisPair.");
      54             :     }
      55             : 
      56          13 :     constexpr real_t numerical_precision = 100 * std::numeric_limits<real_t>::epsilon();
      57             : 
      58             :     // Sort the states, which are eigenstates, by their energy
      59          13 :     auto system1 = systems_atom[0].get();
      60          13 :     auto system2 = systems_atom[1].get();
      61          13 :     system1.transform(system1.get_sorter({TransformationType::SORT_BY_ENERGY}));
      62          13 :     system2.transform(system2.get_sorter({TransformationType::SORT_BY_ENERGY}));
      63             : 
      64             :     // Construct the canonical basis that contains all KetPair objects with allowed energies and
      65             :     // quantum numbers
      66          13 :     auto basis1 = system1.get_basis();
      67          13 :     auto basis2 = system2.get_basis();
      68          13 :     auto eigenenergies1 = system1.get_eigenenergies();
      69          13 :     auto eigenenergies2 = system2.get_eigenenergies();
      70          13 :     real_t *eigenenergies2_begin = eigenenergies2.data();
      71          13 :     real_t *eigenenergies2_end = eigenenergies2_begin + eigenenergies2.size();
      72             : 
      73          13 :     ketvec_t kets;
      74          13 :     kets.reserve(eigenenergies1.size() * eigenenergies2.size());
      75             : 
      76          13 :     typename basis_t::map_range_t map_range_of_state_index2;
      77          13 :     map_range_of_state_index2.reserve(eigenenergies1.size());
      78             : 
      79          13 :     typename basis_t::map_indices_t state_indices_to_ket_index;
      80             : 
      81             :     // Loop only over states with an allowed energy
      82          13 :     size_t ket_index = 0;
      83         982 :     for (size_t idx1 = 0; idx1 < static_cast<size_t>(eigenenergies1.size()); ++idx1) {
      84             :         // Get the energetically allowed range of the second index
      85         969 :         size_t min = 0;
      86         969 :         size_t max = eigenenergies2.size();
      87         969 :         if (range_energy.is_finite()) {
      88         866 :             real_t min_val2 = range_energy.min() - eigenenergies1[idx1];
      89         866 :             real_t max_val2 = range_energy.max() - eigenenergies1[idx1];
      90         866 :             min =
      91         866 :                 std::distance(eigenenergies2_begin,
      92             :                               std::lower_bound(eigenenergies2_begin, eigenenergies2_end, min_val2));
      93         866 :             max =
      94         866 :                 std::distance(eigenenergies2_begin,
      95             :                               std::upper_bound(eigenenergies2_begin, eigenenergies2_end, max_val2));
      96             :         }
      97         969 :         map_range_of_state_index2.emplace(idx1, typename basis_t::range_t(min, max));
      98             : 
      99             :         // Loop over the energetically allowed range of the second index
     100       19171 :         for (size_t idx2 = min; idx2 < max; ++idx2) {
     101             :             // Get energy
     102        9101 :             const real_t energy = eigenenergies1[idx1] + eigenenergies2[idx2];
     103        9101 :             assert(!range_energy.is_finite() ||
     104             :                    (energy >= range_energy.min() && energy <= range_energy.max()));
     105             : 
     106             :             // Check the parity of the sum of the parities
     107        9101 :             if (product_of_parities != Parity::UNKNOWN) {
     108           0 :                 if (static_cast<int>(basis1->get_parity(idx1)) *
     109           0 :                         static_cast<int>(basis2->get_parity(idx2)) !=
     110           0 :                     static_cast<int>(product_of_parities)) {
     111         607 :                     continue;
     112             :                 }
     113             :             }
     114             : 
     115             :             // Create a KetPair object
     116       27303 :             auto ket = std::make_shared<ket_t>(
     117        9101 :                 typename ket_t::Private(), std::initializer_list<size_t>{idx1, idx2},
     118             :                 std::initializer_list<std::shared_ptr<const BasisAtom<Scalar>>>{basis1, basis2},
     119             :                 energy);
     120             : 
     121             :             // Check the quantum number m
     122        9101 :             if (ket->has_quantum_number_m()) {
     123        9881 :                 if (range_quantum_number_m.is_finite() &&
     124         780 :                     (ket->get_quantum_number_m() <
     125         780 :                          range_quantum_number_m.min() - numerical_precision ||
     126         287 :                      ket->get_quantum_number_m() >
     127         287 :                          range_quantum_number_m.max() + numerical_precision)) {
     128         607 :                     continue;
     129             :                 }
     130           0 :             } else if (range_quantum_number_m.is_finite()) {
     131           0 :                 throw std::invalid_argument(
     132             :                     "The quantum number m must not be restricted because it is not well-defined.");
     133             :             }
     134             : 
     135             :             // Store the KetPair object as a ket
     136        8494 :             kets.emplace_back(std::move(ket));
     137             : 
     138             :             // Store the ket index
     139        8494 :             state_indices_to_ket_index.emplace(std::vector<size_t>{idx1, idx2}, ket_index++);
     140             :         }
     141             :     }
     142             : 
     143          13 :     kets.shrink_to_fit();
     144             : 
     145          13 :     return std::make_shared<basis_t>(typename basis_t::Private(), std::move(kets),
     146          13 :                                      std::move(map_range_of_state_index2),
     147          39 :                                      std::move(state_indices_to_ket_index), basis1, basis2);
     148        9114 : }
     149             : 
     150             : // Explicit instantiations
     151             : template class BasisPairCreator<double>;
     152             : template class BasisPairCreator<std::complex<double>>;
     153             : } // namespace pairinteraction

Generated by: LCOV version 1.16