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 _resolveJetCollectionName(jetCollection):
17 """Map the PHYSLITE jet collection aliases onto the actual collection names."""
18 if jetCollection == "AnalysisJets":
19 return "AntiKt4EMPFlowJets"
20 elif jetCollection == "AnalysisLargeRJets":
21 return "AntiKt10UFOCSSKSoftDropBeta100Zcut10Jets"
22 return jetCollection
23
24
25def _addJetUncertaintiesTool(config, alg, toolName, jetCollectionName, configFile,
26 calibArea, mcType, isData, pseudoData):
27 """Add a private JetUncertaintiesTool to `alg` under property `toolName`
28 and configure it."""
29 config.addPrivateTool( toolName, 'JetUncertaintiesTool' )
30 tool = getattr(alg, toolName)
31 tool.JetDefinition = jetCollectionName[:-4]
32 tool.ConfigFile = configFile
33 if calibArea is not None:
34 tool.CalibArea = calibArea
35 tool.MCType = mcType
36 tool.IsData = isData
37 tool.PseudoDataJERsmearingMode = pseudoData
38 return tool
39
40
41def _parseJetCollection(jetCollection):
42 """Parse a jet collection name into its components.
43
44 Returns (radius, jetInput, trim, hasBTag) where:
45 radius -- the jet radius parameter (2, 4, 6, or 10)
46 jetInput -- the input type string (e.g. 'EMPFlow', 'UFO', ...)
47 trim -- the trimming suffix string, or None
48 hasBTag -- whether the original collection name had a '_BTagging' suffix
49 """
50 hasBTag = False
51 btIndex = jetCollection.find('_BTagging')
52 if btIndex != -1:
53 jetCollection = jetCollection[:btIndex]
54 hasBTag = True
55
56 jetCollectionName = _resolveJetCollectionName(jetCollection)
57
58 collection_pattern = re.compile(
59 r"AntiKt(\d+)(EMTopo|EMPFlow|LCTopo|TrackCaloCluster|UFO|Track|HI)"
60 r"(TrimmedPtFrac5SmallR20|CSSKSoftDropBeta100Zcut10)?Jets")
61 match = collection_pattern.match(jetCollectionName)
62 if not match:
63 raise ValueError(
64 f"Jet collection {jetCollectionName} does not match expected pattern!")
65 radius = int(match.group(1))
66 if radius not in [2, 4, 6, 10]:
67 raise ValueError(
68 f"Jet collection has unsupported radius '{radius}'!")
69 jetInput = match.group(2)
70 trim = match.group(3)
71 return radius, jetInput, trim, hasBTag
72
73
74class PreJetAnalysisConfig (ConfigBlock) :
75 """the ConfigBlock for the common preprocessing of jet sequences"""
76
77 def __init__ (self) :
78 super().__init__()
79 self.addOption ('containerName', '', type=str,
80 noneAction='error',
81 info="the name of the output container after calibration.",
82 meta={'role':'container'})
83 self.addOption ('jetCollection', '', type=str,
84 noneAction='error',
85 info="the jet container to run on. It is interpreted to determine "
86 "the correct config blocks to call for small- or large-R jets.")
87 self.addOption('outputTruthLabelIDs', False, type=bool,
88 info='enable or disable `HadronConeExclTruthLabelID` and `PartonTruthLabelID` decorations.')
89 self.addOption ('runOriginalObjectLink', False, type=bool,
90 info='sets up an instance of `CP::AsgOriginalObjectLinkAlg` to link systematically-varied containers to the base one.')
91 self.addOption ('runGhostMuonAssociation', None, type=bool,
92 info="whether to set up the jet-ghost-muon association algorithm "
93 "`CP::JetGhostMuonAssociationAlg`. If left empty, automatically defaults to `False` for PHYSLITE and `True` otherwise.")
94 self.addOption ('runTruthJetTagging', True, type=bool,
95 info="whether to set up the jet truth tagging algorithm "
96 "`CP::JetTruthTagAlg`.")
97
98 def instanceName (self) :
99 """Return the instance name for this block"""
100 return self.containerName
101
102 def makeAlgs (self, config) :
103
104
105 if config.isPhyslite() and self.jetCollection == 'AntiKt4EMPFlowJets' :
106 config.setSourceName (self.containerName, "AnalysisJets", originalName = self.jetCollection)
107 elif config.isPhyslite() and self.jetCollection == 'AntiKt10UFOCSSKSoftDropBeta100Zcut10Jets' :
108 config.setSourceName (self.containerName, "AnalysisLargeRJets", originalName = self.jetCollection)
109 else :
110 config.setSourceName (self.containerName, self.jetCollection, originalName = self.jetCollection)
111
112 # Parse and store jet collection metadata for downstream blocks
113 radius, jetInput, trim, hasBTag = _parseJetCollection(self.jetCollection)
114 config.setContainerMeta(self.containerName, 'jetRadius', radius)
115 config.setContainerMeta(self.containerName, 'jetInput', jetInput)
116 config.setContainerMeta(self.containerName, 'jetTrim', trim)
117 config.setContainerMeta(self.containerName, 'hasBTag', hasBTag)
118 config.setContainerMeta(self.containerName, 'jetCollectionName',
120
121 # Relink original jets in case of b-tagging calibration
122 if self.runOriginalObjectLink or hasBTag :
123 alg = config.createAlgorithm( 'CP::AsgOriginalObjectLinkAlg',
124 'JetOriginalObjectLinkAlg',
125 reentrant=True )
126 alg.baseContainerName = self.jetCollection
127 alg.particles = config.readName (self.containerName)
128 if config.wantCopy (self.containerName) :
129 alg.particlesOut = config.copyName (self.containerName)
130 alg.preselection = config.getPreselection (self.containerName, '')
131
132 # Set up the jet ghost muon association algorithm:
133 if (self.runGhostMuonAssociation is None and not config.isPhyslite()) or \
134 (self.runGhostMuonAssociation is True):
135 alg = config.createAlgorithm( 'CP::JetGhostMuonAssociationAlg',
136 'JetGhostMuonAssociationAlg' )
137 alg.jets = config.readName (self.containerName)
138 if config.isPhyslite():
139 alg.muons = "AnalysisMuons"
140 if config.wantCopy (self.containerName) :
141 alg.jetsOut = config.copyName (self.containerName)
142 taggerName = "GN2HLv01" if config.geometry() >= LHCPeriod.Run4 else "GN2v01"
143 extraInputs = [
144 ( 'xAOD::JetContainer' , f"StoreGateSvc+{self.jetCollection}.DFCommonJets_jetClean_LooseBad"),
145 ( 'xAOD::JetContainer' , f"StoreGateSvc+{self.jetCollection}.DFCommonJets_jetClean_TightBad"),
146 ( 'xAOD::JetContainer' , f"StoreGateSvc+{self.jetCollection}.{taggerName}_pb"),
147 ( 'xAOD::JetContainer' , f"StoreGateSvc+{self.jetCollection}.{taggerName}_pc"),
148 ( 'xAOD::JetContainer' , f"StoreGateSvc+{self.jetCollection}.{taggerName}_ptau"),
149 ( 'xAOD::JetContainer' , f"StoreGateSvc+{self.jetCollection}.{taggerName}_pu")]
150 config.setExtraInputs (extraInputs)
151
152 if self.runTruthJetTagging and config.dataType() is not DataType.Data:
153 # Decorate jets with isHS labels (required to retrieve Jvt SFs)
154 alg = config.createAlgorithm( 'CP::JetDecoratorAlg', 'JetPileupLabelAlg' )
155 config.addPrivateTool( 'decorator', 'JetPileupLabelingTool' )
156 alg.jets = config.readName (self.containerName)
157 alg.jetsOut = config.copyName (self.containerName)
158 alg.decorator.RecoJetContainer = alg.jetsOut.replace ('%SYS%', 'NOSYS')
159 alg.decorator.SuppressOutputDependence=True
160 if self.jetCollection == 'AntiKt10UFOCSSKSoftDropBeta100Zcut10Jets' :
161 extraInputs = [
162 ( 'xAOD::JetContainer' , f"StoreGateSvc+{self.jetCollection}.GN2Xv01_phbb"),
163 ( 'xAOD::JetContainer' , f"StoreGateSvc+{self.jetCollection}.GN2Xv01_phcc"),
164 ( 'xAOD::JetContainer' , f"StoreGateSvc+{self.jetCollection}.GN2Xv01_pqcd"),
165 ( 'xAOD::JetContainer' , f"StoreGateSvc+{self.jetCollection}.GN2Xv01_ptop")]
166 config.setExtraInputs (extraInputs)
167
168 # Set up shallow copy if needed and not yet done
169 if config.wantCopy (self.containerName) :
170 alg = config.createAlgorithm( 'CP::AsgShallowCopyAlg', 'JetShallowCopyAlg' )
171 alg.input = config.readName (self.containerName)
172 alg.output = config.copyName (self.containerName)
173 alg.outputType = 'xAOD::JetContainer'
174 if self.jetCollection == 'AntiKt10UFOCSSKSoftDropBeta100Zcut10Jets' :
175 alg.declareDecorations = ['GN2Xv01_phbb', 'GN2Xv01_phcc',
176 'GN2Xv01_pqcd', 'GN2Xv01_ptop']
177
178 config.addOutputVar (self.containerName, 'pt', 'pt')
179 config.addOutputVar (self.containerName, 'eta', 'eta', noSys=True)
180 config.addOutputVar (self.containerName, 'phi', 'phi', noSys=True)
181 config.addOutputVar (self.containerName, 'charge', 'charge', noSys=True, enabled=False)
182
183
184 if self.outputTruthLabelIDs and config.dataType() is not DataType.Data:
185 config.addOutputVar (self.containerName, 'HadronConeExclTruthLabelID', 'HadronConeExclTruthLabelID', noSys=True, auxType="int")
186 config.addOutputVar (self.containerName, 'PartonTruthLabelID', 'PartonTruthLabelID', noSys=True, auxType="int")
187
188
189
190class SmallRJetAnalysisConfig (ConfigBlock) :
191 """the ConfigBlock for the small-r jet sequence"""
192
193 def __init__ (self) :
194 super().__init__()
195 self.addOption ('containerName', '', type=str,
196 noneAction='error',
197 info="the name of the output container after calibration.",
198 meta={'role':'container'})
199 self.addOption ('jetCollection', '', type=str,
200 noneAction='error',
201 info="the jet container to run on. It is interpreted to determine "
202 "the correct config blocks to call for small- or large-R jets.")
203 self.addOption ('runJvtUpdate', False, type=bool,
204 info="whether to update the JVT.")
205 self.addOption ('runNNJvtUpdate', False, type=bool,
206 info="whether to update the NN-JVT.")
207 self.addOption ('runJvtSelection', True, type=bool,
208 info="whether to run JVT selection.")
209 self.addOption ('runFJvtSelection', False, type=bool,
210 info="whether to run forward JVT selection.")
211 self.addOption ('jvtWP', "FixedEffPt", type=str,
212 info="which Jvt WP to apply.",
213 meta={'choices':(['FixedEffPt'],1)})
214 self.addOption ('fJvtWP', "Loose", type=str,
215 info="which fJvt WP to apply.",
216 meta={'choices':(['Loose','Tight','Tighter'],1)})
217 self.addOption ('runJvtEfficiency', True, type=bool,
218 info="whether to calculate the JVT efficiency.")
219 self.addOption ('runFJvtEfficiency', False, type=bool,
220 info="whether to calculate the forward JVT efficiency.")
221 self.addOption ('runUncertainties', True, type=bool,
222 info="whether to configure JetUncertaintiesTool.", expertMode=True)
223 self.addOption('uncertaintyAnalysisFiles', None, type=dict,
224 info="dictionary of dsids with entry corresponding to file containing gluon fraction histograms needed to calculate an analysis specific "
225 "jet flavor composition uncertainty.")
226 self.addOption ('systematicsModelJES', "Category", type=str,
227 info="the NP reduction scheme to use for JES: All, Global, Category, "
228 "Scenario. The default is Category.")
229 self.addOption ('systematicsModelJER', "Full", type=str,
230 info="the NP reduction scheme to use for JER: All, Full, Simple. The "
231 "default is Full.",
232 meta={'choices':(['All','Full','Simple'],1)})
233 self.addOption ('runJERsystematicsOnData', False, type=bool,
234 info="whether to run the All/Full JER model variations also on data samples. Expert option!",
235 expertMode=True)
236 self.addOption ('recalibratePhyslite', True, type=bool,
237 info="whether to run the `CP::JetCalibrationAlg` on PHYSLITE derivations.")
238 # Calibration tool options
239 self.addOption ('calibToolConfigFile', None, type=str,
240 info="the name of the config file to use for the jet calibration "
241 "tool. Expert option to override JetETmiss recommendations.",
242 expertMode=True)
243 self.addOption ('calibToolCalibArea', None, type=str,
244 info="name of the CVMFS area to use for the jet calibration "
245 "tool. Expert option to override JetETmiss recommendations.",
246 expertMode=True)
247 self.addOption ('calibToolCalibSeq', None, type=str,
248 info="name of the sequence to use for the jet calibration "
249 "tool (e.g. `JetArea_Residual_EtaJES_GSC`). Expert option to override "
250 "JetETmiss recommendations.",
251 expertMode=True)
252 # Uncertainties tool options
253 self.addOption ('uncertToolConfigPath', None, type=str,
254 info="name (str) of the config file to use for the jet uncertainty "
255 "tool. Expert option to override JetETmiss recommendations. The "
256 "default is None.",
257 expertMode=True)
258 self.addOption ('uncertToolCalibArea', None, type=str,
259 info="name (str) of the CVMFS area to use for the jet uncertainty "
260 "tool. Expert option to override JetETmiss recommendations. The "
261 "default is None.",
262 expertMode=True)
263 self.addOption ('uncertToolMCType', None, type=str,
264 info="data type (str) to use for the jet uncertainty tool (e.g. "
265 "'AF3' or 'MC16'). Expert option to override JetETmiss "
266 "recommendations. The default is None.",
267 expertMode=True)
268
269 def instanceName (self) :
270 """Return the instance name for this block"""
271 return self.containerName
272
273 def getUncertaintyToolSettings(self, config):
274
275 # Retrieve appropriate JES/JER recommendations for the JetUncertaintiesTool.
276 # We do this separately from the tool declaration, as we may need to set uo
277 # two such tools, but they have to be private.
278
279 jetInput = config.getContainerMeta(self.containerName, 'jetInput', failOnMiss=True)
280 # Config file:
281 config_file = None
282 if self.systematicsModelJES == "All" and self.systematicsModelJER == "All":
283 config_file = "R4_AllNuisanceParameters_AllJERNP.config"
284 elif "Scenario" in self.systematicsModelJES:
285 if self.systematicsModelJER != "Simple":
286 raise ValueError(
287 "Invalid uncertainty configuration - Scenario* systematicsModelJESs can "
288 "only be used together with the Simple systematicsModelJER")
289 config_file = f"R4_{self.systematicsModelJES}_SimpleJER.config"
290 elif self.systematicsModelJES in ["Global", "Category"] and self.systematicsModelJER in ["Simple", "Full"]:
291 config_file = f"R4_{self.systematicsModelJES}Reduction_{self.systematicsModelJER}JER.config"
292 else:
293 raise ValueError(
294 "Invalid combination of systematicsModelJES and systematicsModelJER settings: "
295 f"systematicsModelJES: {self.systematicsModelJES}, systematicsModelJER: {self.systematicsModelJER}" )
296
297 # Calibration area:
298 calib_area = None
299 if self.uncertToolCalibArea is not None:
300 calib_area = self.uncertToolCalibArea
301
302 # Expert override for config path:
303 if self.uncertToolConfigPath is not None:
304 config_file = self.uncertToolConfigPath
305 else:
306 if config.geometry() is LHCPeriod.Run2:
307 if config.dataType() is DataType.FastSim:
308 config_file = "rel22/Fall2024_PreRec/" + config_file
309 else:
310 if jetInput == "HI":
311 config_file = "HIJetUncertainties/Spring2023/HI" + config_file
312 else:
313 config_file = "rel22/Summer2023_PreRec/" + config_file
314 else:
315 if config.dataType() is DataType.FastSim:
316 config_file = "rel22/Winter2025_AF3_PreRec/" + config_file
317 else:
318 if jetInput == "HI":
319 config_file = "HIJetUncertainties/Spring2023/HI" + config_file
320 else:
321 config_file = "rel22/Winter2025_PreRec/" + config_file
322
323 # MC type:
324 mc_type = None
325 if self.uncertToolMCType is not None:
326 mc_type = self.uncertToolMCType
327 else:
328 if config.geometry() is LHCPeriod.Run2:
329 if config.dataType() is DataType.FastSim:
330 mc_type = "AF3"
331 else:
332 mc_type = "MC20"
333 else:
334 if config.dataType() is DataType.FastSim:
335 mc_type = "MC23AF3"
336 else:
337 if jetInput == "HI":
338 mc_type = "MC16"
339 else:
340 mc_type = "MC23"
341
342 return config_file, calib_area, mc_type
343
344
345 def createUncertaintyTool(self, jetUncertaintiesAlg, config, jetCollectionName, doPseudoData=False):
346
347 # Create an instance of JetUncertaintiesTool, following JetETmiss recommendations.
348 # To run Jet Energy Resolution (JER) uncertainties in the "Full" or "All" schemes,
349 # we need two sets of tools: one configured as normal (MC), the other with the
350 # exact same settings but pretending to run on data (pseudo-data).
351 # This is achieved by passing "isPseudoData=True" to the arguments.
352
353 # Retrieve the common configuration settings
354 configFile, calibArea, mcType = self.getUncertaintyToolSettings(config)
355
356 # The main tool for all JES+JER combinations
357 tool = _addJetUncertaintiesTool(config, jetUncertaintiesAlg, 'uncertaintiesTool',
358 jetCollectionName, configFile, calibArea, mcType,
359 isData=(config.dataType() is DataType.Data),
360 pseudoData=False)
361 from PathResolver import PathResolver
362 if self.uncertaintyAnalysisFiles is not None:
363 if config.dsid() in self.uncertaintyAnalysisFiles:
364 tool.AnalysisFile = PathResolver.FindCalibFile(self.uncertaintyAnalysisFiles[config.dsid()])
365
366 if config.dataType() is DataType.Data and not (doPseudoData and self.runJERsystematicsOnData):
367 # we don't want any systematics on data if we're not using the right JER model!
368 jetUncertaintiesAlg.affectingSystematicsFilter = '.*'
369 if config.dataType() is not DataType.Data and doPseudoData and not self.runJERsystematicsOnData:
370 # The secondary tool for pseudo-data JER smearing
371 _addJetUncertaintiesTool(config, jetUncertaintiesAlg, 'uncertaintiesToolPD',
372 jetCollectionName, configFile, calibArea, mcType,
373 isData=True, pseudoData=True)
374
375
376 def makeAlgs (self, config) :
377
378 # Self-select: only run for radius-4 jets
379 if config.getContainerMeta(self.containerName, 'jetRadius', failOnMiss=True) != 4:
380 return
381
382
383 jetCollectionName = config.getContainerMeta(self.containerName, 'jetCollectionName', failOnMiss=True)
384
385 jetInput = config.getContainerMeta(self.containerName, 'jetInput', failOnMiss=True)
386
387 if jetInput not in ["EMTopo", "EMPFlow", "HI"]:
388 raise ValueError(
389 f"Unsupported input type '{jetInput}' for R=0.4 jets!" )
390
391 if self.jvtWP not in ["FixedEffPt"]:
392 raise ValueError(
393 f"Unsupported NNJvt WP '{self.jvtWP}'" )
394
395 if self.fJvtWP not in ["Loose", "Tight", "Tighter"]:
396 raise ValueError(
397 f"Unsupported fJvt WP '{self.fJvtWP}'" )
398
399 if not config.isPhyslite() or self.recalibratePhyslite:
400 # Create calibration tool as public so it can be shared
401 # (e.g. with FTAG1LITE's JetCalibratedPtDecoratorAlg).
402 # Must be created before the algorithm so EventLoop
403 # initialises the tool first.
404 calibToolName = 'JetCalibTool_' + jetCollectionName[:-4]
405 calibTool = config.createPublicTool( 'JetCalibrationTool', calibToolName )
406 calibTool.JetCollection = jetCollectionName[:-4]
407 # Get the correct string to use in the config file name
408 if jetInput == "EMPFlow":
409 if config.geometry() is LHCPeriod.Run2:
410 configFile = "PreRec_R22_PFlow_ResPU_EtaJES_GSC_February23_230215.config"
411 calibTool.CalibArea = "00-04-82"
412 elif config.geometry() >= LHCPeriod.Run3:
413 configFile = "AntiKt4EMPFlow_MC23a_PreRecR22_Phase2_CalibConfig_ResPU_EtaJES_GSC_241208_InSitu.config"
414 calibTool.CalibArea = "00-04-83"
415 elif jetInput == "HI":
416 if config.geometry() is LHCPeriod.Run2:
417 configFile = "JES_MC16_HI_Jan2021_5TeV.config"
418 elif config.geometry() is LHCPeriod.Run3:
419 configFile = "AntiKt4HI_MC23_EtaJES_Run3PreRec_Run2VJet_Run3EtaInt_5p36TeV.config"
420 calibTool.CalibArea = "00-04-83"
421 else:
422 raise ValueError(
423 f"Unsupported geometry '{config.geometry()}' for HI jet calibration!")
424 else:
425 if config.dataType() is DataType.FastSim:
426 configFile = f"JES_MC16Recommendation_AFII_{jetInput}_Apr2019_Rel21.config"
427 else:
428 configFile = f"JES_MC16Recommendation_Consolidated_{jetInput}_Apr2019_Rel21.config"
429 if self.calibToolCalibArea is not None:
430 calibTool.CalibArea = self.calibToolCalibArea
431 if self.calibToolConfigFile is not None:
432 configFile = self.calibToolConfigFile
433 calibTool.ConfigFile = configFile
434 if config.dataType() is DataType.Data:
435 if jetInput == "HI":
436 calibTool.CalibSequence = 'EtaJES_Insitu'
437 else:
438 calibTool.CalibSequence = 'JetArea_Residual_EtaJES_GSC_Insitu'
439 else:
440 if jetInput == "EMPFlow":
441 calibTool.CalibSequence = 'JetArea_Residual_EtaJES_GSC'
442 elif jetInput == "HI":
443 calibTool.CalibSequence = 'EtaJES'
444 else:
445 calibTool.CalibSequence = 'JetArea_Residual_EtaJES_GSC_Smear'
446 if self.calibToolCalibSeq is not None:
447 calibTool.CalibSequence = self.calibToolCalibSeq
448 calibTool.IsData = (config.dataType() is DataType.Data)
449 # Prepare the jet calibration algorithm
450 alg = config.createAlgorithm( 'CP::JetCalibrationAlg', 'JetCalibrationAlg' )
451 alg.HIsetup = jetInput == "HI"
452 alg.calibrationTool = f'{calibTool.getType()}/{calibTool.getName()}'
453 alg.jets = config.readName (self.containerName)
454 alg.jetsOut = config.copyName (self.containerName)
455
456 # Jet uncertainties
458 alg = config.createAlgorithm( 'CP::JetUncertaintiesAlg', 'JetUncertaintiesAlg' )
459 self.createUncertaintyTool(alg, config, jetCollectionName, doPseudoData=( self.systematicsModelJER in ["Full","All"] ))
460 alg.jets = config.readName (self.containerName)
461 alg.jetsOut = config.copyName (self.containerName)
462 alg.preselection = config.getPreselection (self.containerName, '')
463
464 # Set up the JVT update algorithm:
465 if self.runJvtUpdate :
466 alg = config.createAlgorithm( 'CP::JvtUpdateAlg', 'JvtUpdateAlg' )
467 config.addPrivateTool( 'jvtTool', 'JetVertexTaggerTool' )
468 alg.jvtTool.JetContainer = self.jetCollection
469 alg.jvtTool.SuppressInputDependence=True
470 alg.jets = config.readName (self.containerName)
471 alg.jetsOut = config.copyName (self.containerName)
472 alg.preselection = config.getPreselection (self.containerName, '')
473
475 assert jetInput=="EMPFlow", "NN JVT only defined for PFlow jets"
476 alg = config.createAlgorithm( 'CP::JetDecoratorAlg', 'NNJvtUpdateAlg' )
477 config.addPrivateTool( 'decorator', 'JetPileupTag::JetVertexNNTagger' )
478 alg.jets = config.readName (self.containerName)
479 alg.jetsOut = config.copyName (self.containerName)
480 # Set this actually to the *output* collection
481 alg.decorator.JetContainer = alg.jetsOut.replace ('%SYS%', 'NOSYS')
482 alg.decorator.SuppressInputDependence=True
483 alg.decorator.SuppressOutputDependence=True
484
485 # Set up the jet efficiency scale factor calculation algorithm
486 # Change the truthJetCollection property to AntiKt4TruthWZJets if preferred
488 assert jetInput=="EMPFlow", "NNJvt WPs and SFs only valid for PFlow jets"
489 warnings.warn_explicit(
490 "jvtWP, runJvtSelection and runJvtEfficiency"
491 " are deprecated - please use a"
492 " JVTWorkingPoint block instead.",
493 ConfigDeprecationWarning, filename='', lineno=0)
494
495 alg = config.createAlgorithm('CP::AsgSelectionAlg', 'JvtSelectionAlg')
496 config.addPrivateTool('selectionTool', 'CP::NNJvtSelectionTool')
497 alg.selectionTool.JetContainer = config.readName(self.containerName)
498 alg.selectionTool.JvtMomentName = "NNJvt"
499 alg.selectionTool.WorkingPoint = self.jvtWP
500 alg.selectionTool.MaxPtForJvt = 60*GeV
501 alg.selectionDecoration = "jvt_selection,as_char"
502 alg.particles = config.readName(self.containerName)
503
504 if self.runJvtEfficiency and config.dataType() is not DataType.Data:
505 alg = config.createAlgorithm( 'CP::JvtEfficiencyAlg', 'JvtEfficiencyAlg' )
506 config.addPrivateTool( 'efficiencyTool', 'CP::NNJvtEfficiencyTool' )
507 alg.efficiencyTool.JetContainer = config.readName(self.containerName)
508 alg.efficiencyTool.MaxPtForJvt = 60*GeV
509 alg.efficiencyTool.WorkingPoint = self.jvtWP
510 if config.geometry() is LHCPeriod.Run2:
511 alg.efficiencyTool.SFFile = "JetJvtEfficiency/May2024/NNJvtSFFile_Run2_EMPFlow.root"
512 else:
513 alg.efficiencyTool.SFFile = "JetJvtEfficiency/May2024/NNJvtSFFile_Run3_EMPFlow.root"
514 alg.selection = 'jvt_selection,as_char'
515 alg.scaleFactorDecoration = 'jvt_effSF_%SYS%'
516 alg.outOfValidity = 2
517 alg.outOfValidityDeco = 'no_jvt'
518 alg.skipBadEfficiency = False
519 alg.jets = config.readName (self.containerName)
520 alg.preselection = config.getPreselection (self.containerName, '')
521 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, 'jvtEfficiency')
522 config.addSelection (self.containerName, 'baselineJvt', 'jvt_selection,as_char', preselection=False)
523
525 assert jetInput=="EMPFlow", "fJvt WPs and SFs only valid for PFlow jets"
526 warnings.warn_explicit(
527 "fJvtWP, runFJvtSelection and runFJvtEfficiency"
528 " are deprecated - please use a"
529 " FJVTWorkingPoint block instead.",
530 ConfigDeprecationWarning, filename='', lineno=0)
531
532 alg = config.createAlgorithm('CP::AsgSelectionAlg', 'FJvtSelectionAlg')
533 config.addPrivateTool('selectionTool', 'CP::FJvtSelectionTool')
534 alg.selectionTool.JetContainer = config.readName(self.containerName)
535 alg.selectionTool.JvtMomentName = "DFCommonJets_fJvt"
536 alg.selectionTool.WorkingPoint = self.fJvtWP
537 alg.selectionDecoration = "fjvt_selection,as_char"
538 alg.particles = config.readName(self.containerName)
539
540 if self.runFJvtEfficiency and config.dataType() is not DataType.Data:
541 alg = config.createAlgorithm( 'CP::JvtEfficiencyAlg', 'FJvtEfficiencyAlg' )
542 config.addPrivateTool( 'efficiencyTool', 'CP::FJvtEfficiencyTool' )
543 alg.efficiencyTool.JetContainer = config.readName(self.containerName)
544 alg.efficiencyTool.WorkingPoint = self.fJvtWP
545 if config.geometry() is LHCPeriod.Run2:
546 alg.efficiencyTool.SFFile = "JetJvtEfficiency/May2024/fJvtSFFile_Run2_EMPFlow.root"
547 else:
548 alg.efficiencyTool.SFFile = "JetJvtEfficiency/May2024/fJvtSFFile_Run3_EMPFlow.root"
549 alg.selection = 'fjvt_selection,as_char'
550 alg.scaleFactorDecoration = 'fjvt_effSF_%SYS%'
551 alg.outOfValidity = 2
552 alg.outOfValidityDeco = 'no_fjvt'
553 alg.skipBadEfficiency = False
554 alg.jets = config.readName (self.containerName)
555 alg.preselection = config.getPreselection (self.containerName, '')
556 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, 'fjvtEfficiency')
557 config.addSelection (self.containerName, 'baselineFJvt', 'fjvt_selection,as_char', preselection=False)
558
559 # Additional decorations
560 alg = config.createAlgorithm( 'CP::AsgEnergyDecoratorAlg', 'AsgEnergyDecoratorAlg' )
561 alg.particles = config.readName (self.containerName)
562
563 config.addOutputVar (self.containerName, 'e_%SYS%', 'e')
564
565
566class RScanJetAnalysisConfig (ConfigBlock) :
567 """the ConfigBlock for the r-scan jet sequence"""
568
569 def __init__ (self) :
570 super().__init__()
571 self.addOption ('containerName', '', type=str,
572 noneAction='error',
573 info="the name of the output container after calibration.",
574 meta={'role':'container'})
575 self.addOption ('jetCollection', '', type=str,
576 noneAction='error',
577 info="the jet container to run on. It is interpreted to determine "
578 "the correct config blocks to call for small- or large-R jets.")
579 self.addOption ('recalibratePhyslite', True, type=bool,
580 info="whether to run the CP::JetCalibrationAlg on PHYSLITE "
581 "derivations. The default is True.")
582
583 def instanceName (self) :
584 """Return the instance name for this block"""
585 return self.containerName
586
587 def makeAlgs (self, config) :
588
589 # Self-select: only run for r-scan jets (radius 2 or 6)
590 radius = config.getContainerMeta(self.containerName, 'jetRadius', failOnMiss=True)
591 if radius not in [2, 6]:
592 return
593
594 jetCollectionName = config.getContainerMeta(self.containerName, 'jetCollectionName', failOnMiss=True)
595
596 jetInput = config.getContainerMeta(self.containerName, 'jetInput', failOnMiss=True)
597
598 if not config.isPhyslite() or self.recalibratePhyslite:
599 if jetInput not in ["LCTopo", "HI"]:
600 raise ValueError(
601 f"Unsupported input type '{jetInput}' for R-scan jets!" )
602 # Create calibration tool before algorithm (EventLoop ordering)
603 calibToolName = 'JetCalibTool_' + jetCollectionName[:-4]
604 calibTool = config.createPublicTool( 'JetCalibrationTool', calibToolName )
605 calibTool.JetCollection = jetCollectionName[:-4]
606 if jetInput=="LCTopo":
607 calibTool.ConfigFile = \
608 f"JES_MC16Recommendation_Rscan{radius}LC_Feb2022_R21.config"
609 if config.dataType() is DataType.Data:
610 calibTool.CalibSequence = "JetArea_Residual_EtaJES_GSC_Insitu"
611 else:
612 calibTool.CalibSequence = "JetArea_Residual_EtaJES_GSC_Smear"
613 elif jetInput=="HI":
614 calibTool.ConfigFile = \
615 "JES_MC16_HI_Jan2021_5TeV.config"
616 if config.dataType() is DataType.Data:
617 calibTool.CalibSequence = "EtaJES_Insitu"
618 else:
619 calibTool.CalibSequence = "EtaJES"
620 calibTool.IsData = (config.dataType() is DataType.Data)
621 # Prepare the jet calibration algorithm
622 alg = config.createAlgorithm( 'CP::JetCalibrationAlg', 'JetCalibrationAlg' )
623 alg.HIsetup = jetInput == "HI"
624 alg.calibrationTool = f'{calibTool.getType()}/{calibTool.getName()}'
625 alg.jets = config.readName (self.containerName)
626 warnings.warn_explicit(
627 "Uncertainties for R-Scan jets are not yet released!",
628 JetUncertaintyWarning, filename='', lineno=0)
629
630
631class LargeRJetAnalysisConfig (ConfigBlock) :
632 """the ConfigBlock for the large-r jet sequence"""
633
634 def __init__ (self) :
635 super().__init__()
636 self.addOption ('containerName', '', type=str,
637 noneAction='error',
638 info="the name of the output container after calibration.",
639 meta={'role':'container'})
640 self.addOption ('jetCollection', '', type=str,
641 noneAction='error',
642 info="the jet container to run on. It is interpreted to determine "
643 "the correct config blocks to call for small- or large-R jets.")
644 self.addOption ('recalibratePhyslite', True, type=bool,
645 info="whether to run the CP::JetCalibrationAlg on PHYSLITE "
646 "derivations. The default is True.")
647 self.addOption ('runUncertainties', True, type=bool,
648 info="whether to configure JetUncertaintiesTool.", expertMode=True )
649 self.addOption ('systematicsModelJER', "Full", type=str,
650 info="the NP reduction scheme to use for JER: Full, Simple. The default is Full.",
651 meta={'choices':(['Simple','Full'],1)})
652 self.addOption ('systematicsModelJMS', "Full", type=str,
653 info="the NP reduction scheme to use for JMS: Full, Simple. The default is Full.",
654 meta={'choices':(['Simple','Full'],1)})
655 self.addOption ('systematicsModelJMR', "Full", type=str,
656 info="the NP reduction scheme to use for JMR: Full, Simple. The default is Full.",
657 meta={'choices':(['Full','Simple'],1)})
658 self.addOption ('runJERsystematicsOnData', False, type=bool,
659 info="whether to run the All/Full JER model variations also on data samples. Expert option!",
660 expertMode=True)
661 # Adding these options to override the jet uncertainty config file when we have new recommendations
662 # Calibration tool options
663 self.addOption ('calibToolConfigFile', None, type=str,
664 info="name of the config file to use for the jet calibration "
665 "tool. Expert option to override JetETmiss recommendations.",
666 expertMode=True)
667 self.addOption ('calibToolCalibArea', None, type=str,
668 info="name of the CVMFS area to use for the jet calibration "
669 "tool. Expert option to override JetETmiss recommendations.",
670 expertMode=True)
671 self.addOption ('calibToolCalibSeq', None, type=str,
672 info="name of the sequence to use for the jet calibration "
673 "tool (e.g. `JetArea_Residual_EtaJES_GSC`). Expert option to override "
674 "JetETmiss recommendations.",
675 expertMode=True)
676 # Uncertainties tool options
677 self.addOption ('uncertToolConfigPath', None, type=str,
678 info="name of the config file to use for the JES, JER, and JMS uncertainty "
679 "tool. Expert option to override JetETmiss recommendations.",
680 expertMode=True)
681 self.addOption ('uncertToolConfigPathJMR', None, type=str,
682 info="name of the config file to use for the JMR uncertainty "
683 "tool. Expert option to override JetETmiss recommendations.",
684 expertMode=True)
685 self.addOption ('uncertToolCalibArea', None, type=str,
686 info="name (str) of the CVMFS area to use for the jet uncertainty "
687 "tool. Expert option to override JetETmiss recommendations. The "
688 "default is None.",
689 expertMode=True)
690 self.addOption ('uncertToolMCType', None, type=str,
691 info="data type (str) to use for the jet uncertainty tool (e.g. "
692 "'AF3' or 'MC16'). Expert option to override JetETmiss "
693 "recommendations. The default is None.",
694 expertMode=True)
695 self.addOption ('minPt', 200.*GeV, type=float,
696 info=r"the minimum $p_\mathrm{T}$ cut (in MeV) to apply to calibrated large-R jets.")
697 self.addOption ('maxPt', 3000.*GeV, type=float,
698 info=r"the maximum $p_\mathrm{T}$ cut (in MeV) to apply to calibrated large-R jets.")
699 self.addOption ('maxEta', 0., type=float,
700 info=r"the maximum $\vert\eta\vert$ cut to apply to calibrated large-R jets.")
701 self.addOption ('maxRapidity', 2., type=float,
702 info="the maximum rapidity cut to apply to calibrated large-R jets.")
703 self.addOption ('minMass', 40.*GeV, type=float,
704 info="the minimum mass cut (in MeV) to apply to calibrated large-R jets.")
705 self.addOption ('maxMass', 600.*GeV, type=float,
706 info="the maximum mass cut (in MeV) to apply to calibrated large-R jets.")
707
708 def instanceName (self) :
709 """Return the instance name for this block"""
710 return self.containerName
711
712 def getUncertaintyToolSettings(self, config):
713 # Retrieve appropriate JES/JER recommendations for the JetUncertaintiesTool.
714 # We do this separately from the tool declaration, as we may need to set uo
715 # two such tools, but they have to be private.
716
717
718 # Config file:
719 config_file = None
720 if self.systematicsModelJER in ["Simple", "Full"] and self.systematicsModelJMS in ["Simple", "Full"]:
721 config_file = f"R10_CategoryJES_{self.systematicsModelJER}JER_{self.systematicsModelJMS}JMS.config"
722 else:
723 raise ValueError(
724 "Invalid request for systematicsModelJER/JMS settings: "
725 f"systematicsModelJER = '{self.systematicsModelJER}', "
726 f"systematicsModelJMS = '{self.systematicsModelJMS}'" )
727 if self.uncertToolConfigPath is not None:
728 # Expert override
729 config_file = self.uncertToolConfigPath
730 else:
731 if config.geometry() in [LHCPeriod.Run2, LHCPeriod.Run3]:
732 config_file = "rel22/Summer2025_PreRec/" + config_file
733 else:
734 warnings.warn_explicit(
735 "Uncertainties for UFO jets are not available for Run 4!",
736 JetUncertaintyWarning, filename='', lineno=0)
737
738
739 # Calibration area:
740 calib_area = None
741 if self.uncertToolCalibArea is not None:
742 calib_area = self.uncertToolCalibArea
743
744 # MC type:
745 if self.uncertToolMCType is not None:
746 mc_type = self.uncertToolMCType
747 else:
748 if config.dataType() is DataType.FastSim:
749 if config.geometry() is LHCPeriod.Run2:
750 mc_type = "MC20AF3"
751 else:
752 mc_type = "MC23AF3"
753 else:
754 if config.geometry() is LHCPeriod.Run2:
755 mc_type = "MC20"
756 else:
757 mc_type = "MC23"
758
759 return config_file, calib_area, mc_type
760
761 def createUncertaintyTool(self, jetUncertaintiesAlg, config, jetCollectionName, doPseudoData=False):
762 '''
763 Create instance(s) of JetUncertaintiesTool following JetETmiss recommendations.
764
765 JER uncertainties under the "Full" scheme must be run on MC samples twice:
766 1. Normal (MC) mode,
767 2. Pseudodata (PD) mode, as if the events are Data.
768 '''
769
770 # Retrieve the common configuration settings
771 configFile, calibArea, mcType = self.getUncertaintyToolSettings(config)
772
773 # The main tool for all JER combinations
774 _addJetUncertaintiesTool(config, jetUncertaintiesAlg, 'uncertaintiesTool',
775 jetCollectionName, configFile, calibArea, mcType,
776 isData=(config.dataType() is DataType.Data),
777 pseudoData=False)
778
779 # JER smearing on data
780 if config.dataType() is DataType.Data and not (doPseudoData and self.runJERsystematicsOnData):
781 # we don't want any systematics on data if we're not using the right JER model!
782 jetUncertaintiesAlg.affectingSystematicsFilter = '.*'
783
784 if config.dataType() is not DataType.Data and doPseudoData and not self.runJERsystematicsOnData:
785 # The secondary tool for pseudo-data JER smearing
786 _addJetUncertaintiesTool(config, jetUncertaintiesAlg, 'uncertaintiesToolPD',
787 jetCollectionName, configFile, calibArea, mcType,
788 isData=True, pseudoData=True)
789
790 def createFFSmearingTool(self, jetFFSmearingAlg, config):
791 # Retrieve appropriate large-R jet mass resolution recommendations for the FFJetSmearingTool.
792
793 # Config file:
794 if self.systematicsModelJMR in ["Simple", "Full"]:
795 config_file = f"R10_{self.systematicsModelJMR}JMR.config"
796 else:
797 raise ValueError(
798 f"Invalid request for systematicsModelJMR settings: {self.systematicsModelJMR}"
799 )
800
801 # Expert override for config path:
802 if self.uncertToolConfigPathJMR is not None:
803 config_file = self.uncertToolConfigPathJMR
804 else:
805 config_file = "rel22/Summer2025_PreRec/" + config_file
806 if config.geometry() is LHCPeriod.Run4:
807 warnings.warn_explicit(
808 "Uncertainties for UFO jets are not for Run 4!",
809 JetUncertaintyWarning, filename='', lineno=0)
810
811 # MC type:
812 if config.geometry() is LHCPeriod.Run2:
813 if config.dataType() is DataType.FastSim:
814 mc_type = "MC20AF3"
815 else:
816 mc_type = "MC20"
817 elif config.geometry() >= LHCPeriod.Run3:
818 if config.dataType() is DataType.FastSim:
819 mc_type = "MC23AF3"
820 else:
821 mc_type = "MC23"
822
823 # Set up the FF smearing tool
824 config.addPrivateTool( 'FFSmearingTool', 'CP::FFJetSmearingTool')
825 jetFFSmearingAlg.FFSmearingTool.MassDef = "UFO"
826 jetFFSmearingAlg.FFSmearingTool.MCType = mc_type
827 jetFFSmearingAlg.FFSmearingTool.ConfigFile = config_file
828
829 def makeAlgs (self, config) :
830
831 # Self-select: only run for large-R (radius 10) jets
832 if config.getContainerMeta(self.containerName, 'jetRadius', failOnMiss=True) != 10:
833 return
834
835 jetCollectionName = config.getContainerMeta(self.containerName, 'jetCollectionName', failOnMiss=True)
836
837 jetInput = config.getContainerMeta(self.containerName, 'jetInput', failOnMiss=True)
838 trim = config.getContainerMeta(self.containerName, 'jetTrim', failOnMiss=True)
839
840 if jetInput not in ["UFO"]:
841 raise ValueError(f"Invalid input type '{jetInput}' for large-R jets!" )
842
843 if not trim:
844 raise ValueError("Untrimmed large-R jets are not supported!")
845
846 configFile = "JES_MC20PreRecommendation_R10_UFO_CSSK_SoftDrop_JMS_R21Insitu_26Nov2024.config"
847 calibArea = "00-04-83"
848 if self.calibToolConfigFile is not None:
849 configFile = self.calibToolConfigFile
850
851 if config.dataType() is not DataType.Data:
852 calibSeq = "EtaJES_JMS"
853 else:
854 calibSeq = "EtaJES_JMS_Insitu"
855 if self.calibToolCalibSeq is not None:
856 calibSeq = self.calibToolCalibSeq
857
858 if self.calibToolCalibArea is not None:
859 calibArea = self.calibToolCalibArea
860
861 if not config.isPhyslite() or self.recalibratePhyslite:
862 # Create calibration tool before algorithm (EventLoop ordering)
863 calibToolName = 'JetCalibTool_' + jetCollectionName[:-4]
864 calibTool = config.createPublicTool( 'JetCalibrationTool', calibToolName )
865
866 calibTool.JetCollection = jetCollectionName[:-4]
867
868 calibTool.ConfigFile = configFile
869
870 calibTool.CalibSequence = calibSeq
871
872 if calibArea is not None:
873 calibTool.CalibArea = calibArea
874
875 calibTool.IsData = (config.dataType() is DataType.Data)
876 # Prepare the jet calibration algorithm
877 alg = config.createAlgorithm( 'CP::JetCalibrationAlg', 'JetCalibrationAlg' )
878 alg.calibrationTool = f'{calibTool.getType()}/{calibTool.getName()}'
879 alg.jets = config.readName(self.containerName)
880 alg.jetsOut = config.copyName(self.containerName)
881
882 # Jet uncertainties
883 if config.dataType() in [DataType.FullSim, DataType.FastSim] and self.runUncertainties:
884 alg = config.createAlgorithm( 'CP::JetUncertaintiesAlg', 'JetUncertaintiesAlg' )
885 self.createUncertaintyTool(alg, config, jetCollectionName, doPseudoData=( self.systematicsModelJER in ["Full","All"] ))
886
887 # R=1.0 jets have a validity range
888 alg.outOfValidity = 2 # SILENT
889 alg.outOfValidityDeco = 'outOfValidity'
890
891 alg.jets = config.readName (self.containerName)
892 alg.jetsOut = config.copyName (self.containerName)
893 alg.preselection = config.getPreselection (self.containerName, '')
894
895 if config.dataType() is not DataType.Data:
896 # set up the FF smearing algorithm
897 alg = config.createAlgorithm( 'CP::JetFFSmearingAlg', 'JetFFSmearingAlg' )
898 self.createFFSmearingTool(alg, config)
899 alg.outOfValidity = 2 # SILENT
900 alg.outOfValidityDeco = 'outOfValidityJMR'
901 alg.jets = config.readName (self.containerName)
902 alg.jetsOut = config.copyName (self.containerName)
903 alg.preselection = config.getPreselection (self.containerName, '')
904
905 if self.minPt > 0 or self.maxPt > 0 or self.maxEta > 0 or self.maxRapidity > 0:
906 # Set up the the pt-eta selection
907 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'JetPtEtaCutAlg' )
908 alg.selectionDecoration = 'selectPtEta,as_bits'
909 config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
910 alg.selectionTool.minPt = self.minPt
911 alg.selectionTool.maxPt = self.maxPt
912 alg.selectionTool.maxEta = self.maxEta
913 alg.selectionTool.maxRapidity = self.maxRapidity
914 alg.particles = config.readName (self.containerName)
915 alg.preselection = config.getPreselection (self.containerName, '')
916 config.addSelection (self.containerName, '', alg.selectionDecoration,
917 preselection=True)
918
919 if self.minMass > 0 or self.maxMass > 0:
920 # Set up the the mass selection
921 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'JetMassCutAlg' )
922 alg.selectionDecoration = 'selectMass,as_bits'
923 config.addPrivateTool( 'selectionTool', 'CP::AsgMassSelectionTool' )
924 alg.selectionTool.minM = self.minMass
925 alg.selectionTool.maxM = self.maxMass
926 alg.particles = config.readName (self.containerName)
927 alg.preselection = config.getPreselection (self.containerName, '')
928 config.addSelection (self.containerName, '', alg.selectionDecoration,
929 preselection=True)
930
931 config.addOutputVar (self.containerName, 'm', 'm')
932
933class _JvtWorkingPointBase (ConfigBlock) :
934 """common base for the Jvt and fJvt working point config blocks
935
936 The concrete blocks combine one of these bases with a parameter class
937 (`_NNJvtParameters` or `_FJvtParameters`)."""
938
939 def __init__ (self) :
940 super().__init__()
941 self.setBlockName(self.__class__.__name__)
942 self.addOption ('containerName', '', type=str,
943 noneAction='error',
944 info="the name of the input container.",
945 meta={'role':'containerRef'})
946 self.addOption ('selectionName', '', type=str,
947 noneAction='error',
948 info="the name of the jet selection to define (e.g. `tight` or `loose`).",
949 meta={'role':'selection'})
950 self.addOption (self._wpOption, '', type=str,
951 noneAction='error',
952 info=self._wpInfo,
953 meta={'choices':(self._wpChoices,1)})
954 self.addOption ('useSuffix', True, type=bool,
955 info="whether the working point name is to be used as suffix ."
956 "Not to be disabled if multiple working points are scheduled.")
957
958 def instanceName (self) :
959 return self.containerName + '_' + self.selectionName
960
961 def workingPoint (self) :
962 return getattr(self, self._wpOption)
963
964 def suffix (self) :
965 return f"_{self.workingPoint()}" if self.useSuffix else ""
966
967
969 """common base for the Jvt and fJvt working point selection blocks"""
970
971 def makeAlgs (self, config) :
972
973 suffix = self.suffix()
974 decorationName = f"{self._prefix}_selection{suffix},as_char"
975 selectionName = self.selectionName
976
977 alg = config.createAlgorithm('CP::AsgSelectionAlg', f'{self._algPrefix}SelectionAlg_{self.workingPoint()}')
978 config.addPrivateTool('selectionTool', self._selectionTool)
979 alg.selectionTool.JetContainer = config.readName(self.containerName)
980 alg.selectionTool.JvtMomentName = self._momentName
981 alg.selectionTool.WorkingPoint = self.workingPoint()
982 if self._maxPtForJvt is not None:
983 alg.selectionTool.MaxPtForJvt = self._maxPtForJvt
984 alg.selectionDecoration = decorationName
985 alg.particles = config.readName(self.containerName)
986
987 config.addSelection (self.containerName, selectionName, decorationName, preselection=False)
988
989
991 """common base for the Jvt and fJvt working point efficiency blocks"""
992
993 def __init__ (self) :
994 super().__init__()
995 self.addDependency('EventSelection', required=False)
996 self.addDependency('EventSelectionMerger', required=False)
997 self.addOption ('noEffSF', False, type=bool,
998 info="disables the calculation of efficiencies and scale factors. "
999 "Only useful to test a new WP for which scale factors are not available.",
1000 expertMode=True)
1001
1002 def makeAlgs (self, config) :
1003
1004 suffix = self.suffix()
1005 decorationName = f"{self._prefix}_selection{suffix},as_char"
1006
1007 if not self.noEffSF and config.dataType() is not DataType.Data:
1008 alg = config.createAlgorithm( 'CP::JvtEfficiencyAlg', f'{self._algPrefix}EfficiencyAlg_{self.workingPoint()}' )
1009 config.addPrivateTool( 'efficiencyTool', self._efficiencyTool )
1010 alg.efficiencyTool.JetContainer = config.readName(self.containerName)
1011 if self._maxPtForJvt is not None:
1012 alg.efficiencyTool.MaxPtForJvt = self._maxPtForJvt
1013 alg.efficiencyTool.WorkingPoint = self.workingPoint()
1014 if config.geometry() is LHCPeriod.Run2:
1015 alg.efficiencyTool.SFFile = f"JetJvtEfficiency/May2024/{self._sfFileStem}_Run2_EMPFlow.root"
1016 else:
1017 alg.efficiencyTool.SFFile = f"JetJvtEfficiency/May2024/{self._sfFileStem}_Run3_EMPFlow.root"
1018 alg.selection = decorationName
1019 alg.scaleFactorDecoration = f'{self._prefix}_effSF{suffix}_%SYS%'
1020 alg.outOfValidity = 2
1021 alg.outOfValidityDeco = f'no_{self._prefix}{suffix}'
1022 alg.skipBadEfficiency = False
1023 alg.jets = config.readName (self.containerName)
1024 alg.preselection = config.getPreselection (self.containerName, '')
1025
1026 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, f'{self._prefix}Efficiency{suffix}')
1027
1028
1030 """common base for the Jvt and fJvt event working point efficiency blocks"""
1031
1032 def __init__ (self) :
1033 super().__init__()
1034 self.addDependency(f'{self._algPrefix}WorkingPointEfficiencyConfig', required=True)
1035 self.addDependency('OverlapRemoval', required=False)
1036 self.addDependency('EventSelection', required=False)
1037 self.addDependency('EventSelectionMerger', required=False)
1038 self.addOption ('noEffSF', False, type=bool,
1039 info="disables the calculation of efficiencies and scale factors. "
1040 "Only useful to test a new WP for which scale factors are not available.",
1041 expertMode=True)
1042 self.addOption ('eventSF', True, type=bool,
1043 info="add calculation of event-level efficiency SF.")
1044
1045 def makeAlgs (self, config) :
1046
1047 if (not self.noEffSF and self.eventSF and
1048 config.dataType() is not DataType.Data):
1049 suffix = self.suffix()
1050 alg = config.createAlgorithm( 'CP::AsgEventScaleFactorAlg', f'{self._eventSFAlgPrefix}_{self.workingPoint()}' )
1051 preselection = config.getFullSelection (self.containerName, '')
1052 alg.preselection = preselection + f'&&no_{self._prefix}{suffix}' if preselection else f'no_{self._prefix}{suffix}'
1053 alg.scaleFactorInputDecoration = f'{self._prefix}_effSF{suffix}_%SYS%'
1054 alg.scaleFactorOutputDecoration = f'{self._prefix}_effSF{suffix}_%SYS%'
1055 alg.particles = config.readName (self.containerName)
1056
1057 config.addOutputVar('EventInfo', alg.scaleFactorOutputDecoration, f'weight_{self._prefix}_effSF{suffix}')
1058
1059
1061 """the parameters specific to the (NN)Jvt working point blocks"""
1062 _prefix = 'jvt'
1063 _algPrefix = 'Jvt'
1064 _eventSFAlgPrefix = 'JvtEventScaleFactorAlg'
1065 _wpOption = 'jvtWP'
1066 _wpInfo = "the NNJvt WP to use. Supported WPs: `FixedEffPt`."
1067 _wpChoices = ['FixedEffPt']
1068 _selectionTool = 'CP::NNJvtSelectionTool'
1069 _efficiencyTool = 'CP::NNJvtEfficiencyTool'
1070 _momentName = "NNJvt"
1071 _maxPtForJvt = 60*GeV
1072 _sfFileStem = "NNJvtSFFile"
1073
1074
1076 """the parameters specific to the fJvt working point blocks"""
1077 _prefix = 'fjvt'
1078 _algPrefix = 'FJvt'
1079 _eventSFAlgPrefix = 'ForwardJvtEventScaleFactorAlg'
1080 _wpOption = 'fjvtWP'
1081 _wpInfo = "the fJvt WP to use. Supported WPs: `Loose`, `Tight`, `Tighter`."
1082 _wpChoices = ['Loose','Tight','Tighter']
1083 _selectionTool = 'CP::FJvtSelectionTool'
1084 _efficiencyTool = 'CP::FJvtEfficiencyTool'
1085 _momentName = "DFCommonJets_fJvt"
1086 _maxPtForJvt = None
1087 _sfFileStem = "fJvtSFFile"
1088
1089
1091 """the ConfigBlock for the Jvt working point selection"""
1092
1093
1094class JvtWorkingPointEfficiencyConfig (_NNJvtParameters, _JvtWorkingPointEfficiencyBase) :
1095 """the ConfigBlock for the Jvt working point efficiency"""
1096
1097
1099 """the ConfigBlock for the event Jvt working point efficiency"""
1100
1101
1103 """the ConfigBlock for the fJvt working point selection"""
1104
1105
1107 """the ConfigBlock for the fJvt working point efficiency"""
1108
1109
1111 """the ConfigBlock for the event fJvt working point efficiency"""
1112
1113
1114@groupBlocks
1115def Jets(seq):
1116 seq.append(PreJetAnalysisConfig())
1117 seq.append(SmallRJetAnalysisConfig())
1118 seq.append(RScanJetAnalysisConfig())
1119 seq.append(LargeRJetAnalysisConfig())
1120
1121@groupBlocks
1123 seq.append(JvtWorkingPointSelectionConfig())
1126
1127@groupBlocks
static std::string FindCalibFile(const std::string &logical_file_name)
createFFSmearingTool(self, jetFFSmearingAlg, config)
createUncertaintyTool(self, jetUncertaintiesAlg, config, jetCollectionName, doPseudoData=False)
createUncertaintyTool(self, jetUncertaintiesAlg, config, jetCollectionName, doPseudoData=False)
Definition Jets.py:1
_parseJetCollection(jetCollection)
_resolveJetCollectionName(jetCollection)
_addJetUncertaintiesTool(config, alg, toolName, jetCollectionName, configFile, calibArea, mcType, isData, pseudoData)