GridFire 0.0.1a
General Purpose Nuclear Network
Loading...
Searching...
No Matches
engine_graph.h
Go to the documentation of this file.
1#pragma once
2
3#include "fourdst/composition/atomicSpecies.h"
4#include "fourdst/composition/composition.h"
5#include "fourdst/logging/logging.h"
6#include "fourdst/config/config.h"
7
8#include "gridfire/network.h"
13
14#include <string>
15#include <unordered_map>
16#include <vector>
17#include <memory>
18
19#include <boost/numeric/ublas/matrix_sparse.hpp>
20
21#include "cppad/cppad.hpp"
22
23// PERF: The function getNetReactionStoichiometry returns a map of species to their stoichiometric coefficients for a given reaction.
24// this makes extra copies of the species, which is not ideal and could be optimized further.
25// Even more relevant is the member m_reactionIDMap which makes copies of a REACLIBReaction for each reaction ID.
26// REACLIBReactions are quite large data structures, so this could be a performance bottleneck.
27
28namespace gridfire {
34 typedef CppAD::AD<double> ADDouble;
35
36 using fourdst::config::Config;
37 using fourdst::logging::LogManager;
38 using fourdst::constant::Constants;
39
47 static constexpr double MIN_DENSITY_THRESHOLD = 1e-18;
48
56 static constexpr double MIN_ABUNDANCE_THRESHOLD = 1e-18;
57
64 static constexpr double MIN_JACOBIAN_THRESHOLD = 1e-24;
65
66
90 class GraphEngine final : public DynamicEngine{
91 public:
102 explicit GraphEngine(const fourdst::composition::Composition &composition);
103
112 explicit GraphEngine(const reaction::LogicalReactionSet &reactions);
113
128 const std::vector<double>& Y,
129 const double T9,
130 const double rho
131 ) const override;
132
147 const std::vector<double>& Y,
148 const double T9,
149 const double rho
150 ) override;
151
158 void generateStoichiometryMatrix() override;
159
172 [[nodiscard]] double calculateMolarReactionFlow(
174 const std::vector<double>&Y,
175 const double T9,
176 const double rho
177 ) const override;
178
183 [[nodiscard]] const std::vector<fourdst::atomic::Species>& getNetworkSpecies() const override;
184
189 [[nodiscard]] const reaction::LogicalReactionSet& getNetworkReactions() const override;
190
202 [[nodiscard]] double getJacobianMatrixEntry(
203 const int i,
204 const int j
205 ) const override;
206
213 [[nodiscard]] static std::unordered_map<fourdst::atomic::Species, int> getNetReactionStoichiometry(
215 );
216
228 [[nodiscard]] int getStoichiometryMatrixEntry(
229 const int speciesIndex,
230 const int reactionIndex
231 ) const override;
232
244 [[nodiscard]] std::unordered_map<fourdst::atomic::Species, double> getSpeciesTimescales(
245 const std::vector<double>& Y,
246 double T9,
247 double rho
248 ) const override;
249
250 void update(const NetIn& netIn) override;
251
258 [[nodiscard]] bool involvesSpecies(
259 const fourdst::atomic::Species& species
260 ) const;
261
278 void exportToDot(
279 const std::string& filename
280 ) const;
281
298 void exportToCSV(
299 const std::string& filename
300 ) const;
301
303
304 [[nodiscard]] screening::ScreeningType getScreeningModel() const override;
305
306 void setPrecomputation(bool precompute);
307
308 [[nodiscard]] bool isPrecomputationEnabled() const;
309
310 private:
313 std::vector<size_t> unique_reactant_indices;
314 std::vector<int> reactant_powers;
316 std::vector<size_t> affected_species_indices;
318 };
319
320 struct constants {
321 const double u = Constants::getInstance().get("u").value;
322 const double Na = Constants::getInstance().get("N_a").value;
323 const double c = Constants::getInstance().get("c").value;
324 };
325
326 private:
327 Config& m_config = Config::getInstance();
328 quill::Logger* m_logger = LogManager::getInstance().getLogger("log");
329
331
333 std::unordered_map<std::string_view, reaction::Reaction*> m_reactionIDMap;
334
335 std::vector<fourdst::atomic::Species> m_networkSpecies;
336 std::unordered_map<std::string_view, fourdst::atomic::Species> m_networkSpeciesMap;
337 std::unordered_map<fourdst::atomic::Species, size_t> m_speciesToIndexMap;
338
339 boost::numeric::ublas::compressed_matrix<int> m_stoichiometryMatrix;
340 boost::numeric::ublas::compressed_matrix<double> m_jacobianMatrix;
341
342 CppAD::ADFun<double> m_rhsADFun;
343
345 std::unique_ptr<screening::ScreeningModel> m_screeningModel = screening::selectScreeningModel(m_screeningType);
346
348
349 std::vector<PrecomputedReaction> m_precomputedReactions;
350
351 private:
359 void syncInternalMaps();
360
368
376
384
393
403 void recordADTape();
404
405 void precomputeNetwork();
406
416 [[nodiscard]] bool validateConservation() const;
417
430 const fourdst::composition::Composition &composition,
431 double culling,
432 double T9
433 );
434
436 const std::vector<double> &Y_in,
437 const std::vector<double>& bare_rates,
438 double T9,
439 double rho
440 ) const;
441
455 template <IsArithmeticOrAD T>
458 const std::vector<T> &Y,
459 const T T9,
460 const T rho
461 ) const;
462
475 template<IsArithmeticOrAD T>
477 const std::vector<T> &Y_in,
478 T T9,
479 T rho
480 ) const;
481
495 const std::vector<double>& Y_in,
496 const double T9,
497 const double rho
498 ) const;
499
513 const std::vector<ADDouble>& Y_in,
514 const ADDouble &T9,
515 const ADDouble &rho
516 ) const;
517 };
518
519
520 template<IsArithmeticOrAD T>
522 const std::vector<T> &Y_in, T T9, T rho) const {
523 std::vector<T> screeningFactors = m_screeningModel->calculateScreeningFactors(
526 Y_in,
527 T9,
528 rho
529 );
530
531 // --- Setup output derivatives structure ---
532 StepDerivatives<T> result;
533 result.dydt.resize(m_networkSpecies.size(), static_cast<T>(0.0));
534
535 // --- AD Pre-setup (flags to control conditionals in an AD safe / branch aware manner) ---
536 // ----- Constants for AD safe calculations ---
537 const T zero = static_cast<T>(0.0);
538 const T one = static_cast<T>(1.0);
539
540 // ----- Initialize variables for molar concentration product and thresholds ---
541 // Note: the logic here is that we use CppAD::CondExprLt to test thresholds and if they are less we set the flag
542 // to zero so that the final returned reaction flow is 0. This is as opposed to standard if statements
543 // which create branches that break the AD tape.
544 const T rho_threshold = static_cast<T>(MIN_DENSITY_THRESHOLD);
545
546 // --- Check if the density is below the threshold where we ignore reactions ---
547 T threshold_flag = CppAD::CondExpLt(rho, rho_threshold, zero, one); // If rho < threshold, set flag to 0
548
549 std::vector<T> Y = Y_in;
550 for (size_t i = 0; i < m_networkSpecies.size(); ++i) {
551 // We use CppAD::CondExpLt to handle AD taping and prevent branching
552 // Note that while this is syntactically more complex this is equivalent to
553 // if (Y[i] < 0) {Y[i] = 0;}
554 // The issue is that this would introduce a branch which would require the auto diff tape to be re-recorded
555 // each timestep, which is very inefficient.
556 Y[i] = CppAD::CondExpLt(Y[i], zero, zero, Y[i]); // Ensure no negative abundances
557 }
558
559 const T u = static_cast<T>(m_constants.u); // Atomic mass unit in grams
560 const T N_A = static_cast<T>(m_constants.Na); // Avogadro's number in mol^-1
561 const T c = static_cast<T>(m_constants.c); // Speed of light in cm/s
562
563 // --- SINGLE LOOP OVER ALL REACTIONS ---
564 for (size_t reactionIndex = 0; reactionIndex < m_reactions.size(); ++reactionIndex) {
565 const auto& reaction = m_reactions[reactionIndex];
566
567 // 1. Calculate reaction rate
568 const T molarReactionFlow = screeningFactors[reactionIndex] * calculateMolarReactionFlow<T>(reaction, Y, T9, rho);
569
570 // 2. Use the rate to update all relevant species derivatives (dY/dt)
571 for (size_t speciesIndex = 0; speciesIndex < m_networkSpecies.size(); ++speciesIndex) {
572 const T nu_ij = static_cast<T>(m_stoichiometryMatrix(speciesIndex, reactionIndex));
573 result.dydt[speciesIndex] += threshold_flag * nu_ij * molarReactionFlow / rho;
574 }
575 }
576
577 T massProductionRate = static_cast<T>(0.0); // [mol][s^-1]
578 for (const auto& [species, index] : m_speciesToIndexMap) {
579 massProductionRate += result.dydt[index] * species.mass() * u;
580 }
581
582 result.nuclearEnergyGenerationRate = -massProductionRate * N_A * c * c; // [cm^2][s^-3] = [erg][s^-1][g^-1]
583
584 return result;
585 }
586
587
588 template <IsArithmeticOrAD T>
591 const std::vector<T> &Y,
592 const T T9,
593 const T rho
594 ) const {
595
596 // --- Pre-setup (flags to control conditionals in an AD safe / branch aware manner) ---
597 // ----- Constants for AD safe calculations ---
598 const T zero = static_cast<T>(0.0);
599 const T one = static_cast<T>(1.0);
600
601 // ----- Initialize variables for molar concentration product and thresholds ---
602 // Note: the logic here is that we use CppAD::CondExprLt to test thresholds and if they are less we set the flag
603 // to zero so that the final returned reaction flow is 0. This is as opposed to standard if statements
604 // which create branches that break the AD tape.
605 const T Y_threshold = static_cast<T>(MIN_ABUNDANCE_THRESHOLD);
606 T threshold_flag = one;
607
608 // --- Calculate the molar reaction rate (in units of [s^-1][cm^3(N-1)][mol^(1-N)] for N reactants) ---
609 const T k_reaction = reaction.calculate_rate(T9);
610
611 // --- Cound the number of each reactant species to account for species multiplicity ---
612 std::unordered_map<std::string, int> reactant_counts;
613 reactant_counts.reserve(reaction.reactants().size());
614 for (const auto& reactant : reaction.reactants()) {
615 reactant_counts[std::string(reactant.name())]++;
616 }
617
618 // --- Accumulator for the molar concentration ---
619 auto molar_concentration_product = static_cast<T>(1.0);
620
621 // --- Loop through each unique reactant species and calculate the molar concentration for that species then multiply that into the accumulator ---
622 for (const auto& [species_name, count] : reactant_counts) {
623 // --- Resolve species to molar abundance ---
624 // PERF: Could probably optimize out this lookup
625 const auto species_it = m_speciesToIndexMap.find(m_networkSpeciesMap.at(species_name));
626 const size_t species_index = species_it->second;
627 const T Yi = Y[species_index];
628
629 // --- Check if the species abundance is below the threshold where we ignore reactions ---
630 threshold_flag *= CppAD::CondExpLt(Yi, Y_threshold, zero, one);
631
632 // --- Convert from molar abundance to molar concentration ---
633 T molar_concentration = Yi * rho;
634
635 // --- If count is > 1 , we need to raise the molar concentration to the power of count since there are really count bodies in that reaction ---
636 molar_concentration_product *= CppAD::pow(molar_concentration, static_cast<T>(count)); // ni^count
637
638 // --- Apply factorial correction for identical reactions ---
639 if (count > 1) {
640 molar_concentration_product /= static_cast<T>(std::tgamma(static_cast<double>(count + 1))); // Gamma function for factorial
641 }
642 }
643 // --- Final reaction flow calculation [mol][s^-1][cm^-3] ---
644 // Note: If the threshold flag ever gets set to zero this will return zero.
645 // This will result basically in multiple branches being written to the AD tape, which will make
646 // the tape more expensive to record, but it will also mean that we only need to record it once for
647 // the entire network.
648 return molar_concentration_product * k_reaction * threshold_flag;
649 }
650};
Abstract class for engines supporting Jacobian and stoichiometry operations.
bool isPrecomputationEnabled() const
const std::vector< fourdst::atomic::Species > & getNetworkSpecies() const override
Gets the list of species in the network.
bool m_usePrecomputation
Flag to enable or disable using precomputed reactions for efficiency. Mathematically,...
std::unordered_map< fourdst::atomic::Species, double > getSpeciesTimescales(const std::vector< double > &Y, double T9, double rho) const override
Computes timescales for all species in the network.
void populateReactionIDMap()
Populates the reaction ID map.
CppAD::ADFun< double > m_rhsADFun
CppAD function for the right-hand side of the ODE.
boost::numeric::ublas::compressed_matrix< double > m_jacobianMatrix
Jacobian matrix (species x species).
double getJacobianMatrixEntry(const int i, const int j) const override
Gets an entry from the previously generated Jacobian matrix.
std::unordered_map< std::string_view, fourdst::atomic::Species > m_networkSpeciesMap
Map from species name to Species object.
void populateSpeciesToIndexMap()
Populates the species-to-index map.
quill::Logger * m_logger
void update(const NetIn &netIn) override
Update the internal state of the engine.
screening::ScreeningType m_screeningType
Screening type for the reaction network. Default to no screening.
void reserveJacobianMatrix()
Reserves space for the Jacobian matrix.
std::vector< PrecomputedReaction > m_precomputedReactions
Precomputed reactions for efficiency.
std::unordered_map< std::string_view, reaction::Reaction * > m_reactionIDMap
Map from reaction ID to REACLIBReaction. //PERF: This makes copies of REACLIBReaction and could be a ...
StepDerivatives< double > calculateAllDerivativesUsingPrecomputation(const std::vector< double > &Y_in, const std::vector< double > &bare_rates, double T9, double rho) const
screening::ScreeningType getScreeningModel() const override
Get the current electron screening model.
int getStoichiometryMatrixEntry(const int speciesIndex, const int reactionIndex) const override
Gets an entry from the stoichiometry matrix.
void setPrecomputation(bool precompute)
void setScreeningModel(screening::ScreeningType) override
Set the electron screening model.
void exportToCSV(const std::string &filename) const
Exports the network to a CSV file for analysis.
StepDerivatives< double > calculateRHSAndEnergy(const std::vector< double > &Y, const double T9, const double rho) const override
Calculates the right-hand side (dY/dt) and energy generation rate.
static std::unordered_map< fourdst::atomic::Species, int > getNetReactionStoichiometry(const reaction::Reaction &reaction)
Gets the net stoichiometry for a given reaction.
double calculateMolarReactionFlow(const reaction::Reaction &reaction, const std::vector< double > &Y, const double T9, const double rho) const override
Calculates the molar reaction flow for a given reaction.
std::vector< fourdst::atomic::Species > m_networkSpecies
Vector of unique species in the network.
void recordADTape()
Records the AD tape for the right-hand side of the ODE.
GraphEngine(const fourdst::composition::Composition &composition)
Constructs a GraphEngine from a composition.
bool involvesSpecies(const fourdst::atomic::Species &species) const
Checks if a given species is involved in the network.
reaction::LogicalReactionSet m_reactions
Set of REACLIB reactions in the network.
void syncInternalMaps()
Synchronizes the internal maps.
bool validateConservation() const
Validates mass and charge conservation across all reactions.
boost::numeric::ublas::compressed_matrix< int > m_stoichiometryMatrix
Stoichiometry matrix (species x reactions).
const reaction::LogicalReactionSet & getNetworkReactions() const override
Gets the set of logical reactions in the network.
std::unordered_map< fourdst::atomic::Species, size_t > m_speciesToIndexMap
Map from species to their index in the stoichiometry matrix.
void exportToDot(const std::string &filename) const
Exports the network to a DOT file for visualization.
void generateJacobianMatrix(const std::vector< double > &Y, const double T9, const double rho) override
Generates the Jacobian matrix for the current state.
void generateStoichiometryMatrix() override
Generates the stoichiometry matrix for the network.
void collectNetworkSpecies()
Collects the unique species in the network.
void validateComposition(const fourdst::composition::Composition &composition, double culling, double T9)
Validates the composition against the current reaction set.
std::unique_ptr< screening::ScreeningModel > m_screeningModel
StepDerivatives< T > calculateAllDerivatives(const std::vector< T > &Y_in, T T9, T rho) const
Calculates all derivatives (dY/dt) and the energy generation rate.
Represents a single nuclear reaction from a specific data source.
Definition reaction.h:72
Abstract interfaces for reaction network engines in GridFire.
TemplatedReactionSet< LogicalReaction > LogicalReactionSet
A set of logical reactions.
Definition reaction.h:557
std::unique_ptr< ScreeningModel > selectScreeningModel(ScreeningType type)
A factory function to select and create a screening model.
ScreeningType
Enumerates the available plasma screening models.
@ BARE
No screening applied. The screening factor is always 1.0.
CppAD::AD< double > ADDouble
Alias for CppAD AD type for double precision.
static constexpr double MIN_ABUNDANCE_THRESHOLD
Minimum abundance threshold below which species are ignored.
static constexpr double MIN_DENSITY_THRESHOLD
Minimum density threshold below which reactions are ignored.
static constexpr double MIN_JACOBIAN_THRESHOLD
Minimum value for Jacobian matrix entries.
Defines classes for representing and managing nuclear reactions.
const double u
Atomic mass unit in g.
const double Na
Avogadro's number.
const double c
Speed of light in cm/s.
Structure holding derivatives and energy generation for a network step.
T nuclearEnergyGenerationRate
Specific energy generation rate (e.g., erg/g/s).
std::vector< T > dydt
Derivatives of abundances (dY/dt for each species).