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:
@@ -157,14 +157,19 @@ namespace gridfire {
|
|||||||
double rho
|
double rho
|
||||||
) const = 0;
|
) 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(
|
virtual void generateJacobianMatrix(
|
||||||
const fourdst::composition::Composition& comp,
|
const fourdst::composition::Composition& comp,
|
||||||
double T9,
|
double T9,
|
||||||
double rho,
|
double rho,
|
||||||
const SparsityPattern& sparsityPattern
|
const SparsityPattern& sparsityPattern
|
||||||
) const {
|
) const = 0;
|
||||||
throw std::logic_error("Sparsity pattern not supported by this engine.");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get an entry from the previously generated Jacobian matrix.
|
* @brief Get an entry from the previously generated Jacobian matrix.
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#include "gridfire/screening/screening_types.h"
|
#include "gridfire/screening/screening_types.h"
|
||||||
#include "gridfire/partition/partition_abstract.h"
|
#include "gridfire/partition/partition_abstract.h"
|
||||||
#include "gridfire/engine/procedures/construction.h"
|
#include "gridfire/engine/procedures/construction.h"
|
||||||
#include "gridfire/utils/general_composition.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@@ -235,6 +234,25 @@ namespace gridfire {
|
|||||||
double rho
|
double rho
|
||||||
) const override;
|
) 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.
|
* @brief Generates the Jacobian matrix for the current state with a specified sparsity pattern.
|
||||||
*
|
*
|
||||||
@@ -717,6 +735,7 @@ namespace gridfire {
|
|||||||
// Forward cacheing
|
// Forward cacheing
|
||||||
size_t reaction_index;
|
size_t reaction_index;
|
||||||
reaction::ReactionType reaction_type;
|
reaction::ReactionType reaction_type;
|
||||||
|
uint64_t reaction_hash;
|
||||||
std::vector<size_t> unique_reactant_indices;
|
std::vector<size_t> unique_reactant_indices;
|
||||||
std::vector<int> reactant_powers;
|
std::vector<int> reactant_powers;
|
||||||
double symmetry_factor;
|
double symmetry_factor;
|
||||||
@@ -800,6 +819,7 @@ namespace gridfire {
|
|||||||
mutable CppAD::ADFun<double> m_epsADFun; ///< CppAD function for the energy generation rate.
|
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.
|
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.
|
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;
|
std::vector<std::unique_ptr<AtomicReverseRate>> m_atomicReverseRates;
|
||||||
|
|
||||||
@@ -813,6 +833,7 @@ namespace gridfire {
|
|||||||
BuildDepthType m_depth;
|
BuildDepthType m_depth;
|
||||||
|
|
||||||
std::vector<PrecomputedReaction> m_precomputedReactions; ///< Precomputed reactions for efficiency.
|
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.
|
std::unique_ptr<partition::PartitionFunction> m_partitionFunction; ///< Partition function for the network.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -890,10 +911,10 @@ namespace gridfire {
|
|||||||
|
|
||||||
[[nodiscard]] StepDerivatives<double> calculateAllDerivativesUsingPrecomputation(
|
[[nodiscard]] StepDerivatives<double> calculateAllDerivativesUsingPrecomputation(
|
||||||
const fourdst::composition::Composition &comp,
|
const fourdst::composition::Composition &comp,
|
||||||
const std::vector<double>& bare_rates,
|
const std::vector<double> &bare_rates,
|
||||||
const std::vector<double> &bare_reverse_rates,
|
const std::vector<double> &bare_reverse_rates,
|
||||||
double T9,
|
double T9,
|
||||||
double rho
|
double rho, const reaction::ReactionSet &activeReactions
|
||||||
) const;
|
) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -140,6 +140,20 @@ namespace gridfire {
|
|||||||
double rho
|
double rho
|
||||||
) const override;
|
) 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.
|
* @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.
|
* 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.
|
* 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 NetIn& netIn
|
||||||
) const;
|
) const;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -65,9 +65,44 @@ namespace gridfire{
|
|||||||
*/
|
*/
|
||||||
void generateJacobianMatrix(
|
void generateJacobianMatrix(
|
||||||
const fourdst::composition::Composition& comp,
|
const fourdst::composition::Composition& comp,
|
||||||
const double T9,
|
double T9,
|
||||||
const double rho
|
double rho
|
||||||
) const override;
|
) 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.
|
* @brief Gets an entry from the Jacobian matrix for the active species.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -271,6 +271,64 @@ namespace gridfire {
|
|||||||
double rho
|
double rho
|
||||||
) const override;
|
) 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.
|
* @brief Gets an entry from the previously generated Jacobian matrix.
|
||||||
*
|
*
|
||||||
@@ -720,6 +778,12 @@ namespace gridfire {
|
|||||||
const NetIn &netIn
|
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:
|
private:
|
||||||
/**
|
/**
|
||||||
@@ -806,7 +870,7 @@ namespace gridfire {
|
|||||||
*/
|
*/
|
||||||
MultiscalePartitioningEngineView* m_view;
|
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;
|
const std::set<fourdst::atomic::Species>& m_qse_solve_species;
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gridfire/engine/engine_abstract.h"
|
|
||||||
#include "gridfire/engine/views/engine_defined.h"
|
#include "gridfire/engine/views/engine_defined.h"
|
||||||
|
|
||||||
|
|
||||||
#include "fourdst/logging/logging.h"
|
#include "fourdst/logging/logging.h"
|
||||||
#include "fourdst/composition/atomicSpecies.h"
|
#include "fourdst/composition/atomicSpecies.h"
|
||||||
|
|
||||||
@@ -51,7 +49,7 @@ namespace gridfire {
|
|||||||
|
|
||||||
|
|
||||||
private:
|
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.
|
fourdst::atomic::Species m_primingSpecies; ///< The priming species, if specified.
|
||||||
private:
|
private:
|
||||||
/**
|
/**
|
||||||
|
|||||||
23
src/include/gridfire/exceptions/error_utils.h
Normal file
23
src/include/gridfire/exceptions/error_utils.h
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "gridfire/exceptions/error_engine.h"
|
#include "gridfire/exceptions/error_engine.h"
|
||||||
|
#include "gridfire/exceptions/error_utils.h"
|
||||||
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "gridfire/exceptions/exceptions.h"
|
||||||
|
#include "gridfire/reaction/reaction.h"
|
||||||
|
|
||||||
namespace gridfire::utils {
|
namespace gridfire::utils {
|
||||||
/**
|
/**
|
||||||
* @brief Generate a unique hash for an isotope given its mass number (A) and atomic number (Z).
|
* @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);
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "gridfire/engine/procedures/priming.h"
|
#include "gridfire/engine/procedures/priming.h"
|
||||||
#include "gridfire/partition/partition_ground.h"
|
#include "gridfire/partition/partition_ground.h"
|
||||||
#include "gridfire/engine/procedures/construction.h"
|
#include "gridfire/engine/procedures/construction.h"
|
||||||
|
#include "gridfire/utils/hashing.h"
|
||||||
|
|
||||||
#include "fourdst/composition/species.h"
|
#include "fourdst/composition/species.h"
|
||||||
#include "fourdst/composition/atomicSpecies.h"
|
#include "fourdst/composition/atomicSpecies.h"
|
||||||
@@ -75,12 +76,11 @@ namespace gridfire {
|
|||||||
if (m_usePrecomputation) {
|
if (m_usePrecomputation) {
|
||||||
std::vector<double> bare_rates;
|
std::vector<double> bare_rates;
|
||||||
std::vector<double> bare_reverse_rates;
|
std::vector<double> bare_reverse_rates;
|
||||||
bare_rates.reserve(m_reactions.size());
|
bare_rates.reserve(activeReactions.size());
|
||||||
bare_reverse_rates.reserve(m_reactions.size());
|
bare_reverse_rates.reserve(activeReactions.size());
|
||||||
|
|
||||||
// TODO: Add cache to this
|
for (const auto& reaction: activeReactions) {
|
||||||
|
assert(m_reactions.contains(*reaction)); // A bug which results in this failing indicates a serious internal inconsistency and should only be present during development.
|
||||||
for (const auto& reaction: m_reactions) {
|
|
||||||
bare_rates.push_back(reaction->calculate_rate(T9, rho, Ye, mue, comp.getMolarAbundanceVector(), m_indexToSpeciesMap));
|
bare_rates.push_back(reaction->calculate_rate(T9, rho, Ye, mue, comp.getMolarAbundanceVector(), m_indexToSpeciesMap));
|
||||||
if (reaction->type() != reaction::ReactionType::WEAK) {
|
if (reaction->type() != reaction::ReactionType::WEAK) {
|
||||||
bare_reverse_rates.push_back(calculateReverseRate(*reaction, T9, rho, comp));
|
bare_reverse_rates.push_back(calculateReverseRate(*reaction, T9, rho, comp));
|
||||||
@@ -88,7 +88,7 @@ namespace gridfire {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- The public facing interface can always use the precomputed version since taping is done internally ---
|
// --- The public facing interface can always use the precomputed version since taping is done internally ---
|
||||||
return calculateAllDerivativesUsingPrecomputation(comp, bare_rates, bare_reverse_rates, T9, rho);
|
return calculateAllDerivativesUsingPrecomputation(comp, bare_rates, bare_reverse_rates, T9, rho, activeReactions);
|
||||||
} else {
|
} else {
|
||||||
return calculateAllDerivatives<double>(
|
return calculateAllDerivatives<double>(
|
||||||
comp.getMolarAbundanceVector(),
|
comp.getMolarAbundanceVector(),
|
||||||
@@ -171,6 +171,8 @@ namespace gridfire {
|
|||||||
collectAtomicReverseRateAtomicBases();
|
collectAtomicReverseRateAtomicBases();
|
||||||
generateStoichiometryMatrix();
|
generateStoichiometryMatrix();
|
||||||
reserveJacobianMatrix();
|
reserveJacobianMatrix();
|
||||||
|
|
||||||
|
// PERF: These do *a lot* of the same work* can this be optimized to only do the common work once?
|
||||||
recordADTape(); // Record the AD tape for the RHS function
|
recordADTape(); // Record the AD tape for the RHS function
|
||||||
recordEpsADTape(); // Record the AD tape for the energy generation rate function
|
recordEpsADTape(); // Record the AD tape for the energy generation rate function
|
||||||
|
|
||||||
@@ -183,6 +185,17 @@ namespace gridfire {
|
|||||||
m_rhsADFun.subgraph_sparsity(select_domain, select_range, false, m_full_jacobian_sparsity_pattern);
|
m_rhsADFun.subgraph_sparsity(select_domain, select_range, false, m_full_jacobian_sparsity_pattern);
|
||||||
m_jac_work.clear();
|
m_jac_work.clear();
|
||||||
|
|
||||||
|
m_full_sparsity_set.clear();
|
||||||
|
const auto& rows = m_full_jacobian_sparsity_pattern.row();
|
||||||
|
const auto& cols = m_full_jacobian_sparsity_pattern.col();
|
||||||
|
const size_t nnz = m_full_jacobian_sparsity_pattern.nnz();
|
||||||
|
|
||||||
|
for (size_t k = 0; k < nnz; ++k) {
|
||||||
|
if (cols[k] < m_networkSpecies.size()) {
|
||||||
|
m_full_sparsity_set.insert(std::make_pair(rows[k], cols[k]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
precomputeNetwork();
|
precomputeNetwork();
|
||||||
LOG_INFO(m_logger, "Internal maps synchronized. Network contains {} species and {} reactions.",
|
LOG_INFO(m_logger, "Internal maps synchronized. Network contains {} species and {} reactions.",
|
||||||
m_networkSpecies.size(), m_reactions.size());
|
m_networkSpecies.size(), m_reactions.size());
|
||||||
@@ -190,7 +203,6 @@ namespace gridfire {
|
|||||||
|
|
||||||
// --- Network Graph Construction Methods ---
|
// --- Network Graph Construction Methods ---
|
||||||
void GraphEngine::collectNetworkSpecies() {
|
void GraphEngine::collectNetworkSpecies() {
|
||||||
//TODO: Ensure consistent ordering in the m_networkSpecies vector so that it is ordered by species mass.
|
|
||||||
m_networkSpecies.clear();
|
m_networkSpecies.clear();
|
||||||
m_networkSpeciesMap.clear();
|
m_networkSpeciesMap.clear();
|
||||||
|
|
||||||
@@ -217,7 +229,7 @@ namespace gridfire {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: Currently this works. We sort the vector based on mass so that for the same set of species we always get the same ordering and we get the same ordering as a composition with the same set of species
|
// TODO: Currently this works. We sort the vector based on mass so that for the same set of species we always get the same ordering and we get the same ordering as a composition with the same set of species
|
||||||
// However, we need some checks so that when we get a composition we confirm that it is the same ordering / contains teh same species. This is important for the ODE integrator to work properly.
|
// However, we need some checks so that when we get a composition we confirm that it is the same ordering / contains the same species. This is important for the ODE integrator to work properly.
|
||||||
std::ranges::sort(m_networkSpecies, [](const fourdst::atomic::Species& a, const fourdst::atomic::Species& b) -> bool {
|
std::ranges::sort(m_networkSpecies, [](const fourdst::atomic::Species& a, const fourdst::atomic::Species& b) -> bool {
|
||||||
return a.mass() < b.mass(); // Otherwise, sort by mass
|
return a.mass() < b.mass(); // Otherwise, sort by mass
|
||||||
});
|
});
|
||||||
@@ -255,14 +267,10 @@ namespace gridfire {
|
|||||||
|
|
||||||
// --- Basic Accessors and Queries ---
|
// --- Basic Accessors and Queries ---
|
||||||
const std::vector<fourdst::atomic::Species>& GraphEngine::getNetworkSpecies() const {
|
const std::vector<fourdst::atomic::Species>& GraphEngine::getNetworkSpecies() const {
|
||||||
// Returns a constant reference to the vector of unique species in the network.
|
|
||||||
// LOG_TRACE_L3(m_logger, "Providing access to network species vector. Size: {}.", m_networkSpecies.size());
|
|
||||||
return m_networkSpecies;
|
return m_networkSpecies;
|
||||||
}
|
}
|
||||||
|
|
||||||
const reaction::ReactionSet& GraphEngine::getNetworkReactions() const {
|
const reaction::ReactionSet& GraphEngine::getNetworkReactions() const {
|
||||||
// Returns a constant reference to the set of reactions in the network.
|
|
||||||
// LOG_TRACE_L3(m_logger, "Providing access to network reactions set. Size: {}.", m_reactions.size());
|
|
||||||
return m_reactions;
|
return m_reactions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,64 +581,75 @@ namespace gridfire {
|
|||||||
const std::vector<double> &bare_rates,
|
const std::vector<double> &bare_rates,
|
||||||
const std::vector<double> &bare_reverse_rates,
|
const std::vector<double> &bare_reverse_rates,
|
||||||
const double T9,
|
const double T9,
|
||||||
const double rho
|
const double rho,
|
||||||
|
const reaction::ReactionSet &activeReactions
|
||||||
) const {
|
) const {
|
||||||
// --- Calculate screening factors ---
|
// --- Calculate screening factors ---
|
||||||
const std::vector<double> screeningFactors = m_screeningModel->calculateScreeningFactors(
|
const std::vector<double> screeningFactors = m_screeningModel->calculateScreeningFactors(
|
||||||
m_reactions,
|
activeReactions,
|
||||||
m_networkSpecies,
|
m_networkSpecies,
|
||||||
comp.getMolarAbundanceVector(),
|
comp.getMolarAbundanceVector(),
|
||||||
T9,
|
T9,
|
||||||
rho
|
rho
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: Fix up the precomputation to use the new comp in interface as opposed to a raw vector of molar abundances
|
|
||||||
// This will require carefully checking the way the precomputation is stashed.
|
|
||||||
|
|
||||||
// --- Optimized loop ---
|
// --- Optimized loop ---
|
||||||
std::vector<double> molarReactionFlows;
|
std::vector<double> molarReactionFlows;
|
||||||
molarReactionFlows.reserve(m_precomputedReactions.size());
|
molarReactionFlows.reserve(m_precomputedReactions.size());
|
||||||
|
|
||||||
for (const auto& precomp : m_precomputedReactions) {
|
size_t reactionCounter = 0;
|
||||||
|
for (const auto& reaction : activeReactions) {
|
||||||
|
// --- Efficient lookup of only the active reactions ---
|
||||||
|
uint64_t reactionHash = utils::hash_reaction(*reaction);
|
||||||
|
const size_t reactionIndex = m_precomputedReactionIndexMap.at(reactionHash);
|
||||||
|
PrecomputedReaction precomputedReaction = m_precomputedReactions[reactionIndex];
|
||||||
|
|
||||||
|
// --- Forward abundance product ---
|
||||||
double forwardAbundanceProduct = 1.0;
|
double forwardAbundanceProduct = 1.0;
|
||||||
for (size_t i = 0; i < precomp.unique_reactant_indices.size(); ++i) {
|
for (size_t i = 0; i < precomputedReaction.unique_reactant_indices.size(); ++i) {
|
||||||
const size_t reactantIndex = precomp.unique_reactant_indices[i];
|
const size_t reactantIndex = precomputedReaction.unique_reactant_indices[i];
|
||||||
const fourdst::atomic::Species& reactant = m_networkSpecies[reactantIndex];
|
const fourdst::atomic::Species& reactant = m_networkSpecies[reactantIndex];
|
||||||
const int power = precomp.reactant_powers[i];
|
const int power = precomputedReaction.reactant_powers[i];
|
||||||
|
|
||||||
forwardAbundanceProduct *= std::pow(comp.getMolarAbundance(reactant), power);
|
forwardAbundanceProduct *= std::pow(comp.getMolarAbundance(reactant), power);
|
||||||
}
|
}
|
||||||
|
|
||||||
const double bare_rate = bare_rates[precomp.reaction_index];
|
const double bare_rate = bare_rates.at(reactionCounter);
|
||||||
const double screeningFactor = screeningFactors[precomp.reaction_index];
|
|
||||||
const size_t numReactants = m_reactions[precomp.reaction_index].reactants().size();
|
|
||||||
const size_t numProducts = m_reactions[precomp.reaction_index].products().size();
|
|
||||||
|
|
||||||
|
const double screeningFactor = screeningFactors[reactionCounter];
|
||||||
|
const size_t numReactants = m_reactions[reactionIndex].reactants().size();
|
||||||
|
const size_t numProducts = m_reactions[reactionIndex].products().size();
|
||||||
|
|
||||||
|
// --- Forward reaction flow ---
|
||||||
const double forwardMolarReactionFlow =
|
const double forwardMolarReactionFlow =
|
||||||
screeningFactor *
|
screeningFactor *
|
||||||
bare_rate *
|
bare_rate *
|
||||||
precomp.symmetry_factor *
|
precomputedReaction.symmetry_factor *
|
||||||
forwardAbundanceProduct *
|
forwardAbundanceProduct *
|
||||||
std::pow(rho, numReactants > 1 ? static_cast<double>(numReactants) - 1 : 0.0);
|
std::pow(rho, numReactants > 1 ? static_cast<double>(numReactants) - 1 : 0.0);
|
||||||
|
|
||||||
|
// --- Reverse reaction flow ---
|
||||||
|
// Only do this is the reaction has a non-zero reverse symmetry factor (i.e. is reversible)
|
||||||
double reverseMolarReactionFlow = 0.0;
|
double reverseMolarReactionFlow = 0.0;
|
||||||
if (precomp.reverse_symmetry_factor != 0.0 and m_useReverseReactions) {
|
if (precomputedReaction.reverse_symmetry_factor != 0.0 and m_useReverseReactions) {
|
||||||
const double bare_reverse_rate = bare_reverse_rates[precomp.reaction_index];
|
const double bare_reverse_rate = bare_reverse_rates.at(reactionCounter);
|
||||||
|
|
||||||
double reverseAbundanceProduct = 1.0;
|
double reverseAbundanceProduct = 1.0;
|
||||||
for (size_t i = 0; i < precomp.unique_product_indices.size(); ++i) {
|
for (size_t i = 0; i < precomputedReaction.unique_product_indices.size(); ++i) {
|
||||||
const size_t productIndex = precomp.unique_product_indices[i];
|
const size_t productIndex = precomputedReaction.unique_product_indices[i];
|
||||||
const fourdst::atomic::Species& product = m_networkSpecies[productIndex];
|
const fourdst::atomic::Species& product = m_networkSpecies[productIndex];
|
||||||
reverseAbundanceProduct *= std::pow(comp.getMolarAbundance(product), precomp.product_powers[i]);
|
reverseAbundanceProduct *= std::pow(comp.getMolarAbundance(product), precomputedReaction.product_powers[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
reverseMolarReactionFlow = screeningFactor *
|
reverseMolarReactionFlow = screeningFactor *
|
||||||
bare_reverse_rate *
|
bare_reverse_rate *
|
||||||
precomp.reverse_symmetry_factor *
|
precomputedReaction.reverse_symmetry_factor *
|
||||||
reverseAbundanceProduct *
|
reverseAbundanceProduct *
|
||||||
std::pow(rho, numProducts > 1 ? static_cast<double>(numProducts) - 1 : 0.0);
|
std::pow(rho, numProducts > 1 ? static_cast<double>(numProducts) - 1 : 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
molarReactionFlows.push_back(forwardMolarReactionFlow - reverseMolarReactionFlow);
|
molarReactionFlows.push_back(forwardMolarReactionFlow - reverseMolarReactionFlow);
|
||||||
|
reactionCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Assemble molar abundance derivatives ---
|
// --- Assemble molar abundance derivatives ---
|
||||||
@@ -638,9 +657,12 @@ namespace gridfire {
|
|||||||
for (const auto& species: m_networkSpecies) {
|
for (const auto& species: m_networkSpecies) {
|
||||||
result.dydt[species] = 0.0; // Initialize the change in abundance for each network species to 0
|
result.dydt[species] = 0.0; // Initialize the change in abundance for each network species to 0
|
||||||
}
|
}
|
||||||
for (size_t j = 0; j < m_precomputedReactions.size(); ++j) {
|
|
||||||
|
reactionCounter = 0;
|
||||||
|
for (const auto& reaction: activeReactions) {
|
||||||
|
size_t j = m_precomputedReactionIndexMap.at(utils::hash_reaction(*reaction));
|
||||||
const auto& precomp = m_precomputedReactions[j];
|
const auto& precomp = m_precomputedReactions[j];
|
||||||
const double R_j = molarReactionFlows[j];
|
const double R_j = molarReactionFlows[reactionCounter];
|
||||||
|
|
||||||
for (size_t i = 0; i < precomp.affected_species_indices.size(); ++i) {
|
for (size_t i = 0; i < precomp.affected_species_indices.size(); ++i) {
|
||||||
const size_t speciesIndex = precomp.affected_species_indices[i];
|
const size_t speciesIndex = precomp.affected_species_indices[i];
|
||||||
@@ -651,6 +673,7 @@ namespace gridfire {
|
|||||||
// Update the derivative for this species
|
// Update the derivative for this species
|
||||||
result.dydt.at(species) += static_cast<double>(stoichiometricCoefficient) * R_j;
|
result.dydt.at(species) += static_cast<double>(stoichiometricCoefficient) * R_j;
|
||||||
}
|
}
|
||||||
|
reactionCounter++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Calculate the nuclear energy generation rate ---
|
// --- Calculate the nuclear energy generation rate ---
|
||||||
@@ -802,12 +825,51 @@ namespace gridfire {
|
|||||||
LOG_TRACE_L1_LIMIT_EVERY_N(1000, m_logger, "Jacobian matrix generated with dimensions: {} rows x {} columns.", m_jacobianMatrix.size1(), m_jacobianMatrix.size2());
|
LOG_TRACE_L1_LIMIT_EVERY_N(1000, m_logger, "Jacobian matrix generated with dimensions: {} rows x {} columns.", m_jacobianMatrix.size1(), m_jacobianMatrix.size2());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GraphEngine::generateJacobianMatrix(
|
||||||
|
const fourdst::composition::Composition &comp,
|
||||||
|
const double T9,
|
||||||
|
const double rho,
|
||||||
|
const std::vector<fourdst::atomic::Species> &activeSpecies
|
||||||
|
) const {
|
||||||
|
// PERF: For small k it may make sense to implement a purley forward mode AD computation, some heuristic could be used to switch between the two methods based on k and total network species
|
||||||
|
const size_t k_active = activeSpecies.size();
|
||||||
|
|
||||||
|
// --- 1. Get the list of global indices ---
|
||||||
|
std::vector<size_t> active_indices;
|
||||||
|
active_indices.reserve(k_active);
|
||||||
|
|
||||||
|
for (const auto& species : activeSpecies) {
|
||||||
|
assert(involvesSpecies(species));
|
||||||
|
active_indices.push_back(getSpeciesIndex(species));
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 2. Build the k x k sparsity pattern ---
|
||||||
|
SparsityPattern sparsityPattern;
|
||||||
|
sparsityPattern.reserve(k_active * k_active);
|
||||||
|
|
||||||
|
for (const size_t i_global : active_indices) { // k rows
|
||||||
|
for (const size_t j_global : active_indices) { // k columns
|
||||||
|
sparsityPattern.emplace_back(i_global, j_global);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 3. Call the sparse reverse-mode implementation ---
|
||||||
|
generateJacobianMatrix(comp, T9, rho, sparsityPattern);
|
||||||
|
}
|
||||||
|
|
||||||
void GraphEngine::generateJacobianMatrix(
|
void GraphEngine::generateJacobianMatrix(
|
||||||
const fourdst::composition::Composition &comp,
|
const fourdst::composition::Composition &comp,
|
||||||
const double T9,
|
const double T9,
|
||||||
const double rho,
|
const double rho,
|
||||||
const SparsityPattern &sparsityPattern
|
const SparsityPattern &sparsityPattern
|
||||||
) const {
|
) const {
|
||||||
|
SparsityPattern intersectionSparsityPattern;
|
||||||
|
for (const auto& entry : sparsityPattern) {
|
||||||
|
if (m_full_sparsity_set.contains(entry)) {
|
||||||
|
intersectionSparsityPattern.push_back(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// --- Pack the input variables into a vector for CppAD ---
|
// --- Pack the input variables into a vector for CppAD ---
|
||||||
const size_t numSpecies = m_networkSpecies.size();
|
const size_t numSpecies = m_networkSpecies.size();
|
||||||
std::vector<double> x(numSpecies + 2, 0.0);
|
std::vector<double> x(numSpecies + 2, 0.0);
|
||||||
@@ -819,13 +881,13 @@ namespace gridfire {
|
|||||||
x[numSpecies + 1] = rho;
|
x[numSpecies + 1] = rho;
|
||||||
|
|
||||||
// --- Convert into CppAD Sparsity pattern ---
|
// --- Convert into CppAD Sparsity pattern ---
|
||||||
const size_t nnz = sparsityPattern.size(); // Number of non-zero entries in the sparsity pattern
|
const size_t nnz = intersectionSparsityPattern.size(); // Number of non-zero entries in the sparsity pattern
|
||||||
std::vector<size_t> row_indices(nnz);
|
std::vector<size_t> row_indices(nnz);
|
||||||
std::vector<size_t> col_indices(nnz);
|
std::vector<size_t> col_indices(nnz);
|
||||||
|
|
||||||
for (size_t k = 0; k < nnz; ++k) {
|
for (size_t k = 0; k < nnz; ++k) {
|
||||||
row_indices[k] = sparsityPattern[k].first;
|
row_indices[k] = intersectionSparsityPattern[k].first;
|
||||||
col_indices[k] = sparsityPattern[k].second;
|
col_indices[k] = intersectionSparsityPattern[k].second;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<double> values(nnz);
|
std::vector<double> values(nnz);
|
||||||
@@ -834,7 +896,7 @@ namespace gridfire {
|
|||||||
|
|
||||||
CppAD::sparse_rc<std::vector<size_t>> CppAD_sparsity_pattern(num_rows_jac, num_cols_jac, nnz);
|
CppAD::sparse_rc<std::vector<size_t>> CppAD_sparsity_pattern(num_rows_jac, num_cols_jac, nnz);
|
||||||
for (size_t k = 0; k < nnz; ++k) {
|
for (size_t k = 0; k < nnz; ++k) {
|
||||||
CppAD_sparsity_pattern.set(k, sparsityPattern[k].first, sparsityPattern[k].second);
|
CppAD_sparsity_pattern.set(k, intersectionSparsityPattern[k].first, intersectionSparsityPattern[k].second);
|
||||||
}
|
}
|
||||||
|
|
||||||
CppAD::sparse_rcv<std::vector<size_t>, std::vector<double>> jac_subset(CppAD_sparsity_pattern);
|
CppAD::sparse_rcv<std::vector<size_t>, std::vector<double>> jac_subset(CppAD_sparsity_pattern);
|
||||||
@@ -854,7 +916,7 @@ namespace gridfire {
|
|||||||
const size_t col = jac_subset.col()[k];
|
const size_t col = jac_subset.col()[k];
|
||||||
const double value = jac_subset.val()[k];
|
const double value = jac_subset.val()[k];
|
||||||
|
|
||||||
if (std::abs(value) > MIN_JACOBIAN_THRESHOLD) {
|
if (std::abs(value) > MIN_JACOBIAN_THRESHOLD || row == col) { // Always keep diagonal elements to avoid pathological stiffness
|
||||||
m_jacobianMatrix(row, col) = value; // Insert into the sparse matrix
|
m_jacobianMatrix(row, col) = value; // Insert into the sparse matrix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1241,12 +1303,18 @@ namespace gridfire {
|
|||||||
|
|
||||||
m_precomputedReactions.clear();
|
m_precomputedReactions.clear();
|
||||||
m_precomputedReactions.reserve(m_reactions.size());
|
m_precomputedReactions.reserve(m_reactions.size());
|
||||||
|
m_precomputedReactionIndexMap.clear();
|
||||||
|
m_precomputedReactionIndexMap.reserve(m_reactions.size());
|
||||||
|
|
||||||
for (size_t i = 0; i < m_reactions.size(); ++i) {
|
for (size_t i = 0; i < m_reactions.size(); ++i) {
|
||||||
const auto& reaction = m_reactions[i];
|
const auto& reaction = m_reactions[i];
|
||||||
PrecomputedReaction precomp;
|
PrecomputedReaction precomp;
|
||||||
precomp.reaction_index = i;
|
precomp.reaction_index = i;
|
||||||
precomp.reaction_type = reaction.type();
|
precomp.reaction_type = reaction.type();
|
||||||
|
uint64_t reactionHash = utils::hash_reaction(reaction);
|
||||||
|
|
||||||
|
precomp.reaction_hash = reactionHash;
|
||||||
|
m_precomputedReactionIndexMap[reactionHash] = i;
|
||||||
|
|
||||||
// --- Precompute forward reaction information ---
|
// --- Precompute forward reaction information ---
|
||||||
// Count occurrences for each reactant to determine powers and symmetry
|
// Count occurrences for each reactant to determine powers and symmetry
|
||||||
@@ -1298,6 +1366,7 @@ namespace gridfire {
|
|||||||
|
|
||||||
m_precomputedReactions.push_back(std::move(precomp));
|
m_precomputedReactions.push_back(std::move(precomp));
|
||||||
}
|
}
|
||||||
|
LOG_TRACE_L1(m_logger, "Pre-computation complete. Precomputed data for {} reactions.", m_precomputedReactions.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GraphEngine::AtomicReverseRate::forward(
|
bool GraphEngine::AtomicReverseRate::forward(
|
||||||
|
|||||||
@@ -231,11 +231,11 @@ namespace gridfire {
|
|||||||
// Store the request instead of applying it immediately.
|
// Store the request instead of applying it immediately.
|
||||||
requests.push_back({primingSpecies, equilibriumMassFraction, dominantChannel->reactants()});
|
requests.push_back({primingSpecies, equilibriumMassFraction, dominantChannel->reactants()});
|
||||||
} else {
|
} else {
|
||||||
LOG_ERROR(logger, "Failed to find dominant creation channel for {}.", primingSpecies.name());
|
LOG_TRACE_L1(logger, "Failed to find dominant creation channel for {}.", primingSpecies.name());
|
||||||
report.status = PrimingReportStatus::FAILED_TO_FIND_CREATION_CHANNEL;
|
report.status = PrimingReportStatus::FAILED_TO_FIND_CREATION_CHANNEL;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING(logger, "No destruction channel found for {}. Using fallback abundance.", primingSpecies.name());
|
LOG_TRACE_L2(logger, "No destruction channel found for {}. Using fallback abundance.", primingSpecies.name());
|
||||||
// For species with no destruction, we can't calculate an equilibrium.
|
// For species with no destruction, we can't calculate an equilibrium.
|
||||||
// We add a request with a tiny fallback abundance to ensure it exists in the network.
|
// We add a request with a tiny fallback abundance to ensure it exists in the network.
|
||||||
requests.push_back({primingSpecies, 1e-40, {}});
|
requests.push_back({primingSpecies, 1e-40, {}});
|
||||||
|
|||||||
@@ -176,9 +176,29 @@ namespace gridfire {
|
|||||||
const fourdst::composition::Composition &comp,
|
const fourdst::composition::Composition &comp,
|
||||||
const double T9,
|
const double T9,
|
||||||
const double rho
|
const double rho
|
||||||
|
) const {
|
||||||
|
generateJacobianMatrix(comp, T9, rho, m_activeSpecies);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdaptiveEngineView::generateJacobianMatrix(
|
||||||
|
const fourdst::composition::Composition &comp,
|
||||||
|
const double T9,
|
||||||
|
const double rho,
|
||||||
|
const std::vector<Species> &activeSpecies
|
||||||
) const {
|
) const {
|
||||||
validateState();
|
validateState();
|
||||||
m_baseEngine.generateJacobianMatrix(comp, T9, rho);
|
m_baseEngine.generateJacobianMatrix(comp, T9, rho, activeSpecies);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void AdaptiveEngineView::generateJacobianMatrix(
|
||||||
|
const fourdst::composition::Composition &comp,
|
||||||
|
const double T9,
|
||||||
|
const double rho,
|
||||||
|
const SparsityPattern &sparsityPattern
|
||||||
|
) const {
|
||||||
|
validateState();
|
||||||
|
m_baseEngine.generateJacobianMatrix(comp, T9, rho, sparsityPattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
double AdaptiveEngineView::getJacobianMatrixEntry(
|
double AdaptiveEngineView::getJacobianMatrixEntry(
|
||||||
|
|||||||
@@ -86,11 +86,39 @@ namespace gridfire {
|
|||||||
const double rho
|
const double rho
|
||||||
) const {
|
) const {
|
||||||
validateNetworkState();
|
validateNetworkState();
|
||||||
|
if (!m_activeSpeciesVectorCache.has_value()) {
|
||||||
|
m_activeSpeciesVectorCache = std::vector<Species>(m_activeSpecies.begin(), m_activeSpecies.end());
|
||||||
|
}
|
||||||
const MaskedComposition masked(comp, m_activeSpecies);
|
const MaskedComposition masked(comp, m_activeSpecies);
|
||||||
|
m_baseEngine.generateJacobianMatrix(masked, T9, rho, m_activeSpeciesVectorCache.value());
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: We likely want to be able to think more carefully about this so that the jacobian matches the active species/reactions
|
void DefinedEngineView::generateJacobianMatrix(
|
||||||
m_baseEngine.generateJacobianMatrix(masked, T9, rho);
|
const fourdst::composition::Composition &comp,
|
||||||
|
const double T9,
|
||||||
|
const double rho,
|
||||||
|
const std::vector<fourdst::atomic::Species> &activeSpecies
|
||||||
|
) const {
|
||||||
|
validateNetworkState();
|
||||||
|
|
||||||
|
const std::set<fourdst::atomic::Species> activeSpeciesSet(
|
||||||
|
activeSpecies.begin(),
|
||||||
|
activeSpecies.end()
|
||||||
|
);
|
||||||
|
|
||||||
|
const MaskedComposition masked(comp, activeSpeciesSet);
|
||||||
|
m_baseEngine.generateJacobianMatrix(masked, T9, rho, activeSpecies);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DefinedEngineView::generateJacobianMatrix(
|
||||||
|
const fourdst::composition::Composition &comp,
|
||||||
|
const double T9,
|
||||||
|
const double rho,
|
||||||
|
const SparsityPattern &sparsityPattern
|
||||||
|
) const {
|
||||||
|
validateNetworkState();
|
||||||
|
const MaskedComposition masked(comp, m_activeSpecies);
|
||||||
|
m_baseEngine.generateJacobianMatrix(masked, T9, rho, sparsityPattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
double DefinedEngineView::getJacobianMatrixEntry(
|
double DefinedEngineView::getJacobianMatrixEntry(
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
#include "quill/LogMacros.h"
|
#include "quill/LogMacros.h"
|
||||||
#include "quill/Logger.h"
|
#include "quill/Logger.h"
|
||||||
|
|
||||||
static std::ofstream debug_multiscale_log("debug_multiscale_log.txt");
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
using namespace fourdst::atomic;
|
using namespace fourdst::atomic;
|
||||||
//TODO: Replace all calls to this function with composition.getMolarAbundanceVector() so that
|
//TODO: Replace all calls to this function with composition.getMolarAbundanceVector() so that
|
||||||
@@ -214,8 +212,41 @@ namespace gridfire {
|
|||||||
const double T9,
|
const double T9,
|
||||||
const double rho
|
const double rho
|
||||||
) const {
|
) const {
|
||||||
// TODO: Add sparsity pattern to this to prevent base engine from doing unnecessary work.
|
// We do not need to generate the jacobian for QSE species since those entries are by definition 0
|
||||||
m_baseEngine.generateJacobianMatrix(comp, T9, rho);
|
m_baseEngine.generateJacobianMatrix(comp, T9, rho, m_dynamic_species);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||||
|
const fourdst::composition::Composition &comp,
|
||||||
|
const double T9,
|
||||||
|
const double rho,
|
||||||
|
const std::vector<Species> &activeSpecies
|
||||||
|
) const {
|
||||||
|
const bool activeSpeciesIsSubset = std::ranges::any_of(activeSpecies, [&](const auto& species) -> bool {
|
||||||
|
return !involvesSpecies(species);
|
||||||
|
});
|
||||||
|
if (activeSpeciesIsSubset) {
|
||||||
|
LOG_CRITICAL(m_logger, "Active species contains species not in the network partition. Cannot generate Jacobian matrix for active species.");
|
||||||
|
throw std::runtime_error("Active species contains species not in the network partition. Cannot generate Jacobian matrix for active species.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Species> dynamicActiveSpeciesIntersection;
|
||||||
|
for (const auto& species : activeSpecies) {
|
||||||
|
if (involvesSpeciesInDynamic(species)) {
|
||||||
|
dynamicActiveSpeciesIntersection.push_back(species);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_baseEngine.generateJacobianMatrix(comp, T9, rho, dynamicActiveSpeciesIntersection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||||
|
const fourdst::composition::Composition &comp,
|
||||||
|
const double T9,
|
||||||
|
const double rho,
|
||||||
|
const SparsityPattern &sparsityPattern
|
||||||
|
) const {
|
||||||
|
return m_baseEngine.generateJacobianMatrix(comp, T9, rho, sparsityPattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
double MultiscalePartitioningEngineView::getJacobianMatrixEntry(
|
double MultiscalePartitioningEngineView::getJacobianMatrixEntry(
|
||||||
@@ -811,6 +842,26 @@ namespace gridfire {
|
|||||||
return equilibrateNetwork(primingReport.primedComposition, T9, rho);
|
return equilibrateNetwork(primingReport.primedComposition, T9, rho);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool MultiscalePartitioningEngineView::involvesSpecies(
|
||||||
|
const Species &species
|
||||||
|
) const {
|
||||||
|
if (involvesSpeciesInQSE(species)) return true; // check this first since the vector is likely to be smaller so short circuit cost is less on average
|
||||||
|
if (involvesSpeciesInDynamic(species)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MultiscalePartitioningEngineView::involvesSpeciesInQSE(
|
||||||
|
const Species &species
|
||||||
|
) const {
|
||||||
|
return std::ranges::find(m_algebraic_species, species) != m_algebraic_species.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MultiscalePartitioningEngineView::involvesSpeciesInDynamic(
|
||||||
|
const Species &species
|
||||||
|
) const {
|
||||||
|
return std::ranges::find(m_dynamic_species, species) != m_dynamic_species.end();
|
||||||
|
}
|
||||||
|
|
||||||
size_t MultiscalePartitioningEngineView::getSpeciesIndex(const Species &species) const {
|
size_t MultiscalePartitioningEngineView::getSpeciesIndex(const Species &species) const {
|
||||||
return m_baseEngine.getSpeciesIndex(species);
|
return m_baseEngine.getSpeciesIndex(species);
|
||||||
}
|
}
|
||||||
@@ -1083,7 +1134,7 @@ namespace gridfire {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bool group_is_coupled = (coupling_flux / leakage_flux) > FLUX_RATIO_THRESHOLD) {
|
if (coupling_flux / leakage_flux > FLUX_RATIO_THRESHOLD) {
|
||||||
LOG_TRACE_L1(
|
LOG_TRACE_L1(
|
||||||
m_logger,
|
m_logger,
|
||||||
"Group containing {} is in equilibrium due to high coupling flux and balanced creation and destruction rate: <coupling: leakage flux = {}, coupling flux = {}, ratio = {} (Threshold: {})>, <creation: creation flux = {}, destruction flux = {}, ratio = {} order of mag (Threshold: {} order of mag)>",
|
"Group containing {} is in equilibrium due to high coupling flux and balanced creation and destruction rate: <coupling: leakage flux = {}, coupling flux = {}, ratio = {} (Threshold: {})>, <creation: creation flux = {}, destruction flux = {}, ratio = {} order of mag (Threshold: {} order of mag)>",
|
||||||
|
|||||||
Reference in New Issue
Block a user