feat(GridFire): Added a number of python hooks
python hooks to make getting base composition more reliable; further, a number of small changes made to aid in my analysis in response to ref report 1
This commit is contained in:
@@ -3,13 +3,25 @@
|
||||
#include "fourdst/config/config.h"
|
||||
|
||||
namespace gridfire::config {
|
||||
struct CVODESolverConfig {
|
||||
struct BoundaryFluxConfig {
|
||||
double relativeThreshold = 3e-8;
|
||||
double absoluteThreshold = 1e-24;
|
||||
};
|
||||
|
||||
struct TriggerConfig {
|
||||
double offDiagonalThreshold = 1e10;
|
||||
double timestepCollapseRatio = 0.5;
|
||||
double maxConvergenceFailures = 2;
|
||||
BoundaryFluxConfig boundaryFlux;
|
||||
};
|
||||
struct PointSolverConfig {
|
||||
double absTol = 1.0e-8;
|
||||
double relTol = 1.0e-5;
|
||||
TriggerConfig trigger;
|
||||
};
|
||||
|
||||
struct SolverConfig {
|
||||
CVODESolverConfig cvode;
|
||||
PointSolverConfig pointSolver;
|
||||
};
|
||||
|
||||
struct AdaptiveEngineViewConfig {
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#include "gridfire/engine/types/reporting.h"
|
||||
#include "gridfire/engine/types/jacobian.h"
|
||||
|
||||
#include "gridfire/exceptions/error_engine.h"
|
||||
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
|
||||
#include "fourdst/composition/composition_abstract.h"
|
||||
@@ -183,6 +185,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Generate the Jacobian matrix for the current state.
|
||||
*
|
||||
* @param ctx The scratchpad context for the current state.
|
||||
* @param comp Composition object containing current abundances.
|
||||
* @param T9 Temperature in units of 10^9 K.
|
||||
* @param rho Density in g/cm^3.
|
||||
@@ -200,6 +203,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Generate the Jacobian matrix for the current state using a subset of active species.
|
||||
*
|
||||
* @param ctx The scratchpad context for the current state.
|
||||
* @param comp Composition object containing current abundances.
|
||||
* @param T9 Temperature in units of 10^9 K.
|
||||
* @param rho Density in g/cm^3.
|
||||
@@ -221,6 +225,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Generate the Jacobian matrix for the current state with a specified sparsity pattern.
|
||||
*
|
||||
* @param ctx Get the scratchpad context for the current state.
|
||||
* @param comp Composition object containing current abundances.
|
||||
* @param T9 Temperature in units of 10^9 K.
|
||||
* @param rho Density in g/cm^3.
|
||||
@@ -245,6 +250,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Calculate the molar reaction flow for a given reaction.
|
||||
*
|
||||
* @param ctx The scratchpad context for the current state.
|
||||
* @param reaction The reaction for which to calculate the flow.
|
||||
* @param comp Composition object containing current abundances.
|
||||
* @param T9 Temperature in units of 10^9 K.
|
||||
@@ -289,6 +295,39 @@ namespace gridfire::engine {
|
||||
scratch::StateBlob& ctx
|
||||
) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get the set of inactive reactions in the network.
|
||||
*
|
||||
* @return ReactionSet containing all inactive reactions.
|
||||
*
|
||||
* By default, this method returns an empty set. Derived classes can override
|
||||
* this method to provide the actual set of inactive reactions based on their
|
||||
* internal logic (e.g., reaction flow culling, QSE partitioning).
|
||||
*/
|
||||
[[nodiscard]] virtual reaction::ReactionSet getInactiveNetworkReactions(
|
||||
scratch::StateBlob &ctx
|
||||
) const {
|
||||
return reaction::ReactionSet{};
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual double getInactiveReactionMolarReactionFlow(
|
||||
scratch::StateBlob& ctx,
|
||||
const reaction::Reaction &reaction,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
std::string warning_msg = std::format(
|
||||
"[GridFire Warning ({}, {}, {})]: Engine of type '{}' does not implement getInactiveReactionMolarReactionFlow. Returning 0.0 flow for reaction '{}'.",
|
||||
__FILE__,
|
||||
__LINE__,
|
||||
__FUNCTION__,
|
||||
typeid(*this).name(),
|
||||
reaction.id()
|
||||
);
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Compute timescales for all species in the network.
|
||||
@@ -311,6 +350,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Compute destruction timescales for all species in the network.
|
||||
*
|
||||
* @param ctx The scratchpad context for the current state.
|
||||
* @param comp Composition object containing current abundances.
|
||||
* @param T9 Temperature in units of 10^9 K.
|
||||
* @param rho Density in g/cm^3.
|
||||
@@ -329,6 +369,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Update the thread local scratch pad state of a network.
|
||||
*
|
||||
* @param ctx The scratchpad context for the current state.
|
||||
* @param netIn A struct containing the current network input, such as
|
||||
* temperature, density, and composition.
|
||||
*
|
||||
@@ -354,6 +395,8 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Get the current electron screening model.
|
||||
*
|
||||
* @param ctx The scratchpad context for the current state.
|
||||
*
|
||||
* @return The currently active screening model type.
|
||||
*
|
||||
* @par Usage Example:
|
||||
@@ -368,6 +411,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Get the index of a species in the network.
|
||||
*
|
||||
* @param ctx The scratchpad context for the current state.
|
||||
* @param species The species to look up.
|
||||
*
|
||||
* This method allows querying the index of a specific species in the
|
||||
@@ -382,6 +426,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Prime the engine with initial conditions.
|
||||
*
|
||||
* @param ctx The scratchpad context for the current state.
|
||||
* @param netIn The input conditions for the network.
|
||||
* @return PrimingReport containing information about the priming process.
|
||||
*
|
||||
@@ -403,6 +448,7 @@ namespace gridfire::engine {
|
||||
* from each sub engine.
|
||||
* @note It is up to each engine to decide how to handle filling in the return composition.
|
||||
* @note These methods return an unfinalized composition which must then be finalized by the caller
|
||||
* @param ctx The scratchpad context for the current state.
|
||||
* @param comp Input composition to "normalize".
|
||||
* @param T9
|
||||
* @param rho
|
||||
@@ -434,5 +480,7 @@ namespace gridfire::engine {
|
||||
scratch::StateBlob& ctx
|
||||
) const = 0;
|
||||
|
||||
[[nodiscard]] virtual std::unique_ptr<scratch::StateBlob> constructStateBlob(const scratch::StateBlob *blob) const = 0;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -137,6 +137,18 @@ namespace gridfire::engine {
|
||||
*/
|
||||
explicit GraphEngine(const reaction::ReactionSet &reactions);
|
||||
|
||||
void addReaction(
|
||||
const reaction::Reaction& reaction
|
||||
);
|
||||
|
||||
void addReaction(
|
||||
const std::string& reaction_id
|
||||
);
|
||||
|
||||
std::unique_ptr<scratch::StateBlob> constructStateBlob(
|
||||
const scratch::StateBlob *blob = nullptr
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Calculates the right-hand side (dY/dt) and energy generation rate.
|
||||
*
|
||||
@@ -204,6 +216,7 @@ namespace gridfire::engine {
|
||||
double rho
|
||||
) const override;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Calculates the derivatives of the energy generation rate with respect to temperature and density for a subset of reactions
|
||||
*
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include "fourdst/config/config.h"
|
||||
#include "fourdst/logging/logging.h"
|
||||
|
||||
#include "gridfire/engine/procedures/construction.h"
|
||||
#include "gridfire/engine/scratchpads/blob.h"
|
||||
|
||||
#include "quill/Logger.h"
|
||||
@@ -234,6 +233,26 @@ namespace gridfire::engine {
|
||||
scratch::StateBlob& ctx
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Gets the set of inactive logical reactions in the network.
|
||||
*
|
||||
* @return ReactionSet containing all inactive reactions.
|
||||
*
|
||||
* This method returns the set of reactions that have been culled from the active
|
||||
* network based on the adaptation criteria.
|
||||
*/
|
||||
[[nodiscard]] reaction::ReactionSet getInactiveNetworkReactions(
|
||||
scratch::StateBlob &ctx
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] double getInactiveReactionMolarReactionFlow(
|
||||
scratch::StateBlob& ctx,
|
||||
const reaction::Reaction &reaction,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Computes timescales for all active species in the network.
|
||||
*
|
||||
@@ -319,6 +338,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Primes the engine with the given network input.
|
||||
*
|
||||
* @param ctx The scratchpad context for storing thread-local data.
|
||||
* @param netIn The current network input, containing temperature, density, and composition.
|
||||
* @return A PrimingReport indicating the result of the priming operation.
|
||||
*
|
||||
@@ -367,6 +387,8 @@ namespace gridfire::engine {
|
||||
[[nodiscard]] std::optional<StepDerivatives<double>>getMostRecentRHSCalculation(
|
||||
scratch::StateBlob &ctx
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<scratch::StateBlob> constructStateBlob(const scratch::StateBlob *blob) const override;
|
||||
private:
|
||||
using LogManager = fourdst::logging::LogManager;
|
||||
|
||||
@@ -399,7 +421,7 @@ namespace gridfire::engine {
|
||||
* @param netIn The current network input, containing temperature, density, and composition.
|
||||
* @return A pair with the first element a vector of ReactionFlow structs, each containing a pointer to a
|
||||
* reaction and its calculated flow rate and the second being a composition object where species which were not
|
||||
* present in netIn but are present in the definition of the base engine are registered but have 0 mass fraction
|
||||
* present in netIn but are present in the definition of the base engine are registered but have 0 mass fraction.
|
||||
*
|
||||
* @par Algorithm:
|
||||
* 1. Iterates through all species in the base engine's network.
|
||||
|
||||
@@ -255,6 +255,9 @@ namespace gridfire::engine {
|
||||
[[nodiscard]] std::optional<StepDerivatives<double>>getMostRecentRHSCalculation(
|
||||
scratch::StateBlob &ctx
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<scratch::StateBlob> constructStateBlob(const scratch::StateBlob *blob) const override;
|
||||
|
||||
protected:
|
||||
bool m_isStale = true;
|
||||
GraphEngine& m_baseEngine;
|
||||
@@ -343,7 +346,6 @@ namespace gridfire::engine {
|
||||
scratch::StateBlob& ctx,
|
||||
const std::vector<std::string>& peNames
|
||||
) const;
|
||||
|
||||
};
|
||||
|
||||
class FileDefinedEngineView final: public DefinedEngineView {
|
||||
|
||||
@@ -611,6 +611,8 @@ namespace gridfire::engine {
|
||||
[[nodiscard]] std::optional<StepDerivatives<double>>getMostRecentRHSCalculation(
|
||||
scratch::StateBlob &
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<scratch::StateBlob> constructStateBlob(const scratch::StateBlob *blob) const override;
|
||||
public:
|
||||
/**
|
||||
* @brief Struct representing a QSE group.
|
||||
@@ -990,9 +992,6 @@ namespace gridfire::engine {
|
||||
const std::vector<QSEGroup> &groups,
|
||||
const std::vector<reaction::ReactionSet> &groupReactions
|
||||
);
|
||||
|
||||
public:
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Constructs the view by looking up the priming species by symbol.
|
||||
*
|
||||
* @param ctx State Blob containing Engine context
|
||||
* @param primingSymbol Symbol string of the species to prime.
|
||||
* @param baseEngine Reference to the base DynamicEngine to wrap.
|
||||
* @pre primingSymbol must correspond to a valid species in atomic::species registry.
|
||||
@@ -46,6 +47,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Constructs the view using an existing Species object.
|
||||
*
|
||||
* @param ctx State Blob containing Engine context
|
||||
* @param primingSpecies The species object to prime.
|
||||
* @param baseEngine Reference to the base DynamicEngine to wrap.
|
||||
* @pre primingSpecies must be valid and present in the network of baseEngine.
|
||||
@@ -66,6 +68,7 @@ namespace gridfire::engine {
|
||||
/**
|
||||
* @brief Constructs the set of reaction names that involve the priming species.
|
||||
*
|
||||
* @param ctx State blob containing engine context
|
||||
* @param primingSpecies Species for which to collect priming reactions.
|
||||
* @param baseEngine Base engine containing the full network of reactions.
|
||||
* @pre baseEngine.getNetworkReactions() returns a valid iterable set of reactions.
|
||||
|
||||
@@ -11,4 +11,4 @@
|
||||
#include "gridfire/trigger/trigger.h"
|
||||
#include "gridfire/utils/utils.h"
|
||||
|
||||
#include "types/types.h"
|
||||
#include "gridfire/types/types.h"
|
||||
|
||||
@@ -58,6 +58,8 @@ namespace gridfire::solver {
|
||||
const size_t currentNonlinearIterations; ///< Total number of non-linear iterations
|
||||
const std::map<fourdst::atomic::Species, std::unordered_map<std::string, double>>& reactionContributionMap; ///< Map of reaction contributions for the current step
|
||||
engine::scratch::StateBlob& state_ctx; ///< Reference to the engine scratch state blob
|
||||
double current_total_energy = 0.0; ///< Current energy generation rate [erg/g/s]
|
||||
double current_neutrino_energy_loss_rate = 0.0; ///< Current neutrino energy loss rate [erg/g/s]
|
||||
|
||||
PointSolverTimestepContext(
|
||||
double t,
|
||||
@@ -76,6 +78,8 @@ namespace gridfire::solver {
|
||||
);
|
||||
|
||||
[[nodiscard]] std::vector<std::tuple<std::string, std::string>> describe() const override;
|
||||
|
||||
[[nodiscard]] fourdst::composition::Composition getPhysicalComposition() const;
|
||||
};
|
||||
|
||||
using TimestepCallback = std::function<void(const PointSolverTimestepContext& context)>; ///< Type alias for a timestep callback function.
|
||||
@@ -169,6 +173,13 @@ namespace gridfire::solver {
|
||||
const engine::DynamicEngine& engine
|
||||
);
|
||||
|
||||
PointSolver(
|
||||
const engine::DynamicEngine& engine,
|
||||
const config::GridFireConfig& config
|
||||
);
|
||||
|
||||
|
||||
config::GridFireConfig getConfig() const { return *m_config; }
|
||||
/**
|
||||
* @brief Integrate from t=0 to netIn.tMax and return final composition and energy.
|
||||
*
|
||||
@@ -264,6 +275,17 @@ namespace gridfire::solver {
|
||||
*/
|
||||
static int cvode_jac_wrapper(sunrealtype t, N_Vector y, N_Vector ydot, SUNMatrix J, void *user_data, N_Vector tmp1, N_Vector tmp2, N_Vector tmp3);
|
||||
|
||||
/**
|
||||
* @brief CVODE error handler that logs errors and warnings from SUNDIALS using the solver's logger.
|
||||
* @param line
|
||||
* @param func
|
||||
* @param file
|
||||
* @param msg
|
||||
* @param err_code
|
||||
* @param err_user_data
|
||||
* @param sunctx
|
||||
*/
|
||||
static void cvode_error_handler(int line, const char *func, const char *file, const char *msg, SUNErrCode err_code, void *err_user_data, SUNContext sunctx);
|
||||
/**
|
||||
* @brief Compute RHS into ydot at time t from the engine and current state y.
|
||||
*
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "gridfire/trigger/trigger_result.h"
|
||||
#include "gridfire/solver/strategies/PointSolver.h"
|
||||
#include "fourdst/logging/logging.h"
|
||||
#include "gridfire/config/config.h"
|
||||
|
||||
#include <string>
|
||||
#include <deque>
|
||||
@@ -316,6 +317,46 @@ namespace gridfire::trigger::solver::CVODE {
|
||||
bool rel_failure(const gridfire::solver::PointSolverTimestepContext& ctx) const;
|
||||
};
|
||||
|
||||
class BoundaryFluxTrigger final : public Trigger<gridfire::solver::PointSolverTimestepContext> {
|
||||
public:
|
||||
explicit BoundaryFluxTrigger(double relativeThreshold, double absoluteThreshold);
|
||||
bool check(const gridfire::solver::PointSolverTimestepContext &ctx) const override;
|
||||
void update(const gridfire::solver::PointSolverTimestepContext &ctx) override;
|
||||
void step(const gridfire::solver::PointSolverTimestepContext &ctx) override;
|
||||
void reset() override;
|
||||
|
||||
std::string name() const override;
|
||||
TriggerResult why(const gridfire::solver::PointSolverTimestepContext &ctx) const override;
|
||||
std::string describe() const override;
|
||||
size_t numTriggers() const override;
|
||||
size_t numMisses() const override;
|
||||
private:
|
||||
enum class ReactionSetType : uint8_t {
|
||||
ACTIVE,
|
||||
INACTIVE
|
||||
};
|
||||
|
||||
static double get_reaction_set_flow(
|
||||
const reaction::ReactionSet& reactions,
|
||||
const gridfire::solver::PointSolverTimestepContext& ctx,
|
||||
const fourdst::composition::Composition& comp,
|
||||
double T9,
|
||||
double rho,
|
||||
ReactionSetType type
|
||||
);
|
||||
private:
|
||||
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
|
||||
|
||||
mutable size_t m_hits = 0;
|
||||
mutable size_t m_misses = 0;
|
||||
mutable size_t m_updates = 0;
|
||||
mutable size_t m_resets = 0;
|
||||
|
||||
double m_relativeThreshold;
|
||||
double m_absoluteThreshold;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Compose a trigger suitable for deciding engine re-partitioning during CVODE solves.
|
||||
*
|
||||
@@ -329,18 +370,9 @@ namespace gridfire::trigger::solver::CVODE {
|
||||
* See engine_partitioning_trigger.cpp for construction details using OrTrigger and
|
||||
* EveryNthTrigger from trigger_logical.h.
|
||||
*
|
||||
* @param simulationTimeInterval Interval used by SimulationTimeTrigger (> 0).
|
||||
* @param offDiagonalThreshold Off-diagonal Jacobian magnitude threshold (>= 0).
|
||||
* @param timestepCollapseRatio Threshold for timestep deviation (>= 0, and <= 1 when relative).
|
||||
* @param maxConvergenceFailures Window size for timestep averaging (>= 1 recommended).
|
||||
* @return A unique_ptr to a composed Trigger<TimestepContext> implementing the policy above.
|
||||
*
|
||||
* @note The exact policy is subject to change; this function centralizes that decision.
|
||||
*/
|
||||
std::unique_ptr<Trigger<gridfire::solver::PointSolverTimestepContext>> makeEnginePartitioningTrigger(
|
||||
double simulationTimeInterval,
|
||||
double offDiagonalThreshold,
|
||||
double timestepCollapseRatio,
|
||||
size_t maxConvergenceFailures
|
||||
);
|
||||
std::unique_ptr<Trigger<gridfire::solver::PointSolverTimestepContext>> makeEnginePartitioningTrigger(const config::TriggerConfig& cfg);
|
||||
}
|
||||
|
||||
@@ -32,7 +32,8 @@
|
||||
#include "cppad/cppad.hpp"
|
||||
#include "cppad/utility/sparse_rc.hpp"
|
||||
#include "cppad/utility/sparse_rcv.hpp"
|
||||
|
||||
#include "fourdst/composition/exceptions/exceptions_composition.h"
|
||||
#include "gridfire/reaction/reaclib.h"
|
||||
|
||||
|
||||
namespace {
|
||||
@@ -132,6 +133,36 @@ namespace gridfire::engine {
|
||||
syncInternalMaps();
|
||||
}
|
||||
|
||||
void GraphEngine::addReaction(
|
||||
const reaction::Reaction& reaction
|
||||
) {
|
||||
m_reactions.add_reaction(reaction);
|
||||
syncInternalMaps();
|
||||
}
|
||||
|
||||
void GraphEngine::addReaction(
|
||||
const std::string& reaction_id
|
||||
) {
|
||||
const auto& allReaclibReactions = reaclib::get_all_reaclib_reactions();
|
||||
const auto& reaction = allReaclibReactions.get(reaction_id);
|
||||
if (reaction.has_value()) {
|
||||
m_reactions.add_reaction(reaction.value()->clone());
|
||||
} else {
|
||||
throw exceptions::BadCollectionError(std::format("Unable to locate reaction with ID {} in reaclib set", reaction_id));
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<scratch::StateBlob> GraphEngine::constructStateBlob(const scratch::StateBlob *blob) const {
|
||||
if (blob) {
|
||||
throw exceptions::ScratchPadError("GraphEngine does not support accepting an external StateBlob. The state blob for GraphEngine must be constructed internally to ensure it contains the correct scratchpad states.");
|
||||
}
|
||||
auto i_blob = std::make_unique<scratch::StateBlob>();
|
||||
i_blob->enroll<engine::scratch::GraphEngineScratchPad>();
|
||||
auto* state = scratch::get_state<scratch::GraphEngineScratchPad, false>(*i_blob);
|
||||
state->initialize(*this);
|
||||
return i_blob;
|
||||
}
|
||||
|
||||
std::expected<StepDerivatives<double>, EngineStatus> GraphEngine::calculateRHSAndEnergy(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
@@ -761,7 +792,14 @@ namespace gridfire::engine {
|
||||
for (const auto& species : m_networkSpecies ) {
|
||||
result.registerSpecies(species);
|
||||
if (comp.contains(species)) {
|
||||
result.setMolarAbundance(species, comp.getMolarAbundance(species));
|
||||
double Y = comp.getMolarAbundance(species);
|
||||
if (Y < 0.0 && std::abs(Y) <= 1e-16) {
|
||||
result.setMolarAbundance(species, 0.0);
|
||||
} else if (Y < 0.0 && std::abs(Y) >= 1e-16) {
|
||||
throw fourdst::composition::exceptions::InvalidCompositionError(std::format("Molar abundance for species {} is negative (Y = {}). GraphEngine does not support non-physical negative abundances, even if they are very small in magnitude (clamp is 1e-16). Check input composition for validity.", species.name(), Y));
|
||||
} else {
|
||||
result.setMolarAbundance(species, Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -997,7 +1035,7 @@ namespace gridfire::engine {
|
||||
for (const auto& species: m_networkSpecies) {
|
||||
double Yi = 0.0; // Small floor to avoid issues with zero abundances
|
||||
if (comp.contains(species)) {
|
||||
Yi = comp.getMolarAbundance(species);
|
||||
Yi = std::max(comp.getMolarAbundance(species), 1e-30);
|
||||
}
|
||||
x[i] = Yi;
|
||||
i++;
|
||||
|
||||
@@ -166,6 +166,35 @@ namespace gridfire::engine {
|
||||
return scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx) -> active_reactions;
|
||||
}
|
||||
|
||||
reaction::ReactionSet AdaptiveEngineView::getInactiveNetworkReactions(scratch::StateBlob &ctx) const {
|
||||
const reaction::ReactionSet& baseEngineReactions = m_baseEngine.getNetworkReactions(ctx);
|
||||
const reaction::ReactionSet baseEngineInactiveReactions = m_baseEngine.getInactiveNetworkReactions(ctx);
|
||||
|
||||
|
||||
reaction::ReactionSet inactiveReactions = baseEngineInactiveReactions;
|
||||
const auto* state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, true>(ctx);
|
||||
const reaction::ReactionSet& activeReactions = state->active_reactions;
|
||||
|
||||
for (const auto& active_reaction : baseEngineReactions) {
|
||||
if (!inactiveReactions.contains(*active_reaction) && !activeReactions.contains(*active_reaction)) {
|
||||
inactiveReactions.add_reaction(*active_reaction);
|
||||
}
|
||||
}
|
||||
|
||||
return inactiveReactions;
|
||||
|
||||
}
|
||||
|
||||
double AdaptiveEngineView::getInactiveReactionMolarReactionFlow(
|
||||
scratch::StateBlob &ctx,
|
||||
const reaction::Reaction &reaction,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
return m_baseEngine.calculateMolarReactionFlow(ctx, reaction, comp, T9, rho);
|
||||
}
|
||||
|
||||
std::expected<std::unordered_map<Species, double>, EngineStatus> AdaptiveEngineView::getSpeciesTimescales(
|
||||
scratch::StateBlob& ctx,
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
@@ -268,6 +297,19 @@ namespace gridfire::engine {
|
||||
return m_baseEngine.getMostRecentRHSCalculation(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<scratch::StateBlob> AdaptiveEngineView::constructStateBlob(const scratch::StateBlob *blob) const {
|
||||
std::unique_ptr<scratch::StateBlob> i_blob;
|
||||
if (blob) {
|
||||
i_blob = blob->clone_structure();
|
||||
} else {
|
||||
i_blob = std::make_unique<scratch::StateBlob>();
|
||||
}
|
||||
i_blob->enroll<scratch::AdaptiveEngineViewScratchPad>();
|
||||
auto* state = scratch::get_state<scratch::AdaptiveEngineViewScratchPad, false>(*i_blob);
|
||||
state->initialize(*this);
|
||||
return i_blob;
|
||||
}
|
||||
|
||||
size_t AdaptiveEngineView::getSpeciesIndex(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
|
||||
@@ -267,6 +267,10 @@ namespace gridfire::engine {
|
||||
return m_baseEngine.getMostRecentRHSCalculation(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<scratch::StateBlob> DefinedEngineView::constructStateBlob(const scratch::StateBlob *blob) const {
|
||||
throw exceptions::ScratchPadError("DefinedEngineView does not support StateBlob construction. This will be implemented in a future version.");
|
||||
}
|
||||
|
||||
std::vector<size_t> DefinedEngineView::constructSpeciesIndexMap(
|
||||
scratch::StateBlob& ctx
|
||||
) const {
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "sunlinsol/sunlinsol_dense.h"
|
||||
|
||||
#include "xxhash64.h"
|
||||
#include "fourdst/composition/exceptions/exceptions_composition.h"
|
||||
#include "fourdst/composition/utils/composition_hash.h"
|
||||
|
||||
namespace {
|
||||
@@ -1123,6 +1124,19 @@ namespace gridfire::engine {
|
||||
return m_baseEngine.getMostRecentRHSCalculation(ctx);
|
||||
}
|
||||
|
||||
std::unique_ptr<scratch::StateBlob> MultiscalePartitioningEngineView::constructStateBlob(const scratch::StateBlob *blob) const {
|
||||
std::unique_ptr<scratch::StateBlob> i_blob;
|
||||
if (blob) {
|
||||
i_blob = blob->clone_structure();
|
||||
} else {
|
||||
i_blob = std::make_unique<scratch::StateBlob>();
|
||||
}
|
||||
i_blob->enroll<scratch::MultiscalePartitioningEngineViewScratchPad>();
|
||||
auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, false>(*i_blob);
|
||||
state->initialize();
|
||||
return i_blob;
|
||||
}
|
||||
|
||||
size_t MultiscalePartitioningEngineView::getSpeciesIndex(
|
||||
scratch::StateBlob& ctx,
|
||||
const Species &species
|
||||
@@ -1549,8 +1563,10 @@ namespace gridfire::engine {
|
||||
m_logger->flush_log();
|
||||
throw exceptions::EngineError("Non-finite abundance computed for species " + std::string(sp.name()) + " in QSE group solve.");
|
||||
}
|
||||
if (y < 0.0 && std::abs(y) < 1e-20) {
|
||||
if (y < 0.0 && std::abs(y) < 1e-16) {
|
||||
abundances.push_back(0.0);
|
||||
} else if (y < 0 && std::abs(y) >= 1e-16) {
|
||||
throw fourdst::composition::exceptions::InvalidCompositionError(std::format("Computed negative and non-trivial abundance {} for species {} in QSE group solve at T9 = {}, rho = {}. This likely indicates a failure of the QSE solver to converge to a physical solution.", y, sp.name(), T9, rho));
|
||||
} else {
|
||||
abundances.push_back(y);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "gridfire/trigger/procedures/trigger_pprint.h"
|
||||
#include "gridfire/exceptions/error_solver.h"
|
||||
#include "gridfire/utils/sundials.h"
|
||||
#include "gridfire/config/config.h"
|
||||
|
||||
|
||||
namespace gridfire::solver {
|
||||
@@ -74,6 +75,19 @@ namespace gridfire::solver {
|
||||
return description;
|
||||
}
|
||||
|
||||
fourdst::composition::Composition PointSolverTimestepContext::getPhysicalComposition() const {
|
||||
sunrealtype* y_data = N_VGetArrayPointer(state);
|
||||
std::vector<double> y_vec(y_data, y_data + networkSpecies.size());
|
||||
|
||||
for (int i = 0; i < y_vec.size(); i++) {
|
||||
if (y_vec[i] < 0 && std::abs(y_vec[i]) <= 1e-16) {
|
||||
y_vec[i] = 0.0; // clamp to 0 to avoid small numerical noise issues
|
||||
}
|
||||
}
|
||||
const fourdst::composition::Composition base_comp(networkSpecies, y_vec);
|
||||
return engine.collectComposition(state_ctx, base_comp, T9, rho);
|
||||
}
|
||||
|
||||
void PointSolverContext::init() {
|
||||
reset_all();
|
||||
init_context();
|
||||
@@ -165,6 +179,15 @@ namespace gridfire::solver {
|
||||
const DynamicEngine &engine
|
||||
): SingleZoneNetworkSolver(engine) {}
|
||||
|
||||
PointSolver::PointSolver(
|
||||
const engine::DynamicEngine &engine,
|
||||
const config::GridFireConfig &config
|
||||
) : SingleZoneNetworkSolver(engine) {
|
||||
m_config.mutate([&config](auto& cfg) {
|
||||
cfg = config;
|
||||
});
|
||||
}
|
||||
|
||||
NetOut PointSolver::evaluate(
|
||||
SolverContextBase& solver_ctx,
|
||||
const NetIn& netIn
|
||||
@@ -179,10 +202,13 @@ namespace gridfire::solver {
|
||||
bool forceReinitialize
|
||||
) const {
|
||||
auto* sctx_p = dynamic_cast<PointSolverContext*>(&solver_ctx);
|
||||
if (sctx_p == nullptr) {
|
||||
throw exceptions::SolverError("Provided solver context is not of type PointSolverContext");
|
||||
}
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Starting solver evaluation with T9: {} and rho: {}", netIn.temperature/1e9, netIn.density);
|
||||
LOG_TRACE_L1(m_logger, "Building engine update trigger....");
|
||||
auto trigger = trigger::solver::CVODE::makeEnginePartitioningTrigger(1e12, 1e10, 0.5, 2);
|
||||
auto trigger = trigger::solver::CVODE::makeEnginePartitioningTrigger(m_config->solver.pointSolver.trigger);
|
||||
LOG_TRACE_L1(m_logger, "Engine update trigger built!");
|
||||
|
||||
|
||||
@@ -194,10 +220,10 @@ namespace gridfire::solver {
|
||||
// 3. If the user has not set tolerances in code and the config does not have them, use hardcoded defaults
|
||||
|
||||
if (!sctx_p->abs_tol.has_value()) {
|
||||
sctx_p->abs_tol = m_config->solver.cvode.absTol;
|
||||
sctx_p->abs_tol = m_config->solver.pointSolver.absTol;
|
||||
}
|
||||
if (!sctx_p->rel_tol.has_value()) {
|
||||
sctx_p->rel_tol = m_config->solver.cvode.relTol;
|
||||
sctx_p->rel_tol = m_config->solver.pointSolver.relTol;
|
||||
}
|
||||
|
||||
|
||||
@@ -369,6 +395,8 @@ namespace gridfire::solver {
|
||||
rcMap,
|
||||
*sctx_p->engine_ctx
|
||||
);
|
||||
ctx.current_total_energy = current_energy;
|
||||
ctx.current_neutrino_energy_loss_rate = accumulated_neutrino_energy_loss;
|
||||
|
||||
prev_nonlinear_iterations = nliters + total_nonlinear_iterations;
|
||||
prev_convergence_failures = nlcfails + total_convergence_failures;
|
||||
@@ -395,7 +423,7 @@ namespace gridfire::solver {
|
||||
trigger::printWhy(trigger->why(ctx));
|
||||
}
|
||||
trigger->update(ctx);
|
||||
accumulated_energy += current_energy; // Add the specific energy rate to the accumulated energy
|
||||
accumulated_energy = current_energy; // Add the specific energy rate to the accumulated energy
|
||||
total_nonlinear_iterations += nliters;
|
||||
total_convergence_failures += nlcfails;
|
||||
total_steps += n_steps;
|
||||
@@ -569,7 +597,7 @@ namespace gridfire::solver {
|
||||
LOG_INFO(m_logger, "CVODE iteration complete");
|
||||
|
||||
sunrealtype* y_data = N_VGetArrayPointer(sctx_p->Y);
|
||||
accumulated_energy += y_data[numSpecies];
|
||||
accumulated_energy = y_data[numSpecies];
|
||||
std::vector<double> y_vec(y_data, y_data + numSpecies);
|
||||
|
||||
for (double & i : y_vec) {
|
||||
@@ -789,6 +817,16 @@ namespace gridfire::solver {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void PointSolver::cvode_error_handler(int line, const char *func, const char *file, const char *msg, SUNErrCode err_code, void *err_user_data, SUNContext sunctx) {
|
||||
auto* logger = static_cast<quill::Logger*>(err_user_data);
|
||||
if (!logger) return;
|
||||
|
||||
if (err_code < 0) {
|
||||
LOG_ERROR(logger, "[SUNDIALS ERROR] {} at {}:{}: {}", func, file, line, msg);
|
||||
} else {
|
||||
LOG_WARNING(logger, "[SUNDIALS WARNING] {} at {}:{}: {}", func, file, line, msg);
|
||||
}
|
||||
}
|
||||
PointSolver::CVODERHSOutputData PointSolver::calculate_rhs(
|
||||
const sunrealtype t,
|
||||
N_Vector y,
|
||||
@@ -863,9 +901,12 @@ namespace gridfire::solver {
|
||||
|
||||
sctx_p->cvode_mem = CVodeCreate(CV_BDF, sctx_p->sun_ctx);
|
||||
utils::check_cvode_flag(sctx_p->cvode_mem == nullptr ? -1 : 0, "CVodeCreate");
|
||||
|
||||
sctx_p->Y = utils::init_sun_vector(N, sctx_p->sun_ctx);
|
||||
sctx_p->YErr = N_VClone(sctx_p->Y);
|
||||
|
||||
SUNContext_PushErrHandler(sctx_p->sun_ctx, cvode_error_handler, m_logger);
|
||||
|
||||
sunrealtype *y_data = N_VGetArrayPointer(sctx_p->Y);
|
||||
for (size_t i = 0; i < numSpecies; i++) {
|
||||
const auto& species = m_engine.getNetworkSpecies(*sctx_p->engine_ctx)[i];
|
||||
@@ -880,6 +921,7 @@ namespace gridfire::solver {
|
||||
|
||||
utils::check_cvode_flag(CVodeInit(sctx_p->cvode_mem, cvode_rhs_wrapper, current_time, sctx_p->Y), "CVodeInit");
|
||||
utils::check_cvode_flag(CVodeSStolerances(sctx_p->cvode_mem, relTol, absTol), "CVodeSStolerances");
|
||||
utils::check_cvode_flag(CVodeSetInitStep(sctx_p->cvode_mem, 1.0e-8), "CVodeSetInitStep");
|
||||
|
||||
// Constraints
|
||||
// We constrain the solution vector using CVODE's built in constraint flags as outlines on page 53 of the CVODE manual
|
||||
@@ -1003,10 +1045,10 @@ namespace gridfire::solver {
|
||||
std::vector<double> E_full(y_err_data, y_err_data + num_components - 1);
|
||||
|
||||
if (!sctx_p->abs_tol.has_value()) {
|
||||
sctx_p->abs_tol = m_config->solver.cvode.absTol;
|
||||
sctx_p->abs_tol = m_config->solver.pointSolver.absTol;
|
||||
}
|
||||
if (!sctx_p->rel_tol.has_value()) {
|
||||
sctx_p->rel_tol = m_config->solver.cvode.relTol;
|
||||
sctx_p->rel_tol = m_config->solver.pointSolver.relTol;
|
||||
}
|
||||
|
||||
auto result = diagnostics::report_limiting_species(ctx, *user_data.engine, Y_full, E_full, sctx_p->rel_tol.value(), sctx_p->abs_tol.value(), 10, to_file);
|
||||
|
||||
@@ -4,12 +4,16 @@
|
||||
#include "gridfire/trigger/trigger_logical.h"
|
||||
#include "gridfire/trigger/trigger_abstract.h"
|
||||
|
||||
#include "sundials/sundials_nvector.h"
|
||||
|
||||
#include "quill/LogMacros.h"
|
||||
|
||||
#include <memory>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include "gridfire/utils/utils.h"
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
void push_to_fixed_deque(std::deque<T>& dq, T value, size_t max_size) {
|
||||
@@ -369,23 +373,195 @@ namespace gridfire::trigger::solver::CVODE {
|
||||
return false;
|
||||
}
|
||||
|
||||
BoundaryFluxTrigger::BoundaryFluxTrigger(
|
||||
const double relativeThreshold,
|
||||
const double absoluteThreshold
|
||||
) :
|
||||
m_relativeThreshold(relativeThreshold),
|
||||
m_absoluteThreshold(absoluteThreshold) {
|
||||
if (m_relativeThreshold <= 0.0) {
|
||||
throw exceptions::GridFireError(std::format("Relative threshold must be positive and non zero, currently it is {}", m_relativeThreshold));
|
||||
}
|
||||
}
|
||||
|
||||
void BoundaryFluxTrigger::step(const gridfire::solver::PointSolverTimestepContext &ctx) {
|
||||
// Does nothing; not a stateful trigger
|
||||
}
|
||||
|
||||
|
||||
bool BoundaryFluxTrigger::check(const gridfire::solver::PointSolverTimestepContext &ctx) const {
|
||||
// First get the current total flow through all active reactions
|
||||
sunrealtype* y_data = N_VGetArrayPointer(ctx.state);
|
||||
std::vector<double> Y(y_data, y_data + ctx.networkSpecies.size());
|
||||
// Adjust any tiny negative abundances to zero using std::ranges
|
||||
std::ranges::transform(
|
||||
Y,
|
||||
Y.begin(),
|
||||
[](const double y) {
|
||||
if (y < 0 && y > -1e-16) {
|
||||
return 0.0;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
);
|
||||
const fourdst::composition::Composition comp(ctx.networkSpecies, Y);
|
||||
|
||||
const double net_active_flow = get_reaction_set_flow(
|
||||
ctx.engine.getNetworkReactions(ctx.state_ctx),
|
||||
ctx,
|
||||
comp,
|
||||
ctx.T9,
|
||||
ctx.rho,
|
||||
ReactionSetType::ACTIVE
|
||||
);
|
||||
|
||||
const reaction::ReactionSet inactiveReactions = ctx.engine.getInactiveNetworkReactions(ctx.state_ctx);
|
||||
if (inactiveReactions.empty()) {
|
||||
m_misses++;
|
||||
return false; // No inactive reactions to consider
|
||||
}
|
||||
|
||||
const double net_boundary_flow = get_reaction_set_flow(
|
||||
inactiveReactions,
|
||||
ctx,
|
||||
comp,
|
||||
ctx.T9,
|
||||
ctx.rho,
|
||||
ReactionSetType::INACTIVE
|
||||
);
|
||||
|
||||
|
||||
if (net_boundary_flow > m_absoluteThreshold) {
|
||||
m_hits++;
|
||||
return true;
|
||||
}
|
||||
|
||||
const double relative_boundary_flow = net_boundary_flow / (net_active_flow + 1e-300); // Avoid division by zero
|
||||
if (relative_boundary_flow >= m_relativeThreshold) {
|
||||
m_hits++;
|
||||
return true;
|
||||
}
|
||||
|
||||
m_misses++;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void BoundaryFluxTrigger::update(const gridfire::solver::PointSolverTimestepContext &ctx) {
|
||||
// No-op since this is a stateless trigger
|
||||
m_updates++;
|
||||
}
|
||||
|
||||
void BoundaryFluxTrigger::reset() {
|
||||
m_hits = 0;
|
||||
m_misses = 0;
|
||||
m_updates = 0;
|
||||
m_resets++;
|
||||
}
|
||||
|
||||
std::string BoundaryFluxTrigger::name() const {
|
||||
return "BoundaryFluxTrigger";
|
||||
}
|
||||
|
||||
std::string BoundaryFluxTrigger::describe() const {
|
||||
return std::format("BoundaryFluxTrigger(rel={}, abs={})", m_relativeThreshold, m_absoluteThreshold);
|
||||
}
|
||||
|
||||
TriggerResult BoundaryFluxTrigger::why(const gridfire::solver::PointSolverTimestepContext &ctx) const {
|
||||
sunrealtype* y_data = N_VGetArrayPointer(ctx.state);
|
||||
const std::vector<double> Y(y_data, y_data + ctx.networkSpecies.size());
|
||||
const fourdst::composition::Composition comp(ctx.networkSpecies, Y);
|
||||
|
||||
const double net_active_flow = get_reaction_set_flow(
|
||||
ctx.engine.getNetworkReactions(ctx.state_ctx),
|
||||
ctx,
|
||||
comp,
|
||||
ctx.T9,
|
||||
ctx.rho,
|
||||
ReactionSetType::ACTIVE
|
||||
);
|
||||
const reaction::ReactionSet inactiveReactions = ctx.engine.getInactiveNetworkReactions(ctx.state_ctx);
|
||||
const double net_boundary_flow = get_reaction_set_flow(
|
||||
inactiveReactions,
|
||||
ctx,
|
||||
comp,
|
||||
ctx.T9,
|
||||
ctx.rho,
|
||||
ReactionSetType::INACTIVE
|
||||
);
|
||||
|
||||
TriggerResult result;
|
||||
result.name = name();
|
||||
if (check(ctx)) {
|
||||
result.value = true;
|
||||
result.description = std::format(
|
||||
"Triggered because boundary flux ({} mol/s) exceeded thresholds: absolute threshold = {} mol/s, relative threshold = {} (boundary flow = {} mol/s, active flow = {} mol/s)",
|
||||
net_boundary_flow,
|
||||
m_absoluteThreshold,
|
||||
m_relativeThreshold,
|
||||
net_boundary_flow,
|
||||
net_active_flow
|
||||
);
|
||||
} else {
|
||||
result.value = false;
|
||||
result.description = std::format(
|
||||
"Not triggered because boundary flux ({} mol/g/s) did not exceed thresholds: absolute threshold = {} mol/g/s, relative threshold = {} (boundary flow = {} mol/g/s, active flow = {} mol/g/s)",
|
||||
net_boundary_flow,
|
||||
m_absoluteThreshold,
|
||||
m_relativeThreshold,
|
||||
net_boundary_flow,
|
||||
net_active_flow
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
size_t BoundaryFluxTrigger::numMisses() const {
|
||||
return m_misses;
|
||||
}
|
||||
|
||||
double BoundaryFluxTrigger::get_reaction_set_flow(
|
||||
const reaction::ReactionSet &reactions,
|
||||
const gridfire::solver::PointSolverTimestepContext &ctx,
|
||||
const fourdst::composition::Composition &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const ReactionSetType type
|
||||
) {
|
||||
double flow = 0.0;
|
||||
for (const auto& reaction: reactions) {
|
||||
double rFlow = 0.0;
|
||||
if (type == ReactionSetType::ACTIVE) {
|
||||
rFlow = ctx.engine.calculateMolarReactionFlow(ctx.state_ctx, *reaction, comp, T9, rho);
|
||||
} else {
|
||||
rFlow = ctx.engine.getInactiveReactionMolarReactionFlow(ctx.state_ctx, *reaction, comp, T9, rho);
|
||||
}
|
||||
flow += std::abs(rFlow);
|
||||
}
|
||||
|
||||
return flow;
|
||||
|
||||
}
|
||||
|
||||
size_t BoundaryFluxTrigger::numTriggers() const {
|
||||
return m_hits;
|
||||
}
|
||||
|
||||
std::unique_ptr<Trigger<gridfire::solver::PointSolverTimestepContext>> makeEnginePartitioningTrigger(
|
||||
const double simulationTimeInterval,
|
||||
const double offDiagonalThreshold,
|
||||
const double timestepCollapseRatio,
|
||||
const size_t maxConvergenceFailures
|
||||
const config::TriggerConfig& cfg
|
||||
) {
|
||||
using ctx_t = gridfire::solver::PointSolverTimestepContext;
|
||||
|
||||
// 1. INSTABILITY TRIGGERS (High Priority)
|
||||
// 1. INSTABILITY TRIGGERS
|
||||
auto convergenceFailureTrigger = std::make_unique<ConvergenceFailureTrigger>(
|
||||
maxConvergenceFailures,
|
||||
cfg.maxConvergenceFailures,
|
||||
1.0f,
|
||||
10
|
||||
);
|
||||
|
||||
auto timestepCollapseTrigger = std::make_unique<TimestepCollapseTrigger>(
|
||||
timestepCollapseRatio,
|
||||
cfg.timestepCollapseRatio,
|
||||
true, // relative
|
||||
5
|
||||
);
|
||||
@@ -396,12 +572,24 @@ namespace gridfire::trigger::solver::CVODE {
|
||||
);
|
||||
|
||||
// 2. MAINTENANCE TRIGGERS
|
||||
auto offDiagTrigger = std::make_unique<OffDiagonalTrigger>(offDiagonalThreshold);
|
||||
auto offDiagTrigger = std::make_unique<OffDiagonalTrigger>(cfg.offDiagonalThreshold);
|
||||
|
||||
// 3. PREDICTIVE TRIGGERS
|
||||
auto boundaryFluxTrigger = std::make_unique<BoundaryFluxTrigger>(
|
||||
cfg.boundaryFlux.relativeThreshold,
|
||||
cfg.boundaryFlux.absoluteThreshold
|
||||
);
|
||||
|
||||
// Combine boundary flux into off-diagonal trigger
|
||||
auto nonInstabilityGroup = std::make_unique<OrTrigger<ctx_t>>(
|
||||
std::move(offDiagTrigger),
|
||||
std::move(boundaryFluxTrigger)
|
||||
);
|
||||
|
||||
// Combine: (Instability) OR (Structure Change)
|
||||
return std::make_unique<OrTrigger<ctx_t>>(
|
||||
std::move(instabilityGroup),
|
||||
std::move(offDiagTrigger)
|
||||
std::move(nonInstabilityGroup)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,24 @@
|
||||
namespace py = pybind11;
|
||||
|
||||
void register_config_bindings(pybind11::module &m) {
|
||||
|
||||
py::class_<gridfire::config::BoundaryFluxConfig>(m, "BoundaryFluxConfig")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("relativeThreshold", &gridfire::config::BoundaryFluxConfig::relativeThreshold)
|
||||
.def_readwrite("absoluteThreshold", &gridfire::config::BoundaryFluxConfig::absoluteThreshold);
|
||||
|
||||
py::class_<gridfire::config::TriggerConfig>(m, "TriggerConfig")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("offDiagonalThreshold", &gridfire::config::TriggerConfig::offDiagonalThreshold)
|
||||
.def_readwrite("timestepCollapseRatio", &gridfire::config::TriggerConfig::timestepCollapseRatio)
|
||||
.def_readwrite("maxConvergenceFailures", &gridfire::config::TriggerConfig::maxConvergenceFailures)
|
||||
.def_readwrite("boundaryFlux", &gridfire::config::TriggerConfig::boundaryFlux);
|
||||
|
||||
py::class_<gridfire::config::PointSolverConfig>(m, "PointSolverConfig")
|
||||
.def(py::init<>())
|
||||
.def_readwrite("absTol", &gridfire::config::PointSolverConfig::absTol)
|
||||
.def_readwrite("relTol", &gridfire::config::PointSolverConfig::relTol);
|
||||
.def_readwrite("relTol", &gridfire::config::PointSolverConfig::relTol)
|
||||
.def_readwrite("trigger", &gridfire::config::PointSolverConfig::trigger);
|
||||
|
||||
py::class_<gridfire::config::SolverConfig>(m, "SolverConfig")
|
||||
.def(py::init<>())
|
||||
|
||||
@@ -194,6 +194,25 @@ namespace {
|
||||
py::arg("ctx"),
|
||||
py::arg("species"),
|
||||
"Get the status of a species in the network."
|
||||
)
|
||||
.def("constructStateBlob",
|
||||
&T::constructStateBlob,
|
||||
py::arg("blob") = std::nullopt,
|
||||
"Construct the state blob for this engine. Generally base engines (GraphEngine) can call this with no arguments whereas views should take an argument to an already constructed state blob which will be cloned and then the clone will be modified"
|
||||
)
|
||||
.def(
|
||||
"getMostRecentRHSCalculation",
|
||||
[](const T& self, sp::StateBlob& ctx) -> std::optional<gridfire::engine::StepDerivatives<double>> {
|
||||
auto result = self.getMostRecentRHSCalculation(ctx);
|
||||
if (!result.has_value()) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
return result.value();
|
||||
}
|
||||
|
||||
},
|
||||
py::arg("ctx"),
|
||||
"Retrieve the most recent RHS calculation from the engine"
|
||||
);
|
||||
|
||||
}
|
||||
@@ -529,7 +548,18 @@ void con_stype_register_graph_engine_bindings(const pybind11::module &m) {
|
||||
&gridfire::engine::GraphEngine::isUsingReverseReactions,
|
||||
"Check if the engine is using reverse reactions."
|
||||
);
|
||||
|
||||
py_graph_engine_bindings.def(
|
||||
"addReaction",
|
||||
py::overload_cast<const gridfire::reaction::Reaction&>(&gridfire::engine::GraphEngine::addReaction),
|
||||
py::arg("reaction"),
|
||||
"Add a reaction to the engine's network manually."
|
||||
);
|
||||
py_graph_engine_bindings.def(
|
||||
"addReaction",
|
||||
py::overload_cast<const std::string&>(&gridfire::engine::GraphEngine::addReaction),
|
||||
py::arg("reaction_id"),
|
||||
"Add a reaction to the engine's network manually using a reaction identifier string."
|
||||
);
|
||||
// Register the general dynamic engine bindings
|
||||
registerDynamicEngineDefs<gridfire::engine::GraphEngine, gridfire::engine::DynamicEngine>(py_graph_engine_bindings);
|
||||
}
|
||||
|
||||
@@ -293,6 +293,16 @@ std::optional<gridfire::engine::StepDerivatives<double>> PyDynamicEngine::getMos
|
||||
);
|
||||
}
|
||||
|
||||
std::unique_ptr<gridfire::engine::scratch::StateBlob> PyDynamicEngine::constructStateBlob(
|
||||
const gridfire::engine::scratch::StateBlob *blob) const {
|
||||
PYBIND11_OVERRIDE_PURE(
|
||||
std::unique_ptr<gridfire::engine::scratch::StateBlob>,
|
||||
gridfire::engine::DynamicEngine,
|
||||
constructStateBlob,
|
||||
blob
|
||||
);
|
||||
}
|
||||
|
||||
const gridfire::engine::Engine& PyEngineView::getBaseEngine() const {
|
||||
PYBIND11_OVERRIDE_PURE(
|
||||
const gridfire::engine::Engine&,
|
||||
|
||||
@@ -130,6 +130,10 @@ public:
|
||||
gridfire::engine::scratch::StateBlob &ctx
|
||||
) const override;
|
||||
|
||||
std::unique_ptr<gridfire::engine::scratch::StateBlob> constructStateBlob(
|
||||
const gridfire::engine::scratch::StateBlob *blob
|
||||
) const override;
|
||||
|
||||
private:
|
||||
mutable std::vector<fourdst::atomic::Species> m_species_cache;
|
||||
};
|
||||
|
||||
@@ -51,6 +51,13 @@ void register_solver_bindings(const py::module &m) {
|
||||
},
|
||||
py::return_value_policy::reference_internal
|
||||
);
|
||||
py_cvode_timestep_context.def_property_readonly(
|
||||
"composition",
|
||||
[](const gridfire::solver::PointSolverTimestepContext& self) -> fourdst::composition::Composition {
|
||||
return self.getPhysicalComposition();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
auto py_solver_context_base = py::class_<gridfire::solver::SolverContextBase>(m, "SolverContextBase");
|
||||
@@ -166,6 +173,20 @@ void register_solver_bindings(const py::module &m) {
|
||||
"Initialize the PointSolver object."
|
||||
);
|
||||
|
||||
py_point_solver.def(
|
||||
py::init<gridfire::engine::DynamicEngine&, gridfire::config::GridFireConfig&>(),
|
||||
py::arg("engine"),
|
||||
py::arg("config"),
|
||||
"Initialize the PointSolver object with a configuration set."
|
||||
);
|
||||
|
||||
py_point_solver.def(
|
||||
"getConfig",
|
||||
&gridfire::solver::PointSolver::getConfig,
|
||||
"Get a copy of the config object"
|
||||
);
|
||||
|
||||
|
||||
py_point_solver.def(
|
||||
"evaluate",
|
||||
py::overload_cast<gridfire::solver::SolverContextBase&, const gridfire::NetIn&, bool, bool>(&gridfire::solver::PointSolver::evaluate, py::const_),
|
||||
|
||||
Reference in New Issue
Block a user