Processes an input GenEvent object.
55{
56 auto undecayed_residues = marley_hepmc3::get_particles_with_status(
57 marley_hepmc3::NUHEPMC_UNDECAYED_RESIDUE_STATUS, event );
58
59 MARLEY_LOG( DEBUG, "physics.deexcitation" ) << "NucleusDecayer: processing "
60 << undecayed_residues.size() << " undecayed residue(s)";
61
62
63
64
65 int proc_id = event.attribute< HepMC3::IntAttribute >(
66 "signal_process_id" )->value();
67 auto proc_type = marley_hepmc3::from_nuhepmc_proc_id( proc_id );
68 bool is_continuum_channel = false;
72 {
73 is_continuum_channel = true;
74 }
75
76 for ( auto residue : undecayed_residues ) {
77
78
79
80 double Ex = residue->attribute< HepMC3::DoubleAttribute >( "Ex" )->value();
81 int twoJ = residue->attribute< HepMC3::IntAttribute >( "twoJ" )->value();
82 int p_int = residue->attribute< HepMC3::IntAttribute >( "parity" )->value();
83 marley::Parity P( p_int );
84
85
86
87 if ( Ex == 0. ) continue;
88
89 MARLEY_LOG( DEBUG, "physics.deexcitation" ) << "De-exciting residue PDG "
90 << residue->pid() << ": Ex = " << Ex << " MeV, 2J = " << twoJ
91 << ", P = " << P;
92
93
94 if ( Ex < 0. ) throw marley::Error("Negative excitation energy Ex = "
95 + std::to_string(Ex) + " MeV encountered in marley::NucleusDecayer::"
96 "deexcite_residue()");
97
98
99
100
102 int initial_residue_pdg = residue->pid();
103 int qIon = marley_hepmc3::get_particle_charge( *residue );
104
105
106
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";
112 continue;
113 }
114
115 double residue_mass = residue->generated_mass();
116
117
118 double gs_residue_mass = mt.get_atomic_mass( initial_residue_pdg )
119 - qIon*mt.get_particle_mass( marley_utils::ELECTRON );
120
121 double expected_residue_mass = gs_residue_mass + Ex;
122
123 if ( std::abs(residue_mass - expected_residue_mass) > EX_TOLERANCE ) {
124
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.";
130 continue;
131 }
132
133
134
135
136
137
139 throw marley::Error("Partially de-excited nuclear remnant encountered"
140 " in marley::NucleusDecay::deexcite_residue().");
141 }
142
143
144
145
146
147
148
150 double unbound_threshold = mt.unbound_threshold( initial_residue_pdg );
151
152
153
154
155
156
157
158
159 bool continuum = ( Ex > unbound_threshold )
160 || ( !ds ) || ( is_continuum_channel );
161
162
163
164
165 bool started_from_continuum = continuum;
166
167 MARLEY_LOG( DEBUG, "physics.deexcitation" ) << "De-excitation path: "
168 << ( continuum ? "continuum (Hauser-Feshbach)" : "discrete gamma cascade" );
169
170 if ( continuum ) {
171
172
173
174 auto first = std::make_shared< HepMC3::GenParticle >();
175 auto second = std::make_shared< HepMC3::GenParticle >();
176
177
178
179 while ( continuum && Ex > CONTINUUM_GS_CUTOFF ) {
180
182
183 marley::HauserFeshbachDecay hfd( residue, Ex, twoJ, P, sdb );
184 MARLEY_LOG( DEBUG, "physics.deexcitation.hauser" ) << hfd;
185
186 int q_second;
187 const auto& exit_channel = hfd.do_decay( Ex, twoJ, P, first, second,
188 q_second, gen );
189
190 continuum = exit_channel.is_continuum();
191
192 double width_tot = hfd.total_width();
193 double width_ec = exit_channel.width();
194
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.";
200
201
202 auto decay_vtx = std::make_shared< HepMC3::GenVertex >();
203 decay_vtx->set_status( marley_hepmc3::NUHEPMC_HF_DECAY_VERTEX );
204
205 decay_vtx->add_particle_in( residue );
206 decay_vtx->add_particle_out( first );
207 decay_vtx->add_particle_out( second );
208
209
210
211 marley_hepmc3::store_decay_time( width_ec, gen, decay_vtx, residue );
212
213
214 event.add_vertex( decay_vtx );
215
216
217
218 marley_hepmc3::set_particle_charge( *second, q_second );
219
220
221
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 )) );
228
229
230
231 residue.swap( second );
232
233
234
235
236 decay_vtx->add_attribute( "TotalWidth",
237 std::make_shared< HepMC3::DoubleAttribute >(width_tot) );
238
239 decay_vtx->add_attribute( "ECWidth",
240 std::make_shared< HepMC3::DoubleAttribute >(width_ec) );
241
242
243
244
245 if ( continuum ) {
246 const auto& cec = dynamic_cast< const marley::ContinuumExitChannel& >(
247 exit_channel );
248
249 const auto* spw_ptr = cec.get_last_sampled_spw();
250 double width_sp = spw_ptr->diff_width;
251
252 decay_vtx->add_attribute( "SPWidth",
253 std::make_shared< HepMC3::DoubleAttribute >(width_sp) );
254
255 bool is_fragment_emission = exit_channel.emits_fragment();
256
257 if ( is_fragment_emission ) {
258 const auto* f_spw = static_cast< const marley
259 ::FragmentContinuumExitChannel::FragmentSpinParityWidth* >(
260 spw_ptr );
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) );
265 }
266
267 else {
268
269 const auto* g_spw = static_cast< const marley
270 ::GammaContinuumExitChannel::GammaSpinParityWidth* >( spw_ptr );
271
272 decay_vtx->add_attribute( "multipolarity",
273 std::make_shared< HepMC3::IntAttribute >(g_spw->multipolarity) );
274 }
275 }
276 }
277 }
278
279 if ( !continuum ) {
280
281
282
283
284
287
288
290
291
292 if ( !lev ) throw marley::Error( "Null nuclear level pointer encountered"
293 " in marley::NucleusDecayer::deexcite_residue()" );
294
295
296
297
298
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" );
306 }
307 }
308
309 dec_scheme->
do_cascade( *lev, event, gen, residue );
310 }
311
312 }
313
314}
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)
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.
marley::StructureDatabase & get_structure_db()
Get a reference to the StructureDatabase owned by this Generator.
double energy() const
Get the excitation energy of this level (MeV)
static const MassTable & Instance()
Get a const reference to the singleton instance of the MassTable.
@ NC_Continuum
Nuclear matrix elements contain for a transition to a continuum of nuclear levels.
@ AntiNeutrinoCC_Continuum
Nuclear matrix elements contain for a transition to a continuum of nuclear levels.
@ NeutrinoCC_Continuum
Nuclear matrix elements contain for a transition to a continuum of nuclear levels.
marley::DecayScheme * get_decay_scheme(const int particle_id)
Retrieves discrete level data from the database.