22#include <unordered_set>
25#include "HepMC3/GenEvent.h"
26#include "HepMC3/GenParticle.h"
29#include "marley/DiscreteNuclearReaction.hh"
30#include "marley/Error.hh"
31#include "marley/FileManager.hh"
32#include "marley/JSON.hh"
33#include "marley/Generator.hh"
34#include "marley/StrengthVariationWeightCalculator.hh"
35#include "marley/hepmc3_utils.hh"
36#include "marley/marley_utils.hh"
44 std::vector< std::string > shortest_roundtrip_labels(
45 const std::vector< double >& values )
47 constexpr int MAX_PREC = std::numeric_limits< double >::max_digits10;
48 std::vector< std::string > labels;
49 labels.reserve( values.size() );
51 for (
double v : values ) {
53 for (
int prec = 0; prec <= MAX_PREC; ++prec ) {
54 std::ostringstream ss;
55 ss << std::fixed << std::setprecision( prec ) << v;
56 std::string s = ss.str();
58 auto dot = s.find(
'.' );
59 if ( dot != std::string::npos ) {
60 auto last = s.find_last_not_of(
'0' );
61 if ( last == dot ) s.erase( dot );
62 else s.erase( last + 1 );
65 if ( std::stod( s ) == v ) { best = s;
break; }
67 if ( best.empty() ) best = std::to_string( v );
68 labels.push_back( std::move( best ) );
71 std::unordered_set< std::string > seen;
72 for (
const auto& l : labels ) {
73 if ( !seen.insert( l ).second ) {
75 " shortest_roundtrip_labels" );
87marley::StrengthVariationWeightCalculator
88 ::StrengthVariationWeightCalculator(
const std::string& name,
89 std::shared_ptr< std::mt19937_64 > rng,
90 const std::string& resolved_reaction_file )
91 : WeightCalculator( name ), rng_( std::move( rng ) ),
92 resolved_reaction_file_( resolved_reaction_file ),
93 mode_( VariationMode::multisim )
99marley::StrengthVariationWeightCalculator
100 ::StrengthVariationWeightCalculator(
const std::string& name,
101 double sigma_factor,
const std::string& resolved_reaction_file )
102 : WeightCalculator( name ), rng_( nullptr ),
103 resolved_reaction_file_( resolved_reaction_file ),
104 mode_( VariationMode::shift ),
105 sigma_factor_( sigma_factor )
111marley::StrengthVariationWeightCalculator
112 ::StrengthVariationWeightCalculator(
const std::string& name,
113 double sigma_factor,
size_t matrix_element_index,
114 const std::string& resolved_reaction_file )
115 : WeightCalculator( name ), rng_( nullptr ),
116 resolved_reaction_file_( resolved_reaction_file ),
117 mode_( VariationMode::unisim ),
118 sigma_factor_( sigma_factor ),
119 me_idx_( matrix_element_index )
125std::vector< std::shared_ptr<
131 if ( !config.has_key(
"reaction_file" ) ) {
133 " strength_variation weight calculator JSON configuration" );
135 std::string reaction_file = config.at(
"reaction_file" ).to_string();
138 if ( resolved_reaction_file.empty() ) {
140 + reaction_file +
"\" requested by a strength_variation"
141 " weight calculator" );
145 std::string mode_str =
"multisim";
146 if ( config.has_key(
"mode" ) ) {
147 mode_str = config.at(
"mode" ).to_string();
151 if ( !config.has_key(
"name" ) ) {
153 " strength_variation weight calculator JSON configuration" );
155 std::string base_name = config.at(
"name" ).to_string();
157 std::vector< std::shared_ptr<
158 StrengthVariationWeightCalculator > > instances;
160 if ( mode_str ==
"multisim" ) {
163 if ( config.has_key(
"sigma_factor" ) ) {
164 throw marley::Error(
"The \"sigma_factor\" key is not allowed"
165 " in multisim mode for strength_variation weight calculators" );
169 if ( !config.has_key(
"num_variations" ) ) {
171 " strength_variation weight calculator JSON configuration" );
173 const auto& nv = config.at(
"num_variations" );
174 if ( !nv.is_integer() ) {
175 throw marley::Error(
"The \"num_variations\" value must be a"
176 " positive integer" );
178 long num_instances = nv.to_long();
179 if ( num_instances <= 0 ) {
180 throw marley::Error(
"The \"num_variations\" value must be a"
181 " positive integer" );
186 if ( config.has_key(
"seed" ) ) {
187 seed = config.at(
"seed" ).to_long();
191 auto rng = std::make_shared< std::mt19937_64 >(
192 static_cast< std::mt19937_64::result_type
>( seed ) );
195 for (
long idx = 0; idx < num_instances; ++idx ) {
196 instances.push_back( std::shared_ptr<
197 StrengthVariationWeightCalculator >(
198 new StrengthVariationWeightCalculator(
199 base_name +
'_' + std::to_string( idx ),
200 rng, resolved_reaction_file ) ) );
203 else if ( mode_str ==
"shift" ) {
206 if ( config.has_key(
"num_variations" ) ) {
207 throw marley::Error(
"The \"num_variations\" key is not allowed"
208 " in shift mode for strength_variation weight"
211 if ( config.has_key(
"seed" ) ) {
213 " in shift mode for strength_variation weight"
218 if ( !config.has_key(
"sigma_factor" ) ) {
220 " strength_variation weight calculator JSON configuration"
221 " with shift mode" );
223 const auto& sf = config.at(
"sigma_factor" );
225 std::vector< double > factors;
226 if ( sf.is_array() ) {
227 for (
const auto& elem : sf.array_range() ) {
228 factors.push_back( elem.to_double_or_throw() );
230 if ( factors.empty() ) {
232 " at least one element" );
236 factors.push_back( sf.to_double_or_throw() );
241 std::set< double > seen;
242 for (
double f : factors ) {
243 if ( !seen.insert( f ).second ) {
245 + std::to_string( f ) +
"\" in strength_variation"
246 " weight calculator configuration" );
252 auto labels = shortest_roundtrip_labels( factors );
255 for (
size_t i = 0; i < factors.size(); ++i ) {
256 double k = factors[ i ];
259 instances.push_back( std::shared_ptr<
260 StrengthVariationWeightCalculator >(
261 new StrengthVariationWeightCalculator(
262 base_name +
"-up@" + labels[ i ],
263 +k, resolved_reaction_file ) ) );
266 instances.push_back( std::shared_ptr<
267 StrengthVariationWeightCalculator >(
268 new StrengthVariationWeightCalculator(
269 base_name +
"-down@" + labels[ i ],
270 -k, resolved_reaction_file ) ) );
273 else if ( mode_str ==
"unisim" ) {
276 if ( config.has_key(
"num_variations" ) ) {
277 throw marley::Error(
"The \"num_variations\" key is not allowed"
278 " in unisim mode for strength_variation weight calculators" );
280 if ( config.has_key(
"seed" ) ) {
282 " in unisim mode for strength_variation weight calculators" );
286 if ( !config.has_key(
"sigma_factor" ) ) {
288 " strength_variation weight calculator JSON configuration"
289 " with unisim mode" );
291 const auto& sf = config.at(
"sigma_factor" );
293 std::vector< double > factors;
294 if ( sf.is_array() ) {
295 for (
const auto& elem : sf.array_range() ) {
296 factors.push_back( elem.to_double_or_throw() );
298 if ( factors.empty() ) {
300 " at least one element" );
304 factors.push_back( sf.to_double_or_throw() );
309 std::set< double > seen;
310 for (
double f : factors ) {
311 if ( !seen.insert( f ).second ) {
313 + std::to_string( f ) +
"\" in strength_variation"
314 " weight calculator configuration" );
322 if ( rptr->source_file() != resolved_reaction_file )
continue;
323 auto pt = rptr->process_type();
333 + resolved_reaction_file +
"\" was loaded as a discrete nuclear"
334 " reaction type but the corresponding Reaction object is not a"
335 " DiscreteNuclearReaction. This should not happen and likely"
336 " indicates a bug in MARLEY." );
339 if ( !dnr )
throw marley::Error(
"Could not find a discrete nuclear"
340 " reaction with the source file \"" + resolved_reaction_file
341 +
"\" in the Generator." );
346 auto labels = shortest_roundtrip_labels( factors );
349 for (
size_t i = 0; i < factors.size(); ++i ) {
350 double k = factors[ i ];
351 for (
size_t m = 0; m < M; ++m ) {
353 instances.push_back( std::shared_ptr<
354 StrengthVariationWeightCalculator >(
355 new StrengthVariationWeightCalculator(
356 base_name +
"-me" + std::to_string( m )
357 +
"_up@" + labels[ i ],
358 +k, m, resolved_reaction_file ) ) );
361 instances.push_back( std::shared_ptr<
362 StrengthVariationWeightCalculator >(
363 new StrengthVariationWeightCalculator(
364 base_name +
"-me" + std::to_string( m )
365 +
"_down@" + labels[ i ],
366 -k, m, resolved_reaction_file ) ) );
372 + mode_str +
"\" for strength_variation weight calculator."
373 " Allowed values are \"multisim\", \"shift\","
393 auto pt = reaction_ptr->process_type();
402 reaction_ptr.get() );
405 " reaction type but the corresponding Reaction object is not a"
406 " DiscreteNuclearReaction. This should not happen and likely"
407 " indicates a bug in MARLEY." );
414 const auto& matrix_els = dnr->matrix_elements();
415 varied_.reserve( matrix_els.size() );
417 if (
mode_ == VariationMode::multisim ) {
418 std::normal_distribution< double > normal_dist;
419 for (
const auto& me : matrix_els ) {
420 double nom = me.strength();
421 double err_low = me.strength_err_low();
422 double err_high = me.strength_err_high();
425 if ( err_low == 0. && err_high == 0. ) {
429 double u = normal_dist( *
rng_ );
430 double sigma = ( u >= 0. ) ? err_high : err_low;
431 varied = nom + u * sigma;
432 if ( varied < 0. ) varied = 0.;
437 else if (
mode_ == VariationMode::shift ) {
438 for (
const auto& me : matrix_els ) {
439 double nom = me.strength();
440 double err_low = me.strength_err_low();
441 double err_high = me.strength_err_high();
444 if ( err_low == 0. && err_high == 0. ) {
450 if ( varied < 0. ) varied = 0.;
456 for (
const auto& me : matrix_els ) {
457 varied_.push_back( me.strength() );
459 const auto& target_me = matrix_els[
me_idx_ ];
460 double nom = target_me.strength();
461 double err_low = target_me.strength_err_low();
462 double err_high = target_me.strength_err_high();
463 if ( err_low != 0. || err_high != 0. ) {
466 if ( varied < 0. ) varied = 0.;
475 throw marley::Error(
"Could not find a discrete nuclear reaction"
477 +
"\" in the Generator." );
487 "signal_process_id" );
488 if ( !sp_attr )
return 1.;
489 auto event_pt = marley_hepmc3::from_nuhepmc_proc_id(
494 auto target = marley_hepmc3::get_target( event );
495 if ( !target || target->pdg_id() !=
target_pdg_ )
return 1.;
500 if ( !mi_attr )
return 1.;
501 size_t mi =
static_cast< size_t >( mi_attr->value() );
502 if ( mi >=
varied_.size() )
return 1.;
506 double nom =
dnr_->matrix_elements().at( mi ).strength();
507 if ( nom == 0. )
return 1.;
Stores event-related information.
Attribute that holds an Integer implemented as an int.
A neutrino-nucleus reaction whose cross section is calculated according to the allowed approximation.
const std::vector< marley::MatrixElement > & matrix_elements() const
Allows access to the owned vector of MatrixElement objects.
Base class for all exceptions thrown by MARLEY functions.
static const FileManager & Instance()
Get a const reference to the singleton instance of the FileManager.
std::string find_file(const std::string &base_name, const std::vector< std::string > &search_dirs) const
Searches for a file in the given directories.
The MARLEY Event generator.
const std::vector< std::unique_ptr< marley::Reaction > > & get_reactions() const
Get a const reference to the vector of Reaction objects owned by this Generator.
@ NC_Discrete
Nuclear matrix elements contain for a transition to a discrete nuclear level.
@ AntiNeutrinoCC_Discrete
Nuclear matrix elements contain for a transition to a discrete nuclear level.
@ NeutrinoCC_Discrete
Nuclear matrix elements contain for a transition to a discrete nuclear level.
WeightCalculator that varies nuclear matrix element strengths according to their experimental uncerta...
Reaction::ProcessType process_type_
Process type of the matched Reaction.
void ensure_initialized(marley::Generator &gen) const
Lazy initialization of per-instance varied strengths.
std::string resolved_reaction_file_
Resolved path of the reaction input file of interest.
const DiscreteNuclearReaction * dnr_
Pointer to the matched DiscreteNuclearReaction.
size_t me_idx_
Index of the single matrix element to vary (unisim mode)
double sigma_factor_
Signed sigma factor for systematic shifts (shift and unisim modes)
static std::vector< std::shared_ptr< StrengthVariationWeightCalculator > > create_instances(const marley::JSON &config, marley::Generator &gen)
Static factory: validates JSON configuration and creates all variation instances. Called by the Weigh...
bool initialized_
Whether lazy initialization has been completed.
std::vector< double > varied_
Pre-generated varied strengths (one per matrix element in the matched Reaction)
int target_pdg_
Target nucleus PDG code for the matched Reaction.
VariationMode mode_
Variation mode for this instance.
virtual double weight(HepMC3::GenEvent &event, marley::Generator &gen) const override
Compute the weight for the given event.
std::shared_ptr< std::mt19937_64 > rng_
Shared RNG (seeded once, shared across N instances)