28#include "HepMC3/GenEvent.h"
29#include "HepMC3/GenParticle.h"
30#include "HepMC3/GenVertex.h"
33#include "marley/hepmc3_utils.hh"
34#include "marley/marley_utils.hh"
35#include "marley/DecayScheme.hh"
36#include "marley/Generator.hh"
37#include "marley/marley_kinematics.hh"
38#include "marley/Logger.hh"
39#include "marley/HauserFeshbachDecay.hh"
48 size_t num_levels =
levels_.size();
49 if (num_levels == 0)
return nullptr;
54 if (e_index == num_levels) {
61 else if (e_index > 0) {
65 if (std::abs(E_level -
levels_.at(e_index)->energy())
66 > std::abs(E_level -
levels_.at(e_index - 1)->energy()))
73 return levels_.at(e_index).get();
77 return marley_utils::get_nucleus_pid(
Z_,
A_ );
82 std::shared_ptr< HepMC3::GenParticle >& residue )
84 int qIon = marley_hepmc3::get_particle_charge( *residue );
86 MARLEY_LOG( DEBUG,
"physics.deexcitation.gamma" )
87 <<
"Beginning gamma cascade at level with energy "
88 << initial_level.
energy() <<
" MeV";
95 bool cascade_finished =
false;
96 double gamma_branching_ratio = 0.;
97 double level_total_width = 0.;
99 while ( !cascade_finished ) {
103 &gamma_branching_ratio );
106 MARLEY_LOG( DEBUG,
"physics.deexcitation.gamma" )
107 <<
" this level does not have any gammas";
108 cascade_finished =
true;
113 level_total_width = marley_utils::hbar * marley_utils::log_2
118 if ( !p_current_level ) {
119 throw marley::Error(
"This gamma does not have an end level."
120 " Cannot continue cascade." );
122 MARLEY_LOG( DEBUG,
"physics.deexcitation.gamma" )
123 << std::setprecision(15) << std::scientific
124 <<
" emitted gamma with energy "
125 << p_gamma->
energy() <<
" MeV. New level has energy "
126 << p_current_level->
energy() <<
" MeV.";
131 double Exf = p_current_level->
energy();
135 auto gamma = marley_hepmc3::make_particle( marley_utils::PHOTON,
136 marley_hepmc3::NUHEPMC_FINAL_STATE_STATUS, 0.0 );
138 int pdg = marley_utils::get_nucleus_pid(
Z_,
A_ );
140 auto nucleus = marley_hepmc3::make_particle(
pdg,
141 marley_hepmc3::NUHEPMC_INTERMEDIATE_RESIDUE_STATUS,
146 auto decay_vtx = std::make_shared< HepMC3::GenVertex >();
147 decay_vtx->set_status( marley_hepmc3::NUHEPMC_GAMMA_DECAY_VERTEX );
149 decay_vtx->add_particle_in( residue );
150 decay_vtx->add_particle_out( gamma );
151 decay_vtx->add_particle_out( nucleus );
155 double gamma_partial_width = gamma_branching_ratio * level_total_width;
156 marley_hepmc3::store_decay_time( gamma_partial_width, gen, decay_vtx,
160 event.add_vertex( decay_vtx );
164 marley_hepmc3::set_particle_charge( *nucleus, qIon );
168 nucleus->add_attribute(
"Ex",
169 std::make_shared< HepMC3::DoubleAttribute >(Exf) );
170 nucleus->add_attribute(
"twoJ",
171 std::make_shared< HepMC3::IntAttribute >( p_current_level->
twoJ() )
173 nucleus->add_attribute(
"parity", std::make_shared< HepMC3::IntAttribute >(
174 static_cast< int >(p_current_level->
parity()) )
180 if ( std::isfinite(level_total_width) ) {
181 decay_vtx->add_attribute(
"TotalWidth",
182 std::make_shared<HepMC3::DoubleAttribute>(level_total_width)
187 decay_vtx->add_attribute(
"GammaBranchingRatio",
188 std::make_shared< HepMC3::DoubleAttribute>( gamma_branching_ratio )
199 MARLEY_LOG( TRACE,
"physics.deexcitation.gamma" )
200 <<
" sampled gamma direction: cos_theta = " << gamma_cos_theta
201 <<
", phi = " << gamma_phi <<
" rad";
205 marley_kinematics::two_body_decay( residue, gamma, nucleus,
206 gamma_cos_theta, gamma_phi );
210 residue.swap( nucleus );
214 MARLEY_LOG( DEBUG,
"physics.deexcitation.gamma" )
215 <<
"Finished gamma cascade at level with energy "
216 << p_current_level->
energy();
218 residue->
set_status( marley_hepmc3::NUHEPMC_FINAL_STATE_STATUS );
221marley::DecayScheme::DecayScheme(
int Z,
int A ) :
Z_(
Z ),
A_(
A )
225marley::DecayScheme::DecayScheme(
int Z,
int A,
const std::string& filename,
228 this->parse( filename, ff );
231void marley::DecayScheme::parse_talys(
const std::string& filename ) {
236 std::string nuc_id = marley_utils::nuc_id( Z_, A_ );
240 nuc_id.back() = tolower( nuc_id.back() );
242 const std::regex nuclide_line(
"[0-9 ]{18} {57}" + nuc_id );
245 std::ifstream file_in( filename );
249 if ( !file_in.good() )
throw marley::Error(
"Could not read from the"
250 " TALYS data file " + filename );
254 bool found_decay_scheme =
false;
256 while ( std::getline(file_in, line) ) {
257 if ( std::regex_match(line, nuclide_line) ) {
258 found_decay_scheme =
true;
263 if ( !found_decay_scheme )
throw marley::Error(
"Gamma decay scheme data"
264 " (adopted levels, gammas) for " + marley_utils::nucid_to_symbol( nuc_id )
265 +
" could not be found in the TALYS data file " + filename );
267 MARLEY_LOG( DEBUG,
"init.structure.decay" )
268 <<
"Gamma decay scheme data for " + nuc_id
269 <<
" found. Using TALYS dataset ";
270 MARLEY_LOG( DEBUG,
"init.structure.decay" ) << line;
273 int dummy, num_excited_levels;
276 std::istringstream iss( line );
277 iss >> dummy >> dummy >> dummy >> num_excited_levels;
279 for (
int l_idx = 0; l_idx <= num_excited_levels; ++l_idx ) {
282 std::getline( file_in, line );
291 int level_num, pi, num_gammas;
292 double level_energy, spin, half_life;
293 iss >> level_num >> level_energy >> spin >> pi >> num_gammas >> half_life;
297 int twoJ = std::round( 2 * spin );
300 marley::Parity parity = marley::Parity( pi );
304 marley::Level& current_level = add_level( marley::Level(level_energy,
305 twoJ, parity, half_life) );
307 for (
int g_idx = 0; g_idx < num_gammas; ++g_idx ) {
310 std::getline( file_in, line );
319 int gamma_final_level_num;
321 iss >> gamma_final_level_num >> br;
326 marley::Level* final_level = levels_.at( gamma_final_level_num ).get();
330 double gamma_energy = level_energy - final_level->
energy();
333 current_level.
add_gamma( gamma_energy, br, final_level );
344 for (
const auto& lev :
levels_ ) {
345 int twoj = lev->twoJ();
346 std::string spin = std::to_string( twoj / 2 );
348 if ( twoj % 2 ) spin +=
"/2";
351 ostr <<
"Level at " << lev->energy() <<
" MeV has spin-parity "
352 << spin << parity <<
" and half-life " << lev->half_life() <<
" s\n";
354 std::vector< marley::Gamma >& gammas = lev->gammas();
359 for (
const auto& g : gammas ) {
360 ostr <<
" has a gamma with energy " << g.energy() <<
" MeV";
361 ostr <<
" (transition to level at "
362 << g.end_level()->energy() <<
" MeV)" <<
'\n';
363 ostr <<
" and relative intensity " << g.relative_intensity() <<
'\n';
370 std::string nuc_id = marley_utils::nuc_id(
Z_,
A_ );
372 std::string caption_beginning =
373 std::string(
"{\\textbf{Levels") +
374 " and $\\boldsymbol{\\gamma}$ transitions \n for " +
375 "\\isotope[\\boldsymbol{" +
376 marley_utils::trim_copy(nuc_id.substr(0,3)) +
377 "}]{\\textbf{" + nuc_id.substr(3,1) +
378 marley_utils::trim_copy(
379 marley_utils::to_lowercase(nuc_id.substr(4,1))) +
382 ostr << marley_utils::latex_table_1;
384 ostr << caption_beginning +
"}}\\\\\n";
386 ostr << marley_utils::latex_table_2;
388 ostr << caption_beginning +
" -- \\textit{continued}}} \\\\\n";
390 ostr << marley_utils::latex_table_3;
394 for (
const auto& lev :
levels_ ) {
396 std::string sp = lev->spin_parity_string();
398 ostr << lev->energy() <<
" & " << sp <<
" & ";
400 const auto& gammas = lev->gammas();
405 if ( gammas.empty() ) {
408 if (lev ==
levels_.back()) ostr <<
'\n';
409 else ostr <<
" \\\\ \\addlinespace[\\ExtraRowSpace]\n";
413 for (
const auto& g : gammas ) {
416 if ( &g != &gammas.front() ) ostr <<
" & & ";
418 ostr << g.energy() <<
" & " << g.relative_intensity()
419 <<
" & " << g.end_level()->energy();
423 if ( &g == &gammas.back() ) {
425 if ( lev ==
levels_.back() ) ostr <<
'\n';
426 else ostr <<
" \\\\ \\addlinespace[\\ExtraRowSpace]" <<
'\n';
428 else ostr <<
" \\\\*" <<
'\n';
431 ostr << marley_utils::latex_table_4 <<
'\n';
439 const auto closest_E_iter = std::lower_bound( E_begin, E_end, Ex );
440 return std::distance( E_begin, closest_E_iter );
451 std::make_unique< marley::Level >(level) );
459 size_t num_levels =
levels_.size();
461 out <<
Z_ <<
' ' <<
A_ <<
' ' << num_levels <<
'\n';
463 for (
const auto& lev :
levels_ ) {
464 out <<
" " << lev->energy() <<
' ' << lev->twoJ() <<
' '
465 << lev->parity() <<
' ' << lev->gammas().size() <<
'\n';
466 for (
const auto& g : lev->gammas() ) {
467 out <<
" " << g.energy() <<
' ' << g.relative_intensity();
470 [&g](
const std::unique_ptr< marley::Level >& l )
471 ->
bool { return l.get() == g.end_level(); } );
473 int level_f_idx = -1;
475 level_f_idx = std::distance(
levels_.cbegin(), cit);
477 out <<
" " << level_f_idx <<
'\n';
487 in >>
Z_ >>
A_ >> num_levels;
493 double energy, ri, half_life;
494 int two_j, num_gammas, level_f_idx;
497 for (
int i = 0; i < num_levels; ++i ) {
498 in >> energy >> two_j >> pi >> num_gammas >> half_life;
501 for (
int j = 0; j < num_gammas; ++j ) {
502 in >> energy >> ri >> level_f_idx;
510 double unbound_Ex = mt.unbound_threshold(
Z_,
A_ );
513 auto iter_new_end = std::remove_if(
levels_.begin(),
levels_.end(),
514 [ unbound_Ex ](
const std::unique_ptr< marley::Level >& lev ) ->
bool {
515 double lvl_Ex = lev->energy();
516 bool unbound = ( lvl_Ex > unbound_Ex );
524void marley::DecayScheme::parse(
const std::string& filename,
530 case FileFormat::native:
531 this->parse_native( filename );
534 case FileFormat::talys:
535 this->parse_talys( filename );
542 " marley::DecayScheme constructor." );
547void marley::DecayScheme::parse_native(
const std::string& filename ) {
550 std::ifstream file_in( filename );
554 if ( !file_in.good() )
throw marley::Error(
"Could not read from the"
555 " data file " + filename );
557 read_from_stream( file_in );
Stores event-related information.
void set_status(int status)
Set status code.
void read_from_stream(std::istream &in)
Use a std::istream to initialize this DecayScheme object, replacing any previous data.
int pdg() const
Returns the nuclear PDG code corresponding to Z and A.
std::vector< std::unique_ptr< marley::Level > > levels_
Level objects owned by this DecayScheme.
marley::Level & add_level(const marley::Level &level)
Add a level to the DecayScheme.
void print_latex_table(std::ostream &ostr=std::cout)
Print LaTeX source code that gives a tabular representation of the DecayScheme object.
void do_cascade(marley::Level &initial_level, HepMC3::GenEvent &event, marley::Generator &gen, std::shared_ptr< HepMC3::GenParticle > &residue)
Simulates nuclear de-excitation via γ-ray emission(s)
size_t level_lower_bound_index(double Ex)
Get the index of the first level whose energy is not less than Ex.
FileFormat
The FileFormat type is used to tell the DecayScheme class which format to assume when parsing a discr...
int A() const
Get the mass number.
int Z() const
Get the atomic number.
void print(std::ostream &out=std::cout) const
Print this DecayScheme object to a std::ostream.
void print_report(std::ostream &ostr=std::cout) const
Print a human-readable text representation of the DecayScheme object.
marley::Level * get_pointer_to_closest_level(double E_level)
Gets a pointer to the Level in the DecayScheme whose excitation energy is closest to E_level.
Base class for all exceptions thrown by MARLEY functions.
A gamma-ray transition between two nuclear levels.
marley::Level * end_level() const
Get a pointer to the Level that absorbs this γ-ray.
double energy() const
Get the energy of the emitted γ-ray (MeV)
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].
A discrete nuclear energy level.
static marley::IteratorToPointerMember< It, double > make_energy_iterator(It it)
Convert an iterator that points to a marley::Level* (or a smart pointer to a marley::Level) into an i...
double half_life() const
Get the level half-life (s)
marley::Gamma & add_gamma(const marley::Gamma &gamma)
Add a new gamma-ray transition to this level.
const marley::Gamma * sample_gamma(marley::Generator &gen, double *prob_ptr=nullptr)
Choose a gamma owned by this level randomly based on the relative intensities of all of the gammas.
marley::Parity parity() const
Get the level parity.
int twoJ() const
Get two times the level spin.
double energy() const
Get the excitation energy of this level (MeV)
Singleton lookup table for particle and atomic masses.
static const MassTable & Instance()
Get a const reference to the singleton instance of the MassTable.
double get_atomic_mass(int pdg_code, bool theory_ok=true) const
Get the mass of an atom.
double get_particle_mass(int pdg_code) const
Get the mass of a particle.
Type-safe representation of a parity value (either +1 or -1)