6#include "fourdst/composition/species.h"
7#include "fourdst/composition/atomicSpecies.h"
9#include "quill/LogMacros.h"
17#include <unordered_map>
22#include <boost/numeric/odeint.hpp>
27 const fourdst::composition::Composition &composition
39 const std::vector<double> &Y,
61 std::set<std::string_view> uniqueSpeciesNames;
64 for (
const auto& reactant:
reaction.reactants()) {
65 uniqueSpeciesNames.insert(reactant.name());
67 for (
const auto& product:
reaction.products()) {
68 uniqueSpeciesNames.insert(product.name());
72 for (
const auto& name: uniqueSpeciesNames) {
73 auto it = fourdst::atomic::species.find(std::string(name));
74 if (it != fourdst::atomic::species.end()) {
78 LOG_ERROR(
m_logger,
"Species '{}' not found in global atomic species database.", name);
80 throw std::runtime_error(
"Species not found in global atomic species database: " + std::string(name));
87 LOG_TRACE_L1(
m_logger,
"Populating reaction ID map for REACLIB graph network (serif::network::GraphNetwork)...");
108 LOG_TRACE_L2(
m_logger,
"Jacobian matrix resized to {} rows and {} columns.",
121 LOG_TRACE_L3(
m_logger,
"Providing access to network reactions set. Size: {}.",
m_reactions.size());
128 LOG_DEBUG(
m_logger,
"Checking if species '{}' is involved in the network: {}.", species.name(), found ?
"Yes" :
"No");
134 LOG_TRACE_L1(
m_logger,
"Validating mass (A) and charge (Z) conservation across all reactions in the network.");
137 uint64_t totalReactantA = 0;
138 uint64_t totalReactantZ = 0;
139 uint64_t totalProductA = 0;
140 uint64_t totalProductZ = 0;
143 for (
const auto& reactant :
reaction.reactants()) {
146 totalReactantA += it->second.a();
147 totalReactantZ += it->second.z();
151 LOG_ERROR(
m_logger,
"CRITICAL ERROR: Reactant species '{}' in reaction '{}' not found in network species map during conservation validation.",
158 for (
const auto& product :
reaction.products()) {
161 totalProductA += it->second.a();
162 totalProductZ += it->second.z();
165 LOG_ERROR(
m_logger,
"CRITICAL ERROR: Product species '{}' in reaction '{}' not found in network species map during conservation validation.",
172 if (totalReactantA != totalProductA) {
173 LOG_ERROR(
m_logger,
"Mass number (A) not conserved for reaction '{}': Reactants A={} vs Products A={}.",
174 reaction.id(), totalReactantA, totalProductA);
177 if (totalReactantZ != totalProductZ) {
178 LOG_ERROR(
m_logger,
"Atomic number (Z) not conserved for reaction '{}': Reactants Z={} vs Products Z={}.",
179 reaction.id(), totalReactantZ, totalProductZ);
184 LOG_TRACE_L1(
m_logger,
"Mass (A) and charge (Z) conservation validated successfully for all reactions.");
202 LOG_DEBUG(
m_logger,
"Reaction set not cached. Rebuilding the reaction set for T9={} and culling={}.", T9, culling);
210 LOG_TRACE_L1(
m_logger,
"Generating stoichiometry matrix...");
217 LOG_TRACE_L1(
m_logger,
"Stoichiometry matrix initialized with dimensions: {} rows (species) x {} columns (reactions).",
218 numSpecies, numReactions);
222 size_t reactionColumnIndex = 0;
225 std::unordered_map<fourdst::atomic::Species, int> netStoichiometry =
reaction.stoichiometry();
228 for (
const auto& [species, coefficient] : netStoichiometry) {
232 const size_t speciesRowIndex = it->second;
237 LOG_ERROR(
m_logger,
"CRITICAL ERROR: Species '{}' from reaction '{}' stoichiometry not found in species to index map.",
240 throw std::runtime_error(
"Species not found in species to index map: " + std::string(species.name()));
243 reactionColumnIndex++;
246 LOG_TRACE_L1(
m_logger,
"Stoichiometry matrix population complete. Number of non-zero elements: {}.",
251 const std::vector<double> &Y_in,
259 const std::vector<ADDouble> &Y_in,
277 const std::vector<double> &Y,
285 const std::vector<double> &Y,
290 LOG_TRACE_L1(
m_logger,
"Generating jacobian matrix for T9={}, rho={}..", T9, rho);
294 std::vector<double> adInput(numSpecies + 2, 0.0);
295 for (
size_t i = 0; i < numSpecies; ++i) {
298 adInput[numSpecies] = T9;
299 adInput[numSpecies + 1] = rho;
302 const std::vector<double> dotY =
m_rhsADFun.Jacobian(adInput);
306 for (
size_t i = 0; i < numSpecies; ++i) {
307 for (
size_t j = 0; j < numSpecies; ++j) {
308 const double value = dotY[i * (numSpecies + 2) + j];
328 const int speciesIndex,
329 const int reactionIndex
335 LOG_TRACE_L1(
m_logger,
"Exporting network graph to DOT file: {}", filename);
337 std::ofstream dotFile(filename);
338 if (!dotFile.is_open()) {
339 LOG_ERROR(
m_logger,
"Failed to open file for writing: {}", filename);
341 throw std::runtime_error(
"Failed to open file for writing: " + filename);
344 dotFile <<
"digraph NuclearReactionNetwork {\n";
345 dotFile <<
" graph [rankdir=LR, splines=true, overlap=false, bgcolor=\"#f0f0f0\"];\n";
346 dotFile <<
" node [shape=circle, style=filled, fillcolor=\"#a7c7e7\", fontname=\"Helvetica\"];\n";
347 dotFile <<
" edge [fontname=\"Helvetica\", fontsize=10];\n\n";
350 dotFile <<
" // --- Species Nodes ---\n";
352 dotFile <<
" \"" << species.name() <<
"\" [label=\"" << species.name() <<
"\"];\n";
357 dotFile <<
" // --- Reaction Edges ---\n";
360 std::string reactionNodeId =
"reaction_" + std::string(
reaction.id());
363 dotFile <<
" \"" << reactionNodeId <<
"\" [shape=point, fillcolor=black, width=0.1, height=0.1, label=\"\"];\n";
366 for (
const auto& reactant :
reaction.reactants()) {
367 dotFile <<
" \"" << reactant.name() <<
"\" -> \"" << reactionNodeId <<
"\";\n";
371 for (
const auto& product :
reaction.products()) {
372 dotFile <<
" \"" << reactionNodeId <<
"\" -> \"" << product.name() <<
"\" [label=\"" <<
reaction.qValue() <<
" MeV\"];\n";
379 LOG_TRACE_L1(
m_logger,
"Successfully exported network to {}", filename);
383 LOG_TRACE_L1(
m_logger,
"Exporting network graph to CSV file: {}", filename);
385 std::ofstream csvFile(filename, std::ios::out | std::ios::trunc);
386 if (!csvFile.is_open()) {
387 LOG_ERROR(
m_logger,
"Failed to open file for writing: {}", filename);
389 throw std::runtime_error(
"Failed to open file for writing: " + filename);
391 csvFile <<
"Reaction;Reactants;Products;Q-value;sources;rates\n";
397 for (
const auto& reactant :
reaction.reactants()) {
398 csvFile << reactant.name();
399 if (++count <
reaction.reactants().size()) {
405 for (
const auto& product :
reaction.products()) {
406 csvFile << product.name();
407 if (++count <
reaction.products().size()) {
411 csvFile <<
";" <<
reaction.qValue() <<
";";
415 for (
const auto& source : sources) {
417 if (++count < sources.size()) {
424 for (
const auto& rates :
reaction) {
433 LOG_TRACE_L1(
m_logger,
"Successfully exported network graph to {}", filename);
437 const double rho)
const {
439 std::unordered_map<fourdst::atomic::Species, double> speciesTimescales;
442 double timescale = std::numeric_limits<double>::infinity();
444 if (std::abs(dydt[i]) > 0.0) {
445 timescale = std::abs(Y[i] / dydt[i]);
447 speciesTimescales.emplace(species, timescale);
449 return speciesTimescales;
457 LOG_TRACE_L1(
m_logger,
"Recording AD tape for the RHS calculation...");
461 if (numSpecies == 0) {
462 LOG_ERROR(
m_logger,
"Cannot record AD tape: No species in the network.");
464 throw std::runtime_error(
"Cannot record AD tape: No species in the network.");
466 const size_t numADInputs = numSpecies + 2;
474 const auto uniformMassFraction =
static_cast<CppAD::AD<double>
>(1.0 / numSpecies);
475 std::vector<CppAD::AD<double>> adInput(numADInputs, uniformMassFraction);
476 adInput[numSpecies] = 1.0;
477 adInput[numSpecies + 1] = 1.0;
481 CppAD::Independent(adInput);
483 std::vector<CppAD::AD<double>> adY(numSpecies);
484 for(
size_t i = 0; i < numSpecies; ++i) {
487 const CppAD::AD<double> adT9 = adInput[numSpecies];
488 const CppAD::AD<double> adRho = adInput[numSpecies + 1];
497 LOG_TRACE_L1(
m_logger,
"AD tape recorded successfully for the RHS calculation. Number of independent variables: {}.",
const std::vector< fourdst::atomic::Species > & getNetworkSpecies() const override
Gets the list of species in the network.
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.
void update(const NetIn &netIn) override
screening::ScreeningType m_screeningType
Screening type for the reaction network. Default to no screening.
void reserveJacobianMatrix()
Reserves space for the Jacobian matrix.
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 ...
screening::ScreeningType getScreeningModel() const override
int getStoichiometryMatrixEntry(const int speciesIndex, const int reactionIndex) const override
Gets an entry from the stoichiometry matrix.
void setScreeningModel(screening::ScreeningType) override
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.
TemplatedReactionSet< LogicalReaction > LogicalReactionSet
A set of logical reactions.
std::unique_ptr< ScreeningModel > selectScreeningModel(ScreeningType type)
CppAD::AD< double > ADDouble
Alias for CppAD AD type for double precision.
reaction::LogicalReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition, bool reverse)
static constexpr double MIN_JACOBIAN_THRESHOLD
Minimum value for Jacobian matrix entries.
Defines classes for representing and managing nuclear reactions.
Structure holding derivatives and energy generation for a network step.