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
marley::DecayScheme Class Reference

Discrete level and γ-ray data for a specific nuclide. More...

#include <DecayScheme.hh>

Public Types

enum class  FileFormat { native , talys }
 The FileFormat type is used to tell the DecayScheme class which format to assume when parsing a discrete level data file. More...
 

Public Member Functions

 DecayScheme (int Z, int A)
 Create a DecayScheme without any levels.
 
 DecayScheme (int Z, int A, const std::string &filename, FileFormat format=FileFormat::talys)
 Create a DecayScheme using discrete level data from a file.
 
int A () const
 Get the mass number.
 
marley::Leveladd_level (const marley::Level &level)
 Add a level to the DecayScheme.
 
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)
 
const std::vector< std::unique_ptr< marley::Level > > & get_levels () const
 Get a const reference to the vector that holds the Level objects.
 
marley::Levelget_pointer_to_closest_level (double E_level)
 Gets a pointer to the Level in the DecayScheme whose excitation energy is closest to E_level.
 
int pdg () const
 Returns the nuclear PDG code corresponding to Z and A.
 
void print (std::ostream &out=std::cout) const
 Print this DecayScheme object to a std::ostream.
 
void print_latex_table (std::ostream &ostr=std::cout)
 Print LaTeX source code that gives a tabular representation of the DecayScheme object.
 
void print_report (std::ostream &ostr=std::cout) const
 Print a human-readable text representation of the DecayScheme object.
 
void read_from_stream (std::istream &in)
 Use a std::istream to initialize this DecayScheme object, replacing any previous data.
 
int Z () const
 Get the atomic number.
 

Protected Member Functions

size_t level_lower_bound_index (double Ex)
 Get the index of the first level whose energy is not less than Ex.
 

Protected Attributes

int A_
 Mass number.
 
std::vector< std::unique_ptr< marley::Level > > levels_
 Level objects owned by this DecayScheme.
 
int Z_
 Atomic number.
 

Detailed Description

Discrete level and γ-ray data for a specific nuclide.

Definition at line 34 of file DecayScheme.hh.

Member Enumeration Documentation

◆ FileFormat

The FileFormat type is used to tell the DecayScheme class which format to assume when parsing a discrete level data file.

Currently, only discrete level data in MARLEY's native format (which may be read from a std::istream to initialize a marley::DecayScheme object via the >> operator) or in the format used by the TALYS nuclear code are allowed.

Definition at line 45 of file DecayScheme.hh.

45{ native, talys };

Constructor & Destructor Documentation

◆ DecayScheme() [1/3]

marley::DecayScheme::DecayScheme ( )
inline

Definition at line 47 of file DecayScheme.hh.

47: Z_(0), A_(0) {}
int Z_
Atomic number.
int A_
Mass number.

◆ DecayScheme() [2/3]

marley::DecayScheme::DecayScheme ( int Z,
int A )

Create a DecayScheme without any levels.

Parameters
ZAtomic number of the desired nuclide
AMass number of the desired nuclide

Definition at line 221 of file DecayScheme.cc.

221 : Z_( Z ), A_( A )
222{
223}
int A() const
Get the mass number.
int Z() const
Get the atomic number.

References A(), A_, Z(), and Z_.

◆ DecayScheme() [3/3]

marley::DecayScheme::DecayScheme ( int Z,
int A,
const std::string & filename,
FileFormat format = FileFormat::talys )

Create a DecayScheme using discrete level data from a file.

Parameters
ZAtomic number of the desired nuclide
AMass number of the desired nuclide
filenameName of the file containing the discrete level data
formatFileFormat specifier that indicates which nuclear data format is used in the file

Definition at line 225 of file DecayScheme.cc.

226 : Z_( Z ), A_( A )
227{
228 this->parse( filename, ff );
229}

References A(), A_, Z(), and Z_.

Member Function Documentation

◆ A()

int marley::DecayScheme::A ( ) const
inline

Get the mass number.

Definition at line 155 of file DecayScheme.hh.

155{ return A_; }

References A_.

Referenced by DecayScheme(), and DecayScheme().

◆ add_level()

marley::Level & marley::DecayScheme::add_level ( const marley::Level & level)

Add a level to the DecayScheme.

Returns
A reference to the newly-added Level object

Definition at line 444 of file DecayScheme.cc.

445{
446 // Compute the numerical index for where we will insert the new level
447 size_t index = level_lower_bound_index( level.energy() );
448
449 // Insert the new level into the decay scheme
450 levels_.insert( levels_.begin() + index,
451 std::make_unique< marley::Level >(level) );
452
453 // Return a reference to the newly-added level
454 return *levels_.at( index );
455}
std::vector< std::unique_ptr< marley::Level > > levels_
Level objects owned by this DecayScheme.
size_t level_lower_bound_index(double Ex)
Get the index of the first level whose energy is not less than Ex.
double energy() const
Get the excitation energy of this level (MeV)
Definition Level.hh:135

References marley::Level::energy(), level_lower_bound_index(), and levels_.

Referenced by read_from_stream().

◆ do_cascade()

void marley::DecayScheme::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)

Gamma-rays will be randomly emitted until the nucleus reaches its ground state.

Parameters
[in]initial_levelReference to the first level that will de-excite via γ-ray emission
[in,out]eventReference to an Event object that will store the emitted γs
[in]genReference to the Generator to use for random sampling
qIonNet charge of the atom or ion whose nucleus is de-exciting

Definition at line 80 of file DecayScheme.cc.

83{
84 int qIon = marley_hepmc3::get_particle_charge( *residue );
85
86 MARLEY_LOG( DEBUG, "physics.deexcitation.gamma" )
87 << "Beginning gamma cascade at level with energy "
88 << initial_level.energy() << " MeV";
89
90 marley::Level* p_current_level = &initial_level;
91
92 const marley::MassTable& mt = marley::MassTable::Instance();
93
94 // Initialize some variables used in the gamma cascade loop below
95 bool cascade_finished = false;
96 double gamma_branching_ratio = 0.;
97 double level_total_width = 0.;
98
99 while ( !cascade_finished ) {
100
101 // Randomly select a gamma to produce while storing its branching ratio
102 const marley::Gamma* p_gamma = p_current_level->sample_gamma( gen,
103 &gamma_branching_ratio );
104
105 if ( !p_gamma ) {
106 MARLEY_LOG( DEBUG, "physics.deexcitation.gamma" )
107 << " this level does not have any gammas";
108 cascade_finished = true;
109 }
110 else {
111
112 // Get the total decay width (MeV) for the initial level
113 level_total_width = marley_utils::hbar * marley_utils::log_2
114 / p_current_level->half_life();
115
116 // Update the current level now that the gamma has been emitted
117 p_current_level = p_gamma->end_level();
118 if ( !p_current_level ) {
119 throw marley::Error( "This gamma does not have an end level."
120 " Cannot continue cascade." );
121 }
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.";
127
128 // Get the excitation energy of the end level. This will be added to
129 // the ground state mass of the nucleus to determine its
130 // post-gamma-emission mass.
131 double Exf = p_current_level->energy();
132
133 // Create new particle objects to represent the emitted gamma and
134 // recoiling nucleus
135 auto gamma = marley_hepmc3::make_particle( marley_utils::PHOTON,
136 marley_hepmc3::NUHEPMC_FINAL_STATE_STATUS, 0.0 );
137
138 int pdg = marley_utils::get_nucleus_pid( Z_, A_ );
139
140 auto nucleus = marley_hepmc3::make_particle( pdg,
141 marley_hepmc3::NUHEPMC_INTERMEDIATE_RESIDUE_STATUS,
142 mt.get_atomic_mass(pdg) + Exf
143 - qIon*mt.get_particle_mass(marley_utils::ELECTRON) );
144
145 // Create a new binary decay vertex
146 auto decay_vtx = std::make_shared< HepMC3::GenVertex >();
147 decay_vtx->set_status( marley_hepmc3::NUHEPMC_GAMMA_DECAY_VERTEX );
148
149 decay_vtx->add_particle_in( residue );
150 decay_vtx->add_particle_out( gamma );
151 decay_vtx->add_particle_out( nucleus );
152
153 // Sample a decay time (MeV^{-1}) for emission of the chosen gamma-ray
154 // and store this timing information in the new binary decay vertex
155 double gamma_partial_width = gamma_branching_ratio * level_total_width;
156 marley_hepmc3::store_decay_time( gamma_partial_width, gen, decay_vtx,
157 residue );
158
159 // Add the decay vertex to the event record
160 event.add_vertex( decay_vtx );
161
162 // We can set the charge attribute now that the daughter nucleus
163 // belongs to the decay vertex (and thus the parent event)
164 marley_hepmc3::set_particle_charge( *nucleus, qIon );
165
166 // We can also now set the attributes representing the
167 // excitation energy, spin, and parity of the daughter nucleus
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() )
172 );
173 nucleus->add_attribute( "parity", std::make_shared< HepMC3::IntAttribute >(
174 static_cast< int >(p_current_level->parity()) )
175 );
176
177 // If the total decay width is finite (equivalently, there is a tabulated
178 // value for the level half-life), then store it in an attribute
179 // attached to the vertex
180 if ( std::isfinite(level_total_width) ) {
181 decay_vtx->add_attribute( "TotalWidth",
182 std::make_shared<HepMC3::DoubleAttribute>(level_total_width)
183 );
184 }
185
186 // Also store the selected gamma-ray's branching ratio
187 decay_vtx->add_attribute( "GammaBranchingRatio",
188 std::make_shared< HepMC3::DoubleAttribute>( gamma_branching_ratio )
189 );
190
191 // Sample a direction assuming that the gammas are emitted isotropically
192 // in the nucleus's rest frame.
193 // sample from [-1, 1]
194 double gamma_cos_theta = gen.uniform_random_double( -1.0, 1.0, true );
195 // sample from [0, 2*pi)
196 double gamma_phi = gen.uniform_random_double( 0., 2.*marley_utils::pi,
197 false );
198
199 MARLEY_LOG( TRACE, "physics.deexcitation.gamma" )
200 << " sampled gamma direction: cos_theta = " << gamma_cos_theta
201 << ", phi = " << gamma_phi << " rad";
202
203 // Determine the final energies and momenta for the recoiling nucleus and
204 // emitted gamma ray. Store them in the final state particle objects.
205 marley_kinematics::two_body_decay( residue, gamma, nucleus,
206 gamma_cos_theta, gamma_phi );
207
208 // Update the residue for this event to take into account changes from
209 // gamma ray emission
210 residue.swap( nucleus );
211 }
212 }
213
214 MARLEY_LOG( DEBUG, "physics.deexcitation.gamma" )
215 << "Finished gamma cascade at level with energy "
216 << p_current_level->energy();
217
218 residue->set_status( marley_hepmc3::NUHEPMC_FINAL_STATE_STATUS );
219}
void set_status(int status)
Set status code.
int pdg() const
Returns the nuclear PDG code corresponding to Z and A.
marley::Level * end_level() const
Get a pointer to the Level that absorbs this γ-ray.
Definition Gamma.hh:80
double energy() const
Get the energy of the emitted γ-ray (MeV)
Definition Gamma.hh:84
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
double half_life() const
Get the level half-life (s)
Definition Level.hh:144
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.
Definition Level.cc:30
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
static const MassTable & Instance()
Get a const reference to the singleton instance of the MassTable.
Definition MassTable.cc:69
double get_atomic_mass(int pdg_code, bool theory_ok=true) const
Get the mass of an atom.
Definition MassTable.cc:95
double get_particle_mass(int pdg_code) const
Get the mass of a particle.
Definition MassTable.cc:84

References A_, marley::Gamma::end_level(), marley::Gamma::energy(), marley::Level::energy(), marley::MassTable::get_atomic_mass(), marley::MassTable::get_particle_mass(), marley::Level::half_life(), marley::MassTable::Instance(), marley::Level::parity(), pdg(), marley::Level::sample_gamma(), HepMC3::GenParticle::set_status(), marley::Level::twoJ(), marley::Generator::uniform_random_double(), and Z_.

Referenced by marley::NucleusDecayer::process_event().

◆ get_levels()

const std::vector< std::unique_ptr< marley::Level > > & marley::DecayScheme::get_levels ( ) const
inline

Get a const reference to the vector that holds the Level objects.

Definition at line 158 of file DecayScheme.hh.

158{ return levels_; }

References levels_.

◆ get_pointer_to_closest_level()

marley::Level * marley::DecayScheme::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.

Parameters
E_levelexcitation energy (MeV)
Note
Returns nullptr if the DecayScheme doesn't own any Level objects

Definition at line 44 of file DecayScheme.cc.

46{
47 // If this decay scheme doesn't own any levels, return nullptr immediately
48 size_t num_levels = levels_.size();
49 if (num_levels == 0) return nullptr;
50
51 // Search for the level whose energy is closest to the given value of E_level
52 size_t e_index = level_lower_bound_index(E_level);
53
54 if (e_index == num_levels) {
55 // The given energy is greater than every level energy in our decay scheme.
56 // We will therefore assume that the desired level is the highest level.
57 // Its index is given by one less than the number of elements in the sorted
58 // vector, so subtract one from our previous result.
59 --e_index;
60 }
61 else if (e_index > 0) {
62 // If the calculated index does not correspond to the first element, we
63 // still need to check which of the two levels found (one on each side) is
64 // really the closest. Do so and reassign the index if needed.
65 if (std::abs(E_level - levels_.at(e_index)->energy())
66 > std::abs(E_level - levels_.at(e_index - 1)->energy()))
67 {
68 --e_index;
69 }
70 }
71
72 // Return a pointer to the selected level object
73 return levels_.at(e_index).get();
74}

References level_lower_bound_index(), and levels_.

Referenced by marley::NucleusDecayer::process_event().

◆ level_lower_bound_index()

size_t marley::DecayScheme::level_lower_bound_index ( double Ex)
protected

Get the index of the first level whose energy is not less than Ex.

Parameters
ExExcitation energy (MeV)
Note
Returns levels_.size() if all levels have energies below Ex

Definition at line 435 of file DecayScheme.cc.

435 {
436 const auto E_begin = marley::Level::make_energy_iterator( levels_.cbegin() );
437 const auto E_end = marley::Level::make_energy_iterator( levels_.cend() );
438
439 const auto closest_E_iter = std::lower_bound( E_begin, E_end, Ex );
440 return std::distance( E_begin, closest_E_iter );
441}
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...
Definition Level.hh:155

References levels_, and marley::Level::make_energy_iterator().

Referenced by add_level(), and get_pointer_to_closest_level().

◆ pdg()

int marley::DecayScheme::pdg ( ) const

Returns the nuclear PDG code corresponding to Z and A.

Definition at line 76 of file DecayScheme.cc.

76 {
77 return marley_utils::get_nucleus_pid( Z_, A_ );
78}

References A_, and Z_.

Referenced by do_cascade().

◆ print()

void marley::DecayScheme::print ( std::ostream & out = std::cout) const

Print this DecayScheme object to a std::ostream.

Definition at line 457 of file DecayScheme.cc.

457 {
458
459 size_t num_levels = levels_.size();
460
461 out << Z_ << ' ' << A_ << ' ' << num_levels << '\n';
462
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();
468
469 const auto cit = std::find_if( levels_.cbegin(), levels_.cend(),
470 [&g]( const std::unique_ptr< marley::Level >& l )
471 -> bool { return l.get() == g.end_level(); } );
472
473 int level_f_idx = -1;
474 if ( cit != levels_.cend() ) {
475 level_f_idx = std::distance(levels_.cbegin(), cit);
476 }
477 out << " " << level_f_idx << '\n';
478 }
479 }
480}

References A_, levels_, and Z_.

◆ print_latex_table()

void marley::DecayScheme::print_latex_table ( std::ostream & ostr = std::cout)

Print LaTeX source code that gives a tabular representation of the DecayScheme object.

Definition at line 368 of file DecayScheme.cc.

368 {
369
370 std::string nuc_id = marley_utils::nuc_id( Z_, A_ );
371
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))) +
380 "}} \n";// from file " + filename + " ";
381
382 ostr << marley_utils::latex_table_1;
383
384 ostr << caption_beginning + "}}\\\\\n";
385
386 ostr << marley_utils::latex_table_2;
387
388 ostr << caption_beginning + " -- \\textit{continued}}} \\\\\n";
389
390 ostr << marley_utils::latex_table_3;
391
392 // Cycle through each of the levels owned by this decay scheme
393 // object in order of increasing energy
394 for (const auto& lev : levels_ ) {
395
396 std::string sp = lev->spin_parity_string();
397
398 ostr << lev->energy() << " & " << sp << " & ";
399
400 const auto& gammas = lev->gammas();
401
402 // If there aren't any gammas for this level, finish writing
403 // the current row of the table. Add extra space between this
404 // level and the next one.
405 if ( gammas.empty() ) {
406 ostr << " & &";
407 // If this is the last row of the table, don't add extra space.
408 if (lev == levels_.back()) ostr << '\n';
409 else ostr << " \\\\ \\addlinespace[\\ExtraRowSpace]\n";
410 }
411
412 // Cycle through each of the gammas owned by the current level
413 for ( const auto& g : gammas ) {
414 // If this is not the first gamma, add empty columns
415 // for the level energy and spin-parity
416 if ( &g != &gammas.front() ) ostr << " & & ";
417 // Output information about the current gamma
418 ostr << g.energy() << " & " << g.relative_intensity()
419 << " & " << g.end_level()->energy();
420 // Add vertical space after the final gamma row. Also prevent page breaks
421 // in the middle of a list of gammas by outputting a star at the end of
422 // each row except the final gamma row.
423 if ( &g == &gammas.back() ) {
424 // Don't add the extra row space for the very last row in the table
425 if ( lev == levels_.back() ) ostr << '\n';
426 else ostr << " \\\\ \\addlinespace[\\ExtraRowSpace]" << '\n';
427 }
428 else ostr << " \\\\*" << '\n';
429 }
430 }
431 ostr << marley_utils::latex_table_4 << '\n';
432}

References A_, levels_, and Z_.

◆ print_report()

void marley::DecayScheme::print_report ( std::ostream & ostr = std::cout) const

Print a human-readable text representation of the DecayScheme object.

Definition at line 341 of file DecayScheme.cc.

341 {
342 // Cycle through each of the levels owned by this decay scheme
343 // object in order of increasing energy
344 for ( const auto& lev : levels_ ) {
345 int twoj = lev->twoJ();
346 std::string spin = std::to_string( twoj / 2 );
347 // If 2*J is odd, then the level has half-integer spin
348 if ( twoj % 2 ) spin += "/2";
349 marley::Parity parity = lev->parity();
350
351 ostr << "Level at " << lev->energy() << " MeV has spin-parity "
352 << spin << parity << " and half-life " << lev->half_life() << " s\n";
353
354 std::vector< marley::Gamma >& gammas = lev->gammas();
355
356 // Cycle through each of the gammas owned by the current level
357 // (according to the ENSDF specification, these will already be
358 // sorted in order of increasing energy)
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';
364 }
365 }
366}

References levels_.

◆ read_from_stream()

void marley::DecayScheme::read_from_stream ( std::istream & in)

Use a std::istream to initialize this DecayScheme object, replacing any previous data.

The expected format for data in the std::istream is the same as the output format in DecayScheme::print()

Definition at line 482 of file DecayScheme.cc.

482 {
483
484 levels_.clear();
485
486 int num_levels;
487 in >> Z_ >> A_ >> num_levels;
488
489 // If we had trouble parsing the decay scheme header, then
490 // just return the stream without doing anything else.
491 if ( !in ) return;
492
493 double energy, ri, half_life;
494 int two_j, num_gammas, level_f_idx;
495 marley::Parity pi;
496
497 for ( int i = 0; i < num_levels; ++i ) {
498 in >> energy >> two_j >> pi >> num_gammas >> half_life;
499
500 marley::Level& l = add_level( marley::Level(energy, two_j, pi, half_life) );
501 for ( int j = 0; j < num_gammas; ++j ) {
502 in >> energy >> ri >> level_f_idx;
503 l.add_gamma( energy, ri, levels_.at(level_f_idx).get() );
504 }
505 }
506
507 // Remove levels above the unbound threshold (these will always be
508 // handled using a continuous level density treatment)
509 const auto& mt = marley::MassTable::Instance();
510 double unbound_Ex = mt.unbound_threshold( Z_, A_ );
511
512 // TODO: replace with std::erase_if when updating to C++20
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 );
517 return unbound;
518 }
519 );
520 levels_.erase( iter_new_end, levels_.end() );
521
522}
marley::Level & add_level(const marley::Level &level)
Add a level to the DecayScheme.
marley::Gamma & add_gamma(const marley::Gamma &gamma)
Add a new gamma-ray transition to this level.
Definition Level.cc:55

References A_, marley::Level::add_gamma(), add_level(), marley::MassTable::Instance(), levels_, and Z_.

◆ Z()

int marley::DecayScheme::Z ( ) const
inline

Get the atomic number.

Definition at line 154 of file DecayScheme.hh.

154{ return Z_; }

References Z_.

Referenced by DecayScheme(), and DecayScheme().

Member Data Documentation

◆ A_

int marley::DecayScheme::A_
protected

Mass number.

Definition at line 122 of file DecayScheme.hh.

Referenced by DecayScheme(), DecayScheme(), A(), do_cascade(), pdg(), print(), print_latex_table(), and read_from_stream().

◆ levels_

std::vector< std::unique_ptr<marley::Level> > marley::DecayScheme::levels_
protected

◆ Z_

int marley::DecayScheme::Z_
protected

Atomic number.

Definition at line 121 of file DecayScheme.hh.

Referenced by DecayScheme(), DecayScheme(), do_cascade(), pdg(), print(), print_latex_table(), read_from_stream(), and Z().


The documentation for this class was generated from the following files: