fix(GraphNetwork): working on loads of small bugs
Fized stoichiometry matrix initialization, added penames to reablib reactions, began work on LogicalReaction to sum the contributions of different fitting functions provided by reaclib
This commit is contained in:
538
src/network/lib/approx8.cpp
Normal file
538
src/network/lib/approx8.cpp
Normal file
@@ -0,0 +1,538 @@
|
||||
/* ***********************************************************************
|
||||
//
|
||||
// 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/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
|
||||
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
|
||||
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
|
||||
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 double stiff_abs_tol = m_config.get<double>("Network:Approx8:Stiff:AbsTol", 1.0e-6);
|
||||
const double stiff_rel_tol = m_config.get<double>("Network:Approx8:Stiff:RelTol", 1.0e-6);
|
||||
const double nonstiff_abs_tol = m_config.get<double>("Network:Approx8:NonStiff:AbsTol", 1.0e-6);
|
||||
const double 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.getMassFraction("H-1");
|
||||
y[Approx8Net::ihe3] = netIn.composition.getMassFraction("He-3");
|
||||
y[Approx8Net::ihe4] = netIn.composition.getMassFraction("He-4");
|
||||
y[Approx8Net::ic12] = netIn.composition.getMassFraction("C-12");
|
||||
y[Approx8Net::in14] = netIn.composition.getMassFraction("N-14");
|
||||
y[Approx8Net::io16] = netIn.composition.getMassFraction("O-16");
|
||||
y[Approx8Net::ine20] = netIn.composition.getMassFraction("Ne-20");
|
||||
y[Approx8Net::img24] = netIn.composition.getMassFraction("Mg-24");
|
||||
y[Approx8Net::iTemp] = netIn.temperature;
|
||||
y[Approx8Net::iDensity] = netIn.density;
|
||||
y[Approx8Net::iEnergy] = netIn.energy;
|
||||
|
||||
double ySum = 0.0;
|
||||
for (int i = 0; i < Approx8Net::nIso; i++) {
|
||||
y[i] /= Approx8Net::aIon[i];
|
||||
ySum += y[i];
|
||||
}
|
||||
for (int i = 0; i < Approx8Net::nIso; i++) {
|
||||
y[i] /= ySum;
|
||||
}
|
||||
|
||||
return y;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// main program
|
||||
|
||||
531
src/network/lib/netgraph.cpp
Normal file
531
src/network/lib/netgraph.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
#include "gridfire/netgraph.h"
|
||||
#include "fourdst/composition/atomicSpecies.h"
|
||||
#include "fourdst/constants/const.h"
|
||||
#include "gridfire/network.h"
|
||||
#include "gridfire/reaclib.h"
|
||||
#include "fourdst/composition/species.h"
|
||||
|
||||
#include "quill/LogMacros.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
|
||||
#include <boost/numeric/ublas/vector.hpp>
|
||||
#include <boost/numeric/odeint.hpp>
|
||||
|
||||
|
||||
namespace gridfire {
|
||||
GraphNetwork::GraphNetwork(
|
||||
const fourdst::composition::Composition &composition
|
||||
):
|
||||
Network(REACLIB),
|
||||
m_reactions(build_reaclib_nuclear_network(composition)) {
|
||||
syncInternalMaps();
|
||||
}
|
||||
|
||||
GraphNetwork::GraphNetwork(
|
||||
const fourdst::composition::Composition &composition,
|
||||
const double cullingThreshold,
|
||||
const double T9
|
||||
):
|
||||
Network(REACLIB),
|
||||
m_reactions(build_reaclib_nuclear_network(composition, cullingThreshold, T9)) {
|
||||
syncInternalMaps();
|
||||
}
|
||||
|
||||
GraphNetwork::GraphNetwork(const reaclib::REACLIBReactionSet &reactions) :
|
||||
Network(REACLIB),
|
||||
m_reactions(reactions) {
|
||||
syncInternalMaps();
|
||||
}
|
||||
|
||||
void GraphNetwork::syncInternalMaps() {
|
||||
collectNetworkSpecies();
|
||||
populateReactionIDMap();
|
||||
populateSpeciesToIndexMap();
|
||||
generateStoichiometryMatrix();
|
||||
reserveJacobianMatrix();
|
||||
recordADTape();
|
||||
}
|
||||
|
||||
// --- Network Graph Construction Methods ---
|
||||
void GraphNetwork::collectNetworkSpecies() {
|
||||
m_networkSpecies.clear();
|
||||
m_networkSpeciesMap.clear();
|
||||
|
||||
std::set<std::string_view> uniqueSpeciesNames;
|
||||
|
||||
for (const auto& reaction: m_reactions) {
|
||||
for (const auto& reactant: reaction.reactants()) {
|
||||
uniqueSpeciesNames.insert(reactant.name());
|
||||
}
|
||||
for (const auto& product: reaction.products()) {
|
||||
uniqueSpeciesNames.insert(product.name());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& name: uniqueSpeciesNames) {
|
||||
auto it = fourdst::atomic::species.find(name);
|
||||
if (it != fourdst::atomic::species.end()) {
|
||||
m_networkSpecies.push_back(it->second);
|
||||
m_networkSpeciesMap.insert({name, it->second});
|
||||
} else {
|
||||
LOG_ERROR(m_logger, "Species '{}' not found in global atomic species database.", name);
|
||||
throw std::runtime_error("Species not found in global atomic species database: " + std::string(name));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GraphNetwork::populateReactionIDMap() {
|
||||
LOG_INFO(m_logger, "Populating reaction ID map for REACLIB graph network (serif::network::GraphNetwork)...");
|
||||
m_reactionIDMap.clear();
|
||||
for (const auto& reaction: m_reactions) {
|
||||
m_reactionIDMap.insert({reaction.id(), reaction});
|
||||
}
|
||||
LOG_INFO(m_logger, "Populated {} reactions in the reaction ID map.", m_reactionIDMap.size());
|
||||
}
|
||||
|
||||
void GraphNetwork::populateSpeciesToIndexMap() {
|
||||
m_speciesToIndexMap.clear();
|
||||
for (size_t i = 0; i < m_networkSpecies.size(); ++i) {
|
||||
m_speciesToIndexMap.insert({m_networkSpecies[i], i});
|
||||
}
|
||||
}
|
||||
|
||||
void GraphNetwork::reserveJacobianMatrix() {
|
||||
// The implementation of this function (and others) constrains this nuclear network to a constant temperature and density during
|
||||
// each evaluation.
|
||||
size_t numSpecies = m_networkSpecies.size();
|
||||
m_jacobianMatrix.clear();
|
||||
m_jacobianMatrix.resize(numSpecies, numSpecies, false); // Sparse matrix, no initial values
|
||||
LOG_INFO(m_logger, "Jacobian matrix resized to {} rows and {} columns.",
|
||||
m_jacobianMatrix.size1(), m_jacobianMatrix.size2());
|
||||
}
|
||||
|
||||
// --- Basic Accessors and Queries ---
|
||||
const std::vector<fourdst::atomic::Species>& GraphNetwork::getNetworkSpecies() const {
|
||||
// Returns a constant reference to the vector of unique species in the network.
|
||||
LOG_DEBUG(m_logger, "Providing access to network species vector. Size: {}.", m_networkSpecies.size());
|
||||
return m_networkSpecies;
|
||||
}
|
||||
|
||||
const reaclib::REACLIBReactionSet& GraphNetwork::getNetworkReactions() const {
|
||||
// Returns a constant reference to the set of reactions in the network.
|
||||
LOG_DEBUG(m_logger, "Providing access to network reactions set. Size: {}.", m_reactions.size());
|
||||
return m_reactions;
|
||||
}
|
||||
|
||||
bool GraphNetwork::involvesSpecies(const fourdst::atomic::Species& species) const {
|
||||
// Checks if a given species is present in the network's species map for efficient lookup.
|
||||
const bool found = m_networkSpeciesMap.contains(species.name());
|
||||
LOG_DEBUG(m_logger, "Checking if species '{}' is involved in the network: {}.", species.name(), found ? "Yes" : "No");
|
||||
return found;
|
||||
}
|
||||
|
||||
std::unordered_map<fourdst::atomic::Species, int> GraphNetwork::getNetReactionStoichiometry(const reaclib::REACLIBReaction& reaction) const {
|
||||
// Calculates the net stoichiometric coefficients for species in a given reaction.
|
||||
std::unordered_map<fourdst::atomic::Species, int> stoichiometry;
|
||||
|
||||
// Iterate through reactants, decrementing their counts
|
||||
for (const auto& reactant : reaction.reactants()) {
|
||||
auto it = m_networkSpeciesMap.find(reactant.name());
|
||||
if (it != m_networkSpeciesMap.end()) {
|
||||
stoichiometry[it->second]--; // Copy Species by value (PERF: Future performance improvements by using pointers or references (std::reference_wrapper<const ...>) or something like that)
|
||||
} else {
|
||||
LOG_WARNING(m_logger, "Reactant species '{}' in reaction '{}' not found in network species map during stoichiometry calculation.",
|
||||
reactant.name(), reaction.id());
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate through products, incrementing their counts
|
||||
for (const auto& product : reaction.products()) {
|
||||
auto it = m_networkSpeciesMap.find(product.name());
|
||||
if (it != m_networkSpeciesMap.end()) {
|
||||
stoichiometry[it->second]++; // Copy Species by value (PERF: Future performance improvements by using pointers or references (std::reference_wrapper<const ...>) or something like that)
|
||||
} else {
|
||||
LOG_WARNING(m_logger, "Product species '{}' in reaction '{}' not found in network species map during stoichiometry calculation.",
|
||||
product.name(), reaction.id());
|
||||
}
|
||||
}
|
||||
LOG_DEBUG(m_logger, "Calculated net stoichiometry for reaction '{}'. Total unique species in stoichiometry: {}.", reaction.id(), stoichiometry.size());
|
||||
return stoichiometry;
|
||||
}
|
||||
|
||||
// --- Validation Methods ---
|
||||
bool GraphNetwork::validateConservation() const {
|
||||
LOG_INFO(m_logger, "Validating mass (A) and charge (Z) conservation across all reactions in the network.");
|
||||
|
||||
for (const auto& reaction : m_reactions) {
|
||||
uint64_t totalReactantA = 0;
|
||||
uint64_t totalReactantZ = 0;
|
||||
uint64_t totalProductA = 0;
|
||||
uint64_t totalProductZ = 0;
|
||||
|
||||
// Calculate total A and Z for reactants
|
||||
for (const auto& reactant : reaction.reactants()) {
|
||||
auto it = m_networkSpeciesMap.find(reactant.name());
|
||||
if (it != m_networkSpeciesMap.end()) {
|
||||
totalReactantA += it->second.a();
|
||||
totalReactantZ += it->second.z();
|
||||
} else {
|
||||
// This scenario indicates a severe data integrity issue:
|
||||
// a reactant is part of a reaction but not in the network's species map.
|
||||
LOG_ERROR(m_logger, "CRITICAL ERROR: Reactant species '{}' in reaction '{}' not found in network species map during conservation validation.",
|
||||
reactant.name(), reaction.id());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate total A and Z for products
|
||||
for (const auto& product : reaction.products()) {
|
||||
auto it = m_networkSpeciesMap.find(product.name());
|
||||
if (it != m_networkSpeciesMap.end()) {
|
||||
totalProductA += it->second.a();
|
||||
totalProductZ += it->second.z();
|
||||
} else {
|
||||
// Similar critical error for product species
|
||||
LOG_ERROR(m_logger, "CRITICAL ERROR: Product species '{}' in reaction '{}' not found in network species map during conservation validation.",
|
||||
product.name(), reaction.id());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Compare totals for conservation
|
||||
if (totalReactantA != totalProductA) {
|
||||
LOG_ERROR(m_logger, "Mass number (A) not conserved for reaction '{}': Reactants A={} vs Products A={}.",
|
||||
reaction.id(), totalReactantA, totalProductA);
|
||||
return false;
|
||||
}
|
||||
if (totalReactantZ != totalProductZ) {
|
||||
LOG_ERROR(m_logger, "Atomic number (Z) not conserved for reaction '{}': Reactants Z={} vs Products Z={}.",
|
||||
reaction.id(), totalReactantZ, totalProductZ);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO(m_logger, "Mass (A) and charge (Z) conservation validated successfully for all reactions.");
|
||||
return true; // All reactions passed the conservation check
|
||||
}
|
||||
|
||||
void GraphNetwork::validateComposition(const fourdst::composition::Composition &composition, double culling, double T9) {
|
||||
|
||||
// Check if the requested network has already been cached.
|
||||
// PERF: Rebuilding this should be pretty fast but it may be a good point of optimization in the future.
|
||||
const reaclib::REACLIBReactionSet validationReactionSet = build_reaclib_nuclear_network(composition, culling, T9);
|
||||
// TODO: need some more robust method here to
|
||||
// A. Build the basic network from the composition's species with non zero mass fractions.
|
||||
// B. rebuild a new composition from the reaction set's reactants + products (with the mass fractions from the things that are only products set to 0)
|
||||
// C. Rebuild the reaction set from the new composition
|
||||
// D. Cull reactions where all reactants have mass fractions below the culling threshold.
|
||||
// E. Be careful about maintaining caching through all of this
|
||||
|
||||
|
||||
// This allows for dynamic network modification while retaining caching for networks which are very similar.
|
||||
if (validationReactionSet != m_reactions) {
|
||||
LOG_INFO(m_logger, "Reaction set not cached. Rebuilding the reaction set for T9={} and culling={}.", T9, culling);
|
||||
m_reactions = validationReactionSet;
|
||||
syncInternalMaps(); // Re-sync internal maps after updating reactions. Note this will also retrace the AD tape.
|
||||
}
|
||||
}
|
||||
|
||||
// --- Generate Stoichiometry Matrix ---
|
||||
void GraphNetwork::generateStoichiometryMatrix() {
|
||||
LOG_INFO(m_logger, "Generating stoichiometry matrix...");
|
||||
|
||||
// Task 1: Set dimensions and initialize the matrix
|
||||
size_t numSpecies = m_networkSpecies.size();
|
||||
size_t numReactions = m_reactions.size();
|
||||
m_stoichiometryMatrix.resize(numSpecies, numReactions, false);
|
||||
|
||||
LOG_INFO(m_logger, "Stoichiometry matrix initialized with dimensions: {} rows (species) x {} columns (reactions).",
|
||||
numSpecies, numReactions);
|
||||
|
||||
// Task 2: Populate the stoichiometry matrix
|
||||
// Iterate through all reactions, assign them a column index, and fill in their stoichiometric coefficients.
|
||||
size_t reactionColumnIndex = 0;
|
||||
for (const auto& reaction : m_reactions) {
|
||||
// Get the net stoichiometry for the current reaction
|
||||
std::unordered_map<fourdst::atomic::Species, int> netStoichiometry = getNetReactionStoichiometry(reaction);
|
||||
|
||||
// Iterate through the species and their coefficients in the stoichiometry map
|
||||
for (const auto& pair : netStoichiometry) {
|
||||
const fourdst::atomic::Species& species = pair.first; // The Species object
|
||||
const int coefficient = pair.second; // The stoichiometric coefficient
|
||||
|
||||
// Find the row index for this species
|
||||
auto it = m_speciesToIndexMap.find(species);
|
||||
if (it != m_speciesToIndexMap.end()) {
|
||||
const size_t speciesRowIndex = it->second;
|
||||
// Set the matrix element. Boost.uBLAS handles sparse insertion.
|
||||
m_stoichiometryMatrix(speciesRowIndex, reactionColumnIndex) = coefficient;
|
||||
} else {
|
||||
// This scenario should ideally not happen if m_networkSpeciesMap and m_speciesToIndexMap are correctly synced
|
||||
LOG_ERROR(m_logger, "CRITICAL ERROR: Species '{}' from reaction '{}' stoichiometry not found in species to index map.",
|
||||
species.name(), reaction.id());
|
||||
throw std::runtime_error("Species not found in species to index map: " + std::string(species.name()));
|
||||
}
|
||||
}
|
||||
reactionColumnIndex++; // Move to the next column for the next reaction
|
||||
}
|
||||
|
||||
LOG_INFO(m_logger, "Stoichiometry matrix population complete. Number of non-zero elements: {}.",
|
||||
m_stoichiometryMatrix.nnz()); // Assuming nnz() exists for compressed_matrix
|
||||
}
|
||||
|
||||
void GraphNetwork::generateJacobianMatrix(const std::vector<double> &Y, const double T9,
|
||||
const double rho) {
|
||||
|
||||
LOG_INFO(m_logger, "Generating jacobian matrix for T9={}, rho={}..", T9, rho);
|
||||
const size_t numSpecies = m_networkSpecies.size();
|
||||
|
||||
// 1. Pack the input variables into a vector for CppAD
|
||||
std::vector<double> adInput(numSpecies + 2, 0.0); // +2 for T9 and rho
|
||||
for (size_t i = 0; i < numSpecies; ++i) {
|
||||
adInput[i] = Y[i];
|
||||
}
|
||||
adInput[numSpecies] = T9; // T9
|
||||
adInput[numSpecies + 1] = rho; // rho
|
||||
|
||||
// 2. Calculate the full jacobian
|
||||
const std::vector<double> dotY = m_rhsADFun.Jacobian(adInput);
|
||||
|
||||
// 3. Pack jacobian vector into sparse matrix
|
||||
m_jacobianMatrix.clear();
|
||||
for (size_t i = 0; i < numSpecies; ++i) {
|
||||
for (size_t j = 0; j < numSpecies; ++j) {
|
||||
const double value = dotY[i * (numSpecies + 2) + j];
|
||||
if (std::abs(value) > MIN_JACOBIAN_THRESHOLD) {
|
||||
m_jacobianMatrix(i, j) = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG_INFO(m_logger, "Jacobian matrix generated with dimensions: {} rows x {} columns.", m_jacobianMatrix.size1(), m_jacobianMatrix.size2());
|
||||
}
|
||||
|
||||
void GraphNetwork::detectStiff(const NetIn &netIn, const double T9, const unsigned long numSpecies, const boost::numeric::ublas::vector<double>& Y) {
|
||||
// --- Heuristic for automatic stiffness detection ---
|
||||
const std::vector<double> initial_y_stl(Y.begin(), Y.begin() + numSpecies);
|
||||
const auto [dydt, specificEnergyRate] = calculateAllDerivatives<double>(initial_y_stl, T9, netIn.density);
|
||||
const std::vector<double>& initial_dotY = dydt;
|
||||
|
||||
double min_destruction_timescale = std::numeric_limits<double>::max();
|
||||
|
||||
for (size_t i = 0; i < numSpecies; ++i) {
|
||||
if (Y(i) > MIN_ABUNDANCE_THRESHOLD && initial_dotY[i] < 0.0) {
|
||||
const double timescale = std::abs(Y(i) / initial_dotY[i]);
|
||||
if (timescale < min_destruction_timescale) {
|
||||
min_destruction_timescale = timescale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no species are being destroyed, the system is not stiff.
|
||||
if (min_destruction_timescale == std::numeric_limits<double>::max()) {
|
||||
LOG_INFO(m_logger, "No species are undergoing net destruction. Network is considered non-stiff.");
|
||||
m_stiff = false;
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr double saftey_factor = 10;
|
||||
const bool is_stiff = (netIn.dt0 > saftey_factor * min_destruction_timescale);
|
||||
|
||||
LOG_INFO(m_logger, "Fastest destruction timescale: {}. Initial dt0: {}. Stiffness detected: {}.",
|
||||
min_destruction_timescale, netIn.dt0, is_stiff ? "Yes" : "No");
|
||||
|
||||
if (is_stiff) {
|
||||
m_stiff = true;
|
||||
LOG_INFO(m_logger, "Network is detected as stiff.");
|
||||
} else {
|
||||
m_stiff = false;
|
||||
LOG_INFO(m_logger, "Network is detected as non-stiff.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GraphNetwork::exportToDot(const std::string &filename) const {
|
||||
LOG_INFO(m_logger, "Exporting network graph to DOT file: {}", filename);
|
||||
|
||||
std::ofstream dotFile(filename);
|
||||
if (!dotFile.is_open()) {
|
||||
LOG_ERROR(m_logger, "Failed to open file for writing: {}", filename);
|
||||
throw std::runtime_error("Failed to open file for writing: " + filename);
|
||||
}
|
||||
|
||||
dotFile << "digraph NuclearReactionNetwork {\n";
|
||||
dotFile << " graph [rankdir=LR, splines=true, overlap=false, bgcolor=\"#f0f0f0\"];\n";
|
||||
dotFile << " node [shape=circle, style=filled, fillcolor=\"#a7c7e7\", fontname=\"Helvetica\"];\n";
|
||||
dotFile << " edge [fontname=\"Helvetica\", fontsize=10];\n\n";
|
||||
|
||||
// 1. Define all species as nodes
|
||||
dotFile << " // --- Species Nodes ---\n";
|
||||
for (const auto& species : m_networkSpecies) {
|
||||
dotFile << " \"" << species.name() << "\" [label=\"" << species.name() << "\"];\n";
|
||||
}
|
||||
dotFile << "\n";
|
||||
|
||||
// 2. Define all reactions as intermediate nodes and connect them
|
||||
dotFile << " // --- Reaction Edges ---\n";
|
||||
for (const auto& reaction : m_reactions) {
|
||||
// Create a unique ID for the reaction node
|
||||
std::string reactionNodeId = "reaction_" + std::string(reaction.id());
|
||||
|
||||
// Define the reaction node (small, black dot)
|
||||
dotFile << " \"" << reactionNodeId << "\" [shape=point, fillcolor=black, width=0.1, height=0.1, label=\"\"];\n";
|
||||
|
||||
// Draw edges from reactants to the reaction node
|
||||
for (const auto& reactant : reaction.reactants()) {
|
||||
dotFile << " \"" << reactant.name() << "\" -> \"" << reactionNodeId << "\";\n";
|
||||
}
|
||||
|
||||
// Draw edges from the reaction node to products
|
||||
for (const auto& product : reaction.products()) {
|
||||
dotFile << " \"" << reactionNodeId << "\" -> \"" << product.name() << "\" [label=\"" << reaction.qValue() << " MeV\"];\n";
|
||||
}
|
||||
dotFile << "\n";
|
||||
}
|
||||
|
||||
dotFile << "}\n";
|
||||
dotFile.close();
|
||||
LOG_INFO(m_logger, "Successfully exported network to {}", filename);
|
||||
}
|
||||
|
||||
NetOut GraphNetwork::evaluate(const NetIn &netIn) {
|
||||
namespace ublas = boost::numeric::ublas;
|
||||
namespace odeint = boost::numeric::odeint;
|
||||
|
||||
const double T9 = netIn.temperature / 1e9; // Convert temperature from Kelvin to T9 (T9 = T / 1e9)
|
||||
// validateComposition(netIn.composition, netIn.culling, T9);
|
||||
|
||||
const unsigned long numSpecies = m_networkSpecies.size();
|
||||
constexpr double abs_tol = 1.0e-8;
|
||||
constexpr double rel_tol = 1.0e-8;
|
||||
|
||||
size_t stepCount = 0;
|
||||
|
||||
// TODO: Pull these out into configuration options
|
||||
|
||||
ODETerm rhs_functor(*this, T9, netIn.density);
|
||||
|
||||
|
||||
ublas::vector<double> Y(numSpecies + 1);
|
||||
for (size_t i = 0; i < numSpecies; ++i) {
|
||||
const auto& species = m_networkSpecies[i];
|
||||
// Get the mass fraction for this specific species from the input object
|
||||
try {
|
||||
Y(i) = netIn.composition.getMassFraction(std::string(species.name()));
|
||||
} catch (const std::runtime_error &e) {
|
||||
LOG_INFO(m_logger, "Species {} not in base composition, adding...", species.name());
|
||||
Y(i) = 0.0; // If the species is not in the composition, set its mass fraction to
|
||||
}
|
||||
}
|
||||
Y(numSpecies) = 0; // initial specific energy rate, will be updated later
|
||||
|
||||
detectStiff(netIn, T9, numSpecies, Y);
|
||||
m_stiff = false;
|
||||
|
||||
if (m_stiff) {
|
||||
JacobianTerm jacobian_functor(*this, T9, netIn.density);
|
||||
LOG_INFO(m_logger, "Making use of stiff ODE solver for network evaluation.");
|
||||
auto stepper = odeint::make_controlled<odeint::rosenbrock4<double>>(abs_tol, rel_tol);
|
||||
stepCount = odeint::integrate_adaptive(
|
||||
stepper,
|
||||
std::make_pair(rhs_functor, jacobian_functor),
|
||||
Y,
|
||||
0.0, // Start time
|
||||
netIn.tMax,
|
||||
netIn.dt0
|
||||
);
|
||||
|
||||
} else {
|
||||
LOG_INFO(m_logger, "Making use of ODE solver (non-stiff) for network evaluation.");
|
||||
using state_type = ublas::vector<double>;
|
||||
auto stepper = odeint::make_controlled<odeint::runge_kutta_dopri5<state_type>>(abs_tol, rel_tol);
|
||||
stepCount = odeint::integrate_adaptive(
|
||||
stepper,
|
||||
rhs_functor,
|
||||
Y,
|
||||
0.0, // Start time
|
||||
netIn.tMax,
|
||||
netIn.dt0
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
double sumY = 0.0;
|
||||
for (int i = 0; i < numSpecies; ++i) { sumY += Y(i); }
|
||||
for (int i = 0; i < numSpecies; ++i) { Y(i) /= sumY; }
|
||||
|
||||
// --- Marshall output variables ---
|
||||
// PERF: Im sure this step could be tuned to avoid so many copies, that is a job for another day
|
||||
std::vector<std::string> speciesNames;
|
||||
speciesNames.reserve(numSpecies);
|
||||
for (const auto& species : m_networkSpecies) {
|
||||
speciesNames.push_back(std::string(species.name()));
|
||||
}
|
||||
|
||||
std::vector<double> finalAbundances(Y.begin(), Y.begin() + numSpecies);
|
||||
fourdst::composition::Composition outputComposition(speciesNames, finalAbundances);
|
||||
outputComposition.finalize(true);
|
||||
|
||||
NetOut netOut;
|
||||
netOut.composition = outputComposition;
|
||||
netOut.num_steps = stepCount;
|
||||
netOut.energy = Y(numSpecies); // The last element in Y is the specific energy rate
|
||||
|
||||
return netOut;
|
||||
|
||||
}
|
||||
|
||||
void GraphNetwork::recordADTape() {
|
||||
LOG_INFO(m_logger, "Recording AD tape for the RHS calculation...");
|
||||
|
||||
// Task 1: Set dimensions and initialize the matrix
|
||||
const size_t numSpecies = m_networkSpecies.size();
|
||||
if (numSpecies == 0) {
|
||||
LOG_ERROR(m_logger, "Cannot record AD tape: No species in the network.");
|
||||
throw std::runtime_error("Cannot record AD tape: No species in the network.");
|
||||
}
|
||||
const size_t numADInputs = numSpecies + 2; // Note here that by not letting T9 and rho be independent variables, we are constraining the network to a constant temperature and density during each evaluation.
|
||||
|
||||
// --- CppAD Tape Recording ---
|
||||
// 1. Declare independent variable (adY)
|
||||
// We also initialize the dummy variable for tape recording (these tell CppAD what the derivative chain looks like).
|
||||
// Their numeric values are irrelevant except for in so far as they avoid numerical instabilities.
|
||||
|
||||
// Distribute total mass fraction uniformly between species in the dummy variable space
|
||||
const auto uniformMassFraction = static_cast<CppAD::AD<double>>(1.0 / numSpecies);
|
||||
std::vector<CppAD::AD<double>> adInput(numADInputs, uniformMassFraction);
|
||||
adInput[numSpecies] = 1.0; // Dummy T9
|
||||
adInput[numSpecies + 1] = 1.0; // Dummy rho
|
||||
|
||||
// 3. Declare independent variables (what CppAD will differentiate wrt.)
|
||||
// This also beings the tape recording process.
|
||||
CppAD::Independent(adInput);
|
||||
|
||||
std::vector<CppAD::AD<double>> adY(numSpecies);
|
||||
for(size_t i = 0; i < numSpecies; ++i) {
|
||||
adY[i] = adInput[i];
|
||||
}
|
||||
const CppAD::AD<double> adT9 = adInput[numSpecies];
|
||||
const CppAD::AD<double> adRho = adInput[numSpecies + 1];
|
||||
|
||||
|
||||
// 5. Call the actual templated function
|
||||
// We let T9 and rho be constant, so we pass them as fixed values.
|
||||
auto derivatives = calculateAllDerivatives<CppAD::AD<double>>(adY, adT9, adRho);
|
||||
|
||||
m_rhsADFun.Dependent(adInput, derivatives.dydt);
|
||||
|
||||
LOG_INFO(m_logger, "AD tape recorded successfully for the RHS calculation. Number of independent variables: {}.",
|
||||
adInput.size());
|
||||
}
|
||||
}
|
||||
307
src/network/lib/network.cpp
Normal file
307
src/network/lib/network.cpp
Normal file
@@ -0,0 +1,307 @@
|
||||
/* ***********************************************************************
|
||||
//
|
||||
// Copyright (C) 2025 -- The 4D-STAR Collaboration
|
||||
// File Authors: Aaron Dotter, 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 "gridfire/network.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "gridfire/approx8.h"
|
||||
#include "quill/LogMacros.h"
|
||||
#include "gridfire/reaclib.h"
|
||||
#include "gridfire/reactions.h"
|
||||
|
||||
namespace gridfire {
|
||||
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");
|
||||
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;
|
||||
}
|
||||
|
||||
NetOut Network::evaluate(const NetIn &netIn) {
|
||||
NetOut netOut;
|
||||
switch (m_format) {
|
||||
case APPROX8: {
|
||||
approx8::Approx8Network network;
|
||||
netOut = network.evaluate(netIn);
|
||||
break;
|
||||
}
|
||||
case UNKNOWN: {
|
||||
LOG_ERROR(m_logger, "Network format {} is not implemented.", FormatStringLookup.at(m_format));
|
||||
throw std::runtime_error("Network format not implemented.");
|
||||
}
|
||||
default: {
|
||||
LOG_ERROR(m_logger, "Unknown network format.");
|
||||
throw std::runtime_error("Unknown network format.");
|
||||
}
|
||||
}
|
||||
return netOut;
|
||||
}
|
||||
|
||||
LogicalReaction::LogicalReaction(const std::vector<reaclib::REACLIBReaction> &reactions) {
|
||||
if (reactions.empty()) {
|
||||
LOG_ERROR(m_logger, "Empty reaction set provided to LogicalReaction constructor.");
|
||||
throw std::runtime_error("Empty reaction set provided to LogicalReaction constructor.");
|
||||
}
|
||||
|
||||
const auto& first_reaction = reactions.front();
|
||||
m_peID = first_reaction.peName();
|
||||
m_reactants = first_reaction.reactants();
|
||||
m_products = first_reaction.products();
|
||||
m_qValue = first_reaction.qValue();
|
||||
m_reverse = first_reaction.is_reverse();
|
||||
m_chapter = first_reaction.chapter();
|
||||
m_rates.reserve(reactions.size());
|
||||
for (const auto& reaction : reactions) {
|
||||
m_rates.push_back(reaction.rateFits());
|
||||
if (std::abs(reaction.qValue() - m_qValue) > 1e-6) {
|
||||
LOG_ERROR(m_logger, "Inconsistent Q-values in reactions {}. All reactions must have the same Q-value.", m_peID);
|
||||
throw std::runtime_error("Inconsistent Q-values in reactions. All reactions must have the same Q-value.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogicalReaction::LogicalReaction(const reaclib::REACLIBReaction &first_reaction) {
|
||||
m_peID = first_reaction.peName();
|
||||
m_reactants = first_reaction.reactants();
|
||||
m_products = first_reaction.products();
|
||||
m_qValue = first_reaction.qValue();
|
||||
m_reverse = first_reaction.is_reverse();
|
||||
m_chapter = first_reaction.chapter();
|
||||
m_rates.reserve(1);
|
||||
m_rates.push_back(first_reaction.rateFits());
|
||||
|
||||
}
|
||||
|
||||
void LogicalReaction::add_reaction(const reaclib::REACLIBReaction &reaction) {
|
||||
if (std::abs(reaction.qValue() - m_qValue > 1e-6)) {
|
||||
LOG_ERROR(m_logger, "Inconsistent Q-values in reactions {}. All reactions must have the same Q-value.", m_peID);
|
||||
throw std::runtime_error("Inconsistent Q-values in reactions. All reactions must have the same Q-value.");
|
||||
}
|
||||
m_rates.push_back(reaction.rateFits());
|
||||
}
|
||||
|
||||
auto LogicalReaction::begin() {
|
||||
return m_rates.begin();
|
||||
}
|
||||
auto LogicalReaction::begin() const {
|
||||
return m_rates.cbegin();
|
||||
}
|
||||
auto LogicalReaction::end() {
|
||||
return m_rates.end();
|
||||
}
|
||||
auto LogicalReaction::end() const {
|
||||
return m_rates.cend();
|
||||
}
|
||||
|
||||
LogicalReactionSet::LogicalReactionSet(const std::vector<LogicalReaction> &reactions) {
|
||||
if (reactions.empty()) {
|
||||
LOG_ERROR(m_logger, "Empty reaction set provided to LogicalReactionSet constructor.");
|
||||
throw std::runtime_error("Empty reaction set provided to LogicalReactionSet constructor.");
|
||||
}
|
||||
|
||||
for (const auto& reaction : reactions) {
|
||||
m_reactions.push_back(reaction);
|
||||
}
|
||||
|
||||
m_reactionNameMap.reserve(m_reactions.size());
|
||||
for (const auto& reaction : m_reactions) {
|
||||
if (m_reactionNameMap.contains(reaction.id())) {
|
||||
LOG_ERROR(m_logger, "Duplicate reaction ID '{}' found in LogicalReactionSet.", reaction.id());
|
||||
throw std::runtime_error("Duplicate reaction ID found in LogicalReactionSet: " + std::string(reaction.id()));
|
||||
}
|
||||
m_reactionNameMap[reaction.id()] = reaction;
|
||||
}
|
||||
}
|
||||
|
||||
LogicalReactionSet::LogicalReactionSet(const std::vector<reaclib::REACLIBReaction> &reactions) {
|
||||
std::vector<std::string_view> uniquePeNames;
|
||||
for (const auto& reaction: reactions) {
|
||||
if (uniquePeNames.empty()) {
|
||||
uniquePeNames.emplace_back(reaction.peName());
|
||||
} else if (std::ranges::find(uniquePeNames, reaction.peName()) == uniquePeNames.end()) {
|
||||
uniquePeNames.emplace_back(reaction.peName());
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& peName : uniquePeNames) {
|
||||
std::vector<reaclib::REACLIBReaction> reactionsForPe;
|
||||
for (const auto& reaction : reactions) {
|
||||
if (reaction.peName() == peName) {
|
||||
reactionsForPe.push_back(reaction);
|
||||
}
|
||||
}
|
||||
m_reactions.emplace_back(reactionsForPe);
|
||||
}
|
||||
|
||||
m_reactionNameMap.reserve(m_reactions.size());
|
||||
for (const auto& reaction : m_reactions) {
|
||||
if (m_reactionNameMap.contains(reaction.id())) {
|
||||
LOG_ERROR(m_logger, "Duplicate reaction ID '{}' found in LogicalReactionSet.", reaction.id());
|
||||
throw std::runtime_error("Duplicate reaction ID found in LogicalReactionSet: " + std::string(reaction.id()));
|
||||
}
|
||||
m_reactionNameMap[reaction.id()] = reaction;
|
||||
}
|
||||
}
|
||||
|
||||
LogicalReactionSet::LogicalReactionSet(const reaclib::REACLIBReactionSet &reactionSet) {
|
||||
LogicalReactionSet(reactionSet.get_reactions());
|
||||
}
|
||||
|
||||
void LogicalReactionSet::add_reaction(const LogicalReaction& reaction) {
|
||||
if (m_reactionNameMap.contains(reaction.id())) {
|
||||
LOG_WARNING(m_logger, "Reaction {} already exists in the set. Not adding again.", reaction.id());
|
||||
std::cerr << "Warning: Reaction " << reaction.id() << " already exists in the set. Not adding again." << std::endl;
|
||||
return;
|
||||
}
|
||||
m_reactions.push_back(reaction);
|
||||
m_reactionNameMap[reaction.id()] = reaction;
|
||||
}
|
||||
|
||||
void LogicalReactionSet::add_reaction(const reaclib::REACLIBReaction& reaction) {
|
||||
if (m_reactionNameMap.contains(reaction.id())) {
|
||||
m_reactionNameMap[reaction.id()].add_reaction(reaction);
|
||||
} else {
|
||||
const LogicalReaction logicalReaction(reaction);
|
||||
m_reactions.push_back(logicalReaction);
|
||||
m_reactionNameMap[reaction.id()] = logicalReaction;
|
||||
}
|
||||
}
|
||||
|
||||
bool LogicalReactionSet::contains(const std::string_view &id) const {
|
||||
for (const auto& reaction : m_reactions) {
|
||||
if (reaction.id() == id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LogicalReactionSet::contains(const LogicalReaction &reactions) const {
|
||||
for (const auto& reaction : m_reactions) {
|
||||
if (reaction.id() == reactions.id()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LogicalReactionSet::contains_species(const fourdst::atomic::Species &species) const {
|
||||
return contains_reactant(species) || contains_product(species);
|
||||
}
|
||||
|
||||
bool LogicalReactionSet::contains_reactant(const fourdst::atomic::Species &species) const {
|
||||
for (const auto& reaction : m_reactions) {
|
||||
if (std::ranges::find(reaction.reactants(), species) != reaction.reactants().end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LogicalReactionSet::contains_product(const fourdst::atomic::Species &species) const {
|
||||
for (const auto& reaction : m_reactions) {
|
||||
if (std::ranges::find(reaction.products(), species) != reaction.products().end()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const LogicalReaction & LogicalReactionSet::operator[](size_t index) const {
|
||||
return m_reactions.at(index);
|
||||
}
|
||||
|
||||
const LogicalReaction & LogicalReactionSet::operator[](const std::string_view &id) const {
|
||||
return m_reactionNameMap.at(id);
|
||||
}
|
||||
|
||||
auto LogicalReactionSet::begin() {
|
||||
return m_reactions.begin();
|
||||
}
|
||||
|
||||
auto LogicalReactionSet::begin() const {
|
||||
return m_reactions.cbegin();
|
||||
}
|
||||
|
||||
auto LogicalReactionSet::end() {
|
||||
return m_reactions.end();
|
||||
}
|
||||
|
||||
auto LogicalReactionSet::end() const {
|
||||
return m_reactions.cend();
|
||||
}
|
||||
|
||||
LogicalReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition) {
|
||||
using namespace reaclib;
|
||||
REACLIBReactionSet reactions;
|
||||
auto logger = fourdst::logging::LogManager::getInstance().getLogger("log");
|
||||
|
||||
if (!s_initialized) {
|
||||
LOG_INFO(logger, "REACLIB reactions not initialized. Calling initializeAllReaclibReactions()...");
|
||||
initializeAllReaclibReactions();
|
||||
}
|
||||
|
||||
for (const auto &reaction: s_all_reaclib_reactions | std::views::values) {
|
||||
bool gotReaction = true;
|
||||
const auto& reactants = reaction.reactants();
|
||||
for (const auto& reactant : reactants) {
|
||||
if (!composition.contains(reactant)) {
|
||||
gotReaction = false;
|
||||
break; // If any reactant is not in the composition, skip this reaction
|
||||
}
|
||||
}
|
||||
if (gotReaction) {
|
||||
LOG_INFO(logger, "Adding reaction {} to REACLIB reaction set.", reaction.peName());
|
||||
reactions.add_reaction(reaction);
|
||||
}
|
||||
}
|
||||
reactions.sort();
|
||||
return LogicalReactionSet(reactions);
|
||||
}
|
||||
|
||||
LogicalReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition, const double culling, const double T9) {
|
||||
using namespace reaclib;
|
||||
LogicalReactionSet allReactions = build_reaclib_nuclear_network(composition);
|
||||
LogicalReactionSet reactions;
|
||||
for (const auto& reaction : allReactions) {
|
||||
if (reaction.calculate_rate(T9) >= culling) {
|
||||
reactions.add_reaction(reaction);
|
||||
}
|
||||
}
|
||||
return reactions;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user