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
DiscreteNuclearReaction.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 <cmath>
19#include <complex>
20#include <fstream>
21#include <iostream>
22#include <sstream>
23
24// HepMC3 includes
25#include "HepMC3/Attribute.h"
26#include "HepMC3/GenEvent.h"
27
28// MARLEY includes
29#include "marley/DiscreteNuclearReaction.hh"
30#include "marley/Error.hh"
31#include "marley/Generator.hh"
32#include "marley/JSON.hh"
33#include "marley/JSONConfig.hh"
34#include "marley/LeptonFactors.hh"
35#include "marley/Level.hh"
36#include "marley/Logger.hh"
37#include "marley/MatrixElement.hh"
38#include "marley/NuclearFormFactor.hh"
39#include "marley/NucleonFormFactors.hh"
40#include "marley/NuclearResponses.hh"
41#include "marley/Integrator.hh"
42#include "marley/marley_utils.hh"
43
45using ProcType = marley::Reaction::ProcessType;
46
47namespace {
48 constexpr int BOGUS_TWO_J_VALUE = -99999;
49}
50
52 ProcType pt, int pdg_a, int pdg_b, int pdg_c, int pdg_d, int q_d,
53 const std::shared_ptr< std::vector<marley::MatrixElement> >& mat_els,
55 const std::string& source_file )
56 : marley::NuclearReaction( pt, pdg_a, pdg_b, pdg_c, pdg_d, q_d,
58 matrix_elements_( mat_els ), coulomb_corrector_( pdg_c, pdg_d, mode ),
59 nucleon_form_factors_( ff_config )
60{
61 // Target nucleus proton number
62 int Zb = marley_utils::get_particle_Z( pdg_b_ );
63 // Target nucleus nucleon number
64 int Ab = marley_utils::get_particle_A( pdg_b_ );
65
66 // Choose the model to use for the nuclear form factor
68
69 // Determine whether we're working within the complete allowed approximation
70 // by checking the form factor JSON configuration. If the user has requested
71 // this, then set the corresponding flag to true.
72 allowed_approx_ = marley::JSONConfig
73 ::check_for_allowed_approximation( ff_config );
74}
75
76// Creates an event object by sampling the appropriate quantities and
77// performing kinematic calculations
78std::shared_ptr< HepMC3::GenEvent > marley::DiscreteNuclearReaction
79 ::create_event( int pdg_a, double KEa, marley::Generator& gen ) const
80{
81 // Check that the projectile supplied to this event is correct. If not, alert
82 // the user that this event does not use the requested projectile.
83 if ( pdg_a != pdg_a_ ) throw marley::Error( "Could not create this event."
84 " The requested projectile particle ID, " + std::to_string( pdg_a )
85 + ", does not match the projectile particle ID, " + std::to_string( pdg_a_ )
86 + ", in the reaction dataset." );
87
88 // Sample a final residue energy level. First, check to make sure the given
89 // projectile energy is above threshold for this reaction.
90 if ( KEa < KEa_threshold_ ) throw std::range_error( "Could not create"
91 " this event. Projectile kinetic energy " + std::to_string( KEa )
92 + " MeV is below the threshold value " + std::to_string( KEa_threshold_ )
93 + " MeV." );
94
97
98 // Create an empty vector of sampling weights (partial total cross
99 // sections to each kinematically accessible final level)
100 std::vector<double> level_weights;
101
102 // Create a discrete distribution object for level sampling.
103 // Its default constructor creates a single weight of 1.
104 // We will always explicitly give it weights to use when sampling
105 // levels, so we won't worry about its default behavior.
106 static std::discrete_distribution< size_t > ldist;
107
108 // Compute the total cross section for a transition to each individual
109 // nuclear level, and save the results in the level_weights vector (which
110 // will be cleared by summed_xs_helper() before being loaded with the cross
111 // sections). The summed_xs_helper() method can also be used for differential
112 // (d\sigma/d\cos\theta_c^{CM}) cross sections, so supply a dummy
113 // cos_theta_c_cm value and request total cross sections by setting the last
114 // argument to false.
115 double dummy = 0.;
116 double sum_of_xsecs = summed_xs_helper( pdg_a, KEa, dummy,
117 &level_weights, false );
118
119 // Note that the elements in matrix_elements_ are given in order of
120 // increasing excitation energy (this is currently enforced by the reaction
121 // data format and is checked during parsing). This ensures that we can
122 // sample a matrix element index from level_weights (which is populated in
123 // the same order by summed_xs_helper()) and have it refer to the correct
124 // object.
125
126 // Complain if none of the levels we have data for are kinematically allowed
127 if ( level_weights.empty() ) {
128 throw marley::Error( "Could not create this event. The DecayScheme object"
129 " associated with this reaction does not contain data for any"
130 " kinematically accessible levels for a projectile kinetic energy of "
131 + std::to_string( KEa ) + " MeV (max E_level = "
132 + std::to_string( max_level_energy(KEa) ) + " MeV)." );
133 }
134
135 // Complain if the total cross section (the sum of all partial level cross
136 // sections) is zero or negative (the latter is just to cover all
137 // possibilities).
138 if ( sum_of_xsecs <= 0. ) {
139 throw marley::Error( "Could not create this event. All kinematically"
140 " accessible levels for a projectile kinetic energy of "
141 + std::to_string( KEa ) + " MeV (max E_level = "
142 + std::to_string( max_level_energy(KEa) )
143 + " MeV) have vanishing matrix elements." );
144 }
145
146 // Create a list of parameters used to supply the weights to our discrete
147 // level sampling distribution
148 std::discrete_distribution<size_t>::param_type params( level_weights.begin(),
149 level_weights.end() );
150
151 // Sample a matrix_element using our discrete distribution and the
152 // current set of weights
153 size_t me_index = gen.sample_from_distribution( ldist, params );
154
155 const auto& sampled_matrix_el = matrix_elements_->at( me_index );
156
157 // Get the energy of the selected level.
158 double E_level = sampled_matrix_el.level_energy();
159
160 // Update the residue mass based on its excitation energy for the current
161 // event
162 md_ = md_gs_ + E_level;
163
164 // Compute Mandelstam s, the ejectile's CM frame total energy, the magnitude
165 // of the ejectile's CM frame 3-momentum, and the residue's CM frame total
166 // energy.
167 double s, Ec_cm, pc_cm, Ed_cm;
168 two_two_scatter( KEa, s, Ec_cm, pc_cm, Ed_cm );
169
170 // Determine the CM frame velocity of the ejectile
171 double beta_c_cm = pc_cm / Ec_cm;
172
173 // Sample a CM frame scattering cosine for the ejectile.
174 double cos_theta_c_cm = sample_cos_theta_c_cm( sampled_matrix_el, KEa,
175 beta_c_cm, gen );
176
177 // Sample a CM frame azimuthal scattering angle (phi) uniformly on [0, 2*pi).
178 // We can do this because the matrix elements are azimuthally invariant
179 double phi_c_cm = gen.uniform_random_double( 0.,
180 marley_utils::two_pi, false );
181
182 // Load the initial residue twoJ and parity values into twoJ and P. These
183 // variables are included in the event record and used by NucleusDecayer
184 // to start the Hauser-Feshbach decay cascade.
185 int twoJ = BOGUS_TWO_J_VALUE;
186 marley::Parity P; // defaults to positive parity
187
188 // Get access to the nuclear structure database owned by the Generator
189 auto& sdb = gen.get_structure_db();
190
191 // Retrieve the ground-state spin-parity of the initial nucleus
192 int twoJ_gs;
193 marley::Parity P_gs;
194
195 sdb.get_gs_spin_parity( pdg_b_, twoJ_gs, P_gs );
196
197 // For transitions to discrete nuclear levels, all we need to do is retrieve
198 // these values directly from the Level object
199 const marley::Level* final_lev = sampled_matrix_el.level();
200 if ( final_lev ) {
201 twoJ = final_lev->twoJ();
202 P = final_lev->parity();
203 }
204 // For transitions to the continuum, we rely on the spin-parity selection
205 // rules to determine suitable values of twoJ and P. In cases where more than
206 // one value is allowed, assume equipartition, and sample a spin-parity based
207 // on the relative nuclear level densities at the excitation energy of
208 // interest.
211 else {
212
213 // For a Fermi transition, the final spin-parity is always the same as the
214 // initial one
215 if ( sampled_matrix_el.type() == ME_Type::FERMI ) {
216 twoJ = twoJ_gs;
217 P = P_gs;
218 }
219 else if ( sampled_matrix_el.type() == ME_Type::GAMOW_TELLER ) {
220 // For a Gamow-Teller transition, the final parity is the same as the
221 // initial parity
222 P = P_gs;
223
224 // For a spin-zero initial state, take a shortcut: the final spin will
225 // always be one.
226 if ( twoJ_gs == 0 ) twoJ = 2;
227 else {
228
229 // For an initial state with a non-zero spin, make a vector storing
230 // all of the spin values allowed by the GT selection rules.
231 // Sample an allowed value assuming equipartition of spin. Use the
232 // relative final nuclear level densities as sampling weights.
233 std::vector<int> allowed_twoJs;
234 std::vector<double> ld_weights;
235
236 auto& ldm = sdb.get_level_density_model( pdg_d_ );
237
238 for ( int myTwoJ = std::abs(twoJ_gs - 2); myTwoJ <= twoJ_gs + 2;
239 myTwoJ += 2 )
240 {
241 allowed_twoJs.push_back( myTwoJ );
242 ld_weights.push_back( ldm.level_density(E_level, myTwoJ, P) );
243 }
244
245 std::discrete_distribution<size_t> my_twoJ_dist( ld_weights.begin(),
246 ld_weights.end() );
247
248 size_t my_index = gen.sample_from_distribution( my_twoJ_dist );
249 twoJ = allowed_twoJs.at( my_index );
250 }
251 }
252 else throw marley::Error( "Unrecognized matrix element type encountered"
253 " in marley::DiscreteNuclearReaction::create_event()" );
254 }
255
256 MARLEY_LOG( DEBUG, "physics.reaction" ) << "Sampled a "
257 << sampled_matrix_el.type_str()
258 << " transition from the " << marley::TargetAtom( pdg_b_ )
259 << " ground state (with spin-parity " << static_cast<double>( twoJ_gs ) / 2.
260 << P_gs << ") to the " << marley::TargetAtom( pdg_d_ )
261 << " level with Ex = " << E_level << " MeV and spin-parity "
262 << static_cast<double>( twoJ ) / 2. << P;
263
264 // Create the preliminary event object (after 2-->2 scattering, but before
265 // de-excitation of the residual nucleus)
266 auto event = this->make_nuclear_event_object( KEa, pc_cm, cos_theta_c_cm,
267 phi_c_cm, Ec_cm, Ed_cm, E_level, twoJ, P );
268
269 // Store the matrix element index for use by weight calculators
270 event->add_attribute( "me_index",
271 std::make_shared< HepMC3::IntAttribute >(
272 static_cast<int>( me_index ) ) );
273
274 // Return the preliminary event object (to be processed later by the
275 // NucleusDecayer class)
276 return event;
277}
278
279// Compute the total reaction cross section (summed over all final nuclear
280// levels) in units of MeV^(-2) using the center of momentum frame.
282 const
283{
284 double dummy_cos_theta = 0.;
285 return summed_xs_helper( pdg_a, KEa, dummy_cos_theta, nullptr, false );
286}
287
288// Compute the total reaction cross section (summed over all final nuclear
289// levels) in units of MeV^(-2) using the center of momentum frame. Include
290// only transitions matching the input matrix element type.
292 ME_Type mat_el_type ) const
293{
294 double dummy_cos_theta = 0.;
295 std::vector< double > level_xsecs;
296 // Accumulate partial cross section values for all of the accessible
297 // nuclear transitions
298 this->summed_xs_helper( pdg_a, KEa, dummy_cos_theta, &level_xsecs, false );
299
300 // Sum up the contributions from transitions that match the requested
301 // matrix element type, then return the result
302 double xsec = 0.;
303 for ( size_t j = 0u; j < level_xsecs.size(); ++j ) {
304 const auto& ml = matrix_elements_->at( j );
305 if ( ml.type() == mat_el_type ) {
306 xsec += level_xsecs.at( j );
307 }
308 }
309 return xsec;
310}
311
312// Compute the differential cross section d\sigma / d\cos\theta_c^{CM}
313// summed over all final nuclear levels. This is done in units of MeV^(-2)
314// using the center of momentum frame.
316 double cos_theta_c_cm ) const
317{
318 return summed_xs_helper( pdg_a, KEa, cos_theta_c_cm, nullptr, true );
319}
320
321// Compute the differential cross section d\sigma / d\cos\theta_c^{CM} for a
322// transition to a particular final nuclear level. This is done in units of
323// MeV^(-2) using the center of momentum frame.
325 const marley::MatrixElement& mat_el, double KEa, double cos_theta_c_cm,
326 double& beta_c_cm, bool check_max_E_level ) const
327{
328 // Check that the scattering cosine is within the physically meaningful range
329 if ( std::abs(cos_theta_c_cm) > 1. ) return 0.;
330
331 // Don't bother to compute anything if the matrix element vanishes
332 if ( mat_el.strength() == 0. ) return 0.;
333
334 // Also don't proceed further if the reaction is below threshold
335 // (equivalently, if the requested level excitation energy E_level exceeds
336 // that maximum kinematically-allowed value). To avoid redundant checks of
337 // the threshold, skip this check if check_max_E_level is set to false.
338 if ( check_max_E_level ) {
339 double max_E_level = max_level_energy( KEa );
340 if ( mat_el.level_energy() > max_E_level ) return 0.;
341 }
342
343 // The final nuclear mass (before nuclear de-excitations) is the sum of the
344 // ground state residue mass plus the excitation energy of the accessed level
345 // This includes a correction to the ground-state atomic mass to account for
346 // production of an ion by charged-current interactions (see the constructor
347 // of the NuclearReaction class)
348 double md2 = std::pow( md_gs_ + mat_el.level_energy(), 2 );
349
350 // Compute Mandelstam s (the square of the total CM frame energy)
351 double s = std::pow( ma_ + mb_, 2 ) + 2.*mb_*KEa;
352 double sqrt_s = std::sqrt( s );
353
354 // Compute some CM frame total energies and 3-momentum magnitudes
355 double Eb_cm = ( s + mb_*mb_ - ma_*ma_ ) / ( 2. * sqrt_s );
356
357 double Ea_cm = sqrt_s - Eb_cm;
358 double pa_cm = marley_utils::real_sqrt( std::pow(Ea_cm, 2) - ma_*ma_ );
359
360 double Ec_cm = ( s + mc_*mc_ - md2 ) / ( 2. * sqrt_s );
361 double pc_cm = marley_utils::real_sqrt( std::pow(Ec_cm, 2) - mc_*mc_ );
362
363 // Compute the CM frame value of the energy transfer (omega)
364 double omega_cm = Ea_cm - Ec_cm;
365
366 // Compute the CM frame value of the 3-momentum transfer magnitude (kappa)
367 double kappa_cm = marley_utils::real_sqrt( std::pow( pa_cm, 2 )
368 + std::pow( pc_cm, 2 ) - 2. * pa_cm * pc_cm * cos_theta_c_cm );
369
370 // Negative square of the 4-momentum transfer
371 double Q2 = kappa_cm*kappa_cm - omega_cm*omega_cm;
372
373 // Compute the (dimensionless) speed of the ejectile in the CM frame
374 beta_c_cm = pc_cm / Ec_cm;
375
376 // CM frame total energy of the nuclear residue
377 double Ed_cm = sqrt_s - Ec_cm;
378
379 // Common factors for both CC and NC differential cross sections to a discrete
380 // nuclear level in the CM frame
381 double diff_xsec_prefactor = ( marley_utils::GF2 / ( 2 * marley_utils::pi ) )
382 * ( Eb_cm * Ed_cm / s ) * Ec_cm * pc_cm;
383
384 // Apply extra factors based on the current process type
387 {
388 // Dot product of the four-momenta of particles c and d
389 double pc_dot_pd = Ed_cm*Ec_cm + std::pow( pc_cm, 2 );
390
391 // Relative speed of particles c and d, computed with a manifestly
392 // Lorentz-invariant expression
393 double beta_rel_cd = marley_utils::real_sqrt(
394 std::pow(pc_dot_pd, 2) - mc_*mc_*md2 ) / pc_dot_pd;
395
396 // Calculate a Coulomb correction factor using either a Fermi function
397 // or the effective momentum approximation
398 double factor_C = coulomb_corrector_.coulomb_correction_factor(
399 beta_rel_cd );
400 diff_xsec_prefactor *= marley_utils::Vud2 * factor_C;
401 }
403 {
404 // For NC, extra factors are only needed for Fermi transitions (which
405 // correspond to CEvNS since they can only access the nuclear ground state)
406 if ( mat_el.type() == ME_Type::FERMI ) {
407 double Q_w = weak_nuclear_charge();
408 diff_xsec_prefactor *= 0.25*std::pow( Q_w, 2 );
409 }
410 }
411 else throw marley::Error( "Unrecognized or invalid process type encountered"
412 " in marley::DiscreteNuclearReaction::diff_xs()" );
413
414 // We're done with the overall factors. Now compute the lepton part of the
415 // tensor contraction
416 // @todo Reduce code duplication with a similar implementation of these (in
417 // the lab frame) within the TabulatedXSec class. Note that there are
418 // different conventions used there.
419 int helicity = marley_utils::get_particle_helicity( pdg_a_ );
420 double sin2_theta_c_cm = 1. - cos_theta_c_cm*cos_theta_c_cm;
421 double vcc = 1. + beta_c_cm * cos_theta_c_cm;
422 double vll = vcc - 2.*Ea_cm*Ec_cm*sin2_theta_c_cm
423 * beta_c_cm*beta_c_cm/kappa_cm/kappa_cm;
424 double vcl = -1.* ( omega_cm*vcc/kappa_cm + mc_*mc_/Ec_cm/kappa_cm );
425 double vT = 1. - beta_c_cm*cos_theta_c_cm + Ea_cm*Ec_cm
426 * beta_c_cm*beta_c_cm*sin2_theta_c_cm/kappa_cm/kappa_cm;
427 double vTprime = helicity * ( (Ea_cm + Ec_cm)
428 * (1. - beta_c_cm*cos_theta_c_cm)/kappa_cm - mc_*mc_/kappa_cm/Ec_cm );
429 marley::LeptonFactors lf( vcc, vll, vcl, vT, vTprime );
430
431 // Now compute the nuclear responses. Copy the energy transfer, 3-momentum
432 // transfer magnitude, and Q^2 to effective values. These will be set to zero
433 // if we're working in the allowed approximation.
434 double omega_cm_eff = allowed_approx_ ? 0. : omega_cm;
435 double kappa_cm_eff = allowed_approx_ ? 0. : kappa_cm;
436 double Q2_eff = allowed_approx_ ? 0. : Q2;
437
438 // Scale the transition strength by the squared nuclear form factor
439 double strength_eff = mat_el.strength();
440 strength_eff *= std::pow( nuclear_ff_->F( kappa_cm_eff ), 2 );
441
442 // Scale the strength by the relevant nucleon form factor and divide by the
443 // relevant coupling constant. Use the scaled value to compute the nuclear
444 // responses relevant to the chosen transition type
445 double rCC, rCL, rLL, rTvv, rTaa, rTprime;
446 const double kM = kappa_cm_eff / marley_utils::m_nucleon;
447 const double k2M = kappa_cm_eff * kappa_cm_eff / marley_utils::m_nucleon;
448
449 if ( mat_el.type() == ME_Type::FERMI ) {
450 double F1 = nucleon_form_factors_.F1( Q2_eff );
451 strength_eff *= F1*F1 / marley_utils::g_V2;
452
453 rCC = strength_eff;
454 rCL = strength_eff * kM;
455 rLL = strength_eff * kM * kM / 4.;
456 rTvv = 0.;
457 rTaa = 0.;
458 rTprime = 0.;
459 }
460 else if ( mat_el.type() == ME_Type::GAMOW_TELLER ) {
461 double FA = nucleon_form_factors_.FA( Q2_eff );
462 strength_eff *= FA*FA / marley_utils::g_A2;
463
464 double FP = nucleon_form_factors_.FP( Q2_eff );
465 double F1 = nucleon_form_factors_.F1( Q2_eff );
466 double F2 = nucleon_form_factors_.F2( Q2_eff );
467
468 double FPA = FP / FA;
469 double F12A = ( F1 + 2. * marley_utils::m_nucleon * F2 ) / FA;
470
471 rCC = strength_eff * kM * kM / 12. * ( 1. - 2.*omega_cm_eff*FPA
472 + omega_cm_eff*omega_cm_eff*FPA*FPA );
473 rCL = strength_eff * kM / 3. * ( 1. - (omega_cm_eff + 0.5*k2M)*FPA
474 + 0.5*omega_cm_eff*k2M*FPA*FPA );
475 rLL = strength_eff * ( 1./3. - k2M/3.*FPA + k2M*k2M/12.*FPA*FPA );
476 rTvv = 0.;
477 rTaa = strength_eff * ( 2./3. + kM*kM/6.*F12A*F12A );
478 rTprime = -1. * strength_eff * 2./3. * kM * F12A;
479 }
480 else throw marley::Error( "Unrecognized matrix element type encountered in"
481 " marley::DiscreteNuclearReaction::diff_xs()" );
482
483 marley::NuclearResponses nr( rCC, rLL, rCL, rTvv, rTaa, rTprime );
484
485 double diff_xsec = diff_xsec_prefactor * ( lf * nr );
486 return diff_xsec;
487}
488
489// Compute the total reaction cross section (in MeV^(-2)) for a transition to a
490// particular nuclear level using the center of momentum frame
492 const marley::MatrixElement& mat_el, double KEa, double& beta_c_cm,
493 bool check_max_E_level ) const
494{
495 if ( allowed_approx_ ) return 2. * this->diff_xs( mat_el, KEa, 0., beta_c_cm,
496 check_max_E_level );
497
498 // Integrator object to integrate over the scattering angle
499 static marley::Integrator integrator;
500
501 // Don't bother to compute anything if the matrix element vanishes
502 if ( mat_el.strength() == 0. ) return 0.;
503
504 // Integrate the differential cross section over cos_theta_c_cm
505 double total_xsec = integrator.num_integrate(
506 [ &mat_el, KEa, &beta_c_cm, check_max_E_level, this ](
507 double cos_theta_cm ) -> double
508 { return this->diff_xs( mat_el, KEa, cos_theta_cm, beta_c_cm,
509 check_max_E_level ); }, -1., 1.
510 );
511
512 MARLEY_LOG( DEBUG, "physics.reaction.xsec" ) << "total xsec " << description_
513 << " to level with energy " << mat_el.level_energy() << " MeV is "
514 << total_xsec << " MeV^(-2).";
515
516 return total_xsec;
517}
518
519// Helper function for total_xs and diff_xs()
521 double KEa, double cos_theta_c_cm, std::vector<double>* level_xsecs,
522 bool differential ) const
523{
524 // Check that the projectile supplied to this event is correct. If not,
525 // return a total cross section of zero since this reaction is not available
526 // for the given projectile.
528 if ( pdg_a != pdg_a_ ) return 0.;
529
530 // If we're evaluating a differential cross section, check that the
531 // scattering cosine is within the physically meaningful range. If it's
532 // not, then just return 0.
533 if ( differential && std::abs(cos_theta_c_cm) > 1. ) return 0.;
534
535 // If the projectile kinetic energy is zero (or negative), then
536 // just return zero.
537 if ( KEa <= 0. ) return 0.;
538
539 // If we've been passed a vector to load with the partial cross sections
540 // to each nuclear level, then clear it before storing them
541 if ( level_xsecs ) level_xsecs->clear();
542
543 double max_E_level = max_level_energy( KEa );
544 double xsec = 0.;
545 for ( const auto& mat_el : *matrix_elements_ ) {
546
547 // Get the excitation energy for the current level
548 double level_energy = mat_el.level_energy();
549
550 // Exit the loop early if you reach a level with an energy that's too high
551 if ( level_energy > max_E_level ) break;
552
553 // Check whether the matrix element (B(F) + B(GT)) is nonvanishing for the
554 // current level. If it is, just set the weight equal to zero rather than
555 // computing the total xs.
556 double partial_xsec = 0.;
557 if ( mat_el.strength() != 0. ) {
558
559 // Set the check_max_E_level flag to false when calculating the total
560 // cross section for this level (we've already verified that the
561 // current level is kinematically accessible in the check against
562 // max_E_level above)
563 double beta_c_cm = 0.;
564
565 // Compute either the total or differential (d\sigma / d\cos\theta_{CM})
566 // cross section as requested
567 if ( differential ) {
568 partial_xsec = diff_xs( mat_el, KEa, cos_theta_c_cm, beta_c_cm, false );
569 } else {
570 partial_xsec = total_xs( mat_el, KEa, beta_c_cm, false );
571 }
572
573 if ( std::isnan(partial_xsec) ) {
574 MARLEY_LOG( WARN, "physics.reaction.xsec" )
575 << "Partial cross section for reaction "
576 << description_ << " gave NaN result.";
577 MARLEY_LOG( DEBUG, "physics.reaction.xsec" )
578 << "Parameters were level energy = "
579 << mat_el.level_energy() << " MeV, projectile kinetic energy = "
580 << KEa << " MeV, and reduced matrix element = " << mat_el.strength()
581 << ". Differential was set to " << differential << ".";
582 MARLEY_LOG( DEBUG, "physics.reaction.xsec" )
583 << "The partial cross section to this level"
584 << " will be set to zero.";
585 partial_xsec = 0.;
586 }
587
588 xsec += partial_xsec;
589
590 } // mat_el.strength() != 0.
591
592 // Store the partial cross section to the current individual nuclear
593 // level if needed (i.e., if level_xsecs is not nullptr). This is
594 // done even when the current matrix element is zero so that the
595 // partial cross sections for each transition match the ordering
596 // in the vector of matrix elements.
597 if ( level_xsecs ) level_xsecs->push_back( partial_xsec );
598
599 } // loop over matrix elements
600
601 return xsec;
602}
603
604// Sample an ejectile scattering cosine in the CM frame.
606 const marley::MatrixElement& mat_el, double KEa, double beta_c_cm,
607 marley::Generator& gen ) const
608{
609 // For now the max is unknown, so the rejection sampling algorithm will
610 // calculate it "on the fly"
611 double max = marley_utils::UNKNOWN_MAX;
612
613 // For the allowed approximation, we know where it will be a priori
614 if ( allowed_approx_ ) {
615 if ( mat_el.type() == ME_Type::FERMI ) {
616 max = this->diff_xs( mat_el, KEa, 1., beta_c_cm, false );
617 }
618 else if ( mat_el.type() == ME_Type::GAMOW_TELLER ) {
619 max = this->diff_xs( mat_el, KEa, -1., beta_c_cm, false );
620 }
621 else throw marley::Error( "Unrecognized matrix element type encountered"
622 " in marley::DiscreteNuclearReaction::sample_cos_theta_c_cm()" );
623 }
624
625 return gen.rejection_sample(
626 [ &mat_el, KEa, &beta_c_cm, this ]( double cos_theta_cm ) -> double
627 { return this->diff_xs( mat_el, KEa, cos_theta_cm, beta_c_cm, false ); },
628 -1., 1., max );
629}
630
631// Adds an indication of whether the reaction populates excited levels of the
632// daughter nucleus or only accesses the ground state (e.g., CEvNS)
634 // Initialize the description_ member variable with the basic information
635 // first
637 // Now decide whether this reaction can access excited levels in the daughter
638 // nucleus
639 bool has_excited_state = false;
640 for ( const auto& me : *matrix_elements_ ) {
641 if ( me.level_energy() > 0. ) {
642 has_excited_state = true;
643 break;
644 }
645 }
646 if ( has_excited_state ) description_ += '*';
647 else description_ += " (g.s.)";
648}
CoulombMode
Enumerated type used to set the method for handling Coulomb corrections for CC nuclear reactions.
double summed_xs_helper(int pdg_a, double KEa, double cos_theta_c_cm, std::vector< double > *level_xsecs, bool differential) const
NucleonFormFactors nucleon_form_factors_
Object that handles calculations of nucleon form factors.
bool allowed_approx_
Flag that indicates whether to include aditional terms beyond the q->0 limit.
std::shared_ptr< NuclearFormFactor > nuclear_ff_
Object that handles calculations of the nuclear form factor.
virtual double total_xs(int pdg_a, double KEa) const override
Total reaction cross section (MeV -2) including all kinematically-allowed final nuclear levels.
double diff_xs(const marley::MatrixElement &mat_el, double KEa, double cos_theta_c_cm, double &beta_c_cm, bool check_max_E_level) const
Differential cross section (MeV -2) evaluated in the center-of-momentum frame for a transition to a ...
CoulombCorrector coulomb_corrector_
Object that handles calculations of Coulomb correction factors.
DiscreteNuclearReaction(ProcessType pt, int pdg_a, int pdg_b, int pdg_c, int pdg_d, int q_d, const std::shared_ptr< std::vector< marley::MatrixElement > > &mat_els, CoulombCorrector::CoulombMode mode, const JSON &ff_config, const std::string &source_file)
std::shared_ptr< std::vector< marley::MatrixElement > > matrix_elements_
Matrix elements representing all of the possible nuclear transitions that may be caused by this react...
virtual void set_description() override
Creates the description string based on the PDG code values for the initial and final particles.
double sample_cos_theta_c_cm(const marley::MatrixElement &matrix_el, double KEa, double beta_c_cm, marley::Generator &gen) const
Samples a polar angle cosine for the ejectile using the relevant portion of the reaction nuclear matr...
Base class for all exceptions thrown by MARLEY functions.
Definition Error.hh:26
The MARLEY Event generator.
Definition Generator.hh:54
double uniform_random_double(double min, double max, bool inclusive)
Sample a random number uniformly on either [min, max) or [min, max].
Definition Generator.cc:235
marley::StructureDatabase & get_structure_db()
Get a reference to the StructureDatabase owned by this Generator.
Definition Generator.cc:510
double rejection_sample(const std::function< double(double)> &f, double xmin, double xmax, double &fmax, double safety_factor=1.01, double max_search_tolerance=DEFAULT_REJECTION_SAMPLING_TOLERANCE_)
Sample from a given 1D probability density function f(x) on the interval [xmin, xmax] using a simple ...
Definition Generator.cc:281
auto sample_from_distribution(RandomNumberDistribution &rnd) -> decltype(std::declval< RandomNumberDistribution & >().operator()(std::declval< std::mt19937_64 & >()))
Sample from an arbitrary probability distribution (defined here as any object that implements an oper...
Definition Generator.hh:193
Numerical integrator that uses Clenshaw-Curtis quadrature
Definition Integrator.hh:28
double num_integrate(const std::function< double(double)> &f, double a, double b) const
Numerically integrate an arbitrary 1D function.
Definition Integrator.cc:47
A discrete nuclear energy level.
Definition Level.hh:29
marley::Parity parity() const
Get the level parity.
Definition Level.hh:141
int twoJ() const
Get two times the level spin.
Definition Level.hh:138
A reduced nuclear matrix element that represents a transition caused by a neutrino-nucleus reaction.
TransitionType type() const
Get the kind of nuclear transition (e.g., Fermi, Gamow-Teller) represented by the matrix element.
double strength() const
Get the numerical value (dimensionless) of the matrix element.
double level_energy() const
Get the excitation energy (MeV) of the final-state nuclear level accessed by the matrix element.
TransitionType
Enumerated type that represents the possible kinds of nuclear transitions recognized by MARLEY.
static std::shared_ptr< marley::NuclearFormFactor > create(int Z, int A, const JSON &ff_config)
double weak_nuclear_charge() const
virtual std::shared_ptr< HepMC3::GenEvent > make_nuclear_event_object(double KEa, double pc_cm, double cos_theta_c_cm, double phi_c_cm, double Ec_cm, double Ed_cm, double E_level, int twoJ, const marley::Parity &P) const
Helper function that makes a complete event object for a nuclear reaction.
virtual void set_description()
Creates the description string based on the PDG code values for the initial and final particles.
double max_level_energy(double KEa) const
Get the maximum possible excitation energy (MeV) of the final-state residue that is kinematically all...
NuclearReaction(ProcessType pt, int pdg_a, int pdg_b, int pdg_c, int pdg_d, int q_d, const std::string &source_file)
double KEa_threshold_
Lab-frame kinetic energy of the projectile at threshold for this reaction (i.e., the residue is produ...
double md_gs_
Ground state mass (MeV) of the residue.
Type-safe representation of a parity value (either +1 or -1)
Definition Parity.hh:25
int pdg_a_
PDG code for the projectile.
Definition Reaction.hh:142
double md_
Residue mass (MeV)
Definition Reaction.hh:154
ProcessType process_type_
Type of scattering process (CC, NC) represented by this reaction.
Definition Reaction.hh:161
double mc_
Ejectile mass (MeV)
Definition Reaction.hh:149
void two_two_scatter(double KEa, double &s, double &Ec_cm, double &pc_cm, double &Ed_cm) const
Helper function that handles CM frame kinematics for the reaction.
Definition Reaction.cc:226
ProcessType
Enumerated type describing the kind of scattering process represented by a Reaction.
Definition Reaction.hh:58
@ 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
const std::string & source_file() const
Get the resolved path of the reaction data file used to construct this Reaction.
Definition Reaction.hh:104
std::string description_
String that contains a formula describing the reaction.
Definition Reaction.hh:157
int pdg_d_
PDG code for the residue.
Definition Reaction.hh:145
int pdg_a() const
Get the projectile PDG code.
Definition Reaction.hh:109
int pdg_b_
PDG code for the target.
Definition Reaction.hh:143
double ma_
Projectile mass (MeV)
Definition Reaction.hh:147
int pdg_b() const
Get the target PDG code.
Definition Reaction.hh:112
double mb_
Target mass (MeV)
Definition Reaction.hh:148
An atomic target for a lepton scattering reaction.
Definition TargetAtom.hh:26