diff --git a/assets/static/atomic/meson.build b/assets/static/atomic/meson.build
index 9e024aa..b5b79b7 100644
--- a/assets/static/atomic/meson.build
+++ b/assets/static/atomic/meson.build
@@ -1,3 +1,4 @@
species_weight_dep = declare_dependency(
include_directories: include_directories('include'),
-)
\ No newline at end of file
+)
+message('✅ SERiF species_weight dependency declared')
\ No newline at end of file
diff --git a/docs/html/annotated.html b/docs/html/annotated.html
index 7aad003..79a63f8 100644
--- a/docs/html/annotated.html
+++ b/docs/html/annotated.html
@@ -106,7 +106,7 @@ $(function(){initNavTree('annotated.html',''); initResizable(true); });
[detail level 1234]
| ▼Nserif | |
| ▼Ncomposition | |
-| CComposition | |
+| CComposition | Manages the composition of elements |
| CCompositionEntry | Represents an entry in the composition with a symbol and mass fraction |
| CGlobalComposition | Represents the global composition of a system. This tends to be used after finalize and is primarily for internal use |
| ▼Nconstant | |
diff --git a/docs/html/doxygen_crawl.html b/docs/html/doxygen_crawl.html
index 2caf3b1..f6a2ef5 100644
--- a/docs/html/doxygen_crawl.html
+++ b/docs/html/doxygen_crawl.html
@@ -10,12 +10,10 @@
-
-
diff --git a/docs/html/navtreedata.js b/docs/html/navtreedata.js
index 39dd5ae..3af9c40 100644
--- a/docs/html/navtreedata.js
+++ b/docs/html/navtreedata.js
@@ -64,11 +64,11 @@ var NAVTREE =
var NAVTREEINDEX =
[
"4_d_s_t_a_r_types_8h.html",
-"classserif_1_1polytrope_1_1_g_m_r_e_s_inverter.html#a698ccfa71e87e381b6c0a4d7a081a776",
-"classserif_1_1polytrope_1_1poly_m_f_e_m_utils_1_1_nonlinear_power_integrator.html#ac636b3bdb45524d65d2a3aa1b6e43c7b",
-"dir_daf60af48849bf958020a18d83ad56ea.html",
-"namespaceserif_1_1polytrope_1_1poly_m_f_e_m_utils.html#ac43f5dda296efc47fbdbd351f2f4bf00",
-"structserif_1_1eos_1_1helmholtz_1_1_h_e_l_m_table.html"
+"classserif_1_1polytrope_1_1_g_m_r_e_s_inverter.html#a78c5540e756ad46201097cc83d90d356",
+"classserif_1_1probe_1_1_log_manager.html",
+"dir_e296cd0c311faef12afe540130b9e3be.html",
+"namespaceserif_1_1polytrope_1_1polycoeff.html",
+"structserif_1_1eos_1_1helmholtz_1_1_h_e_l_m_table.html#a0333b97d0f0498b86718cc20cb812e2c"
];
var SYNCONMSG = 'click to disable panel synchronization';
diff --git a/docs/html/navtreeindex0.js b/docs/html/navtreeindex0.js
index b94e174..93f336f 100644
--- a/docs/html/navtreeindex0.js
+++ b/docs/html/navtreeindex0.js
@@ -2,12 +2,10 @@ var NAVTREEINDEX0 =
{
"4_d_s_t_a_r_types_8h.html":[4,0,1,11,0,0],
"4_d_s_t_a_r_types_8h_source.html":[4,0,1,11,0,0],
-"_2_users_2tboudreaux_2_programming_2_s_e_ri_f_2src_2composition_2public_2composition_8h-example.html":[5,0],
-"_2_users_2tboudreaux_2_programming_2_s_e_ri_f_2src_2network_2public_2approx8_8h-example.html":[5,2],
-"_2_users_2tboudreaux_2_programming_2_s_e_ri_f_2src_2polytrope_2solver_2public_2poly_solver_8h-example.html":[5,3],
+"_2_users_2tboudreaux_2_programming_2_s_e_ri_f_2src_2network_2public_2approx8_8h-example.html":[5,0],
+"_2_users_2tboudreaux_2_programming_2_s_e_ri_f_2src_2polytrope_2solver_2public_2poly_solver_8h-example.html":[5,1],
"____init_____8py.html":[4,0,1,9,6,0],
"____init_____8py_source.html":[4,0,1,9,6,0],
-"_constructing-example.html":[5,1],
"_e_o_sio_8cpp.html":[4,0,1,3,0,0],
"_e_o_sio_8cpp_source.html":[4,0,1,3,0,0],
"_e_o_sio_8h.html":[4,0,1,3,1,0],
@@ -249,5 +247,7 @@ var NAVTREEINDEX0 =
"classserif_1_1polytrope_1_1_g_m_r_e_s_inverter.html#a26e52db2e259ceb0efc74a188a0626df":[3,0,0,5,3,0],
"classserif_1_1polytrope_1_1_g_m_r_e_s_inverter.html#a56eb2c60f01e499c905ccf1f9c1766dc":[2,0,3,6,4,4],
"classserif_1_1polytrope_1_1_g_m_r_e_s_inverter.html#a56eb2c60f01e499c905ccf1f9c1766dc":[3,0,0,5,3,4],
-"classserif_1_1polytrope_1_1_g_m_r_e_s_inverter.html#a698ccfa71e87e381b6c0a4d7a081a776":[2,0,3,6,4,2]
+"classserif_1_1polytrope_1_1_g_m_r_e_s_inverter.html#a698ccfa71e87e381b6c0a4d7a081a776":[2,0,3,6,4,2],
+"classserif_1_1polytrope_1_1_g_m_r_e_s_inverter.html#a698ccfa71e87e381b6c0a4d7a081a776":[3,0,0,5,3,2],
+"classserif_1_1polytrope_1_1_g_m_r_e_s_inverter.html#a78c5540e756ad46201097cc83d90d356":[2,0,3,6,4,1]
};
diff --git a/docs/latex/annotated.tex b/docs/latex/annotated.tex
index a74b130..e15013a 100644
--- a/docs/latex/annotated.tex
+++ b/docs/latex/annotated.tex
@@ -2,7 +2,7 @@
Here are the classes, structs, unions and interfaces with brief descriptions\+:\begin{DoxyCompactList}
\item\contentsline{section}{\mbox{\hyperlink{classserif_1_1network_1_1approx8_1_1_approx8_network}{serif\+::network\+::approx8\+::\+Approx8\+Network}} \\*Class for the Approx8 nuclear reaction network }{\pageref{classserif_1_1network_1_1approx8_1_1_approx8_network}}{}
\item\contentsline{section}{\mbox{\hyperlink{classapprox8_test}{approx8\+Test}} }{\pageref{classapprox8_test}}{}
-\item\contentsline{section}{\mbox{\hyperlink{classserif_1_1composition_1_1_composition}{serif\+::composition\+::\+Composition}} }{\pageref{classserif_1_1composition_1_1_composition}}{}
+\item\contentsline{section}{\mbox{\hyperlink{classserif_1_1composition_1_1_composition}{serif\+::composition\+::\+Composition}} \\*Manages the composition of elements }{\pageref{classserif_1_1composition_1_1_composition}}{}
\item\contentsline{section}{\mbox{\hyperlink{structserif_1_1composition_1_1_composition_entry}{serif\+::composition\+::\+Composition\+Entry}} \\*Represents an entry in the composition with a symbol and mass fraction }{\pageref{structserif_1_1composition_1_1_composition_entry}}{}
\item\contentsline{section}{\mbox{\hyperlink{classcomposition_test}{composition\+Test}} \\*Test suite for the composition class }{\pageref{classcomposition_test}}{}
\item\contentsline{section}{\mbox{\hyperlink{classconfig_test}{config\+Test}} \\*Test suite for the Config class }{\pageref{classconfig_test}}{}
diff --git a/docs/latex/refman.tex b/docs/latex/refman.tex
index 9e63bb4..b837d2c 100644
--- a/docs/latex/refman.tex
+++ b/docs/latex/refman.tex
@@ -447,8 +447,6 @@
\input{resource_manager_test_8cpp}
\input{resource_manager_test_8cpp_source}
\chapter{Examples}
-\input{_2_users_2tboudreaux_2_programming_2_s_e_ri_f_2src_2composition_2public_2composition_8h-example}
-\input{_constructing-example}
\input{_2_users_2tboudreaux_2_programming_2_s_e_ri_f_2src_2network_2public_2approx8_8h-example}
\input{_2_users_2tboudreaux_2_programming_2_s_e_ri_f_2src_2polytrope_2solver_2public_2poly_solver_8h-example}
%--- End generated contents ---
diff --git a/src/composition/meson.build b/src/composition/meson.build
index fe928d8..d97cbec 100644
--- a/src/composition/meson.build
+++ b/src/composition/meson.build
@@ -29,4 +29,4 @@ composition_dep = declare_dependency(
)
# Make headers accessible
-install_headers(composition_headers, subdir : '4DSSE/composition')
\ No newline at end of file
+install_headers(composition_headers, subdir : 'SERiF/composition')
\ No newline at end of file
diff --git a/src/composition/private/composition.cpp b/src/composition/private/composition.cpp
index 6af4a88..aad84dc 100644
--- a/src/composition/private/composition.cpp
+++ b/src/composition/private/composition.cpp
@@ -25,6 +25,7 @@
#include
#include
#include
+#include
#include
@@ -32,539 +33,636 @@
namespace serif::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.");
+ CompositionEntry::CompositionEntry() : m_symbol("H-1"), m_isotope(chemSpecies::species.at("H-1")),
+ m_initialized(false) {
}
- if (chemSpecies::species.count(symbol) == 0) {
- throw std::runtime_error("Invalid symbol.");
+
+ CompositionEntry::CompositionEntry(const std::string& symbol, const bool massFracMode) : m_symbol(symbol), m_isotope(chemSpecies::species.at(symbol)), m_massFracMode(massFracMode) {
+ setSpecies(symbol);
}
- m_symbol = symbol;
- m_isotope = chemSpecies::species.at(symbol);
- m_initialized = true;
-}
-std::string CompositionEntry::symbol() const {
- return m_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) {}
-double CompositionEntry::mass_fraction() const {
- if (!m_massFracMode) {
- throw std::runtime_error("Composition entry is in number fraction mode.");
+ 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;
}
- return m_massFraction;
-}
-double CompositionEntry::mass_fraction(double meanMolarMass) const {
- if (m_massFracMode) {
+ 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;
}
- 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);
- }
-}
-
-Composition::Composition(const std::set& symbols) {
- for (const auto& symbol : symbols) {
- registerSymbol(symbol);
- }
-}
-
-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.");
- }
-
- validateComposition(fractions);
-
- for (const auto &symbol : symbols) {
- registerSymbol(symbol);
- }
-
- for (size_t i = 0; i < symbols.size(); ++i) {
+ double CompositionEntry::mass_fraction(double meanMolarMass) const {
if (m_massFracMode) {
- setMassFraction(symbols[i], fractions[i]);
+ 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);
+ }
+ }
+
+ Composition::Composition(const std::set& symbols) {
+ for (const auto& symbol : symbols) {
+ registerSymbol(symbol);
+ }
+ }
+
+ 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.");
+ }
+
+ validateComposition(fractions);
+
+ for (const auto &symbol : symbols) {
+ registerSymbol(symbol);
+ }
+
+ for (size_t i = 0; i < symbols.size(); ++i) {
+ if (m_massFracMode) {
+ setMassFraction(symbols[i], fractions[i]);
+ } else {
+ setNumberFraction(symbols[i], fractions[i]);
+ }
+ }
+ finalize();
+ }
+
+ Composition::Composition(const Composition &composition) {
+ m_finalized = composition.m_finalized;
+ m_specificNumberDensity = composition.m_specificNumberDensity;
+ m_meanParticleMass = composition.m_meanParticleMass;
+ m_massFracMode = composition.m_massFracMode;
+ m_registeredSymbols = composition.m_registeredSymbols;
+ m_compositions = composition.m_compositions;
+ }
+
+ Composition& Composition::operator=(const Composition &other) {
+ if (this != &other) {
+ m_finalized = other.m_finalized;
+ m_specificNumberDensity = other.m_specificNumberDensity;
+ m_meanParticleMass = other.m_meanParticleMass;
+ m_massFracMode = other.m_massFracMode;
+ m_registeredSymbols = other.m_registeredSymbols;
+ m_compositions = other.m_compositions;
+ // note: m_config remains bound to the same singleton, so we skip it
+ }
+ return *this;
+
+ }
+
+ 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 {
- setNumberFraction(symbols[i], fractions[i]);
+ 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.");
+ }
}
- }
- finalize();
-}
-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 (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);
}
- // 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.");
+ void Composition::registerSymbol(const std::vector& symbols, bool massFracMode) {
+ for (const auto& symbol : symbols) {
+ registerSymbol(symbol, massFracMode);
}
}
- if (m_registeredSymbols.find(symbol) != m_registeredSymbols.end()) {
- LOG_WARNING(m_logger, "Symbol {} is already registered.", symbol);
- return;
+ std::set Composition::getRegisteredSymbols() const {
+ return m_registeredSymbols;
}
- 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;
+ void Composition::validateComposition(const std::vector& fractions) const {
+ if (!isValidComposition(fractions)) {
+ LOG_ERROR(m_logger, "Invalid composition.");
+ throw std::runtime_error("Invalid composition.");
+ }
}
- return true;
-}
-
-bool Composition::isValidSymbol(const std::string& symbol) const {
- return chemSpecies::species.count(symbol) > 0;
-}
-
-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 for symbol {}. Currently it is {}.", symbol, mass_fraction);
- throw std::runtime_error("Mass fraction must be between 0 and 1.");
- }
-
- 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::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.");
- }
-
- 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(setMassFraction(symbols[i], mass_fractions[i]));
- }
- return old_mass_fractions;
-}
-
-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.");
- }
-
- 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;
-}
-
-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());
- }
- if (norm) {
+ bool Composition::isValidComposition(const std::vector& fractions) const {
double sum = 0.0;
- for (const auto& mass_fraction : mass_fractions) {
- sum += mass_fraction;
+ for (const auto& fraction : fractions) {
+ sum += 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);
+ if (sum < 0.999999 || sum > 1.000001) {
+ LOG_ERROR(m_logger, "The sum of fractions must be equal to 1.");
+ return false;
}
+
+ return true;
}
- try {
- validateComposition(mass_fractions);
- } catch (const std::runtime_error& e) {
- double massSum = 0.0;
- for (const auto& [_, entry] : m_compositions) {
- massSum += entry.mass_fraction();
+
+ bool Composition::isValidSymbol(const std::string& symbol) {
+ return chemSpecies::species.contains(symbol);
+ }
+
+ double Composition::setMassFraction(const std::string& symbol, const double& mass_fraction) {
+ if (!m_registeredSymbols.contains(symbol)) {
+ LOG_ERROR(m_logger, "Symbol {} is not registered.", symbol);
+ throw std::runtime_error("Symbol is not registered.");
}
- LOG_ERROR(m_logger, "Composition is invalid (Total mass {}).", massSum);
+
+ 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 for symbol {}. Currently it is {}.", symbol, mass_fraction);
+ throw std::runtime_error("Mass fraction must be between 0 and 1.");
+ }
+
m_finalized = false;
- return false;
- }
- for (const auto& [_, entry] : m_compositions) {
- m_specificNumberDensity += entry.rel_abundance();
- }
- m_meanParticleMass = 1.0/m_specificNumberDensity;
- return true;
-}
+ const double old_mass_fraction = m_compositions.at(symbol).mass_fraction();
+ m_compositions.at(symbol).setMassFraction(mass_fraction);
-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());
+ return old_mass_fraction;
}
- if (norm) {
- double sum = 0.0;
- for (const auto& number_fraction : number_fractions) {
- sum += number_fraction;
+
+ 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.");
}
- for (auto& [symbol, entry] : m_compositions) {
- setNumberFraction(symbol, entry.number_fraction() / sum);
+
+ 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(setMassFraction(symbols[i], mass_fractions[i]));
}
+ return old_mass_fractions;
}
- try {
- validateComposition(number_fractions);
- } catch (const std::runtime_error& e) {
- double numberSum = 0.0;
- for (const auto& [_, entry] : m_compositions) {
- numberSum += entry.number_fraction();
+
+ 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.");
}
- LOG_ERROR(m_logger, "Composition is invalid (Total number {}).", numberSum);
+
+ 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;
- return false;
- }
- for (const auto& [_, entry] : m_compositions) {
- m_meanParticleMass += entry.rel_abundance();
- }
- m_specificNumberDensity = 1.0/m_meanParticleMass;
- return true;
-}
+ double old_number_fraction = m_compositions.at(symbol).number_fraction();
+ m_compositions.at(symbol).setNumberFraction(number_fraction);
-Composition Composition::mix(const Composition& other, double fraction) const {
- if (!m_finalized || !other.m_finalized) {
- LOG_ERROR(m_logger, "Compositions have not both been finalized.");
- throw std::runtime_error("Compositions have not been finalized (Consider running .finalize()).");
+ return old_number_fraction;
}
- if (fraction < 0.0 || fraction > 1.0) {
- LOG_ERROR(m_logger, "Fraction must be between 0 and 1.");
- throw std::runtime_error("Fraction must be between 0 and 1.");
+ 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;
}
- std::set mixedSymbols = other.getRegisteredSymbols();
- // Get the union of the two sets
- mixedSymbols.insert(m_registeredSymbols.begin(), m_registeredSymbols.end());
-
- Composition mixedComposition(mixedSymbols);
- for (const auto& symbol : mixedSymbols) {
- double thisMassFrac, otherMassFrac = 0.0;
-
- thisMassFrac = hasSymbol(symbol) ? getMassFraction(symbol) : 0.0;
- otherMassFrac = other.hasSymbol(symbol) ? other.getMassFraction(symbol) : 0.0;
-
- double massFraction = fraction * thisMassFrac + otherMassFrac * (1-fraction);
- mixedComposition.setMassFraction(symbol, massFraction);
- }
- mixedComposition.finalize();
- return mixedComposition;
-}
-
-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()).");
- }
- 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).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);
+ bool Composition::finalize(const bool norm) {
+ bool finalized = false;
+ if (m_massFracMode) {
+ finalized = finalizeMassFracMode(norm);
+ } else {
+ finalized = finalizeNumberFracMode(norm);
+ }
+ if (finalized) {
+ m_finalized = true;
+ }
+ return finalized;
}
- Composition subsetComposition;
- for (const auto& symbol : symbols) {
- if (m_compositions.count(symbol) == 0) {
+ 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());
+ }
+ 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();
+ }
+ 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;
+ }
+
+ 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;
+ }
+
+ Composition Composition::mix(const Composition& other, double fraction) const {
+ if (!m_finalized || !other.m_finalized) {
+ LOG_ERROR(m_logger, "Compositions have not both been finalized.");
+ throw std::runtime_error("Compositions have not been finalized (Consider running .finalize()).");
+ }
+
+ if (fraction < 0.0 || fraction > 1.0) {
+ LOG_ERROR(m_logger, "Fraction must be between 0 and 1.");
+ throw std::runtime_error("Fraction must be between 0 and 1.");
+ }
+
+ std::set mixedSymbols = other.getRegisteredSymbols();
+ // Get the union of the two sets
+ mixedSymbols.insert(m_registeredSymbols.begin(), m_registeredSymbols.end());
+
+ Composition mixedComposition(mixedSymbols);
+ for (const auto& symbol : mixedSymbols) {
+ double otherMassFrac = 0.0;
+
+ const double thisMassFrac = hasSymbol(symbol) ? getMassFraction(symbol) : 0.0;
+ otherMassFrac = other.hasSymbol(symbol) ? other.getMassFraction(symbol) : 0.0;
+
+ double massFraction = fraction * thisMassFrac + otherMassFrac * (1-fraction);
+ mixedComposition.setMassFraction(symbol, massFraction);
+ }
+ mixedComposition.finalize();
+ return mixedComposition;
+ }
+
+ 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()).");
+ }
+ if (!m_compositions.contains(symbol)) {
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).mass_fraction();
} 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 m_compositions.at(symbol).mass_fraction(m_meanParticleMass);
}
}
- return subsetComposition;
-}
-void Composition::setCompositionMode(bool massFracMode) {
- if (!m_finalized) {
- LOG_ERROR(m_logger, "Composition has not been finalized. Mode cannot be set unless composition is finalized.");
- throw std::runtime_error("Composition has not been finalized (Consider running .finalize()). The mode cannot be set unless the composition is finalized.");
+ std::unordered_map Composition::getMassFraction() const {
+ std::unordered_map mass_fractions;
+ for (const auto &symbol: m_compositions | std::views::keys) {
+ mass_fractions[symbol] = getMassFraction(symbol);
+ }
+ return mass_fractions;
}
- bool okay = true;
- for (auto& [_, entry] : m_compositions) {
- if (massFracMode) {
- okay = entry.setMassFracMode(m_meanParticleMass);
+
+ 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.contains(symbol)) {
+ 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 {
- okay = entry.setNumberFracMode(m_specificNumberDensity);
- }
- if (!okay) {
- LOG_ERROR(m_logger, "Composition mode could not be set.");
- throw std::runtime_error("Composition mode could not be set due to an unknown error.");
+ return m_compositions.at(symbol).number_fraction(m_specificNumberDensity);
}
}
- m_massFracMode = massFracMode;
-}
-bool Composition::hasSymbol(const std::string& symbol) const {
- return m_compositions.count(symbol) > 0;
-}
-
-/// OVERLOADS
-
-Composition Composition::operator+(const Composition& other) const {
- return mix(other, 0.5);
-}
-
-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;
-}
-
-std::ostream& operator<<(std::ostream& os, const CompositionEntry& entry) {
- os << "<" << entry.m_symbol << " : m_frac = " << entry.mass_fraction() << ">";
- return os;
-}
-
-std::ostream& operator<<(std::ostream& os, const Composition& composition) {
- os << "Composition: \n";
- for (const auto& [symbol, entry] : composition.m_compositions) {
- os << entry << "\n";
+ std::unordered_map Composition::getNumberFraction() const {
+ std::unordered_map number_fractions;
+ for (const auto &symbol: m_compositions | std::views::keys) {
+ 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.contains(symbol)) {
+ 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}};
+ }
+
+ double Composition::getMeanParticleMass() 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_meanParticleMass;
+ }
+
+ double Composition::getMeanAtomicNumber() const {
+ if (!m_finalized) {
+ LOG_ERROR(m_logger, "Composition must be finalized before getting the mean atomic mass number.");
+ throw std::runtime_error("Composition not finalized. Cannot retrieve mean atomic mass number.");
+ }
+
+ double zSum = 0.0;
+
+ // Loop through all registered species in the composition.
+ for (const auto &val: m_compositions | std::views::values) {
+ zSum += (val.mass_fraction() * val.m_isotope.z())/val.m_isotope.a();
+ }
+
+ const double mean_A = m_meanParticleMass * zSum;
+ return mean_A;
+ }
+
+ Composition Composition::subset(const std::vector& symbols, std::string method) const {
+ const std::array methods = {"norm", "none"};
+
+ if (std::ranges::find(methods, method) == methods.end()) {
+ const 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.contains(symbol)) {
+ 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") {
+ const 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;
+ }
+
+ void Composition::setCompositionMode(const bool massFracMode) {
+ if (!m_finalized) {
+ LOG_ERROR(m_logger, "Composition has not been finalized. Mode cannot be set unless composition is finalized.");
+ throw std::runtime_error("Composition has not been finalized (Consider running .finalize()). The mode cannot be set unless the composition is finalized.");
+ }
+
+ bool okay = true;
+ for (auto &entry: m_compositions | std::views::values) {
+ if (massFracMode) {
+ okay = entry.setMassFracMode(m_meanParticleMass);
+ } else {
+ okay = entry.setNumberFracMode(m_specificNumberDensity);
+ }
+ if (!okay) {
+ LOG_ERROR(m_logger, "Composition mode could not be set.");
+ throw std::runtime_error("Composition mode could not be set due to an unknown error.");
+ }
+ }
+ m_massFracMode = massFracMode;
+ }
+
+ CanonicalComposition Composition::getCanonicalComposition(bool harsh) 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()).");
+ }
+ CanonicalComposition canonicalComposition;
+ constexpr std::array canonicalH = {
+ "H-1", "H-2", "H-3", "H-4", "H-5", "H-6", "H-7"
+ };
+ constexpr std::array canonicalHe = {
+ "He-3", "He-4", "He-5", "He-6", "He-7", "He-8", "He-9", "He-10"
+ };
+ for (const auto& symbol : canonicalH) {
+ if (hasSymbol(symbol)) {
+ canonicalComposition.X += getMassFraction(symbol);
+ }
+ }
+ for (const auto& symbol : canonicalHe) {
+ if (hasSymbol(symbol)) {
+ canonicalComposition.Y += getMassFraction(symbol);
+ }
+ }
+
+ for (const auto& symbol : getRegisteredSymbols()) {
+ const bool isHSymbol = std::ranges::find(canonicalH, symbol) != std::end(canonicalH);
+ const bool isHeSymbol = std::ranges::find(canonicalHe, symbol) != std::end(canonicalHe);
+
+ if (isHSymbol || isHeSymbol) {
+ continue; // Skip canonical H and He symbols
+ }
+
+ canonicalComposition.Z += getMassFraction(symbol);
+ }
+
+ const double Z = 1.0 - (canonicalComposition.X + canonicalComposition.Y);
+ if (std::abs(Z - canonicalComposition.Z) > 1e-6) {
+ if (!harsh) {
+ 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);
+ }
+ else {
+ 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);
+ 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).");
+ }
+ }
+ return canonicalComposition;
+ }
+
+ bool Composition::hasSymbol(const std::string& symbol) const {
+ return m_compositions.count(symbol) > 0;
+ }
+
+ /// OVERLOADS
+
+ Composition Composition::operator+(const Composition& other) const {
+ return mix(other, 0.5);
+ }
+
+ 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;
+ }
+
+ std::ostream& operator<<(std::ostream& os, const CompositionEntry& entry) {
+ os << "<" << entry.m_symbol << " : m_frac = " << entry.mass_fraction() << ">";
+ return os;
+ }
+
+ std::ostream& operator<<(std::ostream& os, const Composition& composition) {
+ os << "Composition: \n";
+ for (const auto& [symbol, entry] : composition.m_compositions) {
+ os << entry << "\n";
+ }
+ return os;
}
- return os;
-}
} // namespace serif::composition
\ No newline at end of file
diff --git a/src/composition/public/composition.h b/src/composition/public/composition.h
index 7226393..3dcf8a0 100644
--- a/src/composition/public/composition.h
+++ b/src/composition/public/composition.h
@@ -18,8 +18,7 @@
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// *********************************************************************** */
-#ifndef COMPOSITION_H
-#define COMPOSITION_H
+#pragma once
#include
#include
@@ -34,6 +33,20 @@
#include "atomicSpecies.h"
namespace serif::composition {
+ struct CanonicalComposition {
+ double X = 0.0; ///< Mass fraction of Hydrogen.
+ double Y = 0.0; ///< Mass fraction of Helium.
+ double Z = 0.0; ///< Mass fraction of Metals.
+
+ friend std::ostream& operator<<(std::ostream& os, const CanonicalComposition& composition) {
+ os << "";
+ return os;
+ }
+ };
+
/**
* @brief Represents the global composition of a system. This tends to be used after finalize and is primarily for internal use.
*/
@@ -68,7 +81,7 @@ namespace serif::composition {
* @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
+ * *Example Usage:*
* @code
* CompositionEntry entry("H", true);
* @endcode
@@ -185,13 +198,13 @@ namespace serif::composition {
* - The only exception to the finalize rule is if the compositon was constructed with symbols and mass fractions at instantiation time. In this case, the composition is automatically finalized.
* however, this means that the composition passed to the constructor must be valid.
*
- * @example Constructing a finalized composition with symbols and mass fractions:
+ * *Example Usage:* Constructing a finalized composition with symbols and mass fractions:
* @code
* std::vector symbols = {"H", "He"};
* std::vector mass_fractions = {0.7, 0.3};
* Composition comp(symbols, mass_fractions);
* @endcode
- * @example Constructing a composition with symbols and finalizing it later:
+ * *Example Usage:* Constructing a composition with symbols and finalizing it later:
* @code
* std::vector symbols = {"H", "He"};
* Composition comp(symbols);
@@ -220,7 +233,7 @@ namespace serif::composition {
* @param symbol The symbol to check.
* @return True if the symbol is valid, false otherwise.
*/
- bool isValidSymbol(const std::string& symbol) const;
+ static bool isValidSymbol(const std::string& symbol);
/**
* @brief Checks if the given mass fractions are valid.
@@ -271,31 +284,31 @@ namespace serif::composition {
/**
* @brief Constructs a Composition with the given symbols.
* @param symbols The symbols to initialize the composition with.
- * @example
+ * *Example Usage:*
* @code
* std::vector symbols = {"H", "O"};
* Composition comp(symbols);
* @endcode
*/
- Composition(const std::vector& symbols);
+ explicit Composition(const std::vector& symbols);
/**
* @brief Constructs a Composition with the given symbols as a set.
* @param symbols The symbols to initialize the composition with.
- * @example
+ * *Example Usage:*
* @code
* std::set symbols = {"H", "O"};
* Composition comp(symbols);
* @endcode
*/
- Composition(const std::set& symbols);
+ explicit Composition(const std::set& symbols);
/**
* @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
+ * *Example Usage:*
* @code
* std::vector symbols = {"H", "O"};
* std::vector mass_fractions = {0.1, 0.9};
@@ -304,11 +317,19 @@ namespace serif::composition {
*/
Composition(const std::vector& symbols, const std::vector& mass_fractions, bool massFracMode=true);
+ /**
+ * @brief Constructs a Composition from another Composition.
+ * @param composition The Composition to copy.
+ */
+ Composition(const Composition& composition);
+
+ Composition& operator=(Composition const& other);
+
/**
* @brief Registers a new symbol.
* @param symbol The symbol to register.
* @param massFracMode True if mass fraction mode, false if number fraction mode.
- * @example
+ * *Example Usage:*
* @code
* Composition comp;
* comp.registerSymbol("H");
@@ -320,7 +341,7 @@ namespace serif::composition {
* @brief Registers multiple new symbols.
* @param symbols The symbols to register.
* @param massFracMode True if mass fraction mode, false if number fraction mode.
- * @example
+ * *Example Usage:*
* @code
* std::vector symbols = {"H", "O"};
* Composition comp;
@@ -333,14 +354,14 @@ namespace serif::composition {
* @brief Gets the registered symbols.
* @return A set of registered symbols.
*/
- std::set getRegisteredSymbols() const;
+ [[nodiscard]] std::set getRegisteredSymbols() const;
/**
* @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
+ * *Example Usage:*
* @code
* Composition comp;
* comp.setMassFraction("H", 0.1);
@@ -353,7 +374,7 @@ namespace serif::composition {
* @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
+ * *Example Usage:*
* @code
* std::vector symbols = {"H", "O"};
* std::vector mass_fractions = {0.1, 0.9};
@@ -390,40 +411,52 @@ namespace serif::composition {
* @brief Gets the mass fractions of all compositions.
* @return An unordered map of compositions with their mass fractions.
*/
- std::unordered_map getMassFraction() const;
+ [[nodiscard]] 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;
+ [[nodiscard]] 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;
+ [[nodiscard]] 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;
+ [[nodiscard]] 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 A pair containing the CompositionEntry and GlobalComposition for the given symbol.
*/
- std::pair getComposition(const std::string& symbol) const;
+ [[nodiscard]] 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;
+ [[nodiscard]] std::pair, GlobalComposition> getComposition() const;
+
+ /**
+ * @brief Compute the mean particle mass of the composition.
+ * @return Mean particle mass in g.
+ */
+ [[nodiscard]] double getMeanParticleMass() const;
+
+ /**
+ * @brief Compute the mean atomic mass number of the composition.
+ * @return Mean atomic mass number.
+ */
+ [[nodiscard]] double getMeanAtomicNumber() const;
/**
* @brief Gets a subset of the composition.
@@ -446,6 +479,16 @@ namespace serif::composition {
*/
void setCompositionMode(bool massFracMode);
+ /**
+ * @brief Gets the current canonical composition (X, Y, Z).
+ * @param harsh If true, this will throw an error if X-Y != Z where Z is computed as the sum of all other elements.
+ * @return True if mass fraction mode, false if number fraction mode.
+ *
+ * @throws std::runtime_error if the composition is not finalized or if the canonical composition cannot be computed.
+ * @throws std::runtime_error if harsh is true and the canonical composition is not valid.
+ */
+ [[nodiscard]] CanonicalComposition getCanonicalComposition(bool harsh=false) const;
+
/**
* @brief Overloaded output stream operator for Composition.
* @param os The output stream.
@@ -463,6 +506,4 @@ namespace serif::composition {
Composition operator+(const Composition& other) const;
};
-}; // namespace serif::composition
-
-#endif // COMPOSITION_H
\ No newline at end of file
+}; // namespace serif::composition
\ No newline at end of file
diff --git a/src/eos/meson.build b/src/eos/meson.build
index 00e285c..43d66e9 100644
--- a/src/eos/meson.build
+++ b/src/eos/meson.build
@@ -1,15 +1,18 @@
# Define the library
eos_sources = files(
'private/helm.cpp',
- 'private/EOSio.cpp'
+ 'private/EOSio.cpp',
+ 'private/EOS.cpp'
)
eos_headers = files(
'public/helm.h',
- 'public/EOSio.h'
+ 'public/EOSio.h',
+ 'public/EOS.h'
)
dependencies = [
+ composition_dep,
const_dep,
quill_dep,
probe_dep,
diff --git a/src/eos/private/EOS.cpp b/src/eos/private/EOS.cpp
new file mode 100644
index 0000000..ba3593d
--- /dev/null
+++ b/src/eos/private/EOS.cpp
@@ -0,0 +1,64 @@
+#include "EOS.h"
+#include "EOSio.h"
+#include "helm.h"
+#include
+
+namespace serif::eos {
+ EOS::EOS(const EOSio& reader) : m_reader(reader) {}
+ EOS::EOS(const std::string& filename, const EOSFormat format) : m_reader(EOSio(filename, format)) {}
+
+ EOSOutput EOS::get(const EOSInput& in) {
+ EOSOutput output;
+ if (getFormat() == EOSFormat::HELM) {
+ helmholtz::HELMEOSInput q;
+ q.T = in.temperature; // Temperature in K
+ q.rho = in.density; // Density in g/cm^3
+
+ q.abar = in.composition.getMeanParticleMass(); // Mean atomic mass in g
+ q.zbar = in.composition.getMeanAtomicNumber(); // Mean atomic number (dimensionless)
+
+ helmholtz::HELMEOSOutput tempOutput;
+ tempOutput = helmholtz::get_helm_EOS(q, *std::get>(m_reader.getTable()));
+
+ output.electronFraction = tempOutput.ye;
+ output.electronChemicalPotential = tempOutput.etaele;
+ output.neutronExcessFraction = tempOutput.xnefer;
+
+ // --- Pressure Variables ---
+ output.pressure.total = tempOutput.ptot;
+ output.pressure.gas = tempOutput.pgas;
+ output.pressure.radiation = tempOutput.ptot;
+ output.pressure.dDensity = tempOutput.dpresdd;
+ output.pressure.dTemperature = tempOutput.dpresdt;
+ output.pressure.dMeanAtomicMassNumber = tempOutput.dpresda;
+ output.pressure.dMeanAtomicNumber = tempOutput.dpresdz;
+
+ // --- Energy Variables ---
+ output.energy.total = tempOutput.etot;
+ output.energy.gas = tempOutput.egas;
+ output.energy.radiation = tempOutput.erad;
+ output.energy.dDensity = tempOutput.denerdd;
+ output.energy.dTemperature = tempOutput.denerdt;
+ output.energy.dMeanAtomicMassNumber = tempOutput.denerda;
+ output.energy.dMeanAtomicNumber = tempOutput.denerdz;
+
+ // --- Entropy Variables ---
+ output.entropy.total = tempOutput.stot;
+ output.entropy.gas = tempOutput.sgas;
+ output.entropy.radiation = tempOutput.srad;
+ output.entropy.dDensity = tempOutput.dentrdd;
+ output.entropy.dTemperature = tempOutput.dentrdt;
+ output.entropy.dMeanAtomicMassNumber = tempOutput.dentrda;
+ output.entropy.dMeanAtomicNumber = tempOutput.dentrdz;
+ }
+ return output;
+ }
+
+ EOSFormat EOS::getFormat() const {
+ return m_reader.getFormat();
+ }
+
+ const EOSio& EOS::getReader() const {
+ return m_reader;
+ }
+}
\ No newline at end of file
diff --git a/src/eos/private/EOSio.cpp b/src/eos/private/EOSio.cpp
index 03a6b43..6945e1a 100644
--- a/src/eos/private/EOSio.cpp
+++ b/src/eos/private/EOSio.cpp
@@ -28,25 +28,32 @@
#include
namespace serif::eos {
- EOSio::EOSio(const std::string &filename) : m_filename(filename) {
+ EOSio::EOSio(const std::string &filename, const EOSFormat format) : m_filename(filename), m_format(format){
load();
}
- std::string EOSio::getFormat() const {
+ EOSio::EOSio(const EOSio &other) {
+ m_filename = other.m_filename;
+ m_format = other.m_format;
+ load();
+ }
+
+ EOSFormat EOSio::getFormat() const {
return m_format;
}
+ std::string EOSio::getFormatName() const {
+ return FormatStringLookup.at(m_format);
+ }
+
+
EOSTable& EOSio::getTable() {
return m_table;
}
void EOSio::load() {
- // Load the EOS table from the file
- // For now, just set the format to HELM
-
- m_format = "helm";
- if (m_format == "helm") {
+ if (m_format == EOSFormat::HELM) {
loadHelm();
}
}
diff --git a/src/eos/private/helm.cpp b/src/eos/private/helm.cpp
index 1c18b05..e2df9de 100644
--- a/src/eos/private/helm.cpp
+++ b/src/eos/private/helm.cpp
@@ -240,7 +240,7 @@ namespace serif::eos::helmholtz {
ion, radiation, electron-positron and Coulomb interaction
and returns the calculated quantities in the input
***/
- serif::eos::helmholtz::EOS get_helm_EOS(serif::eos::helmholtz::EOSInput &q, const serif::eos::helmholtz::HELMTable &table) {
+ serif::eos::helmholtz::HELMEOSOutput get_helm_EOS(serif::eos::helmholtz::HELMEOSInput &q, const serif::eos::helmholtz::HELMTable &table) {
serif::config::Config& config = serif::config::Config::getInstance();
auto logFile = config.get("EOS:Helm:LogFile", "log");
serif::probe::LogManager& logManager = serif::probe::LogManager::getInstance();
@@ -829,7 +829,7 @@ namespace serif::eos::helmholtz {
double csound = clight * sqrt(gamma1/z);
// package in q:
- serif::eos::helmholtz::EOS eos;
+ serif::eos::helmholtz::HELMEOSOutput eos;
eos.ptot = ptot; eos.etot = etot; eos.stot = stot;
eos.pgas = pgas; eos.egas = egas; eos.sgas = sgas;
eos.prad = prad; eos.erad = erad; eos.srad = srad;
diff --git a/src/eos/public/EOS.h b/src/eos/public/EOS.h
new file mode 100644
index 0000000..207d746
--- /dev/null
+++ b/src/eos/public/EOS.h
@@ -0,0 +1,266 @@
+#pragma once
+
+#include "EOSio.h"
+#include "helm.h"
+#include
+#include "composition.h"
+#include
+
+namespace serif::eos {
+
+ /**
+ * @brief Input parameters for an EOS calculation.
+ *
+ * This struct holds the necessary physical conditions (composition, density, temperature)
+ * required to query the Equation of State.
+ */
+ struct EOSInput {
+ serif::composition::Composition composition; ///< The composition of the system.
+ double density; ///< The density of the system in cgs (g/cm^3).
+ double temperature; ///< The temperature of the system in cgs (K).
+ friend std::ostream& operator<<(std::ostream& os, const EOSInput& input) {
+ os << "";
+ return os;
+ }
+ };
+
+ /**
+ * @brief Represents a thermodynamic parameter and its derivatives.
+ *
+ * This struct stores a specific thermodynamic quantity (e.g., pressure, energy, entropy),
+ * its breakdown into gas and radiation components, and its partial derivatives
+ * with respect to density, temperature, mean atomic mass number, and mean atomic number.
+ * All values are in cgs units unless otherwise specified.
+ */
+ struct EOSParameter {
+ explicit EOSParameter(std::string name_)
+ : total(), gas(), radiation(),
+ dDensity(), dTemperature(),
+ dMeanAtomicMassNumber(),
+ dMeanAtomicNumber(),
+ name(std::move(name_)) {}
+ double total; ///< Total value of the parameter (gas + radiation) (cgs).
+ double gas; ///< Gas contribution to the parameter (cgs).
+ double radiation; ///< Radiation contribution to the parameter (cgs).
+
+ double dDensity; ///< Derivative of the total parameter with respect to density (cgs units / (g/cm^3)).
+ double dTemperature; ///< Derivative of the total parameter with respect to temperature (cgs units / K).
+ double dMeanAtomicMassNumber; ///< Derivative of the total parameter with respect to mean atomic mass number (Abar) (cgs units / (g/mol)).
+ double dMeanAtomicNumber; ///< Derivative of the total parameter with respect to mean atomic number (Zbar) (cgs units / dimensionless).
+
+ std::string name; ///< Name of the parameter (e.g., "Pressure", "Energy", "Entropy").
+
+ friend std::ostream& operator<<(std::ostream& os, const EOSParameter& param) {
+ os << std::setprecision(5) << "";
+ return os;
+ }
+
+
+ };
+
+ /**
+ * @brief Output from an EOS calculation.
+ *
+ * This struct contains various thermodynamic quantities and their derivatives
+ * calculated by the EOS for a given set of input conditions.
+ * It includes fundamental properties like electron fraction and chemical potential,
+ * as well as detailed breakdowns of pressure, energy, and entropy.
+ * Additionally, it provides methods to calculate derived quantities like
+ * susceptibilities, sound speed, adiabatic gradients, and specific heats.
+ */
+ struct EOSOutput {
+ double electronFraction{}; ///< Electron fraction (ye), dimensionless.
+ double electronChemicalPotential{}; ///< Electron chemical potential (eta_e) in cgs (erg/g).
+ double neutronExcessFraction{}; ///< Neutron excess fraction (xnefer), dimensionless.
+
+ EOSParameter pressure {"pressure"}; ///< Pressure output data, including total, gas, radiation, and derivatives.
+ EOSParameter energy {"energy"}; ///< Internal energy output data, including total, gas, radiation, and derivatives.
+ EOSParameter entropy {"entropy"}; ///< Entropy output data, including total, gas, radiation, and derivatives.
+
+ /**
+ * @brief Calculates the temperature susceptibility (chi_T).
+ * @return Temperature susceptibility, dimensionless.
+ * @note Placeholder: Actual calculation needs to be implemented based on available EOS derivatives.
+ * Typically, chi_T = (d ln P / d ln T)_rho.
+ */
+ double chiTemperature();
+ /**
+ * @brief Calculates the density susceptibility (chi_rho).
+ * @return Density susceptibility, dimensionless.
+ * @note Placeholder: Actual calculation needs to be implemented based on available EOS derivatives.
+ * Typically, chi_rho = (d ln P / d ln rho)_T.
+ */
+ double chiRho();
+ /**
+ * @brief Calculates the adiabatic sound speed.
+ * @return Sound speed in cgs (cm/s).
+ * @note Placeholder: Actual calculation needs to be implemented based on available EOS derivatives.
+ * Typically, c_s^2 = gamma1 * P / rho.
+ */
+ double soundSpeed();
+ /**
+ * @brief Calculates the adiabatic temperature gradient (nabla_ad).
+ * @return Adiabatic gradient, dimensionless.
+ * @note Placeholder: Actual calculation needs to be implemented based on available EOS derivatives.
+ * Typically, nabla_ad = (P * chi_T) / (rho * T * c_p * chi_rho).
+ */
+ double adiabaticGradient();
+ /**
+ * @brief Calculates the first adiabatic index (Gamma1).
+ * @return First adiabatic index, dimensionless.
+ * @note Placeholder: Actual calculation needs to be implemented based on available EOS derivatives.
+ * Typically, Gamma1 = (d ln P / d ln rho)_S.
+ */
+ double gamma1();
+ /**
+ * @brief Calculates the second adiabatic index (Gamma2).
+ * @return Second adiabatic index, dimensionless.
+ * @note Placeholder: Actual calculation needs to be implemented based on available EOS derivatives.
+ * Typically, Gamma2 / (Gamma2 - 1) = (d ln P / d ln T)_S.
+ */
+ double gamma2();
+ /**
+ * @brief Calculates the third adiabatic index (Gamma3).
+ * @return Third adiabatic index, dimensionless.
+ * @note Placeholder: Actual calculation needs to be implemented based on available EOS derivatives.
+ * Typically, Gamma3 - 1 = (d ln T / d ln rho)_S.
+ */
+ double gamma3();
+ /**
+ * @brief Calculates the specific heat capacity at constant volume (c_v).
+ * @return Specific heat capacity at constant volume in cgs (erg/K/g).
+ * @note Placeholder: Actual calculation needs to be implemented based on available EOS derivatives.
+ * Typically, c_v = (dE/dT)_rho.
+ */
+ double specificHeatCapacityAtConstantVolume();
+ /**
+ * @brief Calculates the specific heat capacity at constant pressure (c_p).
+ * @return Specific heat capacity at constant pressure in cgs (erg/K/g).
+ * @note Placeholder: Actual calculation needs to be implemented based on available EOS derivatives.
+ * Typically, c_p = c_v + (T / rho^2) * ( (d P / d T)_rho^2 / (d P / d rho)_T ).
+ */
+ double specificHeatCapacityAtConstantPressure();
+
+ /**
+ * @brief Returns the format of the EOS data used to generate this output.
+ * @return The EOSFormat enum value (currently only EOSFormat::HELM).
+ */
+ EOSFormat EOSFormat() const;
+
+ friend std::ostream& operator<<(std::ostream& os, const EOSOutput& output) {
+ os << "EOSOutput:\n"
+ << "\tElectron Fraction: " << output.electronFraction << "\n"
+ << "\tElectron Chemical Potential: " << output.electronChemicalPotential << "\n"
+ << "\tNeutron Excess Fraction: " << output.neutronExcessFraction << "\n\t"
+ << output.pressure << "\n\t"
+ << output.energy << "\n\t"
+ << output.entropy;
+ return os;
+ }
+ };
+
+ /**
+ * @class EOS
+ * @brief Main class for accessing Equation of State data.
+ *
+ * This class provides an interface to an underlying EOS table (e.g., Helmholtz EOS).
+ * It handles loading the EOS data and provides a method to retrieve thermodynamic
+ * properties for given physical conditions.
+ *
+ * @example
+ * @code
+ * #include "EOS.h"
+ * #include "composition.h" // For serif::composition::Composition
+ * #include
+ *
+ * int main() {
+ * try {
+ * // Initialize EOS from a Helmholtz table file
+ * serif::eos::EOS helmEOS("path/to/helm_table.dat", serif::eos::EOSFormat::HELM);
+ *
+ * // Define input conditions
+ * serif::eos::EOSInput input;
+ * input.density = 1.0e6; // g/cm^3
+ * input.temperature = 1.0e7; // K
+ * // Assuming a simple composition (e.g., pure Helium-4 for demonstration)
+ * // In a real scenario, initialize Composition properly.
+ * // For example, if Composition has a constructor like:
+ * // Composition(const std::map, double>& mass_fractions);
+ * // std::map, double> he4_mass_fraction = {{{2, 4}, 1.0}};
+ * // input.composition = serif::composition::Composition(he4_mass_fraction);
+ * // For now, let's assume Composition can be default constructed or set up simply:
+ * input.composition.addSpecies(2, 4, 1.0); // Z=2, A=4 (He-4), mass fraction 1.0
+ *
+ * // Get EOS output
+ * serif::eos::EOSOutput output = helmEOS.get(input);
+ *
+ * // Access results
+ * std::cout << "Pressure (total): " << output.pressure.total << " dyne/cm^2" << std::endl;
+ * std::cout << "Energy (total): " << output.energy.total << " erg/g" << std::endl;
+ * std::cout << "Entropy (total): " << output.entropy.total << " erg/K/g" << std::endl;
+ * std::cout << "Electron fraction: " << output.electronFraction << std::endl;
+ *
+ * // Example of accessing derivatives
+ * std::cout << "dP/dRho: " << output.pressure.dDensity << std::endl;
+ * std::cout << "dE/dT: " << output.energy.dTemperature << std::endl;
+ *
+ * } catch (const std::exception& e) {
+ * std::cerr << "An error occurred: " << e.what() << std::endl;
+ * return 1;
+ * }
+ * return 0;
+ * }
+ * @endcode
+ */
+ class EOS {
+ public:
+ /**
+ * @brief Constructs an EOS object by loading data from a file.
+ * @param filename The path to the EOS data file.
+ * @param format The format of the EOS data file (e.g., EOSFormat::HELM).
+ * @throw std::runtime_error If the file cannot be opened or read, or if the format is unsupported.
+ */
+ explicit EOS(const std::string& filename, EOSFormat format=EOSFormat::HELM);
+ /**
+ * @brief Constructs an EOS object from an existing EOSio reader.
+ * @param reader An EOSio object that has already loaded the EOS data.
+ */
+ explicit EOS(const EOSio& reader);
+ /**
+ * @brief Default destructor.
+ */
+ ~EOS() = default;
+
+ /**
+ * @brief Retrieves thermodynamic properties for the given input conditions.
+ * @param in An EOSInput struct containing the density, temperature, and composition.
+ * @return An EOSOutput struct containing the calculated thermodynamic properties.
+ * @throw std::runtime_error If the underlying EOS calculation fails (e.g., out of table bounds for Helmholtz).
+ *
+ * This method queries the loaded EOS table (e.g., Helmholtz) using the provided
+ * density, temperature, and composition (mean atomic mass Abar, mean atomic number Zbar).
+ * It populates and returns an EOSOutput struct with various thermodynamic quantities
+ * such as pressure, energy, entropy, their derivatives, electron fraction, etc.
+ */
+ [[nodiscard]] EOSOutput get(const EOSInput& in);
+ /**
+ * @brief Gets the format of the loaded EOS data.
+ * @return The EOSFormat enum value.
+ */
+ [[nodiscard]] EOSFormat getFormat() const;
+ /**
+ * @brief Gets a constant reference to the internal EOSio reader.
+ * @return A const reference to the EOSio object.
+ */
+ [[nodiscard]] const EOSio& getReader() const;
+ private:
+ EOSio m_reader; ///< The EOS I/O handler responsible for reading and storing EOS table data.
+ };
+}
diff --git a/src/eos/public/EOSio.h b/src/eos/public/EOSio.h
index 1882c07..fff0b64 100644
--- a/src/eos/public/EOSio.h
+++ b/src/eos/public/EOSio.h
@@ -22,15 +22,20 @@
#include
#include
#include
+#include
#include "helm.h"
namespace serif::eos {
+ enum EOSFormat {
+ HELM, ///< Helmholtz EOS format.
+ };
+ static inline std::unordered_map FormatStringLookup = {
+ {HELM, "Helmholtz"}
+ };
// EOS table format includes
- using EOSTable = std::variant<
- std::unique_ptr
- >;
+ using EOSTable = std::variant>;
/**
* @class EOSio
@@ -41,16 +46,19 @@ namespace serif::eos {
*
* Example usage:
* @code
- * EosIO eosIO("path/to/file");
- * std::string format = eosIO.getFormat();
- * EOSTable& table = eosIO.getTable();
+ * EOSio eosReader("path/to/file");
+ * std::string format = eosReader.getFormatName();
+ * EOSTable& table = eosReader.getTable();
* @endcode
+ *
+ * @note The default format used for reading tables is HELM
+ * @note Currently only the HELM format is implemented
*/
class EOSio {
private:
std::string m_filename; ///< The filename of the EOS table.
bool m_loaded = false; ///< Flag indicating if the table is loaded.
- std::string m_format; ///< The format of the EOS table.
+ EOSFormat m_format;
EOSTable m_table; ///< The EOS table data.
/**
@@ -66,8 +74,15 @@ namespace serif::eos {
/**
* @brief Constructs an EosIO object with the given filename.
* @param filename The filename of the EOS table.
+ * @param format The EOS file format (currently only HELM)
*/
- explicit EOSio(const std::string &filename);
+ explicit EOSio(const std::string &filename, EOSFormat format = EOSFormat::HELM);
+
+ /**
+ * @brief Explicit copy constructor
+ * @param other The EOSio to be copied
+ */
+ EOSio(const EOSio& other);
/**
* @brief Default destructor.
@@ -75,10 +90,12 @@ namespace serif::eos {
~EOSio() = default;
/**
- * @brief Gets the format of the EOS table.
+ * @brief Gets the format name (as a string) of the EOS table.
* @return The format of the EOS table as a string.
*/
- [[nodiscard]] std::string getFormat() const;
+ [[nodiscard]] std::string getFormatName() const;
+
+ [[nodiscard]] EOSFormat getFormat() const;
/**
* @brief Gets the EOS table.
@@ -87,6 +104,8 @@ namespace serif::eos {
[[nodiscard]] EOSTable& getTable();
[[nodiscard]] std::string getFilename() const { return m_filename; }
+
+ bool isLoaded() const { return m_loaded; }
};
}
diff --git a/src/eos/public/helm.h b/src/eos/public/helm.h
index 501db49..a20ba49 100644
--- a/src/eos/public/helm.h
+++ b/src/eos/public/helm.h
@@ -169,18 +169,18 @@ namespace serif::eos::helmholtz {
};
/**
- * @struct EOSInput
+ * @struct HELMEOSInput
* @brief Structure to hold the input parameters for the EOS calculation.
*/
- struct EOSInput
+ struct HELMEOSInput
{
double T; ///< Temperature.
double rho; ///< Density.
double abar; ///< Mean atomic mass.
double zbar; ///< Mean atomic number.
- friend std::ostream& operator<<(std::ostream& os, const helmholtz::EOSInput& eosInput) {
- os << "EOSInput Data:\n";
+ friend std::ostream& operator<<(std::ostream& os, const helmholtz::HELMEOSInput& eosInput) {
+ os << "HELMEOSInput Data:\n";
os << " Temperature: " << eosInput.T << "\n";
os << " Density: " << eosInput.rho << "\n";
os << " Mean Atomic Mass: " << eosInput.abar << "\n";
@@ -194,7 +194,7 @@ namespace serif::eos::helmholtz {
* @struct EOS
* @brief Structure to hold the output parameters and derivatives of the EOS calculation.
*/
- struct EOS
+ struct HELMEOSOutput
{
// output
double ye, etaele, xnefer; //
@@ -212,7 +212,7 @@ namespace serif::eos::helmholtz {
double gamma1, gamma2, gamma3, cV, cP; // derived quantities
double dse, dpe, dsp; // Maxwell relations
- friend std::ostream& operator<<(std::ostream& os, const helmholtz::EOS& eos) {
+ friend std::ostream& operator<<(std::ostream& os, const helmholtz::HELMEOSOutput& eos) {
os << "EOS Data:\n" << std::setw(20) << std::left;
os << " Electron Fraction: " << std::format("{0:24.16e}",eos.ye) << "\n";
os << " Electron Chemical Potential: " << std::format("{0:24.16e}",eos.etaele) << "\n";
@@ -379,7 +379,7 @@ namespace serif::eos::helmholtz {
* @param table HELMTable structure containing the table data.
* @return EOS structure containing the calculated quantities.
*/
- EOS get_helm_EOS(EOSInput &q, const HELMTable &table);
+ HELMEOSOutput get_helm_EOS(HELMEOSInput &q, const HELMTable &table);
}
diff --git a/src/network/meson.build b/src/network/meson.build
index 41abf3f..97d410e 100644
--- a/src/network/meson.build
+++ b/src/network/meson.build
@@ -15,7 +15,9 @@ dependencies = [
quill_dep,
mfem_dep,
config_dep,
- probe_dep
+ probe_dep,
+ species_weight_dep,
+ composition_dep,
]
# Define the libnetwork library so it can be linked against by other parts of the build system
diff --git a/src/network/private/approx8.cpp b/src/network/private/approx8.cpp
index c485765..f46bb7e 100644
--- a/src/network/private/approx8.cpp
+++ b/src/network/private/approx8.cpp
@@ -31,7 +31,7 @@
#include "approx8.h"
#include "network.h"
-/* Nuclear reaction network in cgs units based on Frank Timmes' "aprox8".
+/* Nuclear reaction network in cgs units based on Frank Timmes' "approx8".
At this time it does neither screening nor neutrino losses. It includes
the following 8 isotopes:
@@ -78,7 +78,7 @@ namespace serif::network::approx8{
using namespace boost::numeric::odeint;
//helper functions
- // a function to multilpy two arrays and then sum the resulting elements: sum(a*b)
+ // a function to multiply two arrays and then sum the resulting elements: sum(a*b)
double sum_product( const vec7 &a, const vec7 &b){
if (a.size() != b.size()) {
throw std::runtime_error("Error: array size mismatch in sum_product");
@@ -96,8 +96,8 @@ namespace serif::network::approx8{
// this function returns an array of the T9 terms in that order, where T9 is the temperatures in GigaKelvin
vec7 get_T9_array(const double &T) {
vec7 arr;
- double T9=1e-9*T;
- double T913=pow(T9,1./3.);
+ const double T9=1e-9*T;
+ const double T913=pow(T9,1./3.);
arr[0]=1;
arr[1]=1/T9;
@@ -117,125 +117,125 @@ namespace serif::network::approx8{
// p + p -> d; this, like some of the other rates, this is a composite of multiple fits
double pp_rate(const vec7 &T9) {
- vec7 a1 = {-34.78630, 0,-3.511930, 3.100860, -0.1983140, 1.262510e-2, -1.025170};
- vec7 a2 = { -4.364990e+1,-2.460640e-3,-2.750700,-4.248770e-1,1.598700e-2,-6.908750e-4,-2.076250e-1};
+ constexpr vec7 a1 = {-34.78630, 0,-3.511930, 3.100860, -0.1983140, 1.262510e-2, -1.025170};
+ constexpr vec7 a2 = { -4.364990e+1,-2.460640e-3,-2.750700,-4.248770e-1,1.598700e-2,-6.908750e-4,-2.076250e-1};
return rate_fit(T9,a1) + rate_fit(T9,a2);
}
// p + d -> he3
double dp_rate(const vec7 &T9) {
- vec7 a1 = {7.528980, 0, -3.720800, 0.8717820, 0, 0,-0.6666670};
- vec7 a2 = {8.935250, 0, -3.720800, 0.1986540, 0, 0, 0.3333330};
+ constexpr vec7 a1 = {7.528980, 0, -3.720800, 0.8717820, 0, 0,-0.6666670};
+ constexpr vec7 a2 = {8.935250, 0, -3.720800, 0.1986540, 0, 0, 0.3333330};
return rate_fit(T9,a1) + rate_fit(T9,a2);
}
// he3 + he3 -> he4 + 2p
double he3he3_rate(const vec7 &T9){
- vec7 a = {2.477880e+01,0,-12.27700,-0.1036990,-6.499670e-02,1.681910e-02,-6.666670e-01};
+ constexpr vec7 a = {2.477880e+01,0,-12.27700,-0.1036990,-6.499670e-02,1.681910e-02,-6.666670e-01};
return rate_fit(T9,a);
}
// he3(he3,2p)he4
double he3he4_rate(const vec7 &T9){
- vec7 a1 = {1.560990e+01,0.000000e+00,-1.282710e+01,-3.082250e-02,-6.546850e-01,8.963310e-02,-6.666670e-01};
- vec7 a2 = {1.770750e+01,0.000000e+00,-1.282710e+01,-3.812600e+00,9.422850e-02,-3.010180e-03,1.333330e+00};
+ constexpr vec7 a1 = {1.560990e+01,0.000000e+00,-1.282710e+01,-3.082250e-02,-6.546850e-01,8.963310e-02,-6.666670e-01};
+ constexpr vec7 a2 = {1.770750e+01,0.000000e+00,-1.282710e+01,-3.812600e+00,9.422850e-02,-3.010180e-03,1.333330e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2);
}
// he4 + he4 + he4 -> c12
double triple_alpha_rate(const vec7 &T9){
- vec7 a1 = {-9.710520e-01,0.000000e+00,-3.706000e+01,2.934930e+01,-1.155070e+02,-1.000000e+01,-1.333330e+00};
- vec7 a2 = {-1.178840e+01,-1.024460e+00,-2.357000e+01,2.048860e+01,-1.298820e+01,-2.000000e+01,-2.166670e+00};
- vec7 a3 = {-2.435050e+01,-4.126560e+00,-1.349000e+01,2.142590e+01,-1.347690e+00,8.798160e-02,-1.316530e+01};
+ constexpr vec7 a1 = {-9.710520e-01,0.000000e+00,-3.706000e+01,2.934930e+01,-1.155070e+02,-1.000000e+01,-1.333330e+00};
+ constexpr vec7 a2 = {-1.178840e+01,-1.024460e+00,-2.357000e+01,2.048860e+01,-1.298820e+01,-2.000000e+01,-2.166670e+00};
+ constexpr vec7 a3 = {-2.435050e+01,-4.126560e+00,-1.349000e+01,2.142590e+01,-1.347690e+00,8.798160e-02,-1.316530e+01};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3);
}
// c12 + p -> n13
double c12p_rate(const vec7 &T9){
- vec7 a1={1.714820e+01,0.000000e+00,-1.369200e+01,-2.308810e-01,4.443620e+00,-3.158980e+00,-6.666670e-01};
- vec7 a2={1.754280e+01,-3.778490e+00,-5.107350e+00,-2.241110e+00,1.488830e-01,0.000000e+00,-1.500000e+00};
+ constexpr vec7 a1={1.714820e+01,0.000000e+00,-1.369200e+01,-2.308810e-01,4.443620e+00,-3.158980e+00,-6.666670e-01};
+ constexpr vec7 a2={1.754280e+01,-3.778490e+00,-5.107350e+00,-2.241110e+00,1.488830e-01,0.000000e+00,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2);
}
// c12 + he4 -> o16
double c12a_rate(const vec7 &T9){
- vec7 a1={6.965260e+01,-1.392540e+00,5.891280e+01,-1.482730e+02,9.083240e+00,-5.410410e-01,7.035540e+01};
- vec7 a2={2.546340e+02,-1.840970e+00,1.034110e+02,-4.205670e+02,6.408740e+01,-1.246240e+01,1.373030e+02};
+ constexpr vec7 a1={6.965260e+01,-1.392540e+00,5.891280e+01,-1.482730e+02,9.083240e+00,-5.410410e-01,7.035540e+01};
+ constexpr vec7 a2={2.546340e+02,-1.840970e+00,1.034110e+02,-4.205670e+02,6.408740e+01,-1.246240e+01,1.373030e+02};
return rate_fit(T9,a1) + rate_fit(T9,a2);
}
// n14(p,g)o15 - o15 + p -> c12 + he4
double n14p_rate(const vec7 &T9){
- vec7 a1={1.701000e+01,0.000000e+00,-1.519300e+01,-1.619540e-01,-7.521230e+00,-9.875650e-01,-6.666670e-01};
- vec7 a2={2.011690e+01,0.000000e+00,-1.519300e+01,-4.639750e+00,9.734580e+00,-9.550510e+00,3.333330e-01};
- vec7 a3={7.654440e+00,-2.998000e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
- vec7 a4={6.735780e+00,-4.891000e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,6.820000e-02};
+ constexpr vec7 a1={1.701000e+01,0.000000e+00,-1.519300e+01,-1.619540e-01,-7.521230e+00,-9.875650e-01,-6.666670e-01};
+ constexpr vec7 a2={2.011690e+01,0.000000e+00,-1.519300e+01,-4.639750e+00,9.734580e+00,-9.550510e+00,3.333330e-01};
+ constexpr vec7 a3={7.654440e+00,-2.998000e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
+ constexpr vec7 a4={6.735780e+00,-4.891000e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,6.820000e-02};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3) + rate_fit(T9,a4);
}
// n14(a,g)f18 assumed to go on to ne20
double n14a_rate(const vec7 &T9){
- vec7 a1={2.153390e+01,0.000000e+00,-3.625040e+01,0.000000e+00,0.000000e+00,-5.000000e+00,-6.666670e-01};
- vec7 a2={1.968380e-01,-5.160340e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
- vec7 a3={1.389950e+01,-1.096560e+01,-5.622700e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
+ constexpr vec7 a1={2.153390e+01,0.000000e+00,-3.625040e+01,0.000000e+00,0.000000e+00,-5.000000e+00,-6.666670e-01};
+ constexpr vec7 a2={1.968380e-01,-5.160340e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
+ constexpr vec7 a3={1.389950e+01,-1.096560e+01,-5.622700e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3);
}
// n15(p,a)c12 (CNO I)
double n15pa_rate(const vec7 &T9){
- vec7 a1 = {2.747640e+01,0.000000e+00,-1.525300e+01,1.593180e+00,2.447900e+00,-2.197080e+00,-6.666670e-01};
- vec7 a2 = {-4.873470e+00,-2.021170e+00,0.000000e+00,3.084970e+01,-8.504330e+00,-1.544260e+00,-1.500000e+00};
- vec7 a3 = {2.089720e+01,-7.406000e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
- vec7 a4 = {-6.575220e+00,-1.163800e+00,0.000000e+00,2.271050e+01,-2.907070e+00,2.057540e-01,-1.500000e+00};
+ constexpr vec7 a1 = {2.747640e+01,0.000000e+00,-1.525300e+01,1.593180e+00,2.447900e+00,-2.197080e+00,-6.666670e-01};
+ constexpr vec7 a2 = {-4.873470e+00,-2.021170e+00,0.000000e+00,3.084970e+01,-8.504330e+00,-1.544260e+00,-1.500000e+00};
+ constexpr vec7 a3 = {2.089720e+01,-7.406000e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
+ constexpr vec7 a4 = {-6.575220e+00,-1.163800e+00,0.000000e+00,2.271050e+01,-2.907070e+00,2.057540e-01,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3) + rate_fit(T9,a4);
}
// n15(p,g)o16 (CNO II)
double n15pg_rate(const vec7 &T9){
- vec7 a1 = {2.001760e+01,0.000000e+00,-1.524000e+01,3.349260e-01,4.590880e+00,-4.784680e+00,-6.666670e-01};
- vec7 a2 = {6.590560e+00,-2.923150e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
- vec7 a3 = {1.454440e+01,-1.022950e+01,0.000000e+00,0.000000e+00,4.590370e-02,0.000000e+00,-1.500000e+00};
+ constexpr vec7 a1 = {2.001760e+01,0.000000e+00,-1.524000e+01,3.349260e-01,4.590880e+00,-4.784680e+00,-6.666670e-01};
+ constexpr vec7 a2 = {6.590560e+00,-2.923150e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
+ constexpr vec7 a3 = {1.454440e+01,-1.022950e+01,0.000000e+00,0.000000e+00,4.590370e-02,0.000000e+00,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3);
}
double n15pg_frac(const vec7 &T9){
- double f1=n15pg_rate(T9);
- double f2=n15pa_rate(T9);
+ const double f1=n15pg_rate(T9);
+ const double f2=n15pa_rate(T9);
return f1/(f1+f2);
}
// o16(p,g)f17 then f17 -> o17(p,a)n14
double o16p_rate(const vec7 &T9){
- vec7 a={1.909040e+01,0.000000e+00,-1.669600e+01,-1.162520e+00,2.677030e-01,-3.384110e-02,-6.666670e-01};
+ constexpr vec7 a={1.909040e+01,0.000000e+00,-1.669600e+01,-1.162520e+00,2.677030e-01,-3.384110e-02,-6.666670e-01};
return rate_fit(T9,a);
}
// o16(a,g)ne20
double o16a_rate(const vec7 &T9){
- vec7 a1={2.390300e+01,0.000000e+00,-3.972620e+01,-2.107990e-01,4.428790e-01,-7.977530e-02,-6.666670e-01};
- vec7 a2={3.885710e+00,-1.035850e+01,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
- vec7 a3={9.508480e+00,-1.276430e+01,0.000000e+00,-3.659250e+00,7.142240e-01,-1.075080e-03,-1.500000e+00};
+ constexpr vec7 a1={2.390300e+01,0.000000e+00,-3.972620e+01,-2.107990e-01,4.428790e-01,-7.977530e-02,-6.666670e-01};
+ constexpr vec7 a2={3.885710e+00,-1.035850e+01,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
+ constexpr vec7 a3={9.508480e+00,-1.276430e+01,0.000000e+00,-3.659250e+00,7.142240e-01,-1.075080e-03,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3);
}
// ne20(a,g)mg24
double ne20a_rate(const vec7 &T9){
- vec7 a1={2.450580e+01,0.000000e+00,-4.625250e+01,5.589010e+00,7.618430e+00,-3.683000e+00,-6.666670e-01};
- vec7 a2={-3.870550e+01,-2.506050e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
- vec7 a3={1.983070e+00,-9.220260e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
- vec7 a4={-8.798270e+00,-1.278090e+01,0.000000e+00,1.692290e+01,-2.573250e+00,2.089970e-01,-1.500000e+00};
+ constexpr vec7 a1={2.450580e+01,0.000000e+00,-4.625250e+01,5.589010e+00,7.618430e+00,-3.683000e+00,-6.666670e-01};
+ constexpr vec7 a2={-3.870550e+01,-2.506050e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
+ constexpr vec7 a3={1.983070e+00,-9.220260e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
+ constexpr vec7 a4={-8.798270e+00,-1.278090e+01,0.000000e+00,1.692290e+01,-2.573250e+00,2.089970e-01,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3) + rate_fit(T9,a4);
}
// c12(c12,a)ne20
double c12c12_rate(const vec7 &T9){
- vec7 a={6.128630e+01,0.000000e+00,-8.416500e+01,-1.566270e+00,-7.360840e-02,-7.279700e-02,-6.666670e-01};
+ constexpr vec7 a={6.128630e+01,0.000000e+00,-8.416500e+01,-1.566270e+00,-7.360840e-02,-7.279700e-02,-6.666670e-01};
return rate_fit(T9,a);
}
// c12(o16,a)mg24
double c12o16_rate(const vec7 &T9){
- vec7 a={4.853410e+01,3.720400e-01,-1.334130e+02,5.015720e+01,-3.159870e+00,1.782510e-02,-2.370270e+01};
+ constexpr vec7 a={4.853410e+01,3.720400e-01,-1.334130e+02,5.015720e+01,-3.159870e+00,1.782510e-02,-2.370270e+01};
return rate_fit(T9,a);
}
@@ -244,209 +244,211 @@ namespace serif::network::approx8{
// a Jacobian matrix for implicit solvers
- void Jacobian::operator() ( const vector_type &y, matrix_type &J, double /* t */, vector_type &dfdt ) {
+ void Jacobian::operator() ( const vector_type &y, matrix_type &J, double /* t */, vector_type &dfdt ) const {
serif::constant::Constants& constants = serif::constant::Constants::getInstance();
const double avo = constants.get("N_a").value;
const double clight = constants.get("c").value;
// EOS
- vec7 T9=get_T9_array(y[Net::itemp]);
+ const vec7 T9=get_T9_array(y[Approx8Net::iTemp]);
// evaluate rates once per call
- double rpp=pp_rate(T9);
- double r33=he3he3_rate(T9);
- double r34=he3he4_rate(T9);
- double r3a=triple_alpha_rate(T9);
- double rc12p=c12p_rate(T9);
- double rc12a=c12a_rate(T9);
- double rn14p=n14p_rate(T9);
- double rn14a=n14a_rate(T9);
- double ro16p=o16p_rate(T9);
- double ro16a=o16a_rate(T9);
- double rne20a=ne20a_rate(T9);
- double r1212=c12c12_rate(T9);
- double r1216=c12o16_rate(T9);
+ const double rpp=pp_rate(T9);
+ const double r33=he3he3_rate(T9);
+ const double r34=he3he4_rate(T9);
+ const double r3a=triple_alpha_rate(T9);
+ const double rc12p=c12p_rate(T9);
+ const double rc12a=c12a_rate(T9);
+ const double rn14p=n14p_rate(T9);
+ const double rn14a=n14a_rate(T9);
+ const double ro16p=o16p_rate(T9);
+ const double ro16a=o16a_rate(T9);
+ const double rne20a=ne20a_rate(T9);
+ const double r1212=c12c12_rate(T9);
+ const double r1216=c12o16_rate(T9);
- double pfrac=n15pg_frac(T9);
- double afrac=1-pfrac;
+ const double pFrac=n15pg_frac(T9);
+ const double aFrac=1-pFrac;
- double yh1 = y[Net::ih1];
- double yhe3 = y[Net::ihe3];
- double yhe4 = y[Net::ihe4];
- double yc12 = y[Net::ic12];
- double yn14 = y[Net::in14];
- double yo16 = y[Net::io16];
- double yne20 = y[Net::ine20];
+ const double yh1 = y[Approx8Net::ih1];
+ const double yhe3 = y[Approx8Net::ihe3];
+ const double yhe4 = y[Approx8Net::ihe4];
+ const double yc12 = y[Approx8Net::ic12];
+ const double yn14 = y[Approx8Net::in14];
+ const double yo16 = y[Approx8Net::io16];
+ const double yne20 = y[Approx8Net::ine20];
// zero all elements to begin
- for (int i=0; i < Net::nvar; i++) {
- for (int j=0; j < Net::nvar; j++) {
- J(i,j)=0.0;
+ for (int i=0; i < Approx8Net::nVar; i++) {
+ for (int j=0; j < Approx8Net::nVar; j++) {
+ J(i,j)=0.0;
}
}
-
+
// h1 jacobian elements
- J(Net::ih1,Net::ih1) = -3*yh1*rpp - 2*yc12*rc12p -2*yn14*rn14p -2*yo16*ro16p;
- J(Net::ih1,Net::ihe3) = 2*yhe3*r33 - yhe4*r34;
- J(Net::ih1,Net::ihe4) = -yhe3*r34;
- J(Net::ih1,Net::ic12) = -2*yh1*rc12p;
- J(Net::ih1,Net::in14) = -2*yh1*rn14p;
- J(Net::ih1,Net::io16) = -2*yh1*ro16p;
+ J(Approx8Net::ih1,Approx8Net::ih1) = -3*yh1*rpp - 2*yc12*rc12p -2*yn14*rn14p -2*yo16*ro16p;
+ J(Approx8Net::ih1,Approx8Net::ihe3) = 2*yhe3*r33 - yhe4*r34;
+ J(Approx8Net::ih1,Approx8Net::ihe4) = -yhe3*r34;
+ J(Approx8Net::ih1,Approx8Net::ic12) = -2*yh1*rc12p;
+ J(Approx8Net::ih1,Approx8Net::in14) = -2*yh1*rn14p;
+ J(Approx8Net::ih1,Approx8Net::io16) = -2*yh1*ro16p;
// he3 jacobian elements
- J(Net::ihe3,Net::ih1) = yh1*rpp;
- J(Net::ihe3,Net::ihe3) = -2*yhe3*r33 - yhe4*r34;
- J(Net::ihe3,Net::ihe4) = -yhe3*r34;
-
+ J(Approx8Net::ihe3,Approx8Net::ih1) = yh1*rpp;
+ J(Approx8Net::ihe3,Approx8Net::ihe3) = -2*yhe3*r33 - yhe4*r34;
+ J(Approx8Net::ihe3,Approx8Net::ihe4) = -yhe3*r34;
+
// he4 jacobian elements
- J(Net::ihe4,Net::ih1) = yn14*afrac*rn14p + yo16*ro16p;
- J(Net::ihe4,Net::ihe3) = yhe3*r33 - yhe4*r34;
- J(Net::ihe4,Net::ihe4) = yhe3*r34 - 1.5*yhe4*yhe4*r3a - yc12*rc12a - 1.5*yn14*rn14a - yo16*ro16a - yne20*rne20a;
- J(Net::ihe4,Net::ic12) = -yhe4*rc12a + yc12*r1212 + yo16*r1216;
- J(Net::ihe4,Net::in14) = yh1*afrac*rn14p - 1.5*yhe4*rn14a;
- J(Net::ihe4,Net::io16) = yh1*ro16p - yhe4*ro16a + yc12*r1216;
- J(Net::ihe4,Net::ine20) = -yhe4*rne20a;
+ J(Approx8Net::ihe4,Approx8Net::ih1) = yn14*aFrac*rn14p + yo16*ro16p;
+ J(Approx8Net::ihe4,Approx8Net::ihe3) = yhe3*r33 - yhe4*r34;
+ J(Approx8Net::ihe4,Approx8Net::ihe4) = yhe3*r34 - 1.5*yhe4*yhe4*r3a - yc12*rc12a - 1.5*yn14*rn14a - yo16*ro16a - yne20*rne20a;
+ J(Approx8Net::ihe4,Approx8Net::ic12) = -yhe4*rc12a + yc12*r1212 + yo16*r1216;
+ J(Approx8Net::ihe4,Approx8Net::in14) = yh1*aFrac*rn14p - 1.5*yhe4*rn14a;
+ J(Approx8Net::ihe4,Approx8Net::io16) = yh1*ro16p - yhe4*ro16a + yc12*r1216;
+ J(Approx8Net::ihe4,Approx8Net::ine20) = -yhe4*rne20a;
// c12 jacobian elements
- J(Net::ic12,Net::ih1) = -yc12*rc12p + yn14*afrac*rn14p;
- J(Net::ic12,Net::ihe4) = 0.5*yhe4*yhe4*r3a - yhe4*rc12a;
- J(Net::ic12,Net::ic12) = -yh1*rc12p - yhe4*rc12a - yo16*r1216 - 2*yc12*r1212;
- J(Net::ic12,Net::in14) = yh1*yn14*afrac*rn14p;
- J(Net::ic12,Net::io16) = -yc12*r1216;
+ J(Approx8Net::ic12,Approx8Net::ih1) = -yc12*rc12p + yn14*aFrac*rn14p;
+ J(Approx8Net::ic12,Approx8Net::ihe4) = 0.5*yhe4*yhe4*r3a - yhe4*rc12a;
+ J(Approx8Net::ic12,Approx8Net::ic12) = -yh1*rc12p - yhe4*rc12a - yo16*r1216 - 2*yc12*r1212;
+ J(Approx8Net::ic12,Approx8Net::in14) = yh1*yn14*aFrac*rn14p;
+ J(Approx8Net::ic12,Approx8Net::io16) = -yc12*r1216;
// n14 jacobian elements
- J(Net::in14,Net::ih1) = yc12*rc12p - yn14*rn14p + yo16*ro16p;
- J(Net::in14,Net::ihe4) = -yn14*rn14a;
- J(Net::in14,Net::ic12) = yh1*rc12p;
- J(Net::in14,Net::in14) = -yh1*rn14p - yhe4*rn14a;
- J(Net::in14,Net::io16) = yo16*ro16p;
-
+ J(Approx8Net::in14,Approx8Net::ih1) = yc12*rc12p - yn14*rn14p + yo16*ro16p;
+ J(Approx8Net::in14,Approx8Net::ihe4) = -yn14*rn14a;
+ J(Approx8Net::in14,Approx8Net::ic12) = yh1*rc12p;
+ J(Approx8Net::in14,Approx8Net::in14) = -yh1*rn14p - yhe4*rn14a;
+ J(Approx8Net::in14,Approx8Net::io16) = yo16*ro16p;
+
// o16 jacobian elements
- J(Net::io16,Net::ih1) = yn14*pfrac*rn14p - yo16*ro16p;
- J(Net::io16,Net::ihe4) = yc12*rc12a - yo16*ro16a;
- J(Net::io16,Net::ic12) = yhe4*rc12a - yo16*r1216;
- J(Net::io16,Net::in14) = yh1*pfrac*rn14p;
- J(Net::io16,Net::io16) = yh1*ro16p - yc12*r1216 -yhe4*ro16a;
+ J(Approx8Net::io16,Approx8Net::ih1) = yn14*pFrac*rn14p - yo16*ro16p;
+ J(Approx8Net::io16,Approx8Net::ihe4) = yc12*rc12a - yo16*ro16a;
+ J(Approx8Net::io16,Approx8Net::ic12) = yhe4*rc12a - yo16*r1216;
+ J(Approx8Net::io16,Approx8Net::in14) = yh1*pFrac*rn14p;
+ J(Approx8Net::io16,Approx8Net::io16) = yh1*ro16p - yc12*r1216 -yhe4*ro16a;
// ne20 jacobian elements
- J(Net::ine20,Net::ihe4) = yn14*rn14a + yo16*ro16a - yne20*rne20a;
- J(Net::ine20,Net::ic12) = yc12*r1212;
- J(Net::ine20,Net::in14) = yhe4*rn14a;
- J(Net::ine20,Net::io16) = yo16*ro16a;
- J(Net::ine20,Net::ine20) = -yhe4*rne20a;
+ J(Approx8Net::ine20,Approx8Net::ihe4) = yn14*rn14a + yo16*ro16a - yne20*rne20a;
+ J(Approx8Net::ine20,Approx8Net::ic12) = yc12*r1212;
+ J(Approx8Net::ine20,Approx8Net::in14) = yhe4*rn14a;
+ J(Approx8Net::ine20,Approx8Net::io16) = yo16*ro16a;
+ J(Approx8Net::ine20,Approx8Net::ine20) = -yhe4*rne20a;
// mg24 jacobian elements
- J(Net::img24,Net::ihe4) = yne20*rne20a;
- J(Net::img24,Net::ic12) = yo16*r1216;
- J(Net::img24,Net::io16) = yc12*r1216;
- J(Net::img24,Net::ine20) = yhe4*rne20a;
+ J(Approx8Net::img24,Approx8Net::ihe4) = yne20*rne20a;
+ J(Approx8Net::img24,Approx8Net::ic12) = yo16*r1216;
+ J(Approx8Net::img24,Approx8Net::io16) = yc12*r1216;
+ J(Approx8Net::img24,Approx8Net::ine20) = yhe4*rne20a;
- // energy accountings
- for (int j=0; j("Network:Approx8:Stiff:AbsTol", 1.0e-6);
@@ -463,7 +465,7 @@ namespace serif::network::approx8{
std::make_pair(ODE(), Jacobian()),
m_y,
0.0,
- m_tmax,
+ m_tMax,
m_dt0
);
@@ -474,31 +476,33 @@ namespace serif::network::approx8{
ODE(),
m_y,
0.0,
- m_tmax,
+ m_tMax,
m_dt0
);
}
- double ysum = 0.0;
- for (int i = 0; i < Net::niso; i++) {
- m_y[i] *= Net::aion[i];
- ysum += m_y[i];
+ double ySum = 0.0;
+ for (int i = 0; i < Approx8Net::nIso; i++) {
+ m_y[i] *= Approx8Net::aIon[i];
+ ySum += m_y[i];
}
- for (int i = 0; i < Net::niso; i++) {
- m_y[i] /= ysum;
+ for (int i = 0; i < Approx8Net::nIso; i++) {
+ m_y[i] /= ySum;
}
NetOut netOut;
std::vector outComposition;
- outComposition.reserve(Net::nvar);
+ outComposition.reserve(Approx8Net::nVar);
- for (int i = 0; i < Net::niso; i++) {
+ for (int i = 0; i < Approx8Net::nIso; i++) {
outComposition.push_back(m_y[i]);
}
- netOut.energy = m_y[Net::iener];
- netOut.composition = outComposition;
+ netOut.energy = m_y[Approx8Net::iEnergy];
netOut.num_steps = num_steps;
+ const std::vector symbols = {"H-1", "He-3", "He-4", "C-12", "N-14", "O-16", "Ne-20", "Mg-24"};
+ netOut.composition = serif::composition::Composition(symbols, outComposition);
+
return netOut;
}
@@ -507,31 +511,26 @@ namespace serif::network::approx8{
}
vector_type Approx8Network::convert_netIn(const NetIn &netIn) {
- if (netIn.composition.size() != Net::niso) {
- LOG_ERROR(m_logger, "Error: composition size mismatch in convert_netIn");
- throw std::runtime_error("Error: composition size mismatch in convert_netIn");
- }
+ vector_type y(Approx8Net::nVar, 0.0);
+ y[Approx8Net::ih1] = netIn.composition.getMassFraction("H-1");
+ y[Approx8Net::ihe3] = netIn.composition.getMassFraction("He-3");
+ y[Approx8Net::ihe4] = netIn.composition.getMassFraction("He-4");
+ y[Approx8Net::ic12] = netIn.composition.getMassFraction("C-12");
+ y[Approx8Net::in14] = netIn.composition.getMassFraction("N-14");
+ y[Approx8Net::io16] = netIn.composition.getMassFraction("O-16");
+ y[Approx8Net::ine20] = netIn.composition.getMassFraction("Ne-20");
+ y[Approx8Net::img24] = netIn.composition.getMassFraction("Mg-24");
+ y[Approx8Net::iTemp] = netIn.temperature;
+ y[Approx8Net::iDensity] = netIn.density;
+ y[Approx8Net::iEnergy] = netIn.energy;
- vector_type y(Net::nvar, 0.0);
- y[Net::ih1] = netIn.composition[0];
- y[Net::ihe3] = netIn.composition[1];
- y[Net::ihe4] = netIn.composition[2];
- y[Net::ic12] = netIn.composition[3];
- y[Net::in14] = netIn.composition[4];
- y[Net::io16] = netIn.composition[5];
- y[Net::ine20] = netIn.composition[6];
- y[Net::img24] = netIn.composition[7];
- y[Net::itemp] = netIn.temperature;
- y[Net::iden] = netIn.density;
- y[Net::iener] = netIn.energy;
-
- double ysum = 0.0;
- for (int i = 0; i < Net::niso; i++) {
- y[i] /= Net::aion[i];
- ysum += y[i];
+ double ySum = 0.0;
+ for (int i = 0; i < Approx8Net::nIso; i++) {
+ y[i] /= Approx8Net::aIon[i];
+ ySum += y[i];
}
- for (int i = 0; i < Net::niso; i++) {
- y[i] /= ysum;
+ for (int i = 0; i < Approx8Net::nIso; i++) {
+ y[i] /= ySum;
}
return y;
diff --git a/src/network/private/network.cpp b/src/network/private/network.cpp
index d3b3b25..b0e3e2d 100644
--- a/src/network/private/network.cpp
+++ b/src/network/private/network.cpp
@@ -19,18 +19,50 @@
//
// *********************************************************************** */
#include "network.h"
+
+#include "approx8.h"
#include "probe.h"
#include "quill/LogMacros.h"
namespace serif::network {
- Network::Network() :
+ Network::Network(const NetworkFormat format) :
m_config(serif::config::Config::getInstance()),
m_logManager(serif::probe::LogManager::getInstance()),
- m_logger(m_logManager.getLogger("log")) {
+ m_logger(m_logManager.getLogger("log")),
+ m_format(format) {
+ if (format == NetworkFormat::UNKNOWN) {
+ LOG_ERROR(m_logger, "nuclearNetwork::Network::Network() called with UNKNOWN format");
+ throw std::runtime_error("nuclearNetwork::Network::Network() called with UNKNOWN format");
+ }
}
+
+ NetworkFormat Network::getFormat() const {
+ return m_format;
+ }
+
+ NetworkFormat Network::setFormat(const NetworkFormat format) {
+ const NetworkFormat oldFormat = m_format;
+ m_format = format;
+ return oldFormat;
+ }
+
NetOut Network::evaluate(const NetIn &netIn) {
- // You can throw an exception here or log a warning if it should never be used.
- LOG_ERROR(m_logger, "nuclearNetwork::Network::evaluate() is not implemented");
- throw std::runtime_error("nuclearNetwork::Network::evaluate() is not implemented");
+ NetOut netOut;
+ switch (m_format) {
+ case APPROX8: {
+ approx8::Approx8Network network;
+ netOut = network.evaluate(netIn);
+ break;
+ }
+ case UNKNOWN: {
+ LOG_ERROR(m_logger, "Network format {} is not implemented.", FormatStringLookup.at(m_format));
+ throw std::runtime_error("Network format not implemented.");
+ }
+ default: {
+ LOG_ERROR(m_logger, "Unknown network format.");
+ throw std::runtime_error("Unknown network format.");
+ }
+ }
+ return netOut;
}
}
diff --git a/src/network/public/approx8.h b/src/network/public/approx8.h
index 711d2a1..634225e 100644
--- a/src/network/public/approx8.h
+++ b/src/network/public/approx8.h
@@ -31,54 +31,55 @@
* @brief Header file for the Approx8 nuclear reaction network.
*
* This file contains the definitions and declarations for the Approx8 nuclear reaction network.
- * The network is based on Frank Timmes' "aprox8" and includes 8 isotopes and various nuclear reactions.
+ * The network is based on Frank Timmes' "approx8" and includes 8 isotopes and various nuclear reactions.
* The rates are evaluated using a fitting function with coefficients from reaclib.jinaweb.org.
*/
-/**
- * @typedef vector_type
- * @brief Alias for a vector of doubles using Boost uBLAS.
- */
-typedef boost::numeric::ublas::vector< double > vector_type;
-
-/**
- * @typedef matrix_type
- * @brief Alias for a matrix of doubles using Boost uBLAS.
- */
-typedef boost::numeric::ublas::matrix< double > matrix_type;
-
-/**
- * @typedef vec7
- * @brief Alias for a std::array of 7 doubles.
- */
-typedef std::array vec7;
namespace serif::network::approx8{
+ /**
+ * @typedef vector_type
+ * @brief Alias for a vector of doubles using Boost uBLAS.
+ */
+ typedef boost::numeric::ublas::vector< double > vector_type;
+
+ /**
+ * @typedef matrix_type
+ * @brief Alias for a matrix of doubles using Boost uBLAS.
+ */
+ typedef boost::numeric::ublas::matrix< double > matrix_type;
+
+ /**
+ * @typedef vec7
+ * @brief Alias for a std::array of 7 doubles.
+ */
+ typedef std::array vec7;
+
using namespace boost::numeric::odeint;
/**
- * @struct Net
+ * @struct Approx8Net
* @brief Contains constants and arrays related to the nuclear network.
*/
- struct Net{
- const static int ih1=0;
- const static int ihe3=1;
- const static int ihe4=2;
- const static int ic12=3;
- const static int in14=4;
- const static int io16=5;
- const static int ine20=6;
- const static int img24=7;
+ struct Approx8Net{
+ static constexpr int ih1=0;
+ static constexpr int ihe3=1;
+ static constexpr int ihe4=2;
+ static constexpr int ic12=3;
+ static constexpr int in14=4;
+ static constexpr int io16=5;
+ static constexpr int ine20=6;
+ static constexpr int img24=7;
- const static int itemp=img24+1;
- const static int iden =itemp+1;
- const static int iener=iden+1;
+ static constexpr int iTemp=img24+1;
+ static constexpr int iDensity =iTemp+1;
+ static constexpr int iEnergy=iDensity+1;
- const static int niso=img24+1; // number of isotopes
- const static int nvar=iener+1; // number of variables
+ static constexpr int nIso=img24+1; // number of isotopes
+ static constexpr int nVar=iEnergy+1; // number of variables
- static constexpr std::array aion = {
+ static constexpr std::array aIon = {
1,
3,
4,
@@ -89,7 +90,7 @@ namespace serif::network::approx8{
24
};
- static constexpr std::array mion = {
+ static constexpr std::array mIon = {
1.67262164e-24,
5.00641157e-24,
6.64465545e-24,
@@ -270,10 +271,9 @@ namespace serif::network::approx8{
* @brief Calculates the Jacobian matrix.
* @param y State vector.
* @param J Jacobian matrix.
- * @param t Time.
* @param dfdt Derivative of the state vector.
*/
- void operator() ( const vector_type &y, matrix_type &J, double /* t */, vector_type &dfdt );
+ void operator() ( const vector_type &y, matrix_type &J, double /* t */, vector_type &dfdt ) const;
};
/**
@@ -285,23 +285,24 @@ namespace serif::network::approx8{
* @brief Calculates the derivatives of the state vector.
* @param y State vector.
* @param dydt Derivative of the state vector.
- * @param t Time.
*/
- void operator() ( const vector_type &y, vector_type &dydt, double /* t */);
+ void operator() ( const vector_type &y, vector_type &dydt, double /* t */) const;
};
/**
* @class Approx8Network
* @brief Class for the Approx8 nuclear reaction network.
*/
- class Approx8Network : public Network {
+ class Approx8Network final : public Network {
public:
+ Approx8Network();
+
/**
* @brief Evaluates the nuclear network.
* @param netIn Input parameters for the network.
* @return Output results from the network.
*/
- virtual NetOut evaluate(const NetIn &netIn);
+ NetOut evaluate(const NetIn &netIn) override;
/**
* @brief Sets whether the solver should use a stiff method.
@@ -313,11 +314,11 @@ namespace serif::network::approx8{
* @brief Checks if the solver is using a stiff method.
* @return Boolean indicating if a stiff method is being used.
*/
- bool isStiff() { return m_stiff; }
+ bool isStiff() const { return m_stiff; }
private:
vector_type m_y;
- double m_tmax;
- double m_dt0;
+ double m_tMax = 0;
+ double m_dt0 = 0;
bool m_stiff = false;
/**
@@ -325,7 +326,8 @@ namespace serif::network::approx8{
* @param netIn Input parameters for the network.
* @return Internal state vector.
*/
- vector_type convert_netIn(const NetIn &netIn);
+ static vector_type convert_netIn(const NetIn &netIn);
};
+
} // namespace nnApprox8
diff --git a/src/network/public/network.h b/src/network/public/network.h
index 0ae4677..0d7692b 100644
--- a/src/network/public/network.h
+++ b/src/network/public/network.h
@@ -25,15 +25,27 @@
#include "probe.h"
#include "config.h"
#include "quill/Logger.h"
+#include "composition.h"
+#include
namespace serif::network {
+ enum NetworkFormat {
+ APPROX8, ///< Approx8 nuclear reaction network format.
+ UNKNOWN,
+ };
+
+ static inline std::unordered_map FormatStringLookup = {
+ {APPROX8, "Approx8"},
+ {UNKNOWN, "Unknown"}
+ };
+
/**
* @struct NetIn
* @brief Input structure for the network evaluation.
- *
+ *
* This structure holds the input parameters required for the network evaluation.
- *
+ *
* Example usage:
* @code
* nuclearNetwork::NetIn netIn;
@@ -46,8 +58,8 @@ namespace serif::network {
* @endcode
*/
struct NetIn {
- std::vector composition; ///< Composition of the network
- double tmax; ///< Maximum time
+ serif::composition::Composition composition; ///< Composition of the network
+ double tMax; ///< Maximum time
double dt0; ///< Initial time step
double temperature; ///< Temperature in Kelvin
double density; ///< Density in g/cm^3
@@ -69,7 +81,7 @@ namespace serif::network {
* @endcode
*/
struct NetOut {
- std::vector composition; ///< Composition of the network after evaluation
+ serif::composition::Composition composition; ///< Composition of the network after evaluation
int num_steps; ///< Number of steps taken in the evaluation
double energy; ///< Energy in ergs after evaluation
};
@@ -90,9 +102,12 @@ namespace serif::network {
*/
class Network {
public:
- Network();
+ explicit Network(const NetworkFormat format = NetworkFormat::APPROX8);
virtual ~Network() = default;
+ NetworkFormat getFormat() const;
+ NetworkFormat setFormat(const NetworkFormat format);
+
/**
* @brief Evaluate the network based on the input parameters.
*
@@ -105,6 +120,10 @@ namespace serif::network {
serif::config::Config& m_config; ///< Configuration instance
serif::probe::LogManager& m_logManager; ///< Log manager instance
quill::Logger* m_logger; ///< Logger instance
+
+ NetworkFormat m_format; ///< Format of the network
};
+
+
} // namespace nuclearNetwork
diff --git a/src/python/eos/bindings.cpp b/src/python/eos/bindings.cpp
index e8c6314..ca4b87c 100644
--- a/src/python/eos/bindings.cpp
+++ b/src/python/eos/bindings.cpp
@@ -34,7 +34,7 @@ void register_eos_bindings(pybind11::module &eos_submodule) {
}, py::return_value_policy::reference_internal, // IMPORTANT: Keep this policy!
"Get the EOS table data.")
.def("__repr__", [](const serif::eos::EOSio &eos) {
- return "";
+ return "";
});
py::class_(eos_submodule, "EOSTable");
@@ -88,64 +88,64 @@ void register_eos_bindings(pybind11::module &eos_submodule) {
);
}, py::return_value_policy::reference_internal); // Keep parent 'table' alive
- py::class_(eos_submodule, "EOS")
+ py::class_(eos_submodule, "EOS")
.def(py::init<>())
- .def_readonly("ye", &serif::eos::helmholtz::EOS::ye)
- .def_readonly("etaele", &serif::eos::helmholtz::EOS::etaele)
- .def_readonly("xnefer", &serif::eos::helmholtz::EOS::xnefer)
+ .def_readonly("ye", &serif::eos::helmholtz::HELMEOSOutput::ye)
+ .def_readonly("etaele", &serif::eos::helmholtz::HELMEOSOutput::etaele)
+ .def_readonly("xnefer", &serif::eos::helmholtz::HELMEOSOutput::xnefer)
- .def_readonly("ptot", &serif::eos::helmholtz::EOS::ptot)
- .def_readonly("pgas", &serif::eos::helmholtz::EOS::pgas)
- .def_readonly("prad", &serif::eos::helmholtz::EOS::prad)
+ .def_readonly("ptot", &serif::eos::helmholtz::HELMEOSOutput::ptot)
+ .def_readonly("pgas", &serif::eos::helmholtz::HELMEOSOutput::pgas)
+ .def_readonly("prad", &serif::eos::helmholtz::HELMEOSOutput::prad)
- .def_readonly("etot", &serif::eos::helmholtz::EOS::etot)
- .def_readonly("egas", &serif::eos::helmholtz::EOS::egas)
- .def_readonly("erad", &serif::eos::helmholtz::EOS::erad)
+ .def_readonly("etot", &serif::eos::helmholtz::HELMEOSOutput::etot)
+ .def_readonly("egas", &serif::eos::helmholtz::HELMEOSOutput::egas)
+ .def_readonly("erad", &serif::eos::helmholtz::HELMEOSOutput::erad)
- .def_readonly("stot", &serif::eos::helmholtz::EOS::stot)
- .def_readonly("sgas", &serif::eos::helmholtz::EOS::sgas)
- .def_readonly("srad", &serif::eos::helmholtz::EOS::srad)
+ .def_readonly("stot", &serif::eos::helmholtz::HELMEOSOutput::stot)
+ .def_readonly("sgas", &serif::eos::helmholtz::HELMEOSOutput::sgas)
+ .def_readonly("srad", &serif::eos::helmholtz::HELMEOSOutput::srad)
- .def_readonly("dpresdd", &serif::eos::helmholtz::EOS::dpresdd)
- .def_readonly("dpresdt", &serif::eos::helmholtz::EOS::dpresdt)
- .def_readonly("dpresda", &serif::eos::helmholtz::EOS::dpresda)
- .def_readonly("dpresdz", &serif::eos::helmholtz::EOS::dpresdz)
- // TODO: Finish adding all the derivatives to the bound class
- .def_readonly("dentrdd", &serif::eos::helmholtz::EOS::dentrdd)
- .def_readonly("dentrdt", &serif::eos::helmholtz::EOS::dentrdt)
- .def_readonly("dentrda", &serif::eos::helmholtz::EOS::dentrda)
- .def_readonly("dentrdz", &serif::eos::helmholtz::EOS::dentrdz)
+ .def_readonly("dpresdd", &serif::eos::helmholtz::HELMEOSOutput::dpresdd)
+ .def_readonly("dpresdt", &serif::eos::helmholtz::HELMEOSOutput::dpresdt)
+ .def_readonly("dpresda", &serif::eos::helmholtz::HELMEOSOutput::dpresda)
+ .def_readonly("dpresdz", &serif::eos::helmholtz::HELMEOSOutput::dpresdz)
- .def_readonly("denerdd", &serif::eos::helmholtz::EOS::denerdd)
- .def_readonly("denerdt", &serif::eos::helmholtz::EOS::denerdt)
- .def_readonly("denerda", &serif::eos::helmholtz::EOS::denerda)
- .def_readonly("denerdz", &serif::eos::helmholtz::EOS::denerdz)
+ .def_readonly("dentrdd", &serif::eos::helmholtz::HELMEOSOutput::dentrdd)
+ .def_readonly("dentrdt", &serif::eos::helmholtz::HELMEOSOutput::dentrdt)
+ .def_readonly("dentrda", &serif::eos::helmholtz::HELMEOSOutput::dentrda)
+ .def_readonly("dentrdz", &serif::eos::helmholtz::HELMEOSOutput::dentrdz)
- .def_readonly("chiT", &serif::eos::helmholtz::EOS::chiT)
- .def_readonly("chiRho", &serif::eos::helmholtz::EOS::chiRho)
- .def_readonly("csound", &serif::eos::helmholtz::EOS::csound)
- .def_readonly("grad_ad", &serif::eos::helmholtz::EOS::grad_ad)
- .def_readonly("gamma1", &serif::eos::helmholtz::EOS::gamma1)
- .def_readonly("gamma2", &serif::eos::helmholtz::EOS::gamma2)
- .def_readonly("gamma3", &serif::eos::helmholtz::EOS::gamma3)
- .def_readonly("cV", &serif::eos::helmholtz::EOS::cV)
- .def_readonly("cP", &serif::eos::helmholtz::EOS::cP)
- .def_readonly("dse", &serif::eos::helmholtz::EOS::dse)
- .def_readonly("dpe", &serif::eos::helmholtz::EOS::dpe)
- .def_readonly("dsp", &serif::eos::helmholtz::EOS::dsp)
+ .def_readonly("denerdd", &serif::eos::helmholtz::HELMEOSOutput::denerdd)
+ .def_readonly("denerdt", &serif::eos::helmholtz::HELMEOSOutput::denerdt)
+ .def_readonly("denerda", &serif::eos::helmholtz::HELMEOSOutput::denerda)
+ .def_readonly("denerdz", &serif::eos::helmholtz::HELMEOSOutput::denerdz)
- .def("__repr__", [](const serif::eos::helmholtz::EOS &eos) {
+ .def_readonly("chiT", &serif::eos::helmholtz::HELMEOSOutput::chiT)
+ .def_readonly("chiRho", &serif::eos::helmholtz::HELMEOSOutput::chiRho)
+ .def_readonly("csound", &serif::eos::helmholtz::HELMEOSOutput::csound)
+ .def_readonly("grad_ad", &serif::eos::helmholtz::HELMEOSOutput::grad_ad)
+ .def_readonly("gamma1", &serif::eos::helmholtz::HELMEOSOutput::gamma1)
+ .def_readonly("gamma2", &serif::eos::helmholtz::HELMEOSOutput::gamma2)
+ .def_readonly("gamma3", &serif::eos::helmholtz::HELMEOSOutput::gamma3)
+ .def_readonly("cV", &serif::eos::helmholtz::HELMEOSOutput::cV)
+ .def_readonly("cP", &serif::eos::helmholtz::HELMEOSOutput::cP)
+ .def_readonly("dse", &serif::eos::helmholtz::HELMEOSOutput::dse)
+ .def_readonly("dpe", &serif::eos::helmholtz::HELMEOSOutput::dpe)
+ .def_readonly("dsp", &serif::eos::helmholtz::HELMEOSOutput::dsp)
+
+ .def("__repr__", [](const serif::eos::helmholtz::HELMEOSOutput &eos) {
return "";
});
- py::class_(eos_submodule, "EOSInput")
+ py::class_(eos_submodule, "HELMEOSInput")
.def(py::init<>())
- .def_readwrite("T", &serif::eos::helmholtz::EOSInput::T)
- .def_readwrite("rho", &serif::eos::helmholtz::EOSInput::rho)
- .def_readwrite("abar", &serif::eos::helmholtz::EOSInput::abar)
- .def_readwrite("zbar", &serif::eos::helmholtz::EOSInput::zbar)
- .def("__repr__", [](const serif::eos::helmholtz::EOSInput &input) {
- return "";
diff --git a/tests/eos/eosTest.cpp b/tests/eos/eosTest.cpp
index b7a670b..d98a25c 100644
--- a/tests/eos/eosTest.cpp
+++ b/tests/eos/eosTest.cpp
@@ -1,5 +1,4 @@
#include
-#include
#include
#include
#include
@@ -7,9 +6,11 @@
#include "helm.h"
#include "resourceManager.h"
#include "config.h"
+#include "composition.h"
+#include "EOS.h"
/**
- * @file constTest.cpp
+ * @file eosTest.cpp
* @brief Unit tests for the const class.
*/
@@ -26,44 +27,43 @@ std::string TEST_CONFIG = std::string(getenv("MESON_SOURCE_ROOT")) + "/tests/tes
TEST_F(eosTest, read_helm_table) {
serif::config::Config::getInstance().loadConfig(TEST_CONFIG);
- serif::resource::ResourceManager& rm = serif::resource::ResourceManager::getInstance();
+ const serif::resource::ResourceManager& rm = serif::resource::ResourceManager::getInstance();
auto& eos = std::get>(rm.getResource("eos:helm"));
- auto& table = eos->getTable();
- auto& helmTable = *std::get>(table);
+ const auto& table = eos->getTable();
+ const auto& helmTable = *std::get>(table);
std::stringstream ss;
ss << helmTable;
EXPECT_EQ(ss.str(), "HELMTable Data:\n imax: 541, jmax: 201\n Temperature Range: [1000, 1e+13]\n Density Range: [1e-12, 1e+15]\n");
}
TEST_F(eosTest, get_helm_EOS) {
+ constexpr int nel=3;
+ double xMass[nel], aIon[nel], zIon[nel];
+ serif::eos::helmholtz::HELMEOSInput eos1;
- const int nel=3;
- double xmass[nel], aion[nel], zion[nel];
- serif::eos::helmholtz::EOSInput eos1;
-
- xmass[0] = 0.75; aion[0] = 1.0; zion[0] = 1.0;
- xmass[1] = 0.23; aion[1] = 4.0; zion[1] = 2.0;
- xmass[2] = 0.02; aion[2] = 12.0; zion[2] = 6.0;
+ xMass[0] = 0.75; aIon[0] = 1.0; zIon[0] = 1.0;
+ xMass[1] = 0.23; aIon[1] = 4.0; zIon[1] = 2.0;
+ xMass[2] = 0.02; aIon[2] = 12.0; zIon[2] = 6.0;
eos1.T = 1.0e8;
eos1.rho = 1.0e6;
- double asum = 0.0;
- double zsum = 0.0;
+ double aSum = 0.0;
+ double zSum = 0.0;
for (int i=0; i>(rm.getResource("eos:helm"));
auto& table = eos->getTable();
auto& helmTable = *std::get>(table);
- serif::eos::helmholtz::EOS helmEos = get_helm_EOS(eos1, helmTable);
+ serif::eos::helmholtz::HELMEOSOutput helmEos = get_helm_EOS(eos1, helmTable);
- const double absErr = 1e-12;
+ constexpr double absErr = 1e-12;
//Check composition info
EXPECT_NEAR( helmEos.ye, 8.75e-01, absErr);
@@ -85,3 +85,44 @@ TEST_F(eosTest, get_helm_EOS) {
EXPECT_NEAR( helmEos.dpe, 0, absErr);
EXPECT_NEAR( helmEos.dsp, 0, absErr);
}
+
+TEST_F(eosTest, eos_using_composition) {
+ serif::composition::Composition composition;
+ composition.registerSymbol({
+ "H-1",
+ "He-4",
+ "C-12",
+ "O-16",
+ "Ne-20",
+ "Fe-56",
+ "N-14",
+ "Si-28",
+ "Mg-24"
+ }, true);
+ composition.setMassFraction("H-1", 0.75);
+ composition.setMassFraction("He-4", 0.23);
+ composition.setMassFraction("C-12", 0.0044);
+ composition.setMassFraction("O-16", 0.0096);
+ composition.setMassFraction("Ne-20", 0.002);
+ composition.setMassFraction("Fe-56", 0.0018);
+ composition.setMassFraction("N-14", 0.001);
+ composition.setMassFraction("Si-28", 0.0008);
+ composition.setMassFraction("Mg-24", 0.0004);
+ composition.finalize();
+
+ serif::resource::ResourceManager& rm = serif::resource::ResourceManager::getInstance();
+ auto& EOSio = std::get>(rm.getResource("eos:helm"));
+
+ serif::eos::EOS EOS(*EOSio);
+
+ serif::eos::EOSInput eosInput;
+ eosInput.temperature = 1.0e8; // Temperature in K
+ eosInput.density = 1.0e6; // Density in g/cm^3
+ eosInput.composition = composition; // Set the composition
+
+ serif::eos::EOSOutput eosOutput;
+ EXPECT_NO_THROW(eosOutput = EOS.get(eosInput));
+ eosOutput = EOS.get(eosInput);
+ constexpr double absErr = 1e-8;
+ EXPECT_NEAR(eosOutput.pressure.total, 6.9548533046915791E+22, absErr);
+}
diff --git a/tests/eos/meson.build b/tests/eos/meson.build
index d8154e6..61b86e3 100644
--- a/tests/eos/meson.build
+++ b/tests/eos/meson.build
@@ -3,6 +3,16 @@ test_sources = [
'eosTest.cpp',
]
+dependencies = [
+ gtest_dep,
+ eos_dep,
+ gtest_main,
+ resourceManager_dep,
+ config_dep,
+ composition_dep,
+]
+
+
foreach test_file : test_sources
exe_name = test_file.split('.')[0]
message('Building test: ' + exe_name)
@@ -11,7 +21,7 @@ foreach test_file : test_sources
test_exe = executable(
exe_name,
test_file,
- dependencies: [gtest_dep, eos_dep, gtest_main, resourceManager_dep, config_dep],
+ dependencies: dependencies,
install_rpath: '@loader_path/../../src' # Ensure runtime library path resolves correctly
)
diff --git a/tests/network/approx8Test.cpp b/tests/network/approx8Test.cpp
index 2bfe7fa..899eaf6 100644
--- a/tests/network/approx8Test.cpp
+++ b/tests/network/approx8Test.cpp
@@ -4,6 +4,7 @@
#include "approx8.h"
#include "config.h"
#include "network.h"
+#include "composition.h"
#include
@@ -32,19 +33,31 @@ TEST_F(approx8Test, evaluate) {
serif::network::NetIn netIn;
std::vector comp = {0.708, 2.94e-5, 0.276, 0.003, 0.0011, 9.62e-3, 1.62e-3, 5.16e-4};
+ std::vector symbols = {"H-1", "He-3", "He-4", "C-12", "N-14", "O-16", "Ne-20", "Mg-24"};
- netIn.composition = comp;
+ serif::composition::Composition composition;
+ composition.registerSymbol(symbols, true);
+ composition.setMassFraction(symbols, comp);
+ bool isFinalized = composition.finalize(true);
+ EXPECT_TRUE(isFinalized);
+
+ netIn.composition = composition;
netIn.temperature = 1e7;
netIn.density = 1e2;
netIn.energy = 0.0;
- netIn.tmax = 3.15e17;
+ netIn.tMax = 3.15e17;
netIn.dt0 = 1e12;
serif::network::NetOut netOut;
EXPECT_NO_THROW(netOut = network.evaluate(netIn));
- EXPECT_DOUBLE_EQ(netOut.composition[serif::network::approx8::Net::ih1], 0.50166260916650918);
- EXPECT_DOUBLE_EQ(netOut.composition[serif::network::approx8::Net::ihe4],0.48172270591286032);
- EXPECT_DOUBLE_EQ(netOut.energy, 1.6433049870528356e+18);
+ double energyFraction = netOut.energy / 1.6433051127589775E+18;
+ double H1MassFraction = netOut.composition.getMassFraction("H-1")/ 0.50166262445895604;
+ double He4MassFraction = netOut.composition.getMassFraction("He-4") / 0.48172273720971226;
+
+ double relError = 1e-8;
+ EXPECT_NEAR(H1MassFraction, 1.0, relError);
+ EXPECT_NEAR(He4MassFraction, 1.0, relError);
+ EXPECT_NEAR(energyFraction, 1.0, relError);
}
diff --git a/tests/network/meson.build b/tests/network/meson.build
index 7b84063..1e6ccf3 100644
--- a/tests/network/meson.build
+++ b/tests/network/meson.build
@@ -11,7 +11,7 @@ foreach test_file : test_sources
test_exe = executable(
exe_name,
test_file,
- dependencies: [gtest_dep, gtest_main, network_dep],
+ dependencies: [gtest_dep, gtest_main, network_dep, species_weight_dep, composition_dep],
include_directories: include_directories('../../src/network/public'),
link_with: libnetwork, # Link the dobj library
install_rpath: '@loader_path/../../src' # Ensure runtime library path resolves correctly
diff --git a/tests/resource/resourceManagerTest.cpp b/tests/resource/resourceManagerTest.cpp
index 2936653..4238689 100644
--- a/tests/resource/resourceManagerTest.cpp
+++ b/tests/resource/resourceManagerTest.cpp
@@ -48,7 +48,7 @@ TEST_F(resourceManagerTest, getResource) {
const serif::resource::types::Resource &r = rm.getResource(name);
// BREAKPOINT();
const auto &eos = std::get>(r);
- EXPECT_EQ("helm", eos->getFormat());
+ EXPECT_EQ("Helmholtz", eos->getFormatName());
serif::eos::EOSTable &table = eos->getTable();
// -- Extract the Helm table from the EOSTable