fix(MultiscalePartitioningEngineView): made qse partitioning much more robust

This commit is contained in:
2025-07-16 12:14:02 -04:00
parent 18f9e13107
commit 1f7e765671
5 changed files with 860 additions and 279 deletions

View File

@@ -4,11 +4,11 @@
#include "gridfire/engine/views/engine_view_abstract.h" #include "gridfire/engine/views/engine_view_abstract.h"
#include "gridfire/engine/engine_graph.h" #include "gridfire/engine/engine_graph.h"
#include "Eigen/Dense"
#include "unsupported/Eigen/NonLinearOptimization" #include "unsupported/Eigen/NonLinearOptimization"
namespace gridfire { namespace gridfire {
class MultiscalePartitioningEngineView final: public DynamicEngine, public EngineView<DynamicEngine> { class MultiscalePartitioningEngineView final: public DynamicEngine, public EngineView<DynamicEngine> {
typedef std::tuple<std::vector<fourdst::atomic::Species>, std::vector<size_t>, std::vector<fourdst::atomic::Species>, std::vector<size_t>> QSEPartition;
public: public:
explicit MultiscalePartitioningEngineView(GraphEngine& baseEngine); explicit MultiscalePartitioningEngineView(GraphEngine& baseEngine);
@@ -68,16 +68,19 @@ namespace gridfire {
void partitionNetwork( void partitionNetwork(
const std::vector<double>& Y, const std::vector<double>& Y,
double T9, double T9,
double rho, double rho
double dt_control
); );
void partitionNetwork( void partitionNetwork(
const NetIn& netIn, const NetIn& netIn
double dt_control
); );
void exportToDot(const std::string& filename) const; void exportToDot(
const std::string& filename,
const std::vector<double>& Y,
const double T9,
const double rho
) const;
[[nodiscard]] int getSpeciesIndex(const fourdst::atomic::Species &species) const override; [[nodiscard]] int getSpeciesIndex(const fourdst::atomic::Species &species) const override;
@@ -91,21 +94,53 @@ namespace gridfire {
fourdst::composition::Composition equilibrateNetwork( fourdst::composition::Composition equilibrateNetwork(
const std::vector<double> &Y, const std::vector<double> &Y,
double T9, double T9,
double rho, double rho
double dt_control
); );
fourdst::composition::Composition equilibrateNetwork( fourdst::composition::Composition equilibrateNetwork(
const NetIn &netIn, const NetIn &netIn
const double dt_control
); );
private: private:
struct QSEGroup { struct QSEGroup {
std::vector<size_t> species_indices; ///< Indices of all species in this group. std::set<size_t> species_indices; ///< Indices of all species in this group.
size_t seed_nucleus_index; ///< Index of the one species that will be evolved dynamically.
bool is_in_equilibrium = false; ///< Flag set by flux analysis. bool is_in_equilibrium = false; ///< Flag set by flux analysis.
std::set<size_t> algebraic_indices; ///< Indices of algebraic species in this group.
std::set<size_t> seed_indices; ///< Indices of dynamic species in this group.
friend std::ostream& operator<<(std::ostream& os, const QSEGroup& group) {
os << "QSEGroup(species_indices: [";
int count = 0;
for (const auto& idx : group.species_indices) {
os << idx;
if (count < group.species_indices.size() - 1) {
os << ", ";
}
count++;
}
count = 0;
os << "], is_in_equilibrium: " << group.is_in_equilibrium
<< ", algebraic_indices: [";
for (const auto& idx : group.algebraic_indices) {
os << idx;
if (count < group.algebraic_indices.size() - 1) {
os << ", ";
}
count++;
}
count = 0;
os << "], seed_indices: [";
for (const auto& idx : group.seed_indices) {
os << idx;
if (count < group.seed_indices.size() - 1) {
os << ", ";
}
count++;
}
os << "])";
return os;
}
}; };
struct EigenFunctor { struct EigenFunctor {
@@ -147,36 +182,29 @@ namespace gridfire {
}; };
private: private:
quill::Logger* m_logger = fourdst::logging::LogManager::getInstance().getLogger("log"); quill::Logger* m_logger = LogManager::getInstance().getLogger("log");
GraphEngine& m_baseEngine; ///< The base engine to which this view delegates calculations. GraphEngine& m_baseEngine; ///< The base engine to which this view delegates calculations.
std::vector<QSEGroup> m_qse_groups; ///< The list of identified equilibrium groups. std::vector<QSEGroup> m_qse_groups; ///< The list of identified equilibrium groups.
std::vector<fourdst::atomic::Species> m_dynamic_species; ///< The simplified set of species presented to the solver. std::vector<fourdst::atomic::Species> m_dynamic_species; ///< The simplified set of species presented to the solver.
std::vector<size_t> m_dynamic_species_indices; ///< Indices mapping the dynamic species back to the master engine's list. std::vector<size_t> m_dynamic_species_indices; ///< Indices mapping the dynamic species back to the master engine's list.
std::vector<fourdst::atomic::Species> m_algebraic_species; ///< Species that are algebraic in the QSE groups.
std::vector<size_t> m_algebraic_species_indices; ///< Indices of algebraic species in the full network.
std::unordered_map<size_t, std::vector<size_t>> m_connectivity_graph; std::unordered_map<size_t, std::vector<size_t>> m_connectivity_graph;
private: private:
std::unordered_set<size_t> identifyFastReactions( std::vector<std::vector<size_t>> partitionByTimescale(
const std::vector<double>& Y_full, const std::vector<double> &Y_full,
double T9,
double rho,
double dt_control
) const;
void buildConnectivityGraph(
const std::unordered_set<size_t>& fast_reaction_indices
);
void findConnectedComponents();
void validateGroupsWithFluxAnalysis(
const std::vector<double>& Y,
double T9, double T9,
double rho double rho
); ) const;
std::pair<std::vector<fourdst::atomic::Species>, std::vector<size_t>> identifyDynamicSpecies( std::unordered_map<size_t, std::vector<size_t>> buildConnectivityGraph(
const std::unordered_set<size_t> &fast_reaction_indices
) const;
std::vector<QSEGroup> validateGroupsWithFluxAnalysis(
const std::vector<QSEGroup> &candidate_groups,
const std::vector<double>& Y, const std::vector<double>& Y,
const std::vector<QSEGroup>& qse_groups,
double T9, double T9,
double rho double rho
) const; ) const;
@@ -186,5 +214,19 @@ namespace gridfire {
double T9, double T9,
double rho double rho
); );
size_t identifyMeanSlowestPool(
const std::vector<std::vector<size_t>>& pools,
const std::vector<double> &Y,
double T9,
double rho
) const;
std::vector<QSEGroup> constructCandidateGroups(
const std::vector<std::vector<size_t>>& timescale_pools,
const std::vector<double>& Y,
double T9,
double rho
) const;
}; };
} }

View File

@@ -690,23 +690,23 @@ namespace gridfire {
} }
} }
} }
LOG_DEBUG( // LOG_DEBUG(
m_logger, // m_logger,
"Final Jacobian is:\n{}", // "Final Jacobian is:\n{}",
[&]() -> std::string { // [&]() -> std::string {
std::stringstream ss; // std::stringstream ss;
ss << std::scientific << std::setprecision(5); // ss << std::scientific << std::setprecision(5);
for (size_t i = 0; i < m_jacobianMatrix.size1(); ++i) { // for (size_t i = 0; i < m_jacobianMatrix.size1(); ++i) {
for (size_t j = 0; j < m_jacobianMatrix.size2(); ++j) { // for (size_t j = 0; j < m_jacobianMatrix.size2(); ++j) {
ss << m_jacobianMatrix(i, j); // ss << m_jacobianMatrix(i, j);
if (j < m_jacobianMatrix.size2() - 1) { // if (j < m_jacobianMatrix.size2() - 1) {
ss << ", "; // ss << ", ";
} // }
} // }
ss << "\n"; // ss << "\n";
} // }
return ss.str(); // return ss.str();
}()); // }());
LOG_TRACE_L1(m_logger, "Jacobian matrix generated with dimensions: {} rows x {} columns.", m_jacobianMatrix.size1(), m_jacobianMatrix.size2()); LOG_TRACE_L1(m_logger, "Jacobian matrix generated with dimensions: {} rows x {} columns.", m_jacobianMatrix.size1(), m_jacobianMatrix.size2());
} }

View File

@@ -110,6 +110,10 @@ namespace gridfire {
if (destructionRateConstant > 1e-99) { if (destructionRateConstant > 1e-99) {
const double creationRate = calculateCreationRate(primer, primingSpecies, Y, T9, rho); const double creationRate = calculateCreationRate(primer, primingSpecies, Y, T9, rho);
equilibriumMassFraction = (creationRate / destructionRateConstant) * primingSpecies.mass(); equilibriumMassFraction = (creationRate / destructionRateConstant) * primingSpecies.mass();
if (std::isnan(equilibriumMassFraction)) {
LOG_WARNING(logger, "Equilibrium mass fraction for {} is NaN. Setting to 0.0. This is likely not an issue. It probably originates from all reactions leading to creation and destruction being frozen out. In that case 0.0 should be a good approximation. Hint: This happens often when the network temperature is very the low. ", primingSpecies.name());
equilibriumMassFraction = 0.0;
}
LOG_INFO(logger, "Found equilibrium for {}: X_eq = {:.4e}", primingSpecies.name(), equilibriumMassFraction); LOG_INFO(logger, "Found equilibrium for {}: X_eq = {:.4e}", primingSpecies.name(), equilibriumMassFraction);
const reaction::Reaction* dominantChannel = findDominantCreationChannel(primer, primingSpecies, Y, T9, rho); const reaction::Reaction* dominantChannel = findDominantCreationChannel(primer, primingSpecies, Y, T9, rho);

File diff suppressed because it is too large Load Diff

View File

@@ -71,22 +71,27 @@ int main() {
NetIn netIn; NetIn netIn;
netIn.composition = composition; netIn.composition = composition;
netIn.temperature = 1.5e6; netIn.temperature = 1.5e7;
netIn.density = 1.5e3; netIn.density = 1.5e3;
netIn.energy = 0; netIn.energy = 0;
netIn.tMax = 3e17; netIn.tMax = 3e17;
netIn.dt0 = 1e-12; netIn.dt0 = 1e-12;
GraphEngine ReaclibEngine(composition, partitionFunction, NetworkBuildDepth::SecondOrder); GraphEngine ReaclibEngine(composition, partitionFunction, NetworkBuildDepth::SecondOrder);
ReaclibEngine.exportToDot("GraphEngine.dot");
auto primedReport = ReaclibEngine.primeEngine(netIn); auto primedReport = ReaclibEngine.primeEngine(netIn);
std::cout << primedReport << std::endl; if (!primedReport.success) {
std::cout << "Initial Composition\n"; LOG_CRITICAL(logger, "Failed to prime the network!");
for (const auto& [symbol, entry] : netIn.composition) { return 1;
std::cout << "\t" << symbol << ": " << entry.mass_fraction() << "\n";
} }
std::cout << "\nPrimed Composition\n"; NetIn primedNetIn = netIn;
for (const auto& [symbol, entry] : primedReport.primedComposition) { primedNetIn.composition = primedReport.primedComposition;
std::cout << "\t" << symbol << ": " << entry.mass_fraction() << "\n";
} std::cout << primedReport.primedComposition << std::endl;
MultiscalePartitioningEngineView partitioningView(ReaclibEngine);
fourdst::composition::Composition qseComp = partitioningView.equilibrateNetwork(primedNetIn);
std::cout << qseComp.getMolarAbundance("H-2") / qseComp.getMolarAbundance("H-1") << std::endl;
} }