56 auto undecayed_residues = marley_hepmc3::get_particles_with_status(
57 marley_hepmc3::NUHEPMC_UNDECAYED_RESIDUE_STATUS, event );
59 MARLEY_LOG( DEBUG,
"physics.deexcitation" ) <<
"NucleusDecayer: processing "
60 << undecayed_residues.size() <<
" undecayed residue(s)";
66 "signal_process_id" )->value();
67 auto proc_type = marley_hepmc3::from_nuhepmc_proc_id( proc_id );
68 bool is_continuum_channel =
false;
73 is_continuum_channel =
true;
76 for (
auto residue : undecayed_residues ) {
87 if ( Ex == 0. )
continue;
89 MARLEY_LOG( DEBUG,
"physics.deexcitation" ) <<
"De-exciting residue PDG "
90 << residue->pid() <<
": Ex = " << Ex <<
" MeV, 2J = " << twoJ
94 if ( Ex < 0. )
throw marley::Error(
"Negative excitation energy Ex = "
95 + std::to_string(Ex) +
" MeV encountered in marley::NucleusDecayer::"
96 "deexcite_residue()");
102 int initial_residue_pdg = residue->pid();
103 int qIon = marley_hepmc3::get_particle_charge( *residue );
107 if ( !marley_utils::is_ion(initial_residue_pdg) ) {
108 MARLEY_LOG( WARN,
"physics.deexcitation" )
109 <<
"Unrecognized nuclear PDG code "
110 << initial_residue_pdg <<
" encountered in marley::NucleusDecayer::"
111 <<
"deexcite_residue(). The de-excitation cascade will be skipped";
115 double residue_mass = residue->generated_mass();
118 double gs_residue_mass = mt.get_atomic_mass( initial_residue_pdg )
119 - qIon*mt.get_particle_mass( marley_utils::ELECTRON );
121 double expected_residue_mass = gs_residue_mass + Ex;
123 if ( std::abs(residue_mass - expected_residue_mass) > EX_TOLERANCE ) {
125 if ( std::abs(residue_mass - gs_residue_mass) <= EX_TOLERANCE ) {
126 MARLEY_LOG( WARN,
"physics.deexcitation" )
127 <<
"Encountered ground-state nuclear remnant"
128 <<
" in marley::NucleusDecay::deexcite_residue(). The de-excitation"
129 <<
" cascade has already been applied.";
139 throw marley::Error(
"Partially de-excited nuclear remnant encountered"
140 " in marley::NucleusDecay::deexcite_residue().");
150 double unbound_threshold = mt.unbound_threshold( initial_residue_pdg );
159 bool continuum = ( Ex > unbound_threshold )
160 || ( !ds ) || ( is_continuum_channel );
165 bool started_from_continuum = continuum;
167 MARLEY_LOG( DEBUG,
"physics.deexcitation" ) <<
"De-excitation path: "
168 << ( continuum ?
"continuum (Hauser-Feshbach)" :
"discrete gamma cascade" );
174 auto first = std::make_shared< HepMC3::GenParticle >();
175 auto second = std::make_shared< HepMC3::GenParticle >();
179 while ( continuum && Ex > CONTINUUM_GS_CUTOFF ) {
184 MARLEY_LOG( DEBUG,
"physics.deexcitation.hauser" ) << hfd;
187 const auto& exit_channel = hfd.
do_decay( Ex, twoJ, P, first, second,
192 double width_tot = hfd.total_width();
193 double width_ec = exit_channel.width();
195 MARLEY_LOG( DEBUG,
"physics.deexcitation.hauser" )
196 <<
"Hauser-Feshbach decay to " << first->pid()
197 <<
" and " << second->pid();
198 MARLEY_LOG( DEBUG,
"physics.deexcitation.hauser" )
199 << second->pid() <<
" is at Ex = " << Ex <<
" MeV.";
202 auto decay_vtx = std::make_shared< HepMC3::GenVertex >();
203 decay_vtx->set_status( marley_hepmc3::NUHEPMC_HF_DECAY_VERTEX );
205 decay_vtx->add_particle_in( residue );
206 decay_vtx->add_particle_out( first );
207 decay_vtx->add_particle_out( second );
211 marley_hepmc3::store_decay_time( width_ec, gen, decay_vtx, residue );
214 event.add_vertex( decay_vtx );
218 marley_hepmc3::set_particle_charge( *second, q_second );
222 second->add_attribute(
"Ex",
223 std::make_shared< HepMC3::DoubleAttribute >(Ex) );
224 second->add_attribute(
"twoJ",
225 std::make_shared< HepMC3::IntAttribute >(twoJ) );
226 second->add_attribute(
"parity",
227 std::make_shared< HepMC3::IntAttribute >(
static_cast<int>( P )) );
231 residue.swap( second );
236 decay_vtx->add_attribute(
"TotalWidth",
237 std::make_shared< HepMC3::DoubleAttribute >(width_tot) );
239 decay_vtx->add_attribute(
"ECWidth",
240 std::make_shared< HepMC3::DoubleAttribute >(width_ec) );
252 decay_vtx->add_attribute(
"SPWidth",
253 std::make_shared< HepMC3::DoubleAttribute >(width_sp) );
255 bool is_fragment_emission = exit_channel.emits_fragment();
257 if ( is_fragment_emission ) {
258 const auto* f_spw =
static_cast< const marley
261 decay_vtx->add_attribute(
"two_j_frag",
262 std::make_shared< HepMC3::IntAttribute >(f_spw->two_j_frag) );
263 decay_vtx->add_attribute(
"orb_l",
264 std::make_shared< HepMC3::IntAttribute >(f_spw->orb_l) );
269 const auto* g_spw =
static_cast< const marley
272 decay_vtx->add_attribute(
"multipolarity",
273 std::make_shared< HepMC3::IntAttribute >(g_spw->multipolarity) );
292 if ( !lev )
throw marley::Error(
"Null nuclear level pointer encountered"
293 " in marley::NucleusDecayer::deexcite_residue()" );
299 if ( !started_from_continuum ) {
300 double Ex_level = lev->
energy();
301 if ( std::abs(Ex - Ex_level) > EX_TOLERANCE ) {
302 throw marley::Error(
"Excitation energy mismatch encountered in"
303 " marley::NucleusDecayer::deexcite_residue(). Event has Ex = "
304 + std::to_string(Ex) +
" MeV while the initial discrete level has "
305 + std::to_string(Ex_level) +
" MeV" );
309 dec_scheme->
do_cascade( *lev, event, gen, residue );