perf(graph_engine): finished sparsity system for jacobian, major preformance win, roughly 20x faster

essentially all callers can now inform the graph engine about which species they hold active and graph engine then uses those to define a sparsity pattern and only calculate the jacobian along that sparsity pattern
This commit is contained in:
2025-10-24 11:17:22 -04:00
parent 0581f69c48
commit 98db2b1d43
14 changed files with 441 additions and 66 deletions

View File

@@ -157,14 +157,19 @@ namespace gridfire {
double rho
) const = 0;
virtual void generateJacobianMatrix(
const fourdst::composition::Composition& comp,
double T9,
double rho,
const std::vector<fourdst::atomic::Species>& activeSpecies
) const = 0;
virtual void generateJacobianMatrix(
const fourdst::composition::Composition& comp,
double T9,
double rho,
const SparsityPattern& sparsityPattern
) const {
throw std::logic_error("Sparsity pattern not supported by this engine.");
}
) const = 0;
/**
* @brief Get an entry from the previously generated Jacobian matrix.

View File

@@ -12,7 +12,6 @@
#include "gridfire/screening/screening_types.h"
#include "gridfire/partition/partition_abstract.h"
#include "gridfire/engine/procedures/construction.h"
#include "gridfire/utils/general_composition.h"
#include <string>
#include <unordered_map>
@@ -235,6 +234,25 @@ namespace gridfire {
double rho
) const override;
/**
* @brief Generates the Jacobian matrix for the current state with a specified set of active species.
* generally this will be much faster than the full matrix generation. Here we use forward mode
* to generate the Jacobian only for the active species.
* @param comp The Composition object containing current abundances.
* @param T9 The temperature in units of 10^9 K.
* @param rho The density in g/cm^3.
* @param activeSpecies A vector of Species objects representing the active species.
*
* @see getJacobianMatrixEntry()
* @see generateJacobianMatrix()
*/
void generateJacobianMatrix(
const fourdst::composition::Composition& comp,
double T9,
double rho,
const std::vector<fourdst::atomic::Species>& activeSpecies
) const override;
/**
* @brief Generates the Jacobian matrix for the current state with a specified sparsity pattern.
*
@@ -717,6 +735,7 @@ namespace gridfire {
// Forward cacheing
size_t reaction_index;
reaction::ReactionType reaction_type;
uint64_t reaction_hash;
std::vector<size_t> unique_reactant_indices;
std::vector<int> reactant_powers;
double symmetry_factor;
@@ -800,6 +819,7 @@ namespace gridfire {
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.
CppAD::sparse_rc<std::vector<size_t>> m_full_jacobian_sparsity_pattern; ///< Full sparsity pattern for the Jacobian matrix.
std::set<std::pair<size_t, size_t>> m_full_sparsity_set; ///< For quick lookups of the base sparsity pattern
std::vector<std::unique_ptr<AtomicReverseRate>> m_atomicReverseRates;
@@ -813,6 +833,7 @@ namespace gridfire {
BuildDepthType m_depth;
std::vector<PrecomputedReaction> m_precomputedReactions; ///< Precomputed reactions for efficiency.
std::unordered_map<uint64_t, size_t> m_precomputedReactionIndexMap; ///< Set of hashed precomputed reactions for quick lookup.
std::unique_ptr<partition::PartitionFunction> m_partitionFunction; ///< Partition function for the network.
private:
@@ -890,10 +911,10 @@ namespace gridfire {
[[nodiscard]] StepDerivatives<double> calculateAllDerivativesUsingPrecomputation(
const fourdst::composition::Composition &comp,
const std::vector<double>& bare_rates,
const std::vector<double> &bare_rates,
const std::vector<double> &bare_reverse_rates,
double T9,
double rho
double rho, const reaction::ReactionSet &activeReactions
) const;
/**

View File

@@ -140,6 +140,20 @@ namespace gridfire {
double rho
) const override;
void generateJacobianMatrix(
const fourdst::composition::Composition &comp,
double T9,
double rho,
const std::vector<fourdst::atomic::Species> &activeSpecies
) const override;
void generateJacobianMatrix(
const fourdst::composition::Composition &comp,
double T9,
double rho,
const SparsityPattern &sparsityPattern
) const override;
/**
* @brief Gets an entry from the Jacobian matrix for the active species.
*
@@ -409,7 +423,7 @@ namespace gridfire {
* 5. For each reaction, it calls the base engine's `calculateMolarReactionFlow` to get the flow rate.
* 6. Stores the reaction pointer and its flow rate in a `ReactionFlow` struct and adds it to the returned vector.
*/
std::pair<std::vector<ReactionFlow>, fourdst::composition::Composition> calculateAllReactionFlows(
[[nodiscard]] std::pair<std::vector<ReactionFlow>, fourdst::composition::Composition> calculateAllReactionFlows(
const NetIn& netIn
) const;
/**

View File

@@ -65,9 +65,44 @@ namespace gridfire{
*/
void generateJacobianMatrix(
const fourdst::composition::Composition& comp,
const double T9,
const double rho
double T9,
double rho
) const override;
/**
* @brief Generates the Jacobian matrix for the active species.
*
* @param comp A Composition object containing the current composition of the system
* @param T9 The temperature in units of 10^9 K.
* @param rho The density in g/cm^3.
* @param activeSpecies The vector of active species to include in the Jacobian.
*
* @throws std::runtime_error If the view is stale.
*/
void generateJacobianMatrix(
const fourdst::composition::Composition &comp,
double T9,
double rho,
const std::vector<fourdst::atomic::Species> &activeSpecies
) const override;
/**
* @brief Generates the Jacobian matrix for a given sparsity pattern
*
* @param comp A Composition object containing the current composition of the system
* @param T9 The temperature in units of 10^9 K.
* @param rho The density in g/cm^3.
* @param sparsityPattern The sparsity pattern to use for the Jacobian matrix.
*
* @throws std::runtime_error If the view is stale.
*/
void generateJacobianMatrix(
const fourdst::composition::Composition &comp,
double T9,
double rho,
const SparsityPattern &sparsityPattern
) const override;
/**
* @brief Gets an entry from the Jacobian matrix for the active species.
*

View File

@@ -271,6 +271,64 @@ namespace gridfire {
double rho
) const override;
/**
* @brief Generates the Jacobian matrix for a subset of active species.
*
* @param comp The current composition.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @param activeSpecies The subset of species to include in the Jacobian.
*
* @par Purpose
* To compute a reduced Jacobian matrix for implicit solvers that only
* consider a subset of species.
*
* @par How
* Similar to the full Jacobian generation, it first checks the QSE cache.
* On a hit, it calls the base engine's `generateJacobianMatrix` with
* the specified active species. The returned Jacobian still reflects
* the full network, but only for the active species subset.
*
* @pre The engine must have a valid QSE cache entry for the given state.
* @post The base engine's internal Jacobian is updated for the active species.
*
* @throws exceptions::StaleEngineError If the QSE cache misses.
*/
void generateJacobianMatrix(
const fourdst::composition::Composition &comp,
double T9,
double rho,
const std::vector<fourdst::atomic::Species> &activeSpecies
) const override;
/**
* @brief Generates the Jacobian matrix using a sparsity pattern.
*
* @param comp The current composition.
* @param T9 Temperature in units of 10^9 K.
* @param rho Density in g/cm^3.
* @param sparsityPattern The sparsity pattern to use for the Jacobian.
*
* @par Purpose
* To compute the Jacobian matrix while leveraging a known sparsity pattern
* for efficiency. This is effectively a lower level version of the active species method.
*
* @par How
* It first checks the QSE cache. On a hit, it delegates to the base engine's
* `generateJacobianMatrix` method with the provided sparsity pattern.
*
* @pre The engine must have a valid QSE cache entry for the given state.
* @post The base engine's internal Jacobian is updated according to the sparsity pattern.
*
* @throws exceptions::StaleEngineError If the QSE cache misses.
*/
void generateJacobianMatrix(
const fourdst::composition::Composition &comp,
double T9,
double rho,
const SparsityPattern &sparsityPattern
) const override;
/**
* @brief Gets an entry from the previously generated Jacobian matrix.
*
@@ -720,6 +778,12 @@ namespace gridfire {
const NetIn &netIn
);
bool involvesSpecies(const fourdst::atomic::Species &species) const;
bool involvesSpeciesInQSE(const fourdst::atomic::Species &species) const;
bool involvesSpeciesInDynamic(const fourdst::atomic::Species &species) const;
private:
/**
@@ -806,7 +870,7 @@ namespace gridfire {
*/
MultiscalePartitioningEngineView* m_view;
/**
* @brief Indices of the species to solve for in the QSE group.
* @brief The set of species to solve for in the QSE group.
*/
const std::set<fourdst::atomic::Species>& m_qse_solve_species;
/**

View File

@@ -1,9 +1,7 @@
#pragma once
#include "gridfire/engine/engine_abstract.h"
#include "gridfire/engine/views/engine_defined.h"
#include "fourdst/logging/logging.h"
#include "fourdst/composition/atomicSpecies.h"
@@ -51,7 +49,7 @@ namespace gridfire {
private:
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
quill::Logger* m_logger = LogManager::getInstance().getLogger("log");
fourdst::atomic::Species m_primingSpecies; ///< The priming species, if specified.
private:
/**

View File

@@ -0,0 +1,23 @@
#pragma once
#include <exception>
#include <string>
#include <utility>
namespace gridfire::exceptions {
class UtilityError : public std::exception {};
class HashingError final : public UtilityError {
public:
explicit HashingError() = default;
explicit HashingError(std::string message)
: m_message(std::move(message)) {}
[[nodiscard]] const char* what() const noexcept override {
return m_message.c_str();
}
private:
std::string m_message;
};
}

View File

@@ -1,3 +1,4 @@
#pragma once
#include "gridfire/exceptions/error_engine.h"
#include "gridfire/exceptions/error_engine.h"
#include "gridfire/exceptions/error_utils.h"

View File

@@ -2,6 +2,9 @@
#include <cstdint>
#include "gridfire/exceptions/exceptions.h"
#include "gridfire/reaction/reaction.h"
namespace gridfire::utils {
/**
* @brief Generate a unique hash for an isotope given its mass number (A) and atomic number (Z).
@@ -16,4 +19,47 @@ namespace gridfire::utils {
return (static_cast<uint_fast32_t>(a) << 8) | static_cast<uint_fast32_t>(z);
}
}
namespace hashing::reaction {
static std::uint64_t splitmix64(std::uint64_t x) noexcept {
x += 0x9E3779B97F4A7C15ULL;
x = (x ^ (x >> 30)) * 0xBF58476D1CE4E5B9ULL;
x = (x ^ (x >> 27)) * 0x94D049BB133111EBULL;
x ^= (x >> 31);
return x;
}
static std::uint64_t mix_species(const unsigned a, const unsigned z) noexcept {
const std::uint64_t code = (static_cast<std::uint64_t>(a) << 7) | static_cast<std::uint64_t>(z);
return splitmix64(code);
}
static std::uint64_t multiset_combine(std::uint64_t acc, const std::uint64_t x) noexcept {
acc += x;
acc ^= (x << 23) | (x >> (64 - 23));
acc = splitmix64(acc);
return acc;
}
}
inline std::uint64_t hash_reaction(const reaction::Reaction& reaction) noexcept {
using namespace hashing::reaction;
std::uint64_t hR = 0;
for (const auto& s : reaction.reactants()) {
hR = multiset_combine(hR, mix_species(static_cast<unsigned>(s.a()),
static_cast<unsigned>(s.z())));
}
std::uint64_t hP = 0;
for (const auto& s : reaction.products()) {
hP = multiset_combine(hP, mix_species(static_cast<unsigned>(s.a()),
static_cast<unsigned>(s.z())));
}
std::uint64_t h = splitmix64(hR ^ 0xC3A5C85C97CB3127ULL);
h ^= splitmix64((hP << 1) | (hP >> 63));
return splitmix64(h);
}
}