18#include "marley/hepmc3_utils.hh"
19#include "marley/marley_utils.hh"
20#include "marley/ExitChannel.hh"
21#include "marley/GammaStrengthFunctionModel.hh"
22#include "marley/LevelDensityModel.hh"
23#include "marley/Logger.hh"
24#include "marley/OpticalModel.hh"
25#include "marley/marley_kinematics.hh"
31 void issue_Exf_warning(
double Exf,
double Exf_max ) {
32 MARLEY_LOG( WARN,
"physics.deexcitation" )
33 <<
"Final nuclear excitation energy Exf = "
34 << Exf <<
" MeV exceeds the maximum accessible value of " << Exf_max
35 <<
" MeV. The decay width for this exit channel will be set to zero.";
40 void issue_Exf_continuum_warning(
double Exf,
double E_c_min,
43 MARLEY_LOG( WARN,
"physics.deexcitation" )
44 <<
"Final nuclear excitation energy Exf = "
45 << Exf <<
" MeV lies outside the accessible continuum [ " << E_c_min
46 <<
" MeV, " << E_c_max <<
" MeV ]. The differential decay width"
47 <<
" will be set to zero.";
52 void throw_continuum_bounds_error(
double E_c_min,
double E_c_max ) {
53 throw marley::Error(
"Invalid continuum excitation energy bounds "
54 " E_c_min = " + std::to_string(E_c_min) +
" MeV and E_c_max = "
55 + std::to_string(E_c_max) +
" MeV encountered" );
59 constexpr double TINY_OFFSET = 1e-6;
65 int Zi = marley_utils::get_particle_Z(
pdgi_ );
66 int Ai = marley_utils::get_particle_A(
pdgi_ );
74 int pdgf = marley_utils::get_nucleus_pid( Zf, Af );
81 double Exf_max =
Exi_ - Sa;
98 double Exf_max = this->
max_Exf();
106 if ( Exf >= Exf_max ) {
107 issue_Exf_warning( Exf, Exf_max );
112 double total_KE_CM_frame = Exf_max - Exf;
117 for (
int two_j = std::abs(
twoJi_ - twoJf); two_j <=
twoJi_ + twoJf;
120 int j_plus_s = (two_j + two_s) / 2;
124 int l = std::abs(two_j - two_s) / 2;
125 bool l_is_odd = l % 2;
127 for ( ; l <= j_plus_s; ++l, !P_final_state )
131 if (
Pi_ == P_final_state ) {
149double marley::GammaExitChannel::gamma_energy(
double Exf )
const {
153 double E_gamma = Exi_ - Exf;
158TrType marley::GammaExitChannel::get_transition_type(
int mpol,
166 bool mpol_is_odd = mpol % 2;
168 TrType type = ( Pi_ == P_final_state ) ? TrType::electric : TrType::magnetic;
177 =
sdb_->get_gamma_strength_function_model(
pdgi_ );
188 issue_Exf_warning( Exf,
Exi_ );
193 double E_gamma = this->gamma_energy( Exf );
197 int start_mpol = std::max( 1, std::abs(
twoJi_ - twoJf) / 2 );
198 int end_mpol = (
twoJi_ + twoJf ) / 2;
201 for (
int mpol = start_mpol; mpol <= end_mpol; ++mpol ) {
206 TrType type = this->get_transition_type( mpol, Pf );
221double marley::FragmentContinuumExitChannel::differential_width(
double Exf,
222 bool store_jpi_widths )
const
224 if ( store_jpi_widths ) this->clear_jpi_widths();
226 int remnant_pdg = this->final_nucleus_pdg();
231 double Exf_max = this->max_Exf();
234 double diff_width = 0.;
236 if ( Exf_max < E_c_min_ ) throw_continuum_bounds_error( E_c_min_, Exf_max );
239 if ( Exf < (E_c_min_ - TINY_OFFSET) || Exf > (Exf_max + TINY_OFFSET) ) {
241 issue_Exf_continuum_warning( Exf, E_c_min_, Exf_max );
245 double total_KE_CM_frame = Exf_max - Exf;
261 if (Pi_ == Pa) Pf = 1;
264 for (
int l = 0; l <= l_max_; ++l, !Pf) {
266 for (
int two_j = std::abs(two_l - two_s);
267 two_j <= two_l + two_s; two_j += 2)
270 for (
int twoJf = std::abs(twoJi_ - two_j);
271 twoJf <= twoJi_ + two_j; twoJf += 2)
275 fragment_pdg_, two_j, l, two_s );
279 double term = one_over_two_pi_rho_i_ * Tlj * rho_f;
283 if ( store_jpi_widths ) {
285 auto f_spw = std::make_unique< FragmentSpinParityWidth >(
286 twoJf, Pf, term, two_j, l );
288 jpi_widths_table_.push_back( std::move(f_spw) );
302 double Ec_max = this->E_c_max();
305 throw_continuum_bounds_error(
E_c_min_, Ec_max );
311 std::function<double(
double)> dw = [
this](
double Exf) ->
double {
312 return this->differential_width( Exf );
327double marley::GammaContinuumExitChannel::differential_width(
double Exf,
328 bool store_jpi_widths )
const
330 if ( store_jpi_widths ) this->clear_jpi_widths();
332 auto& ldm = sdb_->get_level_density_model( pdgi_ );
333 auto& gsfm = sdb_->get_gamma_strength_function_model( pdgi_ );
336 double diff_width = 0.;
339 if ( Exi_ < E_c_min_ ) throw_continuum_bounds_error( E_c_min_, Exi_ );
342 if ( Exf < (E_c_min_ - TINY_OFFSET) || Exf > (Exi_ + TINY_OFFSET) ) {
344 issue_Exf_continuum_warning( Exf, E_c_min_, Exi_ );
349 double E_gamma = this->gamma_energy( Exf );
353 constexpr std::array<marley::Parity, 2>
358 for (
int mpol = 1; mpol <= l_max_; ++mpol ) {
360 int two_mpol = 2 * mpol;
362 for (
int twoJf = std::abs(twoJi_ - two_mpol); twoJf <= twoJi_ + two_mpol;
365 for (
const auto& Pf : parities ) {
370 TrType type = this->get_transition_type( mpol, Pf );
372 double Txl = gsfm.transmission_coefficient( type, mpol, E_gamma );
375 double term = one_over_two_pi_rho_i_ * Txl * rho_f;
377 if ( store_jpi_widths ) {
379 auto g_spw = std::make_unique< GammaSpinParityWidth >(
380 twoJf, Pf, term, mpol );
382 jpi_widths_table_.push_back( std::move(g_spw) );
394 compound_nucleus, std::shared_ptr< HepMC3::GenParticle >& emitted_particle,
395 std::shared_ptr< HepMC3::GenParticle >& residual_nucleus,
402 residual_nucleus, Exf, qIon, gen );
406 const std::shared_ptr< HepMC3::GenParticle >& compound_nucleus,
407 std::shared_ptr< HepMC3::GenParticle >& emitted_particle,
408 std::shared_ptr< HepMC3::GenParticle >& residual_nucleus,
413 double ep_mass = mt.get_particle_mass( ep_pdg );
415 emitted_particle = marley_hepmc3::make_particle( ep_pdg,
416 marley_hepmc3::NUHEPMC_FINAL_STATE_STATUS, ep_mass );
419 int ep_Z = marley_utils::get_particle_Z( ep_pdg );
424 double me = mt.get_particle_mass( marley_utils::ELECTRON );
430 double Mfgs_ion = mt.get_atomic_mass( remnant_pdg ) - qf*me;
432 residual_nucleus = marley_hepmc3::make_particle( remnant_pdg,
433 marley_hepmc3::NUHEPMC_INTERMEDIATE_RESIDUE_STATUS, Mfgs_ion + Exf );
439 double cos_theta_emitted_particle
441 double phi_emitted_particle
446 marley_kinematics::two_body_decay( compound_nucleus, emitted_particle,
447 residual_nucleus, cos_theta_emitted_particle, phi_emitted_particle );
454 double Emax = this->E_c_max();
462 ->
double {
return this->differential_width(Exf); }, E_c_min_, Emax,
463 marley::DEFAULT_N_CHEBYSHEV );
466 Exf_cdf_ = std::make_unique<marley::ChebyshevInterpolatingFunction>(
478 compound_nucleus, std::shared_ptr< HepMC3::GenParticle >& emitted_particle,
479 std::shared_ptr< HepMC3::GenParticle >& residual_nucleus,
482 Exf = this->sample_Exf( gen );
492 residual_nucleus, Exf, qIon, gen );
495void marley::ContinuumExitChannel::sample_spin_parity(
double Exf,
int& twoJ,
499 this->clear_jpi_widths();
503 double diff_width = this->differential_width( Exf,
true );
507 "continue Hauser-Feshbach decay. All partial differential decay widths "
512 std::vector< std::unique_ptr<SpinParityWidth> >::const_iterator,
513 const double>( jpi_widths_table_.cbegin(),
514 &SpinParityWidth::diff_width );
517 std::vector< std::unique_ptr<SpinParityWidth> >::const_iterator,
518 const double>( jpi_widths_table_.cend(),
519 &SpinParityWidth::diff_width );
521 std::discrete_distribution<size_t> jpi_dist( begin, end );
525 last_sampled_spw_ = jpi_widths_table_.at( jpi_index ).get();
526 twoJ = last_sampled_spw_->twoJf;
527 Pi = last_sampled_spw_->Pf;
Approximates a 1D function using Chebyshev points.
std::vector< std::unique_ptr< SpinParityWidth > > jpi_widths_table_
Table of possible final-state spin-parities together with their partial differential decay widths.
virtual void compute_total_width() final override
bool skip_jpi_sampling_
Flag that allows skipping the sampling of a final nuclear spin-parity (useful only for testing purpos...
virtual void do_decay(double &Ex, int &two_J, marley::Parity &Pi, const std::shared_ptr< HepMC3::GenParticle > &compound_nucleus, std::shared_ptr< HepMC3::GenParticle > &emitted_particle, std::shared_ptr< HepMC3::GenParticle > &residual_nucleus, int &qIon, marley::Generator &gen) const final override
Simulates a nuclear decay into this channel.
SpinParityWidth * last_sampled_spw_
Points to the last SpinParityWidth object sampled in a previous call to sample_spin_parity()
void clear_jpi_widths() const
Helper function that resets the table of SpinParityWidth objects.
double E_c_min_
Minimum accessible nuclear excitation energy (MeV) in the continuum.
const marley::Level & final_level_
Reference to the final-state nuclear level.
virtual void do_decay(double &Ex, int &two_J, marley::Parity &Pi, const std::shared_ptr< HepMC3::GenParticle > &compound_nucleus, std::shared_ptr< HepMC3::GenParticle > &emitted_particle, std::shared_ptr< HepMC3::GenParticle > &residual_nucleus, int &qIon, marley::Generator &gen) const final override
Simulates a nuclear decay into this channel.
Base class for all exceptions thrown by MARLEY functions.
int twoJi_
Two times the initial nuclear spin .
int pdgi_
PDG code for the initial nucleus.
double width_
Total decay width into this channel (MeV)
marley::StructureDatabase * sdb_
StructureDatabase to use in calculations.
marley::Parity Pi_
Initial nuclear parity .
virtual void prepare_products(const std::shared_ptr< HepMC3::GenParticle > &compound_nucleus, std::shared_ptr< HepMC3::GenParticle > &emitted_particle, std::shared_ptr< HepMC3::GenParticle > &residual_nucleus, double Exf, int &qf, marley::Generator &gen) const
Helper function that prepares Particle objects representing the products of the two-body decay.
virtual int emitted_particle_pdg() const =0
Returns the PDG code for the particle (gamma-ray or nuclear fragment) emitted by decays into this Exi...
double Exi_
Initial nuclear excitation energy (MeV)
virtual int final_nucleus_pdg() const =0
Returns the PDG code for the final nucleus.
double one_over_two_pi_rho_i_
virtual void compute_total_width() final override
int fragment_pdg_
PDG code identifying the emitted fragment.
virtual int final_nucleus_pdg() const final override
Returns the PDG code for the final nucleus.
double max_Exf() const
Helper function that returns that maximum possible excitation energy for the daughter nucleus after e...
Simple container for storing reference data about each of the nuclear fragments considered by MARLEY'...
int get_two_s() const
Get two times the spin of this fragment.
marley::Parity get_parity() const
Get the parity of this fragment.
virtual void compute_total_width() final override
Abstract base class for models of gamma-ray strength functions.
virtual double transmission_coefficient(TransitionType type, int l, double e_gamma)=0
Returns the gamma-ray transmission coefficient (dimensionless) for the requested gamma energy and mul...
TransitionType
Electromagnetic transitions in nuclei may be classified by their multipolarity (electric vs....
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].
double inverse_transform_sample(const marley::InterpolatingFunction &cdf, double xmin, double xmax, double bisection_tolerance=1e-12)
Sample from a given 1D cumulative density function cdf(x) on the interval [xmin, xmax] using bisectio...
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...
Template class that creates an iterator to a class member based on an iterator to a pointer (either b...
Abstract base class for models of nuclear level densities.
virtual double level_density(double Ex)=0
static const MassTable & Instance()
Get a const reference to the singleton instance of the MassTable.
Abstract base class for nuclear optical model implementations.
virtual double transmission_coefficient(double total_KE_CM, int fragment_pdg, int two_j, int l, int two_s, int target_charge=0)=0
Calculate the transmission coefficient for a nuclear fragment.
Type-safe representation of a parity value (either +1 or -1)