feat(Comoposition-Tracking): updated GridFire to use new, molar-abundance based, version of libcomposition (v2.0.6)
This entailed a major rewrite of the composition handling from each engine and engine view along with the solver and primer. The intent here is to let Compositions be constructed from the same extensive property which the solver tracks internally. This addressed C0 discontinuity issues in the tracked molar abundances of species which were introduced by repeadidly swaping from molar abundance space to mass fraction space and back. This also allowed for a simplification of the primeNetwork method. Specifically the mass borrowing system was dramatically simplified as molar abundances are extensive.
This commit is contained in:
@@ -1,13 +1,11 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <format>
|
||||
|
||||
#include "gridfire/engine/engine_graph.h"
|
||||
#include "gridfire/engine/engine_approx8.h"
|
||||
#include "gridfire/engine/views/engine_adaptive.h"
|
||||
#include "gridfire/partition/partition_types.h"
|
||||
#include "gridfire/engine/views/engine_multiscale.h"
|
||||
#include "gridfire/solver/strategies/CVODE_solver_strategy.h"
|
||||
#include "gridfire/policy/stellar_policy.h"
|
||||
#include "gridfire/utils/table_format.h"
|
||||
|
||||
#include "gridfire/network.h"
|
||||
|
||||
@@ -19,25 +17,16 @@
|
||||
#include "quill/Logger.h"
|
||||
#include "quill/LogMacros.h"
|
||||
#include "quill/Backend.h"
|
||||
#include "quill/Frontend.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <clocale>
|
||||
#include <functional>
|
||||
|
||||
#include "gridfire/engine/views/engine_defined.h"
|
||||
#include "gridfire/partition/composite/partition_composite.h"
|
||||
#include "fourdst/atomic/species.h"
|
||||
#include "fourdst/composition/utils.h"
|
||||
|
||||
|
||||
static std::terminate_handler g_previousHandler = nullptr;
|
||||
|
||||
void measure_execution_time(const std::function<void()>& callback, const std::string& name)
|
||||
{
|
||||
const auto startTime = std::chrono::steady_clock::now();
|
||||
callback();
|
||||
const auto endTime = std::chrono::steady_clock::now();
|
||||
const auto duration = std::chrono::duration_cast<std::chrono::nanoseconds>(endTime - startTime);
|
||||
std::cout << "Execution time for " << name << ": " << duration.count()/1e9 << " s\n";
|
||||
}
|
||||
|
||||
void quill_terminate_handler()
|
||||
{
|
||||
quill::Backend::stop();
|
||||
@@ -47,62 +36,18 @@ void quill_terminate_handler()
|
||||
std::abort();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]){
|
||||
|
||||
// Valid usages are either
|
||||
// ./graphnet_sandbox
|
||||
//or
|
||||
// ./graphnet_sandbox --plug <plugin_bundle_path>
|
||||
if (argc == 3 && std::string(argv[1]) == "--plug") {
|
||||
std::filesystem::path pluginBundlePath(argv[2]);
|
||||
if (!std::filesystem::exists(pluginBundlePath)) {
|
||||
std::cerr << "Error: Plugin bundle path does not exist: " << pluginBundlePath << "\n";
|
||||
std::cerr << "Usage: " << argv[0] << " [--plug <plugin_bundle_path>]\n";
|
||||
return 1;
|
||||
}
|
||||
std::cout << "Loading plugin bundle from: " << pluginBundlePath << "\n";
|
||||
fourdst::plugin::bundle::PluginBundle pluginBundle(pluginBundlePath);
|
||||
}
|
||||
if (argc == 2 && std::string(argv[1]) != "--plug") {
|
||||
std::cerr << "Invalid argument: " << argv[1] << "\n";
|
||||
std::cerr << "Usage: " << argv[0] << " [--plug <plugin_bundle_path>]\n";
|
||||
return 1;
|
||||
}
|
||||
if (argc == 2 && std::string(argv[1]) == "--plug") {
|
||||
std::cerr << "Error: No plugin bundle path provided.\n";
|
||||
std::cerr << "Usage: " << argv[0] << " [--plug <plugin_bundle_path>]\n";
|
||||
return 1;
|
||||
}
|
||||
if (argc > 3) {
|
||||
std::cerr << "Too many arguments provided.\n";
|
||||
std::cerr << "Usage: " << argv[0] << " [--plug <plugin_bundle_path>]\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
gridfire::NetIn init() {
|
||||
std::setlocale(LC_ALL, "");
|
||||
g_previousHandler = std::set_terminate(quill_terminate_handler);
|
||||
quill::Logger* logger = fourdst::logging::LogManager::getInstance().getLogger("log");
|
||||
logger->set_log_level(quill::LogLevel::TraceL2);
|
||||
LOG_INFO(logger, "Starting Adaptive Engine View Example...");
|
||||
logger->set_log_level(quill::LogLevel::TraceL1);
|
||||
|
||||
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<double> X = {0.7081145999999999, 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"};
|
||||
|
||||
|
||||
fourdst::composition::Composition composition;
|
||||
composition.registerSymbol(symbols, true);
|
||||
composition.setMassFraction(symbols, comp);
|
||||
bool didFinalize = composition.finalize(true);
|
||||
if (!didFinalize) {
|
||||
std::cerr << "Failed to finalize initial composition." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
using partition::BasePartitionType;
|
||||
const auto partitionFunction = partition::CompositePartitionFunction({
|
||||
BasePartitionType::RauscherThielemann,
|
||||
BasePartitionType::GroundState
|
||||
});
|
||||
fourdst::composition::Composition composition = fourdst::composition::buildCompositionFromMassFractions(symbols, X);
|
||||
|
||||
NetIn netIn;
|
||||
netIn.composition = composition;
|
||||
@@ -110,37 +55,134 @@ int main(int argc, char* argv[]){
|
||||
netIn.density = 1.6e2;
|
||||
netIn.energy = 0;
|
||||
|
||||
// TODO: There is a bug when I get to very low concentrations of hydrogen where the solver will crash. I suspect this can be resolved with triggers
|
||||
netIn.tMax = 3e17;
|
||||
// netIn.tMax = 1e-14;
|
||||
netIn.tMax = 3e16;
|
||||
netIn.dt0 = 1e-12;
|
||||
|
||||
GraphEngine ReaclibEngine(composition, partitionFunction, NetworkBuildDepth::SecondOrder, NetworkConstructionFlags::DEFAULT);
|
||||
return netIn;
|
||||
}
|
||||
|
||||
ReaclibEngine.setPrecomputation(true);
|
||||
ReaclibEngine.setUseReverseReactions(false);
|
||||
void log_results(const gridfire::NetOut& netOut, const gridfire::NetIn& netIn) {
|
||||
double initialH1_X = netIn.composition.getMassFraction("H-1");
|
||||
double finalH1_X = netOut.composition.getMassFraction("H-1");
|
||||
double deltaH1_X = finalH1_X - initialH1_X;
|
||||
double fractionalChangeH1 = (deltaH1_X) / initialH1_X * 100.0;
|
||||
|
||||
DefinedEngineView subsetView({"p(p,e+)d", "d(p,g)he3", "n(p,g)d", "d(n,g)t"}, ReaclibEngine);
|
||||
MultiscalePartitioningEngineView partitioningView(ReaclibEngine);
|
||||
double initialHe4_X = netIn.composition.getMassFraction("He-4");
|
||||
double finalHe4_X = netOut.composition.getMassFraction("He-4");
|
||||
double deltaHe4_X = finalHe4_X - initialHe4_X;
|
||||
double fractionalChangeHe4 = (deltaHe4_X) / initialHe4_X * 100.0;
|
||||
|
||||
AdaptiveEngineView adaptiveView(partitioningView);
|
||||
double initialBe7_X = 0.0;
|
||||
double finalBe7_X = netOut.composition.getMassFraction("Be-7");
|
||||
double deltaBe7_X = finalBe7_X - initialBe7_X;
|
||||
double fractionalChangeBe7 = (deltaBe7_X) / initialBe7_X * 100.0;
|
||||
|
||||
double initialC12_X = netIn.composition.getMassFraction("C-12");
|
||||
double finalC12_X = netOut.composition.getMassFraction("C-12");
|
||||
double deltaC12_X = finalC12_X - initialC12_X;
|
||||
double fractionalChangeC12 = (deltaC12_X) / initialC12_X * 100.0;
|
||||
|
||||
double finalEnergy = netOut.energy;
|
||||
double dEpsdT = netOut.dEps_dT;
|
||||
double dEpsdRho = netOut.dEps_dRho;
|
||||
|
||||
double initialMeanMolecularMass = netIn.composition.getMeanParticleMass();
|
||||
double finalMeanMolecularMass = netOut.composition.getMeanParticleMass();
|
||||
|
||||
std::vector<std::string> rowLabels = {"H-1", "He-4", "Be-7", "C-12", "ε", "dε/dT", "dε/dρ", "<μ>"};
|
||||
std::vector<std::string> header = {"Parameter", "Initial", "Final", "Δ", "% Change"};
|
||||
std::vector<double> H1Data = {initialH1_X, finalH1_X, deltaH1_X, fractionalChangeH1};
|
||||
std::vector<double> He4Data = {initialHe4_X, finalHe4_X, deltaHe4_X, fractionalChangeHe4};
|
||||
std::vector<double> Be7Data = {initialBe7_X, finalBe7_X, deltaBe7_X, fractionalChangeBe7};
|
||||
std::vector<double> C12Data = {initialC12_X, finalC12_X, deltaC12_X, fractionalChangeC12};
|
||||
std::vector<double> energyData = {0.0, finalEnergy, finalEnergy, 0.0};
|
||||
std::vector<double> dEpsdTData = {0.0, dEpsdT, dEpsdT, 0.0};
|
||||
std::vector<double> dEpsdRhoData = {0.0, dEpsdRho, dEpsdRho, 0.0};
|
||||
std::vector<double> meanMolecularMassData = {initialMeanMolecularMass, finalMeanMolecularMass, finalMeanMolecularMass - initialMeanMolecularMass,
|
||||
(finalMeanMolecularMass - initialMeanMolecularMass) / initialMeanMolecularMass * 100.0};
|
||||
|
||||
gridfire::utils::Column<std::string> paramCol(header[0], rowLabels);
|
||||
gridfire::utils::Column<double> initialCol(header[1], {H1Data[0], He4Data[0], Be7Data[0], C12Data[0], energyData[0], dEpsdTData[0], dEpsdRhoData[0], meanMolecularMassData[0]});
|
||||
gridfire::utils::Column<double> finalCol (header[2], {H1Data[1], He4Data[1], Be7Data[1], C12Data[1], energyData[1], dEpsdTData[1], dEpsdRhoData[1], meanMolecularMassData[1]});
|
||||
gridfire::utils::Column<double> deltaCol (header[3], {H1Data[2], He4Data[2], Be7Data[2], C12Data[2], energyData[2], dEpsdTData[2], dEpsdRhoData[2], meanMolecularMassData[2]});
|
||||
gridfire::utils::Column<double> percentCol(header[4], {H1Data[3], He4Data[3], Be7Data[3], C12Data[3], energyData[3], dEpsdTData[3], dEpsdRhoData[3], meanMolecularMassData[3]});
|
||||
|
||||
std::vector<std::unique_ptr<gridfire::utils::ColumnBase>> columns;
|
||||
columns.push_back(std::make_unique<gridfire::utils::Column<std::string>>(paramCol));
|
||||
columns.push_back(std::make_unique<gridfire::utils::Column<double>>(initialCol));
|
||||
columns.push_back(std::make_unique<gridfire::utils::Column<double>>(finalCol));
|
||||
columns.push_back(std::make_unique<gridfire::utils::Column<double>>(deltaCol));
|
||||
columns.push_back(std::make_unique<gridfire::utils::Column<double>>(percentCol));
|
||||
|
||||
|
||||
fourdst::composition::Composition updatedComposition = adaptiveView.update(netIn);
|
||||
gridfire::utils::print_table("Simulation Results", columns);
|
||||
}
|
||||
|
||||
adaptiveView.generateJacobianMatrix(updatedComposition, netIn.temperature/1e9, netIn.density);
|
||||
|
||||
solver::CVODESolverStrategy solver(adaptiveView);
|
||||
NetOut netOut;
|
||||
netOut = solver.evaluate(netIn, true);
|
||||
static std::vector<std::pair<double, std::unordered_map<std::string, std::pair<double, double>>>> g_callbackHistory;
|
||||
void record_abundance_history_callback(const gridfire::solver::CVODESolverStrategy::TimestepContext& ctx) {
|
||||
const auto& engine = ctx.engine;
|
||||
std::unordered_map<std::string, std::pair<double, double>> abundances;
|
||||
for (const auto& species : engine.getNetworkSpecies()) {
|
||||
const size_t sid = engine.getSpeciesIndex(species);
|
||||
abundances[std::string(species.name())] = std::make_pair(species.mass(), N_VGetArrayPointer(ctx.state)[sid]);
|
||||
}
|
||||
g_callbackHistory.emplace_back(ctx.t, abundances);
|
||||
}
|
||||
|
||||
std::cout << "Initial H-1: " << netIn.composition.getMassFraction("H-1") << std::endl;
|
||||
std::cout << "NetOut H-1: " << netOut.composition.getMassFraction("H-1") << std::endl;
|
||||
double initialHydrogen = netIn.composition.getMassFraction("H-1");
|
||||
double finalHydrogen = netOut.composition.getMassFraction("H-1");
|
||||
double fractionalConsumedHydrogen = (initialHydrogen - finalHydrogen) / initialHydrogen * 100.0;
|
||||
std::cout << "Fractional consumed hydrogen: " << fractionalConsumedHydrogen << "%" << std::endl;
|
||||
std::cout << "Final D abundance " << netOut.composition.getMolarAbundance("H-2") << std::endl;
|
||||
std::cout << netOut << std::endl;
|
||||
void save_callback_data(const std::string_view filename) {
|
||||
std::set<std::string> unique_species;
|
||||
for (const auto &abundances: g_callbackHistory | std::views::values) {
|
||||
for (const auto &species_name: abundances | std::views::keys) {
|
||||
unique_species.insert(species_name);
|
||||
}
|
||||
}
|
||||
std::ofstream csvFile(filename.data(), std::ios::out);
|
||||
csvFile << "t,";
|
||||
|
||||
size_t i = 0;
|
||||
for (const auto& species_name : unique_species) {
|
||||
csvFile << species_name;
|
||||
if (i < unique_species.size() - 1) {
|
||||
csvFile << ",";
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
csvFile << "\n";
|
||||
|
||||
for (const auto& [time, data] : g_callbackHistory) {
|
||||
csvFile << time << ",";
|
||||
size_t j = 0;
|
||||
for (const auto& species_name : unique_species) {
|
||||
if (!data.contains(species_name)) {
|
||||
csvFile << "0.0";
|
||||
} else {
|
||||
csvFile << data.at(species_name).second;
|
||||
}
|
||||
if (j < unique_species.size() - 1) {
|
||||
csvFile << ",";
|
||||
}
|
||||
++j;
|
||||
}
|
||||
csvFile << "\n";
|
||||
}
|
||||
|
||||
csvFile.close();
|
||||
}
|
||||
|
||||
int main() {
|
||||
using namespace gridfire;
|
||||
const NetIn netIn = init();
|
||||
|
||||
policy::MainSequencePolicy stellarPolicy(netIn.composition);
|
||||
DynamicEngine& engine = stellarPolicy.construct();
|
||||
|
||||
solver::CVODESolverStrategy solver(engine);
|
||||
solver.set_callback(solver::CVODESolverStrategy::TimestepCallback(record_abundance_history_callback));
|
||||
|
||||
const NetOut netOut = solver.evaluate(netIn, false);
|
||||
|
||||
log_results(netOut, netIn);
|
||||
save_callback_data("abundance_history.csv");
|
||||
}
|
||||
0
tests/graphnet_sandbox/post.dat
Normal file
0
tests/graphnet_sandbox/post.dat
Normal file
0
tests/graphnet_sandbox/prior.dat
Normal file
0
tests/graphnet_sandbox/prior.dat
Normal file
Reference in New Issue
Block a user