Loading...
Searching...
No Matches
solver.cpp
Go to the documentation of this file.
#include "gridfire/solver/solver.h"
#include "gridfire/engine/engine_graph.h"
#include "gridfire/network.h"
#include "gridfire/exceptions/error_engine.h"
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/composition/composition.h"
#include "fourdst/config/config.h"
#include "unsupported/Eigen/NonLinearOptimization"
#include <boost/numeric/odeint.hpp>
#include <vector>
#include <string>
#include <stdexcept>
#include <iomanip>
#include "quill/LogMacros.h"
namespace gridfire::solver {
NetOut DirectNetworkSolver::evaluate(const NetIn &netIn) {
namespace ublas = boost::numeric::ublas;
namespace odeint = boost::numeric::odeint;
using fourdst::composition::Composition;
const double T9 = netIn.temperature / 1e9; // Convert temperature from Kelvin to T9 (T9 = T / 1e9)
const auto absTol = m_config.get<double>("gridfire:solver:DirectNetworkSolver:absTol", 1.0e-8);
const auto relTol = m_config.get<double>("gridfire:solver:DirectNetworkSolver:relTol", 1.0e-8);
Composition equilibratedComposition = m_engine.update(netIn);
size_t numSpecies = m_engine.getNetworkSpecies().size();
ublas::vector<double> Y(numSpecies + 1);
RHSManager manager(m_engine, T9, netIn.density);
JacobianFunctor jacobianFunctor(m_engine, T9, netIn.density);
auto populateY = [&](const Composition& comp) {
const size_t numSpeciesInternal = m_engine.getNetworkSpecies().size();
Y.resize(numSpeciesInternal + 1);
for (size_t i = 0; i < numSpeciesInternal; i++) {
const auto& species = m_engine.getNetworkSpecies()[i];
if (!comp.contains(species)) {
double lim = std::numeric_limits<double>::min();
LOG_DEBUG(m_logger, "Species '{}' not found in composition. Setting abundance to {:0.3E}.", species.name(), lim);
Y(i) = lim; // Species not in the composition, set to zero
} else {
Y(i) = comp.getMolarAbundance(species);
}
}
// TODO: a good starting point to make the temperature, density, and energy self consistent would be to turn this into an accumulator
Y(numSpeciesInternal) = 0.0; // Specific energy rate, initialized to zero
};
// This is a quick debug that can be turned on. For solar code input parameters (T~1.5e7K, ρ~1.5e3 g/cm^3) this should be near 8e-17
// std::cout << "D/H: " << equilibratedComposition.getMolarAbundance("H-2") / equilibratedComposition.getMolarAbundance("H-1") << std::endl;
populateY(equilibratedComposition);
const auto stepper = odeint::make_controlled<odeint::rosenbrock4<double>>(absTol, relTol);
double current_time = 0.0;
double current_initial_timestep = netIn.dt0;
double accumulated_energy = 0.0;
// size_t total_update_stages_triggered = 0;
while (current_time < netIn.tMax) {
try {
odeint::integrate_adaptive(
stepper,
std::make_pair(manager, jacobianFunctor),
Y,
current_time,
netIn.tMax,
current_initial_timestep,
[&](const auto& state, double t) {
current_time = t;
manager.observe(state, t);
}
);
current_time = netIn.tMax;
} catch (const exceptions::StaleEngineTrigger &e) {
LOG_INFO(m_logger, "Catching StaleEngineTrigger at t = {:0.3E} with T9 = {:0.3E}, rho = {:0.3E}. Triggering update stage (last stage took {} steps).", current_time, T9, netIn.density, e.totalSteps());
exceptions::StaleEngineTrigger::state staleState = e.getState();
accumulated_energy += e.energy(); // Add the specific energy rate to the accumulated energy
// total_update_stages_triggered++;
Composition temp_comp;
std::vector<double> mass_fractions;
size_t num_species_at_stop = e.numSpecies();
if (num_species_at_stop != m_engine.getNetworkSpecies().size()) {
throw std::runtime_error(
"StaleEngineError state has a different number of species than the engine. This should not happen."
);
}
mass_fractions.reserve(num_species_at_stop);
for (size_t i = 0; i < num_species_at_stop; ++i) {
const auto& species = m_engine.getNetworkSpecies()[i];
temp_comp.registerSpecies(species);
mass_fractions.push_back(e.getMolarAbundance(i) * species.mass()); // Convert from molar abundance to mass fraction
}
temp_comp.setMassFraction(m_engine.getNetworkSpecies(), mass_fractions);
temp_comp.finalize(true);
NetIn netInTemp = netIn;
netInTemp.temperature = e.temperature();
netInTemp.density = e.density();
netInTemp.composition = std::move(temp_comp);
Composition currentComposition = m_engine.update(netInTemp);
populateY(currentComposition);
Y(Y.size() - 1) = e.energy(); // Set the specific energy rate from the stale state
numSpecies = m_engine.getNetworkSpecies().size();
// current_initial_timestep = 0.001 * manager.m_last_step_time; // set the new timestep to the last successful timestep before repartitioning
}
}
accumulated_energy += Y(Y.size() - 1); // Add the specific energy rate to the accumulated energy
std::vector<double> finalMassFractions(numSpecies);
for (size_t i = 0; i < numSpecies; ++i) {
const double molarMass = m_engine.getNetworkSpecies()[i].mass();
finalMassFractions[i] = Y(i) * molarMass; // Convert from molar abundance to mass fraction
if (finalMassFractions[i] < MIN_ABUNDANCE_THRESHOLD) {
finalMassFractions[i] = 0.0;
}
}
std::vector<std::string> speciesNames;
speciesNames.reserve(numSpecies);
for (const auto& species : m_engine.getNetworkSpecies()) {
speciesNames.push_back(std::string(species.name()));
}
Composition outputComposition(speciesNames);
outputComposition.setMassFraction(speciesNames, finalMassFractions);
outputComposition.finalize(true);
NetOut netOut;
netOut.composition = std::move(outputComposition);
netOut.energy = accumulated_energy; // Specific energy rate
netOut.num_steps = manager.m_num_steps;
return netOut;
}
void DirectNetworkSolver::RHSManager::operator()(
const boost::numeric::ublas::vector<double> &Y,
boost::numeric::ublas::vector<double> &dYdt,
const double t
) const {
const size_t numSpecies = m_engine.getNetworkSpecies().size();
if (t != m_cached_time || !m_cached_result.has_value() || m_cached_result.value().dydt.size() != numSpecies + 1) {
compute_and_cache(Y, t);
}
const auto&[dydt, nuclearEnergyGenerationRate] = m_cached_result.value();
dYdt.resize(numSpecies + 1);
std::ranges::copy(dydt, dYdt.begin());
dYdt(numSpecies) = nuclearEnergyGenerationRate; // Set the last element to the specific energy rate
}
void DirectNetworkSolver::RHSManager::observe(
const boost::numeric::ublas::vector<double> &state,
const double t
) const {
double dt = t - m_last_observed_time;
compute_and_cache(state, t);
LOG_INFO(
m_logger,
"(Step {}) Observed state at t = {:0.3E} (dt = {:0.3E})",
m_num_steps,
t,
dt
);
std::ostringstream oss;
oss << std::scientific << std::setprecision(3);
oss << "(Step: " << std::setw(10) << m_num_steps << ") t = " << t << " (dt = " << dt << ", eps_nuc: " << state(state.size() - 1) << " [erg])\n";
std::cout << oss.str();
m_last_observed_time = t;
m_last_step_time = dt;
}
void DirectNetworkSolver::RHSManager::compute_and_cache(
const boost::numeric::ublas::vector<double> &state,
double t
) const {
std::vector<double> y_vec(state.begin(), state.end() - 1);
std::ranges::replace_if(
y_vec,
[](const double yi){
return yi < 0.0;
},
0.0 // Avoid negative abundances
);
const auto result = m_engine.calculateRHSAndEnergy(y_vec, m_T9, m_rho);
if (!result) {
LOG_INFO(m_logger,
"Triggering update stage due to stale engine detected at t = {:0.3E} with T9 = {:0.3E}, rho = {:0.3E}, y_vec (size: {})",
t, m_T9, m_rho, y_vec.size());
throw exceptions::StaleEngineTrigger({m_T9, m_rho, y_vec, t, m_num_steps, state(state.size() - 1)});
}
m_cached_result = result.value();
m_cached_time = t;
m_num_steps++;
}
void DirectNetworkSolver::JacobianFunctor::operator()(
const boost::numeric::ublas::vector<double> &Y,
boost::numeric::ublas::matrix<double> &J,
double t,
boost::numeric::ublas::vector<double> &dfdt
) const {
size_t numSpecies = m_engine.getNetworkSpecies().size();
J.resize(numSpecies+1, numSpecies+1);
J.clear();
for (size_t i = 0; i < numSpecies; ++i) {
for (size_t j = 0; j < numSpecies; ++j) {
J(i, j) = m_engine.getJacobianMatrixEntry(i, j);
}
}
}
}
gridfire::exceptions::StaleEngineTrigger
Definition error_engine.h:10
gridfire::exceptions::StaleEngineTrigger::getState
state getState() const
Definition error_engine.h:27
gridfire::exceptions::StaleEngineTrigger::totalSteps
size_t totalSteps() const
Definition error_engine.h:35
gridfire::exceptions::StaleEngineTrigger::temperature
double temperature() const
Definition error_engine.h:50
gridfire::exceptions::StaleEngineTrigger::getMolarAbundance
double getMolarAbundance(const size_t index) const
Definition error_engine.h:43
gridfire::exceptions::StaleEngineTrigger::numSpecies
size_t numSpecies() const
Definition error_engine.h:31
gridfire::exceptions::StaleEngineTrigger::density
double density() const
Definition error_engine.h:54
gridfire::exceptions::StaleEngineTrigger::energy
double energy() const
Definition error_engine.h:39
gridfire::solver::DirectNetworkSolver::m_logger
quill::Logger * m_logger
Logger instance.
Definition solver.h:180
gridfire::solver::DirectNetworkSolver::evaluate
NetOut evaluate(const NetIn &netIn) override
Evaluates the network for a given timestep using direct integration.
Definition solver.cpp:22
gridfire::solver::DirectNetworkSolver::m_config
Config & m_config
Configuration instance.
Definition solver.h:181
gridfire::solver::NetworkSolverStrategy< DynamicEngine >::m_engine
DynamicEngine & m_engine
Definition solver.h:47
engine_graph.h
error_engine.h
gridfire::solver
Definition solver.h:15
gridfire::MIN_ABUNDANCE_THRESHOLD
static constexpr double MIN_ABUNDANCE_THRESHOLD
Minimum abundance threshold below which species are ignored.
Definition engine_graph.h:66
network.h
solver.h
gridfire::NetIn
Definition network.h:53
gridfire::NetIn::density
double density
Density in g/cm^3.
Definition network.h:58
gridfire::NetIn::tMax
double tMax
Maximum time.
Definition network.h:55
gridfire::NetIn::composition
fourdst::composition::Composition composition
Composition of the network.
Definition network.h:54
gridfire::NetIn::dt0
double dt0
Initial time step.
Definition network.h:56
gridfire::NetIn::temperature
double temperature
Temperature in Kelvin.
Definition network.h:57
gridfire::NetOut
Definition network.h:65
gridfire::NetOut::composition
fourdst::composition::Composition composition
Composition of the network after evaluation.
Definition network.h:66
gridfire::NetOut::energy
double energy
Energy in ergs after evaluation.
Definition network.h:68
gridfire::NetOut::num_steps
int num_steps
Number of steps taken in the evaluation.
Definition network.h:67
gridfire::exceptions::StaleEngineTrigger::state
Definition error_engine.h:12
gridfire::solver::DirectNetworkSolver::JacobianFunctor
Functor for calculating the Jacobian matrix.
Definition solver.h:143
gridfire::solver::DirectNetworkSolver::JacobianFunctor::m_engine
DynamicEngine & m_engine
The engine used to evaluate the network.
Definition solver.h:144
gridfire::solver::DirectNetworkSolver::JacobianFunctor::operator()
void operator()(const boost::numeric::ublas::vector< double > &Y, boost::numeric::ublas::matrix< double > &J, double t, boost::numeric::ublas::vector< double > &dfdt) const
Calculates the Jacobian matrix.
Definition solver.cpp:215
gridfire::solver::DirectNetworkSolver::RHSManager
Definition solver.h:88
gridfire::solver::DirectNetworkSolver::RHSManager::m_engine
DynamicEngine & m_engine
The engine used to evaluate the network.
Definition solver.h:89
gridfire::solver::DirectNetworkSolver::RHSManager::m_cached_time
double m_cached_time
Definition solver.h:93
gridfire::solver::DirectNetworkSolver::RHSManager::observe
void observe(const boost::numeric::ublas::vector< double > &state, double t) const
Definition solver.cpp:167
gridfire::solver::DirectNetworkSolver::RHSManager::m_T9
const double m_T9
Temperature in units of 10^9 K.
Definition solver.h:90
gridfire::solver::DirectNetworkSolver::RHSManager::m_last_observed_time
double m_last_observed_time
Last time the state was observed.
Definition solver.h:96
gridfire::solver::DirectNetworkSolver::RHSManager::compute_and_cache
void compute_and_cache(const boost::numeric::ublas::vector< double > &state, double t) const
Definition solver.cpp:189
gridfire::solver::DirectNetworkSolver::RHSManager::m_last_step_time
double m_last_step_time
Definition solver.h:101
gridfire::solver::DirectNetworkSolver::RHSManager::m_logger
quill::Logger * m_logger
Logger instance.
Definition solver.h:99
gridfire::solver::DirectNetworkSolver::RHSManager::m_rho
const double m_rho
Density in g/cm^3.
Definition solver.h:91
gridfire::solver::DirectNetworkSolver::RHSManager::m_cached_result
std::optional< StepDerivatives< double > > m_cached_result
Definition solver.h:94
gridfire::solver::DirectNetworkSolver::RHSManager::m_num_steps
int m_num_steps
Definition solver.h:100
gridfire::solver::DirectNetworkSolver::RHSManager::operator()
void operator()(const boost::numeric::ublas::vector< double > &Y, boost::numeric::ublas::vector< double > &dYdt, double t) const
Calculates the time derivatives of the species abundances.
Definition solver.cpp:152