feat(weak): major weak rate progress

Major weak rate progress which includes: A refactor of many of the public interfaces for GridFire Engines to use composition objects as opposed to raw abundance vectors. This helps prevent index mismatch errors. Further, the weak reaction class has been expanded with the majority of an implimentation, including an atomic_base derived class to allow for proper auto diff tracking of the interpolated table results. Some additional changes are that the version of fourdst and libcomposition have been bumped to versions with smarter caching of intermediate vectors and a few bug fixes.
This commit is contained in:
2025-10-07 15:16:03 -04:00
parent 4f1c260444
commit 8a0b5b2c36
53 changed files with 2310 additions and 1759 deletions

View File

@@ -24,6 +24,8 @@
#include "cppad/cppad.hpp"
#include "cppad/utility/sparse_rc.hpp"
#include "cppad/speed/sparse_jac_fun.hpp"
#include "gridfire/reaction/weak/weak_interpolator.h"
#include "gridfire/reaction/weak/weak_rate_library.h"
// PERF: The function getNetReactionStoichiometry returns a map of species to their stoichiometric coefficients for a given reaction.
// this makes extra copies of the species, which is not ideal and could be optimized further.
@@ -106,13 +108,13 @@ namespace gridfire {
*/
explicit GraphEngine(
const fourdst::composition::Composition &composition,
const BuildDepthType = NetworkBuildDepth::Full
BuildDepthType = NetworkBuildDepth::Full
);
explicit GraphEngine(
const fourdst::composition::Composition &composition,
const partition::PartitionFunction& partitionFunction,
const BuildDepthType buildDepth = NetworkBuildDepth::Full
BuildDepthType buildDepth = NetworkBuildDepth::Full
);
/**
@@ -128,7 +130,7 @@ namespace gridfire {
/**
* @brief Calculates the right-hand side (dY/dt) and energy generation rate.
*
* @param Y Vector of current abundances for all species.
* @param comp Composition object containing current abundances.
* @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.
@@ -139,21 +141,21 @@ namespace gridfire {
* @see StepDerivatives
*/
[[nodiscard]] std::expected<StepDerivatives<double>, expectations::StaleEngineError> calculateRHSAndEnergy(
const std::vector<double>& Y,
const double T9,
const double rho
const fourdst::composition::Composition& comp,
double T9,
double rho
) const override;
[[nodiscard]] EnergyDerivatives calculateEpsDerivatives(
const std::vector<double>& Y,
const double T9,
const double rho
const fourdst::composition::Composition& comp,
double T9,
double rho
) const override;
/**
* @brief Generates the Jacobian matrix for the current state.
*
* @param Y_dynamic Vector of current abundances.
* @param comp Composition object containing current abundances.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
*
@@ -164,13 +166,13 @@ namespace gridfire {
* @see getJacobianMatrixEntry()
*/
void generateJacobianMatrix(
const std::vector<double>& Y_dynamic,
const double T9,
const double rho
const fourdst::composition::Composition& comp,
double T9,
double rho
) const override;
void generateJacobianMatrix(
const std::vector<double> &Y_dynamic,
const fourdst::composition::Composition& comp,
double T9,
double rho,
const SparsityPattern &sparsityPattern
@@ -188,7 +190,7 @@ namespace gridfire {
* @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 comp Composition object containing 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).
@@ -198,9 +200,9 @@ namespace gridfire {
*/
[[nodiscard]] double calculateMolarReactionFlow(
const reaction::Reaction& reaction,
const std::vector<double>&Y,
const double T9,
const double rho
const fourdst::composition::Composition& comp,
double T9,
double rho
) const override;
/**
@@ -220,8 +222,8 @@ namespace gridfire {
/**
* @brief Gets an entry from the previously generated Jacobian matrix.
*
* @param i Row index (species index).
* @param j Column index (species index).
* @param rowSpecies Species corresponding to the row index (i).
* @param colSpecies Species corresponding to the column index (j).
* @return Value of the Jacobian matrix at (i, j).
*
* The Jacobian must have been generated by `generateJacobianMatrix()` before calling this.
@@ -229,8 +231,8 @@ namespace gridfire {
* @see generateJacobianMatrix()
*/
[[nodiscard]] double getJacobianMatrixEntry(
const int i,
const int j
const fourdst::atomic::Species& rowSpecies,
const fourdst::atomic::Species& colSpecies
) const override;
/**
@@ -246,8 +248,8 @@ namespace gridfire {
/**
* @brief Gets an entry from the stoichiometry matrix.
*
* @param speciesIndex Index of the species.
* @param reactionIndex Index of the reaction.
* @param species Species to look up stoichiometry for.
* @param reaction Reaction to find.
* @return Stoichiometric coefficient for the species in the reaction.
*
* The stoichiometry matrix must have been generated by `generateStoichiometryMatrix()`.
@@ -255,14 +257,14 @@ namespace gridfire {
* @see generateStoichiometryMatrix()
*/
[[nodiscard]] int getStoichiometryMatrixEntry(
const int speciesIndex,
const int reactionIndex
const fourdst::atomic::Species& species,
const reaction::Reaction& reaction
) const override;
/**
* @brief Computes timescales for all species in the network.
*
* @param Y Vector of current abundances.
* @param comp Composition object containing 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).
@@ -271,13 +273,13 @@ namespace gridfire {
* which can be used for timestep control or diagnostics.
*/
[[nodiscard]] std::expected<std::unordered_map<fourdst::atomic::Species, double>, expectations::StaleEngineError> getSpeciesTimescales(
const std::vector<double>& Y,
const fourdst::composition::Composition& comp,
double T9,
double rho
) const override;
[[nodiscard]] std::expected<std::unordered_map<fourdst::atomic::Species, double>, expectations::StaleEngineError> getSpeciesDestructionTimescales(
const std::vector<double>& Y,
const fourdst::composition::Composition& comp,
double T9,
double rho
) const override;
@@ -396,7 +398,7 @@ namespace gridfire {
* @param reaction The reaction for which to calculate the reverse rate.
* @param T9 Temperature in units of 10^9 K.
* @param rho
* @param Y
* @param comp Composition object containing current abundances.
* @return Reverse rate for the reaction (e.g., mol/g/s).
*
* This method computes the reverse rate based on the forward rate and
@@ -404,7 +406,9 @@ namespace gridfire {
*/
[[nodiscard]] double calculateReverseRate(
const reaction::Reaction &reaction,
double T9, double rho, const std::vector<double> &Y
double T9,
double rho,
const fourdst::composition::Composition& comp
) const;
/**
@@ -428,8 +432,10 @@ namespace gridfire {
[[nodiscard]] double calculateReverseRateTwoBodyDerivative(
const reaction::Reaction &reaction,
const double T9,
double rho, const std::vector<double> &Y, const double reverseRate
double T9,
double rho,
const fourdst::composition::Composition& comp,
double reverseRate
) const;
/**
@@ -577,12 +583,16 @@ namespace gridfire {
constants m_constants;
rates::weak::WeakRateInterpolator m_weakRateInterpolator; ///< Interpolator for weak reaction rates.
reaction::ReactionSet 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.
std::unordered_map<std::string_view, fourdst::atomic::Species> m_networkSpeciesMap; ///< Map from species name to Species object.
std::unordered_map<fourdst::atomic::Species, size_t> m_speciesToIndexMap; ///< Map from species to their index in the stoichiometry matrix.
std::unordered_map<size_t, fourdst::atomic::Species> m_indexToSpeciesMap; ///< Map from index to species in the stoichiometry matrix.
boost::numeric::ublas::compressed_matrix<int> m_stoichiometryMatrix; ///< Stoichiometry matrix (species x reactions).
@@ -680,10 +690,11 @@ namespace gridfire {
[[nodiscard]] StepDerivatives<double> calculateAllDerivativesUsingPrecomputation(
const std::vector<double> &Y_in,
const fourdst::composition::Composition &comp,
const std::vector<double>& bare_rates,
const std::vector<double> &bare_reverse_rates,
double T9, double rho
double T9,
double rho
) const;
/**
@@ -691,9 +702,11 @@ namespace gridfire {
*
* @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 Y Vector of molar abundances for all species in the network.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @param Ye
* @param mue
* @return Molar flow rate for the reaction (e.g., mol/g/s).
*
* This method computes the net rate at which the given reaction proceeds
@@ -702,17 +715,17 @@ namespace gridfire {
template <IsArithmeticOrAD T>
T calculateMolarReactionFlow(
const reaction::Reaction &reaction,
const std::vector<T> &Y,
const std::vector<T>& Y,
const T T9,
const T rho
const T rho, T Ye, T mue
) const;
template<IsArithmeticOrAD T>
T calculateReverseMolarReactionFlow(
T T9,
T rho,
const T T9,
const T rho,
std::vector<T> screeningFactors,
std::vector<T> Y,
const std::vector<T>& Y,
size_t reactionIndex,
const reaction::Reaction &reaction
) const;
@@ -721,9 +734,11 @@ namespace gridfire {
* @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 Y_in Vector of molar abundances for all species in the network.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @param Ye
* @param mue
* @return StepDerivatives<T> containing dY/dt and energy generation rate.
*
* This method calculates the time derivatives of all species and the
@@ -731,46 +746,46 @@ namespace gridfire {
*/
template<IsArithmeticOrAD T>
[[nodiscard]] StepDerivatives<T> calculateAllDerivatives(
const std::vector<T> &Y_in,
const std::vector<T>& Y_in,
T T9,
T rho
T rho, T Ye, T mue
) 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.
*/
[[nodiscard]] 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.
*/
[[nodiscard]] StepDerivatives<ADDouble> calculateAllDerivatives(
const std::vector<ADDouble>& Y_in,
const ADDouble &T9,
const ADDouble &rho
) const;
// /**
// * @brief Calculates all derivatives (dY/dt) and the energy generation rate (double precision).
// *
// * @param Y Vector of molar abundances for all species in the network.
// * @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.
// */
// [[nodiscard]] StepDerivatives<double> calculateAllDerivatives(
// const std::vector<double>& Y,
// double T9,
// double rho
// ) const;
//
// /**
// * @brief Calculates all derivatives (dY/dt) and the energy generation rate (automatic differentiation).
// *
// * @param Y Vector of molar abundances for all species in the network.
// * @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.
// */
// [[nodiscard]] StepDerivatives<ADDouble> calculateAllDerivatives(
// const std::vector<ADDouble> &Y,
// ADDouble T9,
// ADDouble rho
// ) const;
};
@@ -780,7 +795,7 @@ namespace gridfire {
T T9,
T rho,
std::vector<T> screeningFactors,
std::vector<T> Y,
const std::vector<T>& Y,
size_t reactionIndex,
const reaction::Reaction &reaction
) const {
@@ -804,9 +819,15 @@ namespace gridfire {
} else {
return reverseMolarFlow; // If no atomic function is available, return zero
}
} else {
} else { // The case where T is of type double
// A,B If not calling with an AD type, calculate the reverse rate directly
reverseRateConstant = calculateReverseRate(reaction, T9, 0, {});
std::vector<std::string> symbols;
symbols.reserve(m_networkSpecies.size());
for (const auto& species : m_networkSpecies) {
symbols.emplace_back(species.name());
}
fourdst::composition::Composition comp(symbols, Y);
reverseRateConstant = calculateReverseRate(reaction, T9, rho, comp);
}
// C. Get product multiplicities
@@ -824,7 +845,7 @@ namespace gridfire {
// E. Calculate the abundance term
T productAbundanceTerm = static_cast<T>(1.0);
for (const auto& [species, count] : productCounts) {
const unsigned long speciesIndex = m_speciesToIndexMap.at(species);
const size_t speciesIndex = m_speciesToIndexMap.at(species);
productAbundanceTerm *= CppAD::pow(Y[speciesIndex], count);
}
@@ -844,7 +865,12 @@ namespace gridfire {
template<IsArithmeticOrAD T>
StepDerivatives<T> GraphEngine::calculateAllDerivatives(
const std::vector<T> &Y_in, T T9, T rho) const {
const std::vector<T>& Y_in,
const T T9,
const T rho,
const T Ye,
const T mue
) const {
std::vector<T> screeningFactors = m_screeningModel->calculateScreeningFactors(
m_reactions,
m_networkSpecies,
@@ -854,8 +880,10 @@ namespace gridfire {
);
// --- Setup output derivatives structure ---
StepDerivatives<T> result;
result.dydt.resize(m_networkSpecies.size(), static_cast<T>(0.0));
StepDerivatives<T> result{};
for (const auto& species : m_networkSpecies) {
result.dydt[species] = static_cast<T>(0.0); // default the change in abundance to zero
}
// --- AD Pre-setup (flags to control conditionals in an AD safe / branch aware manner) ---
// ----- Constants for AD safe calculations ---
@@ -885,13 +913,16 @@ namespace gridfire {
const T N_A = static_cast<T>(m_constants.Na); // Avogadro's number in mol^-1
const T c = static_cast<T>(m_constants.c); // Speed of light in cm/s
// TODO: It may be prudent to introduce assertions here which validate units but will be removed in release builds (to ensure that unit inconsistencies do not creep in during future development)
// libconstants already has units built in so this should be straightforward.
// --- SINGLE LOOP OVER ALL REACTIONS ---
for (size_t reactionIndex = 0; reactionIndex < m_reactions.size(); ++reactionIndex) {
const auto& reaction = m_reactions[reactionIndex];
// 1. Calculate forward reaction rate
const T forwardMolarReactionFlow = screeningFactors[reactionIndex] *
calculateMolarReactionFlow<T>(reaction, Y, T9, rho);
calculateMolarReactionFlow<T>(reaction, Y, T9, rho, Ye, mue);
// 2. Calculate reverse reaction rate
T reverseMolarFlow = static_cast<T>(0.0);
@@ -910,15 +941,15 @@ namespace gridfire {
const T molarReactionFlow = forwardMolarReactionFlow - reverseMolarFlow; // Net molar reaction flow
// 3. Use the rate to update all relevant species derivatives (dY/dt)
for (size_t speciesIndex = 0; speciesIndex < m_networkSpecies.size(); ++speciesIndex) {
const T nu_ij = static_cast<T>(m_stoichiometryMatrix(speciesIndex, reactionIndex));
result.dydt[speciesIndex] += threshold_flag * nu_ij * molarReactionFlow;
for (const auto& species: m_networkSpecies) {
const T nu_ij = static_cast<T>(reaction.stoichiometry(species));
result.dydt[species] += threshold_flag * nu_ij * molarReactionFlow;
}
}
T massProductionRate = static_cast<T>(0.0); // [mol][s^-1]
for (const auto& [species, index] : m_speciesToIndexMap) {
massProductionRate += result.dydt[index] * species.mass() * u;
for (const auto &species: m_speciesToIndexMap | std::views::keys) {
massProductionRate += result.dydt.at(species) * species.mass() * u;
}
result.nuclearEnergyGenerationRate = -massProductionRate * N_A * c * c; // [cm^2][s^-3] = [erg][s^-1][g^-1]
@@ -930,9 +961,11 @@ namespace gridfire {
template <IsArithmeticOrAD T>
T GraphEngine::calculateMolarReactionFlow(
const reaction::Reaction &reaction,
const std::vector<T> &Y,
const std::vector<T>& Y,
const T T9,
const T rho
const T rho,
const T Ye,
const T mue
) const {
// --- Pre-setup (flags to control conditionals in an AD safe / branch aware manner) ---
@@ -940,7 +973,7 @@ namespace gridfire {
const T zero = static_cast<T>(0.0);
// --- Calculate the molar reaction rate (in units of [s^-1][cm^3(N-1)][mol^(1-N)] for N reactants) ---
const T k_reaction = reaction.calculate_rate(T9, rho, Y);
const T k_reaction = reaction.calculate_rate(T9, rho, Ye, mue, Y, m_indexToSpeciesMap);
// --- Cound the number of each reactant species to account for species multiplicity ---
std::unordered_map<std::string, int> reactant_counts;