ATLAS Offline Software
TTbarMassFilter.cxx
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
3 */
4 
7 #include <algorithm>
8 
9 TTbarMassFilter::TTbarMassFilter(const std::string& name, ISvcLocator* pSvcLocator)
10  : GenFilter(name, pSvcLocator)
11 {
12  declareProperty("TopPairMassLowThreshold", m_massRangeLowThr = 300000.);
13  declareProperty("TopPairMassHighThreshold", m_massRangeHighThr = 500000.);
14 }
15 
16 
18  int top = 0;
19  int topbar = 0;
20  bool isLastTop = false;
21  bool isFirstTop = false;
22  double topPairInvariantMass = 0.;
23  std::vector<HepMC::ConstGenParticlePtr> tops;
24  std::vector<HepMC::ConstGenVertexPtr> top_vtxs;
25 
26  for (McEventCollection::const_iterator itr = events()->begin(); itr!=events()->end(); ++itr) {
27  const HepMC::GenEvent* genEvt = (*itr);
28 #ifdef HEPMC3
29  for (const auto& mcpart: *genEvt) {
30  // Reset the flag. it become true if the top with a final ('last') status is found
31  isLastTop = false;
32  // Reset the flag. becomes true if the top with the initial ('first') status is found
33  isFirstTop = false;
34 
35  // Work only with tops
36  if (std::abs(mcpart->pdg_id()) == 6) {
37  // Assume that this is the 'last' top
38  isLastTop=true;
39  auto decayVtx = mcpart->end_vertex();
40  // Unusual case...
41  if (!decayVtx) {
42  ATH_MSG_WARNING("top particle" << mcpart << " has no valid decay vertex. ");
43  ATH_MSG_WARNING("It looks like a Pythia history particle. Skip this particle ");
44  continue;
45  }
46 
47  // Find out whether this is the top particle with final statuscode, so, just before its decay.
49  for (const auto& child_mcpart: decayVtx->particles_out() ) {
50  if (std::abs(child_mcpart->pdg_id()) == 6) {
51  // This is not a 'last' top: break the loop over the children, and do nothing with this top particle
52  isLastTop = false;
53  break;
54  }
55  }
56 
57  // Store the 'last' top
58  if (isLastTop) {
59  ATH_MSG_DEBUG("Top particle " << mcpart << " is found and stored");
60  tops.push_back(mcpart);
61  }
62 
63  // Find and store production vertices for the 'last' tops. This will help to easily couple top and anti-top
64  // pairs later if there are 2 pairs of them. It's a very rare case but needs to be properly handled.
65  if (isLastTop) {
66  // Investigate whether this top particle is the 'first' one.
67  isFirstTop = false;
68  // Retrieve the production vertex of the current 'last' top particle
69  auto prodVtx = mcpart->production_vertex();
70  if (!prodVtx) {
71  ATH_MSG_WARNING("Top particle " << mcpart << " has no valid production vertex");
72  //save null pointer for consistency
73  top_vtxs.emplace_back(nullptr);
74  } else {
75  // Loop until the 'first' top particle production vertex is not reached
76  while (!isFirstTop && prodVtx) {
77  // Loop over the top mother particles
78  for (const auto& mother_mcpart: prodVtx->particles_in()) {
79  // One of the mother particles is still top quark. needed to go up in the hierarchy
80  if (mother_mcpart->pdg_id() == 6) {
81  isFirstTop = false;
82  prodVtx = mother_mcpart->production_vertex();
83  if (!prodVtx) {
84  ATH_MSG_WARNING("mother particle is still a top " << mcpart << ", but has no valid production vertex");
85  top_vtxs.emplace_back(nullptr);
86  }
87  break;
88  } else {
89  // The current (in loop over the mother particles) mother particle is not a top.
90  // maybe the found top particle is the initial one? set the flag true. it will be
91  // set false if one of the other mother particles is top quark.
92  isFirstTop = true;
93  }
94  }
95  }
96 
97  //Store the production vertex for the given top particle. it's a production vertex
98  // of the initial ('first') top particle for the current final ('last') top.
99  if (isFirstTop) {
100  top_vtxs.push_back(prodVtx);
101  }
102  }
103  }
104 
105  // Investigate how many top and anti-tops are found and make a decision
106  // to continue or stop looping over GenParticles in the event
107  if (isLastTop) {
108  // One more 'last' top particle was stored into the vector --> increment the number of top or anti-top particles.
109  if (tops[tops.size()-1]->pdg_id() == 6) top++; else topbar++;
110 
111  // One top and one ati-top 'last' particles are found. Stop looping
112  // over the particles
113  if (top==1 && topbar==1) break;
114 
115  // This should not happen. But depends on a style how a given
116  // generator records the particles. In very rare cases there are more
117  // than 1 top-pairs created. If occasionally the same signed tops from
118  // the different couples are ordered first in the generator record,
119  // then the situation below is possible
120  if (top > 1 || topbar > 1) ATH_MSG_INFO("More than one top-pair exist in the event.");
121 
122  // Both top-pairs are found. don't expect more than that. So, it's
123  // time to break the loop over particles. this fairly assumes that
124  // there are no more top-pairs
125  if (top==2 && topbar==2) break;
126  }
127  }
128  }
129 #else
130  HepMC::GenEvent::particle_const_iterator pitr = genEvt->particles_begin();
131  for (; pitr!=genEvt->particles_end(); ++pitr) {
132  // Reset the flag. it become true if the top with a final ('last') status is found
133  isLastTop = false;
134  // Reset the flag. becomes true if the top with the initial ('first') status is found
135  isFirstTop = false;
136  HepMC::GenParticle* mcpart = (*pitr);
137 
138  // Work only with tops
139  if (std::abs(mcpart->pdg_id()) == 6) {
140  // Assume that this is the 'last' top
141  isLastTop=true;
142  HepMC::GenVertex* decayVtx = mcpart->end_vertex();
143  // Unusual case...
144  if (!decayVtx) {
145  ATH_MSG_WARNING("top particle " << mcpart << " has no valid decay vertex. ");
146  ATH_MSG_WARNING("It looks like a Pythia history particle. Skip this particle ");
147  continue;
148  }
149 
150  // Find out whether this is the top particle with final statuscode, so, just before its decay.
152 
153  HepMC::GenVertex::particles_out_const_iterator child_mcpartItr = decayVtx->particles_out_const_begin();
154  HepMC::GenVertex::particles_out_const_iterator child_mcpartItrE = decayVtx->particles_out_const_end();
155  for (; child_mcpartItr!=child_mcpartItrE; ++child_mcpartItr) {
156  HepMC::GenParticle* child_mcpart = (*child_mcpartItr);
157  if (std::abs(child_mcpart->pdg_id()) == 6) {
158  // This is not a 'last' top: break the loop over the children, and do nothing with this top particle
159  isLastTop = false;
160  break;
161  }
162  }
163 
164  // Store the 'last' top
165  if (isLastTop) {
166  ATH_MSG_DEBUG("Top particle " << mcpart << " is found and stored");
167  tops.push_back(mcpart);
168  }
169 
170  // Find and store production vertices for the 'last' tops. This will help to easily couple top and anti-top
171  // pairs later if there are 2 pairs of them. It's a very rare case but needs to be properly handled.
172  if (isLastTop) {
173  // Investigate whether this top particle is the 'first' one.
174  isFirstTop = false;
175  // Retrieve the production vertex of the current 'last' top particle
176  HepMC::GenVertex* prodVtx = mcpart->production_vertex();
177  if (!prodVtx) {
178  ATH_MSG_WARNING("Top particle " << mcpart << " has no valid production vertex");
179  //save null pointer for consistency
180  top_vtxs.push_back(NULL);
181  } else {
182  // Loop until the 'first' top particle production vertex is not reached
183  while (!isFirstTop && prodVtx) {
184  // Loop over the top mother particles
185  HepMC::GenVertex::particles_in_const_iterator mother_mcpartItr = prodVtx->particles_in_const_begin();
186  HepMC::GenVertex::particles_in_const_iterator mother_mcpartItrE = prodVtx->particles_in_const_end();
187  for (; mother_mcpartItr != mother_mcpartItrE; ++mother_mcpartItr) {
188  HepMC::GenParticle* mother_mcpart = (*mother_mcpartItr);
189  // One of the mother particles is still top quark. needed to go up in the hierarchy
190  if (mother_mcpart->pdg_id() == 6) {
191  isFirstTop = false;
192  prodVtx = mother_mcpart->production_vertex();
193  if (!prodVtx) {
194  ATH_MSG_WARNING("mother particle is still a top " << mcpart << ", but has no valid production vertex");
195  top_vtxs.push_back(NULL);
196  }
197  break;
198  } else {
199  // The current (in loop over the mother particles) mother particle is not a top.
200  // maybe the found top particle is the initial one? set the flag true. it will be
201  // set false if one of the other mother particles is top quark.
202  isFirstTop = true;
203  }
204  }
205  }
206 
207  //Store the production vertex for the given top particle. it's a production vertex
208  // of the initial ('first') top particle for the current final ('last') top.
209  if (isFirstTop) {
210  top_vtxs.push_back(prodVtx);
211  }
212  }
213  }
214 
215  // Investigate how many top and anti-tops are found and make a decision
216  // to continue or stop looping over GenParticles in the event
217  if (isLastTop) {
218  // One more 'last' top particle was stored into the vector --> increment the number of top or anti-top particles.
219  if (tops[tops.size()-1]->pdg_id() == 6) top++; else topbar++;
220 
221  // One top and one ati-top 'last' particles are found. Stop looping
222  // over the particles
223  if (top==1 && topbar==1) break;
224 
225  // This should not happen. But depends on a style how a given
226  // generator records the particles. In very rare cases there are more
227  // than 1 top-pairs created. If occasionally the same signed tops from
228  // the different couples are ordered first in the generator record,
229  // then the situation below is possible
230  if (top > 1 || topbar > 1) ATH_MSG_INFO("More than one top-pair exist in the event.");
231 
232  // Both top-pairs are found. don't expect more than that. So, it's
233  // time to break the loop over particles. this fairly assumes that
234  // there are no more top-pairs
235  if (top==2 && topbar==2) break;
236  }
237 
238  }
239  }
240 #endif
241  }
242 
243  // Main case: there is one top-pair in the event
244  if (tops.size()==2) {
245  HepMC::FourVector topSumMomentum(0.,0.,0.,0.);
246  topSumMomentum.set(tops[0]->momentum().px()+tops[1]->momentum().px(),
247  tops[0]->momentum().py()+tops[1]->momentum().py(),
248  tops[0]->momentum().pz()+tops[1]->momentum().pz(),
249  tops[0]->momentum().e() +tops[1]->momentum().e());
250  topPairInvariantMass = topSumMomentum.m();
251  }
252 
253  // Very rare case: there are 2 top-pairs found in the event.
254  // One pair comes from the hard scattering and another from additional gluon
255  // splitting. Using particle production vertices one needs to correctly couple
256  // the tops to anti-tops
257  else if (tops.size() == 4 && top_vtxs.size() == 4) {
258  HepMC::FourVector topSumMomentum_1(0.,0.,0.,0.);
259  HepMC::FourVector topSumMomentum_2(0.,0.,0.,0.);
260  double topPairInvMass_1=0.;
261  double topPairInvMass_2=0.;
262 
263  // There must be only two different vertecies out of the four
264  auto prodVtx = top_vtxs[0];
265  int top_11 = 0;
266  int top_12 = -1;
267  int top_21 = -1;
268  int top_22 = -1;
269  for (size_t i = 1; i < top_vtxs.size(); ++i) {
270  if (top_vtxs[i] == prodVtx) top_12=i;
271  else {
272  if (top_21 < 0) top_21 = i;
273  else top_22 = i;
274  }
275  }
276 
277  // Check that the first top particle shares a production vertex with another top
278  if (top_12 < 0) {
279  ATH_MSG_ERROR("First top particle doesn't share the production vertex to any other top particles. Event failed the filter");
280  setFilterPassed(false);
281  return StatusCode::SUCCESS;
282  }
283  if ((top_21 < 0) or (top_22 < 0)) {
284  ATH_MSG_ERROR("Indexing error. Event failed the filter");
285  setFilterPassed(false);
286  return StatusCode::SUCCESS;
287  }
288 
289  // Check that the second top-pair really has the same production vertex
290  if (top_vtxs[top_21] != top_vtxs[top_22]) {
291  ATH_MSG_ERROR("Production vertex for the second top-pair particles is not the same. Event failed the filter");
292  setFilterPassed(false);
293  return StatusCode::SUCCESS;
294  }
295 
296  // First couple. check the coupled top quark's signs. They must be opposite.
297  if (tops[top_11]->pdg_id() == tops[top_12]->pdg_id()) ATH_MSG_ERROR("first top-pair particles have the same charge");
298  else {
299  // The right coupling seems to be achieved. Calculate the invariant mass of the couple
300  topSumMomentum_1.set(tops[top_11]->momentum().px()+tops[top_12]->momentum().px(),
301  tops[top_11]->momentum().py()+tops[top_12]->momentum().py(),
302  tops[top_11]->momentum().pz()+tops[top_12]->momentum().pz(),
303  tops[top_11]->momentum().e() +tops[top_12]->momentum().e());
304  topPairInvMass_1 = topSumMomentum_1.m();
305  }
306 
307  // Second couple. check the coupled top quark's signs. They must be opposite.
308  if (tops[top_21]->pdg_id() == tops[top_21]->pdg_id()) ATH_MSG_ERROR("second top-pair particles have the same charge");
309  else {
310  // The right coupling seems to be achieved. Calculate the invariant mass of the couple
311  topSumMomentum_2.set(tops[top_21]->momentum().px()+tops[top_22]->momentum().px(),
312  tops[top_21]->momentum().py()+tops[top_22]->momentum().py(),
313  tops[top_21]->momentum().pz()+tops[top_22]->momentum().pz(),
314  tops[top_21]->momentum().e() +tops[top_22]->momentum().e());
315  topPairInvMass_2 = topSumMomentum_2.m();
316  }
317 
318  // Choose the top-pair with the largest invariant mass
319  topPairInvariantMass = std::max(topPairInvMass_1, topPairInvMass_2);
320  }
321 
322  // Something wrong with the number of tops in the event or with the stored vertices....
323  else ATH_MSG_ERROR("Size of the collected final top particles vector " << tops.size() <<
324  " doesn't match to the size of there production vertices vector size " << top_vtxs.size() <<
325  ". Event fails the filter");
326 
327  // Filter is expected to run on the ttbar sample. This means there should be 1
328  // or very rarely 2 top-pairs in the truth. If this is not the case then
329  // something wrong must have a place in this event or in the filter and has to
330  // be investigated.
331  if (tops.size() != 2 && tops.size() != 4) {
332  ATH_MSG_WARNING("1 or 2 top-pairs are expected in the event. But there are " << tops.size() << " final-status top particles.");
333  }
334 
335  // Proper top-pair invariant mass is calculated. Check if it is in the range of the interest and accept the event if so.
336  setFilterPassed(topPairInvariantMass > m_massRangeLowThr && topPairInvariantMass <= m_massRangeHighThr);
337  ATH_MSG_DEBUG("The top-pair invariant mass is " << topPairInvariantMass << " CLHEP::MeV");
338  return StatusCode::SUCCESS;
339 }
TTbarMassFilter::filterEvent
virtual StatusCode filterEvent()
Definition: TTbarMassFilter.cxx:17
DataModel_detail::const_iterator
Const iterator class for DataVector/DataList.
Definition: DVLIterator.h:82
test_pyathena.px
px
Definition: test_pyathena.py:18
top
TopConfig A simple configuration that is NOT a singleton.
Definition: AnalysisTrackingHelper.cxx:58
max
#define max(a, b)
Definition: cfImp.cxx:41
TTbarMassFilter::TTbarMassFilter
TTbarMassFilter(const std::string &name, ISvcLocator *pSvcLocator)
Definition: TTbarMassFilter.cxx:9
ATH_MSG_INFO
#define ATH_MSG_INFO(x)
Definition: AthMsgStreamMacros.h:31
AthCommonDataStore< AthCommonMsg< Algorithm > >::declareProperty
Gaudi::Details::PropertyBase & declareProperty(Gaudi::Property< T > &t)
Definition: AthCommonDataStore.h:145
PlotCalibFromCool.begin
begin
Definition: PlotCalibFromCool.py:94
TTbarMassFilter::m_massRangeLowThr
double m_massRangeLowThr
Definition: TTbarMassFilter.h:22
python.DataFormatRates.events
events
Definition: DataFormatRates.py:105
GenFilter
Base class for event generator filtering modules.
Definition: GenFilter.h:30
SimpleVector.h
ATH_MSG_ERROR
#define ATH_MSG_ERROR(x)
Definition: AthMsgStreamMacros.h:33
ParticleGun_EoverP_Config.momentum
momentum
Definition: ParticleGun_EoverP_Config.py:63
lumiFormat.i
int i
Definition: lumiFormat.py:92
EL::StatusCode
::StatusCode StatusCode
StatusCode definition for legacy code.
Definition: PhysicsAnalysis/D3PDTools/EventLoop/EventLoop/StatusCode.h:22
ATH_MSG_DEBUG
#define ATH_MSG_DEBUG(x)
Definition: AthMsgStreamMacros.h:29
Amg::pz
@ pz
Definition: GeoPrimitives.h:40
Amg::py
@ py
Definition: GeoPrimitives.h:39
name
std::string name
Definition: Control/AthContainers/Root/debug.cxx:195
DiTauMassTools::MaxHistStrategyV2::e
e
Definition: PhysicsAnalysis/TauID/DiTauMassTools/DiTauMassTools/HelperFunctions.h:26
ATH_MSG_WARNING
#define ATH_MSG_WARNING(x)
Definition: AthMsgStreamMacros.h:32
TTbarMassFilter::m_massRangeHighThr
double m_massRangeHighThr
Definition: TTbarMassFilter.h:23
GenParticle
@ GenParticle
Definition: TruthClasses.h:30
TTbarMassFilter.h