perf(thread saftey): All Engines are now thread safe
Previously engines were not thread safe, a seperate engine would be needed for every thread. This is no longer the case. This allows for much more efficient parallel execution
This commit is contained in:
@@ -3,12 +3,15 @@
|
||||
#include "gridfire/utils/table_format.h"
|
||||
#include "fourdst/atomic/species.h"
|
||||
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
namespace gridfire::engine::diagnostics {
|
||||
std::optional<nlohmann::json> report_limiting_species(
|
||||
scratch::StateBlob& ctx,
|
||||
const DynamicEngine &engine,
|
||||
const std::vector<double> &Y_full,
|
||||
const std::vector<double> &E_full,
|
||||
@@ -24,7 +27,7 @@ namespace gridfire::engine::diagnostics {
|
||||
double abundance;
|
||||
};
|
||||
|
||||
const auto& species_list = engine.getNetworkSpecies();
|
||||
const auto& species_list = engine.getNetworkSpecies(ctx);
|
||||
std::vector<SpeciesError> errors;
|
||||
|
||||
for (size_t i = 0; i < species_list.size(); ++i) {
|
||||
@@ -75,6 +78,7 @@ namespace gridfire::engine::diagnostics {
|
||||
}
|
||||
|
||||
std::optional<nlohmann::json> inspect_species_balance(
|
||||
scratch::StateBlob& ctx,
|
||||
const DynamicEngine& engine,
|
||||
const std::string& species_name,
|
||||
const fourdst::composition::Composition &comp,
|
||||
@@ -90,11 +94,11 @@ namespace gridfire::engine::diagnostics {
|
||||
double total_creation_flow = 0.0;
|
||||
double total_destruction_flow = 0.0;
|
||||
|
||||
for (const auto& reaction : engine.getNetworkReactions()) {
|
||||
for (const auto& reaction : engine.getNetworkReactions(ctx)) {
|
||||
const int stoichiometry = reaction->stoichiometry(species_obj);
|
||||
if (stoichiometry == 0) continue;
|
||||
|
||||
const double flow = engine.calculateMolarReactionFlow(*reaction, comp, T9, rho);
|
||||
const double flow = engine.calculateMolarReactionFlow(ctx, *reaction, comp, T9, rho);
|
||||
|
||||
if (stoichiometry > 0) {
|
||||
creation_ids.emplace_back(reaction->id());
|
||||
@@ -157,17 +161,18 @@ namespace gridfire::engine::diagnostics {
|
||||
}
|
||||
|
||||
std::optional<nlohmann::json> inspect_jacobian_stiffness(
|
||||
scratch::StateBlob& ctx,
|
||||
const DynamicEngine &engine,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const bool json
|
||||
) {
|
||||
NetworkJacobian jac = engine.generateJacobianMatrix(comp, T9, rho);
|
||||
NetworkJacobian jac = engine.generateJacobianMatrix(ctx, comp, T9, rho);
|
||||
|
||||
jac = regularize_jacobian(jac, comp);
|
||||
|
||||
const auto& species_list = engine.getNetworkSpecies();
|
||||
const auto& species_list = engine.getNetworkSpecies(ctx);
|
||||
|
||||
double max_diag = 0.0;
|
||||
double max_off_diag = 0.0;
|
||||
|
||||
@@ -8,11 +8,16 @@
|
||||
#include "gridfire/utils/hashing.h"
|
||||
#include "gridfire/utils/table_format.h"
|
||||
|
||||
#include "gridfire/engine/scratchpads/engine_graph_scratchpad.h"
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
#include "gridfire/engine/scratchpads/utils.h"
|
||||
|
||||
#include "fourdst/atomic/species.h"
|
||||
#include "fourdst/atomic/atomicSpecies.h"
|
||||
|
||||
#include "quill/LogMacros.h"
|
||||
|
||||
// ReSharper disable once CppUnusedIncludeDirective
|
||||
#include <cstdint>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
@@ -28,9 +33,6 @@
|
||||
#include "cppad/utility/sparse_rc.hpp"
|
||||
#include "cppad/utility/sparse_rcv.hpp"
|
||||
|
||||
#ifdef GRIDFIRE_USE_OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
|
||||
namespace {
|
||||
@@ -115,8 +117,9 @@ namespace gridfire::engine {
|
||||
const NetworkConstructionFlags reactionTypes ) :
|
||||
m_weakRateInterpolator(rates::weak::UNIFIED_WEAK_DATA),
|
||||
m_reactions(build_nuclear_network(composition, m_weakRateInterpolator, buildDepth, reactionTypes)),
|
||||
m_partitionFunction(partitionFunction.clone()),
|
||||
m_depth(buildDepth),
|
||||
m_partitionFunction(partitionFunction.clone())
|
||||
m_state_blob_offset(0) // For a base engine the offset is always 0
|
||||
{
|
||||
syncInternalMaps();
|
||||
}
|
||||
@@ -125,33 +128,37 @@ namespace gridfire::engine {
|
||||
const reaction::ReactionSet &reactions
|
||||
) :
|
||||
m_weakRateInterpolator(rates::weak::UNIFIED_WEAK_DATA),
|
||||
m_reactions(reactions)
|
||||
m_reactions(reactions),
|
||||
m_state_blob_offset(0)
|
||||
{
|
||||
syncInternalMaps();
|
||||
}
|
||||
|
||||
std::expected<StepDerivatives<double>, EngineStatus> GraphEngine::calculateRHSAndEnergy(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
bool trust
|
||||
) const {
|
||||
return calculateRHSAndEnergy(comp, T9, rho, m_reactions);
|
||||
return calculateRHSAndEnergy(ctx, comp, T9, rho, m_reactions);
|
||||
}
|
||||
|
||||
std::expected<StepDerivatives<double>, EngineStatus> GraphEngine::calculateRHSAndEnergy(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const reaction::ReactionSet &activeReactions
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::GraphEngineScratchPad, true>(ctx);
|
||||
LOG_TRACE_L3(m_logger, "Calculating RHS and Energy in GraphEngine at T9 = {}, rho = {}.", T9, rho);
|
||||
const double Ye = comp.getElectronAbundance();
|
||||
const std::vector<double> molarAbundances = comp.getMolarAbundanceVector();
|
||||
if (m_usePrecomputation) {
|
||||
const std::size_t state_hash = utils::hash_state(comp, T9, rho, activeReactions);
|
||||
if (m_stepDerivativesCache.contains(state_hash)) {
|
||||
return m_stepDerivativesCache.at(state_hash);
|
||||
if (state->stepDerivativesCache.contains(state_hash)) {
|
||||
return state->stepDerivativesCache.at(state_hash);
|
||||
}
|
||||
LOG_TRACE_L3(m_logger, "Using precomputation for reaction rates in GraphEngine calculateRHSAndEnergy.");
|
||||
std::vector<double> bare_rates;
|
||||
@@ -171,9 +178,9 @@ namespace gridfire::engine {
|
||||
LOG_TRACE_L3(m_logger, "Precomputed {} forward and {} reverse reaction rates for active reactions.", bare_rates.size(), bare_reverse_rates.size());
|
||||
|
||||
// --- The public facing interface can always use the precomputed version since taping is done internally ---
|
||||
StepDerivatives<double> result = calculateAllDerivativesUsingPrecomputation(comp, bare_rates, bare_reverse_rates, T9, rho, activeReactions);
|
||||
m_stepDerivativesCache.insert(std::make_pair(state_hash, result));
|
||||
m_most_recent_rhs_calculation = result;
|
||||
StepDerivatives<double> result = calculateAllDerivativesUsingPrecomputation(ctx, comp, bare_rates, bare_reverse_rates, T9, rho, activeReactions);
|
||||
state->stepDerivativesCache.insert(std::make_pair(state_hash, result));
|
||||
state->most_recent_rhs_calculation = result;
|
||||
return result;
|
||||
} else {
|
||||
LOG_TRACE_L2(m_logger, "Not using precomputation for reaction rates in GraphEngine calculateRHSAndEnergy.");
|
||||
@@ -194,25 +201,28 @@ namespace gridfire::engine {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
m_most_recent_rhs_calculation = result;
|
||||
state->most_recent_rhs_calculation = result;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
EnergyDerivatives GraphEngine::calculateEpsDerivatives(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
return calculateEpsDerivatives(comp, T9, rho, m_reactions);
|
||||
return calculateEpsDerivatives(ctx, comp, T9, rho, m_reactions);
|
||||
}
|
||||
|
||||
EnergyDerivatives GraphEngine::calculateEpsDerivatives(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const reaction::ReactionSet &activeReactions
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::GraphEngineScratchPad, true>(ctx);
|
||||
const size_t numSpecies = m_networkSpecies.size();
|
||||
const size_t numADInputs = numSpecies + 2; // +2 for T9 and rho
|
||||
|
||||
@@ -236,10 +246,11 @@ namespace gridfire::engine {
|
||||
w[numSpecies] = 1.0; // We want the derivative of the energy generation rate
|
||||
|
||||
// Sweep the tape forward to record the function value at x
|
||||
m_rhsADFun.Forward(0, x);
|
||||
assert(state->rhsADFun.has_value() && "AD tape for energy derivatives has not been recorded.");
|
||||
state->rhsADFun.value().Forward(0, x);
|
||||
|
||||
// Extract the gradient at the previously evaluated point x using reverse mode
|
||||
const std::vector<double> eps_derivatives = m_rhsADFun.Reverse(1, w);
|
||||
const std::vector<double> eps_derivatives = state->rhsADFun.value().Reverse(1, w);
|
||||
|
||||
const double dEps_dT9 = eps_derivatives[numSpecies];
|
||||
const double dEps_dRho = eps_derivatives[numSpecies + 1];
|
||||
@@ -252,19 +263,8 @@ namespace gridfire::engine {
|
||||
return {dEps_dT, dEps_dRho};
|
||||
}
|
||||
|
||||
void GraphEngine::syncInternalMaps() {
|
||||
|
||||
LOG_INFO(m_logger, "Synchronizing internal maps for REACLIB graph network (serif::network::GraphNetwork)...");
|
||||
collectNetworkSpecies();
|
||||
populateReactionIDMap();
|
||||
populateSpeciesToIndexMap();
|
||||
collectAtomicReverseRateAtomicBases();
|
||||
generateStoichiometryMatrix();
|
||||
|
||||
recordADTape(); // Record the AD tape for the RHS of the ODE (dY/di and dEps/di) for all independent variables i
|
||||
|
||||
[[maybe_unused]] const size_t inputSize = m_rhsADFun.Domain();
|
||||
const size_t outputSize = m_rhsADFun.Range();
|
||||
void GraphEngine::generate_jacobian_sparsity_pattern() {
|
||||
const size_t outputSize = m_authoritativeADFun.Range();
|
||||
|
||||
// Create a range x range identity pattern
|
||||
CppAD::sparse_rc<std::vector<size_t>> patternIn(outputSize, outputSize, outputSize);
|
||||
@@ -272,9 +272,8 @@ namespace gridfire::engine {
|
||||
patternIn.set(i, i, i);
|
||||
}
|
||||
|
||||
m_rhsADFun.rev_jac_sparsity(patternIn, false, false, false, m_full_jacobian_sparsity_pattern);
|
||||
m_authoritativeADFun.rev_jac_sparsity(patternIn, false, false, false, m_full_jacobian_sparsity_pattern);
|
||||
|
||||
m_jac_work.clear();
|
||||
m_full_sparsity_set.clear();
|
||||
const auto& rows = m_full_jacobian_sparsity_pattern.row();
|
||||
const auto& cols = m_full_jacobian_sparsity_pattern.col();
|
||||
@@ -285,6 +284,17 @@ namespace gridfire::engine {
|
||||
m_full_sparsity_set.insert(std::make_pair(rows[k], cols[k]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEngine::syncInternalMaps() {
|
||||
LOG_INFO(m_logger, "Synchronizing internal maps for REACLIB graph network (serif::network::GraphNetwork)...");
|
||||
collectNetworkSpecies();
|
||||
populateReactionIDMap();
|
||||
populateSpeciesToIndexMap();
|
||||
collectAtomicReverseRateAtomicBases();
|
||||
|
||||
recordADTape(); // Record the AD tape for the RHS of the ODE (dY/di and dEps/di) for all independent variables i
|
||||
generate_jacobian_sparsity_pattern();
|
||||
|
||||
precomputeNetwork();
|
||||
LOG_INFO(m_logger, "Internal maps synchronized. Network contains {} species and {} reactions.",
|
||||
@@ -344,81 +354,26 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
// --- Basic Accessors and Queries ---
|
||||
const std::vector<fourdst::atomic::Species>& GraphEngine::getNetworkSpecies() const {
|
||||
const std::vector<fourdst::atomic::Species>& GraphEngine::getNetworkSpecies(scratch::StateBlob &ctx) const {
|
||||
return m_networkSpecies;
|
||||
}
|
||||
|
||||
const reaction::ReactionSet& GraphEngine::getNetworkReactions() const {
|
||||
const reaction::ReactionSet& GraphEngine::getNetworkReactions(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return m_reactions;
|
||||
}
|
||||
|
||||
void GraphEngine::setNetworkReactions(const reaction::ReactionSet &reactions) {
|
||||
m_reactions = reactions;
|
||||
syncInternalMaps();
|
||||
}
|
||||
|
||||
bool GraphEngine::involvesSpecies(const fourdst::atomic::Species& species) const {
|
||||
bool GraphEngine::involvesSpecies(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::atomic::Species& species
|
||||
) const {
|
||||
const bool found = m_networkSpeciesMap.contains(species.name());
|
||||
return found;
|
||||
}
|
||||
|
||||
// --- Validation Methods ---
|
||||
bool GraphEngine::validateConservation() const {
|
||||
LOG_TRACE_L1(m_logger, "Validating mass (A) and charge (Z) conservation across all reactions in the network.");
|
||||
|
||||
for (const auto& reaction : m_reactions) {
|
||||
uint64_t totalReactantA = 0;
|
||||
uint64_t totalReactantZ = 0;
|
||||
uint64_t totalProductA = 0;
|
||||
uint64_t totalProductZ = 0;
|
||||
|
||||
// Calculate total A and Z for reactants
|
||||
for (const auto& reactant : reaction->reactants()) {
|
||||
auto it = m_networkSpeciesMap.find(reactant.name());
|
||||
if (it != m_networkSpeciesMap.end()) {
|
||||
totalReactantA += it->second.a();
|
||||
totalReactantZ += it->second.z();
|
||||
} else {
|
||||
// This scenario indicates a severe data integrity issue:
|
||||
// a reactant is part of a reaction but not in the network's species map.
|
||||
LOG_ERROR(m_logger, "CRITICAL ERROR: Reactant species '{}' in reaction '{}' not found in network species map during conservation validation.",
|
||||
reactant.name(), reaction->id());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate total A and Z for products
|
||||
for (const auto& product : reaction->products()) {
|
||||
auto it = m_networkSpeciesMap.find(product.name());
|
||||
if (it != m_networkSpeciesMap.end()) {
|
||||
totalProductA += it->second.a();
|
||||
totalProductZ += it->second.z();
|
||||
} else {
|
||||
// Similar critical error for product species
|
||||
LOG_ERROR(m_logger, "CRITICAL ERROR: Product species '{}' in reaction '{}' not found in network species map during conservation validation.",
|
||||
product.name(), reaction->id());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare totals for conservation
|
||||
if (totalReactantA != totalProductA) {
|
||||
LOG_ERROR(m_logger, "Mass number (A) not conserved for reaction '{}': Reactants A={} vs Products A={}.",
|
||||
reaction->id(), totalReactantA, totalProductA);
|
||||
return false;
|
||||
}
|
||||
if (totalReactantZ != totalProductZ) {
|
||||
LOG_ERROR(m_logger, "Atomic number (Z) not conserved for reaction '{}': Reactants Z={} vs Products Z={}.",
|
||||
reaction->id(), totalReactantZ, totalProductZ);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Mass (A) and charge (Z) conservation validated successfully for all reactions.");
|
||||
return true; // All reactions passed the conservation check
|
||||
}
|
||||
|
||||
double GraphEngine::compute_reaction_flow(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<double> &local_abundances,
|
||||
const std::vector<double> &screening_factors,
|
||||
const std::vector<double> &bare_rates,
|
||||
@@ -488,6 +443,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::pair<double, double> GraphEngine::compute_neutrino_fluxes(
|
||||
scratch::StateBlob& ctx,
|
||||
const double netFlow,
|
||||
const reaction::Reaction &reaction
|
||||
) const {
|
||||
@@ -518,6 +474,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
GraphEngine::PrecomputationKernelResults GraphEngine::accumulate_flows_serial(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<double> &local_abundances,
|
||||
const std::vector<double> &screening_factors,
|
||||
const std::vector<double> &bare_rates,
|
||||
@@ -529,19 +486,20 @@ namespace gridfire::engine {
|
||||
results.dydt_vector.resize(m_networkSpecies.size(), 0.0);
|
||||
|
||||
std::vector<double> molarReactionFlows;
|
||||
molarReactionFlows.reserve(m_precomputedReactions.size());
|
||||
molarReactionFlows.reserve(m_precomputed_reactions.size());
|
||||
|
||||
size_t reactionCounter = 0;
|
||||
std::vector<size_t> reactionIndices;
|
||||
reactionIndices.reserve(m_precomputedReactions.size());
|
||||
reactionIndices.reserve(m_precomputed_reactions.size());
|
||||
|
||||
for (const auto& reaction : activeReactions) {
|
||||
uint64_t reactionHash = reaction->hash(0);
|
||||
const size_t reactionIndex = m_precomputedReactionIndexMap.at(reactionHash);
|
||||
const size_t reactionIndex = m_precomputed_reaction_index_map.at(reactionHash);
|
||||
reactionIndices.push_back(reactionIndex);
|
||||
const PrecomputedReaction& precomputedReaction = m_precomputedReactions[reactionIndex];
|
||||
const PrecomputedReaction& precomputedReaction = m_precomputed_reactions[reactionIndex];
|
||||
|
||||
double netFlow = compute_reaction_flow(
|
||||
ctx,
|
||||
local_abundances,
|
||||
screening_factors,
|
||||
bare_rates,
|
||||
@@ -554,7 +512,7 @@ namespace gridfire::engine {
|
||||
|
||||
molarReactionFlows.push_back(netFlow);
|
||||
|
||||
auto [local_neutrino_loss, local_neutrino_flux] = compute_neutrino_fluxes(netFlow, *reaction);
|
||||
auto [local_neutrino_loss, local_neutrino_flux] = compute_neutrino_fluxes(ctx, netFlow, *reaction);
|
||||
results.total_neutrino_energy_loss_rate += local_neutrino_loss;
|
||||
results.total_neutrino_flux += local_neutrino_flux;
|
||||
|
||||
@@ -565,7 +523,7 @@ namespace gridfire::engine {
|
||||
|
||||
reactionCounter = 0;
|
||||
for (const auto& [reaction, j]: std::views::zip(activeReactions, reactionIndices)) {
|
||||
const auto& precomp = m_precomputedReactions[j];
|
||||
const auto& precomp = m_precomputed_reactions[j];
|
||||
const double R_j = molarReactionFlows[reactionCounter];
|
||||
|
||||
for (size_t i = 0; i < precomp.affected_species_indices.size(); ++i) {
|
||||
@@ -746,28 +704,24 @@ namespace gridfire::engine {
|
||||
|
||||
}
|
||||
|
||||
bool GraphEngine::isUsingReverseReactions() const {
|
||||
bool GraphEngine::isUsingReverseReactions(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return m_useReverseReactions;
|
||||
}
|
||||
|
||||
void GraphEngine::setUseReverseReactions(const bool useReverse) {
|
||||
m_useReverseReactions = useReverse;
|
||||
syncInternalMaps();
|
||||
}
|
||||
|
||||
size_t GraphEngine::getSpeciesIndex(const fourdst::atomic::Species &species) const {
|
||||
size_t GraphEngine::getSpeciesIndex(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::atomic::Species &species
|
||||
) const {
|
||||
return m_speciesToIndexMap.at(species); // Returns the index of the species in the stoichiometry matrix
|
||||
}
|
||||
|
||||
std::vector<double> GraphEngine::mapNetInToMolarAbundanceVector(const NetIn &netIn) const {
|
||||
std::vector<double> Y(m_networkSpecies.size(), 0.0); // Initialize with zeros
|
||||
for (const auto& [sp, y] : netIn.composition) {
|
||||
Y[getSpeciesIndex(sp)] = y; // Map species to their molar abundance
|
||||
}
|
||||
return Y; // Return the vector of molar abundances
|
||||
}
|
||||
PrimingReport GraphEngine::primeEngine(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) const {
|
||||
|
||||
PrimingReport GraphEngine::primeEngine(const NetIn &netIn) {
|
||||
NetIn fullNetIn;
|
||||
fourdst::composition::Composition composition;
|
||||
|
||||
@@ -787,27 +741,13 @@ namespace gridfire::engine {
|
||||
reactionTypesToIgnore = {reaction::ReactionType::WEAK};
|
||||
}
|
||||
|
||||
auto primingReport = primeNetwork(fullNetIn, *this, reactionTypesToIgnore);
|
||||
auto primingReport = primeNetwork(ctx, fullNetIn, *this, reactionTypesToIgnore);
|
||||
|
||||
m_has_been_primed = true;
|
||||
return primingReport;
|
||||
}
|
||||
|
||||
BuildDepthType GraphEngine::getDepth() const {
|
||||
return m_depth;
|
||||
}
|
||||
|
||||
void GraphEngine::rebuild(const fourdst::composition::CompositionAbstract &comp, const BuildDepthType depth) {
|
||||
if (depth != m_depth) {
|
||||
m_depth = depth;
|
||||
m_reactions = build_nuclear_network(comp, m_weakRateInterpolator, m_depth);
|
||||
syncInternalMaps(); // Resync internal maps after changing the depth
|
||||
} else {
|
||||
LOG_DEBUG(m_logger, "Rebuild requested with the same depth. No changes made to the network.");
|
||||
}
|
||||
}
|
||||
|
||||
fourdst::composition::Composition GraphEngine::collectComposition(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho
|
||||
@@ -827,7 +767,10 @@ namespace gridfire::engine {
|
||||
return result;
|
||||
}
|
||||
|
||||
SpeciesStatus GraphEngine::getSpeciesStatus(const fourdst::atomic::Species &species) const {
|
||||
SpeciesStatus GraphEngine::getSpeciesStatus(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::atomic::Species &species
|
||||
) const {
|
||||
if (m_networkSpeciesMap.contains(species.name())) {
|
||||
return SpeciesStatus::ACTIVE;
|
||||
}
|
||||
@@ -835,15 +778,18 @@ namespace gridfire::engine {
|
||||
|
||||
}
|
||||
|
||||
std::optional<StepDerivatives<double>> GraphEngine::getMostRecentRHSCalculation() const {
|
||||
if (!m_most_recent_rhs_calculation.has_value()) {
|
||||
std::optional<StepDerivatives<double>> GraphEngine::getMostRecentRHSCalculation(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
const auto *state = scratch::get_state<scratch::GraphEngineScratchPad, true>(ctx);
|
||||
if (!state->most_recent_rhs_calculation.has_value()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return m_most_recent_rhs_calculation.value();
|
||||
return state->most_recent_rhs_calculation.value();
|
||||
}
|
||||
|
||||
|
||||
StepDerivatives<double> GraphEngine::calculateAllDerivativesUsingPrecomputation(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const std::vector<double> &bare_rates,
|
||||
const std::vector<double> &bare_reverse_rates,
|
||||
@@ -851,6 +797,7 @@ namespace gridfire::engine {
|
||||
const double rho,
|
||||
const reaction::ReactionSet &activeReactions
|
||||
) const {
|
||||
auto *state = scratch::get_state<scratch::GraphEngineScratchPad, true>(ctx);
|
||||
LOG_TRACE_L3(m_logger, "Computing screening factors for {} active reactions.", activeReactions.size());
|
||||
// --- Calculate screening factors ---
|
||||
const std::vector<double> screeningFactors = m_screeningModel->calculateScreeningFactors(
|
||||
@@ -860,17 +807,17 @@ namespace gridfire::engine {
|
||||
T9,
|
||||
rho
|
||||
);
|
||||
m_local_abundance_cache.clear();
|
||||
state->local_abundance_cache.clear();
|
||||
for (const auto& species: m_networkSpecies) {
|
||||
m_local_abundance_cache.push_back(comp.contains(species) ? comp.getMolarAbundance(species) : 0.0);
|
||||
state->local_abundance_cache.push_back(comp.contains(species) ? comp.getMolarAbundance(species) : 0.0);
|
||||
}
|
||||
|
||||
StepDerivatives<double> result;
|
||||
std::vector<double> dydt_scratch(m_networkSpecies.size(), 0.0);
|
||||
|
||||
#ifndef GRIDFIRE_USE_OPENMP
|
||||
const auto [dydt_vector, total_neutrino_energy_loss_rate, total_neutrino_flux] = accumulate_flows_serial(
|
||||
m_local_abundance_cache,
|
||||
ctx,
|
||||
state->local_abundance_cache,
|
||||
screeningFactors,
|
||||
bare_rates,
|
||||
bare_reverse_rates,
|
||||
@@ -880,19 +827,6 @@ namespace gridfire::engine {
|
||||
dydt_scratch = dydt_vector;
|
||||
result.neutrinoEnergyLossRate = total_neutrino_energy_loss_rate;
|
||||
result.totalNeutrinoFlux = total_neutrino_flux;
|
||||
#else
|
||||
const auto [dydt_vector, total_neutrino_energy_loss_rate, total_neutrino_flux] = accumulate_flows_parallel(
|
||||
m_local_abundance_cache,
|
||||
screeningFactors,
|
||||
bare_rates,
|
||||
bare_reverse_rates,
|
||||
rho,
|
||||
activeReactions
|
||||
);
|
||||
dydt_scratch = dydt_vector;
|
||||
result.neutrinoEnergyLossRate = total_neutrino_energy_loss_rate;
|
||||
result.totalNeutrinoFlux = total_neutrino_flux;
|
||||
#endif
|
||||
|
||||
// load scratch into result.dydt
|
||||
for (size_t i = 0; i < m_networkSpecies.size(); ++i) {
|
||||
@@ -910,33 +844,26 @@ namespace gridfire::engine {
|
||||
|
||||
}
|
||||
|
||||
// --- Generate Stoichiometry Matrix ---
|
||||
void GraphEngine::generateStoichiometryMatrix() {
|
||||
return; // Deprecated
|
||||
}
|
||||
|
||||
void GraphEngine::setScreeningModel(const screening::ScreeningType model) {
|
||||
m_screeningModel = screening::selectScreeningModel(model);
|
||||
m_screeningType = model;
|
||||
}
|
||||
|
||||
screening::ScreeningType GraphEngine::getScreeningModel() const {
|
||||
screening::ScreeningType GraphEngine::getScreeningModel(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return m_screeningType;
|
||||
}
|
||||
|
||||
void GraphEngine::setPrecomputation(const bool precompute) {
|
||||
m_usePrecomputation = precompute;
|
||||
}
|
||||
|
||||
bool GraphEngine::isPrecomputationEnabled() const {
|
||||
bool GraphEngine::isPrecomputationEnabled(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return m_usePrecomputation;
|
||||
}
|
||||
|
||||
const partition::PartitionFunction & GraphEngine::getPartitionFunction() const {
|
||||
const partition::PartitionFunction & GraphEngine::getPartitionFunction(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return *m_partitionFunction;
|
||||
}
|
||||
|
||||
double GraphEngine::calculateMolarReactionFlow(
|
||||
scratch::StateBlob& ctx,
|
||||
const reaction::Reaction &reaction,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
@@ -962,10 +889,12 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
NetworkJacobian GraphEngine::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
auto *state = scratch::get_state<scratch::GraphEngineScratchPad, true>(ctx);
|
||||
fourdst::composition::Composition mutableComp;
|
||||
for (const auto& species : m_networkSpecies) {
|
||||
mutableComp.registerSpecies(species);
|
||||
@@ -986,10 +915,11 @@ namespace gridfire::engine {
|
||||
adInput[numSpecies + 1] = rho; // rho
|
||||
|
||||
// 2. Calculate the full jacobian
|
||||
const std::vector<double> dotY = m_rhsADFun.Jacobian(adInput);
|
||||
assert(state->rhsADFun.has_value() && "RHS ADFun not recorded before Jacobian generation.");
|
||||
const std::vector<double> dotY = state->rhsADFun.value().Jacobian(adInput);
|
||||
|
||||
// 3. Pack jacobian vector into sparse matrix
|
||||
Eigen::SparseMatrix<double> jacobianMatrix(numSpecies, numSpecies);
|
||||
Eigen::SparseMatrix<double> jacobianMatrix(static_cast<long>(numSpecies), static_cast<long>(numSpecies));
|
||||
std::vector<Eigen::Triplet<double> > triplets;
|
||||
for (size_t i = 0; i < numSpecies; ++i) {
|
||||
for (size_t j = 0; j < numSpecies; ++j) {
|
||||
@@ -1013,12 +943,15 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
NetworkJacobian GraphEngine::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const std::vector<fourdst::atomic::Species> &activeSpecies
|
||||
) const {
|
||||
// PERF: For small k it may make sense to implement a purley forward mode AD computation, some heuristic could be used to switch between the two methods based on k and total network species
|
||||
// PERF: For small k it may make sense to implement a purley forward mode AD computation,
|
||||
// some heuristic could be used to switch between the two methods based on k and
|
||||
// total network species
|
||||
const size_t k_active = activeSpecies.size();
|
||||
|
||||
// --- 1. Get the list of global indices ---
|
||||
@@ -1026,8 +959,8 @@ namespace gridfire::engine {
|
||||
active_indices.reserve(k_active);
|
||||
|
||||
for (const auto& species : activeSpecies) {
|
||||
assert(involvesSpecies(species));
|
||||
active_indices.push_back(getSpeciesIndex(species));
|
||||
assert(involvesSpecies(ctx, species));
|
||||
active_indices.push_back(getSpeciesIndex(ctx, species));
|
||||
}
|
||||
|
||||
// --- 2. Build the k x k sparsity pattern ---
|
||||
@@ -1041,15 +974,17 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
// --- 3. Call the sparse reverse-mode implementation ---
|
||||
return generateJacobianMatrix(comp, T9, rho, sparsityPattern);
|
||||
return generateJacobianMatrix(ctx, comp, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
NetworkJacobian GraphEngine::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const SparsityPattern &sparsityPattern
|
||||
) const {
|
||||
auto *state = scratch::get_state<scratch::GraphEngineScratchPad, true>(ctx);
|
||||
// --- Compute the intersection of the requested sparsity pattern with the full sparsity pattern ---
|
||||
SparsityPattern intersectionSparsityPattern;
|
||||
for (const auto& entry : sparsityPattern) {
|
||||
@@ -1097,30 +1032,32 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
// --- Check cache for existing subset ---
|
||||
if (!m_jacobianSubsetCache.contains(sparsity_hash)) {
|
||||
m_jacobianSubsetCache.emplace(sparsity_hash, CppAD_sparsity_pattern);
|
||||
m_jac_work.clear();
|
||||
if (!state->jacobianSubsetCache.contains(sparsity_hash)) {
|
||||
state->jacobianSubsetCache.emplace(sparsity_hash, CppAD_sparsity_pattern);
|
||||
state->jac_work.clear();
|
||||
} else {
|
||||
if (m_jacWorkCache.contains(sparsity_hash)) {
|
||||
m_jac_work.clear();
|
||||
m_jac_work = m_jacWorkCache.at(sparsity_hash);
|
||||
if (state->jacWorkCache.contains(sparsity_hash)) {
|
||||
state->jac_work.clear();
|
||||
state->jac_work = state->jacWorkCache.at(sparsity_hash);
|
||||
}
|
||||
}
|
||||
auto& jac_subset = m_jacobianSubsetCache.at(sparsity_hash);
|
||||
m_rhsADFun.sparse_jac_rev(
|
||||
auto& jac_subset = state->jacobianSubsetCache.at(sparsity_hash);
|
||||
|
||||
assert(state->rhsADFun.has_value() && "RHS ADFun not recorded before Jacobian generation.");
|
||||
state->rhsADFun.value().sparse_jac_rev(
|
||||
x,
|
||||
jac_subset, // Sparse Jacobian output
|
||||
m_full_jacobian_sparsity_pattern,
|
||||
"cppad",
|
||||
m_jac_work // Work vector for CppAD
|
||||
state->jac_work // Work vector for CppAD
|
||||
);
|
||||
|
||||
// --- Stash the now populated work vector in the cache if not already present ---
|
||||
if (!m_jacWorkCache.contains(sparsity_hash)) {
|
||||
m_jacWorkCache.emplace(sparsity_hash, m_jac_work);
|
||||
if (!state->jacWorkCache.contains(sparsity_hash)) {
|
||||
state->jacWorkCache.emplace(sparsity_hash, state->jac_work);
|
||||
}
|
||||
|
||||
Eigen::SparseMatrix<double> jacobianMatrix(numSpecies, numSpecies);
|
||||
Eigen::SparseMatrix<double> jacobianMatrix(static_cast<long>(numSpecies), static_cast<long>(numSpecies));
|
||||
std::vector<Eigen::Triplet<double> > triplets;
|
||||
for (size_t k = 0; k < nnz; ++k) {
|
||||
const size_t row = jac_subset.row()[k];
|
||||
@@ -1142,20 +1079,10 @@ namespace gridfire::engine {
|
||||
return jac;
|
||||
}
|
||||
|
||||
std::unordered_map<fourdst::atomic::Species, int> GraphEngine::getNetReactionStoichiometry(
|
||||
const reaction::Reaction &reaction
|
||||
) {
|
||||
return reaction.stoichiometry();
|
||||
}
|
||||
|
||||
int GraphEngine::getStoichiometryMatrixEntry(
|
||||
const fourdst::atomic::Species& species,
|
||||
const reaction::Reaction &reaction
|
||||
void GraphEngine::exportToDot(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::string &filename
|
||||
) const {
|
||||
return reaction.stoichiometry(species);
|
||||
}
|
||||
|
||||
void GraphEngine::exportToDot(const std::string &filename) const {
|
||||
LOG_TRACE_L1(m_logger, "Exporting network graph to DOT file: {}", filename);
|
||||
|
||||
std::ofstream dotFile(filename);
|
||||
@@ -1203,7 +1130,10 @@ namespace gridfire::engine {
|
||||
LOG_TRACE_L1(m_logger, "Successfully exported network to {}", filename);
|
||||
}
|
||||
|
||||
void GraphEngine::exportToCSV(const std::string &filename) const {
|
||||
void GraphEngine::exportToCSV(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::string &filename
|
||||
) const {
|
||||
LOG_TRACE_L1(m_logger, "Exporting network graph to CSV file: {}", filename);
|
||||
|
||||
std::ofstream csvFile(filename, std::ios::out | std::ios::trunc);
|
||||
@@ -1241,14 +1171,16 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<fourdst::atomic::Species, double>, EngineStatus> GraphEngine::getSpeciesTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
return getSpeciesTimescales(comp, T9, rho, m_reactions);
|
||||
return getSpeciesTimescales(ctx, comp, T9, rho, m_reactions);
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<fourdst::atomic::Species, double>, EngineStatus> GraphEngine::getSpeciesTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
@@ -1287,14 +1219,16 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<fourdst::atomic::Species, double>, EngineStatus> GraphEngine::getSpeciesDestructionTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
return getSpeciesDestructionTimescales(comp, T9, rho, m_reactions);
|
||||
return getSpeciesDestructionTimescales(ctx, comp, T9, rho, m_reactions);
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<fourdst::atomic::Species, double>, EngineStatus> GraphEngine::getSpeciesDestructionTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
@@ -1337,7 +1271,10 @@ namespace gridfire::engine {
|
||||
return speciesDestructionTimescales;
|
||||
}
|
||||
|
||||
fourdst::composition::Composition GraphEngine::update(const NetIn &netIn) {
|
||||
fourdst::composition::Composition GraphEngine::project(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) const {
|
||||
fourdst::composition::Composition baseUpdatedComposition = netIn.composition;
|
||||
for (const auto& species : m_networkSpecies) {
|
||||
if (!netIn.composition.contains(species)) {
|
||||
@@ -1347,11 +1284,8 @@ namespace gridfire::engine {
|
||||
return baseUpdatedComposition;
|
||||
}
|
||||
|
||||
bool GraphEngine::isStale(const NetIn &netIn) {
|
||||
return false;
|
||||
}
|
||||
void GraphEngine::recordADTape() {
|
||||
|
||||
void GraphEngine::recordADTape() const {
|
||||
LOG_TRACE_L1(m_logger, "Recording AD tape for the RHS calculation...");
|
||||
|
||||
// Task 1: Set dimensions and initialize the matrix
|
||||
@@ -1415,13 +1349,14 @@ namespace gridfire::engine {
|
||||
);
|
||||
dependentVector.push_back(result.nuclearEnergyGenerationRate);
|
||||
|
||||
m_rhsADFun.Dependent(adInput, dependentVector);
|
||||
m_rhsADFun.optimize();
|
||||
m_authoritativeADFun.Dependent(adInput, dependentVector);
|
||||
m_authoritativeADFun.optimize();
|
||||
|
||||
LOG_TRACE_L1(m_logger, "AD tape recorded successfully for the RHS and Eps calculation. Number of independent variables: {}.", adInput.size());
|
||||
}
|
||||
|
||||
void GraphEngine::collectAtomicReverseRateAtomicBases() {
|
||||
void GraphEngine::collectAtomicReverseRateAtomicBases(
|
||||
) {
|
||||
m_atomicReverseRates.clear();
|
||||
m_atomicReverseRates.reserve(m_reactions.size());
|
||||
|
||||
@@ -1434,7 +1369,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEngine::precomputeNetwork() {
|
||||
void GraphEngine::precomputeNetwork() {
|
||||
LOG_TRACE_L1(m_logger, "Pre-computing constant components of GraphNetwork state...");
|
||||
|
||||
// --- Reverse map for fast species lookups ---
|
||||
@@ -1443,10 +1378,10 @@ namespace gridfire::engine {
|
||||
speciesIndexMap[m_networkSpecies[i]] = i;
|
||||
}
|
||||
|
||||
m_precomputedReactions.clear();
|
||||
m_precomputedReactions.reserve(m_reactions.size());
|
||||
m_precomputedReactionIndexMap.clear();
|
||||
m_precomputedReactionIndexMap.reserve(m_reactions.size());
|
||||
m_precomputed_reactions.clear();
|
||||
m_precomputed_reactions.reserve(m_reactions.size());
|
||||
m_precomputed_reaction_index_map.clear();
|
||||
m_precomputed_reaction_index_map.reserve(m_reactions.size());
|
||||
|
||||
for (size_t i = 0; i < m_reactions.size(); ++i) {
|
||||
const auto& reaction = m_reactions[i];
|
||||
@@ -1456,7 +1391,7 @@ namespace gridfire::engine {
|
||||
uint64_t reactionHash = reaction.hash(0);
|
||||
|
||||
precomp.reaction_hash = reactionHash;
|
||||
m_precomputedReactionIndexMap[reactionHash] = i;
|
||||
m_precomputed_reaction_index_map[reactionHash] = i;
|
||||
|
||||
// --- Precompute forward reaction information ---
|
||||
// Count occurrences for each reactant to determine powers and symmetry
|
||||
@@ -1506,7 +1441,7 @@ namespace gridfire::engine {
|
||||
precomp.stoichiometric_coefficients.push_back(coeff);
|
||||
}
|
||||
|
||||
m_precomputedReactions.push_back(std::move(precomp));
|
||||
m_precomputed_reactions.push_back(std::move(precomp));
|
||||
}
|
||||
LOG_TRACE_L1(m_logger, "Pre-computation complete. Precomputed data for {} reactions.", m_precomputedReactions.size());
|
||||
}
|
||||
@@ -1523,6 +1458,7 @@ namespace gridfire::engine {
|
||||
if ( p != 0) { return false; }
|
||||
const double T9 = tx[0];
|
||||
|
||||
|
||||
// We can pass a dummy comp and rho because reverse rates should only be calculated for strong reactions whose
|
||||
// rates of progression do not depend on composition or density.
|
||||
const fourdst::composition::Composition dummyComp;
|
||||
@@ -1612,68 +1548,4 @@ namespace gridfire::engine {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef GRIDFIRE_USE_OPENMP
|
||||
GraphEngine::PrecomputationKernelResults GraphEngine::accumulate_flows_parallel(
|
||||
const std::vector<double> &local_abundances,
|
||||
const std::vector<double> &screening_factors,
|
||||
const std::vector<double> &bare_rates,
|
||||
const std::vector<double> &bare_reverse_rates,
|
||||
const double rho,
|
||||
const reaction::ReactionSet &activeReactions
|
||||
) const {
|
||||
int n_threads = omp_get_max_threads();
|
||||
std::vector<std::vector<double>> thread_local_dydt(n_threads, std::vector<double>(m_networkSpecies.size(), 0.0));
|
||||
|
||||
double total_neutrino_energy_loss_rate = 0.0;
|
||||
double total_neutrino_flux = 0.0;
|
||||
|
||||
#pragma omp parallel for schedule(static) reduction(+:total_neutrino_energy_loss_rate, total_neutrino_flux)
|
||||
for (size_t k = 0; k < activeReactions.size(); ++k) {
|
||||
int t_id = omp_get_thread_num();
|
||||
const auto& reaction = activeReactions[k];
|
||||
const size_t reactionIndex = m_precomputedReactionIndexMap.at(reaction.hash(0));
|
||||
const PrecomputedReaction& precomputedReaction = m_precomputedReactions[reactionIndex];
|
||||
|
||||
double netFlow = compute_reaction_flow(
|
||||
local_abundances,
|
||||
screening_factors,
|
||||
bare_rates,
|
||||
bare_reverse_rates,
|
||||
rho,
|
||||
reactionIndex,
|
||||
reaction,
|
||||
reactionIndex,
|
||||
precomputedReaction
|
||||
);
|
||||
|
||||
auto [neutrinoEnergyLossRate, neutrinoFlux] = compute_neutrino_fluxes(
|
||||
netFlow,
|
||||
reaction
|
||||
);
|
||||
|
||||
total_neutrino_energy_loss_rate += neutrinoEnergyLossRate;
|
||||
total_neutrino_flux += neutrinoFlux;
|
||||
|
||||
for (size_t i = 0; i < precomputedReaction.affected_species_indices.size(); ++i) {
|
||||
thread_local_dydt[t_id][precomputedReaction.affected_species_indices[i]] +=
|
||||
netFlow * precomputedReaction.stoichiometric_coefficients[i];
|
||||
}
|
||||
|
||||
}
|
||||
PrecomputationKernelResults results;
|
||||
results.total_neutrino_energy_loss_rate = total_neutrino_energy_loss_rate;
|
||||
results.total_neutrino_flux = total_neutrino_flux;
|
||||
|
||||
results.dydt_vector.resize(m_networkSpecies.size(), 0.0);
|
||||
#pragma omp parallel for schedule(static)
|
||||
for (size_t i = 0; i < m_networkSpecies.size(); ++i) {
|
||||
double sum = 0.0;
|
||||
for (int t = 0; t < n_threads; ++t) sum += thread_local_dydt[t][i];
|
||||
results.dydt_vector[i] = sum;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#include "gridfire/types/types.h"
|
||||
#include "gridfire/exceptions/error_solver.h"
|
||||
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
#include "gridfire/engine/scratchpads/engine_graph_scratchpad.h"
|
||||
|
||||
#include "fourdst/logging/logging.h"
|
||||
#include "gridfire/solver/strategies/CVODE_solver_strategy.h"
|
||||
#include "quill/Logger.h"
|
||||
@@ -20,12 +23,12 @@ namespace gridfire::engine {
|
||||
using fourdst::atomic::Species;
|
||||
|
||||
PrimingReport primeNetwork(
|
||||
const NetIn& netIn,
|
||||
GraphEngine& engine,
|
||||
const std::optional<std::vector<reaction::ReactionType>>& ignoredReactionTypes
|
||||
scratch::StateBlob &ctx,
|
||||
const NetIn& netIn,
|
||||
const GraphEngine& engine, const std::optional<std::vector<reaction::ReactionType>>& ignoredReactionTypes
|
||||
) {
|
||||
const auto logger = LogManager::getInstance().getLogger("log");
|
||||
solver::CVODESolverStrategy integrator(engine);
|
||||
solver::CVODESolverStrategy integrator(engine, ctx);
|
||||
|
||||
// Do not need high precision for priming
|
||||
integrator.set_absTol(1e-3);
|
||||
@@ -70,7 +73,7 @@ namespace gridfire::engine {
|
||||
minAbundance = y;
|
||||
}
|
||||
}
|
||||
double abundanceForUnprimedSpecies = minAbundance / 1e10;
|
||||
const double abundanceForUnprimedSpecies = minAbundance / 1e10;
|
||||
for (const auto& sp : unprimedSpecies) {
|
||||
LOG_TRACE_L1(logger, "Clamping Species {}: initial abundance {}, primed abundance {} to {}", sp.name(), netIn.composition.getMolarAbundance(sp), report.primedComposition.getMolarAbundance(sp), abundanceForUnprimedSpecies);
|
||||
report.primedComposition.setMolarAbundance(sp, abundanceForUnprimedSpecies);
|
||||
|
||||
@@ -9,6 +9,10 @@
|
||||
#include "gridfire/exceptions/error_engine.h"
|
||||
#include "gridfire/utils/hashing.h"
|
||||
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
#include "gridfire/engine/scratchpads/utils.h"
|
||||
#include "gridfire/engine/scratchpads/engine_adaptive_scratchpad.h"
|
||||
|
||||
#include "quill/LogMacros.h"
|
||||
#include "quill/Logger.h"
|
||||
|
||||
@@ -17,23 +21,24 @@ namespace gridfire::engine {
|
||||
AdaptiveEngineView::AdaptiveEngineView(
|
||||
DynamicEngine &baseEngine
|
||||
) :
|
||||
m_baseEngine(baseEngine),
|
||||
m_activeSpecies(baseEngine.getNetworkSpecies()),
|
||||
m_activeReactions(baseEngine.getNetworkReactions())
|
||||
{}
|
||||
m_baseEngine(baseEngine) {}
|
||||
|
||||
fourdst::composition::Composition AdaptiveEngineView::update(const NetIn &netIn) {
|
||||
m_activeReactions.clear();
|
||||
m_activeSpecies.clear();
|
||||
fourdst::composition::Composition AdaptiveEngineView::project(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) const {
|
||||
auto *state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
state->active_reactions.clear();
|
||||
state->active_species.clear();
|
||||
|
||||
fourdst::composition::Composition baseUpdatedComposition = m_baseEngine.update(netIn);
|
||||
fourdst::composition::Composition baseUpdatedComposition = m_baseEngine.project(ctx, netIn);
|
||||
NetIn updatedNetIn = netIn;
|
||||
|
||||
updatedNetIn.composition = baseUpdatedComposition;
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Updating AdaptiveEngineView with new network input...");
|
||||
|
||||
auto [allFlows, composition] = calculateAllReactionFlows(updatedNetIn);
|
||||
auto [allFlows, composition] = calculateAllReactionFlows(ctx, updatedNetIn);
|
||||
|
||||
double maxFlow = 0.0;
|
||||
|
||||
@@ -44,51 +49,50 @@ namespace gridfire::engine {
|
||||
}
|
||||
LOG_DEBUG(m_logger, "Maximum reaction flow rate in adaptive engine view: {:0.3E} [mol/s]", maxFlow);
|
||||
|
||||
const std::unordered_set<Species> reachableSpecies = findReachableSpecies(updatedNetIn);
|
||||
const std::unordered_set<Species> reachableSpecies = findReachableSpecies(ctx, updatedNetIn);
|
||||
LOG_DEBUG(m_logger, "Found {} reachable species in adaptive engine view.", reachableSpecies.size());
|
||||
|
||||
const std::vector<const reaction::Reaction*> finalReactions = cullReactionsByFlow(allFlows, reachableSpecies, composition, maxFlow);
|
||||
const std::vector<const reaction::Reaction*> finalReactions = cullReactionsByFlow(ctx, allFlows, reachableSpecies, composition, maxFlow);
|
||||
|
||||
finalizeActiveSet(finalReactions);
|
||||
finalizeActiveSet(ctx, finalReactions);
|
||||
|
||||
auto [rescuedReactions, rescuedSpecies] = rescueEdgeSpeciesDestructionChannel(composition, netIn.temperature/1e9, netIn.density, m_activeSpecies, m_activeReactions);
|
||||
auto [rescuedReactions, rescuedSpecies] = rescueEdgeSpeciesDestructionChannel(
|
||||
ctx,
|
||||
composition,
|
||||
netIn.temperature/1e9,
|
||||
netIn.density
|
||||
);
|
||||
|
||||
for (const auto& reactionPtr : rescuedReactions) {
|
||||
m_activeReactions.add_reaction(*reactionPtr);
|
||||
state->active_reactions.add_reaction(*reactionPtr);
|
||||
}
|
||||
|
||||
for (const auto& species : rescuedSpecies) {
|
||||
if (!std::ranges::contains(m_activeSpecies, species) && m_baseEngine.getSpeciesStatus(species) == SpeciesStatus::ACTIVE) {
|
||||
m_activeSpecies.push_back(species);
|
||||
if (!std::ranges::contains(state->active_species, species) && m_baseEngine.getSpeciesStatus(ctx, species) == SpeciesStatus::ACTIVE) {
|
||||
state->active_species.push_back(species);
|
||||
}
|
||||
}
|
||||
|
||||
m_isStale = false;
|
||||
|
||||
LOG_INFO(m_logger, "AdaptiveEngineView updated successfully with {} active species and {} active reactions.", m_activeSpecies.size(), m_activeReactions.size());
|
||||
LOG_INFO(m_logger, "AdaptiveEngineView updated successfully with {} active species and {} active reactions.", state->active_species.size(), state->active_reactions.size());
|
||||
|
||||
return updatedNetIn.composition;
|
||||
}
|
||||
|
||||
bool AdaptiveEngineView::isStale(const NetIn &netIn) {
|
||||
return m_isStale || m_baseEngine.isStale(netIn);
|
||||
}
|
||||
|
||||
const std::vector<Species> & AdaptiveEngineView::getNetworkSpecies() const {
|
||||
return m_activeSpecies;
|
||||
const std::vector<Species> & AdaptiveEngineView::getNetworkSpecies(scratch::StateBlob& ctx) const {
|
||||
return scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx)->active_species;
|
||||
}
|
||||
|
||||
std::expected<StepDerivatives<double>, EngineStatus> AdaptiveEngineView::calculateRHSAndEnergy(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho, bool trust
|
||||
) const {
|
||||
LOG_TRACE_L2(m_logger, "Calculating RHS and Energy in AdaptiveEngineView at T9 = {}, rho = {}.", T9, rho);
|
||||
validateState();
|
||||
|
||||
const fourdst::composition::Composition collectedComp = collectComposition(comp, T9, rho);
|
||||
const fourdst::composition::Composition collectedComp = collectComposition(ctx, comp, T9, rho);
|
||||
|
||||
auto result = m_baseEngine.calculateRHSAndEnergy(collectedComp, T9, rho, true);
|
||||
auto result = m_baseEngine.calculateRHSAndEnergy(ctx, collectedComp, T9, rho, true);
|
||||
LOG_TRACE_L2(m_logger, "Base engine calculation of RHS and Energy complete.");
|
||||
|
||||
if (!result) {
|
||||
@@ -100,99 +104,89 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
EnergyDerivatives AdaptiveEngineView::calculateEpsDerivatives(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
validateState();
|
||||
return m_baseEngine.calculateEpsDerivatives(comp, T9, rho);
|
||||
return m_baseEngine.calculateEpsDerivatives(ctx, comp, T9, rho);
|
||||
}
|
||||
|
||||
NetworkJacobian AdaptiveEngineView::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
return generateJacobianMatrix(comp, T9, rho, m_activeSpecies);
|
||||
const auto *state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
return generateJacobianMatrix(ctx, comp, T9, rho, state->active_species);
|
||||
}
|
||||
|
||||
NetworkJacobian AdaptiveEngineView::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const std::vector<Species> &activeSpecies
|
||||
) const {
|
||||
validateState();
|
||||
return m_baseEngine.generateJacobianMatrix(comp, T9, rho, activeSpecies);
|
||||
const auto *state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
return m_baseEngine.generateJacobianMatrix(ctx, comp, T9, rho, state->active_species);
|
||||
|
||||
}
|
||||
|
||||
NetworkJacobian AdaptiveEngineView::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const SparsityPattern &sparsityPattern
|
||||
) const {
|
||||
validateState();
|
||||
return m_baseEngine.generateJacobianMatrix(comp, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
void AdaptiveEngineView::generateStoichiometryMatrix() {
|
||||
validateState();
|
||||
m_baseEngine.generateStoichiometryMatrix();
|
||||
}
|
||||
|
||||
int AdaptiveEngineView::getStoichiometryMatrixEntry(
|
||||
const Species &species,
|
||||
const reaction::Reaction& reaction
|
||||
) const {
|
||||
validateState();
|
||||
return m_baseEngine.getStoichiometryMatrixEntry(species, reaction);
|
||||
return m_baseEngine.generateJacobianMatrix(ctx, comp, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
double AdaptiveEngineView::calculateMolarReactionFlow(
|
||||
scratch::StateBlob& ctx,
|
||||
const reaction::Reaction &reaction,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
validateState();
|
||||
if (!m_activeReactions.contains(reaction)) {
|
||||
const auto *state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
if (!state->active_reactions.contains(reaction)) {
|
||||
LOG_ERROR(m_logger, "Reaction '{}' is not part of the active reactions in the adaptive engine view.", reaction.id());
|
||||
m_logger -> flush_log();
|
||||
throw std::runtime_error("Reaction not found in active reactions: " + std::string(reaction.id()));
|
||||
}
|
||||
|
||||
return m_baseEngine.calculateMolarReactionFlow(reaction, comp, T9, rho);
|
||||
return m_baseEngine.calculateMolarReactionFlow(ctx, reaction, comp, T9, rho);
|
||||
}
|
||||
|
||||
const reaction::ReactionSet & AdaptiveEngineView::getNetworkReactions() const {
|
||||
return m_activeReactions;
|
||||
}
|
||||
|
||||
void AdaptiveEngineView::setNetworkReactions(const reaction::ReactionSet &reactions) {
|
||||
LOG_CRITICAL(m_logger, "AdaptiveEngineView does not support setting network reactions directly. Use update() with NetIn instead. Perhaps you meant to call this on the base engine?");
|
||||
throw exceptions::UnableToSetNetworkReactionsError("AdaptiveEngineView does not support setting network reactions directly. Use update() with NetIn instead. Perhaps you meant to call this on the base engine?");
|
||||
const reaction::ReactionSet & AdaptiveEngineView::getNetworkReactions(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx) -> active_reactions;
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<Species, double>, EngineStatus> AdaptiveEngineView::getSpeciesTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
validateState();
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(comp, T9, rho);
|
||||
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(ctx, comp, T9, rho);
|
||||
|
||||
if (!result) {
|
||||
return std::unexpected{result.error()};
|
||||
}
|
||||
|
||||
const auto* state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
const std::unordered_map<Species, double>& fullTimescales = result.value();
|
||||
|
||||
|
||||
std::unordered_map<Species, double> culledTimescales;
|
||||
culledTimescales.reserve(m_activeSpecies.size());
|
||||
for (const auto& active_species : m_activeSpecies) {
|
||||
culledTimescales.reserve(state->active_species.size());
|
||||
for (const auto& active_species : state->active_species) {
|
||||
if (fullTimescales.contains(active_species)) {
|
||||
culledTimescales[active_species] = fullTimescales.at(active_species);
|
||||
}
|
||||
@@ -202,21 +196,23 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<Species, double>, EngineStatus> AdaptiveEngineView::getSpeciesDestructionTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
validateState();
|
||||
|
||||
const auto result = m_baseEngine.getSpeciesDestructionTimescales(comp, T9, rho);
|
||||
const auto result = m_baseEngine.getSpeciesDestructionTimescales(ctx, comp, T9, rho);
|
||||
if (!result) {
|
||||
return std::unexpected{result.error()};
|
||||
}
|
||||
|
||||
const auto* state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
const std::unordered_map<Species, double>& destructionTimescales = result.value();
|
||||
|
||||
std::unordered_map<Species, double> culledTimescales;
|
||||
culledTimescales.reserve(m_activeSpecies.size());
|
||||
for (const auto& active_species : m_activeSpecies) {
|
||||
culledTimescales.reserve(state->active_species.size());
|
||||
for (const auto& active_species : state->active_species) {
|
||||
if (destructionTimescales.contains(active_species)) {
|
||||
culledTimescales[active_species] = destructionTimescales.at(active_species);
|
||||
}
|
||||
@@ -224,34 +220,29 @@ namespace gridfire::engine {
|
||||
return culledTimescales;
|
||||
}
|
||||
|
||||
void AdaptiveEngineView::setScreeningModel(const screening::ScreeningType model) {
|
||||
m_baseEngine.setScreeningModel(model);
|
||||
screening::ScreeningType AdaptiveEngineView::getScreeningModel(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return m_baseEngine.getScreeningModel(ctx);
|
||||
}
|
||||
|
||||
screening::ScreeningType AdaptiveEngineView::getScreeningModel() const {
|
||||
return m_baseEngine.getScreeningModel();
|
||||
}
|
||||
|
||||
std::vector<double> AdaptiveEngineView::mapNetInToMolarAbundanceVector(const NetIn &netIn) const {
|
||||
std::vector<double> Y(m_activeSpecies.size(), 0.0); // Initialize with zeros
|
||||
for (const auto& [species, y] : netIn.composition) {
|
||||
Y[getSpeciesIndex(species)] = y; // Map species to their molar abundance
|
||||
}
|
||||
return Y; // Return the vector of molar abundances
|
||||
}
|
||||
|
||||
PrimingReport AdaptiveEngineView::primeEngine(const NetIn &netIn) {
|
||||
return m_baseEngine.primeEngine(netIn);
|
||||
PrimingReport AdaptiveEngineView::primeEngine(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) const {
|
||||
return m_baseEngine.primeEngine(ctx, netIn);
|
||||
}
|
||||
|
||||
fourdst::composition::Composition AdaptiveEngineView::collectComposition(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
fourdst::composition::Composition result = m_baseEngine.collectComposition(comp, T9, rho);
|
||||
const auto* state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
fourdst::composition::Composition result = m_baseEngine.collectComposition(ctx, comp, T9, rho);
|
||||
|
||||
for (const auto& species : m_activeSpecies) {
|
||||
for (const auto& species : state->active_species) {
|
||||
if (!result.contains(species)) {
|
||||
result.registerSpecies(species);
|
||||
}
|
||||
@@ -260,18 +251,32 @@ namespace gridfire::engine {
|
||||
return result;
|
||||
}
|
||||
|
||||
SpeciesStatus AdaptiveEngineView::getSpeciesStatus(const fourdst::atomic::Species &species) const {
|
||||
const SpeciesStatus status = m_baseEngine.getSpeciesStatus(species);
|
||||
if (status == SpeciesStatus::ACTIVE && std::ranges::find(m_activeSpecies, species) == m_activeSpecies.end()) {
|
||||
SpeciesStatus AdaptiveEngineView::getSpeciesStatus(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
) const {
|
||||
const auto* state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
const SpeciesStatus status = m_baseEngine.getSpeciesStatus(ctx, species);
|
||||
if (status == SpeciesStatus::ACTIVE && std::ranges::find(state->active_species, species) == state->active_species.end()) {
|
||||
return SpeciesStatus::INACTIVE_FLOW;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t AdaptiveEngineView::getSpeciesIndex(const fourdst::atomic::Species &species) const {
|
||||
const auto it = std::ranges::find(m_activeSpecies, species);
|
||||
if (it != m_activeSpecies.end()) {
|
||||
return static_cast<int>(std::distance(m_activeSpecies.begin(), it));
|
||||
std::optional<StepDerivatives<double>> AdaptiveEngineView::getMostRecentRHSCalculation(
|
||||
scratch::StateBlob &ctx
|
||||
) const {
|
||||
return m_baseEngine.getMostRecentRHSCalculation(ctx);
|
||||
}
|
||||
|
||||
size_t AdaptiveEngineView::getSpeciesIndex(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
) const {
|
||||
const auto *state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
const auto it = std::ranges::find(state->active_species, species);
|
||||
if (it != state->active_species.end()) {
|
||||
return static_cast<int>(std::distance(state->active_species.begin(), it));
|
||||
} else {
|
||||
LOG_ERROR(m_logger, "Species '{}' not found in active species list.", species.name());
|
||||
m_logger->flush_log();
|
||||
@@ -279,18 +284,11 @@ namespace gridfire::engine {
|
||||
}
|
||||
}
|
||||
|
||||
void AdaptiveEngineView::validateState() const {
|
||||
if (m_isStale) {
|
||||
LOG_ERROR(m_logger, "AdaptiveEngineView is stale. Please call update() before calculating RHS and energy.");
|
||||
m_logger->flush_log();
|
||||
throw std::runtime_error("AdaptiveEngineView is stale. Please call update() before calculating RHS and energy.");
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<std::vector<AdaptiveEngineView::ReactionFlow>, fourdst::composition::Composition> AdaptiveEngineView::calculateAllReactionFlows(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) const {
|
||||
const auto& fullSpeciesList = m_baseEngine.getNetworkSpecies();
|
||||
const auto& fullSpeciesList = m_baseEngine.getNetworkSpecies(ctx);
|
||||
fourdst::composition::Composition composition = netIn.composition;
|
||||
|
||||
for (const auto& species: fullSpeciesList) {
|
||||
@@ -304,10 +302,10 @@ namespace gridfire::engine {
|
||||
const double rho = netIn.density; // Density in g/cm^3
|
||||
|
||||
std::vector<ReactionFlow> reactionFlows;
|
||||
const auto& fullReactionSet = m_baseEngine.getNetworkReactions();
|
||||
const auto& fullReactionSet = m_baseEngine.getNetworkReactions(ctx);
|
||||
reactionFlows.reserve(fullReactionSet.size());
|
||||
for (const auto& reaction : fullReactionSet) {
|
||||
const double flow = m_baseEngine.calculateMolarReactionFlow(*reaction, composition, T9, rho);
|
||||
const double flow = m_baseEngine.calculateMolarReactionFlow(ctx, *reaction, composition, T9, rho);
|
||||
reactionFlows.push_back({reaction.get(), flow});
|
||||
LOG_TRACE_L3(m_logger, "Reaction '{}' has flow rate: {:0.3E} [mol/s/g]", reaction->id(), flow);
|
||||
}
|
||||
@@ -315,13 +313,14 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::unordered_set<Species> AdaptiveEngineView::findReachableSpecies(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) const {
|
||||
std::unordered_set<Species> reachable;
|
||||
std::queue<Species> to_vist;
|
||||
|
||||
constexpr double ABUNDANCE_FLOOR = 1e-12; // Abundance floor for a species to be considered part of the initial fuel
|
||||
for (const auto& species: m_baseEngine.getNetworkSpecies()) {
|
||||
for (const auto& species: m_baseEngine.getNetworkSpecies(ctx)) {
|
||||
if (netIn.composition.contains(species) && netIn.composition.getMassFraction(std::string(species.name())) > ABUNDANCE_FLOOR) {
|
||||
if (!reachable.contains(species)) {
|
||||
to_vist.push(species);
|
||||
@@ -334,7 +333,7 @@ namespace gridfire::engine {
|
||||
bool new_species_found_in_pass = true;
|
||||
while (new_species_found_in_pass) {
|
||||
new_species_found_in_pass = false;
|
||||
for (const auto& reaction: m_baseEngine.getNetworkReactions()) {
|
||||
for (const auto& reaction: m_baseEngine.getNetworkReactions(ctx)) {
|
||||
bool all_reactants_reachable = true;
|
||||
for (const auto& reactant: reaction->reactants()) {
|
||||
if (!reachable.contains(reactant)) {
|
||||
@@ -358,6 +357,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::vector<const reaction::Reaction *> AdaptiveEngineView::cullReactionsByFlow(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<ReactionFlow> &allFlows,
|
||||
const std::unordered_set<fourdst::atomic::Species> &reachableSpecies,
|
||||
const fourdst::composition::Composition &comp,
|
||||
@@ -401,21 +401,22 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
AdaptiveEngineView::RescueSet AdaptiveEngineView::rescueEdgeSpeciesDestructionChannel(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const std::vector<Species> &activeSpecies,
|
||||
const reaction::ReactionSet &activeReactions
|
||||
const double rho
|
||||
) const {
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(comp, T9, rho);
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(ctx, comp, T9, rho);
|
||||
if (!result) {
|
||||
LOG_CRITICAL(m_logger, "Failed to get species timescales due to base engine failure");
|
||||
m_logger->flush_log();
|
||||
throw exceptions::EngineError("Failed to get species timescales due base engine failure");
|
||||
}
|
||||
|
||||
const auto* state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
std::unordered_map<Species, double> timescales = result.value();
|
||||
std::set<Species> onlyProducedSpecies;
|
||||
for (const auto& reaction : activeReactions) {
|
||||
for (const auto& reaction : state->active_reactions) {
|
||||
const std::vector<Species>& products = reaction->products();
|
||||
onlyProducedSpecies.insert(products.begin(), products.end());
|
||||
}
|
||||
@@ -424,7 +425,7 @@ namespace gridfire::engine {
|
||||
std::erase_if(
|
||||
onlyProducedSpecies,
|
||||
[&](const Species &species) {
|
||||
for (const auto& reaction : activeReactions) {
|
||||
for (const auto& reaction : state->active_reactions) {
|
||||
if (reaction->contains_reactant(species)) {
|
||||
return true; // If any active reaction consumes the species then erase it from the set.
|
||||
}
|
||||
@@ -444,14 +445,14 @@ namespace gridfire::engine {
|
||||
std::unordered_map<Species, const reaction::Reaction*> reactionsToRescue;
|
||||
for (const auto& species : onlyProducedSpecies) {
|
||||
double maxSpeciesConsumptionRate = 0.0;
|
||||
for (const auto& reaction : m_baseEngine.getNetworkReactions()) {
|
||||
for (const auto& reaction : m_baseEngine.getNetworkReactions(ctx)) {
|
||||
const bool speciesToCheckIsConsumed = reaction->contains_reactant(species);
|
||||
if (!speciesToCheckIsConsumed) {
|
||||
continue; // If the species is not consumed by this reaction, skip it.
|
||||
}
|
||||
bool allOtherReactantsAreAvailable = true;
|
||||
for (const auto& reactant : reaction->reactants()) {
|
||||
const bool reactantIsAvailable = std::ranges::contains(activeSpecies, reactant);
|
||||
const bool reactantIsAvailable = std::ranges::contains(state->active_species, reactant);
|
||||
if (!reactantIsAvailable && reactant != species) {
|
||||
allOtherReactantsAreAvailable = false;
|
||||
}
|
||||
@@ -547,31 +548,33 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
void AdaptiveEngineView::finalizeActiveSet(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<const reaction::Reaction *> &finalReactions
|
||||
) {
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
std::unordered_set<Species>finalSpeciesSet;
|
||||
m_activeReactions.clear();
|
||||
state->active_reactions.clear();
|
||||
for (const auto* reactionPtr: finalReactions) {
|
||||
m_activeReactions.add_reaction(*reactionPtr);
|
||||
state->active_reactions.add_reaction(*reactionPtr);
|
||||
for (const auto& reactant : reactionPtr->reactants()) {
|
||||
const SpeciesStatus reactantStatus = m_baseEngine.getSpeciesStatus(reactant);
|
||||
const SpeciesStatus reactantStatus = m_baseEngine.getSpeciesStatus(ctx, reactant);
|
||||
if (!finalSpeciesSet.contains(reactant) && (reactantStatus == SpeciesStatus::ACTIVE || reactantStatus == SpeciesStatus::EQUILIBRIUM)) {
|
||||
LOG_TRACE_L3(m_logger, "Adding reactant '{}' to active species set through reaction {}.", reactant.name(), reactionPtr->id());
|
||||
finalSpeciesSet.insert(reactant);
|
||||
}
|
||||
}
|
||||
for (const auto& product : reactionPtr->products()) {
|
||||
const SpeciesStatus productStatus = m_baseEngine.getSpeciesStatus(product);
|
||||
const SpeciesStatus productStatus = m_baseEngine.getSpeciesStatus(ctx, product);
|
||||
if (!finalSpeciesSet.contains(product) && (productStatus == SpeciesStatus::ACTIVE || productStatus == SpeciesStatus::EQUILIBRIUM)) {
|
||||
LOG_TRACE_L3(m_logger, "Adding product '{}' to active species set through reaction {}.", product.name(), reactionPtr->id());
|
||||
finalSpeciesSet.insert(product);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_activeSpecies.clear();
|
||||
m_activeSpecies = std::vector<Species>(finalSpeciesSet.begin(), finalSpeciesSet.end());
|
||||
state->active_species.clear();
|
||||
state->active_species = std::vector<Species>(finalSpeciesSet.begin(), finalSpeciesSet.end());
|
||||
std::ranges::sort(
|
||||
m_activeSpecies,
|
||||
state->active_species,
|
||||
[](const Species &a, const Species &b) { return a.mass() < b.mass(); }
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
#include "fourdst/atomic/atomicSpecies.h"
|
||||
#include "fourdst/composition/decorators/composition_masked.h"
|
||||
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
#include "gridfire/engine/scratchpads/engine_defined_scratchpad.h"
|
||||
#include "gridfire/engine/scratchpads/utils.h"
|
||||
|
||||
#include "quill/LogMacros.h"
|
||||
|
||||
#include <string>
|
||||
@@ -23,30 +27,34 @@ namespace gridfire::engine {
|
||||
GraphEngine& baseEngine
|
||||
) :
|
||||
m_baseEngine(baseEngine) {
|
||||
collect(peNames);
|
||||
// collect(peNames);
|
||||
}
|
||||
|
||||
const DynamicEngine & DefinedEngineView::getBaseEngine() const {
|
||||
return m_baseEngine;
|
||||
}
|
||||
|
||||
const std::vector<Species> & DefinedEngineView::getNetworkSpecies() const {
|
||||
if (m_activeSpeciesVectorCache.has_value()) {
|
||||
return m_activeSpeciesVectorCache.value();
|
||||
const std::vector<Species> & DefinedEngineView::getNetworkSpecies(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
if (state->active_species_vector_cache.has_value()) {
|
||||
return state->active_species_vector_cache.value();
|
||||
}
|
||||
m_activeSpeciesVectorCache = std::vector<Species>(m_activeSpecies.begin(), m_activeSpecies.end());
|
||||
return m_activeSpeciesVectorCache.value();
|
||||
state->active_species_vector_cache = std::vector<Species>(state->active_species.begin(), state->active_species.end());
|
||||
return state->active_species_vector_cache.value();
|
||||
}
|
||||
|
||||
std::expected<StepDerivatives<double>, EngineStatus> DefinedEngineView::calculateRHSAndEnergy(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho, bool trust
|
||||
) const {
|
||||
validateNetworkState();
|
||||
auto *state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
|
||||
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies | std::ranges::to<std::vector>());
|
||||
const auto result = m_baseEngine.calculateRHSAndEnergy(masked, T9, rho, m_activeReactions);
|
||||
const fourdst::composition::MaskedComposition masked(comp, state->active_species | std::ranges::to<std::vector>());
|
||||
const auto result = m_baseEngine.calculateRHSAndEnergy(ctx, masked, T9, rho, state->active_reactions);
|
||||
|
||||
if (!result) {
|
||||
return std::unexpected{result.error()};
|
||||
@@ -56,37 +64,39 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
EnergyDerivatives DefinedEngineView::calculateEpsDerivatives(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
validateNetworkState();
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
const fourdst::composition::MaskedComposition masked(comp, state->active_species | std::ranges::to<std::vector>());
|
||||
|
||||
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies | std::ranges::to<std::vector>());
|
||||
|
||||
return m_baseEngine.calculateEpsDerivatives(masked, T9, rho, m_activeReactions);
|
||||
return m_baseEngine.calculateEpsDerivatives(ctx, masked, T9, rho, state->active_reactions);
|
||||
}
|
||||
|
||||
NetworkJacobian DefinedEngineView::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
validateNetworkState();
|
||||
if (!m_activeSpeciesVectorCache.has_value()) {
|
||||
m_activeSpeciesVectorCache = std::vector<Species>(m_activeSpecies.begin(), m_activeSpecies.end());
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
|
||||
if (!state->active_species_vector_cache.has_value()) {
|
||||
state->active_species_vector_cache = std::vector<Species>(state->active_species.begin(), state->active_species.end());
|
||||
}
|
||||
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies | std::ranges::to<std::vector>());
|
||||
return m_baseEngine.generateJacobianMatrix(masked, T9, rho, m_activeSpeciesVectorCache.value());
|
||||
const fourdst::composition::MaskedComposition masked(comp, state->active_species | std::ranges::to<std::vector>());
|
||||
return m_baseEngine.generateJacobianMatrix(ctx, masked, T9, rho, state->active_species_vector_cache.value());
|
||||
}
|
||||
|
||||
NetworkJacobian DefinedEngineView::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const std::vector<fourdst::atomic::Species> &activeSpecies
|
||||
const std::vector<Species> &activeSpecies
|
||||
) const {
|
||||
validateNetworkState();
|
||||
|
||||
const std::set<fourdst::atomic::Species> activeSpeciesSet(
|
||||
activeSpecies.begin(),
|
||||
@@ -94,96 +104,65 @@ namespace gridfire::engine {
|
||||
);
|
||||
|
||||
const fourdst::composition::MaskedComposition masked(comp, activeSpeciesSet | std::ranges::to<std::vector>());
|
||||
return m_baseEngine.generateJacobianMatrix(masked, T9, rho, activeSpecies);
|
||||
return m_baseEngine.generateJacobianMatrix(ctx, masked, T9, rho, activeSpecies);
|
||||
}
|
||||
|
||||
NetworkJacobian DefinedEngineView::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const SparsityPattern &sparsityPattern
|
||||
) const {
|
||||
validateNetworkState();
|
||||
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies | std::ranges::to<std::vector>());
|
||||
return m_baseEngine.generateJacobianMatrix(masked, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
void DefinedEngineView::generateStoichiometryMatrix() {
|
||||
validateNetworkState();
|
||||
|
||||
m_baseEngine.generateStoichiometryMatrix();
|
||||
}
|
||||
|
||||
int DefinedEngineView::getStoichiometryMatrixEntry(
|
||||
const Species& species,
|
||||
const reaction::Reaction& reaction
|
||||
) const {
|
||||
validateNetworkState();
|
||||
|
||||
if (!m_activeSpecies.contains(species)) {
|
||||
LOG_ERROR(m_logger, "Species '{}' is not part of the active species in the DefinedEngineView.", species.name());
|
||||
m_logger -> flush_log();
|
||||
throw std::runtime_error("Species not found in active species: " + std::string(species.name()));
|
||||
}
|
||||
|
||||
if (!m_activeReactions.contains(reaction)) {
|
||||
LOG_ERROR(m_logger, "Reaction '{}' is not part of the active reactions in the DefinedEngineView.", reaction.id());
|
||||
m_logger -> flush_log();
|
||||
throw std::runtime_error("Reaction not found in active reactions: " + std::string(reaction.id()));
|
||||
}
|
||||
|
||||
return m_baseEngine.getStoichiometryMatrixEntry(species, reaction);
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
const fourdst::composition::MaskedComposition masked(comp, state->active_species | std::ranges::to<std::vector>());
|
||||
return m_baseEngine.generateJacobianMatrix(ctx, masked, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
double DefinedEngineView::calculateMolarReactionFlow(
|
||||
scratch::StateBlob& ctx,
|
||||
const reaction::Reaction &reaction,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
validateNetworkState();
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
|
||||
if (!m_activeReactions.contains(reaction)) {
|
||||
if (!state->active_reactions.contains(reaction)) {
|
||||
LOG_ERROR(m_logger, "Reaction '{}' is not part of the active reactions in the DefinedEngineView.", reaction.id());
|
||||
m_logger -> flush_log();
|
||||
throw std::runtime_error("Reaction not found in active reactions: " + std::string(reaction.id()));
|
||||
}
|
||||
|
||||
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies | std::ranges::to<std::vector>());
|
||||
return m_baseEngine.calculateMolarReactionFlow(reaction, masked, T9, rho);
|
||||
const fourdst::composition::MaskedComposition masked(comp, state->active_species | std::ranges::to<std::vector>());
|
||||
return m_baseEngine.calculateMolarReactionFlow(ctx, reaction, masked, T9, rho);
|
||||
}
|
||||
|
||||
const reaction::ReactionSet & DefinedEngineView::getNetworkReactions() const {
|
||||
validateNetworkState();
|
||||
const reaction::ReactionSet & DefinedEngineView::getNetworkReactions(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
|
||||
return m_activeReactions;
|
||||
}
|
||||
|
||||
void DefinedEngineView::setNetworkReactions(const reaction::ReactionSet &reactions) {
|
||||
std::vector<std::string> peNames;
|
||||
for (const auto& reaction : reactions) {
|
||||
peNames.emplace_back(reaction->id());
|
||||
}
|
||||
collect(peNames);
|
||||
m_activeSpeciesVectorCache = std::nullopt; // Invalidate species vector cache
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
return state->active_reactions;
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<Species, double>, EngineStatus> DefinedEngineView::getSpeciesTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
validateNetworkState();
|
||||
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies | std::ranges::to<std::vector>());
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
const fourdst::composition::MaskedComposition masked(comp, state->active_species | std::ranges::to<std::vector>());
|
||||
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(masked, T9, rho, m_activeReactions);
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(ctx, masked, T9, rho, state->active_reactions);
|
||||
if (!result) {
|
||||
return std::unexpected{result.error()};
|
||||
}
|
||||
const auto& fullTimescales = result.value();
|
||||
|
||||
std::unordered_map<Species, double> definedTimescales;
|
||||
for (const auto& active_species : m_activeSpecies) {
|
||||
for (const auto& active_species : state->active_species) {
|
||||
if (fullTimescales.contains(active_species)) {
|
||||
definedTimescales[active_species] = fullTimescales.at(active_species);
|
||||
}
|
||||
@@ -192,14 +171,15 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<Species, double>, EngineStatus> DefinedEngineView::getSpeciesDestructionTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
validateNetworkState();
|
||||
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies | std::ranges::to<std::vector>());
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
const fourdst::composition::MaskedComposition masked(comp, state->active_species| std::ranges::to<std::vector>());
|
||||
|
||||
const auto result = m_baseEngine.getSpeciesDestructionTimescales(masked, T9, rho, m_activeReactions);
|
||||
const auto result = m_baseEngine.getSpeciesDestructionTimescales(ctx, masked, T9, rho, state->active_reactions);
|
||||
|
||||
if (!result) {
|
||||
return std::unexpected{result.error()};
|
||||
@@ -208,7 +188,7 @@ namespace gridfire::engine {
|
||||
const auto& destructionTimescales = result.value();
|
||||
|
||||
std::unordered_map<Species, double> definedTimescales;
|
||||
for (const auto& active_species : m_activeSpecies) {
|
||||
for (const auto& active_species : state->active_species){
|
||||
if (destructionTimescales.contains(active_species)) {
|
||||
definedTimescales[active_species] = destructionTimescales.at(active_species);
|
||||
}
|
||||
@@ -216,29 +196,28 @@ namespace gridfire::engine {
|
||||
return definedTimescales;
|
||||
}
|
||||
|
||||
fourdst::composition::Composition DefinedEngineView::update(const NetIn &netIn) {
|
||||
return m_baseEngine.update(netIn);
|
||||
fourdst::composition::Composition DefinedEngineView::project(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) const {
|
||||
return m_baseEngine.project(ctx, netIn);
|
||||
}
|
||||
|
||||
bool DefinedEngineView::isStale(const NetIn &netIn) {
|
||||
return m_baseEngine.isStale(netIn);
|
||||
screening::ScreeningType DefinedEngineView::getScreeningModel(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return m_baseEngine.getScreeningModel(ctx);
|
||||
}
|
||||
|
||||
void DefinedEngineView::setScreeningModel(const screening::ScreeningType model) {
|
||||
m_baseEngine.setScreeningModel(model);
|
||||
}
|
||||
size_t DefinedEngineView::getSpeciesIndex(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
|
||||
screening::ScreeningType DefinedEngineView::getScreeningModel() const {
|
||||
return m_baseEngine.getScreeningModel();
|
||||
}
|
||||
|
||||
size_t DefinedEngineView::getSpeciesIndex(const Species &species) const {
|
||||
// TODO: We are working to phase out all of these methods, its probably broken but it also should no longer be used and will be removed soon
|
||||
validateNetworkState();
|
||||
|
||||
const auto it = std::ranges::find(m_activeSpecies, species);
|
||||
if (it != m_activeSpecies.end()) {
|
||||
return static_cast<int>(std::distance(m_activeSpecies.begin(), it));
|
||||
const auto it = std::ranges::find(state->active_species, species);
|
||||
if (it != state->active_species.end()) {
|
||||
return static_cast<int>(std::distance(state->active_species.begin(), it));
|
||||
} else {
|
||||
LOG_ERROR(m_logger, "Species '{}' not found in active species list.", species.name());
|
||||
m_logger->flush_log();
|
||||
@@ -246,29 +225,23 @@ namespace gridfire::engine {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> DefinedEngineView::mapNetInToMolarAbundanceVector(const NetIn &netIn) const {
|
||||
std::vector<double> Y(m_activeSpecies.size(), 0.0); // Initialize with zeros
|
||||
for (const auto& [sp, y] : netIn.composition) {
|
||||
auto it = std::ranges::find(m_activeSpecies, sp);
|
||||
if (it != m_activeSpecies.end()) {
|
||||
Y[getSpeciesIndex(sp)] = y; // Map species to their molar abundance
|
||||
}
|
||||
}
|
||||
return Y; // Return the vector of molar abundances
|
||||
}
|
||||
|
||||
PrimingReport DefinedEngineView::primeEngine(const NetIn &netIn) {
|
||||
return m_baseEngine.primeEngine(netIn);
|
||||
PrimingReport DefinedEngineView::primeEngine(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) const {
|
||||
return m_baseEngine.primeEngine(ctx, netIn);
|
||||
}
|
||||
|
||||
fourdst::composition::Composition DefinedEngineView::collectComposition(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
fourdst::composition::Composition result = m_baseEngine.collectComposition(comp, T9, rho);
|
||||
fourdst::composition::Composition result = m_baseEngine.collectComposition(ctx, comp, T9, rho);
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
|
||||
for (const auto& species : m_activeSpecies) {
|
||||
for (const auto& species : state->active_species) {
|
||||
if (!result.contains(species)) {
|
||||
result.registerSpecies(species);
|
||||
}
|
||||
@@ -276,18 +249,30 @@ namespace gridfire::engine {
|
||||
return result;
|
||||
}
|
||||
|
||||
SpeciesStatus DefinedEngineView::getSpeciesStatus(const Species &species) const {
|
||||
const SpeciesStatus status = m_baseEngine.getSpeciesStatus(species);
|
||||
if (status == SpeciesStatus::ACTIVE && !m_activeSpecies.contains(species)) {
|
||||
SpeciesStatus DefinedEngineView::getSpeciesStatus(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
) const {
|
||||
const auto *state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
const SpeciesStatus status = m_baseEngine.getSpeciesStatus(ctx, species);
|
||||
if (status == SpeciesStatus::ACTIVE && !state->active_species.contains(species)) {
|
||||
return SpeciesStatus::INACTIVE_FLOW;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
std::vector<size_t> DefinedEngineView::constructSpeciesIndexMap() const {
|
||||
std::optional<StepDerivatives<double>> DefinedEngineView::getMostRecentRHSCalculation(
|
||||
scratch::StateBlob &ctx
|
||||
) const {
|
||||
return m_baseEngine.getMostRecentRHSCalculation(ctx);
|
||||
}
|
||||
|
||||
std::vector<size_t> DefinedEngineView::constructSpeciesIndexMap(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
LOG_TRACE_L3(m_logger, "Constructing species index map for DefinedEngineView...");
|
||||
std::unordered_map<Species, size_t> fullSpeciesReverseMap;
|
||||
const auto& fullSpeciesList = m_baseEngine.getNetworkSpecies();
|
||||
const auto& fullSpeciesList = m_baseEngine.getNetworkSpecies(ctx);
|
||||
|
||||
fullSpeciesReverseMap.reserve(fullSpeciesList.size());
|
||||
|
||||
@@ -296,9 +281,10 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::vector<size_t> speciesIndexMap;
|
||||
speciesIndexMap.reserve(m_activeSpecies.size());
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
speciesIndexMap.reserve(state->active_species.size());
|
||||
|
||||
for (const auto& active_species : m_activeSpecies) {
|
||||
for (const auto& active_species : state->active_species) {
|
||||
auto it = fullSpeciesReverseMap.find(active_species);
|
||||
if (it != fullSpeciesReverseMap.end()) {
|
||||
speciesIndexMap.push_back(it->second);
|
||||
@@ -313,12 +299,15 @@ namespace gridfire::engine {
|
||||
|
||||
}
|
||||
|
||||
std::vector<size_t> DefinedEngineView::constructReactionIndexMap() const {
|
||||
std::vector<size_t> DefinedEngineView::constructReactionIndexMap(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
LOG_TRACE_L3(m_logger, "Constructing reaction index map for DefinedEngineView...");
|
||||
|
||||
// --- Step 1: Create a reverse map using the reaction's unique ID as the key. ---
|
||||
std::unordered_map<std::string_view, size_t> fullReactionReverseMap;
|
||||
const auto& fullReactionSet = m_baseEngine.getNetworkReactions();
|
||||
const auto& fullReactionSet = m_baseEngine.getNetworkReactions(ctx);
|
||||
fullReactionReverseMap.reserve(fullReactionSet.size());
|
||||
|
||||
for (size_t i_full = 0; i_full < fullReactionSet.size(); ++i_full) {
|
||||
@@ -327,9 +316,9 @@ namespace gridfire::engine {
|
||||
|
||||
// --- Step 2: Build the final index map using the active reaction set. ---
|
||||
std::vector<size_t> reactionIndexMap;
|
||||
reactionIndexMap.reserve(m_activeReactions.size());
|
||||
reactionIndexMap.reserve(state->active_reactions.size());
|
||||
|
||||
for (const auto& active_reaction_ptr : m_activeReactions) {
|
||||
for (const auto& active_reaction_ptr : state->active_reactions) {
|
||||
auto it = fullReactionReverseMap.find(active_reaction_ptr->id());
|
||||
|
||||
if (it != fullReactionReverseMap.end()) {
|
||||
@@ -345,54 +334,66 @@ namespace gridfire::engine {
|
||||
return reactionIndexMap;
|
||||
}
|
||||
|
||||
std::vector<double> DefinedEngineView::mapViewToFull(const std::vector<double>& culled) const {
|
||||
std::vector<double> full(m_baseEngine.getNetworkSpecies().size(), 0.0);
|
||||
std::vector<double> DefinedEngineView::mapViewToFull(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<double>& culled
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
std::vector<double> full(m_baseEngine.getNetworkSpecies(ctx).size(), 0.0);
|
||||
for (size_t i_culled = 0; i_culled < culled.size(); ++i_culled) {
|
||||
const size_t i_full = m_speciesIndexMap[i_culled];
|
||||
const size_t i_full = state->species_index_map[i_culled];
|
||||
full[i_full] += culled[i_culled];
|
||||
}
|
||||
return full;
|
||||
}
|
||||
|
||||
std::vector<double> DefinedEngineView::mapFullToView(const std::vector<double>& full) const {
|
||||
std::vector<double> culled(m_activeSpecies.size(), 0.0);
|
||||
for (size_t i_culled = 0; i_culled < m_activeSpecies.size(); ++i_culled) {
|
||||
const size_t i_full = m_speciesIndexMap[i_culled];
|
||||
std::vector<double> DefinedEngineView::mapFullToView(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<double>& full
|
||||
) {
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
std::vector<double> culled(state->active_species.size(), 0.0);
|
||||
for (size_t i_culled = 0; i_culled < state->active_species.size(); ++i_culled) {
|
||||
const size_t i_full = state->species_index_map[i_culled];
|
||||
culled[i_culled] = full[i_full];
|
||||
}
|
||||
return culled;
|
||||
}
|
||||
|
||||
size_t DefinedEngineView::mapViewToFullSpeciesIndex(size_t culledSpeciesIndex) const {
|
||||
if (culledSpeciesIndex >= m_speciesIndexMap.size()) {
|
||||
LOG_ERROR(m_logger, "Defined index {} is out of bounds for species index map of size {}.", culledSpeciesIndex, m_speciesIndexMap.size());
|
||||
size_t DefinedEngineView::mapViewToFullSpeciesIndex(
|
||||
scratch::StateBlob& ctx,
|
||||
size_t culledSpeciesIndex
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
if (culledSpeciesIndex >= state->species_index_map.size()) {
|
||||
LOG_ERROR(m_logger, "Defined index {} is out of bounds for species index map of size {}.", culledSpeciesIndex, state->species_index_map.size());
|
||||
m_logger->flush_log();
|
||||
throw std::out_of_range("Defined index " + std::to_string(culledSpeciesIndex) + " is out of bounds for species index map of size " + std::to_string(m_speciesIndexMap.size()) + ".");
|
||||
throw std::out_of_range("Defined index " + std::to_string(culledSpeciesIndex) + " is out of bounds for species index map of size " + std::to_string(state->species_index_map.size()) + ".");
|
||||
}
|
||||
return m_speciesIndexMap[culledSpeciesIndex];
|
||||
return state->species_index_map[culledSpeciesIndex];
|
||||
}
|
||||
|
||||
size_t DefinedEngineView::mapViewToFullReactionIndex(size_t culledReactionIndex) const {
|
||||
if (culledReactionIndex >= m_reactionIndexMap.size()) {
|
||||
LOG_ERROR(m_logger, "Defined index {} is out of bounds for reaction index map of size {}.", culledReactionIndex, m_reactionIndexMap.size());
|
||||
size_t DefinedEngineView::mapViewToFullReactionIndex(
|
||||
scratch::StateBlob& ctx,
|
||||
size_t culledReactionIndex
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
if (culledReactionIndex >= state->reaction_index_map.size()) {
|
||||
LOG_ERROR(m_logger, "Defined index {} is out of bounds for reaction index map of size {}.", culledReactionIndex, state->reaction_index_map.size());
|
||||
m_logger->flush_log();
|
||||
throw std::out_of_range("Defined index " + std::to_string(culledReactionIndex) + " is out of bounds for reaction index map of size " + std::to_string(m_reactionIndexMap.size()) + ".");
|
||||
throw std::out_of_range("Defined index " + std::to_string(culledReactionIndex) + " is out of bounds for reaction index map of size " + std::to_string(state->reaction_index_map.size()) + ".");
|
||||
}
|
||||
return m_reactionIndexMap[culledReactionIndex];
|
||||
return state->reaction_index_map[culledReactionIndex];
|
||||
}
|
||||
|
||||
void DefinedEngineView::validateNetworkState() const {
|
||||
if (m_isStale) {
|
||||
LOG_ERROR(m_logger, "DefinedEngineView is stale. Please call update() with a valid NetIn object.");
|
||||
m_logger->flush_log();
|
||||
throw std::runtime_error("DefinedEngineView is stale. Please call update() with a valid NetIn object.");
|
||||
}
|
||||
}
|
||||
|
||||
void DefinedEngineView::collect(const std::vector<std::string> &peNames) {
|
||||
void DefinedEngineView::collect(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<std::string> &peNames
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::DefinedEngineViewScratchPad, true>(ctx);
|
||||
std::unordered_set<Species> seenSpecies;
|
||||
|
||||
const auto& fullNetworkReactionSet = m_baseEngine.getNetworkReactions();
|
||||
const auto& fullNetworkReactionSet = m_baseEngine.getNetworkReactions(ctx);
|
||||
for (const auto& peName : peNames) {
|
||||
if (!fullNetworkReactionSet.contains(peName)) {
|
||||
LOG_ERROR(m_logger, "Reaction with name '{}' not found in the base engine's network reactions. Aborting...", peName);
|
||||
@@ -403,16 +404,16 @@ namespace gridfire::engine {
|
||||
for (const auto& reactant : reaction->reactants()) {
|
||||
if (!seenSpecies.contains(reactant)) {
|
||||
seenSpecies.insert(reactant);
|
||||
m_activeSpecies.emplace(reactant);
|
||||
state->active_species.emplace(reactant);
|
||||
}
|
||||
}
|
||||
for (const auto& product : reaction->products()) {
|
||||
if (!seenSpecies.contains(product)) {
|
||||
seenSpecies.insert(product);
|
||||
m_activeSpecies.emplace(product);
|
||||
state->active_species.emplace(product);
|
||||
}
|
||||
}
|
||||
m_activeReactions.add_reaction(*reaction);
|
||||
state->active_reactions.add_reaction(*reaction);
|
||||
}
|
||||
LOG_TRACE_L3(m_logger, "DefinedEngineView built with {} active species and {} active reactions.", m_activeSpecies.size(), m_activeReactions.size());
|
||||
LOG_TRACE_L3(m_logger, "Active species: {}", [this]() -> std::string {
|
||||
@@ -437,9 +438,8 @@ namespace gridfire::engine {
|
||||
}
|
||||
return result;
|
||||
}());
|
||||
m_speciesIndexMap = constructSpeciesIndexMap();
|
||||
m_reactionIndexMap = constructReactionIndexMap();
|
||||
m_isStale = false;
|
||||
state->species_index_map = constructSpeciesIndexMap(ctx);
|
||||
state->reaction_index_map = constructReactionIndexMap(ctx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,10 @@
|
||||
#include "gridfire/utils/sundials.h"
|
||||
#include "gridfire/utils/logging.h"
|
||||
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
#include "gridfire/engine/scratchpads/utils.h"
|
||||
#include "gridfire/engine/scratchpads/engine_multiscale_scratchpad.h"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
#include <ranges>
|
||||
@@ -151,18 +155,6 @@ namespace {
|
||||
return reactantSample != productSample;
|
||||
}
|
||||
|
||||
void QuietErrorRouter(int line, const char *func, const char *file, const char *msg,
|
||||
SUNErrCode err_code, void *err_user_data, SUNContext sunctx) {
|
||||
|
||||
// LIST OF ERRORS TO IGNORE
|
||||
if (err_code == KIN_LINESEARCH_NONCONV) {
|
||||
return;
|
||||
}
|
||||
|
||||
// For everything else, use the default SUNDIALS logger (or your own)
|
||||
SUNLogErrHandlerFn(line, func, file, msg, err_code, err_user_data, sunctx);
|
||||
}
|
||||
|
||||
struct DisjointSet {
|
||||
std::vector<size_t> parent;
|
||||
explicit DisjointSet(const size_t n) {
|
||||
@@ -170,7 +162,7 @@ namespace {
|
||||
std::iota(parent.begin(), parent.end(), 0);
|
||||
}
|
||||
|
||||
size_t find(const size_t i) {
|
||||
size_t find(const size_t i) { // NOLINT(*-no-recursion)
|
||||
if (parent.at(i) == i) return i;
|
||||
return parent.at(i) = find(parent.at(i)); // Path compression
|
||||
}
|
||||
@@ -192,28 +184,16 @@ namespace gridfire::engine {
|
||||
MultiscalePartitioningEngineView::MultiscalePartitioningEngineView(
|
||||
DynamicEngine& baseEngine
|
||||
) : m_baseEngine(baseEngine) {
|
||||
const int flag = SUNContext_Create(SUN_COMM_NULL, &m_sun_ctx);
|
||||
if (flag != 0) {
|
||||
LOG_CRITICAL(m_logger, "Error while creating SUNContext in MultiscalePartitioningEngineView");
|
||||
throw std::runtime_error("Error creating SUNContext in MultiscalePartitioningEngineView");
|
||||
}
|
||||
SUNContext_PushErrHandler(m_sun_ctx, QuietErrorRouter, nullptr);
|
||||
}
|
||||
|
||||
MultiscalePartitioningEngineView::~MultiscalePartitioningEngineView() {
|
||||
LOG_TRACE_L1(m_logger, "Cleaning up MultiscalePartitioningEngineView...");
|
||||
m_qse_solvers.clear();
|
||||
if (m_sun_ctx) {
|
||||
SUNContext_Free(&m_sun_ctx);
|
||||
m_sun_ctx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Species> & MultiscalePartitioningEngineView::getNetworkSpecies() const {
|
||||
return m_baseEngine.getNetworkSpecies();
|
||||
const std::vector<Species> & MultiscalePartitioningEngineView::getNetworkSpecies(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return m_baseEngine.getNetworkSpecies(ctx);
|
||||
}
|
||||
|
||||
std::expected<StepDerivatives<double>, EngineStatus> MultiscalePartitioningEngineView::calculateRHSAndEnergy(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
@@ -232,7 +212,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
return ss.str();
|
||||
}());
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho, trust);
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(ctx, comp, T9, rho, trust);
|
||||
LOG_TRACE_L2(m_logger, "Equilibrated composition prior to calling base engine is {}", [&qseComposition, &comp]() -> std::string {
|
||||
std::stringstream ss;
|
||||
size_t i = 0;
|
||||
@@ -249,7 +229,7 @@ namespace gridfire::engine {
|
||||
return ss.str();
|
||||
}());
|
||||
|
||||
const auto result = m_baseEngine.calculateRHSAndEnergy(qseComposition, T9, rho, false);
|
||||
const auto result = m_baseEngine.calculateRHSAndEnergy(ctx, qseComposition, T9, rho, false);
|
||||
LOG_TRACE_L2(m_logger, "Base engine calculation of RHS and Energy complete.");
|
||||
|
||||
if (!result) {
|
||||
@@ -258,9 +238,10 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
auto deriv = result.value();
|
||||
const auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
|
||||
LOG_TRACE_L2(m_logger, "Zeroing out algebraic species derivatives.");
|
||||
for (const auto& species : m_algebraic_species) {
|
||||
for (const auto& species : state->algebraic_species) {
|
||||
deriv.dydt[species] = 0.0; // Fix the algebraic species to the equilibrium abundances we calculate.
|
||||
}
|
||||
LOG_TRACE_L2(m_logger, "Done Zeroing out algebraic species derivatives.");
|
||||
@@ -268,24 +249,28 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
EnergyDerivatives MultiscalePartitioningEngineView::calculateEpsDerivatives(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho, false);
|
||||
return m_baseEngine.calculateEpsDerivatives(qseComposition, T9, rho);
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(ctx, comp, T9, rho, false);
|
||||
return m_baseEngine.calculateEpsDerivatives(ctx, qseComposition, T9, rho);
|
||||
}
|
||||
|
||||
NetworkJacobian MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho, false);
|
||||
return m_baseEngine.generateJacobianMatrix(qseComposition, T9, rho, m_dynamic_species);
|
||||
const auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(ctx, comp, T9, rho, false);
|
||||
return m_baseEngine.generateJacobianMatrix(ctx, qseComposition, T9, rho, state->dynamic_species);
|
||||
}
|
||||
|
||||
NetworkJacobian MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
@@ -293,7 +278,7 @@ namespace gridfire::engine {
|
||||
) const {
|
||||
bool activeSpeciesIsSubset = true;
|
||||
for (const auto& species : activeSpecies) {
|
||||
if (!involvesSpecies(species)) activeSpeciesIsSubset = false;
|
||||
if (!involvesSpecies(ctx, species)) activeSpeciesIsSubset = false;
|
||||
}
|
||||
if (!activeSpeciesIsSubset) {
|
||||
std::string msg = std::format(
|
||||
@@ -301,7 +286,7 @@ namespace gridfire::engine {
|
||||
[&]() -> std::string {
|
||||
std::stringstream ss;
|
||||
for (const auto& species : activeSpecies) {
|
||||
if (!this->involvesSpecies(species)) {
|
||||
if (!involvesSpecies(ctx, species)) {
|
||||
ss << species << " ";
|
||||
}
|
||||
}
|
||||
@@ -314,114 +299,104 @@ namespace gridfire::engine {
|
||||
|
||||
std::vector<Species> dynamicActiveSpeciesIntersection;
|
||||
for (const auto& species : activeSpecies) {
|
||||
if (involvesSpeciesInDynamic(species)) {
|
||||
if (involvesSpeciesInDynamic(ctx, species)) {
|
||||
dynamicActiveSpeciesIntersection.push_back(species);
|
||||
}
|
||||
}
|
||||
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho, false);
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(ctx, comp, T9, rho, false);
|
||||
|
||||
return m_baseEngine.generateJacobianMatrix(qseComposition, T9, rho, dynamicActiveSpeciesIntersection);
|
||||
return m_baseEngine.generateJacobianMatrix(ctx, qseComposition, T9, rho, dynamicActiveSpeciesIntersection);
|
||||
}
|
||||
|
||||
NetworkJacobian MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const SparsityPattern &sparsityPattern
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho, false);
|
||||
return m_baseEngine.generateJacobianMatrix(qseComposition, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
void MultiscalePartitioningEngineView::generateStoichiometryMatrix() {
|
||||
m_baseEngine.generateStoichiometryMatrix();
|
||||
}
|
||||
|
||||
int MultiscalePartitioningEngineView::getStoichiometryMatrixEntry(
|
||||
const Species& species,
|
||||
const reaction::Reaction& reaction
|
||||
) const {
|
||||
return m_baseEngine.getStoichiometryMatrixEntry(species, reaction);
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(ctx, comp, T9, rho, false);
|
||||
return m_baseEngine.generateJacobianMatrix(ctx, qseComposition, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
double MultiscalePartitioningEngineView::calculateMolarReactionFlow(
|
||||
scratch::StateBlob& ctx,
|
||||
const reaction::Reaction &reaction,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho, false);
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(ctx, comp, T9, rho, false);
|
||||
|
||||
return m_baseEngine.calculateMolarReactionFlow(reaction, qseComposition, T9, rho);
|
||||
return m_baseEngine.calculateMolarReactionFlow(ctx, reaction, qseComposition, T9, rho);
|
||||
}
|
||||
|
||||
const reaction::ReactionSet & MultiscalePartitioningEngineView::getNetworkReactions() const {
|
||||
return m_baseEngine.getNetworkReactions();
|
||||
}
|
||||
|
||||
void MultiscalePartitioningEngineView::setNetworkReactions(const reaction::ReactionSet &reactions) {
|
||||
LOG_CRITICAL(m_logger, "setNetworkReactions is not supported in MultiscalePartitioningEngineView. Did you mean to call this on the base engine?");
|
||||
throw exceptions::UnableToSetNetworkReactionsError("setNetworkReactions is not supported in MultiscalePartitioningEngineView. Did you mean to call this on the base engine?");
|
||||
const reaction::ReactionSet & MultiscalePartitioningEngineView::getNetworkReactions(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return m_baseEngine.getNetworkReactions(ctx);
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<Species, double>, EngineStatus> MultiscalePartitioningEngineView::getSpeciesTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho, false);
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(qseComposition, T9, rho);
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(ctx, comp, T9, rho, false);
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(ctx, qseComposition, T9, rho);
|
||||
if (!result) {
|
||||
return std::unexpected{result.error()};
|
||||
}
|
||||
|
||||
const auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
std::unordered_map<Species, double> speciesTimescales = result.value();
|
||||
for (const auto& algebraicSpecies : m_algebraic_species) {
|
||||
for (const auto& algebraicSpecies : state->algebraic_species) {
|
||||
speciesTimescales[algebraicSpecies] = std::numeric_limits<double>::infinity(); // Algebraic species have infinite timescales.
|
||||
}
|
||||
return speciesTimescales;
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<Species, double>, EngineStatus> MultiscalePartitioningEngineView::getSpeciesDestructionTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho, false);
|
||||
const auto result = m_baseEngine.getSpeciesDestructionTimescales(qseComposition, T9, rho);
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(ctx, comp, T9, rho, false);
|
||||
const auto result = m_baseEngine.getSpeciesDestructionTimescales(ctx, qseComposition, T9, rho);
|
||||
if (!result) {
|
||||
return std::unexpected{result.error()};
|
||||
}
|
||||
std::unordered_map<Species, double> speciesDestructionTimescales = result.value();
|
||||
for (const auto& algebraicSpecies : m_algebraic_species) {
|
||||
const auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
for (const auto& algebraicSpecies : state->algebraic_species) {
|
||||
speciesDestructionTimescales[algebraicSpecies] = std::numeric_limits<double>::infinity(); // Algebraic species have infinite destruction timescales.
|
||||
}
|
||||
return speciesDestructionTimescales;
|
||||
}
|
||||
|
||||
fourdst::composition::Composition MultiscalePartitioningEngineView::update(const NetIn &netIn) {
|
||||
const fourdst::composition::Composition baseUpdatedComposition = m_baseEngine.update(netIn);
|
||||
fourdst::composition::Composition MultiscalePartitioningEngineView::project(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) const {
|
||||
const fourdst::composition::Composition baseUpdatedComposition = m_baseEngine.project(ctx, netIn);
|
||||
|
||||
NetIn baseUpdatedNetIn = netIn;
|
||||
baseUpdatedNetIn.composition = baseUpdatedComposition;
|
||||
fourdst::composition::Composition equilibratedComposition = partitionNetwork(baseUpdatedNetIn);
|
||||
m_composition_cache.clear();
|
||||
fourdst::composition::Composition equilibratedComposition = partitionNetwork(ctx, baseUpdatedNetIn);
|
||||
|
||||
auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
state->composition_cache.clear();
|
||||
|
||||
return equilibratedComposition;
|
||||
}
|
||||
|
||||
bool MultiscalePartitioningEngineView::isStale(const NetIn &netIn) {
|
||||
return m_baseEngine.isStale(netIn);
|
||||
}
|
||||
|
||||
void MultiscalePartitioningEngineView::setScreeningModel(
|
||||
const screening::ScreeningType model
|
||||
) {
|
||||
m_baseEngine.setScreeningModel(model);
|
||||
}
|
||||
|
||||
screening::ScreeningType MultiscalePartitioningEngineView::getScreeningModel() const {
|
||||
return m_baseEngine.getScreeningModel();
|
||||
screening::ScreeningType MultiscalePartitioningEngineView::getScreeningModel(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return m_baseEngine.getScreeningModel(ctx);
|
||||
}
|
||||
|
||||
const DynamicEngine & MultiscalePartitioningEngineView::getBaseEngine() const {
|
||||
@@ -429,8 +404,11 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::vector<std::vector<Species>> MultiscalePartitioningEngineView::analyzeTimescalePoolConnectivity(
|
||||
const std::vector<std::vector<Species>> ×cale_pools, const fourdst::composition::Composition &comp, double T9, double
|
||||
rho
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<std::vector<Species>> ×cale_pools,
|
||||
const fourdst::composition::Composition &comp,
|
||||
double T9,
|
||||
double rho
|
||||
) const {
|
||||
std::vector<std::vector<Species>> final_connected_pools;
|
||||
|
||||
@@ -440,7 +418,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
// For each timescale pool, we need to analyze connectivity.
|
||||
auto connectivity_graph = buildConnectivityGraph(pool, comp, T9, rho);
|
||||
auto connectivity_graph = buildConnectivityGraph(ctx, pool, comp, T9, rho);
|
||||
auto components = findConnectedComponentsBFS(connectivity_graph, pool);
|
||||
final_connected_pools.insert(final_connected_pools.end(), components.begin(), components.end());
|
||||
}
|
||||
@@ -449,20 +427,21 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::vector<MultiscalePartitioningEngineView::QSEGroup> MultiscalePartitioningEngineView::pruneValidatedGroups(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<QSEGroup> &groups,
|
||||
const std::vector<reaction::ReactionSet> &groupReactions,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(comp, T9, rho);
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(ctx, comp, T9, rho);
|
||||
if (!result) {
|
||||
throw std::runtime_error("Base engine returned stale error during pruneValidatedGroups timescale retrieval.");
|
||||
}
|
||||
std::unordered_map<Species, double> speciesTimescales = result.value();
|
||||
const std::unordered_map<Species, double>& speciesTimescales = result.value();
|
||||
std::vector<QSEGroup> newGroups;
|
||||
for (const auto &[group, reactions] : std::views::zip(groups, groupReactions)) {
|
||||
if (reactions.size() == 0) { // If a QSE group has gotten here it should have reactions associated with it. If it doesn't that is a serious error.
|
||||
if (reactions.empty()) { // If a QSE group has gotten here it should have reactions associated with it. If it doesn't that is a serious error.
|
||||
LOG_CRITICAL(m_logger, "No reactions specified for QSE group {} during pruning analysis.", group.toString(false));
|
||||
throw std::runtime_error("No reactions specified for QSE group " + group.toString(false) + " during pruneValidatedGroups flux analysis.");
|
||||
}
|
||||
@@ -475,7 +454,7 @@ namespace gridfire::engine {
|
||||
for (const auto& species : group.algebraic_species) {
|
||||
mean_molar_abundance += comp.getMolarAbundance(species);
|
||||
}
|
||||
mean_molar_abundance /= group.algebraic_species.size();
|
||||
mean_molar_abundance /= static_cast<double>(group.algebraic_species.size());
|
||||
{ // Safety Valve to ensure valid log scaling
|
||||
if (mean_molar_abundance <= 0) {
|
||||
LOG_CRITICAL(m_logger, "Non-positive mean molar abundance {} calculated for QSE group during pruning analysis.", mean_molar_abundance);
|
||||
@@ -484,7 +463,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
for (const auto& reaction : reactions) {
|
||||
const double flux = m_baseEngine.calculateMolarReactionFlow(*reaction, comp, T9, rho);
|
||||
const double flux = m_baseEngine.calculateMolarReactionFlow(ctx, *reaction, comp, T9, rho);
|
||||
size_t hash = reaction->hash(0);
|
||||
if (reactionFluxes.contains(hash)) {
|
||||
throw std::runtime_error("Duplicate reaction hash found during pruneValidatedGroups flux analysis.");
|
||||
@@ -624,7 +603,7 @@ namespace gridfire::engine {
|
||||
for (const auto &species : g.algebraic_species) {
|
||||
meanTimescale += speciesTimescales.at(species);
|
||||
}
|
||||
meanTimescale /= g.algebraic_species.size();
|
||||
meanTimescale /= static_cast<double>(g.algebraic_species.size());
|
||||
g.mean_timescale = meanTimescale;
|
||||
newGroups.push_back(g);
|
||||
}
|
||||
@@ -634,6 +613,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::vector<MultiscalePartitioningEngineView::QSEGroup> MultiscalePartitioningEngineView::merge_coupled_groups(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<QSEGroup> &groups,
|
||||
const std::vector<reaction::ReactionSet> &groupReactions
|
||||
) {
|
||||
@@ -688,10 +668,12 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
fourdst::composition::Composition MultiscalePartitioningEngineView::partitionNetwork(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) {
|
||||
) const {
|
||||
auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
// --- Step 0. Prime the network ---
|
||||
const PrimingReport primingReport = m_baseEngine.primeEngine(netIn);
|
||||
const PrimingReport primingReport = m_baseEngine.primeEngine(ctx, netIn);
|
||||
const fourdst::composition::Composition& comp = primingReport.primedComposition;
|
||||
const double T9 = netIn.temperature / 1e9;
|
||||
const double rho = netIn.density;
|
||||
@@ -699,25 +681,25 @@ namespace gridfire::engine {
|
||||
// --- Step 0.5 Clear previous state ---
|
||||
LOG_TRACE_L1(m_logger, "Partitioning network...");
|
||||
LOG_TRACE_L1(m_logger, "Clearing previous state...");
|
||||
m_qse_groups.clear();
|
||||
m_qse_solvers.clear();
|
||||
m_dynamic_species.clear();
|
||||
m_algebraic_species.clear();
|
||||
m_composition_cache.clear(); // We need to clear the cache now cause the same comp, temp, and density may result in a different value
|
||||
state->qse_groups.clear();
|
||||
state->qse_solvers.clear();
|
||||
state->dynamic_species.clear();
|
||||
state->algebraic_species.clear();
|
||||
state->composition_cache.clear(); // We need to clear the cache now cause the same comp, temp, and density may result in a different value
|
||||
|
||||
// --- Step 1. Identify distinct timescale regions ---
|
||||
LOG_TRACE_L1(m_logger, "Identifying fast reactions...");
|
||||
const std::vector<std::vector<Species>> timescale_pools = partitionByTimescale(comp, T9, rho);
|
||||
const std::vector<std::vector<Species>> timescale_pools = partitionByTimescale(ctx, comp, T9, rho);
|
||||
LOG_TRACE_L1(m_logger, "Found {} timescale pools.", timescale_pools.size());
|
||||
|
||||
// --- Step 2. Select the mean slowest pool as the base dynamical group ---
|
||||
LOG_TRACE_L1(m_logger, "Identifying mean slowest pool...");
|
||||
const size_t mean_slowest_pool_index = identifyMeanSlowestPool(timescale_pools, comp, T9, rho);
|
||||
const size_t mean_slowest_pool_index = identifyMeanSlowestPool(ctx, timescale_pools, comp, T9, rho);
|
||||
LOG_TRACE_L1(m_logger, "Mean slowest pool index: {}", mean_slowest_pool_index);
|
||||
|
||||
// --- Step 3. Push the slowest pool into the dynamic species list ---
|
||||
for (const auto& slowSpecies : timescale_pools[mean_slowest_pool_index]) {
|
||||
m_dynamic_species.push_back(slowSpecies);
|
||||
state->dynamic_species.push_back(slowSpecies);
|
||||
}
|
||||
|
||||
// --- Step 4. Pack Candidate QSE Groups ---
|
||||
@@ -729,40 +711,40 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Preforming connectivity analysis on timescale pools...");
|
||||
const std::vector<std::vector<Species>> connected_pools = analyzeTimescalePoolConnectivity(candidate_pools, comp, T9, rho);
|
||||
const std::vector<std::vector<Species>> connected_pools = analyzeTimescalePoolConnectivity(ctx, candidate_pools, comp, T9, rho);
|
||||
LOG_TRACE_L1(m_logger, "Found {} connected pools (compared to {} timescale pools) for QSE analysis.", connected_pools.size(), timescale_pools.size());
|
||||
|
||||
|
||||
// --- Step 5. Identify potential seed species for each candidate pool ---
|
||||
LOG_TRACE_L1(m_logger, "Identifying potential seed species for candidate pools...");
|
||||
const std::vector<QSEGroup> candidate_groups = constructCandidateGroups(connected_pools, comp, T9, rho);
|
||||
const std::vector<QSEGroup> candidate_groups = constructCandidateGroups(ctx, connected_pools, comp, T9, rho);
|
||||
LOG_TRACE_L1(m_logger, "Found {} candidate QSE groups for further analysis ({})", candidate_groups.size(), utils::iterable_to_delimited_string(candidate_groups));
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Validating candidate groups with flux analysis...");
|
||||
const auto [validated_groups, invalidate_groups, validated_group_reactions] = validateGroupsWithFluxAnalysis(candidate_groups, comp, T9, rho);
|
||||
const auto [validated_groups, invalidate_groups, validated_group_reactions] = validateGroupsWithFluxAnalysis(ctx, candidate_groups, comp, T9, rho);
|
||||
LOG_TRACE_L1(m_logger, "Validated {} group(s) QSE groups. {}", validated_groups.size(), utils::iterable_to_delimited_string(validated_groups));
|
||||
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Pruning groups based on log abundance-normalized flux analysis...");
|
||||
const std::vector<QSEGroup> prunedGroups = pruneValidatedGroups(validated_groups, validated_group_reactions, comp, T9, rho);
|
||||
const std::vector<QSEGroup> prunedGroups = pruneValidatedGroups(ctx, validated_groups, validated_group_reactions, comp, T9, rho);
|
||||
LOG_TRACE_L1(m_logger, "After Pruning remaining groups are: {}", utils::iterable_to_delimited_string(prunedGroups));
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Re-validating pruned groups with flux analysis...");
|
||||
auto [pruned_validated_groups, _, pruned_validated_reactions] = validateGroupsWithFluxAnalysis(prunedGroups, comp, T9, rho);
|
||||
auto [pruned_validated_groups, _, pruned_validated_reactions] = validateGroupsWithFluxAnalysis(ctx, prunedGroups, comp, T9, rho);
|
||||
LOG_TRACE_L1(m_logger, "After re-validation, {} QSE groups remain. ({})",pruned_validated_groups.size(), utils::iterable_to_delimited_string(pruned_validated_groups));
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Merging coupled QSE groups...");
|
||||
const std::vector<QSEGroup> merged_groups = merge_coupled_groups(pruned_validated_groups, pruned_validated_reactions);
|
||||
const std::vector<QSEGroup> merged_groups = merge_coupled_groups(ctx, pruned_validated_groups, pruned_validated_reactions);
|
||||
LOG_TRACE_L1(m_logger, "After merging, {} QSE groups remain. ({})", merged_groups.size(), utils::iterable_to_delimited_string(merged_groups));
|
||||
|
||||
m_qse_groups = pruned_validated_groups;
|
||||
state->qse_groups = pruned_validated_groups;
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Pushing all identified algebraic species into algebraic set...");
|
||||
for (const auto& group : m_qse_groups) {
|
||||
for (const auto& group : state->qse_groups) {
|
||||
// Add algebraic species to the algebraic set
|
||||
for (const auto& species : group.algebraic_species) {
|
||||
if (std::ranges::find(m_algebraic_species, species) == m_algebraic_species.end()) {
|
||||
m_algebraic_species.push_back(species);
|
||||
if (std::ranges::find(state->algebraic_species, species) == state->algebraic_species.end()) {
|
||||
state->algebraic_species.push_back(species);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -771,46 +753,47 @@ namespace gridfire::engine {
|
||||
LOG_INFO(
|
||||
m_logger,
|
||||
"Partitioning complete. Found {} dynamic species, {} algebraic (QSE) species ({}) spread over {} QSE group{}.",
|
||||
m_dynamic_species.size(),
|
||||
m_algebraic_species.size(),
|
||||
utils::iterable_to_delimited_string(m_algebraic_species),
|
||||
m_qse_groups.size(),
|
||||
m_qse_groups.size() == 1 ? "" : "s"
|
||||
state->dynamic_species.size(),
|
||||
state->algebraic_species.size(),
|
||||
utils::iterable_to_delimited_string(state->algebraic_species),
|
||||
state->qse_groups.size(),
|
||||
state->qse_groups.size() == 1 ? "" : "s"
|
||||
);
|
||||
|
||||
// Sort the QSE groups by mean timescale so that fastest groups get equilibrated first (as these may feed slower groups)
|
||||
LOG_TRACE_L1(m_logger, "Sorting algebraic set by mean timescale...");
|
||||
std::ranges::sort(m_qse_groups, [](const QSEGroup& a, const QSEGroup& b) {
|
||||
std::ranges::sort(state->qse_groups, [](const QSEGroup& a, const QSEGroup& b) {
|
||||
return a.mean_timescale < b.mean_timescale;
|
||||
});
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Finalizing dynamic species list...");
|
||||
for (const auto& species : m_baseEngine.getNetworkSpecies()) {
|
||||
const bool involvesAlgebraic = involvesSpeciesInQSE(species);
|
||||
if (std::ranges::find(m_dynamic_species, species) == m_dynamic_species.end() && !involvesAlgebraic) {
|
||||
m_dynamic_species.push_back(species);
|
||||
for (const auto& species : m_baseEngine.getNetworkSpecies(ctx)) {
|
||||
const bool involvesAlgebraic = involvesSpeciesInQSE(ctx, species);
|
||||
if (std::ranges::find(state->dynamic_species, species) == state->dynamic_species.end() && !involvesAlgebraic) {
|
||||
state->dynamic_species.push_back(species);
|
||||
}
|
||||
}
|
||||
LOG_TRACE_L1(m_logger, "Final dynamic species set: {}", utils::iterable_to_delimited_string(m_dynamic_species));
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Creating QSE solvers for each identified QSE group...");
|
||||
for (const auto& group : m_qse_groups) {
|
||||
for (const auto& group : state->qse_groups) {
|
||||
std::vector<Species> groupAlgebraicSpecies;
|
||||
for (const auto& species : group.algebraic_species) {
|
||||
groupAlgebraicSpecies.push_back(species);
|
||||
}
|
||||
m_qse_solvers.push_back(std::make_unique<QSESolver>(groupAlgebraicSpecies, m_baseEngine, m_sun_ctx));
|
||||
state->qse_solvers.push_back(std::make_unique<QSESolver>(groupAlgebraicSpecies, m_baseEngine, state->sun_ctx));
|
||||
}
|
||||
LOG_TRACE_L1(m_logger, "{} QSE solvers created.", m_qse_solvers.size());
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Calculating final equilibrated composition...");
|
||||
fourdst::composition::Composition result = getNormalizedEquilibratedComposition(comp, T9, rho, false);
|
||||
fourdst::composition::Composition result = getNormalizedEquilibratedComposition(ctx, comp, T9, rho, false);
|
||||
LOG_TRACE_L1(m_logger, "Final equilibrated composition calculated...");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void MultiscalePartitioningEngineView::exportToDot(
|
||||
scratch::StateBlob &ctx,
|
||||
const std::string &filename,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
@@ -822,22 +805,24 @@ namespace gridfire::engine {
|
||||
throw std::runtime_error("Failed to open file for writing: " + filename);
|
||||
}
|
||||
|
||||
const auto& all_species = m_baseEngine.getNetworkSpecies();
|
||||
const auto& all_reactions = m_baseEngine.getNetworkReactions();
|
||||
const auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
|
||||
const auto& all_species = m_baseEngine.getNetworkSpecies(ctx);
|
||||
const auto& all_reactions = m_baseEngine.getNetworkReactions(ctx);
|
||||
|
||||
// --- 1. Pre-computation and Categorization ---
|
||||
|
||||
// Categorize species into algebraic, seed, and core dynamic
|
||||
std::unordered_set<Species> algebraic_species;
|
||||
std::unordered_set<Species> seed_species;
|
||||
for (const auto& group : m_qse_groups) {
|
||||
for (const auto& group : state->qse_groups) {
|
||||
if (group.is_in_equilibrium) {
|
||||
algebraic_species.insert(group.algebraic_species.begin(), group.algebraic_species.end());
|
||||
seed_species.insert(group.seed_species.begin(), group.seed_species.end());
|
||||
}
|
||||
}
|
||||
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho, false);
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(ctx, comp, T9, rho, false);
|
||||
// Calculate reaction flows and find min/max for logarithmic scaling of transparency
|
||||
std::vector<double> reaction_flows;
|
||||
reaction_flows.reserve(all_reactions.size());
|
||||
@@ -845,7 +830,7 @@ namespace gridfire::engine {
|
||||
double max_log_flow = std::numeric_limits<double>::lowest();
|
||||
|
||||
for (const auto& reaction : all_reactions) {
|
||||
double flow = std::abs(m_baseEngine.calculateMolarReactionFlow(*reaction, qseComposition, T9, rho));
|
||||
double flow = std::abs(m_baseEngine.calculateMolarReactionFlow(ctx, *reaction, qseComposition, T9, rho));
|
||||
reaction_flows.push_back(flow);
|
||||
if (flow > 1e-99) { // Avoid log(0)
|
||||
double log_flow = std::log10(flow);
|
||||
@@ -875,7 +860,7 @@ namespace gridfire::engine {
|
||||
fillcolor = "#e0f2fe"; // Light Blue: Algebraic (in QSE)
|
||||
} else if (seed_species.contains(species)) {
|
||||
fillcolor = "#a7f3d0"; // Light Green: Seed (Dynamic, feeds a QSE group)
|
||||
} else if (std::ranges::contains(m_dynamic_species, species)) {
|
||||
} else if (std::ranges::contains(state->dynamic_species, species)) {
|
||||
fillcolor = "#dcfce7"; // Pale Green: Core Dynamic
|
||||
}
|
||||
dotFile << " \"" << species.name() << "\" [label=\"" << species.name() << "\", fillcolor=\"" << fillcolor << "\"];\n";
|
||||
@@ -918,7 +903,7 @@ namespace gridfire::engine {
|
||||
// Draw a prominent box around the algebraic species of each valid QSE group.
|
||||
dotFile << " // --- QSE Group Clusters ---\n";
|
||||
int group_counter = 0;
|
||||
for (const auto& group : m_qse_groups) {
|
||||
for (const auto& group : state->qse_groups) {
|
||||
if (!group.is_in_equilibrium || group.algebraic_species.empty()) {
|
||||
continue;
|
||||
}
|
||||
@@ -1019,58 +1004,64 @@ namespace gridfire::engine {
|
||||
dotFile.close();
|
||||
}
|
||||
|
||||
|
||||
std::vector<double> MultiscalePartitioningEngineView::mapNetInToMolarAbundanceVector(const NetIn &netIn) const {
|
||||
std::vector<double> Y(netIn.composition.size(), 0.0); // Initialize with zeros
|
||||
for (const auto& [sp, y] : netIn.composition) {
|
||||
Y[getSpeciesIndex(sp)] = y; // Map species to their molar abundance
|
||||
}
|
||||
return Y; // Return the vector of molar abundances
|
||||
}
|
||||
|
||||
std::vector<Species> MultiscalePartitioningEngineView::getFastSpecies() const {
|
||||
const auto& all_species = m_baseEngine.getNetworkSpecies();
|
||||
std::vector<Species> MultiscalePartitioningEngineView::getFastSpecies(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
const auto& all_species = m_baseEngine.getNetworkSpecies(ctx);
|
||||
const auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
std::vector<Species> fast_species;
|
||||
fast_species.reserve(all_species.size() - m_dynamic_species.size());
|
||||
fast_species.reserve(all_species.size() - state->dynamic_species.size());
|
||||
for (const auto& species : all_species) {
|
||||
auto it = std::ranges::find(m_dynamic_species, species);
|
||||
if (it == m_dynamic_species.end()) {
|
||||
auto it = std::ranges::find(state->dynamic_species, species);
|
||||
if (it == state->dynamic_species.end()) {
|
||||
fast_species.push_back(species);
|
||||
}
|
||||
}
|
||||
return fast_species;
|
||||
}
|
||||
|
||||
const std::vector<Species> & MultiscalePartitioningEngineView::getDynamicSpecies() const {
|
||||
return m_dynamic_species;
|
||||
const std::vector<Species> & MultiscalePartitioningEngineView::getDynamicSpecies(
|
||||
scratch::StateBlob& ctx
|
||||
) {
|
||||
const auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
return state->dynamic_species;
|
||||
}
|
||||
|
||||
PrimingReport MultiscalePartitioningEngineView::primeEngine(const NetIn &netIn) {
|
||||
return m_baseEngine.primeEngine(netIn);
|
||||
PrimingReport MultiscalePartitioningEngineView::primeEngine(
|
||||
scratch::StateBlob& ctx,
|
||||
const NetIn &netIn
|
||||
) const {
|
||||
return m_baseEngine.primeEngine(ctx, netIn);
|
||||
}
|
||||
|
||||
|
||||
bool MultiscalePartitioningEngineView::involvesSpecies(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
) const {
|
||||
if (involvesSpeciesInQSE(species)) return true; // check this first since the vector is likely to be smaller so short circuit cost is less on average
|
||||
if (involvesSpeciesInDynamic(species)) return true;
|
||||
) {
|
||||
if (involvesSpeciesInQSE(ctx, species)) return true; // check this first since the vector is likely to be smaller so short circuit cost is less on average
|
||||
if (involvesSpeciesInDynamic(ctx, species)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MultiscalePartitioningEngineView::involvesSpeciesInQSE(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
) const {
|
||||
return std::ranges::find(m_algebraic_species, species) != m_algebraic_species.end();
|
||||
) {
|
||||
const auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
return std::ranges::find(state->algebraic_species, species) != state->algebraic_species.end();
|
||||
}
|
||||
|
||||
bool MultiscalePartitioningEngineView::involvesSpeciesInDynamic(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
) const {
|
||||
return std::ranges::find(m_dynamic_species, species) != m_dynamic_species.end();
|
||||
) {
|
||||
const auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
return std::ranges::find(state->dynamic_species, species) != state->dynamic_species.end();
|
||||
}
|
||||
|
||||
fourdst::composition::Composition MultiscalePartitioningEngineView::getNormalizedEquilibratedComposition(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract& comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
@@ -1086,54 +1077,69 @@ namespace gridfire::engine {
|
||||
std::hash<double>()(rho)
|
||||
};
|
||||
|
||||
auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
const uint64_t composite_hash = XXHash64::hash(hashes.begin(), sizeof(uint64_t) * 3, 0);
|
||||
if (m_composition_cache.contains(composite_hash)) {
|
||||
if (state->composition_cache.contains(composite_hash)) {
|
||||
LOG_TRACE_L3(m_logger, "Cache Hit in Multiscale Partitioning Engine View for composition at T9 = {}, rho = {}.", T9, rho);
|
||||
return m_composition_cache.at(composite_hash);
|
||||
return state->composition_cache.at(composite_hash);
|
||||
}
|
||||
LOG_TRACE_L3(m_logger, "Cache Miss in Multiscale Partitioning Engine View for composition at T9 = {}, rho = {}. Solving QSE abundances...", T9, rho);
|
||||
|
||||
// Only solve if the composition and thermodynamic conditions have not been cached yet
|
||||
fourdst::composition::Composition qseComposition(solveQSEAbundances(comp, T9, rho));
|
||||
fourdst::composition::Composition qseComposition(solveQSEAbundances(ctx, comp, T9, rho));
|
||||
|
||||
m_composition_cache[composite_hash] = qseComposition;
|
||||
state->composition_cache[composite_hash] = qseComposition;
|
||||
|
||||
return qseComposition;
|
||||
}
|
||||
|
||||
fourdst::composition::Composition MultiscalePartitioningEngineView::collectComposition(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition result = m_baseEngine.collectComposition(comp, T9, rho);
|
||||
const fourdst::composition::Composition result = m_baseEngine.collectComposition(ctx, comp, T9, rho);
|
||||
|
||||
fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(result, T9, rho, false);
|
||||
fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(ctx, result, T9, rho, false);
|
||||
|
||||
return qseComposition;
|
||||
}
|
||||
|
||||
SpeciesStatus MultiscalePartitioningEngineView::getSpeciesStatus(const Species &species) const {
|
||||
const SpeciesStatus status = m_baseEngine.getSpeciesStatus(species);
|
||||
if (status == SpeciesStatus::ACTIVE && involvesSpeciesInQSE(species)) {
|
||||
SpeciesStatus MultiscalePartitioningEngineView::getSpeciesStatus(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
) const {
|
||||
const SpeciesStatus status = m_baseEngine.getSpeciesStatus(ctx, species);
|
||||
if (status == SpeciesStatus::ACTIVE && involvesSpeciesInQSE(ctx, species)) {
|
||||
return SpeciesStatus::EQUILIBRIUM;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t MultiscalePartitioningEngineView::getSpeciesIndex(const Species &species) const {
|
||||
return m_baseEngine.getSpeciesIndex(species);
|
||||
std::optional<StepDerivatives<double>> MultiscalePartitioningEngineView::getMostRecentRHSCalculation(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
return m_baseEngine.getMostRecentRHSCalculation(ctx);
|
||||
}
|
||||
|
||||
size_t MultiscalePartitioningEngineView::getSpeciesIndex(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
) const {
|
||||
return m_baseEngine.getSpeciesIndex(ctx, species);
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::vector<Species>> MultiscalePartitioningEngineView::partitionByTimescale(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
LOG_TRACE_L1(m_logger, "Partitioning by timescale...");
|
||||
const auto destructionTimescale= m_baseEngine.getSpeciesDestructionTimescales(comp, T9, rho);
|
||||
const auto netTimescale = m_baseEngine.getSpeciesTimescales(comp, T9, rho);
|
||||
const auto destructionTimescale= m_baseEngine.getSpeciesDestructionTimescales(ctx, comp, T9, rho);
|
||||
const auto netTimescale = m_baseEngine.getSpeciesTimescales(ctx, comp, T9, rho);
|
||||
|
||||
if (!destructionTimescale || !netTimescale) {
|
||||
LOG_CRITICAL(m_logger, "Failed to compute species timescales for partitioning due to base engine error.");
|
||||
@@ -1155,7 +1161,7 @@ namespace gridfire::engine {
|
||||
}()
|
||||
);
|
||||
|
||||
const auto& all_species = m_baseEngine.getNetworkSpecies();
|
||||
const auto& all_species = m_baseEngine.getNetworkSpecies(ctx);
|
||||
|
||||
std::vector<std::pair<double, Species>> sorted_destruction_timescales;
|
||||
for (const auto & species : all_species) {
|
||||
@@ -1311,6 +1317,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::pair<bool, reaction::ReactionSet> MultiscalePartitioningEngineView::group_is_a_qse_cluster(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
@@ -1332,8 +1339,8 @@ namespace gridfire::engine {
|
||||
double coupling_flux = 0.0;
|
||||
double leakage_flux = 0.0;
|
||||
|
||||
for (const auto& reaction: m_baseEngine.getNetworkReactions()) {
|
||||
const double flow = std::abs(m_baseEngine.calculateMolarReactionFlow(*reaction, comp, T9, rho));
|
||||
for (const auto& reaction: m_baseEngine.getNetworkReactions(ctx)) {
|
||||
const double flow = std::abs(m_baseEngine.calculateMolarReactionFlow(ctx, *reaction, comp, T9, rho));
|
||||
if (flow == 0.0) {
|
||||
continue; // Skip reactions with zero flow
|
||||
}
|
||||
@@ -1422,10 +1429,11 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
bool MultiscalePartitioningEngineView::group_is_a_qse_pipeline(
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const QSEGroup &group
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const QSEGroup &group
|
||||
) const {
|
||||
// Total fluxes (Standard check)
|
||||
double total_prod = 0.0;
|
||||
@@ -1435,8 +1443,8 @@ namespace gridfire::engine {
|
||||
double charged_prod = 0.0;
|
||||
double charged_dest = 0.0;
|
||||
|
||||
for (const auto& reaction : m_baseEngine.getNetworkReactions()) {
|
||||
const double flow = m_baseEngine.calculateMolarReactionFlow(*reaction, comp, T9, rho);
|
||||
for (const auto& reaction : m_baseEngine.getNetworkReactions(ctx)) {
|
||||
const double flow = m_baseEngine.calculateMolarReactionFlow(ctx, *reaction, comp, T9, rho);
|
||||
if (std::abs(flow) < 1.0e-99) continue;
|
||||
|
||||
int groupNetStoichiometry = 0;
|
||||
@@ -1476,6 +1484,7 @@ namespace gridfire::engine {
|
||||
|
||||
|
||||
MultiscalePartitioningEngineView::FluxValidationResult MultiscalePartitioningEngineView::validateGroupsWithFluxAnalysis(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<QSEGroup> &candidate_groups,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9, const double rho
|
||||
@@ -1487,10 +1496,10 @@ namespace gridfire::engine {
|
||||
group_reactions.reserve(candidate_groups.size());
|
||||
for (auto& group : candidate_groups) {
|
||||
// Values for measuring the flux coupling vs leakage
|
||||
auto [leakage_coupled, group_reaction_set] = group_is_a_qse_cluster(comp, T9, rho, group);
|
||||
auto [leakage_coupled, group_reaction_set] = group_is_a_qse_cluster(ctx, comp, T9, rho, group);
|
||||
|
||||
|
||||
bool is_flow_balanced = group_is_a_qse_pipeline(comp, T9, rho, group);
|
||||
bool is_flow_balanced = group_is_a_qse_pipeline(ctx, comp, T9, rho, group);
|
||||
|
||||
if (leakage_coupled) {
|
||||
LOG_TRACE_L1(m_logger, "{} is in equilibrium due to high coupling flux", group.toString(false));
|
||||
@@ -1516,21 +1525,23 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
fourdst::composition::Composition MultiscalePartitioningEngineView::solveQSEAbundances(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
LOG_TRACE_L2(m_logger, "Solving for QSE abundances...");
|
||||
auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
|
||||
|
||||
fourdst::composition::Composition outputComposition(comp);
|
||||
|
||||
std::vector<Species> species;
|
||||
std::vector<double> abundances;
|
||||
species.reserve(m_algebraic_species.size());
|
||||
abundances.reserve(m_algebraic_species.size());
|
||||
species.reserve(state->algebraic_species.size());
|
||||
abundances.reserve(state->algebraic_species.size());
|
||||
|
||||
for (const auto& [group, solver]: std::views::zip(m_qse_groups, m_qse_solvers)) {
|
||||
const fourdst::composition::Composition& groupResult = solver->solve(outputComposition, T9, rho);
|
||||
for (const auto& [group, solver]: std::views::zip(state->qse_groups, state->qse_solvers)) {
|
||||
const fourdst::composition::Composition& groupResult = solver->solve(ctx, outputComposition, T9, rho);
|
||||
for (const auto& [sp, y] : groupResult) {
|
||||
if (!std::isfinite(y)) {
|
||||
LOG_CRITICAL(m_logger, "Non-finite abundance {} computed for species {} in QSE group solve at T9 = {}, rho = {}.",
|
||||
@@ -1553,12 +1564,13 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
size_t MultiscalePartitioningEngineView::identifyMeanSlowestPool(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<std::vector<Species>> &pools,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const auto& result = m_baseEngine.getSpeciesDestructionTimescales(comp, T9, rho);
|
||||
const auto& result = m_baseEngine.getSpeciesDestructionTimescales(ctx, comp, T9, rho);
|
||||
if (!result) {
|
||||
LOG_CRITICAL(m_logger, "Failed to get species destruction timescales due base engine failure");
|
||||
m_logger->flush_log();
|
||||
@@ -1603,6 +1615,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::unordered_map<Species, std::vector<Species>> MultiscalePartitioningEngineView::buildConnectivityGraph(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<Species> &species_pool,
|
||||
const fourdst::composition::Composition &comp,
|
||||
double T9,
|
||||
@@ -1622,7 +1635,7 @@ namespace gridfire::engine {
|
||||
std::map<size_t, std::vector<reaction::LogicalReaclibReaction*>> speciesReactionMap;
|
||||
std::vector<const reaction::LogicalReaclibReaction*> candidate_reactions;
|
||||
|
||||
for (const auto& reaction : m_baseEngine.getNetworkReactions()) {
|
||||
for (const auto& reaction : m_baseEngine.getNetworkReactions(ctx)) {
|
||||
const std::vector<Species> &reactants = reaction->reactants();
|
||||
const std::vector<Species> &products = reaction->products();
|
||||
|
||||
@@ -1660,13 +1673,14 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
std::vector<MultiscalePartitioningEngineView::QSEGroup> MultiscalePartitioningEngineView::constructCandidateGroups(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<std::vector<Species>> &candidate_pools,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const auto& all_reactions = m_baseEngine.getNetworkReactions();
|
||||
const auto& result = m_baseEngine.getSpeciesDestructionTimescales(comp, T9, rho);
|
||||
const auto& all_reactions = m_baseEngine.getNetworkReactions(ctx);
|
||||
const auto& result = m_baseEngine.getSpeciesDestructionTimescales(ctx, comp, T9, rho);
|
||||
if (!result) {
|
||||
LOG_ERROR(m_logger, "Failed to get species destruction timescales due base engine failure");
|
||||
m_logger->flush_log();
|
||||
@@ -1694,7 +1708,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
}
|
||||
if (has_external_reactant) {
|
||||
double flow = std::abs(m_baseEngine.calculateMolarReactionFlow(*reaction, comp, T9, rho));
|
||||
double flow = std::abs(m_baseEngine.calculateMolarReactionFlow(ctx, *reaction, comp, T9, rho));
|
||||
LOG_TRACE_L3(m_logger, "Found bridge reaction {} with flow {} for species {}.", reaction->id(), flow, ash.name());
|
||||
bridge_reactions.emplace_back(reaction.get(), flow);
|
||||
}
|
||||
@@ -1872,6 +1886,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
fourdst::composition::Composition MultiscalePartitioningEngineView::QSESolver::solve(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
@@ -1885,7 +1900,8 @@ namespace gridfire::engine {
|
||||
result,
|
||||
m_speciesMap,
|
||||
m_species,
|
||||
*this
|
||||
*this,
|
||||
ctx
|
||||
};
|
||||
|
||||
utils::check_sundials_flag(KINSetUserData(m_kinsol_mem, &data), "KINSetUserData", utils::SUNDIALS_RET_CODE_TYPES::KINSOL);
|
||||
@@ -1905,9 +1921,9 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
StepDerivatives<double> rhsGuess;
|
||||
auto cached_rhs = m_engine.getMostRecentRHSCalculation();
|
||||
auto cached_rhs = m_engine.getMostRecentRHSCalculation(ctx);
|
||||
if (!cached_rhs) {
|
||||
const auto initial_rhs = m_engine.calculateRHSAndEnergy(result, T9, rho, false);
|
||||
const auto initial_rhs = m_engine.calculateRHSAndEnergy(ctx, result, T9, rho, false);
|
||||
if (!initial_rhs) {
|
||||
throw std::runtime_error("In QSE solver failed to calculate initial RHS for caching");
|
||||
}
|
||||
@@ -2063,6 +2079,16 @@ namespace gridfire::engine {
|
||||
getLogger()->flush_log(true);
|
||||
}
|
||||
|
||||
std::unique_ptr<MultiscalePartitioningEngineView::QSESolver> MultiscalePartitioningEngineView::QSESolver::clone() const {
|
||||
auto new_solver = std::make_unique<QSESolver>(m_species, m_engine, m_sun_ctx);
|
||||
return new_solver;
|
||||
}
|
||||
|
||||
std::unique_ptr<MultiscalePartitioningEngineView::QSESolver> MultiscalePartitioningEngineView::QSESolver::clone(SUNContext sun_ctx) const {
|
||||
auto new_solver = std::make_unique<QSESolver>(m_species, m_engine, sun_ctx);
|
||||
return new_solver;
|
||||
}
|
||||
|
||||
|
||||
int MultiscalePartitioningEngineView::QSESolver::sys_func(
|
||||
const N_Vector y,
|
||||
@@ -2086,7 +2112,7 @@ namespace gridfire::engine {
|
||||
data->comp.setMolarAbundance(species, y_data[index]);
|
||||
}
|
||||
|
||||
const auto result = data->engine.calculateRHSAndEnergy(data->comp, data->T9, data->rho, false);
|
||||
const auto result = data->engine.calculateRHSAndEnergy(data->ctx, data->comp, data->T9, data->rho, false);
|
||||
|
||||
if (!result) {
|
||||
return 1; // Potentially recoverable error
|
||||
@@ -2102,7 +2128,7 @@ namespace gridfire::engine {
|
||||
for (const auto &s: map | std::views::keys) {
|
||||
const double v = dydt.at(s);
|
||||
if (!std::isfinite(v)) {
|
||||
invalid_species.push_back(std::make_pair(s, v));
|
||||
invalid_species.emplace_back(s, v);
|
||||
}
|
||||
}
|
||||
std::string msg = std::format("Non-finite dydt values encountered for species: {}",
|
||||
@@ -2150,6 +2176,7 @@ namespace gridfire::engine {
|
||||
}
|
||||
|
||||
const NetworkJacobian jac = data->engine.generateJacobianMatrix(
|
||||
data->ctx,
|
||||
data->comp,
|
||||
data->T9,
|
||||
data->rho,
|
||||
@@ -2159,9 +2186,6 @@ namespace gridfire::engine {
|
||||
sunrealtype* J_data = SUNDenseMatrix_Data(J);
|
||||
const sunindextype N = SUNDenseMatrix_Columns(J);
|
||||
|
||||
if (data->row_scaling_factors.size() != static_cast<size_t>(N)) {
|
||||
data->row_scaling_factors.resize(N, 0.0);
|
||||
}
|
||||
|
||||
for (const auto& [row_species, row_idx]: map) {
|
||||
double max_value = std::numeric_limits<double>::lowest();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "gridfire/engine/views/engine_priming.h"
|
||||
#include "gridfire/solver/solver.h"
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
|
||||
#include "fourdst/atomic/species.h"
|
||||
|
||||
@@ -11,16 +11,17 @@
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace gridfire::engine {
|
||||
using fourdst::atomic::species;
|
||||
|
||||
NetworkPrimingEngineView::NetworkPrimingEngineView(
|
||||
scratch::StateBlob& ctx,
|
||||
const std::string &primingSymbol,
|
||||
GraphEngine &baseEngine
|
||||
) :
|
||||
DefinedEngineView(
|
||||
constructPrimingReactionSet(
|
||||
ctx,
|
||||
species.at(primingSymbol),
|
||||
baseEngine
|
||||
),
|
||||
@@ -29,26 +30,27 @@ namespace gridfire::engine {
|
||||
m_primingSpecies(species.at(primingSymbol)) {}
|
||||
|
||||
NetworkPrimingEngineView::NetworkPrimingEngineView(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::atomic::Species &primingSpecies,
|
||||
GraphEngine &baseEngine
|
||||
) :
|
||||
DefinedEngineView(
|
||||
constructPrimingReactionSet(
|
||||
ctx,
|
||||
primingSpecies,
|
||||
baseEngine
|
||||
),
|
||||
baseEngine
|
||||
),
|
||||
m_primingSpecies(primingSpecies) {
|
||||
}
|
||||
|
||||
m_primingSpecies(primingSpecies) {}
|
||||
|
||||
std::vector<std::string> NetworkPrimingEngineView::constructPrimingReactionSet(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::atomic::Species &primingSpecies,
|
||||
const GraphEngine &baseEngine
|
||||
) const {
|
||||
std::unordered_set<std::string> primeReactions;
|
||||
for (const auto &reaction : baseEngine.getNetworkReactions()) {
|
||||
for (const auto &reaction : baseEngine.getNetworkReactions(ctx)) {
|
||||
if (reaction->contains(primingSpecies)) {
|
||||
primeReactions.insert(std::string(reaction->id()));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <optional>
|
||||
|
||||
#include "gridfire/engine/engine_abstract.h"
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
@@ -137,8 +138,11 @@ namespace gridfire::io::gen {
|
||||
|
||||
}
|
||||
|
||||
std::string exportEngineToPy(const engine::DynamicEngine& engine) {
|
||||
auto reactions = engine.getNetworkReactions();
|
||||
std::string exportEngineToPy(
|
||||
engine::scratch::StateBlob& ctx,
|
||||
const engine::DynamicEngine& engine
|
||||
) {
|
||||
auto reactions = engine.getNetworkReactions(ctx);
|
||||
std::vector<std::string> functions;
|
||||
functions.emplace_back(R"(import numpy as np
|
||||
from typing import Dict, List, Tuple, Callable)");
|
||||
@@ -150,8 +154,8 @@ from typing import Dict, List, Tuple, Callable)");
|
||||
return join<std::string>(functions, "\n\n");
|
||||
}
|
||||
|
||||
void exportEngineToPy(const engine::DynamicEngine &engine, const std::string &fileName) {
|
||||
const std::string funcCode = exportEngineToPy(engine);
|
||||
void exportEngineToPy(engine::scratch::StateBlob &ctx, const engine::DynamicEngine &engine, const std::string &fileName) {
|
||||
const std::string funcCode = exportEngineToPy(ctx, engine);
|
||||
std::ofstream outFile(fileName);
|
||||
outFile << funcCode;
|
||||
outFile.close();
|
||||
|
||||
@@ -5,6 +5,13 @@
|
||||
#include "gridfire/engine/engine_abstract.h"
|
||||
#include "gridfire/engine/engine_graph.h"
|
||||
#include "gridfire/engine/views/engine_views.h"
|
||||
#include "gridfire/utils/logging.h"
|
||||
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
#include "gridfire/engine/scratchpads/utils.h"
|
||||
#include "gridfire/engine/scratchpads/engine_graph_scratchpad.h"
|
||||
#include "gridfire/engine/scratchpads/engine_adaptive_scratchpad.h"
|
||||
#include "gridfire/engine/scratchpads/engine_multiscale_scratchpad.h"
|
||||
|
||||
#include "fourdst/atomic/species.h"
|
||||
#include "fourdst/composition/utils.h"
|
||||
@@ -47,17 +54,13 @@ namespace gridfire::policy {
|
||||
m_partition_function = build_partition_function();
|
||||
}
|
||||
|
||||
engine::DynamicEngine& MainSequencePolicy::construct() {
|
||||
ConstructionResults MainSequencePolicy::construct() {
|
||||
m_network_stack.clear();
|
||||
|
||||
m_network_stack.emplace_back(
|
||||
std::make_unique<engine::GraphEngine>(m_initializing_composition, *m_partition_function, engine::NetworkBuildDepth::ThirdOrder, engine::NetworkConstructionFlags::DEFAULT)
|
||||
);
|
||||
|
||||
auto& graphRepr = dynamic_cast<engine::GraphEngine&>(*m_network_stack.back().get());
|
||||
graphRepr.setUseReverseReactions(false);
|
||||
|
||||
|
||||
m_network_stack.emplace_back(
|
||||
std::make_unique<engine::MultiscalePartitioningEngineView>(*m_network_stack.back().get())
|
||||
);
|
||||
@@ -65,8 +68,9 @@ namespace gridfire::policy {
|
||||
std::make_unique<engine::AdaptiveEngineView>(*m_network_stack.back().get())
|
||||
);
|
||||
|
||||
std::unique_ptr<engine::scratch::StateBlob> scratch_blob = get_stack_scratch_blob();
|
||||
m_status = NetworkPolicyStatus::INITIALIZED_UNVERIFIED;
|
||||
m_status = check_status();
|
||||
m_status = check_status(*scratch_blob);
|
||||
|
||||
switch (m_status) {
|
||||
case NetworkPolicyStatus::MISSING_KEY_REACTION:
|
||||
@@ -80,7 +84,7 @@ namespace gridfire::policy {
|
||||
case NetworkPolicyStatus::INITIALIZED_VERIFIED:
|
||||
break;
|
||||
}
|
||||
return *m_network_stack.back();
|
||||
return {.engine = *m_network_stack.back(), .scratch_blob = std::move(scratch_blob)};
|
||||
}
|
||||
|
||||
inline std::unique_ptr<partition::PartitionFunction> MainSequencePolicy::build_partition_function() {
|
||||
@@ -115,13 +119,49 @@ namespace gridfire::policy {
|
||||
return m_partition_function;
|
||||
}
|
||||
|
||||
inline NetworkPolicyStatus MainSequencePolicy::check_status() const {
|
||||
std::unique_ptr<engine::scratch::StateBlob> MainSequencePolicy::get_stack_scratch_blob() const {
|
||||
if (m_network_stack.empty()) {
|
||||
throw exceptions::PolicyError("Cannot get stack scratch blob from MainSequencePolicy: Engine stack is empty. Call construct() first.");
|
||||
}
|
||||
auto blob = std::make_unique<engine::scratch::StateBlob>();
|
||||
blob->enroll<engine::scratch::GraphEngineScratchPad>();
|
||||
blob->enroll<engine::scratch::AdaptiveEngineViewScratchPad>();
|
||||
blob->enroll<engine::scratch::MultiscalePartitioningEngineViewScratchPad>();
|
||||
|
||||
|
||||
const engine::GraphEngine* graph_engine = dynamic_cast<engine::GraphEngine*>(m_network_stack.front().get());
|
||||
if (!graph_engine) {
|
||||
throw exceptions::PolicyError("Cannot get stack scratch blob from MainSequencePolicy: The base engine is not a GraphEngine. This indicates a serious internal inconsistency and should be reported to the GridFire developers, thank you.");
|
||||
}
|
||||
|
||||
const engine::MultiscalePartitioningEngineView* multiscale_engine = dynamic_cast<engine::MultiscalePartitioningEngineView*>(m_network_stack[1].get());
|
||||
if (!multiscale_engine) {
|
||||
throw exceptions::PolicyError("Cannot get stack scratch blob from MainSequencePolicy: The middle engine is not a MultiscalePartitioningEngineView. This indicates a serious internal inconsistency and should be reported to the GridFire developers, thank you.");
|
||||
}
|
||||
|
||||
const engine::AdaptiveEngineView* adaptive_engine = dynamic_cast<engine::AdaptiveEngineView*>(m_network_stack.back().get());
|
||||
if (!adaptive_engine) {
|
||||
throw exceptions::PolicyError("Cannot get stack scratch blob from MainSequencePolicy: The top engine is not an AdaptiveEngineView. This indicates a serious internal inconsistency and should be reported to the GridFire developers, thank you.");
|
||||
}
|
||||
|
||||
|
||||
auto* graph_engine_state = engine::scratch::get_state<engine::scratch::GraphEngineScratchPad, false>(*blob);
|
||||
graph_engine_state->initialize(*graph_engine);
|
||||
auto* multiscale_engine_state = engine::scratch::get_state<engine::scratch::MultiscalePartitioningEngineViewScratchPad, false>(*blob);
|
||||
multiscale_engine_state->initialize();
|
||||
auto* adaptive_engine_state = engine::scratch::get_state<engine::scratch::AdaptiveEngineViewScratchPad, false>(*blob);
|
||||
adaptive_engine_state->initialize(*adaptive_engine);
|
||||
|
||||
return blob;
|
||||
}
|
||||
|
||||
inline NetworkPolicyStatus MainSequencePolicy::check_status(engine::scratch::StateBlob& ctx) const {
|
||||
for (const auto& species : m_seed_species) {
|
||||
if (!m_initializing_composition.contains(species)) {
|
||||
return NetworkPolicyStatus::MISSING_KEY_SPECIES;
|
||||
}
|
||||
}
|
||||
const reaction::ReactionSet& baseReactions = m_network_stack.front()->getNetworkReactions();
|
||||
const reaction::ReactionSet& baseReactions = m_network_stack.front()->getNetworkReactions(ctx);
|
||||
for (const auto& reaction : m_reaction_policy->get_reactions()) {
|
||||
const bool result = baseReactions.contains(*reaction);
|
||||
if (!result) {
|
||||
@@ -130,4 +170,4 @@ namespace gridfire::policy {
|
||||
}
|
||||
return NetworkPolicyStatus::INITIALIZED_VERIFIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#include "fourdst/atomic/species.h"
|
||||
#include "fourdst/composition/exceptions/exceptions_composition.h"
|
||||
#include "gridfire/engine/engine_graph.h"
|
||||
#include "gridfire/engine/types/engine_types.h"
|
||||
#include "gridfire/solver/strategies/triggers/engine_partitioning_trigger.h"
|
||||
#include "gridfire/trigger/procedures/trigger_pprint.h"
|
||||
#include "gridfire/exceptions/error_solver.h"
|
||||
@@ -41,7 +40,8 @@ namespace gridfire::solver {
|
||||
const std::vector<fourdst::atomic::Species> &networkSpecies,
|
||||
const size_t currentConvergenceFailure,
|
||||
const size_t currentNonlinearIterations,
|
||||
const std::map<fourdst::atomic::Species, std::unordered_map<std::string, double>> &reactionContributionMap
|
||||
const std::map<fourdst::atomic::Species, std::unordered_map<std::string, double>> &reactionContributionMap,
|
||||
scratch::StateBlob& ctx
|
||||
) :
|
||||
t(t),
|
||||
state(state),
|
||||
@@ -54,7 +54,8 @@ namespace gridfire::solver {
|
||||
networkSpecies(networkSpecies),
|
||||
currentConvergenceFailures(currentConvergenceFailure),
|
||||
currentNonlinearIterations(currentNonlinearIterations),
|
||||
reactionContributionMap(reactionContributionMap)
|
||||
reactionContributionMap(reactionContributionMap),
|
||||
state_ctx(ctx)
|
||||
{}
|
||||
|
||||
std::vector<std::tuple<std::string, std::string>> CVODESolverStrategy::TimestepContext::describe() const {
|
||||
@@ -74,8 +75,11 @@ namespace gridfire::solver {
|
||||
}
|
||||
|
||||
|
||||
CVODESolverStrategy::CVODESolverStrategy(DynamicEngine &engine): SingleZoneNetworkSolverStrategy<DynamicEngine>(engine) {
|
||||
// TODO: In order to support MPI this function must be changed
|
||||
CVODESolverStrategy::CVODESolverStrategy(
|
||||
const DynamicEngine &engine,
|
||||
const scratch::StateBlob& ctx
|
||||
): SingleZoneNetworkSolver<DynamicEngine>(engine, ctx) {
|
||||
// PERF: In order to support MPI this function must be changed
|
||||
const int flag = SUNContext_Create(SUN_COMM_NULL, &m_sun_ctx);
|
||||
if (flag < 0) {
|
||||
throw std::runtime_error("Failed to create SUNDIALS context (SUNDIALS Errno: " + std::to_string(flag) + ")");
|
||||
@@ -137,10 +141,10 @@ namespace gridfire::solver {
|
||||
(!resourcesExist ? "CVODE resources do not exist" :
|
||||
"Input composition inconsistent with previous state"));
|
||||
LOG_TRACE_L1(m_logger, "Starting engine update chain...");
|
||||
equilibratedComposition = m_engine.update(netIn);
|
||||
equilibratedComposition = m_engine.project(*m_scratch_blob, netIn);
|
||||
LOG_TRACE_L1(m_logger, "Engine updated and equilibrated composition found!");
|
||||
|
||||
size_t numSpecies = m_engine.getNetworkSpecies().size();
|
||||
size_t numSpecies = m_engine.getNetworkSpecies(*m_scratch_blob).size();
|
||||
uint64_t N = numSpecies + 1;
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Number of species: {} ({} independent variables)", numSpecies, N);
|
||||
@@ -153,10 +157,10 @@ namespace gridfire::solver {
|
||||
} else {
|
||||
LOG_INFO(m_logger, "Reusing existing CVODE resources (size: {})", m_last_size);
|
||||
|
||||
const size_t numSpecies = m_engine.getNetworkSpecies().size();
|
||||
const size_t numSpecies = m_engine.getNetworkSpecies(*m_scratch_blob).size();
|
||||
sunrealtype *y_data = N_VGetArrayPointer(m_Y);
|
||||
for (size_t i = 0; i < numSpecies; i++) {
|
||||
const auto& species = m_engine.getNetworkSpecies()[i];
|
||||
const auto& species = m_engine.getNetworkSpecies(*m_scratch_blob)[i];
|
||||
if (netIn.composition.contains(species)) {
|
||||
y_data[i] = netIn.composition.getMolarAbundance(species);
|
||||
} else {
|
||||
@@ -170,10 +174,12 @@ namespace gridfire::solver {
|
||||
equilibratedComposition = netIn.composition; // Use the provided composition as-is if we already have validated CVODE resources and that the composition is consistent with the previous state
|
||||
}
|
||||
|
||||
size_t numSpecies = m_engine.getNetworkSpecies().size();
|
||||
CVODEUserData user_data;
|
||||
user_data.solver_instance = this;
|
||||
user_data.engine = &m_engine;
|
||||
size_t numSpecies = m_engine.getNetworkSpecies(*m_scratch_blob).size();
|
||||
CVODEUserData user_data {
|
||||
.solver_instance = this,
|
||||
.ctx = *m_scratch_blob,
|
||||
.engine = &m_engine,
|
||||
};
|
||||
LOG_TRACE_L1(m_logger, "CVODE resources successfully initialized!");
|
||||
|
||||
double current_time = 0;
|
||||
@@ -199,7 +205,7 @@ namespace gridfire::solver {
|
||||
while (current_time < netIn.tMax) {
|
||||
user_data.T9 = T9;
|
||||
user_data.rho = netIn.density;
|
||||
user_data.networkSpecies = &m_engine.getNetworkSpecies();
|
||||
user_data.networkSpecies = &m_engine.getNetworkSpecies(*m_scratch_blob);
|
||||
user_data.captured_exception.reset();
|
||||
|
||||
utils::check_cvode_flag(CVodeSetUserData(m_cvode_mem, &user_data), "CVodeSetUserData");
|
||||
@@ -247,7 +253,7 @@ namespace gridfire::solver {
|
||||
);
|
||||
}
|
||||
for (size_t i = 0; i < numSpecies; ++i) {
|
||||
const auto& species = m_engine.getNetworkSpecies()[i];
|
||||
const auto& species = m_engine.getNetworkSpecies(*m_scratch_blob)[i];
|
||||
if (y_data[i] > 0.0) {
|
||||
postStep.setMolarAbundance(species, y_data[i]);
|
||||
}
|
||||
@@ -260,7 +266,7 @@ namespace gridfire::solver {
|
||||
LOG_DEBUG(m_logger, "Current composition (molar abundance): {}", [&]() -> std::string {
|
||||
std::stringstream ss;
|
||||
for (size_t i = 0; i < numSpecies; ++i) {
|
||||
const auto& species = m_engine.getNetworkSpecies()[i];
|
||||
const auto& species = m_engine.getNetworkSpecies(*m_scratch_blob)[i];
|
||||
ss << species.name() << ": (y_data = " << y_data[i] << ", collected = " << postStep.getMolarAbundance(species) << ")";
|
||||
if (i < numSpecies - 1) {
|
||||
ss << ", ";
|
||||
@@ -285,10 +291,11 @@ namespace gridfire::solver {
|
||||
netIn.density,
|
||||
n_steps,
|
||||
m_engine,
|
||||
m_engine.getNetworkSpecies(),
|
||||
m_engine.getNetworkSpecies(*m_scratch_blob),
|
||||
convFail_diff,
|
||||
iter_diff,
|
||||
rcMap
|
||||
rcMap,
|
||||
*m_scratch_blob
|
||||
);
|
||||
|
||||
prev_nonlinear_iterations = nliters + total_nonlinear_iterations;
|
||||
@@ -300,7 +307,7 @@ namespace gridfire::solver {
|
||||
trigger->step(ctx);
|
||||
|
||||
if (m_detailed_step_logging) {
|
||||
log_step_diagnostics(user_data, true, true, true, "step_" + std::to_string(total_steps + n_steps) + ".json");
|
||||
log_step_diagnostics(*m_scratch_blob, user_data, true, true, true, "step_" + std::to_string(total_steps + n_steps) + ".json");
|
||||
}
|
||||
|
||||
if (trigger->check(ctx)) {
|
||||
@@ -326,7 +333,7 @@ namespace gridfire::solver {
|
||||
|
||||
fourdst::composition::Composition temp_comp;
|
||||
std::vector<double> mass_fractions;
|
||||
auto num_species_at_stop = static_cast<long int>(m_engine.getNetworkSpecies().size());
|
||||
auto num_species_at_stop = static_cast<long int>(m_engine.getNetworkSpecies(*m_scratch_blob).size());
|
||||
|
||||
if (num_species_at_stop > m_Y->ops->nvgetlength(m_Y) - 1) {
|
||||
LOG_ERROR(
|
||||
@@ -338,8 +345,8 @@ namespace gridfire::solver {
|
||||
throw std::runtime_error("Number of species at engine update exceeds the number of species in the CVODE solver. This should never happen.");
|
||||
}
|
||||
|
||||
for (const auto& species: m_engine.getNetworkSpecies()) {
|
||||
const size_t sid = m_engine.getSpeciesIndex(species);
|
||||
for (const auto& species: m_engine.getNetworkSpecies(*m_scratch_blob)) {
|
||||
const size_t sid = m_engine.getSpeciesIndex(*m_scratch_blob, species);
|
||||
temp_comp.registerSpecies(species);
|
||||
double y = end_of_step_abundances[sid];
|
||||
if (y > 0.0) {
|
||||
@@ -349,7 +356,7 @@ namespace gridfire::solver {
|
||||
|
||||
#ifndef NDEBUG
|
||||
for (long int i = 0; i < num_species_at_stop; ++i) {
|
||||
const auto& species = m_engine.getNetworkSpecies()[i];
|
||||
const auto& species = m_engine.getNetworkSpecies(*m_scratch_blob)[i];
|
||||
if (std::abs(temp_comp.getMolarAbundance(species) - y_data[i]) > 1e-12) {
|
||||
throw exceptions::UtilityError("Conversion from solver state to composition molar abundance failed verification.");
|
||||
}
|
||||
@@ -384,7 +391,7 @@ namespace gridfire::solver {
|
||||
"Prior to Engine Update active reactions are: {}",
|
||||
[&]() -> std::string {
|
||||
std::stringstream ss;
|
||||
const gridfire::reaction::ReactionSet& reactions = m_engine.getNetworkReactions();
|
||||
const gridfire::reaction::ReactionSet& reactions = m_engine.getNetworkReactions(*m_scratch_blob);
|
||||
size_t count = 0;
|
||||
for (const auto& reaction : reactions) {
|
||||
ss << reaction -> id();
|
||||
@@ -396,7 +403,7 @@ namespace gridfire::solver {
|
||||
return ss.str();
|
||||
}()
|
||||
);
|
||||
fourdst::composition::Composition currentComposition = m_engine.update(netInTemp);
|
||||
fourdst::composition::Composition currentComposition = m_engine.project(*m_scratch_blob, netInTemp);
|
||||
LOG_DEBUG(
|
||||
m_logger,
|
||||
"After to Engine update composition is (molar abundance) {}",
|
||||
@@ -443,7 +450,7 @@ namespace gridfire::solver {
|
||||
"After Engine Update active reactions are: {}",
|
||||
[&]() -> std::string {
|
||||
std::stringstream ss;
|
||||
const gridfire::reaction::ReactionSet& reactions = m_engine.getNetworkReactions();
|
||||
const gridfire::reaction::ReactionSet& reactions = m_engine.getNetworkReactions(*m_scratch_blob);
|
||||
size_t count = 0;
|
||||
for (const auto& reaction : reactions) {
|
||||
ss << reaction -> id();
|
||||
@@ -459,10 +466,10 @@ namespace gridfire::solver {
|
||||
m_logger,
|
||||
"Due to a triggered engine update the composition was updated from size {} to {} species.",
|
||||
num_species_at_stop,
|
||||
m_engine.getNetworkSpecies().size()
|
||||
m_engine.getNetworkSpecies(*m_scratch_blob).size()
|
||||
);
|
||||
|
||||
numSpecies = m_engine.getNetworkSpecies().size();
|
||||
numSpecies = m_engine.getNetworkSpecies(*m_scratch_blob).size();
|
||||
size_t N = numSpecies + 1;
|
||||
|
||||
LOG_INFO(m_logger, "Starting CVODE reinitialization after engine update...");
|
||||
@@ -490,15 +497,15 @@ namespace gridfire::solver {
|
||||
accumulated_energy += y_data[numSpecies];
|
||||
std::vector<double> y_vec(y_data, y_data + numSpecies);
|
||||
|
||||
for (size_t i = 0; i < y_vec.size(); ++i) {
|
||||
if (y_vec[i] < 0 && std::abs(y_vec[i]) < 1e-16) {
|
||||
y_vec[i] = 0.0; // Regularize tiny negative abundances to zero
|
||||
for (double & i : y_vec) {
|
||||
if (i < 0 && std::abs(i) < 1e-16) {
|
||||
i = 0.0; // Regularize tiny negative abundances to zero
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(m_logger, "Constructing final composition= with {} species", numSpecies);
|
||||
|
||||
fourdst::composition::Composition topLevelComposition(m_engine.getNetworkSpecies(), y_vec);
|
||||
fourdst::composition::Composition topLevelComposition(m_engine.getNetworkSpecies(*m_scratch_blob), y_vec);
|
||||
LOG_INFO(m_logger, "Final composition constructed from solver state successfully! ({})", [&topLevelComposition]() -> std::string {
|
||||
std::ostringstream ss;
|
||||
size_t i = 0;
|
||||
@@ -513,7 +520,7 @@ namespace gridfire::solver {
|
||||
}());
|
||||
|
||||
LOG_INFO(m_logger, "Collecting final composition...");
|
||||
fourdst::composition::Composition outputComposition = m_engine.collectComposition(topLevelComposition, netIn.temperature/1e9, netIn.density);
|
||||
fourdst::composition::Composition outputComposition = m_engine.collectComposition(*m_scratch_blob, topLevelComposition, netIn.temperature/1e9, netIn.density);
|
||||
|
||||
assert(outputComposition.getRegisteredSymbols().size() == equilibratedComposition.getRegisteredSymbols().size());
|
||||
|
||||
@@ -538,6 +545,7 @@ namespace gridfire::solver {
|
||||
|
||||
LOG_TRACE_L2(m_logger, "generating final nuclear energy generation rate derivatives...");
|
||||
auto [dEps_dT, dEps_dRho] = m_engine.calculateEpsDerivatives(
|
||||
*m_scratch_blob,
|
||||
outputComposition,
|
||||
T9,
|
||||
netIn.density
|
||||
@@ -640,7 +648,7 @@ namespace gridfire::solver {
|
||||
const auto* solver_instance = data->solver_instance;
|
||||
|
||||
LOG_TRACE_L2(solver_instance->m_logger, "CVODE Jacobian wrapper starting");
|
||||
const size_t numSpecies = engine->getNetworkSpecies().size();
|
||||
const size_t numSpecies = engine->getNetworkSpecies(data->ctx).size();
|
||||
sunrealtype* y_data = N_VGetArrayPointer(y);
|
||||
|
||||
|
||||
@@ -653,7 +661,7 @@ namespace gridfire::solver {
|
||||
}
|
||||
}
|
||||
std::vector<double> y_vec(y_data, y_data + numSpecies);
|
||||
fourdst::composition::Composition composition(engine->getNetworkSpecies(), y_vec);
|
||||
fourdst::composition::Composition composition(engine->getNetworkSpecies(data->ctx), y_vec);
|
||||
LOG_TRACE_L2(solver_instance->m_logger, "Generating Jacobian matrix at time {} with {} species in composition (mean molecular mass: {})", t, composition.size(), composition.getMeanParticleMass());
|
||||
LOG_TRACE_L2(solver_instance->m_logger, "Composition is {}", [&composition]() -> std::string {
|
||||
std::stringstream ss;
|
||||
@@ -669,11 +677,11 @@ namespace gridfire::solver {
|
||||
}());
|
||||
|
||||
LOG_TRACE_L2(solver_instance->m_logger, "Generating Jacobian matrix at time {}", t);
|
||||
NetworkJacobian jac = engine->generateJacobianMatrix(composition, data->T9, data->rho);
|
||||
NetworkJacobian jac = engine->generateJacobianMatrix(data->ctx, composition, data->T9, data->rho);
|
||||
LOG_TRACE_L2(solver_instance->m_logger, "Regularizing Jacobian matrix at time {}", t);
|
||||
jac = regularize_jacobian(jac, composition, solver_instance->m_logger);
|
||||
LOG_TRACE_L2(solver_instance->m_logger, "Done regularizing Jacobian matrix at time {}", t);
|
||||
if (jac.infs().size() != 0 || jac.nans().size() != 0) {
|
||||
if (!jac.infs().empty() || !jac.nans().empty()) {
|
||||
auto infString = [&jac]() -> std::string {
|
||||
std::stringstream ss;
|
||||
size_t i = 0;
|
||||
@@ -685,7 +693,7 @@ namespace gridfire::solver {
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (entries.size() == 0) {
|
||||
if (entries.empty()) {
|
||||
ss << "None";
|
||||
}
|
||||
return ss.str();
|
||||
@@ -701,7 +709,7 @@ namespace gridfire::solver {
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (entries.size() == 0) {
|
||||
if (entries.empty()) {
|
||||
ss << "None";
|
||||
}
|
||||
return ss.str();
|
||||
@@ -724,9 +732,9 @@ namespace gridfire::solver {
|
||||
|
||||
LOG_TRACE_L2(solver_instance->m_logger, "Transferring Jacobian matrix data to SUNDenseMatrix format at time {}", t);
|
||||
for (size_t j = 0; j < numSpecies; ++j) {
|
||||
const fourdst::atomic::Species& species_j = engine->getNetworkSpecies()[j];
|
||||
const fourdst::atomic::Species& species_j = engine->getNetworkSpecies(data->ctx)[j];
|
||||
for (size_t i = 0; i < numSpecies; ++i) {
|
||||
const fourdst::atomic::Species& species_i = engine->getNetworkSpecies()[i];
|
||||
const fourdst::atomic::Species& species_i = engine->getNetworkSpecies(data->ctx)[i];
|
||||
// J(i,j) = d(f_i)/d(y_j)
|
||||
// Column-major order format for SUNDenseMatrix: J_data[j*N + i] indexes J(i,j)
|
||||
const double dYi_dt = jac(species_i, species_j);
|
||||
@@ -752,7 +760,7 @@ namespace gridfire::solver {
|
||||
N_Vector ydot,
|
||||
const CVODEUserData *data
|
||||
) const {
|
||||
const size_t numSpecies = m_engine.getNetworkSpecies().size();
|
||||
const size_t numSpecies = m_engine.getNetworkSpecies(data->ctx).size();
|
||||
sunrealtype* y_data = N_VGetArrayPointer(y);
|
||||
|
||||
// Solver constraints should keep these values very close to 0 but floating point noise can still result in very
|
||||
@@ -764,10 +772,10 @@ namespace gridfire::solver {
|
||||
}
|
||||
}
|
||||
std::vector<double> y_vec(y_data, y_data + numSpecies);
|
||||
fourdst::composition::Composition composition(m_engine.getNetworkSpecies(), y_vec);
|
||||
fourdst::composition::Composition composition(m_engine.getNetworkSpecies(*m_scratch_blob), y_vec);
|
||||
|
||||
LOG_TRACE_L2(m_logger, "Calculating RHS at time {} with {} species in composition", t, composition.size());
|
||||
const auto result = m_engine.calculateRHSAndEnergy(composition, data->T9, data->rho, false);
|
||||
const auto result = m_engine.calculateRHSAndEnergy(*m_scratch_blob, composition, data->T9, data->rho, false);
|
||||
if (!result) {
|
||||
LOG_CRITICAL(m_logger, "Failed to calculate RHS at time {}: {}", t, EngineStatus_to_string(result.error()));
|
||||
throw exceptions::BadRHSEngineError(std::format("Failed to calculate RHS at time {}: {}", t, EngineStatus_to_string(result.error())));
|
||||
@@ -797,7 +805,7 @@ namespace gridfire::solver {
|
||||
}());
|
||||
|
||||
for (size_t i = 0; i < numSpecies; ++i) {
|
||||
fourdst::atomic::Species species = m_engine.getNetworkSpecies()[i];
|
||||
fourdst::atomic::Species species = m_engine.getNetworkSpecies(*m_scratch_blob)[i];
|
||||
ydot_data[i] = dydt.at(species);
|
||||
}
|
||||
ydot_data[numSpecies] = nuclearEnergyGenerationRate; // Set the last element to the specific energy rate
|
||||
@@ -822,7 +830,7 @@ namespace gridfire::solver {
|
||||
|
||||
sunrealtype *y_data = N_VGetArrayPointer(m_Y);
|
||||
for (size_t i = 0; i < numSpecies; i++) {
|
||||
const auto& species = m_engine.getNetworkSpecies()[i];
|
||||
const auto& species = m_engine.getNetworkSpecies(*m_scratch_blob)[i];
|
||||
if (composition.contains(species)) {
|
||||
y_data[i] = composition.getMolarAbundance(species);
|
||||
} else {
|
||||
@@ -893,11 +901,11 @@ namespace gridfire::solver {
|
||||
}
|
||||
|
||||
void CVODESolverStrategy::log_step_diagnostics(
|
||||
scratch::StateBlob &ctx,
|
||||
const CVODEUserData &user_data,
|
||||
bool displayJacobianStiffness,
|
||||
bool displaySpeciesBalance,
|
||||
bool to_file,
|
||||
std::optional<std::string> filename
|
||||
bool to_file, std::optional<std::string> filename
|
||||
) const {
|
||||
if (to_file && !filename.has_value()) {
|
||||
LOG_ERROR(m_logger, "Filename must be provided when logging diagnostics to file.");
|
||||
@@ -982,7 +990,7 @@ namespace gridfire::solver {
|
||||
std::vector<double> Y_full(y_data, y_data + num_components - 1);
|
||||
std::vector<double> E_full(y_err_data, y_err_data + num_components - 1);
|
||||
|
||||
auto result = diagnostics::report_limiting_species(*user_data.engine, Y_full, E_full, relTol, absTol, 10, to_file);
|
||||
auto result = diagnostics::report_limiting_species(ctx, *user_data.engine, Y_full, E_full, relTol, absTol, 10, to_file);
|
||||
if (to_file && result.has_value()) {
|
||||
j["Limiting_Species"] = result.value();
|
||||
}
|
||||
@@ -1005,11 +1013,11 @@ namespace gridfire::solver {
|
||||
err_ratios[i] = err_ratio;
|
||||
}
|
||||
|
||||
fourdst::composition::Composition composition(user_data.engine->getNetworkSpecies(), Y_full);
|
||||
fourdst::composition::Composition collectedComposition = user_data.engine->collectComposition(composition, user_data.T9, user_data.rho);
|
||||
fourdst::composition::Composition composition(user_data.engine->getNetworkSpecies(*m_scratch_blob), Y_full);
|
||||
fourdst::composition::Composition collectedComposition = user_data.engine->collectComposition(*m_scratch_blob, composition, user_data.T9, user_data.rho);
|
||||
|
||||
auto destructionTimescales = user_data.engine->getSpeciesDestructionTimescales(collectedComposition, user_data.T9, user_data.rho);
|
||||
auto netTimescales = user_data.engine->getSpeciesTimescales(collectedComposition, user_data.T9, user_data.rho);
|
||||
auto destructionTimescales = user_data.engine->getSpeciesDestructionTimescales(*m_scratch_blob, collectedComposition, user_data.T9, user_data.rho);
|
||||
auto netTimescales = user_data.engine->getSpeciesTimescales(*m_scratch_blob, collectedComposition, user_data.T9, user_data.rho);
|
||||
|
||||
bool timescaleOkay = false;
|
||||
if (destructionTimescales && netTimescales) timescaleOkay = true;
|
||||
@@ -1029,7 +1037,7 @@ namespace gridfire::solver {
|
||||
if (destructionTimescales.value().contains(sp)) destructionTimescales_list.emplace_back(destructionTimescales.value().at(sp));
|
||||
else destructionTimescales_list.emplace_back(std::numeric_limits<double>::infinity());
|
||||
|
||||
speciesStatus_list.push_back(SpeciesStatus_to_string(user_data.engine->getSpeciesStatus(sp)));
|
||||
speciesStatus_list.push_back(SpeciesStatus_to_string(user_data.engine->getSpeciesStatus(*m_scratch_blob, sp)));
|
||||
}
|
||||
|
||||
utils::Column<fourdst::atomic::Species> speciesColumn("Species", species_list);
|
||||
@@ -1093,7 +1101,7 @@ namespace gridfire::solver {
|
||||
|
||||
// --- 4. Call Your Jacobian and Balance Diagnostics ---
|
||||
if (displayJacobianStiffness) {
|
||||
auto jStiff = diagnostics::inspect_jacobian_stiffness(*user_data.engine, composition, user_data.T9, user_data.rho, to_file);
|
||||
auto jStiff = diagnostics::inspect_jacobian_stiffness(ctx, *user_data.engine, composition, user_data.T9, user_data.rho, to_file);
|
||||
if (to_file && jStiff.has_value()) {
|
||||
j["Jacobian_Stiffness_Diagnostics"] = jStiff.value();
|
||||
}
|
||||
@@ -1103,7 +1111,7 @@ namespace gridfire::solver {
|
||||
const size_t num_species_to_inspect = std::min(sorted_species.size(), static_cast<size_t>(5));
|
||||
for (size_t i = 0; i < num_species_to_inspect; ++i) {
|
||||
const auto& species = sorted_species[i];
|
||||
auto sbr = diagnostics::inspect_species_balance(*user_data.engine, std::string(species.name()), composition, user_data.T9, user_data.rho, to_file);
|
||||
auto sbr = diagnostics::inspect_species_balance(ctx, *user_data.engine, std::string(species.name()), composition, user_data.T9, user_data.rho, to_file);
|
||||
if (to_file && sbr.has_value()) {
|
||||
j[std::string("Species_Balance_Diagnostics_") + species.name().data()] = sbr.value();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "gridfire/utils/logging.h"
|
||||
#include "gridfire/engine/engine_abstract.h"
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
@@ -9,12 +10,12 @@
|
||||
#include <string>
|
||||
|
||||
std::string gridfire::utils::formatNuclearTimescaleLogString(
|
||||
engine::scratch::StateBlob &ctx,
|
||||
const engine::DynamicEngine& engine,
|
||||
const fourdst::composition::Composition& composition,
|
||||
const double T9,
|
||||
const double rho
|
||||
const double T9, const double rho
|
||||
) {
|
||||
auto const& result = engine.getSpeciesTimescales(composition, T9, rho);
|
||||
auto const& result = engine.getSpeciesTimescales(ctx, composition, T9, rho);
|
||||
if (!result) {
|
||||
std::ostringstream ss;
|
||||
ss << "Failed to get species timescales: " << engine::EngineStatus_to_string(result.error());
|
||||
|
||||
Reference in New Issue
Block a user