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