23 namespace ublas = boost::numeric::ublas;
24 namespace odeint = boost::numeric::odeint;
25 using fourdst::composition::Composition;
30 const auto absTol =
m_config.get<
double>(
"gridfire:solver:DirectNetworkSolver:absTol", 1.0e-8);
31 const auto relTol =
m_config.get<
double>(
"gridfire:solver:DirectNetworkSolver:relTol", 1.0e-8);
33 Composition equilibratedComposition =
m_engine.update(netIn);
34 size_t numSpecies =
m_engine.getNetworkSpecies().size();
35 ublas::vector<double> Y(numSpecies + 1);
40 auto populateY = [&](
const Composition& comp) {
41 const size_t numSpeciesInternal =
m_engine.getNetworkSpecies().size();
42 Y.resize(numSpeciesInternal + 1);
43 for (
size_t i = 0; i < numSpeciesInternal; i++) {
44 const auto& species =
m_engine.getNetworkSpecies()[i];
45 if (!comp.contains(species)) {
46 double lim = std::numeric_limits<double>::min();
47 LOG_DEBUG(
m_logger,
"Species '{}' not found in composition. Setting abundance to {:0.3E}.", species.name(), lim);
50 Y(i) = comp.getMolarAbundance(species);
55 Y(numSpeciesInternal) = 0.0;
61 populateY(equilibratedComposition);
62 const auto stepper = odeint::make_controlled<odeint::rosenbrock4<double>>(absTol, relTol);
64 double current_time = 0.0;
65 double current_initial_timestep = netIn.
dt0;
66 double accumulated_energy = 0.0;
69 while (current_time < netIn.
tMax) {
71 odeint::integrate_adaptive(
73 std::make_pair(manager, jacobianFunctor),
77 current_initial_timestep,
78 [&](
const auto& state,
double t) {
80 manager.observe(state, t);
83 current_time = netIn.
tMax;
85 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());
87 accumulated_energy += e.
energy();
90 Composition temp_comp;
91 std::vector<double> mass_fractions;
94 if (num_species_at_stop !=
m_engine.getNetworkSpecies().size()) {
95 throw std::runtime_error(
96 "StaleEngineError state has a different number of species than the engine. This should not happen."
99 mass_fractions.reserve(num_species_at_stop);
101 for (
size_t i = 0; i < num_species_at_stop; ++i) {
102 const auto& species =
m_engine.getNetworkSpecies()[i];
103 temp_comp.registerSpecies(species);
106 temp_comp.setMassFraction(
m_engine.getNetworkSpecies(), mass_fractions);
107 temp_comp.finalize(
true);
109 NetIn netInTemp = netIn;
114 Composition currentComposition =
m_engine.update(netInTemp);
115 populateY(currentComposition);
116 Y(Y.size() - 1) = e.
energy();
117 numSpecies =
m_engine.getNetworkSpecies().size();
123 accumulated_energy += Y(Y.size() - 1);
125 std::vector<double> finalMassFractions(numSpecies);
126 for (
size_t i = 0; i < numSpecies; ++i) {
127 const double molarMass =
m_engine.getNetworkSpecies()[i].mass();
128 finalMassFractions[i] = Y(i) * molarMass;
130 finalMassFractions[i] = 0.0;
134 std::vector<std::string> speciesNames;
135 speciesNames.reserve(numSpecies);
136 for (
const auto& species :
m_engine.getNetworkSpecies()) {
137 speciesNames.push_back(std::string(species.name()));
140 Composition outputComposition(speciesNames);
141 outputComposition.setMassFraction(speciesNames, finalMassFractions);
142 outputComposition.finalize(
true);
146 netOut.
energy = accumulated_energy;
153 const boost::numeric::ublas::vector<double> &Y,
154 boost::numeric::ublas::vector<double> &dYdt,
157 const size_t numSpecies =
m_engine.getNetworkSpecies().size();
161 const auto&[dydt, nuclearEnergyGenerationRate] =
m_cached_result.value();
162 dYdt.resize(numSpecies + 1);
163 std::ranges::copy(dydt, dYdt.begin());
164 dYdt(numSpecies) = nuclearEnergyGenerationRate;
216 const boost::numeric::ublas::vector<double> &Y,
217 boost::numeric::ublas::matrix<double> &J,
219 boost::numeric::ublas::vector<double> &dfdt
221 size_t numSpecies =
m_engine.getNetworkSpecies().size();
222 J.resize(numSpecies+1, numSpecies+1);
224 for (
size_t i = 0; i < numSpecies; ++i) {
225 for (
size_t j = 0; j < numSpecies; ++j) {
226 J(i, j) =
m_engine.getJacobianMatrixEntry(i, j);