23#include "quill/LogMacros.h"
26#include <unordered_map>
33#include "atomicSpecies.h"
57 throw std::runtime_error(
"Composition entry is already initialized.");
59 if (serif::atomic::species.count(
symbol) == 0) {
60 throw std::runtime_error(
"Invalid symbol.");
73 throw std::runtime_error(
"Composition entry is in number fraction mode.");
88 throw std::runtime_error(
"Composition entry is in mass fraction mode.");
110 throw std::runtime_error(
"Composition entry is in number fraction mode.");
118 throw std::runtime_error(
"Composition entry is in mass fraction mode.");
147 for (
const auto& symbol : symbols) {
153 for (
const auto& symbol : symbols) {
159 if (symbols.size() != fractions.size()) {
160 LOG_ERROR(m_logger,
"The number of symbols and fractions must be equal.");
161 throw std::runtime_error(
"The number of symbols and fractions must be equal.");
166 for (
const auto &symbol : symbols) {
170 for (
size_t i = 0; i < symbols.size(); ++i) {
190 if (
this != &other) {
205 LOG_ERROR(
m_logger,
"Invalid symbol: {}", symbol);
206 throw std::runtime_error(
"Invalid symbol.");
214 LOG_ERROR(
m_logger,
"Composition is in mass fraction mode. Cannot register symbol in number fraction mode.");
215 throw std::runtime_error(
"Composition is in mass fraction mode. Cannot register symbol in number fraction mode.");
220 LOG_WARNING(
m_logger,
"Symbol {} is already registered.", symbol);
227 LOG_INFO(
m_logger,
"Registered symbol: {}", symbol);
231 for (
const auto& symbol : symbols) {
242 LOG_ERROR(
m_logger,
"Invalid composition.");
243 throw std::runtime_error(
"Invalid composition.");
249 for (
const auto& fraction : fractions) {
252 if (sum < 0.999999 || sum > 1.000001) {
253 LOG_ERROR(
m_logger,
"The sum of fractions must be equal to 1.");
261 return serif::atomic::species.contains(symbol);
266 LOG_ERROR(
m_logger,
"Symbol {} is not registered.", symbol);
267 throw std::runtime_error(
"Symbol is not registered.");
271 LOG_ERROR(
m_logger,
"Composition is in number fraction mode.");
272 throw std::runtime_error(
"Composition is in number fraction mode.");
275 if (mass_fraction < 0.0 || mass_fraction > 1.0) {
276 LOG_ERROR(
m_logger,
"Mass fraction must be between 0 and 1 for symbol {}. Currently it is {}.", symbol, mass_fraction);
277 throw std::runtime_error(
"Mass fraction must be between 0 and 1.");
281 const double old_mass_fraction =
m_compositions.at(symbol).mass_fraction();
284 return old_mass_fraction;
288 if (symbols.size() != mass_fractions.size()) {
289 LOG_ERROR(
m_logger,
"The number of symbols and mass fractions must be equal.");
290 throw std::runtime_error(
"The number of symbols and mass fractions must be equal.");
293 std::vector<double> old_mass_fractions;
294 old_mass_fractions.reserve(symbols.size());
295 for (
size_t i = 0; i < symbols.size(); ++i) {
296 old_mass_fractions.push_back(
setMassFraction(symbols[i], mass_fractions[i]));
298 return old_mass_fractions;
303 LOG_ERROR(
m_logger,
"Symbol {} is not registered.", symbol);
304 throw std::runtime_error(
"Symbol is not registered.");
308 LOG_ERROR(
m_logger,
"Composition is in mass fraction mode.");
309 throw std::runtime_error(
"Composition is in mass fraction mode.");
312 if (number_fraction < 0.0 || number_fraction > 1.0) {
313 LOG_ERROR(
m_logger,
"Number fraction must be between 0 and 1 for symbol {}. Currently it is {}.", symbol, number_fraction);
314 throw std::runtime_error(
"Number fraction must be between 0 and 1.");
318 double old_number_fraction =
m_compositions.at(symbol).number_fraction();
321 return old_number_fraction;
325 if (symbols.size() != number_fractions.size()) {
326 LOG_ERROR(
m_logger,
"The number of symbols and number fractions must be equal.");
327 throw std::runtime_error(
"The number of symbols and number fractions must be equal.");
330 std::vector<double> old_number_fractions;
331 old_number_fractions.reserve(symbols.size());
332 for (
size_t i = 0; i < symbols.size(); ++i) {
333 old_number_fractions.push_back(
setNumberFraction(symbols[i], number_fractions[i]));
335 return old_number_fractions;
339 bool finalized =
false;
352 std::vector<double> mass_fractions;
355 mass_fractions.push_back(entry.mass_fraction());
359 for (
const auto& mass_fraction : mass_fractions) {
360 sum += mass_fraction;
362 for (
int i = 0; i < static_cast<int>(mass_fractions.size()); ++i) {
363 mass_fractions[i] /= sum;
371 }
catch (
const std::runtime_error& e) {
372 double massSum = 0.0;
374 massSum += entry.mass_fraction();
376 LOG_ERROR(
m_logger,
"Composition is invalid (Total mass {}).", massSum);
388 std::vector<double> number_fractions;
391 number_fractions.push_back(entry.number_fraction());
395 for (
const auto& number_fraction : number_fractions) {
396 sum += number_fraction;
404 }
catch (
const std::runtime_error& e) {
405 double numberSum = 0.0;
407 numberSum += entry.number_fraction();
409 LOG_ERROR(
m_logger,
"Composition is invalid (Total number {}).", numberSum);
422 LOG_ERROR(
m_logger,
"Compositions have not both been finalized.");
423 throw std::runtime_error(
"Compositions have not been finalized (Consider running .finalize()).");
426 if (fraction < 0.0 || fraction > 1.0) {
427 LOG_ERROR(
m_logger,
"Fraction must be between 0 and 1.");
428 throw std::runtime_error(
"Fraction must be between 0 and 1.");
436 for (
const auto& symbol : mixedSymbols) {
437 double otherMassFrac = 0.0;
442 double massFraction = fraction * thisMassFrac + otherMassFrac * (1-fraction);
446 return mixedComposition;
451 LOG_ERROR(
m_logger,
"Composition has not been finalized.");
452 throw std::runtime_error(
"Composition has not been finalized (Consider running .finalize()).");
455 LOG_ERROR(
m_logger,
"Symbol {} is not in the composition.", symbol);
456 throw std::runtime_error(
"Symbol is not in the composition.");
466 std::unordered_map<std::string, double> mass_fractions;
470 return mass_fractions;
476 LOG_ERROR(
m_logger,
"Composition has not been finalized.");
477 throw std::runtime_error(
"Composition has not been finalized (Consider running .finalize()).");
480 LOG_ERROR(
m_logger,
"Symbol {} is not in the composition.", symbol);
481 throw std::runtime_error(
"Symbol is not in the composition.");
491 std::unordered_map<std::string, double> number_fractions;
495 return number_fractions;
500 LOG_ERROR(
m_logger,
"Composition has not been finalized.");
501 throw std::runtime_error(
"Composition has not been finalized (Consider running .finalize()).");
504 LOG_ERROR(
m_logger,
"Symbol {} is not in the composition.", symbol);
505 throw std::runtime_error(
"Symbol is not in the composition.");
512 LOG_ERROR(
m_logger,
"Composition has not been finalized.");
513 throw std::runtime_error(
"Composition has not been finalized (Consider running .finalize()).");
520 LOG_ERROR(
m_logger,
"Composition has not been finalized.");
521 throw std::runtime_error(
"Composition has not been finalized (Consider running .finalize()).");
528 LOG_ERROR(
m_logger,
"Composition must be finalized before getting the mean atomic mass number.");
529 throw std::runtime_error(
"Composition not finalized. Cannot retrieve mean atomic mass number.");
536 zSum += (val.mass_fraction() * val.m_isotope.z())/val.m_isotope.a();
544 const std::array<std::string, 2> methods = {
"norm",
"none"};
546 if (std::ranges::find(methods, method) == methods.end()) {
547 const std::string errorMessage =
"Invalid method: " + method +
". Valid methods are 'norm' and 'none'.";
548 LOG_ERROR(
m_logger,
"Invalid method: {}. Valid methods are norm and none.", method);
549 throw std::runtime_error(errorMessage);
553 for (
const auto& symbol : symbols) {
555 LOG_ERROR(
m_logger,
"Symbol {} is not in the composition.", symbol);
556 throw std::runtime_error(
"Symbol is not in the composition.");
562 if (method ==
"norm") {
563 const bool isNorm = subsetComposition.
finalize(
true);
565 LOG_ERROR(
m_logger,
"Subset composition is invalid.");
566 throw std::runtime_error(
"Subset composition is invalid.");
569 return subsetComposition;
574 LOG_ERROR(
m_logger,
"Composition has not been finalized. Mode cannot be set unless composition is finalized.");
575 throw std::runtime_error(
"Composition has not been finalized (Consider running .finalize()). The mode cannot be set unless the composition is finalized.");
586 LOG_ERROR(
m_logger,
"Composition mode could not be set.");
587 throw std::runtime_error(
"Composition mode could not be set due to an unknown error.");
595 LOG_ERROR(
m_logger,
"Composition has not been finalized.");
596 throw std::runtime_error(
"Composition has not been finalized (Consider running .finalize()).");
599 const std::array<std::string, 7> canonicalH = {
600 "H-1",
"H-2",
"H-3",
"H-4",
"H-5",
"H-6",
"H-7"
602 const std::array<std::string, 8> canonicalHe = {
603 "He-3",
"He-4",
"He-5",
"He-6",
"He-7",
"He-8",
"He-9",
"He-10"
605 for (
const auto& symbol : canonicalH) {
610 for (
const auto& symbol : canonicalHe) {
617 const bool isHSymbol = std::ranges::find(canonicalH, symbol) != std::end(canonicalH);
618 const bool isHeSymbol = std::ranges::find(canonicalHe, symbol) != std::end(canonicalHe);
620 if (isHSymbol || isHeSymbol) {
627 const double Z = 1.0 - (canonicalComposition.
X + canonicalComposition.
Y);
628 if (std::abs(Z - canonicalComposition.
Z) > 1e-6) {
630 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);
633 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);
634 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).");
637 return canonicalComposition;
647 LOG_ERROR(
m_logger,
"Composition has not been finalized.");
648 throw std::runtime_error(
"Composition has not been finalized (Consider running .finalize()).");
650 const auto symbol =
static_cast<std::string
>(isotope.name());
660 return mix(other, 0.5);
664 os <<
"Global Composition: \n";
665 os <<
"\tSpecific Number Density: " << comp.specificNumberDensity <<
"\n";
666 os <<
"\tMean Particle Mass: " << comp.meanParticleMass <<
"\n";
676 os <<
"Composition(finalized: " << (
composition.m_finalized ?
"true" :
"false") <<
", " ;
678 for (
const auto& [symbol, entry] :
composition.m_compositions) {
680 if (count <
composition.m_compositions.size() - 1) {
bool hasSymbol(const std::string &symbol) const
Check if a symbol is registered.
std::unordered_map< std::string, double > getMassFraction() const
Gets the mass fractions of all compositions.
bool isValidComposition(const std::vector< double > &fractions) const
Checks if the given mass fractions are valid.
double getMeanAtomicNumber() const
Compute the mean atomic mass number of the composition.
bool contains(const serif::atomic::Species &isotope) const
double m_meanParticleMass
The mean particle mass of the composition (\sum_{i} \frac{n_i}{m_i}. where n_i is the number fraction...
bool finalizeMassFracMode(bool norm)
Finalizes the composition in mass fraction mode.
std::set< std::string > getRegisteredSymbols() const
Gets the registered symbols.
Composition()=default
Default constructor.
Composition mix(const Composition &other, double fraction) const
Mix two compositions together with a given fraction.
double m_specificNumberDensity
The specific number density of the composition (\sum_{i} X_i m_i. Where X_i is the number fraction of...
void registerSymbol(const std::string &symbol, bool massFracMode=true)
Registers a new symbol.
std::set< std::string > m_registeredSymbols
The registered symbols.
Composition subset(const std::vector< std::string > &symbols, std::string method="norm") const
Gets a subset of the composition.
std::unordered_map< std::string, double > getNumberFraction() const
Gets the number fractions of all compositions.
bool finalize(bool norm=false)
Finalizes the composition.
double setMassFraction(const std::string &symbol, const double &mass_fraction)
Sets the mass fraction for a given symbol.
bool m_finalized
True if the composition is finalized.
bool finalizeNumberFracMode(bool norm)
Finalizes the composition in number fraction mode.
std::pair< std::unordered_map< std::string, CompositionEntry >, GlobalComposition > getComposition() const
Gets all composition entries and the global composition.
Composition & operator=(Composition const &other)
Composition operator+(const Composition &other) const
Overloads the + operator to mix two compositions together with a fraction of 0.5.
bool m_massFracMode
True if mass fraction mode, false if number fraction mode.
CanonicalComposition getCanonicalComposition(bool harsh=false) const
Gets the current canonical composition (X, Y, Z).
double getMeanParticleMass() const
Compute the mean particle mass of the composition.
std::unordered_map< std::string, CompositionEntry > m_compositions
The compositions.
double setNumberFraction(const std::string &symbol, const double &number_fraction)
Sets the number fraction for a given symbol.
void validateComposition(const std::vector< double > &fractions) const
Validates the given mass fractions.
void setCompositionMode(bool massFracMode)
Sets the composition mode.
static bool isValidSymbol(const std::string &symbol)
Checks if the given symbol is valid.
std::ostream & operator<<(std::ostream &os, const GlobalComposition &comp)
double X
Mass fraction of Hydrogen.
double Z
Mass fraction of Metals.
double Y
Mass fraction of Helium.
Represents an entry in the composition with a symbol and mass fraction.
std::string symbol() const
Gets the chemical symbol of the species.
serif::atomic::Species isotope() const
Gets the isotope of the species.
double rel_abundance() const
Gets the relative abundance of the species.
bool setNumberFracMode(double totalMoles)
Sets the mode to number fraction mode.
void setNumberFraction(double number_fraction)
Sets the number fraction of the species.
bool m_massFracMode
The mode of the composition entry. True if mass fraction, false if number fraction.
void setMassFraction(double mass_fraction)
Sets the mass fraction of the species.
serif::atomic::Species m_isotope
The isotope of the species.
double m_numberFraction
The number fraction of the species.
void setSpecies(const std::string &symbol)
Sets the species for the composition entry.
double number_fraction() const
Gets the number fraction of the species.
double mass_fraction() const
Gets the mass fraction of the species.
double m_relAbundance
The relative abundance of the species for converting between mass and number fractions.
bool setMassFracMode(double meanMolarMass)
Sets the mode to mass fraction mode.
std::string m_symbol
The chemical symbol of the species.
CompositionEntry()
Default constructor.
double m_massFraction
The mass fraction of the species.
bool m_initialized
True if the composition entry has been initialized.
bool getMassFracMode() const
Gets the mode of the composition entry.
Represents the global composition of a system. This tends to be used after finalize and is primarily ...