feat(Jacobian): Jacobian generation is now stateless.
Previously Jacobians were stored by engines and accessed through engine
accessors (e.g getJacobianMatrixEntry); however, this resulted in
desynced jacobian states. We have changed to a pattern of Engine creates
a jacobian and returns it to the caller. The caller can then do what
they will with it. Because of this the getJacobianMatrixEntry method has
been removed.
BREAKING CHANGE:
- There is no longer any getJacobianMatrixEntry method on
DynamicEngine classes
- the generateJacobian method signature has changed to return a
NetworkJacobian object. Internally this uses an Eigen Sparse Matrix to
store its data.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "gridfire/engine/types/reporting.h"
|
||||
#include "gridfire/engine/types/building.h"
|
||||
#include "gridfire/engine/types/jacobian.h"
|
||||
|
||||
#include "gridfire/expectations/expected_engine.h"
|
||||
|
||||
@@ -155,40 +156,26 @@ namespace gridfire {
|
||||
* 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(
|
||||
[[nodiscard]] virtual NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho
|
||||
) const = 0;
|
||||
|
||||
virtual void generateJacobianMatrix(
|
||||
[[nodiscard]] virtual NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho,
|
||||
const std::vector<fourdst::atomic::Species>& activeSpecies
|
||||
) const = 0;
|
||||
|
||||
virtual void generateJacobianMatrix(
|
||||
[[nodiscard]] virtual NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho,
|
||||
const SparsityPattern& sparsityPattern
|
||||
) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Get an entry from the previously generated Jacobian matrix.
|
||||
*
|
||||
* @param rowSpecies The species corresponding to the row index (i)
|
||||
* @param colSpecies The 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.
|
||||
*/
|
||||
[[nodiscard]] virtual double getJacobianMatrixEntry(
|
||||
const fourdst::atomic::Species& rowSpecies,
|
||||
const fourdst::atomic::Species& colSpecies
|
||||
) const = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Generate the stoichiometry matrix for the network.
|
||||
@@ -418,5 +405,7 @@ namespace gridfire {
|
||||
double rho
|
||||
) const = 0;
|
||||
|
||||
[[nodiscard]] virtual SpeciesStatus getSpeciesStatus(const fourdst::atomic::Species& species) const = 0;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -73,6 +73,7 @@ namespace gridfire {
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @class GraphEngine
|
||||
* @brief A reaction network engine that uses a graph-based representation.
|
||||
@@ -236,7 +237,7 @@ namespace gridfire {
|
||||
*
|
||||
* @see getJacobianMatrixEntry()
|
||||
*/
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho
|
||||
@@ -254,7 +255,7 @@ namespace gridfire {
|
||||
* @see getJacobianMatrixEntry()
|
||||
* @see generateJacobianMatrix()
|
||||
*/
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho,
|
||||
@@ -276,7 +277,7 @@ namespace gridfire {
|
||||
*
|
||||
* @see getJacobianMatrixEntry()
|
||||
*/
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho,
|
||||
@@ -334,22 +335,6 @@ namespace gridfire {
|
||||
*/
|
||||
void setNetworkReactions(const reaction::ReactionSet& reactions) override;
|
||||
|
||||
/**
|
||||
* @brief Gets an entry from the previously generated Jacobian matrix.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @see generateJacobianMatrix()
|
||||
*/
|
||||
[[nodiscard]] double getJacobianMatrixEntry(
|
||||
const fourdst::atomic::Species& rowSpecies,
|
||||
const fourdst::atomic::Species& colSpecies
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Gets the net stoichiometry for a given reaction.
|
||||
*
|
||||
@@ -754,6 +739,8 @@ namespace gridfire {
|
||||
*/
|
||||
fourdst::composition::Composition collectComposition(const fourdst::composition::CompositionAbstract &comp, double T9, double rho) const override;
|
||||
|
||||
SpeciesStatus getSpeciesStatus(const fourdst::atomic::Species &species) const override;
|
||||
|
||||
|
||||
private:
|
||||
struct PrecomputedReaction {
|
||||
@@ -864,9 +851,6 @@ namespace gridfire {
|
||||
|
||||
boost::numeric::ublas::compressed_matrix<int> m_stoichiometryMatrix; ///< Stoichiometry matrix (species x reactions).
|
||||
|
||||
mutable boost::numeric::ublas::compressed_matrix<double> m_jacobianMatrix; ///< Jacobian matrix (species x species).
|
||||
mutable JacobianMatrixState m_jacobianMatrixState = JacobianMatrixState::UNINITIALIZED;
|
||||
|
||||
mutable CppAD::ADFun<double> m_rhsADFun; ///< CppAD function for the right-hand side of the ODE.
|
||||
mutable CppAD::ADFun<double> m_epsADFun; ///< CppAD function for the energy generation rate.
|
||||
mutable CppAD::sparse_jac_work m_jac_work; ///< Work object for sparse Jacobian calculations.
|
||||
@@ -921,14 +905,6 @@ namespace gridfire {
|
||||
*/
|
||||
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() const;
|
||||
|
||||
/**
|
||||
* @brief Records the AD tape for the right-hand side of the ODE.
|
||||
@@ -1254,7 +1230,7 @@ namespace gridfire {
|
||||
std::unordered_map<fourdst::atomic::Species, int> reactant_counts;
|
||||
reactant_counts.reserve(reaction.reactants().size());
|
||||
for (const auto& reactant : reaction.reactants()) {
|
||||
reactant_counts[reactant]++;
|
||||
reactant_counts[reactant] = reaction.countReactantOccurrences(reactant);
|
||||
}
|
||||
const int totalReactants = static_cast<int>(reaction.reactants().size());
|
||||
|
||||
@@ -1285,7 +1261,6 @@ namespace gridfire {
|
||||
// the tape more expensive to record, but it will also mean that we only need to record it once for
|
||||
// the entire network.
|
||||
const T densityTerm = CppAD::pow(rho, totalReactants > 1 ? static_cast<T>(totalReactants - 1) : zero); // Density raised to the power of (N-1) for N reactants
|
||||
|
||||
return molar_concentration_product * k_reaction * densityTerm;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
|
||||
#include "fourdst/atomic/atomicSpecies.h"
|
||||
#include <Eigen/SparseCore>
|
||||
#include <Eigen/SparseQR>
|
||||
|
||||
#include <tuple>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace gridfire {
|
||||
class NetworkJacobian {
|
||||
public:
|
||||
explicit NetworkJacobian(
|
||||
const Eigen::SparseMatrix<double>& jacobianMatrix,
|
||||
const std::function<fourdst::atomic::Species(size_t)> &indexToSpeciesFunc
|
||||
);
|
||||
|
||||
double operator()(
|
||||
const fourdst::atomic::Species& row,
|
||||
const fourdst::atomic::Species& col
|
||||
) const;
|
||||
|
||||
double operator()(
|
||||
size_t i,
|
||||
size_t j
|
||||
) const;
|
||||
|
||||
std::tuple<size_t, size_t> shape() const;
|
||||
size_t rank() const;
|
||||
size_t nnz() const;
|
||||
|
||||
bool singular() const;
|
||||
|
||||
private:
|
||||
Eigen::SparseMatrix<double> m_jacobianMatrix;
|
||||
std::unordered_map<fourdst::atomic::Species, size_t> m_speciesToIndexMap;
|
||||
size_t m_rank;
|
||||
};
|
||||
}
|
||||
@@ -134,45 +134,26 @@ namespace gridfire {
|
||||
* @throws std::runtime_error If the AdaptiveEngineView is stale (i.e., `update()` has not been called).
|
||||
* @see AdaptiveEngineView::update()
|
||||
*/
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho
|
||||
) const override;
|
||||
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho,
|
||||
const std::vector<fourdst::atomic::Species> &activeSpecies
|
||||
) const override;
|
||||
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho,
|
||||
const SparsityPattern &sparsityPattern
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Gets an entry from the Jacobian matrix for the active species.
|
||||
*
|
||||
* @param rowSpecies The species corresponding to the row index in the culled species list.
|
||||
* @param colSpecies The species corresponding to the column index in the culled species list
|
||||
* @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()
|
||||
*/
|
||||
[[nodiscard]] double getJacobianMatrixEntry(
|
||||
const fourdst::atomic::Species& rowSpecies,
|
||||
const fourdst::atomic::Species& colSpecies
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Generates the stoichiometry matrix for the active reactions and species.
|
||||
*
|
||||
@@ -304,6 +285,8 @@ namespace gridfire {
|
||||
[[nodiscard]] PrimingReport primeEngine(const NetIn &netIn) override;
|
||||
|
||||
fourdst::composition::Composition collectComposition(const fourdst::composition::CompositionAbstract &comp, double T9, double rho) const override;
|
||||
|
||||
[[nodiscard]] SpeciesStatus getSpeciesStatus(const fourdst::atomic::Species &species) const override;
|
||||
private:
|
||||
using Config = fourdst::config::Config;
|
||||
using LogManager = fourdst::logging::LogManager;
|
||||
@@ -321,11 +304,6 @@ namespace gridfire {
|
||||
/** @brief The set of reactions that are currently active in the network. */
|
||||
reaction::ReactionSet m_activeReactions;
|
||||
|
||||
/** @brief A map from the indices of the active species to the indices of the corresponding species in the full network. */
|
||||
std::vector<size_t> m_speciesIndexMap;
|
||||
/** @brief A map from the indices of the active reactions to the indices of the corresponding reactions in the full network. */
|
||||
std::vector<size_t> m_reactionIndexMap;
|
||||
|
||||
/** @brief A flag indicating whether the view is stale and needs to be updated. */
|
||||
bool m_isStale = true;
|
||||
|
||||
@@ -338,67 +316,6 @@ namespace gridfire {
|
||||
double flowRate;
|
||||
};
|
||||
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()
|
||||
*/
|
||||
[[nodiscard]] 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()
|
||||
*/
|
||||
[[nodiscard]] 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.
|
||||
*/
|
||||
[[nodiscard]] 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.
|
||||
*/
|
||||
[[nodiscard]] 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.
|
||||
*/
|
||||
[[nodiscard]] 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.
|
||||
*/
|
||||
[[nodiscard]] size_t mapCulledToFullReactionIndex(size_t culledReactionIndex) const;
|
||||
|
||||
/**
|
||||
* @brief Validates that the AdaptiveEngineView is not stale.
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace gridfire{
|
||||
*
|
||||
* @throws std::runtime_error If the view is stale.
|
||||
*/
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho
|
||||
@@ -79,7 +79,7 @@ namespace gridfire{
|
||||
*
|
||||
* @throws std::runtime_error If the view is stale.
|
||||
*/
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho,
|
||||
@@ -96,33 +96,20 @@ namespace gridfire{
|
||||
*
|
||||
* @throws std::runtime_error If the view is stale.
|
||||
*/
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho,
|
||||
const SparsityPattern &sparsityPattern
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Gets an entry from the Jacobian matrix for the active species.
|
||||
*
|
||||
* @param rowSpecies The species corresponding to the row index.
|
||||
* @param colSpecies The species corresponding to the column index.
|
||||
* @return The value of the Jacobian matrix at (row species index, col species index).
|
||||
*
|
||||
* @throws std::runtime_error If the view is stale.
|
||||
* @throws std::out_of_range If an index is out of bounds.
|
||||
*/
|
||||
[[nodiscard]] double getJacobianMatrixEntry(
|
||||
const fourdst::atomic::Species& rowSpecies,
|
||||
const fourdst::atomic::Species& colSpecies
|
||||
) const override;
|
||||
/**
|
||||
* @brief Generates the stoichiometry matrix for the active reactions and species.
|
||||
*
|
||||
* @throws std::runtime_error If the view is stale.
|
||||
*/
|
||||
void generateStoichiometryMatrix() override;
|
||||
|
||||
/**
|
||||
* @brief Gets an entry from the stoichiometry matrix for the active species and reactions.
|
||||
*
|
||||
@@ -222,6 +209,8 @@ namespace gridfire{
|
||||
[[nodiscard]] PrimingReport primeEngine(const NetIn &netIn) override;
|
||||
|
||||
fourdst::composition::Composition collectComposition(const fourdst::composition::CompositionAbstract &comp, double T9, double rho) const override;
|
||||
|
||||
[[nodiscard]] SpeciesStatus getSpeciesStatus(const fourdst::atomic::Species &species) const override;
|
||||
protected:
|
||||
bool m_isStale = true;
|
||||
GraphEngine& m_baseEngine;
|
||||
|
||||
@@ -148,7 +148,7 @@ namespace gridfire {
|
||||
* @throws exceptions::StaleEngineError If the QSE cache misses, as it cannot proceed
|
||||
* without a valid partition.
|
||||
*/
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho
|
||||
@@ -177,7 +177,7 @@ namespace gridfire {
|
||||
*
|
||||
* @throws exceptions::StaleEngineError If the QSE cache misses.
|
||||
*/
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho,
|
||||
@@ -205,35 +205,13 @@ namespace gridfire {
|
||||
*
|
||||
* @throws exceptions::StaleEngineError If the QSE cache misses.
|
||||
*/
|
||||
void generateJacobianMatrix(
|
||||
[[nodiscard]] NetworkJacobian generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
double T9,
|
||||
double rho,
|
||||
const SparsityPattern &sparsityPattern
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Gets an entry from the previously generated Jacobian matrix.
|
||||
*
|
||||
* @param rowSpecies Species corresponding to the row index (i_full).
|
||||
* @param colSpecies Species corresponding to the column index (j_full).
|
||||
* @return Value of the Jacobian matrix at (i_full, j_full).
|
||||
*
|
||||
* @par Purpose
|
||||
* To provide Jacobian entries to an implicit solver.
|
||||
*
|
||||
* @par How
|
||||
* This method directly delegates to the base engine's `getJacobianMatrixEntry`.
|
||||
* It does not currently modify the Jacobian to reflect the QSE algebraic constraints,
|
||||
* as these are handled by setting `dY/dt = 0` in `calculateRHSAndEnergy`.
|
||||
*
|
||||
* @pre `generateJacobianMatrix()` must have been called for the current state.
|
||||
*/
|
||||
[[nodiscard]] double getJacobianMatrixEntry(
|
||||
const fourdst::atomic::Species& rowSpecies,
|
||||
const fourdst::atomic::Species& colSpecies
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Generates the stoichiometry matrix for the network.
|
||||
*
|
||||
@@ -578,6 +556,8 @@ namespace gridfire {
|
||||
*/
|
||||
fourdst::composition::Composition collectComposition(const fourdst::composition::CompositionAbstract &comp, double T9, double rho) const override;
|
||||
|
||||
SpeciesStatus getSpeciesStatus(const fourdst::atomic::Species &species) const override;
|
||||
|
||||
|
||||
private:
|
||||
/**
|
||||
|
||||
@@ -18,4 +18,8 @@ namespace gridfire::exceptions {
|
||||
class CVODESolverFailureError final : public SolverError {
|
||||
using SolverError::SolverError;
|
||||
};
|
||||
|
||||
class SingularJacobianError final : public SolverError {
|
||||
using SolverError::SolverError;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user