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
OMPWeightCalculator.cc
1
4//
5// This file is part of MARLEY (Model of Argon Reaction Low Energy Yields)
6//
7// MARLEY is free software: you can redistribute it and/or modify it under the
8// terms of version 3 of the GNU General Public License as published by the
9// Free Software Foundation.
10//
11// For the full text of the license please see COPYING or
12// visit http://opensource.org/licenses/GPL-3.0
13//
14// Please respect the MCnet academic usage guidelines. See GUIDELINES
15// or visit https://www.montecarlonet.org/GUIDELINES for details.
16
17// Standard library includes
18#include <algorithm>
19
20// HepMC3 includes
21#include "HepMC3/FourVector.h"
22#include "HepMC3/GenEvent.h"
23#include "HepMC3/GenParticle.h"
24#include "HepMC3/GenVertex.h"
25
26// MARLEY includes
27#include "marley/hepmc3_utils.hh"
28#include "marley/Error.hh"
29#include "marley/FileManager.hh"
30#include "marley/HauserFeshbachDecay.hh"
31#include "marley/JSON.hh"
32#include "marley/Logger.hh"
33#include "marley/OMPWeightCalculator.hh"
34#include "marley/StructureDatabase.hh"
35
36namespace {
37 constexpr double PRETTY_SMALL = 1e-6;
38}
39
40marley::OMPWeightCalculator::OMPWeightCalculator( const marley::JSON& config )
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}
64
66 marley::Generator& /*gen*/ ) const
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}
Attribute that holds a real number as a double.
Definition Attribute.h:245
double value() const
get the value associated to this Attribute.
Definition Attribute.h:271
Stores event-related information.
Definition GenEvent.h:47
Attribute that holds an Integer implemented as an int.
Definition Attribute.h:157
int value() const
get the value associated to this Attribute.
Definition Attribute.h:180
Abstract base class for ExitChannel objects that lead to the unbound continuum in the final state.
Abstract base class for ExitChannel objects that lead to discrete nuclear levels in the final state.
const marley::Level & get_final_level() const
Get a const reference to the final-state nuclear level.
Base class for all exceptions thrown by MARLEY functions.
Definition Error.hh:26
virtual bool is_continuum() const =0
Returns true if this channel accesses the particle-unbound continuum of nuclear levels or false other...
The MARLEY Event generator.
Definition Generator.hh:54
Monte Carlo implementation of the Hauser-Feshbach statistical model for decays of highly-excited nucl...
std::vector< std::unique_ptr< marley::ExitChannel > > & exit_channels()
Get a non-const reference to the owned vector of ExitChannel pointers.
double energy() const
Get the excitation energy of this level (MeV)
Definition Level.hh:135
std::shared_ptr< marley::StructureDatabase > sdb_
Owned StructureDatabase with a custom set of optical model parameters.
virtual double weight(HepMC3::GenEvent &event, marley::Generator &gen) const override
Compute the weight for the given event.
Type-safe representation of a parity value (either +1 or -1)
Definition Parity.hh:25