ATLAS Offline Software
JetPresel.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2025 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>(VETOMULT)?\d?\d?(Z[\d\D]+)?[jacf]\d?(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|bg|bgtwo|gntau|uht1tau)\d\d', '', p)
97 
98  hasBjetSel = bool(re.match(r'.*(b|bg|bgtwo)\d\d', p))
99  hasTauSel = bool(re.match(r'.*(gntau|uht1tau)\d\d', p))
100  hasDIPZsel = bool(re.match(r'.*Z', p))
101 
102  if hasDIPZsel and not doTaggingSel: continue # Skipping calopresel step when DIPZ is run
103  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)
104 
105  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."
106 
107  bmatches = r'(?P<btagger>(b|bg|bgtwo))(?P<bwp>\d\d)' if hasBjetSel else ""
108  taumatches = r'(?P<tauid>(gntau|uht1tau))(?P<tauwp>\d\d)' if hasTauSel else ""
109 
110  pattern_to_test = r'(?P<veto>(VETOMULT))?' # veto flag #TODO veto pt as well
111  pattern_to_test += r'(?P<mult>\d*)?(?P<region>[jacf])' # jet multiplicity and region and ptlow
112  pattern_to_test += r'(?P<scenario>(HT))?(?P<cut>\d+)?' # scenario string # could be made more general
113  pattern_to_test += bmatches
114  pattern_to_test += taumatches
115  pattern_to_test += r'emf(?P<emfc>\d+)' if hascalSel else ''
116  if hasDIPZsel: pattern_to_test = r'(?P<scenario>Z)(?P<dipzwp>\d+)?(?P<prefilt>(MAXMULT\d+[jacf]?)?)'
117  matched = re.match(pattern_to_test, p)
118  assert matched is not None, "Impossible to extract preselection cut for \'{0}\' substring. Please investigate.".format(p)
119  cut_dict = matched.groupdict()
120  if hasDIPZsel: cut_dict['region'] = 'c'
121  if hasDIPZsel: cut_dict['cut'] = ptCut
122 
123 
124  # any missing keys k below need to have cut_dict[k] set to "".
125  testkeys = \
126  [ "mult" , "emfc" , "cut" , "cut_add" , "prefilt"
127  , "bwp" , "btagger" , "dipzwp"
128  , "tauwp"
129  ]
130 
131 
132  for k in testkeys:
133  cut_dict.setdefault(k, "")
134 
135  veto = 'veto' in cut_dict
136  if veto:
137  veto = cut_dict['veto'] is not None
138  mult = cut_dict['mult']
139  region = cut_dict['region']
140  scenario = cut_dict['scenario']
141  cut = cut_dict['cut']
142  dipzwp = cut_dict['dipzwp']
143  emfc = cut_dict['emfc']
144  prefilters = []
145 
146  if mult=='': mult='1'
147  etarange = etaRangeAbbrev[region]
148  if veto:
149  scenario = cut_dict['veto'][4:]
150  assert (not (scenario in ['HT','Z'])), "Veto preselection not yet supported for HT or Z scenarios. Please investigate."
151  if scenario == "HT":
152  hyposcenario=f'HT{cut}XX{etarange}'
153  threshold='0'
154  chainPartName=f'j0_{hyposcenario}'
155  elif scenario == "Z":
156  hyposcenario=f'Z{dipzwp}XX{nCentralJets}c{cut}'
157  prefilt = cut_dict['prefilt']
158  if prefilt != '': prefilters.append(prefilt)
159  threshold='0'
160  chainPartName=f'j0_{hyposcenario}'
161  elif scenario == "MULT":
162  hyposcenario=f'MULT0mult{mult}XX{cut}ptXX{etarange}'
163  threshold='0'
164  chainPartName=f'j0_{hyposcenario}'
165  log.info(f'Generated chainPartName {chainPartName}')
166  else:
167  hyposcenario='simple'
168  threshold=cut
169  chainPartName=f'{mult}j{cut}_{etarange}'
170 
171  if hasBjetSel:
172  btagger = cut_dict['btagger']
173  bwp = cut_dict['bwp']
174 
175  if btagger == 'bg':
176  btagger = 'gnone'
177  elif btagger =='bgtwo':
178  btagger = 'bgntwo'
179  elif btagger == 'b':
180  btagger = 'bdips'
181 
182 
183  tmpChainDict = dict(preselCommonJetParts)
184 
185  if hasTauSel:
186  tauid = cut_dict["tauid"]
187  tauwp = cut_dict["tauwp"]
188 
189  tmpChainDict[tauid] = tauwp+tauid
190 
191  tmpChainDict.update(
192  {'L1threshold': 'FSNOSEED',
193  'chainPartName': chainPartName,
194  'multiplicity': mult,
195  'threshold': threshold,
196  'etaRange':etarange,
197  'jvt':'',
198  'clrsel': emfc,
199  'bsel': f'{bwp}{btagger}' if hasBjetSel else "",
200  'chainPartIndex': ip,
201  'hypoScenario': hyposcenario,
202  'prefilters': prefilters,
203  }
204  )
205 
206  preselChainDict['chainParts'] += [tmpChainDict]
207 
208 
209  # We need to pad by the legs not in the preselection expression
210  # otherwise the ComboHypo does not find the corresponding
211  # legs in the DecisionObject and kills the event
212  jetlegs = sum([p['signature'] in ["Jet","Bjet"] for p in mainChainDict['chainParts']])
213  padding = jetlegs-len(preselChainDict['chainParts'])
214  if padding>0:
215  preselChainDict['chainParts'][-1]['chainPartName']+='_SHARED'
216  preselChainDict['chainParts'][-1]['tboundary']='SHARED'
217  dummyLegPart = dict(preselCommonJetParts)
218  dummyLegPart.update(
219  {'L1threshold': 'FSNOSEED',
220  'chainPartName': 'j0_SHARED',
221  'multiplicity': '1',
222  'threshold': '0',
223  'jvt':'',
224  'tboundary': 'SHARED',
225  }
226  )
227  preselChainDict['chainParts'] += [dict(dummyLegPart) for i in range(padding)]
228  # Last one is not permitted to be shared as there is nothing following
229  preselChainDict['chainParts'][-1]['chainPartName']='j0'
230  preselChainDict['chainParts'][-1]['tboundary']=''
231 
232  # Update certain keys to be consistent with the main dict
233  # Also set the chainPart indices correctly from the main dict
234  # These should be index-parallel now, as we only receive jet chainParts
235  for porig,ppresel in zip(mainChainDict['chainParts'],preselChainDict['chainParts']):
236  for key in ['chainPartIndex','signature']:
237  ppresel[key] = porig[key]
238 
239  assert(len(preselChainDict['chainParts'])==len(mainChainDict['chainParts']))
240  try:
241  return trigJetHypoToolFromDict(flags, preselChainDict)
242  except NoHypoToolCreated as nohypo:
243  raise nohypo # We only generate the hypo tool for the first jet leg
244  except Exception as e:
245  log.error("Failure with preselection for chain %s",mainChainDict['chainName'])
246  raise e
vtune_athena.format
format
Definition: vtune_athena.py:14
min
constexpr double min()
Definition: ap_fixedTest.cxx:26
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:264
plotBeamSpotVxVal.range
range
Definition: plotBeamSpotVxVal.py:194
python.HLT.Jet.JetRecoCommon.jetRecoDictToString
def jetRecoDictToString(jetRecoDict)
— General reco dict handling —
Definition: JetRecoCommon.py:45
python.HLT.Jet.JetPresel._preselJetHypoToolFromDict
def _preselJetHypoToolFromDict(flags, mainChainDict, doTaggingSel=False)
Definition: JetPresel.py:63
python.CaloAddPedShiftConfig.int
int
Definition: CaloAddPedShiftConfig.py:45
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:34
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