fix(engine_multiscale): resolved a major species index ordering bug

All jacobian calculations were broken because the indexing used to record the AD tape was broken (see not parallel to) the indexing used by the composition object. A fix for this was to sort the network species by mass. However, more generally we should introduce a mechanism to ensure these two indexed sets always remain parallel
This commit is contained in:
2025-10-14 13:37:48 -04:00
parent 408f6d83a2
commit 3b8a0a1f33
10 changed files with 276 additions and 232 deletions

View File

@@ -192,7 +192,9 @@ namespace gridfire {
throw std::runtime_error("Species not found in global atomic species database: " + std::string(name));
}
}
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
});
}
void GraphEngine::populateReactionIDMap() {
@@ -508,12 +510,21 @@ namespace gridfire {
composition.setMassFraction(symbol, 0.0);
}
}
composition.finalize(true);
const bool didFinalize = composition.finalize(true);
if (!didFinalize) {
LOG_ERROR(m_logger, "Failed to finalize composition during priming. Check input mass fractions for validity.");
throw std::runtime_error("Failed to finalize composition during network priming.");
}
fullNetIn.composition = composition;
fullNetIn.temperature = netIn.temperature;
fullNetIn.density = netIn.density;
auto primingReport = primeNetwork(fullNetIn, *this);
std::optional<std::vector<reaction::ReactionType>> reactionTypesToIgnore = std::nullopt;
if (!m_useReverseReactions) {
reactionTypesToIgnore = {reaction::ReactionType::WEAK};
}
auto primingReport = primeNetwork(fullNetIn, *this, reactionTypesToIgnore);
return primingReport;
}
@@ -737,7 +748,6 @@ namespace gridfire {
const double T9,
const double rho
) const {
LOG_TRACE_L1_LIMIT_EVERY_N(1000, m_logger, "Generating jacobian matrix for T9={}, rho={}..", T9, rho);
const size_t numSpecies = m_networkSpecies.size();
@@ -760,6 +770,7 @@ namespace gridfire {
const double value = dotY[i * (numSpecies + 2) + j];
if (std::abs(value) > MIN_JACOBIAN_THRESHOLD || i == j) { // Always keep diagonal elements to avoid pathological stiffness
m_jacobianMatrix(i, j) = value;
}
}
}
@@ -1023,7 +1034,11 @@ namespace gridfire {
baseUpdatedComposition.setMassFraction(species, 0.0);
}
}
baseUpdatedComposition.finalize(false);
const bool didFinalize = baseUpdatedComposition.finalize(false);
if (!didFinalize) {
LOG_ERROR(m_logger, "Failed to finalize composition during update. Check input mass fractions for validity.");
throw std::runtime_error("Failed to finalize composition during network update.");
}
return baseUpdatedComposition;
}
@@ -1079,10 +1094,11 @@ namespace gridfire {
adYe,
adMue,
[&](const fourdst::atomic::Species& querySpecies) -> size_t {
return m_speciesToIndexMap.at(querySpecies); // TODO: This is bad, needs to be fixed
return m_speciesToIndexMap.at(querySpecies);
}
);
// Extract the raw vector from the associative map
std::vector<CppAD::AD<double>> dydt_vec;
dydt_vec.reserve(dydt.size());