fix(GridFire): changes based on ref report

This commit is contained in:
2026-04-20 12:37:53 -04:00
parent f4d988fa25
commit 3a22792fd1
17 changed files with 291 additions and 68 deletions

View File

@@ -70,6 +70,7 @@ struct AdaptiveEngineViewScratchPad final : AbstractScratchPad {
/// @brief Flag indicating whether the scratchpad has been initialized.
bool has_initialized = false;
/// @brief Vector of species currently active in the adaptive network.
std::vector<fourdst::atomic::Species> active_species;

View File

@@ -103,6 +103,9 @@ struct MultiscalePartitioningEngineViewScratchPad final : AbstractScratchPad {
/// @brief Flag indicating whether the scratchpad has been initialized.
bool has_initialized = false;
/// @breif User configurable parameter to control flux coupling threshold used
double flux_coupling_threshold = 5.0;
/// @brief Vector of QSE groups representing equilibrium clusters.
std::vector<QSEGroup> qse_groups;

View File

@@ -1,3 +1,4 @@
#pragma once
#include "gridfire/io/generative/python.h"
#include "gridfire/io/generative/python.h"
#include "gridfire/io/generative/mesa.h"

View File

@@ -0,0 +1,17 @@
#pragma once
#include "fourdst/atomic/atomicSpecies.h"
#include "gridfire/reaction/reaction.h"
#include "gridfire/engine/engine_abstract.h"
#include <format>
namespace gridfire::io::generative {
std::string get_mesa_iso_name(const fourdst::atomic::Species& species);
bool is_proton(const fourdst::atomic::Species& species);
bool is_alpha(const fourdst::atomic::Species& species);
bool is_neutron(const fourdst::atomic::Species& species);
std::string get_mesa_reaction_name(const reaction::Reaction& reaction);
std::string export_engine_to_mesa_net(const engine::DynamicEngine& engine, engine::scratch::StateBlob& ctx, bool skip_weak);
}

View File

@@ -37,5 +37,5 @@ configure_file(
output : 'config.h',
configuration : conf_data,
install : do_install_version_file,
install_dir : get_option('includedir') / '/gridfire/utils'
install_dir : get_option('includedir') / 'gridfire/utils'
)

View File

@@ -1337,7 +1337,8 @@ namespace gridfire::engine {
const double rho,
const QSEGroup &group
) const {
constexpr double FLUX_RATIO_THRESHOLD = 5;
auto* state = scratch::get_state<scratch::MultiscalePartitioningEngineViewScratchPad, true>(ctx);
double FLUX_RATIO_THRESHOLD = state->flux_coupling_threshold;
const std::unordered_set<Species> algebraic_group_members(
group.algebraic_species.begin(),
@@ -1484,8 +1485,8 @@ namespace gridfire::engine {
const double diff_total = std::abs(total_prod - total_dest);
bool total_balanced = (mean_total > 0) && ((diff_total / mean_total) < 0.05);
// Check 2: Charged-Particle Balance (The "Neutron-Exclusion" Check)
// Only valid if there IS charged flow (avoid 0/0 success)
// Check 2: Charged-Particle Balance
// Only valid if there IS charged flow
const double mean_charged = (charged_prod + charged_dest) / 2.0;
const double diff_charged = std::abs(charged_prod - charged_dest);
bool charged_balanced = (mean_charged > 0) && ((diff_charged / mean_charged) < 0.05);

View File

@@ -0,0 +1,155 @@
#include "gridfire/io/generative/mesa.h"
#include "gridfire/engine/engine_abstract.h"
#include "gridfire/reaction/reaction.h"
#include "fourdst/atomic/atomicSpecies.h"
#include "gridfire/utils/config.h"
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <chrono>
#include <cctype>
namespace gridfire::io::generative {
std::string get_mesa_iso_name(const fourdst::atomic::Species& species) {
auto name = std::string(species.name());
std::ranges::transform(name, name.begin(), ::tolower);
name.erase(std::ranges::remove(name, '-').begin(), name.end());
if (name == "p") return "h1";
if (name == "n" || name == "n1") return "neut";
if (name == "d") return "h2";
if (name == "t") return "h3";
if (name == "a") return "he4";
return name;
}
bool is_proton(const fourdst::atomic::Species& s) { return get_mesa_iso_name(s) == "h1"; }
bool is_alpha(const fourdst::atomic::Species& s) { return get_mesa_iso_name(s) == "he4"; }
bool is_neutron(const fourdst::atomic::Species& s) { return get_mesa_iso_name(s) == "neut"; }
std::string get_mesa_reaction_name(const reaction::Reaction& reaction) {
std::vector<fourdst::atomic::Species> react_sorted = reaction.reactants();
std::vector<fourdst::atomic::Species> prod_sorted = reaction.products();
auto sort_species = [](std::vector<fourdst::atomic::Species>& list) {
std::ranges::sort(list, [](const auto& a, const auto& b) {
if (a.z() != b.z()) return a.z() < b.z();
return a.a() < b.a();
});
};
sort_species(react_sorted);
sort_species(prod_sorted);
if (react_sorted.size() == 1 && prod_sorted.size() == 1) {
if (reaction.type() == reaction::ReactionType::WEAK ||
reaction.type() == reaction::ReactionType::REACLIB_WEAK ||
reaction.type() == reaction::ReactionType::LOGICAL_REACLIB_WEAK) {
return "r_" + get_mesa_iso_name(react_sorted[0]) + "_wk_" + get_mesa_iso_name(prod_sorted[0]);
}
}
if (react_sorted.size() == 2 && prod_sorted.size() == 1) {
std::string x, cap;
if (is_proton(react_sorted[0]) || is_proton(react_sorted[1])) {
cap = "pg";
x = is_proton(react_sorted[0]) ? get_mesa_iso_name(react_sorted[1]) : get_mesa_iso_name(react_sorted[0]);
}
else if (is_alpha(react_sorted[0]) || is_alpha(react_sorted[1])) {
cap = "ag";
x = is_alpha(react_sorted[0]) ? get_mesa_iso_name(react_sorted[1]) : get_mesa_iso_name(react_sorted[0]);
}
else if (is_neutron(react_sorted[0]) || is_neutron(react_sorted[1])) {
cap = "ng";
x = is_neutron(react_sorted[0]) ? get_mesa_iso_name(react_sorted[1]) : get_mesa_iso_name(react_sorted[0]);
}
if (!cap.empty()) return "r_" + x + "_" + cap + "_" + get_mesa_iso_name(prod_sorted[0]);
}
if (react_sorted.size() == 1 && prod_sorted.size() == 2) {
std::string x, em;
if (is_proton(prod_sorted[0]) || is_proton(prod_sorted[1])) {
em = "gp";
x = is_proton(prod_sorted[0]) ? get_mesa_iso_name(prod_sorted[1]) : get_mesa_iso_name(prod_sorted[0]);
}
else if (is_alpha(prod_sorted[0]) || is_alpha(prod_sorted[1])) {
em = "ga";
x = is_alpha(prod_sorted[0]) ? get_mesa_iso_name(prod_sorted[1]) : get_mesa_iso_name(prod_sorted[0]);
}
else if (is_neutron(prod_sorted[0]) || is_neutron(prod_sorted[1])) {
em = "gn";
x = is_neutron(prod_sorted[0]) ? get_mesa_iso_name(prod_sorted[1]) : get_mesa_iso_name(prod_sorted[0]);
}
if (!em.empty()) return "r_" + get_mesa_iso_name(react_sorted[0]) + "_" + em + "_" + x;
}
if (react_sorted.size() == 2 && prod_sorted.size() == 2) {
int r_p = -1, r_a = -1, r_n = -1;
int p_p = -1, p_a = -1, p_n = -1;
for(int i=0; i<2; ++i) {
if(is_proton(react_sorted[i])) r_p = i;
if(is_alpha(react_sorted[i])) r_a = i;
if(is_neutron(react_sorted[i])) r_n = i;
if(is_proton(prod_sorted[i])) p_p = i;
if(is_alpha(prod_sorted[i])) p_a = i;
if(is_neutron(prod_sorted[i])) p_n = i;
}
std::string x, y, exc;
if (r_a != -1 && p_p != -1) { exc = "ap"; x = get_mesa_iso_name(react_sorted[1-r_a]); y = get_mesa_iso_name(prod_sorted[1-p_p]); }
else if (r_p != -1 && p_a != -1) { exc = "pa"; x = get_mesa_iso_name(react_sorted[1-r_p]); y = get_mesa_iso_name(prod_sorted[1-p_a]); }
else if (r_n != -1 && p_p != -1) { exc = "np"; x = get_mesa_iso_name(react_sorted[1-r_n]); y = get_mesa_iso_name(prod_sorted[1-p_p]); }
else if (r_p != -1 && p_n != -1) { exc = "pn"; x = get_mesa_iso_name(react_sorted[1-r_p]); y = get_mesa_iso_name(prod_sorted[1-p_n]); }
else if (r_n != -1 && p_a != -1) { exc = "na"; x = get_mesa_iso_name(react_sorted[1-r_n]); y = get_mesa_iso_name(prod_sorted[1-p_a]); }
else if (r_a != -1 && p_n != -1) { exc = "an"; x = get_mesa_iso_name(react_sorted[1-r_a]); y = get_mesa_iso_name(prod_sorted[1-p_n]); }
if (!exc.empty()) return "r_" + x + "_" + exc + "_" + y;
}
std::string fallback = "r";
for (const auto& s : react_sorted) fallback += "_" + get_mesa_iso_name(s);
fallback += "_to";
for (const auto& s : prod_sorted) fallback += "_" + get_mesa_iso_name(s);
return fallback;
}
std::string export_engine_to_mesa_net(const engine::DynamicEngine& engine, engine::scratch::StateBlob& ctx, bool skip_weak) {
std::stringstream ss;
ss << "! Auto-generated MESA .net file from GridFire\n";
ss << "! Generated by GridFire version: " << version().toString() << "\n";
ss << "! Generated on " << std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) << "\n\n";
ss << "add_isos(\n";
for (const auto& species : engine.getNetworkSpecies(ctx)) {
ss << " " << get_mesa_iso_name(species) << "\n";
}
ss << ")\n\n";
ss << "add_reactions(\n";
const auto& reactions = engine.getNetworkReactions(ctx);
for (const auto& reaction_ptr : reactions) {
if (skip_weak && (reaction_ptr->type() == reaction::ReactionType::WEAK ||
reaction_ptr->type() == reaction::ReactionType::REACLIB_WEAK ||
reaction_ptr->type() == reaction::ReactionType::LOGICAL_REACLIB_WEAK)) {
continue;
}
ss << " " << get_mesa_reaction_name(*reaction_ptr) << "\n";
}
ss << ")\n";
return ss.str();
}
}

View File

@@ -61,14 +61,7 @@ namespace gridfire::policy {
std::make_unique<engine::GraphEngine>(m_initializing_composition, *m_partition_function, engine::NetworkBuildDepth::ThirdOrder, engine::NetworkConstructionFlags::DEFAULT)
);
m_network_stack.emplace_back(
std::make_unique<engine::MultiscalePartitioningEngineView>(*m_network_stack.back().get())
);
m_network_stack.emplace_back(
std::make_unique<engine::AdaptiveEngineView>(*m_network_stack.back().get())
);
std::unique_ptr<engine::scratch::StateBlob> scratch_blob = get_stack_scratch_blob();
std::unique_ptr<engine::scratch::StateBlob> scratch_blob = m_network_stack.back()->constructStateBlob(nullptr);
m_status = NetworkPolicyStatus::INITIALIZED_UNVERIFIED;
m_status = check_status(*scratch_blob);
@@ -110,8 +103,6 @@ namespace gridfire::policy {
std::vector<engine::EngineTypes> MainSequencePolicy::get_engine_types_stack() const {
return {
engine::EngineTypes::GRAPH_ENGINE,
engine::EngineTypes::MULTISCALE_PARTITIONING_ENGINE_VIEW,
engine::EngineTypes::ADAPTIVE_ENGINE_VIEW
};
}
@@ -125,32 +116,14 @@ namespace gridfire::policy {
}
auto blob = std::make_unique<engine::scratch::StateBlob>();
blob->enroll<engine::scratch::GraphEngineScratchPad>();
blob->enroll<engine::scratch::AdaptiveEngineViewScratchPad>();
blob->enroll<engine::scratch::MultiscalePartitioningEngineViewScratchPad>();
const engine::GraphEngine* graph_engine = dynamic_cast<engine::GraphEngine*>(m_network_stack.front().get());
if (!graph_engine) {
throw exceptions::PolicyError("Cannot get stack scratch blob from MainSequencePolicy: The base engine is not a GraphEngine. This indicates a serious internal inconsistency and should be reported to the GridFire developers, thank you.");
}
const engine::MultiscalePartitioningEngineView* multiscale_engine = dynamic_cast<engine::MultiscalePartitioningEngineView*>(m_network_stack[1].get());
if (!multiscale_engine) {
throw exceptions::PolicyError("Cannot get stack scratch blob from MainSequencePolicy: The middle engine is not a MultiscalePartitioningEngineView. This indicates a serious internal inconsistency and should be reported to the GridFire developers, thank you.");
}
const engine::AdaptiveEngineView* adaptive_engine = dynamic_cast<engine::AdaptiveEngineView*>(m_network_stack.back().get());
if (!adaptive_engine) {
throw exceptions::PolicyError("Cannot get stack scratch blob from MainSequencePolicy: The top engine is not an AdaptiveEngineView. This indicates a serious internal inconsistency and should be reported to the GridFire developers, thank you.");
}
auto* graph_engine_state = engine::scratch::get_state<engine::scratch::GraphEngineScratchPad, false>(*blob);
graph_engine_state->initialize(*graph_engine);
auto* multiscale_engine_state = engine::scratch::get_state<engine::scratch::MultiscalePartitioningEngineViewScratchPad, false>(*blob);
multiscale_engine_state->initialize();
auto* adaptive_engine_state = engine::scratch::get_state<engine::scratch::AdaptiveEngineViewScratchPad, false>(*blob);
adaptive_engine_state->initialize(*adaptive_engine);
return blob;
}

View File

@@ -17,6 +17,7 @@ gridfire_sources = files(
'lib/reaction/weak/weak_interpolator.cpp',
'lib/io/network_file.cpp',
'lib/io/generative/python.cpp',
'lib/io/generative/mesa.cpp',
'lib/solver/strategies/PointSolver.cpp',
'lib/solver/strategies/GridSolver.cpp',
'lib/solver/strategies/triggers/engine_partitioning_trigger.cpp',