From f3d946bc5180606558f64568d20b7a42a64fef22 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Wed, 2 Jul 2025 10:35:54 -0400 Subject: [PATCH] feat(species): added spin parsing from spin parity string --- meson.build | 2 + .../fourdst/composition/atomicSpecies.h | 108 ++++++++++++++---- tests/composition/compositionTest.cpp | 16 +++ utils/atomic/format.py | 3 +- 4 files changed, 104 insertions(+), 25 deletions(-) diff --git a/meson.build b/meson.build index 79dc85e..61cc323 100644 --- a/meson.build +++ b/meson.build @@ -27,6 +27,8 @@ add_project_arguments('-Wno-shadow', language: 'cpp') cpp = meson.get_compiler('cpp') subdir('build-config') + + subdir('src') subdir('tests') diff --git a/src/composition/include/fourdst/composition/atomicSpecies.h b/src/composition/include/fourdst/composition/atomicSpecies.h index 53ea41b..9155b32 100644 --- a/src/composition/include/fourdst/composition/atomicSpecies.h +++ b/src/composition/include/fourdst/composition/atomicSpecies.h @@ -5,6 +5,8 @@ #include namespace fourdst::atomic { + inline double convert_jpi_to_double(const std::string& jpi_string); + struct Species { std::string m_name; //< Name of the species std::string m_el; //< Element symbol @@ -20,6 +22,7 @@ namespace fourdst::atomic { std::string m_decayModes; //< Decay modes double m_atomicMass; //< Atomic mass double m_atomicMassUnc; //< Atomic mass uncertainty + double m_spin = 0.0; //< Spin of the species, default is 0.0 Species( const std::string_view name, @@ -50,7 +53,9 @@ namespace fourdst::atomic { m_spinParity(spinParity), m_decayModes(decayModes), m_atomicMass(atomicMass), - m_atomicMassUnc(atomicMassUnc) {}; + m_atomicMassUnc(atomicMassUnc) { + m_spin = convert_jpi_to_double(m_spinParity); + }; //Copy constructor Species(const Species& species) { @@ -68,65 +73,70 @@ namespace fourdst::atomic { m_decayModes = species.m_decayModes; m_atomicMass = species.m_atomicMass; m_atomicMassUnc = species.m_atomicMassUnc; + m_spin = convert_jpi_to_double(m_spinParity); } - double mass() const { + [[nodiscard]] double mass() const { return m_atomicMass; } - double massUnc() const { + [[nodiscard]] double massUnc() const { return m_atomicMassUnc; } - double halfLife() const { + [[nodiscard]] double halfLife() const { return m_halfLife_s; } - std::string_view spinParity() const { + [[nodiscard]] std::string_view spinParity() const { return m_spinParity; } - std::string_view decayModes() const { + [[nodiscard]] std::string_view decayModes() const { return m_decayModes; } - double bindingEnergy() const { + [[nodiscard]] double bindingEnergy() const { return m_bindingEnergy; } - double betaDecayEnergy() const { + [[nodiscard]] double betaDecayEnergy() const { return m_betaDecayEnergy; } - std::string_view betaCode() const { + [[nodiscard]] std::string_view betaCode() const { return m_betaCode; } - std::string_view name() const { + [[nodiscard]] std::string_view name() const { return m_name; } - std::string_view el() const { + [[nodiscard]] std::string_view el() const { return m_el; } - int nz() const { + [[nodiscard]] int nz() const { return m_nz; } - int n() const { + [[nodiscard]] int n() const { return m_n; } - int z() const { + [[nodiscard]] int z() const { return m_z; } - int a() const { + [[nodiscard]] int a() const { return m_a; } + [[nodiscard]] double spin() const { + return m_spin; + } + friend std::ostream& operator<<(std::ostream& os, const Species& species) { os << species.m_name; return os; @@ -142,13 +152,65 @@ namespace fourdst::atomic { return (lhs.m_name != rhs.m_name); } + inline double convert_jpi_to_double(const std::string& jpi_string) { + std::string s = jpi_string; + + if (s.empty()) { + return std::numeric_limits::quiet_NaN(); + } + + std::erase_if(s, [](const char c) { + return c == '(' || c == ')' || c == '*' || c == '#'; + }); + + if (s == "+" || s == "-") { + return 0.0; + } + + if (const size_t comma_pos = s.find(','); comma_pos != std::string::npos) { + s = s.substr(0, comma_pos); + } + + if (!s.empty() && (s.back() == '+' || s.back() == '-')) { + s.pop_back(); + } + + if (s.empty()) { + return std::numeric_limits::quiet_NaN(); + } + + try { + if (size_t slash_pos = s.find('/'); slash_pos != std::string::npos) { + if (slash_pos == 0) { + s = "1" + s; + slash_pos = 1; + } + const std::string numerator_str = s.substr(0, slash_pos); + const std::string denominator_str = s.substr(slash_pos + 1); + if (denominator_str.empty()) { + return std::numeric_limits::quiet_NaN(); + } + const double numerator = std::stod(numerator_str); + const double denominator = std::stod(denominator_str); + if (denominator == 0.0) { + return std::numeric_limits::quiet_NaN(); + } + return numerator / denominator; + } else { + return std::stod(s); + } + } catch (const std::invalid_argument&) { + return std::numeric_limits::quiet_NaN(); + } catch (const std::out_of_range&) { + return std::numeric_limits::quiet_NaN(); + } + } + } -namespace std { - template<> - struct hash { - size_t operator()(const fourdst::atomic::Species& s) const noexcept { - return std::hash()(s.m_name); - } - }; -} // namespace std +template<> +struct std::hash { + size_t operator()(const fourdst::atomic::Species& s) const noexcept { + return std::hash()(s.m_name); + } +}; // namespace std diff --git a/tests/composition/compositionTest.cpp b/tests/composition/compositionTest.cpp index 3a010c1..22c9020 100644 --- a/tests/composition/compositionTest.cpp +++ b/tests/composition/compositionTest.cpp @@ -32,6 +32,22 @@ TEST_F(compositionTest, isotopeHalfLives) { EXPECT_DOUBLE_EQ(fourdst::atomic::B_20.halfLife(), 0.0); } +TEST_F(compositionTest, isotopeSpin) { + using namespace fourdst::atomic; + EXPECT_DOUBLE_EQ(H_1.spin(), 0.5); + EXPECT_DOUBLE_EQ(He_4.spin(), 0.0); + EXPECT_DOUBLE_EQ(Pm_164.spin(), 0.0); + EXPECT_DOUBLE_EQ(Tb_164.spin(), 5.0); + EXPECT_DOUBLE_EQ(Ta_163.spin(), 0.5); + EXPECT_DOUBLE_EQ(Hf_165.spin(), 2.5); + EXPECT_DOUBLE_EQ(Ta_165.spin(), 0.5); + EXPECT_DOUBLE_EQ(Li_10.spin(), 1.0); + EXPECT_DOUBLE_EQ(He_9.spin(), 0.5); + EXPECT_DOUBLE_EQ(F_18.spin(), 0.0); + EXPECT_DOUBLE_EQ(B_20.spin(), 1.0); + EXPECT_TRUE(std::isnan(Bh_270.spin())); +} + TEST_F(compositionTest, constructor) { fourdst::config::Config::getInstance().loadConfig(EXAMPLE_FILENAME); EXPECT_NO_THROW(fourdst::composition::Composition comp); diff --git a/utils/atomic/format.py b/utils/atomic/format.py index 97a48ff..a55491a 100644 --- a/utils/atomic/format.py +++ b/utils/atomic/format.py @@ -151,11 +151,10 @@ def formatHeader(dataFrame): namespace fourdst::atomic {{ {'\n '.join([formatSpecies(row)[0] for index, row in dataFrame.iterrows()])} - static const std::unordered_map species = {{ + static const std::unordered_map species = {{ {'\n '.join([f'{{"{row["el"].strip()}-{row["a"]}", {mkInstanceName(row)}}},' for index, row in dataFrame.iterrows()])} }}; }}; // namespace fourdst::atomic -{'\n'.join([formatSpeciesDefines(row) for index, row in dataFrame.iterrows()])} """ return header