ATLAS Offline Software
Loading...
Searching...
No Matches
JetAnalysisConfig.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3
4
5# AnaAlgorithm import(s):
6from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
7from AnalysisAlgorithmsConfig.ConfigSequence import groupBlocks
8from AnalysisAlgorithmsConfig.ConfigAccumulator import DataType
9from AthenaCommon.SystemOfUnits import GeV
10from AthenaConfiguration.Enums import LHCPeriod
11from AthenaCommon.Logging import logging
12import re
13
14
15class PreJetAnalysisConfig (ConfigBlock) :
16 """the ConfigBlock for the common preprocessing of jet sequences"""
17
18 def __init__ (self) :
19 super (PreJetAnalysisConfig, self).__init__ ()
20 self.addOption ('containerName', '', type=str,
21 noneAction='error',
22 info="the name of the output container after calibration.")
23 self.addOption ('jetCollection', '', type=str,
24 noneAction='error',
25 info="the jet container to run on. It is interpreted to determine "
26 "the correct config blocks to call for small- or large-R jets.")
27 self.addOption('outputTruthLabelIDs', False, type=bool,
28 info='enable or disable `HadronConeExclTruthLabelID` and `PartonTruthLabelID` decorations.')
29 self.addOption ('runOriginalObjectLink', False, type=bool,
30 info='sets up an instance of `CP::AsgOriginalObjectLinkAlg` to link systematically-varied containers to the base one.')
31 self.addOption ('runGhostMuonAssociation', None, type=bool,
32 info="whether to set up the jet-ghost-muon association algorithm "
33 "`CP::JetGhostMuonAssociationAlg`. If left empty, automatically defaults to `False` for PHYSLITE and `True` otherwise.")
34 self.addOption ('runTruthJetTagging', True, type=bool,
35 info="whether to set up the jet truth tagging algorithm "
36 "`CP::JetTruthTagAlg`.")
37
38 def instanceName (self) :
39 """Return the instance name for this block"""
40 return self.containerName
41
42 def makeAlgs (self, config) :
43
44
45 if config.isPhyslite() and self.jetCollection == 'AntiKt4EMPFlowJets' :
46 config.setSourceName (self.containerName, "AnalysisJets", originalName = self.jetCollection)
47 elif config.isPhyslite() and self.jetCollection == 'AntiKt10UFOCSSKSoftDropBeta100Zcut10Jets' :
48 config.setSourceName (self.containerName, "AnalysisLargeRJets", originalName = self.jetCollection)
49 else :
50 config.setSourceName (self.containerName, self.jetCollection, originalName = self.jetCollection)
51
52 # Relink original jets in case of b-tagging calibration
54 alg = config.createAlgorithm( 'CP::AsgOriginalObjectLinkAlg',
55 'JetOriginalObjectLinkAlg',
56 reentrant=True )
57 alg.baseContainerName = self.jetCollection
58 alg.particles = config.readName (self.containerName)
59 if config.wantCopy (self.containerName) :
60 alg.particlesOut = config.copyName (self.containerName)
61 alg.preselection = config.getPreselection (self.containerName, '')
62
63 # Set up the jet ghost muon association algorithm:
64 if (self.runGhostMuonAssociation is None and not config.isPhyslite()) or \
65 (self.runGhostMuonAssociation is True):
66 alg = config.createAlgorithm( 'CP::JetGhostMuonAssociationAlg',
67 'JetGhostMuonAssociationAlg' )
68 alg.jets = config.readName (self.containerName)
69 if config.isPhyslite():
70 alg.muons = "AnalysisMuons"
71 if config.wantCopy (self.containerName) :
72 alg.jetsOut = config.copyName (self.containerName)
73 taggerName = "GN2HL" if config.geometry() >= LHCPeriod.Run4 else "GN2v01"
74 extraInputs = [
75 ( 'xAOD::JetContainer' , "StoreGateSvc+{baseName}.DFCommonJets_jetClean_LooseBad".format(baseName=self.jetCollection)),
76 ( 'xAOD::JetContainer' , "StoreGateSvc+{baseName}.DFCommonJets_jetClean_TightBad".format(baseName=self.jetCollection)),
77 ( 'xAOD::JetContainer' , "StoreGateSvc+{baseName}.{tagger}_pb".format(baseName=self.jetCollection, tagger=taggerName)),
78 ( 'xAOD::JetContainer' , "StoreGateSvc+{baseName}.{tagger}_pc".format(baseName=self.jetCollection, tagger=taggerName)),
79 ( 'xAOD::JetContainer' , "StoreGateSvc+{baseName}.{tagger}_ptau".format(baseName=self.jetCollection, tagger=taggerName)),
80 ( 'xAOD::JetContainer' , "StoreGateSvc+{baseName}.{tagger}_pu".format(baseName=self.jetCollection, tagger=taggerName))]
81 config.setExtraInputs (extraInputs)
82
83 if self.runTruthJetTagging and config.dataType() is not DataType.Data:
84 # Decorate jets with isHS labels (required to retrieve Jvt SFs)
85 alg = config.createAlgorithm( 'CP::JetDecoratorAlg', 'JetPileupLabelAlg' )
86 config.addPrivateTool( 'decorator', 'JetPileupLabelingTool' )
87 alg.jets = config.readName (self.containerName)
88 alg.jetsOut = config.copyName (self.containerName)
89 alg.decorator.RecoJetContainer = alg.jetsOut.replace ('%SYS%', 'NOSYS')
90 alg.decorator.SuppressOutputDependence=True
91 if self.jetCollection == 'AntiKt10UFOCSSKSoftDropBeta100Zcut10Jets' :
92 extraInputs = [
93 ( 'xAOD::JetContainer' , "StoreGateSvc+{baseName}.GN2Xv01_phbb".format(baseName= self.jetCollection)),
94 ( 'xAOD::JetContainer' , "StoreGateSvc+{baseName}.GN2Xv01_phcc".format(baseName= self.jetCollection)),
95 ( 'xAOD::JetContainer' , "StoreGateSvc+{baseName}.GN2Xv01_pqcd".format(baseName= self.jetCollection)),
96 ( 'xAOD::JetContainer' , "StoreGateSvc+{baseName}.GN2Xv01_ptop".format(baseName= self.jetCollection))]
97 config.setExtraInputs (extraInputs)
98
99 # Set up shallow copy if needed and not yet done
100 if config.wantCopy (self.containerName) :
101 alg = config.createAlgorithm( 'CP::AsgShallowCopyAlg', 'JetShallowCopyAlg' )
102 alg.input = config.readName (self.containerName)
103 alg.output = config.copyName (self.containerName)
104 alg.outputType = 'xAOD::JetContainer'
105 if self.jetCollection == 'AntiKt10UFOCSSKSoftDropBeta100Zcut10Jets' :
106 alg.declareDecorations = ['GN2Xv01_phbb', 'GN2Xv01_phcc',
107 'GN2Xv01_pqcd', 'GN2Xv01_ptop']
108
109 config.addOutputVar (self.containerName, 'pt', 'pt')
110 config.addOutputVar (self.containerName, 'eta', 'eta', noSys=True)
111 config.addOutputVar (self.containerName, 'phi', 'phi', noSys=True)
112 config.addOutputVar (self.containerName, 'charge', 'charge', noSys=True, enabled=False)
113
114 if self.outputTruthLabelIDs and config.dataType() is not DataType.Data:
115 config.addOutputVar (self.containerName, 'HadronConeExclTruthLabelID', 'HadronConeExclTruthLabelID', noSys=True, auxType="int")
116 config.addOutputVar (self.containerName, 'PartonTruthLabelID', 'PartonTruthLabelID', noSys=True, auxType="int")
117
118
119
120class SmallRJetAnalysisConfig (ConfigBlock) :
121 """the ConfigBlock for the small-r jet sequence"""
122
123 def __init__ (self) :
124 super (SmallRJetAnalysisConfig, self).__init__ ()
125 self.addOption ('containerName', '', type=str,
126 noneAction='error',
127 info="the name of the output container after calibration.")
128 self.addOption ('jetCollection', '', type=str,
129 noneAction='error',
130 info="the jet container to run on. It is interpreted to determine "
131 "the correct config blocks to call for small- or large-R jets.")
132 self.addOption ('jetInput', '', type=str,
133 noneAction='error',
134 info="the type of jet input. Supported options are: `EMPFlow`, `EMTopo`, `HI`.")
135 self.addOption ('runJvtUpdate', False, type=bool,
136 info="whether to update the JVT.")
137 self.addOption ('runNNJvtUpdate', False, type=bool,
138 info="whether to update the NN-JVT.")
139 self.addOption ('runJvtSelection', True, type=bool,
140 info="whether to run JVT selection.")
141 self.addOption ('runFJvtSelection', False, type=bool,
142 info="whether to run forward JVT selection.")
143 self.addOption ('jvtWP', "FixedEffPt", type=str,
144 info="which Jvt WP to apply.")
145 self.addOption ('fJvtWP', "Loose", type=str,
146 info="which fJvt WP to apply.")
147 self.addOption ('runJvtEfficiency', True, type=bool,
148 info="whether to calculate the JVT efficiency.")
149 self.addOption ('runFJvtEfficiency', False, type=bool,
150 info="whether to calculate the forward JVT efficiency.")
151 self.addOption ('recalibratePhyslite', True, type=bool,
152 info="whether to run the `CP::JetCalibrationAlg` on PHYSLITE derivations.")
153 # Calibration tool options
154 self.addOption ('calibToolConfigFile', None, type=str,
155 info="the name of the config file to use for the jet calibration "
156 "tool. Expert option to override JetETmiss recommendations.",
157 expertMode=True)
158 self.addOption ('calibToolCalibArea', None, type=str,
159 info="name of the CVMFS area to use for the jet calibration "
160 "tool. Expert option to override JetETmiss recommendations.",
161 expertMode=True)
162 self.addOption ('calibToolCalibSeq', None, type=str,
163 info="name of the sequence to use for the jet calibration "
164 "tool (e.g. `JetArea_Residual_EtaJES_GSC`). Expert option to override "
165 "JetETmiss recommendations.",
166 expertMode=True)
167
168 def instanceName (self) :
169 """Return the instance name for this block"""
170 return self.containerName
171
172 def makeAlgs (self, config) :
173
174 jetCollectionName=self.jetCollection
175 if(self.jetCollection=="AnalysisJets") :
176 jetCollectionName="AntiKt4EMPFlowJets"
177 if(self.jetCollection=="AnalysisLargeRJets") :
178 jetCollectionName="AntiKt10UFOCSSKSoftDropBeta100Zcut10Jets"
179
180 if self.jetInput not in ["EMTopo", "EMPFlow", "HI"]:
181 raise ValueError(
182 "Unsupported input type '{0}' for R=0.4 jets!".format(self.jetInput) )
183
184 if self.jvtWP not in ["FixedEffPt"]:
185 raise ValueError(
186 "Unsupported NNJvt WP '{0}'".format(self.jvtWP) )
187
188 if self.fJvtWP not in ["Loose", "Tight", "Tighter"]:
189 raise ValueError(
190 "Unsupported fJvt WP '{0}'".format(self.fJvtWP) )
191
192 if not config.isPhyslite() or self.recalibratePhyslite:
193 # Create calibration tool as public so it can be shared
194 # (e.g. with FTAG1LITE's JetCalibratedPtDecoratorAlg).
195 # Must be created before the algorithm so EventLoop
196 # initialises the tool first.
197 calibToolName = 'JetCalibTool_' + jetCollectionName[:-4]
198 calibTool = config.createPublicTool( 'JetCalibrationTool', calibToolName )
199 calibTool.JetCollection = jetCollectionName[:-4]
200 # Get the correct string to use in the config file name
201 if self.jetInput == "EMPFlow":
202 if config.geometry() is LHCPeriod.Run2:
203 configFile = "PreRec_R22_PFlow_ResPU_EtaJES_GSC_February23_230215.config"
204 calibTool.CalibArea = "00-04-82"
205 elif config.geometry() >= LHCPeriod.Run3:
206 configFile = "AntiKt4EMPFlow_MC23a_PreRecR22_Phase2_CalibConfig_ResPU_EtaJES_GSC_241208_InSitu.config"
207 calibTool.CalibArea = "00-04-83"
208 elif self.jetInput == "HI":
209 if config.geometry() is LHCPeriod.Run2:
210 configFile = "JES_MC16_HI_Jan2021_5TeV.config"
211 if config.geometry() is LHCPeriod.Run3:
212 configFile = "AntiKt4HI_MC23_EtaJES_Run3PreRec_Run2VJet_Run3EtaInt_5p36TeV.config"
213 calibTool.CalibArea = "00-04-83"
214 else:
215 if config.dataType() is DataType.FastSim:
216 configFile = "JES_MC16Recommendation_AFII_{0}_Apr2019_Rel21.config"
217 else:
218 configFile = "JES_MC16Recommendation_Consolidated_{0}_Apr2019_Rel21.config"
219 configFile = configFile.format(self.jetInput)
220 if self.calibToolCalibArea is not None:
221 calibTool.CalibArea = self.calibToolCalibArea
222 if self.calibToolConfigFile is not None:
223 configFile = self.calibToolConfigFile
224 calibTool.ConfigFile = configFile
225 if config.dataType() is DataType.Data:
226 if self.jetInput == "HI":
227 calibTool.CalibSequence = 'EtaJES_Insitu'
228 else:
229 calibTool.CalibSequence = 'JetArea_Residual_EtaJES_GSC_Insitu'
230 else:
231 if self.jetInput == "EMPFlow":
232 calibTool.CalibSequence = 'JetArea_Residual_EtaJES_GSC'
233 elif self.jetInput == "HI":
234 calibTool.CalibSequence = 'EtaJES'
235 else:
236 calibTool.CalibSequence = 'JetArea_Residual_EtaJES_GSC_Smear'
237 if self.calibToolCalibSeq is not None:
238 calibTool.CalibSequence = self.calibToolCalibSeq
239 calibTool.IsData = (config.dataType() is DataType.Data)
240 # Prepare the jet calibration algorithm
241 alg = config.createAlgorithm( 'CP::JetCalibrationAlg', 'JetCalibrationAlg' )
242 alg.HIsetup = self.jetInput == "HI"
243 alg.calibrationTool = f'{calibTool.getType()}/{calibTool.getName()}'
244 alg.jets = config.readName (self.containerName)
245 alg.jetsOut = config.copyName (self.containerName)
246
247 # Set up the JVT update algorithm:
248 if self.runJvtUpdate :
249 alg = config.createAlgorithm( 'CP::JvtUpdateAlg', 'JvtUpdateAlg' )
250 config.addPrivateTool( 'jvtTool', 'JetVertexTaggerTool' )
251 alg.jvtTool.JetContainer = self.jetCollection
252 alg.jvtTool.SuppressInputDependence=True
253 alg.jets = config.readName (self.containerName)
254 alg.jetsOut = config.copyName (self.containerName)
255 alg.preselection = config.getPreselection (self.containerName, '')
256
258 assert self.jetInput=="EMPFlow", "NN JVT only defined for PFlow jets"
259 alg = config.createAlgorithm( 'CP::JetDecoratorAlg', 'NNJvtUpdateAlg' )
260 config.addPrivateTool( 'decorator', 'JetPileupTag::JetVertexNNTagger' )
261 # Set this actually to the *output* collection
262 alg.jets = config.readName (self.containerName)
263 alg.jetsOut = config.copyName (self.containerName)
264 alg.decorator.JetContainer = alg.jetsOut.replace ('%SYS%', 'NOSYS')
265 alg.decorator.SuppressInputDependence=True
266 alg.decorator.SuppressOutputDependence=True
267
268 # Set up the jet efficiency scale factor calculation algorithm
269 # Change the truthJetCollection property to AntiKt4TruthWZJets if preferred
271 assert self.jetInput=="EMPFlow", "NNJvt WPs and SFs only valid for PFlow jets"
272 alg = config.createAlgorithm('CP::AsgSelectionAlg', 'JvtSelectionAlg')
273 config.addPrivateTool('selectionTool', 'CP::NNJvtSelectionTool')
274 alg.selectionTool.JetContainer = config.readName(self.containerName)
275 alg.selectionTool.JvtMomentName = "NNJvt"
276 alg.selectionTool.WorkingPoint = self.jvtWP
277 alg.selectionTool.MaxPtForJvt = 60*GeV
278 alg.selectionDecoration = "jvt_selection,as_char"
279 alg.particles = config.readName(self.containerName)
280
281 if self.runJvtEfficiency and config.dataType() is not DataType.Data:
282 alg = config.createAlgorithm( 'CP::JvtEfficiencyAlg', 'JvtEfficiencyAlg' )
283 config.addPrivateTool( 'efficiencyTool', 'CP::NNJvtEfficiencyTool' )
284 alg.efficiencyTool.JetContainer = config.readName(self.containerName)
285 alg.efficiencyTool.MaxPtForJvt = 60*GeV
286 alg.efficiencyTool.WorkingPoint = self.jvtWP
287 if config.geometry() is LHCPeriod.Run2:
288 alg.efficiencyTool.SFFile = "JetJvtEfficiency/May2024/NNJvtSFFile_Run2_EMPFlow.root"
289 else:
290 alg.efficiencyTool.SFFile = "JetJvtEfficiency/May2024/NNJvtSFFile_Run3_EMPFlow.root"
291 alg.selection = 'jvt_selection,as_char'
292 alg.scaleFactorDecoration = 'jvt_effSF_%SYS%'
293 alg.outOfValidity = 2
294 alg.outOfValidityDeco = 'no_jvt'
295 alg.skipBadEfficiency = False
296 alg.jets = config.readName (self.containerName)
297 alg.preselection = config.getPreselection (self.containerName, '')
298 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, 'jvtEfficiency')
299 config.addSelection (self.containerName, 'baselineJvt', 'jvt_selection,as_char', preselection=False)
300
302 assert self.jetInput=="EMPFlow", "fJvt WPs and SFs only valid for PFlow jets"
303 alg = config.createAlgorithm('CP::AsgSelectionAlg', 'FJvtSelectionAlg')
304 config.addPrivateTool('selectionTool', 'CP::FJvtSelectionTool')
305 alg.selectionTool.JetContainer = config.readName(self.containerName)
306 alg.selectionTool.JvtMomentName = "DFCommonJets_fJvt"
307 alg.selectionTool.WorkingPoint = self.fJvtWP
308 alg.selectionDecoration = "fjvt_selection,as_char"
309 alg.particles = config.readName(self.containerName)
310
311 if self.runFJvtEfficiency and config.dataType() is not DataType.Data:
312 alg = config.createAlgorithm( 'CP::JvtEfficiencyAlg', 'FJvtEfficiencyAlg' )
313 config.addPrivateTool( 'efficiencyTool', 'CP::FJvtEfficiencyTool' )
314 alg.efficiencyTool.JetContainer = config.readName(self.containerName)
315 alg.efficiencyTool.WorkingPoint = self.fJvtWP
316 if config.geometry() is LHCPeriod.Run2:
317 alg.efficiencyTool.SFFile = "JetJvtEfficiency/May2024/fJvtSFFile_Run2_EMPFlow.root"
318 else:
319 alg.efficiencyTool.SFFile = "JetJvtEfficiency/May2024/fJvtSFFile_Run3_EMPFlow.root"
320 alg.selection = 'fjvt_selection,as_char'
321 alg.scaleFactorDecoration = 'fjvt_effSF_%SYS%'
322 alg.outOfValidity = 2
323 alg.outOfValidityDeco = 'no_fjvt'
324 alg.skipBadEfficiency = False
325 alg.jets = config.readName (self.containerName)
326 alg.preselection = config.getPreselection (self.containerName, '')
327 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, 'fjvtEfficiency')
328 config.addSelection (self.containerName, 'baselineFJvt', 'fjvt_selection,as_char', preselection=False)
329
330
331class RScanJetAnalysisConfig (ConfigBlock) :
332 """the ConfigBlock for the r-scan jet sequence"""
333
334 def __init__ (self) :
335 super (RScanJetAnalysisConfig, self).__init__ ()
336 self.addOption ('containerName', '', type=str,
337 noneAction='error',
338 info="the name of the output container after calibration.")
339 self.addOption ('jetCollection', '', type=str,
340 noneAction='error',
341 info="the jet container to run on. It is interpreted to determine "
342 "the correct config blocks to call for small- or large-R jets.")
343 # TODO: add info string
344 self.addOption ('jetInput', '', type=str,
345 noneAction='error',
346 info="")
347 # TODO: add info string
348 self.addOption ('radius', None, type=int,
349 noneAction='error',
350 info="")
351 self.addOption ('recalibratePhyslite', True, type=bool,
352 info="whether to run the CP::JetCalibrationAlg on PHYSLITE "
353 "derivations. The default is True.")
354
355 def instanceName (self) :
356 """Return the instance name for this block"""
357 return self.containerName
358
359 def makeAlgs (self, config) :
360
361 log = logging.getLogger('RScanJetAnalysisConfig')
362
363 jetCollectionName=self.jetCollection
364 if(self.jetCollection=="AnalysisJets") :
365 jetCollectionName="AntiKt4EMPFlowJets"
366 if(self.jetCollection=="AnalysisLargeRJets") :
367 jetCollectionName="AntiKt10LCTopoTrimmedPtFrac5SmallR20Jets"
368
369 if not config.isPhyslite() or self.recalibratePhyslite:
370 if self.jetInput not in ["LCTopo", "HI"]:
371 raise ValueError(
372 "Unsupported input type '{0}' for R-scan jets!".format(self.jetInput) )
373 # Create calibration tool before algorithm (EventLoop ordering)
374 calibToolName = 'JetCalibTool_' + jetCollectionName[:-4]
375 calibTool = config.createPublicTool( 'JetCalibrationTool', calibToolName )
376 calibTool.JetCollection = jetCollectionName[:-4]
377 if self.jetInput=="LCTopo":
378 calibTool.ConfigFile = \
379 "JES_MC16Recommendation_Rscan{0}LC_Feb2022_R21.config".format(self.radius)
380 if config.dataType() is DataType.Data:
381 calibTool.CalibSequence = "JetArea_Residual_EtaJES_GSC_Insitu"
382 else:
383 calibTool.CalibSequence = "JetArea_Residual_EtaJES_GSC_Smear"
384 elif self.jetInput=="HI":
385 calibTool.ConfigFile = \
386 "JES_MC16_HI_Jan2021_5TeV.config"
387 if config.dataType() is DataType.Data:
388 calibTool.CalibSequence = "EtaJES_Insitu"
389 else:
390 calibTool.CalibSequence = "EtaJES"
391 calibTool.IsData = (config.dataType() is DataType.Data)
392 # Prepare the jet calibration algorithm
393 alg = config.createAlgorithm( 'CP::JetCalibrationAlg', 'JetCalibrationAlg' )
394 alg.HIsetup = self.jetInput == "HI"
395 alg.calibrationTool = f'{calibTool.getType()}/{calibTool.getName()}'
396 alg.jets = config.readName (self.containerName)
397 # Logging would be good
398 log.warning("Uncertainties for R-Scan jets are not yet released!")
399
400
401class LargeRJetAnalysisConfig (ConfigBlock) :
402 """the ConfigBlock for the large-r jet sequence"""
403
404 def __init__ (self) :
405 super (LargeRJetAnalysisConfig, self).__init__ ()
406 self.addOption ('containerName', '', type=str,
407 noneAction='error',
408 info="the name of the output container after calibration.")
409 self.addOption ('jetCollection', '', type=str,
410 noneAction='error',
411 info="the jet container to run on. It is interpreted to determine "
412 "the correct config blocks to call for small- or large-R jets.")
413 self.addOption ('jetInput', '', type=str,
414 noneAction='error',
415 info="the type of jet input. Supported options are: `UFO`.")
416 self.addOption ('recalibratePhyslite', True, type=bool,
417 info="whether to run the `CP::JetCalibrationAlg` on PHYSLITE "
418 "derivations.")
419 self.addOption ('systematicsModelJMR', "Full", type=str,
420 info="the NP reduction scheme to use for JMR. Supported options are: `Full`, `Simple`.")
421 # Adding these options to override the jet uncertainty config file when we have new recommendations
422 # Calibration tool options
423 self.addOption ('calibToolConfigFile', None, type=str,
424 info="name of the config file to use for the jet calibration "
425 "tool. Expert option to override JetETmiss recommendations.",
426 expertMode=True)
427 self.addOption ('calibToolCalibArea', None, type=str,
428 info="name of the CVMFS area to use for the jet calibration "
429 "tool. Expert option to override JetETmiss recommendations.",
430 expertMode=True)
431 self.addOption ('calibToolCalibSeq', None, type=str,
432 info="name of the sequence to use for the jet calibration "
433 "tool (e.g. `JetArea_Residual_EtaJES_GSC`). Expert option to override "
434 "JetETmiss recommendations.",
435 expertMode=True)
436 # Uncertainties tool options
437 self.addOption ('uncertToolConfigPath', None, type=str,
438 info="name of the config file to use for the JES, JER, and JMS uncertainty "
439 "tool. Expert option to override JetETmiss recommendations.",
440 expertMode=True)
441 self.addOption ('uncertToolConfigPathJMR', None, type=str,
442 info="name of the config file to use for the JMR uncertainty "
443 "tool. Expert option to override JetETmiss recommendations.",
444 expertMode=True)
445 self.addOption ('minPt', 200.*GeV, type=float,
446 info=r"the minimum $p_\mathrm{T}$ cut (in MeV) to apply to calibrated large-R jets.")
447 self.addOption ('maxPt', 3000.*GeV, type=float,
448 info=r"the maximum $p_\mathrm{T}$ cut (in MeV) to apply to calibrated large-R jets.")
449 self.addOption ('maxEta', 0., type=float,
450 info=r"the maximum $\vert\eta\vert$ cut to apply to calibrated large-R jets.")
451 self.addOption ('maxRapidity', 2., type=float,
452 info="the maximum rapidity cut to apply to calibrated large-R jets.")
453 self.addOption ('minMass', 40.*GeV, type=float,
454 info="the minimum mass cut (in MeV) to apply to calibrated large-R jets.")
455 self.addOption ('maxMass', 600.*GeV, type=float,
456 info="the maximum mass cut (in MeV) to apply to calibrated large-R jets.")
457
458 def instanceName (self) :
459 """Return the instance name for this block"""
460 return self.containerName
461
462
463 def createFFSmearingTool(self, jetFFSmearingAlg, config):
464 # Retrieve appropriate large-R jet mass resolution recommendations for the FFJetSmearingTool.
465
466 log = logging.getLogger('LargeRJetAnalysisConfig')
467
468 # Config file:
469 if self.systematicsModelJMR in ["Simple", "Full"]:
470 config_file = f"R10_{self.systematicsModelJMR}JMR.config"
471 else:
472 raise ValueError(
473 f"Invalid request for systematicsModelJMR settings: {self.systematicsModelJMR}"
474 )
475
476 # Expert override for config path:
477 if self.uncertToolConfigPathJMR is not None:
478 config_file = self.uncertToolConfigPathJMR
479 else:
480 config_file = "rel22/Summer2025_PreRec/" + config_file
481 if config.geometry() is LHCPeriod.Run4:
482 log.warning("Uncertainties for UFO jets are not for Run 4!")
483
484 # MC type:
485 if config.geometry() is LHCPeriod.Run2:
486 if config.dataType() is DataType.FastSim:
487 mc_type = "MC20AF3"
488 else:
489 mc_type = "MC20"
490 elif config.geometry() >= LHCPeriod.Run3:
491 if config.dataType() is DataType.FastSim:
492 mc_type = "MC23AF3"
493 else:
494 mc_type = "MC23"
495
496 # Set up the FF smearing tool
497 config.addPrivateTool( 'FFSmearingTool', 'CP::FFJetSmearingTool')
498 jetFFSmearingAlg.FFSmearingTool.MassDef = "UFO"
499 jetFFSmearingAlg.FFSmearingTool.MCType = mc_type
500 jetFFSmearingAlg.FFSmearingTool.ConfigFile = config_file
501
502 def makeAlgs (self, config) :
503
504 configFile = None
505 calibSeq = None
506 calibArea = None
507
508 jetCollectionName=self.jetCollection
509 if(self.jetCollection=="AnalysisJets") :
510 jetCollectionName="AntiKt4EMPFlowJets"
511 if(self.jetCollection=="AnalysisLargeRJets") :
512 jetCollectionName="AntiKt10UFOCSSKSoftDropBeta100Zcut10Jets"
513
514 if self.jetInput not in ["UFO"]:
515 raise ValueError("Invalid input type '{0}' for large-R jets!".format(self.jetInput) )
516
517 configFile = "JES_MC20PreRecommendation_R10_UFO_CSSK_SoftDrop_JMS_R21Insitu_26Nov2024.config"
518 calibArea = "00-04-83"
519 if self.calibToolConfigFile is not None:
520 configFile = self.calibToolConfigFile
521
522 if config.dataType() is not DataType.Data:
523 calibSeq = "EtaJES_JMS"
524 elif config.dataType() is DataType.Data:
525 calibSeq = "EtaJES_JMS_Insitu"
526 if self.calibToolCalibSeq is not None:
527 calibSeq = self.calibToolCalibSeq
528
529 if self.calibToolCalibArea is not None:
530 calibArea = self.calibToolCalibArea
531
532 if not config.isPhyslite() or self.recalibratePhyslite:
533 # Create calibration tool before algorithm (EventLoop ordering)
534 calibToolName = 'JetCalibTool_' + jetCollectionName[:-4]
535 calibTool = config.createPublicTool( 'JetCalibrationTool', calibToolName )
536
537 calibTool.JetCollection = jetCollectionName[:-4]
538
539 if configFile is None:
540 raise ValueError(f'Unsupported: {self.jetInput=}, {config.dataType()=}')
541 calibTool.ConfigFile = configFile
542
543 if calibSeq is None:
544 raise ValueError(f'Unsupported: {self.jetInput=}, {config.dataType()=}')
545 calibTool.CalibSequence = calibSeq
546
547 if calibArea is not None:
548 calibTool.CalibArea = calibArea
549
550 calibTool.IsData = (config.dataType() is DataType.Data)
551 # Prepare the jet calibration algorithm
552 alg = config.createAlgorithm( 'CP::JetCalibrationAlg', 'JetCalibrationAlg' )
553 alg.calibrationTool = f'{calibTool.getType()}/{calibTool.getName()}'
554 alg.jets = config.readName(self.containerName)
555 alg.jetsOut = config.copyName(self.containerName)
556
557 if self.jetInput == "UFO" and config.dataType() is not DataType.Data:
558 # set up the FF smearing algorithm
559 alg = config.createAlgorithm( 'CP::JetFFSmearingAlg', 'JetFFSmearingAlg' )
560 self.createFFSmearingTool(alg, config)
561 alg.outOfValidity = 2 # SILENT
562 alg.outOfValidityDeco = 'outOfValidityJMR'
563 alg.jets = config.readName (self.containerName)
564 alg.jetsOut = config.copyName (self.containerName)
565 alg.preselection = config.getPreselection (self.containerName, '')
566
567 if self.minPt > 0 or self.maxPt > 0 or self.maxEta > 0 or self.maxRapidity > 0:
568 # Set up the the pt-eta selection
569 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'JetPtEtaCutAlg' )
570 alg.selectionDecoration = 'selectPtEta,as_bits'
571 config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
572 alg.selectionTool.minPt = self.minPt
573 alg.selectionTool.maxPt = self.maxPt
574 alg.selectionTool.maxEta = self.maxEta
575 alg.selectionTool.maxRapidity = self.maxRapidity
576 alg.particles = config.readName (self.containerName)
577 alg.preselection = config.getPreselection (self.containerName, '')
578 config.addSelection (self.containerName, '', alg.selectionDecoration,
579 preselection=True)
580
581 if self.minMass > 0 or self.maxMass > 0:
582 # Set up the the mass selection
583 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'JetMassCutAlg' )
584 alg.selectionDecoration = 'selectMass,as_bits'
585 config.addPrivateTool( 'selectionTool', 'CP::AsgMassSelectionTool' )
586 alg.selectionTool.minM = self.minMass
587 alg.selectionTool.maxM = self.maxMass
588 alg.particles = config.readName (self.containerName)
589 alg.preselection = config.getPreselection (self.containerName, '')
590 config.addSelection (self.containerName, '', alg.selectionDecoration,
591 preselection=True)
592
593 config.addOutputVar (self.containerName, 'm', 'm')
594
595# These algorithms set up the jet recommendations as-of 04/02/2019.
596# Jet recommendations:
597# https://atlas-jetetmiss.docs.cern.ch/recs/latest-recs/
598
599@groupBlocks
600def makeJetAnalysisConfig( seq, containerName, jetCollection,
601 runGhostMuonAssociation = None):
602 """Create a jet analysis algorithm sequence
603 The jet collection is interpreted and selects the correct function to call,
604 makeSmallRJetAnalysisConfig, makeRScanJetAnalysisConfig or
605 makeLargeRJetAnalysisConfig
606
607 Keyword arguments
608 jetCollection -- The jet container to run on.
609 """
610
611 # Remove b-tagging calibration from the container name
612 btIndex = jetCollection.find('_BTagging')
613 if btIndex != -1:
614 jetCollection = jetCollection[:btIndex]
615
616 jetCollectionName=jetCollection
617 # needed for PHYSLITE
618 if(jetCollection=="AnalysisJets") :
619 jetCollectionName="AntiKt4EMPFlowJets"
620 if(jetCollection=="AnalysisLargeRJets") :
621 jetCollectionName="AntiKt10UFOCSSKSoftDropBeta100Zcut10Jets"
622
623 # interpret the jet collection
624 collection_pattern = re.compile(
625 r"AntiKt(\d+)(EMTopo|EMPFlow|LCTopo|TrackCaloCluster|UFO|Track|HI)(TrimmedPtFrac5SmallR20|CSSKSoftDropBeta100Zcut10)?Jets")
626 match = collection_pattern.match(jetCollectionName)
627 if not match:
628 raise ValueError(
629 "Jet collection {0} does not match expected pattern!".format(jetCollectionName) )
630 radius = int(match.group(1) )
631 if radius not in [2, 4, 6, 10]:
632 raise ValueError("Jet collection has an unsupported radius '{0}'!".format(radius) )
633 jetInput = match.group(2)
634
635 config = PreJetAnalysisConfig()
636 config.setOptionValue ('containerName', containerName)
637 config.setOptionValue ('jetCollection', jetCollection)
638 config.runOriginalObjectLink = (btIndex != -1)
639 config.setOptionValue ('runGhostMuonAssociation', runGhostMuonAssociation)
640 seq.append (config)
641
642 if radius == 4:
643 makeSmallRJetAnalysisConfig(seq, containerName,
644 jetCollection, jetInput=jetInput)
645 elif radius in [2, 6]:
646 makeRScanJetAnalysisConfig(seq, containerName,
647 jetCollection, jetInput=jetInput, radius=radius)
648 else:
649 trim = match.group(3)
650 if trim == "":
651 raise ValueError("Untrimmed large-R jets are not supported!")
652 makeLargeRJetAnalysisConfig(seq, containerName,
653 jetCollection, jetInput=jetInput)
654
655
656
657def makeSmallRJetAnalysisConfig( seq, containerName, jetCollection, jetInput,
658 runJvtUpdate = None, runNNJvtUpdate = None,
659 runJvtSelection = None, runFJvtSelection = None,
660 jvtWP = None, fJvtWP = None,
661 runJvtEfficiency = None, runFJvtEfficiency = None):
662 """Add algorithms for the R=0.4 jets.
663
664 Keyword arguments
665 seq -- The sequence to add the algorithms to
666 jetCollection -- The jet container to run on.
667 jetInput -- The type of input used, read from the collection name.
668 runJvtUpdate -- Determines whether or not to update JVT on the jets
669 runNNJvtUpdate -- Determines whether or not to update NN JVT on the jets
670 runJvtSelection -- Determines whether or not to run JVT selection on the jets
671 runFJvtSelection -- Determines whether or not to run forward JVT selection on the jets
672 jvtWP -- Defines the NNJvt WP to apply on the jets
673 fJvtWP -- Defines the fJvt WP to apply on the jets
674 runJvtEfficiency -- Determines whether or not to calculate the JVT efficiency
675 runFJvtEfficiency -- Determines whether or not to calculate the forward JVT efficiency
676 """
677
678 if jetInput not in ["EMTopo", "EMPFlow", "HI"]:
679 raise ValueError(
680 "Unsupported input type '{0}' for R=0.4 jets!".format(jetInput) )
681
682 config = SmallRJetAnalysisConfig()
683 config.setOptionValue ('containerName', containerName)
684 config.setOptionValue ('jetCollection', jetCollection)
685 config.setOptionValue ('jetInput', jetInput)
686 config.setOptionValue ('runJvtUpdate', runJvtUpdate)
687 config.setOptionValue ('runNNJvtUpdate', runNNJvtUpdate)
688 config.setOptionValue ('runJvtSelection', runJvtSelection)
689 config.setOptionValue ('runFJvtSelection', runFJvtSelection)
690 config.setOptionValue ('jvtWP', jvtWP)
691 config.setOptionValue ('fJvtWP', fJvtWP)
692 config.setOptionValue ('runJvtEfficiency', runJvtEfficiency)
693 config.setOptionValue ('runFJvtEfficiency', runFJvtEfficiency)
694 seq.append (config)
695
696
697def makeRScanJetAnalysisConfig( seq, containerName, jetCollection,
698 jetInput, radius ):
699 """Add algorithms for the R-scan jets.
700
701 Keyword arguments
702 seq -- The sequence to add the algorithms to
703 jetCollection -- The jet container to run on.
704 jetInput -- The type of input used, read from the collection name.
705 radius -- The radius of the r-scan jets.
706 """
707
708 config = RScanJetAnalysisConfig()
709 config.setOptionValue ('containerName', containerName)
710 config.setOptionValue ('jetCollection', jetCollection)
711 config.setOptionValue ('jetInput', jetInput)
712 config.setOptionValue ('radius', radius)
713 seq.append (config)
714
715
716
717
718def makeLargeRJetAnalysisConfig( seq, containerName, jetCollection,
719 jetInput):
720 """Add algorithms for the R=1.0 jets.
721
722 Keyword arguments
723 seq -- The sequence to add the algorithms to
724 jetCollection -- The jet container to run on.
725 jetInput -- The type of input used, read from the collection name.
726 """
727 config = LargeRJetAnalysisConfig()
728 config.setOptionValue ('containerName', containerName)
729 config.setOptionValue ('jetCollection', jetCollection)
730 config.setOptionValue ('jetInput', jetInput)
731 seq.append (config)
732
createFFSmearingTool(self, jetFFSmearingAlg, config)
makeJetAnalysisConfig(seq, containerName, jetCollection, runGhostMuonAssociation=None)
makeSmallRJetAnalysisConfig(seq, containerName, jetCollection, jetInput, runJvtUpdate=None, runNNJvtUpdate=None, runJvtSelection=None, runFJvtSelection=None, jvtWP=None, fJvtWP=None, runJvtEfficiency=None, runFJvtEfficiency=None)
makeRScanJetAnalysisConfig(seq, containerName, jetCollection, jetInput, radius)
makeLargeRJetAnalysisConfig(seq, containerName, jetCollection, jetInput)