ATLAS Offline Software
Loading...
Searching...
No Matches
FixHepMC.cxx
Go to the documentation of this file.
1/*
2 Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3*/
4
5#ifndef XAOD_ANALYSIS
6
10#include "AtlasHepMC/GenEvent.h"
12#include <vector>
13
14
15FixHepMC::FixHepMC(const std::string& name, ISvcLocator* pSvcLocator)
16 : GenBase(name, pSvcLocator)
17 , m_loopKilled(0)
18 , m_pdg0Killed(0)
21 , m_totalSeen(0)
23{
24 declareProperty("KillLoops", m_killLoops = true, "Remove particles in loops?");
25 declareProperty("KillPDG0", m_killPDG0 = true, "Remove particles with PDG ID 0?");
26 declareProperty("CleanDecays", m_cleanDecays = true, "Clean decay chains from non-propagating particles?");
27 declareProperty("PurgeUnstableWithoutEndVtx", m_purgeUnstableWithoutEndVtx = false, "Remove unstable particles without decay vertex?");
28 declareProperty("IgnoreSemiDisconnected", m_ignoreSemiDisconnected = false, "Ignore semi-disconnected particles (normal in Sherpa)");
29 declareProperty("PIDmap", m_pidmap = std::map<int,int>(), "Map of PDG IDs to replace");
30 declareProperty("forced_momentum", m_forced_momentum = "MEV", "Forced momentum unit");
31 declareProperty("forced_length", m_forced_length = "MM", "Forced length unit");
32 declareProperty("ApplyUnitsFix", m_unitsFix = true, "Attempt to identify momentum units problems and fix them");
33 declareProperty("SetHasCycles", m_setHasCycles = false, "Note hat this event has cycles (loops)");
34}
35
36StatusCode FixHepMC::execute(const EventContext& /*ctx*/) {
37 for (McEventCollection::const_iterator ievt = events()->begin(); ievt != events()->end(); ++ievt) {
38 // FIXME: const_cast
39 HepMC::GenEvent* evt = const_cast<HepMC::GenEvent*>(*ievt);
40 m_looper.findLoops(evt,true);
41 /* AV: To understand why some algorithms fail one would have to request debug explicitly, but that is the reviwers consensus.*/
42 if (!m_looper.loop_particles().empty() || !m_looper.loop_vertices().empty()) {
43 ATH_MSG_DEBUG("Found " << m_looper.loop_vertices().size() << " vertices in loops");
44 ATH_MSG_DEBUG("Found " << m_looper.loop_particles().size() << " particles in loops");
45 ATH_MSG_DEBUG("Please use MC::Loops::findLoops for this event to obtain all particles and vertices in the loops");
46 }
47 auto old_momentum = evt->momentum_unit();
48 auto old_length = evt->length_unit();
49 evt->set_units(
50 m_forced_momentum != "" ? HepMC3::Units::momentum_unit(m_forced_momentum): old_momentum,
51 m_forced_length != "" ? HepMC3::Units::length_unit(m_forced_length): old_length);
52 if ( m_forced_momentum != "" && m_forced_momentum != HepMC3::Units::name(old_momentum) ) ATH_MSG_WARNING("Updated momentum units " << HepMC3::Units::name(old_momentum) << "->" << m_forced_momentum);
53 if ( m_forced_length != "" && m_forced_length != HepMC3::Units::name(old_length) ) ATH_MSG_WARNING("Updated length units " << HepMC3::Units::name(old_length) << "->" << m_forced_length);
54
55 if (!m_pidmap.empty()) {
56 for (auto ip: *evt) {
57 // Skip this particle if (somehow) its pointer is null
58 if (!ip) continue;
59 auto newpid = m_pidmap.find(ip->pdg_id());
60 if (newpid == m_pidmap.end()) continue;
61 ip->set_pdg_id(newpid->second);
62 // Increment the counter for replaced PDG IDs
64 m_replacedpid_counts[ip->pdg_id()]++;
65 }
66 }
67
68 if (m_setHasCycles){
69 // If asked, tag the event as having cycles and alert the user that this problem exists
70 auto cycles = std::make_shared<HepMC3::IntAttribute>(1);
71 evt->add_attribute(HepMCStr::cycles, cycles);
72 }
73
74 // Add a unit entry to the event weight vector if it's currently empty
75 if (evt->weights().empty()) {
76 ATH_MSG_DEBUG("Adding a unit weight to empty event weight vector");
77 evt->weights().push_back(1);
78 }
79 // Set a (0,0,0) vertex to be the signal vertex if not already set
81 const HepMC::FourVector nullpos;
82 for (auto iv: evt->vertices()) {
83 if (iv->position() == nullpos) {
84 ATH_MSG_DEBUG("Setting representative event position vertex");
86 break;
87 }
88 }
89 }
90
91 // Catch cases with more than 2 beam particles (16.11.2021)
92 std::vector<HepMC::GenParticlePtr> beams_t;
93 for (const HepMC::GenParticlePtr& p : evt->beams()) {
94 if (p->status() == 4) beams_t.push_back(p);
95 }
96 if (beams_t.size() > 2) {
97 ATH_MSG_INFO("Invalid number of beam particles " << beams_t.size() << ". Will try to fix.");
98 std::vector<HepMC::GenParticlePtr> bparttoremove;
99 for (const auto& bpart : beams_t) {
100 if (bpart->id() == 0 && bpart->production_vertex()) bparttoremove.push_back(bpart);
101 }
102 for (auto bpart: bparttoremove) {
103 bpart->production_vertex()->remove_particle_out(bpart);
104 }
105 }
106
107 // Some generators / samples have a mis-match between the units they report and the units used for momentum, usually because we have applied
108 // a correction to the momentum without also correcting the units. Here we're going to check for states that look particularly suspicious
109 // and apply a correction if required. We will try to identify those issues based on the beam particles.
110 if (m_unitsFix){ // Only if requested - allow folks to disable this if they know what they're doing
111 double units_problem = -1.;
112 // If we have beam particles, let's use them - it's much faster!
113 if (beams_t.size() > 0 ){
114 for (const HepMC::GenParticlePtr& p : beams_t){
115 if (p->momentum().pz() > 1e9) units_problem = p->momentum().pz();
116 }
117 // if we didn't have beam particles, we'll go through some of the main record
118 } else {
119 for (const HepMC::GenParticlePtr& p : evt->particles()) {
120 if (p && p->momentum().pz() > 1e9) units_problem = p->momentum().pz();
121 }
122 }
123 if (units_problem>0){ // No particles should have momenta above 1 PeV; this must be a units issue
124 ATH_MSG_INFO("Apparent units problem; beam particles have z-momentum " << units_problem << " in MeV. Will divide by 1000.");
125 MC::MeVToGeV(evt); //Only scales momenta and masses
126 }
127 }
128
129 // Some heuristics to catch problematic cases
130 std::vector<HepMC::GenParticlePtr> semi_disconnected, decay_loop_particles;
131 for (auto ip : evt->particles()) {
132 // Skip this particle if (somehow) its pointer is null
133 if (!ip) continue;
134 bool particle_to_fix = false;
135 int abspid = std::abs(ip->pdg_id());
136 auto vProd = ip->production_vertex();
137 auto vEnd = ip->end_vertex();
139 if ( (!vProd || vProd->id() == 0) && vEnd && ip->status() != 4) {
140 particle_to_fix = true;
141 ATH_MSG_DEBUG("Found particle " << ip->pdg_id() << " without production vertex! HepMC status = " << ip->status());
142 }
144 if (vProd && !vEnd && ip->status() != 1) {
145 particle_to_fix = true;
146 ATH_MSG_DEBUG("Found particle " << ip->pdg_id() << " without decay vertex! HepMC status = " << ip->status());
147 }
148 if (particle_to_fix) semi_disconnected.push_back(ip);
149 // Case 3: keep track of loop particles inside decay chains (seen in H7+EvtGen)
150 if (abspid == 43 || abspid == 44 || abspid == 30353 || abspid == 30343) {
151 decay_loop_particles.push_back(std::move(ip));
152 }
153 }
154
158
163 if ( !m_ignoreSemiDisconnected && (semi_disconnected.size() == 4 || semi_disconnected.size() == 3 || semi_disconnected.size() == 2)) {
164 size_t no_endv = 0;
165 size_t no_prov = 0;
166 HepMC::FourVector sum(0,0,0,0);
167 std::set<HepMC::GenVertexPtr> standalone;
168 for (const auto& part : semi_disconnected) {
169 if (!part->production_vertex() || !part->production_vertex()->id()) {
170 no_prov++; sum += part->momentum();
171 }
172 if (!part->end_vertex()) {
173 no_endv++; sum -= part->momentum();
174 }
175 if (part->production_vertex()) standalone.insert(part->production_vertex());
176 if (part->end_vertex()) standalone.insert(part->end_vertex());
177 }
178 ATH_MSG_INFO("Heuristics: found " << semi_disconnected.size() << " semi-disconnected particles. Momentum sum is " << sum << " Standalone vertices " << standalone.size());
179 bool standalonevertex = (standalone.size() == 1 && (*standalone.begin())->particles_in().size() + (*standalone.begin())->particles_out().size() == semi_disconnected.size());
181 if (! standalonevertex && no_endv && no_prov && ( no_endv + no_prov == semi_disconnected.size() )) {
182 if (std::abs(sum.px()) < 1e-2 && std::abs(sum.py()) < 1e-2 && std::abs(sum.pz()) < 1e-2 ) {
183 ATH_MSG_INFO("Try " << no_endv << "->" << no_prov << " splitting/merging.");
184 auto v = HepMC::newGenVertexPtr();
185 for (auto part : semi_disconnected) {
186 if (!part->production_vertex() || part->production_vertex()->id() == 0) v->add_particle_out(std::move(part));
187 }
188 for (auto part : semi_disconnected) {
189 if (!part->end_vertex()) v->add_particle_in(std::move(part));
190 }
191 evt->add_vertex(std::move(v));
192 }
193 }
194 }
195
200 for (auto part: decay_loop_particles) {
202 auto vend = part->end_vertex();
203 auto vprod = part->production_vertex();
204 if (!vprod || !vend) continue;
205 bool loop_in_decay = true;
208 auto sisters = vend->particles_in();
209 for (auto sister: sisters) {
210 if (vprod != sister->production_vertex()) loop_in_decay = false;
211 }
212 if (!loop_in_decay) continue;
213
215 auto daughters = vend->particles_out();
216 for (auto p : daughters) vprod->add_particle_out(std::move(p));
217 for (auto sister : sisters) {
218 vprod->remove_particle_out(sister);
219 vend->remove_particle_in(sister);
220 evt->remove_particle(std::move(sister));
221 }
222 evt->remove_vertex(std::move(vend));
223
224 }
225
226 // Event particle content cleaning -- remove "bad" structures
227 std::vector<HepMC::GenParticlePtr> toremove;
228 for (auto ip: evt->particles()) {
229 // Skip this particle if (somehow) its pointer is null
230 if (!ip) continue;
231 m_totalSeen += 1;
232 // Flag to declare if a particle should be removed
233 bool bad_particle = false;
234 // Check for loops
235 if ( m_killLoops && isSimpleLoop(ip) ) {
236 bad_particle = true;
237 m_loopKilled += 1;
238 ATH_MSG_DEBUG( "Found a looper : " );
239 if ( msgLvl( MSG::DEBUG ) ) HepMC::Print::line(ip);
240 }
241 // Check on PDG ID 0
242 if ( m_killPDG0 && isPID0(ip) ) {
243 bad_particle = true;
244 m_pdg0Killed += 1;
245 ATH_MSG_DEBUG( "Found PDG ID 0 : " );
246 if ( msgLvl( MSG::DEBUG ) )HepMC::Print::line(ip);
247 }
248 // Clean decays
249 int abs_pdg_id = std::abs(ip->pdg_id());
250 bool is_decayed_weak_boson = ( abs_pdg_id == 23 || abs_pdg_id == 24 || abs_pdg_id == 25 ) && ip->end_vertex();
251 if ( m_cleanDecays && isNonTransportableInDecayChain(ip) && !is_decayed_weak_boson ) {
252 bad_particle = true;
253 m_decayCleaned += 1;
254 ATH_MSG_DEBUG( "Found a bad particle in a decay chain : " );
255 if ( msgLvl( MSG::DEBUG ) ) HepMC::Print::line(ip);
256 }
257 // Only add to the toremove vector once, even if multiple tests match
258 if (bad_particle) toremove.push_back(std::move(ip));
259 }
260
261 // Properties before cleaning
262 const int num_particles_orig = evt->particles().size();
263
264 // Do the cleaning
265 if (!toremove.empty()) {
266 ATH_MSG_DEBUG("Cleaning event record of " << toremove.size() << " bad particles");
267 for (auto part: toremove) evt->remove_particle(std::move(part));
268 }
269
271 int purged=0;
272 do {
273 purged=0;
274 const std::vector <HepMC::GenParticlePtr> allParticles=evt->particles();
275 for(auto p : allParticles) {
276 HepMC::ConstGenVertexPtr end_v=p->end_vertex();
277 if(p->status() == 2 && !end_v) {
278 evt->remove_particle(std::move(p));
279 ++purged;
281 }
282 }
283 }
284 while (purged>0);
285 }
286
287 const int num_particles_filt = evt->particles().size();
288
289 if(num_particles_orig!=num_particles_filt) {
290 // Write out the change in the number of particles
291 ATH_MSG_INFO("Particles filtered: " << num_particles_orig << " -> " << num_particles_filt);
292 }
293
294 } // End of the loop over events in the MC event collection
295 return StatusCode::SUCCESS;
296}
297
298
299StatusCode FixHepMC::finalize() {
300 if (m_killLoops ) ATH_MSG_INFO( "Removed " << m_loopKilled << " of " << m_totalSeen << " particles because of loops." );
301 if (m_killPDG0 ) ATH_MSG_INFO( "Removed " << m_pdg0Killed << " of " << m_totalSeen << " particles because of PDG ID 0." );
302 if (m_cleanDecays) ATH_MSG_INFO( "Removed " << m_decayCleaned << " of " << m_totalSeen << " particles while cleaning decay chains." );
303 if(m_purgeUnstableWithoutEndVtx) ATH_MSG_INFO( "Removed " << m_unstablePurged << " of " << m_totalSeen << " unstable particles because they had no decay vertex." );
304 if (!m_pidmap.empty()) {
305 ATH_MSG_INFO( "Replaced " << m_replacedPIDs << " PIDs of particles." );
306 for (const auto& p: m_replacedpid_counts) ATH_MSG_INFO( "Replaced " << p.first << " PIDs " << p.second << "times." );
307 }
308 return StatusCode::SUCCESS;
309}
310
311
313
314
315// Identify PDG ID = 0 particles
317 return p->pdg_id() == 0;
318}
319
320// Identify the particles from
321bool FixHepMC::fromDecay(const HepMC::ConstGenParticlePtr& p, std::shared_ptr<std::set<int> >& storage) const {
322 if (!p) return false;
323 auto v=p->production_vertex();
324 if (!v) return false;
325 for ( const auto& anc: v->particles_in()) {
326 if (MC::isDecayed(anc) && (MC::isTau(anc->pdg_id()) || MC::isHadron(anc->pdg_id()))) return true;
327 }
328 if (storage->find(p->id()) != storage->end()) return false;
329 storage->insert(p->id());
330 for ( const auto& anc: v->particles_in()) {
331 if (fromDecay(anc, storage)) return true;
332 }
333 return false;
334}
335
336// Identify non-transportable stuff _after_ hadronisation
338 auto storage = std::make_shared<std::set<int>>();
339 return !MC::isTransportable(p->pdg_id()) && fromDecay(p, storage);
340}
341
342// Identify internal "loop" particles
344 return (p->production_vertex() == p->end_vertex() && p->end_vertex() != nullptr);
345}
346
348
349#endif
350
#define ATH_MSG_INFO(x)
#define ATH_MSG_WARNING(x)
#define ATH_MSG_DEBUG(x)
ATLAS-specific HepMC functions.
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T, V, H > &t)
bool msgLvl(const MSG::Level lvl) const
DataModel_detail::const_iterator< DataVector > const_iterator
Definition DataVector.h:838
bool isPID0(const HepMC::ConstGenParticlePtr &p) const
Definition FixHepMC.cxx:316
bool m_setHasCycles
Definition FixHepMC.h:59
FixHepMC(const std::string &name, ISvcLocator *pSvcLocator)
Definition FixHepMC.cxx:15
bool m_purgeUnstableWithoutEndVtx
Definition FixHepMC.h:54
long m_replacedPIDs
Definition FixHepMC.h:69
bool m_killPDG0
Definition FixHepMC.h:52
StatusCode execute(const EventContext &ctx)
Execute method.
Definition FixHepMC.cxx:36
bool isNonTransportableInDecayChain(const HepMC::ConstGenParticlePtr &p) const
Definition FixHepMC.cxx:337
std::string m_forced_momentum
Definition FixHepMC.h:56
bool m_unitsFix
Definition FixHepMC.h:58
bool fromDecay(const HepMC::ConstGenParticlePtr &p, std::shared_ptr< std::set< int > > &storage) const
Definition FixHepMC.cxx:321
long m_totalSeen
Definition FixHepMC.h:68
StatusCode finalize()
Definition FixHepMC.cxx:299
long m_pdg0Killed
Definition FixHepMC.h:65
long m_decayCleaned
Definition FixHepMC.h:66
bool m_ignoreSemiDisconnected
Definition FixHepMC.h:55
bool m_cleanDecays
Definition FixHepMC.h:53
bool m_killLoops
Definition FixHepMC.h:51
long m_unstablePurged
Definition FixHepMC.h:67
std::map< int, int > m_replacedpid_counts
map of counters of replacements.
Definition FixHepMC.h:73
long m_loopKilled
Definition FixHepMC.h:64
std::map< int, int > m_pidmap
map of pids to change.
Definition FixHepMC.h:72
MC::Loops< HepMC::GenEvent, HepMC::ConstGenParticlePtr, HepMC::ConstGenVertexPtr > m_looper
member to detect loops
Definition FixHepMC.h:76
bool isSimpleLoop(const HepMC::ConstGenParticlePtr &p) const
Definition FixHepMC.cxx:343
std::string m_forced_length
Definition FixHepMC.h:57
GenBase(const std::string &name, ISvcLocator *pSvcLocator)
Constructor.
Definition GenBase.cxx:11
const std::string cycles
HepMC3::FourVector FourVector
void set_signal_process_vertex(GenEvent *e, T &v)
Definition GenEvent.h:591
ConstGenVertexPtr signal_process_vertex(const GenEvent *e)
Definition GenEvent.h:597
HepMC3::GenParticlePtr GenParticlePtr
Definition GenParticle.h:19
GenVertexPtr newGenVertexPtr(const HepMC3::FourVector &pos=HepMC3::FourVector::ZERO_VECTOR(), const int i=0)
Definition GenVertex.h:25
HepMC3::ConstGenParticlePtr ConstGenParticlePtr
Definition GenParticle.h:20
HepMC3::ConstGenVertexPtr ConstGenVertexPtr
Definition GenVertex.h:24
HepMC3::GenEvent GenEvent
Definition GenEvent.h:39
void MeVToGeV(HepMC::GenEvent *evt)
bool isTransportable(const T &p)
bool isDecayed(const T &p)
Identify if the particle decayed.
bool isHadron(const T &p)
bool isTau(const T &p)