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:
@@ -133,7 +133,7 @@ namespace gridfire::diagnostics {
|
||||
const double T9,
|
||||
const double rho
|
||||
) {
|
||||
engine.generateJacobianMatrix(comp, T9, rho);
|
||||
const NetworkJacobian jac = engine.generateJacobianMatrix(comp, T9, rho);
|
||||
const auto& species_list = engine.getNetworkSpecies();
|
||||
|
||||
double max_diag = 0.0;
|
||||
@@ -143,7 +143,7 @@ namespace gridfire::diagnostics {
|
||||
|
||||
for (const auto& rowSpecies : species_list) {
|
||||
for (const auto& colSpecies : species_list) {
|
||||
const double val = std::abs(engine.getJacobianMatrixEntry(rowSpecies, colSpecies));
|
||||
const double val = std::abs(jac(rowSpecies, colSpecies));
|
||||
if (rowSpecies == colSpecies) {
|
||||
if (val > max_diag) { max_diag = val; max_diag_species = colSpecies; }
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "gridfire/engine/types/jacobian.h"
|
||||
#include <Eigen/SparseCore>
|
||||
#include <Eigen/SparseQR>
|
||||
|
||||
namespace gridfire {
|
||||
NetworkJacobian::NetworkJacobian(
|
||||
const Eigen::SparseMatrix<double>& jacobianMatrix,
|
||||
const std::function<fourdst::atomic::Species(size_t)> &indexToSpeciesFunc
|
||||
): m_jacobianMatrix(jacobianMatrix) {
|
||||
for (size_t i = 0; i < jacobianMatrix.rows(); ++i) {
|
||||
fourdst::atomic::Species species = indexToSpeciesFunc(i);
|
||||
m_speciesToIndexMap[species] = i;
|
||||
}
|
||||
|
||||
if (m_jacobianMatrix.rows() == 0 || m_jacobianMatrix.cols() == 0) {
|
||||
m_rank = 0;
|
||||
} else {
|
||||
Eigen::SparseQR<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int>> solver;
|
||||
solver.compute(m_jacobianMatrix);
|
||||
m_rank = solver.rank();
|
||||
}
|
||||
}
|
||||
|
||||
double NetworkJacobian::operator()(const fourdst::atomic::Species &row, const fourdst::atomic::Species &col) const {
|
||||
if (!m_speciesToIndexMap.contains(row) || !m_speciesToIndexMap.contains(col)) {
|
||||
throw std::out_of_range("Species not found in NetworkJacobian operator().");
|
||||
}
|
||||
const size_t i = m_speciesToIndexMap.at(row);
|
||||
const size_t j = m_speciesToIndexMap.at(col);
|
||||
return this->operator()(i, j);
|
||||
}
|
||||
|
||||
double NetworkJacobian::operator()(const size_t i, const size_t j) const {
|
||||
if (i >= m_jacobianMatrix.rows() || j >= m_jacobianMatrix.cols()) {
|
||||
throw std::out_of_range("Index out of bounds in NetworkJacobian operator().");
|
||||
}
|
||||
return m_jacobianMatrix.coeff(i, j);
|
||||
}
|
||||
|
||||
std::tuple<size_t, size_t> NetworkJacobian::shape() const {
|
||||
return {m_jacobianMatrix.rows(), m_jacobianMatrix.cols()};
|
||||
}
|
||||
|
||||
size_t NetworkJacobian::nnz() const {
|
||||
return m_jacobianMatrix.nonZeros();
|
||||
}
|
||||
|
||||
size_t NetworkJacobian::rank() const {
|
||||
return m_rank;
|
||||
}
|
||||
|
||||
bool NetworkJacobian::singular() const {
|
||||
const size_t rows = m_jacobianMatrix.rows();
|
||||
const size_t cols = m_jacobianMatrix.cols();
|
||||
const size_t minDim = (rows < cols) ? rows : cols;
|
||||
return m_rank < minDim;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -18,74 +18,13 @@ namespace gridfire {
|
||||
) :
|
||||
m_baseEngine(baseEngine),
|
||||
m_activeSpecies(baseEngine.getNetworkSpecies()),
|
||||
m_activeReactions(baseEngine.getNetworkReactions()),
|
||||
m_speciesIndexMap(constructSpeciesIndexMap()),
|
||||
m_reactionIndexMap(constructReactionIndexMap())
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<size_t> AdaptiveEngineView::constructSpeciesIndexMap() const {
|
||||
LOG_TRACE_L1(m_logger, "Constructing species index map for adaptive engine view...");
|
||||
std::unordered_map<Species, size_t> fullSpeciesReverseMap;
|
||||
const auto& fullSpeciesList = m_baseEngine.getNetworkSpecies();
|
||||
|
||||
fullSpeciesReverseMap.reserve(fullSpeciesList.size());
|
||||
|
||||
for (size_t i = 0; i < fullSpeciesList.size(); ++i) {
|
||||
fullSpeciesReverseMap[fullSpeciesList[i]] = i;
|
||||
}
|
||||
|
||||
std::vector<size_t> speciesIndexMap;
|
||||
speciesIndexMap.reserve(m_activeSpecies.size());
|
||||
|
||||
for (const auto& active_species : m_activeSpecies) {
|
||||
auto it = fullSpeciesReverseMap.find(active_species);
|
||||
if (it != fullSpeciesReverseMap.end()) {
|
||||
speciesIndexMap.push_back(it->second);
|
||||
} else {
|
||||
LOG_ERROR(m_logger, "Species '{}' not found in full species map.", active_species.name());
|
||||
m_logger -> flush_log();
|
||||
throw std::runtime_error("Species not found in full species map: " + std::string(active_species.name()));
|
||||
}
|
||||
}
|
||||
LOG_TRACE_L1(m_logger, "Species index map constructed with {} entries.", speciesIndexMap.size());
|
||||
return speciesIndexMap;
|
||||
|
||||
}
|
||||
|
||||
std::vector<size_t> AdaptiveEngineView::constructReactionIndexMap() const {
|
||||
LOG_TRACE_L1(m_logger, "Constructing reaction index map for adaptive engine view...");
|
||||
|
||||
// --- Step 1: Create a reverse map using the reaction's unique ID as the key. ---
|
||||
std::unordered_map<std::string_view, size_t> fullReactionReverseMap;
|
||||
const auto& fullReactionSet = m_baseEngine.getNetworkReactions();
|
||||
fullReactionReverseMap.reserve(fullReactionSet.size());
|
||||
|
||||
for (size_t i_full = 0; i_full < fullReactionSet.size(); ++i_full) {
|
||||
fullReactionReverseMap[fullReactionSet[i_full].id()] = i_full;
|
||||
}
|
||||
|
||||
// --- Step 2: Build the final index map using the active reaction set. ---
|
||||
std::vector<size_t> reactionIndexMap;
|
||||
reactionIndexMap.reserve(m_activeReactions.size());
|
||||
|
||||
for (const auto& active_reaction_ptr : m_activeReactions) {
|
||||
auto it = fullReactionReverseMap.find(active_reaction_ptr->id());
|
||||
|
||||
if (it != fullReactionReverseMap.end()) {
|
||||
reactionIndexMap.push_back(it->second);
|
||||
} else {
|
||||
LOG_ERROR(m_logger, "Active reaction '{}' not found in base engine during reaction index map construction.", active_reaction_ptr->id());
|
||||
m_logger->flush_log();
|
||||
throw std::runtime_error("Mismatch between active reactions and base engine.");
|
||||
}
|
||||
}
|
||||
|
||||
LOG_TRACE_L1(m_logger, "Reaction index map constructed with {} entries.", reactionIndexMap.size());
|
||||
return reactionIndexMap;
|
||||
}
|
||||
m_activeReactions(baseEngine.getNetworkReactions())
|
||||
{}
|
||||
|
||||
fourdst::composition::Composition AdaptiveEngineView::update(const NetIn &netIn) {
|
||||
m_activeReactions.clear();
|
||||
m_activeSpecies.clear();
|
||||
|
||||
fourdst::composition::Composition baseUpdatedComposition = m_baseEngine.update(netIn);
|
||||
NetIn updatedNetIn = netIn;
|
||||
|
||||
@@ -118,14 +57,11 @@ namespace gridfire {
|
||||
}
|
||||
|
||||
for (const auto& species : rescuedSpecies) {
|
||||
if (!std::ranges::contains(m_activeSpecies, species)) {
|
||||
if (!std::ranges::contains(m_activeSpecies, species) && m_baseEngine.getSpeciesStatus(species) == SpeciesStatus::ACTIVE) {
|
||||
m_activeSpecies.push_back(species);
|
||||
}
|
||||
}
|
||||
|
||||
m_speciesIndexMap = constructSpeciesIndexMap();
|
||||
m_reactionIndexMap = constructReactionIndexMap();
|
||||
|
||||
m_isStale = false;
|
||||
|
||||
LOG_INFO(m_logger, "AdaptiveEngineView updated successfully with {} active species and {} active reactions.", m_activeSpecies.size(), m_activeReactions.size());
|
||||
@@ -166,42 +102,33 @@ namespace gridfire {
|
||||
return m_baseEngine.calculateEpsDerivatives(comp, T9, rho);
|
||||
}
|
||||
|
||||
void AdaptiveEngineView::generateJacobianMatrix(
|
||||
NetworkJacobian AdaptiveEngineView::generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
generateJacobianMatrix(comp, T9, rho, m_activeSpecies);
|
||||
return generateJacobianMatrix(comp, T9, rho, m_activeSpecies);
|
||||
}
|
||||
|
||||
void AdaptiveEngineView::generateJacobianMatrix(
|
||||
NetworkJacobian AdaptiveEngineView::generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const std::vector<Species> &activeSpecies
|
||||
) const {
|
||||
validateState();
|
||||
m_baseEngine.generateJacobianMatrix(comp, T9, rho, activeSpecies);
|
||||
return m_baseEngine.generateJacobianMatrix(comp, T9, rho, activeSpecies);
|
||||
|
||||
}
|
||||
|
||||
void AdaptiveEngineView::generateJacobianMatrix(
|
||||
NetworkJacobian AdaptiveEngineView::generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const SparsityPattern &sparsityPattern
|
||||
) const {
|
||||
validateState();
|
||||
m_baseEngine.generateJacobianMatrix(comp, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
double AdaptiveEngineView::getJacobianMatrixEntry(
|
||||
const Species &rowSpecies,
|
||||
const Species &colSpecies
|
||||
) const {
|
||||
validateState();
|
||||
|
||||
return m_baseEngine.getJacobianMatrixEntry(rowSpecies, colSpecies);
|
||||
return m_baseEngine.generateJacobianMatrix(comp, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
void AdaptiveEngineView::generateStoichiometryMatrix() {
|
||||
@@ -328,6 +255,14 @@ namespace gridfire {
|
||||
return result;
|
||||
}
|
||||
|
||||
SpeciesStatus AdaptiveEngineView::getSpeciesStatus(const fourdst::atomic::Species &species) const {
|
||||
const SpeciesStatus status = m_baseEngine.getSpeciesStatus(species);
|
||||
if (status == SpeciesStatus::ACTIVE && std::ranges::find(m_activeSpecies, species) == m_activeSpecies.end()) {
|
||||
return SpeciesStatus::INACTIVE_FLOW;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t AdaptiveEngineView::getSpeciesIndex(const fourdst::atomic::Species &species) const {
|
||||
const auto it = std::ranges::find(m_activeSpecies, species);
|
||||
if (it != m_activeSpecies.end()) {
|
||||
@@ -339,42 +274,6 @@ namespace gridfire {
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> AdaptiveEngineView::mapCulledToFull(const std::vector<double>& culled) const {
|
||||
std::vector<double> full(m_baseEngine.getNetworkSpecies().size(), 0.0);
|
||||
for (size_t i_culled = 0; i_culled < culled.size(); ++i_culled) {
|
||||
const size_t i_full = m_speciesIndexMap[i_culled];
|
||||
full[i_full] += culled[i_culled];
|
||||
}
|
||||
return full;
|
||||
}
|
||||
|
||||
std::vector<double> AdaptiveEngineView::mapFullToCulled(const std::vector<double>& full) const {
|
||||
std::vector<double> culled(m_activeSpecies.size(), 0.0);
|
||||
for (size_t i_culled = 0; i_culled < m_activeSpecies.size(); ++i_culled) {
|
||||
const size_t i_full = m_speciesIndexMap[i_culled];
|
||||
culled[i_culled] = full[i_full];
|
||||
}
|
||||
return culled;
|
||||
}
|
||||
|
||||
size_t AdaptiveEngineView::mapCulledToFullSpeciesIndex(size_t culledSpeciesIndex) const {
|
||||
if (culledSpeciesIndex >= m_speciesIndexMap.size()) {
|
||||
LOG_ERROR(m_logger, "Culled index {} is out of bounds for species index map of size {}.", culledSpeciesIndex, m_speciesIndexMap.size());
|
||||
m_logger->flush_log();
|
||||
throw std::out_of_range("Culled index " + std::to_string(culledSpeciesIndex) + " is out of bounds for species index map of size " + std::to_string(m_speciesIndexMap.size()) + ".");
|
||||
}
|
||||
return m_speciesIndexMap[culledSpeciesIndex];
|
||||
}
|
||||
|
||||
size_t AdaptiveEngineView::mapCulledToFullReactionIndex(size_t culledReactionIndex) const {
|
||||
if (culledReactionIndex >= m_reactionIndexMap.size()) {
|
||||
LOG_ERROR(m_logger, "Culled index {} is out of bounds for reaction index map of size {}.", culledReactionIndex, m_reactionIndexMap.size());
|
||||
m_logger->flush_log();
|
||||
throw std::out_of_range("Culled index " + std::to_string(culledReactionIndex) + " is out of bounds for reaction index map of size " + std::to_string(m_reactionIndexMap.size()) + ".");
|
||||
}
|
||||
return m_reactionIndexMap[culledReactionIndex];
|
||||
}
|
||||
|
||||
void AdaptiveEngineView::validateState() const {
|
||||
if (m_isStale) {
|
||||
LOG_ERROR(m_logger, "AdaptiveEngineView is stale. Please call update() before calculating RHS and energy.");
|
||||
@@ -534,29 +433,6 @@ namespace gridfire {
|
||||
}
|
||||
);
|
||||
|
||||
LOG_TRACE_L1(
|
||||
m_logger,
|
||||
"Found {} species {}that are produced but not consumed in any reaction and do not have an inf timescale (those are expected to be equilibrium species and do not contribute to the stiffness of the network).",
|
||||
onlyProducedSpecies.size(),
|
||||
[&]() -> std::string {
|
||||
std::ostringstream ss;
|
||||
if (onlyProducedSpecies.empty()) {
|
||||
return "";
|
||||
}
|
||||
int count = 0;
|
||||
ss << "(";
|
||||
for (const auto& species : onlyProducedSpecies) {
|
||||
ss << species.name();
|
||||
if (count < onlyProducedSpecies.size() - 1) {
|
||||
ss << ", ";
|
||||
}
|
||||
count++;
|
||||
}
|
||||
ss << ") ";
|
||||
return ss.str();
|
||||
}()
|
||||
);
|
||||
|
||||
std::unordered_map<Species, const reaction::Reaction*> reactionsToRescue;
|
||||
for (const auto& species : onlyProducedSpecies) {
|
||||
double maxSpeciesConsumptionRate = 0.0;
|
||||
@@ -576,8 +452,6 @@ namespace gridfire {
|
||||
std::vector<double> Y = comp.getMolarAbundanceVector();
|
||||
|
||||
const double Ye = comp.getElectronAbundance();
|
||||
// TODO: This is a dummy placeholder which must be replaced with an EOS call
|
||||
const double mue = 5.0e-3 * std::pow(rho * Ye, 1.0 / 3.0) + 0.5 * T9;
|
||||
|
||||
std::unordered_map<Species, double> speciesMassMap;
|
||||
for (const auto &sp: comp | std::views::keys) {
|
||||
@@ -588,7 +462,7 @@ namespace gridfire {
|
||||
size_t distance = std::distance(speciesMassMap.begin(), speciesMassMap.find(sp));
|
||||
speciesIndexMap.emplace(distance, sp);
|
||||
}
|
||||
double rate = reaction->calculate_rate(T9, rho, Ye, mue, Y, speciesIndexMap);
|
||||
double rate = reaction->calculate_rate(T9, rho, Ye, 0.0, Y, speciesIndexMap);
|
||||
if (rate > maxSpeciesConsumptionRate) {
|
||||
maxSpeciesConsumptionRate = rate;
|
||||
reactionsToRescue[species] = reaction.get();
|
||||
@@ -652,7 +526,6 @@ namespace gridfire {
|
||||
}()
|
||||
);
|
||||
|
||||
RescueSet rescueSet;
|
||||
std::unordered_set<const reaction::Reaction*> newReactions;
|
||||
std::unordered_set<Species> newSpecies;
|
||||
|
||||
@@ -673,20 +546,18 @@ namespace gridfire {
|
||||
for (const auto* reactionPtr: finalReactions) {
|
||||
m_activeReactions.add_reaction(*reactionPtr);
|
||||
for (const auto& reactant : reactionPtr->reactants()) {
|
||||
if (!finalSpeciesSet.contains(reactant)) {
|
||||
LOG_TRACE_L1(m_logger, "Adding reactant '{}' to active species set through reaction {}.", reactant.name(), reactionPtr->id());
|
||||
} else {
|
||||
LOG_TRACE_L1(m_logger, "Reactant '{}' already in active species set through another reaction.", reactant.name());
|
||||
const SpeciesStatus reactantStatus = m_baseEngine.getSpeciesStatus(reactant);
|
||||
if (!finalSpeciesSet.contains(reactant) && (reactantStatus == SpeciesStatus::ACTIVE || reactantStatus == SpeciesStatus::EQUILIBRIUM)) {
|
||||
LOG_TRACE_L3(m_logger, "Adding reactant '{}' to active species set through reaction {}.", reactant.name(), reactionPtr->id());
|
||||
finalSpeciesSet.insert(reactant);
|
||||
}
|
||||
finalSpeciesSet.insert(reactant);
|
||||
}
|
||||
for (const auto& product : reactionPtr->products()) {
|
||||
if (!finalSpeciesSet.contains(product)) {
|
||||
LOG_TRACE_L1(m_logger, "Adding product '{}' to active species set through reaction {}.", product.name(), reactionPtr->id());
|
||||
} else {
|
||||
LOG_TRACE_L1(m_logger, "Product '{}' already in active species set through another reaction.", product.name());
|
||||
const SpeciesStatus productStatus = m_baseEngine.getSpeciesStatus(product);
|
||||
if (!finalSpeciesSet.contains(product) && (productStatus == SpeciesStatus::ACTIVE || productStatus == SpeciesStatus::EQUILIBRIUM)) {
|
||||
LOG_TRACE_L3(m_logger, "Adding product '{}' to active species set through reaction {}.", product.name(), reactionPtr->id());
|
||||
finalSpeciesSet.insert(product);
|
||||
}
|
||||
finalSpeciesSet.insert(product);
|
||||
}
|
||||
}
|
||||
m_activeSpecies.clear();
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace gridfire {
|
||||
return m_baseEngine.calculateEpsDerivatives(masked, T9, rho, m_activeReactions);
|
||||
}
|
||||
|
||||
void DefinedEngineView::generateJacobianMatrix(
|
||||
NetworkJacobian DefinedEngineView::generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
@@ -79,10 +79,10 @@ namespace gridfire {
|
||||
m_activeSpeciesVectorCache = std::vector<Species>(m_activeSpecies.begin(), m_activeSpecies.end());
|
||||
}
|
||||
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies);
|
||||
m_baseEngine.generateJacobianMatrix(masked, T9, rho, m_activeSpeciesVectorCache.value());
|
||||
return m_baseEngine.generateJacobianMatrix(masked, T9, rho, m_activeSpeciesVectorCache.value());
|
||||
}
|
||||
|
||||
void DefinedEngineView::generateJacobianMatrix(
|
||||
NetworkJacobian DefinedEngineView::generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
@@ -96,10 +96,10 @@ namespace gridfire {
|
||||
);
|
||||
|
||||
const fourdst::composition::MaskedComposition masked(comp, activeSpeciesSet);
|
||||
m_baseEngine.generateJacobianMatrix(masked, T9, rho, activeSpecies);
|
||||
return m_baseEngine.generateJacobianMatrix(masked, T9, rho, activeSpecies);
|
||||
}
|
||||
|
||||
void DefinedEngineView::generateJacobianMatrix(
|
||||
NetworkJacobian DefinedEngineView::generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
@@ -107,27 +107,7 @@ namespace gridfire {
|
||||
) const {
|
||||
validateNetworkState();
|
||||
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies);
|
||||
m_baseEngine.generateJacobianMatrix(masked, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
double DefinedEngineView::getJacobianMatrixEntry(
|
||||
const Species& rowSpecies,
|
||||
const Species& colSpecies
|
||||
) const {
|
||||
validateNetworkState();
|
||||
|
||||
if (!m_activeSpecies.contains(rowSpecies)) {
|
||||
LOG_ERROR(m_logger, "Row species '{}' is not part of the active species in the DefinedEngineView.", rowSpecies.name());
|
||||
m_logger -> flush_log();
|
||||
throw std::runtime_error("Row species not found in active species: " + std::string(rowSpecies.name()));
|
||||
}
|
||||
if (!m_activeSpecies.contains(colSpecies)) {
|
||||
LOG_ERROR(m_logger, "Column species '{}' is not part of the active species in the DefinedEngineView.", colSpecies.name());
|
||||
m_logger -> flush_log();
|
||||
throw std::runtime_error("Column species not found in active species: " + std::string(colSpecies.name()));
|
||||
}
|
||||
|
||||
return m_baseEngine.getJacobianMatrixEntry(rowSpecies, colSpecies);
|
||||
return m_baseEngine.generateJacobianMatrix(masked, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
void DefinedEngineView::generateStoichiometryMatrix() {
|
||||
@@ -298,6 +278,14 @@ namespace gridfire {
|
||||
return result;
|
||||
}
|
||||
|
||||
SpeciesStatus DefinedEngineView::getSpeciesStatus(const Species &species) const {
|
||||
const SpeciesStatus status = m_baseEngine.getSpeciesStatus(species);
|
||||
if (status == SpeciesStatus::ACTIVE && !m_activeSpecies.contains(species)) {
|
||||
return SpeciesStatus::INACTIVE_FLOW;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
std::vector<size_t> DefinedEngineView::constructSpeciesIndexMap() const {
|
||||
LOG_TRACE_L3(m_logger, "Constructing species index map for DefinedEngineView...");
|
||||
std::unordered_map<Species, size_t> fullSpeciesReverseMap;
|
||||
|
||||
@@ -163,9 +163,9 @@ namespace gridfire {
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
// const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
|
||||
const auto result = m_baseEngine.calculateRHSAndEnergy(qseComposition, T9, rho);
|
||||
const auto result = m_baseEngine.calculateRHSAndEnergy(comp, T9, rho);
|
||||
|
||||
if (!result) {
|
||||
return std::unexpected{result.error()};
|
||||
@@ -184,28 +184,29 @@ namespace gridfire {
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
return m_baseEngine.calculateEpsDerivatives(qseComposition, T9, rho);
|
||||
// const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
return m_baseEngine.calculateEpsDerivatives(comp, T9, rho);
|
||||
}
|
||||
|
||||
void MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||
NetworkJacobian MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
m_baseEngine.generateJacobianMatrix(qseComposition, T9, rho, m_dynamic_species);
|
||||
// const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
return m_baseEngine.generateJacobianMatrix(comp, T9, rho, m_dynamic_species);
|
||||
}
|
||||
|
||||
void MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||
NetworkJacobian MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &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);
|
||||
});
|
||||
bool activeSpeciesIsSubset = true;
|
||||
for (const auto& species : activeSpecies) {
|
||||
if (!involvesSpecies(species)) activeSpeciesIsSubset = false;
|
||||
}
|
||||
if (!activeSpeciesIsSubset) {
|
||||
std::string msg = std::format(
|
||||
"Active species set contains species ({}) not present in network partition. Cannot generate jacobian matrix due to this.",
|
||||
@@ -230,33 +231,19 @@ namespace gridfire {
|
||||
}
|
||||
}
|
||||
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
// const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
|
||||
m_baseEngine.generateJacobianMatrix(qseComposition, T9, rho, dynamicActiveSpeciesIntersection);
|
||||
return m_baseEngine.generateJacobianMatrix(comp, T9, rho, dynamicActiveSpeciesIntersection);
|
||||
}
|
||||
|
||||
void MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||
NetworkJacobian MultiscalePartitioningEngineView::generateJacobianMatrix(
|
||||
const fourdst::composition::CompositionAbstract &comp,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const SparsityPattern &sparsityPattern
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
return m_baseEngine.generateJacobianMatrix(qseComposition, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
double MultiscalePartitioningEngineView::getJacobianMatrixEntry(
|
||||
const Species& rowSpecies,
|
||||
const Species& colSpecies
|
||||
) const {
|
||||
// Check if the species we are differentiating with respect to is algebraic or dynamic. If it is algebraic we can reduce the work significantly...
|
||||
if (std::ranges::contains(m_algebraic_species, colSpecies)) {
|
||||
return 0.0;
|
||||
}
|
||||
if (std::ranges::contains(m_algebraic_species, rowSpecies)) {
|
||||
return 0.0;
|
||||
}
|
||||
return m_baseEngine.getJacobianMatrixEntry(rowSpecies, colSpecies);
|
||||
// const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
return m_baseEngine.generateJacobianMatrix(comp, T9, rho, sparsityPattern);
|
||||
}
|
||||
|
||||
void MultiscalePartitioningEngineView::generateStoichiometryMatrix() {
|
||||
@@ -276,9 +263,9 @@ namespace gridfire {
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
// const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
|
||||
return m_baseEngine.calculateMolarReactionFlow(reaction, qseComposition, T9, rho);
|
||||
return m_baseEngine.calculateMolarReactionFlow(reaction, comp, T9, rho);
|
||||
}
|
||||
|
||||
const reaction::ReactionSet & MultiscalePartitioningEngineView::getNetworkReactions() const {
|
||||
@@ -295,8 +282,8 @@ namespace gridfire {
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(qseComposition, T9, rho);
|
||||
// const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
const auto result = m_baseEngine.getSpeciesTimescales(comp, T9, rho);
|
||||
if (!result) {
|
||||
return std::unexpected{result.error()};
|
||||
}
|
||||
@@ -313,8 +300,8 @@ namespace gridfire {
|
||||
const double T9,
|
||||
const double rho
|
||||
) const {
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
const auto result = m_baseEngine.getSpeciesDestructionTimescales(qseComposition, T9, rho);
|
||||
// const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
const auto result = m_baseEngine.getSpeciesDestructionTimescales(comp, T9, rho);
|
||||
if (!result) {
|
||||
return std::unexpected{result.error()};
|
||||
}
|
||||
@@ -524,6 +511,14 @@ namespace gridfire {
|
||||
std::ranges::sort(m_qse_groups, [](const QSEGroup& a, const QSEGroup& b) {
|
||||
return a.mean_timescale < b.mean_timescale;
|
||||
});
|
||||
|
||||
for (const auto& species : m_baseEngine.getNetworkSpecies()) {
|
||||
bool involvesAlgebraic = involvesSpeciesInQSE(species);
|
||||
if (std::ranges::find(m_dynamic_species, species) == m_dynamic_species.end() && !involvesAlgebraic) {
|
||||
// Species is classed as neither dynamic nor algebraic at end of partitioning → add to dynamic set
|
||||
m_dynamic_species.push_back(species);
|
||||
}
|
||||
}
|
||||
return getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
}
|
||||
|
||||
@@ -554,7 +549,7 @@ namespace gridfire {
|
||||
}
|
||||
}
|
||||
|
||||
const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
// const fourdst::composition::Composition qseComposition = getNormalizedEquilibratedComposition(comp, T9, rho);
|
||||
// Calculate reaction flows and find min/max for logarithmic scaling of transparency
|
||||
std::vector<double> reaction_flows;
|
||||
reaction_flows.reserve(all_reactions.size());
|
||||
@@ -562,7 +557,7 @@ namespace gridfire {
|
||||
double max_log_flow = std::numeric_limits<double>::lowest();
|
||||
|
||||
for (const auto& reaction : all_reactions) {
|
||||
double flow = std::abs(m_baseEngine.calculateMolarReactionFlow(*reaction, qseComposition, T9, rho));
|
||||
double flow = std::abs(m_baseEngine.calculateMolarReactionFlow(*reaction, comp, T9, rho));
|
||||
reaction_flows.push_back(flow);
|
||||
if (flow > 1e-99) { // Avoid log(0)
|
||||
double log_flow = std::log10(flow);
|
||||
@@ -826,6 +821,14 @@ namespace gridfire {
|
||||
return qseComposition;
|
||||
}
|
||||
|
||||
SpeciesStatus MultiscalePartitioningEngineView::getSpeciesStatus(const Species &species) const {
|
||||
const SpeciesStatus status = m_baseEngine.getSpeciesStatus(species);
|
||||
if (status == SpeciesStatus::ACTIVE && involvesSpeciesInQSE(species)) {
|
||||
return SpeciesStatus::EQUILIBRIUM;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
size_t MultiscalePartitioningEngineView::getSpeciesIndex(const Species &species) const {
|
||||
return m_baseEngine.getSpeciesIndex(species);
|
||||
}
|
||||
@@ -1499,7 +1502,7 @@ namespace gridfire {
|
||||
}
|
||||
|
||||
std::vector<Species> qse_species_vector(m_qse_solve_species.begin(), m_qse_solve_species.end());
|
||||
m_view.getBaseEngine().generateJacobianMatrix(comp_trial, m_T9, m_rho, qse_species_vector);
|
||||
NetworkJacobian jac = m_view.getBaseEngine().generateJacobianMatrix(comp_trial, m_T9, m_rho, qse_species_vector);
|
||||
const auto result = m_view.getBaseEngine().calculateRHSAndEnergy(comp_trial, m_T9, m_rho);
|
||||
if (!result) {
|
||||
throw exceptions::StaleEngineError("Failed to calculate RHS and energy due to stale engine state");
|
||||
@@ -1512,10 +1515,7 @@ namespace gridfire {
|
||||
for (const auto& rowSpecies : m_qse_solve_species) {
|
||||
long colID = 0;
|
||||
for (const auto& colSpecies: m_qse_solve_species) {
|
||||
J_qse(rowID, colID) = m_view.getBaseEngine().getJacobianMatrixEntry(
|
||||
rowSpecies,
|
||||
colSpecies
|
||||
);
|
||||
J_qse(rowID, colID) = jac(rowSpecies, colSpecies);
|
||||
colID += 1;
|
||||
LOG_TRACE_L3(m_view.m_logger, "Jacobian[{}, {}] (d(dY({}))/dY({})) = {}", rowID, colID - 1, rowSpecies.name(), colSpecies.name(), J_qse(rowID, colID - 1));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user