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