8#include "quill/LogMacros.h"
9#include "quill/Logger.h"
12 using fourdst::atomic::Species;
25 LOG_TRACE_L1(
m_logger,
"Constructing species index map for adaptive engine view...");
26 std::unordered_map<Species, size_t> fullSpeciesReverseMap;
27 const auto& fullSpeciesList =
m_baseEngine.getNetworkSpecies();
29 fullSpeciesReverseMap.reserve(fullSpeciesList.size());
31 for (
size_t i = 0; i < fullSpeciesList.size(); ++i) {
32 fullSpeciesReverseMap[fullSpeciesList[i]] = i;
35 std::vector<size_t> speciesIndexMap;
39 auto it = fullSpeciesReverseMap.find(active_species);
40 if (it != fullSpeciesReverseMap.end()) {
41 speciesIndexMap.push_back(it->second);
43 LOG_ERROR(
m_logger,
"Species '{}' not found in full species map.", active_species.name());
45 throw std::runtime_error(
"Species not found in full species map: " + std::string(active_species.name()));
48 LOG_TRACE_L1(
m_logger,
"Species index map constructed with {} entries.", speciesIndexMap.size());
49 return speciesIndexMap;
54 LOG_TRACE_L1(
m_logger,
"Constructing reaction index map for adaptive engine view...");
57 std::unordered_map<std::string_view, size_t> fullReactionReverseMap;
58 const auto& fullReactionSet =
m_baseEngine.getNetworkReactions();
59 fullReactionReverseMap.reserve(fullReactionSet.size());
61 for (
size_t i_full = 0; i_full < fullReactionSet.size(); ++i_full) {
62 fullReactionReverseMap[fullReactionSet[i_full].id()] = i_full;
66 std::vector<size_t> reactionIndexMap;
70 auto it = fullReactionReverseMap.find(active_reaction_ptr.id());
72 if (it != fullReactionReverseMap.end()) {
73 reactionIndexMap.push_back(it->second);
75 LOG_ERROR(
m_logger,
"Active reaction '{}' not found in base engine during reaction index map construction.", active_reaction_ptr.id());
77 throw std::runtime_error(
"Mismatch between active reactions and base engine.");
81 LOG_TRACE_L1(
m_logger,
"Reaction index map constructed with {} entries.", reactionIndexMap.size());
82 return reactionIndexMap;
86 LOG_TRACE_L1(
m_logger,
"Updating AdaptiveEngineView with new network input...");
88 std::vector<double> Y_Full;
93 for (
const auto&[reactionPtr, flowRate]: allFlows) {
94 if (flowRate > maxFlow) {
98 LOG_DEBUG(
m_logger,
"Maximum reaction flow rate in adaptive engine view: {:0.3E} [mol/s]", maxFlow);
101 LOG_DEBUG(
m_logger,
"Found {} reachable species in adaptive engine view.", reachableSpecies.size());
103 const std::vector<const reaction::LogicalReaction*> finalReactions =
cullReactionsByFlow(allFlows, reachableSpecies, Y_Full, maxFlow);
121 const std::vector<double> &Y_culled,
129 const auto [dydt, nuclearEnergyGenerationRate] =
m_baseEngine.calculateRHSAndEnergy(Y_full, T9, rho);
135 return culledResults;
139 const std::vector<double> &Y_culled,
157 return m_baseEngine.getJacobianMatrixEntry(i_full, j_full);
166 const int speciesIndex_culled,
167 const int reactionIndex_culled
172 return m_baseEngine.getStoichiometryMatrixEntry(speciesIndex_full, reactionIndex_full);
177 const std::vector<double> &Y_culled,
183 LOG_ERROR(
m_logger,
"Reaction '{}' is not part of the active reactions in the adaptive engine view.",
reaction.id());
185 throw std::runtime_error(
"Reaction not found in active reactions: " + std::string(
reaction.id()));
197 const std::vector<double> &Y_culled,
203 const auto fullTimescales =
m_baseEngine.getSpeciesTimescales(Y_full, T9, rho);
205 std::unordered_map<Species, double> culledTimescales;
208 if (fullTimescales.contains(active_species)) {
209 culledTimescales[active_species] = fullTimescales.at(active_species);
212 return culledTimescales;
225 std::vector<double> full(
m_baseEngine.getNetworkSpecies().size(), 0.0);
226 for (
size_t i_culled = 0; i_culled < culled.size(); ++i_culled) {
228 full[i_full] += culled[i_culled];
235 for (
size_t i_culled = 0; i_culled <
m_activeSpecies.size(); ++i_culled) {
237 culled[i_culled] = full[i_full];
243 if (culledSpeciesIndex < 0 || culledSpeciesIndex >=
static_cast<int>(
m_speciesIndexMap.size())) {
244 LOG_ERROR(
m_logger,
"Culled index {} is out of bounds for species index map of size {}.", culledSpeciesIndex,
m_speciesIndexMap.size());
246 throw std::out_of_range(
"Culled index " + std::to_string(culledSpeciesIndex) +
" is out of bounds for species index map of size " + std::to_string(
m_speciesIndexMap.size()) +
".");
252 if (culledReactionIndex < 0 || culledReactionIndex >=
static_cast<int>(
m_reactionIndexMap.size())) {
253 LOG_ERROR(
m_logger,
"Culled index {} is out of bounds for reaction index map of size {}.", culledReactionIndex,
m_reactionIndexMap.size());
255 throw std::out_of_range(
"Culled index " + std::to_string(culledReactionIndex) +
" is out of bounds for reaction index map of size " + std::to_string(
m_reactionIndexMap.size()) +
".");
262 LOG_ERROR(
m_logger,
"AdaptiveEngineView is stale. Please call update() before calculating RHS and energy.");
264 throw std::runtime_error(
"AdaptiveEngineView is stale. Please call update() before calculating RHS and energy.");
270 std::vector<double> &out_Y_Full
272 const auto& fullSpeciesList =
m_baseEngine.getNetworkSpecies();
274 out_Y_Full.reserve(fullSpeciesList.size());
276 for (
const auto& species: fullSpeciesList) {
278 out_Y_Full.push_back(netIn.
composition.getMolarAbundance(std::string(species.name())));
280 LOG_TRACE_L2(
m_logger,
"Species '{}' not found in composition. Setting abundance to 0.0.", species.name());
281 out_Y_Full.push_back(0.0);
286 const double rho = netIn.
density;
288 std::vector<ReactionFlow> reactionFlows;
289 const auto& fullReactionSet =
m_baseEngine.getNetworkReactions();
290 reactionFlows.reserve(fullReactionSet.size());
291 for (
const auto&
reaction : fullReactionSet) {
293 reactionFlows.push_back({&
reaction, flow});
294 LOG_TRACE_L2(
m_logger,
"Reaction '{}' has flow rate: {:0.3E} [mol/s]",
reaction.id(), flow);
296 return reactionFlows;
302 std::unordered_set<Species> reachable;
303 std::queue<Species> to_vist;
305 constexpr double ABUNDANCE_FLOOR = 1e-12;
306 for (
const auto& species:
m_baseEngine.getNetworkSpecies()) {
307 if (netIn.
composition.contains(species) && netIn.
composition.getMassFraction(std::string(species.name())) > ABUNDANCE_FLOOR) {
308 if (!reachable.contains(species)) {
309 to_vist.push(species);
310 reachable.insert(species);
311 LOG_TRACE_L2(
m_logger,
"Network Connectivity Analysis: Species '{}' is part of the initial fuel.", species.name());
316 bool new_species_found_in_pass =
true;
317 while (new_species_found_in_pass) {
318 new_species_found_in_pass =
false;
320 bool all_reactants_reachable =
true;
321 for (
const auto& reactant:
reaction.reactants()) {
322 if (!reachable.contains(reactant)) {
323 all_reactants_reachable =
false;
327 if (all_reactants_reachable) {
328 for (
const auto& product:
reaction.products()) {
329 if (!reachable.contains(product)) {
330 reachable.insert(product);
331 new_species_found_in_pass =
true;
332 LOG_TRACE_L2(
m_logger,
"Network Connectivity Analysis: Species '{}' is reachable via reaction '{}'.", product.name(),
reaction.id());
343 const std::vector<ReactionFlow> &allFlows,
344 const std::unordered_set<fourdst::atomic::Species> &reachableSpecies,
345 const std::vector<double> &Y_full,
348 LOG_TRACE_L1(
m_logger,
"Culling reactions based on flow rates...");
349 const auto relative_culling_threshold =
m_config.get<
double>(
"gridfire:AdaptiveEngineView:RelativeCullingThreshold", 1e-75);
350 double absoluteCullingThreshold = relative_culling_threshold * maxFlow;
351 LOG_DEBUG(
m_logger,
"Relative culling threshold: {:0.3E} ({})", relative_culling_threshold, absoluteCullingThreshold);
352 std::vector<const reaction::LogicalReaction*> culledReactions;
353 for (
const auto& [reactionPtr, flowRate]: allFlows) {
354 bool keepReaction =
false;
355 if (flowRate > absoluteCullingThreshold) {
356 LOG_TRACE_L2(
m_logger,
"Maintaining reaction '{}' with relative (abs) flow rate: {:0.3E} ({:0.3E} [mol/s])", reactionPtr->id(), flowRate/maxFlow, flowRate);
359 bool zero_flow_due_to_reachable_reactants =
false;
360 if (flowRate < 1e-99) {
361 for (
const auto& reactant: reactionPtr->reactants()) {
362 const auto it = std::ranges::find(
m_baseEngine.getNetworkSpecies(), reactant);
363 const size_t index = std::distance(
m_baseEngine.getNetworkSpecies().begin(), it);
364 if (Y_full[index] < 1e-99 && reachableSpecies.contains(reactant)) {
365 LOG_TRACE_L2(
m_logger,
"Maintaining reaction '{}' with zero flow due to reachable reactant '{}'.", reactionPtr->id(), reactant.name());
366 zero_flow_due_to_reachable_reactants =
true;
371 if (zero_flow_due_to_reachable_reactants) {
376 culledReactions.push_back(reactionPtr);
378 LOG_TRACE_L1(
m_logger,
"Culling reaction '{}' due to low flow rate or lack of connectivity.", reactionPtr->id());
381 LOG_DEBUG(
m_logger,
"Selected {} (total: {}, culled: {}) reactions based on flow rates.", culledReactions.size(), allFlows.size(), allFlows.size() - culledReactions.size());
382 return culledReactions;
386 const std::vector<const reaction::LogicalReaction *> &finalReactions
388 std::unordered_set<Species>finalSpeciesSet;
390 for (
const auto* reactionPtr: finalReactions) {
392 for (
const auto& reactant : reactionPtr->reactants()) {
393 finalSpeciesSet.insert(reactant);
395 for (
const auto& product : reactionPtr->products()) {
396 finalSpeciesSet.insert(product);
399 m_activeSpecies.assign(finalSpeciesSet.begin(), finalSpeciesSet.end());
402 [](
const Species &a,
const Species &b) {
return a.mass() < b.mass(); }
double calculateMolarReactionFlow(const reaction::Reaction &reaction, const std::vector< double > &Y_culled, double T9, double rho) const override
Calculates the molar reaction flow for a given reaction in the active network.
screening::ScreeningType getScreeningModel() const override
std::unordered_set< fourdst::atomic::Species > findReachableSpecies(const NetIn &netIn) const
const reaction::LogicalReactionSet & getNetworkReactions() const override
Gets the set of active logical reactions in the network.
reaction::LogicalReactionSet m_activeReactions
std::vector< size_t > m_reactionIndexMap
void generateStoichiometryMatrix() override
Generates the stoichiometry matrix for the active reactions and species.
size_t mapCulledToFullSpeciesIndex(size_t culledSpeciesIndex) const
Maps a culled species index to a full species index.
std::vector< double > mapFullToCulled(const std::vector< double > &full) const
Maps a vector of full abundances to a vector of culled abundances.
std::vector< const reaction::LogicalReaction * > cullReactionsByFlow(const std::vector< ReactionFlow > &allFlows, const std::unordered_set< fourdst::atomic::Species > &reachableSpecies, const std::vector< double > &Y_full, double maxFlow) const
double getJacobianMatrixEntry(const int i_culled, const int j_culled) const override
Gets an entry from the Jacobian matrix for the active species.
DynamicEngine & m_baseEngine
std::vector< size_t > m_speciesIndexMap
int getStoichiometryMatrixEntry(const int speciesIndex_culled, const int reactionIndex_culled) const override
Gets an entry from the stoichiometry matrix for the active species and reactions.
std::vector< double > mapCulledToFull(const std::vector< double > &culled) const
Maps a vector of culled abundances to a vector of full abundances.
StepDerivatives< double > calculateRHSAndEnergy(const std::vector< double > &Y_culled, const double T9, const double rho) const override
Calculates the right-hand side (dY/dt) and energy generation for the active species.
void update(const NetIn &netIn) override
Updates the active species and reactions based on the current conditions.
std::vector< size_t > constructReactionIndexMap() const
Constructs the reaction index map.
std::vector< size_t > constructSpeciesIndexMap() const
Constructs the species index map.
size_t mapCulledToFullReactionIndex(size_t culledReactionIndex) const
Maps a culled reaction index to a full reaction index.
std::unordered_map< fourdst::atomic::Species, double > getSpeciesTimescales(const std::vector< double > &Y_culled, double T9, double rho) const override
Computes timescales for all active species in the network.
void finalizeActiveSet(const std::vector< const reaction::LogicalReaction * > &finalReactions)
void setScreeningModel(screening::ScreeningType model) override
std::vector< ReactionFlow > calculateAllReactionFlows(const NetIn &netIn, std::vector< double > &out_Y_Full) const
const std::vector< fourdst::atomic::Species > & getNetworkSpecies() const override
Gets the list of active species in the network.
void generateJacobianMatrix(const std::vector< double > &Y_culled, const double T9, const double rho) override
Generates the Jacobian matrix for the active species.
AdaptiveEngineView(DynamicEngine &baseEngine)
Constructs an AdaptiveEngineView.
void validateState() const
Validates that the AdaptiveEngineView is not stale.
std::vector< fourdst::atomic::Species > m_activeSpecies
Abstract class for engines supporting Jacobian and stoichiometry operations.
Represents a single nuclear reaction from a specific data source.
TemplatedReactionSet< LogicalReaction > LogicalReactionSet
A set of logical reactions.
double density
Density in g/cm^3.
fourdst::composition::Composition composition
Composition of the network.
double temperature
Temperature in Kelvin.
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).