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
Gets the screening model from the base engine.
std::unordered_set< fourdst::atomic::Species > findReachableSpecies(const NetIn &netIn) const
Finds all species that are reachable from the initial fuel through the reaction network.
const reaction::LogicalReactionSet & getNetworkReactions() const override
Gets the set of active logical reactions in the network.
Config & m_config
A reference to the singleton Config instance, used for retrieving configuration parameters.
reaction::LogicalReactionSet m_activeReactions
The set of reactions that are currently active in the network.
std::vector< size_t > m_reactionIndexMap
A map from the indices of the active reactions to the indices of the corresponding reactions in the f...
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
Culls reactions from the network based on their flow rates.
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
The underlying engine to which this view delegates calculations.
std::vector< size_t > m_speciesIndexMap
A map from the indices of the active species to the indices of the corresponding species in the full ...
bool m_isStale
A flag indicating whether the view is stale and needs to be updated.
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)
Finalizes the set of active species and reactions.
void setScreeningModel(screening::ScreeningType model) override
Sets the screening model for the base engine.
std::vector< ReactionFlow > calculateAllReactionFlows(const NetIn &netIn, std::vector< double > &out_Y_Full) const
Calculates the molar reaction flow rate for all reactions in the full network.
quill::Logger * m_logger
A pointer to the logger instance, used for logging messages.
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
The set of species that are currently active in the network.
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.
ScreeningType
Enumerates the available plasma screening models.
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).