diff --git a/src/composition/private/composition.cpp b/src/composition/private/composition.cpp index 415bbb6..0fefc7b 100644 --- a/src/composition/private/composition.cpp +++ b/src/composition/private/composition.cpp @@ -2,98 +2,235 @@ #include "quill/LogMacros.h" #include +#include +#include +#include + +#include #include "atomicSpecies.h" using namespace composition; +CompositionEntry::CompositionEntry() : m_symbol("H-1"), m_isotope(chemSpecies::species.at("H-1")), m_initialized(false) {} + +CompositionEntry::CompositionEntry(const std::string& symbol, bool massFracMode) : m_symbol(symbol), m_isotope(chemSpecies::species.at(symbol)), m_massFracMode(massFracMode) { + setSpecies(symbol); +} + +CompositionEntry::CompositionEntry(const CompositionEntry& entry) : + m_symbol(entry.m_symbol), + m_isotope(entry.m_isotope), + m_massFracMode(entry.m_massFracMode), + m_massFraction(entry.m_massFraction), + m_numberFraction(entry.m_numberFraction), + m_relAbundance(entry.m_relAbundance), + m_initialized(entry.m_initialized) {} + +void CompositionEntry::setSpecies(const std::string& symbol) { + if (m_initialized) { + throw std::runtime_error("Composition entry is already initialized."); + } + if (chemSpecies::species.count(symbol) == 0) { + throw std::runtime_error("Invalid symbol."); + } + m_symbol = symbol; + m_isotope = chemSpecies::species.at(symbol); + m_initialized = true; +} + +std::string CompositionEntry::symbol() const { + return m_symbol; +} + +double CompositionEntry::mass_fraction() const { + if (!m_massFracMode) { + throw std::runtime_error("Composition entry is in number fraction mode."); + } + return m_massFraction; +} + +double CompositionEntry::mass_fraction(double meanMolarMass) const { + if (m_massFracMode) { + return m_massFraction; + } + return m_relAbundance / meanMolarMass; +} + + +double CompositionEntry::number_fraction() const { + if (m_massFracMode) { + throw std::runtime_error("Composition entry is in mass fraction mode."); + } + return m_numberFraction; +} + +double CompositionEntry::number_fraction(double totalMoles) const { + if (m_massFracMode) { + return m_relAbundance / totalMoles; + } + return m_numberFraction; +} + +double CompositionEntry::rel_abundance() const { + return m_relAbundance; +} + +chemSpecies::Species CompositionEntry::isotope() const { + return m_isotope; +} + +void CompositionEntry::setMassFraction(double mass_fraction) { + if (!m_massFracMode) { + throw std::runtime_error("Composition entry is in number fraction mode."); + } + m_massFraction = mass_fraction; + m_relAbundance = m_massFraction / m_isotope.mass(); +} + +void CompositionEntry::setNumberFraction(double number_fraction) { + if (m_massFracMode) { + throw std::runtime_error("Composition entry is in mass fraction mode."); + } + m_numberFraction = number_fraction; + m_relAbundance = m_numberFraction * m_isotope.mass(); +} + +bool CompositionEntry::setMassFracMode(double meanParticleMass) { + if (m_massFracMode) { + return false; + } + m_massFracMode = true; + m_massFraction = m_relAbundance / meanParticleMass; + return true; +} + +bool CompositionEntry::setNumberFracMode(double specificNumberDensity) { + if (!m_massFracMode) { + return false; + } + m_massFracMode = false; + m_numberFraction = m_relAbundance / specificNumberDensity; + return true; +} + +bool CompositionEntry::getMassFracMode() const { + return m_massFracMode; +} + Composition::Composition(const std::vector& symbols) { for (const auto& symbol : symbols) { registerSymbol(symbol); } } -void Composition::validateComposition(const std::vector& mass_fractions) const { - if (!isValidComposition(mass_fractions)) { - LOG_ERROR(m_logger, "Invalid composition."); - throw std::runtime_error("Invalid composition."); - } -} - -bool Composition::isValidComposition(const std::vector& mass_fractions) const { - double sum = 0.0; - for (const auto& mass_fraction : mass_fractions) { - sum += mass_fraction; - } - if (sum < 0.99999999 || sum > 1.000000001) { - LOG_ERROR(m_logger, "The sum of mass fractions must be equal to 1."); - return false; +Composition::Composition(const std::vector& symbols, const std::vector& fractions, bool massFracMode) : m_massFracMode(massFracMode) { + if (symbols.size() != fractions.size()) { + LOG_ERROR(m_logger, "The number of symbols and fractions must be equal."); + throw std::runtime_error("The number of symbols and fractions must be equal."); } - return true; -} - -Composition::Composition(const std::vector& symbols, const std::vector& mass_fractions) { - if (symbols.size() != mass_fractions.size()) { - LOG_ERROR(m_logger, "The number of symbols and mass fractions must be equal."); - throw std::runtime_error("The number of symbols and mass fractions must be equal."); - } - - validateComposition(mass_fractions); - finalize(); + validateComposition(fractions); for (const auto &symbol : symbols) { registerSymbol(symbol); } for (size_t i = 0; i < symbols.size(); ++i) { - setComposition(symbols[i], mass_fractions[i]); + if (m_massFracMode) { + setMassFraction(symbols[i], fractions[i]); + } else { + setNumberFraction(symbols[i], fractions[i]); + } } + finalize(); } -void Composition::registerSymbol(const std::string& symbol) { +void Composition::registerSymbol(const std::string& symbol, bool massFracMode) { if (!isValidSymbol(symbol)) { LOG_ERROR(m_logger, "Invalid symbol: {}", symbol); throw std::runtime_error("Invalid symbol."); } + // If no symbols have been registered allow mode to be set + if (m_registeredSymbols.size() == 0) { + m_massFracMode = massFracMode; + } else { + if (m_massFracMode != massFracMode) { + LOG_ERROR(m_logger, "Composition is in mass fraction mode. Cannot register symbol in number fraction mode."); + throw std::runtime_error("Composition is in mass fraction mode. Cannot register symbol in number fraction mode."); + } + } + if (m_registeredSymbols.find(symbol) != m_registeredSymbols.end()) { LOG_WARNING(m_logger, "Symbol {} is already registered.", symbol); return; } m_registeredSymbols.insert(symbol); + CompositionEntry entry(symbol, m_massFracMode); + m_compositions[symbol] = entry; LOG_INFO(m_logger, "Registered symbol: {}", symbol); } +void Composition::registerSymbol(const std::vector& symbols, bool massFracMode) { + for (const auto& symbol : symbols) { + registerSymbol(symbol, massFracMode); + } +} + +std::set Composition::getRegisteredSymbols() const { + return m_registeredSymbols; +} + +void Composition::validateComposition(const std::vector& fractions) const { + if (!isValidComposition(fractions)) { + LOG_ERROR(m_logger, "Invalid composition."); + throw std::runtime_error("Invalid composition."); + } +} + +bool Composition::isValidComposition(const std::vector& fractions) const { + double sum = 0.0; + for (const auto& fraction : fractions) { + sum += fraction; + } + if (sum < 0.999999 || sum > 1.000001) { + LOG_ERROR(m_logger, "The sum of fractions must be equal to 1."); + return false; + } + + return true; +} + bool Composition::isValidSymbol(const std::string& symbol) const { return chemSpecies::species.count(symbol) > 0; } -double Composition::setComposition(const std::string& symbol, const double& mass_fraction) { - m_finalized = false; +double Composition::setMassFraction(const std::string& symbol, const double& mass_fraction) { if (m_registeredSymbols.find(symbol) == m_registeredSymbols.end()) { LOG_ERROR(m_logger, "Symbol {} is not registered.", symbol); throw std::runtime_error("Symbol is not registered."); } + if (!m_massFracMode) { + LOG_ERROR(m_logger, "Composition is in number fraction mode."); + throw std::runtime_error("Composition is in number fraction mode."); + } + if (mass_fraction < 0.0 || mass_fraction > 1.0) { - LOG_ERROR(m_logger, "Mass fraction must be between 0 and 1."); + LOG_ERROR(m_logger, "Mass fraction must be between 0 and 1 for symbol {}. Currently it is {}.", symbol, mass_fraction); throw std::runtime_error("Mass fraction must be between 0 and 1."); } - double old_mass_fraction = 0.0; - if (m_compositions.find(symbol) != m_compositions.end()) { - old_mass_fraction = m_compositions[symbol].mass_fraction; - } - m_compositions[symbol] = {symbol, mass_fraction}; - + m_finalized = false; + double old_mass_fraction = m_compositions.at(symbol).mass_fraction(); + m_compositions.at(symbol).setMassFraction(mass_fraction); return old_mass_fraction; } -std::vector Composition::setComposition(const std::vector& symbols, const std::vector& mass_fractions) { - m_finalized = false; +std::vector Composition::setMassFraction(const std::vector& symbols, const std::vector& mass_fractions) { if (symbols.size() != mass_fractions.size()) { LOG_ERROR(m_logger, "The number of symbols and mass fractions must be equal."); throw std::runtime_error("The number of symbols and mass fractions must be equal."); @@ -102,45 +239,131 @@ std::vector Composition::setComposition(const std::vector& std::vector old_mass_fractions; old_mass_fractions.reserve(symbols.size()); for (size_t i = 0; i < symbols.size(); ++i) { - old_mass_fractions.push_back(setComposition(symbols[i], mass_fractions[i])); + old_mass_fractions.push_back(setMassFraction(symbols[i], mass_fractions[i])); } return old_mass_fractions; } -std::set Composition::getRegisteredSymbols() const { - return m_registeredSymbols; -} - -std::unordered_map Composition::getCompositions() 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())."); +double Composition::setNumberFraction(const std::string& symbol, const double& number_fraction) { + if (m_registeredSymbols.find(symbol) == m_registeredSymbols.end()) { + LOG_ERROR(m_logger, "Symbol {} is not registered.", symbol); + throw std::runtime_error("Symbol is not registered."); } - return m_compositions; + + if (m_massFracMode) { + LOG_ERROR(m_logger, "Composition is in mass fraction mode."); + throw std::runtime_error("Composition is in mass fraction mode."); + } + + if (number_fraction < 0.0 || number_fraction > 1.0) { + LOG_ERROR(m_logger, "Number fraction must be between 0 and 1 for symbol {}. Currently it is {}.", symbol, number_fraction); + throw std::runtime_error("Number fraction must be between 0 and 1."); + } + + m_finalized = false; + double old_number_fraction = m_compositions.at(symbol).number_fraction(); + m_compositions.at(symbol).setNumberFraction(number_fraction); + + return old_number_fraction; } -bool Composition::finalize() { - m_finalized = true; +std::vector Composition::setNumberFraction(const std::vector& symbols, const std::vector& number_fractions) { + if (symbols.size() != number_fractions.size()) { + LOG_ERROR(m_logger, "The number of symbols and number fractions must be equal."); + throw std::runtime_error("The number of symbols and number fractions must be equal."); + } + + std::vector old_number_fractions; + old_number_fractions.reserve(symbols.size()); + for (size_t i = 0; i < symbols.size(); ++i) { + old_number_fractions.push_back(setNumberFraction(symbols[i], number_fractions[i])); + } + return old_number_fractions; +} + +bool Composition::finalize(bool norm) { + bool finalized = false; + if (m_massFracMode) { + finalized = finalizeMassFracMode(norm); + } else { + finalized = finalizeNumberFracMode(norm); + } + if (finalized) { + m_finalized = true; + } + return finalized; +} + +bool Composition::finalizeMassFracMode(bool norm) { std::vector mass_fractions; mass_fractions.reserve(m_compositions.size()); for (const auto& [_, entry] : m_compositions) { - mass_fractions.push_back(entry.mass_fraction); + mass_fractions.push_back(entry.mass_fraction()); + } + if (norm) { + double sum = 0.0; + for (const auto& mass_fraction : mass_fractions) { + sum += mass_fraction; + } + for (int i = 0; i < mass_fractions.size(); ++i) { + mass_fractions[i] /= sum; + } + for (auto& [symbol, entry] : m_compositions) { + setMassFraction(symbol, entry.mass_fraction() / sum); + } } try { validateComposition(mass_fractions); } catch (const std::runtime_error& e) { double massSum = 0.0; for (const auto& [_, entry] : m_compositions) { - massSum += entry.mass_fraction; + 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) { + m_specificNumberDensity += entry.rel_abundance(); + } + m_meanParticleMass = 1.0/m_specificNumberDensity; return true; } -CompositionEntry Composition::getComposition(const std::string& symbol) const { +bool Composition::finalizeNumberFracMode(bool norm) { + std::vector number_fractions; + number_fractions.reserve(m_compositions.size()); + for (const auto& [_, entry] : m_compositions) { + number_fractions.push_back(entry.number_fraction()); + } + if (norm) { + double sum = 0.0; + for (const auto& number_fraction : number_fractions) { + sum += number_fraction; + } + for (auto& [symbol, entry] : m_compositions) { + setNumberFraction(symbol, entry.number_fraction() / sum); + } + } + try { + validateComposition(number_fractions); + } catch (const std::runtime_error& e) { + double numberSum = 0.0; + for (const auto& [_, entry] : m_compositions) { + 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) { + m_meanParticleMass += entry.rel_abundance(); + } + m_specificNumberDensity = 1.0/m_meanParticleMass; + return true; +} + +double Composition::getMassFraction(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())."); @@ -149,5 +372,91 @@ CompositionEntry Composition::getComposition(const std::string& symbol) const { LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol); throw std::runtime_error("Symbol is not in the composition."); } - return m_compositions.at(symbol); + if (m_massFracMode) { + return m_compositions.at(symbol).mass_fraction(); + } else { + return m_compositions.at(symbol).mass_fraction(m_meanParticleMass); + } +} + +std::unordered_map Composition::getMassFraction() const { + std::unordered_map mass_fractions; + for (const auto& [symbol, entry] : m_compositions) { + mass_fractions[symbol] = getMassFraction(symbol); + } + return mass_fractions; +} + + +double Composition::getNumberFraction(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.count(symbol) == 0) { + LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol); + throw std::runtime_error("Symbol is not in the composition."); + } + if (!m_massFracMode) { + return m_compositions.at(symbol).number_fraction(); + } else { + return m_compositions.at(symbol).number_fraction(m_specificNumberDensity); + } +} + +std::unordered_map Composition::getNumberFraction() const { + std::unordered_map number_fractions; + for (const auto& [symbol, entry] : m_compositions) { + number_fractions[symbol] = getNumberFraction(symbol); + } + return number_fractions; +} + +std::pair Composition::getComposition(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.count(symbol) == 0) { + LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol); + throw std::runtime_error("Symbol is not in the composition."); + } + return {m_compositions.at(symbol), {m_specificNumberDensity, m_meanParticleMass}}; +} + +std::pair, GlobalComposition> Composition::getComposition() 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())."); + } + return {m_compositions, {m_specificNumberDensity, m_meanParticleMass}}; +} + +Composition Composition::subset(const std::vector& symbols, std::string method) const { + std::array methods = {"norm", "none"}; + + if (std::find(methods.begin(), methods.end(), method) == methods.end()) { + std::string errorMessage = "Invalid method: " + method + ". Valid methods are 'norm' and 'none'."; + LOG_ERROR(m_logger, "Invalid method: {}. Valid methods are norm and none.", method); + throw std::runtime_error(errorMessage); + } + + Composition subsetComposition; + for (const auto& symbol : symbols) { + if (m_compositions.count(symbol) == 0) { + LOG_ERROR(m_logger, "Symbol {} is not in the composition.", symbol); + throw std::runtime_error("Symbol is not in the composition."); + } else { + subsetComposition.registerSymbol(symbol); + } + subsetComposition.setMassFraction(symbol, m_compositions.at(symbol).mass_fraction()); + } + if (method == "norm") { + bool isNorm = subsetComposition.finalize(true); + if (!isNorm) { + LOG_ERROR(m_logger, "Subset composition is invalid."); + throw std::runtime_error("Subset composition is invalid."); + } + } + return subsetComposition; } \ No newline at end of file diff --git a/src/composition/public/composition.h b/src/composition/public/composition.h index ea5215a..7335efb 100644 --- a/src/composition/public/composition.h +++ b/src/composition/public/composition.h @@ -1,13 +1,12 @@ #ifndef COMPOSITION_H #define COMPOSITION_H -#include #include #include #include #include -#include "quill/LogMacros.h" +#include #include "probe.h" #include "config.h" @@ -15,12 +14,139 @@ #include "atomicSpecies.h" namespace composition{ + /** + * @brief Represents the global composition of a system. This tends to be used after finalize and is primarily for internal use. + */ + struct GlobalComposition { + double specificNumberDensity; ///< The specific number density of the composition (\sum_{i} X_i m_i. Where X_i is the number fraction of the ith species and m_i is the mass of the ith species). + double meanParticleMass; ///< The mean particle mass of the composition (\sum_{i} \frac{n_i}{m_i}. where n_i is the number fraction of the ith species and m_i is the mass of the ith species). + + // Overload the output stream operator for GlobalComposition + friend std::ostream& operator<<(std::ostream& os, const GlobalComposition& comp) { + os << "Global Composition: \n"; + os << "\tSpecific Number Density: " << comp.specificNumberDensity << "\n"; + os << "\tMean Particle Mass: " << comp.meanParticleMass << "\n"; + return os; + } + }; + /** * @brief Represents an entry in the composition with a symbol and mass fraction. */ struct CompositionEntry { - std::string symbol; ///< The chemical symbol of the element. - double mass_fraction; ///< The mass fraction of the element. + std::string m_symbol; ///< The chemical symbol of the species. + chemSpecies::Species m_isotope; ///< The isotope of the species. + bool m_massFracMode = true; ///< The mode of the composition entry. True if mass fraction, false if number fraction. + + double m_massFraction = 0.0; ///< The mass fraction of the species. + double m_numberFraction = 0.0; ///< The number fraction of the species. + double m_relAbundance = 0.0; ///< The relative abundance of the species for converting between mass and number fractions. + + bool m_initialized = false; ///< True if the composition entry has been initialized. + + /** + * @brief Default constructor. + */ + CompositionEntry(); + + /** + * @brief Constructs a CompositionEntry with the given symbol and mode. + * @param symbol The chemical symbol of the species. + * @param massFracMode True if mass fraction mode, false if number fraction mode. + * @example + * @code + * CompositionEntry entry("H", true); + * @endcode + */ + CompositionEntry(const std::string& symbol, bool massFracMode=true); + + /** + * @brief Copy constructor. + * @param entry The CompositionEntry to copy. + */ + CompositionEntry(const CompositionEntry& entry); + + /** + * @brief Sets the species for the composition entry. + * @param symbol The chemical symbol of the species. + */ + void setSpecies(const std::string& symbol); + + /** + * @brief Gets the chemical symbol of the species. + * @return The chemical symbol of the species. + */ + std::string symbol() const; + + /** + * @brief Gets the mass fraction of the species. + * @return The mass fraction of the species. + */ + 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; + + /** + * @brief Gets the number fraction of the species. + * @return The number fraction of the species. + */ + 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; + + /** + * @brief Gets the relative abundance of the species. + * @return The relative abundance of the species. + */ + double rel_abundance() const; + + /** + * @brief Gets the isotope of the species. + * @return The isotope of the species. + */ + chemSpecies::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; + + /** + * @brief Sets the mass fraction of the species. + * @param mass_fraction The mass fraction to set. + */ + void setMassFraction(double mass_fraction); + + /** + * @brief Sets the number fraction of the species. + * @param number_fraction The number fraction to set. + */ + void setNumberFraction(double number_fraction); + + /** + * @brief Sets the mode to mass fraction mode. + * @param meanMolarMass The mean molar mass. + * @return True if the mode was successfully set, false otherwise. + */ + bool setMassFracMode(double meanMolarMass); + + /** + * @brief Sets the mode to number fraction mode. + * @param totalMoles The total moles. + * @return True if the mode was successfully set, false otherwise. + */ + bool setNumberFracMode(double totalMoles); /** * @brief Overloaded output stream operator for CompositionEntry. @@ -29,7 +155,7 @@ namespace composition{ * @return The output stream. */ friend std::ostream& operator<<(std::ostream& os, const CompositionEntry& entry) { - os << "<" << entry.symbol << " : " << entry.mass_fraction << ">"; + os << "<" << entry.m_symbol << " : m_frac = " << entry.mass_fraction() << ">"; return os; } }; @@ -68,10 +194,13 @@ namespace composition{ Probe::LogManager& m_logManager = Probe::LogManager::getInstance(); quill::Logger* m_logger = m_logManager.getLogger("log"); - bool m_finalized = false; + bool m_finalized = false; ///< True if the composition is finalized. + double m_specificNumberDensity = 0.0; ///< The specific number density of the composition (\sum_{i} X_i m_i. Where X_i is the number fraction of the ith species and m_i is the mass of the ith species). + double m_meanParticleMass = 0.0; ///< The mean particle mass of the composition (\sum_{i} \frac{n_i}{m_i}. where n_i is the number fraction of the ith species and m_i is the mass of the ith species). + bool m_massFracMode = true; ///< True if mass fraction mode, false if number fraction mode. - std::set m_registeredSymbols; - std::unordered_map m_compositions; + std::set m_registeredSymbols; ///< The registered symbols. + std::unordered_map m_compositions; ///< The compositions. /** * @brief Checks if the given symbol is valid. @@ -86,14 +215,28 @@ namespace composition{ * @param mass_fractions The mass fractions to check. * @return True if the mass fractions are valid, false otherwise. */ - bool isValidComposition(const std::vector& mass_fractions) const; + bool isValidComposition(const std::vector& fractions) const; /** * @brief Validates the given mass fractions. * @param mass_fractions The mass fractions to validate. * @throws std::invalid_argument if the mass fractions are invalid. */ - void validateComposition(const std::vector& mass_fractions) const; + void validateComposition(const std::vector& fractions) const; + + /** + * @brief Finalizes the composition in mass fraction mode. + * @param norm If true, the composition will be normalized to sum to 1. + * @return True if the composition is successfully finalized, false otherwise. + */ + bool finalizeMassFracMode(bool norm); + + /** + * @brief Finalizes the composition in number fraction mode. + * @param norm If true, the composition will be normalized to sum to 1. + * @return True if the composition is successfully finalized, false otherwise. + */ + bool finalizeNumberFracMode(bool norm); public: /** @@ -108,9 +251,10 @@ namespace composition{ /** * @brief Finalizes the composition. + * @param norm If true, the composition will be normalized to sum to 1 [Default False] * @return True if the composition is successfully finalized, false otherwise. */ - bool finalize(); + bool finalize(bool norm=false); /** * @brief Constructs a Composition with the given symbols. @@ -127,6 +271,7 @@ namespace composition{ * @brief Constructs a Composition with the given symbols and mass fractions. * @param symbols The symbols to initialize the composition with. * @param mass_fractions The mass fractions corresponding to the symbols. + * @param massFracMode True if mass fraction mode, false if number fraction mode. * @example * @code * std::vector symbols = {"H", "O"}; @@ -134,18 +279,32 @@ namespace composition{ * Composition comp(symbols, mass_fractions); * @endcode */ - Composition(const std::vector& symbols, const std::vector& mass_fractions); + Composition(const std::vector& symbols, const std::vector& mass_fractions, bool massFracMode=true); /** * @brief Registers a new symbol. * @param symbol The symbol to register. + * @param massFracMode True if mass fraction mode, false if number fraction mode. * @example * @code * Composition comp; * comp.registerSymbol("H"); * @endcode */ - void registerSymbol(const std::string& symbol); + void registerSymbol(const std::string& symbol, bool massFracMode=true); + + /** + * @brief Registers multiple new symbols. + * @param symbols The symbols to register. + * @param massFracMode True if mass fraction mode, false if number fraction mode. + * @example + * @code + * std::vector symbols = {"H", "O"}; + * Composition comp; + * comp.registerSymbol(symbols); + * @endcode + */ + void registerSymbol(const std::vector& symbols, bool massFracMode=true); /** * @brief Gets the registered symbols. @@ -154,21 +313,21 @@ namespace composition{ std::set getRegisteredSymbols() const; /** - * @brief Sets the composition for a given symbol. - * @param symbol The symbol to set the composition for. + * @brief Sets the mass fraction for a given symbol. + * @param symbol The symbol to set the mass fraction for. * @param mass_fraction The mass fraction to set. * @return The mass fraction that was set. * @example * @code * Composition comp; - * comp.setComposition("H", 0.1); + * comp.setMassFraction("H", 0.1); * @endcode */ - double setComposition(const std::string& symbol, const double& mass_fraction); + double setMassFraction(const std::string& symbol, const double& mass_fraction); /** - * @brief Sets the composition for multiple symbols. - * @param symbols The symbols to set the composition for. + * @brief Sets the mass fraction for multiple symbols. + * @param symbols The symbols to set the mass fraction for. * @param mass_fractions The mass fractions corresponding to the symbols. * @return A vector of mass fractions that were set. * @example @@ -176,23 +335,73 @@ namespace composition{ * std::vector symbols = {"H", "O"}; * std::vector mass_fractions = {0.1, 0.9}; * Composition comp; - * comp.setComposition(symbols, mass_fractions); + * comp.setMassFraction(symbols, mass_fractions); * @endcode */ - std::vector setComposition(const std::vector& symbols, const std::vector& mass_fractions); + std::vector setMassFraction(const std::vector& symbols, const std::vector& mass_fractions); /** - * @brief Gets the compositions. - * @return An unordered map of compositions. + * @brief Sets the number fraction for a given symbol. + * @param symbol The symbol to set the number fraction for. + * @param number_fraction The number fraction to set. + * @return The number fraction that was set. */ - std::unordered_map getCompositions() const; + double setNumberFraction(const std::string& symbol, const double& number_fraction); /** - * @brief Gets the composition for a given symbol. + * @brief Sets the number fraction for multiple symbols. + * @param symbols The symbols to set the number fraction for. + * @param number_fractions The number fractions corresponding to the symbols. + * @return A vector of number fractions that were set. + */ + std::vector setNumberFraction(const std::vector& symbols, const std::vector& number_fractions); + + /** + * @brief Gets the mass fractions of all compositions. + * @return An unordered map of compositions with their mass fractions. + */ + std::unordered_map getMassFraction() const; + + /** + * @brief Gets the mass fraction for a given symbol. + * @param symbol The symbol to get the mass fraction for. + * @return The mass fraction for the given symbol. + */ + double getMassFraction(const std::string& symbol) const; + + /** + * @brief Gets the number fraction for a given symbol. + * @param symbol The symbol to get the number fraction for. + * @return The number fraction for the given symbol. + */ + double getNumberFraction(const std::string& symbol) const; + + /** + * @brief Gets the number fractions of all compositions. + * @return An unordered map of compositions with their number fractions. + */ + std::unordered_map getNumberFraction() const; + + /** + * @brief Gets the composition entry and global composition for a given symbol. * @param symbol The symbol to get the composition for. - * @return The CompositionEntry for the given symbol. + * @return A pair containing the CompositionEntry and GlobalComposition for the given symbol. */ - CompositionEntry getComposition(const std::string& symbol) const; + std::pair getComposition(const std::string& symbol) const; + + /** + * @brief Gets all composition entries and the global composition. + * @return A pair containing an unordered map of CompositionEntries and the GlobalComposition. + */ + std::pair, GlobalComposition> getComposition() const; + + /** + * @brief Gets a subset of the composition. + * @param symbols The symbols to include in the subset. + * @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; /** * @brief Overloaded output stream operator for Composition. diff --git a/tests/composition/compositionTest.cpp b/tests/composition/compositionTest.cpp index 5bd2a3d..5d6da06 100644 --- a/tests/composition/compositionTest.cpp +++ b/tests/composition/compositionTest.cpp @@ -49,23 +49,93 @@ TEST_F(compositionTest, setGetComposition) { comp.registerSymbol("H-1"); comp.registerSymbol("He-4"); - EXPECT_DOUBLE_EQ(comp.setComposition("H-1", 0.5), 0.0); - EXPECT_DOUBLE_EQ(comp.setComposition("He-4", 0.5), 0.0); - EXPECT_DOUBLE_EQ(comp.setComposition("H-1", 0.6), 0.5); - EXPECT_DOUBLE_EQ(comp.setComposition("He-4", 0.4), 0.5); + EXPECT_DOUBLE_EQ(comp.setMassFraction("H-1", 0.5), 0.0); + EXPECT_DOUBLE_EQ(comp.setMassFraction("He-4", 0.5), 0.0); + EXPECT_DOUBLE_EQ(comp.setMassFraction("H-1", 0.6), 0.5); + EXPECT_DOUBLE_EQ(comp.setMassFraction("He-4", 0.4), 0.5); EXPECT_NO_THROW(comp.finalize()); - std::unordered_map compositions = comp.getCompositions(); - EXPECT_DOUBLE_EQ(compositions["H-1"].mass_fraction, 0.6); + EXPECT_DOUBLE_EQ(comp.getMassFraction("H-1"), 0.6); - EXPECT_THROW(comp.setComposition("He-3", 0.3), std::runtime_error); + EXPECT_THROW(comp.setMassFraction("He-3", 0.3), std::runtime_error); - EXPECT_NO_THROW(comp.setComposition({"H-1", "He-4"}, {0.5, 0.5})); + EXPECT_NO_THROW(comp.setMassFraction({"H-1", "He-4"}, {0.5, 0.5})); EXPECT_THROW(comp.getComposition("H-1"), std::runtime_error); EXPECT_TRUE(comp.finalize()); - EXPECT_DOUBLE_EQ(comp.getComposition("H-1").mass_fraction, 0.5); + EXPECT_DOUBLE_EQ(comp.getComposition("H-1").first.mass_fraction(), 0.5); - EXPECT_NO_THROW(comp.setComposition({"H-1", "He-4"}, {0.6, 0.6})); + EXPECT_NO_THROW(comp.setMassFraction({"H-1", "He-4"}, {0.6, 0.6})); EXPECT_FALSE(comp.finalize()); EXPECT_THROW(comp.getComposition("H-1"), std::runtime_error); +} + +TEST_F(compositionTest, setGetNumberFraction) { + Config::getInstance().loadConfig(EXAMPLE_FILENAME); + composition::Composition comp; + comp.registerSymbol("H-1", false); + comp.registerSymbol("He-4", false); + + EXPECT_DOUBLE_EQ(comp.setNumberFraction("H-1", 0.5), 0.0); + EXPECT_DOUBLE_EQ(comp.setNumberFraction("He-4", 0.5), 0.0); + EXPECT_DOUBLE_EQ(comp.setNumberFraction("H-1", 0.6), 0.5); + EXPECT_DOUBLE_EQ(comp.setNumberFraction("He-4", 0.4), 0.5); + + EXPECT_NO_THROW(comp.finalize()); + EXPECT_DOUBLE_EQ(comp.getNumberFraction("H-1"), 0.6); + + EXPECT_THROW(comp.setNumberFraction("He-3", 0.3), std::runtime_error); +} + +TEST_F(compositionTest, subset) { + Config::getInstance().loadConfig(EXAMPLE_FILENAME); + composition::Composition comp; + comp.registerSymbol("H-1"); + comp.registerSymbol("He-4"); + comp.setMassFraction("H-1", 0.6); + comp.setMassFraction("He-4", 0.4); + EXPECT_NO_THROW(comp.finalize()); + + std::vector symbols = {"H-1"}; + composition::Composition subsetComp = comp.subset(symbols, "norm"); + EXPECT_TRUE(subsetComp.finalize()); + EXPECT_DOUBLE_EQ(subsetComp.getMassFraction("H-1"), 1.0); +} + +TEST_F(compositionTest, finalizeWithNormalization) { + Config::getInstance().loadConfig(EXAMPLE_FILENAME); + composition::Composition comp; + comp.registerSymbol("H-1"); + comp.registerSymbol("He-4"); + comp.setMassFraction("H-1", 0.3); + comp.setMassFraction("He-4", 0.3); + EXPECT_TRUE(comp.finalize(true)); + EXPECT_DOUBLE_EQ(comp.getMassFraction("H-1"), 0.5); + EXPECT_DOUBLE_EQ(comp.getMassFraction("He-4"), 0.5); +} + +TEST_F(compositionTest, finalizeWithoutNormalization) { + Config::getInstance().loadConfig(EXAMPLE_FILENAME); + composition::Composition comp; + comp.registerSymbol("H-1"); + comp.registerSymbol("He-4"); + comp.setMassFraction("H-1", 0.5); + comp.setMassFraction("He-4", 0.5); + EXPECT_TRUE(comp.finalize(false)); + EXPECT_DOUBLE_EQ(comp.getMassFraction("H-1"), 0.5); + EXPECT_DOUBLE_EQ(comp.getMassFraction("He-4"), 0.5); +} + +TEST_F(compositionTest, getComposition) { + Config::getInstance().loadConfig(EXAMPLE_FILENAME); + composition::Composition comp; + comp.registerSymbol("H-1"); + comp.registerSymbol("He-4"); + comp.setMassFraction("H-1", 0.6); + comp.setMassFraction("He-4", 0.4); + EXPECT_NO_THROW(comp.finalize()); + + auto compositionEntry = comp.getComposition("H-1"); + EXPECT_DOUBLE_EQ(compositionEntry.first.mass_fraction(), 0.6); + EXPECT_DOUBLE_EQ(compositionEntry.second.meanParticleMass, 1.4382769310381101); + EXPECT_DOUBLE_EQ(compositionEntry.second.specificNumberDensity, 1.0/1.4382769310381101); } \ No newline at end of file