feat(network): added half lifes, spin flip parity, better reaction acritecture

This commit is contained in:
2025-06-29 14:53:39 -04:00
parent 2a410dc3fd
commit 29af4c3bab
14 changed files with 2270 additions and 637 deletions

View File

@@ -1,30 +1,99 @@
#pragma once
#include "gridfire/network.h" // For NetIn, NetOut
#include "../reaction/reaction.h"
#include "fourdst/composition/composition.h"
#include "fourdst/config/config.h"
#include "fourdst/logging/logging.h"
#include "gridfire/reaction/reaction.h"
#include <vector>
#include <unordered_map>
/**
* @file engine_abstract.h
* @brief Abstract interfaces for reaction network engines in GridFire.
*
* This header defines the abstract base classes and concepts for implementing
* reaction network solvers in the GridFire framework. It provides the contract
* for calculating right-hand sides, energy generation, Jacobians, stoichiometry,
* and other core operations required for time integration of nuclear reaction networks.
*
* @author
* Emily M. Boudreaux
*/
namespace gridfire {
/**
* @brief Concept for types allowed in engine calculations.
*
* This concept restricts template parameters to either double or CppAD::AD<double>,
* enabling both standard and automatic differentiation types.
*/
template<typename T>
concept IsArithmeticOrAD = std::is_same_v<T, double> || std::is_same_v<T, CppAD::AD<double>>;
/**
* @brief Structure holding derivatives and energy generation for a network step.
*
* @tparam T Numeric type (double or CppAD::AD<double>).
*
* This struct is used to return both the time derivatives of all species abundances
* and the specific nuclear energy generation rate for a single network evaluation.
*
* Example usage:
* @code
* StepDerivatives<double> result = engine.calculateRHSAndEnergy(Y, T9, rho);
* for (double dydt_i : result.dydt) {
* // Use derivative
* }
* double energyRate = result.nuclearEnergyGenerationRate;
* @endcode
*/
template <IsArithmeticOrAD T>
struct StepDerivatives {
std::vector<T> dydt; ///< Derivatives of abundances.
T nuclearEnergyGenerationRate = T(0.0); ///< Specific energy generation rate.
std::vector<T> dydt; ///< Derivatives of abundances (dY/dt for each species).
T nuclearEnergyGenerationRate = T(0.0); ///< Specific energy generation rate (e.g., erg/g/s).
};
/**
* @brief Abstract base class for a reaction network engine.
*
* This class defines the minimal interface for a reaction network engine,
* which is responsible for evaluating the right-hand side (dY/dt) and
* energy generation for a given set of abundances, temperature, and density.
*
* Intended usage: Derive from this class to implement a concrete engine
* for a specific network or integration method.
*
* Example:
* @code
* class MyEngine : public gridfire::Engine {
* // Implement required methods...
* };
* @endcode
*/
class Engine {
public:
/**
* @brief Virtual destructor.
*/
virtual ~Engine() = default;
/**
* @brief Get the list of species in the network.
* @return Vector of Species objects representing all network species.
*/
virtual const std::vector<fourdst::atomic::Species>& getNetworkSpecies() const = 0;
/**
* @brief Calculate the right-hand side (dY/dt) and energy generation.
*
* @param Y Vector of current abundances for all species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return StepDerivatives<double> containing dY/dt and energy generation rate.
*
* This function must be implemented by derived classes to compute the
* time derivatives of all species and the specific nuclear energy generation
* rate for the current state.
*/
virtual StepDerivatives<double> calculateRHSAndEnergy(
const std::vector<double>& Y,
double T9,
@@ -32,23 +101,85 @@ namespace gridfire {
) const = 0;
};
/**
* @brief Abstract class for engines supporting Jacobian and stoichiometry operations.
*
* Extends Engine with additional methods for:
* - Generating and accessing the Jacobian matrix (for implicit solvers).
* - Generating and accessing the stoichiometry matrix.
* - Calculating molar reaction flows for individual reactions.
* - Accessing the set of logical reactions in the network.
* - Computing timescales for each species.
*
* Intended usage: Derive from this class to implement engines that support
* advanced solver features such as implicit integration, sensitivity analysis,
* QSE (Quasi-Steady-State Equilibrium) handling, and more.
*/
class DynamicEngine : public Engine {
public:
/**
* @brief Generate the Jacobian matrix for the current state.
*
* @param Y Vector of current abundances.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
*
* This method must compute and store the Jacobian matrix (∂(dY/dt)_i/∂Y_j)
* for the current state. The matrix can then be accessed via getJacobianMatrixEntry().
*/
virtual void generateJacobianMatrix(
const std::vector<double>& Y,
double T9, double rho
) = 0;
/**
* @brief Get an entry from the previously generated Jacobian matrix.
*
* @param i Row index (species index).
* @param j Column index (species index).
* @return Value of the Jacobian matrix at (i, j).
*
* The Jacobian must have been generated by generateJacobianMatrix() before calling this.
*/
virtual double getJacobianMatrixEntry(
int i,
int j
) const = 0;
/**
* @brief Generate the stoichiometry matrix for the network.
*
* This method must compute and store the stoichiometry matrix,
* which encodes the net change of each species in each reaction.
*/
virtual void generateStoichiometryMatrix() = 0;
/**
* @brief Get an entry from the stoichiometry matrix.
*
* @param speciesIndex Index of the species.
* @param reactionIndex Index of the reaction.
* @return Stoichiometric coefficient for the species in the reaction.
*
* The stoichiometry matrix must have been generated by generateStoichiometryMatrix().
*/
virtual int getStoichiometryMatrixEntry(
int speciesIndex,
int reactionIndex
) const = 0;
/**
* @brief Calculate the molar reaction flow for a given reaction.
*
* @param reaction The reaction for which to calculate the flow.
* @param Y Vector of current abundances.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return Molar flow rate for the reaction (e.g., mol/g/s).
*
* This method computes the net rate at which the given reaction proceeds
* under the current state.
*/
virtual double calculateMolarReactionFlow(
const reaction::Reaction& reaction,
const std::vector<double>& Y,
@@ -56,7 +187,24 @@ namespace gridfire {
double rho
) const = 0;
virtual const reaction::REACLIBLogicalReactionSet& getNetworkReactions() const = 0;
/**
* @brief Get the set of logical reactions in the network.
*
* @return Reference to the LogicalReactionSet containing all reactions.
*/
virtual const reaction::LogicalReactionSet& getNetworkReactions() const = 0;
/**
* @brief Compute timescales for all species in the network.
*
* @param Y Vector of current abundances.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return Map from Species to their characteristic timescales (s).
*
* This method estimates the timescale for abundance change of each species,
* which can be used for timestep control, diagnostics, and reaction network culling.
*/
virtual std::unordered_map<fourdst::atomic::Species, double> getSpeciesTimescales(
const std::vector<double>& Y,
double T9,

View File

@@ -1,75 +1,316 @@
#pragma once
#include "gridfire/engine/engine_abstract.h"
#include "gridfire/engine/engine_view_abstract.h"
#include "gridfire/network.h"
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/config/config.h"
#include "fourdst/logging/logging.h"
#include "quill/Logger.h"
namespace gridfire {
/**
* @class AdaptiveEngineView
* @brief An engine view that dynamically adapts the reaction network based on runtime conditions.
*
* This class implements an EngineView that dynamically culls species and reactions from the
* full reaction network based on their reaction flow rates and connectivity. This allows for
* efficient simulation of reaction networks by focusing computational effort on the most
* important species and reactions.
*
* The AdaptiveEngineView maintains a subset of "active" species and reactions, and maps
* between the full network indices and the active subset indices. This allows the base engine
* to operate on the full network data, while the AdaptiveEngineView provides a reduced view
* for external clients.
*
* The adaptation process is driven by the `update()` method, which performs the following steps:
* 1. **Reaction Flow Calculation:** Calculates the molar reaction flow rate for each reaction
* in the full network based on the current temperature, density, and composition.
* 2. **Reaction Culling:** Culls reactions with flow rates below a threshold, determined by
* a relative culling threshold multiplied by the maximum flow rate.
* 3. **Connectivity Analysis:** Performs a connectivity analysis to identify species that are
* reachable from the initial fuel species through the culled reaction network.
* 4. **Species Culling:** Culls species that are not reachable from the initial fuel.
* 5. **Index Map Construction:** Constructs index maps to map between the full network indices
* and the active subset indices for species and reactions.
*
* @implements DynamicEngine
* @implements EngineView<DynamicEngine>
*
* @see engine_abstract.h
* @see engine_view_abstract.h
* @see AdaptiveEngineView::update()
*/
class AdaptiveEngineView final : public DynamicEngine, public EngineView<DynamicEngine> {
public:
/**
* @brief Constructs an AdaptiveEngineView.
*
* @param baseEngine The underlying DynamicEngine to which this view delegates calculations.
*
* Initializes the active species and reactions to the full network, and constructs the
* initial index maps.
*/
explicit AdaptiveEngineView(DynamicEngine& baseEngine);
/**
* @brief Updates the active species and reactions based on the current conditions.
*
* @param netIn The current network input, containing temperature, density, and composition.
*
* This method performs the reaction flow calculation, reaction culling, connectivity analysis,
* and index map construction steps described above.
*
* The culling thresholds are read from the configuration using the following keys:
* - `gridfire:AdaptiveEngineView:RelativeCullingThreshold` (default: 1e-75)
*
* @throws std::runtime_error If there is a mismatch between the active reactions and the base engine.
* @post The active species and reactions are updated, and the index maps are reconstructed.
* @see AdaptiveEngineView
* @see AdaptiveEngineView::constructSpeciesIndexMap()
* @see AdaptiveEngineView::constructReactionIndexMap()
*/
void update(const NetIn& netIn);
/**
* @brief Gets the list of active species in the network.
* @return A const reference to the vector of active species.
*/
const std::vector<fourdst::atomic::Species>& getNetworkSpecies() const override;
/**
* @brief Calculates the right-hand side (dY/dt) and energy generation for the active species.
*
* @param Y_culled A vector of abundances for the active species.
* @param T9 The temperature in units of 10^9 K.
* @param rho The density in g/cm^3.
* @return A StepDerivatives struct containing the derivatives of the active species and the
* nuclear energy generation rate.
*
* This method maps the culled abundances to the full network abundances, calls the base engine
* to calculate the RHS and energy generation, and then maps the full network derivatives back
* to the culled derivatives.
*
* @throws std::runtime_error If the AdaptiveEngineView is stale (i.e., `update()` has not been called).
* @see AdaptiveEngineView::update()
*/
StepDerivatives<double> calculateRHSAndEnergy(
const std::vector<double> &Y,
const std::vector<double> &Y_culled,
const double T9,
const double rho
) const override;
/**
* @brief Generates the Jacobian matrix for the active species.
*
* @param Y_culled A vector of abundances for the active species.
* @param T9 The temperature in units of 10^9 K.
* @param rho The density in g/cm^3.
*
* This method maps the culled abundances to the full network abundances and calls the base engine
* to generate the Jacobian matrix.
*
* @throws std::runtime_error If the AdaptiveEngineView is stale (i.e., `update()` has not been called).
* @see AdaptiveEngineView::update()
*/
void generateJacobianMatrix(
const std::vector<double> &Y,
const std::vector<double> &Y_culled,
const double T9,
const double rho
) override;
/**
* @brief Gets an entry from the Jacobian matrix for the active species.
*
* @param i_culled The row index (species index) in the culled matrix.
* @param j_culled The column index (species index) in the culled matrix.
* @return The value of the Jacobian matrix at (i_culled, j_culled).
*
* This method maps the culled indices to the full network indices and calls the base engine
* to get the Jacobian matrix entry.
*
* @throws std::runtime_error If the AdaptiveEngineView is stale (i.e., `update()` has not been called).
* @throws std::out_of_range If the culled index is out of bounds for the species index map.
* @see AdaptiveEngineView::update()
*/
double getJacobianMatrixEntry(
const int i,
const int j
const int i_culled,
const int j_culled
) const override;
/**
* @brief Generates the stoichiometry matrix for the active reactions and species.
*
* This method calls the base engine to generate the stoichiometry matrix.
*
* @throws std::runtime_error If the AdaptiveEngineView is stale (i.e., `update()` has not been called).
* @note The stoichiometry matrix generated by the base engine is assumed to be consistent with
* the active species and reactions in this view.
*/
void generateStoichiometryMatrix() override;
/**
* @brief Gets an entry from the stoichiometry matrix for the active species and reactions.
*
* @param speciesIndex_culled The index of the species in the culled species list.
* @param reactionIndex_culled The index of the reaction in the culled reaction list.
* @return The stoichiometric coefficient for the given species and reaction.
*
* This method maps the culled indices to the full network indices and calls the base engine
* to get the stoichiometry matrix entry.
*
* @throws std::runtime_error If the AdaptiveEngineView is stale (i.e., `update()` has not been called).
* @throws std::out_of_range If the culled index is out of bounds for the species or reaction index map.
* @see AdaptiveEngineView::update()
*/
int getStoichiometryMatrixEntry(
const int speciesIndex,
const int reactionIndex
const int speciesIndex_culled,
const int reactionIndex_culled
) const override;
/**
* @brief Calculates the molar reaction flow for a given reaction in the active network.
*
* @param reaction The reaction for which to calculate the flow.
* @param Y_culled Vector of current abundances for the active species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return Molar flow rate for the reaction (e.g., mol/g/s).
*
* This method maps the culled abundances to the full network abundances and calls the base engine
* to calculate the molar reaction flow.
*
* @throws std::runtime_error If the AdaptiveEngineView is stale (i.e., `update()` has not been called).
* @throws std::runtime_error If the reaction is not part of the active reactions in the adaptive engine view.
*/
double calculateMolarReactionFlow(
const reaction::Reaction &reaction,
const std::vector<double> &Y,
const std::vector<double> &Y_culled,
double T9,
double rho
) const override;
const reaction::REACLIBLogicalReactionSet& getNetworkReactions() const override;
/**
* @brief Gets the set of active logical reactions in the network.
*
* @return Reference to the LogicalReactionSet containing all active reactions.
*/
const reaction::LogicalReactionSet& getNetworkReactions() const override;
/**
* @brief Computes timescales for all active species in the network.
*
* @param Y_culled Vector of current abundances for the active species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return Map from Species to their characteristic timescales (s).
*
* This method maps the culled abundances to the full network abundances and calls the base engine
* to compute the species timescales.
*
* @throws std::runtime_error If the AdaptiveEngineView is stale (i.e., `update()` has not been called).
*/
std::unordered_map<fourdst::atomic::Species, double> getSpeciesTimescales(
const std::vector<double> &Y,
const std::vector<double> &Y_culled,
double T9,
double rho
) const override;
/**
* @brief Gets the base engine.
* @return A const reference to the base engine.
*/
const DynamicEngine& getBaseEngine() const override { return m_baseEngine; }
private:
using Config = fourdst::config::Config;
using LogManager = fourdst::logging::LogManager;
Config& m_config = Config::getInstance();
quill::Logger* m_logger = LogManager::getInstance().getLogger("log");
DynamicEngine& m_baseEngine;
std::vector<fourdst::atomic::Species> m_activeSpecies;
reaction::REACLIBLogicalReactionSet m_activeReactions;
reaction::LogicalReactionSet m_activeReactions;
std::vector<size_t> m_speciesIndexMap;
std::vector<size_t> m_reactionIndexMap;
bool m_isStale = true;
Config& m_config = Config::getInstance();
quill::Logger* m_logger = LogManager::getInstance().getLogger("log");
private:
/**
* @brief Constructs the species index map.
*
* @return A vector mapping culled species indices to full species indices.
*
* This method creates a map from the indices of the active species to the indices of the
* corresponding species in the full network.
*
* @see AdaptiveEngineView::update()
*/
std::vector<size_t> constructSpeciesIndexMap() const;
/**
* @brief Constructs the reaction index map.
*
* @return A vector mapping culled reaction indices to full reaction indices.
*
* This method creates a map from the indices of the active reactions to the indices of the
* corresponding reactions in the full network.
*
* @see AdaptiveEngineView::update()
*/
std::vector<size_t> constructReactionIndexMap() const;
/**
* @brief Maps a vector of culled abundances to a vector of full abundances.
*
* @param culled A vector of abundances for the active species.
* @return A vector of abundances for the full network, with the abundances of the active
* species copied from the culled vector.
*/
std::vector<double> mapCulledToFull(const std::vector<double>& culled) const;
/**
* @brief Maps a vector of full abundances to a vector of culled abundances.
*
* @param full A vector of abundances for the full network.
* @return A vector of abundances for the active species, with the abundances of the active
* species copied from the full vector.
*/
std::vector<double> mapFullToCulled(const std::vector<double>& full) const;
/**
* @brief Maps a culled species index to a full species index.
*
* @param culledSpeciesIndex The index of the species in the culled species list.
* @return The index of the corresponding species in the full network.
*
* @throws std::out_of_range If the culled index is out of bounds for the species index map.
*/
size_t mapCulledToFullSpeciesIndex(size_t culledSpeciesIndex) const;
/**
* @brief Maps a culled reaction index to a full reaction index.
*
* @param culledReactionIndex The index of the reaction in the culled reaction list.
* @return The index of the corresponding reaction in the full network.
*
* @throws std::out_of_range If the culled index is out of bounds for the reaction index map.
*/
size_t mapCulledToFullReactionIndex(size_t culledReactionIndex) const;
/**
* @brief Validates that the AdaptiveEngineView is not stale.
*
* @throws std::runtime_error If the AdaptiveEngineView is stale (i.e., `update()` has not been called).
*/
void validateState() const;
private:
/**
* @brief A struct to hold a reaction and its flow rate.
*/
struct ReactionFlow {
const reaction::Reaction* reactionPtr;
double flowRate;

View File

@@ -23,35 +23,148 @@
// REACLIBReactions are quite large data structures, so this could be a performance bottleneck.
namespace gridfire {
typedef CppAD::AD<double> ADDouble; ///< Alias for CppAD AD type for double precision.
/**
* @brief Alias for CppAD AD type for double precision.
*
* This alias simplifies the use of the CppAD automatic differentiation type.
*/
typedef CppAD::AD<double> ADDouble;
using fourdst::config::Config;
using fourdst::logging::LogManager;
using fourdst::constant::Constants;
/**
* @brief Minimum density threshold below which reactions are ignored.
*
* Reactions are not calculated if the density falls below this threshold.
* This helps to improve performance by avoiding unnecessary calculations
* in very low-density regimes.
*/
static constexpr double MIN_DENSITY_THRESHOLD = 1e-18;
/**
* @brief Minimum abundance threshold below which species are ignored.
*
* Species with abundances below this threshold are treated as zero in
* reaction rate calculations. This helps to improve performance by
* avoiding unnecessary calculations for trace species.
*/
static constexpr double MIN_ABUNDANCE_THRESHOLD = 1e-18;
/**
* @brief Minimum value for Jacobian matrix entries.
*
* Jacobian matrix entries with absolute values below this threshold are
* treated as zero to maintain sparsity and improve performance.
*/
static constexpr double MIN_JACOBIAN_THRESHOLD = 1e-24;
/**
* @class GraphEngine
* @brief A reaction network engine that uses a graph-based representation.
*
* The GraphEngine class implements the DynamicEngine interface using a
* graph-based representation of the reaction network. It uses sparse
* matrices for efficient storage and computation of the stoichiometry
* and Jacobian matrices. Automatic differentiation (AD) is used to
* calculate the Jacobian matrix.
*
* The engine supports:
* - Calculation of the right-hand side (dY/dt) and energy generation rate.
* - Generation and access to the Jacobian matrix.
* - Generation and access to the stoichiometry matrix.
* - Calculation of molar reaction flows.
* - Access to the set of logical reactions in the network.
* - Computation of timescales for each species.
* - Exporting the network to DOT and CSV formats for visualization and analysis.
*
* @implements DynamicEngine
*
* @see engine_abstract.h
*/
class GraphEngine final : public DynamicEngine{
public:
/**
* @brief Constructs a GraphEngine from a composition.
*
* @param composition The composition of the material.
*
* This constructor builds the reaction network from the given composition
* using the `build_reaclib_nuclear_network` function.
*
* @see build_reaclib_nuclear_network
*/
explicit GraphEngine(const fourdst::composition::Composition &composition);
explicit GraphEngine(reaction::REACLIBLogicalReactionSet reactions);
/**
* @brief Constructs a GraphEngine from a set of reactions.
*
* @param reactions The set of reactions to use in the network.
*
* This constructor uses the given set of reactions to construct the
* reaction network.
*/
explicit GraphEngine(reaction::LogicalReactionSet reactions);
/**
* @brief Calculates the right-hand side (dY/dt) and energy generation rate.
*
* @param Y Vector of current abundances for all species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return StepDerivatives<double> containing dY/dt and energy generation rate.
*
* This method calculates the time derivatives of all species and the
* specific nuclear energy generation rate for the current state.
*
* @see StepDerivatives
*/
StepDerivatives<double> calculateRHSAndEnergy(
const std::vector<double>& Y,
const double T9,
const double rho
) const override;
/**
* @brief Generates the Jacobian matrix for the current state.
*
* @param Y Vector of current abundances.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
*
* This method computes and stores the Jacobian matrix (∂(dY/dt)_i/∂Y_j)
* for the current state using automatic differentiation. The matrix can
* then be accessed via `getJacobianMatrixEntry()`.
*
* @see getJacobianMatrixEntry()
*/
void generateJacobianMatrix(
const std::vector<double>& Y,
const double T9,
const double rho
) override;
/**
* @brief Generates the stoichiometry matrix for the network.
*
* This method computes and stores the stoichiometry matrix,
* which encodes the net change of each species in each reaction.
*/
void generateStoichiometryMatrix() override;
/**
* @brief Calculates the molar reaction flow for a given reaction.
*
* @param reaction The reaction for which to calculate the flow.
* @param Y Vector of current abundances.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return Molar flow rate for the reaction (e.g., mol/g/s).
*
* This method computes the net rate at which the given reaction proceeds
* under the current state.
*/
double calculateMolarReactionFlow(
const reaction::Reaction& reaction,
const std::vector<double>&Y,
@@ -59,39 +172,130 @@ namespace gridfire {
const double rho
) const override;
/**
* @brief Gets the list of species in the network.
* @return Vector of Species objects representing all network species.
*/
[[nodiscard]] const std::vector<fourdst::atomic::Species>& getNetworkSpecies() const override;
[[nodiscard]] const reaction::REACLIBLogicalReactionSet& getNetworkReactions() const override;
/**
* @brief Gets the set of logical reactions in the network.
* @return Reference to the LogicalReactionSet containing all reactions.
*/
[[nodiscard]] const reaction::LogicalReactionSet& getNetworkReactions() const override;
/**
* @brief Gets an entry from the previously generated Jacobian matrix.
*
* @param i Row index (species index).
* @param j Column index (species index).
* @return Value of the Jacobian matrix at (i, j).
*
* The Jacobian must have been generated by `generateJacobianMatrix()` before calling this.
*
* @see generateJacobianMatrix()
*/
[[nodiscard]] double getJacobianMatrixEntry(
const int i,
const int j
) const override;
[[nodiscard]] std::unordered_map<fourdst::atomic::Species, int> getNetReactionStoichiometry(
/**
* @brief Gets the net stoichiometry for a given reaction.
*
* @param reaction The reaction for which to get the stoichiometry.
* @return Map of species to their stoichiometric coefficients.
*/
[[nodiscard]] static std::unordered_map<fourdst::atomic::Species, int> getNetReactionStoichiometry(
const reaction::Reaction& reaction
) const;
);
/**
* @brief Gets an entry from the stoichiometry matrix.
*
* @param speciesIndex Index of the species.
* @param reactionIndex Index of the reaction.
* @return Stoichiometric coefficient for the species in the reaction.
*
* The stoichiometry matrix must have been generated by `generateStoichiometryMatrix()`.
*
* @see generateStoichiometryMatrix()
*/
[[nodiscard]] int getStoichiometryMatrixEntry(
const int speciesIndex,
const int reactionIndex
) const override;
/**
* @brief Computes timescales for all species in the network.
*
* @param Y Vector of current abundances.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return Map from Species to their characteristic timescales (s).
*
* This method estimates the timescale for abundance change of each species,
* which can be used for timestep control or diagnostics.
*/
[[nodiscard]] std::unordered_map<fourdst::atomic::Species, double> getSpeciesTimescales(
const std::vector<double>& Y,
double T9,
double rho
) const override;
/**
* @brief Checks if a given species is involved in the network.
*
* @param species The species to check.
* @return True if the species is involved in the network, false otherwise.
*/
[[nodiscard]] bool involvesSpecies(
const fourdst::atomic::Species& species
) const;
/**
* @brief Exports the network to a DOT file for visualization.
*
* @param filename The name of the DOT file to create.
*
* This method generates a DOT file that can be used to visualize the
* reaction network as a graph. The DOT file can be converted to a
* graphical image using Graphviz.
*
* @throws std::runtime_error If the file cannot be opened for writing.
*
* Example usage:
* @code
* engine.exportToDot("network.dot");
* @endcode
*/
void exportToDot(
const std::string& filename
) const;
/**
* @brief Exports the network to a CSV file for analysis.
*
* @param filename The name of the CSV file to create.
*
* This method generates a CSV file containing information about the
* reactions in the network, including the reactants, products, Q-value,
* and reaction rate coefficients.
*
* @throws std::runtime_error If the file cannot be opened for writing.
*
* Example usage:
* @code
* engine.exportToCSV("network.csv");
* @endcode
*/
void exportToCSV(
const std::string& filename
) const;
private:
reaction::REACLIBLogicalReactionSet m_reactions; ///< Set of REACLIB reactions in the network.
reaction::LogicalReactionSet m_reactions; ///< Set of REACLIB reactions in the network.
std::unordered_map<std::string_view, reaction::Reaction*> m_reactionIDMap; ///< Map from reaction ID to REACLIBReaction. //PERF: This makes copies of REACLIBReaction and could be a performance bottleneck.
std::vector<fourdst::atomic::Species> m_networkSpecies; ///< Vector of unique species in the network.
@@ -108,20 +312,100 @@ namespace gridfire {
quill::Logger* m_logger = LogManager::getInstance().getLogger("log");
private:
/**
* @brief Synchronizes the internal maps.
*
* This method synchronizes the internal maps used by the engine,
* including the species map, reaction ID map, and species-to-index map.
* It also generates the stoichiometry matrix and records the AD tape.
*/
void syncInternalMaps();
/**
* @brief Collects the unique species in the network.
*
* This method collects the unique species in the network from the
* reactants and products of all reactions.
*/
void collectNetworkSpecies();
/**
* @brief Populates the reaction ID map.
*
* This method populates the reaction ID map, which maps reaction IDs
* to REACLIBReaction objects.
*/
void populateReactionIDMap();
/**
* @brief Populates the species-to-index map.
*
* This method populates the species-to-index map, which maps species
* to their index in the stoichiometry matrix.
*/
void populateSpeciesToIndexMap();
/**
* @brief Reserves space for the Jacobian matrix.
*
* This method reserves space for the Jacobian matrix, which is used
* to store the partial derivatives of the right-hand side of the ODE
* with respect to the species abundances.
*/
void reserveJacobianMatrix();
/**
* @brief Records the AD tape for the right-hand side of the ODE.
*
* This method records the AD tape for the right-hand side of the ODE,
* which is used to calculate the Jacobian matrix using automatic
* differentiation.
*
* @throws std::runtime_error If there are no species in the network.
*/
void recordADTape();
/**
* @brief Validates mass and charge conservation across all reactions.
*
* @return True if all reactions conserve mass and charge, false otherwise.
*
* This method checks that all reactions in the network conserve mass
* and charge. If any reaction does not conserve mass or charge, an
* error message is logged and false is returned.
*/
[[nodiscard]] bool validateConservation() const;
/**
* @brief Validates the composition against the current reaction set.
*
* @param composition The composition to validate.
* @param culling The culling threshold to use.
* @param T9 The temperature to use.
*
* This method validates the composition against the current reaction set.
* If the composition is not compatible with the reaction set, the
* reaction set is rebuilt from the composition.
*/
void validateComposition(
const fourdst::composition::Composition &composition,
double culling,
double T9
);
/**
* @brief Calculates the molar reaction flow for a given reaction.
*
* @tparam T The numeric type to use for the calculation.
* @param reaction The reaction for which to calculate the flow.
* @param Y Vector of current abundances.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return Molar flow rate for the reaction (e.g., mol/g/s).
*
* This method computes the net rate at which the given reaction proceeds
* under the current state.
*/
template <IsArithmeticOrAD T>
T calculateMolarReactionFlow(
const reaction::Reaction &reaction,
@@ -130,6 +414,18 @@ namespace gridfire {
const T rho
) const;
/**
* @brief Calculates all derivatives (dY/dt) and the energy generation rate.
*
* @tparam T The numeric type to use for the calculation.
* @param Y_in Vector of current abundances for all species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return StepDerivatives<T> containing dY/dt and energy generation rate.
*
* This method calculates the time derivatives of all species and the
* specific nuclear energy generation rate for the current state.
*/
template<IsArithmeticOrAD T>
StepDerivatives<T> calculateAllDerivatives(
const std::vector<T> &Y_in,
@@ -137,16 +433,40 @@ namespace gridfire {
T rho
) const;
/**
* @brief Calculates all derivatives (dY/dt) and the energy generation rate (double precision).
*
* @param Y_in Vector of current abundances for all species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return StepDerivatives<double> containing dY/dt and energy generation rate.
*
* This method calculates the time derivatives of all species and the
* specific nuclear energy generation rate for the current state using
* double precision arithmetic.
*/
StepDerivatives<double> calculateAllDerivatives(
const std::vector<double>& Y_in,
const double T9,
const double rho
) const;
/**
* @brief Calculates all derivatives (dY/dt) and the energy generation rate (automatic differentiation).
*
* @param Y_in Vector of current abundances for all species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return StepDerivatives<ADDouble> containing dY/dt and energy generation rate.
*
* This method calculates the time derivatives of all species and the
* specific nuclear energy generation rate for the current state using
* automatic differentiation.
*/
StepDerivatives<ADDouble> calculateAllDerivatives(
const std::vector<ADDouble>& Y_in,
const ADDouble T9,
const ADDouble rho
const ADDouble &T9,
const ADDouble &rho
) const;
};

View File

@@ -1,8 +1,98 @@
//
// Created by Emily Boudreaux on 6/27/25.
//
#pragma once
#ifndef ENGINE_VIEW_ABSTRACT_H
#define ENGINE_VIEW_ABSTRACT_H
#include "gridfire/engine/engine_abstract.h"
#endif //ENGINE_VIEW_ABSTRACT_H
/**
* @file engine_view_abstract.h
* @brief Abstract interfaces for engine "views" in GridFire.
*
* This header defines the abstract base classes and concepts for "views" of reaction network engines.
* The primary purpose of an EngineView is to enable dynamic or adaptive network topologies
* (such as species/reaction culling, masking, or remapping) without modifying the underlying
* physics engine or its implementation. Engine views act as a flexible interface layer,
* allowing the network structure to be changed at runtime while preserving the core
* physics and solver logic in the base engine.
*
* Typical use cases include:
* - Adaptive or reduced networks for computational efficiency.
* - Dynamic masking or culling of species/reactions based on runtime criteria.
* - Providing a filtered or remapped view of the network for integration or analysis.
* - Supporting dynamic network reconfiguration in multi-zone or multi-physics contexts.
*
* The base engine types referenced here are defined in @ref engine_abstract.h.
*
* @author
* Emily M. Boudreaux
*/
namespace gridfire {
/**
* @brief Concept for types allowed as engine bases in EngineView.
*
* This concept restricts template parameters to types derived from either
* gridfire::Engine or gridfire::DynamicEngine, as defined in engine_abstract.h.
*
* Example usage:
* @code
* static_assert(EngineType<MyEngine>);
* @endcode
*/
template<typename EngineT>
concept EngineType = std::is_base_of_v<Engine, EngineT> || std::is_base_of_v<DynamicEngine, EngineT>;
/**
* @brief Abstract base class for a "view" of a reaction network engine.
*
* @tparam EngineT The engine type being viewed (must satisfy EngineType).
*
* EngineView provides an interface for accessing an underlying engine instance,
* while presenting a potentially modified or reduced network structure to the user.
* This enables dynamic or adaptive network topologies (e.g., culling, masking, or
* remapping of species and reactions) without altering the core physics engine.
*
* Intended usage: Derive from this class to implement a custom view or wrapper
* that manages a dynamic or adaptive network structure, delegating core calculations
* to the base engine. The contract is that getBaseEngine() must return a reference
* to the underlying engine instance, which remains responsible for the full physics.
*
* Example (see also AdaptiveEngineView):
* @code
* class MyAdaptiveView : public gridfire::EngineView<DynamicEngine> {
* public:
* MyAdaptiveView(DynamicEngine& engine) : engine_(engine) {}
* const DynamicEngine& getBaseEngine() const override { return engine_; }
* // Implement dynamic masking/culling logic...
* private:
* DynamicEngine& engine_;
* };
* @endcode
*
* @see gridfire::AdaptiveEngineView for a concrete example of dynamic culling.
*/
template<EngineType EngineT>
class EngineView {
public:
/**
* @brief Virtual destructor.
*/
virtual ~EngineView() = default;
/**
* @brief Access the underlying engine instance.
*
* @return Const reference to the underlying engine.
*
* This method must be implemented by derived classes to provide access
* to the base engine. The returned reference should remain valid for the
* lifetime of the EngineView.
*
* Example:
* @code
* const DynamicEngine& engine = myView.getBaseEngine();
* @endcode
*/
virtual const EngineT& getBaseEngine() const = 0;
};
}

View File

@@ -50,23 +50,6 @@ namespace gridfire {
{UNKNOWN, "Unknown"}
};
/**
* @struct NetIn
* @brief Input structure for the network evaluation.
*
* This structure holds the input parameters required for the network evaluation.
*
* Example usage:
* @code
* nuclearNetwork::NetIn netIn;
* netIn.composition = {1.0, 0.0, 0.0};
* netIn.tmax = 1.0e6;
* netIn.dt0 = 1.0e-3;
* netIn.temperature = 1.0e8;
* netIn.density = 1.0e5;
* netIn.energy = 1.0e12;
* @endcode
*/
struct NetIn {
fourdst::composition::Composition composition; ///< Composition of the network
double tMax; ///< Maximum time
@@ -75,22 +58,10 @@ namespace gridfire {
double density; ///< Density in g/cm^3
double energy; ///< Energy in ergs
double culling = 0.0; ///< Culling threshold for reactions (default is 0.0, meaning no culling)
std::vector<double> MolarAbundance() const;
};
/**
* @struct NetOut
* @brief Output structure for the network evaluation.
*
* This structure holds the output results from the network evaluation.
*
* Example usage:
* @code
* nuclearNetwork::NetOut netOut = network.evaluate(netIn);
* std::vector<double> composition = netOut.composition;
* int steps = netOut.num_steps;
* double energy = netOut.energy;
* @endcode
*/
struct NetOut {
fourdst::composition::Composition composition; ///< Composition of the network after evaluation
int num_steps; ///< Number of steps taken in the evaluation
@@ -102,20 +73,6 @@ namespace gridfire {
}
};
/**
* @class Network
* @brief Class for network evaluation.
*
* This class provides methods to evaluate the network based on the input parameters.
*
* Example usage:
* @code
* nuclearNetwork::Network network;
* nuclearNetwork::NetIn netIn;
* // Set netIn parameters...
* nuclearNetwork::NetOut netOut = network.evaluate(netIn);
* @endcode
*/
class Network {
public:
explicit Network(const NetworkFormat format = NetworkFormat::APPROX8);
@@ -147,8 +104,7 @@ namespace gridfire {
};
reaction::REACLIBLogicalReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition, bool reverse);
reaction::REACLIBLogicalReactionSet build_reaclib_nuclear_network_from_file(const std::string& filename, bool reverse);
reaction::LogicalReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition, bool reverse);
} // namespace nuclearNetwork

View File

@@ -7,154 +7,87 @@
#include "quill/Logger.h"
#include <unordered_map>
#include <vector>
#include <memory>
#include <unordered_set>
#include <cstdint>
#include "cppad/cppad.hpp"
/**
* @file reaction.h
* @brief Defines classes for representing and managing nuclear reactions.
*
* This file contains the core data structures for handling nuclear reactions,
* including individual reactions from specific sources (`Reaction`), collections
* of reactions (`ReactionSet`), and logical reactions that aggregate rates from
* multiple sources (`LogicalReaction`, `LogicalReactionSet`).
*/
namespace gridfire::reaction {
/**
* @struct REACLIBRateCoefficientSet
* @brief Holds the seven fitting parameters for a single REACLIB rate set.
* @details The thermonuclear reaction rate for a single set is calculated as:
* rate = exp(a0 + a1/T9 + a2/T9^(-1/3) + a3*T9^(1/3) + a4*T9 + a5*T9^(5/3) + a6*ln(T9))
* where T9 is the temperature in billions of Kelvin. The total rate for a
* reaction is the sum of the rates from all its sets.
* @struct RateCoefficientSet
* @brief Holds the seven coefficients for the REACLIB rate equation.
*
* This struct stores the parameters (a0-a6) used to calculate reaction rates
* as a function of temperature.
*/
struct RateCoefficientSet {
double a0; ///< Coefficient a0
double a1; ///< Coefficient a1
double a2; ///< Coefficient a2
double a3; ///< Coefficient a3
double a4; ///< Coefficient a4
double a5; ///< Coefficient a5
double a6; ///< Coefficient a6
struct REACLIBRateCoefficientSet {
const double a0;
const double a1;
const double a2;
const double a3;
const double a4;
const double a5;
const double a6;
friend std::ostream& operator<<(std::ostream& os, const REACLIBRateCoefficientSet& r) {
/**
* @brief Overloads the stream insertion operator for easy printing.
* @param os The output stream.
* @param r The RateCoefficientSet to print.
* @return The output stream.
*/
friend std::ostream& operator<<(std::ostream& os, const RateCoefficientSet& r) {
os << "[" << r.a0 << ", " << r.a1 << ", " << r.a2 << ", "
<< r.a3 << ", " << r.a4 << ", " << r.a5 << ", " << r.a6 << "]";
return os;
}
};
/**
* @class Reaction
* @brief Represents a single nuclear reaction from a specific data source.
*
* This class encapsulates all properties of a single nuclear reaction as defined
* in formats like REACLIB, including reactants, products, Q-value, and rate
* coefficients from a particular evaluation (source).
*
* Example:
* @code
* // Assuming species and rate coefficients are defined
* Reaction p_gamma_d(
* "H_1_H_1_to_H_2", "p(p,g)d", 1, {H_1, H_1}, {H_2}, 5.493, "st08", rate_coeffs
* );
* double rate = p_gamma_d.calculate_rate(0.1); // T9 = 0.1
* @endcode
*/
class Reaction {
public:
Reaction(
const std::string_view id,
const double qValue,
const std::vector<fourdst::atomic::Species>& reactants,
const std::vector<fourdst::atomic::Species>& products,
const bool reverse = false
);
/**
* @brief Virtual destructor.
*/
virtual ~Reaction() = default;
virtual std::unique_ptr<Reaction> clone() const = 0;
virtual double calculate_rate(double T9) const = 0;
virtual CppAD::AD<double> calculate_rate(const CppAD::AD<double> T9) const = 0;
virtual std::string_view peName() const { return ""; }
[[nodiscard]] bool contains(const fourdst::atomic::Species& species) const;
[[nodiscard]] bool contains_reactant(const fourdst::atomic::Species& species) const;
[[nodiscard]] bool contains_product(const fourdst::atomic::Species& species) const;
std::unordered_set<fourdst::atomic::Species> all_species() const;
std::unordered_set<fourdst::atomic::Species> reactant_species() const;
std::unordered_set<fourdst::atomic::Species> product_species() const;
size_t num_species() const;
int stoichiometry(const fourdst::atomic::Species& species) const;
std::unordered_map<fourdst::atomic::Species, int> stoichiometry() const;
std::string_view id() const { return m_id; }
double qValue() const { return m_qValue; }
const std::vector<fourdst::atomic::Species>& reactants() const { return m_reactants; }
const std::vector<fourdst::atomic::Species>& products() const { return m_products; }
bool is_reverse() const { return m_reverse; }
double excess_energy() const;
bool operator==(const Reaction& other) const { return m_id == other.m_id; }
bool operator!=(const Reaction& other) const { return !(*this == other); }
[[nodiscard]] uint64_t hash(uint64_t seed) const;
protected:
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
std::string m_id;
double m_qValue = 0.0; ///< Q-value of the reaction
std::vector<fourdst::atomic::Species> m_reactants; ///< Reactants of the reaction
std::vector<fourdst::atomic::Species> m_products; ///< Products of the reaction
bool m_reverse = false;
};
class ReactionSet {
public:
explicit ReactionSet(std::vector<std::unique_ptr<Reaction>> reactions);
ReactionSet(const ReactionSet& other);
ReactionSet& operator=(const ReactionSet& other);
virtual ~ReactionSet() = default;
virtual void add_reaction(std::unique_ptr<Reaction> reaction);
virtual void remove_reaction(const std::unique_ptr<Reaction>& reaction);
bool contains(const std::string_view& id) const;
bool contains(const Reaction& reaction) const;
size_t size() const { return m_reactions.size(); }
void sort(double T9=1.0);
bool contains_species(const fourdst::atomic::Species& species) const;
bool contains_reactant(const fourdst::atomic::Species& species) const;
bool contains_product(const fourdst::atomic::Species& species) const;
[[nodiscard]] const Reaction& operator[](size_t index) const;
[[nodiscard]] const Reaction& operator[](const std::string_view& id) const;
bool operator==(const ReactionSet& other) const;
bool operator!=(const ReactionSet& other) const;
[[nodiscard]] uint64_t hash(uint64_t seed = 0) const;
auto begin() { return m_reactions.begin(); }
auto begin() const { return m_reactions.cbegin(); }
auto end() { return m_reactions.end(); }
auto end() const { return m_reactions.cend(); }
protected:
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
std::vector<std::unique_ptr<Reaction>> m_reactions;
std::string m_id;
std::unordered_map<std::string, Reaction*> m_reactionNameMap; ///< Maps reaction IDs to Reaction objects for quick lookup
};
/**
* @struct REACLIBReaction
* @brief Represents a single nuclear reaction from the JINA REACLIB database.
* @details This struct is designed to be constructed at compile time (constexpr) from
* the data parsed by the Python generation script. It stores all necessary
* information to identify a reaction and calculate its rate.
*/
class REACLIBReaction final : public Reaction {
public:
/**
* @brief Constructs a REACLIBReaction object at compile time.
* @param id A unique string identifier generated by the Python script.
* @param peName
* @param chapter The REACLIB chapter number, defining the reaction structure.
* @param reactants A vector of strings with the names of the reactant species.
* @param products A vector of strings with the names of the product species.
* @brief Constructs a Reaction object.
* @param id A unique identifier for the reaction.
* @param peName The name in (projectile, ejectile) notation (e.g., "p(p,g)d").
* @param chapter The REACLIB chapter number, defining reaction structure.
* @param reactants A vector of reactant species.
* @param products A vector of product species.
* @param qValue The Q-value of the reaction in MeV.
* @param label The source label for the rate data (e.g., "wc12w", "st08").
* @param sets A vector of RateFitSet, containing the fitting coefficients for the rate.
* @param reverse A boolean indicating if the reaction is reversed (default is false).
* @param label The source label for the rate data (e.g., "wc12", "st08").
* @param sets The set of rate coefficients.
* @param reverse True if this is a reverse reaction rate.
*/
REACLIBReaction(
Reaction(
const std::string_view id,
const std::string_view peName,
const int chapter,
@@ -162,67 +95,433 @@ namespace gridfire::reaction {
const std::vector<fourdst::atomic::Species> &products,
const double qValue,
const std::string_view label,
const REACLIBRateCoefficientSet &sets,
const RateCoefficientSet &sets,
const bool reverse = false);
[[nodiscard]] std::unique_ptr<Reaction> clone() const override;
/**
* @brief Calculates the reaction rate for a given temperature.
* @param T9 The temperature in units of 10^9 K.
* @return The calculated reaction rate.
*/
[[nodiscard]] virtual double calculate_rate(const double T9) const;
[[nodiscard]] double calculate_rate(const double T9) const override;
[[nodiscard]] CppAD::AD<double> calculate_rate(const CppAD::AD<double> T9) const override;
/**
* @brief Calculates the reaction rate for a given temperature using CppAD types.
* @param T9 The temperature in units of 10^9 K, as a CppAD::AD<double>.
* @return The calculated reaction rate, as a CppAD::AD<double>.
*/
[[nodiscard]] virtual CppAD::AD<double> calculate_rate(const CppAD::AD<double> T9) const;
template <typename GeneralScalarType>
[[nodiscard]] GeneralScalarType calculate_rate(const GeneralScalarType T9) const {
const GeneralScalarType T913 = CppAD::pow(T9, 1.0/3.0);
const GeneralScalarType rateExponent = m_rateCoefficients.a0 +
m_rateCoefficients.a1 / T9 +
m_rateCoefficients.a2 / T913 +
m_rateCoefficients.a3 * T913 +
m_rateCoefficients.a4 * T9 +
m_rateCoefficients.a5 * CppAD::pow(T9, 5.0/3.0) +
m_rateCoefficients.a6 * CppAD::log(T9);
return CppAD::exp(rateExponent);
}
[[nodiscard]] std::string_view peName() const override { return m_peName; }
/**
* @brief Gets the reaction name in (projectile, ejectile) notation.
* @return The reaction name (e.g., "p(p,g)d").
*/
[[nodiscard]] virtual std::string_view peName() const { return m_peName; }
/**
* @brief Gets the REACLIB chapter number.
* @return The chapter number.
*/
[[nodiscard]] int chapter() const { return m_chapter; }
/**
* @brief Gets the source label for the rate data.
* @return The source label (e.g., "wc12w", "st08").
*/
[[nodiscard]] std::string_view sourceLabel() const { return m_sourceLabel; }
[[nodiscard]] const REACLIBRateCoefficientSet& rateCoefficients() const { return m_rateCoefficients; }
/**
* @brief Gets the set of rate coefficients.
* @return A const reference to the RateCoefficientSet.
*/
[[nodiscard]] const RateCoefficientSet& rateCoefficients() const { return m_rateCoefficients; }
friend std::ostream& operator<<(std::ostream& os, const REACLIBReaction& reaction);
private:
std::string m_peName; ///< Name of the reaction in (projectile, ejectile) notation (e.g. p(p, g)d)
/**
* @brief Checks if the reaction involves a given species as a reactant or product.
* @param species The species to check for.
* @return True if the species is involved, false otherwise.
*/
[[nodiscard]] bool contains(const fourdst::atomic::Species& species) const;
/**
* @brief Checks if the reaction involves a given species as a reactant.
* @param species The species to check for.
* @return True if the species is a reactant, false otherwise.
*/
[[nodiscard]] bool contains_reactant(const fourdst::atomic::Species& species) const;
/**
* @brief Checks if the reaction involves a given species as a product.
* @param species The species to check for.
* @return True if the species is a product, false otherwise.
*/
[[nodiscard]] bool contains_product(const fourdst::atomic::Species& species) const;
/**
* @brief Gets a set of all unique species involved in the reaction.
* @return An unordered_set of all reactant and product species.
*/
[[nodiscard]] std::unordered_set<fourdst::atomic::Species> all_species() const;
/**
* @brief Gets a set of all unique reactant species.
* @return An unordered_set of reactant species.
*/
[[nodiscard]] std::unordered_set<fourdst::atomic::Species> reactant_species() const;
/**
* @brief Gets a set of all unique product species.
* @return An unordered_set of product species.
*/
[[nodiscard]] std::unordered_set<fourdst::atomic::Species> product_species() const;
/**
* @brief Gets the number of unique species involved in the reaction.
* @return The count of unique species.
*/
[[nodiscard]] size_t num_species() const;
/**
* @brief Calculates the stoichiometric coefficient for a given species.
* @param species The species for which to find the coefficient.
* @return The stoichiometric coefficient (negative for reactants, positive for products).
*/
[[nodiscard]] int stoichiometry(const fourdst::atomic::Species& species) const;
/**
* @brief Gets a map of all species to their stoichiometric coefficients.
* @return An unordered_map from species to their integer coefficients.
*/
[[nodiscard]] std::unordered_map<fourdst::atomic::Species, int> stoichiometry() const;
/**
* @brief Gets the unique identifier of the reaction.
* @return The reaction ID.
*/
[[nodiscard]] std::string_view id() const { return m_id; }
/**
* @brief Gets the Q-value of the reaction.
* @return The Q-value in whatever units the reaction was defined in (usually MeV).
*/
[[nodiscard]] double qValue() const { return m_qValue; }
/**
* @brief Gets the vector of reactant species.
* @return A const reference to the vector of reactants.
*/
[[nodiscard]] const std::vector<fourdst::atomic::Species>& reactants() const { return m_reactants; }
/**
* @brief Gets the vector of product species.
* @return A const reference to the vector of products.
*/
[[nodiscard]] const std::vector<fourdst::atomic::Species>& products() const { return m_products; }
/**
* @brief Checks if this is a reverse reaction rate.
* @return True if it is a reverse rate, false otherwise.
*/
[[nodiscard]] bool is_reverse() const { return m_reverse; }
/**
* @brief Calculates the excess energy from the mass difference of reactants and products.
* @return The excess energy in MeV.
*/
[[nodiscard]] double excess_energy() const;
/**
* @brief Compares this reaction with another for equality based on their IDs.
* @param other The other Reaction to compare with.
* @return True if the reaction IDs are the same.
*/
bool operator==(const Reaction& other) const { return m_id == other.m_id; }
/**
* @brief Compares this reaction with another for inequality.
* @param other The other Reaction to compare with.
* @return True if the reactions are not equal.
*/
bool operator!=(const Reaction& other) const { return !(*this == other); }
/**
* @brief Computes a hash for the reaction based on its ID.
* @param seed The seed for the hash function.
* @return A 64-bit hash value.
* @details Uses the XXHash64 algorithm on the reaction's ID string.
*/
[[nodiscard]] uint64_t hash(uint64_t seed = 0) const;
protected:
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
std::string m_id; ///< Unique identifier for the reaction (e.g., "h1+h1=>h2+e+nu").
std::string m_peName; ///< Name of the reaction in (projectile, ejectile) notation (e.g. "p(p,g)d").
int m_chapter; ///< Chapter number from the REACLIB database, defining the reaction structure.
std::string m_sourceLabel; ///< Source label for the rate data, indicating the origin of the rate coefficients (e.g., "wc12w", "st08").
REACLIBRateCoefficientSet m_rateCoefficients;
double m_qValue = 0.0; ///< Q-value of the reaction in MeV.
std::vector<fourdst::atomic::Species> m_reactants; ///< Reactants of the reaction.
std::vector<fourdst::atomic::Species> m_products; ///< Products of the reaction.
std::string m_sourceLabel; ///< Source label for the rate data (e.g., "wc12w", "st08").
RateCoefficientSet m_rateCoefficients; ///< The seven rate coefficients.
bool m_reverse = false; ///< Flag indicating if this is a reverse reaction rate.
private:
/**
* @brief Template implementation for calculating the reaction rate.
* @tparam T The numeric type (double or CppAD::AD<double>).
* @param T9 The temperature in units of 10^9 K.
* @return The calculated reaction rate.
* @details The rate is calculated using the standard REACLIB formula:
* `rate = exp(a0 + a1/T9 + a2/T9^(1/3) + a3*T9^(1/3) + a4*T9 + a5*T9^(5/3) + a6*ln(T9))`
*/
template <typename T>
[[nodiscard]] T calculate_rate(const T T9) const {
const T T913 = CppAD::pow(T9, 1.0/3.0);
const T T953 = CppAD::pow(T9, 5.0/3.0);
const T logT9 = CppAD::log(T9);
const T exponent = m_rateCoefficients.a0 +
m_rateCoefficients.a1 / T9 +
m_rateCoefficients.a2 / T913 +
m_rateCoefficients.a3 * T913 +
m_rateCoefficients.a4 * T9 +
m_rateCoefficients.a5 * T953 +
m_rateCoefficients.a6 * logT9;
return CppAD::exp(exponent);
}
};
class REACLIBReactionSet final : public ReactionSet {
/**
* @class ReactionSet
* @brief A collection of Reaction objects.
*
* This class manages a set of individual `Reaction` objects, providing
* efficient lookup by ID and functionality to query the entire set.
*
* Example:
* @code
* ReactionSet my_set({reaction1, reaction2});
* my_set.add_reaction(reaction3);
* if (my_set.contains("h1(p,g)h2")) {
* const Reaction& r = my_set["h1(p,g)h2"];
* }
* @endcode
*/
class ReactionSet {
public:
explicit REACLIBReactionSet(std::vector<REACLIBReaction>);
std::unordered_set<std::string> peNames() const;
friend std::ostream& operator<<(std::ostream& os, const REACLIBReactionSet& set);
/**
* @brief Constructs a ReactionSet from a vector of reactions.
* @param reactions The initial vector of Reaction objects.
*/
explicit ReactionSet(std::vector<Reaction> reactions);
/**
* @brief Copy constructor.
* @param other The ReactionSet to copy.
*/
ReactionSet(const ReactionSet& other);
/**
* @brief Copy assignment operator.
* @param other The ReactionSet to assign from.
* @return A reference to this ReactionSet.
*/
ReactionSet& operator=(const ReactionSet& other);
/**
* @brief Virtual destructor.
*/
virtual ~ReactionSet() = default;
/**
* @brief Adds a reaction to the set.
* @param reaction The Reaction to add.
*/
virtual void add_reaction(Reaction reaction);
/**
* @brief Removes a reaction from the set.
* @param reaction The Reaction to remove.
*/
virtual void remove_reaction(const Reaction& reaction);
/**
* @brief Checks if the set contains a reaction with the given ID.
* @param id The ID of the reaction to find.
* @return True if the reaction is in the set, false otherwise.
*/
[[nodiscard]] bool contains(const std::string_view& id) const;
/**
* @brief Checks if the set contains the given reaction.
* @param reaction The Reaction to find.
* @return True if the reaction is in the set, false otherwise.
*/
[[nodiscard]] bool contains(const Reaction& reaction) const;
/**
* @brief Gets the number of reactions in the set.
* @return The size of the set.
*/
[[nodiscard]] virtual size_t size() const { return m_reactions.size(); }
/**
* @brief Removes all reactions from the set.
*/
void clear();
/**
* @brief Checks if any reaction in the set involves the given species.
* @param species The species to check for.
* @return True if the species is involved in any reaction.
*/
[[nodiscard]] bool contains_species(const fourdst::atomic::Species& species) const;
/**
* @brief Checks if any reaction in the set contains the given species as a reactant.
* @param species The species to check for.
* @return True if the species is a reactant in any reaction.
*/
[[nodiscard]] bool contains_reactant(const fourdst::atomic::Species& species) const;
/**
* @brief Checks if any reaction in the set contains the given species as a product.
* @param species The species to check for.
* @return True if the species is a product in any reaction.
*/
[[nodiscard]] bool contains_product(const fourdst::atomic::Species& species) const;
/**
* @brief Accesses a reaction by its index.
* @param index The index of the reaction to access.
* @return A const reference to the Reaction.
* @throws std::out_of_range if the index is out of bounds.
*/
[[nodiscard]] virtual const Reaction& operator[](size_t index) const;
/**
* @brief Accesses a reaction by its ID.
* @param id The ID of the reaction to access.
* @return A const reference to the Reaction.
* @throws std::out_of_range if no reaction with the given ID exists.
*/
[[nodiscard]] const Reaction& operator[](const std::string_view& id) const;
/**
* @brief Compares this set with another for equality.
* @param other The other ReactionSet to compare with.
* @return True if the sets are equal (same size and hash).
*/
bool operator==(const ReactionSet& other) const;
/**
* @brief Compares this set with another for inequality.
* @param other The other ReactionSet to compare with.
* @return True if the sets are not equal.
*/
bool operator!=(const ReactionSet& other) const;
/**
* @brief Computes a hash for the entire set.
* @param seed The seed for the hash function.
* @return A 64-bit hash value.
* @details The algorithm computes the hash of each individual reaction,
* sorts the hashes, and then computes a final hash over the sorted list
* of hashes. This ensures the hash is order-independent.
*/
[[nodiscard]] uint64_t hash(uint64_t seed = 0) const;
/** @name Iterators
* Provides iterators to loop over the reactions in the set.
*/
///@{
auto begin() { return m_reactions.begin(); }
[[nodiscard]] auto begin() const { return m_reactions.cbegin(); }
auto end() { return m_reactions.end(); }
[[nodiscard]] auto end() const { return m_reactions.cend(); }
///@}
private:
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
std::vector<Reaction> m_reactions;
std::string m_id;
std::unordered_map<std::string, Reaction> m_reactionNameMap; ///< Maps reaction IDs to Reaction objects for quick lookup.
};
class REACLIBLogicalReaction final : public Reaction {
/**
* @class LogicalReaction
* @brief Represents a "logical" reaction that aggregates rates from multiple sources.
*
* A LogicalReaction shares the same reactants and products but combines rates
* from different evaluations (e.g., "wc12" and "st08" for the same physical
* reaction). The total rate is the sum of the individual rates.
* It inherits from Reaction, using the properties of the first provided reaction
* as its base properties (reactants, products, Q-value, etc.).
*/
class LogicalReaction final : public Reaction {
public:
explicit REACLIBLogicalReaction(const std::vector<REACLIBReaction> &reactions);
explicit REACLIBLogicalReaction(const REACLIBReaction &reaction);
void add_reaction(const REACLIBReaction& reaction);
/**
* @brief Constructs a LogicalReaction from a vector of `Reaction` objects.
* @param reactions A vector of reactions that represent the same logical process.
* @throws std::runtime_error if the provided reactions have inconsistent Q-values.
*/
explicit LogicalReaction(const std::vector<Reaction> &reactions);
[[nodiscard]] std::unique_ptr<Reaction> clone() const override;
/**
* @brief Adds another `Reaction` source to this logical reaction.
* @param reaction The reaction to add.
* @throws std::runtime_error if the reaction has a different `peName`, a duplicate
* source label, or an inconsistent Q-value.
*/
void add_reaction(const Reaction& reaction);
[[nodiscard]] std::string_view peName() const override { return m_id; };
/**
* @brief Gets the number of source rates contributing to this logical reaction.
* @return The number of aggregated rates.
*/
[[nodiscard]] size_t size() const { return m_rates.size(); }
/**
* @brief Gets the list of source labels for the aggregated rates.
* @return A vector of source label strings.
*/
[[nodiscard]] std::vector<std::string> sources() const { return m_sources; }
/**
* @brief Calculates the total reaction rate by summing all source rates.
* @param T9 The temperature in units of 10^9 K.
* @return The total calculated reaction rate.
*/
[[nodiscard]] double calculate_rate(const double T9) const override;
/**
* @brief Calculates the total reaction rate using CppAD types.
* @param T9 The temperature in units of 10^9 K, as a CppAD::AD<double>.
* @return The total calculated reaction rate, as a CppAD::AD<double>.
*/
[[nodiscard]] CppAD::AD<double> calculate_rate(const CppAD::AD<double> T9) const override;
/** @name Iterators
* Provides iterators to loop over the rate coefficient sets.
*/
///@{
auto begin() { return m_rates.begin(); }
[[nodiscard]] auto begin() const { return m_rates.cbegin(); }
auto end() { return m_rates.end(); }
[[nodiscard]] auto end() const { return m_rates.cend(); }
///@}
private:
std::vector<std::string> m_sources; ///< List of source labels.
std::vector<RateCoefficientSet> m_rates; ///< List of rate coefficient sets from each source.
private:
/**
* @brief Template implementation for calculating the total reaction rate.
* @tparam T The numeric type (double or CppAD::AD<double>).
* @param T9 The temperature in units of 10^9 K.
* @return The total calculated reaction rate.
* @details This method iterates through all stored `RateCoefficientSet`s,
* calculates the rate for each, and returns their sum.
*/
template <typename T>
[[nodiscard]] T calculate_rate(const T T9) const {
T sum = static_cast<T>(0.0);
@@ -242,29 +541,59 @@ namespace gridfire::reaction {
}
return sum;
}
[[nodiscard]] int chapter() const { return m_chapter; }
auto begin() { return m_rates.begin(); }
auto begin() const { return m_rates.cbegin(); }
auto end() { return m_rates.end(); }
auto end() const { return m_rates.cend(); }
private:
int m_chapter;
std::vector<std::string> m_sources;
std::vector<REACLIBRateCoefficientSet> m_rates;
};
class REACLIBLogicalReactionSet final : public ReactionSet {
/**
* @class LogicalReactionSet
* @brief A collection of LogicalReaction objects.
*
* This class takes a `ReactionSet` and groups individual `Reaction` objects
* into `LogicalReaction` objects based on their `peName`. This provides a
* view of the network where all rates for the same physical process are combined.
*/
class LogicalReactionSet final : public ReactionSet {
public:
REACLIBLogicalReactionSet() = delete;
explicit REACLIBLogicalReactionSet(const REACLIBReactionSet& reactionSet);
/**
* @brief Deleted default constructor.
*/
LogicalReactionSet() = delete;
[[nodiscard]] std::unordered_set<std::string> peNames() const;
/**
* @brief Constructs a LogicalReactionSet from a ReactionSet.
* @param reactionSet The set of individual reactions to group.
* @details This constructor iterates through the provided `ReactionSet`,
* groups reactions by their `peName`, and creates a `LogicalReaction` for each group.
*/
explicit LogicalReactionSet(const ReactionSet& reactionSet);
/** @name Iterators
* Provides iterators to loop over the logical reactions in the set.
*/
///@{
auto begin() { return m_reactions.begin(); }
[[nodiscard]] auto begin() const { return m_reactions.cbegin(); }
auto end() { return m_reactions.end(); }
[[nodiscard]] auto end() const { return m_reactions.cend(); }
///@}
/**
* @brief Gets the number of logical reactions in the set.
* @return The size of the set.
*/
[[nodiscard]] size_t size() const { return m_reactions.size(); }
/**
* @brief Accesses a logical reaction by its index.
* @param index The index of the logical reaction.
* @return A const reference to the LogicalReaction.
*/
[[nodiscard]] const LogicalReaction& operator[](size_t index) const { return m_reactions[index]; }
private:
std::unordered_set<std::string> m_peNames;
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
std::vector<LogicalReaction> m_reactions;
std::string m_id;
std::unordered_map<std::string, LogicalReaction> m_reactionNameMap; ///< Maps reaction IDs to LogicalReaction objects for quick lookup.
};
}
}

View File

@@ -2,6 +2,7 @@
#include "gridfire/engine/engine_graph.h"
#include "gridfire/engine/engine_abstract.h"
#include "gridfire/engine/engine_adaptive.h"
#include "gridfire/network.h"
#include "fourdst/logging/logging.h"
@@ -15,53 +16,207 @@
namespace gridfire::solver {
/**
* @struct dynamicQSESpeciesIndices
* @brief Structure to hold indices of dynamic and QSE species.
*
* This structure is used by the QSENetworkSolver to store the indices of species
* that are treated dynamically and those that are assumed to be in Quasi-Steady-State
* Equilibrium (QSE).
*/
struct dynamicQSESpeciesIndices {
std::vector<size_t> dynamicSpeciesIndices; // Slow species that are not in QSE
std::vector<size_t> QSESpeciesIndices; // Fast species that are in QSE
std::vector<size_t> dynamicSpeciesIndices; ///< Indices of slow species that are not in QSE.
std::vector<size_t> QSESpeciesIndices; ///< Indices of fast species that are in QSE.
};
/**
* @class NetworkSolverStrategy
* @brief Abstract base class for network solver strategies.
*
* This class defines the interface for network solver strategies, which are responsible
* for integrating the reaction network ODEs over a given timestep. It is templated on the
* engine type to allow for different engine implementations to be used with the same solver.
*
* @tparam EngineT The type of engine to use with this solver strategy. Must inherit from Engine.
*/
template <typename EngineT>
class NetworkSolverStrategy {
public:
/**
* @brief Constructor for the NetworkSolverStrategy.
* @param engine The engine to use for evaluating the network.
*/
explicit NetworkSolverStrategy(EngineT& engine) : m_engine(engine) {};
/**
* @brief Virtual destructor.
*/
virtual ~NetworkSolverStrategy() = default;
/**
* @brief Evaluates the network for a given timestep.
* @param netIn The input conditions for the network.
* @return The output conditions after the timestep.
*/
virtual NetOut evaluate(const NetIn& netIn) = 0;
protected:
EngineT& m_engine;
EngineT& m_engine; ///< The engine used by this solver strategy.
};
/**
* @brief Type alias for a network solver strategy that uses a DynamicEngine.
*/
using DynamicNetworkSolverStrategy = NetworkSolverStrategy<DynamicEngine>;
/**
* @brief Type alias for a network solver strategy that uses an AdaptiveEngineView.
*/
using AdaptiveNetworkSolverStrategy = NetworkSolverStrategy<AdaptiveEngineView>;
/**
* @brief Type alias for a network solver strategy that uses a static Engine.
*/
using StaticNetworkSolverStrategy = NetworkSolverStrategy<Engine>;
class QSENetworkSolver final : public DynamicNetworkSolverStrategy {
/**
* @class QSENetworkSolver
* @brief A network solver that uses a Quasi-Steady-State Equilibrium (QSE) approach.
*
* This solver partitions the network into "fast" species in QSE and "slow" (dynamic) species.
* The abundances of the fast species are determined by solving a system of algebraic
* equations, while the abundances of the slow species are integrated using an ODE solver.
* This hybrid approach is highly effective for stiff networks with disparate timescales.
*
* The QSE solver uses an AdaptiveEngineView to dynamically cull unimportant species and
* reactions, which significantly improves performance for large networks.
*
* @implements AdaptiveNetworkSolverStrategy
*
* @see AdaptiveEngineView
* @see DynamicEngine::getSpeciesTimescales()
*/
class QSENetworkSolver final : public AdaptiveNetworkSolverStrategy {
public:
using DynamicNetworkSolverStrategy::DynamicNetworkSolverStrategy;
/**
* @brief Constructor for the QSENetworkSolver.
* @param engine The adaptive engine view to use for evaluating the network.
*/
using AdaptiveNetworkSolverStrategy::AdaptiveNetworkSolverStrategy;
/**
* @brief Evaluates the network for a given timestep using the QSE approach.
* @param netIn The input conditions for the network.
* @return The output conditions after the timestep.
*
* This method performs the following steps:
* 1. Updates the adaptive engine view (if necessary).
* 2. Partitions the species into dynamic and QSE species based on their timescales.
* 3. Calculates the steady-state abundances of the QSE species.
* 4. Integrates the ODEs for the dynamic species using a Runge-Kutta solver.
* 5. Marshals the output variables into a NetOut struct.
*
* @throws std::runtime_error If the steady-state abundances cannot be calculated.
*
* @see AdaptiveEngineView::update()
* @see packSpeciesTypeIndexVectors()
* @see calculateSteadyStateAbundances()
*/
NetOut evaluate(const NetIn& netIn) override;
private: // methods
/**
* @brief Packs the species indices into vectors based on their type (dynamic or QSE).
* @param Y Vector of current abundances for all species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @return A dynamicQSESpeciesIndices struct containing the indices of the dynamic and QSE species.
*
* This method determines whether each species should be treated dynamically or as
* being in QSE based on its timescale and abundance. Species with short timescales
* or low abundances are assumed to be in QSE.
*
* @see DynamicEngine::getSpeciesTimescales()
*/
dynamicQSESpeciesIndices packSpeciesTypeIndexVectors(
const std::vector<double>& Y,
const double T9,
const double rho
) const;
/**
* @brief Calculates the steady-state abundances of the QSE species.
* @param Y Vector of current abundances for all species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @param indices A dynamicQSESpeciesIndices struct containing the indices of the dynamic and QSE species.
* @return An Eigen::VectorXd containing the steady-state abundances of the QSE species.
*
* This method solves a system of algebraic equations to determine the steady-state
* abundances of the QSE species.
*
* @throws std::runtime_error If the steady-state abundances cannot be calculated.
*/
Eigen::VectorXd calculateSteadyStateAbundances(
const std::vector<double>& Y,
const double T9,
const double rho,
const dynamicQSESpeciesIndices& indices
) const;
/**
* @brief Initializes the network with a short ignition phase.
* @param netIn The input conditions for the network.
* @return The output conditions after the ignition phase.
*
* This method performs a short integration of the network at a high temperature and
* density to ignite the network and bring it closer to equilibrium. This can improve
* the convergence of the QSE solver.
*
* @see DirectNetworkSolver::evaluate()
*/
NetOut initializeNetworkWithShortIgnition(
const NetIn& netIn
) const;
private: // Nested functors for ODE integration
struct RHSFunctor {
DynamicEngine& m_engine;
const std::vector<size_t>& m_dynamicSpeciesIndices;
const std::vector<size_t>& m_QSESpeciesIndices;
const Eigen::VectorXd& m_Y_QSE;
const double m_T9;
const double m_rho;
/**
* @brief Determines whether the adaptive engine view should be updated.
* @param conditions The current input conditions.
* @return True if the view should be updated, false otherwise.
*
* This method implements a policy for determining when the adaptive engine view
* should be updated. The view is updated if the temperature or density has changed
* significantly, or if a primary fuel source has been depleted.
*
* @see AdaptiveEngineView::update()
*/
bool shouldUpdateView(const NetIn& conditions) const;
private: // Nested functors for ODE integration
/**
* @struct RHSFunctor
* @brief Functor for calculating the right-hand side of the ODEs for the dynamic species.
*
* This functor is used by the ODE solver to calculate the time derivatives of the
* dynamic species. It takes the current abundances of the dynamic species as input
* and returns the time derivatives of those abundances.
*/
struct RHSFunctor {
DynamicEngine& m_engine; ///< The engine used to evaluate the network.
const std::vector<size_t>& m_dynamicSpeciesIndices; ///< Indices of the dynamic species.
const std::vector<size_t>& m_QSESpeciesIndices; ///< Indices of the QSE species.
const Eigen::VectorXd& m_Y_QSE; ///< Steady-state abundances of the QSE species.
const double m_T9; ///< Temperature in units of 10^9 K.
const double m_rho; ///< Density in g/cm^3.
bool m_isViewInitialized = false;
/**
* @brief Constructor for the RHSFunctor.
* @param engine The engine used to evaluate the network.
* @param dynamicSpeciesIndices Indices of the dynamic species.
* @param QSESpeciesIndices Indices of the QSE species.
* @param Y_QSE Steady-state abundances of the QSE species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
*/
RHSFunctor(
DynamicEngine& engine,
const std::vector<size_t>& dynamicSpeciesIndices,
@@ -77,6 +232,12 @@ namespace gridfire::solver {
m_T9(T9),
m_rho(rho) {}
/**
* @brief Calculates the time derivatives of the dynamic species.
* @param YDynamic Vector of current abundances for the dynamic species.
* @param dYdtDynamic Vector to store the time derivatives of the dynamic species.
* @param t Current time.
*/
void operator()(
const boost::numeric::ublas::vector<double>& YDynamic,
boost::numeric::ublas::vector<double>& dYdtDynamic,
@@ -85,13 +246,31 @@ namespace gridfire::solver {
};
/**
* @struct JacobianFunctor
* @brief Functor for calculating the Jacobian matrix of the ODEs for the dynamic species.
*
* This functor is used by the ODE solver to calculate the Jacobian matrix of the
* ODEs for the dynamic species. It takes the current abundances of the dynamic
* species as input and returns the Jacobian matrix.
*
* @todo Implement the Jacobian functor.
*/
struct JacobianFunctor {
DynamicEngine& m_engine;
const std::vector<size_t>& m_dynamicSpeciesIndices;
const std::vector<size_t>& m_QSESpeciesIndices;
const double m_T9;
const double m_rho;
DynamicEngine& m_engine; ///< The engine used to evaluate the network.
const std::vector<size_t>& m_dynamicSpeciesIndices; ///< Indices of the dynamic species.
const std::vector<size_t>& m_QSESpeciesIndices; ///< Indices of the QSE species.
const double m_T9; ///< Temperature in units of 10^9 K.
const double m_rho; ///< Density in g/cm^3.
/**
* @brief Constructor for the JacobianFunctor.
* @param engine The engine used to evaluate the network.
* @param dynamicSpeciesIndices Indices of the dynamic species.
* @param QSESpeciesIndices Indices of the QSE species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
*/
JacobianFunctor(
DynamicEngine& engine,
const std::vector<size_t>& dynamicSpeciesIndices,
@@ -105,6 +284,13 @@ namespace gridfire::solver {
m_T9(T9),
m_rho(rho) {}
/**
* @brief Calculates the Jacobian matrix of the ODEs for the dynamic species.
* @param YDynamic Vector of current abundances for the dynamic species.
* @param JDynamic Matrix to store the Jacobian matrix.
* @param t Current time.
* @param dfdt Vector to store the time derivatives of the dynamic species (not used).
*/
void operator()(
const boost::numeric::ublas::vector<double>& YDynamic,
boost::numeric::ublas::matrix<double>& JDynamic,
@@ -113,19 +299,34 @@ namespace gridfire::solver {
) const;
};
/**
* @struct EigenFunctor
* @brief Functor for calculating the residual and Jacobian for the QSE species using Eigen.
*
* @tparam T The numeric type to use for the calculation (double or ADDouble).
*/
template<typename T>
struct EigenFunctor {
using InputType = Eigen::Matrix<T, Eigen::Dynamic, 1>;
using OutputType = Eigen::Matrix<T, Eigen::Dynamic, 1>;
using JacobianType = Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>;
DynamicEngine& m_engine;
const std::vector<double>& m_YDynamic;
const std::vector<size_t>& m_dynamicSpeciesIndices;
const std::vector<size_t>& m_QSESpeciesIndices;
const double m_T9;
const double m_rho;
DynamicEngine& m_engine; ///< The engine used to evaluate the network.
const std::vector<double>& m_YDynamic; ///< Abundances of the dynamic species.
const std::vector<size_t>& m_dynamicSpeciesIndices; ///< Indices of the dynamic species.
const std::vector<size_t>& m_QSESpeciesIndices; ///< Indices of the QSE species.
const double m_T9; ///< Temperature in units of 10^9 K.
const double m_rho; ///< Density in g/cm^3.
/**
* @brief Constructor for the EigenFunctor.
* @param engine The engine used to evaluate the network.
* @param YDynamic Abundances of the dynamic species.
* @param dynamicSpeciesIndices Indices of the dynamic species.
* @param QSESpeciesIndices Indices of the QSE species.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
*/
EigenFunctor(
DynamicEngine& engine,
const std::vector<double>& YDynamic,
@@ -141,25 +342,75 @@ namespace gridfire::solver {
m_T9(T9),
m_rho(rho) {}
/**
* @brief Calculates the residual vector for the QSE species.
* @param v_QSE Input vector of QSE species abundances (logarithmic).
* @param f_QSE Output vector of residuals.
* @return 0 for success.
*/
int operator()(const InputType& v_QSE, OutputType& f_QSE) const;
/**
* @brief Calculates the Jacobian matrix for the QSE species.
* @param v_QSE Input vector of QSE species abundances (logarithmic).
* @param J_QSE Output Jacobian matrix.
* @return 0 for success.
*/
int df(const InputType& v_QSE, JacobianType& J_QSE) const;
};
private:
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
fourdst::config::Config& m_config = fourdst::config::Config::getInstance();
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log"); ///< Logger instance.
fourdst::config::Config& m_config = fourdst::config::Config::getInstance(); ///< Configuration instance.
bool m_isViewInitialized = false; ///< Flag indicating whether the adaptive engine view has been initialized.
NetIn m_lastSeenConditions; ///< The last seen input conditions.
};
/**
* @class DirectNetworkSolver
* @brief A network solver that directly integrates the reaction network ODEs.
*
* This solver uses a Runge-Kutta method to directly integrate the reaction network
* ODEs. It is simpler than the QSENetworkSolver, but it can be less efficient for
* stiff networks with disparate timescales.
*
* @implements DynamicNetworkSolverStrategy
*/
class DirectNetworkSolver final : public DynamicNetworkSolverStrategy {
public:
/**
* @brief Constructor for the DirectNetworkSolver.
* @param engine The dynamic engine to use for evaluating the network.
*/
using DynamicNetworkSolverStrategy::DynamicNetworkSolverStrategy;
/**
* @brief Evaluates the network for a given timestep using direct integration.
* @param netIn The input conditions for the network.
* @return The output conditions after the timestep.
*/
NetOut evaluate(const NetIn& netIn) override;
private:
/**
* @struct RHSFunctor
* @brief Functor for calculating the right-hand side of the ODEs.
*
* This functor is used by the ODE solver to calculate the time derivatives of the
* species abundances. It takes the current abundances as input and returns the
* time derivatives.
*/
struct RHSFunctor {
DynamicEngine& m_engine;
const double m_T9;
const double m_rho;
const size_t m_numSpecies;
DynamicEngine& m_engine; ///< The engine used to evaluate the network.
const double m_T9; ///< Temperature in units of 10^9 K.
const double m_rho; ///< Density in g/cm^3.
const size_t m_numSpecies; ///< The number of species in the network.
/**
* @brief Constructor for the RHSFunctor.
* @param engine The engine used to evaluate the network.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
*/
RHSFunctor(
DynamicEngine& engine,
const double T9,
@@ -170,18 +421,38 @@ namespace gridfire::solver {
m_rho(rho),
m_numSpecies(engine.getNetworkSpecies().size()) {}
/**
* @brief Calculates the time derivatives of the species abundances.
* @param Y Vector of current abundances.
* @param dYdt Vector to store the time derivatives.
* @param t Current time.
*/
void operator()(
const boost::numeric::ublas::vector<double>& Y,
boost::numeric::ublas::vector<double>& dYdt,
double t
) const;
};
struct JacobianFunctor {
DynamicEngine& m_engine;
const double m_T9;
const double m_rho;
const size_t m_numSpecies;
/**
* @struct JacobianFunctor
* @brief Functor for calculating the Jacobian matrix.
*
* This functor is used by the ODE solver to calculate the Jacobian matrix of the
* ODEs. It takes the current abundances as input and returns the Jacobian matrix.
*/
struct JacobianFunctor {
DynamicEngine& m_engine; ///< The engine used to evaluate the network.
const double m_T9; ///< Temperature in units of 10^9 K.
const double m_rho; ///< Density in g/cm^3.
const size_t m_numSpecies; ///< The number of species in the network.
/**
* @brief Constructor for the JacobianFunctor.
* @param engine The engine used to evaluate the network.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
*/
JacobianFunctor(
DynamicEngine& engine,
const double T9,
@@ -192,6 +463,13 @@ namespace gridfire::solver {
m_rho(rho),
m_numSpecies(engine.getNetworkSpecies().size()) {}
/**
* @brief Calculates the Jacobian matrix.
* @param Y Vector of current abundances.
* @param J Matrix to store the Jacobian matrix.
* @param t Current time.
* @param dfdt Vector to store the time derivatives (not used).
*/
void operator()(
const boost::numeric::ublas::vector<double>& Y,
boost::numeric::ublas::matrix<double>& J,
@@ -202,8 +480,8 @@ namespace gridfire::solver {
};
private:
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
fourdst::config::Config& m_config = fourdst::config::Config::getInstance();
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log"); ///< Logger instance.
fourdst::config::Config& m_config = fourdst::config::Config::getInstance(); ///< Configuration instance.
};
template<typename T>