From 5990fa62a7c1211d34b406ee8bb71d000b231614 Mon Sep 17 00:00:00 2001 From: Emily Boudreaux Date: Wed, 25 Jun 2025 08:26:50 -0400 Subject: [PATCH] feat(Composition): added getMolarAbundance method --- meson.build | 4 +- .../include/fourdst/composition/composition.h | 48 +++++++++-------- src/composition/lib/composition.cpp | 51 ++++++++++++------- tests/composition/compositionTest.cpp | 12 +++++ 4 files changed, 74 insertions(+), 41 deletions(-) diff --git a/meson.build b/meson.build index a3b2bbf..6628446 100644 --- a/meson.build +++ b/meson.build @@ -18,10 +18,12 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # *********************************************************************** # -project('libcomposition', 'cpp', version: 'v1.0.7', default_options: ['cpp_std=c++23'], meson_version: '>=1.5.0') +project('libcomposition', 'cpp', version: 'v1.0.8', default_options: ['cpp_std=c++23'], meson_version: '>=1.5.0') # Add default visibility for all C++ targets add_project_arguments('-fvisibility=default', language: 'cpp') +# Disable shadow warnings +add_project_arguments('-Wno-shadow', language: 'cpp') cpp = meson.get_compiler('cpp') subdir('build-config') diff --git a/src/composition/include/fourdst/composition/composition.h b/src/composition/include/fourdst/composition/composition.h index 250f374..5d36364 100644 --- a/src/composition/include/fourdst/composition/composition.h +++ b/src/composition/include/fourdst/composition/composition.h @@ -20,7 +20,6 @@ // *********************************************************************** */ #pragma once -#include #include #include #include @@ -85,7 +84,7 @@ namespace fourdst::composition { * CompositionEntry entry("H", true); * @endcode */ - CompositionEntry(const std::string& symbol, bool massFracMode=true); + explicit CompositionEntry(const std::string& symbol, bool massFracMode=true); /** * @brief Copy constructor. @@ -103,51 +102,51 @@ namespace fourdst::composition { * @brief Gets the chemical symbol of the species. * @return The chemical symbol of the species. */ - std::string symbol() const; + [[nodiscard]] std::string symbol() const; /** * @brief Gets the mass fraction of the species. * @return The mass fraction of the species. */ - double mass_fraction() const; + [[nodiscard]] double mass_fraction() const; /** * @brief Gets the mass fraction of the species given the mean molar mass. * @param meanMolarMass The mean molar mass. * @return The mass fraction of the species. */ - double mass_fraction(double meanMolarMass) const; + [[nodiscard]] double mass_fraction(double meanMolarMass) const; /** * @brief Gets the number fraction of the species. * @return The number fraction of the species. */ - double number_fraction() const; + [[nodiscard]] double number_fraction() const; /** * @brief Gets the number fraction of the species given the total moles. * @param totalMoles The total moles. * @return The number fraction of the species. */ - double number_fraction(double totalMoles) const; + [[nodiscard]] double number_fraction(double totalMoles) const; /** * @brief Gets the relative abundance of the species. * @return The relative abundance of the species. */ - double rel_abundance() const; + [[nodiscard]] double rel_abundance() const; /** * @brief Gets the isotope of the species. * @return The isotope of the species. */ - fourdst::atomic::Species isotope() const; + [[nodiscard]] atomic::Species isotope() const; /** * @brief Gets the mode of the composition entry. * @return True if mass fraction mode, false if number fraction mode. */ - bool getMassFracMode() const; + [[nodiscard]] bool getMassFracMode() const; /** * @brief Sets the mass fraction of the species. @@ -191,10 +190,10 @@ namespace fourdst::composition { * any part of 4DSSE. There are a few rules when using this class. * - Only species in the atomicSpecies.h database can be used. There are 1000s (All species from AME2020) in there so it should not be a problem. * - Before a mass fraction can be set with a particular instance of Composition, the symbol must be registered. (i.e. register He-3 before setting its mass fraction) - * - Before any composition information can be retrived (e.g. getComposition), the composition must be finalized (call to .finalize()). This checks if the total mass fraction sums to approximatly 1 (within 1 part in 10^8) - * - Any changes made to the composition after finalization will "unfinalize" the composition. This means that the composition must be finalized again before any information can be retrived. + * - Before any composition information can be retrieved (e.g. getComposition), the composition must be finalized (call to .finalize()). This checks if the total mass fraction sums to approximately 1 (within 1 part in 10^8) + * - Any changes made to the composition after finalization will "un-finalize" the composition. This means that the composition must be finalized again before any information can be retrieved. * - The mass fraction of any individual species must be no more than 1 and no less than 0. - * - The only exception to the finalize rule is if the compositon was constructed with symbols and mass fractions at instantiation time. In this case, the composition is automatically finalized. + * - The only exception to the finalize rule is if the composition was constructed with symbols and mass fractions at instantiation time. In this case, the composition is automatically finalized. * however, this means that the composition passed to the constructor must be valid. * * *Example Usage:* Constructing a finalized composition with symbols and mass fractions: @@ -236,14 +235,14 @@ namespace fourdst::composition { /** * @brief Checks if the given mass fractions are valid. - * @param mass_fractions The mass fractions to check. + * @param fractions The mass fractions to check. * @return True if the mass fractions are valid, false otherwise. */ - bool isValidComposition(const std::vector& fractions) const; + [[nodiscard]] bool isValidComposition(const std::vector& fractions) const; /** * @brief Validates the given mass fractions. - * @param mass_fractions The mass fractions to validate. + * @param fractions The mass fractions to validate. * @throws std::invalid_argument if the mass fractions are invalid. */ void validateComposition(const std::vector& fractions) const; @@ -402,9 +401,9 @@ namespace fourdst::composition { /** * @brief Mix two compositions together with a given fraction. * @param other The other composition to mix with. - * @param fraction The fraction of the other composition to mix with. This is the fraction of the other composition wrt. to the current. i.e. fraction=1 would mean that 50% of the new composition is from the other and 50% from the current). + * @param fraction The fraction of the other composition to mix with. This is the fraction of the other composition wrt. to the current. i.e. fraction=1 would mean that 50% of the new composition is from the other and 50% from the current. */ - Composition mix(const Composition& other, double fraction) const; + [[nodiscard]] Composition mix(const Composition& other, double fraction) const; /** * @brief Gets the mass fractions of all compositions. @@ -432,6 +431,13 @@ namespace fourdst::composition { */ [[nodiscard]] std::unordered_map getNumberFraction() const; + /** + * @brief Gets the molar abundance for a given symbol. + * @param symbol The symbol to get the molar abundance for. + * @return The molar abundance for the given symbol. + */ + [[nodiscard]] double getMolarAbundance(const std::string& symbol) const; + /** * @brief Gets the composition entry and global composition for a given symbol. * @param symbol The symbol to get the composition for. @@ -463,16 +469,16 @@ namespace fourdst::composition { * @param method The method to use for the subset (default is "norm"). * @return A Composition object containing the subset. */ - Composition subset(const std::vector& symbols, std::string method="norm") const; + [[nodiscard]] Composition subset(const std::vector& symbols, const std::string& method="norm") const; /** * @brief Check if a symbol is registered. * @param symbol The symbol to check. * @return True if the symbol is registered, false otherwise. */ - bool hasSymbol(const std::string& symbol) const; + [[nodiscard]] bool hasSymbol(const std::string& symbol) const; - bool contains(const fourdst::atomic::Species& isotope) const; + [[nodiscard]] bool contains(const fourdst::atomic::Species& isotope) const; /** * @brief Sets the composition mode. diff --git a/src/composition/lib/composition.cpp b/src/composition/lib/composition.cpp index d1ae121..53c4f81 100644 --- a/src/composition/lib/composition.cpp +++ b/src/composition/lib/composition.cpp @@ -56,7 +56,7 @@ namespace fourdst::composition { if (m_initialized) { throw std::runtime_error("Composition entry is already initialized."); } - if (fourdst::atomic::species.count(symbol) == 0) { + if (!fourdst::atomic::species.contains(symbol)) { throw std::runtime_error("Invalid symbol."); } m_symbol = symbol; @@ -207,7 +207,7 @@ namespace fourdst::composition { } // If no symbols have been registered allow mode to be set - if (m_registeredSymbols.size() == 0) { + if (m_registeredSymbols.empty()) { m_massFracMode = massFracMode; } else { if (m_massFracMode != massFracMode) { @@ -216,13 +216,13 @@ namespace fourdst::composition { } } - if (m_registeredSymbols.find(symbol) != m_registeredSymbols.end()) { + if (m_registeredSymbols.contains(symbol)) { LOG_WARNING(m_logger, "Symbol {} is already registered.", symbol); return; } m_registeredSymbols.insert(symbol); - CompositionEntry entry(symbol, m_massFracMode); + const CompositionEntry entry(symbol, m_massFracMode); m_compositions[symbol] = entry; LOG_INFO(m_logger, "Registered symbol: {}", symbol); } @@ -299,7 +299,7 @@ namespace fourdst::composition { } double Composition::setNumberFraction(const std::string& symbol, const double& number_fraction) { - if (m_registeredSymbols.find(symbol) == m_registeredSymbols.end()) { + if (!m_registeredSymbols.contains(symbol)) { LOG_ERROR(m_logger, "Symbol {} is not registered.", symbol); throw std::runtime_error("Symbol is not registered."); } @@ -351,7 +351,7 @@ namespace fourdst::composition { bool Composition::finalizeMassFracMode(bool norm) { std::vector mass_fractions; mass_fractions.reserve(m_compositions.size()); - for (const auto& [_, entry] : m_compositions) { + for (const auto &entry: m_compositions | std::views::values) { mass_fractions.push_back(entry.mass_fraction()); } if (norm) { @@ -359,8 +359,8 @@ namespace fourdst::composition { for (const auto& mass_fraction : mass_fractions) { sum += mass_fraction; } - for (int i = 0; i < static_cast(mass_fractions.size()); ++i) { - mass_fractions[i] /= sum; + for (double & mass_fraction : mass_fractions) { + mass_fraction /= sum; } for (auto& [symbol, entry] : m_compositions) { setMassFraction(symbol, entry.mass_fraction() / sum); @@ -368,16 +368,16 @@ namespace fourdst::composition { } try { validateComposition(mass_fractions); - } catch (const std::runtime_error& e) { + } catch ([[maybe_unused]] const std::runtime_error& e) { double massSum = 0.0; - for (const auto& [_, entry] : m_compositions) { + for (const auto &entry: m_compositions | std::views::values) { massSum += entry.mass_fraction(); } LOG_ERROR(m_logger, "Composition is invalid (Total mass {}).", massSum); m_finalized = false; return false; } - for (const auto& [_, entry] : m_compositions) { + for (const auto &entry: m_compositions | std::views::values) { m_specificNumberDensity += entry.rel_abundance(); } m_meanParticleMass = 1.0/m_specificNumberDensity; @@ -387,7 +387,7 @@ namespace fourdst::composition { bool Composition::finalizeNumberFracMode(bool norm) { std::vector number_fractions; number_fractions.reserve(m_compositions.size()); - for (const auto& [_, entry] : m_compositions) { + for (const auto &entry: m_compositions | std::views::values) { number_fractions.push_back(entry.number_fraction()); } if (norm) { @@ -401,16 +401,16 @@ namespace fourdst::composition { } try { validateComposition(number_fractions); - } catch (const std::runtime_error& e) { + } catch ([[maybe_unused]] const std::runtime_error& e) { double numberSum = 0.0; - for (const auto& [_, entry] : m_compositions) { + for (const auto &entry: m_compositions | std::views::values) { numberSum += entry.number_fraction(); } LOG_ERROR(m_logger, "Composition is invalid (Total number {}).", numberSum); m_finalized = false; return false; } - for (const auto& [_, entry] : m_compositions) { + for (const auto &entry: m_compositions | std::views::values) { m_meanParticleMass += entry.rel_abundance(); } m_specificNumberDensity = 1.0/m_meanParticleMass; @@ -453,7 +453,7 @@ namespace fourdst::composition { } if (!m_compositions.contains(symbol)) { LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol); - std::string currentSymbols = ""; + std::string currentSymbols; int count = 0; for (const auto& sym : m_compositions | std::views::keys) { currentSymbols += sym; @@ -506,6 +506,19 @@ namespace fourdst::composition { return number_fractions; } + double Composition::getMolarAbundance(const std::string &symbol) const { + if (!m_finalized) { + LOG_ERROR(m_logger, "Composition has not been finalized."); + throw std::runtime_error("Composition has not been finalized (Consider running .finalize())."); + } + if (!m_compositions.contains(symbol)) { + LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol); + throw std::runtime_error("Symbol is not in the composition."); + } + return getMassFraction(symbol) / m_compositions.at(symbol).isotope().mass(); + + } + std::pair Composition::getComposition(const std::string& symbol) const { if (!m_finalized) { LOG_ERROR(m_logger, "Composition has not been finalized."); @@ -551,7 +564,7 @@ namespace fourdst::composition { return mean_A; } - Composition Composition::subset(const std::vector& symbols, std::string method) const { + Composition Composition::subset(const std::vector& symbols, const std::string& method) const { const std::array methods = {"norm", "none"}; if (std::ranges::find(methods, method) == methods.end()) { @@ -649,7 +662,7 @@ namespace fourdst::composition { } bool Composition::hasSymbol(const std::string& symbol) const { - return m_compositions.count(symbol) > 0; + return m_compositions.contains(symbol); } bool Composition::contains(const fourdst::atomic::Species &isotope) const { @@ -686,7 +699,7 @@ namespace fourdst::composition { std::ostream& operator<<(std::ostream& os, const Composition& composition) { os << "Composition(finalized: " << (composition.m_finalized ? "true" : "false") << ", " ; int count = 0; - for (const auto& [symbol, entry] : composition.m_compositions) { + for (const auto &entry: composition.m_compositions | std::views::values) { os << entry; if (count < composition.m_compositions.size() - 1) { os << ", "; diff --git a/tests/composition/compositionTest.cpp b/tests/composition/compositionTest.cpp index 605f8c4..e637aaa 100644 --- a/tests/composition/compositionTest.cpp +++ b/tests/composition/compositionTest.cpp @@ -206,3 +206,15 @@ TEST_F(compositionTest, mix) { EXPECT_DOUBLE_EQ(mixedComp2.getMassFraction("H-1"), 0.45); EXPECT_DOUBLE_EQ(mixedComp2.getMassFraction("He-4"), 0.55); } + +TEST_F(compositionTest, molarAbundance) { + fourdst::composition::Composition comp1; + comp1.registerSymbol("H-1"); + comp1.registerSymbol("He-4"); + comp1.setMassFraction("H-1", 0.5); + comp1.setMassFraction("He-4", 0.5); + comp1.finalize(); + + EXPECT_DOUBLE_EQ(comp1.getMolarAbundance("H-1"), 0.5/fourdst::atomic::H_1.mass()); + EXPECT_DOUBLE_EQ(comp1.getMolarAbundance("He-4"), 0.5/fourdst::atomic::He_4.mass()); +}