GridFire 0.0.1a
General Purpose Nuclear Network
Loading...
Searching...
No Matches
engine_graph.cpp
Go to the documentation of this file.
3#include "gridfire/network.h"
5
6#include "fourdst/composition/species.h"
7#include "fourdst/composition/atomicSpecies.h"
8
9#include "quill/LogMacros.h"
10
11#include <cstdint>
12#include <iostream>
13#include <set>
14#include <stdexcept>
15#include <string>
16#include <string_view>
17#include <unordered_map>
18#include <utility>
19#include <vector>
20#include <fstream>
21
22#include <boost/numeric/odeint.hpp>
23
24
25namespace gridfire {
27 const fourdst::composition::Composition &composition
28 ):
29 m_reactions(build_reaclib_nuclear_network(composition, false)) {
32 }
33
35 const reaction::LogicalReactionSet &reactions
36 ) :
37 m_reactions(reactions) {
40 }
41
43 const std::vector<double> &Y,
44 const double T9,
45 const double rho
46 ) const {
48 std::vector<double> bare_rates;
49 bare_rates.reserve(m_reactions.size());
50 for (const auto& reaction: m_reactions) {
51 bare_rates.push_back(reaction.calculate_rate(T9));
52 }
53
54 // --- The public facing interface can always use the precomputed version since taping is done internally ---
55 return calculateAllDerivativesUsingPrecomputation(Y, bare_rates, T9, rho);
56 } else {
57 return calculateAllDerivatives<double>(Y, T9, rho);
58 }
59 }
60
61
70
71 // --- Network Graph Construction Methods ---
73 m_networkSpecies.clear();
74 m_networkSpeciesMap.clear();
75
76 std::set<std::string_view> uniqueSpeciesNames;
77
78 for (const auto& reaction: m_reactions) {
79 for (const auto& reactant: reaction.reactants()) {
80 uniqueSpeciesNames.insert(reactant.name());
81 }
82 for (const auto& product: reaction.products()) {
83 uniqueSpeciesNames.insert(product.name());
84 }
85 }
86
87 for (const auto& name: uniqueSpeciesNames) {
88 auto it = fourdst::atomic::species.find(std::string(name));
89 if (it != fourdst::atomic::species.end()) {
90 m_networkSpecies.push_back(it->second);
91 m_networkSpeciesMap.insert({name, it->second});
92 } else {
93 LOG_ERROR(m_logger, "Species '{}' not found in global atomic species database.", name);
94 m_logger->flush_log();
95 throw std::runtime_error("Species not found in global atomic species database: " + std::string(name));
96 }
97 }
98
99 }
100
102 LOG_TRACE_L1(m_logger, "Populating reaction ID map for REACLIB graph network (serif::network::GraphNetwork)...");
103 m_reactionIDMap.clear();
104 for (auto& reaction: m_reactions) {
105 m_reactionIDMap.emplace(reaction.id(), &reaction);
106 }
107 LOG_TRACE_L1(m_logger, "Populated {} reactions in the reaction ID map.", m_reactionIDMap.size());
108 }
109
111 m_speciesToIndexMap.clear();
112 for (size_t i = 0; i < m_networkSpecies.size(); ++i) {
114 }
115 }
116
118 // The implementation of this function (and others) constrains this nuclear network to a constant temperature and density during
119 // each evaluation.
120 size_t numSpecies = m_networkSpecies.size();
121 m_jacobianMatrix.clear();
122 m_jacobianMatrix.resize(numSpecies, numSpecies, false); // Sparse matrix, no initial values
123 LOG_TRACE_L2(m_logger, "Jacobian matrix resized to {} rows and {} columns.",
124 m_jacobianMatrix.size1(), m_jacobianMatrix.size2());
125 }
126
127 // --- Basic Accessors and Queries ---
128 const std::vector<fourdst::atomic::Species>& GraphEngine::getNetworkSpecies() const {
129 // Returns a constant reference to the vector of unique species in the network.
130 LOG_TRACE_L3(m_logger, "Providing access to network species vector. Size: {}.", m_networkSpecies.size());
131 return m_networkSpecies;
132 }
133
135 // Returns a constant reference to the set of reactions in the network.
136 LOG_TRACE_L3(m_logger, "Providing access to network reactions set. Size: {}.", m_reactions.size());
137 return m_reactions;
138 }
139
140 bool GraphEngine::involvesSpecies(const fourdst::atomic::Species& species) const {
141 // Checks if a given species is present in the network's species map for efficient lookup.
142 const bool found = m_networkSpeciesMap.contains(species.name());
143 LOG_DEBUG(m_logger, "Checking if species '{}' is involved in the network: {}.", species.name(), found ? "Yes" : "No");
144 return found;
145 }
146
147 // --- Validation Methods ---
149 LOG_TRACE_L1(m_logger, "Validating mass (A) and charge (Z) conservation across all reactions in the network.");
150
151 for (const auto& reaction : m_reactions) {
152 uint64_t totalReactantA = 0;
153 uint64_t totalReactantZ = 0;
154 uint64_t totalProductA = 0;
155 uint64_t totalProductZ = 0;
156
157 // Calculate total A and Z for reactants
158 for (const auto& reactant : reaction.reactants()) {
159 auto it = m_networkSpeciesMap.find(reactant.name());
160 if (it != m_networkSpeciesMap.end()) {
161 totalReactantA += it->second.a();
162 totalReactantZ += it->second.z();
163 } else {
164 // This scenario indicates a severe data integrity issue:
165 // a reactant is part of a reaction but not in the network's species map.
166 LOG_ERROR(m_logger, "CRITICAL ERROR: Reactant species '{}' in reaction '{}' not found in network species map during conservation validation.",
167 reactant.name(), reaction.id());
168 return false;
169 }
170 }
171
172 // Calculate total A and Z for products
173 for (const auto& product : reaction.products()) {
174 auto it = m_networkSpeciesMap.find(product.name());
175 if (it != m_networkSpeciesMap.end()) {
176 totalProductA += it->second.a();
177 totalProductZ += it->second.z();
178 } else {
179 // Similar critical error for product species
180 LOG_ERROR(m_logger, "CRITICAL ERROR: Product species '{}' in reaction '{}' not found in network species map during conservation validation.",
181 product.name(), reaction.id());
182 return false;
183 }
184 }
185
186 // Compare totals for conservation
187 if (totalReactantA != totalProductA) {
188 LOG_ERROR(m_logger, "Mass number (A) not conserved for reaction '{}': Reactants A={} vs Products A={}.",
189 reaction.id(), totalReactantA, totalProductA);
190 return false;
191 }
192 if (totalReactantZ != totalProductZ) {
193 LOG_ERROR(m_logger, "Atomic number (Z) not conserved for reaction '{}': Reactants Z={} vs Products Z={}.",
194 reaction.id(), totalReactantZ, totalProductZ);
195 return false;
196 }
197 }
198
199 LOG_TRACE_L1(m_logger, "Mass (A) and charge (Z) conservation validated successfully for all reactions.");
200 return true; // All reactions passed the conservation check
201 }
202
203 void GraphEngine::validateComposition(const fourdst::composition::Composition &composition, double culling, double T9) {
204 // Check if the requested network has already been cached.
205 // PERF: Rebuilding this should be pretty fast but it may be a good point of optimization in the future.
206 const reaction::LogicalReactionSet validationReactionSet = build_reaclib_nuclear_network(composition, false);
207 // TODO: need some more robust method here to
208 // A. Build the basic network from the composition's species with non zero mass fractions.
209 // B. rebuild a new composition from the reaction set's reactants + products (with the mass fractions from the things that are only products set to 0)
210 // C. Rebuild the reaction set from the new composition
211 // D. Cull reactions where all reactants have mass fractions below the culling threshold.
212 // E. Be careful about maintaining caching through all of this
213
214
215 // This allows for dynamic network modification while retaining caching for networks which are very similar.
216 if (validationReactionSet != m_reactions) {
217 LOG_DEBUG(m_logger, "Reaction set not cached. Rebuilding the reaction set for T9={} and culling={}.", T9, culling);
218 m_reactions = validationReactionSet;
219 syncInternalMaps(); // Re-sync internal maps after updating reactions. Note this will also retrace the AD tape.
220 }
221 }
222
224 const std::vector<double> &Y_in,
225 const std::vector<double> &bare_rates,
226 const double T9,
227 const double rho
228 ) const {
229 // --- Calculate screening factors ---
230 const std::vector<double> screeningFactors = m_screeningModel->calculateScreeningFactors(
233 Y_in,
234 T9,
235 rho
236 );
237
238 // --- Optimized loop ---
239 std::vector<double> molarReactionFlows;
240 molarReactionFlows.reserve(m_precomputedReactions.size());
241
242 for (const auto& precomp : m_precomputedReactions) {
243 double abundanceProduct = 1.0;
244 bool below_threshold = false;
245 for (size_t i = 0; i < precomp.unique_reactant_indices.size(); ++i) {
246 const size_t reactantIndex = precomp.unique_reactant_indices[i];
247 const int power = precomp.reactant_powers[i];
248 const double abundance = Y_in[reactantIndex];
249 if (abundance < MIN_ABUNDANCE_THRESHOLD) {
250 below_threshold = true;
251 break;
252 }
253
254 abundanceProduct *= std::pow(Y_in[reactantIndex], power);
255 }
256 if (below_threshold) {
257 molarReactionFlows.push_back(0.0);
258 continue; // Skip this reaction if any reactant is below the abundance threshold
259 }
260
261 const double bare_rate = bare_rates[precomp.reaction_index];
262 const double screeningFactor = screeningFactors[precomp.reaction_index];
263 const size_t numReactants = m_reactions[precomp.reaction_index].reactants().size();
264
265 const double molarReactionFlow =
266 screeningFactor *
267 bare_rate *
268 precomp.symmetry_factor *
269 abundanceProduct *
270 std::pow(rho, numReactants);
271
272 molarReactionFlows.push_back(molarReactionFlow);
273 }
274
275 // --- Assemble molar abundance derivatives ---
277 result.dydt.assign(m_networkSpecies.size(), 0.0); // Initialize derivatives to zero
278 for (size_t j = 0; j < m_precomputedReactions.size(); ++j) {
279 const auto& precomp = m_precomputedReactions[j];
280 const double R_j = molarReactionFlows[j];
281
282 for (size_t i = 0; i < precomp.affected_species_indices.size(); ++i) {
283 const size_t speciesIndex = precomp.affected_species_indices[i];
284 const int stoichiometricCoefficient = precomp.stoichiometric_coefficients[i];
285
286 // Update the derivative for this species
287 result.dydt[speciesIndex] += static_cast<double>(stoichiometricCoefficient) * R_j / rho;
288 }
289 }
290
291 // --- Calculate the nuclear energy generation rate ---
292 double massProductionRate = 0.0; // [mol][s^-1]
293 for (size_t i = 0; i < m_networkSpecies.size(); ++i) {
294 const auto& species = m_networkSpecies[i];
295 massProductionRate += result.dydt[i] * species.mass() * m_constants.u;
296 }
297 result.nuclearEnergyGenerationRate = -massProductionRate * m_constants.Na * m_constants.c * m_constants.c; // [erg][s^-1][g^-1]
298 return result;
299
300 }
301
302 // --- Generate Stoichiometry Matrix ---
304 LOG_TRACE_L1(m_logger, "Generating stoichiometry matrix...");
305
306 // Task 1: Set dimensions and initialize the matrix
307 size_t numSpecies = m_networkSpecies.size();
308 size_t numReactions = m_reactions.size();
309 m_stoichiometryMatrix.resize(numSpecies, numReactions, false);
310
311 LOG_TRACE_L1(m_logger, "Stoichiometry matrix initialized with dimensions: {} rows (species) x {} columns (reactions).",
312 numSpecies, numReactions);
313
314 // Task 2: Populate the stoichiometry matrix
315 // Iterate through all reactions, assign them a column index, and fill in their stoichiometric coefficients.
316 size_t reactionColumnIndex = 0;
317 for (const auto& reaction : m_reactions) {
318 // Get the net stoichiometry for the current reaction
319 std::unordered_map<fourdst::atomic::Species, int> netStoichiometry = reaction.stoichiometry();
320
321 // Iterate through the species and their coefficients in the stoichiometry map
322 for (const auto& [species, coefficient] : netStoichiometry) {
323 // Find the row index for this species
324 auto it = m_speciesToIndexMap.find(species);
325 if (it != m_speciesToIndexMap.end()) {
326 const size_t speciesRowIndex = it->second;
327 // Set the matrix element. Boost.uBLAS handles sparse insertion.
328 m_stoichiometryMatrix(speciesRowIndex, reactionColumnIndex) = coefficient;
329 } else {
330 // This scenario should ideally not happen if m_networkSpeciesMap and m_speciesToIndexMap are correctly synced
331 LOG_ERROR(m_logger, "CRITICAL ERROR: Species '{}' from reaction '{}' stoichiometry not found in species to index map.",
332 species.name(), reaction.id());
333 m_logger -> flush_log();
334 throw std::runtime_error("Species not found in species to index map: " + std::string(species.name()));
335 }
336 }
337 reactionColumnIndex++; // Move to the next column for the next reaction
338 }
339
340 LOG_TRACE_L1(m_logger, "Stoichiometry matrix population complete. Number of non-zero elements: {}.",
341 m_stoichiometryMatrix.nnz()); // Assuming nnz() exists for compressed_matrix
342 }
343
345 const std::vector<double> &Y_in,
346 const double T9,
347 const double rho
348 ) const {
349 return calculateAllDerivatives<double>(Y_in, T9, rho);
350 }
351
353 const std::vector<ADDouble> &Y_in,
354 const ADDouble &T9,
355 const ADDouble &rho
356 ) const {
357 return calculateAllDerivatives<ADDouble>(Y_in, T9, rho);
358 }
359
364
368
369 void GraphEngine::setPrecomputation(const bool precompute) {
370 m_usePrecomputation = precompute;
371 }
372
376
379 const std::vector<double> &Y,
380 const double T9,
381 const double rho
382 ) const {
384 }
385
387 const std::vector<double> &Y,
388 const double T9,
389 const double rho
390 ) {
391
392 LOG_TRACE_L1(m_logger, "Generating jacobian matrix for T9={}, rho={}..", T9, rho);
393 const size_t numSpecies = m_networkSpecies.size();
394
395 // 1. Pack the input variables into a vector for CppAD
396 std::vector<double> adInput(numSpecies + 2, 0.0); // +2 for T9 and rho
397 for (size_t i = 0; i < numSpecies; ++i) {
398 adInput[i] = Y[i];
399 }
400 adInput[numSpecies] = T9; // T9
401 adInput[numSpecies + 1] = rho; // rho
402
403 // 2. Calculate the full jacobian
404 const std::vector<double> dotY = m_rhsADFun.Jacobian(adInput);
405
406 // 3. Pack jacobian vector into sparse matrix
407 m_jacobianMatrix.clear();
408 for (size_t i = 0; i < numSpecies; ++i) {
409 for (size_t j = 0; j < numSpecies; ++j) {
410 const double value = dotY[i * (numSpecies + 2) + j];
411 if (std::abs(value) > MIN_JACOBIAN_THRESHOLD) {
412 m_jacobianMatrix(i, j) = value;
413 }
414 }
415 }
416 LOG_TRACE_L1(m_logger, "Jacobian matrix generated with dimensions: {} rows x {} columns.", m_jacobianMatrix.size1(), m_jacobianMatrix.size2());
417 }
418
419 double GraphEngine::getJacobianMatrixEntry(const int i, const int j) const {
420 return m_jacobianMatrix(i, j);
421 }
422
423 std::unordered_map<fourdst::atomic::Species, int> GraphEngine::getNetReactionStoichiometry(
425 ) {
426 return reaction.stoichiometry();
427 }
428
430 const int speciesIndex,
431 const int reactionIndex
432 ) const {
433 return m_stoichiometryMatrix(speciesIndex, reactionIndex);
434 }
435
436 void GraphEngine::exportToDot(const std::string &filename) const {
437 LOG_TRACE_L1(m_logger, "Exporting network graph to DOT file: {}", filename);
438
439 std::ofstream dotFile(filename);
440 if (!dotFile.is_open()) {
441 LOG_ERROR(m_logger, "Failed to open file for writing: {}", filename);
442 m_logger->flush_log();
443 throw std::runtime_error("Failed to open file for writing: " + filename);
444 }
445
446 dotFile << "digraph NuclearReactionNetwork {\n";
447 dotFile << " graph [rankdir=LR, splines=true, overlap=false, bgcolor=\"#f0f0f0\"];\n";
448 dotFile << " node [shape=circle, style=filled, fillcolor=\"#a7c7e7\", fontname=\"Helvetica\"];\n";
449 dotFile << " edge [fontname=\"Helvetica\", fontsize=10];\n\n";
450
451 // 1. Define all species as nodes
452 dotFile << " // --- Species Nodes ---\n";
453 for (const auto& species : m_networkSpecies) {
454 dotFile << " \"" << species.name() << "\" [label=\"" << species.name() << "\"];\n";
455 }
456 dotFile << "\n";
457
458 // 2. Define all reactions as intermediate nodes and connect them
459 dotFile << " // --- Reaction Edges ---\n";
460 for (const auto& reaction : m_reactions) {
461 // Create a unique ID for the reaction node
462 std::string reactionNodeId = "reaction_" + std::string(reaction.id());
463
464 // Define the reaction node (small, black dot)
465 dotFile << " \"" << reactionNodeId << "\" [shape=point, fillcolor=black, width=0.1, height=0.1, label=\"\"];\n";
466
467 // Draw edges from reactants to the reaction node
468 for (const auto& reactant : reaction.reactants()) {
469 dotFile << " \"" << reactant.name() << "\" -> \"" << reactionNodeId << "\";\n";
470 }
471
472 // Draw edges from the reaction node to products
473 for (const auto& product : reaction.products()) {
474 dotFile << " \"" << reactionNodeId << "\" -> \"" << product.name() << "\" [label=\"" << reaction.qValue() << " MeV\"];\n";
475 }
476 dotFile << "\n";
477 }
478
479 dotFile << "}\n";
480 dotFile.close();
481 LOG_TRACE_L1(m_logger, "Successfully exported network to {}", filename);
482 }
483
484 void GraphEngine::exportToCSV(const std::string &filename) const {
485 LOG_TRACE_L1(m_logger, "Exporting network graph to CSV file: {}", filename);
486
487 std::ofstream csvFile(filename, std::ios::out | std::ios::trunc);
488 if (!csvFile.is_open()) {
489 LOG_ERROR(m_logger, "Failed to open file for writing: {}", filename);
490 m_logger->flush_log();
491 throw std::runtime_error("Failed to open file for writing: " + filename);
492 }
493 csvFile << "Reaction;Reactants;Products;Q-value;sources;rates\n";
494 for (const auto& reaction : m_reactions) {
495 // Dynamic cast to REACLIBReaction to access specific properties
496 csvFile << reaction.id() << ";";
497 // Reactants
498 int count = 0;
499 for (const auto& reactant : reaction.reactants()) {
500 csvFile << reactant.name();
501 if (++count < reaction.reactants().size()) {
502 csvFile << ",";
503 }
504 }
505 csvFile << ";";
506 count = 0;
507 for (const auto& product : reaction.products()) {
508 csvFile << product.name();
509 if (++count < reaction.products().size()) {
510 csvFile << ",";
511 }
512 }
513 csvFile << ";" << reaction.qValue() << ";";
514 // Reaction coefficients
515 auto sources = reaction.sources();
516 count = 0;
517 for (const auto& source : sources) {
518 csvFile << source;
519 if (++count < sources.size()) {
520 csvFile << ",";
521 }
522 }
523 csvFile << ";";
524 // Reaction coefficients
525 count = 0;
526 for (const auto& rates : reaction) {
527 csvFile << rates;
528 if (++count < reaction.size()) {
529 csvFile << ",";
530 }
531 }
532 csvFile << "\n";
533 }
534 csvFile.close();
535 LOG_TRACE_L1(m_logger, "Successfully exported network graph to {}", filename);
536 }
537
538 std::unordered_map<fourdst::atomic::Species, double> GraphEngine::getSpeciesTimescales(const std::vector<double> &Y, const double T9,
539 const double rho) const {
540 auto [dydt, _] = calculateAllDerivatives<double>(Y, T9, rho);
541 std::unordered_map<fourdst::atomic::Species, double> speciesTimescales;
542 speciesTimescales.reserve(m_networkSpecies.size());
543 for (size_t i = 0; i < m_networkSpecies.size(); ++i) {
544 double timescale = std::numeric_limits<double>::infinity();
545 const auto species = m_networkSpecies[i];
546 if (std::abs(dydt[i]) > 0.0) {
547 timescale = std::abs(Y[i] / dydt[i]);
548 }
549 speciesTimescales.emplace(species, timescale);
550 }
551 return speciesTimescales;
552 }
553
554 void GraphEngine::update(const NetIn &netIn) {
555 // No-op for GraphEngine, as it does not support manually triggering updates.
556 }
557
559 LOG_TRACE_L1(m_logger, "Recording AD tape for the RHS calculation...");
560
561 // Task 1: Set dimensions and initialize the matrix
562 const size_t numSpecies = m_networkSpecies.size();
563 if (numSpecies == 0) {
564 LOG_ERROR(m_logger, "Cannot record AD tape: No species in the network.");
565 m_logger->flush_log();
566 throw std::runtime_error("Cannot record AD tape: No species in the network.");
567 }
568 const size_t numADInputs = numSpecies + 2; // Note here that by not letting T9 and rho be independent variables, we are constraining the network to a constant temperature and density during each evaluation.
569
570 // --- CppAD Tape Recording ---
571 // 1. Declare independent variable (adY)
572 // We also initialize the dummy variable for tape recording (these tell CppAD what the derivative chain looks like).
573 // Their numeric values are irrelevant except for in so far as they avoid numerical instabilities.
574
575 // Distribute total mass fraction uniformly between species in the dummy variable space
576 const auto uniformMassFraction = static_cast<CppAD::AD<double>>(1.0 / static_cast<double>(numSpecies));
577 std::vector<CppAD::AD<double>> adInput(numADInputs, uniformMassFraction);
578 adInput[numSpecies] = 1.0; // Dummy T9
579 adInput[numSpecies + 1] = 1.0; // Dummy rho
580
581 // 3. Declare independent variables (what CppAD will differentiate wrt.)
582 // This also beings the tape recording process.
583 CppAD::Independent(adInput);
584
585 std::vector<CppAD::AD<double>> adY(numSpecies);
586 for(size_t i = 0; i < numSpecies; ++i) {
587 adY[i] = adInput[i];
588 }
589 const CppAD::AD<double> adT9 = adInput[numSpecies];
590 const CppAD::AD<double> adRho = adInput[numSpecies + 1];
591
592
593 // 5. Call the actual templated function
594 // We let T9 and rho be constant, so we pass them as fixed values.
595 auto [dydt, nuclearEnergyGenerationRate] = calculateAllDerivatives<CppAD::AD<double>>(adY, adT9, adRho);
596
597 m_rhsADFun.Dependent(adInput, dydt);
598
599 LOG_TRACE_L1(m_logger, "AD tape recorded successfully for the RHS calculation. Number of independent variables: {}.",
600 adInput.size());
601 }
602
604 LOG_TRACE_L1(m_logger, "Pre-computing constant components of GraphNetwork state...");
605
606 // --- Reverse map for fast species lookups ---
607 std::unordered_map<fourdst::atomic::Species, size_t> speciesIndexMap;
608 for (size_t i = 0; i < m_networkSpecies.size(); ++i) {
609 speciesIndexMap[m_networkSpecies[i]] = i;
610 }
611
613 m_precomputedReactions.reserve(m_reactions.size());
614
615 for (size_t i = 0; i < m_reactions.size(); ++i) {
616 const auto& reaction = m_reactions[i];
617 PrecomputedReaction precomp;
618 precomp.reaction_index = i;
619
620 // --- Precompute reactant information ---
621 // Count occurrences for each reactant to determine powers and symmetry
622 std::unordered_map<size_t, int> reactantCounts;
623 for (const auto& reactant: reaction.reactants()) {
624 size_t reactantIndex = speciesIndexMap.at(reactant);
625 reactantCounts[reactantIndex]++;
626 }
627
628 double symmetryDenominator = 1.0;
629 for (const auto& [index, count] : reactantCounts) {
630 precomp.unique_reactant_indices.push_back(index);
631 precomp.reactant_powers.push_back(count);
632
633 symmetryDenominator *= 1.0/std::tgamma(count + 1);
634 }
635
636 precomp.symmetry_factor = symmetryDenominator;
637
638 // --- Precompute stoichiometry information ---
639 const auto stoichiometryMap = reaction.stoichiometry();
640 precomp.affected_species_indices.reserve(stoichiometryMap.size());
641 precomp.stoichiometric_coefficients.reserve(stoichiometryMap.size());
642
643 for (const auto& [species, coeff] : stoichiometryMap) {
644 precomp.affected_species_indices.push_back(speciesIndexMap.at(species));
645 precomp.stoichiometric_coefficients.push_back(coeff);
646 }
647
648 m_precomputedReactions.push_back(std::move(precomp));
649 }
650 }
651}
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
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.
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.
reaction::LogicalReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition, bool reverse)
Definition network.cpp:64
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.
T nuclearEnergyGenerationRate
Specific energy generation rate (e.g., erg/g/s).
std::vector< T > dydt
Derivatives of abundances (dY/dt for each species).