ATLAS Offline Software
Loading...
Searching...
No Matches
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
6from AthenaCommon import Logging
7LHEToolsLog = Logging.logging.getLogger('MCJobOptionUtils/LHETools.py')
8
9import os
10import subprocess
11import glob
12import collections
13import re
14
15
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
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
220def 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{
236if (NR==1) {
237 print $1 > \""""+inFileName+"-new"+"""\"
238 ORS=\"\\n\"
239}
240else {
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}
246END{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
if(febId1==febId2)
__init__(self, info, particles, extra_lines=None, spurious_event_markup=False)
Definition LHETools.py:99
__delitem__(self, index)
Definition LHETools.py:168
to_string(self)
Definition LHETools.py:150
from_lines(cls, lines)
Definition LHETools.py:109
__getitem__(self, index)
Definition LHETools.py:161
to_string(self)
Definition LHETools.py:35
__init__(self, **kwargs)
Definition LHETools.py:17
from_string(cls, string)
Definition LHETools.py:23
STL class.
void xrange(TH1 *h, bool symmetric)
FindDuplicates(inFileName=None, crashMode=False, printDuplicated=False)
Definition LHETools.py:220
Definition index.py:1