MARLEY (Model of Argon Reaction Low Energy Yields) v2.0.0
A Monte Carlo event generator for tens-of-MeV neutrino interactions
Loading...
Searching...
No Matches
marley::StrengthVariationWeightCalculator Class Reference

WeightCalculator that varies nuclear matrix element strengths according to their experimental uncertainties. Three modes are available: "multisim" (dimidiated Gaussian random draws), "shift" (deterministic systematic shifts of all matrix elements), and "unisim" (deterministic systematic shifts of a single matrix element at a time). More...

#include <StrengthVariationWeightCalculator.hh>

Inheritance diagram for marley::StrengthVariationWeightCalculator:
marley::WeightCalculator

Public Types

enum class  VariationMode { multisim , shift , unisim }
 Supported variation modes. More...
 

Public Member Functions

virtual double weight (HepMC3::GenEvent &event, marley::Generator &gen) const override
 Compute the weight for the given event.
 
- Public Member Functions inherited from marley::WeightCalculator
 WeightCalculator (const marley::JSON &config)
 
 WeightCalculator (const std::string &name)
 
const std::string & name () const
 

Static Public Member Functions

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 Weighter for each strength_variation entry in the weights config array.
 

Protected Member Functions

void ensure_initialized (marley::Generator &gen) const
 Lazy initialization of per-instance varied strengths.
 

Protected Attributes

const DiscreteNuclearReactiondnr_ = nullptr
 Pointer to the matched DiscreteNuclearReaction.
 
bool initialized_ = false
 Whether lazy initialization has been completed.
 
size_t me_idx_ = 0
 Index of the single matrix element to vary (unisim mode)
 
VariationMode mode_
 Variation mode for this instance.
 
Reaction::ProcessType process_type_ = Reaction::ProcessType::Unknown
 Process type of the matched Reaction.
 
std::string resolved_reaction_file_
 Resolved path of the reaction input file of interest.
 
std::shared_ptr< std::mt19937_64 > rng_
 Shared RNG (seeded once, shared across N instances)
 
double sigma_factor_ = 0.
 Signed sigma factor for systematic shifts (shift and unisim modes)
 
int target_pdg_ = 0
 Target nucleus PDG code for the matched Reaction.
 
std::vector< double > varied_
 Pre-generated varied strengths (one per matrix element in the matched Reaction)
 
- Protected Attributes inherited from marley::WeightCalculator
std::string name_
 Name used to label the output event weight.
 

Detailed Description

WeightCalculator that varies nuclear matrix element strengths according to their experimental uncertainties. Three modes are available: "multisim" (dimidiated Gaussian random draws), "shift" (deterministic systematic shifts of all matrix elements), and "unisim" (deterministic systematic shifts of a single matrix element at a time).

Definition at line 45 of file StrengthVariationWeightCalculator.hh.

Member Enumeration Documentation

◆ VariationMode

Supported variation modes.

Definition at line 50 of file StrengthVariationWeightCalculator.hh.

50{ multisim, shift, unisim };

Member Function Documentation

◆ create_instances()

std::vector< std::shared_ptr< marley::StrengthVariationWeightCalculator > > marley::StrengthVariationWeightCalculator::create_instances ( const marley::JSON & config,
marley::Generator & gen )
static

Static factory: validates JSON configuration and creates all variation instances. Called by the Weighter for each strength_variation entry in the weights config array.

Parameters
configJSON configuration for this calculator
genGenerator whose loaded reactions are used by the unisim mode to determine the number of matrix elements
Returns
Vector of shared pointers to the created instances

Definition at line 127 of file StrengthVariationWeightCalculator.cc.

129{
130 // Resolve the reaction file
131 if ( !config.has_key( "reaction_file" ) ) {
132 throw marley::Error( "Missing \"reaction_file\" key in a"
133 " strength_variation weight calculator JSON configuration" );
134 }
135 std::string reaction_file = config.at( "reaction_file" ).to_string();
136 std::string resolved_reaction_file = marley::FileManager::Instance()
137 .find_file( reaction_file );
138 if ( resolved_reaction_file.empty() ) {
139 throw marley::Error( "Could not find reaction data file \""
140 + reaction_file + "\" requested by a strength_variation"
141 " weight calculator" );
142 }
143
144 // Read the optional variation mode (default "multisim")
145 std::string mode_str = "multisim";
146 if ( config.has_key( "mode" ) ) {
147 mode_str = config.at( "mode" ).to_string();
148 }
149
150 // Extract the base name
151 if ( !config.has_key( "name" ) ) {
152 throw marley::Error( "Missing \"name\" key in a"
153 " strength_variation weight calculator JSON configuration" );
154 }
155 std::string base_name = config.at( "name" ).to_string();
156
157 std::vector< std::shared_ptr<
158 StrengthVariationWeightCalculator > > instances;
159
160 if ( mode_str == "multisim" ) {
161
162 // Reject sigma_factor key in multisim mode
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" );
166 }
167
168 // Read and validate the number of variations
169 if ( !config.has_key( "num_variations" ) ) {
170 throw marley::Error( "Missing \"num_variations\" key in a"
171 " strength_variation weight calculator JSON configuration" );
172 }
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" );
177 }
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" );
182 }
183
184 // Read the optional seed (default 0)
185 long seed = 0;
186 if ( config.has_key( "seed" ) ) {
187 seed = config.at( "seed" ).to_long();
188 }
189
190 // Create one shared RNG for all instances
191 auto rng = std::make_shared< std::mt19937_64 >(
192 static_cast< std::mt19937_64::result_type >( seed ) );
193
194 // Create num_instances weight calculators, each sharing the RNG
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 ) ) );
201 }
202 }
203 else if ( mode_str == "shift" ) {
204
205 // Reject keys inappropriate for 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"
209 " calculators" );
210 }
211 if ( config.has_key( "seed" ) ) {
212 throw marley::Error( "The \"seed\" key is not allowed"
213 " in shift mode for strength_variation weight"
214 " calculators" );
215 }
216
217 // Read and validate sigma_factor
218 if ( !config.has_key( "sigma_factor" ) ) {
219 throw marley::Error( "Missing \"sigma_factor\" key in a"
220 " strength_variation weight calculator JSON configuration"
221 " with shift mode" );
222 }
223 const auto& sf = config.at( "sigma_factor" );
224
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() );
229 }
230 if ( factors.empty() ) {
231 throw marley::Error( "The \"sigma_factor\" array must have"
232 " at least one element" );
233 }
234 }
235 else {
236 factors.push_back( sf.to_double_or_throw() );
237 }
238
239 // Reject duplicate sigma_factor values (exact equality)
240 {
241 std::set< double > seen;
242 for ( double f : factors ) {
243 if ( !seen.insert( f ).second ) {
244 throw marley::Error( "Duplicate sigma_factor value \""
245 + std::to_string( f ) + "\" in strength_variation"
246 " weight calculator configuration" );
247 }
248 }
249 }
250
251 // Generate shortest roundtrip labels for all factors
252 auto labels = shortest_roundtrip_labels( factors );
253
254 // Create two instances per factor (+k and -k)
255 for ( size_t i = 0; i < factors.size(); ++i ) {
256 double k = factors[ i ];
257
258 // +k "up" instance
259 instances.push_back( std::shared_ptr<
260 StrengthVariationWeightCalculator >(
261 new StrengthVariationWeightCalculator(
262 base_name + "-up@" + labels[ i ],
263 +k, resolved_reaction_file ) ) );
264
265 // -k "down" instance
266 instances.push_back( std::shared_ptr<
267 StrengthVariationWeightCalculator >(
268 new StrengthVariationWeightCalculator(
269 base_name + "-down@" + labels[ i ],
270 -k, resolved_reaction_file ) ) );
271 }
272 }
273 else if ( mode_str == "unisim" ) {
274
275 // Reject keys inappropriate for 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" );
279 }
280 if ( config.has_key( "seed" ) ) {
281 throw marley::Error( "The \"seed\" key is not allowed"
282 " in unisim mode for strength_variation weight calculators" );
283 }
284
285 // Read and validate sigma_factor
286 if ( !config.has_key( "sigma_factor" ) ) {
287 throw marley::Error( "Missing \"sigma_factor\" key in a"
288 " strength_variation weight calculator JSON configuration"
289 " with unisim mode" );
290 }
291 const auto& sf = config.at( "sigma_factor" );
292
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() );
297 }
298 if ( factors.empty() ) {
299 throw marley::Error( "The \"sigma_factor\" array must have"
300 " at least one element" );
301 }
302 }
303 else {
304 factors.push_back( sf.to_double_or_throw() );
305 }
306
307 // Reject duplicate sigma_factor values (exact equality)
308 {
309 std::set< double > seen;
310 for ( double f : factors ) {
311 if ( !seen.insert( f ).second ) {
312 throw marley::Error( "Duplicate sigma_factor value \""
313 + std::to_string( f ) + "\" in strength_variation"
314 " weight calculator configuration" );
315 }
316 }
317 }
318
319 // Find the matching DiscreteNuclearReaction from the Generator
320 const DiscreteNuclearReaction* dnr = nullptr;
321 for ( const auto& rptr : gen.get_reactions() ) {
322 if ( rptr->source_file() != resolved_reaction_file ) continue;
323 auto pt = rptr->process_type();
327 {
328 continue;
329 }
330 dnr = dynamic_cast< const DiscreteNuclearReaction* >(
331 rptr.get() );
332 if ( !dnr ) throw marley::Error( "Reaction data file \""
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." );
337 break;
338 }
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." );
342
343 size_t M = dnr->matrix_elements().size();
344
345 // Generate shortest roundtrip labels for all factors
346 auto labels = shortest_roundtrip_labels( factors );
347
348 // Create two instances per factor per matrix element
349 for ( size_t i = 0; i < factors.size(); ++i ) {
350 double k = factors[ i ];
351 for ( size_t m = 0; m < M; ++m ) {
352 // +k "up" instance for this matrix element
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 ) ) );
359
360 // -k "down" instance for this matrix element
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 ) ) );
367 }
368 }
369 }
370 else {
371 throw marley::Error( "Unrecognized variation mode \""
372 + mode_str + "\" for strength_variation weight calculator."
373 " Allowed values are \"multisim\", \"shift\","
374 " and \"unisim\"" );
375 }
376
377 return instances;
378}
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.
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.
Definition Generator.hh:468
@ NC_Discrete
Nuclear matrix elements contain for a transition to a discrete nuclear level.
Definition Reaction.hh:62
@ AntiNeutrinoCC_Discrete
Nuclear matrix elements contain for a transition to a discrete nuclear level.
Definition Reaction.hh:61
@ NeutrinoCC_Discrete
Nuclear matrix elements contain for a transition to a discrete nuclear level.
Definition Reaction.hh:60

References marley::Reaction::AntiNeutrinoCC_Discrete, marley::FileManager::find_file(), marley::Generator::get_reactions(), marley::FileManager::Instance(), marley::DiscreteNuclearReaction::matrix_elements(), marley::Reaction::NC_Discrete, and marley::Reaction::NeutrinoCC_Discrete.

◆ ensure_initialized()

void marley::StrengthVariationWeightCalculator::ensure_initialized ( marley::Generator & gen) const
protected

Lazy initialization of per-instance varied strengths.

Definition at line 380 of file StrengthVariationWeightCalculator.cc.

382{
383 if ( initialized_ ) return;
384
385 // Scan the Generator's reactions to find the one matching our
386 // resolved reaction file path and a discrete nuclear process type
387 for ( const auto& reaction_ptr : gen.get_reactions() ) {
388
389 if ( reaction_ptr->source_file() != resolved_reaction_file_ ) continue;
390
391 // Only nuclear reactions populating discrete nuclear levels may be
392 // handled by this weight calculator
393 auto pt = reaction_ptr->process_type();
397 {
398 continue;
399 }
400
401 auto* dnr = dynamic_cast< const DiscreteNuclearReaction* >(
402 reaction_ptr.get() );
403 if ( !dnr ) throw marley::Error( "Reaction data file \""
404 + resolved_reaction_file_ + "\" was loaded as a discrete nuclear"
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." );
408
409 dnr_ = dnr;
410 process_type_ = pt;
411 target_pdg_ = dnr->pdg_b();
412
413 // Pre-generate varied strengths depending on the variation mode
414 const auto& matrix_els = dnr->matrix_elements();
415 varied_.reserve( matrix_els.size() );
416
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();
423
424 double varied;
425 if ( err_low == 0. && err_high == 0. ) {
426 varied = nom;
427 }
428 else {
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.;
433 }
434 varied_.push_back( varied );
435 }
436 }
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();
442
443 double varied;
444 if ( err_low == 0. && err_high == 0. ) {
445 varied = nom;
446 }
447 else {
448 double sigma = ( sigma_factor_ >= 0. ) ? err_high : err_low;
449 varied = nom + sigma_factor_ * sigma;
450 if ( varied < 0. ) varied = 0.;
451 }
452 varied_.push_back( varied );
453 }
454 }
455 else { // unisim
456 for ( const auto& me : matrix_els ) {
457 varied_.push_back( me.strength() );
458 }
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. ) {
464 double sigma = ( sigma_factor_ >= 0. ) ? err_high : err_low;
465 double varied = nom + sigma_factor_ * sigma;
466 if ( varied < 0. ) varied = 0.;
467 varied_[ me_idx_ ] = varied;
468 }
469 }
470
471 initialized_ = true;
472 return;
473 }
474
475 throw marley::Error( "Could not find a discrete nuclear reaction"
476 " with the source file \"" + resolved_reaction_file_
477 + "\" in the Generator." );
478}
Reaction::ProcessType process_type_
Process type of the matched Reaction.
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)
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.
std::shared_ptr< std::mt19937_64 > rng_
Shared RNG (seeded once, shared across N instances)

References marley::Reaction::AntiNeutrinoCC_Discrete, dnr_, marley::Generator::get_reactions(), initialized_, me_idx_, mode_, marley::Reaction::NC_Discrete, marley::Reaction::NeutrinoCC_Discrete, process_type_, resolved_reaction_file_, rng_, sigma_factor_, target_pdg_, and varied_.

Referenced by weight().

◆ weight()

double marley::StrengthVariationWeightCalculator::weight ( HepMC3::GenEvent & event,
marley::Generator & gen ) const
overridevirtual

Compute the weight for the given event.

Note
If a derived class uses random numbers, it MUST own its own random number generator (seeded with a fixed value from its JSON config) rather than using the Generator's RNG. Random numbers MUST also only be used by derived classes during initialization, not on an event-by-event basis. These rules ensure that the calculated weights remain deterministic and replayable regardless of when or in what context the calculator is used. In particular, this allows the "resume" behavior of the "marley generate" command to be correct even after reweighting has been run on an existing sample.
Todo
Revisit this constraint if a clear use case is found for event-by-event random numbers sampled within a WeightCalculator derived class implementation.

Implements marley::WeightCalculator.

Definition at line 480 of file StrengthVariationWeightCalculator.cc.

482{
483 ensure_initialized( gen );
484
485 // Check that the event's process type matches this calculator's
486 auto sp_attr = event.attribute< HepMC3::IntAttribute >(
487 "signal_process_id" );
488 if ( !sp_attr ) return 1.;
489 auto event_pt = marley_hepmc3::from_nuhepmc_proc_id(
490 sp_attr->value() );
491 if ( event_pt != process_type_ ) return 1.;
492
493 // Verify that the event's target nucleus PDG code matches
494 auto target = marley_hepmc3::get_target( event );
495 if ( !target || target->pdg_id() != target_pdg_ ) return 1.;
496
497 // Read the matrix element index from the event
498 auto mi_attr = event.attribute< HepMC3::IntAttribute >(
499 "me_index" );
500 if ( !mi_attr ) return 1.;
501 size_t mi = static_cast< size_t >( mi_attr->value() );
502 if ( mi >= varied_.size() ) return 1.;
503
504 // Compute the weight as the ratio of the varied strength to the
505 // nominal strength
506 double nom = dnr_->matrix_elements().at( mi ).strength();
507 if ( nom == 0. ) return 1.;
508 return varied_.at( mi ) / nom;
509}
void ensure_initialized(marley::Generator &gen) const
Lazy initialization of per-instance varied strengths.

References dnr_, ensure_initialized(), process_type_, target_pdg_, and varied_.

Member Data Documentation

◆ dnr_

const DiscreteNuclearReaction* marley::StrengthVariationWeightCalculator::dnr_ = nullptr
mutableprotected

Pointer to the matched DiscreteNuclearReaction.

Definition at line 112 of file StrengthVariationWeightCalculator.hh.

Referenced by ensure_initialized(), and weight().

◆ initialized_

bool marley::StrengthVariationWeightCalculator::initialized_ = false
mutableprotected

Whether lazy initialization has been completed.

Definition at line 109 of file StrengthVariationWeightCalculator.hh.

Referenced by ensure_initialized().

◆ me_idx_

size_t marley::StrengthVariationWeightCalculator::me_idx_ = 0
protected

Index of the single matrix element to vary (unisim mode)

Definition at line 106 of file StrengthVariationWeightCalculator.hh.

Referenced by ensure_initialized().

◆ mode_

VariationMode marley::StrengthVariationWeightCalculator::mode_
protected

Variation mode for this instance.

Definition at line 99 of file StrengthVariationWeightCalculator.hh.

Referenced by ensure_initialized().

◆ process_type_

Reaction::ProcessType marley::StrengthVariationWeightCalculator::process_type_ = Reaction::ProcessType::Unknown
mutableprotected

Process type of the matched Reaction.

Definition at line 115 of file StrengthVariationWeightCalculator.hh.

Referenced by ensure_initialized(), and weight().

◆ resolved_reaction_file_

std::string marley::StrengthVariationWeightCalculator::resolved_reaction_file_
protected

Resolved path of the reaction input file of interest.

Definition at line 96 of file StrengthVariationWeightCalculator.hh.

Referenced by ensure_initialized().

◆ rng_

std::shared_ptr< std::mt19937_64 > marley::StrengthVariationWeightCalculator::rng_
protected

Shared RNG (seeded once, shared across N instances)

Definition at line 93 of file StrengthVariationWeightCalculator.hh.

Referenced by ensure_initialized().

◆ sigma_factor_

double marley::StrengthVariationWeightCalculator::sigma_factor_ = 0.
protected

Signed sigma factor for systematic shifts (shift and unisim modes)

Definition at line 103 of file StrengthVariationWeightCalculator.hh.

Referenced by ensure_initialized().

◆ target_pdg_

int marley::StrengthVariationWeightCalculator::target_pdg_ = 0
mutableprotected

Target nucleus PDG code for the matched Reaction.

Definition at line 119 of file StrengthVariationWeightCalculator.hh.

Referenced by ensure_initialized(), and weight().

◆ varied_

std::vector< double > marley::StrengthVariationWeightCalculator::varied_
mutableprotected

Pre-generated varied strengths (one per matrix element in the matched Reaction)

Definition at line 123 of file StrengthVariationWeightCalculator.hh.

Referenced by ensure_initialized(), and weight().


The documentation for this class was generated from the following files: