ATLAS Offline Software
LHETools.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2022 CERN for the benefit of the ATLAS collaboration
2 
3 # This code is almost identical to ColourFlow_LHE.py by Fabian Wilk
4 # It provides an interface to reading, writing, and possibly modify events in lhe files
5 
6 from AthenaCommon import Logging
7 LHEToolsLog = Logging.logging.getLogger('MCJobOptionUtils/LHETools.py')
8 
9 import os
10 import subprocess
11 import glob
12 import collections
13 import re
14 
15 
16 class Record(object):
17  def __init__(self, **kwargs):
18  self.invalid = not (set(kwargs.keys()) == set(self.FIELD_NAMES))
19  self.__dict__.update({field: self.FIELD_TYPES[field](value)
20  for field, value in kwargs.items()})
21 
22  @classmethod
23  def from_string(cls, string):
24  if len(cls.FIELD_NAMES) != len(string.split()):
25  LHEToolsLog.warning("Inconsistent number of fields in lhe file line:")
26  LHEToolsLog.warning(string)
27  return cls()
28  try:
29  return cls(**dict(zip(cls.FIELD_NAMES, string.split())))
30  except:
31  LHEToolsLog.warning("Impossible to interpret lhe file line:")
32  LHEToolsLog.warning(string)
33  return cls()
34 
35  def to_string(self):
36  return self.FMT.format(**{field: getattr(self, field) for field in self.FIELD_NAMES}) if not self.invalid else ""
37 
38 
40  #: These are the allowed field names for each LHE Event Info Record.
41  FIELD_NAMES = ['nparticles', 'id', 'weight', 'scale', 'A_QED', 'A_QCD']
42 
43  #: These are the python types of each field.
44  FIELD_TYPES = {
45  'nparticles': int,
46  'id': int,
47  'weight': float,
48  'scale': float,
49  'A_QCD': float,
50  'A_QED': float
51  }
52 
53  #: This is the formatting string.
54  FMT = "{nparticles:7d} {id:6d} {weight: .5E} {scale: .5E} {A_QED: .5E} {A_QCD: .5E}"
55 
56 
58  #: These are the allowed field names for each LHE Particle Record.
59  FIELD_NAMES = [
60  'pdg_id', 'status', 'mother0', 'mother1', 'colour0', 'colour1', 'px',
61  'py', 'pz', 'e', 'm', 'lifetime', 'spin'
62  ]
63 
64  #: These are the python types of each field.
65  FIELD_TYPES = {
66  'pdg_id': int,
67  'status': int,
68  'mother0': int,
69  'mother1': int,
70  'colour0': int,
71  'colour1': int,
72  'px': float,
73  'py': float,
74  'pz': float,
75  'e': float,
76  'm': float,
77  'lifetime': float,
78  'spin': float
79  }
80 
81  #: This is the formatting string.
82  FMT = "{pdg_id:8d} {status:5d} {mother0:5d} {mother1:5d} {colour0:5d} {colour1:5d} {px: .9E} {py: .9E} {pz: .9E} {e: .9E} {m: .9E} {lifetime: .5E} {spin: .3E}"
83 
84  def mothers(self):
85  return (self.event.particles[mo]
86  for mo in xrange(self.mother0 - 1, self.mother1))
87 
88  def daughters(self):
89  self_id = self.index() + 1
90  for p in self.event:
91  if p.mother0 <= self_id <= p.mother1:
92  yield p
93 
94  def index(self):
95  return self.event.particles.index(self)
96 
97 
98 class Event(object):
99  def __init__(self, info, particles, extra_lines=None, spurious_event_markup=False):
100  self.info = info
101  self.particles = particles
102  self.extra_lines = extra_lines or []
103  self.spurious_event_markup = spurious_event_markup
104 
105  for p in self.particles:
106  p.event = self
107 
108  @classmethod
109  def from_lines(cls, lines):
110  info = None
111  particles = []
112  extra_lines = []
113  spurious_event_markup = False
114  event_markup = re.compile('<event>')
115 
116  for i, datum in enumerate(lines):
117  # there shouldn't be a <event> markup in the middle of the event record; if yes, we shouldn't keep the event
118  if event_markup.match(datum):
119  LHEToolsLog.warning("Spurious <event> markup was found in the middle of an event record. Probably the result of one event being incompletely written.")
120  spurious_event_markup = True
121 
122  # The first line is the info block.
123  if not info:
124  info = EventInfo.from_string(datum)
125  if info.invalid:
126  LHEToolsLog.warning("Could not retrieve EventInfo from input lhe file")
127  continue
128 
129  # The lines [1, N_PARTICLE] define particles.
130  if 1 <= i <= info.nparticles:
131  p = Particle.from_string(datum)
132  if p.invalid:
133  LHEToolsLog.warning("Could not retrieve Particle from input lhe file")
134  particles.append(p)
135  continue
136 
137  # All subsequent lines are additional configuration which is
138  # retrieved but isn't parsed.
139  extra_lines.append(datum)
140 
141  return cls(info=info,
142  particles=particles,
143  extra_lines=extra_lines,
144  spurious_event_markup=spurious_event_markup)
145 
146  def is_buggy(self):
147  return self.info.invalid or self.spurious_event_markup or any(part.invalid for part in self.particles)
148 
149 
150  def to_string(self):
151  lines = [self.info.to_string() + '\n']
152  for p in self.particles:
153  lines.append(p.to_string() + '\n')
154  lines.extend(self.extra_lines)
155 
156  return ''.join(lines)
157 
158  def __len__(self):
159  return len(self.particles)
160 
161  def __getitem__(self, index):
162  if not isinstance(index, (int)):
163  LHEToolsLog.error("Index must be integer")
164  raise TypeError
165 
166  return self.particles[index]
167 
168  def __delitem__(self, index):
169  if not isinstance(index, (int)):
170  LHEToolsLog.error("Index must be integer")
171  raise TypeError
172 
173  # When removing a particle at a location ``index`` -- which corresponds
174  # to LHE id ``i_lhe`` -- we need to correct two things:
175  #
176  # 1.) Any particle that had ``i_lhe`` as mother, needs to get the
177  # mother of the particle to be removed as mother.
178  #
179  # - If both have exactly one mother, perform the replacement
180  # verbatim.
181  # - If the particle that is deleted has two mothers but the
182  # child particle has only one (the particle to be deleted),
183  # set the two mother reference of the child particle to the
184  # ones of the particle to be deleted.
185  # - If the particle that is deleted has one mother but the
186  # child particle has two, abort.
187  #
188  # 2.) Since we've changed the LHE indexing, any particle that had a
189  # mother reference ``mother > i_lhe`` must get a new mother
190  # reference ``mother - 1``
191  p_rm = self[index]
192  i_lhe = index + 1
193 
194  mo0, mo1 = p_rm.mother0, p_rm.mother1
195  for p in self.particles:
196  if p is p_rm:
197  continue
198 
199  if p.mother0 == i_lhe or p.mother1 == i_lhe:
200  if p.mother0 != p.mother1:
201  LHEToolsLog.error("Can not delete particle whose child has also other parents")
202  raise RuntimeError
203 
204  p.mother0 = mo0
205  p.mother1 = mo1
206 
207  # Remove the particle.
208  del self.particles[index]
209 
210  for p in self.particles:
211  if p.mother0 > i_lhe:
212  p.mother0 -= 1
213  if p.mother1 > i_lhe:
214  p.mother1 -= 1
215 
216  # Decrement the particle counter.
217  self.info.nparticles -= 1
218 
219 
220 def FindDuplicates(inFileName=None, crashMode=False, printDuplicated=False):
221  if inFileName == None:
222  LHEToolsLog.error("No input file for Duplicate finder")
223  raise RuntimeError
224 
225  if printDuplicated:
226  LHEToolsLog.info("Possible duplicated events in lhe file will be printed out if any")
227 
228  if printDuplicated:
229  LHEToolsLog.info("Possible duplicated events in lhe file will be printed out if any")
230 
231  # writing awk script to be executed on the input lhe file
232  fScript = open("finder.awk", 'w')
233  fScript.write(
234 """BEGIN{RS="<event>"; ORS=""; FS="</event>"; OFS=""; nDuplicates=0}
235 {
236 if (NR==1) {
237  print $1 > \""""+inFileName+"-new"+"""\"
238  ORS=\"\\n\"
239 }
240 else {
241  c[$1]++;
242  if (c[$1]==1) {print \"<event>\"$1\"</event>\" > \""""+inFileName+"-new"+"""\" }
243  else {print \"<event>\"$1\"</event>\" > \""""+inFileName+"-duplicates"+"""\"; nDuplicates++}
244 }
245 }
246 END{print \"</LesHouchesEvents>\\n\" > \""""+inFileName+"-new"+"""\"; print nDuplicates}""")
247  fScript.close()
248 
249  command = 'awk -f finder.awk '+inFileName+" > nDuplicates.txt"
250  try:
251  os.system(command)
252  except:
253  LHEToolsLog.error("Impossible to execute awk script")
254  raise RuntimeError
255 
256  # read the number of duplicate events from temp file
257  fnDuplicates = open("nDuplicates.txt", 'r')
258  s = fnDuplicates.read().rstrip()
259  fnDuplicates.close()
260 
261  if int(s) >0:
262  fDuplicates = open(inFileName+"-duplicates", 'r')
263  # some usefull prints
264  LHEToolsLog.info(s+" duplicate events were found in "+inFileName)
265  if printDuplicated:
266  LHEToolsLog.info("Possible duplicated events in lhe file will be printed out if any")
267  for line in fDuplicates:
268  LHEToolsLog.info(line)
269  fDuplicates.close() # don't need it anymore
270  # either crash of replace file
271  if(crashMode):
272  LHEToolsLog.error("Duplicate events found. To avoid this crash, use crashmode=False. Exiting...")
273  raise RuntimeError
274  else:
275  LHEToolsLog.info("Duplicate events found. Replacing input file "+inFileName+" by fixed file "+inFileName+"-new")
276  LHEToolsLog.info("Old file saved in "+inFileName+"-old")
277  os.rename(inFileName,inFileName+"-old")
278  os.rename(inFileName+"-new",inFileName)
279  else:
280  LHEToolsLog.info("No duplicate events was found in "+inFileName)
281  os.remove(inFileName+"-new") # removing the useless "-new" file
xrange
void xrange(TH1 *h, bool symmetric)
Definition: computils.cxx:516
LHETools.Record.invalid
invalid
Definition: LHETools.py:18
LHETools.Event.to_string
def to_string(self)
Definition: LHETools.py:150
LHETools.Event.__len__
def __len__(self)
Definition: LHETools.py:158
vtune_athena.format
format
Definition: vtune_athena.py:14
LHETools.Event.__init__
def __init__(self, info, particles, extra_lines=None, spurious_event_markup=False)
Definition: LHETools.py:99
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
LHETools.Event.__getitem__
def __getitem__(self, index)
Definition: LHETools.py:161
LHETools.Event.extra_lines
extra_lines
Definition: LHETools.py:102
LHETools.Event
Definition: LHETools.py:98
CaloClusterListBadChannel.cls
cls
Definition: CaloClusterListBadChannel.py:8
LHETools.Particle.index
def index(self)
Definition: LHETools.py:94
LHETools.Event.spurious_event_markup
spurious_event_markup
Definition: LHETools.py:103
LHETools.Event.is_buggy
def is_buggy(self)
Definition: LHETools.py:146
LHETools.Event.from_lines
def from_lines(cls, lines)
Definition: LHETools.py:109
LHETools.EventInfo
Definition: LHETools.py:39
LHETools.Event.particles
particles
Definition: LHETools.py:101
LHETools.Record
Definition: LHETools.py:16
CxxUtils::set
constexpr std::enable_if_t< is_bitmask_v< E >, E & > set(E &lhs, E rhs)
Convenience function to set bits in a class enum bitmask.
Definition: bitmask.h:232
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
LHETools.Event.info
info
Definition: LHETools.py:100
LHETools.Record.to_string
def to_string(self)
Definition: LHETools.py:35
Trk::open
@ open
Definition: BinningType.h:40
LHETools.Particle.mothers
def mothers(self)
Definition: LHETools.py:84
LHETools.FindDuplicates
def FindDuplicates(inFileName=None, crashMode=False, printDuplicated=False)
Definition: LHETools.py:220
if
if(febId1==febId2)
Definition: LArRodBlockPhysicsV0.cxx:567
LHETools.Record.from_string
def from_string(cls, string)
Definition: LHETools.py:23
pickleTool.object
object
Definition: pickleTool.py:30
LHETools.Particle.daughters
def daughters(self)
Definition: LHETools.py:88
LHETools.Record.__init__
def __init__(self, **kwargs)
Definition: LHETools.py:17
WriteBchToCool.update
update
Definition: WriteBchToCool.py:67
LHETools.Particle
Definition: LHETools.py:57
LHETools.Event.__delitem__
def __delitem__(self, index)
Definition: LHETools.py:168