21#include "quill/LogMacros.h"
24#include <unordered_map>
152 for (
const auto& symbol : symbols) {
158 for (
const auto& symbol : symbols) {
164 if (symbols.size() != fractions.size()) {
165 LOG_CRITICAL(m_logger,
"The number of symbols and fractions must be equal (got {} symbols and {} fractions).", symbols.size(), fractions.size());
166 throw exceptions::InvalidCompositionError(
"The number of symbols and fractions must be equal. Got " + std::to_string(symbols.size()) +
" symbols and " + std::to_string(fractions.size()) +
" fractions.");
171 for (
const auto &symbol : symbols) {
175 for (
size_t i = 0; i < symbols.size(); ++i) {
195 if (
this != &other) {
210 LOG_ERROR(
m_logger,
"Invalid symbol: {}", symbol);
219 LOG_ERROR(
m_logger,
"Composition is in mass fraction mode. Cannot register symbol ({}) in number fraction mode.", symbol);
225 LOG_WARNING(
m_logger,
"Symbol {} is already registered.", symbol);
232 LOG_TRACE_L3(
m_logger,
"Registered symbol: {}", symbol);
236 for (
const auto& symbol : symbols) {
246 for (
const auto& s : species) {
256 std::set<fourdst::atomic::Species> result;
258 result.insert(entry.isotope());
265 LOG_ERROR(
m_logger,
"Invalid composition.");
272 for (
const auto& fraction : fractions) {
275 if (sum < 0.999999 || sum > 1.000001) {
276 LOG_ERROR(
m_logger,
"The sum of fractions must be equal to 1 (expected 1, got {}).", sum);
289 LOG_ERROR(
m_logger,
"Symbol {} is not registered.", symbol);
294 LOG_ERROR(
m_logger,
"Composition is in number fraction mode.");
298 if (mass_fraction < 0.0 || mass_fraction > 1.0) {
299 LOG_ERROR(
m_logger,
"Mass fraction must be between 0 and 1 for symbol {}. Currently it is {}.", symbol, mass_fraction);
304 const double old_mass_fraction =
m_compositions.at(symbol).mass_fraction();
307 return old_mass_fraction;
311 if (symbols.size() != mass_fractions.size()) {
312 LOG_ERROR(
m_logger,
"The number of symbols and mass fractions must be equal (currently {} symbols and {} mass fractions).", symbols.size(), mass_fractions.size());
313 throw exceptions::InvalidCompositionError(
"The number of symbols and mass fractions must be equal (currently " + std::to_string(symbols.size()) +
" symbols and " + std::to_string(mass_fractions.size()) +
" mass fractions).");
316 std::vector<double> old_mass_fractions;
317 old_mass_fractions.reserve(symbols.size());
318 for (
size_t i = 0; i < symbols.size(); ++i) {
319 old_mass_fractions.push_back(
setMassFraction(symbols[i], mass_fractions[i]));
321 return old_mass_fractions;
329 const std::vector<double> &mass_fractions) {
330 std::vector<double> old_mass_fractions;
331 old_mass_fractions.reserve(species.size());
332 for (
const auto& spec : species) {
333 old_mass_fractions.push_back(
setMassFraction(spec, mass_fractions[&spec - &species[0]]));
335 return old_mass_fractions;
340 LOG_ERROR(
m_logger,
"Symbol {} is not registered.", symbol);
345 LOG_ERROR(
m_logger,
"Composition is in mass fraction mode, should be in number fraction mode to call setNumberFraction. Hint: The mode can be switched by first finalizing and then calling setCompositionMode(false).");
346 throw exceptions::CompositionModeError(
"Composition is in mass fraction mode, should be in number fraction mode to call setNumberFraction. Hint: The mode can be switched by first finalizing and then calling setCompositionMode(false).");
349 if (number_fraction < 0.0 || number_fraction > 1.0) {
350 LOG_ERROR(
m_logger,
"Number fraction must be between 0 and 1 for symbol {}. Currently it is {}.", symbol, number_fraction);
355 const double old_number_fraction =
m_compositions.at(symbol).number_fraction();
358 return old_number_fraction;
362 if (symbols.size() != number_fractions.size()) {
363 LOG_ERROR(
m_logger,
"The number of symbols and number fractions must be equal. (Currently {} symbols and {} number fractions).", symbols.size(), number_fractions.size());
364 throw exceptions::InvalidCompositionError(
"The number of symbols and number fractions must be equal. (Currently " + std::to_string(symbols.size()) +
" symbols and " + std::to_string(number_fractions.size()) +
" number fractions).");
367 std::vector<double> old_number_fractions;
368 old_number_fractions.reserve(symbols.size());
369 for (
size_t i = 0; i < symbols.size(); ++i) {
370 old_number_fractions.push_back(
setNumberFraction(symbols[i], number_fractions[i]));
372 return old_number_fractions;
380 const std::vector<double> &number_fractions) {
381 std::vector<double> old_number_fractions;
382 old_number_fractions.reserve(species.size());
383 for (
const auto& spec : species) {
384 old_number_fractions.push_back(
setNumberFraction(spec, number_fractions[&spec - &species[0]]));
386 return old_number_fractions;
390 bool finalized =
false;
403 std::vector<double> mass_fractions;
406 mass_fractions.push_back(entry.mass_fraction());
410 for (
const auto& mass_fraction : mass_fractions) {
411 sum += mass_fraction;
413 for (
double & mass_fraction : mass_fractions) {
414 mass_fraction /= sum;
423 double massSum = 0.0;
425 massSum += entry.mass_fraction();
427 LOG_ERROR(
m_logger,
"Composition is invalid (Total mass {}).", massSum);
440 std::vector<double> number_fractions;
443 number_fractions.push_back(entry.number_fraction());
447 for (
const auto& number_fraction : number_fractions) {
448 sum += number_fraction;
456 }
catch ([[maybe_unused]]
const std::runtime_error& e) {
457 double numberSum = 0.0;
459 numberSum += entry.number_fraction();
461 LOG_ERROR(
m_logger,
"Composition is invalid (Total number {}).", numberSum);
475 LOG_ERROR(
m_logger,
"Compositions have not both been finalized. Hint: Consider running .finalize() on both compositions before mixing.");
479 if (fraction < 0.0 || fraction > 1.0) {
480 LOG_ERROR(
m_logger,
"Mixing fraction must be between 0 and 1. Currently it is {}.", fraction);
489 for (
const auto& symbol : mixedSymbols) {
490 double otherMassFrac = 0.0;
496 double massFraction = fraction * thisMassFrac + otherMassFrac * (1-fraction);
500 return mixedComposition;
505 LOG_ERROR(
m_logger,
"Composition has not been finalized. Hint: Consider running .finalize().");
509 LOG_ERROR(
m_logger,
"Symbol {} is not in the composition.", symbol);
510 std::string currentSymbols;
513 currentSymbols += sym;
515 currentSymbols +=
", ";
517 currentSymbols +=
", and ";
535 std::unordered_map<std::string, double> mass_fractions;
539 return mass_fractions;
545 LOG_ERROR(
m_logger,
"Composition has not been finalized. Hint: Consider running .finalize().");
549 LOG_ERROR(
m_logger,
"Symbol {} is not in the composition.", symbol);
564 std::unordered_map<std::string, double> number_fractions;
568 return number_fractions;
573 LOG_ERROR(
m_logger,
"Composition has not been finalized. Hint: Consider running .finalize().");
577 LOG_ERROR(
m_logger,
"Symbol {} is not in the composition.", symbol);
590 LOG_ERROR(
m_logger,
"Composition has not been finalized. Hint: Consider running .finalize().");
594 LOG_ERROR(
m_logger,
"Symbol {} is not in the composition.", symbol);
607 LOG_ERROR(
m_logger,
"Composition has not been finalized. Hint: Consider running .finalize().");
615 LOG_ERROR(
m_logger,
"Composition has not been finalized. Hint: Consider running .finalize().");
623 LOG_ERROR(
m_logger,
"Composition must be finalized before getting the mean atomic mass number. Hint: Consider running .finalize().");
632 zSum += (val.mass_fraction() * val.m_isotope.z())/val.m_isotope.a();
641 const std::array<std::string, 2> methods = {
"norm",
"none"};
643 if (std::ranges::find(methods, method) == methods.end()) {
644 const std::string errorMessage =
"Invalid method: " + method +
". Valid methods are 'norm' and 'none'.";
645 LOG_ERROR(
m_logger,
"Invalid method: {}. Valid methods are norm and none.", method);
650 for (
const auto& symbol : symbols) {
652 LOG_ERROR(
m_logger,
"Symbol {} is not in the composition.", symbol);
659 if (method ==
"norm") {
660 const bool isNorm = subsetComposition.
finalize(
true);
662 LOG_ERROR(
m_logger,
"Subset composition is invalid. (Unable to finalize with normalization).");
666 return subsetComposition;
671 LOG_ERROR(
m_logger,
"Composition has not been finalized. Mode cannot be set unless composition is finalized. Hint: Consider running .finalize().");
683 LOG_ERROR(
m_logger,
"Composition mode could not be set due to some unknown error.");
684 throw std::runtime_error(
"Composition mode could not be set due to an unknown error.");
692 LOG_ERROR(
m_logger,
"Composition has not been finalized. Hint: Consider running .finalize().");
696 const std::array<std::string, 7> canonicalH = {
697 "H-1",
"H-2",
"H-3",
"H-4",
"H-5",
"H-6",
"H-7"
699 const std::array<std::string, 8> canonicalHe = {
700 "He-3",
"He-4",
"He-5",
"He-6",
"He-7",
"He-8",
"He-9",
"He-10"
702 for (
const auto& symbol : canonicalH) {
707 for (
const auto& symbol : canonicalHe) {
714 const bool isHSymbol = std::ranges::find(canonicalH, symbol) != std::end(canonicalH);
715 const bool isHeSymbol = std::ranges::find(canonicalHe, symbol) != std::end(canonicalHe);
717 if (isHSymbol || isHeSymbol) {
724 const double Z = 1.0 - (canonicalComposition.
X + canonicalComposition.
Y);
725 if (std::abs(Z - canonicalComposition.
Z) > 1e-6) {
727 LOG_WARNING(
m_logger,
"Validation composition Z (X-Y = {}) is different than canonical composition Z ({}) (∑a_i where a_i != H/He).", Z, canonicalComposition.
Z);
730 LOG_ERROR(
m_logger,
"Validation composition Z (X-Y = {}) is different than canonical composition Z ({}) (∑a_i where a_i != H/He).", Z, canonicalComposition.
Z);
731 throw std::runtime_error(
"Validation composition Z (X-Y = " + std::to_string(Z) +
") is different than canonical composition Z (" + std::to_string(canonicalComposition.
Z) +
") (∑a_i where a_i != H/He).");
734 return canonicalComposition;
744 LOG_ERROR(
m_logger,
"Composition has not been finalized. Hint: Consider running .finalize().");
747 const auto symbol =
static_cast<std::string
>(isotope.
name());
757 return mix(other, 0.5);
761 os <<
"Global Composition: \n";
773 os <<
"Composition(finalized: " << (
composition.m_finalized ?
"true" :
"false") <<
", " ;
775 for (
const auto &entry:
composition.m_compositions | std::views::values) {
777 if (count <
composition.m_compositions.size() - 1) {
void setCompositionMode(bool massFracMode)
Sets the composition mode (mass fraction vs. number fraction).
std::pair< std::unordered_map< std::string, CompositionEntry >, GlobalComposition > getComposition() const
Gets all composition entries and the global composition data.
Composition subset(const std::vector< std::string > &symbols, const std::string &method="norm") const
Creates a new Composition object containing a subset of species from this one.
void registerSymbol(const std::string &symbol, bool massFracMode=true)
Registers a new symbol for inclusion in the composition.
Composition()=default
Default constructor.
Composition operator+(const Composition &other) const
Overloads the + operator to mix two compositions with a 50/50 fraction.
std::set< std::string > m_registeredSymbols
The registered symbols.
Composition mix(const Composition &other, double fraction) const
Mixes this composition with another to produce a new composition.
std::set< fourdst::atomic::Species > getRegisteredSpecies() const
Get a set of all species that are registered in the composition.
bool finalizeNumberFracMode(bool norm)
Finalizes the composition in number fraction mode.
double setMassFraction(const std::string &symbol, const double &mass_fraction)
Sets the mass fraction for a given symbol.
double m_meanParticleMass
The mean particle mass of the composition (\sum_{i} \frac{n_i}{m_i}. where n_i is the number fraction...
void registerSpecies(const fourdst::atomic::Species &species, bool massFracMode=true)
Registers a new species by extracting its symbol.
Composition & operator=(Composition const &other)
Assignment operator.
double getMeanParticleMass() const
Compute the mean particle mass of the composition.
bool m_massFracMode
True if mass fraction mode, false if number fraction mode.
double getMolarAbundance(const std::string &symbol) const
Gets the molar abundance (X_i / A_i) for a given symbol.
bool hasSymbol(const std::string &symbol) const
Checks if a symbol is registered in the composition.
bool finalize(bool norm=false)
Finalizes the composition, making it ready for querying.
std::unordered_map< std::string, double > getNumberFraction() const
Gets the number fractions of all species in the composition.
double setNumberFraction(const std::string &symbol, const double &number_fraction)
Sets the number fraction for a given symbol.
std::set< std::string > getRegisteredSymbols() const
Gets the registered symbols.
void validateComposition(const std::vector< double > &fractions) const
Validates the given fractions, throwing an exception on failure.
bool finalizeMassFracMode(bool norm)
Finalizes the composition in mass fraction mode.
static bool isValidSymbol(const std::string &symbol)
Checks if the given symbol is valid by checking against the global species database.
double getMeanAtomicNumber() const
Compute the mean atomic number of the composition.
bool m_finalized
True if the composition is finalized.
std::unordered_map< std::string, CompositionEntry > m_compositions
The compositions.
CanonicalComposition getCanonicalComposition(bool harsh=false) const
Gets the current canonical composition (X, Y, Z).
bool contains(const fourdst::atomic::Species &isotope) const
Checks if a given isotope is present in the composition.
std::unordered_map< std::string, double > getMassFraction() const
Gets the mass fractions of all species in the composition.
double m_specificNumberDensity
The specific number density of the composition (\sum_{i} X_i m_i. Where X_i is the number fraction of...
bool isValidComposition(const std::vector< double > &fractions) const
Checks if the given fractions are valid (sum to ~1.0).
Exception thrown due to a conflict in composition modes at the entry level.
Exception thrown when an operation is attempted on a composition that has not been finalized.
Exception thrown when attempting to initialize a composition entry that has already been initialized.
Exception thrown when the finalization process of a composition fails.
Exception thrown when a composition is in an invalid or inconsistent state.
Exception thrown for an invalid or unsupported mixing mode.
Exception thrown for an invalid chemical species symbol in a composition entry.
Exception thrown when a symbol used in a composition is invalid.
Exception thrown when a symbol is used that has not been registered.
Contains classes and functions related to atomic data, such as properties of atomic species.
static const std::unordered_map< std::string, const Species & > species
std::ostream & operator<<(std::ostream &os, const GlobalComposition &comp)
Represents an atomic species (isotope) with its fundamental physical properties.
std::string_view name() const
Gets the name of the species.
Represents the canonical (X, Y, Z) composition of stellar material.
double Y
Mass fraction of Helium.
double X
Mass fraction of Hydrogen.
double Z
Mass fraction of Metals.
Represents a single entry (an isotope) within a composition.
double m_relAbundance
The relative abundance, used internally for conversions. For mass fraction mode, this is X_i / A_i; f...
bool getMassFracMode() const
Gets the mode of the composition entry.
CompositionEntry()
Default constructor. Initializes a default entry (H-1), but in an uninitialized state.
bool m_massFracMode
The mode of the composition entry. True if mass fraction, false if number fraction.
double m_numberFraction
The number fraction (mole fraction) of the species. Valid only if m_massFracMode is false.
double number_fraction() const
Gets the number fraction of the species.
bool m_initialized
True if the composition entry has been initialized with a valid species.
bool setMassFracMode(double meanMolarMass)
Switches the mode to mass fraction mode.
void setMassFraction(double mass_fraction)
Sets the mass fraction of the species.
std::string symbol() const
Gets the chemical symbol of the species.
void setSpecies(const std::string &symbol)
Sets the species for the composition entry. This can only be done once.
double mass_fraction() const
Gets the mass fraction of the species.
bool setNumberFracMode(double totalMoles)
Switches the mode to number fraction mode.
atomic::Species m_isotope
The atomic::Species object containing detailed isotope data.
void setNumberFraction(double number_fraction)
Sets the number fraction of the species.
double rel_abundance() const
Gets the relative abundance of the species.
std::string m_symbol
The chemical symbol of the species (e.g., "H-1", "Fe-56").
double m_massFraction
The mass fraction of the species. Valid only if m_massFracMode is true.
atomic::Species isotope() const
Gets the isotope data for the species.
Represents global properties of a finalized composition.
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...
double meanParticleMass
The mean mass per particle (inverse of specific number density). Units: g/mol.