18#include "HepMC3/FourVector.h"
19#include "HepMC3/GenParticle.h"
22#include "marley/Error.hh"
23#include "marley/Generator.hh"
24#include "marley/MassTable.hh"
25#include "marley/ContinuumNuclearReaction.hh"
26#include "marley/marley_utils.hh"
27#include "marley/hepmc3_utils.hh"
34 = SubContinuumMode::ACCUMULATE;
38const std::map< SubContinuumMode, std::string >
40 { SubContinuumMode::IGNORE,
"ignore" },
41 { SubContinuumMode::MIRROR,
"mirror" },
42 { SubContinuumMode::ACCUMULATE,
"accumulate" }
45marley::ContinuumNuclearReaction::ContinuumNuclearReaction(
46 Reaction::ProcessType pt,
int pdg_a,
int pdg_b,
int pdg_c,
int pdg_d,
47 int q_d,
const std::shared_ptr<TabulatedXSec>& txsec,
48 const std::string& source_file )
49 : marley::NuclearReaction( pt, pdg_a, pdg_b, pdg_c, pdg_d, q_d,
62std::shared_ptr< HepMC3::GenEvent > marley::ContinuumNuclearReaction
71 " The requested projectile particle ID, " + std::to_string(
pdg_a )
72 +
", does not match the projectile particle ID, "
73 + std::to_string(
pdg_a_ ) +
", in the reaction dataset." );
78 " not create this event. Projectile kinetic energy "
79 + std::to_string( KEa ) +
" MeV is below the threshold value "
84 std::vector< double > multipole_weights;
85 std::vector< marley::TabulatedXSec::MultipoleLabel > multipoles;
86 std::vector< double > diff_max_values;
87 const auto& table_map =
xsec_->get_table_map();
88 double sum_of_xsecs = 0.;
89 for (
const auto& pair : table_map ) {
90 const auto& ml = pair.first;
92 double total_xsec =
xsec_->integral(
pdg_a_, KEa, ml, diff_max );
94 sum_of_xsecs += total_xsec;
96 multipole_weights.push_back( total_xsec );
97 multipoles.push_back( ml );
98 diff_max_values.push_back( diff_max );
103 if ( multipole_weights.empty() ) {
104 throw marley::Error(
"Could not create this event. The TabulatedXSec"
105 " object associated with this reaction does not own any nuclear response"
112 if ( sum_of_xsecs <= 0. ) {
113 throw marley::Error(
"Could not create this event. All multipole total"
114 " cross sections are nonpositive." );
119 std::discrete_distribution< size_t > multipole_dist(
120 multipole_weights.begin(), multipole_weights.end() );
127 const auto& sampled_ml = multipoles.at( multipole_index );
130 double diff_max = diff_max_values.at( multipole_index );
134 const auto& rt =
xsec_->get_table( sampled_ml );
141 double table_wmin = rt.w_min() -
xsec_->delta_ias();
142 double table_wmax = rt.w_max() -
xsec_->delta_ias();
145 double Ea = KEa +
ma_;
149 double wmin = std::max( 0., table_wmin );
154 double wmax = std::min( Ea -
mc_, table_wmax );
159 double w, ctl, diff, y;
160 int sampling_attempts = 0;
161 bool recomputed_diff_max =
false;
174 if ( sampling_attempts > marley_utils::LARGE_NUMBER_OF_ITERATIONS ) {
176 if ( recomputed_diff_max ) {
181 throw marley::Error(
"Reached maximum iteration count for rejection"
182 " sampling in marley::ContinuumNuclearReaction::create_event()" );
187 xsec_->compute_integral(
pdg_a_, KEa, sampled_ml, diff_max );
188 sampling_attempts = 0;
189 recomputed_diff_max =
true;
194 diff =
xsec_->diff_xsec(
pdg_a_, KEa, w, ctl, sampled_ml );
224 int twoJ = 2 * sampled_ml.J_;
228 double stl = marley_utils::real_sqrt( 1. - std::pow(ctl, 2) );
235 double pc = marley_utils::real_sqrt( Ec*Ec -
mc_*
mc_ );
236 double pc_x = stl * std::cos( phi_c ) * pc;
237 double pc_y = stl * std::sin( phi_c ) * pc;
238 double pc_z = ctl * pc;
241 double pa = marley_utils::real_sqrt( Ea*Ea -
ma_*
ma_ );
249 double Ed = pro_mom4.
e() + tar_mom4.
e() - eje_mom4.
e();
250 double pd_x = pro_mom4.
px() + tar_mom4.
px() - eje_mom4.
px();
251 double pd_y = pro_mom4.
py() + tar_mom4.
py() - eje_mom4.
py();
252 double pd_z = pro_mom4.
pz() + tar_mom4.
pz() - eje_mom4.
pz();
255 md_ = marley_utils::real_sqrt( Ed*Ed - pd_x*pd_x - pd_y*pd_y - pd_z*pd_z );
262 auto ejectile = marley_hepmc3::make_particle( eje_mom4,
pdg_c_,
263 marley_hepmc3::NUHEPMC_FINAL_STATE_STATUS,
mc_ );
265 auto residue = marley_hepmc3::make_particle(
pdg_d_, pd_x, pd_y, pd_z, Ed,
266 marley_hepmc3::NUHEPMC_UNDECAYED_RESIDUE_STATUS,
md_ );
291 const double ctl,
const double KEa )
const
295 if (
sc_mode_ == SubContinuumMode::IGNORE )
return true;
298 double Ea = KEa +
ma_;
299 double pa = marley_utils::real_sqrt( Ea*Ea -
ma_*
ma_ );
303 double pc = marley_utils::real_sqrt( Ec*Ec -
mc_*
mc_ );
306 double kappa2 = pa*pa + pc*pc - 2.*pa*pc*ctl;
310 double Ex = marley_utils::real_sqrt( Ed*Ed - kappa2 ) -
md_gs_;
314 double unbound_threshold = mt.unbound_threshold(
pdg_d_ );
319 if ( Ex >= unbound_threshold )
return true;
324 if (
sc_mode_ == SubContinuumMode::ACCUMULATE ) {
327 Ex = unbound_threshold;
329 MARLEY_LOG( DEBUG,
"physics.reaction" ) <<
"Excitation energy " << Ex
330 <<
" MeV is below the unbound threshold " << unbound_threshold
331 <<
" MeV. Sampling exactly at the unbound threshold.";
333 else if (
sc_mode_ == SubContinuumMode::MIRROR ) {
337 Ex = 2.*unbound_threshold - Ex;
339 MARLEY_LOG( DEBUG,
"physics.reaction" ) <<
"Excitation energy " << Ex
340 <<
" MeV is below the unbound threshold " << unbound_threshold
341 <<
" MeV. Mirroring the energy transfer around the unbound"
345 throw marley::Error(
"Unrecognized sub-continuum mode encountered"
346 " in marley::ContinuumNuclearReaction::reassign_sub_continuum()" );
360 if ( Ec <
mc_ )
return false;
367 const double cos_theta,
const double KEa,
double* jacobian )
const
373 double Ea = KEa +
ma_;
376 double Etot = Ea +
mb_;
379 double pa = marley_utils::real_sqrt( Ea*Ea -
ma_*
ma_ );
380 double help = md*md -
mc_*
mc_ + pa*pa - Etot*Etot;
381 double other_help = 4.*pa*pa*cos_theta*cos_theta;
384 double a = 4.*Etot*Etot - other_help;
385 double b = 4.*Etot*help;
386 double c = help*help + other_help*
mc_*
mc_;
389 double sol_plus, sol_minus;
390 marley_utils::solve_quadratic_equation( a, b, c,
391 sol_plus, sol_minus );
398 double Ec = sol_plus;
399 if ( cos_theta < 0. ) Ec = sol_minus;
402 double pc = marley_utils::real_sqrt( Ec*Ec -
mc_*
mc_ );
403 *jacobian = md / ( Ea +
mb_ - pa*Ec*cos_theta/pc );
410SubContinuumMode marley::ContinuumNuclearReaction
411 ::sub_continuum_mode_from_string(
const std::string& str )
413 for (
const auto& pair : sc_mode_string_map_ ) {
414 if ( str == pair.second )
return pair.first;
416 throw marley::Error(
"The string \"" + str +
"\" was not recognized"
417 " as a valid sub-continuum mode setting" );
421std::string marley::ContinuumNuclearReaction::string_from_sub_continuum_mode(
422 SubContinuumMode mode )
424 auto it = sc_mode_string_map_.find( mode );
425 if ( it != sc_mode_string_map_.end() )
return it->second;
426 else throw marley::Error(
"Unrecognized sub-continuum mode value encountered"
427 " in marley::ContinuumNuclearReaction::string_from_sub_continuum_mode()" );
double px() const
x-component of momentum
double py() const
y-component of momentum
double pz() const
z-component of momentum
double e() const
Energy component of momentum.
virtual void set_description() override
Creates the description string based on the PDG code values for the initial and final particles.
static SubContinuumMode sc_mode_
Indicates the desired method for handling events with excitation energies originally sampled below th...
virtual double total_xs(int pdg_a, double KEa) const override
Compute the reaction's total cross section (MeV -2)
bool reassign_sub_continuum(double &w, const double ctl, const double KEa) const
Helper function for create_event() that potentially reassigns the value of the energy transfer.
std::shared_ptr< TabulatedXSec > xsec_
Helper object that handles cross section calculations.
static const std::map< SubContinuumMode, std::string > sc_mode_string_map_
Helper map used for conversions between a SubContinuumMode value and a std::string.
double get_Ec_from_Ex(const double Ex, const double cos_theta, const double KEa, double *jacobian=nullptr) const
Helper function for reassign_sub_continuum() that solves for the outgoing lepton total energy that co...
Base class for all exceptions thrown by MARLEY functions.
The MARLEY Event generator.
double uniform_random_double(double min, double max, bool inclusive)
Sample a random number uniformly on either [min, max) or [min, max].
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...
static const MassTable & Instance()
Get a const reference to the singleton instance of the MassTable.
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 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)
int pdg_a_
PDG code for the projectile.
double md_
Residue mass (MeV)
double mc_
Ejectile mass (MeV)
std::string description_
String that contains a formula describing the reaction.
int pdg_c_
PDG code for the ejectile.
int pdg_d_
PDG code for the residue.
int pdg_a() const
Get the projectile PDG code.
double ma_
Projectile mass (MeV)
double mb_
Target mass (MeV)