ATLAS Offline Software
Loading...
Searching...
No Matches
TriggerInfo.py
Go to the documentation of this file.
1# Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2
3__author__ = 'Javier Montejo'
4__version__="$Revision: 2.0 $"
5__doc__="Class containing all the information of an HLT chain"
6
7import re
8from TriggerMenuMT.TriggerAPI.TriggerEnums import TriggerType, TriggerPeriod
9from collections import Counter
10from AthenaCommon.Logging import logging
11log = logging.getLogger(__name__)
12
14 ''' Object containing all the HLT information related to a given period.
15 Stores a list of TriggerChain objects and the functions to skim them
16 '''
17 def __init__(self,period=0, customGRL=None, release=None, flags=None):
18 self.triggerChains = []
19 self.period = period
20 self.totalLB = 0
21 self.totalLBByRun = {}
22
23 if not period and not customGRL: return
24 from .TriggerDataAccess import getHLTlist
25 HLTlist, self.totalLB, self.totalLBByRun = getHLTlist(period, customGRL, release, flags)
26 for hlt, l1, livefraction, activeLB, hasRerun, activeLBByRun in HLTlist:
27 self.triggerChains.append( TriggerChain(hlt, l1, livefraction, activeLB, hasRerun, activeLBByRun))
28
29 def setRunRange(self,start=0,end=999999):
30 if not self.totalLBByRun:
31 # empty dict means either loaded from trigger menu or from old json
32 totalLB = self.totalLB
33 else:
34 totalLB = 0
35 for run,efflb in self.totalLBByRun.items():
36 if int(run)<int(start) or int(run)>int(end): continue
37 totalLB += efflb
38 for tc in self.triggerChains:
39 tc.setRunRange(start,end,totalLB)
40
41 def __str__(self):
42 return f"<TriggerMenuMT.TriggerAPI.TriggerInfo.TriggerInfo object with {len(self.triggerChains)} triggerChains, period={self.period}, totalLB={self.totalLB}>"
43
44 def toJSON(self):
45 return dict(period= TriggerPeriod.toName(self.period), totalLB=self.totalLB,triggerChains=self.triggerChains,totalLBByRun=self.totalLBByRun)
46
47 @classmethod
48 def merge(cls,listofTI):
49 from copy import deepcopy
50 mergedTI = TriggerInfo()
51 mergedHLTmap = {}
52 for ti in listofTI:
53 mergedTI.period |= ti.period
54 mergedTI.totalLB += ti.totalLB
55 for run,lb in ti.totalLBByRun.items():
56 if run not in mergedTI.totalLBByRun: mergedTI.totalLBByRun[run] = 0
57 mergedTI.totalLBByRun[run] += lb
58 for tc in ti.triggerChains:
59 if tc.name not in mergedHLTmap: mergedHLTmap[tc.name] = deepcopy(tc)
60 else: mergedHLTmap[tc.name].activeLB += tc.activeLB
61 # copy activeLBByRun values too
62 for run,lb in tc.activeLBByRun.items():
63 if run not in mergedHLTmap[tc.name].activeLBByRun: mergedHLTmap[tc.name].activeLBByRun = 0
64 mergedHLTmap[tc.name].activeLBByRun += lb
65 for tc in mergedHLTmap.values():
66 tc.livefraction = tc.activeLB/float(mergedTI.totalLB)
67 mergedTI.triggerChains = list(mergedHLTmap.values())
68 return mergedTI
69
70
71 @classmethod
72 def testCustomGRL(cls, grl):
73 from TriggerMenuMT.TriggerAPI.TriggerPeriodData import TriggerPeriodData
74 return TriggerPeriodData.testCustomGRL(grl)
75
76 def reparse(self):
77 self.triggerChains = [ TriggerChain(t.name, t.l1seed, t.livefraction, t.activeLB) for t in self.triggerChains ]
78
79 def _getUnprescaled(self,triggerType, additionalTriggerType, matchPattern, livefraction=1.0):
80 return [x.name for x in self.triggerChains if x.isUnprescaled(livefraction) and x.passType(triggerType, additionalTriggerType) and re.search(matchPattern, x.name)]
81
82 def _getLowestUnprescaled(self,triggerType, additionalTriggerType, matchPattern, livefraction=1.0):
83 chainList = [ x for x in self.triggerChains if x.isUnprescaled(livefraction) and x.passType(triggerType, additionalTriggerType) and re.search(matchPattern, x.name)]
84 typeMap = {}
85 for chain in chainList:
86 if chain.triggerType not in typeMap:
87 typeMap[chain.triggerType] = [chain]
88 continue
89 append = False
90 for other in typeMap[chain.triggerType][:]:
91 comp = chain.isLowerThan(other,self.period)
92 if comp == 0:
93 append = False
94 break
95 append = True
96 if comp == 1: typeMap[chain.triggerType].remove(other)
97 if append:
98 typeMap[chain.triggerType].append(chain)
99 return [x.name for t in typeMap.values() for x in t ]
100
101
102 def _getAllHLT(self,triggerType, additionalTriggerType, matchPattern, livefraction):
103 return {x.name: x.livefraction for x in self.triggerChains if x.passType(triggerType, additionalTriggerType) and re.search(matchPattern, x.name) and x.isUnprescaled(livefraction)}
104
105 def _getActive(self,triggerType, additionalTriggerType, matchPattern, livefraction):
106 return [x.name for x in self.triggerChains if x.isActive(livefraction) and x.passType(triggerType, additionalTriggerType) and re.search(matchPattern, x.name)]
107 def _getInactive(self,triggerType, additionalTriggerType, matchPattern, livefraction):
108 return [x.name for x in self.triggerChains if x.isInactive(livefraction) and x.passType(triggerType, additionalTriggerType) and re.search(matchPattern, x.name)]
109
110 def _checkPeriodConsistency(self,triggerType, additionalTriggerType, matchPattern):
111 inconsistent = set()
112 for i in range(len(self.triggerChains)):
113 probe1 = self.triggerChains[i]
114 if not (probe1.passType(triggerType, additionalTriggerType) and re.search(matchPattern, probe1.name)): continue
115 for j in range(i+1,len(self.triggerChains)):
116 probe2 = self.triggerChains[j]
117 if not (probe2.passType(triggerType, additionalTriggerType) and re.search(matchPattern, probe2.name)): continue
118 if probe1.isUnprescaled() and not probe2.isUnprescaled() and probe1.isLowerThan(probe2,self.period)==1:
119 log.error(f"Found {probe2.name} that is tighter than Primary {probe1.name}")
120 inconsistent.add(probe2.name)
121 if probe2.isUnprescaled() and not probe1.isUnprescaled() and probe2.isLowerThan(probe1,self.period)==1:
122 log.error(f"Found {probe1.name} that is tighter than Primary {probe2.name}")
123 inconsistent.add(probe1.name)
124
125 return inconsistent
126
127
129 types = ('e','j','mu','tau','xe','g','ht')
130 uctTypes = ('isotrk', 'fslrt', 'dedxtrk', 'hitdvjet', 'fsvsi', 'distrk', 'dispjet', 'dispvtx') # unconventional tracking types
131 legpattern = re.compile('([0-9]*)(%s)([0-9]+)(noL1)?' % '|'.join(types+uctTypes))
132 detailpattern = re.compile(r'(?:-?\d+)|(?:[^0-9 -]+)') #split into text-only vs number-only
133 bjetpattern = re.compile('bmv|bhmv|btight|bmedium|bloose|bld1|bgn1|bgn2|bdl1')
134 bphyspattern = re.compile('b[A-Z]')
135 exoticspattern = re.compile('llp|LLP|muvtx|hiptrt|LATE|NOMATCH|distrk|hitdvjet')
136 afppattern = re.compile('afp|AFP')
137
138 def __init__(self,legname, chainseed, chainname=None):
139 self.legname = legname
140 self.l1seed = ""
141 details = []
142 chainseed= chainseed.replace("L1_","")
143 blocks = legname.split("_L1")
144
145 for token in blocks[0].split("_"):
146 m = self.legpattern.match(token)
147 if m:
148 count,legtype,thr,noL1 = m.groups()
149 self.count = int(count) if count else 1
150 self.thr = int(thr)
151 if legtype == 'e':
152 if self.count > 1: self.legtype = TriggerType.el_multi
153 else: self.legtype = TriggerType.el_single
154 elif legtype == 'mu':
155 if self.count > 1: self.legtype = TriggerType.mu_multi
156 else: self.legtype = TriggerType.mu_single
157 elif legtype == 'j':
158 if self.count > 1: self.legtype = TriggerType.j_multi
159 else: self.legtype = TriggerType.j_single
160 elif legtype == 'tau':
161 if self.count > 1: self.legtype = TriggerType.tau_multi
162 else: self.legtype = TriggerType.tau_single
163 elif legtype == 'g':
164 if self.count > 1: self.legtype = TriggerType.g_multi
165 else: self.legtype = TriggerType.g_single
166 elif legtype == 'xe':
167 self.legtype = TriggerType.xe
168 elif legtype == 'ht':
169 self.legtype = TriggerType.ht
170 elif legtype in self.uctTypes:
171 self.legtype = TriggerType.exotics
172 # all these leg types are actually distinct, so include legtype in the detail list too
173 details.append(legtype)
174 else:
175 log.info("Unknown trigger type: %s",legtype)
176 if noL1: details.append(noL1)
177 else:
178 if self.bjetpattern.match(token):
179 if self.legtype == TriggerType.j_single: self.legtype = TriggerType.bj_single
180 if self.legtype == TriggerType.j_multi: self.legtype = TriggerType.bj_multi
181 if self.bphyspattern.match(token):
182 self.legtype = TriggerType.mu_bphys
183 if self.exoticspattern.search(token):
184 self.legtype = TriggerType.exotics
185 if self.afppattern.search(token):
186 self.legtype = TriggerType.afp
187 details.append(token)
188
189 for l1seed in blocks[1:]:
190 if self.exoticspattern.search(l1seed):
191 self.legtype = TriggerType.exotics
192 if self.afppattern.search(l1seed):
193 self.legtype = TriggerType.afp
194 if l1seed == chainseed: continue
195 else:
196 assert self.l1seed=="", (self.l1seed, chainseed, chainname, blocks[1:])
197 self.l1seed = l1seed
198 if not self.l1seed: self.l1seed = chainseed
199 self.details = tuple(details)
200
201 def __eq__(self,other):
202 return (self.l1seed == other.l1seed and self.count == other.count and self.thr == other.thr and self.legtype == other.legtype and self.details == other.details)
203
204 def __hash__(self):
205 return hash((self.l1seed,self.count,self.thr,self.legtype,self.details))
206
207 def __repr__(self):
208 return "{0} l1seed={1} count={2} thr={3} details={4} legtype={5}".format(self.legname,self.l1seed,self.count,self.thr,self.details,TriggerType.toStr(self.legtype))
209
210 def isLegLowerThan(self, other, is2015, debug=False):
211 ''' Returns -9 if none of them is lower than the other (e.g. different met flavour).
212 Returns -1 if identical
213 Returns 0 if other is lower than self.
214 Returns 1 if self is lower than other.
215 '''
216 if debug:
217 log.info("DEBUG LEGS --------")
218 log.info(f"{self.legname} {other.legname}")
219 log.info(f"{self.legtype} {other.legtype}")
220 log.info(f"{self.l1seed} {other.l1seed}")
221 log.info(f"{self.details} {other.details}")
222 log.info(f"{self.thr} {other.thr}")
223 log.info(self.compareDetails(other, is2015, debug=True))
224 log.info(self.details == other.details)
225 log.info("DEBUG LEGS END --------")
226
227 if self.legtype != other.legtype: return -9
228 if self.compareDetails(other, is2015) == -1:
229 if self.thr < other.thr: return 1
230 if self.thr > other.thr: return 0
231 else: return -1
232
233 if self.compareDetails(other, is2015) == 1 and self.thr <= other.thr: return 1
234 if self.compareDetails(other, is2015) == 0 and other.thr <= self.thr: return 0
235 return -9
236
237 def compareDetails(self, other, is2015, debug=False):
238 ''' Returns -9 if none of them is lower than the other (e.g. different met flavour).
239 Returns -1 if identical
240 Returns 0 if other is lower than self.
241 Returns 1 if self is lower than other.
242 '''
243 from copy import deepcopy
244
245 if debug: log.info(f"compareDetails: {len(self.details)} {len(other.details)} {(self.l1seed == other.l1seed)} {(self.details == other.details)}")
246 if len(self.details) != len(other.details):
247 if not is2015 and any([x.startswith("noL1") for x in self.details]):
248 cloneself = deepcopy(self)
249 cloneself.details = [ x for x in self.details if not x.startswith("noL1")]
250 compno = cloneself.compareDetails(other,is2015,debug)
251 if compno ==1 or compno == -1:
252 return 1
253 if not is2015 and any([x.startswith("noL1") for x in other.details]):
254 cloneother = deepcopy(other)
255 cloneother.details = [ x for x in other.details if not x.startswith("noL1")]
256 compno = self.compareDetails(cloneother,is2015,debug)
257 if compno ==0 or compno == -1:
258 return 0
259 if not is2015 and any([x.startswith("nod0") for x in self.details]):
260 cloneself = deepcopy(self)
261 cloneself.details = [ x for x in self.details if not x.startswith("nod0")]
262 compno = cloneself.compareDetails(other,is2015,debug)
263 if compno ==1 or compno == -1:
264 return 1
265 if not is2015 and any([x.startswith("nod0") for x in other.details]):
266 cloneother = deepcopy(other)
267 cloneother.details = [ x for x in other.details if not x.startswith("nod0")]
268 compno = self.compareDetails(cloneother,is2015,debug)
269 if compno ==0 or compno == -1:
270 return 0
271 if any([x.startswith("cut") for x in self.details]):
272 cloneself = deepcopy(self)
273 cloneself.details = [ x for x in self.details if not x.startswith("cut")]
274 compno = cloneself.compareDetails(other,is2015,debug)
275 if compno ==0 or compno == -1:
276 return 0
277 if any([x.startswith("cut") for x in other.details]):
278 cloneother = deepcopy(other)
279 cloneother.details = [ x for x in other.details if not x.startswith("cut")]
280 compno = self.compareDetails(cloneother,is2015,debug)
281 if compno ==1 or compno == -1:
282 return 1
283 return -9
284 compl1seed = self.compareTags(self.l1seed, other.l1seed, stringSubset=True, debug=debug)
285 compdetails = self.compareTags(" ".join(self.details), " ".join(other.details), debug=debug )
286 if self.l1seed == other.l1seed:
287 if self.details == other.details: return -1
288 if debug: log.info(f"compareTags 1: {compdetails}")
289 return compdetails
290
291 if self.details == other.details:
292 if debug: log.info(f"compareTags 2: {compl1seed}")
293 return compl1seed
294
295 if compl1seed == compdetails:
296 return compl1seed
297 return -9
298
299 def compareTags(self, tag1, tag2, stringSubset=False,debug=False):
300 def mycomp(x,y):
301 ''' Return -9 for different strings,
302 -1 for identical strings/nums,
303 0/1 for high/low numbers or string subsets
304 '''
305 try:
306 x,y = int(x), int(y)
307 if x < y: return 1
308 elif x > y: return 0
309 else: return -1
310 except ValueError:
311 if x==y: return -1
312 if x == y.replace("vloose","loose"): return 0
313 if x == y.replace("vloose","loose").replace("loose","medium"): return 0
314 if x == y.replace("vloose","loose").replace("loose","medium").replace("medium","tight"): return 0
315 if y == x.replace("vloose","loose"): return 1
316 if y == x.replace("vloose","loose").replace("loose","medium"): return 1
317 if y == x.replace("vloose","loose").replace("loose","medium").replace("medium","tight"): return 1
318 l1ThresholdLevels = ["L","M","T"]
319 if x in l1ThresholdLevels and y in l1ThresholdLevels:
320 if l1ThresholdLevels.index(x) > l1ThresholdLevels.index(y): return 0
321 return 1 # don't need to check for equality, that was done above
322 if stringSubset:
323 if x in y: return 1
324 if y in x: return 0
325 return -9
326
327 if tag1 == tag2: return -1
328 #lower mv2 and deltaR/deltaZ/deltaPhi values are tighter, put a minus sign to trick it
329 inverseCuts = ("mv2c","dr","dz","dphi","dl1d","dl1r","gn1","gn2")
330 for cut in inverseCuts:
331 tag1 = tag1.replace(cut,cut+"-")
332 tag2 = tag2.replace(cut,cut+"-")
333 #only make a statement on the numerical values, with everything else identical
334 extra_inverseCuts = ("b","emf","bgtwo","gntau", "uht1tau") # all only appear in "presel" block
335 def findall(tag): # do the findall per tag block, so that can apply special rule to presel block only
336 out = []
337 for s in tag.split(" "):
338 l = self.detailpattern.findall(s)
339 if len(l) and l[0]=='presel':
340 for i,s in enumerate(l):
341 for cut in extra_inverseCuts:
342 if s==cut: l[i+1] = "-" + l[i+1] # adds a minus sign to the number that follows
343 out += l
344 return out
345 reself = findall(tag1)
346 reother = findall(tag2)
347
348 if len(reself) != len(reother): return -9
349 thecomp = [mycomp(a,b) for a,b in zip(reself,reother)]
350 if debug: log.info(f"thecomp: {thecomp} {reself} {reother}")
351 if any([x == -9 for x in thecomp]): return -9
352 if all([x !=0 for x in thecomp]) and any([x == 1 for x in thecomp]): return 1
353 if all([x !=1 for x in thecomp]) and any([x == 0 for x in thecomp]): return 0
354 return -9
355
356 @classmethod
357 def parse_legs(cls,name,l1seed,chainname):
358 legsname = []
359 name = name.replace("HLT_","")
360 for token in name.split("_"):
361 m = cls.legpattern.match(token)
362 if m:
363 legsname.append(token)
364 elif legsname:
365 legsname[-1] += "_"+token
366 else: #first token doesn't match
367 #log.info("parse_legs: Problem parsing",name)
368 return []
369 return [TriggerLeg(l,l1seed,chainname) for l in legsname]
370
372 l1types = ('EM','J','MU','TAU','XE','XS','HT','eEM','eTAU','jJ','jTAU','jXE','gXEJWOJ','gJ','gLJ','jEM')
373 l1pattern = re.compile('([0-9]*)(%s)([0-9]+)' % '|'.join(l1types))
374
375 def __init__(self,name,l1seed,livefraction,activeLB=1,hasRerun=False, activeLBByRun={}):
376 self.name = name
377 self.l1seed = l1seed
378 tmplegs = TriggerLeg.parse_legs(name,l1seed,name)
379 self.legs = self.splitAndOrderLegs(tmplegs)
380 self.livefraction = livefraction
381 self.activeLB = activeLB
382 self.hasRerun = hasRerun
383 self.activeLBByRun = activeLBByRun
384 self.triggerType = self.getTriggerType(self.legs, l1seed)
385
386 def setRunRange(self,start,end,totalLB):
387 """
388 This method is called by the owning TriggerInfo method to adjust the livefractions and activeLB
389 to only the given runs
390 :param start:
391 :param end:
392 :param totalLB:
393 :return:
394 """
395 if not self.activeLBByRun:
396 # empty dict means either loaded from trigger menu or from old json
397 return
398 self.activeLB = 0
399 for run,efflb in self.activeLBByRun.items():
400 if int(run)<int(start) or int(run)>int(end): continue
401 self.activeLB += efflb
402 self.livefraction = self.activeLB/totalLB
403 pass
404
405 def toJSON(self):
406 return dict(name=self.name, l1seed=self.l1seed, livefraction=self.livefraction, activeLB=self.activeLB, hasRerun=self.hasRerun,activeLBByRun=self.activeLBByRun)
407
408 def splitAndOrderLegs(self, legs):
409 from copy import deepcopy
410 newLegs = []
411 for triggerType in TriggerType:
412 for l in legs:
413 if not l.legtype == triggerType: continue
414 for i in range(l.count): #split into N single legs
415 tmp = deepcopy(l)
416 tmp.count = 1
417 if tmp.legtype & TriggerType.el_multi:
418 tmp.legtype |= TriggerType.el_single
419 tmp.legtype &= ~TriggerType.el_multi
420 elif tmp.legtype & TriggerType.mu_multi:
421 tmp.legtype |= TriggerType.mu_single
422 tmp.legtype &= ~TriggerType.mu_multi
423 elif tmp.legtype & TriggerType.tau_multi:
424 tmp.legtype |= TriggerType.tau_single
425 tmp.legtype &= ~TriggerType.tau_multi
426 elif tmp.legtype & TriggerType.j_multi:
427 tmp.legtype |= TriggerType.j_single
428 tmp.legtype &= ~TriggerType.j_multi
429 elif tmp.legtype & TriggerType.bj_multi:
430 tmp.legtype |= TriggerType.bj_single
431 tmp.legtype &= ~TriggerType.bj_multi
432 elif tmp.legtype & TriggerType.g_multi:
433 tmp.legtype |= TriggerType.g_single
434 tmp.legtype &= ~TriggerType.g_multi
435 newLegs.append(tmp)
436 return newLegs
437
438
439 # def getChainGroupTriggerType(self):
440 # """
441 # :return: triggerType determined by chainGroup information
442 # """
443 # mtype = TriggerType.UNDEFINED
444 #
445 # # decide trigger type on chainGroup's "RATE:" group if present
446 # for group in self.chainGroups:
447 # if not group.startswith("RATE:"): continue
448 # rateGroup = group[5:] # strip prefix
449 # if rateGroup=="SingleElectron": mtype |= TriggerType.el_single
450 # elif rateGroup=="SingleMuon": mtype |= TriggerType.mu_single
451 # elif rateGroup=="MultiElectron": mtype |= TriggerType.el_multi
452 # elif rateGroup=="MultiMuon": mtype |= TriggerType.mu_multi
453 # elif rateGroup=="SinglePhoton": mtype |= TriggerType.g_single
454 # elif rateGroup=="MultiPhoton": mtype |= TriggerType.g_multi
455 # elif rateGroup=="SingleJet": mtype |= TriggerType.j_single
456 # elif rateGroup=="MultiJet": mtype |= TriggerType.j_multi
457 # elif rateGroup=="SingleBJet": mtype |= TriggerType.bj_single
458 # elif rateGroup=="MultiBJet": mtype |= TriggerType.bj_multi
459 # elif rateGroup=="SingleTau": mtype |= TriggerType.tau_single
460 # elif rateGroup=="MultiTau": mtype |= TriggerType.tau_multi
461 # else:
462 # if "Bphysics" in rateGroup: mtype |= TriggerType.mu_bphys
463 # if "MET" in rateGroup: mtype |= TriggerType.xe
464 # if "Muon" in rateGroup: mtype |= TriggerType.mu
465 # if "Electron" in rateGroup: mtype |= TriggerType.el
466 # if "Bjet" in rateGroup: mtype |= TriggerType.bj
467 # if "Jet" in rateGroup: mtype |= TriggerType.j
468 # if "Tau" in rateGroup: mtype |= TriggerType.tau
469 #
470 # return mtype
471
472 def getTriggerType(self, legs, l1seed):
473 mtype = TriggerType.UNDEFINED
474
475 for l in legs:
476 if mtype & TriggerType.el and l.legtype & TriggerType.el:
477 mtype |= TriggerType.el_multi
478 mtype &= ~TriggerType.el_single
479 elif mtype & TriggerType.mu and l.legtype & TriggerType.mu:
480 mtype |= TriggerType.mu_multi
481 mtype &= ~TriggerType.mu_single
482 elif mtype & TriggerType.tau and l.legtype & TriggerType.tau:
483 mtype |= TriggerType.tau_multi
484 mtype &= ~TriggerType.tau_single
485 elif mtype & TriggerType.j and l.legtype & TriggerType.j:
486 mtype |= TriggerType.j_multi
487 mtype &= ~TriggerType.j_single
488 elif mtype & TriggerType.bj and l.legtype & TriggerType.bj:
489 mtype |= TriggerType.bj_multi
490 mtype &= ~TriggerType.bj_single
491 elif mtype & TriggerType.g and l.legtype & TriggerType.g:
492 mtype |= TriggerType.g_multi
493 mtype &= ~TriggerType.g_single
494 elif l.legtype & TriggerType.mu_bphys:
495 mtype |= TriggerType.mu_bphys
496 mtype &= ~(TriggerType.mu_single | TriggerType.mu_multi)
497 elif l.legtype & TriggerType.exotics:
498 mtype |= TriggerType.exotics
499 elif l.legtype & TriggerType.afp:
500 mtype = TriggerType.afp #on purpose not OR-ed
501 else:
502 mtype |= l.legtype
503
504 l1seed= l1seed.replace("L1_","")
505 if mtype & TriggerType.exotics or mtype & TriggerType.afp:
506 return mtype
507 for token in l1seed.split("_"):
508 m = self.l1pattern.match(token)
509 if m:
510 count,legtype,thr = m.groups()
511 count = int(count) if count else 1
512 if 'EM' in legtype or 'TAU' in legtype:
513 pass
514 elif 'MU' in legtype:
515 if not mtype & TriggerType.mu_bphys:
516 if count > 1: mtype |= TriggerType.mu_multi
517 elif not mtype & TriggerType.mu_multi: mtype |= TriggerType.mu_single
518 elif 'J' in legtype:
519 if not mtype & TriggerType.bj and not mtype & TriggerType.j and not mtype & TriggerType.tau and not mtype & TriggerType.ht:
520 if count > 1: mtype |= TriggerType.j_multi
521 elif not mtype & TriggerType.j_multi: mtype |= TriggerType.j_single
522 elif 'XE' in legtype or 'XS' in legtype:
523 mtype |= TriggerType.xe
524 elif 'HT' in legtype:
525 mtype |= TriggerType.ht
526 else:
527 log.info("Unknown trigger type: %s %s %s %s", legtype, mtype, token, self.name)
528 return mtype
529
530 def isActive(self, livefraction=1e-99):
531 return self.livefraction > livefraction or self.hasRerun
532 def isInactive(self, livefraction=1e-99):
533 return not self.isActive(livefraction)
534
535 def isUnprescaled(self, livefraction=1.0):
536 return self.livefraction >= livefraction
537
538 def getType(self):
539 return self.triggerType
540
541 def passType(self, triggerType, additionalTriggerType):
542 if self.triggerType == TriggerType.UNDEFINED: return False
543 if self.triggerType == TriggerType.ALL: return True
544 match = (self.triggerType & triggerType)
545 if not match: return False
546 tmpType = self.triggerType & ~triggerType
547
548 try:
549 for t in additionalTriggerType:
550 match = (tmpType & t)
551 if not match: return False
552 tmpType = tmpType & ~t
553 except TypeError: #Not iterable
554 if additionalTriggerType!=TriggerType.UNDEFINED:
555 match = (tmpType & additionalTriggerType)
556 if not match: return False
557 tmpType = tmpType & ~additionalTriggerType
558
559 return tmpType == TriggerType.UNDEFINED #After matches nothing remains
560
561 def __repr__(self):
562 return repr({"name":self.name, "legs":self.legs, "triggerType":TriggerType.toStr(self.triggerType), "livefraction":self.livefraction, "activeLB":self.activeLB})
563
564 def isSubsetOf(self, other):
565 ''' Returns -1 if none of them is a strict subset of the other
566 Returns 0 if the legs in other are a subset of self.
567 Returns 1 if the legs in self are a subset of other.
568 '''
569 if not self.legs or not other.legs: return -1 #problems with AFP
570 selfcounter = Counter(self.legs)
571 othercounter = Counter(other.legs)
572 for leg, count in selfcounter.items():
573 if leg not in othercounter or count > othercounter[leg]: break
574 else: return 1
575 for leg, count in othercounter.items():
576 if leg not in selfcounter or count > selfcounter[leg]: break
577 else: return 0
578 return -1
579
580 def isLowerThan(self, other,period=TriggerPeriod.future):
581 ''' Returns -1 if none of them is lower than the other (e.g. asymmetric dilepton).
582 Returns 0 if other is lower than self.
583 Returns 1 if self is lower than other.
584 '''
585 is2015 = period & TriggerPeriod.y2015 and not TriggerPeriod.isRunNumber(period)
586 is2015 |= period <= 284484 and TriggerPeriod.isRunNumber(period)
587 if self.triggerType != other.triggerType: return -1
588 if len(self.legs) != len(other.legs): return -1
589 comp = -1
590 debug = False
591 #if re.search("HLT_j55_gsc75_bmv2c1040_split_3j55_gsc75_boffperf_split", self.name): debug = True
592 if debug: log.info("DEBUG: %s %s",self.name,other.name)
593 for selfleg, otherleg in zip(self.legs, other.legs):
594 legcomp = selfleg.isLegLowerThan(otherleg, is2015, debug)
595 if debug: log.info("DEBUG LEG return: %s", legcomp)
596 if legcomp == -9: return -1
597 elif legcomp == -1: continue
598 elif legcomp == 0 and comp == 1: return -1
599 elif legcomp == 1 and comp == 0: return -1
600 elif legcomp == 0 : comp = 0
601 elif legcomp == 1 : comp = 1
602 if debug: log.info("DEBUG FINAL: %s",comp)
603 return comp
604
605
606def test():
607 a = TriggerChain("HLT_j50_gsc65_bmv2c1040_split_3j50_gsc65_boffperf_split", "L1J100",1)
608 log.info(a)
609 log.info(bin(a.getType()))
610 log.info(a.passType(TriggerType.j_multi, TriggerType.UNDEFINED))
611 log.info(a.passType(TriggerType.j_multi | TriggerType.bj_single, TriggerType.UNDEFINED))
612 log.info(a.isUnprescaled())
613
614if __name__ == "__main__":
615 import sys
616 sys.exit(test())
passType(self, triggerType, additionalTriggerType)
__init__(self, name, l1seed, livefraction, activeLB=1, hasRerun=False, activeLBByRun={})
setRunRange(self, start, end, totalLB)
isLowerThan(self, other, period=TriggerPeriod.future)
_getLowestUnprescaled(self, triggerType, additionalTriggerType, matchPattern, livefraction=1.0)
_getInactive(self, triggerType, additionalTriggerType, matchPattern, livefraction)
setRunRange(self, start=0, end=999999)
_getActive(self, triggerType, additionalTriggerType, matchPattern, livefraction)
_checkPeriodConsistency(self, triggerType, additionalTriggerType, matchPattern)
_getUnprescaled(self, triggerType, additionalTriggerType, matchPattern, livefraction=1.0)
_getAllHLT(self, triggerType, additionalTriggerType, matchPattern, livefraction)
__init__(self, period=0, customGRL=None, release=None, flags=None)
__init__(self, legname, chainseed, chainname=None)
isLegLowerThan(self, other, is2015, debug=False)
compareTags(self, tag1, tag2, stringSubset=False, debug=False)
parse_legs(cls, name, l1seed, chainname)
compareDetails(self, other, is2015, debug=False)
STL class.
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition hcg.cxx:310
void search(TDirectory *td, const std::string &s, std::string cwd, node *n)
recursive directory search for TH1 and TH2 and TProfiles
Definition hcg.cxx:739
std::vector< std::string > split(const std::string &s, const std::string &t=":")
Definition hcg.cxx:177
bool match(std::string s1, std::string s2)
match the individual directories of two strings
Definition hcg.cxx:357
Definition merge.py:1