feat(Comoposition-Tracking): updated GridFire to use new, molar-abundance based, version of libcomposition (v2.0.6)

This entailed a major rewrite of the composition handling from each engine and engine view along with the solver and primer. The intent here is to let Compositions be constructed from the same extensive property which the solver tracks internally. This addressed C0 discontinuity issues in the tracked molar abundances of species which were introduced by repeadidly swaping from molar abundance space to mass fraction space and back. This also allowed for a simplification of the primeNetwork method. Specifically the mass borrowing system was dramatically simplified as molar abundances are extensive.
This commit is contained in:
2025-11-10 10:40:03 -05:00
parent 534a44448b
commit a7a4a30028
57 changed files with 1878 additions and 2823 deletions

View File

@@ -1,6 +1,7 @@
#include "gridfire/engine/diagnostics/dynamic_engine_diagnostics.h"
#include "gridfire/engine/engine_abstract.h"
#include "gridfire/utils/table_format.h"
#include "fourdst/atomic/species.h"
#include <vector>
#include <string>

View File

@@ -1,529 +0,0 @@
/* ***********************************************************************
//
// Copyright (C) 2025 -- The 4D-STAR Collaboration
// File Author: Emily Boudreaux
// Last Modified: March 21, 2025
//
// 4DSSE is free software; you can use it and/or modify
// it under the terms and restrictions the GNU General Library Public
// License version 3 (GPLv3) as published by the Free Software Foundation.
//
// 4DSSE is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU Library General Public License for more details.
//
// You should have received a copy of the GNU Library General Public License
// along with this software; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//
// *********************************************************************** */
#include <cmath>
#include <stdexcept>
#include <array>
#include <boost/numeric/odeint.hpp>
#include "fourdst/constants/const.h"
#include "fourdst/config/config.h"
#include "quill/LogMacros.h"
#include "gridfire/engine/engine_approx8.h"
#include "gridfire/network.h"
/* Nuclear reaction network in cgs units based on Frank Timmes' "approx8".
At this time it does neither screening nor neutrino losses. It includes
the following 8 isotopes:
h1
he3
he4
c12
n14
o16
ne20
mg24
and the following nuclear reactions:
---pp chain---
p(p,e+)d
d(p,g)he3
he3(he3,2p)he4
---CNO cycle---
c12(p,g)n13 - n13 -> c13 + p -> n14
n14(p,g)o15 - o15 + p -> c12 + he4
n14(a,g)f18 - proceeds to ne20
n15(p,a)c12 - / these two n15 reactions are \ CNO I
n15(p,g)o16 - \ used to calculate a fraction / CNO II
o16(p,g)f17 - f17 + e -> o17 and then o17 + p -> n14 + he4
---alpha captures---
c12(a,g)o16
triple alpha
o16(a,g)ne20
ne20(a,g)mg24
c12(c12,a)ne20
c12(o16,a)mg24
At present the rates are all evaluated using a fitting function.
The coefficients to the fit are from reaclib.jinaweb.org .
*/
namespace gridfire::approx8{
// using namespace std;
using namespace boost::numeric::odeint;
//helper functions
// a function to multiply two arrays and then sum the resulting elements: sum(a*b)
double sum_product( const vec7 &a, const vec7 &b){
double sum=0;
for (size_t i=0; i < a.size(); i++) {
sum += a[i] * b[i];
}
return sum;
}
// the fit to the nuclear reaction rates is of the form:
// exp( a0 + a1/T9 + a2/T9^(1/3) + a3*T9^(1/3) + a4*T9 + a5*T9^(5/3) + log(T9) )
// this function returns an array of the T9 terms in that order, where T9 is the temperatures in GigaKelvin
vec7 get_T9_array(const double &T) {
vec7 arr;
const double T9=1e-9*T;
const double T913=pow(T9,1./3.);
arr[0]=1;
arr[1]=1/T9;
arr[2]=1/T913;
arr[3]=T913;
arr[4]=T9;
arr[5]=pow(T9,5./3.);
arr[6]=log(T9);
return arr;
}
// this function uses the two preceding functions to evaluate the rate given the T9 array and the coefficients
double rate_fit(const vec7 &T9, const vec7 &coef){
return exp(sum_product(T9,coef));
}
// p + p -> d; this, like some of the other rates, this is a composite of multiple fits
double pp_rate(const vec7 &T9) {
constexpr vec7 a1 = {-34.78630, 0,-3.511930, 3.100860, -0.1983140, 1.262510e-2, -1.025170};
constexpr vec7 a2 = { -4.364990e+1,-2.460640e-3,-2.750700,-4.248770e-1,1.598700e-2,-6.908750e-4,-2.076250e-1};
return rate_fit(T9,a1) + rate_fit(T9,a2);
}
// p + d -> he3
double dp_rate(const vec7 &T9) {
constexpr vec7 a1 = {7.528980, 0, -3.720800, 0.8717820, 0, 0,-0.6666670};
constexpr vec7 a2 = {8.935250, 0, -3.720800, 0.1986540, 0, 0, 0.3333330};
return rate_fit(T9,a1) + rate_fit(T9,a2);
}
// he3 + he3 -> he4 + 2p
double he3he3_rate(const vec7 &T9){
constexpr vec7 a = {2.477880e+01,0,-12.27700,-0.1036990,-6.499670e-02,1.681910e-02,-6.666670e-01};
return rate_fit(T9,a);
}
// he3(he3,2p)he4 ** (missing both coefficients but have a reaction)
double he3he4_rate(const vec7 &T9){
constexpr vec7 a1 = {1.560990e+01,0.000000e+00,-1.282710e+01,-3.082250e-02,-6.546850e-01,8.963310e-02,-6.666670e-01};
constexpr vec7 a2 = {1.770750e+01,0.000000e+00,-1.282710e+01,-3.812600e+00,9.422850e-02,-3.010180e-03,1.333330e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2);
}
// he4 + he4 + he4 -> c12 ** (missing middle coefficient but have other two)
double triple_alpha_rate(const vec7 &T9){
constexpr vec7 a1 = {-9.710520e-01,0.000000e+00,-3.706000e+01,2.934930e+01,-1.155070e+02,-1.000000e+01,-1.333330e+00};
constexpr vec7 a2 = {-1.178840e+01,-1.024460e+00,-2.357000e+01,2.048860e+01,-1.298820e+01,-2.000000e+01,-2.166670e+00};
constexpr vec7 a3 = {-2.435050e+01,-4.126560e+00,-1.349000e+01,2.142590e+01,-1.347690e+00,8.798160e-02,-1.316530e+01};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3);
}
// c12 + p -> n13
double c12p_rate(const vec7 &T9){
constexpr vec7 a1={1.714820e+01,0.000000e+00,-1.369200e+01,-2.308810e-01,4.443620e+00,-3.158980e+00,-6.666670e-01};
constexpr vec7 a2={1.754280e+01,-3.778490e+00,-5.107350e+00,-2.241110e+00,1.488830e-01,0.000000e+00,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2);
}
// c12 + he4 -> o16 ** (missing first coefficient but have the second)
double c12a_rate(const vec7 &T9){
constexpr vec7 a1={6.965260e+01,-1.392540e+00,5.891280e+01,-1.482730e+02,9.083240e+00,-5.410410e-01,7.035540e+01};
constexpr vec7 a2={2.546340e+02,-1.840970e+00,1.034110e+02,-4.205670e+02,6.408740e+01,-1.246240e+01,1.373030e+02};
return rate_fit(T9,a1) + rate_fit(T9,a2);
}
// n14(p,g)o15 - o15 + p -> c12 + he4
double n14p_rate(const vec7 &T9){
constexpr vec7 a1={1.701000e+01,0.000000e+00,-1.519300e+01,-1.619540e-01,-7.521230e+00,-9.875650e-01,-6.666670e-01};
constexpr vec7 a2={2.011690e+01,0.000000e+00,-1.519300e+01,-4.639750e+00,9.734580e+00,-9.550510e+00,3.333330e-01};
constexpr vec7 a3={7.654440e+00,-2.998000e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
constexpr vec7 a4={6.735780e+00,-4.891000e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,6.820000e-02};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3) + rate_fit(T9,a4);
}
// n14(a,g)f18 assumed to go on to ne20
double n14a_rate(const vec7 &T9){
constexpr vec7 a1={2.153390e+01,0.000000e+00,-3.625040e+01,0.000000e+00,0.000000e+00,-5.000000e+00,-6.666670e-01};
constexpr vec7 a2={1.968380e-01,-5.160340e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
constexpr vec7 a3={1.389950e+01,-1.096560e+01,-5.622700e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3);
}
// n15(p,a)c12 (CNO I)
double n15pa_rate(const vec7 &T9){
constexpr vec7 a1 = {2.747640e+01,0.000000e+00,-1.525300e+01,1.593180e+00,2.447900e+00,-2.197080e+00,-6.666670e-01};
constexpr vec7 a2 = {-4.873470e+00,-2.021170e+00,0.000000e+00,3.084970e+01,-8.504330e+00,-1.544260e+00,-1.500000e+00};
constexpr vec7 a3 = {2.089720e+01,-7.406000e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
constexpr vec7 a4 = {-6.575220e+00,-1.163800e+00,0.000000e+00,2.271050e+01,-2.907070e+00,2.057540e-01,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3) + rate_fit(T9,a4);
}
// n15(p,g)o16 (CNO II)
double n15pg_rate(const vec7 &T9){
constexpr vec7 a1 = {2.001760e+01,0.000000e+00,-1.524000e+01,3.349260e-01,4.590880e+00,-4.784680e+00,-6.666670e-01};
constexpr vec7 a2 = {6.590560e+00,-2.923150e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
constexpr vec7 a3 = {1.454440e+01,-1.022950e+01,0.000000e+00,0.000000e+00,4.590370e-02,0.000000e+00,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3);
}
double n15pg_frac(const vec7 &T9){
const double f1=n15pg_rate(T9);
const double f2=n15pa_rate(T9);
return f1/(f1+f2);
}
// o16(p,g)f17 then f17 -> o17(p,a)n14
double o16p_rate(const vec7 &T9){
constexpr vec7 a={1.909040e+01,0.000000e+00,-1.669600e+01,-1.162520e+00,2.677030e-01,-3.384110e-02,-6.666670e-01};
return rate_fit(T9,a);
}
// o16(a,g)ne20
double o16a_rate(const vec7 &T9){
constexpr vec7 a1={2.390300e+01,0.000000e+00,-3.972620e+01,-2.107990e-01,4.428790e-01,-7.977530e-02,-6.666670e-01};
constexpr vec7 a2={3.885710e+00,-1.035850e+01,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
constexpr vec7 a3={9.508480e+00,-1.276430e+01,0.000000e+00,-3.659250e+00,7.142240e-01,-1.075080e-03,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3);
}
// ne20(a,g)mg24
double ne20a_rate(const vec7 &T9){
constexpr vec7 a1={2.450580e+01,0.000000e+00,-4.625250e+01,5.589010e+00,7.618430e+00,-3.683000e+00,-6.666670e-01};
constexpr vec7 a2={-3.870550e+01,-2.506050e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
constexpr vec7 a3={1.983070e+00,-9.220260e+00,0.000000e+00,0.000000e+00,0.000000e+00,0.000000e+00,-1.500000e+00};
constexpr vec7 a4={-8.798270e+00,-1.278090e+01,0.000000e+00,1.692290e+01,-2.573250e+00,2.089970e-01,-1.500000e+00};
return rate_fit(T9,a1) + rate_fit(T9,a2) + rate_fit(T9,a3) + rate_fit(T9,a4);
}
// c12(c12,a)ne20
double c12c12_rate(const vec7 &T9){
constexpr vec7 a={6.128630e+01,0.000000e+00,-8.416500e+01,-1.566270e+00,-7.360840e-02,-7.279700e-02,-6.666670e-01};
return rate_fit(T9,a);
}
// c12(o16,a)mg24
double c12o16_rate(const vec7 &T9){
constexpr vec7 a={4.853410e+01,3.720400e-01,-1.334130e+02,5.015720e+01,-3.159870e+00,1.782510e-02,-2.370270e+01};
return rate_fit(T9,a);
}
// for Boost ODE solvers either a struct or a class is required
// a Jacobian matrix for implicit solvers
void Jacobian::operator() ( const vector_type &y, matrix_type &J, double /* t */, vector_type &dfdt ) const {
fourdst::constant::Constants& constants = fourdst::constant::Constants::getInstance();
const double avo = constants.get("N_a").value;
const double clight = constants.get("c").value;
// EOS
const vec7 T9=get_T9_array(y[Approx8Net::iTemp]);
// evaluate rates once per call
const double rpp=pp_rate(T9);
const double r33=he3he3_rate(T9);
const double r34=he3he4_rate(T9);
const double r3a=triple_alpha_rate(T9);
const double rc12p=c12p_rate(T9);
const double rc12a=c12a_rate(T9);
const double rn14p=n14p_rate(T9);
const double rn14a=n14a_rate(T9);
const double ro16p=o16p_rate(T9);
const double ro16a=o16a_rate(T9);
const double rne20a=ne20a_rate(T9);
const double r1212=c12c12_rate(T9);
const double r1216=c12o16_rate(T9);
const double pFrac=n15pg_frac(T9);
const double aFrac=1-pFrac;
const double yh1 = y[Approx8Net::ih1];
const double yhe3 = y[Approx8Net::ihe3];
const double yhe4 = y[Approx8Net::ihe4];
const double yc12 = y[Approx8Net::ic12];
const double yn14 = y[Approx8Net::in14];
const double yo16 = y[Approx8Net::io16];
const double yne20 = y[Approx8Net::ine20];
// zero all elements to begin
for (int i=0; i < Approx8Net::nVar; i++) {
for (int j=0; j < Approx8Net::nVar; j++) {
J(i,j)=0.0;
}
}
// h1 jacobian elements
J(Approx8Net::ih1,Approx8Net::ih1) = -3*yh1*rpp - 2*yc12*rc12p -2*yn14*rn14p -2*yo16*ro16p;
J(Approx8Net::ih1,Approx8Net::ihe3) = 2*yhe3*r33 - yhe4*r34;
J(Approx8Net::ih1,Approx8Net::ihe4) = -yhe3*r34;
J(Approx8Net::ih1,Approx8Net::ic12) = -2*yh1*rc12p;
J(Approx8Net::ih1,Approx8Net::in14) = -2*yh1*rn14p;
J(Approx8Net::ih1,Approx8Net::io16) = -2*yh1*ro16p;
// he3 jacobian elements
J(Approx8Net::ihe3,Approx8Net::ih1) = yh1*rpp;
J(Approx8Net::ihe3,Approx8Net::ihe3) = -2*yhe3*r33 - yhe4*r34;
J(Approx8Net::ihe3,Approx8Net::ihe4) = -yhe3*r34;
// he4 jacobian elements
J(Approx8Net::ihe4,Approx8Net::ih1) = yn14*aFrac*rn14p + yo16*ro16p;
J(Approx8Net::ihe4,Approx8Net::ihe3) = yhe3*r33 - yhe4*r34;
J(Approx8Net::ihe4,Approx8Net::ihe4) = yhe3*r34 - 1.5*yhe4*yhe4*r3a - yc12*rc12a - 1.5*yn14*rn14a - yo16*ro16a - yne20*rne20a;
J(Approx8Net::ihe4,Approx8Net::ic12) = -yhe4*rc12a + yc12*r1212 + yo16*r1216;
J(Approx8Net::ihe4,Approx8Net::in14) = yh1*aFrac*rn14p - 1.5*yhe4*rn14a;
J(Approx8Net::ihe4,Approx8Net::io16) = yh1*ro16p - yhe4*ro16a + yc12*r1216;
J(Approx8Net::ihe4,Approx8Net::ine20) = -yhe4*rne20a;
// c12 jacobian elements
J(Approx8Net::ic12,Approx8Net::ih1) = -yc12*rc12p + yn14*aFrac*rn14p;
J(Approx8Net::ic12,Approx8Net::ihe4) = 0.5*yhe4*yhe4*r3a - yhe4*rc12a;
J(Approx8Net::ic12,Approx8Net::ic12) = -yh1*rc12p - yhe4*rc12a - yo16*r1216 - 2*yc12*r1212;
J(Approx8Net::ic12,Approx8Net::in14) = yh1*yn14*aFrac*rn14p;
J(Approx8Net::ic12,Approx8Net::io16) = -yc12*r1216;
// n14 jacobian elements
J(Approx8Net::in14,Approx8Net::ih1) = yc12*rc12p - yn14*rn14p + yo16*ro16p;
J(Approx8Net::in14,Approx8Net::ihe4) = -yn14*rn14a;
J(Approx8Net::in14,Approx8Net::ic12) = yh1*rc12p;
J(Approx8Net::in14,Approx8Net::in14) = -yh1*rn14p - yhe4*rn14a;
J(Approx8Net::in14,Approx8Net::io16) = yo16*ro16p;
// o16 jacobian elements
J(Approx8Net::io16,Approx8Net::ih1) = yn14*pFrac*rn14p - yo16*ro16p;
J(Approx8Net::io16,Approx8Net::ihe4) = yc12*rc12a - yo16*ro16a;
J(Approx8Net::io16,Approx8Net::ic12) = yhe4*rc12a - yo16*r1216;
J(Approx8Net::io16,Approx8Net::in14) = yh1*pFrac*rn14p;
J(Approx8Net::io16,Approx8Net::io16) = yh1*ro16p - yc12*r1216 -yhe4*ro16a;
// ne20 jacobian elements
J(Approx8Net::ine20,Approx8Net::ihe4) = yn14*rn14a + yo16*ro16a - yne20*rne20a;
J(Approx8Net::ine20,Approx8Net::ic12) = yc12*r1212;
J(Approx8Net::ine20,Approx8Net::in14) = yhe4*rn14a;
J(Approx8Net::ine20,Approx8Net::io16) = yo16*ro16a;
J(Approx8Net::ine20,Approx8Net::ine20) = -yhe4*rne20a;
// mg24 jacobian elements
J(Approx8Net::img24,Approx8Net::ihe4) = yne20*rne20a;
J(Approx8Net::img24,Approx8Net::ic12) = yo16*r1216;
J(Approx8Net::img24,Approx8Net::io16) = yc12*r1216;
J(Approx8Net::img24,Approx8Net::ine20) = yhe4*rne20a;
// energy accounting
for (int j=0; j<Approx8Net::nIso; j++) {
for (int i=0; i<Approx8Net::nIso; i++) {
J(Approx8Net::iEnergy,j) += J(i,j)*Approx8Net::mIon[i];
}
J(Approx8Net::iEnergy,j) *= -avo*clight*clight;
}
}
void ODE::operator() ( const vector_type &y, vector_type &dydt, double /* t */) const {
const fourdst::constant::Constants& constants = fourdst::constant::Constants::getInstance();
const double avo = constants.get("N_a").value;
const double clight = constants.get("c").value;
// EOS
const double T = y[Approx8Net::iTemp];
const double den = y[Approx8Net::iDensity];
const vec7 T9=get_T9_array(T);
// rates
const double rpp=den*pp_rate(T9);
const double r33=den*he3he3_rate(T9);
const double r34=den*he3he4_rate(T9);
const double r3a=den*den*triple_alpha_rate(T9);
const double rc12p=den*c12p_rate(T9);
const double rc12a=den*c12a_rate(T9);
const double rn14p=den*n14p_rate(T9);
const double rn14a=n14a_rate(T9);
const double ro16p=den*o16p_rate(T9);
const double ro16a=den*o16a_rate(T9);
const double rne20a=den*ne20a_rate(T9);
const double r1212=den*c12c12_rate(T9);
const double r1216=den*c12o16_rate(T9);
const double pFrac=n15pg_frac(T9);
const double aFrac=1-pFrac;
const double yh1 = y[Approx8Net:: ih1];
const double yhe3 = y[Approx8Net:: ihe3];
const double yhe4 = y[Approx8Net:: ihe4];
const double yc12 = y[Approx8Net:: ic12];
const double yn14 = y[Approx8Net:: in14];
const double yo16 = y[Approx8Net:: io16];
const double yne20 = y[Approx8Net::ine20];
dydt[Approx8Net::ih1] = -1.5*yh1*yh1*rpp;
dydt[Approx8Net::ih1] += yhe3*yhe3*r33;
dydt[Approx8Net::ih1] += -yhe3*yhe4*r34;
dydt[Approx8Net::ih1] += -2*yh1*yc12*rc12p;
dydt[Approx8Net::ih1] += -2*yh1*yn14*rn14p;
dydt[Approx8Net::ih1] += -2*yh1*yo16*ro16p;
dydt[Approx8Net::ihe3] = 0.5*yh1*yh1*rpp;
dydt[Approx8Net::ihe3] += -yhe3*yhe3*r33;
dydt[Approx8Net::ihe3] += -yhe3*yhe4*r34;
dydt[Approx8Net::ihe4] = 0.5*yhe3*yhe3*r33;
dydt[Approx8Net::ihe4] += yhe3*yhe4*r34;
dydt[Approx8Net::ihe4] += -yhe4*yc12*rc12a;
dydt[Approx8Net::ihe4] += yh1*yn14*aFrac*rn14p;
dydt[Approx8Net::ihe4] += yh1*yo16*ro16p;
dydt[Approx8Net::ihe4] += -0.5*yhe4*yhe4*yhe4*r3a;
dydt[Approx8Net::ihe4] += -yhe4*yo16*ro16a;
dydt[Approx8Net::ihe4] += 0.5*yc12*yc12*r1212;
dydt[Approx8Net::ihe4] += yc12*yo16*r1216;
dydt[Approx8Net::ihe4] += -yhe4*yne20*rne20a;
dydt[Approx8Net::ic12] = (1./6.)*yhe4*yhe4*yhe4*r3a;
dydt[Approx8Net::ic12] += -yhe4*yc12*rc12a;
dydt[Approx8Net::ic12] += -yh1*yc12*rc12p;
dydt[Approx8Net::ic12] += yh1*yn14*aFrac*rn14p;
dydt[Approx8Net::ic12] += -yc12*yc12*r1212;
dydt[Approx8Net::ic12] += -yc12*yo16*r1216;
dydt[Approx8Net::in14] = yh1*yc12*rc12p;
dydt[Approx8Net::in14] += -yh1*yn14*rn14p;
dydt[Approx8Net::in14] += yh1*yo16*ro16p;
dydt[Approx8Net::in14] += -yhe4*yn14*rn14a;
dydt[Approx8Net::io16] = yhe4*yc12*rc12a;
dydt[Approx8Net::io16] += yh1*yn14*pFrac*rn14p;
dydt[Approx8Net::io16] += -yh1*yo16*ro16p;
dydt[Approx8Net::io16] += -yc12*yo16*r1216;
dydt[Approx8Net::io16] += -yhe4*yo16*ro16a;
dydt[Approx8Net::ine20] = 0.5*yc12*yc12*r1212;
dydt[Approx8Net::ine20] += yhe4*yn14*rn14a;
dydt[Approx8Net::ine20] += yhe4*yo16*ro16a;
dydt[Approx8Net::ine20] += -yhe4*yne20*rne20a;
dydt[Approx8Net::img24] = yc12*yo16*r1216;
dydt[Approx8Net::img24] += yhe4*yne20*rne20a;
dydt[Approx8Net::iTemp] = 0.;
dydt[Approx8Net::iDensity] = 0.;
// energy accounting
double eNuc = 0.;
for (int i=0; i<Approx8Net::nIso; i++) {
eNuc += Approx8Net::mIon[i]*dydt[i];
}
dydt[Approx8Net::iEnergy] = -eNuc*avo*clight*clight;
}
Approx8Network::Approx8Network() : Network(APPROX8) {}
NetOut Approx8Network::evaluate(const NetIn &netIn) {
m_y = convert_netIn(netIn);
m_tMax = netIn.tMax;
m_dt0 = netIn.dt0;
const auto stiff_abs_tol = m_config.get<double>("Network:Approx8:Stiff:AbsTol", 1.0e-6);
const auto stiff_rel_tol = m_config.get<double>("Network:Approx8:Stiff:RelTol", 1.0e-6);
const auto nonstiff_abs_tol = m_config.get<double>("Network:Approx8:NonStiff:AbsTol", 1.0e-6);
const auto nonstiff_rel_tol = m_config.get<double>("Network:Approx8:NonStiff:RelTol", 1.0e-6);
int num_steps = -1;
if (m_stiff) {
LOG_DEBUG(m_logger, "Using stiff solver for Approx8Network");
num_steps = integrate_const(
make_dense_output<rosenbrock4<double>>(stiff_abs_tol, stiff_rel_tol),
std::make_pair(ODE(), Jacobian()),
m_y,
0.0,
m_tMax,
m_dt0
);
} else {
LOG_DEBUG(m_logger, "Using non stiff solver for Approx8Network");
num_steps = integrate_const (
make_dense_output<runge_kutta_dopri5<vector_type>>(nonstiff_abs_tol, nonstiff_rel_tol),
ODE(),
m_y,
0.0,
m_tMax,
m_dt0
);
}
double ySum = 0.0;
for (int i = 0; i < Approx8Net::nIso; i++) {
m_y[i] *= Approx8Net::aIon[i];
ySum += m_y[i];
}
for (int i = 0; i < Approx8Net::nIso; i++) {
m_y[i] /= ySum;
}
NetOut netOut;
std::vector<double> outComposition;
outComposition.reserve(Approx8Net::nVar);
for (int i = 0; i < Approx8Net::nIso; i++) {
outComposition.push_back(m_y[i]);
}
netOut.energy = m_y[Approx8Net::iEnergy];
netOut.num_steps = num_steps;
const std::vector<std::string> symbols = {"H-1", "He-3", "He-4", "C-12", "N-14", "O-16", "Ne-20", "Mg-24"};
netOut.composition = fourdst::composition::Composition(symbols, outComposition);
return netOut;
}
void Approx8Network::setStiff(bool stiff) {
m_stiff = stiff;
}
vector_type Approx8Network::convert_netIn(const NetIn &netIn) {
vector_type y(Approx8Net::nVar, 0.0);
y[Approx8Net::ih1] = netIn.composition.getNumberFraction("H-1");
y[Approx8Net::ihe3] = netIn.composition.getNumberFraction("He-3");
y[Approx8Net::ihe4] = netIn.composition.getNumberFraction("He-4");
y[Approx8Net::ic12] = netIn.composition.getNumberFraction("C-12");
y[Approx8Net::in14] = netIn.composition.getNumberFraction("N-14");
y[Approx8Net::io16] = netIn.composition.getNumberFraction("O-16");
y[Approx8Net::ine20] = netIn.composition.getNumberFraction("Ne-20");
y[Approx8Net::img24] = netIn.composition.getNumberFraction("Mg-24");
y[Approx8Net::iTemp] = netIn.temperature;
y[Approx8Net::iDensity] = netIn.density;
y[Approx8Net::iEnergy] = netIn.energy;
return y;
}
};
// main program

View File

@@ -8,8 +8,8 @@
#include "gridfire/utils/hashing.h"
#include "gridfire/utils/table_format.h"
#include "fourdst/composition/species.h"
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/atomic/species.h"
#include "fourdst/atomic/atomicSpecies.h"
#include "quill/LogMacros.h"
@@ -66,7 +66,7 @@ namespace gridfire {
}
std::expected<StepDerivatives<double>, expectations::StaleEngineError> GraphEngine::calculateRHSAndEnergy(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -74,7 +74,7 @@ namespace gridfire {
}
std::expected<StepDerivatives<double>, expectations::StaleEngineError> GraphEngine::calculateRHSAndEnergy(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const reaction::ReactionSet &activeReactions
@@ -119,7 +119,7 @@ namespace gridfire {
}
EnergyDerivatives GraphEngine::calculateEpsDerivatives(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -127,7 +127,7 @@ namespace gridfire {
}
EnergyDerivatives GraphEngine::calculateEpsDerivatives(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const reaction::ReactionSet &activeReactions
@@ -357,7 +357,7 @@ namespace gridfire {
const reaction::Reaction &reaction,
const double T9,
const double rho,
const fourdst::composition::Composition &comp
const fourdst::composition::CompositionAbstract &comp
) const {
if (!m_useReverseReactions) {
LOG_TRACE_L3_LIMIT_EVERY_N(std::numeric_limits<int>::max(), m_logger, "Reverse reactions are disabled. Returning 0.0 for reverse rate of reaction '{}'.", reaction.id());
@@ -534,8 +534,8 @@ namespace gridfire {
std::vector<double> GraphEngine::mapNetInToMolarAbundanceVector(const NetIn &netIn) const {
std::vector<double> Y(m_networkSpecies.size(), 0.0); // Initialize with zeros
for (const auto& [symbol, entry] : netIn.composition) {
Y[getSpeciesIndex(entry.isotope())] = netIn.composition.getMolarAbundance(symbol); // Map species to their molar abundance
for (const auto& [sp, y] : netIn.composition) {
Y[getSpeciesIndex(sp)] = y; // Map species to their molar abundance
}
return Y; // Return the vector of molar abundances
}
@@ -544,24 +544,13 @@ namespace gridfire {
NetIn fullNetIn;
fourdst::composition::Composition composition;
std::vector<std::string> symbols;
symbols.reserve(m_networkSpecies.size());
for (const auto &symbol: m_networkSpecies) {
symbols.emplace_back(symbol.name());
}
composition.registerSymbol(symbols);
for (const auto& [symbol, entry] : netIn.composition) {
if (m_networkSpeciesMap.contains(symbol)) {
composition.setMassFraction(symbol, entry.mass_fraction());
} else {
composition.setMassFraction(symbol, 0.0);
for (const auto& sp : m_networkSpecies) {
composition.registerSpecies(sp);
if (netIn.composition.contains(sp)) {
composition.setMolarAbundance(sp, netIn.composition.getMolarAbundance(sp));
}
}
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;
@@ -580,7 +569,7 @@ namespace gridfire {
return m_depth;
}
void GraphEngine::rebuild(const fourdst::composition::Composition& comp, const BuildDepthType depth) {
void GraphEngine::rebuild(const fourdst::composition::CompositionAbstract &comp, const BuildDepthType depth) {
if (depth != m_depth) {
m_depth = depth;
m_reactions = build_nuclear_network(comp, m_weakRateInterpolator, m_depth);
@@ -592,27 +581,25 @@ namespace gridfire {
}
fourdst::composition::Composition GraphEngine::collectComposition(
fourdst::composition::Composition &comp
fourdst::composition::CompositionAbstract &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");
for (const auto &species: comp.getRegisteredSpecies()) {
if (!m_networkSpeciesMap.contains(species.name())) {
throw exceptions::BadCollectionError("Cannot collect composition from GraphEngine as " + std::string(species.name()) + " 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);
if (comp.contains(species)) {
result.setMolarAbundance(species, comp.getMolarAbundance(species));
}
}
return result;
}
StepDerivatives<double> GraphEngine::calculateAllDerivativesUsingPrecomputation(
const fourdst::composition::Composition& comp,
const fourdst::composition::CompositionAbstract &comp,
const std::vector<double> &bare_rates,
const std::vector<double> &bare_reverse_rates,
const double T9,
@@ -646,6 +633,10 @@ namespace gridfire {
const fourdst::atomic::Species& reactant = m_networkSpecies[reactantIndex];
const int power = precomputedReaction.reactant_powers[i];
if (!comp.contains(reactant)) {
forwardAbundanceProduct = 0.0;
break; // No need to continue if one of the reactants has zero abundance
}
forwardAbundanceProduct *= std::pow(comp.getMolarAbundance(reactant), power);
}
@@ -787,7 +778,7 @@ namespace gridfire {
double GraphEngine::calculateMolarReactionFlow(
const reaction::Reaction &reaction,
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -811,22 +802,17 @@ namespace gridfire {
}
void GraphEngine::generateJacobianMatrix(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
fourdst::composition::Composition mutableComp = comp;
fourdst::composition::Composition mutableComp;
for (const auto& species : m_networkSpecies) {
if (!comp.hasSpecies(species)) {
mutableComp.registerSpecies(species);
mutableComp.setMassFraction(species, 0.0);
mutableComp.registerSpecies(species);
if (comp.contains(species)) {
mutableComp.setMolarAbundance(species, comp.getMolarAbundance(species));
}
}
const bool didFinalize = mutableComp.finalize(false);
if (!didFinalize) {
LOG_CRITICAL(m_logger, "Could not finalize the composition used to generate the jacobian matrix!");
throw std::runtime_error("Could not finalize the composition used to generate the jacobian matrix");
}
LOG_TRACE_L1_LIMIT_EVERY_N(1000, m_logger, "Generating jacobian matrix for T9={}, rho={}..", T9, rho);
const size_t numSpecies = m_networkSpecies.size();
@@ -863,7 +849,7 @@ namespace gridfire {
}
void GraphEngine::generateJacobianMatrix(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const std::vector<fourdst::atomic::Species> &activeSpecies
@@ -895,7 +881,7 @@ namespace gridfire {
}
void GraphEngine::generateJacobianMatrix(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const SparsityPattern &sparsityPattern
@@ -1102,7 +1088,7 @@ namespace gridfire {
}
std::expected<std::unordered_map<fourdst::atomic::Species, double>, expectations::StaleEngineError> GraphEngine::getSpeciesTimescales(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -1110,7 +1096,7 @@ namespace gridfire {
}
std::expected<std::unordered_map<fourdst::atomic::Species, double>, expectations::StaleEngineError> GraphEngine::getSpeciesTimescales(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const reaction::ReactionSet &activeReactions
@@ -1146,7 +1132,7 @@ namespace gridfire {
}
std::expected<std::unordered_map<fourdst::atomic::Species, double>, expectations::StaleEngineError> GraphEngine::getSpeciesDestructionTimescales(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -1154,7 +1140,7 @@ namespace gridfire {
}
std::expected<std::unordered_map<fourdst::atomic::Species, double>, expectations::StaleEngineError> GraphEngine::getSpeciesDestructionTimescales(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const reaction::ReactionSet &activeReactions
@@ -1213,14 +1199,8 @@ namespace gridfire {
for (const auto& species : m_networkSpecies) {
if (!netIn.composition.contains(species)) {
baseUpdatedComposition.registerSpecies(species);
baseUpdatedComposition.setMassFraction(species, 0.0);
}
}
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;
}
@@ -1403,7 +1383,8 @@ namespace gridfire {
// We can pass a dummy comp and rho because reverse rates should only be calculated for strong reactions whose
// rates of progression do not depend on composition or density.
const double reverseRate = m_engine.calculateReverseRate(m_reaction, T9, 0.0, {});
const fourdst::composition::Composition dummyComp;
const double reverseRate = m_engine.calculateReverseRate(m_reaction, T9, 0.0, dummyComp);
ty[0] = reverseRate; // Store the reverse rate in the output vector
if (vx.size() > 0) {

View File

@@ -6,12 +6,12 @@
#include <ranges>
#include <stdexcept>
#include <memory>
#include <cmath>
#include "fourdst/atomic/species.h"
#include "gridfire/reaction/reaction.h"
#include "gridfire/reaction/reaclib.h"
#include "fourdst/composition/composition.h"
#include "fourdst/composition/composition_abstract.h"
#include "fourdst/logging/logging.h"
@@ -146,11 +146,10 @@ namespace {
namespace gridfire {
using reaction::ReactionSet;
using reaction::Reaction;
using fourdst::composition::Composition;
using fourdst::atomic::Species;
ReactionSet build_nuclear_network(
const Composition& composition,
const fourdst::composition::CompositionAbstract &composition,
const rates::weak::WeakRateInterpolator &weakInterpolator,
BuildDepthType maxLayers,
NetworkConstructionFlags ReactionTypes
@@ -202,9 +201,9 @@ namespace gridfire {
// --- Step 3: Execute the layered network build using observing pointers ---
std::unordered_set<Species> availableSpecies;
for (const auto& entry : composition | std::views::values) {
if (entry.mass_fraction() > 0.0) {
availableSpecies.insert(entry.isotope());
for (const auto& sp : composition.getRegisteredSpecies()) {
if (composition.getMolarAbundance(sp) > 0.0) {
availableSpecies.insert(sp);
}
}

View File

@@ -1,4 +1,7 @@
#include "gridfire/engine/procedures/priming.h"
#include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h"
#include "gridfire/engine/views/engine_priming.h"
#include "gridfire/engine/procedures/construction.h"
#include "gridfire/solver/solver.h"
@@ -11,30 +14,6 @@
#include "quill/LogMacros.h"
namespace {
// Create a dummy wrapper Composition to measure the unrestricted flow rate of species
class UnrestrictedComposition final : public fourdst::composition::Composition {
private:
const fourdst::atomic::Species& m_unrestrictedSpecies;
public:
explicit UnrestrictedComposition(const Composition& baseComposition, const fourdst::atomic::Species& unrestrictedSpecies) :
Composition(baseComposition),
m_unrestrictedSpecies(unrestrictedSpecies) {}
double getMolarAbundance(const fourdst::atomic::Species &species) const override {
if (species == m_unrestrictedSpecies) {
return 1.0; // Set to a high value to simulate unrestricted abundance
}
return Composition::getMolarAbundance(species);
}
double getMolarAbundance(const std::string &symbol) const override {
if (symbol == m_unrestrictedSpecies.name()) {
return 1.0; // Set to a high value to simulate unrestricted abundance
}
return Composition::getMolarAbundance(symbol);
}
};
bool isReactionIgnorable(
const gridfire::reaction::Reaction& reaction,
const std::optional<std::vector<gridfire::reaction::ReactionType>>& reactionsTypesToIgnore
@@ -117,18 +96,18 @@ namespace gridfire {
GraphEngine& engine,
const std::optional<std::vector<reaction::ReactionType>>& ignoredReactionTypes
) {
auto logger = fourdst::logging::LogManager::getInstance().getLogger("log");
auto logger = LogManager::getInstance().getLogger("log");
// --- Initial Setup ---
// Identify all species with zero initial mass fraction that need to be primed.
// Identify all species with zero initial abundance that need to be primed.
std::vector<Species> speciesToPrime;
for (const auto &entry: netIn.composition | std::views::values) {
if (entry.mass_fraction() == 0.0) {
speciesToPrime.push_back(entry.isotope());
for (const auto &[sp, y]: netIn.composition) {
if (y == 0.0) {
speciesToPrime.push_back(sp);
}
}
// sort primingSpecies by mass number, lightest to heaviest. This ensures we prime in a physically sensible order.
// Sort priming species by mass number, lightest to heaviest.
std::ranges::sort(speciesToPrime, [](const Species& a, const Species& b) {
return a.mass() < b.mass();
});
@@ -147,46 +126,37 @@ namespace gridfire {
const double T9 = netIn.temperature / 1e9;
const double rho = netIn.density;
const auto initialReactionSet = engine.getNetworkReactions();
report.status = PrimingReportStatus::FULL_SUCCESS;
report.success = true;
// Create a mutable map of the mass fractions that we will modify.
std::unordered_map<Species, double> currentMassFractions;
for (const auto& entry : netIn.composition | std::views::values) {
currentMassFractions[entry.isotope()] = entry.mass_fraction();
// Build full set of species
std::set<Species> allSpecies;
for (const auto &sp: netIn.composition | std::views::keys) {
allSpecies.insert(sp);
}
// Ensure all species to be primed exist in the map, initialized to zero.
for (const auto& entry : speciesToPrime) {
currentMassFractions[entry] = 0.0;
for (const auto& sp : speciesToPrime) {
allSpecies.insert(sp);
}
// Rebuild the engine with the full network to ensure all possible creation channels are available.
engine.rebuild(netIn.composition, NetworkBuildDepth::Full);
// --- STAGE 1: Calculation and Bookkeeping (No Modifications) ---
// In this stage, we calculate all required mass transfers but do not apply them yet.
// Initialize mutable molar abundances for all species
std::map<Species, double> molarAbundances;
for (const auto& sp : allSpecies) {
molarAbundances[sp] = netIn.composition.contains(sp) ? netIn.composition.getMolarAbundance(sp) : 0.0;
}
// A struct to hold the result of each individual priming calculation.
struct MassTransferRequest {
Species species_to_prime;
double equilibrium_mass_fraction;
std::vector<Species> reactants;
};
std::vector<MassTransferRequest> requests;
// --- Prime Each Species ---
// Since molar abundances are independent, we can directly calculate and apply changes
std::unordered_map<Species, double> totalMolarAbundanceChanges;
for (const auto& primingSpecies : speciesToPrime) {
// Create a temporary composition reflecting the current state for rate calculations.
Composition tempComp;
for(const auto& [sp, mf] : currentMassFractions) {
tempComp.registerSymbol(std::string(sp.name()));
tempComp.setMassFraction(sp, std::max(0.0, mf));
}
bool didFinalize = tempComp.finalize(true);
if (!didFinalize) {
LOG_ERROR(logger, "Failed to finalize temporary composition during priming.");
report.success = false;
report.status = PrimingReportStatus::FAILED_TO_FINALIZE_COMPOSITION;
continue;
Composition tempComp(allSpecies);
for (const auto& [sp, abundance] : molarAbundances) {
tempComp.setMolarAbundance(sp, abundance);
}
NetworkPrimingEngineView primer(primingSpecies, engine);
@@ -206,6 +176,9 @@ namespace gridfire {
ignoredReactionTypes
);
double equilibriumMolarAbundance = 0.0;
std::vector<Species> reactants;
if (destructionRateConstant > 1e-99) {
const double creationRate = calculateCreationRate(
primer,
@@ -215,10 +188,13 @@ namespace gridfire {
rho,
ignoredReactionTypes
);
double equilibriumMassFraction = (creationRate / destructionRateConstant) * primingSpecies.mass();
// ReSharper disable once CppDFAUnusedValue
if (std::isnan(equilibriumMassFraction)) equilibriumMassFraction = 0.0;
// Equilibrium: creation rate = destruction rate constant * molar abundance
equilibriumMolarAbundance = creationRate / destructionRateConstant;
if (std::isnan(equilibriumMolarAbundance)) {
equilibriumMolarAbundance = 0.0;
}
if (const reaction::Reaction* dominantChannel = findDominantCreationChannel(
primer,
@@ -228,102 +204,74 @@ namespace gridfire {
rho,
ignoredReactionTypes)
) {
// Store the request instead of applying it immediately.
requests.push_back({primingSpecies, equilibriumMassFraction, dominantChannel->reactants()});
reactants = dominantChannel->reactants();
} else {
LOG_TRACE_L1(logger, "Failed to find dominant creation channel for {}.", primingSpecies.name());
report.status = PrimingReportStatus::FAILED_TO_FIND_CREATION_CHANNEL;
reactants.clear(); // Use fallback
}
} else {
LOG_TRACE_L2(logger, "No destruction channel found for {}. Using fallback abundance.", primingSpecies.name());
// For species with no destruction, we can't calculate an equilibrium.
// We add a request with a tiny fallback abundance to ensure it exists in the network.
requests.push_back({primingSpecies, 1e-40, {}});
// For species with no destruction, use a tiny fallback abundance
equilibriumMolarAbundance = 1e-40;
}
}
// --- STAGE 2: Collective Scaling Based on Reactant Availability ---
// Now, we determine the total demand for each reactant and find a global scaling factor.
// Add the equilibrium molar abundance to the primed species
molarAbundances.at(primingSpecies) += equilibriumMolarAbundance;
totalMolarAbundanceChanges[primingSpecies] += equilibriumMolarAbundance;
std::unordered_map<Species, double> total_mass_debits;
for (const auto& req : requests) {
if (req.reactants.empty()) continue; // Skip fallbacks which don't consume reactants.
double totalReactantMass = 0.0;
for (const auto& reactant : req.reactants) {
totalReactantMass += reactant.mass();
}
if (totalReactantMass == 0.0) continue;
for (const auto& reactant : req.reactants) {
const double massToSubtract = req.equilibrium_mass_fraction * (reactant.mass() / totalReactantMass);
total_mass_debits[reactant] += massToSubtract;
}
}
double globalScalingFactor = 1.0;
for (const auto& [reactant, total_debit] : total_mass_debits) {
double availableMass;
if (currentMassFractions.contains(reactant)) {
availableMass = currentMassFractions.at(reactant);
} else {
availableMass = 0.0;
}
if (total_debit > availableMass && availableMass > 0) {
globalScalingFactor = std::min(globalScalingFactor, availableMass / total_debit);
}
}
if (globalScalingFactor < 1.0) {
LOG_WARNING(logger, "Priming was limited by reactant availability. All transfers will be scaled by {:.4e}", globalScalingFactor);
}
// --- STAGE 3: Application of Scaled Mass Transfers ---
// Finally, apply all the transfers, scaled by our global factor.
std::unordered_map<Species, double> totalMassFractionChanges;
for (const auto&[species_to_prime, equilibrium_mass_fraction, reactants] : requests) {
const double scaled_equilibrium_mf = equilibrium_mass_fraction * globalScalingFactor;
// Add the scaled mass to the primed species.
currentMassFractions.at(species_to_prime) += scaled_equilibrium_mf;
totalMassFractionChanges[species_to_prime] += scaled_equilibrium_mf;
// Subtract the scaled mass from the reactants.
// Subtract from reactants proportionally to their stoichiometry
if (!reactants.empty()) {
double totalReactantMass = 0.0;
for (const auto& reactant : reactants) {
totalReactantMass += reactant.mass();
}
if (totalReactantMass == 0.0) continue;
const double totalStoichiometry = static_cast<double>(reactants.size());
const double abundancePerReactant = equilibriumMolarAbundance / totalStoichiometry;
for (const auto& reactant : reactants) {
const double massToSubtract = scaled_equilibrium_mf * (reactant.mass() / totalReactantMass);
if (massToSubtract != 0) {
currentMassFractions.at(reactant) -= massToSubtract;
totalMassFractionChanges[reactant] -= massToSubtract;
LOG_TRACE_L1(logger, "Transferring {:.4e} molar abundance from {} to {} during priming.",
abundancePerReactant, reactant.name(), primingSpecies.name());
if (!molarAbundances.contains(reactant)) {
continue;
}
molarAbundances.at(reactant) -= abundancePerReactant;
totalMolarAbundanceChanges[reactant] -= abundancePerReactant;
// Ensure non-negative abundances
if (molarAbundances.at(reactant) < 0.0) {
LOG_WARNING(logger, "Species {} went negative during priming. Clamping to zero.", reactant.name());
totalMolarAbundanceChanges[reactant] += molarAbundances.at(reactant); // Adjust change to reflect clamp
molarAbundances.at(reactant) = 0.0;
}
}
}
}
// --- Final Composition Construction ---
std::vector<std::string> final_symbols;
std::vector<double> final_mass_fractions;
for(const auto& [species, mass_fraction] : currentMassFractions) {
final_symbols.emplace_back(species.name());
final_mass_fractions.push_back(std::max(0.0, mass_fraction)); // Ensure no negative mass fractions.
std::vector<Species> final_species;
std::vector<double> final_molar_abundances;
for (const auto& [species, abundance] : molarAbundances) {
final_species.emplace_back(species);
final_molar_abundances.push_back(std::max(0.0, abundance));
LOG_TRACE_L1(logger, "After priming, species {} has molar abundance {:.4e} (had {:0.4e} prior to priming).",
species.name(),
std::max(0.0, abundance),
netIn.composition.getMolarAbundance(species));
}
Composition primedComposition(final_symbols, final_mass_fractions, true);
Composition primedComposition(final_species, final_molar_abundances);
report.primedComposition = primedComposition;
for (const auto& [species, change] : totalMassFractionChanges) {
report.massFractionChanges.emplace_back(species, change);
// Convert molar abundance changes to mass fraction changes for reporting
for (const auto& [species, molarChange] : totalMolarAbundanceChanges) {
double massFractionChange = molarChange * species.mass();
report.massFractionChanges.emplace_back(species, massFractionChange);
}
// Restore the engine to its original, smaller network state.
engine.setNetworkReactions(initialReactionSet);
return report;
}
@@ -335,7 +283,8 @@ namespace gridfire {
const double rho,
const std::optional<std::vector<reaction::ReactionType>> &reactionTypesToIgnore
) {
const UnrestrictedComposition unrestrictedComp(composition, species); // Create a composition that simulates an enormous source abundance of the target species (getMolarAbundance(species) always returns 1.0)
Composition unrestrictedComp(composition);
unrestrictedComp.setMolarAbundance(species, 1.0);
double destructionRateConstant = 0.0;
for (const auto& reaction: engine.getNetworkReactions()) {
if (isReactionIgnorable(*reaction, reactionTypesToIgnore)) continue;

View File

@@ -91,12 +91,6 @@ namespace gridfire {
updatedNetIn.composition = baseUpdatedComposition;
bool didFinalize = updatedNetIn.composition.finalize(false);
if (!didFinalize) {
LOG_ERROR(m_logger, "Failed to finalize composition during adaptive engine view update. Check input mass fractions for validity.");
throw std::runtime_error("Failed to finalize composition during adaptive engine view update.");
}
LOG_TRACE_L1(m_logger, "Updating AdaptiveEngineView with new network input...");
auto [allFlows, composition] = calculateAllReactionFlows(updatedNetIn);
@@ -148,7 +142,7 @@ namespace gridfire {
}
std::expected<StepDerivatives<double>, expectations::StaleEngineError> AdaptiveEngineView::calculateRHSAndEnergy(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -164,7 +158,7 @@ namespace gridfire {
}
EnergyDerivatives AdaptiveEngineView::calculateEpsDerivatives(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -173,7 +167,7 @@ namespace gridfire {
}
void AdaptiveEngineView::generateJacobianMatrix(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -181,7 +175,7 @@ namespace gridfire {
}
void AdaptiveEngineView::generateJacobianMatrix(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const std::vector<Species> &activeSpecies
@@ -192,7 +186,7 @@ namespace gridfire {
}
void AdaptiveEngineView::generateJacobianMatrix(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const SparsityPattern &sparsityPattern
@@ -225,7 +219,7 @@ namespace gridfire {
double AdaptiveEngineView::calculateMolarReactionFlow(
const reaction::Reaction &reaction,
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -249,7 +243,7 @@ namespace gridfire {
}
std::expected<std::unordered_map<Species, double>, expectations::StaleEngineError> AdaptiveEngineView::getSpeciesTimescales(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -276,7 +270,7 @@ namespace gridfire {
std::expected<std::unordered_map<Species, double>, expectations::StaleEngineError>
AdaptiveEngineView::getSpeciesDestructionTimescales(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -308,8 +302,8 @@ namespace gridfire {
std::vector<double> AdaptiveEngineView::mapNetInToMolarAbundanceVector(const NetIn &netIn) const {
std::vector<double> Y(m_activeSpecies.size(), 0.0); // Initialize with zeros
for (const auto& [symbol, entry] : netIn.composition) {
Y[getSpeciesIndex(entry.isotope())] = netIn.composition.getMolarAbundance(symbol); // Map species to their molar abundance
for (const auto& [species, y] : netIn.composition) {
Y[getSpeciesIndex(species)] = y; // Map species to their molar abundance
}
return Y; // Return the vector of molar abundances
}
@@ -319,14 +313,13 @@ namespace gridfire {
}
fourdst::composition::Composition AdaptiveEngineView::collectComposition(
fourdst::composition::Composition &comp
fourdst::composition::CompositionAbstract &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)) {
if (!result.contains(species)) {
result.registerSpecies(species);
result.setMassFraction(species, 0.0);
}
}
@@ -396,9 +389,8 @@ namespace gridfire {
for (const auto& species: fullSpeciesList) {
if (!netIn.composition.contains(species)) {
LOG_TRACE_L2(m_logger, "Species '{}' not found in composition. Setting abundance to 0.0.", species.name());
LOG_TRACE_L2(m_logger, "Species '{}' not found in composition. Registering", species.name());
composition.registerSpecies(species);
composition.setMassFraction(species, 0.0);
}
}
@@ -411,7 +403,7 @@ namespace gridfire {
for (const auto& reaction : fullReactionSet) {
const double flow = m_baseEngine.calculateMolarReactionFlow(*reaction, composition, T9, rho);
reactionFlows.push_back({reaction.get(), flow});
LOG_TRACE_L1(m_logger, "Reaction '{}' has flow rate: {:0.3E} [mol/s/g]", reaction->id(), flow);
LOG_TRACE_L3(m_logger, "Reaction '{}' has flow rate: {:0.3E} [mol/s/g]", reaction->id(), flow);
}
return {reactionFlows, composition};
}
@@ -586,13 +578,13 @@ namespace gridfire {
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 &entry: comp | std::views::values) {
speciesMassMap[entry.isotope()] = entry.isotope().mass();
for (const auto &sp: comp | std::views::keys) {
speciesMassMap[sp] = sp.mass();
}
std::unordered_map<size_t, Species> speciesIndexMap;
for (const auto& entry: comp | std::views::values) {
size_t distance = std::distance(speciesMassMap.begin(), speciesMassMap.find(entry.isotope()));
speciesIndexMap.emplace(distance, entry.isotope());
for (const auto& sp: comp | std::views::keys) {
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);
if (rate > maxSpeciesConsumptionRate) {

View File

@@ -1,7 +1,9 @@
#include "gridfire/engine/views/engine_defined.h"
#include "gridfire/engine/engine_graph.h"
#include <ranges>
#include "fourdst/atomic/species.h"
#include "fourdst/atomic/atomicSpecies.h"
#include "fourdst/composition/decorators/composition_masked.h"
#include "quill/LogMacros.h"
@@ -13,20 +15,7 @@
#include <unordered_map>
#include <utility>
namespace {
class MaskedComposition final : public fourdst::composition::Composition {
private:
std::set<fourdst::atomic::Species> m_activeSpecies;
public:
MaskedComposition(const Composition& baseComposition, const std::set<fourdst::atomic::Species>& activeSpecies) :
Composition(baseComposition),
m_activeSpecies(activeSpecies) {}
bool contains(const fourdst::atomic::Species& species) const override {
return Composition::contains(species) && m_activeSpecies.contains(species);
}
};
}
#include "fourdst/composition/exceptions/exceptions_composition.h"
namespace gridfire {
using fourdst::atomic::Species;
@@ -52,13 +41,13 @@ namespace gridfire {
}
std::expected<StepDerivatives<double>, expectations::StaleEngineError> DefinedEngineView::calculateRHSAndEnergy(
const fourdst::composition::Composition& comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
validateNetworkState();
const MaskedComposition masked(comp, m_activeSpecies);
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies);
const auto result = m_baseEngine.calculateRHSAndEnergy(masked, T9, rho, m_activeReactions);
if (!result) {
@@ -69,19 +58,19 @@ namespace gridfire {
}
EnergyDerivatives DefinedEngineView::calculateEpsDerivatives(
const fourdst::composition::Composition& comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
validateNetworkState();
const MaskedComposition masked(comp, m_activeSpecies);
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies);
return m_baseEngine.calculateEpsDerivatives(masked, T9, rho, m_activeReactions);
}
void DefinedEngineView::generateJacobianMatrix(
const fourdst::composition::Composition& comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -89,12 +78,12 @@ namespace gridfire {
if (!m_activeSpeciesVectorCache.has_value()) {
m_activeSpeciesVectorCache = std::vector<Species>(m_activeSpecies.begin(), m_activeSpecies.end());
}
const MaskedComposition masked(comp, m_activeSpecies);
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies);
m_baseEngine.generateJacobianMatrix(masked, T9, rho, m_activeSpeciesVectorCache.value());
}
void DefinedEngineView::generateJacobianMatrix(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const std::vector<fourdst::atomic::Species> &activeSpecies
@@ -106,18 +95,18 @@ namespace gridfire {
activeSpecies.end()
);
const MaskedComposition masked(comp, activeSpeciesSet);
const fourdst::composition::MaskedComposition masked(comp, activeSpeciesSet);
m_baseEngine.generateJacobianMatrix(masked, T9, rho, activeSpecies);
}
void DefinedEngineView::generateJacobianMatrix(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const SparsityPattern &sparsityPattern
) const {
validateNetworkState();
const MaskedComposition masked(comp, m_activeSpecies);
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies);
m_baseEngine.generateJacobianMatrix(masked, T9, rho, sparsityPattern);
}
@@ -170,7 +159,7 @@ namespace gridfire {
double DefinedEngineView::calculateMolarReactionFlow(
const reaction::Reaction &reaction,
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -182,7 +171,7 @@ namespace gridfire {
throw std::runtime_error("Reaction not found in active reactions: " + std::string(reaction.id()));
}
const MaskedComposition masked(comp, m_activeSpecies);
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies);
return m_baseEngine.calculateMolarReactionFlow(reaction, masked, T9, rho);
}
@@ -202,12 +191,12 @@ namespace gridfire {
}
std::expected<std::unordered_map<Species, double>, expectations::StaleEngineError> DefinedEngineView::getSpeciesTimescales(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
validateNetworkState();
const MaskedComposition masked(comp, m_activeSpecies);
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies);
const auto result = m_baseEngine.getSpeciesTimescales(masked, T9, rho, m_activeReactions);
if (!result) {
@@ -225,12 +214,12 @@ namespace gridfire {
}
std::expected<std::unordered_map<Species, double>, expectations::StaleEngineError> DefinedEngineView::getSpeciesDestructionTimescales(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
validateNetworkState();
const MaskedComposition masked(comp, m_activeSpecies);
const fourdst::composition::MaskedComposition masked(comp, m_activeSpecies);
const auto result = m_baseEngine.getSpeciesDestructionTimescales(masked, T9, rho, m_activeReactions);
@@ -257,7 +246,6 @@ namespace gridfire {
return m_baseEngine.isStale(netIn);
}
void DefinedEngineView::setScreeningModel(const screening::ScreeningType model) {
m_baseEngine.setScreeningModel(model);
}
@@ -282,10 +270,10 @@ namespace gridfire {
std::vector<double> DefinedEngineView::mapNetInToMolarAbundanceVector(const NetIn &netIn) const {
std::vector<double> Y(m_activeSpecies.size(), 0.0); // Initialize with zeros
for (const auto& [symbol, entry] : netIn.composition) {
auto it = std::ranges::find(m_activeSpecies, entry.isotope());
for (const auto& [sp, y] : netIn.composition) {
auto it = std::ranges::find(m_activeSpecies, sp);
if (it != m_activeSpecies.end()) {
Y[getSpeciesIndex(entry.isotope())] = netIn.composition.getMolarAbundance(symbol); // Map species to their molar abundance
Y[getSpeciesIndex(sp)] = y; // Map species to their molar abundance
}
}
return Y; // Return the vector of molar abundances
@@ -296,14 +284,13 @@ namespace gridfire {
}
fourdst::composition::Composition DefinedEngineView::collectComposition(
fourdst::composition::Composition &comp
fourdst::composition::CompositionAbstract &comp
) const {
fourdst::composition::Composition result = m_baseEngine.collectComposition(comp);
for (const auto& species : m_activeSpecies) {
if (!result.hasSpecies(species)) {
if (!result.contains(species)) {
result.registerSpecies(species);
result.setMassFraction(species, 0.0);
}
}
return result;

View File

@@ -1,7 +1,6 @@
#include "gridfire/engine/views/engine_multiscale.h"
#include "gridfire/exceptions/error_engine.h"
#include "gridfire/engine/procedures/priming.h"
#include "gridfire/utils/general_composition.h"
#include <stdexcept>
#include <vector>
@@ -14,6 +13,7 @@
#include <algorithm>
#include "fourdst/atomic/species.h"
#include "quill/LogMacros.h"
#include "quill/Logger.h"
@@ -182,7 +182,7 @@ namespace gridfire {
}
std::expected<StepDerivatives<double>, expectations::StaleEngineError> MultiscalePartitioningEngineView::calculateRHSAndEnergy(
const fourdst::composition::Composition& comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -200,7 +200,7 @@ namespace gridfire {
}
EnergyDerivatives MultiscalePartitioningEngineView::calculateEpsDerivatives(
const fourdst::composition::Composition& comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -208,7 +208,7 @@ namespace gridfire {
}
void MultiscalePartitioningEngineView::generateJacobianMatrix(
const fourdst::composition::Composition& comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -217,7 +217,7 @@ namespace gridfire {
}
void MultiscalePartitioningEngineView::generateJacobianMatrix(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const std::vector<Species> &activeSpecies
@@ -253,7 +253,7 @@ namespace gridfire {
}
void MultiscalePartitioningEngineView::generateJacobianMatrix(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho,
const SparsityPattern &sparsityPattern
@@ -288,34 +288,20 @@ namespace gridfire {
double MultiscalePartitioningEngineView::calculateMolarReactionFlow(
const reaction::Reaction &reaction,
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
// Fix the algebraic species to the equilibrium abundances we calculate.
fourdst::composition::Composition comp_mutable = comp;
const bool didFinalize = comp_mutable.finalize(false);
if (!didFinalize) {
LOG_ERROR(m_logger, "Failed to finalize composition before setting algebraic species abundances.");
m_logger->flush_log();
throw std::runtime_error("Failed to finalize composition before setting algebraic species abundances.");
fourdst::composition::Composition comp_mutable;
for (const auto& species : comp.getRegisteredSpecies()) {
comp_mutable.registerSpecies(species);
comp_mutable.setMolarAbundance(species, comp.getMolarAbundance(species));
}
for (const auto& species : m_algebraic_species) {
const double Yi = m_algebraic_abundances.at(species);
double Xi = utils::massFractionFromMolarAbundanceAndComposition(comp_mutable, species, Yi);
comp_mutable.setMassFraction(species, Xi); // Convert Yi (mol/g) to Xi (mass fraction)
if (!comp_mutable.finalize(false)) {
LOG_ERROR(m_logger, "Failed to finalize composition after setting algebraic species abundance for species '{}'.", species.name());
m_logger->flush_log();
throw std::runtime_error("Failed to finalize composition after setting algebraic species abundance for species: " + std::string(species.name()));
}
}
if (!comp_mutable.finalize()) {
LOG_ERROR(m_logger, "Failed to finalize composition after setting algebraic species abundances.");
m_logger->flush_log();
throw std::runtime_error("Failed to finalize composition after setting algebraic species abundances.");
comp_mutable.setMolarAbundance(species, Yi);
}
return m_baseEngine.calculateMolarReactionFlow(reaction, comp_mutable, T9, rho);
@@ -331,7 +317,7 @@ namespace gridfire {
}
std::expected<std::unordered_map<Species, double>, expectations::StaleEngineError> MultiscalePartitioningEngineView::getSpeciesTimescales(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -348,7 +334,7 @@ namespace gridfire {
std::expected<std::unordered_map<Species, double>, expectations::StaleEngineError>
MultiscalePartitioningEngineView::getSpeciesDestructionTimescales(
const fourdst::composition::Composition &comp,
const fourdst::composition::CompositionAbstract &comp,
const double T9,
const double rho
) const {
@@ -371,7 +357,7 @@ namespace gridfire {
const fourdst::composition::Composition equilibratedComposition = equilibrateNetwork(baseUpdatedNetIn);
std::unordered_map<Species, double> algebraicAbundances;
for (const auto& species : m_algebraic_species) {
algebraicAbundances[species] = equilibratedComposition.getMolarAbundance(species);
algebraicAbundances.emplace(species, equilibratedComposition.getMolarAbundance(species));
}
m_algebraic_abundances = std::move(algebraicAbundances);
@@ -791,8 +777,8 @@ namespace gridfire {
std::vector<double> MultiscalePartitioningEngineView::mapNetInToMolarAbundanceVector(const NetIn &netIn) const {
std::vector<double> Y(m_dynamic_species.size(), 0.0); // Initialize with zeros
for (const auto& [symbol, entry] : netIn.composition) {
Y[getSpeciesIndex(entry.isotope())] = netIn.composition.getMolarAbundance(symbol); // Map species to their molar abundance
for (const auto& [sp, y] : netIn.composition) {
Y[getSpeciesIndex(sp)] = y; // Map species to their molar abundance
}
return Y; // Return the vector of molar abundances
}
@@ -826,20 +812,12 @@ namespace gridfire {
partitionNetwork(comp, T9, rho);
fourdst::composition::Composition qseComposition = solveQSEAbundances(comp, T9, rho);
for (const auto &symbol: qseComposition | std::views::keys) {
const double speciesMassFraction = qseComposition.getMassFraction(symbol);
if (speciesMassFraction < 0.0 && std::abs(speciesMassFraction) < 1e-20) {
qseComposition.setMassFraction(symbol, 0.0); // Avoid negative mass fractions
for (const auto &[sp, y]: qseComposition) {
if (y < 0.0 && std::abs(y) < 1e-20) {
qseComposition.setMolarAbundance(sp, 0.0); // Avoid negative mass fractions
}
}
bool didFinalize = qseComposition.finalize(true);
if (!didFinalize) {
LOG_ERROR(m_logger, "Failed to finalize composition after solving QSE abundances.");
m_logger->flush_log();
throw std::runtime_error("Failed to finalize composition after solving QSE abundances.");
}
return qseComposition;
}
@@ -875,18 +853,12 @@ namespace gridfire {
}
fourdst::composition::Composition MultiscalePartitioningEngineView::collectComposition(
fourdst::composition::Composition &comp
fourdst::composition::CompositionAbstract &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& [sp, y] : result) {
Ym.emplace(sp, y);
}
for (const auto& [species, Yi] : m_algebraic_abundances) {
if (!Ym.contains(species)) {
@@ -894,21 +866,16 @@ namespace gridfire {
}
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);
return {speciesNames, Y};
}
size_t MultiscalePartitioningEngineView::getSpeciesIndex(const Species &species) const {
@@ -1254,20 +1221,13 @@ namespace gridfire {
for (const auto& species: algebraic_species) {
if (!normalized_composition.contains(species)) {
normalized_composition.registerSpecies(species);
normalized_composition.setMassFraction(species, 0.0);
}
}
for (const auto& species: seed_species) {
if (!normalized_composition.contains(species)) {
normalized_composition.registerSpecies(species);
normalized_composition.setMassFraction(species, 0.0);
}
}
bool normCompFinalizedOkay = normalized_composition.finalize(true);
if (!normCompFinalizedOkay) {
LOG_ERROR(m_logger, "Failed to finalize composition before QSE solve.");
throw std::runtime_error("Failed to finalize composition before QSE solve.");
}
Eigen::VectorXd Y_scale(algebraic_species.size());
Eigen::VectorXd v_initial(algebraic_species.size());
@@ -1319,20 +1279,13 @@ namespace gridfire {
Y_final_qse(i)
);
// double Xi = Y_final_qse(i) * species.mass(); // Convert from molar abundance to mass fraction
double Xi = utils::massFractionFromMolarAbundanceAndComposition(normalized_composition, species, Y_final_qse(i));
if (!outputComposition.hasSpecies(species)) {
if (!outputComposition.contains (species)) {
outputComposition.registerSpecies(species);
}
outputComposition.setMassFraction(species, Xi);
outputComposition.setMolarAbundance(species, Y_final_qse(i));
i++;
}
}
bool didFinalize = outputComposition.finalize(false);
if (!didFinalize) {
LOG_ERROR(m_logger, "Failed to finalize composition after solving QSE abundances.");
m_logger->flush_log();
throw std::runtime_error("Failed to finalize composition after solving QSE abundances.");
}
return outputComposition;
}
@@ -1540,22 +1493,11 @@ namespace gridfire {
Eigen::VectorXd y_qse = v_qse.array().exp(); // Convert to physical abundances using exponential scaling
for (const auto& species: m_qse_solve_species) {
if (!comp_trial.hasSymbol(std::string(species.name()))) {
if (!comp_trial.contains(std::string(species.name()))) {
comp_trial.registerSpecies(species);
}
auto index = static_cast<long>(m_qse_solve_species_index_map.at(species));
const double molarAbundance = y_qse[index];
double massFraction = utils::massFractionFromMolarAbundanceAndComposition(m_initial_comp, species, molarAbundance);
comp_trial.setMassFraction(species, massFraction);
}
const bool didFinalize = comp_trial.finalize(false);
if (!didFinalize) {
LOG_TRACE_L1(m_view->m_logger, "While evaluating the functor, failed to finalize composition. This is likely because the solver took a step outside of physical abundances. This is not an error; rather, the solver will be told to take a different step.");
f_qse.resize(static_cast<long>(m_qse_solve_species.size()));
f_qse.setConstant(1.0e20); // Return a large residual to indicate failure
return 0;
comp_trial.setMolarAbundance(species, y_qse[index]);
}
const auto result = m_view->getBaseEngine().calculateRHSAndEnergy(comp_trial, m_T9, m_rho);
@@ -1608,20 +1550,11 @@ namespace gridfire {
Eigen::VectorXd y_qse = v_qse.array().exp(); // Convert to physical abundances using exponential scaling
for (const auto& species: m_qse_solve_species) {
if (!comp_trial.hasSymbol(std::string(species.name()))) {
if (!comp_trial.contains(std::string(species.name()))) {
comp_trial.registerSpecies(species);
}
const double molarAbundance = y_qse[static_cast<long>(m_qse_solve_species_index_map.at(species))];
double massFraction = utils::massFractionFromMolarAbundanceAndComposition(m_initial_comp, species, molarAbundance);
comp_trial.setMassFraction(species, massFraction);
}
const bool didFinalize = comp_trial.finalize(false);
if (!didFinalize) {
LOG_TRACE_L1(m_view->m_logger, "While evaluating the Jacobian, failed to finalize composition. This is likely because the solver took a step outside of physical abundances. This is not an error; rather, the solver will be told to take a different step. Returning Identity");
J_qse.resize(static_cast<long>(m_qse_solve_species.size()), static_cast<long>(m_qse_solve_species.size()));
J_qse.setIdentity();
return 0;
comp_trial.setMolarAbundance(species, molarAbundance);
}
std::vector<Species> qse_species_vector(m_qse_solve_species.begin(), m_qse_solve_species.end());

View File

@@ -1,7 +1,7 @@
#include "gridfire/engine/views/engine_priming.h"
#include "gridfire/solver/solver.h"
#include "fourdst/composition/species.h"
#include "fourdst/atomic/species.h"
#include "quill/LogMacros.h"
#include "quill/Logger.h"
@@ -9,7 +9,6 @@
#include <vector>
#include <string>
#include <unordered_set>
#include <stdexcept>
#include <unordered_map>

View File

@@ -1,11 +1,9 @@
#include "gridfire/io/generative/python.h"
#include "fourdst/composition/species.h"
#include "fourdst/atomic/atomicSpecies.h"
#include <string>
#include <vector>
#include <format>
#include <algorithm>
#include <ranges>
#include <fstream>
#include <optional>
@@ -14,7 +12,7 @@
namespace {
template <typename T>
std::string join(std::vector<T> arr, std::string delim) {
std::string join(std::vector<T> arr, const std::string& delim) {
if (arr.empty()) return {};
size_t total = delim.size() * (arr.size() - 1);
@@ -142,7 +140,7 @@ namespace gridfire::io::gen {
std::string exportEngineToPy(const gridfire::DynamicEngine& engine) {
auto reactions = engine.getNetworkReactions();
std::vector<std::string> functions;
functions.push_back(R"(import numpy as np
functions.emplace_back(R"(import numpy as np
from typing import Dict, List, Tuple, Callable)");
functions.push_back(tfFunc);
for (const auto& reaction : reactions) {

View File

@@ -23,43 +23,7 @@
#include <ranges>
#include "quill/LogMacros.h"
namespace gridfire {
std::vector<double> NetIn::MolarAbundance() const {
std::vector <double> y;
y.reserve(composition.getRegisteredSymbols().size());
const auto [fst, snd] = composition.getComposition();
for (const auto &name: fst | std::views::keys) {
y.push_back(composition.getMolarAbundance(name));
}
return y;
}
Network::Network(const NetworkFormat format) :
m_config(fourdst::config::Config::getInstance()),
m_logManager(fourdst::logging::LogManager::getInstance()),
m_logger(m_logManager.getLogger("log")),
m_format(format),
m_constants(fourdst::constant::Constants::getInstance()){
if (format == NetworkFormat::UNKNOWN) {
LOG_ERROR(m_logger, "nuclearNetwork::Network::Network() called with UNKNOWN format");
m_logger->flush_log();
throw std::runtime_error("nuclearNetwork::Network::Network() called with UNKNOWN format");
}
}
NetworkFormat Network::getFormat() const {
return m_format;
}
NetworkFormat Network::setFormat(const NetworkFormat format) {
const NetworkFormat oldFormat = m_format;
m_format = format;
return oldFormat;
}
// Trim whitespace from both ends of a string
std::string trim_whitespace(const std::string& str) {
auto startIt = str.begin();

View File

@@ -2,8 +2,8 @@
#include <ranges>
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/composition/species.h"
#include "fourdst/atomic/atomicSpecies.h"
#include "fourdst/atomic/species.h"
#include "quill/LogMacros.h"

View File

@@ -101,8 +101,7 @@ namespace gridfire::policy {
"d(p,g)he3",
"he4(he3,g)be7",
"be7(p,g)b8",
"b8(,e+)be8",
"be8(,a)he4"
"b8(,e+ a)he4"
}, 0.001) {}
std::unique_ptr<ReactionChainPolicy> ProtonProtonIChainPolicy::clone() const {

View File

@@ -33,6 +33,7 @@ namespace gridfire::policy {
return std::make_unique<MultiReactionChainPolicy>(
[this]() {
std::vector<std::unique_ptr<ReactionChainPolicy>> chain_policies;
chain_policies.reserve(m_chain_policies.size());
for (const auto &ch : m_chain_policies) {
chain_policies.push_back(ch->clone());
}

View File

@@ -6,12 +6,28 @@
#include "gridfire/engine/engine_graph.h"
#include "gridfire/engine/views/engine_views.h"
#include "fourdst/composition/exceptions/exceptions_composition.h"
#include "fourdst/atomic/species.h"
#include "fourdst/composition/utils.h"
namespace {
std::set<fourdst::atomic::Species> initialize_seed_species() {
return {
fourdst::atomic::H_1,
fourdst::atomic::He_3,
fourdst::atomic::He_4,
fourdst::atomic::C_12,
fourdst::atomic::N_14,
fourdst::atomic::O_16,
fourdst::atomic::Ne_20,
fourdst::atomic::Mg_24
};
}
}
namespace gridfire::policy {
MainSequencePolicy::MainSequencePolicy(const fourdst::composition::Composition& composition) {
MainSequencePolicy::MainSequencePolicy(const fourdst::composition::Composition& composition) : m_seed_species(initialize_seed_species()) {
for (const auto& species : m_seed_species) {
if (!composition.hasSpecies(species)) {
if (!composition.contains(species)) {
throw exceptions::MissingSeedSpeciesError("Cannot initialize MainSequencePolicy: Required Seed species " + std::string(species.name()) + " is missing from the provided composition.");
}
}
@@ -19,22 +35,14 @@ namespace gridfire::policy {
m_partition_function = build_partition_function();
}
MainSequencePolicy::MainSequencePolicy(std::vector<fourdst::atomic::Species> seed_species, std::vector<double> mass_fractions) {
MainSequencePolicy::MainSequencePolicy(std::vector<fourdst::atomic::Species> seed_species, const std::vector<double> &mass_fractions) {
for (const auto& species : m_seed_species) {
if (std::ranges::find(seed_species, species) == seed_species.end()) {
throw exceptions::MissingSeedSpeciesError("Cannot initialize MainSequencePolicy: Required Seed species " + std::string(species.name()) + " is missing from the provided composition.");
}
}
for (const auto& [species, x] : std::views::zip(seed_species, mass_fractions)) {
m_initializing_composition.registerSpecies(species);
m_initializing_composition.setMassFraction(species, x);
}
const bool didFinalize = m_initializing_composition.finalize(true);
if (!didFinalize) {
throw fourdst::composition::exceptions::CompositionNotFinalizedError("Failed to finalize initial composition for MainSequencePolicy.");
}
m_initializing_composition = fourdst::composition::buildCompositionFromMassFractions(seed_species, mass_fractions);
m_partition_function = build_partition_function();
}
@@ -45,6 +53,11 @@ namespace gridfire::policy {
m_network_stack.emplace_back(
std::make_unique<GraphEngine>(m_initializing_composition, *m_partition_function, NetworkBuildDepth::ThirdOrder, NetworkConstructionFlags::DEFAULT)
);
auto& graphRepr = dynamic_cast<GraphEngine&>(*m_network_stack.back().get());
graphRepr.setUseReverseReactions(false);
m_network_stack.emplace_back(
std::make_unique<MultiscalePartitioningEngineView>(*m_network_stack.back().get())
);
@@ -85,7 +98,7 @@ namespace gridfire::policy {
inline NetworkPolicyStatus MainSequencePolicy::check_status() const {
for (const auto& species : m_seed_species) {
if (!m_initializing_composition.hasSpecies(species)) {
if (!m_initializing_composition.contains(species)) {
return NetworkPolicyStatus::MISSING_KEY_SPECIES;
}
}

View File

@@ -1,5 +1,5 @@
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/composition/species.h"
#include "fourdst/atomic/atomicSpecies.h"
#include "fourdst/atomic/species.h"
#include "gridfire/reaction/reaclib.h"
#include "gridfire/reaction/reactions_data.h"

View File

@@ -10,7 +10,7 @@
#include "quill/LogMacros.h"
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/atomic/atomicSpecies.h"
#include "xxhash64.h"

View File

@@ -1,8 +1,6 @@
#include "gridfire/reaction/weak/weak_rate_library.h"
#include "gridfire/reaction/weak/weak.h"
#include "fourdst/composition/species.h"
#include <array>
#include <ranges>
#include <unordered_map>
@@ -13,6 +11,7 @@
#include "gridfire/reaction/weak/weak_interpolator.h"
#include "xxhash64.h"
#include "fourdst/atomic/species.h"
namespace {

View File

@@ -4,7 +4,6 @@
#include "gridfire/utils/hashing.h"
#include <algorithm>
#include <map>
#include <set>
#include <unordered_map>
#include <vector>
@@ -12,7 +11,7 @@
#include <expected>
#include <ranges>
#include "fourdst/composition/species.h"
#include "fourdst/atomic/species.h"
#include "quill/LogMacros.h"

View File

@@ -1,6 +1,6 @@
#include "gridfire/screening/screening_bare.h"
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/atomic/atomicSpecies.h"
#include "cppad/cppad.hpp"

View File

@@ -1,6 +1,6 @@
#include "gridfire/screening/screening_weak.h"
#include "fourdst/composition/atomicSpecies.h"
#include "fourdst/atomic/atomicSpecies.h"
#include "cppad/cppad.hpp"

View File

@@ -20,7 +20,6 @@
#include "gridfire/engine/engine_graph.h"
#include "gridfire/solver/strategies/triggers/engine_partitioning_trigger.h"
#include "gridfire/trigger/procedures/trigger_pprint.h"
#include "gridfire/utils/general_composition.h"
namespace {
std::unordered_map<int, std::string> cvode_ret_code_map {
@@ -68,9 +67,9 @@ namespace {
N_Vector init_sun_vector(uint64_t size, SUNContext sun_ctx) {
#ifdef SUNDIALS_HAVE_OPENMP
N_Vector vec = N_VNew_OpenMP(size, 0, sun_ctx);
const N_Vector vec = N_VNew_OpenMP(size, 0, sun_ctx);
#elif SUNDIALS_HAVE_PTHREADS
N_Vector vec = N_VNew_Pthreads(size, sun_ctx);
const N_Vector vec = N_VNew_Pthreads(size, sun_ctx);
#else
const N_Vector vec = N_VNew_Serial(static_cast<long long>(size), sun_ctx);
#endif
@@ -273,6 +272,8 @@ namespace gridfire::solver {
total_convergence_failures += nlcfails;
total_steps += n_steps;
sunrealtype* end_of_step_abundances = N_VGetArrayPointer(ctx.state);
LOG_INFO(
m_logger,
"Engine Update Triggered at time {} ({} update{} triggered). Current total specific energy {} [erg/g]",
@@ -284,7 +285,7 @@ namespace gridfire::solver {
fourdst::composition::Composition temp_comp;
std::vector<double> mass_fractions;
long int num_species_at_stop = m_engine.getNetworkSpecies().size();
auto num_species_at_stop = static_cast<long int>(m_engine.getNetworkSpecies().size());
if (num_species_at_stop > m_Y->ops->nvgetlength(m_Y) - 1) {
LOG_ERROR(
@@ -296,29 +297,126 @@ namespace gridfire::solver {
throw std::runtime_error("Number of species at engine update exceeds the number of species in the CVODE solver. This should never happen.");
}
mass_fractions.reserve(num_species_at_stop);
for (const auto& species: m_engine.getNetworkSpecies()) {
const size_t sid = m_engine.getSpeciesIndex(species);
temp_comp.registerSpecies(species);
double y = end_of_step_abundances[sid];
if (y > 0.0) {
temp_comp.setMolarAbundance(species, y);
}
}
#ifndef NDEBUG
for (long int i = 0; i < num_species_at_stop; ++i) {
const auto& species = m_engine.getNetworkSpecies()[i];
temp_comp.registerSpecies(species);
mass_fractions.push_back(y_data[i] * species.mass()); // Convert from molar abundance to mass fraction
}
temp_comp.setMassFraction(m_engine.getNetworkSpecies(), mass_fractions);
bool didFinalize = temp_comp.finalize(true);
if (!didFinalize) {
LOG_ERROR(m_logger, "Failed to finalize composition during engine update. Check input mass fractions for validity.");
throw std::runtime_error("Failed to finalize composition during engine update.");
if (std::abs(temp_comp.getMolarAbundance(species) - y_data[i]) > 1e-12) {
throw exceptions::UtilityError("Conversion from solver state to composition molar abundance failed verification.");
}
}
#endif
NetIn netInTemp = netIn;
NetIn netInTemp;
netInTemp.temperature = T9 * 1e9; // Convert back to Kelvin
netInTemp.density = netIn.density;
netInTemp.composition = temp_comp;
LOG_DEBUG(
m_logger,
"Prior to Engine update composition is (molar abundance) {}",
[&]() -> std::string {
std::stringstream ss;
const size_t ns = temp_comp.size();
size_t count = 0;
for (const auto &symbol : temp_comp | std::views::keys) {
ss << symbol << ": " << temp_comp.getMolarAbundance(symbol);
if (count < ns - 1) {
ss << ", ";
}
count++;
}
return ss.str();
}()
);
LOG_DEBUG(
m_logger,
"Prior to Engine Update active reactions are: {}",
[&]() -> std::string {
std::stringstream ss;
const gridfire::reaction::ReactionSet& reactions = m_engine.getNetworkReactions();
size_t count = 0;
for (const auto& reaction : reactions) {
ss << reaction -> id();
if (count < reactions.size() - 1) {
ss << ", ";
}
count++;
}
return ss.str();
}()
);
fourdst::composition::Composition currentComposition = m_engine.update(netInTemp);
LOG_DEBUG(
m_logger,
"After to Engine update composition is (molar abundance) {}",
[&]() -> std::string {
std::stringstream ss;
const size_t ns = currentComposition.size();
size_t count = 0;
for (const auto &symbol : currentComposition | std::views::keys) {
ss << symbol << ": " << currentComposition.getMolarAbundance(symbol);
if (count < ns - 1) {
ss << ", ";
}
count++;
}
return ss.str();
}()
);
LOG_DEBUG(
m_logger,
"Fractional Abundance Changes: {}",
[&]() -> std::string {
std::stringstream ss;
const size_t ns = currentComposition.size();
size_t count = 0;
for (const auto &symbol : currentComposition | std::views::keys) {
if (!temp_comp.contains(symbol)) {
ss << symbol << ": New Species";
} else {
const double old_X = temp_comp.getMolarAbundance(symbol);
const double new_X = currentComposition.getMolarAbundance(symbol);
const double frac_change = (new_X - old_X) / (old_X + std::numeric_limits<double>::epsilon());
ss << symbol << ": " << frac_change;
}
if (count < ns - 1) {
ss << ", ";
}
count++;
}
return ss.str();
}()
);
LOG_DEBUG(
m_logger,
"After Engine Update active reactions are: {}",
[&]() -> std::string {
std::stringstream ss;
const gridfire::reaction::ReactionSet& reactions = m_engine.getNetworkReactions();
size_t count = 0;
for (const auto& reaction : reactions) {
ss << reaction -> id();
if (count < reactions.size() - 1) {
ss << ", ";
}
count++;
}
return ss.str();
}()
);
LOG_INFO(
m_logger,
"Due to a triggered stale engine the composition was updated from size {} to {} species.",
"Due to a triggered engine update the composition was updated from size {} to {} species.",
num_species_at_stop,
m_engine.getNetworkSpecies().size()
);
@@ -350,40 +448,21 @@ namespace gridfire::solver {
sunrealtype* y_data = N_VGetArrayPointer(m_Y);
accumulated_energy += y_data[numSpecies];
std::vector<double> y_vec(y_data, y_data + numSpecies);
std::vector<double> finalMassFractions(numSpecies);
for (size_t i = 0; i < numSpecies; ++i) {
const double molarMass = m_engine.getNetworkSpecies()[i].mass();
finalMassFractions[i] = y_data[i] * molarMass; // Convert from molar abundance to mass fraction
if (finalMassFractions[i] < MIN_ABUNDANCE_THRESHOLD) {
finalMassFractions[i] = 0.0;
for (size_t i = 0; i < y_vec.size(); ++i) {
if (y_vec[i] < 0 && std::abs(y_vec[i]) < 1e-16) {
y_vec[i] = 0.0; // Regularize tiny negative abundances to zero
}
}
std::vector<std::string> speciesNames;
speciesNames.reserve(numSpecies);
for (const auto& species : m_engine.getNetworkSpecies()) {
speciesNames.emplace_back(species.name());
}
LOG_TRACE_L2(m_logger, "Constructing final composition= with {} species", numSpecies);
LOG_TRACE_L2(m_logger, "Constructing final composition= with {} species", speciesNames.size());
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 topLevelComposition(m_engine.getNetworkSpecies(), y_vec);
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...");
@@ -511,19 +590,7 @@ namespace gridfire::solver {
// some consistent memory layout for the composition object to make this cheeper. That optimization would need to be
// done in the libcomposition library though...
std::vector<double> y_vec(y_data, y_data + numSpecies);
std::vector<std::string> symbols;
symbols.reserve(numSpecies);
for (const auto& species : m_engine.getNetworkSpecies()) {
symbols.emplace_back(species.name());
}
std::vector<double> M;
M.reserve(numSpecies);
for (size_t i = 0; i < numSpecies; ++i) {
const double molarMass = m_engine.getNetworkSpecies()[i].mass();
M.push_back(molarMass);
}
std::vector<double> X = utils::massFractionFromMolarAbundanceAndMolarMass(y_vec, M);
fourdst::composition::Composition composition(symbols, X);
fourdst::composition::Composition composition(m_engine.getNetworkSpecies(), y_vec);
const auto result = m_engine.calculateRHSAndEnergy(composition, data->T9, data->rho);
if (!result) {
@@ -623,14 +690,9 @@ namespace gridfire::solver {
check_cvode_flag(CVodeGetEstLocalErrors(m_cvode_mem, m_YErr), "CVodeGetEstLocalErrors");
sunrealtype *y_data = N_VGetArrayPointer(m_Y);
sunrealtype *y_err_data = N_VGetArrayPointer(m_YErr);
std::vector<double> err_ratios;
std::vector<std::string> speciesNames;
const auto absTol = m_config.get<double>("gridfire:solver:CVODESolverStrategy:absTol", 1.0e-8);
const auto relTol = m_config.get<double>("gridfire:solver:CVODESolverStrategy:relTol", 1.0e-8);
const size_t num_components = N_VGetLength(m_Y);
err_ratios.resize(num_components - 1);
@@ -646,26 +708,13 @@ namespace gridfire::solver {
0.0
);
std::vector<double> M;
M.reserve(num_components);
for (size_t i = 0; i < num_components - 1; i++) {
const double weight = relTol * std::abs(y_data[i]) + absTol;
if (weight == 0.0) continue; // Skip components with zero weight
const double err_ratio = std::abs(y_err_data[i]) / weight;
err_ratios[i] = err_ratio;
speciesNames.emplace_back(user_data.networkSpecies->at(i).name());
M.push_back(user_data.networkSpecies->at(i).mass());
}
std::vector<double> X = utils::massFractionFromMolarAbundanceAndMolarMass(Y_full, M);
fourdst::composition::Composition composition(speciesNames, X);
fourdst::composition::Composition composition(user_data.engine->getNetworkSpecies(), Y_full);
if (err_ratios.empty()) {
return;
}
std::vector<size_t> indices(speciesNames.size());
std::vector<size_t> indices(composition.size());
for (size_t i = 0; i < indices.size(); ++i) {
indices[i] = i;
}
@@ -677,29 +726,29 @@ namespace gridfire::solver {
}
);
std::vector<std::string> sorted_speciesNames;
std::vector<fourdst::atomic::Species> sorted_species;
std::vector<double> sorted_err_ratios;
sorted_speciesNames.reserve(indices.size());
sorted_species.reserve(indices.size());
sorted_err_ratios.reserve(indices.size());
for (const auto idx: indices) {
sorted_speciesNames.push_back(speciesNames[idx]);
sorted_species.push_back(composition.getSpeciesAtIndex(idx));
sorted_err_ratios.push_back(err_ratios[idx]);
}
std::vector<std::unique_ptr<utils::ColumnBase>> columns;
columns.push_back(std::make_unique<utils::Column<std::string>>("Species", sorted_speciesNames));
columns.push_back(std::make_unique<utils::Column<fourdst::atomic::Species>>("Species", sorted_species));
columns.push_back(std::make_unique<utils::Column<double>>("Error Ratio", sorted_err_ratios));
std::cout << utils::format_table("Species Error Ratios", columns) << std::endl;
if (displayJacobianStiffness) {
diagnostics::inspect_jacobian_stiffness(*user_data.engine, composition, user_data.T9, user_data.rho);
for (const auto& species : sorted_speciesNames) {
diagnostics::inspect_species_balance(*user_data.engine, species, composition, user_data.T9, user_data.rho);
for (const auto& species : sorted_species) {
diagnostics::inspect_species_balance(*user_data.engine, std::string(species.name()), composition, user_data.T9, user_data.rho);
}
}

View File

@@ -344,11 +344,11 @@ namespace gridfire::trigger::solver::CVODE {
}
float ConvergenceFailureTrigger::current_mean() const {
float acc = 0;
size_t acc = 0;
for (const auto nlcfails: m_window) {
acc += nlcfails;
}
return acc / m_windowSize;
return static_cast<float>(acc) / static_cast<float>(m_windowSize);
}
bool ConvergenceFailureTrigger::abs_failure(
@@ -364,7 +364,7 @@ namespace gridfire::trigger::solver::CVODE {
const gridfire::solver::CVODESolverStrategy::TimestepContext &ctx
) const {
const float mean = current_mean();
if (ctx.currentConvergenceFailures - mean > m_relativeFailureRate * mean) {
if (static_cast<float>(ctx.currentConvergenceFailures) - mean > m_relativeFailureRate * mean) {
return true;
}
return false;
@@ -395,8 +395,8 @@ namespace gridfire::trigger::solver::CVODE {
// Combine the triggers using logical OR
auto orTriggerA = std::make_unique<OrTrigger<ctx_t>>(std::move(simulationTimeTrigger), std::move(offDiagTrigger));
auto orTriggerB = std::make_unique<OrTrigger<ctx_t>>(std::move(orTriggerA), std::move(timestepGrowthTrigger));
auto orTriggerC = std::make_unique<OrTrigger<ctx_t>>(std::move(orTriggerB), std::move(convergenceFailureTrigger));
// auto orTriggerC = std::make_unique<OrTrigger<ctx_t>>(std::move(orTriggerB), std::move(convergenceFailureTrigger));
return orTriggerC;
return convergenceFailureTrigger;
}
}

View File

@@ -7,7 +7,6 @@
#include <ranges>
#include <string_view>
#include <string>
#include <vector>
std::string gridfire::utils::formatNuclearTimescaleLogString(
const DynamicEngine& engine,