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