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:
10
README.md
10
README.md
@@ -156,9 +156,17 @@ int main() {
|
||||
```
|
||||
Save that file to `main.cpp` and compile it with the following command:
|
||||
```bash
|
||||
g++ main.cpp $(pkg-config --cflags --libs gridfire) -o main
|
||||
clang++ main.cpp $(pkg-config --cflags --libs gridfire) -I/opt/homebrew/include -o main -std=c++23
|
||||
./main
|
||||
```
|
||||
> Note that here I have included the `-I/opt/homebrew/include` flag to specify the header location for boost
|
||||
> on my system. This very well may already be in your compiler's include path, or if not it might be in a different
|
||||
> location. That is all very system dependent. You can try to get a sense of where the boost headers are located
|
||||
> by looking at the `build/compile-commmands.json` file generate by meson after running `meson setup build`.
|
||||
|
||||
|
||||
> -std=c++23 or -std=c++20 is required as we use some C++20 feature in GridFire (specifically concepts). Compiling with
|
||||
> C++23 is **strongly** recommended.
|
||||
|
||||
Using a different network is as simple as changing the type of the `network` variable to the desired network type. For
|
||||
example, if you wanted to use the `gridfire::Approx8Network`, you would change the line
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <unordered_map>
|
||||
#include "atomicSpecies.h"
|
||||
#include "fourdst/composition/atomicSpecies.h"
|
||||
|
||||
#include "cppad/cppad.hpp"
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace gridfire::reaclib {
|
||||
class REACLIBReaction {
|
||||
private:
|
||||
std::string m_id; ///< Unique identifier for the reaction, generated by the Python script.
|
||||
std::string_view m_peName; ///< Name of the reaction in (projectile, ejectile) notation (e.g. p(p, g)d)
|
||||
int m_chapter; ///< Chapter number from the REACLIB database, defining the reaction structure.
|
||||
std::vector<fourdst::atomic::Species> m_reactantNames; ///< Names of the reactant species involved in the reaction.
|
||||
std::vector<fourdst::atomic::Species> m_productNames; ///< Names of the product species produced by the reaction.
|
||||
@@ -56,6 +57,7 @@ namespace gridfire::reaclib {
|
||||
*/
|
||||
REACLIBReaction(
|
||||
std::string id,
|
||||
std::string_view peName,
|
||||
int chapter,
|
||||
std::vector<fourdst::atomic::Species> reactants,
|
||||
std::vector<fourdst::atomic::Species> products,
|
||||
@@ -64,6 +66,7 @@ namespace gridfire::reaclib {
|
||||
RateFitSet sets,
|
||||
bool reverse = false)
|
||||
: m_id(std::move(id)),
|
||||
m_peName(peName),
|
||||
m_chapter(chapter),
|
||||
m_reactantNames(std::move(reactants)),
|
||||
m_productNames(std::move(products)),
|
||||
@@ -87,6 +90,8 @@ namespace gridfire::reaclib {
|
||||
|
||||
[[nodiscard]] const std::string& id() const { return m_id; }
|
||||
|
||||
[[nodiscard]] std::string_view peName() const { return m_peName; }
|
||||
|
||||
[[nodiscard]] int chapter() const { return m_chapter; }
|
||||
|
||||
[[nodiscard]] const std::vector<fourdst::atomic::Species>& reactants() const { return m_reactantNames; }
|
||||
@@ -99,8 +104,10 @@ namespace gridfire::reaclib {
|
||||
|
||||
[[nodiscard]] bool is_reverse() const { return m_reverse; }
|
||||
|
||||
[[nodiscard]] RateFitSet rateFits() const { return m_rateSets; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const REACLIBReaction& reaction) {
|
||||
os << "REACLIBReaction(" << reaction.m_id << ", "
|
||||
os << "REACLIBReaction(" << reaction.m_peName << ", "
|
||||
<< "Chapter: " << reaction.m_chapter << ")";
|
||||
return os;
|
||||
}
|
||||
@@ -234,6 +241,7 @@ namespace gridfire::reaclib {
|
||||
inline bool operator!=(const REACLIBReactionSet& lhs, const REACLIBReactionSet& rhs) {
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace std {
|
||||
22021
assets/static/reaclib/include/gridfire/reactions.h
Normal file
22021
assets/static/reaclib/include/gridfire/reactions.h
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,20 @@
|
||||
required_headers = [
|
||||
'reaclib.h',
|
||||
'reactions.h',
|
||||
'gridfire/reaclib.h',
|
||||
'gridfire/reactions.h',
|
||||
]
|
||||
|
||||
foreach h : required_headers
|
||||
if not cpp.has_header(h, include_directories: include_directories('include'))
|
||||
error('SERiF requires the header file ' + h + ' to be present in the assets/static/reaclib/include directory.')
|
||||
error('GridFire requires the header file ' + h + ' to be present in the assets/static/reaclib/include/gridfire directory.')
|
||||
endif
|
||||
endforeach
|
||||
reaclib_reactions_dep = declare_dependency(
|
||||
include_directories: include_directories('include'),
|
||||
)
|
||||
message('✅ SERiF reaclib_reactions dependency declared')
|
||||
message('✅ GridFire reaclib_reactions dependency declared')
|
||||
|
||||
to_install_headers = [
|
||||
'include/gridfire/reaclib.h',
|
||||
'include/gridfire/reactions.h',
|
||||
]
|
||||
install_headers(to_install_headers, subdir: 'gridfire/gridfire')
|
||||
|
||||
@@ -2,4 +2,8 @@ cppad_dep = declare_dependency(
|
||||
include_directories: include_directories('include'),
|
||||
)
|
||||
|
||||
message('Registering CppAD headers for installation...')
|
||||
install_subdir('include/cppad', install_dir: get_option('includedir'))
|
||||
message('Done registering CppAD headers for installation!')
|
||||
|
||||
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
config_p = subproject('libconfig')
|
||||
config_dep = config_p.get_variable('config_dep')
|
||||
libconfig = config_p.get_variable('libconfig')
|
||||
@@ -1,2 +1,3 @@
|
||||
const_p = subproject('libconstants')
|
||||
const_dep = const_p.get_variable('const_dep')
|
||||
libconst = const_p.get_variable('libconst')
|
||||
@@ -1,4 +1,5 @@
|
||||
logging_p = subproject('liblogging')
|
||||
liblogging = logging_p.get_variable('liblogging')
|
||||
|
||||
logging_dep = logging_p.get_variable('logging_dep')
|
||||
quill_dep = logging_p.get_variable('quill_dep')
|
||||
|
||||
@@ -39,7 +39,7 @@ pkg.generate(
|
||||
name: 'gridfire',
|
||||
description: 'GridFire nuclear reaction network solver',
|
||||
version: meson.project_version(),
|
||||
libraries: [libnetwork, libcomposition],
|
||||
libraries: [libnetwork, libcomposition, libconfig, libconst, liblogging],
|
||||
subdirs: ['gridfire'],
|
||||
filebase: 'gridfire',
|
||||
install_dir: join_paths(get_option('libdir'), 'pkgconfig')
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include <boost/numeric/odeint.hpp>
|
||||
|
||||
#include "network.h"
|
||||
#include "gridfire/network.h"
|
||||
|
||||
/**
|
||||
* @file approx8.h
|
||||
@@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "atomicSpecies.h"
|
||||
#include "composition.h"
|
||||
#include "network.h"
|
||||
#include "reaclib.h"
|
||||
#include "fourdst/composition/atomicSpecies.h"
|
||||
#include "fourdst/composition/composition.h"
|
||||
|
||||
#include "gridfire/network.h"
|
||||
#include "gridfire/reaclib.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@@ -93,6 +94,8 @@ namespace gridfire {
|
||||
explicit GraphNetwork(const fourdst::composition::Composition &composition,
|
||||
const double cullingThreshold, const double T9);
|
||||
|
||||
explicit GraphNetwork(const reaclib::REACLIBReactionSet& reactions);
|
||||
|
||||
/**
|
||||
* @brief Evolve the network for the given input conditions.
|
||||
*
|
||||
@@ -171,14 +174,14 @@ namespace gridfire {
|
||||
* @note This is not yet implemented; however, it will allow for easy detection of things like the CNO cycle.
|
||||
* @deprecated Not implemented.
|
||||
*/
|
||||
[[deprecated("not implimented")]] std::vector<std::vector<std::string>> detectCycles() const = delete;
|
||||
[[deprecated("not implimented")]] [[nodiscard]] std::vector<std::vector<std::string>> detectCycles() const = delete;
|
||||
|
||||
/**
|
||||
* @brief Perform a topological sort of the reactions (not implemented).
|
||||
* @return Vector of reaction IDs in topologically sorted order.
|
||||
* @deprecated Not implemented.
|
||||
*/
|
||||
[[deprecated("not implimented")]] std::vector<std::string> topologicalSortReactions() const = delete;
|
||||
[[deprecated("not implimented")]] [[nodiscard]] std::vector<std::string> topologicalSortReactions() const = delete;
|
||||
|
||||
/**
|
||||
* @brief Export the network to a DOT file for visualization.
|
||||
@@ -227,7 +230,7 @@ namespace gridfire {
|
||||
* @note this functor does not need auto differentiation to it called the <double> version of calculateAllDerivatives.
|
||||
*/
|
||||
void operator()(const boost::numeric::ublas::vector<double>&Y, boost::numeric::ublas::vector<double>& dYdt, double /* t */) const {
|
||||
const std::vector<double> y(Y.begin(), Y.begin() + m_numSpecies);
|
||||
const std::vector<double> y(Y.begin(), m_numSpecies + Y.begin());
|
||||
|
||||
StepDerivatives<double> derivatives = m_network.calculateAllDerivatives<double>(y, m_T9, m_rho);
|
||||
|
||||
@@ -344,7 +347,7 @@ namespace gridfire {
|
||||
*
|
||||
* @note Logs errors for any violations.
|
||||
*/
|
||||
bool validateConservation() const;
|
||||
[[nodiscard]] bool validateConservation() const;
|
||||
|
||||
/**
|
||||
* @brief Validate and update the network composition if needed.
|
||||
@@ -391,7 +394,7 @@ namespace gridfire {
|
||||
* @param T9 Temperature in 10^9 K.
|
||||
* @param rho Density in g/cm^3.
|
||||
*/
|
||||
void generateJacobianMatrix(const std::vector<double>& Y, const double T9, const double rho);
|
||||
void generateJacobianMatrix(const std::vector<double>& Y, double T9, const double rho);
|
||||
|
||||
/**
|
||||
* @brief Calculate the right-hand side (dY/dt) for the ODE system.
|
||||
@@ -403,7 +406,7 @@ namespace gridfire {
|
||||
*/
|
||||
template <IsArithmeticOrAD GeneralScalarType>
|
||||
std::vector<GeneralScalarType> calculateRHS(const std::vector<GeneralScalarType> &Y, const GeneralScalarType T9,
|
||||
const GeneralScalarType rho) const;
|
||||
GeneralScalarType rho) const;
|
||||
|
||||
/**
|
||||
* @brief Calculate the reaction rate for a given reaction in dimensions of particles per cm^3 per second.
|
||||
@@ -443,7 +446,7 @@ namespace gridfire {
|
||||
* @param numSpecies Number of species.
|
||||
* @param Y Vector of abundances.
|
||||
*/
|
||||
void detectStiff(const NetIn &netIn, double T9, double numSpecies, const boost::numeric::ublas::vector<double>& Y);
|
||||
void detectStiff(const NetIn &netIn, double T9, unsigned long numSpecies, const boost::numeric::ublas::vector<double>& Y);
|
||||
|
||||
};
|
||||
|
||||
@@ -497,10 +500,10 @@ namespace gridfire {
|
||||
const auto &constants = fourdst::constant::Constants::getInstance();
|
||||
|
||||
const auto u = constants.get("u"); // Atomic mass unit in g/mol
|
||||
const GeneralScalarType uValue = static_cast<GeneralScalarType>(u.value); // Convert to double for calculations
|
||||
const auto uValue = static_cast<GeneralScalarType>(u.value); // Convert to double for calculations
|
||||
const GeneralScalarType k_reaction = reaction.calculate_rate(T9);
|
||||
|
||||
GeneralScalarType reactant_product_or_particle_densities = static_cast<GeneralScalarType>(1.0);
|
||||
auto reactant_product_or_particle_densities = static_cast<GeneralScalarType>(1.0);
|
||||
|
||||
std::unordered_map<std::string, int> reactant_counts;
|
||||
reactant_counts.reserve(reaction.reactants().size());
|
||||
@@ -508,8 +511,8 @@ namespace gridfire {
|
||||
reactant_counts[std::string(reactant.name())]++;
|
||||
}
|
||||
|
||||
const GeneralScalarType minAbundanceThreshold = static_cast<GeneralScalarType>(MIN_ABUNDANCE_THRESHOLD);
|
||||
const GeneralScalarType minDensityThreshold = static_cast<GeneralScalarType>(MIN_DENSITY_THRESHOLD);
|
||||
const auto minAbundanceThreshold = static_cast<GeneralScalarType>(MIN_ABUNDANCE_THRESHOLD);
|
||||
const auto minDensityThreshold = static_cast<GeneralScalarType>(MIN_DENSITY_THRESHOLD);
|
||||
|
||||
if (rho < minDensityThreshold) {
|
||||
// LOG_INFO(m_logger, "Density is below the minimum threshold ({} g/cm^3), returning zero reaction rate for reaction '{}'.",
|
||||
@@ -531,16 +534,13 @@ namespace gridfire {
|
||||
return static_cast<GeneralScalarType>(0.0); // If any reactant is below a threshold, return zero rate.
|
||||
}
|
||||
|
||||
GeneralScalarType atomicMassAMU = static_cast<GeneralScalarType>(m_networkSpecies[species_index].mass());
|
||||
auto atomicMassAMU = static_cast<GeneralScalarType>(m_networkSpecies[species_index].mass());
|
||||
|
||||
// Convert to number density
|
||||
GeneralScalarType ni;
|
||||
const GeneralScalarType denominator = atomicMassAMU * uValue;
|
||||
if (denominator > minDensityThreshold) {
|
||||
assert (denominator > 0.0);
|
||||
ni = (Yi * rho) / (denominator);
|
||||
} else {
|
||||
ni = static_cast<GeneralScalarType>(0.0);
|
||||
}
|
||||
|
||||
reactant_product_or_particle_densities *= ni;
|
||||
|
||||
@@ -551,7 +551,7 @@ namespace gridfire {
|
||||
}
|
||||
const GeneralScalarType Na = static_cast<GeneralScalarType>(constants.get("N_a").value); // Avogadro's number in mol^-1
|
||||
const int numReactants = static_cast<int>(reaction.reactants().size());
|
||||
GeneralScalarType molarCorrectionFactor = static_cast<GeneralScalarType>(1.0); // No correction needed for single reactant reactions
|
||||
auto molarCorrectionFactor = static_cast<GeneralScalarType>(1.0); // No correction needed for single reactant reactions
|
||||
if (numReactants > 1) {
|
||||
molarCorrectionFactor = CppAD::pow(Na, static_cast<GeneralScalarType>(reaction.reactants().size() - 1));
|
||||
}
|
||||
@@ -22,17 +22,19 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "logging.h"
|
||||
#include "config.h"
|
||||
#include "fourdst/logging/logging.h"
|
||||
#include "fourdst/config/config.h"
|
||||
#include "fourdst/composition/species.h"
|
||||
#include "quill/Logger.h"
|
||||
#include "composition.h"
|
||||
#include "reaclib.h"
|
||||
#include "fourdst/composition/composition.h"
|
||||
#include "gridfire/reaclib.h"
|
||||
#include <unordered_map>
|
||||
|
||||
#include "const.h"
|
||||
#include "fourdst/constants/const.h"
|
||||
|
||||
namespace gridfire {
|
||||
|
||||
|
||||
enum NetworkFormat {
|
||||
APPROX8, ///< Approx8 nuclear reaction network format.
|
||||
REACLIB, ///< General REACLIB nuclear reaction network format.
|
||||
@@ -141,19 +143,105 @@ namespace gridfire {
|
||||
bool m_stiff = false; ///< Flag indicating if the network is stiff
|
||||
};
|
||||
|
||||
class ReaclibNetwork final : public Network {
|
||||
class LogicalReaction {
|
||||
public:
|
||||
explicit ReaclibNetwork(const NetworkFormat format = NetworkFormat::APPROX8);
|
||||
explicit LogicalReaction(const std::vector<reaclib::REACLIBReaction> &reactions);
|
||||
explicit LogicalReaction(const reaclib::REACLIBReaction &reaction);
|
||||
void add_reaction(const reaclib::REACLIBReaction& reaction);
|
||||
|
||||
explicit ReaclibNetwork(fourdst::composition::Composition composition, const NetworkFormat format = NetworkFormat::APPROX8);
|
||||
template <typename T>
|
||||
[[nodiscard]] T calculate_rate(const T T9) const {
|
||||
T sum = static_cast<T>(0.0);
|
||||
const T T913 = CppAD::pow(T9, 1.0/3.0);
|
||||
const T T953 = CppAD::pow(T9, 5.0/3.0);
|
||||
const T logT9 = CppAD::log(T9);
|
||||
for (const auto& rate : m_rates) {
|
||||
const T exponent = rate.a0 +
|
||||
rate.a1 / T9 +
|
||||
rate.a2 / T913 +
|
||||
rate.a3 * T913 +
|
||||
rate.a4 * T9 +
|
||||
rate.a5 * T953 +
|
||||
rate.a6 * logT9;
|
||||
sum += CppAD::exp(exponent);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
[[nodiscard]] const std::string& id() const {return std::string(m_peID); }
|
||||
|
||||
[[nodiscard]] std::string_view peName() const { return m_peID; }
|
||||
|
||||
[[nodiscard]] int chapter() const { return m_chapter; }
|
||||
|
||||
[[nodiscard]] const std::vector<fourdst::atomic::Species>& reactants() const { return m_reactants; }
|
||||
|
||||
[[nodiscard]] const std::vector<fourdst::atomic::Species>& products() const { return m_products; }
|
||||
|
||||
[[nodiscard]] double qValue() const { return m_qValue; }
|
||||
|
||||
[[nodiscard]] bool is_reverse() const { return m_reverse; }
|
||||
|
||||
|
||||
auto begin();
|
||||
auto begin() const;
|
||||
auto end();
|
||||
auto end() const;
|
||||
|
||||
NetOut evaluate(const NetIn &netIn) override;
|
||||
private:
|
||||
reaclib::REACLIBReactionSet m_reactions; ///< Set of REACLIB reactions
|
||||
const quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
|
||||
std::string_view m_peID;
|
||||
std::vector<fourdst::atomic::Species> m_reactants; ///< Reactants of the reaction
|
||||
std::vector<fourdst::atomic::Species> m_products; ///< Products of the reaction
|
||||
double m_qValue = 0.0; ///< Q-value of the reaction
|
||||
bool m_reverse = false; ///< True if the reaction is reverse
|
||||
int m_chapter;
|
||||
|
||||
std::vector<reaclib::RateFitSet> m_rates;
|
||||
|
||||
};
|
||||
|
||||
reaclib::REACLIBReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition);
|
||||
reaclib::REACLIBReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition, double culling, double T9 = 1.0);
|
||||
class LogicalReactionSet {
|
||||
public:
|
||||
LogicalReactionSet() = default;
|
||||
explicit LogicalReactionSet(const std::vector<LogicalReaction>& reactions);
|
||||
explicit LogicalReactionSet(const std::vector<reaclib::REACLIBReaction>& reactions);
|
||||
explicit LogicalReactionSet(const reaclib::REACLIBReactionSet& reactionSet);
|
||||
|
||||
void add_reaction(const LogicalReaction& reaction);
|
||||
void add_reaction(const reaclib::REACLIBReaction& reaction);
|
||||
|
||||
void remove_reaction(const LogicalReaction& reaction);
|
||||
|
||||
[[nodiscard]] bool contains(const std::string_view& id) const;
|
||||
[[nodiscard]] bool contains(const LogicalReaction& reactions) const;
|
||||
[[nodiscard]] bool contains(const reaclib::REACLIBReaction& reaction) const;
|
||||
|
||||
[[nodiscard]] size_t size() const;
|
||||
|
||||
void sort(double T9=1.0);
|
||||
|
||||
bool contains_species(const fourdst::atomic::Species &species) const;
|
||||
bool contains_reactant(const fourdst::atomic::Species &species) const;
|
||||
bool contains_product(const fourdst::atomic::Species &species) const;
|
||||
|
||||
[[nodiscard]] const LogicalReaction& operator[](size_t index) const;
|
||||
[[nodiscard]] const LogicalReaction& operator[](const std::string_view& id) const;
|
||||
|
||||
auto begin();
|
||||
auto begin() const;
|
||||
auto end();
|
||||
auto end() const;
|
||||
|
||||
|
||||
private:
|
||||
const quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log");
|
||||
std::vector<LogicalReaction> m_reactions;
|
||||
std::unordered_map<std::string_view, LogicalReaction&> m_reactionNameMap;
|
||||
};
|
||||
|
||||
LogicalReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition);
|
||||
LogicalReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition, double culling, double T9 = 1.0);
|
||||
|
||||
|
||||
} // namespace nuclearNetwork
|
||||
@@ -24,12 +24,12 @@
|
||||
|
||||
#include <boost/numeric/odeint.hpp>
|
||||
|
||||
#include "const.h"
|
||||
#include "config.h"
|
||||
#include "fourdst/constants/const.h"
|
||||
#include "fourdst/config/config.h"
|
||||
#include "quill/LogMacros.h"
|
||||
|
||||
#include "approx8.h"
|
||||
#include "network.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
|
||||
@@ -80,10 +80,6 @@ namespace gridfire::approx8{
|
||||
//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){
|
||||
if (a.size() != b.size()) {
|
||||
throw std::runtime_error("Error: array size mismatch in sum_product");
|
||||
}
|
||||
|
||||
double sum=0;
|
||||
for (size_t i=0; i < a.size(); i++) {
|
||||
sum += a[i] * b[i];
|
||||
@@ -1,9 +1,9 @@
|
||||
#include "netgraph.h"
|
||||
#include "atomicSpecies.h"
|
||||
#include "const.h"
|
||||
#include "network.h"
|
||||
#include "reaclib.h"
|
||||
#include "species.h"
|
||||
#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"
|
||||
|
||||
@@ -40,10 +40,17 @@ namespace gridfire {
|
||||
syncInternalMaps();
|
||||
}
|
||||
|
||||
GraphNetwork::GraphNetwork(const reaclib::REACLIBReactionSet &reactions) :
|
||||
Network(REACLIB),
|
||||
m_reactions(reactions) {
|
||||
syncInternalMaps();
|
||||
}
|
||||
|
||||
void GraphNetwork::syncInternalMaps() {
|
||||
collectNetworkSpecies();
|
||||
populateReactionIDMap();
|
||||
populateSpeciesToIndexMap();
|
||||
generateStoichiometryMatrix();
|
||||
reserveJacobianMatrix();
|
||||
recordADTape();
|
||||
}
|
||||
@@ -303,36 +310,44 @@ namespace gridfire {
|
||||
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 double numSpecies, const boost::numeric::ublas::vector<double>& Y) {
|
||||
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); // Copy only the species abundances, not the specific energy rate
|
||||
const auto derivatives = calculateAllDerivatives<double>(initial_y_stl, T9, netIn.density);
|
||||
const std::vector<double>& initial_dotY = derivatives.dydt;
|
||||
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_timescale = std::numeric_limits<double>::max();
|
||||
double max_timescale = 0.0;
|
||||
double min_destruction_timescale = std::numeric_limits<double>::max();
|
||||
|
||||
for (size_t i = 0; i < numSpecies; ++i) {
|
||||
if (std::abs(initial_dotY[i]) > 0) {
|
||||
if (Y(i) > MIN_ABUNDANCE_THRESHOLD && initial_dotY[i] < 0.0) {
|
||||
const double timescale = std::abs(Y(i) / initial_dotY[i]);
|
||||
if (timescale > max_timescale) {max_timescale = timescale;}
|
||||
if (timescale < min_timescale) {min_timescale = timescale;}
|
||||
if (timescale < min_destruction_timescale) {
|
||||
min_destruction_timescale = timescale;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const double stiffnessRatio = max_timescale / min_timescale;
|
||||
|
||||
// TODO: Pull this out into a configuration option or a more sophisticated heuristic.
|
||||
constexpr double stiffnessThreshold = 1.0e6; // This is a heuristic threshold, can be tuned based on network characteristics.
|
||||
|
||||
LOG_INFO(m_logger, "Stiffness ratio is {} (max timescale: {}, min timescale: {}).", stiffnessRatio, max_timescale, min_timescale);
|
||||
if (stiffnessRatio > stiffnessThreshold) {
|
||||
LOG_INFO(m_logger, "Network is detected to be stiff. Using stiff ODE solver.");
|
||||
m_stiff = true;
|
||||
} else {
|
||||
LOG_INFO(m_logger, "Network is detected to be non-stiff. Using non-stiff ODE solver.");
|
||||
// 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 {
|
||||
@@ -387,13 +402,13 @@ namespace gridfire {
|
||||
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);
|
||||
// validateComposition(netIn.composition, netIn.culling, T9);
|
||||
|
||||
const double numSpecies = m_networkSpecies.size();
|
||||
const unsigned long numSpecies = m_networkSpecies.size();
|
||||
constexpr double abs_tol = 1.0e-8;
|
||||
constexpr double rel_tol = 1.0e-8;
|
||||
|
||||
int stepCount = 0;
|
||||
size_t stepCount = 0;
|
||||
|
||||
// TODO: Pull these out into configuration options
|
||||
|
||||
@@ -404,11 +419,17 @@ namespace gridfire {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,15 +1,10 @@
|
||||
# Define the library
|
||||
network_sources = files(
|
||||
'private/network.cpp',
|
||||
'private/approx8.cpp',
|
||||
'private/netgraph.cpp'
|
||||
'lib/network.cpp',
|
||||
'lib/approx8.cpp',
|
||||
'lib/netgraph.cpp'
|
||||
)
|
||||
|
||||
network_headers = files(
|
||||
'public/network.h',
|
||||
'public/approx8.h',
|
||||
'public/netgraph.h',
|
||||
)
|
||||
|
||||
dependencies = [
|
||||
boost_dep,
|
||||
@@ -24,16 +19,21 @@ dependencies = [
|
||||
# Define the libnetwork library so it can be linked against by other parts of the build system
|
||||
libnetwork = library('network',
|
||||
network_sources,
|
||||
include_directories: include_directories('public'),
|
||||
include_directories: include_directories('include'),
|
||||
dependencies: dependencies,
|
||||
install : true)
|
||||
|
||||
network_dep = declare_dependency(
|
||||
include_directories: include_directories('public'),
|
||||
include_directories: include_directories('include'),
|
||||
link_with: libnetwork,
|
||||
sources: network_sources,
|
||||
dependencies: dependencies,
|
||||
)
|
||||
|
||||
# Make headers accessible
|
||||
network_headers = files(
|
||||
'include/gridfire/network.h',
|
||||
'include/gridfire/approx8.h',
|
||||
'include/gridfire/netgraph.h',
|
||||
)
|
||||
install_headers(network_headers, subdir : 'gridfire/gridfire')
|
||||
|
||||
@@ -1,113 +0,0 @@
|
||||
/* ***********************************************************************
|
||||
//
|
||||
// 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 "network.h"
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include "approx8.h"
|
||||
#include "quill/LogMacros.h"
|
||||
#include "reaclib.h"
|
||||
#include "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;
|
||||
}
|
||||
|
||||
reaclib::REACLIBReactionSet 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.id());
|
||||
reactions.add_reaction(reaction);
|
||||
}
|
||||
}
|
||||
reactions.sort();
|
||||
return reactions;
|
||||
}
|
||||
|
||||
reaclib::REACLIBReactionSet build_reaclib_nuclear_network(const fourdst::composition::Composition &composition, const double culling, const double T9) {
|
||||
using namespace reaclib;
|
||||
REACLIBReactionSet allReactions = build_reaclib_nuclear_network(composition);
|
||||
REACLIBReactionSet reactions;
|
||||
for (const auto& reaction : allReactions) {
|
||||
if (reaction.calculate_rate(T9) >= culling) {
|
||||
reactions.add_reaction(reaction);
|
||||
}
|
||||
}
|
||||
return reactions;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/4D-STAR/libcomposition.git
|
||||
revision = v1.0.5
|
||||
revision = v1.0.7
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
libcomposition = composition_dep
|
||||
@@ -1,7 +1,4 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/4D-STAR/libconfig.git
|
||||
revision = v1.0.3
|
||||
revision = v1.0.8
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
libconfig = config_dep
|
||||
@@ -1,7 +1,4 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/4D-STAR/libconstants.git
|
||||
revision = v1.0.5
|
||||
revision = v1.0.6
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
libconstants = const_dep
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
[wrap-git]
|
||||
url = https://github.com/4D-STAR/liblogging.git
|
||||
revision = v1.0.5
|
||||
revision = v1.0.8
|
||||
depth = 1
|
||||
|
||||
[provide]
|
||||
liblogging = logging_dep
|
||||
@@ -1,16 +1,16 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include <string>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "approx8.h"
|
||||
#include "config.h"
|
||||
#include "network.h"
|
||||
#include "composition.h"
|
||||
#include "reaclib.h"
|
||||
#include "netgraph.h"
|
||||
#include "fourdst/composition/composition.h"
|
||||
#include "fourdst/config/config.h"
|
||||
#include "gridfire/approx8.h"
|
||||
#include "gridfire/netgraph.h"
|
||||
#include "gridfire/network.h"
|
||||
#include "gridfire/reaclib.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "reactions.h"
|
||||
#include "gridfire/reactions.h"
|
||||
|
||||
std::string TEST_CONFIG = std::string(getenv("MESON_SOURCE_ROOT")) + "/tests/testsConfig.yaml";
|
||||
class approx8Test : public ::testing::Test {};
|
||||
@@ -68,14 +68,15 @@ TEST_F(approx8Test, evaluate) {
|
||||
|
||||
TEST_F(approx8Test, reaclib) {
|
||||
using namespace gridfire;
|
||||
const std::vector<double> comp = {0.708, 2.94e-5, 0.276, 0.003, 0.0011, 9.62e-3, 1.62e-3, 5.16e-4};
|
||||
const std::vector<std::string> symbols = {"H-1", "He-3", "He-4", "C-12", "N-14", "O-16", "Ne-20", "Mg-24"};
|
||||
const std::vector<double> comp = {0.708, 0.0, 2.94e-5, 0.276, 0.003, 0.0011, 9.62e-3, 1.62e-3, 5.16e-4};
|
||||
const std::vector<std::string> symbols = {"H-1", "H-2", "He-3", "He-4", "C-12", "N-14", "O-16", "Ne-20", "Mg-24"};
|
||||
|
||||
fourdst::composition::Composition composition;
|
||||
composition.registerSymbol(symbols, true);
|
||||
composition.setMassFraction(symbols, comp);
|
||||
composition.finalize(true);
|
||||
|
||||
|
||||
NetIn netIn;
|
||||
netIn.composition = composition;
|
||||
netIn.temperature = 1e7;
|
||||
@@ -86,7 +87,6 @@ TEST_F(approx8Test, reaclib) {
|
||||
netIn.dt0 = 1e12;
|
||||
|
||||
GraphNetwork network(composition);
|
||||
network.exportToDot("Test.dot");
|
||||
NetOut netOut;
|
||||
netOut = network.evaluate(netIn);
|
||||
std::cout << netOut << std::endl;
|
||||
|
||||
@@ -12,8 +12,6 @@ foreach test_file : test_sources
|
||||
exe_name,
|
||||
test_file,
|
||||
dependencies: [gtest_dep, gtest_main, network_dep, composition_dep],
|
||||
include_directories: include_directories('../../src/network/public'),
|
||||
link_with: libnetwork, # Link the dobj library
|
||||
install_rpath: '@loader_path/../../src' # Ensure runtime library path resolves correctly
|
||||
)
|
||||
|
||||
|
||||
7333
utils/reaclib/energy.txt
Normal file
7333
utils/reaclib/energy.txt
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,15 @@
|
||||
import re
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
from typing import List, Tuple
|
||||
from re import Match
|
||||
from typing import List, Tuple, Any, LiteralString
|
||||
import numpy as np
|
||||
from serif.atomic import species
|
||||
from serif.atomic import Species
|
||||
from serif.constants import Constants
|
||||
import hashlib
|
||||
from collections import Counter
|
||||
import math
|
||||
|
||||
#import dataclasses
|
||||
from dataclasses import dataclass
|
||||
@@ -18,18 +24,11 @@ class Reaction:
|
||||
coeffs: List[float]
|
||||
projectile: str # added
|
||||
ejectile: str # added
|
||||
rpName: str # added
|
||||
reactionType: str # added (e.g. "(p,γ)")
|
||||
reverse: bool
|
||||
def format_rp_name(self) -> str:
|
||||
# radiative or particle captures: 2 reactants -> 1 product
|
||||
if len(self.reactants) == 2 and len(self.products) == 1:
|
||||
target = self.reactants[0]
|
||||
prod = self.products[0]
|
||||
return f"{target}({self.projectile},{self.ejectile}){prod}"
|
||||
# fallback: join lists
|
||||
react_str = '+'.join(self.reactants)
|
||||
prod_str = '+'.join(self.products)
|
||||
return f"{react_str}->{prod_str}"
|
||||
return self.rpName
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
@@ -61,7 +60,7 @@ class ReaclibParseError(Exception):
|
||||
|
||||
|
||||
def format_cpp_identifier(name: str) -> str:
|
||||
name_map = {'p': 'H_1', 'n': 'n_1', 'd': 'd', 't': 't', 'a': 'a'}
|
||||
name_map = {'p': 'H_1', 'd': 'H_2', 't': 'H_3', 'n': 'n_1', 'a': 'He_4'}
|
||||
if name.lower() in name_map:
|
||||
return name_map[name.lower()]
|
||||
match = re.match(r"([a-zA-Z]+)(\d+)", name)
|
||||
@@ -70,7 +69,7 @@ def format_cpp_identifier(name: str) -> str:
|
||||
return f"{element.capitalize()}_{mass}"
|
||||
return f"{name.capitalize()}_1"
|
||||
|
||||
def parse_reaclib_entry(entry: str) -> Tuple[List[str], str, float, List[float], bool]:
|
||||
def parse_reaclib_entry(entry: str) -> tuple[Match[str] | None, bool]:
|
||||
pattern = re.compile(r"""^([1-9]|1[0-1])\r?\n
|
||||
[ \t]*
|
||||
((?:[A-Za-z0-9-*]+[ \t]+)*
|
||||
@@ -104,35 +103,161 @@ def get_rp(group: str, chapter: int) -> Tuple[List[str], List[str]]:
|
||||
products = species[nReact:nReact + nProd]
|
||||
return reactants, products
|
||||
|
||||
def translate_names_to_species(names: List[str]) -> List[Species]:
|
||||
sp = list()
|
||||
split_alpha_digits = lambda inputString: re.match(r'([A-Za-z]+)[-+*]?(\d+)$', inputString).groups()
|
||||
for name in names:
|
||||
if name in ('t', 'a', 'd', 'n', 'p'):
|
||||
name = {'t': 'H-3', 'a': 'He-4', 'd': 'H-2', 'n': 'n-1', 'p': 'H-1'}[name]
|
||||
else:
|
||||
name = '-'.join(split_alpha_digits(name)).capitalize()
|
||||
try:
|
||||
sp.append(species[name])
|
||||
except Exception as e:
|
||||
print("Error: Species not found in database:", name, e)
|
||||
raise ReaclibParseError(f"Species '{name}' not found in species database.", line_content=name)
|
||||
return sp
|
||||
|
||||
|
||||
|
||||
def determine_reaction_type(reactants: List[str],
|
||||
products: List[str],
|
||||
qValue: float
|
||||
) -> Tuple[str, List[str], List[str], str]:
|
||||
"""
|
||||
Return (targetToken, projectiles, ejectiles, residualToken)
|
||||
|
||||
• targetToken – the nucleus that appears before the parenthesis (A)
|
||||
• projectiles – every explicit projectile that must be written inside ‘( … )’
|
||||
• ejectiles – every explicit ejectile that must be written after the comma
|
||||
• residualToken– the main heavy product that appears after the parenthesis (D)
|
||||
|
||||
Photons and neutrinos are added / omitted exactly the way JINA REACLIB expects:
|
||||
– γ is explicit only when it is a **projectile** (photodisintegration)
|
||||
– ν/ν̄ are never explicit
|
||||
"""
|
||||
|
||||
if abs(qValue - 4.621) < 1e-6:
|
||||
print("Looking at he3(he3, 2p)he4")
|
||||
# --- helper look-ups ----------------------------------------------------
|
||||
reactantSpecies = translate_names_to_species(reactants)
|
||||
productSpecies = translate_names_to_species(products)
|
||||
|
||||
# Heaviest reactant → target (A); heaviest product → residual (D)
|
||||
targetSpecies = max(reactantSpecies, key=lambda s: s.mass())
|
||||
residualSpecies = max(productSpecies, key=lambda s: s.mass())
|
||||
|
||||
# Any other nuclear reactant is the normal projectile candidate
|
||||
nuclearProjectiles = [x for x in reactantSpecies]
|
||||
nuclearProjectiles.remove(targetSpecies)
|
||||
|
||||
nuclearEjectiles = [x for x in productSpecies]
|
||||
nuclearEjectiles.remove(residualSpecies)
|
||||
|
||||
# --- bulk bookkeeping (nuclei only) -------------------------------------
|
||||
aReact = sum(s.a() for s in reactantSpecies)
|
||||
zReact = sum(s.z() for s in reactantSpecies)
|
||||
nReact = len(reactantSpecies)
|
||||
|
||||
aProd = sum(s.a() for s in productSpecies)
|
||||
zProd = sum(s.z() for s in productSpecies)
|
||||
nProd = len(productSpecies)
|
||||
|
||||
dA = aReact - aProd # must be 0 – abort if not
|
||||
dZ = zReact - zProd # ≠0 ⇒ leptons needed
|
||||
dN = nReact - nProd # ±1 ⇒ photon candidate
|
||||
|
||||
assert dA == 0, (
|
||||
f"Baryon number mismatch: A₍react₎={aReact}, A₍prod₎={aProd}"
|
||||
)
|
||||
|
||||
projectiles: List[str] = []
|
||||
ejectiles: List[str] = []
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 1. Charged-lepton bookkeeping (|ΔZ| = 1) ------------------------------
|
||||
# -----------------------------------------------------------------------
|
||||
if abs(dZ) == 1:
|
||||
# Proton → neutron (β⁻ / e- capture)
|
||||
if dZ == -1:
|
||||
# Electron capture when (i) exo-thermic and (ii) nucleus count unchanged
|
||||
if qValue > 0 and dN == 0:
|
||||
projectiles.append("e-") # write e- as projectile
|
||||
else:
|
||||
ejectiles.append("e-") # β⁻ decay: e- is an ejectile
|
||||
|
||||
# Neutron → proton (β⁺ / positron capture – capture is vanishingly rare)
|
||||
elif dZ == 1:
|
||||
ejectiles.append("e+") # β⁺ / weak-proton capture
|
||||
|
||||
# Neutrino companion is implicit – never written
|
||||
# (dL is automatically fixed by hiding ν or ν̄)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 2. Photon bookkeeping (ΔZ = 0) ----------------------------------------
|
||||
# -----------------------------------------------------------------------
|
||||
if dZ == 0:
|
||||
# Two → one nucleus and exothermic ⇒ radiative capture (γ ejectile, implicit)
|
||||
if dN == 1 and qValue > 0:
|
||||
ejectiles.append("g")
|
||||
pass # γ is implicit; nothing to write
|
||||
|
||||
# One → two nuclei and endothermic ⇒ photodisintegration (γ projectile, explicit)
|
||||
elif dN == -1 and qValue < 0:
|
||||
projectiles.append("g")
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 3. Add the ordinary nuclear projectile (if any) -----------------------
|
||||
# -----------------------------------------------------------------------
|
||||
if nuclearProjectiles:
|
||||
for nucP in nuclearProjectiles:
|
||||
name = nucP.name().replace("-", "").lower()
|
||||
if name in ('h1', 'h2', 'h3', 'he4', 'n1', 'p'):
|
||||
name = name.replace('h1', 'p').replace('h2', 'd').replace('h3', 't').replace('he4', 'a').replace('n1', 'n')
|
||||
projectiles.append(name) # REACLIB allows exactly one
|
||||
|
||||
if nuclearEjectiles:
|
||||
for nucE in nuclearEjectiles:
|
||||
name = nucE.name().replace("-", "").lower()
|
||||
if name in ('h1', 'h2', 'h3', 'he4', 'n1', 'p'):
|
||||
name = name.replace('h1', 'p').replace('h2', 'd').replace('h3', 't').replace('he4', 'a').replace('n1', 'n')
|
||||
ejectiles.append(name)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# 4. Build return values -------------------------------------------------
|
||||
# -----------------------------------------------------------------------
|
||||
targetToken = targetSpecies.name().replace("-", "").lower()
|
||||
residualToken = residualSpecies.name().replace("-", "").lower()
|
||||
|
||||
if targetToken in ('h1', 'h2', 'h3', 'n1', 'p'):
|
||||
targetToken = targetToken.replace('h1', 'p').replace('h2', 'd').replace('h3', 't').replace('n1', 'n')
|
||||
if residualToken in ('h1', 'h2', 'h3', 'n1', 'p'):
|
||||
residualToken = residualToken.replace('h1', 'p').replace('h2', 'd').replace('h3', 't').replace('n1', 'n')
|
||||
|
||||
uniqueProjectiles = set(projectiles)
|
||||
uniqueEjectiles = set(ejectiles)
|
||||
numPerUniqueProjectiles = {x: projectiles.count(x) for x in uniqueProjectiles}
|
||||
numPerUniqueEjectiles = {x: ejectiles.count(x) for x in uniqueEjectiles}
|
||||
formatedProjectileNames = [f"{numPerUniqueProjectiles[x]}{x}" if numPerUniqueProjectiles[x] > 1 else x for x in uniqueProjectiles]
|
||||
formatedEjectileNames = [f"{numPerUniqueEjectiles[x]}{x}" if numPerUniqueEjectiles[x] > 1 else x for x in uniqueEjectiles]
|
||||
rType = f"({" ".join(formatedProjectileNames)},{' '.join(formatedEjectileNames)})"
|
||||
reactionKey = f"{targetToken}{rType}{residualToken}"
|
||||
|
||||
|
||||
return targetToken, projectiles, ejectiles, residualToken, reactionKey, rType
|
||||
|
||||
|
||||
def determine_reaction_type(reactants: List[str], products: List[str]) -> Tuple[str, str, str]:
|
||||
# assumes no reverse flag applied
|
||||
projectile = ''
|
||||
ejectile = ''
|
||||
# projectile is the lighter reactant (p, n, he4)
|
||||
for sp in reactants:
|
||||
if sp in ('p', 'n', 'he4'):
|
||||
projectile = sp
|
||||
break
|
||||
# ejectile logic
|
||||
if len(products) == 1:
|
||||
ejectile = 'g'
|
||||
elif 'he4' in products:
|
||||
ejectile = 'a'
|
||||
elif 'p' in products:
|
||||
ejectile = 'p'
|
||||
elif 'n' in products:
|
||||
ejectile = 'n'
|
||||
reactionType = f"({projectile},{ejectile})"
|
||||
return projectile, ejectile, reactionType
|
||||
def extract_groups(match: re.Match, reverse: bool) -> Reaction:
|
||||
groups = match.groups()
|
||||
chapter = int(groups[0].strip())
|
||||
rawGroup = groups[1].strip()
|
||||
rList, pList = get_rp(rawGroup, chapter)
|
||||
if 'c12' in rList and 'mg24' in pList:
|
||||
print("Found it!")
|
||||
if reverse:
|
||||
rList, pList = pList, rList
|
||||
proj, ejec, rType = determine_reaction_type(rList, pList)
|
||||
qValue = float(groups[3].strip())
|
||||
target, proj, ejec, residual, key, rType = determine_reaction_type(rList, pList, qValue)
|
||||
reaction = Reaction(
|
||||
reactants=rList,
|
||||
products=pList,
|
||||
@@ -142,10 +267,12 @@ def extract_groups(match: re.Match, reverse: bool) -> Reaction:
|
||||
coeffs=[float(groups[i].strip()) for i in range(4, 11)],
|
||||
projectile=proj,
|
||||
ejectile=ejec,
|
||||
rpName=key,
|
||||
reactionType=rType,
|
||||
reverse=reverse
|
||||
)
|
||||
return reaction
|
||||
|
||||
def format_emplacment(reaction: Reaction) -> str:
|
||||
reactantNames = [f'{format_cpp_identifier(r)}' for r in reaction.reactants]
|
||||
productNames = [f'{format_cpp_identifier(p)}' for p in reaction.products]
|
||||
@@ -163,19 +290,23 @@ def format_emplacment(reaction: Reaction) -> str:
|
||||
chapter_str = reaction.chapter
|
||||
|
||||
rate_sets_str = ', '.join([str(x) for x in reaction.coeffs])
|
||||
emplacment: str = f"s_all_reaclib_reactions.emplace(\"{label}\", REACLIBReaction(\"{label}\", {chapter_str}, {{{reactants_str}}}, {{{products_str}}}, {q_value_str}, \"{reaction.label}\", {{{rate_sets_str}}}, {"true" if reaction.reverse else "false"}));"
|
||||
emplacment: str = f"s_all_reaclib_reactions.emplace(\"{label}\", REACLIBReaction(\"{label}\", \"{reaction.format_rp_name()}\", {chapter_str}, {{{reactants_str}}}, {{{products_str}}}, {q_value_str}, \"{reaction.label}\", {{{rate_sets_str}}}, {"true" if reaction.reverse else "false"}));"
|
||||
|
||||
return emplacment
|
||||
|
||||
|
||||
|
||||
|
||||
def generate_reaclib_header(reaclib_filepath: str, culling: float, T9: float, verbose: bool) -> str:
|
||||
def generate_reaclib_header(reaclib_filepath: str, culling: float, T9: float, verbose: bool) -> tuple[
|
||||
LiteralString, int | Any, int | Any]:
|
||||
"""
|
||||
Parses a JINA REACLIB file using regular expressions and generates a C++ header file string.
|
||||
|
||||
Args:
|
||||
reaclib_filepath: The path to the REACLIB data file.
|
||||
culling: The threshold for culling reactions based on their rates at T9.
|
||||
T9: The temperature in billions of Kelvin to evaluate the reaction rates for culling.
|
||||
verbose: If True, prints additional information about skipped reactions.
|
||||
|
||||
Returns:
|
||||
A string containing the complete C++ header content.
|
||||
@@ -190,7 +321,12 @@ def generate_reaclib_header(reaclib_filepath: str, culling: float, T9: float, ve
|
||||
for entry in entries:
|
||||
m, r = parse_reaclib_entry(entry)
|
||||
if m is not None:
|
||||
try:
|
||||
reac = extract_groups(m, r)
|
||||
except ReaclibParseError as e:
|
||||
continue
|
||||
if verbose:
|
||||
print(f"Parsed reaction: {reac.format_rp_name()} ({reac.coeffs}) with label {reac.label} (reverse: {reac.reverse})")
|
||||
reactions.append(reac)
|
||||
|
||||
# --- Generate the C++ Header String ---
|
||||
@@ -204,8 +340,8 @@ def generate_reaclib_header(reaclib_filepath: str, culling: float, T9: float, ve
|
||||
"// Includes %%TOTAL%% reactions.",
|
||||
"// Note: Only reactions with species defined in the atomicSpecies.h header will be included at compile time.",
|
||||
"#pragma once",
|
||||
"#include \"atomicSpecies.h\"",
|
||||
"#include \"species.h\"",
|
||||
"#include \"fourdst/composition/atomicSpecies.h\"",
|
||||
"#include \"fourdst/composition/species.h\"",
|
||||
"#include \"reaclib.h\"",
|
||||
"\nnamespace gridfire::reaclib {\n",
|
||||
"""
|
||||
@@ -218,7 +354,13 @@ def generate_reaclib_header(reaclib_filepath: str, culling: float, T9: float, ve
|
||||
]
|
||||
totalSkipped = 0
|
||||
totalIncluded = 0
|
||||
energy = list()
|
||||
energyFile = open('energy.txt', 'w')
|
||||
energyFile.write("name;maxEnergy;QValue,reactants;products;a0;a1;a2;a3;a4;a5;a6\n")
|
||||
for reaction in reactions:
|
||||
maxEnergy = calculate_peak_importance(reaction)
|
||||
energyFile.write(f"{reaction.format_rp_name()};{maxEnergy};{reaction.qValue};{' '.join(reaction.reactants)};{' '.join(reaction.products)};{';'.join([str(x) for x in reaction.coeffs])}\n")
|
||||
energy.append(maxEnergy)
|
||||
reactantNames = [f'{format_cpp_identifier(r)}' for r in reaction.reactants]
|
||||
productNames = [f'{format_cpp_identifier(p)}' for p in reaction.products]
|
||||
reactionName = f"{'_'.join(reactantNames)}_to_{'_'.join(productNames)}_{reaction.label.upper()}"
|
||||
@@ -226,11 +368,13 @@ def generate_reaclib_header(reaclib_filepath: str, culling: float, T9: float, ve
|
||||
rate = evaluate_rate(reaction.coeffs, T9)
|
||||
if rate < culling:
|
||||
if verbose:
|
||||
print(f"Skipping reaction {reactionName} with rate {rate:.6e} at T9={T9} (culling threshold: {culling} at T9={T9})")
|
||||
print(f"Skipping reaction {reaction.format_rp_name()} ({reactionName}) with rate {rate:.6e} at T9={T9} (culling threshold: {culling} at T9={T9})")
|
||||
totalSkipped += 1
|
||||
continue
|
||||
else:
|
||||
totalIncluded += 1
|
||||
else:
|
||||
totalIncluded += 1
|
||||
|
||||
defines = ' && '.join(set([f"defined(SERIF_SPECIES_{name.upper().replace('-', '_min_').replace('+', '_add_').replace('*', '_mult_')})" for name in reactantNames + productNames]))
|
||||
cpp_lines.append(f" #if {defines}")
|
||||
@@ -238,8 +382,64 @@ def generate_reaclib_header(reaclib_filepath: str, culling: float, T9: float, ve
|
||||
cpp_lines.append(f" {emplacment}")
|
||||
cpp_lines.append(f" #endif // {defines}")
|
||||
cpp_lines.append("\n }\n} // namespace gridfire::reaclib\n")
|
||||
energyFile.close()
|
||||
#save energy data to a file
|
||||
return "\n".join(cpp_lines), totalSkipped, totalIncluded
|
||||
|
||||
def calculate_peak_importance(reaction: Reaction) -> float:
|
||||
TGrid = np.logspace(-3, 2, 100) # Temperature grid from 0.001 to 100 T9
|
||||
RhoGrid = np.logspace(0.0, 6.0, 100) # Density grid from 1e0 to 1e3 g/cm^3
|
||||
N_A: float = Constants['N_a'].value
|
||||
u: float = Constants['u'].value
|
||||
max_energy_proxy: float = 0.0
|
||||
|
||||
if not reaction.reactants:
|
||||
return 0.0
|
||||
|
||||
numReactants: int = len(reaction.reactants)
|
||||
maxRate: float = 0.0
|
||||
|
||||
reactantCount: Counter = Counter(reaction.reactants)
|
||||
factorial_correction: float = 1.0
|
||||
for count in reactantCount.values():
|
||||
if count > 1:
|
||||
factorial_correction *= math.factorial(count)
|
||||
|
||||
molar_correction_factor = 1.0
|
||||
if numReactants > 1:
|
||||
molar_correction_factor = N_A ** (numReactants - 1)
|
||||
|
||||
Y_ideal = 1.0 / numReactants
|
||||
|
||||
mass_term = 1.0
|
||||
split_alpha_digits = lambda inputString: re.match(r'([A-Za-z]+)(\d+)$', inputString).groups()
|
||||
for reactant in reaction.reactants:
|
||||
try:
|
||||
if reactant in ('t', 'a', 'he4', 'd', 'n', 'p'):
|
||||
reactant = {'t': 'H-3', 'a': 'He-4', 'he4': 'He-4', 'd': 'H-2', 'n': 'n-1', 'p': 'H-1'}[reactant]
|
||||
else:
|
||||
reactant = '-'.join(split_alpha_digits(reactant)).capitalize()
|
||||
# print(f"Parsing reactant {reactant} using split_alpha_digits")
|
||||
reactantMassAMU = species[reactant].mass()
|
||||
reactantMassG = reactantMassAMU * u
|
||||
mass_term *= (Y_ideal/ reactantMassG)
|
||||
except Exception as e:
|
||||
# print(f"Error: Reactant {reactant} not found in species database. (what: {e})")
|
||||
return 0.0
|
||||
|
||||
for T9 in TGrid:
|
||||
k_reaclib = evaluate_rate(reaction.coeffs, T9)
|
||||
for rho in RhoGrid:
|
||||
n_product_no_rho = mass_term / factorial_correction
|
||||
full_rate = (n_product_no_rho *( rho ** numReactants) * k_reaclib) / molar_correction_factor
|
||||
energy_proxy = full_rate * abs(reaction.qValue)
|
||||
if energy_proxy > max_energy_proxy:
|
||||
max_energy_proxy = energy_proxy
|
||||
print(f"For reaction {reaction.format_rp_name()}, max energy proxy: {max_energy_proxy:.6e} MeV")
|
||||
return max_energy_proxy
|
||||
|
||||
# def smart_cull(reactions: List[Reaction], verbose: bool = False):
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Generate a C++ header from a REACLIB file.")
|
||||
|
||||
7427
utils/reaclib/log
Normal file
7427
utils/reaclib/log
Normal file
File diff suppressed because it is too large
Load Diff
BIN
utils/reaclib/reaction_energy.npy
Normal file
BIN
utils/reaclib/reaction_energy.npy
Normal file
Binary file not shown.
22021
utils/reaclib/reactions.h
Normal file
22021
utils/reaclib/reactions.h
Normal file
File diff suppressed because it is too large
Load Diff
10458
utils/reaclib/reactionsAll.dat
Normal file
10458
utils/reaclib/reactionsAll.dat
Normal file
File diff suppressed because it is too large
Load Diff
31399
utils/reaclib/test.h
Normal file
31399
utils/reaclib/test.h
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user