21#include "quill/LogMacros.h"
24#include <unordered_map>
41 template<
typename A,
typename B>
42 std::vector<A> sortVectorBy(
43 std::vector<A> toSort,
44 const std::vector<B>& by
46 std::vector<std::size_t> indices(by.size());
47 for (
size_t i = 0; i < indices.size(); i++) {
51 std::ranges::sort(indices, [&](
size_t a,
size_t b) {
55 std::vector<A> sorted;
56 sorted.reserve(indices.size());
58 for (
const auto idx: indices) {
59 sorted.push_back(toSort[idx]);
65 std::optional<fourdst::atomic::Species> getSpecies(
const std::string& symbol) {
72 void throw_unknown_symbol(quill::Logger* logger,
const std::string& symbol) {
73 LOG_ERROR(logger,
"Symbol {} is not a valid species symbol (not in the species database)", symbol);
77 void throw_unregistered_symbol(quill::Logger* logger,
const std::string& symbol) {
78 LOG_ERROR(logger,
"Symbol {} is not registered in the composition.", symbol);
85 const std::vector<std::string>& symbols
87 for (
const auto& symbol : symbols) {
93 const std::set<std::string>& symbols
95 for (
const auto& symbol : symbols) {
101 const std::vector<atomic::Species> &species
103 for (
const auto& s : species) {
109 const std::set<atomic::Species> &species
111 for (
const auto& s : species) {
117 for (
const auto& symbol : symbols) {
123 for (
const auto& s : species) {
129 const std::vector<std::string>& symbols,
130 const std::vector<double>& molarAbundances
132 if (symbols.size() != molarAbundances.size()) {
133 LOG_CRITICAL(
getLogger(),
"The number of symbols and molarAbundances must be equal (got {} symbols and {} molarAbundances).", symbols.size(), molarAbundances.size());
134 throw exceptions::InvalidCompositionError(
"The number of symbols and fractions must be equal. Got " + std::to_string(symbols.size()) +
" symbols and " + std::to_string(molarAbundances.size()) +
" fractions.");
137 for (
const auto &[symbol, y] : std::views::zip(symbols, molarAbundances)) {
144 const std::vector<atomic::Species> &species,
145 const std::vector<double> &molarAbundances
147 if (species.size() != molarAbundances.size()) {
148 LOG_CRITICAL(
getLogger(),
"The number of species and molarAbundances must be equal (got {} species and {} molarAbundances).", species.size(), molarAbundances.size());
149 throw exceptions::InvalidCompositionError(
"The number of species and fractions must be equal. Got " + std::to_string(species.size()) +
" species and " + std::to_string(molarAbundances.size()) +
" fractions.");
152 for (
const auto& [s, y] : std::views::zip(species, molarAbundances)) {
159 const std::set<std::string> &symbols,
160 const std::vector<double> &molarAbundances
162 if (symbols.size() != molarAbundances.size()) {
163 LOG_CRITICAL(
getLogger(),
"The number of symbols and molarAbundances must be equal (got {} symbols and {} molarAbundances).", symbols.size(), molarAbundances.size());
164 throw exceptions::InvalidCompositionError(
"The number of symbols and fractions must be equal. Got " + std::to_string(symbols.size()) +
" symbols and " + std::to_string(molarAbundances.size()) +
" fractions.");
167 for (
const auto& [symbol, y] : std::views::zip(sortVectorBy<std::string>(std::vector<std::string>(symbols.begin(), symbols.end()), molarAbundances), molarAbundances)) {
174 for (
const auto& [symbol, y] : symbolMolarAbundances) {
181 for (
const auto& [symbol, y] : symbolMolarAbundances) {
188 for (
const auto& [species, y] : speciesMolarAbundances) {
195 for (
const auto& [species, y] : speciesMolarAbundances) {
218 if (
this != &other) {
226 const std::string& symbol
228 const auto result = getSpecies(symbol);
230 throw_unknown_symbol(
getLogger(), symbol);
237 const std::vector<std::string>& symbols
239 for (
const auto& symbol : symbols) {
247 m_registeredSpecies.insert(species);
248 if (!m_molarAbundances.contains(species)) {
249 m_molarAbundances.emplace(species, 0.0);
254 const std::vector<atomic::Species> &species
256 for (
const auto& s : species) {
262 std::set<std::string> symbols;
264 symbols.insert(std::string(species.name()));
275 const auto species = getSpecies(symbol);
277 throw_unknown_symbol(
getLogger(), symbol);
286 throw_unregistered_symbol(
getLogger(), std::string(species.
name()));
288 std::map<atomic::Species, double> raw_mass;
289 double totalMass = 0;
291 const double contrib = y * sp.mass();
292 totalMass += contrib;
293 raw_mass.emplace(sp, contrib);
295 return raw_mass.at(species) / totalMass;
299 std::unordered_map<atomic::Species, double> mass_fractions;
303 return mass_fractions;
308 const std::string& symbol
310 const auto species = getSpecies(symbol);
312 throw_unknown_symbol(
getLogger(), symbol);
321 throw_unregistered_symbol(
getLogger(), std::string(species.
name()));
323 double total_moles_per_gram = 0.0;
325 total_moles_per_gram += y;
331 std::unordered_map<atomic::Species, double> number_fractions;
335 return number_fractions;
339 const std::string &symbol
341 const auto species = getSpecies(symbol);
343 throw_unknown_symbol(
getLogger(), symbol);
353 throw_unregistered_symbol(
getLogger(), std::string(species.
name()));
362 sum += x/species.mass();
371 Ye += species.z() * y;
385 const std::set<Species> canonicalH = {H_1, H_2, H_3, H_4, H_5, H_6, H_7};
386 const std::set<Species> canonicalHe = {He_3, He_4, He_5, He_6, He_7, He_8, He_9, He_10};
388 for (
const auto& symbol : canonicalH) {
393 for (
const auto& symbol : canonicalHe) {
400 const bool isHIsotope = canonicalH.contains(species);
401 const bool isHeIsotope = canonicalHe.contains(species);
403 if (isHIsotope || isHeIsotope) {
411 const double Z = 1.0 - (canonicalComposition.
X + canonicalComposition.
Y);
412 if (std::abs(Z - canonicalComposition.
Z) > 1e-16) {
413 LOG_ERROR(
getLogger(),
"Validation composition Z (X-Y = {}) is different than canonical composition Z ({}) (∑a_i where a_i != H/He).", Z, canonicalComposition.
Z);
414 throw exceptions::InvalidCompositionError(
"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).");
417 return canonicalComposition;
425 std::vector<double> massFractionVector;
426 std::vector<double> speciesMass;
433 speciesMass.push_back(species.mass());
436 std::vector<double> massFractions = sortVectorBy(massFractionVector, speciesMass);
438 return massFractions;
447 std::vector<double> numberFractionVector;
448 std::vector<double> speciesMass;
455 speciesMass.push_back(species.mass());
458 std::vector<double> numberFractions = sortVectorBy(numberFractionVector, speciesMass);
460 return numberFractions;
468 std::vector<double> molarAbundanceVector;
469 std::vector<double> speciesMass;
475 molarAbundanceVector.push_back(y);
476 speciesMass.push_back(species.mass());
479 std::vector<double> molarAbundances = sortVectorBy(molarAbundanceVector, speciesMass);
481 return molarAbundances;
486 const std::string &symbol
488 const auto species = getSpecies(symbol);
490 throw_unknown_symbol(
getLogger(), symbol);
500 LOG_ERROR(
getLogger(),
"Species {} is not in the composition.", species.
name());
504 return std::distance(
514 std::vector<atomic::Species> speciesVector;
515 std::vector<double> speciesMass;
521 speciesVector.emplace_back(s);
522 speciesMass.push_back(s.mass());
525 std::vector<atomic::Species> sortedSpecies = sortVectorBy(speciesVector, speciesMass);
527 return std::distance(sortedSpecies.begin(), std::ranges::find(sortedSpecies, species));
537 std::vector<atomic::Species> speciesVector;
538 std::vector<double> speciesMass;
544 speciesVector.emplace_back(species);
545 speciesMass.push_back(species.mass());
548 std::vector<atomic::Species> sortedSymbols = sortVectorBy(speciesVector, speciesMass);
549 if (index >= sortedSymbols.size()) {
550 LOG_ERROR(
getLogger(),
"Index {} is out of range for composition of size {}.", index, sortedSymbols.size());
551 throw std::out_of_range(
"Index " + std::to_string(index) +
" is out of range for composition of size " + std::to_string(sortedSymbols.size()) +
".");
553 return sortedSymbols.at(index);
557 return std::make_unique<Composition>(*
this);
563 return m_registeredSpecies.contains(species);
567 const std::string &symbol
569 const auto species = getSpecies(symbol);
571 throw_unknown_symbol(
getLogger(), symbol);
581 const std::string &symbol,
582 const double &molar_abundance
584 const auto species = getSpecies(symbol);
586 throw_unknown_symbol(
getLogger(), symbol);
594 const double &molar_abundance
597 throw_unregistered_symbol(
getLogger(), std::string(species.
name()));
599 if (molar_abundance < 0.0) {
600 LOG_ERROR(
getLogger(),
"Molar abundance must be non-negative for symbol {}. Currently it is {}.", species.
name(), molar_abundance);
607 const std::vector<std::string> &symbols,
608 const std::vector<double> &molar_abundances
610 for (
const auto& [symbol, y] : std::views::zip(symbols, molar_abundances)) {
616 const std::vector<atomic::Species> &species,
617 const std::vector<double> &molar_abundances
619 for (
const auto& [s, y] : std::views::zip(species, molar_abundances)) {
625 const std::set<std::string> &symbols,
626 const std::vector<double> &molar_abundances
628 for (
const auto& [symbol, y] : std::views::zip(symbols, molar_abundances)) {
634 const std::set<atomic::Species> &species,
635 const std::vector<double> &molar_abundances
637 for (
const auto& [s, y] : std::views::zip(species, molar_abundances)) {
648 os <<
"Composition(Mass Fractions => [";
652 if (count < composition.
size() - 1) {
Abstract base class for chemical composition representations.
virtual double getMolarAbundance(const std::string &symbol) const =0
Get the molar abundance for a given symbol.
virtual const std::set< fourdst::atomic::Species > & getRegisteredSpecies() const noexcept=0
Get all registered atomic species in the composition.
Manages a collection of chemical species and their abundances.
CompositionCache m_cache
Cache for computed properties to avoid redundant calculations.
size_t getSpeciesIndex(const std::string &symbol) const override
get the index in the sorted vector representation for a given symbol
bool contains(const atomic::Species &species) const noexcept override
Checks if a given species is present in the composition.
std::unordered_map< atomic::Species, double > getNumberFraction() const noexcept override
Gets the number fractions of all species in the composition.
Composition()=default
Default constructor.
void setMolarAbundance(const std::string &symbol, const double &molar_abundance)
Sets the molar abundance for a given symbol.
const std::set< atomic::Species > & getRegisteredSpecies() const noexcept override
Get a set of all species that are registered in the composition.
void registerSpecies(const atomic::Species &species) noexcept
Registers a new species by extracting its symbol.
void registerSymbol(const std::string &symbol)
Registers a new symbol for inclusion in the composition.
std::set< std::string > getRegisteredSymbols() const noexcept override
Gets the registered symbols.
std::set< atomic::Species > m_registeredSpecies
Set of registered species in the composition.
static quill::Logger * getLogger()
Gets the logger instance for the Composition class. This is static to ensure that all composition obj...
Composition & operator=(Composition const &other)
Assignment operator.
std::unique_ptr< CompositionAbstract > clone() const override
double getElectronAbundance() const noexcept override
Compute the electron abundance of the composition.
size_t size() const noexcept override
Gets the number of registered species in the composition.
std::unordered_map< atomic::Species, double > getMassFraction() const noexcept override
Gets the mass fractions of all species in the composition.
std::map< atomic::Species, double > m_molarAbundances
Map of species to their molar abundances.
CanonicalComposition getCanonicalComposition() const
Compute the canonical composition (X, Y, Z) of the composition.
std::vector< double > getMolarAbundanceVector() const noexcept override
Get a uniform vector representation of the molar abundances stored in the composition object sorted s...
double getMolarAbundance(const std::string &symbol) const override
Gets the molar abundances of all species in the composition.
std::vector< double > getNumberFractionVector() const noexcept override
Get a uniform vector representation of the number fractions stored in the composition object sorted s...
atomic::Species getSpeciesAtIndex(size_t index) const override
Get the species at a given index in the sorted vector representation.
std::vector< double > getMassFractionVector() const noexcept override
Get a uniform vector representation of the mass fraction stored in the composition object sorted such...
double getMeanParticleMass() const noexcept override
Compute the mean particle mass of the composition.
Exception thrown when a composition is in an invalid or inconsistent state.
Exception thrown when an unknown symbol is encountered.
Exception thrown when a symbol is used that has not been registered.
Contains canonical information about atomic species and elements used by 4D-STAR.
static const std::unordered_map< std::string, const Species & > species
Map of species names to their corresponding Species objects.
Utilities and types for representing and manipulating chemical compositions.
std::ostream & operator<<(std::ostream &os, const Composition &composition)
OVERLOADS.
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.
std::optional< std::vector< atomic::Species > > sortedSpecies
Cached vector of sorted species (by mass).
std::optional< std::vector< double > > numberFractions
Cached vector of number fractions.
std::optional< CanonicalComposition > canonicalComp
Cached canonical composition data.
std::optional< std::vector< double > > molarAbundances
Cached vector of molar abundances.
std::optional< std::vector< double > > massFractions
Cached vector of mass fractions.