Line data Source code
1 : // SPDX-FileCopyrightText: 2024 PairInteraction Developers 2 : // SPDX-License-Identifier: LGPL-3.0-or-later 3 : 4 : #include "pairinteraction/ket/KetAtom.hpp" 5 : 6 : #include "pairinteraction/utils/hash.hpp" 7 : 8 : #include <array> 9 : #include <cctype> 10 : #include <cmath> 11 : #include <fmt/core.h> 12 : #include <fmt/format.h> 13 : #include <string> 14 : #include <string_view> 15 : #include <vector> 16 : 17 : namespace pairinteraction { 18 : 19 : constexpr std::array<std::string_view, 6> quantum_number_l_labels = {"S", "P", "D", "F", "G", "H"}; 20 : 21 40225 : KetAtom::KetAtom(Private /*unused*/, double energy, std::string species, 22 : std::unordered_map<std::string, double> quantum_numbers, 23 : std::unordered_map<std::string, double> quantum_numbers_std, Database &database, 24 40225 : size_t id_in_database) 25 40225 : : Ket(energy), species(std::move(species)), quantum_numbers(std::move(quantum_numbers)), 26 40225 : quantum_numbers_std(std::move(quantum_numbers_std)), database(database), 27 40225 : id_in_database(id_in_database) {} 28 : 29 263 : Database &KetAtom::get_database() const { return database; } 30 : 31 41459 : size_t KetAtom::get_id_in_database() const { return id_in_database; } 32 : 33 170826 : double KetAtom::get_quantum_number(const std::string &name) const { 34 170826 : return quantum_numbers.at(name); 35 : } 36 : 37 5 : double KetAtom::get_quantum_number_std(const std::string &name) const { 38 5 : auto it = quantum_numbers_std.find(name); 39 5 : return it != quantum_numbers_std.end() ? it->second : 0; 40 : } 41 : 42 1232 : std::string KetAtom::get_label() const { 43 1232 : double s = get_quantum_number("s"); 44 1232 : double l = get_quantum_number("l"); 45 1232 : double f = get_quantum_number("f"); 46 1232 : double m = get_quantum_number("m"); 47 1232 : bool is_calculated_with_mqdt = get_quantum_number("is_calculated_with_mqdt") != 0; 48 : 49 1232 : size_t pos = species.find('_'); 50 1232 : std::string label = (pos != std::string::npos) ? species.substr(0, pos) : species; 51 1232 : label[0] = static_cast<char>(std::toupper(label[0])); 52 : 53 1232 : if (!is_calculated_with_mqdt) { 54 1191 : if (s == 0) { 55 13 : label += "_singlet"; 56 1178 : } else if (s == 1) { 57 16 : label += "_triplet"; 58 1162 : } else if (s != 0.5) { 59 0 : throw std::runtime_error( 60 0 : "Invalid value for quantum number s in the single-channel description."); 61 : } 62 : } 63 : 64 1232 : label += ":"; 65 : 66 1232 : if (is_calculated_with_mqdt) { 67 82 : label += fmt::format("S={:.1f},nu={:.1f},L={:.1f},", s, get_quantum_number("nu"), l); 68 41 : label += get_quantum_number("is_j_total_momentum") != 0 ? "J=" : "F="; 69 : } else { 70 2382 : label += fmt::format("{:.0f},", get_quantum_number("n")); 71 1191 : if (l == std::rint(l) && l < quantum_number_l_labels.size()) { 72 1191 : label += quantum_number_l_labels.at(static_cast<size_t>(l)); 73 : } else { 74 0 : label += fmt::format("{:.0f}", l); 75 : } 76 1191 : label += "_"; 77 : } 78 : 79 1232 : if (f == std::rint(f)) { 80 28 : label += fmt::format("{:.0f}", f); 81 1218 : } else if (2 * f == std::rint(2 * f)) { 82 2436 : label += fmt::format("{:.0f}/2", 2 * f); 83 : } else { 84 0 : std::abort(); // can't happen because the total momentum is validated to be an integer 85 : // or half-integer 86 : } 87 : 88 1232 : if (m == std::rint(m)) { 89 28 : label += fmt::format(",{:.0f}", m); 90 1218 : } else if (2 * m == std::rint(2 * m)) { 91 2436 : label += fmt::format(",{:.0f}/2", 2 * m); 92 : } else { 93 0 : std::abort(); // can't happen because the quantum number m is validated to be an integer 94 : // or half-integer 95 : } 96 : 97 2464 : return label; 98 0 : } 99 : 100 : std::shared_ptr<KetAtom> 101 0 : KetAtom::get_ket_for_different_quantum_number_m(double new_quantum_number_m) const { 102 0 : auto ket = *this; 103 0 : ket.quantum_numbers.at("m") = new_quantum_number_m; 104 0 : return std::make_shared<KetAtom>(ket); 105 0 : } 106 : 107 11989 : const std::string &KetAtom::get_species() const { return species; } 108 : 109 39325 : bool KetAtom::operator==(const KetAtom &other) const { 110 41275 : return Ket::operator==(other) && species == other.species && 111 42418 : quantum_numbers == other.quantum_numbers && 112 40468 : quantum_numbers_std == other.quantum_numbers_std; 113 : } 114 : 115 1 : bool KetAtom::operator!=(const KetAtom &other) const { return !(*this == other); } 116 : 117 40304 : size_t KetAtom::hash::operator()(const KetAtom &k) const { 118 40304 : size_t seed = typename Ket::hash()(k); 119 40304 : utils::hash_combine(seed, k.species); 120 : // The quantum numbers are stored in an unordered map, so we combine the per-entry hashes in an 121 : // order-independent way (via xor) to obtain a deterministic result. 122 40304 : size_t quantum_numbers_hash = 0; 123 604560 : for (const auto &[key, value] : k.quantum_numbers) { 124 564256 : size_t entry_seed = 0; 125 564256 : utils::hash_combine(entry_seed, key); 126 564256 : utils::hash_combine(entry_seed, value); 127 564256 : quantum_numbers_hash ^= entry_seed; 128 : } 129 282128 : for (const auto &[key, value] : k.quantum_numbers_std) { 130 241824 : size_t entry_seed = 0; 131 241824 : utils::hash_combine(entry_seed, key); 132 241824 : utils::hash_combine(entry_seed, value); 133 241824 : quantum_numbers_hash ^= entry_seed; 134 : } 135 40304 : utils::hash_combine(seed, quantum_numbers_hash); 136 40304 : return seed; 137 : } 138 : 139 : } // namespace pairinteraction