fix(python-bindings): Updated python bindings to new interface

The python bindings now work with the polymorphic reaction class and the CVODE solver
This commit is contained in:
2025-10-30 15:05:08 -04:00
parent 23df87f915
commit 7fded59814
27 changed files with 962 additions and 255 deletions

View File

@@ -176,7 +176,7 @@ namespace gridfire {
recordADTape(); // Record the AD tape for the RHS of the ODE (dY/di and dEps/di) for all independent variables i
const size_t inputSize = m_rhsADFun.Domain();
[[maybe_unused]] const size_t inputSize = m_rhsADFun.Domain();
const size_t outputSize = m_rhsADFun.Range();
// Create a range x range identity pattern
@@ -584,6 +584,26 @@ namespace gridfire {
}
}
fourdst::composition::Composition GraphEngine::collectComposition(
fourdst::composition::Composition &comp
) const {
for (const auto &speciesName: comp | std::views::keys) {
if (!m_networkSpeciesMap.contains(speciesName)) {
throw exceptions::BadCollectionError("Cannot collect composition from GraphEngine as " + speciesName + " present in input composition does not exist in the network species map");
}
}
fourdst::composition::Composition result;
for (const auto& species : m_networkSpecies ) {
result.registerSpecies(species);
if (comp.hasSpecies(species)) {
result.setMassFraction(species, comp.getMassFraction(species));
} else {
result.setMassFraction(species, 0.0);
}
}
return result;
}
StepDerivatives<double> GraphEngine::calculateAllDerivativesUsingPrecomputation(
const fourdst::composition::Composition& comp,
const std::vector<double> &bare_rates,

View File

@@ -318,6 +318,21 @@ namespace gridfire {
return m_baseEngine.primeEngine(netIn);
}
fourdst::composition::Composition AdaptiveEngineView::collectComposition(
fourdst::composition::Composition &comp
) const {
fourdst::composition::Composition result = m_baseEngine.collectComposition(comp); // Step one is to bubble the results from lower levels of the engine chain up
for (const auto& species : m_activeSpecies) {
if (!result.hasSpecies(species)) {
result.registerSpecies(species);
result.setMassFraction(species, 0.0);
}
}
return result;
}
size_t AdaptiveEngineView::getSpeciesIndex(const fourdst::atomic::Species &species) const {
const auto it = std::ranges::find(m_activeSpecies, species);
if (it != m_activeSpecies.end()) {

View File

@@ -295,6 +295,20 @@ namespace gridfire {
return m_baseEngine.primeEngine(netIn);
}
fourdst::composition::Composition DefinedEngineView::collectComposition(
fourdst::composition::Composition &comp
) const {
fourdst::composition::Composition result = m_baseEngine.collectComposition(comp);
for (const auto& species : m_activeSpecies) {
if (!result.hasSpecies(species)) {
result.registerSpecies(species);
result.setMassFraction(species, 0.0);
}
}
return result;
}
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;

View File

@@ -874,6 +874,43 @@ namespace gridfire {
return std::ranges::find(m_dynamic_species, species) != m_dynamic_species.end();
}
fourdst::composition::Composition MultiscalePartitioningEngineView::collectComposition(
fourdst::composition::Composition &comp
) const {
fourdst::composition::Composition result = m_baseEngine.collectComposition(comp);
bool didFinalize = result.finalize(false);
if (!didFinalize) {
std::string msg = "Failed to finalize collected composition from MultiscalePartitioningEngine view after calling base engines collectComposition method.";
LOG_ERROR(m_logger, "{}", msg);
throw exceptions::BadCollectionError(msg);
}
std::map<Species, double> Ym; // Use an ordered map here so that this is ordered by atomic mass (which is the </> comparator for Species)
for (const auto& [speciesName, entry] : result) {
Ym.emplace(entry.isotope(), result.getMolarAbundance(speciesName));
}
for (const auto& [species, Yi] : m_algebraic_abundances) {
if (!Ym.contains(species)) {
throw exceptions::BadCollectionError("MuiltiscalePartitioningEngineView failed to collect composition for species " + std::string(species.name()) + " as the base engine did not report that species present in its composition!");
}
Ym.at(species) = Yi;
}
std::vector<double> M;
std::vector<double> Y;
std::vector<std::string> speciesNames;
M.reserve(Ym.size());
Y.reserve(Ym.size());
for (const auto& [species, Yi] : Ym) {
M.emplace_back(species.mass());
Y.emplace_back(Yi);
speciesNames.emplace_back(species.name());
}
std::vector<double> X = utils::massFractionFromMolarAbundanceAndMolarMass(Y, M);
return fourdst::composition::Composition(speciesNames, X);
}
size_t MultiscalePartitioningEngineView::getSpeciesIndex(const Species &species) const {
return m_baseEngine.getSpeciesIndex(species);
}

View File

@@ -136,6 +136,13 @@ namespace gridfire::solver {
}
NetOut CVODESolverStrategy::evaluate(const NetIn& netIn) {
return evaluate(netIn, false);
}
NetOut CVODESolverStrategy::evaluate(
const NetIn &netIn,
bool displayTrigger
) {
LOG_TRACE_L1(m_logger, "Starting solver evaluation with T9: {} and rho: {}", netIn.temperature/1e9, netIn.density);
LOG_TRACE_L1(m_logger, "Building engine update trigger....");
auto trigger = trigger::solver::CVODE::makeEnginePartitioningTrigger(1e12, 1e10, 1, true, 10);
@@ -182,12 +189,7 @@ namespace gridfire::solver {
check_cvode_flag(CVodeSetUserData(m_cvode_mem, &user_data), "CVodeSetUserData");
int flag{};
if (m_stdout_logging_enabled) {
flag = CVode(m_cvode_mem, netIn.tMax, m_Y, &current_time, CV_ONE_STEP);
} else {
flag = CVode(m_cvode_mem, netIn.tMax, m_Y, &current_time, CV_NORMAL);
}
int flag = CVode(m_cvode_mem, netIn.tMax, m_Y, &current_time, CV_ONE_STEP);
if (user_data.captured_exception){
std::rethrow_exception(std::make_exception_ptr(*user_data.captured_exception));
@@ -206,14 +208,17 @@ namespace gridfire::solver {
sunrealtype* y_data = N_VGetArrayPointer(m_Y);
const double current_energy = y_data[numSpecies]; // Specific energy rate
std::cout << std::scientific << std::setprecision(3)
<< "Step: " << std::setw(6) << n_steps
<< " | Time: " << current_time << " [s]"
<< " | Last Step Size: " << last_step_size
<< " | Accumulated Energy: " << current_energy << " [erg/g]"
<< " | NonLinIters: " << std::setw(2) << nliters
<< " | ConvFails: " << std::setw(2) << nlcfails
<< std::endl;
if (m_stdout_logging_enabled) {
std::cout << std::scientific << std::setprecision(3)
<< "Step: " << std::setw(6) << n_steps
<< " | Time: " << current_time << " [s]"
<< " | Last Step Size: " << last_step_size
<< " | Current Lightest Molar Abundance: " << y_data[0] << " [mol/g]"
<< " | Accumulated Energy: " << current_energy << " [erg/g]"
<< " | Total Non Linear Iterations: " << std::setw(2) << nliters
<< " | Total Convergence Failures: " << std::setw(2) << nlcfails
<< "\n";
}
auto ctx = TimestepContext(
current_time,
@@ -227,7 +232,9 @@ namespace gridfire::solver {
m_engine.getNetworkSpecies());
if (trigger->check(ctx)) {
trigger::printWhy(trigger->why(ctx));
if (m_stdout_logging_enabled && displayTrigger) {
trigger::printWhy(trigger->why(ctx));
}
trigger->update(ctx);
accumulated_energy += current_energy; // Add the specific energy rate to the accumulated energy
LOG_INFO(
@@ -299,8 +306,9 @@ namespace gridfire::solver {
}
// TODO: Need a more reliable way to get the final composition out, probably some methods that bubble it or something
// aside from that this now seems to be working
if (m_stdout_logging_enabled) { // Flush the buffer if standard out logging is enabled
std::cout << std::flush;
}
LOG_TRACE_L2(m_logger, "CVODE iteration complete");
@@ -323,13 +331,23 @@ namespace gridfire::solver {
}
LOG_TRACE_L2(m_logger, "Constructing final composition= with {} species", speciesNames.size());
fourdst::composition::Composition outputComposition(speciesNames);
outputComposition.setMassFraction(speciesNames, finalMassFractions);
bool didFinalize = outputComposition.finalize(true);
if (!didFinalize) {
LOG_ERROR(m_logger, "Failed to finalize output composition after CVODE integration. Check output mass fractions for validity.");
fourdst::composition::Composition topLevelComposition(speciesNames);
topLevelComposition.setMassFraction(speciesNames, finalMassFractions);
bool didFinalizeTopLevel = topLevelComposition.finalize(true);
if (!didFinalizeTopLevel) {
LOG_ERROR(m_logger, "Failed to finalize top level reconstructed composition after CVODE integration. Check output mass fractions for validity.");
throw std::runtime_error("Failed to finalize output composition after CVODE integration.");
}
fourdst::composition::Composition outputComposition = m_engine.collectComposition(topLevelComposition);
assert(outputComposition.getRegisteredSymbols().size() == equilibratedComposition.getRegisteredSymbols().size());
bool didFinalizeOutput = outputComposition.finalize(false);
if (!didFinalizeOutput) {
LOG_ERROR(m_logger, "Failed to finalize output composition after CVODE integration.");
throw std::runtime_error("Failed to finalize output composition after CVODE integration.");
}
LOG_TRACE_L2(m_logger, "Final composition constructed successfully!");
LOG_TRACE_L2(m_logger, "Constructing output data...");
@@ -351,6 +369,7 @@ namespace gridfire::solver {
LOG_TRACE_L2(m_logger, "Output data built!");
LOG_TRACE_L2(m_logger, "Solver evaluation complete!.");
return netOut;
}
@@ -362,8 +381,8 @@ namespace gridfire::solver {
return m_stdout_logging_enabled;
}
void CVODESolverStrategy::set_stdout_logging_enabled(const bool value) {
m_stdout_logging_enabled = value;
void CVODESolverStrategy::set_stdout_logging_enabled(const bool logging_enabled) {
m_stdout_logging_enabled = logging_enabled;
}
std::vector<std::tuple<std::string, std::string>> CVODESolverStrategy::describe_callback_context() const {