feat(composition): changed how composition is conmstructed

Composition objects now must be built from vectors of molar abundances.  Things such as mass fraction and number fraction are computed on the fly (with some caching for performance). This will allow for many fewer issues when converting from solver space to composition space.

BREAKING CHANGE: The entire old API has been broken. There is no longer any need to finalize. In fact the entire concept of finalization has been removed. Further, the entire CompositionEntry and GlobalComposition data structure has been removed. Any code written for the old version will no longer work and major reworking will be needed to use the new version.
This commit is contained in:
2025-11-07 15:49:25 -05:00
parent 106f7c1c96
commit 84947a2b12
11 changed files with 519 additions and 1966 deletions

View File

@@ -3,7 +3,7 @@
#include <string_view>
#include <string>
#include <iostream>
#include <optional>
#include <limits>
@@ -17,7 +17,7 @@ namespace fourdst::atomic {
* @param jpi_string The spin-parity string to convert (e.g., "1/2+", "5/2-", "0+").
* @return The spin value as a double. Returns `NaN` for invalid or unparsable strings.
*/
inline double convert_jpi_to_double(const std::string& jpi_string);
inline double convert_jpi_to_double(const std::string& jpi_string) noexcept;
/**
* @struct Species
@@ -63,7 +63,7 @@ namespace fourdst::atomic {
std::string m_decayModes; ///< Decay modes as a string.
double m_atomicMass; ///< Atomic mass in atomic mass units (u).
double m_atomicMassUnc; ///< Uncertainty in the atomic mass.
double m_spin = 0.0; ///< Nuclear spin as a double, derived from m_spinParity.
mutable std::optional<double> m_spin = std::nullopt; ///< Nuclear spin as a double, derived from m_spinParity.
/**
* @brief Constructs a Species object with detailed properties.
@@ -114,9 +114,7 @@ namespace fourdst::atomic {
m_spinParity(spinParity),
m_decayModes(decayModes),
m_atomicMass(atomicMass),
m_atomicMassUnc(atomicMassUnc) {
m_spin = convert_jpi_to_double(m_spinParity);
};
m_atomicMassUnc(atomicMassUnc) {};
/**
* @brief Copy constructor for Species.
@@ -138,7 +136,6 @@ 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);
}
@@ -259,7 +256,10 @@ namespace fourdst::atomic {
* @return The spin as a double.
*/
[[nodiscard]] double spin() const {
return m_spin;
if (!m_spin.has_value()) { // The spin calculation is very expensive, and we almost never need it so we only compute it the first time it is requested
m_spin = convert_jpi_to_double(m_spinParity);
}
return m_spin.value();
}
/**
@@ -345,7 +345,7 @@ namespace fourdst::atomic {
*
* @return The spin value as a `double`. Returns `NaN` for invalid or unparsable strings.
*/
inline double convert_jpi_to_double(const std::string& jpi_string) {
inline double convert_jpi_to_double(const std::string& jpi_string) noexcept {
std::string s = jpi_string;
if (s.empty()) {

View File

@@ -3,8 +3,8 @@
#include <string_view>
#include <string>
#include <limits> // Required for std::numeric_limits
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/composition/elements.h"
#include "fourdst/atomic/atomicSpecies.h"
#include "elements.h"
#include <expected> // For std::expected

View File

@@ -24,13 +24,12 @@
#include <unordered_map>
#include <set>
#include <utility>
#include <optional>
#include "fourdst/config/config.h"
#include "fourdst/logging/logging.h"
#include "fourdst/composition/composition_abstract.h"
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/atomic/atomicSpecies.h"
namespace fourdst::composition {
/**
@@ -62,158 +61,6 @@ namespace fourdst::composition {
}
};
/**
* @brief Represents global properties of a finalized composition.
* @details This struct holds derived quantities that describe the entire composition,
* such as mean particle mass. It is typically returned by `Composition` methods
* after the composition has been finalized and is intended for internal or advanced use.
*/
struct GlobalComposition {
double specificNumberDensity; ///< The specific number density (moles per unit mass, sum of X_i/M_i), where X_i is mass fraction and M_i is molar mass. Units: mol/g.
double meanParticleMass; ///< The mean mass per particle (inverse of specific number density). Units: g/mol.
// Overload the output stream operator for GlobalComposition
friend std::ostream& operator<<(std::ostream& os, const GlobalComposition& comp);
};
/**
* @brief Represents a single entry (an isotope) within a composition.
* @details This struct holds the properties of one component, including its symbol,
* the corresponding `atomic::Species` object, and its abundance (either as a mass
* fraction or number fraction). It manages the state and conversions for that single entry.
*/
struct CompositionEntry {
std::optional<std::string> m_symbol = std::nullopt; ///< The chemical symbol of the species (e.g., "H-1", "Fe-56").
std::optional<atomic::Species> m_isotope = std::nullopt; ///< The `atomic::Species` object containing detailed isotope data.
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. Valid only if `m_massFracMode` is true.
double m_numberFraction = 0.0; ///< The number fraction (mole fraction) of the species. Valid only if `m_massFracMode` is false.
double m_relAbundance = 0.0; ///< The relative abundance, used internally for conversions. For mass fraction mode, this is X_i / A_i; for number fraction mode, it's n_i * A_i.
double m_molesPerMass = 0.0;
double m_cachedNumberFraction = 0.0; ///< Cached number fraction for conversions when in mass fraction mode.
bool m_initialized = false; ///< True if the composition entry has been initialized with a valid species.
/**
* @brief Default constructor. Initializes a default entry (H-1), but in an uninitialized state.
*/
CompositionEntry();
/**
* @brief Constructs a CompositionEntry for a given symbol and abundance mode.
* @param symbol The chemical symbol of the species (e.g., "He-4").
* @param massFracMode True to operate in mass fraction mode, false for number fraction mode.
* @throws exceptions::InvalidSpeciesSymbolError if the symbol does not exist in the atomic species database.
* @throws exceptions::EntryAlreadyInitializedError if setSpecies is called on an already initialized entry.
* @par Usage Example:
* @code
* CompositionEntry entry("H-1", true); // Entry for H-1 in mass fraction mode.
* @endcode
*/
explicit 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. This can only be done once.
* @param symbol The chemical symbol of the species.
* @throws exceptions::EntryAlreadyInitializedError if the entry has already been initialized.
* @throws exceptions::InvalidSpeciesSymbolError if the symbol is not found in the atomic species database.
*/
void setSpecies(const std::string& symbol);
/**
* @brief Gets the chemical symbol of the species.
* @return The chemical symbol.
*/
[[nodiscard]] std::string symbol() const;
/**
* @brief Gets the mass fraction of the species.
* @pre The entry must be in mass fraction mode.
* @return The mass fraction of the species.
* @throws exceptions::CompositionModeError if the entry is in number fraction mode.
*/
[[nodiscard]] double mass_fraction() const;
/**
* @brief Gets the number fraction of the species.
* @pre The entry must be in number fraction mode.
* @return The number fraction of the species.
* @throws exceptions::CompositionModeError if the entry is in mass fraction mode.
*/
[[nodiscard]] double number_fraction() const;
/**
* @brief Gets the number fraction, converting from mass fraction if necessary.
* @param totalMolesPerMass The total moles per unit mass (specific number density) of the entire composition.
* @return The number fraction of the species.
*/
[[nodiscard]] double number_fraction(double totalMolesPerMass) const;
/**
* @brief Gets the relative abundance of the species.
* @return The relative abundance.
*/
[[nodiscard]] double rel_abundance() const;
/**
* @brief Gets the isotope data for the species.
* @return A const reference to the `atomic::Species` object.
*/
[[nodiscard]] atomic::Species isotope() const;
/**
* @brief Gets the mode of the composition entry.
* @return True if in mass fraction mode, false if in number fraction mode.
*/
[[nodiscard]] bool getMassFracMode() const;
/**
* @brief Sets the mass fraction of the species.
* @param mass_fraction The mass fraction to set. Must be in [0, 1].
* @pre The entry must be in mass fraction mode.
* @throws exceptions::CompositionModeError if the entry is in number fraction mode.
*/
void setMassFraction(double mass_fraction);
/**
* @brief Sets the number fraction of the species.
* @param number_fraction The number fraction to set. Must be in [0, 1].
* @pre The entry must be in number fraction mode.
* @throws exceptions::CompositionModeError if the entry is in mass fraction mode.
*/
void setNumberFraction(double number_fraction);
/**
* @brief Switches the mode to mass fraction mode.
* @param meanMolarMass The mean molar mass of the composition, required for conversion.
* @return True if the mode was successfully set, false otherwise.
*/
bool setMassFracMode(double meanMolarMass);
/**
* @brief Switches the mode to number fraction mode.
* @param totalMolesPerMass The total moles per unit mass (specific number density) of the composition.
* @return True if the mode was successfully set, false otherwise.
*/
bool setNumberFracMode(double totalMolesPerMass);
/**
* @brief Overloaded output stream operator for CompositionEntry.
* @param os The output stream.
* @param entry The CompositionEntry to output.
* @return The output stream.
*/
friend std::ostream& operator<<(std::ostream& os, const CompositionEntry& entry);
};
/**
* @class Composition
* @brief Manages a collection of chemical species and their abundances.
@@ -256,7 +103,6 @@ namespace fourdst::composition {
class Composition : public CompositionAbstract {
private:
struct CompositionCache {
std::optional<GlobalComposition> globalComp; ///< Cached global composition data.
std::optional<CanonicalComposition> canonicalComp; ///< Cached canonical composition data.
std::optional<std::vector<double>> massFractions; ///< Cached vector of mass fractions.
std::optional<std::vector<double>> numberFractions; ///< Cached vector of number fractions.
@@ -266,7 +112,6 @@ namespace fourdst::composition {
std::optional<double> Ye; ///< Cached electron abundance.
void clear() {
globalComp = std::nullopt;
canonicalComp = std::nullopt;
massFractions = std::nullopt;
numberFractions = std::nullopt;
@@ -277,7 +122,7 @@ namespace fourdst::composition {
}
[[nodiscard]] bool is_clear() const {
return !globalComp.has_value() && !canonicalComp.has_value() && !massFractions.has_value() &&
return !canonicalComp.has_value() && !massFractions.has_value() &&
!numberFractions.has_value() && !molarAbundances.has_value() && !sortedSymbols.has_value() &&
!Ye.has_value() && !sortedSpecies.has_value();
}
@@ -289,52 +134,11 @@ namespace fourdst::composition {
return logger;
}
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<std::string> m_registeredSymbols; ///< The registered symbols.
std::unordered_map<std::string, CompositionEntry> m_compositions; ///< The compositions.
std::set<atomic::Species> m_registeredSpecies;
std::map<atomic::Species, double> m_molarAbundances;
mutable CompositionCache m_cache; ///< Cache for computed properties to avoid redundant calculations.
/**
* @brief Checks if the given symbol is valid by checking against the global species database.
* @param symbol The symbol to check.
* @return True if the symbol is valid, false otherwise.
*/
static bool isValidSymbol(const std::string& symbol);
/**
* @brief Checks if the given fractions are valid (sum to ~1.0).
* @param fractions The fractions to check.
* @return True if the fractions are valid, false otherwise.
*/
[[nodiscard]] static bool isValidComposition(const std::vector<double>& fractions) ;
/**
* @brief Validates the given fractions, throwing an exception on failure.
* @param fractions The fractions to validate.
* @throws exceptions::InvalidCompositionError if the fractions are invalid.
*/
static void validateComposition(const std::vector<double>& fractions) ;
/**
* @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:
/**
* @brief Default constructor.
@@ -346,17 +150,6 @@ namespace fourdst::composition {
*/
~Composition() override = default;
/**
* @brief Finalizes the composition, making it ready for querying.
* @details This method checks if the sum of all fractions (mass or number) is approximately 1.0.
* It also computes global properties like mean particle mass. This **must** be called before
* any `get...` method can be used.
* @param norm If true, the composition will be normalized to sum to 1 before validation. [Default: false]
* @return True if the composition is valid and successfully finalized, false otherwise.
* @post If successful, `m_finalized` is true and global properties are computed.
*/
[[nodiscard]] bool finalize(bool norm=false);
/**
* @brief Constructs a Composition and registers the given symbols.
* @param symbols The symbols to register. The composition will be in mass fraction mode by default.
@@ -372,6 +165,8 @@ namespace fourdst::composition {
*/
explicit Composition(const std::vector<std::string>& symbols);
explicit Composition(const std::vector<atomic::Species>& species);
/**
* @brief Constructs a Composition and registers the given symbols from a set.
* @param symbols The symbols to register. The composition will be in mass fraction mode by default.
@@ -384,13 +179,14 @@ namespace fourdst::composition {
*/
explicit Composition(const std::set<std::string>& symbols);
explicit Composition(const std::set<atomic::Species>& species);
/**
* @brief Constructs and finalizes a Composition with the given symbols and fractions.
* @details This constructor provides a convenient way to create a fully-formed, finalized composition in one step.
* The provided fractions must be valid and sum to 1.0.
* @param symbols The symbols to initialize the composition with.
* @param fractions The fractions (mass or number) corresponding to the symbols.
* @param massFracMode True if `fractions` are mass fractions, false if they are number fractions. [Default: true]
* @param molarAbundances The corresponding molar abundances for each symbol.
* @throws exceptions::InvalidCompositionError if the number of symbols and fractions do not match, or if the fractions do not sum to ~1.0.
* @throws exceptions::InvalidSymbolError if any symbol is invalid.
* @post The composition is immediately finalized.
@@ -401,7 +197,11 @@ namespace fourdst::composition {
* Composition comp(symbols, mass_fractions); // Finalized on construction
* @endcode
*/
Composition(const std::vector<std::string>& symbols, const std::vector<double>& fractions, bool massFracMode=true);
Composition(const std::vector<std::string>& symbols, const std::vector<double>& molarAbundances);
Composition(const std::vector<atomic::Species>& species, const std::vector<double>& molarAbundances);
Composition(const std::set<std::string>& symbols, const std::vector<double>& molarAbundances);
/**
* @brief Constructs a Composition from another Composition.
@@ -420,7 +220,6 @@ namespace fourdst::composition {
* @brief Registers a new symbol for inclusion in the composition.
* @details A symbol must be registered before its abundance can be set. The first registration sets the mode (mass/number fraction) for the entire composition.
* @param symbol The symbol to register (e.g., "Fe-56").
* @param massFracMode True for mass fraction mode, false for number fraction mode. This is only effective for the first symbol registered.
* @throws exceptions::InvalidSymbolError if the symbol is not in the atomic species database.
* @throws exceptions::CompositionModeError if attempting to register with a mode that conflicts with the existing mode.
* @par Usage Example:
@@ -430,12 +229,11 @@ namespace fourdst::composition {
* comp.registerSymbol("He-4"); // Must also be mass fraction mode
* @endcode
*/
void registerSymbol(const std::string& symbol, bool massFracMode=true);
void registerSymbol(const std::string& symbol);
/**
* @brief Registers multiple new symbols.
* @param symbols The symbols to register.
* @param massFracMode True for mass fraction mode, false for number fraction mode.
* @throws exceptions::InvalidSymbolError if any symbol is invalid.
* @throws exceptions::CompositionModeError if the mode conflicts with an already set mode.
* @par Usage Example:
@@ -445,12 +243,11 @@ namespace fourdst::composition {
* comp.registerSymbol(symbols);
* @endcode
*/
void registerSymbol(const std::vector<std::string>& symbols, bool massFracMode=true);
void registerSymbol(const std::vector<std::string>& symbols);
/**
* @brief Registers a new species by extracting its symbol.
* @param species The species to register.
* @param massFracMode True for mass fraction mode, false for number fraction mode.
* @throws exceptions::InvalidSymbolError if the species' symbol is invalid.
* @throws exceptions::CompositionModeError if the mode conflicts.
* @par Usage Example:
@@ -460,13 +257,12 @@ namespace fourdst::composition {
* comp.registerSpecies(fourdst::atomic::species.at("H-1"));
* @endcode
*/
void registerSpecies(const fourdst::atomic::Species& species, bool massFracMode=true);
void registerSpecies(const atomic::Species& species);
/**
* @brief Registers a vector of new species.
* @param species The vector of species to register.
* @param massFracMode True for mass fraction mode, false for number fraction mode.
* @throws exceptions::InvalidSymbolError if any species' symbol is invalid.
* @throws exceptions::CompositionModeError if the mode conflicts.
* @par Usage Example:
@@ -477,8 +273,50 @@ namespace fourdst::composition {
* comp.registerSpecies(my_species, false); // Number fraction mode
* @endcode
*/
void registerSpecies(const std::vector<fourdst::atomic::Species>& species, bool massFracMode=true);
void registerSpecies(const std::vector<atomic::Species>& species);
/**
* @brief Checks if a given isotope is present in the composition.
* @pre The composition must be finalized.
* @param species The isotope to check for.
* @return True if the isotope is in the composition, false otherwise.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
*/
[[nodiscard]] bool contains(const atomic::Species& species) const override;
[[nodiscard]] bool contains(const std::string& symbol) const override;
[[nodiscard]] size_t size() const override;
void setMolarAbundance(
const std::string& symbol,
const double& molar_abundance
);
void setMolarAbundance(
const atomic::Species& species,
const double& molar_abundance
);
void setMolarAbundance(
const std::vector<std::string>& symbols,
const std::vector<double>& molar_abundances
);
void setMolarAbundance(
const std::vector<atomic::Species>& species,
const std::vector<double>& molar_abundances
);
void setMolarAbundance(
const std::set<std::string>& symbols,
const std::vector<double>& molar_abundances
);
void setMolarAbundance(
const std::set<atomic::Species>& species,
const std::vector<double>& molar_abundances
);
/**
* @brief Gets the registered symbols.
@@ -490,112 +328,7 @@ namespace fourdst::composition {
* @brief Get a set of all species that are registered in the composition.
* @return A set of `atomic::Species` objects registered in the composition.
*/
[[nodiscard]] std::set<fourdst::atomic::Species> getRegisteredSpecies() const override;
/**
* @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 (must be in [0, 1]).
* @return The previous mass fraction that was set for the symbol.
* @throws exceptions::UnregisteredSymbolError if the symbol is not registered.
* @throws exceptions::CompositionModeError if the composition is in number fraction mode.
* @throws exceptions::InvalidCompositionError if the mass fraction is not between 0 and 1.
* @post The composition is marked as not finalized.
* @par Usage Example:
* @code
* Composition comp;
* comp.registerSymbol("H-1");
* comp.setMassFraction("H-1", 0.7);
* @endcode
*/
double setMassFraction(const std::string& symbol, const double& mass_fraction);
/**
* @brief Sets the mass fraction for multiple symbols.
* @param symbols The symbols to set the mass fractions for.
* @param mass_fractions The mass fractions corresponding to the symbols.
* @return A vector of the previous mass fractions that were set.
* @throws exceptions::InvalidCompositionError if symbol and fraction counts differ.
* @throws See `setMassFraction(const std::string&, const double&)` for other exceptions.
* @post The composition is marked as not finalized.
*/
std::vector<double> setMassFraction(const std::vector<std::string>& symbols, const std::vector<double>& mass_fractions);
/**
* @brief Sets the mass fraction for a given species.
* @param species The species to set the mass fraction for.
* @param mass_fraction The mass fraction to set.
* @return The previous mass fraction that was set for the species.
* @throws exceptions::UnregisteredSymbolError if the species is not registered.
* @throws exceptions::CompositionModeError if the composition is in number fraction mode.
* @throws exceptions::InvalidCompositionError if the mass fraction is not between 0 and 1.
*/
double setMassFraction(const fourdst::atomic::Species& species, const double& mass_fraction);
/**
* @brief Sets the mass fraction for multiple species.
* @param species The vector of species to set the mass fractions for.
* @param mass_fractions The vector of mass fractions corresponding to the species.
* @return A vector of the previous mass fractions that were set.
* @throws See `setMassFraction(const std::vector<std::string>&, const std::vector<double>&)` for exceptions.
*/
std::vector<double> setMassFraction(const std::vector<fourdst::atomic::Species>& species, const std::vector<double>& mass_fractions);
/**
* @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 (must be in [0, 1]).
* @return The previous number fraction that was set.
* @throws exceptions::UnregisteredSymbolError if the symbol is not registered.
* @throws exceptions::CompositionModeError if the composition is in mass fraction mode.
* @throws exceptions::InvalidCompositionError if the number fraction is not between 0 and 1.
* @post The composition is marked as not finalized.
*/
double setNumberFraction(const std::string& symbol, const double& number_fraction);
/**
* @brief Sets the number fraction for multiple symbols.
* @param symbols The symbols to set the number fractions for.
* @param number_fractions The number fractions corresponding to the symbols.
* @return A vector of the previous number fractions that were set.
* @throws exceptions::InvalidCompositionError if symbol and fraction counts differ.
* @throws See `setNumberFraction(const std::string&, const double&)` for other exceptions.
*/
std::vector<double> setNumberFraction(const std::vector<std::string>& symbols, const std::vector<double>& number_fractions);
/**
* @brief Sets the number fraction for a given species.
* @param species The species to set the number fraction for.
* @param number_fraction The number fraction to set for the species.
* @return The previous number fraction that was set for the species.
* @throws exceptions::UnregisteredSymbolError if the species is not registered.
* @throws exceptions::CompositionModeError if the composition is in mass fraction mode.
* @throws exceptions::InvalidCompositionError if the number fraction is not between 0 and 1.
*/
double setNumberFraction(const fourdst::atomic::Species& species, const double& number_fraction);
/**
* @brief Sets the number fraction for multiple species.
* @param species The vector of species to set the number fractions for.
* @param number_fractions The vector of number fractions corresponding to the species.
* @return The vector of the previous number fractions that were set.
* @throws See `setNumberFraction(const std::vector<std::string>&, const std::vector<double>&)` for exceptions.
*/
std::vector<double> setNumberFraction(const std::vector<fourdst::atomic::Species>& species, const std::vector<double>& number_fractions);
/**
* @brief Mixes this composition with another to produce a new composition.
* @details The mixing is performed linearly on the mass fractions. The formula for each species is:
* `new_X_i = fraction * this_X_i + (1 - fraction) * other_X_i`.
* The resulting composition is automatically finalized.
* @param other The other composition to mix with.
* @param fraction The mixing fraction. A value of 1.0 means the new composition is 100% `this`, 0.0 means 100% `other`.
* @return A new, finalized `Composition` object representing the mixture.
* @pre Both `this` and `other` compositions must be finalized.
* @throws exceptions::CompositionNotFinalizedError if either composition is not finalized.
* @throws exceptions::InvalidCompositionError if the fraction is not between 0 and 1.
*/
[[nodiscard]] Composition mix(const Composition& other, double fraction) const;
[[nodiscard]] const std::set<atomic::Species> &getRegisteredSpecies() const override;
/**
* @brief Gets the mass fractions of all species in the composition.
@@ -603,7 +336,7 @@ namespace fourdst::composition {
* @return An unordered map of symbols to their mass fractions.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
*/
[[nodiscard]] std::unordered_map<std::string, double> getMassFraction() const override;
[[nodiscard]] std::unordered_map<atomic::Species, double> getMassFraction() const override;
/**
* @brief Gets the mass fraction for a given symbol.
@@ -651,7 +384,7 @@ namespace fourdst::composition {
* @return An unordered map of symbols to their number fractions.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
*/
[[nodiscard]] std::unordered_map<std::string, double> getNumberFraction() const override;
[[nodiscard]] std::unordered_map<atomic::Species, double> getNumberFraction() const override;
/**
* @brief Gets the molar abundance (X_i / A_i) for a given symbol.
@@ -671,35 +404,7 @@ namespace fourdst::composition {
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
* @throws exceptions::UnregisteredSymbolError if the isotope is not registered in the composition.
*/
[[nodiscard]] double getMolarAbundance(const fourdst::atomic::Species& species) const override;
/**
* @brief Gets the composition entry and global composition data for a given symbol.
* @pre The composition must be finalized.
* @param symbol The symbol to get the composition for.
* @return A pair containing the CompositionEntry and GlobalComposition for the given symbol.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
* @throws exceptions::UnregisteredSymbolError if the symbol is not in the composition.
*/
[[nodiscard]] std::pair<CompositionEntry, GlobalComposition> getComposition(const std::string& symbol) const;
/**
* @brief Gets the composition entry and global composition data for a given species.
* @pre The composition must be finalized.
* @param species The species to get the composition for.
* @return A pair containing the CompositionEntry and GlobalComposition for the given species.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
* @throws exceptions::UnregisteredSymbolError if the species is not in the composition.
*/
[[nodiscard]] std::pair<CompositionEntry, GlobalComposition> getComposition(const fourdst::atomic::Species& species) const;
/**
* @brief Gets all composition entries and the global composition data.
* @pre The composition must be finalized.
* @return A pair containing an unordered map of all CompositionEntries and the GlobalComposition.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
*/
[[nodiscard]] std::pair<std::unordered_map<std::string, CompositionEntry>, GlobalComposition> getComposition() const;
[[nodiscard]] double getMolarAbundance(const atomic::Species& species) const override;
/**
* @brief Compute the mean particle mass of the composition.
@@ -709,14 +414,6 @@ namespace fourdst::composition {
*/
[[nodiscard]] double getMeanParticleMass() const override;
/**
* @brief Compute the mean atomic number of the composition.
* @pre The composition must be finalized.
* @return Mean atomic number <Z>.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
*/
[[nodiscard]] double getMeanAtomicNumber() const override;
/**
* @brief Compute the electron abundance of the composition.
* @details Ye is defined as the sum over all species of (Z_i * X_i / A_i), where Z_i is the atomic number, X_i is the mass fraction, and A_i is the atomic mass of species i.
@@ -725,49 +422,6 @@ namespace fourdst::composition {
*/
[[nodiscard]] double getElectronAbundance() const override;
/**
* @brief Creates a new Composition object containing a subset of species from this one.
* @param symbols The symbols to include in the subset.
* @param method The method for handling the abundances of the new subset. Can be "norm" (normalize abundances to sum to 1) or "none" (keep original abundances).
* @return A new `Composition` object containing the subset.
* @throws exceptions::UnregisteredSymbolError if any requested symbol is not in the original composition.
* @throws exceptions::InvalidMixingMode if an invalid method is provided.
* @throws exceptions::FailedToFinalizeCompositionError if normalization fails.
*/
[[nodiscard]] Composition subset(const std::vector<std::string>& symbols, const std::string& method="norm") const;
/**
* @brief Checks if a symbol is registered in the composition.
* @param symbol The symbol to check.
* @return True if the symbol is registered, false otherwise.
*/
[[nodiscard]] bool hasSymbol(const std::string& symbol) const override;
/**
* @brief Checks if a species is registered in the composition.
* @param species The species to check.
* @return True if the species is registered, false otherwise.
*/
[[nodiscard]] bool hasSpecies(const fourdst::atomic::Species &species) const override;
/**
* @brief Checks if a given isotope is present in the composition.
* @pre The composition must be finalized.
* @param isotope The isotope to check for.
* @return True if the isotope is in the composition, false otherwise.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
*/
[[nodiscard]] bool contains(const atomic::Species& isotope) const override;
/**
* @brief Sets the composition mode (mass fraction vs. number fraction).
* @details This function converts all entries in the composition to the specified mode.
* @pre The composition must be finalized before the mode can be switched.
* @param massFracMode True to switch to mass fraction mode, false for number fraction mode.
* @throws exceptions::CompositionNotFinalizedError if the composition is not finalized.
* @throws std::runtime_error if the conversion fails for an unknown reason.
*/
void setCompositionMode(bool massFracMode);
/**
* @brief Gets the current canonical composition (X, Y, Z).
@@ -850,22 +504,12 @@ namespace fourdst::composition {
*/
friend std::ostream& operator<<(std::ostream& os, const Composition& composition);
/**
* @brief Overloads the + operator to mix two compositions with a 50/50 fraction.
* @details This is a convenience operator that calls `mix(other, 0.5)`.
* @param other The other composition to mix with.
* @return The new, mixed composition.
* @pre Both compositions must be finalized.
* @throws See `mix()` for exceptions.
*/
Composition operator+(const Composition& other) const;
/**
* @brief Returns an iterator to the beginning of the composition map.
* @return An iterator to the beginning.
*/
auto begin() {
return m_compositions.begin();
return m_molarAbundances.begin();
}
/**
@@ -873,7 +517,7 @@ namespace fourdst::composition {
* @return A const iterator to the beginning.
*/
[[nodiscard]] auto begin() const {
return m_compositions.cbegin();
return m_molarAbundances.cbegin();
}
/**
@@ -881,7 +525,7 @@ namespace fourdst::composition {
* @return An iterator to the end.
*/
auto end() {
return m_compositions.end();
return m_molarAbundances.end();
}
/**
@@ -889,7 +533,7 @@ namespace fourdst::composition {
* @return A const iterator to the end.
*/
[[nodiscard]] auto end() const {
return m_compositions.cend();
return m_molarAbundances.cend();
}
};

View File

@@ -1,6 +1,6 @@
#pragma once
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/atomic/atomicSpecies.h"
#include <string>
#include <unordered_map>
@@ -36,20 +36,6 @@ public:
*/
virtual ~CompositionAbstract() = default;
/**
* @brief Check if a chemical symbol is registered in the composition.
* @param symbol The chemical symbol to check (e.g., "H", "He").
* @return True if the symbol is present, false otherwise.
*/
[[nodiscard]] virtual bool hasSymbol(const std::string& symbol) const = 0;
/**
* @brief Check if a species is registered in the composition.
* @param species The atomic species to check.
* @return True if the species is present, false otherwise.
*/
[[nodiscard]] virtual bool hasSpecies(const fourdst::atomic::Species& species) const = 0;
/**
* @brief Check if the composition contains the given species.
* @param species The atomic species to check.
@@ -57,6 +43,15 @@ public:
*/
[[nodiscard]] virtual bool contains(const fourdst::atomic::Species& species) const = 0;
/**
* @brief Check if the composition contains the given species.
* @param symbol The symbol of the atomic species to check.
* @return True if the species is contained, false otherwise.
*/
[[nodiscard]] virtual bool contains(const std::string& symbol) const = 0;
[[nodiscard]] virtual size_t size() const = 0;
/**
* @brief Get all registered chemical symbols in the composition.
* @return A set of registered chemical symbols.
@@ -67,19 +62,19 @@ public:
* @brief Get all registered atomic species in the composition.
* @return A set of registered atomic species.
*/
[[nodiscard]] virtual std::set<fourdst::atomic::Species> getRegisteredSpecies() const = 0;
[[nodiscard]] virtual const std::set<fourdst::atomic::Species> &getRegisteredSpecies() const = 0;
/**
* @brief Get the mass fraction for all registered symbols.
* @return An unordered map from symbol to mass fraction.
*/
[[nodiscard]] virtual std::unordered_map<std::string, double> getMassFraction() const = 0;
[[nodiscard]] virtual std::unordered_map<fourdst::atomic::Species, double> getMassFraction() const = 0;
/**
* @brief Get the number fraction for all registered symbols.
* @return An unordered map from symbol to number fraction.
*/
[[nodiscard]] virtual std::unordered_map<std::string, double> getNumberFraction() const = 0;
[[nodiscard]] virtual std::unordered_map<fourdst::atomic::Species, double> getNumberFraction() const = 0;
/**
* @brief Get the mass fraction for a given symbol.
@@ -129,12 +124,6 @@ public:
*/
[[nodiscard]] virtual double getMeanParticleMass() const = 0;
/**
* @brief Get the mean atomic number of the composition.
* @return The mean atomic number.
*/
[[nodiscard]] virtual double getMeanAtomicNumber() const = 0;
/**
* @brief Get the electron abundance of the composition.
* @return The electron abundance.

View File

@@ -94,14 +94,6 @@ namespace fourdst::composition::exceptions {
using CompositionError::CompositionError;
};
/**
* @class InvalidSymbolError
* @brief Exception thrown when a symbol used in a composition is invalid.
*/
class InvalidSymbolError final : public CompositionError {
using CompositionError::CompositionError;
};
/**
* @class UnregisteredSymbolError
* @brief Exception thrown when a symbol is used that has not been registered.
@@ -146,4 +138,20 @@ namespace fourdst::composition::exceptions {
using CompositionEntryError::CompositionEntryError;
};
class SpeciesError : public std::exception {
protected:
std::string m_message;
public:
explicit SpeciesError(const std::string& message)
: m_message(std::move(message)) {}
const char* what() const noexcept override {
return m_message.c_str();
}
};
class UnknownSymbolError final : public SpeciesError {
using SpeciesError::SpeciesError;
};
}

View File

@@ -0,0 +1,23 @@
#pragma once
#include "fourdst/composition/composition.h"
#include "fourdst/atomic/atomicSpecies.h"
#include <vector>
namespace fourdst::composition {
Composition buildCompositionFromMassFractions(
const std::vector<std::string>& symbols,
const std::vector<double>& massFractions
);
Composition buildCompositionFromMassFractions(
const std::vector<atomic::Species>& species,
const std::vector<double>& massFractions
);
Composition buildCompositionFromMassFractions(
const std::set<atomic::Species>& species,
const std::vector<double>& massFractions
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,58 @@
#include "fourdst/composition/composition.h"
#include "fourdst/composition/exceptions/exceptions_composition.h"
#include "fourdst/atomic/atomicSpecies.h"
#include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h"
#include <ranges>
#include <vector>
#include <set>
#include <string>
namespace {
std::optional<fourdst::atomic::Species> getSpecies(const std::string& symbol) {
if (!fourdst::atomic::species.contains(symbol)) {
return std::nullopt;
}
return fourdst::atomic::species.at(symbol);
}
void throw_unknown_symbol(quill::Logger* logger, const std::string& symbol) {
throw fourdst::composition::exceptions::UnknownSymbolError("Symbol " + symbol + " is not a valid species symbol (not in the species database)");
}
}
namespace fourdst::composition {
Composition buildCompositionFromMassFractions(
const std::set<atomic::Species> &species,
const std::vector<double> &massFractions
) {
Composition composition;
for (const auto& [sp, xi] : std::views::zip(species, massFractions)) {
composition.registerSpecies(sp);
composition.setMolarAbundance(sp, xi/sp.mass());
}
return composition;
}
Composition buildCompositionFromMassFractions(const std::vector<atomic::Species> &species, const std::vector<double> &massFractions) {
return buildCompositionFromMassFractions(std::set<atomic::Species>(species.begin(), species.end()), massFractions);
}
Composition buildCompositionFromMassFractions(const std::vector<std::string> &symbols, const std::vector<double> &massFractions) {
std::set<atomic::Species> species;
for (const auto& symbol : symbols) {
auto result = getSpecies(symbol);
if (!result) {
throw_unknown_symbol(nullptr, symbol);
}
species.insert(result.value());
}
return buildCompositionFromMassFractions(species, massFractions);
}
}

View File

@@ -1,7 +1,10 @@
required_headers = [
'fourdst/composition/atomicSpecies.h',
'fourdst/composition/species.h',
'fourdst/composition/composition.h'
'fourdst/composition/composition.h',
'fourdst/composition/utils.h',
'fourdst/composition/composition_abstract.h',
'fourdst/composition/exceptions/exceptions_composition.h'
]
foreach h : required_headers
@@ -18,6 +21,7 @@ message('✅ libcomposition species_weight dependency declared')
composition_sources = files(
'lib/composition.cpp',
'lib/utils.cpp',
)
@@ -44,12 +48,21 @@ composition_dep = declare_dependency(
# Make headers accessible
composition_headers = files(
'include/fourdst/composition/composition.h',
'include/fourdst/composition/atomicSpecies.h',
'include/fourdst/composition/species.h',
'include/fourdst/composition/elements.h'
)
install_headers(composition_headers, subdir : 'fourdst/fourdst/composition')
composition_headers_utils = files(
'include/fourdst/composition/utils.h',
)
install_headers(composition_headers_utils, subdir : 'fourdst/fourdst/composition/utils')
composition_headers_atomic = files(
'include/fourdst/atomic/atomicSpecies.h',
'include/fourdst/atomic/elements.h',
'include/fourdst/atomic/species.h',
)
install_headers(composition_headers_atomic, subdir : 'fourdst/fourdst/atomic')
composition_exception_headers = files(
'include/fourdst/composition/exceptions/exceptions_composition.h',
)