feat(AdaptiveEngine): system much more stable
dramatically increased stability of jacobian. System is burning again with much more robust physics
This commit is contained in:
@@ -8,9 +8,12 @@
|
||||
#include "gridfire/engine/types/reporting.h"
|
||||
#include "gridfire/engine/types/building.h"
|
||||
|
||||
#include "gridfire/expectations/expected_engine.h"
|
||||
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <expected>
|
||||
|
||||
/**
|
||||
* @file engine_abstract.h
|
||||
@@ -140,14 +143,14 @@ namespace gridfire {
|
||||
const std::vector<double>& Y_dynamic,
|
||||
double T9,
|
||||
double rho
|
||||
) = 0;
|
||||
) const = 0;
|
||||
|
||||
virtual void generateJacobianMatrix(
|
||||
const std::vector<double>& Y_dynamic,
|
||||
double T9,
|
||||
double rho,
|
||||
const SparsityPattern& sparsityPattern
|
||||
) {
|
||||
) const {
|
||||
throw std::logic_error("Sparsity pattern not supported by this engine.");
|
||||
}
|
||||
|
||||
@@ -251,7 +254,9 @@ namespace gridfire {
|
||||
*
|
||||
* @post The internal state of the engine is updated to reflect the new conditions.
|
||||
*/
|
||||
virtual void update(const NetIn& netIn) = 0;
|
||||
virtual fourdst::composition::Composition update(const NetIn &netIn) = 0;
|
||||
|
||||
virtual bool isStale(const NetIn& netIn) = 0;
|
||||
|
||||
/**
|
||||
* @brief Set the electron screening model.
|
||||
@@ -296,5 +301,6 @@ namespace gridfire {
|
||||
virtual void rebuild(const fourdst::composition::Composition& comp, BuildDepthType depth) {
|
||||
throw std::logic_error("Setting network depth not supported by this engine.");
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
// this makes extra copies of the species, which is not ideal and could be optimized further.
|
||||
// Even more relevant is the member m_reactionIDMap which makes copies of a REACLIBReaction for each reaction ID.
|
||||
// REACLIBReactions are quite large data structures, so this could be a performance bottleneck.
|
||||
|
||||
// static bool isF17 = false;
|
||||
namespace gridfire {
|
||||
static bool s_debug = false; // Global debug flag for the GraphEngine
|
||||
/**
|
||||
@@ -167,14 +167,14 @@ namespace gridfire {
|
||||
const std::vector<double>& Y_dynamic,
|
||||
const double T9,
|
||||
const double rho
|
||||
) override;
|
||||
) const override;
|
||||
|
||||
void generateJacobianMatrix(
|
||||
const std::vector<double> &Y_dynamic,
|
||||
double T9,
|
||||
double rho,
|
||||
const SparsityPattern &sparsityPattern
|
||||
) override;
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Generates the stoichiometry matrix for the network.
|
||||
@@ -274,7 +274,9 @@ namespace gridfire {
|
||||
double rho
|
||||
) const override;
|
||||
|
||||
void update(const NetIn& netIn) override;
|
||||
fourdst::composition::Composition update(const NetIn &netIn) override;
|
||||
|
||||
bool isStale(const NetIn &netIn) override;
|
||||
|
||||
/**
|
||||
* @brief Checks if a given species is involved in the network.
|
||||
@@ -371,7 +373,6 @@ namespace gridfire {
|
||||
void rebuild(const fourdst::composition::Composition& comp, const BuildDepthType depth) override;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
struct PrecomputedReaction {
|
||||
// Forward cacheing
|
||||
@@ -449,11 +450,12 @@ namespace gridfire {
|
||||
std::unordered_map<fourdst::atomic::Species, size_t> m_speciesToIndexMap; ///< Map from species to their index in the stoichiometry matrix.
|
||||
|
||||
boost::numeric::ublas::compressed_matrix<int> m_stoichiometryMatrix; ///< Stoichiometry matrix (species x reactions).
|
||||
boost::numeric::ublas::compressed_matrix<double> m_jacobianMatrix; ///< Jacobian matrix (species x species).
|
||||
|
||||
CppAD::ADFun<double> m_rhsADFun; ///< CppAD function for the right-hand side of the ODE.
|
||||
CppAD::sparse_jac_work m_jac_work; ///< Work object for sparse Jacobian calculations.
|
||||
mutable boost::numeric::ublas::compressed_matrix<double> m_jacobianMatrix; ///< Jacobian matrix (species x species).
|
||||
mutable CppAD::ADFun<double> m_rhsADFun; ///< CppAD function for the right-hand side of the ODE.
|
||||
mutable CppAD::sparse_jac_work m_jac_work; ///< Work object for sparse Jacobian calculations.
|
||||
CppAD::sparse_rc<std::vector<size_t>> m_full_jacobian_sparsity_pattern; ///< Full sparsity pattern for the Jacobian matrix.
|
||||
|
||||
std::vector<std::unique_ptr<AtomicReverseRate>> m_atomicReverseRates;
|
||||
|
||||
screening::ScreeningType m_screeningType = screening::ScreeningType::BARE; ///< Screening type for the reaction network. Default to no screening.
|
||||
@@ -793,25 +795,43 @@ namespace gridfire {
|
||||
);
|
||||
|
||||
const T molarReactionFlow = forwardMolarReactionFlow - reverseMolarFlow; // Net molar reaction flow
|
||||
std::stringstream ss;
|
||||
ss << "Forward: " << forwardMolarReactionFlow
|
||||
<< ", Reverse: " << reverseMolarFlow
|
||||
<< ", Net: " << molarReactionFlow;
|
||||
LOG_TRACE_L2(
|
||||
m_logger,
|
||||
"Reaction: {}, {}",
|
||||
reaction.peName(),
|
||||
ss.str()
|
||||
);
|
||||
// std::stringstream ss;
|
||||
// ss << "Forward: " << forwardMolarReactionFlow
|
||||
// << ", Reverse: " << reverseMolarFlow
|
||||
// << ", Net: " << molarReactionFlow;
|
||||
// LOG_TRACE_L3(
|
||||
// m_logger,
|
||||
// "Reaction: {}, {}",
|
||||
// reaction.peName(),
|
||||
// ss.str()
|
||||
// );
|
||||
// std::cout << "Forward molar flow for reaction " << reaction.peName() << ": " << forwardMolarReactionFlow << std::endl;
|
||||
// std::cout << "Reverse molar flow for reaction " << reaction.peName() << ": " << reverseMolarFlow << std::endl;
|
||||
// std::cout << "Net molar flow for reaction " << reaction.peName() << ": " << molarReactionFlow << std::endl;
|
||||
|
||||
// 3. Use the rate to update all relevant species derivatives (dY/dt)
|
||||
for (size_t speciesIndex = 0; speciesIndex < m_networkSpecies.size(); ++speciesIndex) {
|
||||
const auto& species = m_networkSpecies[speciesIndex];
|
||||
const T nu_ij = static_cast<T>(m_stoichiometryMatrix(speciesIndex, reactionIndex));
|
||||
// bool taping = false;
|
||||
// if constexpr (std::is_same_v<T, CppAD::AD<double>>) {
|
||||
// taping = true;
|
||||
// }
|
||||
// if (species.name() == "F-17" && nu_ij != static_cast<T>(0.0)) {
|
||||
// T f17dydt = result.dydt[speciesIndex] + threshold_flag * nu_ij * molarReactionFlow;
|
||||
// std::string tstring = taping ? "During Taping" : "During Evaluation";
|
||||
// std::cout << "(" << tstring << ") F-17 Debugging. For Reaction " << reaction.id() << " (" << reaction.peName() << "): "
|
||||
// << "nu_ij: " << nu_ij << ", molarReactionFlow: " << molarReactionFlow
|
||||
// << ", threshold_flag: " << threshold_flag << ", dY/dt: " << f17dydt << ", Y: " << Y[speciesIndex]
|
||||
// << std::endl;
|
||||
// isF17 = true;
|
||||
// }
|
||||
result.dydt[speciesIndex] += threshold_flag * nu_ij * molarReactionFlow;
|
||||
}
|
||||
// if (isF17) {
|
||||
// std::cout << "=========================================================" << std::endl;
|
||||
// isF17 = false;
|
||||
// }
|
||||
}
|
||||
|
||||
T massProductionRate = static_cast<T>(0.0); // [mol][s^-1]
|
||||
|
||||
@@ -76,7 +76,9 @@ namespace gridfire {
|
||||
* @see AdaptiveEngineView::constructSpeciesIndexMap()
|
||||
* @see AdaptiveEngineView::constructReactionIndexMap()
|
||||
*/
|
||||
void update(const NetIn& netIn) override;
|
||||
fourdst::composition::Composition update(const NetIn &netIn) override;
|
||||
|
||||
bool isStale(const NetIn& netIn) override;
|
||||
|
||||
/**
|
||||
* @brief Gets the list of active species in the network.
|
||||
@@ -123,7 +125,7 @@ namespace gridfire {
|
||||
const std::vector<double> &Y_dynamic,
|
||||
const double T9,
|
||||
const double rho
|
||||
) override;
|
||||
) const override;
|
||||
|
||||
/**
|
||||
* @brief Gets an entry from the Jacobian matrix for the active species.
|
||||
@@ -438,6 +440,16 @@ namespace gridfire {
|
||||
const std::vector<double>& Y_full,
|
||||
double maxFlow
|
||||
) const;
|
||||
|
||||
typedef std::pair<std::unordered_set<const reaction::LogicalReaction*>, std::unordered_set<fourdst::atomic::Species>> RescueSet;
|
||||
[[nodiscard]] RescueSet rescueEdgeSpeciesDestructionChannel(
|
||||
const std::vector<ReactionFlow>& allFlows,
|
||||
const std::vector<double>& Y_full,
|
||||
const double T9,
|
||||
const double rho,
|
||||
const std::vector<fourdst::atomic::Species>& activeSpecies,
|
||||
const reaction::LogicalReactionSet& activeReactions
|
||||
) const;
|
||||
/**
|
||||
* @brief Finalizes the set of active species and reactions.
|
||||
*
|
||||
|
||||
@@ -55,7 +55,7 @@ namespace gridfire{
|
||||
const std::vector<double>& Y_dynamic,
|
||||
const double T9,
|
||||
const double rho
|
||||
) override;
|
||||
) const override;
|
||||
/**
|
||||
* @brief Gets an entry from the Jacobian matrix for the active species.
|
||||
*
|
||||
@@ -142,7 +142,9 @@ namespace gridfire{
|
||||
*
|
||||
* @post If the view was stale, it is rebuilt and is no longer stale.
|
||||
*/
|
||||
void update(const NetIn &netIn) override;
|
||||
fourdst::composition::Composition update(const NetIn &netIn) override;
|
||||
|
||||
bool isStale(const NetIn& netIn) override;
|
||||
|
||||
/**
|
||||
* @brief Sets the screening model for the base engine.
|
||||
|
||||
@@ -6,6 +6,58 @@
|
||||
|
||||
#include "unsupported/Eigen/NonLinearOptimization"
|
||||
|
||||
namespace gridfire {
|
||||
struct QSECacheConfig {
|
||||
double T9_tol; ///< Absolute tolerance to produce the same hash for T9.
|
||||
double rho_tol; ///< Absolute tolerance to produce the same hash for rho.
|
||||
double Yi_tol; ///< Absolute tolerance to produce the same hash for species abundances.
|
||||
};
|
||||
|
||||
struct QSECacheKey {
|
||||
double m_T9;
|
||||
double m_rho;
|
||||
std::vector<double> m_Y; ///< Note that the ordering of Y must match the dynamic species indices in the view.
|
||||
|
||||
std::size_t m_hash = 0; ///< Precomputed hash value for this key.
|
||||
|
||||
// TODO: We should probably sort out how to adjust these from absolute to relative tolerances.
|
||||
QSECacheConfig m_cacheConfig = {
|
||||
1e-3, // Default tolerance for T9
|
||||
1e-1, // Default tolerance for rho
|
||||
1e-3 // Default tolerance for species abundances
|
||||
};
|
||||
|
||||
QSECacheKey(
|
||||
const double T9,
|
||||
const double rho,
|
||||
const std::vector<double>& Y
|
||||
);
|
||||
|
||||
size_t hash() const;
|
||||
|
||||
static long bin(double value, double tol);
|
||||
|
||||
bool operator==(const QSECacheKey& other) const;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
// Needs to be in this order (splitting gridfire namespace up) to avoid some issues with forward declarations and the () operator.
|
||||
namespace std {
|
||||
template <>
|
||||
struct hash<gridfire::QSECacheKey> {
|
||||
/**
|
||||
* @brief Computes the hash of a QSECacheKey.
|
||||
* @param key The QSECacheKey to hash.
|
||||
* @return The pre-computed hash value of the key.
|
||||
*/
|
||||
size_t operator()(const gridfire::QSECacheKey& key) const noexcept {
|
||||
// The hash is pre-computed, so we just return it.
|
||||
return key.m_hash;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
namespace gridfire {
|
||||
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;
|
||||
@@ -15,20 +67,20 @@ namespace gridfire {
|
||||
[[nodiscard]] const std::vector<fourdst::atomic::Species> & getNetworkSpecies() const override;
|
||||
|
||||
[[nodiscard]] StepDerivatives<double> calculateRHSAndEnergy(
|
||||
const std::vector<double> &Y,
|
||||
const std::vector<double> &Y_full,
|
||||
double T9,
|
||||
double rho
|
||||
) const override;
|
||||
|
||||
void generateJacobianMatrix(
|
||||
const std::vector<double> &Y_dynamic,
|
||||
const std::vector<double> &Y_full,
|
||||
double T9,
|
||||
double rho
|
||||
) override;
|
||||
) const override;
|
||||
|
||||
[[nodiscard]] double getJacobianMatrixEntry(
|
||||
int i,
|
||||
int j
|
||||
int i_full,
|
||||
int j_full
|
||||
) const override;
|
||||
|
||||
void generateStoichiometryMatrix() override;
|
||||
@@ -40,7 +92,7 @@ namespace gridfire {
|
||||
|
||||
[[nodiscard]] double calculateMolarReactionFlow(
|
||||
const reaction::Reaction &reaction,
|
||||
const std::vector<double> &Y,
|
||||
const std::vector<double> &Y_full,
|
||||
double T9,
|
||||
double rho
|
||||
) const override;
|
||||
@@ -53,10 +105,12 @@ namespace gridfire {
|
||||
double rho
|
||||
) const override;
|
||||
|
||||
void update(
|
||||
fourdst::composition::Composition update(
|
||||
const NetIn &netIn
|
||||
) override;
|
||||
|
||||
bool isStale(const NetIn& netIn) override;
|
||||
|
||||
void setScreeningModel(
|
||||
screening::ScreeningType model
|
||||
) override;
|
||||
@@ -181,6 +235,72 @@ namespace gridfire {
|
||||
int df(const InputType& v_qse, JacobianType& J_qse) const;
|
||||
};
|
||||
|
||||
|
||||
struct CacheStats {
|
||||
enum class operators {
|
||||
CalculateRHSAndEnergy,
|
||||
GenerateJacobianMatrix,
|
||||
CalculateMolarReactionFlow,
|
||||
GetSpeciesTimescales,
|
||||
Other,
|
||||
All
|
||||
};
|
||||
|
||||
std::map<operators, std::string> operatorsNameMap = {
|
||||
{operators::CalculateRHSAndEnergy, "calculateRHSAndEnergy"},
|
||||
{operators::GenerateJacobianMatrix, "generateJacobianMatrix"},
|
||||
{operators::CalculateMolarReactionFlow, "calculateMolarReactionFlow"},
|
||||
{operators::GetSpeciesTimescales, "getSpeciesTimescales"},
|
||||
{operators::Other, "other"}
|
||||
};
|
||||
|
||||
size_t m_hit = 0;
|
||||
size_t m_miss = 0;
|
||||
|
||||
std::map<operators, size_t> m_operatorHits = {
|
||||
{operators::CalculateRHSAndEnergy, 0},
|
||||
{operators::GenerateJacobianMatrix, 0},
|
||||
{operators::CalculateMolarReactionFlow, 0},
|
||||
{operators::GetSpeciesTimescales, 0},
|
||||
{operators::Other, 0}
|
||||
};
|
||||
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const CacheStats& stats) {
|
||||
os << "CacheStats(hit: " << stats.m_hit << ", miss: " << stats.m_miss << ")";
|
||||
return os;
|
||||
}
|
||||
|
||||
void hit(const operators op=operators::Other) {
|
||||
if (op==operators::All) {
|
||||
throw std::invalid_argument("Cannot use 'All' as an operator for hit/miss.");
|
||||
}
|
||||
m_hit++;
|
||||
m_operatorHits[op]++;
|
||||
}
|
||||
void miss(const operators op=operators::Other) {
|
||||
if (op==operators::All) {
|
||||
throw std::invalid_argument("Cannot use 'All' as an operator for hit/miss.");
|
||||
}
|
||||
m_miss++;
|
||||
m_operatorHits[op]++;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t hits(const operators op=operators::All) const {
|
||||
if (op==operators::All) {
|
||||
return m_hit;
|
||||
}
|
||||
return m_operatorHits.at(op);
|
||||
}
|
||||
[[nodiscard]] size_t misses(const operators op=operators::All) const {
|
||||
if (op==operators::All) {
|
||||
return m_miss;
|
||||
}
|
||||
return m_operatorHits.at(op);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private:
|
||||
quill::Logger* m_logger = LogManager::getInstance().getLogger("log");
|
||||
GraphEngine& m_baseEngine; ///< The base engine to which this view delegates calculations.
|
||||
@@ -189,7 +309,16 @@ namespace gridfire {
|
||||
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::vector<size_t> m_activeSpeciesIndices; ///< Indices of active species in the full network.
|
||||
std::vector<size_t> m_activeReactionIndices; ///< Indices of active reactions in the full network.
|
||||
|
||||
// TODO: Enhance the hashing for the cache to consider not just T and rho but also the current abundance in some careful way that automatically ignores small changes (i.e. network should only be repartitioned sometimes)
|
||||
std::unordered_map<QSECacheKey, std::vector<double>> m_qse_abundance_cache; ///< Cache for QSE abundances based on T9 and rho.
|
||||
mutable CacheStats m_cacheStats; ///< Statistics for the QSE abundance cache.
|
||||
|
||||
|
||||
|
||||
|
||||
private:
|
||||
std::vector<std::vector<size_t>> partitionByTimescale(
|
||||
@@ -230,3 +359,4 @@ namespace gridfire {
|
||||
) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user