SERiF 0.0.1a
3+1D Stellar Structure and Evolution
Loading...
Searching...
No Matches
netgraph.h
Go to the documentation of this file.
1#pragma once
2
3#include "atomicSpecies.h"
4#include "composition.h"
5#include "network.h"
6#include "reaclib.h"
7
8#include <string>
9#include <unordered_map>
10#include <vector>
11
12#include <boost/numeric/ublas/matrix_sparse.hpp>
13
14#include "cppad/cppad.hpp"
15
16#include "quill/LogMacros.h"
17
18// PERF: The function getNetReactionStoichiometry returns a map of species to their stoichiometric coefficients for a given reaction.
19// this makes extra copies of the species, which is not ideal and could be optimized further.
20// Even more relevant is the member m_reactionIDMap which makes copies of a REACLIBReaction for each reaction ID.
21// REACLIBReactions are quite large data structures, so this could be a performance bottleneck.
22
36
37namespace serif::network {
38
44 template<typename T>
45 concept IsArithmeticOrAD = std::is_same_v<T, double> || std::is_same_v<T, CppAD::AD<double>>;
46
48 static constexpr double MIN_DENSITY_THRESHOLD = 1e-18;
50 static constexpr double MIN_ABUNDANCE_THRESHOLD = 1e-18;
52 static constexpr double MIN_JACOBIAN_THRESHOLD = 1e-24;
53
77 class GraphNetwork final : public Network {
78 public:
84
94 const double cullingThreshold, const double T9);
95
112 NetOut evaluate(const NetIn &netIn) override;
113
126 [[nodiscard]] const std::vector<serif::atomic::Species>& getNetworkSpecies() const;
127
132 [[nodiscard]] const reaclib::REACLIBReactionSet& getNetworkReactions() const;
133
152 [[nodiscard]] std::unordered_map<serif::atomic::Species, int> getNetReactionStoichiometry(
153 const reaclib::REACLIBReaction &reaction) const;
154
165 [[nodiscard]] bool involvesSpecies(const serif::atomic::Species& species) const;
166
174 [[deprecated("not implimented")]] std::vector<std::vector<std::string>> detectCycles() const;
175
181 [[deprecated("not implimented")]] std::vector<std::string> topologicalSortReactions() const;
182
183 private:
184 reaclib::REACLIBReactionSet m_reactions;
185 std::unordered_map<std::string_view, const reaclib::REACLIBReaction> m_reactionIDMap;
186
187 std::vector<serif::atomic::Species> m_networkSpecies;
188 std::unordered_map<std::string_view, serif::atomic::Species> m_networkSpeciesMap;
189 std::unordered_map<serif::atomic::Species, size_t> m_speciesToIndexMap;
190
191 boost::numeric::ublas::compressed_matrix<int> m_stoichiometryMatrix;
192 boost::numeric::ublas::compressed_matrix<double> m_jacobianMatrix;
193
194 CppAD::ADFun<double> m_rhsADFun;
195
201 struct ODETerm {
203 const double m_T9;
204 const double m_rho;
205 const size_t m_numSpecies;
206
213 ODETerm(GraphNetwork& network, const double T9, double rho) :
215
223 void operator()(const boost::numeric::ublas::vector<double>&Y, boost::numeric::ublas::vector<double>& dYdt, double /* t */) const {
224 const std::vector<double> y(Y.begin(), Y.begin() + m_numSpecies);
225
226 StepDerivatives<double> derivatives = m_network.calculateAllDerivatives<double>(y, m_T9, m_rho);
227
228 dYdt.resize(m_numSpecies + 1);
229 std::ranges::copy(derivatives.dydt, dYdt.begin());
230 dYdt(m_numSpecies) = derivatives.specificEnergyRate; // Last element is the specific energy rate
231 }
232 };
233
239 const double m_T9;
240 const double m_rho;
241 const size_t m_numSpecies;
242
249 JacobianTerm(GraphNetwork& network, const double T9, const double rho) :
251
257 void operator()(const boost::numeric::ublas::vector<double>& Y, boost::numeric::ublas::matrix<double>& J, double /* t */, boost::numeric::ublas::vector<double>& /* dfdt */) const {
258 const std::vector<double> y_species(Y.begin(), Y.begin() + m_numSpecies);
259
260 m_network.generateJacobianMatrix(y_species, m_T9, m_rho);
261
262 J.resize(m_numSpecies + 1, m_numSpecies + 1);
263 J.clear(); // Zero out the matrix
264
265 // PERF: Could probably be optimized
266 for (auto it1 = m_network.m_jacobianMatrix.begin1(); it1 != m_network.m_jacobianMatrix.end1(); ++it1) {
267 for (auto it2 = it1.begin(); it2 != it1.end(); ++it2) {
268 J(it2.index1(), it2.index2()) = *it2;
269 }
270 }
271 }
272 };
273
278 template <IsArithmeticOrAD T>
280 std::vector<T> dydt;
282 };
283
284 private:
292 void syncInternalMaps();
293
303
310
317
324
331 void recordADTape();
332
333 // --- Validation methods ---
334
341 bool validateConservation() const;
342
354 void validateComposition(const serif::composition::Composition &composition, double culling, double T9);
355
356 // --- Simple Derivative Calculations ---
357
366 template <IsArithmeticOrAD T>
367 StepDerivatives<T> calculateAllDerivatives(const std::vector<T>& Y, T T9, T rho) const;
368
369 // --- Generate the system matrices ---
370
378
388 void generateJacobianMatrix(const std::vector<double>& Y, const double T9, const double rho);
389
398 template <IsArithmeticOrAD GeneralScalarType>
399 std::vector<GeneralScalarType> calculateRHS(const std::vector<GeneralScalarType> &Y, const GeneralScalarType T9,
400 const GeneralScalarType rho) const;
401
416 template <IsArithmeticOrAD GeneralScalarType>
417 GeneralScalarType calculateReactionRate(const reaclib::REACLIBReaction &reaction,
418 const std::vector<GeneralScalarType> &Y, const GeneralScalarType T9,
419 const GeneralScalarType rho) const;
420
440 void detectStiff(const NetIn &netIn, double T9, double numSpecies, const boost::numeric::ublas::vector<double>& Y);
441 };
442
443
444 template<IsArithmeticOrAD T>
446 const std::vector<T> &Y, T T9, T rho) const {
447 StepDerivatives<T> result;
448 result.dydt.resize(m_networkSpecies.size(), static_cast<T>(0.0));
449
450 if (rho < MIN_DENSITY_THRESHOLD) {
451 return result; // Return zero for everything if density is too low
452 }
453
454 const T u = static_cast<T>(m_constants.get("u").value); // Atomic mass unit in grams
455 const T MeV_to_erg = static_cast<T>(m_constants.get("MeV_to_erg").value);
456
457 T volumetricEnergyRate = static_cast<T>(0.0); // Accumulator for erg / (cm^3 * s)
458
459 // --- SINGLE LOOP OVER ALL REACTIONS ---
460 for (size_t reactionIndex = 0; reactionIndex < m_reactions.size(); ++reactionIndex) {
461 const auto& reaction = m_reactions[reactionIndex];
462
463 // 1. Calculate reaction rate
464 const T reactionRate = calculateReactionRate(reaction, Y, T9, rho);
465
466 // 2. Use the rate to update all relevant species derivatives (dY/dt)
467 for (size_t speciesIndex = 0; speciesIndex < m_networkSpecies.size(); ++speciesIndex) {
468 const T nu_ij = static_cast<T>(m_stoichiometryMatrix(speciesIndex, reactionIndex));
469 if (nu_ij != 0) {
470 const T speciesAtomicMassAMU = static_cast<T>(m_networkSpecies[speciesIndex].mass());
471 const T speciesAtomicMassGrams = speciesAtomicMassAMU * u;
472 result.dydt[speciesIndex] += (nu_ij * reactionRate * speciesAtomicMassGrams) / rho;
473 }
474 }
475
476 // 3. Use the same rate to update the energy generation rate
477 const T q_value_ergs = static_cast<T>(reaction.qValue()) * MeV_to_erg;
478 volumetricEnergyRate += reactionRate * q_value_ergs;
479 }
480
481 result.specificEnergyRate = volumetricEnergyRate / rho;
482
483 return result;
484 }
485
486
487 template <IsArithmeticOrAD GeneralScalarType>
488 GeneralScalarType GraphNetwork::calculateReactionRate(const reaclib::REACLIBReaction &reaction, const std::vector<GeneralScalarType> &Y,
489 const GeneralScalarType T9, const GeneralScalarType rho) const {
490 const auto &constants = serif::constant::Constants::getInstance();
491
492 const auto u = constants.get("u"); // Atomic mass unit in g/mol
493 const GeneralScalarType uValue = static_cast<GeneralScalarType>(u.value); // Convert to double for calculations
494 const GeneralScalarType k_reaction = reaction.calculate_rate(T9);
495
496 GeneralScalarType reactant_product_or_particle_densities = static_cast<GeneralScalarType>(1.0);
497
498 std::unordered_map<std::string, int> reactant_counts;
499 reactant_counts.reserve(reaction.reactants().size());
500 for (const auto& reactant : reaction.reactants()) {
501 reactant_counts[std::string(reactant.name())]++;
502 }
503
504 const GeneralScalarType minAbundanceThreshold = static_cast<GeneralScalarType>(MIN_ABUNDANCE_THRESHOLD);
505 const GeneralScalarType minDensityThreshold = static_cast<GeneralScalarType>(MIN_DENSITY_THRESHOLD);
506
507 if (rho < minDensityThreshold) {
508 // LOG_INFO(m_logger, "Density is below the minimum threshold ({} g/cm^3), returning zero reaction rate for reaction '{}'.",
509 // CppAD::Value(rho), reaction.id()); // CppAD::Value causes a compilation error here, not clear why...
510 return static_cast<GeneralScalarType>(0.0); // If density is below a threshold, return zero rate.
511 }
512
513 for (const auto& [species_name, count] : reactant_counts) {
514 auto species_it = m_speciesToIndexMap.find(m_networkSpeciesMap.at(species_name));
515 if (species_it == m_speciesToIndexMap.end()) {
516 LOG_ERROR(m_logger, "Reactant species '{}' not found in species to index map for reaction '{}'.",
517 species_name, reaction.id());
518 throw std::runtime_error("Reactant species not found in species to index map: " + species_name);
519 }
520 const size_t species_index = species_it->second;
521 const GeneralScalarType Yi = Y[species_index];
522
523 if (Yi < minAbundanceThreshold) {
524 return static_cast<GeneralScalarType>(0.0); // If any reactant is below a threshold, return zero rate.
525 }
526
527 GeneralScalarType atomicMassAMU = static_cast<GeneralScalarType>(m_networkSpecies[species_index].mass());
528
529 // Convert to number density
530 GeneralScalarType ni;
531 const GeneralScalarType denominator = atomicMassAMU * uValue;
532 if (denominator > minDensityThreshold) {
533 ni = (Yi * rho) / (denominator);
534 } else {
535 ni = static_cast<GeneralScalarType>(0.0);
536 }
537
538 reactant_product_or_particle_densities *= ni;
539
540 // Apply factorial correction for identical reactions
541 if (count > 1) {
542 reactant_product_or_particle_densities /= static_cast<GeneralScalarType>(std::tgamma(static_cast<double>(count + 1))); // Gamma function for factorial
543 }
544 }
545 const GeneralScalarType Na = static_cast<GeneralScalarType>(constants.get("N_a").value); // Avogadro's number in mol^-1
546 const int numReactants = static_cast<int>(reaction.reactants().size());
547 GeneralScalarType molarCorrectionFactor = static_cast<GeneralScalarType>(1.0); // No correction needed for single reactant reactions
548 if (numReactants > 1) {
549 molarCorrectionFactor = CppAD::pow(Na, static_cast<GeneralScalarType>(reaction.reactants().size() - 1));
550 }
551 return (reactant_product_or_particle_densities * k_reaction) / molarCorrectionFactor; // reaction rate in per volume per time (particles/cm^3/s)
552 }
553
554 template <IsArithmeticOrAD GeneralScalarType>
555 std::vector<GeneralScalarType> GraphNetwork::calculateRHS(
556 const std::vector<GeneralScalarType>& Y,
557 const GeneralScalarType T9,
558 const GeneralScalarType rho) const {
559
560 auto derivatives = calculateAllDerivatives<GeneralScalarType>(Y, T9, rho);
561 return derivatives.dydt;
562 }
563
564};
Manages the composition of elements.
static Constants & getInstance()
get instance of constants singleton
Definition const.h:99
std::vector< GeneralScalarType > calculateRHS(const std::vector< GeneralScalarType > &Y, const GeneralScalarType T9, const GeneralScalarType rho) const
Calculate the right-hand side (dY/dt) for the ODE system.
Definition netgraph.h:555
std::vector< std::string > topologicalSortReactions() const
Perform a topological sort of the reactions (not implemented).
boost::numeric::ublas::compressed_matrix< double > m_jacobianMatrix
Jacobian matrix (species x species).
Definition netgraph.h:192
const std::vector< serif::atomic::Species > & getNetworkSpecies() const
Get the vector of unique species in the network.
Definition netgraph.cpp:106
std::unordered_map< std::string_view, serif::atomic::Species > m_networkSpeciesMap
Map from species name to Species object.
Definition netgraph.h:188
std::unordered_map< std::string_view, const reaclib::REACLIBReaction > m_reactionIDMap
Map from reaction ID to REACLIBReaction. //PERF: This makes copies of REACLIBReaction and could be a ...
Definition netgraph.h:185
GeneralScalarType calculateReactionRate(const reaclib::REACLIBReaction &reaction, const std::vector< GeneralScalarType > &Y, const GeneralScalarType T9, const GeneralScalarType rho) const
Calculate the reaction rate for a given reaction in dimensions of particles per cm^3 per second.
Definition netgraph.h:488
CppAD::ADFun< double > m_rhsADFun
CppAD function for the right-hand side of the ODE.
Definition netgraph.h:194
reaclib::REACLIBReactionSet m_reactions
Set of REACLIB reactions in the network.
Definition netgraph.h:184
std::unordered_map< serif::atomic::Species, int > getNetReactionStoichiometry(const reaclib::REACLIBReaction &reaction) const
Get the net stoichiometric coefficients for all species in a reaction.
Definition netgraph.cpp:125
void populateReactionIDMap()
Populate the reaction ID map from the reaction set.
Definition netgraph.cpp:79
void detectStiff(const NetIn &netIn, double T9, double numSpecies, const boost::numeric::ublas::vector< double > &Y)
Detect if the network is stiff and select the appropriate ODE solver.
Definition netgraph.cpp:305
GraphNetwork(const serif::composition::Composition &composition)
Construct a GraphNetwork from a composition.
Definition netgraph.cpp:24
bool validateConservation() const
Validate mass and charge conservation for all reactions.
Definition netgraph.cpp:155
void populateSpeciesToIndexMap()
Populate the species-to-index map for matrix construction.
Definition netgraph.cpp:88
void reserveJacobianMatrix()
Reserve and resize the Jacobian matrix.
Definition netgraph.cpp:95
void validateComposition(const serif::composition::Composition &composition, double culling, double T9)
Validate and update the network composition if needed.
Definition netgraph.cpp:210
void collectNetworkSpecies()
Collect all unique species from the reaction set.
Definition netgraph.cpp:51
std::unordered_map< serif::atomic::Species, size_t > m_speciesToIndexMap
Map from species to their index in the stoichiometry matrix.
Definition netgraph.h:189
void generateStoichiometryMatrix()
Generate the stoichiometry matrix for the network.
Definition netgraph.cpp:232
void syncInternalMaps()
Synchronize all internal maps and matrices after network changes.
Definition netgraph.cpp:42
bool involvesSpecies(const serif::atomic::Species &species) const
Check if a species is present in the network.
Definition netgraph.cpp:118
void generateJacobianMatrix(const std::vector< double > &Y, const double T9, const double rho)
Generate the Jacobian matrix for the network.
Definition netgraph.cpp:275
const reaclib::REACLIBReactionSet & getNetworkReactions() const
Get the set of REACLIB reactions in the network.
Definition netgraph.cpp:112
boost::numeric::ublas::compressed_matrix< int > m_stoichiometryMatrix
Stoichiometry matrix (species x reactions).
Definition netgraph.h:191
std::vector< std::vector< std::string > > detectCycles() const
Detect cycles in the reaction network (not implemented).
NetOut evaluate(const NetIn &netIn) override
Evolve the network for the given input conditions.
Definition netgraph.cpp:337
std::vector< serif::atomic::Species > m_networkSpecies
Vector of unique species in the network.
Definition netgraph.h:187
void recordADTape()
Record the CppAD tape for automatic differentiation.
Definition netgraph.cpp:419
StepDerivatives< T > calculateAllDerivatives(const std::vector< T > &Y, T T9, T rho) const
Calculate all derivatives (dY/dt and energy rate) for the current state.
Definition netgraph.h:445
Network(const NetworkFormat format=NetworkFormat::APPROX8)
Definition network.cpp:32
serif::constant::Constants & m_constants
Definition network.h:139
quill::Logger * m_logger
Logger instance.
Definition network.h:136
Concept to check if a type is either double or CppAD::AD<double>.
Definition netgraph.h:45
static constexpr double MIN_JACOBIAN_THRESHOLD
Minimum Jacobian entry threshold for sparsity.
Definition netgraph.h:52
static constexpr double MIN_DENSITY_THRESHOLD
Minimum density threshold (g/cm^3) below which reactions are ignored.
Definition netgraph.h:48
static constexpr double MIN_ABUNDANCE_THRESHOLD
Minimum abundance threshold below which reactions are ignored.
Definition netgraph.h:50
GraphNetwork & m_network
Reference to the network.
Definition netgraph.h:238
const double m_T9
Temperature in 10^9 K.
Definition netgraph.h:239
const size_t m_numSpecies
Number of species.
Definition netgraph.h:241
JacobianTerm(GraphNetwork &network, const double T9, const double rho)
Construct a JacobianTerm functor.
Definition netgraph.h:249
void operator()(const boost::numeric::ublas::vector< double > &Y, boost::numeric::ublas::matrix< double > &J, double, boost::numeric::ublas::vector< double > &) const
Compute the Jacobian matrix for the ODE solver.
Definition netgraph.h:257
const double m_rho
Density in g/cm^3.
Definition netgraph.h:240
const double m_T9
Temperature in 10^9 K.
Definition netgraph.h:203
void operator()(const boost::numeric::ublas::vector< double > &Y, boost::numeric::ublas::vector< double > &dYdt, double) const
Compute dY/dt and energy rate for the ODE solver.
Definition netgraph.h:223
ODETerm(GraphNetwork &network, const double T9, double rho)
Construct an ODETerm functor.
Definition netgraph.h:213
GraphNetwork & m_network
Reference to the network.
Definition netgraph.h:202
const size_t m_numSpecies
Number of species.
Definition netgraph.h:205
const double m_rho
Density in g/cm^3.
Definition netgraph.h:204
Struct holding derivatives for a single ODE step.
Definition netgraph.h:279
T specificEnergyRate
Specific energy generation rate.
Definition netgraph.h:281
std::vector< T > dydt
Derivatives of abundances.
Definition netgraph.h:280
Input structure for the network evaluation.
Definition network.h:65
Output structure for the network evaluation.
Definition network.h:89