LCOV - code coverage report
Current view: top level - pairinteraction - SystemTwo.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 693 852 81.3 %
Date: 2024-04-29 00:41:50 Functions: 43 60 71.7 %

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2016 Sebastian Weber, Henri Menke, Johannes Block. All rights reserved.
       3             :  *
       4             :  * This file is part of the pairinteraction library.
       5             :  *
       6             :  * The pairinteraction library is free software: you can redistribute it and/or modify
       7             :  * it under the terms of the GNU Lesser General Public License as published by
       8             :  * the Free Software Foundation, either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * The pairinteraction library is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU Lesser General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public License
      17             :  * along with the pairinteraction library. If not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #include "SystemTwo.hpp"
      21             : #include "Constants.hpp"
      22             : #include "GreenTensor.hpp"
      23             : #include "MatrixElementCache.hpp"
      24             : 
      25             : #include <algorithm>
      26             : #include <cmath>
      27             : #include <limits>
      28             : #include <numeric>
      29             : #include <string>
      30             : #include <unordered_set>
      31             : #include <vector>
      32             : 
      33             : template <typename Scalar>
      34           0 : SystemTwo<Scalar>::SystemTwo()
      35           0 :     : minimal_le_roy_radius(std::numeric_limits<double>::max()),
      36           0 :       distance(std::numeric_limits<double>::max()), distance_x(0), distance_y(0),
      37           0 :       distance_z(std::numeric_limits<double>::max()), GTbool(false),
      38           0 :       surface_distance(std::numeric_limits<double>::max()), ordermax(3), sym_permutation(NA),
      39           0 :       sym_inversion(NA), sym_reflection(NA), sym_rotation({ARB}) {}
      40             : 
      41             : template <typename Scalar>
      42          62 : SystemTwo<Scalar>::SystemTwo(const SystemOne<Scalar> &b1, const SystemOne<Scalar> &b2,
      43             :                              MatrixElementCache &cache)
      44         248 :     : SystemBase<Scalar, StateTwo>(cache), species({{b1.getSpecies(), b2.getSpecies()}}),
      45          62 :       system1(b1), system2(b2), minimal_le_roy_radius(std::numeric_limits<double>::max()),
      46          62 :       distance(std::numeric_limits<double>::max()), distance_x(0), distance_y(0),
      47          62 :       distance_z(std::numeric_limits<double>::max()), GTbool(false),
      48          62 :       surface_distance(std::numeric_limits<double>::max()), ordermax(3), sym_permutation(NA),
      49         124 :       sym_inversion(NA), sym_reflection(NA), sym_rotation({ARB}) {}
      50             : 
      51             : template <typename Scalar>
      52           0 : SystemTwo<Scalar>::SystemTwo(const SystemOne<Scalar> &b1, const SystemOne<Scalar> &b2,
      53             :                              MatrixElementCache &cache, bool memory_saving)
      54             :     : SystemBase<Scalar, StateTwo>(cache, memory_saving),
      55           0 :       species({{b1.getSpecies(), b2.getSpecies()}}), system1(b1), system2(b2),
      56           0 :       minimal_le_roy_radius(std::numeric_limits<double>::max()),
      57           0 :       distance(std::numeric_limits<double>::max()), distance_x(0), distance_y(0),
      58           0 :       distance_z(std::numeric_limits<double>::max()), GTbool(false),
      59           0 :       surface_distance(std::numeric_limits<double>::max()), ordermax(3), sym_permutation(NA),
      60           0 :       sym_inversion(NA), sym_reflection(NA), sym_rotation({ARB}) {}
      61             : 
      62             : template <typename Scalar>
      63             : std::vector<StateOne>
      64          95 : SystemTwo<Scalar>::getStatesFirst() { // TODO @hmenke typemap for "state_set<StateOne>"
      65          95 :     this->buildBasis();
      66         190 :     std::unordered_set<StateOne> states_one;
      67       14631 :     for (const auto &entry : this->states) {
      68       14536 :         states_one.insert(StateOne(entry.state.getFirstState()));
      69             :     }
      70         190 :     return std::vector<StateOne>(states_one.begin(), states_one.end());
      71             : }
      72             : 
      73             : template <typename Scalar>
      74             : std::vector<StateOne>
      75          95 : SystemTwo<Scalar>::getStatesSecond() { // TODO @hmenke typemap for "state_set<StateOne>"
      76          95 :     this->buildBasis();
      77         190 :     std::unordered_set<StateOne> states_one;
      78       14631 :     for (const auto &entry : this->states) {
      79       14536 :         states_one.insert(StateOne(entry.state.getSecondState()));
      80             :     }
      81         190 :     return std::vector<StateOne>(states_one.begin(), states_one.end());
      82             : }
      83             : 
      84             : template <typename Scalar>
      85           0 : const std::array<std::string, 2> &SystemTwo<Scalar>::getSpecies() {
      86           0 :     return species;
      87             : }
      88             : 
      89             : template <typename Scalar>
      90          46 : void SystemTwo<Scalar>::enableGreenTensor(bool GTboolean) {
      91          46 :     this->onParameterChange();
      92          46 :     GTbool = GTboolean;
      93          46 :     if (!GTbool && surface_distance != std::numeric_limits<double>::max()) {
      94           0 :         throw std::runtime_error("If there is interaction with a surface, the Green tensor "
      95             :                                  "approach must not be disabled.");
      96             :     }
      97          46 : }
      98             : 
      99             : template <typename Scalar>
     100          37 : void SystemTwo<Scalar>::setSurfaceDistance(double d) {
     101          37 :     this->onParameterChange();
     102          37 :     surface_distance = d;
     103          37 :     if (surface_distance != std::numeric_limits<double>::max()) {
     104          37 :         this->enableGreenTensor(true);
     105             :     }
     106          37 : }
     107             : 
     108             : template <typename Scalar>
     109         149 : void SystemTwo<Scalar>::setDistance(double d) {
     110         149 :     this->onParameterChange();
     111         149 :     distance_x = distance_x / distance * d;
     112         149 :     distance_y = distance_y / distance * d;
     113         149 :     distance_z = distance_z / distance * d;
     114         149 :     distance = d;
     115         149 : }
     116             : 
     117             : template <typename Scalar>
     118           0 : void SystemTwo<Scalar>::setDistanceVector(std::array<double, 3> d) {
     119           0 :     this->onParameterChange();
     120           0 :     distance_x = d[0];
     121           0 :     distance_y = d[1];
     122           0 :     distance_z = d[2];
     123           0 :     distance = std::sqrt(d[0] * d[0] + d[1] * d[1] + d[2] * d[2]);
     124           0 :     if (distance_y != 0) {
     125           0 :         this->enableGreenTensor(true);
     126             :     }
     127           0 : }
     128             : 
     129             : template <typename Scalar>
     130          15 : void SystemTwo<Scalar>::setAngle(double a) {
     131          15 :     this->onParameterChange();
     132          15 :     distance_x = distance * std::sin(a);
     133          15 :     distance_y = 0;
     134          15 :     distance_z = distance * std::cos(a);
     135          15 : }
     136             : 
     137             : template <typename Scalar>
     138          28 : void SystemTwo<Scalar>::setOrder(double o) {
     139          28 :     this->onParameterChange();
     140          28 :     ordermax = o;
     141          28 : }
     142             : 
     143             : template <typename Scalar>
     144          23 : void SystemTwo<Scalar>::setConservedParityUnderPermutation(parity_t parity) {
     145          23 :     this->onSymmetryChange();
     146          23 :     sym_permutation = parity;
     147          23 : }
     148             : 
     149             : template <typename Scalar>
     150          19 : void SystemTwo<Scalar>::setConservedParityUnderInversion(parity_t parity) {
     151          19 :     this->onSymmetryChange();
     152          19 :     sym_inversion = parity;
     153          19 : }
     154             : 
     155             : template <typename Scalar>
     156          10 : void SystemTwo<Scalar>::setConservedParityUnderReflection(parity_t parity) {
     157          10 :     this->onSymmetryChange();
     158          10 :     sym_reflection = parity;
     159          10 :     if (!this->isRefelectionAndRotationCompatible()) {
     160           0 :         throw std::runtime_error("The conserved parity under reflection is not compatible to the "
     161             :                                  "previously specified conserved momenta.");
     162             :     }
     163             :     // if (sym_reflection != NA) std::cerr << "Warning: The one-atom states must already be
     164             :     // reflection symmetric in order to build reflection symmetric two-atom states." << std::endl;
     165             : 
     166             :     // TODO make it work with one-atom states that are not pre-symmetrized
     167          10 : }
     168             : 
     169             : template <typename Scalar>
     170          16 : void SystemTwo<Scalar>::setConservedMomentaUnderRotation(const std::set<int> &momenta) {
     171          16 :     if (momenta.count(ARB) != 0 && momenta.size() > 1) {
     172           0 :         throw std::runtime_error(
     173             :             "If ARB (=arbitrary momentum) is specified, momenta must not be passed explicitely.");
     174             :     }
     175          16 :     this->onSymmetryChange();
     176          16 :     sym_rotation = momenta;
     177          16 :     if (!this->isRefelectionAndRotationCompatible()) {
     178           0 :         throw std::runtime_error("The conserved momenta are not compatible to the previously "
     179             :                                  "specified conserved parity under reflection.");
     180             :     }
     181          16 : }
     182             : 
     183             : template <typename Scalar>
     184           2 : void SystemTwo<Scalar>::setOneAtomBasisvectors(const std::vector<std::array<size_t, 2>> &indices) {
     185             :     // Check that all pairs of indices are unique
     186           4 :     std::vector<std::array<size_t, 2>> tmp(indices);
     187           2 :     std::sort(tmp.begin(), tmp.end());
     188           2 :     if (auto const it = std::adjacent_find(tmp.begin(), tmp.end()); it != tmp.end()) {
     189           2 :         std::string const which =
     190           2 :             "[" + std::to_string((*it)[0]) + "," + std::to_string((*it)[1]) + "]";
     191           1 :         throw std::runtime_error("The pairs of indices are not unique: " + which);
     192             :     }
     193             : 
     194           1 :     one_atom_basisvectors_indices = indices;
     195           1 : }
     196             : 
     197             : ////////////////////////////////////////////////////////////////////
     198             : /// Method that allows base class to initialize Basis //////////////
     199             : ////////////////////////////////////////////////////////////////////
     200             : 
     201             : template <typename Scalar>
     202          75 : void SystemTwo<Scalar>::initializeBasis() {
     203             :     ////////////////////////////////////////////////////////////////////
     204             :     /// Restrict one atom states to the allowed quantum numbers ////////
     205             :     ////////////////////////////////////////////////////////////////////
     206             : 
     207          75 :     system1.diagonalize(); // it is important to call this method here!
     208          75 :     system1.restrictN(this->range_n);
     209          75 :     system1.restrictL(this->range_l);
     210          75 :     system1.restrictJ(this->range_j);
     211          75 :     system1.restrictM(this->range_m);
     212          75 :     system2.diagonalize(); // it is important to call this method here!
     213          75 :     system2.restrictN(this->range_n);
     214          75 :     system2.restrictL(this->range_l);
     215          75 :     system2.restrictJ(this->range_j);
     216          75 :     system2.restrictM(this->range_m);
     217             : 
     218             :     ////////////////////////////////////////////////////////////////////
     219             :     /// Check whether the single atom states fit to the symmetries /////
     220             :     ////////////////////////////////////////////////////////////////////
     221             : 
     222          75 :     if (sym_permutation != NA) {
     223             :         // TODO check system1 == system2
     224             :     }
     225             : 
     226             :     // TODO consider further symmetries and check whether they are applicable
     227             : 
     228             :     ////////////////////////////////////////////////////////////////////
     229             :     /// Check which basis vectors contain artificial states ////////////
     230             :     ////////////////////////////////////////////////////////////////////
     231             : 
     232             :     // TODO check whether system1 == system2
     233             : 
     234         150 :     std::vector<bool> artificial1(system1.getNumBasisvectors(), false);
     235        2641 :     for (size_t col = 0; col < system1.getNumBasisvectors(); ++col) {
     236       15158 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(system1.getBasisvectors(),
     237             :                                                                         col);
     238       12592 :              triple; ++triple) {
     239       12592 :             if (system1.getStatesMultiIndex()[triple.row()].state.isArtificial()) {
     240           0 :                 artificial1[triple.col()] = true;
     241             :             }
     242             :         }
     243             :     }
     244             : 
     245         150 :     std::vector<bool> artificial2(system2.getNumBasisvectors(), false);
     246        2641 :     for (size_t col = 0; col < system2.getNumBasisvectors(); ++col) {
     247       15158 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(system2.getBasisvectors(),
     248             :                                                                         col);
     249       12592 :              triple; ++triple) {
     250       12592 :             if (system2.getStatesMultiIndex()[triple.row()].state.isArtificial()) {
     251           0 :                 artificial2[triple.col()] = true;
     252             :             }
     253             :         }
     254             :     }
     255             : 
     256             :     ////////////////////////////////////////////////////////////////////
     257             :     /// Build two atom states //////////////////////////////////////////
     258             :     ////////////////////////////////////////////////////////////////////
     259             : 
     260          75 :     auto &cache = *this->m_cache;
     261             : 
     262             :     /// Combine one atom states ////////////////////////////////////////
     263             : 
     264         150 :     std::vector<Eigen::Triplet<Scalar>> hamiltonian_triplets;
     265          75 :     hamiltonian_triplets.reserve(system1.getNumBasisvectors() * system2.getNumBasisvectors());
     266         150 :     this->states.reserve(system1.getNumStates() * system2.getNumStates() +
     267          75 :                          this->states_to_add.size());
     268         150 :     std::vector<Eigen::Triplet<Scalar>> basisvectors_triplets; // TODO reserve
     269         225 :     std::vector<double> sqnorm_list(
     270          75 :         system1.getNumStates() * system2.getNumStates() + this->states_to_add.size(), 0);
     271             : 
     272          75 :     int M = 0;
     273          75 :     int parityL = 0;
     274          75 :     int parityJ = 0;
     275          75 :     int parityM = 0;
     276             : 
     277          75 :     size_t col_new = 0;
     278             : 
     279             :     // Generate vector containing pairs of indices of one atom basisvectors
     280         150 :     std::vector<std::array<size_t, 2>> pairs;
     281          75 :     if (!one_atom_basisvectors_indices.empty()) {
     282           1 :         pairs = one_atom_basisvectors_indices;
     283             :     } else {
     284          74 :         pairs.reserve(system1.getNumBasisvectors() * system2.getNumBasisvectors());
     285        2626 :         for (size_t col_1 = 0; col_1 < system1.getNumBasisvectors(); ++col_1) {
     286      286712 :             for (size_t col_2 = 0; col_2 < system2.getNumBasisvectors(); ++col_2) {
     287      284160 :                 pairs.push_back({col_1, col_2});
     288             :             }
     289             :         }
     290             :     }
     291             : 
     292             :     // Loop over the pairs
     293      284239 :     for (const auto &[col_1, col_2] : pairs) {
     294             : 
     295             :         // In case of artificial states, some symmetries won't work
     296      284164 :         auto sym_inversion_local = sym_inversion;
     297      284164 :         auto sym_reflection_local = sym_reflection;
     298      284164 :         auto sym_rotation_local = sym_rotation;
     299      284164 :         if (artificial1[col_1] || artificial2[col_2]) {
     300           0 :             if (sym_inversion_local != NA || sym_reflection_local != NA ||
     301           0 :                 sym_rotation_local.count(ARB) == 0) {
     302             :                 std::cerr
     303           0 :                     << "WARNING: Only permutation symmetry can be applied to artificial states."
     304           0 :                     << std::endl;
     305             :             }
     306           0 :             sym_inversion_local = NA;
     307           0 :             sym_reflection_local = NA;
     308           0 :             sym_rotation_local = std::set<int>({ARB});
     309             :         }
     310             : 
     311             :         // In case of inversion or permutation symmetry: skip half of the basis vector pairs
     312      284164 :         if ((sym_inversion_local == EVEN && col_1 <= col_2) || // gerade
     313      134840 :             (sym_inversion_local == ODD && col_1 < col_2)) {   // ungerade
     314      123431 :             continue;
     315             :         }
     316      160733 :         if ((sym_permutation == EVEN && col_1 <= col_2) || // sym
     317      160128 :             (sym_permutation == ODD && col_1 < col_2)) {   // asym
     318        7614 :             continue;
     319             :         }
     320             : 
     321             :         // Continue if the pair statet energy is not valid
     322      153119 :         double energy = std::real(system1.getHamiltonian().coeff(col_1, col_1) +
     323      153119 :                                   system2.getHamiltonian().coeff(col_2, col_2));
     324      153119 :         if (!this->checkIsEnergyValid(energy)) {
     325      141699 :             continue;
     326             :         }
     327             : 
     328             :         // Store the pair state energy
     329       11420 :         hamiltonian_triplets.emplace_back(col_new, col_new, energy);
     330             : 
     331             :         // Build the basis vector that corresponds to the stored pair state energy
     332       69387 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple_1(system1.getBasisvectors(),
     333             :                                                                           col_1);
     334       57967 :              triple_1; ++triple_1) {
     335       57967 :             size_t row_1 = triple_1.row();
     336      115934 :             StateOne state_1 = system1.getStatesMultiIndex()[row_1].state;
     337             : 
     338     1282528 :             for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple_2(
     339       57967 :                      system2.getBasisvectors(), col_2);
     340     1224561 :                  triple_2; ++triple_2) {
     341     1224561 :                 size_t row_2 = triple_2.row();
     342     1224561 :                 StateOne state_2 = system2.getStatesMultiIndex()[row_2].state;
     343             : 
     344     1224561 :                 Scalar value_new = triple_1.value() * triple_2.value();
     345             : 
     346     1224561 :                 if (!artificial1[col_1] && !artificial2[col_2]) {
     347     1224561 :                     M = state_1.getM() + state_2.getM();
     348     1224561 :                     parityL = std::pow(-1, state_1.getL() + state_2.getL());
     349     1224561 :                     parityJ = std::pow(-1, state_1.getJ() + state_2.getJ());
     350     1224561 :                     parityM = std::pow(-1, M);
     351             :                 }
     352             : 
     353     1224561 :                 bool different = col_1 != col_2;
     354             : 
     355             :                 // Consider rotation symmetry
     356     1224561 :                 if (sym_rotation_local.count(ARB) == 0 && sym_rotation_local.count(M) == 0) {
     357       31378 :                     continue;
     358             :                 }
     359             : 
     360             :                 // Combine symmetries
     361     1193183 :                 bool skip_reflection = false;
     362     1193183 :                 if (different) {
     363             :                     // In case of inversion and permutation symmetry: the inversion symmetric
     364             :                     // state is already permutation symmetric
     365     1107617 :                     if (sym_inversion_local != NA && sym_permutation != NA) {
     366       24818 :                         if (((sym_inversion_local == EVEN) ? -parityL : parityL) !=
     367       12409 :                             ((sym_permutation == EVEN) ? -1 : 1)) {
     368        6162 :                             continue; // parity under inversion and permutation is different
     369             :                         }
     370             :                     }
     371             : 
     372             :                     // In case of inversion or permutation and reflection symmetry: the
     373             :                     // inversion or permutation symmetric state is already reflection symmetric
     374     1107215 :                     if (sym_inversion_local != NA && sym_reflection_local != NA &&
     375     1107215 :                         StateTwo(state_1.getReflected(), state_2.getReflected()) ==
     376     1107215 :                             StateTwo(state_2, state_1)) {
     377         800 :                         if (((sym_inversion_local == EVEN) ? -parityL : parityL) !=
     378         400 :                             ((sym_reflection_local == EVEN) ? parityL * parityJ * parityM
     379         200 :                                                             : -parityL * parityJ * parityM)) {
     380         200 :                             continue; // parity under inversion and reflection is different
     381             :                         }
     382         200 :                         skip_reflection = true; // parity under inversion and reflection is the same
     383             : 
     384     1106415 :                     } else if (sym_permutation != NA && sym_reflection_local != NA &&
     385     1106415 :                                StateTwo(state_1.getReflected(), state_2.getReflected()) ==
     386     1106415 :                                    StateTwo(state_2, state_1)) {
     387           0 :                         if (((sym_permutation == EVEN) ? -1 : 1) !=
     388           0 :                             ((sym_reflection_local == EVEN) ? parityL * parityJ * parityM
     389           0 :                                                             : -parityL * parityJ * parityM)) {
     390           0 :                             continue; // parity under permutation and reflection is different
     391             :                         }
     392           0 :                         skip_reflection =
     393             :                             true; // parity under permutation and reflection is the same
     394             :                     }
     395             :                 }
     396             : 
     397             :                 // Adapt the normalization if required by symmetries
     398     1186821 :                 if ((sym_inversion_local != NA || sym_permutation != NA) && different) {
     399      873521 :                     value_new /= std::sqrt(2);
     400             :                 }
     401     1186821 :                 if (sym_reflection_local != NA && !skip_reflection) {
     402       82704 :                     value_new /= std::sqrt(2) * std::sqrt(2);
     403             :                     // the second factor std::sqrt(2) is because of double counting
     404             :                 }
     405             : 
     406             :                 // Add an entry to the current basis vector
     407     1186821 :                 this->addBasisvectors(StateTwo(state_1, state_2), col_new, value_new,
     408             :                                       basisvectors_triplets, sqnorm_list);
     409             : 
     410             :                 // Add further entries to the current basis vector if required by symmetries
     411     1186821 :                 if (different) {
     412     1101255 :                     if (sym_inversion_local != NA) {
     413       41751 :                         Scalar v = value_new;
     414       41751 :                         v *= (sym_inversion_local == EVEN) ? -parityL : parityL;
     415       41751 :                         this->addBasisvectors(StateTwo(state_2, state_1), col_new, v,
     416             :                                               basisvectors_triplets, sqnorm_list);
     417     1059504 :                     } else if (sym_permutation != NA) {
     418      831770 :                         Scalar v = value_new;
     419      831770 :                         v *= (sym_permutation == EVEN) ? -1 : 1;
     420      831770 :                         this->addBasisvectors(StateTwo(state_2, state_1), col_new, v,
     421             :                                               basisvectors_triplets, sqnorm_list);
     422             :                     }
     423             :                 }
     424     1186821 :                 if (sym_reflection_local != NA && !skip_reflection) {
     425       82704 :                     Scalar v = value_new;
     426      124056 :                     v *= (sym_reflection_local == EVEN) ? parityL * parityJ * parityM
     427       41352 :                                                         : -parityL * parityJ * parityM;
     428       82704 :                     this->addBasisvectors(StateTwo(state_1.getReflected(), state_2.getReflected()),
     429             :                                           col_new, v, basisvectors_triplets, sqnorm_list);
     430             : 
     431       82704 :                     if (different) {
     432       76704 :                         if (sym_inversion_local != NA) {
     433        5360 :                             Scalar v = value_new;
     434        8040 :                             v *= (sym_reflection_local == EVEN) ? parityL * parityJ * parityM
     435        2680 :                                                                 : -parityL * parityJ * parityM;
     436        5360 :                             v *= (sym_inversion_local == EVEN) ? -parityL : parityL;
     437        5360 :                             this->addBasisvectors(
     438       10720 :                                 StateTwo(state_2.getReflected(), state_1.getReflected()), col_new,
     439             :                                 v, basisvectors_triplets, sqnorm_list);
     440       71344 :                         } else if (sym_permutation != NA) {
     441           0 :                             Scalar v = value_new;
     442           0 :                             v *= (sym_reflection_local == EVEN) ? parityL * parityJ * parityM
     443           0 :                                                                 : -parityL * parityJ * parityM;
     444           0 :                             v *= (sym_permutation == EVEN) ? -1 : 1;
     445           0 :                             this->addBasisvectors(
     446           0 :                                 StateTwo(state_2.getReflected(), state_1.getReflected()), col_new,
     447             :                                 v, basisvectors_triplets, sqnorm_list);
     448             :                         }
     449             :                     }
     450             :                 }
     451             :             }
     452             :         }
     453             : 
     454       11420 :         ++col_new;
     455             :     }
     456             : 
     457             :     // Delete unecessary storage
     458             :     // system1 = SystemOne(species[0], cache); // TODO
     459             :     // system2 = SystemOne(species[1], cache); // TODO
     460             : 
     461             :     /// Loop over user-defined states //////////////////////////////////
     462             : 
     463             :     // Check that the user-defined states are not already contained in the list of states
     464          75 :     for (const auto &state : this->states_to_add) {
     465           0 :         if (this->states.template get<1>().find(state) != this->states.template get<1>().end()) {
     466           0 :             throw std::runtime_error("The state " + state.str() +
     467             :                                      " is already contained in the list of states.");
     468             :         }
     469           0 :         for (int idx = 0; idx < 2; ++idx) {
     470           0 :             if (!state.isArtificial(idx) && state.getSpecies(idx) != species[idx]) {
     471           0 :                 throw std::runtime_error("The state " + state.str() + " is of the wrong species.");
     472             :             }
     473             :         }
     474             :     }
     475             : 
     476             :     // Warn if reflection symmetry is selected
     477          75 :     if (!this->states_to_add.empty() && sym_reflection != NA) {
     478           0 :         std::cerr << "WARNING: Reflection symmetry cannot be handled for user-defined states."
     479           0 :                   << std::endl;
     480             :     }
     481             : 
     482             :     // Add user-defined states
     483          75 :     for (const auto &state : this->states_to_add) {
     484           0 :         bool different = state.getFirstState() != state.getSecondState();
     485             : 
     486             :         // Get energy of the state
     487           0 :         double energy = 0;
     488           0 :         for (int idx = 0; idx < 2; ++idx) {
     489           0 :             if (!state.isArtificial(idx)) {
     490           0 :                 energy += state.getEnergy(idx, cache);
     491             :             }
     492             :         }
     493             : 
     494             :         // In case of artificial states, some symmetries won't work
     495           0 :         auto sym_inversion_local = sym_inversion;
     496           0 :         auto sym_rotation_local = sym_rotation;
     497           0 :         if (state.isArtificial(0) || state.isArtificial(1)) {
     498           0 :             if (sym_inversion_local != NA || sym_rotation_local.count(ARB) == 0) {
     499             :                 std::cerr
     500           0 :                     << "WARNING: Only permutation symmetry can be applied to artificial states."
     501           0 :                     << std::endl;
     502             :             }
     503           0 :             sym_inversion_local = NA;
     504           0 :             sym_rotation_local = std::set<int>({ARB});
     505             :         } else {
     506           0 :             M = state.getM(0) + state.getM(1);
     507           0 :             parityL = std::pow(-1, state.getL(0) + state.getL(1));
     508             :         }
     509             : 
     510             :         // Consider rotation symmetry
     511           0 :         if (sym_rotation_local.count(ARB) == 0 && sym_rotation_local.count(M) == 0) {
     512           0 :             continue;
     513             :         }
     514             : 
     515             :         // Combine symmetries (in case of inversion and permutation symmetry: the inversion
     516             :         // symmetric state is already permutation symmetric)
     517           0 :         if (sym_inversion_local != NA && sym_permutation != NA && different) {
     518           0 :             if (((sym_inversion_local == EVEN) ? -parityL : parityL) !=
     519           0 :                 ((sym_permutation == EVEN) ? -1 : 1)) {
     520           0 :                 continue; // parity under inversion and permutation is different
     521             :             }
     522             :         }
     523             : 
     524             :         // Check whether the symmetries can be realized with the states available
     525           0 :         if ((sym_inversion_local != NA || sym_permutation != NA) && different) {
     526           0 :             auto state_changed = StateTwo(state.getSecondState(), state.getFirstState());
     527           0 :             if (this->states_to_add.find(state_changed) == this->states_to_add.end()) {
     528           0 :                 throw std::runtime_error("The state " + state_changed.str() +
     529             :                                          " required by symmetries cannot be found.");
     530             :             }
     531             :         }
     532             : 
     533             :         // In case of inversion or permutation symmetry: skip half of the states
     534           0 :         if ((sym_inversion_local == EVEN &&
     535           0 :              state.getFirstState() <= state.getSecondState()) || // gerade
     536           0 :             (sym_inversion_local == ODD &&
     537           0 :              state.getFirstState() < state.getSecondState())) { // ungerade
     538           0 :             continue;
     539             :         }
     540           0 :         if ((sym_permutation == EVEN && state.getFirstState() <= state.getSecondState()) || // sym
     541           0 :             (sym_permutation == ODD && state.getFirstState() < state.getSecondState())) {   // asym
     542           0 :             continue;
     543             :         }
     544             : 
     545             :         // Store the energy of the two atom state
     546           0 :         hamiltonian_triplets.emplace_back(col_new, col_new, energy);
     547             : 
     548             :         // Adapt the normalization if required by symmetries
     549           0 :         Scalar value_new = 1;
     550           0 :         if ((sym_inversion_local != NA || sym_permutation != NA) && different) {
     551           0 :             value_new /= std::sqrt(2);
     552             :         }
     553             : 
     554             :         // Add an entry to the current basis vector
     555           0 :         this->addBasisvectors(state, col_new, value_new, basisvectors_triplets, sqnorm_list);
     556             : 
     557             :         // Add further entries to the current basis vector if required by symmetries
     558           0 :         if (different) {
     559           0 :             if (sym_inversion_local != NA) {
     560           0 :                 Scalar v = value_new;
     561           0 :                 v *= (sym_inversion_local == EVEN) ? -parityL : parityL;
     562           0 :                 this->addBasisvectors(StateTwo(state.getSecondState(), state.getFirstState()),
     563             :                                       col_new, v, basisvectors_triplets, sqnorm_list);
     564           0 :             } else if (sym_permutation != NA) {
     565           0 :                 Scalar v = value_new;
     566           0 :                 v *= (sym_permutation == EVEN) ? -1 : 1;
     567           0 :                 this->addBasisvectors(StateTwo(state.getSecondState(), state.getFirstState()),
     568             :                                       col_new, v, basisvectors_triplets, sqnorm_list);
     569             :             }
     570             :         }
     571             : 
     572           0 :         ++col_new;
     573             :     }
     574             : 
     575             :     /// Build data /////////////////////////////////////////////////////
     576             : 
     577          75 :     this->states.shrink_to_fit();
     578             : 
     579          75 :     this->basisvectors.resize(this->states.size(), col_new);
     580          75 :     this->basisvectors.setFromTriplets(basisvectors_triplets.begin(), basisvectors_triplets.end());
     581          75 :     basisvectors_triplets.clear();
     582             : 
     583          75 :     this->hamiltonian.resize(col_new, col_new);
     584          75 :     this->hamiltonian.setFromTriplets(hamiltonian_triplets.begin(), hamiltonian_triplets.end());
     585          75 :     hamiltonian_triplets.clear();
     586             : 
     587             :     ////////////////////////////////////////////////////////////////////
     588             :     /// Remove vectors with too small norm /////////////////////////////
     589             :     ////////////////////////////////////////////////////////////////////
     590             : 
     591             :     // Build transformator and remove vectors (if the squared norm is too small)
     592          75 :     std::vector<Eigen::Triplet<Scalar>> triplets_transformator;
     593          75 :     triplets_transformator.reserve(this->basisvectors.cols());
     594             : 
     595          75 :     size_t idx_new = 0;
     596       11495 :     for (int idx = 0; idx < this->basisvectors.cols(); ++idx) { // idx = col = num basis vector
     597       11420 :         double_t sqnorm = 0;
     598             : 
     599             :         // Calculate the square norm of the columns of the basisvector matrix
     600     1223357 :         for (typename Eigen::SparseMatrix<Scalar>::InnerIterator triple(this->basisvectors, idx);
     601     1211937 :              triple; ++triple) {
     602     1211937 :             sqnorm += std::pow(std::abs(triple.value()), 2);
     603             :         }
     604             : 
     605       11420 :         if (sqnorm > this->threshold_for_sqnorm) {
     606        4618 :             triplets_transformator.emplace_back(idx, idx_new++, 1);
     607             :         }
     608             :     }
     609             : 
     610          75 :     this->applyRightsideTransformator(triplets_transformator);
     611             : 
     612             :     ////////////////////////////////////////////////////////////////////
     613             :     /// Remove states that barely occur ////////////////////////////////
     614             :     ////////////////////////////////////////////////////////////////////
     615             : 
     616             :     // Build transformator and remove states if the squared norm is to small
     617       29999 :     this->removeRestrictedStates([=](const enumerated_state<StateTwo> &entry) -> bool {
     618       14962 :         return sqnorm_list[entry.idx] > this->threshold_for_sqnorm;
     619             :     });
     620             : 
     621             :     /*////////////////////////////////////////////////////////////////////
     622             :     /// Sort states ////////////////////////////////////////////////////
     623             :     ////////////////////////////////////////////////////////////////////
     624             : 
     625             :     // TODO put this into an extra method
     626             : 
     627             :     // Initialize original index locations
     628             :     std::vector<size_t> idx_sorted(states.size());
     629             :     iota(idx_sorted.begin(), idx_sorted.end(), 0);
     630             : 
     631             :     // Sort indexes based on comparing values in v
     632             :     std::sort(idx_sorted.begin(), idx_sorted.end(), [=](size_t i1, size_t i2) {return
     633             :     this->states[i1].state < this->states[i2].state;});
     634             : 
     635             :     // Make use of the sorted indexes in order to sort the states and transform the basisvectors
     636             :     accordingly states_set<StateTwo> states_new; states_new.reserve(states.size());
     637             :     triplets_transformator.clear();
     638             :     triplets_transformator.reserve(states.size());
     639             : 
     640             :     idx_new = 0;
     641             :     for (size_t idx : idx_sorted) {
     642             :         states_new.push_back(enumerated_state<StateTwo>(idx_new, states[idx].state));
     643             :         triplets_transformator.push_back(Eigen::Triplet<Scalar>(idx_new++,idx,1));
     644             :     }
     645             : 
     646             :     states_new.shrink_to_fit();
     647             :     states = states_new;
     648             :     this->applyLeftsideTransformator(triplets_transformator);*/
     649          75 : }
     650             : 
     651             : ////////////////////////////////////////////////////////////////////
     652             : /// Method that allows base class to calculate the interaction /////
     653             : ////////////////////////////////////////////////////////////////////
     654             : 
     655             : template <typename Scalar>
     656         198 : void SystemTwo<Scalar>::initializeInteraction() {
     657         198 :     if (distance == std::numeric_limits<double>::max()) {
     658         103 :         return;
     659             :     }
     660             : 
     661             :     // Check whether distance is larger than the minimal Le Roy radius
     662         198 :     this->checkDistance(distance);
     663             : 
     664             :     ////////////////////////////////////////////////////////////////////
     665             :     /// Prepare the calculation of the interaction /////////////////////
     666             :     ////////////////////////////////////////////////////////////////////
     667             : 
     668         198 :     std::unordered_set<int> interaction_angulardipole_keys;
     669         198 :     std::vector<int> interaction_greentensor_dd_keys;
     670         198 :     std::vector<int> interaction_greentensor_dq_keys;
     671         198 :     std::vector<int> interaction_greentensor_qd_keys;
     672         198 :     std::vector<int> interaction_multipole_keys;
     673             : 
     674         198 :     greentensor_terms_dd.clear();
     675         198 :     greentensor_terms_dq.clear();
     676         198 :     greentensor_terms_qd.clear();
     677         198 :     angle_terms.clear();
     678             : 
     679         198 :     auto &cache = *this->m_cache;
     680             : 
     681         198 :     double tolerance = 1e-16;
     682             : 
     683         198 :     if (distance_y != 0 || surface_distance != std::numeric_limits<double>::max() ||
     684         396 :         (distance_x != 0 && ordermax > 3) || GTbool) {
     685             : 
     686             :         // Check compatibility of settings
     687          42 :         if (!GTbool) {
     688           0 :             throw std::runtime_error(
     689             :                 "The Green tensor approach must be used if a) the distance vector has a non-zero "
     690             :                 "y-coordinate, b) a surface is present, or c) the interaction angle is non-zero "
     691             :                 "and interaction of higher order than dipole-dipole is considered.");
     692             :         }
     693          42 :         if (surface_distance != std::numeric_limits<double>::max() && distance_y != 0) {
     694           0 :             throw std::runtime_error("The atoms must be in the xz-plane if a surface is present");
     695             :         }
     696             : 
     697             :         // Calculate Green tensor
     698          42 :         GreenTensor GT(distance_x, distance_y, distance_z);
     699          42 :         if (surface_distance != std::numeric_limits<double>::max()) {
     700          37 :             GT.addSurface(surface_distance);
     701             :         }
     702             : 
     703             :         // Determine which interaction matrices have to be calculated
     704          42 :         if (ordermax >= 3) {
     705          42 :             const auto &dd_green_tensor = GT.getDDTensor();
     706         168 :             for (int i1 = 0; i1 < 3; ++i1) {
     707         504 :                 for (int i2 = 0; i2 < 3; ++i2) {
     708         378 :                     if (std::abs(dd_green_tensor(i1, i2)) > tolerance) {
     709         196 :                         greentensor_terms_dd[3 * i2 + i1] = dd_green_tensor(i1, i2);
     710         196 :                         interaction_greentensor_dd_keys.push_back(3 * i2 + i1);
     711             :                     }
     712             :                 }
     713             :             }
     714             :         }
     715          42 :         if (ordermax >= 4) {
     716           1 :             const auto &dq_green_tensor = GT.getDQTensor();
     717           1 :             const auto &qd_green_tensor = GT.getQDTensor();
     718           4 :             for (int i1 = 0; i1 < 3; ++i1) {
     719          12 :                 for (int i2 = 0; i2 < 3; ++i2) {
     720          36 :                     for (int i3 = 0; i3 < 3; i3++) {
     721          27 :                         if (std::abs(dq_green_tensor(i1, i2, i3)) > tolerance) {
     722           0 :                             greentensor_terms_dq[9 * i3 + 3 * i2 + i1] =
     723           7 :                                 dq_green_tensor(i1, i2, i3);
     724           7 :                             interaction_greentensor_dq_keys.push_back(9 * i3 + 3 * i2 + i1);
     725             :                         }
     726          27 :                         if (std::abs(qd_green_tensor(i1, i2, i3)) > tolerance) {
     727           0 :                             greentensor_terms_qd[9 * i3 + 3 * i2 + i1] =
     728           7 :                                 qd_green_tensor(i1, i2, i3);
     729           7 :                             interaction_greentensor_qd_keys.push_back(9 * i3 + 3 * i2 + i1);
     730             :                         }
     731             :                     }
     732             :                 }
     733             :             }
     734             :         }
     735          42 :         if (ordermax >= 5) {
     736           0 :             throw std::runtime_error(
     737             :                 "If the Green tensor approach is used, multipole interaction of "
     738             :                 "higher order than dipole-quadrupole cannot be considered.");
     739             :         }
     740             : 
     741         156 :     } else if (distance_x != 0) {
     742             : 
     743             :         // We know from before that distance_y is zero so that we can easily calculate the
     744             :         // interaction angle
     745           7 :         double angle = std::atan2(distance_x, distance_z);
     746             : 
     747             :         // Calculate angle terms
     748           7 :         double val = 1. - 3. * std::pow(std::cos(angle), 2); // 0,0
     749           7 :         if (std::abs(val) > tolerance) {
     750           7 :             angle_terms[3 * (0 + 1) + (0 + 1)] = val;
     751             :         }
     752           7 :         val = -1. + 1.5 * std::pow(std::sin(angle), 2); // 1,-1; -1,1
     753           7 :         if (std::abs(val) > tolerance) {
     754           7 :             angle_terms[3 * (-1 + 1) + (1 + 1)] = val;
     755             :         }
     756           7 :         val = 3. / std::sqrt(2) * std::sin(angle) * std::cos(angle); // 1,0; 0,1
     757           7 :         if (std::abs(val) > tolerance) {
     758           7 :             angle_terms[3 * (0 + 1) + (1 + 1)] = val;
     759             :         }
     760           7 :         val = -3. / std::sqrt(2) * std::sin(angle) * std::cos(angle); // -1,0; 0,-1
     761           7 :         if (std::abs(val) > tolerance) {
     762           7 :             angle_terms[3 * (-1 + 1) + (0 + 1)] = val;
     763             :         }
     764           7 :         val = -1.5 * std::pow(std::sin(angle), 2); // 1,1
     765           7 :         if (std::abs(val) > tolerance) {
     766           7 :             angle_terms[3 * (1 + 1) + (1 + 1)] = val;
     767             :         }
     768           7 :         val = -1.5 * std::pow(std::sin(angle), 2); // -1,-1
     769           7 :         if (std::abs(val) > tolerance) {
     770           7 :             angle_terms[3 * (-1 + 1) + (-1 + 1)] = val;
     771             :         }
     772             : 
     773             :         // Determine which interaction matrices have to be calculated
     774          49 :         for (const auto &a : angle_terms) {
     775          42 :             if (interaction_angulardipole.find(a.first) == interaction_angulardipole.end()) {
     776          42 :                 interaction_angulardipole_keys.insert(a.first);
     777             :             }
     778             :         }
     779             :     } else {
     780             :         // Determine which interaction matrices have to be calculated
     781         345 :         for (unsigned int order = 3; order <= ordermax; ++order) {
     782         196 :             if (interaction_multipole.find(order) == interaction_multipole.end()) {
     783          93 :                 interaction_multipole_keys.push_back(order);
     784             :             }
     785             :         }
     786             :     }
     787             : 
     788             :     // Is there something to do?
     789         198 :     bool interaction_multipole_uncalculated = !interaction_multipole_keys.empty();
     790         198 :     bool interaction_greentensor_dd_uncalculated = !interaction_greentensor_dd_keys.empty();
     791         198 :     bool interaction_greentensor_dq_uncalculated = !interaction_greentensor_dq_keys.empty();
     792         198 :     bool interaction_greentensor_qd_uncalculated = !interaction_greentensor_qd_keys.empty();
     793         198 :     bool interaction_angulardipole_uncalculated = !interaction_angulardipole_keys.empty();
     794             : 
     795             :     // Return if there is nothing to do
     796         198 :     if (!interaction_multipole_uncalculated && !interaction_greentensor_dd_uncalculated &&
     797         110 :         !interaction_greentensor_dq_uncalculated && !interaction_greentensor_qd_uncalculated &&
     798         110 :         !interaction_angulardipole_uncalculated) {
     799         103 :         return;
     800             :     }
     801             : 
     802             :     // Precalculate matrix elements
     803         190 :     auto states1 = this->getStatesFirst();
     804         190 :     auto states2 = this->getStatesSecond();
     805         238 :     for (unsigned int kappa = 1; kappa <= ordermax - 2; ++kappa) {
     806         143 :         cache.precalculateMultipole(states1, kappa);
     807         143 :         cache.precalculateMultipole(states2, kappa); // TODO check whether system1 == system2
     808             :     }
     809             : 
     810             :     ////////////////////////////////////////////////////////////////////
     811             :     /// Generate the interaction in the canonical basis ////////////////
     812             :     ////////////////////////////////////////////////////////////////////
     813             : 
     814             :     std::unordered_map<int, std::vector<Eigen::Triplet<double>>>
     815         190 :         interaction_angulardipole_triplets; // TODO reserve
     816             :     std::unordered_map<int, std::vector<Eigen::Triplet<double>>>
     817         190 :         interaction_multipole_triplets; // TODO reserve
     818             :     std::unordered_map<int, std::vector<Eigen::Triplet<Scalar>>>
     819         190 :         interaction_greentensor_dd_triplets; // TODO reserve
     820             :     std::unordered_map<int, std::vector<Eigen::Triplet<Scalar>>>
     821         190 :         interaction_greentensor_dq_triplets; // TODO reserve
     822             :     std::unordered_map<int, std::vector<Eigen::Triplet<Scalar>>>
     823         190 :         interaction_greentensor_qd_triplets; // TODO reserve
     824             : 
     825             :     /*// Categorize states // TODO
     826             :     std::unordered_map<ljm_t, std::vector<enumerated_state>> states_ordered;
     827             :     for (const auto &s : states) {
     828             :         states_categorized[ljm_t(s.getL(), s.getJ(), s.getM())].push_back(s)
     829             :     }
     830             : 
     831             :     // Reserve vectors // TODO
     832             :     for (const auto &state : states) {
     833             :         for (const auto &order : orange) {
     834             :             auto list_of_coupled_ljm = getCoupled(state, order); //, q, kappa
     835             :             for (const auto &coupled : list_of_coupled_ljm) {
     836             :                 num_elements[order] += states_categorized[coupled].size();
     837             :             }
     838             :         }
     839             :     }
     840             :     for (const auto &order : orange) {
     841             :         interaction_multipole_triplets[order].reserve(num_elements[order]);
     842             :     }
     843             :     // TODO
     844             :     for (size_t i = 0; i < calculation_required.size(); ++i) {
     845             :         // TODO
     846             :     }*/
     847             : 
     848             :     // Loop over column entries
     849       29167 :     for (const auto &c : this->states) { // TODO parallelization
     850       14536 :         if (c.state.isArtificial(0) || c.state.isArtificial(1)) {
     851           0 :             continue;
     852             :         }
     853             : 
     854             :         // Loop over row entries
     855     6220492 :         for (const auto &r : this->states) {
     856     3102978 :             if (r.state.isArtificial(0) || r.state.isArtificial(1)) {
     857           0 :                 continue;
     858             :             }
     859     3102978 :             if (r.idx < c.idx) {
     860     1544221 :                 continue;
     861             :             }
     862             : 
     863     1558757 :             int q1 = r.state.getM(0) - c.state.getM(0);
     864     1558757 :             int q2 = r.state.getM(1) - c.state.getM(1);
     865             : 
     866             :             // Green tensor approach
     867             : 
     868             :             // Begin of dipole-dipole interaction
     869     1558757 :             if (interaction_greentensor_dd_uncalculated) {
     870      767067 :                 if (selectionRulesMultipoleNew(r.state.getFirstState(), c.state.getFirstState(),
     871      862226 :                                                1) &&
     872       95159 :                     selectionRulesMultipoleNew(r.state.getSecondState(), c.state.getSecondState(),
     873             :                                                1)) { // dipole-dipole interaction
     874             : 
     875      451428 :                     for (const auto &key : interaction_greentensor_dd_keys) {
     876      372171 :                         int i1 = key % 3;
     877      372171 :                         int i2 = key / 3;
     878             : 
     879             :                         // Dipole vector of first atom
     880      372171 :                         double vec1_entry = 0;
     881      372171 :                         if (i1 == 0 && std::abs(q1) == 1) { // Calculate x-entry
     882       87876 :                             vec1_entry = 1 / std::sqrt(2.) *
     883       87876 :                                 cache.getElectricDipole(r.state.getFirstState(),
     884             :                                                         c.state.getFirstState());
     885       87876 :                             vec1_entry *= (q1 == 1) ? -1 : 1;
     886      284295 :                         } else if (i1 == 1 && std::abs(q1) == 1) { // Calculate y-entry
     887       45156 :                             vec1_entry = 1 / std::sqrt(2.) *
     888       45156 :                                 cache.getElectricDipole(r.state.getFirstState(),
     889             :                                                         c.state.getFirstState());
     890      239139 :                         } else if (i1 == 2 && q1 == 0) { // Calculate z-entry
     891       60181 :                             vec1_entry = cache.getElectricDipole(r.state.getFirstState(),
     892             :                                                                  c.state.getFirstState());
     893             :                         } else {
     894      178958 :                             continue;
     895             :                         }
     896             : 
     897             :                         // Dipole vector of second atom
     898      193213 :                         double vec2_entry = 0;
     899      193213 :                         if (i2 == 0 && std::abs(q2) == 1) { // Calculate x-entry
     900       44196 :                             vec2_entry = 1 / std::sqrt(2.) *
     901       44196 :                                 cache.getElectricDipole(r.state.getSecondState(),
     902             :                                                         c.state.getSecondState());
     903       44196 :                             vec2_entry *= (q2 == 1) ? -1 : 1;
     904      149017 :                         } else if (i2 == 1 && std::abs(q2) == 1) { // Calculate y-entry
     905       27108 :                             vec2_entry = 1 / std::sqrt(2.) *
     906       27108 :                                 cache.getElectricDipole(r.state.getSecondState(),
     907             :                                                         c.state.getSecondState());
     908      121909 :                         } else if (i2 == 2 && q2 == 0) { // Calculate z-entry
     909       29781 :                             vec2_entry = cache.getElectricDipole(r.state.getSecondState(),
     910             :                                                                  c.state.getSecondState());
     911             :                         } else {
     912       92128 :                             continue;
     913             :                         }
     914             : 
     915             :                         // Combine everything
     916      101085 :                         Scalar val = -coulombs_constant * vec1_entry * vec2_entry;
     917      101085 :                         if (i1 == 1 && i2 == 1) {
     918       27108 :                             val *= -1; //"-" from i^2
     919             :                         }
     920      101085 :                         if (key % 2 != 0) {
     921           0 :                             val *= utils::imaginary_unit<Scalar>();
     922             :                         }
     923      101085 :                         this->addTriplet(interaction_greentensor_dd_triplets[3 * i2 + i1], r.idx,
     924      101085 :                                          c.idx, val);
     925             :                     }
     926             :                 }
     927             :             } // End of dipole-dipole interaction
     928             : 
     929             :             // Begin of dipole-quadrupole interaction
     930     1558757 :             if (interaction_greentensor_dq_uncalculated) {
     931         561 :                 if (((selectionRulesMultipoleNew(r.state.getFirstState(), c.state.getFirstState(),
     932         119 :                                                  1) &&
     933         119 :                       selectionRulesMultipoleNew(r.state.getSecondState(), c.state.getSecondState(),
     934        1122 :                                                  2)) ||
     935         525 :                      (selectionRulesMultipoleNew(r.state.getFirstState(), c.state.getFirstState(),
     936          83 :                                                  1) &&
     937          83 :                       selectionRulesMultipoleNew(r.state.getSecondState(), c.state.getSecondState(),
     938             :                                                  0)))) { // dipole-quadrupole
     939         288 :                     for (const auto &key : interaction_greentensor_dq_keys) {
     940         252 :                         int i1 = (key % 9) % 3;
     941         252 :                         int i2 = (key % 9) / 3;
     942         252 :                         int i3 = key / 9;
     943         252 :                         double vec_entry = 0.;
     944         252 :                         double matrix_entry = 0.;
     945             :                         // dipole vector of atom1
     946         252 :                         if (i1 == 0 && std::abs(q1) == 1) {
     947          48 :                             vec_entry = 1. / std::sqrt(2.) *
     948          48 :                                 cache.getElectricDipole(r.state.getFirstState(),
     949             :                                                         c.state.getFirstState());
     950          48 :                             vec_entry *= (q1 == 1) ? -1. : 1.;
     951         204 :                         } else if (i1 == 1 && std::abs(q1) == 1) {
     952          48 :                             vec_entry = 1. / std::sqrt(2.) *
     953          48 :                                 cache.getElectricDipole(r.state.getFirstState(),
     954             :                                                         c.state.getFirstState());
     955         156 :                         } else if (i1 == 2 && q1 == 0) {
     956          36 :                             vec_entry = cache.getElectricDipole(r.state.getFirstState(),
     957             :                                                                 c.state.getFirstState());
     958             :                         } else {
     959         120 :                             continue;
     960             :                         }
     961             :                         // quadrupole matrix of atom2
     962         132 :                         if (i2 == 0 && i3 == 0) {
     963          12 :                             if (std::abs(q2) == 2) {
     964           0 :                                 matrix_entry =
     965           0 :                                     cache.getElectricMultipole(r.state.getSecondState(),
     966             :                                                                c.state.getSecondState(), 2) *
     967             :                                     std::sqrt(5. / 4.); // with std::sqrt(2kappa +1 /4pi)
     968          12 :                             } else if (q2 == 0) {
     969          24 :                                 matrix_entry = -std::sqrt(2. / 3.) *
     970          12 :                                         cache.getElectricMultipole(r.state.getSecondState(),
     971          12 :                                                                    c.state.getSecondState(), 2) *
     972             :                                         std::sqrt(5. / 4.) +
     973          12 :                                     std::sqrt(10. / 3.) *
     974          12 :                                         cache.getElectricMultipole(r.state.getSecondState(),
     975          12 :                                                                    c.state.getSecondState(), 0) *
     976             :                                         std::sqrt(1. / 4.);
     977             :                             }
     978         120 :                         } else if (i2 == 0 && i3 == 1) {
     979           0 :                             if (std::abs(q2) == 2) {
     980           0 :                                 matrix_entry =
     981           0 :                                     cache.getElectricMultipole(r.state.getSecondState(),
     982             :                                                                c.state.getSecondState(), 2) *
     983             :                                     std::sqrt(5. / 4.);
     984           0 :                                 matrix_entry *= (q2 == 2) ? -1. : 1.;
     985             :                             }
     986         120 :                         } else if (i2 == 0 && i3 == 2) {
     987          24 :                             if (std::abs(q2) == 1) {
     988          24 :                                 matrix_entry =
     989          24 :                                     cache.getElectricMultipole(r.state.getSecondState(),
     990             :                                                                c.state.getSecondState(), 2) *
     991             :                                     std::sqrt(5. / 4.);
     992          24 :                                 matrix_entry *= (q2 == 1) ? -1. : 1.;
     993             :                             }
     994          96 :                         } else if (i2 == 1 && i3 == 0) {
     995           0 :                             if (std::abs(q2) == 2) {
     996           0 :                                 matrix_entry =
     997           0 :                                     cache.getElectricMultipole(r.state.getSecondState(),
     998             :                                                                c.state.getSecondState(), 2) *
     999             :                                     std::sqrt(5. / 4.);
    1000           0 :                                 matrix_entry *= (q2 == 2) ? -1. : 1.;
    1001             :                             }
    1002          96 :                         } else if (i2 == 1 && i3 == 1) {
    1003          12 :                             if (std::abs(q2) == 2) {
    1004           0 :                                 matrix_entry =
    1005           0 :                                     -cache.getElectricMultipole(r.state.getSecondState(),
    1006           0 :                                                                 c.state.getSecondState(), 2) *
    1007             :                                     std::sqrt(5. / 4.); // with std::sqrt(2kappa +1 /4pi)
    1008          12 :                             } else if (q2 == 0) {
    1009          24 :                                 matrix_entry = -std::sqrt(2. / 3.) *
    1010          12 :                                         cache.getElectricMultipole(r.state.getSecondState(),
    1011          12 :                                                                    c.state.getSecondState(), 2) *
    1012             :                                         std::sqrt(5. / 4.) +
    1013          12 :                                     std::sqrt(10. / 3.) *
    1014          12 :                                         cache.getElectricMultipole(r.state.getSecondState(),
    1015          12 :                                                                    c.state.getSecondState(), 0) *
    1016             :                                         std::sqrt(1. / 4.);
    1017             :                             }
    1018          84 :                         } else if (i2 == 1 && i3 == 2) {
    1019          24 :                             if (std::abs(q2) == 1) {
    1020          24 :                                 matrix_entry =
    1021          24 :                                     cache.getElectricMultipole(r.state.getSecondState(),
    1022             :                                                                c.state.getSecondState(), 2) *
    1023             :                                     std::sqrt(5. / 4.);
    1024             :                             }
    1025          60 :                         } else if (i2 == 2 && i3 == 0) {
    1026          24 :                             if (std::abs(q2) == 1) {
    1027          24 :                                 matrix_entry =
    1028          24 :                                     cache.getElectricMultipole(r.state.getSecondState(),
    1029             :                                                                c.state.getSecondState(), 2) *
    1030             :                                     std::sqrt(5. / 4.);
    1031          24 :                                 matrix_entry *= (q2 == 1) ? -1. : 1.;
    1032             :                             }
    1033             : 
    1034          36 :                         } else if (i2 == 2 && i3 == 1) {
    1035          24 :                             if (std::abs(q2) == 1) {
    1036          24 :                                 matrix_entry =
    1037          24 :                                     cache.getElectricMultipole(r.state.getSecondState(),
    1038             :                                                                c.state.getSecondState(), 2) *
    1039             :                                     std::sqrt(5. / 4.);
    1040             :                             }
    1041          12 :                         } else if (i2 == 2 && i3 == 2) {
    1042          12 :                             if (q2 == 0) {
    1043          24 :                                 matrix_entry = std::sqrt(8. / 3.) *
    1044          12 :                                         cache.getElectricMultipole(r.state.getSecondState(),
    1045          12 :                                                                    c.state.getSecondState(), 2) *
    1046             :                                         std::sqrt(5. / 4.) +
    1047          12 :                                     std::sqrt(10. / 3.) *
    1048          12 :                                         cache.getElectricMultipole(r.state.getSecondState(),
    1049          12 :                                                                    c.state.getSecondState(), 0) *
    1050             :                                         std::sqrt(1. / 4.);
    1051             :                             }
    1052             :                         }
    1053         132 :                         if (std::abs(vec_entry * matrix_entry) > tolerance) {
    1054         132 :                             Scalar val =
    1055         132 :                                 coulombs_constant * vec_entry * matrix_entry / std::sqrt(30.);
    1056         132 :                             if ((i1 == 1) && (i2 != i3 && (i2 == 1 || i3 == 1))) {
    1057          48 :                                 val *= -1.; // from i^2
    1058             :                             }
    1059         132 :                             if (key % 2 != 0) {
    1060           0 :                                 val *= utils::imaginary_unit<Scalar>();
    1061             :                             }
    1062         132 :                             this->addTriplet(
    1063         132 :                                 interaction_greentensor_dq_triplets[9 * i3 + 3 * i2 + i1], r.idx,
    1064         132 :                                 c.idx, val);
    1065             :                         }
    1066             :                     }
    1067             :                 }
    1068             :             }
    1069             : 
    1070     1558757 :             if (interaction_greentensor_qd_uncalculated) {
    1071         561 :                 if (((selectionRulesMultipoleNew(r.state.getFirstState(), c.state.getFirstState(),
    1072         271 :                                                  2) &&
    1073         271 :                       selectionRulesMultipoleNew(r.state.getSecondState(), c.state.getSecondState(),
    1074        1122 :                                                  1)) ||
    1075         525 :                      (selectionRulesMultipoleNew(r.state.getFirstState(), c.state.getFirstState(),
    1076          53 :                                                  0) &&
    1077          53 :                       selectionRulesMultipoleNew(r.state.getSecondState(), c.state.getSecondState(),
    1078             :                                                  1)))) {
    1079             : 
    1080             :                     // quadrupole dipole terms
    1081         288 :                     for (const auto &key : interaction_greentensor_qd_keys) {
    1082         252 :                         int i1 = (key % 9) % 3;
    1083         252 :                         int i2 = (key % 9) / 3;
    1084         252 :                         int i3 = key / 9;
    1085         252 :                         double vec_entry = 0.;
    1086         252 :                         double matrix_entry = 0.;
    1087             : 
    1088             :                         // dipole vector of atom 2
    1089         252 :                         if (i3 == 0 && std::abs(q2) == 1) {
    1090          48 :                             vec_entry = 1. / std::sqrt(2.) *
    1091          48 :                                 cache.getElectricDipole(r.state.getSecondState(),
    1092             :                                                         c.state.getSecondState());
    1093          48 :                             vec_entry *= (q2 == 1) ? -1. : 1.;
    1094         204 :                         } else if (i3 == 1 && std::abs(q2) == 1) {
    1095          48 :                             vec_entry = 1. / std::sqrt(2.) *
    1096          48 :                                 cache.getElectricDipole(r.state.getSecondState(),
    1097             :                                                         c.state.getSecondState());
    1098         156 :                         } else if (i3 == 2 && q2 == 0) {
    1099          36 :                             vec_entry = cache.getElectricDipole(r.state.getSecondState(),
    1100             :                                                                 c.state.getSecondState());
    1101             :                         } else {
    1102         120 :                             continue;
    1103             :                         }
    1104             :                         // quadrupole matrix of atom 1
    1105         132 :                         if (i1 == 0 && i2 == 0) {
    1106          12 :                             if (std::abs(q1) == 2) {
    1107           0 :                                 matrix_entry =
    1108           0 :                                     cache.getElectricMultipole(r.state.getFirstState(),
    1109             :                                                                c.state.getFirstState(), 2) *
    1110             :                                     std::sqrt(5. / 4.);
    1111          12 :                             } else if (q1 == 0) {
    1112          24 :                                 matrix_entry = -std::sqrt(2. / 3.) *
    1113          12 :                                         cache.getElectricMultipole(r.state.getFirstState(),
    1114          12 :                                                                    c.state.getFirstState(), 2) *
    1115             :                                         std::sqrt(5. / 4.) +
    1116          12 :                                     std::sqrt(10. / 3.) *
    1117          12 :                                         cache.getElectricMultipole(r.state.getFirstState(),
    1118          12 :                                                                    c.state.getFirstState(), 0) *
    1119             :                                         std::sqrt(1. / 4.);
    1120             :                             }
    1121         120 :                         } else if (i1 == 0 && i2 == 1) {
    1122           0 :                             if (std::abs(q1) == 2) {
    1123           0 :                                 matrix_entry =
    1124           0 :                                     cache.getElectricMultipole(r.state.getFirstState(),
    1125             :                                                                c.state.getFirstState(), 2) *
    1126             :                                     std::sqrt(5. / 4.);
    1127           0 :                                 matrix_entry *= (q1 == 2) ? -1. : 1.;
    1128             :                             }
    1129         120 :                         } else if (i1 == 0 && i2 == 2) {
    1130          24 :                             if (std::abs(q1) == 1) {
    1131          24 :                                 matrix_entry =
    1132          24 :                                     cache.getElectricMultipole(r.state.getFirstState(),
    1133             :                                                                c.state.getFirstState(), 2) *
    1134             :                                     std::sqrt(5. / 4.);
    1135          24 :                                 matrix_entry *= (q1 == 1) ? -1. : 1.;
    1136             :                             }
    1137          96 :                         } else if (i1 == 1 && i2 == 0) {
    1138           0 :                             if (std::abs(q1) == 2) {
    1139           0 :                                 matrix_entry =
    1140           0 :                                     cache.getElectricMultipole(r.state.getFirstState(),
    1141             :                                                                c.state.getFirstState(), 2) *
    1142             :                                     std::sqrt(5. / 4.);
    1143           0 :                                 matrix_entry *= (q1 == 2) ? -1. : 1.;
    1144             :                             }
    1145          96 :                         } else if (i1 == 1 && i2 == 1) {
    1146          12 :                             if (std::abs(q1) == 2) {
    1147           0 :                                 matrix_entry =
    1148           0 :                                     -cache.getElectricMultipole(r.state.getFirstState(),
    1149           0 :                                                                 c.state.getFirstState(), 2) *
    1150             :                                     std::sqrt(5. / 4.);
    1151          12 :                             } else if (q1 == 0) {
    1152          24 :                                 matrix_entry = -std::sqrt(2. / 3.) *
    1153          12 :                                         cache.getElectricMultipole(r.state.getFirstState(),
    1154          12 :                                                                    c.state.getFirstState(), 2) *
    1155             :                                         std::sqrt(5. / 4.) +
    1156          12 :                                     std::sqrt(10. / 3.) *
    1157          12 :                                         cache.getElectricMultipole(r.state.getFirstState(),
    1158          12 :                                                                    c.state.getFirstState(), 0) *
    1159             :                                         std::sqrt(1. / 4.);
    1160             :                             }
    1161          84 :                         } else if (i1 == 1 && i2 == 2) {
    1162          24 :                             if (std::abs(q1) == 1) {
    1163          24 :                                 matrix_entry =
    1164          24 :                                     cache.getElectricMultipole(r.state.getFirstState(),
    1165             :                                                                c.state.getFirstState(), 2) *
    1166             :                                     std::sqrt(5. / 4.);
    1167             :                             }
    1168          60 :                         } else if (i1 == 2 && i2 == 0) {
    1169          24 :                             if (std::abs(q1) == 1) {
    1170          24 :                                 matrix_entry =
    1171          24 :                                     cache.getElectricMultipole(r.state.getFirstState(),
    1172             :                                                                c.state.getFirstState(), 2) *
    1173             :                                     std::sqrt(5. / 4.);
    1174          24 :                                 matrix_entry *= (q1 == 1) ? -1. : 1.;
    1175             :                             }
    1176             : 
    1177          36 :                         } else if (i1 == 2 && i2 == 1) {
    1178          24 :                             if (std::abs(q1) == 1) {
    1179          24 :                                 matrix_entry =
    1180          24 :                                     cache.getElectricMultipole(r.state.getFirstState(),
    1181             :                                                                c.state.getFirstState(), 2) *
    1182             :                                     std::sqrt(5. / 4.);
    1183             :                             }
    1184          12 :                         } else if (i1 == 2 && i2 == 2) {
    1185          12 :                             if (q1 == 0) {
    1186          24 :                                 matrix_entry = std::sqrt(8. / 3.) *
    1187          12 :                                         cache.getElectricMultipole(r.state.getFirstState(),
    1188          12 :                                                                    c.state.getFirstState(), 2) *
    1189             :                                         std::sqrt(5. / 4.) +
    1190          12 :                                     std::sqrt(10. / 3.) *
    1191          12 :                                         cache.getElectricMultipole(r.state.getFirstState(),
    1192          12 :                                                                    c.state.getFirstState(), 0) *
    1193             :                                         std::sqrt(1. / 4.);
    1194             :                             }
    1195             :                         }
    1196         132 :                         if (std::abs(vec_entry * matrix_entry) > tolerance) {
    1197         132 :                             Scalar val =
    1198         132 :                                 coulombs_constant * vec_entry * matrix_entry / std::sqrt(30.);
    1199         132 :                             if ((i3 == 1) && (i1 != i2 && (i1 == 1 || i2 == 1))) {
    1200          48 :                                 val *= -1.; // from i^2
    1201             :                             }
    1202         132 :                             if (key % 2 != 0) {
    1203           0 :                                 val *= utils::imaginary_unit<Scalar>();
    1204             :                             }
    1205         132 :                             this->addTriplet(
    1206         132 :                                 interaction_greentensor_qd_triplets[9 * i3 + 3 * i2 + i1], r.idx,
    1207         132 :                                 c.idx, val);
    1208             :                         }
    1209             :                     }
    1210             :                 }
    1211             :             } // End of dipole-quadrupole interaction
    1212             : 
    1213             :             // Angular dependent dipole-dipole interaction (setAngle and setOrder take care that
    1214             :             // a non-zero angle cannot occur for other interaction than dipole-dipole if the
    1215             :             // Green tensor approach is not used)
    1216     1558757 :             if (interaction_angulardipole_uncalculated) {
    1217      276250 :                 if (selectionRulesMultipoleNew(r.state.getFirstState(), c.state.getFirstState(),
    1218      325890 :                                                1) &&
    1219       49640 :                     selectionRulesMultipoleNew(r.state.getSecondState(), c.state.getSecondState(),
    1220             :                                                1)) {
    1221       20432 :                     auto key = (q1 < q2) ? 3 * (q1 + 1) + (q2 + 1) : 3 * (q2 + 1) + (q1 + 1);
    1222             : 
    1223       20432 :                     if (interaction_angulardipole_keys.count(key) != 0) {
    1224       40864 :                         double val = coulombs_constant *
    1225       20432 :                             cache.getElectricDipole(r.state.getFirstState(),
    1226             :                                                     c.state.getFirstState()) *
    1227       20432 :                             cache.getElectricDipole(r.state.getSecondState(),
    1228             :                                                     c.state.getSecondState());
    1229             : 
    1230       20432 :                         this->addTriplet(interaction_angulardipole_triplets[key], r.idx, c.idx,
    1231             :                                          val);
    1232             :                     }
    1233             :                 }
    1234             :             }
    1235             : 
    1236             :             // Multipole interaction
    1237     1558757 :             if (interaction_multipole_uncalculated) {
    1238      515440 :                 if (q1 + q2 == 0) {
    1239      328609 :                     for (const auto &order : interaction_multipole_keys) {
    1240             : 
    1241      204896 :                         double val = 0;
    1242      531286 :                         for (int kappa1 = 1; kappa1 <= order - 2; ++kappa1) {
    1243             : 
    1244      326390 :                             int kappa2 = order - 1 - kappa1;
    1245      326390 :                             if (selectionRulesMultipoleNew(r.state.getFirstState(),
    1246      433245 :                                                            c.state.getFirstState(), kappa1) &&
    1247      106855 :                                 selectionRulesMultipoleNew(r.state.getSecondState(),
    1248             :                                                            c.state.getSecondState(), kappa2)) {
    1249             : 
    1250      125631 :                                 double binomials = boost::math::binomial_coefficient<double>(
    1251       41877 :                                                        kappa1 + kappa2, kappa1 + q1) *
    1252       83754 :                                     boost::math::binomial_coefficient<double>(kappa1 + kappa2,
    1253       41877 :                                                                               kappa2 - q2);
    1254             : 
    1255       41877 :                                 val += coulombs_constant * std::pow(-1, kappa2) *
    1256       83754 :                                     std::sqrt(binomials) *
    1257       41877 :                                     cache.getElectricMultipole(r.state.getFirstState(),
    1258       41877 :                                                                c.state.getFirstState(), kappa1) *
    1259       41877 :                                     cache.getElectricMultipole(r.state.getSecondState(),
    1260             :                                                                c.state.getSecondState(), kappa2);
    1261             :                             }
    1262             :                         }
    1263             : 
    1264      204896 :                         this->addTriplet(interaction_multipole_triplets[order], r.idx, c.idx, val);
    1265             :                     }
    1266             :                 }
    1267             :             }
    1268             :         }
    1269             :     }
    1270             : 
    1271             :     ////////////////////////////////////////////////////////////////////
    1272             :     /// Build and transform the interaction to the used basis //////////
    1273             :     ////////////////////////////////////////////////////////////////////
    1274             : 
    1275             :     // Build the interaction and change it from the canonical to the symmetrized basis
    1276             : 
    1277         291 :     for (const auto &i : interaction_greentensor_dd_keys) {
    1278         196 :         interaction_greentensor_dd[i].resize(this->states.size(), this->states.size());
    1279         392 :         interaction_greentensor_dd[i].setFromTriplets(
    1280         196 :             interaction_greentensor_dd_triplets[i].begin(),
    1281         196 :             interaction_greentensor_dd_triplets[i].end());
    1282         196 :         interaction_greentensor_dd_triplets[i].clear();
    1283             : 
    1284         392 :         interaction_greentensor_dd[i] = this->basisvectors.adjoint() *
    1285         588 :             interaction_greentensor_dd[i].template selfadjointView<Eigen::Lower>() *
    1286             :             this->basisvectors;
    1287             :     }
    1288         102 :     for (const auto &i : interaction_greentensor_dq_keys) {
    1289           7 :         interaction_greentensor_dq[i].resize(this->states.size(), this->states.size());
    1290          14 :         interaction_greentensor_dq[i].setFromTriplets(
    1291           7 :             interaction_greentensor_dq_triplets[i].begin(),
    1292           7 :             interaction_greentensor_dq_triplets[i].end());
    1293           7 :         interaction_greentensor_dq_triplets[i].clear();
    1294             : 
    1295          14 :         interaction_greentensor_dq[i] = this->basisvectors.adjoint() *
    1296          21 :             interaction_greentensor_dq[i].template selfadjointView<Eigen::Lower>() *
    1297             :             this->basisvectors;
    1298             :     }
    1299         102 :     for (const auto &i : interaction_greentensor_qd_keys) {
    1300           7 :         interaction_greentensor_qd[i].resize(this->states.size(), this->states.size());
    1301          14 :         interaction_greentensor_qd[i].setFromTriplets(
    1302           7 :             interaction_greentensor_qd_triplets[i].begin(),
    1303           7 :             interaction_greentensor_qd_triplets[i].end());
    1304           7 :         interaction_greentensor_qd_triplets[i].clear();
    1305             : 
    1306          14 :         interaction_greentensor_qd[i] = this->basisvectors.adjoint() *
    1307          21 :             interaction_greentensor_qd[i].template selfadjointView<Eigen::Lower>() *
    1308             :             this->basisvectors;
    1309             :     }
    1310         137 :     for (const auto &i : interaction_angulardipole_keys) {
    1311          42 :         interaction_angulardipole[i].resize(this->states.size(), this->states.size());
    1312          84 :         interaction_angulardipole[i].setFromTriplets(interaction_angulardipole_triplets[i].begin(),
    1313          42 :                                                      interaction_angulardipole_triplets[i].end());
    1314          42 :         interaction_angulardipole_triplets[i].clear();
    1315             : 
    1316          84 :         interaction_angulardipole[i] = this->basisvectors.adjoint() *
    1317         126 :             interaction_angulardipole[i].template selfadjointView<Eigen::Lower>() *
    1318             :             this->basisvectors;
    1319             :     }
    1320             : 
    1321         188 :     for (const auto &i : interaction_multipole_keys) {
    1322          93 :         interaction_multipole[i].resize(this->states.size(), this->states.size());
    1323         186 :         interaction_multipole[i].setFromTriplets(interaction_multipole_triplets[i].begin(),
    1324          93 :                                                  interaction_multipole_triplets[i].end());
    1325          93 :         interaction_multipole_triplets[i].clear();
    1326             : 
    1327         186 :         interaction_multipole[i] = this->basisvectors.adjoint() *
    1328         279 :             interaction_multipole[i].template selfadjointView<Eigen::Lower>() * this->basisvectors;
    1329             :     }
    1330             : }
    1331             : 
    1332             : ////////////////////////////////////////////////////////////////////
    1333             : /// Method that allows base class to construct Hamiltonian /////////
    1334             : ////////////////////////////////////////////////////////////////////
    1335             : 
    1336             : template <typename Scalar>
    1337         192 : void SystemTwo<Scalar>::addInteraction() {
    1338         192 :     if (distance == std::numeric_limits<double>::max()) {
    1339           0 :         return;
    1340             :     }
    1341             : 
    1342             :     // Build the total Hamiltonian
    1343         192 :     if (GTbool) {
    1344         236 :         for (const auto &g : greentensor_terms_dd) {
    1345         195 :             this->hamiltonian += interaction_greentensor_dd[g.first] * g.second;
    1346             :         }
    1347          48 :         for (const auto &g : greentensor_terms_dq) {
    1348           7 :             this->hamiltonian += interaction_greentensor_dq[g.first] * g.second;
    1349             :         }
    1350          48 :         for (const auto &g : greentensor_terms_qd) {
    1351           7 :             this->hamiltonian += interaction_greentensor_qd[g.first] * g.second;
    1352             :         }
    1353         151 :     } else if (distance_x != 0) {
    1354           7 :         double powerlaw = 1. / std::pow(distance, 3);
    1355          49 :         for (const auto &a : angle_terms) {
    1356          42 :             this->hamiltonian += interaction_angulardipole[a.first] * a.second * powerlaw;
    1357             :         }
    1358             :     } else {
    1359         335 :         for (unsigned int order = 3; order <= ordermax; ++order) {
    1360         191 :             double powerlaw = 1. / std::pow(distance, order);
    1361         191 :             this->hamiltonian += interaction_multipole[order] * powerlaw;
    1362             :         }
    1363             :     }
    1364             : }
    1365             : 
    1366             : ////////////////////////////////////////////////////////////////////
    1367             : /// Method that allows base class to transform the interaction /////
    1368             : ////////////////////////////////////////////////////////////////////
    1369             : 
    1370             : template <typename Scalar>
    1371          85 : void SystemTwo<Scalar>::transformInteraction(const Eigen::SparseMatrix<Scalar> &transformator) {
    1372          85 :     for (auto &entry : interaction_greentensor_dd) {
    1373           0 :         entry.second = transformator.adjoint() * entry.second * transformator;
    1374             :     }
    1375          85 :     for (auto &entry : interaction_greentensor_dq) {
    1376           0 :         entry.second = transformator.adjoint() * entry.second * transformator;
    1377             :     }
    1378          85 :     for (auto &entry : interaction_greentensor_qd) {
    1379           0 :         entry.second = transformator.adjoint() * entry.second * transformator;
    1380             :     }
    1381          97 :     for (auto &entry : interaction_angulardipole) {
    1382          12 :         entry.second = transformator.adjoint() * entry.second * transformator;
    1383             :     }
    1384          89 :     for (auto &entry : interaction_multipole) {
    1385           4 :         entry.second = transformator.adjoint() * entry.second * transformator; // NOLINT
    1386             :     }
    1387          85 : }
    1388             : 
    1389             : ////////////////////////////////////////////////////////////////////
    1390             : /// Method that allows base class to delete the interaction ////////
    1391             : ////////////////////////////////////////////////////////////////////
    1392             : 
    1393             : template <typename Scalar>
    1394          33 : void SystemTwo<Scalar>::deleteInteraction() {
    1395          33 :     interaction_greentensor_dd.clear();
    1396          33 :     interaction_greentensor_dq.clear();
    1397          33 :     interaction_greentensor_qd.clear();
    1398          33 :     interaction_angulardipole.clear();
    1399          33 :     interaction_multipole.clear();
    1400          33 : }
    1401             : 
    1402             : ////////////////////////////////////////////////////////////////////
    1403             : /// Methods that allows base class to rotate states ////////////////
    1404             : ////////////////////////////////////////////////////////////////////
    1405             : 
    1406             : template <typename Scalar>
    1407             : Eigen::SparseMatrix<Scalar>
    1408          39 : SystemTwo<Scalar>::rotateStates(const std::vector<size_t> &states_indices, double alpha,
    1409             :                                 double beta, double gamma) {
    1410             :     // Initialize Wigner D matrix
    1411          39 :     WignerD wigner;
    1412             : 
    1413             :     // Rotate state
    1414          78 :     std::vector<Eigen::Triplet<Scalar>> states_rotated_triplets;
    1415          39 :     states_rotated_triplets.reserve(
    1416          39 :         std::min(static_cast<size_t>(10 * 10), this->states.size()) *
    1417          39 :         states_indices.size()); // TODO std::min( (2*jmax+1)*(2*jmax+1), states.size() ) *
    1418             :     // states_indices.size()
    1419             : 
    1420          39 :     size_t current = 0;
    1421          78 :     for (auto const &idx : states_indices) {
    1422          39 :         this->addRotated(this->states[idx].state, current++, states_rotated_triplets, wigner, alpha,
    1423             :                          beta, gamma);
    1424             :     }
    1425             : 
    1426          39 :     Eigen::SparseMatrix<Scalar> states_rotated(this->states.size(), states_indices.size());
    1427          39 :     states_rotated.setFromTriplets(states_rotated_triplets.begin(), states_rotated_triplets.end());
    1428          39 :     states_rotated_triplets.clear();
    1429             : 
    1430          78 :     return states_rotated;
    1431             : }
    1432             : 
    1433             : template <typename Scalar>
    1434           1 : Eigen::SparseMatrix<Scalar> SystemTwo<Scalar>::buildStaterotator(double alpha, double beta,
    1435             :                                                                  double gamma) {
    1436             :     // Initialize Wigner D matrix
    1437           1 :     WignerD wigner;
    1438             : 
    1439             :     // Build rotator
    1440           2 :     std::vector<Eigen::Triplet<Scalar>> rotator_triplets;
    1441           1 :     rotator_triplets.reserve(
    1442           1 :         std::min(static_cast<size_t>(10 * 10), this->states.size()) *
    1443             :         this->states
    1444           1 :             .size()); // TODO std::min( (2*jmax+1)*(2*jmax+1), states.size() ) * states.size()
    1445             : 
    1446         537 :     for (auto const &entry : this->states) {
    1447         268 :         this->addRotated(entry.state, entry.idx, rotator_triplets, wigner, alpha, beta, gamma);
    1448             :     }
    1449             : 
    1450           1 :     Eigen::SparseMatrix<Scalar> rotator(this->states.size(), this->states.size());
    1451           1 :     rotator.setFromTriplets(rotator_triplets.begin(), rotator_triplets.end()); // NOLINT
    1452           1 :     rotator_triplets.clear();
    1453             : 
    1454           2 :     return rotator;
    1455             : }
    1456             : 
    1457             : ////////////////////////////////////////////////////////////////////
    1458             : /// Method that allows base class to combine systems ///////////////
    1459             : ////////////////////////////////////////////////////////////////////
    1460             : 
    1461             : template <typename Scalar>
    1462          33 : void SystemTwo<Scalar>::incorporate(SystemBase<Scalar, StateTwo> &system) {
    1463             :     // Combine parameters
    1464          33 :     if (species[0] != dynamic_cast<SystemTwo &>(system).species[0]) {
    1465           0 :         throw std::runtime_error(
    1466             :             "The value of the variable 'element' must be the same for both systems.");
    1467             :     }
    1468          33 :     if (species[1] != dynamic_cast<SystemTwo &>(system).species[1]) {
    1469           0 :         throw std::runtime_error(
    1470             :             "The value of the variable 'element' must be the same for both systems.");
    1471             :     }
    1472          33 :     if (distance_x != dynamic_cast<SystemTwo &>(system).distance_x) {
    1473           0 :         throw std::runtime_error(
    1474             :             "The value of the variable 'distance' must be the same for both systems.");
    1475             :     }
    1476          33 :     if (distance_y != dynamic_cast<SystemTwo &>(system).distance_y) {
    1477           0 :         throw std::runtime_error(
    1478             :             "The value of the variable 'distance' must be the same for both systems.");
    1479             :     }
    1480          33 :     if (distance_z != dynamic_cast<SystemTwo &>(system).distance_z) {
    1481           0 :         throw std::runtime_error(
    1482             :             "The value of the variable 'distance' must be the same for both systems.");
    1483             :     }
    1484          33 :     if (ordermax != dynamic_cast<SystemTwo &>(system).ordermax) {
    1485           0 :         throw std::runtime_error(
    1486             :             "The value of the variable 'ordermax' must be the same for both systems.");
    1487             :     }
    1488             : 
    1489             :     // Combine symmetries
    1490          33 :     unsigned int num_different_symmetries = 0;
    1491          33 :     if (sym_permutation != dynamic_cast<SystemTwo &>(system).sym_permutation) {
    1492           8 :         sym_permutation = NA;
    1493           8 :         ++num_different_symmetries;
    1494             :     }
    1495          33 :     if (sym_inversion != dynamic_cast<SystemTwo &>(system).sym_inversion) {
    1496           7 :         sym_inversion = NA;
    1497           7 :         ++num_different_symmetries;
    1498             :     }
    1499          33 :     if (sym_reflection != dynamic_cast<SystemTwo &>(system).sym_reflection) {
    1500           4 :         sym_reflection = NA;
    1501           4 :         ++num_different_symmetries;
    1502             :     }
    1503          61 :     if (!(sym_rotation.size() == dynamic_cast<SystemTwo &>(system).sym_rotation.size() &&
    1504          28 :           std::equal(sym_rotation.begin(), sym_rotation.end(),
    1505          28 :                      dynamic_cast<SystemTwo &>(system).sym_rotation.begin()))) {
    1506          12 :         if (sym_rotation.count(ARB) != 0 ||
    1507           6 :             dynamic_cast<SystemTwo &>(system).sym_rotation.count(ARB) != 0) {
    1508           0 :             sym_rotation = {ARB};
    1509             :         } else {
    1510           6 :             sym_rotation.insert(dynamic_cast<SystemTwo &>(system).sym_rotation.begin(),
    1511           6 :                                 dynamic_cast<SystemTwo &>(system).sym_rotation.end());
    1512             :         }
    1513           6 :         ++num_different_symmetries;
    1514             :     }
    1515             :     // if (num_different_symmetries > 1) std::cerr << "Warning: The systems differ in more than one
    1516             :     // symmetry. For the combined system, the notion of symmetries might be meaningless." <<
    1517             :     // std::endl; // TODO let the user enable/disable this warning
    1518             : 
    1519             :     // Clear cached interaction
    1520          33 :     this->deleteInteraction();
    1521          33 : }
    1522             : 
    1523             : ////////////////////////////////////////////////////////////////////
    1524             : /// Methods that allows base class to communicate with subclass ////
    1525             : ////////////////////////////////////////////////////////////////////
    1526             : 
    1527             : template <typename Scalar>
    1528           0 : void SystemTwo<Scalar>::onStatesChange() {
    1529           0 :     minimal_le_roy_radius = std::numeric_limits<double>::max();
    1530           0 : }
    1531             : 
    1532             : ////////////////////////////////////////////////////////////////////
    1533             : /// Utility methods ////////////////////////////////////////////////
    1534             : ////////////////////////////////////////////////////////////////////
    1535             : 
    1536             : template <typename Scalar>
    1537         198 : void SystemTwo<Scalar>::checkDistance(const double &distance) {
    1538             : 
    1539         198 :     auto &cache = *this->m_cache;
    1540             : 
    1541             :     // Get the minimal Le Roy radius
    1542         198 :     if (minimal_le_roy_radius == std::numeric_limits<double>::max()) {
    1543             : 
    1544             :         // Estimate minimal Le Roy radius
    1545         177 :         StateTwo crucial_state{{{"None", "None"}}};
    1546       15019 :         for (const auto &e : this->states) {
    1547        7480 :             if (e.state.isArtificial(0) || e.state.isArtificial(1)) {
    1548           0 :                 continue;
    1549             :             }
    1550             : 
    1551        7480 :             auto n = e.state.getNStar(cache);
    1552        7480 :             auto l = e.state.getL();
    1553             : 
    1554        7480 :             double le_roy_radius = 2 * au2um *
    1555        7480 :                 (std::sqrt(0.5 * n[0] * n[0] * (5 * n[0] * n[0] + 1 - 3 * l[0] * (l[0] + 1))) +
    1556        7480 :                  std::sqrt(0.5 * n[1] * n[1] * (5 * n[1] * n[1] + 1 - 3 * l[1] * (l[1] + 1))));
    1557             : 
    1558        7480 :             if (le_roy_radius < minimal_le_roy_radius) {
    1559         146 :                 minimal_le_roy_radius = le_roy_radius;
    1560         146 :                 crucial_state = e.state;
    1561             :             }
    1562             :         }
    1563             : 
    1564             :         // Calculate minimal Le Roy radius precisely
    1565          59 :         if (crucial_state.isArtificial(0) || crucial_state.isArtificial(1)) {
    1566           0 :             minimal_le_roy_radius = 0;
    1567             :         } else {
    1568          59 :             minimal_le_roy_radius = crucial_state.getLeRoyRadius(cache);
    1569             :         }
    1570             :     }
    1571             : 
    1572         198 :     if (distance < minimal_le_roy_radius) {
    1573           1 :         std::cerr << "WARNING: The distance " << distance
    1574           1 :                   << " um is smaller than the Le Roy radius " << minimal_le_roy_radius << " um."
    1575           1 :                   << std::endl;
    1576             :     }
    1577         198 : }
    1578             : 
    1579             : template <typename Scalar>
    1580     2148406 : void SystemTwo<Scalar>::addBasisvectors(const StateTwo &state, const size_t &col_new,
    1581             :                                         const Scalar &value_new,
    1582             :                                         std::vector<Eigen::Triplet<Scalar>> &basisvectors_triplets,
    1583             :                                         std::vector<double> &sqnorm_list) {
    1584     2148406 :     auto state_iter = this->states.template get<1>().find(state);
    1585             : 
    1586             :     size_t row_new;
    1587     2148406 :     if (state_iter != this->states.template get<1>().end()) {
    1588     2133444 :         row_new = state_iter->idx;
    1589             :     } else {
    1590       14962 :         row_new = this->states.size();
    1591       14962 :         this->states.emplace_back(row_new, state);
    1592             :     }
    1593             : 
    1594     2148406 :     basisvectors_triplets.emplace_back(row_new, col_new, value_new);
    1595     2148406 :     sqnorm_list[row_new] += std::pow(std::abs(value_new), 2);
    1596     2148406 : }
    1597             : 
    1598             : template <typename Scalar>
    1599          26 : bool SystemTwo<Scalar>::isRefelectionAndRotationCompatible() {
    1600          26 :     if (sym_rotation.count(ARB) != 0 || sym_reflection == NA) {
    1601          26 :         return true;
    1602             :     }
    1603             : 
    1604           0 :     for (const auto &s : sym_rotation) {
    1605           0 :         if (sym_rotation.count(-s) == 0) {
    1606           0 :             return false;
    1607             :         }
    1608             :     }
    1609             : 
    1610           0 :     return true;
    1611             : }
    1612             : 
    1613             : template class SystemTwo<std::complex<double>>;
    1614             : template class SystemTwo<double>;

Generated by: LCOV version 1.14