ATLAS Offline Software
JetPresel.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
2 
3 from AthenaCommon.Logging import logging
4 log = logging.getLogger(__name__)
5 
6 import re
7 
8 from .JetRecoCommon import getJetCalibDefaultString, jetChainParts, etaRangeAbbrev, jetRecoDictToString
9 from ..Menu.SignatureDicts import JetChainParts_Default
10 from TriggerMenuMT.HLT.Config.ControlFlow.HLTCFTools import NoHypoToolCreated
11 
12 
13 from TrigHLTJetHypo.TrigJetHypoToolConfig import trigJetHypoToolFromDict
14 
15 # Extract preselection reco dictionary
16 # This is 1:1 based on the main (tracking/PF) jet collection
17 def getPreselRecoDict(reco,roiftf=False):
18 
19  # Define a fixed preselection dictionary for prototyping -- we may expand the options
20  preselRecoDict = {
21  'recoAlg':reco,
22  'constitType':'tc',
23  'clusterCalib':'em',
24  'constitMod':'',
25  'trkopt':'notrk',
26  'ionopt':'noion',
27  }
28  ''' #Here you can set custom calibrations for large-R preselections. If you set to LCW you'll get an issue though, as the trigger expects the *same* topocluster collection to be used in the preselection and in the PFlow stage with tracking. Therefore this would need to be adapted, but it might not be so easy...
29 
30  if preselRecoDict['recoAlg']=='a10': #Setting LC calibrations for large-R jets
31  preselRecoDict['clusterCalib']='lcw'
32  '''
33  preselRecoDict.update({
34  'jetCalib':getJetCalibDefaultString(preselRecoDict['recoAlg'],preselRecoDict['constitType'],preselRecoDict['clusterCalib'])
35  if preselRecoDict['recoAlg']=='a4' else 'nojcalib'
36  }) #Adding default calibration for corresponding chain
37 
38  # Overwriting tracking option to roiftf tracking
39  if roiftf: preselRecoDict['trkopt'] = 'roiftf'
40 
41  preselRecoDict['jetDefStr'] = jetRecoDictToString(preselRecoDict)
42 
43  return preselRecoDict
44 
45 # Find the preselection definition in the chainParts
46 # Simply taken from the last chainPart
47 def extractPreselection(fullChainDict):
48  jparts = jetChainParts(fullChainDict['chainParts'])
49  return jparts[-1]['trkpresel']
50 
51 # Calo jet preselection hypo tool generator
52 # Translate the preselection expression in the main jet chainDict into a temporary chainDict
53 # that is only seen by the standard hypo tool generator, and used to return a configured
54 # hypo tool for the preselection step
55 def caloPreselJetHypoToolFromDict(flags, mainChainDict):
56  return _preselJetHypoToolFromDict(flags, mainChainDict)
57 
58 
59 def roiPreselJetHypoToolFromDict(flags, mainChainDict):
60  return _preselJetHypoToolFromDict(flags, mainChainDict,doTaggingSel=True)
61 
62 
63 def _preselJetHypoToolFromDict(flags, mainChainDict, doTaggingSel=False):
64 
65  preselChainDict = dict(mainChainDict)
66  preselChainDict['chainParts']=[]
67  trkpresel = extractPreselection(mainChainDict)
68 
69  # Get from the last chainPart in order to avoid to specify preselection for every leg
70  #TODO: add protection for cases where the preselection is not specified in the last chainPart
71  presel_matched = re.match(r'presel(?P<cut>\d?\d?(Z[\d\D]+)?[jacf](HT)?[\d\D]+)', trkpresel)
72  assert presel_matched is not None, "Impossible to match preselection pattern for self.trkpresel=\'{0}\'.".format(trkpresel)
73  presel_cut_str = presel_matched.groupdict()['cut'] #This is the cut string you want to parse. For example 'presel2j50XXj40'
74 
75  usingDIPZ = bool(re.match(r'.*Z', presel_cut_str)) # Need to determine if there's DIPZ leg anywhere to enforce central jets across all calopresel legs
76  if usingDIPZ:
77  findSel = re.finditer(r'(?P<nJet>\d?[jacf])(?P<ptcut>\d+)', presel_cut_str)
78  findAllJets=[]
79  findAllPts=[]
80  for match in findSel:
81  nJ = match.group("nJet")
82  findAllJets.append('1'+nJ if len(nJ)==1 else nJ)
83  findAllPts.append(int(match.group("ptcut")))
84  nAllJets = sum(int(i[:-1]) for i in findAllJets)
85  nCentralJets = sum(int(i[:-1]) if 'c' in i else 0 for i in findAllJets)
86  ptCut = min(int(i) for i in findAllPts)
87  assert nAllJets == nCentralJets, "Your preselection has a DIPZ part but not only central jets were required. This isn't currently supported. Please investigate."
88 
89  preselCommonJetParts = dict(JetChainParts_Default)
90 
91  for ip,p in enumerate(presel_cut_str.split('XX')):
92  hascalSel= bool(re.match(r'.*emf\w?\d+', p))
93  # This appears to be very much a hack... we should just have separate
94  # functions for with/without bjet or tau selections. -- Chris Pollard
95  if not doTaggingSel: # Removing b-jet and tau parts if b-jet presel is not requested
96  p = re.sub(r'b\d\d|bg\d\d|bgtwo\d\d', '', p)
97  p = re.sub(r'gntau\d\d', '', p)
98 
99  hasBjetSel = bool(re.match(r'.*(b\d\d|bg\d\d|bgtwo\d\d)', p))
100  hasTauSel = bool(re.match(r'.*(gntau\d\d)', p))
101  hasDIPZsel = bool(re.match(r'.*Z', p))
102 
103  if hasDIPZsel and not doTaggingSel: continue # Skipping calopresel step when DIPZ is run
104  if usingDIPZ and not hasDIPZsel and not hasBjetSel and doTaggingSel: continue # Skipping roiftf step only when running the calo selection leg (and if in the DIPZ scenario)
105 
106  assert not ( (hasBjetSel or hasDIPZsel) and not doTaggingSel), "Your jet preselection has a b-jet or DIPZ part but a calo-only preselection was requested instead. This should not be possible. Please investigate."
107 
108  pattern_to_test = r'(?P<mult>\d?\d?)(?P<region>[jacf])' # jet multiplicity and region
109  pattern_to_test += r'(?P<scenario>(HT)?)(?P<cut>\d+)' # scenario string # could be made more general
110  pattern_to_test += r'b(?P<btagger>\D*)(?P<bwp>\d+)' if hasBjetSel else '' # b-tagging if needed
111  pattern_to_test += r'gntau(?P<tauwp>\d\d)' if hasTauSel else '' # tau preselection if needed
112  pattern_to_test += r'emf(?P<emfc>\d+)' if hascalSel else ''
113  if hasDIPZsel: pattern_to_test = r'(?P<scenario>Z)((?P<dipzwp>\d+))?(?P<prefilt>(MAXMULT\d+[jacf]?)?)'
114  matched = re.match(pattern_to_test, p)
115  assert matched is not None, "Impossible to extract preselection cut for \'{0}\' substring. Please investigate.".format(p)
116  cut_dict = matched.groupdict()
117  if hasDIPZsel: cut_dict['region'] = 'c'
118  if hasDIPZsel: cut_dict['cut'] = ptCut
119 
120 
121  # any missing keys k below need to have cut_dict[k] set to "".
122  testkeys = \
123  [ "mult" , "emfc" , "cut" , "cut_add" , "prefilt"
124  , "bwp" , "btagger" , "dipzwp"
125  , "tauwp"
126  ]
127 
128 
129  for k in testkeys:
130  cut_dict.setdefault(k, "")
131 
132  # why oh why is it written this way?
133  mult,region,scenario,cut,btagger,bwp,dipzwp,emfc = \
134  cut_dict['mult'],cut_dict['region'],cut_dict['scenario'],cut_dict['cut'],cut_dict['btagger'],cut_dict['bwp'],cut_dict['dipzwp'],cut_dict['emfc']
135 
136  tauwp = cut_dict["tauwp"]
137 
138  prefilters = []
139 
140  if mult=='': mult='1'
141  etarange = etaRangeAbbrev[region]
142  if scenario == "HT":
143  hyposcenario=f'HT{cut}XX{etarange}'
144  threshold='0'
145  chainPartName=f'j0_{hyposcenario}'
146  elif scenario == "Z":
147  hyposcenario=f'Z{dipzwp}XX{nCentralJets}c{cut}'
148  prefilt = cut_dict['prefilt']
149  if prefilt != '': prefilters.append(prefilt)
150  threshold='0'
151  chainPartName=f'j0_{hyposcenario}'
152  else:
153  hyposcenario='simple'
154  threshold=cut
155  chainPartName=f'{mult}j{cut}_{etarange}'
156 
157  if btagger == 'g':
158  btagger = 'gnone'
159  elif btagger =='gtwo':
160  btagger = 'gntwo'
161  elif btagger == '':
162  btagger = 'dips'
163 
164  tmpChainDict = dict(preselCommonJetParts)
165  tmpChainDict.update(
166  {'L1threshold': 'FSNOSEED',
167  'chainPartName': chainPartName,
168  'multiplicity': mult,
169  'threshold': threshold,
170  'etaRange':etarange,
171  'jvt':'',
172  'clrsel': emfc,
173  'bsel': '' if bwp == '' else f'{bwp}b{btagger}',
174  'tausel': "" if tauwp == '' else f'{tauwp}gntau',
175  'chainPartIndex': ip,
176  'hypoScenario': hyposcenario,
177  'prefilters': prefilters,
178  }
179  )
180  preselChainDict['chainParts'] += [tmpChainDict]
181 
182 
183  # We need to pad by the legs not in the preselection expression
184  # otherwise the ComboHypo does not find the corresponding
185  # legs in the DecisionObject and kills the event
186  jetlegs = sum([p['signature'] in ["Jet","Bjet"] for p in mainChainDict['chainParts']])
187  padding = jetlegs-len(preselChainDict['chainParts'])
188  if padding>0:
189  preselChainDict['chainParts'][-1]['chainPartName']+='_SHARED'
190  preselChainDict['chainParts'][-1]['tboundary']='SHARED'
191  dummyLegPart = dict(preselCommonJetParts)
192  dummyLegPart.update(
193  {'L1threshold': 'FSNOSEED',
194  'chainPartName': 'j0_SHARED',
195  'multiplicity': '1',
196  'threshold': '0',
197  'jvt':'',
198  'tboundary': 'SHARED',
199  }
200  )
201  preselChainDict['chainParts'] += [dict(dummyLegPart) for i in range(padding)]
202  # Last one is not permitted to be shared as there is nothing following
203  preselChainDict['chainParts'][-1]['chainPartName']='j0'
204  preselChainDict['chainParts'][-1]['tboundary']=''
205 
206  # Update certain keys to be consistent with the main dict
207  # Also set the chainPart indices correctly from the main dict
208  # These should be index-parallel now, as we only receive jet chainParts
209  for porig,ppresel in zip(mainChainDict['chainParts'],preselChainDict['chainParts']):
210  for key in ['chainPartIndex','signature']:
211  ppresel[key] = porig[key]
212 
213  assert(len(preselChainDict['chainParts'])==len(mainChainDict['chainParts']))
214  try:
215  return trigJetHypoToolFromDict(flags, preselChainDict)
216  except NoHypoToolCreated as nohypo:
217  raise nohypo # We only generate the hypo tool for the first jet leg
218  except Exception as e:
219  log.error("Failure with preselection for chain %s",mainChainDict['chainName'])
220  raise e
vtune_athena.format
format
Definition: vtune_athena.py:14
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
python.HLT.Jet.JetPresel.getPreselRecoDict
def getPreselRecoDict(reco, roiftf=False)
Definition: JetPresel.py:17
convertTimingResiduals.sum
sum
Definition: convertTimingResiduals.py:55
python.HLT.Jet.JetRecoCommon.getJetCalibDefaultString
def getJetCalibDefaultString(recoAlg, constitType, trkopt)
Definition: JetRecoCommon.py:262
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:195
min
#define min(a, b)
Definition: cfImp.cxx:40
python.HLT.Jet.JetRecoCommon.jetRecoDictToString
def jetRecoDictToString(jetRecoDict)
— General reco dict handling —
Definition: JetRecoCommon.py:44
python.HLT.Jet.JetPresel._preselJetHypoToolFromDict
def _preselJetHypoToolFromDict(flags, mainChainDict, doTaggingSel=False)
Definition: JetPresel.py:63
python.HLT.Jet.JetPresel.roiPreselJetHypoToolFromDict
def roiPreselJetHypoToolFromDict(flags, mainChainDict)
Definition: JetPresel.py:59
python.HLT.Jet.JetRecoCommon.jetChainParts
def jetChainParts(chainParts)
— Extracting jet chain parts —
Definition: JetRecoCommon.py:33
python.TrigJetHypoToolConfig.trigJetHypoToolFromDict
def trigJetHypoToolFromDict(flags, chain_dict)
Definition: TrigJetHypoToolConfig.py:20
python.HLT.Jet.JetPresel.caloPreselJetHypoToolFromDict
def caloPreselJetHypoToolFromDict(flags, mainChainDict)
Definition: JetPresel.py:55
xAOD::bool
setBGCode setTAP setLVL2ErrorBits bool
Definition: TrigDecision_v1.cxx:60
python.HLT.Jet.JetPresel.extractPreselection
def extractPreselection(fullChainDict)
Definition: JetPresel.py:47