2025-07-14 14:50:49 -04:00
# include "gridfire/engine/procedures/construction.h"
2025-10-07 15:16:03 -04:00
# include "gridfire/reaction/weak/weak_interpolator.h"
# include "gridfire/reaction/weak/weak.h"
# include "gridfire/reaction/weak/weak_types.h"
2025-07-14 14:50:49 -04:00
# include <ranges>
# include <stdexcept>
2025-10-07 15:16:03 -04:00
# include <memory>
2025-10-30 15:30:55 -04:00
# include <cmath>
2025-07-14 14:50:49 -04:00
# include "gridfire/reaction/reaction.h"
# include "gridfire/reaction/reaclib.h"
# include "fourdst/composition/composition.h"
# include "fourdst/logging/logging.h"
# include "quill/Logger.h"
# include "quill/LogMacros.h"
2025-10-30 15:30:55 -04:00
namespace {
2025-10-31 07:08:54 -04:00
// Simple heuristic to check if a reaclib reaction is a strong or weak reaction
2025-10-30 15:30:55 -04:00
bool reaclib_reaction_is_weak ( const gridfire : : reaction : : Reaction & reaction ) {
2025-10-31 07:08:54 -04:00
const std : : vector < fourdst : : atomic : : Species > & reactants = reaction . reactants ( ) ;
const std : : vector < fourdst : : atomic : : Species > & products = reaction . products ( ) ;
2025-10-30 15:30:55 -04:00
if ( reactants . size ( ) ! = products . size ( ) ) {
return false ;
}
if ( reactants . size ( ) ! = 1 | | products . size ( ) ! = 1 ) {
return false ;
}
if ( std : : floor ( reactants [ 0 ] . a ( ) ) ! = std : : floor ( products [ 0 ] . a ( ) ) ) {
return false ;
}
return true ;
}
}
2025-07-14 14:50:49 -04:00
namespace gridfire {
using reaction : : ReactionSet ;
using reaction : : Reaction ;
using fourdst : : composition : : Composition ;
using fourdst : : atomic : : Species ;
2025-10-07 15:16:03 -04:00
ReactionSet build_nuclear_network (
2025-10-10 09:12:40 -04:00
const Composition & composition ,
const rates : : weak : : WeakRateInterpolator & weakInterpolator ,
BuildDepthType maxLayers ,
bool reverse
) {
2025-07-14 14:50:49 -04:00
int depth ;
if ( std : : holds_alternative < NetworkBuildDepth > ( maxLayers ) ) {
depth = static_cast < int > ( std : : get < NetworkBuildDepth > ( maxLayers ) ) ;
} else {
depth = std : : get < int > ( maxLayers ) ;
}
auto logger = fourdst : : logging : : LogManager : : getInstance ( ) . getLogger ( " log " ) ;
if ( depth = = 0 ) {
LOG_ERROR ( logger , " Network build depth is set to 0. No reactions will be collected. " ) ;
throw std : : logic_error ( " Network build depth is set to 0. No reactions will be collected. " ) ;
}
2025-10-07 15:16:03 -04:00
// --- Step 1: Create a single master pool that owns all possible reactions ---
ReactionSet master_reaction_pool ;
// Clone all relevant REACLIB reactions into the master pool
const auto & allReaclibReactions = reaclib : : get_all_reaclib_reactions ( ) ;
for ( const auto & reaction : allReaclibReactions ) {
2025-10-31 07:08:54 -04:00
if ( reaction - > is_reverse ( ) = = reverse & & ! reaclib_reaction_is_weak ( * reaction ) ) { // Only add reactions of the correct direction and which are not weak. Weak reactions are handled from the WRL separately which provides much higher quality weak reactions than reaclib does
2025-10-07 15:16:03 -04:00
master_reaction_pool . add_reaction ( reaction - > clone ( ) ) ;
2025-07-14 14:50:49 -04:00
}
}
2025-10-29 14:47:11 -04:00
// --- Clone all possible weak reactions into the master reaction pool ---
// for (const auto& parent_species: weakInterpolator.available_isotopes()) {
// std::expected<Species, fourdst::atomic::SpeciesErrorType> upProduct = fourdst::atomic::az_to_species(
// parent_species.a(),
// parent_species.z() + 1
// );
// std::expected<Species, fourdst::atomic::SpeciesErrorType> downProduct = fourdst::atomic::az_to_species(
// parent_species.a(),
// parent_species.z() - 1
// );
// if (downProduct.has_value()) { // Only add the reaction if the Species map contains the product
// master_reaction_pool.add_reaction(
// std::make_unique<rates::weak::WeakReaction>(
// parent_species,
// rates::weak::WeakReactionType::BETA_PLUS_DECAY,
// weakInterpolator
// )
// );
// master_reaction_pool.add_reaction(
// std::make_unique<rates::weak::WeakReaction>(
// parent_species,
// rates::weak::WeakReactionType::ELECTRON_CAPTURE,
// weakInterpolator
// )
// );
// }
// if (upProduct.has_value()) { // Only add the reaction if the Species map contains the product
// master_reaction_pool.add_reaction(
// std::make_unique<rates::weak::WeakReaction>(
// parent_species,
// rates::weak::WeakReactionType::BETA_MINUS_DECAY,
// weakInterpolator
// )
// );
// master_reaction_pool.add_reaction(
// std::make_unique<rates::weak::WeakReaction>(
// parent_species,
// rates::weak::WeakReactionType::POSITRON_CAPTURE,
// weakInterpolator
// )
// );
// }
2025-10-30 15:30:55 -04:00
// } // TODO: Remove comments, weak reactions have been disabled for testing
2025-10-07 15:16:03 -04:00
// --- Step 2: Use non-owning raw pointers for the fast build algorithm ---
std : : vector < Reaction * > remainingReactions ;
remainingReactions . reserve ( master_reaction_pool . size ( ) ) ;
for ( const auto & reaction : master_reaction_pool ) {
remainingReactions . push_back ( reaction . get ( ) ) ;
}
2025-07-14 14:50:49 -04:00
if ( depth = = static_cast < int > ( NetworkBuildDepth : : Full ) ) {
2025-10-07 15:16:03 -04:00
LOG_INFO ( logger , " Building full nuclear network with a total of {} reactions. " , remainingReactions . size ( ) ) ;
return master_reaction_pool ; // No need to build layer by layer if we want the full network
2025-07-14 14:50:49 -04:00
}
2025-10-07 15:16:03 -04:00
// --- Step 3: Execute the layered network build using observing pointers ---
2025-07-14 14:50:49 -04:00
std : : unordered_set < Species > availableSpecies ;
2025-10-07 15:16:03 -04:00
for ( const auto & entry : composition | std : : views : : values ) {
2025-07-14 14:50:49 -04:00
if ( entry . mass_fraction ( ) > 0.0 ) {
availableSpecies . insert ( entry . isotope ( ) ) ;
}
}
2025-10-07 15:16:03 -04:00
std : : vector < Reaction * > collectedReactionPtrs ;
2025-07-14 14:50:49 -04:00
LOG_INFO ( logger , " Starting network construction with {} available species. " , availableSpecies . size ( ) ) ;
2025-10-07 15:16:03 -04:00
2025-07-14 14:50:49 -04:00
for ( int layer = 0 ; layer < depth & & ! remainingReactions . empty ( ) ; + + layer ) {
2025-10-14 13:37:48 -04:00
size_t collectedThisLayer = 0 ;
size_t collectedStrong = 0 ;
size_t collectedWeak = 0 ;
2025-07-14 14:50:49 -04:00
LOG_TRACE_L1 ( logger , " Collecting reactions for layer {} with {} remaining reactions. Currently there are {} available species " , layer , remainingReactions . size ( ) , availableSpecies . size ( ) ) ;
2025-08-14 13:33:46 -04:00
std : : vector < Reaction * > reactionsForNextPass ;
2025-07-14 14:50:49 -04:00
std : : unordered_set < Species > newProductsThisLayer ;
bool newReactionsAdded = false ;
reactionsForNextPass . reserve ( remainingReactions . size ( ) ) ;
2025-10-07 15:16:03 -04:00
for ( Reaction * reaction : remainingReactions ) {
2025-07-14 14:50:49 -04:00
bool allReactantsAvailable = true ;
2025-08-14 13:33:46 -04:00
for ( const auto & reactant : reaction - > reactants ( ) ) {
2025-07-14 14:50:49 -04:00
if ( ! availableSpecies . contains ( reactant ) ) {
allReactantsAvailable = false ;
break ;
}
}
if ( allReactantsAvailable ) {
2025-10-07 15:16:03 -04:00
collectedReactionPtrs . push_back ( reaction ) ;
2025-10-14 13:37:48 -04:00
if ( reaction - > type ( ) = = reaction : : ReactionType : : WEAK ) {
collectedWeak + + ;
} else {
collectedStrong + + ;
}
collectedThisLayer + + ;
2025-07-14 14:50:49 -04:00
newReactionsAdded = true ;
2025-08-14 13:33:46 -04:00
for ( const auto & product : reaction - > products ( ) ) {
2025-07-14 14:50:49 -04:00
newProductsThisLayer . insert ( product ) ;
}
} else {
reactionsForNextPass . push_back ( reaction ) ;
}
}
if ( ! newReactionsAdded ) {
2025-10-07 15:16:03 -04:00
LOG_INFO ( logger , " No new reactions added in layer {}. Stopping network construction. " , layer ) ;
2025-07-14 14:50:49 -04:00
break ;
}
2025-10-14 13:37:48 -04:00
size_t oldProductCount = availableSpecies . size ( ) ;
availableSpecies . insert ( newProductsThisLayer . begin ( ) , newProductsThisLayer . end ( ) ) ;
size_t newProductCount = availableSpecies . size ( ) - oldProductCount ;
2025-10-10 09:12:40 -04:00
LOG_TRACE_L1 (
logger ,
2025-10-14 13:37:48 -04:00
" Layer {}: Collected {} new reactions ({} strong, {} weak). New products this layer: {} " ,
2025-10-10 09:12:40 -04:00
layer ,
2025-10-14 13:37:48 -04:00
collectedThisLayer ,
collectedStrong ,
collectedWeak ,
newProductCount
2025-10-10 09:12:40 -04:00
) ;
2025-07-14 14:50:49 -04:00
remainingReactions = std : : move ( reactionsForNextPass ) ;
}
2025-10-07 15:16:03 -04:00
// --- Step 4: Construct the final ReactionSet by moving ownership ---
LOG_INFO ( logger , " Network construction completed. Assembling final set of {} reactions. " , collectedReactionPtrs . size ( ) ) ;
ReactionSet finalReactionSet ;
std : : unordered_set < Reaction * > collectedPtrSet ( collectedReactionPtrs . begin ( ) , collectedReactionPtrs . end ( ) ) ;
for ( auto & reaction_ptr : master_reaction_pool ) {
if ( reaction_ptr & & collectedPtrSet . contains ( reaction_ptr . get ( ) ) ) {
finalReactionSet . add_reaction ( std : : move ( reaction_ptr ) ) ;
}
}
return finalReactionSet ;
2025-07-14 14:50:49 -04:00
}
2025-10-07 15:16:03 -04:00
2025-07-14 14:50:49 -04:00
}