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::OMPWeightCalculator Class Reference

Computes event weights based on variations to the optical model potential. More...

#include <OMPWeightCalculator.hh>

Inheritance diagram for marley::OMPWeightCalculator:
marley::WeightCalculator

Public Member Functions

 OMPWeightCalculator (const marley::JSON &config)
 
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
 

Protected Attributes

std::shared_ptr< marley::StructureDatabasesdb_
 Owned StructureDatabase with a custom set of optical model parameters.
 
- Protected Attributes inherited from marley::WeightCalculator
std::string name_
 Name used to label the output event weight.
 

Detailed Description

Computes event weights based on variations to the optical model potential.

Definition at line 24 of file OMPWeightCalculator.hh.

Constructor & Destructor Documentation

◆ OMPWeightCalculator()

marley::OMPWeightCalculator::OMPWeightCalculator ( const marley::JSON & config)

Definition at line 40 of file OMPWeightCalculator.cc.

41 : marley::WeightCalculator( config )
42{
43 if ( !config.is_object() ) {
44 throw marley::Error( "Non-object JSON configuration passed to constructor"
45 " of marley::OMPWeightCalculator" );
46 }
47
48 if ( !config.has_key("opt_mod") ) {
49 throw marley::Error( "Missing \"opt_mod\" key in marley::OMPWeight"
50 "Calculator JSON configuration" );
51 }
52
53 // Load the JSON configuration for the optical model parameters
54 const auto& om_config = config.at( "opt_mod" );
55 if ( !om_config.is_object() ) {
56 throw marley::Error( "Non-object associated with \"opt_mod\" key in"
57 " marley::OMPWeightCalculator JSON configuration" );
58 }
59
60 // Create a structure database with a custom set of optical model parameters
61 sdb_ = std::make_shared< marley::StructureDatabase >();
62 sdb_->load_optical_model_params( &om_config );
63}
std::shared_ptr< marley::StructureDatabase > sdb_
Owned StructureDatabase with a custom set of optical model parameters.

Member Function Documentation

◆ weight()

double marley::OMPWeightCalculator::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 65 of file OMPWeightCalculator.cc.

67{
68 // Start with a result of unity
69 double weight = 1.;
70
71 // Get a vector of pointers to all decay vertices in the event that
72 // were handled using the Hauser-Feshbach model
73 auto hf_vtx_vec = marley_hepmc3::get_vertices_with_status(
74 marley_hepmc3::NUHEPMC_HF_DECAY_VERTEX, ev );
75
76 // Include each vertex that was found in the reweighting calculation
77 for ( const auto& vtx : hf_vtx_vec ) {
78
79 // Start with the default assumption that the decay was to a discrete
80 // nuclear level
81 bool decayed_to_continuum = false;
82
83 const auto& in_vec = vtx->particles_in();
84 const auto& out_vec = vtx->particles_out();
85
86 // Double-check that the expected numbers of particles are present
87 if ( in_vec.size() != 1u || out_vec.size() != 2u ) {
88 throw marley::Error( "Hauser-Feshbach vertex encountered that"
89 " is not a binary decay" );
90 }
91
92 // Retrieve the decay widths computed in the original simulation
93 auto width_tot_attr = vtx->attribute< HepMC3::DoubleAttribute >(
94 "TotalWidth" );
95 double width_tot = width_tot_attr->value();
96
97 auto width_ec_attr = vtx->attribute< HepMC3::DoubleAttribute >(
98 "ECWidth" );
99 double width_ec = width_ec_attr->value();
100
101 // This attribute is only present for decays to the continuum
102 auto width_sp_attr = vtx->attribute< HepMC3::DoubleAttribute >(
103 "SPWidth" );
104 double width_sp = 0.;
105 if ( width_sp_attr ) {
106 decayed_to_continuum = true;
107 width_sp = width_sp_attr->value();
108 }
109
110 // Get access to the mother nucleus for the current binary decay
111 const auto& mother = in_vec.front();
112
113 // Retrieve the starting nuclear excitation energy, spin, and parity
114 // TODO: add error handling here for missing attributes
115 auto Exi_attr = mother->attribute< HepMC3::DoubleAttribute >( "Ex" );
116 double Exi = Exi_attr->value();
117
118 auto twoJi_attr = mother->attribute< HepMC3::IntAttribute >( "twoJ" );
119 int twoJi = twoJi_attr->value();
120
121 auto Pi_attr = mother->attribute< HepMC3::IntAttribute >( "parity" );
122 marley::Parity Pi( Pi_attr->value() );
123
124 // NucleusDecayer populates the outgoing particles in the decay
125 // vertex in a specific order. The first is the emitted nuclear
126 // fragment or gamma-ray, while the second is the daughter nucleus.
127 const auto& emitted_particle = out_vec.front();
128 const auto& daughter = out_vec.back();
129
130 int emitted_pdg = emitted_particle->pid();
131
132 // Retrieve the final nuclear excitation energy, spin, and parity
133 // TODO: add error handling here for missing attributes
134 auto Exf_attr = daughter->attribute< HepMC3::DoubleAttribute >( "Ex" );
135 double Exf = Exf_attr->value();
136
137 auto twoJf_attr = daughter->attribute< HepMC3::IntAttribute >( "twoJ" );
138 int twoJf = twoJf_attr->value();
139
140 auto Pf_attr = daughter->attribute< HepMC3::IntAttribute >( "parity" );
141 marley::Parity Pf( Pf_attr->value() );
142
143 // Construct a set of decay widths calculated with alternative settings
144 marley::HauserFeshbachDecay hf_alt( mother, Exi, twoJi, Pi, *sdb_ );
145
146 // Get the total decay width under the alternative calculation
147 double width_tot_alt = hf_alt.total_width();
148
149 // Find the ExitChannel in the alternative calculation corresponding
150 // to the original decay that was sampled
151 const auto& ec_vec = hf_alt.exit_channels();
152 auto ec_iter = std::find_if( ec_vec.cbegin(), ec_vec.cend(),
153 [ emitted_pdg, decayed_to_continuum, Exf ](
154 const std::unique_ptr< marley::ExitChannel >& test_ec ) -> bool
155 {
156 if ( emitted_pdg != test_ec->emitted_particle_pdg() ) return false;
157
158 if ( decayed_to_continuum ) {
159 if ( !test_ec->is_continuum() ) return false;
160 else return true;
161 }
162
163 // If we get to here, then we're dealing with a discrete transition
164 if ( test_ec->is_continuum() ) return false;
165
166 const auto* dec = dynamic_cast<
167 const marley::DiscreteExitChannel* >( test_ec.get() );
168 if ( !dec ) return false;
169
170 // Check the excitation energy of the final discrete level as
171 // a last confirmation that we've found the correct ExitChannel
172 const auto& lev = dec->get_final_level();
173 double Ex_level = lev.energy();
174
175 // To deal with possible numerical precision issues, this check of
176 // the final excitation energy allows for small differences
177 if ( std::abs(Exf - Ex_level) > PRETTY_SMALL ) return false;
178 return true;
179 }
180 );
181
182 if ( ec_iter == ec_vec.cend() ) {
183 MARLEY_LOG( WARN, "physics.opticalmodel" )
184 << "Could not find ExitChannel during reweighting";
185 weight = 0.;
186 continue;
187 }
188
189 // Partial width for the chosen ExitChannel under the alternative
190 // calculation
191 double width_ec_alt = ( *ec_iter )->width();
192
193 // Partial differential width for the chosen spin-parity under the
194 // alternative calculation (applies only to decays to the continuum)
195 double width_sp_alt = 0.;
196 if ( decayed_to_continuum ) {
197 // Evaluate the differential width at the sampled excitation energy.
198 // This will populate the table of SpinParityWidth objects with the
199 // correct values.
200 const auto& cec = dynamic_cast<
201 const marley::ContinuumExitChannel& >( *(*ec_iter) );
202 cec.differential_width( Exf, true );
203
204 // Retrieve extra information based on the kind of emitted particle
205 int mpol = 0, two_j_frag = 0, orb_l = 0;
206 bool emitted_gamma = false;
207 auto mpol_attr = vtx->attribute< HepMC3::IntAttribute >(
208 "multipolarity" );
209 if ( mpol_attr ) {
210 emitted_gamma = true;
211 mpol = mpol_attr->value();
212 }
213 else {
214 auto two_j_frag_attr = vtx->attribute< HepMC3::IntAttribute >(
215 "two_j_frag" );
216 auto orb_l_attr = vtx->attribute< HepMC3::IntAttribute >( "orb_l" );
217
218 // TODO: add error handling for missing attributes here
219
220 two_j_frag = two_j_frag_attr->value();
221 orb_l = orb_l_attr->value();
222 }
223
224 // Find the SpinParityWidth object corresponding to the spin-parity
225 // value that was actually sampled
226 const auto& spw_vec = cec.get_spw_table();
227 auto spw_iter = std::find_if( spw_vec.cbegin(), spw_vec.cend(),
228 [ twoJf, Pf, emitted_gamma, mpol, two_j_frag, orb_l ](
229 const std::unique_ptr< marley::ContinuumExitChannel
230 ::SpinParityWidth >& spw ) -> bool
231 {
232 if ( Pf != spw->Pf ) return false;
233 if ( twoJf != spw->twoJf ) return false;
234 if ( emitted_gamma ) {
235 const auto* g_spw = static_cast< const marley
236 ::GammaContinuumExitChannel::GammaSpinParityWidth* >(
237 spw.get() );
238 if ( !g_spw ) return false;
239 if ( mpol != g_spw->multipolarity ) return false;
240 }
241 else {
242 // emitted fragment
243 const auto* f_spw = static_cast< const marley
244 ::FragmentContinuumExitChannel::FragmentSpinParityWidth* >(
245 spw.get() );
246 if ( !f_spw ) return false;
247 if ( two_j_frag != f_spw->two_j_frag ) return false;
248 if ( orb_l != f_spw->orb_l ) return false;
249 }
250 return true;
251 }
252 );
253
254 if ( spw_iter == spw_vec.cend() ) {
255 MARLEY_LOG( WARN, "physics.opticalmodel" )
256 << "Could not find SpinParityWidth during reweighting";
257 weight = 0.;
258 continue;
259 }
260
261 // Store the partial differential width to this spin-parity state
262 // under the alternative calculation
263 width_sp_alt = spw_iter->get()->diff_width;
264 }
265
266 // We've accumulated all the width values we need to compute an
267 // event weight. Do some sanity checks beforehand.
268 std::string bad_width_name;
269 if ( width_tot_alt <= 0. ) {
270 bad_width_name = "Alternate total_width";
271 }
272 else if ( width_tot <= 0. ) {
273 bad_width_name = "Original total width";
274 }
275 else if ( width_ec <= 0. ) {
276 bad_width_name = "Original exit channel";
277 }
278 else if ( width_ec_alt <= 0. ) {
279 bad_width_name = "Alternate exit channel";
280 }
281 else if ( decayed_to_continuum ) {
282 if ( width_sp_alt <= 0. ) bad_width_name = "Alternate spin-parity";
283 else if ( width_sp <= 0. ) bad_width_name = "Original spin-parity";
284 }
285
286 if ( !bad_width_name.empty() ) {
287 MARLEY_LOG( WARN, "physics.opticalmodel" ) << bad_width_name << " is non-positive";
288 weight = 0.;
289 continue;
290 }
291
292 // Everything looks okay, so do the weight calculation using the
293 // appropriate expression for a decay to the continuum or a discrete
294 // nuclear level
295 double w = width_tot / width_tot_alt;
296 if ( decayed_to_continuum ) {
297 w *= width_sp_alt / width_sp;
298 }
299 else {
300 w *= width_ec_alt / width_ec;
301 }
302
303 MARLEY_LOG( DEBUG, "physics.opticalmodel" ) << "OMP reweight: vertex"
304 " weight w = " << w << " (tot_width_alt/tot_width = "
305 << width_tot_alt << "/" << width_tot << ")";
306
307 // Multiply the weight for the current decay vertex into the overall
308 // event weight
309 weight *= w;
310
311 } // decay vertex loop
312
313 MARLEY_LOG( DEBUG, "physics.opticalmodel" ) << "OMP reweight: final event"
314 " weight = " << weight;
315 return weight;
316}
int value() const
get the value associated to this Attribute.
Definition Attribute.h:180
virtual bool is_continuum() const =0
Returns true if this channel accesses the particle-unbound continuum of nuclear levels or false other...
virtual double weight(HepMC3::GenEvent &event, marley::Generator &gen) const override
Compute the weight for the given event.

References marley::Level::energy(), marley::HauserFeshbachDecay::exit_channels(), marley::DiscreteExitChannel::get_final_level(), marley::ExitChannel::is_continuum(), sdb_, HepMC3::DoubleAttribute::value(), HepMC3::IntAttribute::value(), and weight().

Referenced by weight().

Member Data Documentation

◆ sdb_

std::shared_ptr< marley::StructureDatabase > marley::OMPWeightCalculator::sdb_
protected

Owned StructureDatabase with a custom set of optical model parameters.

Definition at line 38 of file OMPWeightCalculator.hh.

Referenced by weight().


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