ATLAS Offline Software
Loading...
Searching...
No Matches
PhotonAnalysisConfig.py
Go to the documentation of this file.
1# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
3# AnaAlgorithm import(s):
4from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
5from AnalysisAlgorithmsConfig.ConfigSequence import groupBlocks
6from AthenaCommon.SystemOfUnits import GeV
7from AthenaConfiguration.Enums import LHCPeriod
8from AnalysisAlgorithmsConfig.ConfigAccumulator import (
9 DataType, Run4FallbackWarning, TestingOnlyWarning)
10import warnings
11
12import ROOT
13
14# E/gamma import(s).
15from xAODEgamma.xAODEgammaParameters import xAOD
16
18
19class PhotonCalibrationConfig (ConfigBlock) :
20 """the ConfigBlock for the photon four-momentum correction"""
21
22 def __init__ (self) :
23 super (PhotonCalibrationConfig, self).__init__ ()
24 self.setBlockName('Photons')
25 self.addOption ('containerName', '', type=str,
26 noneAction='error',
27 info="the name of the output container after calibration.",
28 meta={'role':'container'})
29 self.addOption ('ESModel', '', type=str,
30 info="flag for EGamma calibration. If left empty, uses the current recommendations.")
31 self.addOption ('decorrelationModel', '1NP_v1', type=str,
32 info="decorrelation model for the EGamma energy scale. Supported choices are: `FULL_v1`, `1NP_v1`.",
33 meta={'choices':(['FULL_v1','1NP_v1'],1)})
34 self.addOption ('postfix', '', type=str,
35 info="a postfix to apply to decorations and algorithm names. "
36 "Typically not needed here since the calibration is common to "
37 "all photons.")
38 self.addOption ('crackVeto', False, type=bool,
39 info=r"whether to perform LAr crack veto based on the cluster $\eta$, "
40 r"i.e. remove photons within $1.37<\vert\eta\vert<1.52$.")
41 self.addOption ('enableCleaning', True, type=bool,
42 info="whether to enable photon cleaning (`DFCommonPhotonsCleaning`).")
43 self.addOption ('cleaningAllowLate', False, type=bool,
44 info="whether to ignore timing information in cleaning "
45 "(`DFCommonPhotonsCleaningNoTime`).")
46 self.addOption ('recomputeIsEM', False, type=bool,
47 info="whether to recompute the photon shower shape fudge "
48 "corrections (sets up an instance of `CP::PhotonShowerShapeFudgeAlg`) or rely on derivation flags.")
49 self.addOption ('recalibratePhyslite', True, type=bool,
50 info="whether to run the `CP::EgammaCalibrationAndSmearingAlg` on "
51 "PHYSLITE derivations.")
52 self.addOption ('minPt', 10*GeV, type=float,
53 info=r"the minimum $p_\mathrm{T}$ cut (in MeV) to apply to calibrated photons.")
54 self.addOption ('maxEta', 2.37, type=float,
55 info=r"maximum photon $\vert\eta\vert$.")
56 self.addOption ('forceFullSimConfigForP4', False, type=bool,
57 info="whether to force the tool to use the configuration meant for "
58 "full simulation samples for 4-vector corrections. Only for testing purposes.")
59 self.addOption ('forceFullSimConfigForIso', False, type=bool,
60 info="whether to force the tool to use the configuration meant for "
61 "full simulation samples for isolation corrections. Only for testing purposes.")
62 self.addOption ('applyIsolationCorrection', True, type=bool,
63 info="whether to apply the isolation corrections.")
64 self.addOption ('splitCalibrationAndSmearing', False, type=bool,
65 info="EXPERIMENTAL: This splits the `EgammaCalibrationAndSmearingTool` "
66 " into two steps. The first step applies a baseline calibration that "
67 "is not affected by systematics. The second step then applies the "
68 "systematics dependent corrections. The net effect is that the "
69 "slower first step only has to be run once, while the second is run "
70 "once per systematic. ATLASG-2358.",
71 expertMode=True)
72 self.addOption ('decorateTruth', False, type=bool,
73 info="decorate the truth particle information on the reconstructed one.")
74 self.addOption ('decorateCaloClusterEta', False, type=bool,
75 info=r"decorate the calo-cluster $\eta$.")
76 self.addOption ('decorateEmva', False, type=bool,
77 info="decorate `E_mva_only` on the photons (needed for columnar tools/PHYSLITE).")
78 self.addOption ('addGlobalFELinksDep', False, type=bool,
79 info="whether to add dependencies for the global FE links (needed for PHYSLITE production)",
80 expertMode=True)
81
82 def instanceName (self) :
83 """Return the instance name for this block"""
84 return self.containerName + self.postfix
85
86
87 def makeCalibrationAndSmearingAlg (self, config, name) :
88 """Create the calibration and smearing algorithm
89
90 Factoring this out into its own function, as we want to
91 instantiate it in multiple places"""
92
93 # Set up the calibration and smearing algorithm:
94 alg = config.createAlgorithm( 'CP::EgammaCalibrationAndSmearingAlg', name )
95 config.addPrivateTool( 'calibrationAndSmearingTool',
96 'CP::EgammaCalibrationAndSmearingTool' )
97 # Set default ESModel per period
98 if self.ESModel:
99 alg.calibrationAndSmearingTool.ESModel = self.ESModel
100 else:
101 if config.geometry() is LHCPeriod.Run2:
102 alg.calibrationAndSmearingTool.ESModel = 'es2023_R22_Run2_v1'
103 elif config.geometry() is LHCPeriod.Run3:
104 alg.calibrationAndSmearingTool.ESModel = 'es2024_Run3_v0'
105 elif config.geometry() is LHCPeriod.Run4:
106 warnings.warn_explicit(
107 "No ESModel set for Run4, using Run3 model",
108 Run4FallbackWarning, filename='', lineno=0)
109 alg.calibrationAndSmearingTool.ESModel = 'es2024_Run3_v0'
110 else:
111 raise ValueError (f"Can't set up the ElectronCalibrationConfig with {config.geometry().value}, "
112 "there must be something wrong!")
113
114 alg.calibrationAndSmearingTool.decorrelationModel = self.decorrelationModel
115 alg.calibrationAndSmearingTool.useFastSim = (
117 else int( config.dataType() is DataType.FastSim ))
118 alg.calibrationAndSmearingTool.decorateEmva = self.decorateEmva
119 alg.egammas = config.readName (self.containerName)
120 alg.egammasOut = config.copyName (self.containerName)
121 alg.preselection = config.getPreselection (self.containerName, '')
122 return alg
123
124
125 def makeAlgs (self, config) :
126
127 postfix = self.postfix
128 if postfix != '' and postfix[0] != '_' :
129 postfix = '_' + postfix
130
132 warnings.warn_explicit(
133 "You are running PhotonCalibrationConfig forcing"
134 " full sim config for P4 corrections."
135 " This is only intended to be used for testing purposes.",
136 TestingOnlyWarning, filename='', lineno=0)
137
138 if config.isPhyslite() :
139 config.setSourceName (self.containerName, "AnalysisPhotons")
140 else :
141 config.setSourceName (self.containerName, "Photons")
142
143 cleaningWP = 'NoTime' if self.cleaningAllowLate else ''
144
145 # Decorate calo cluster eta if required
147 alg = config.createAlgorithm( 'CP::EgammaCaloClusterEtaAlg',
148 'ElectronEgammaCaloClusterEtaAlg',
149 reentrant=True )
150 alg.particles = config.readName(self.containerName)
151 config.addOutputVar (self.containerName, 'caloEta2', 'caloEta2', noSys=True)
152
153 # Set up a shallow copy to decorate
154 if config.wantCopy (self.containerName) :
155 alg = config.createAlgorithm( 'CP::AsgShallowCopyAlg', 'PhotonShallowCopyAlg' )
156 alg.input = config.readName (self.containerName)
157 alg.output = config.copyName (self.containerName)
158 alg.outputType = 'xAOD::PhotonContainer'
159 decorationList = ['DFCommonPhotonsCleaning',
160 'ptcone20_CloseByCorr',
161 'topoetcone20_CloseByCorr',
162 'topoetcone40_CloseByCorr']
164 decorationList += ['neutralGlobalFELinks', 'chargedGlobalFELinks']
165 if config.dataType() is not DataType.Data:
166 decorationList += ['TruthLink']
167 alg.declareDecorations = decorationList
168
169 # Set up the eta-cut on all photons prior to everything else
170 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'PhotonEtaCutAlg' )
171 alg.selectionDecoration = 'selectEta' + postfix + ',as_bits'
172 config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
173 alg.selectionTool.maxEta = self.maxEta
174 if self.crackVeto:
175 alg.selectionTool.etaGapLow = 1.37
176 alg.selectionTool.etaGapHigh = 1.52
177 alg.selectionTool.useClusterEta = True
178 alg.particles = config.readName (self.containerName)
179 alg.preselection = config.getPreselection (self.containerName, '')
180 config.addSelection (self.containerName, '', alg.selectionDecoration)
181
182 # Setup shower shape fudge
183 if self.recomputeIsEM and config.dataType() is DataType.FullSim:
184 alg = config.createAlgorithm( 'CP::PhotonShowerShapeFudgeAlg',
185 'PhotonShowerShapeFudgeAlg' )
186 config.addPrivateTool( 'showerShapeFudgeTool',
187 'ElectronPhotonVariableCorrectionTool' )
188 if config.geometry() is LHCPeriod.Run2:
189 alg.showerShapeFudgeTool.ConfigFile = \
190 'EGammaVariableCorrection/TUNE25/ElPhVariableNominalCorrection.conf'
191 if config.geometry() is LHCPeriod.Run3:
192 alg.showerShapeFudgeTool.ConfigFile = \
193 'EGammaVariableCorrection/TUNE23/ElPhVariableNominalCorrection.conf'
194 alg.photons = config.readName (self.containerName)
195 alg.photonsOut = config.copyName (self.containerName)
196 alg.preselection = config.getPreselection (self.containerName, '')
197
198 # Select photons only with good object quality.
199 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'PhotonObjectQualityAlg' )
200 config.setExtraInputs ({('xAOD::EventInfo', 'EventInfo.RandomRunNumber')})
201 alg.selectionDecoration = 'goodOQ,as_bits'
202 config.addPrivateTool( 'selectionTool', 'CP::EgammaIsGoodOQSelectionTool' )
203 alg.selectionTool.Mask = xAOD.EgammaParameters.BADCLUSPHOTON
204 alg.particles = config.readName (self.containerName)
205 alg.preselection = config.getPreselection (self.containerName, '')
206 config.addSelection (self.containerName, '', alg.selectionDecoration)
207
208 # Select clean photons
210 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'PhotonCleaningAlg' )
211 config.addPrivateTool( 'selectionTool', 'CP::AsgFlagSelectionTool' )
212 alg.selectionDecoration = 'isClean,as_bits'
213 alg.selectionTool.selectionFlags = ['DFCommonPhotonsCleaning' + cleaningWP]
214 alg.particles = config.readName (self.containerName)
215 alg.preselection = config.getPreselection (self.containerName, '')
216 config.addSelection (self.containerName, '', alg.selectionDecoration)
217
218 # Change the origin of Photons from (0,0,0) to (0,0,z)
219 # where z comes from the position of a vertex
220 # Default the one tagged as Primary
221 alg = config.createAlgorithm( 'CP::PhotonOriginCorrectionAlg',
222 'PhotonOriginCorrectionAlg',
223 reentrant=True )
224 alg.photons = config.readName (self.containerName)
225 alg.photonsOut = config.copyName (self.containerName)
226 alg.preselection = config.getPreselection (self.containerName, '')
227
229 # Set up the calibration and smearing algorithm:
230 alg = self.makeCalibrationAndSmearingAlg (config, 'PhotonCalibrationAndSmearingAlg')
231 if config.isPhyslite() and not self.recalibratePhyslite :
232 alg.skipNominal = True
233 else:
234 # This splits the EgammaCalibrationAndSmearingTool into two
235 # steps. The first step applies a baseline calibration that
236 # is not affected by systematics. The second step then
237 # applies the systematics dependent corrections. The net
238 # effect is that the slower first step only has to be run
239 # once, while the second is run once per systematic.
240 #
241 # For now (22 May 24) this has to happen in the same job, as
242 # the output of the first step is not part of PHYSLITE, and
243 # even for the nominal the output of the first and second
244 # step are different. In the future the plan is to put both
245 # the output of the first and second step into PHYSLITE,
246 # allowing to skip the first step when running on PHYSLITE.
247 #
248 # WARNING: All of this is experimental, see: ATLASG-2358
249
250 # Set up the calibration algorithm:
251 alg = self.makeCalibrationAndSmearingAlg (config, 'PhotonBaseCalibrationAlg')
252 # turn off systematics for the calibration step
253 alg.noToolSystematics = True
254 # turn off smearing for the calibration step
255 alg.calibrationAndSmearingTool.doSmearing = False
256
257 # Set up the smearing algorithm:
258 alg = self.makeCalibrationAndSmearingAlg (config, 'PhotonCalibrationSystematicsAlg')
259 # turn off scale corrections for the smearing step
260 alg.calibrationAndSmearingTool.doScaleCorrection = False
261 alg.calibrationAndSmearingTool.useMVACalibration = False
262 alg.calibrationAndSmearingTool.decorateEmva = False
263
265 warnings.warn_explicit(
266 "You are not applying the isolation corrections."
267 " This is only intended to be used for testing purposes.",
268 TestingOnlyWarning, filename='', lineno=0)
269
270 if self.minPt > 0:
271
272 # Set up the the pt selection
273 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'PhotonPtCutAlg' )
274 alg.selectionDecoration = 'selectPt' + postfix + ',as_bits'
275 config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
276 alg.selectionTool.minPt = self.minPt
277 alg.particles = config.readName (self.containerName)
278 alg.preselection = config.getPreselection (self.containerName, '')
279 config.addSelection (self.containerName, '', alg.selectionDecoration,
280 preselection=True)
281
282 # Set up the isolation correction algorithm.
284
286 warnings.warn_explicit(
287 "You are running PhotonCalibrationConfig forcing"
288 " full sim config for isolation corrections."
289 " This is only intended to be used for testing purposes.",
290 TestingOnlyWarning, filename='', lineno=0)
291
292 alg = config.createAlgorithm( 'CP::EgammaIsolationCorrectionAlg',
293 'PhotonIsolationCorrectionAlg' )
294 config.addPrivateTool( 'isolationCorrectionTool',
295 'CP::IsolationCorrectionTool' )
296 alg.isolationCorrectionTool.IsMC = config.dataType() is not DataType.Data
297 alg.isolationCorrectionTool.AFII_corr = (
299 else config.dataType() is DataType.FastSim)
300 alg.isolationCorrectionTool.FixTimingIssueInCore = True
301 alg.egammas = config.readName (self.containerName)
302 alg.egammasOut = config.copyName (self.containerName)
303 alg.preselection = config.getPreselection (self.containerName, '')
304
305 # Additional decorations
306 alg = config.createAlgorithm( 'CP::AsgEnergyDecoratorAlg', 'EnergyDecorator' )
307 alg.particles = config.readName (self.containerName)
308
309 config.addOutputVar (self.containerName, 'pt', 'pt')
310 config.addOutputVar (self.containerName, 'eta', 'eta', noSys=True)
311 config.addOutputVar (self.containerName, 'phi', 'phi', noSys=True)
312 config.addOutputVar (self.containerName, 'e_%SYS%', 'e')
313 config.addOutputVar (self.containerName, 'caloClusterEnergyReso_%SYS%', 'caloClusterEnergyReso', noSys=True)
314
315 # decorate truth information on the reconstructed object:
316 if self.decorateTruth and config.dataType() is not DataType.Data:
317 config.addOutputVar (self.containerName, "truthType", "truth_type", noSys=True)
318 config.addOutputVar (self.containerName, "truthOrigin", "truth_origin", noSys=True)
319
320
322 """the ConfigBlock for the photon working point selection"""
323
324 def __init__ (self) :
325 super (PhotonWorkingPointSelectionConfig, self).__init__ ()
326 self.setBlockName('PhotonWorkingPointSelection')
327 self.addOption ('containerName', '', type=str,
328 noneAction='error',
329 info="the name of the input container.",
330 meta={'role':'containerRef'})
331 self.addOption ('selectionName', '', type=str,
332 noneAction='error',
333 info="the name of the photon selection to define (e.g. `tight` or "
334 "`loose`).",
335 meta={'role':'selection'})
336 self.addOption ('postfix', None, type=str,
337 info="a postfix to apply to decorations and algorithm names. "
338 "Typically not needed here as `selectionName` is used internally.")
339 self.addOption ('qualityWP', None, type=str,
340 info="the ID WP to use. Supported ID WPs: `Tight`, `Medium`, `Loose`.",
341 meta={'choices':(['Tight','Medium','Loose'],1)})
342 self.addOption ('isolationWP', None, type=str,
343 info="the isolation WP to use. Supported isolation WPs: "
344 "`FixedCutLoose`, `FixedCutTight`, `TightCaloOnly`, `NonIso`.",
345 meta={'choices':(['FixedCutLoose','FixedCutTight','TightCaloOnly','NonIso'],1)})
346 self.addOption ('addSelectionToPreselection', True, type=bool,
347 info="whether to retain only photons satisfying the working point "
348 "requirements.")
349 self.addOption ('closeByCorrection', False, type=bool,
350 info="whether to use close-by-corrected isolation working points.")
351 self.addOption ('recomputeIsEM', False, type=bool,
352 info="whether to rerun the cut-based selection (`True`), or rely on derivation flags (`False`).")
353 self.addOption ('doFSRSelection', False, type=bool,
354 info="whether to accept additional photons close to muons for the "
355 "purpose of FSR corrections to these muons. Expert feature "
356 "requested by the H4l analysis running on PHYSLITE.",
357 expertMode=True)
358 self.addOption ('muonsForFSRSelection', None, type=str,
359 info="the name of the muon container to use for the FSR selection. "
360 "If not specified, AnalysisMuons is used.",
361 expertMode=True,
362 meta={'role':'containerRef'})
363
364 def instanceName (self) :
365 """Return the instance name for this block"""
366 if self.postfix is not None :
367 return self.containerName + '_' + self.selectionName + self.postfix
368 return self.containerName + '_' + self.selectionName
369
370 def makeAlgs (self, config) :
371
372 # The setup below is inappropriate for Run 1
373 if config.geometry() is LHCPeriod.Run1:
374 raise ValueError ("Can't set up the PhotonWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
375
376 postfix = self.postfix
377 if postfix is None :
378 postfix = self.selectionName
379 if postfix != '' and postfix[0] != '_' :
380 postfix = '_' + postfix
381
382 if self.qualityWP == 'Tight' :
383 quality = ROOT.egammaPID.PhotonTight
384 elif self.qualityWP == 'Medium' :
385 quality = ROOT.egammaPID.PhotonMedium
386 elif self.qualityWP == 'Loose' :
387 quality = ROOT.egammaPID.PhotonLoose
388 else :
389 raise Exception ('unknown photon quality working point "' + self.qualityWP + '" should be Tight, Medium or Loose')
390
391 # Set up the photon selection algorithm:
392 alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'PhotonIsEMSelectorAlg' )
393 alg.selectionDecoration = 'selectEM' + postfix + ',as_char'
395 # Rerun the cut-based ID
396 config.addPrivateTool( 'selectionTool', 'AsgPhotonIsEMSelector' )
397 alg.selectionTool.isEMMask = quality
398 if config.geometry() is LHCPeriod.Run2:
399 if self.qualityWP == 'Tight':
400 alg.selectionTool.ConfigFile = 'ElectronPhotonSelectorTools/offline/mc20_20240510/PhotonIsEMTightSelectorCutDefs_pTdep_mc20_smooth.conf'
401 elif self.qualityWP == 'Loose':
402 alg.selectionTool.ConfigFile = 'ElectronPhotonSelectorTools/offline/mc15_20150712/PhotonIsEMLooseSelectorCutDefs.conf'
403 elif self.qualityWP == 'Medium':
404 alg.selectionTool.ConfigFile = 'ElectronPhotonSelectorTools/offline/mc20_20240510/PhotonIsEMMediumSelectorCutDefs_pTdep_smooth.conf'
405 if config.geometry() is LHCPeriod.Run3:
406 if self.qualityWP == 'Tight':
407 alg.selectionTool.ConfigFile = 'ElectronPhotonSelectorTools/offline/20180825/PhotonIsEMTightSelectorCutDefs.conf'
408 elif self.qualityWP == 'Loose':
409 alg.selectionTool.ConfigFile = 'ElectronPhotonSelectorTools/offline/mc15_20150712/PhotonIsEMLooseSelectorCutDefs.conf'
410 elif self.qualityWP == 'Medium':
411 raise ValueError('No Medium menu available for Run-3. Please get in contact with egamma')
412 else:
413 # Select from Derivation Framework flags
414 config.addPrivateTool( 'selectionTool', 'CP::AsgFlagSelectionTool' )
415 dfFlag = 'DFCommonPhotonsIsEM' + self.qualityWP
416 alg.selectionTool.selectionFlags = [ dfFlag ]
417 alg.particles = config.readName (self.containerName)
418 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
419
420 # Set up the FSR selection
422 # wpSelection needs the ',as_char' suffix so SysReadSelectionHandle knows the type
423 wpDecoration = alg.selectionDecoration
424 wpDecorationName = wpDecoration.split(',')[0]
425 # Insert FSR before the postfix (e.g., selectEM_loose -> selectEMFSR_loose)
426 underscorePos = wpDecorationName.index('_')
427 outputDecorationName = wpDecorationName[:underscorePos] + 'FSR' + wpDecorationName[underscorePos:]
428
429 alg = config.createAlgorithm( 'CP::EgammaFSRForMuonsCollectorAlg', 'EgammaFSRForMuonsCollectorAlg')
430 alg.wpSelection = wpDecoration # Input: read the WP selection (with type suffix)
431 alg.selectionDecoration = outputDecorationName # Output: combined WP||FSR (name only for SysWriteDecorHandle)
432 alg.ElectronOrPhotonContKey = config.readName (self.containerName)
433 if self.muonsForFSRSelection is not None:
434 alg.MuonContKey = config.readName (self.muonsForFSRSelection)
435
436 # Register the FSR COMBINED selection
437 config.addSelection (self.containerName, self.selectionName,
438 alg.selectionDecoration + ',as_char',
439 preselection=self.addSelectionToPreselection)
440 else:
441 # No FSR - register the WP selection directly
442 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
443 preselection=self.addSelectionToPreselection)
444
445 # Set up the isolation selection algorithm:
446 if self.isolationWP != 'NonIso' :
447 alg = config.createAlgorithm( 'CP::EgammaIsolationSelectionAlg',
448 'PhotonIsolationSelectionAlg' )
449 alg.selectionDecoration = 'isolated' + postfix + ',as_char'
450 config.addPrivateTool( 'selectionTool', 'CP::IsolationSelectionTool' )
451 alg.selectionTool.PhotonWP = self.isolationWP
453 alg.selectionTool.IsoDecSuffix = "CloseByCorr"
454 alg.isPhoton = True
455 alg.egammas = config.readName (self.containerName)
456 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
457 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
458 preselection=self.addSelectionToPreselection)
459
460
462 """the ConfigBlock for the photon working point efficiency computation"""
463
464 def __init__ (self) :
465 super (PhotonWorkingPointEfficiencyConfig, self).__init__ ()
466 self.setBlockName('PhotonWorkingPointEfficiency')
467 self.addDependency('PhotonWorkingPointSelection', required=True)
468 self.addDependency('EventSelection', required=False)
469 self.addDependency('EventSelectionMerger', required=False)
470 self.addOption ('containerName', '', type=str,
471 noneAction='error',
472 info="the name of the input container.",
473 meta={'role':'containerRef'})
474 self.addOption ('selectionName', '', type=str,
475 noneAction='error',
476 info="the name of the photon selection to define (e.g. `tight` or "
477 "`loose`).",
478 meta={'role':'selection'})
479 self.addOption ('postfix', None, type=str,
480 info="a postfix to apply to decorations and algorithm names. "
481 "Typically not needed here as `selectionName` is used internally.")
482 self.addOption ('qualityWP', None, type=str,
483 info="the ID WP to use. Supported ID WPs: `Tight`, `Medium`, `Loose`.",
484 meta={'choices':(['Tight','Medium','Loose'],1)})
485 self.addOption ('isolationWP', None, type=str,
486 info="the isolation WP to use. Supported isolation WPs: "
487 "`FixedCutLoose`, `FixedCutTight`, `TightCaloOnly`, `NonIso`.",
488 meta={'choices':(['FixedCutLoose','FixedCutTight','TightCaloOnly','NonIso'],1)})
489 self.addOption ('noEffSFForID', False, type=bool,
490 info="disables the calculation of ID efficiencies and scale factors. "
491 "Experimental! only useful to test a new WP for which scale "
492 "factors are not available.",
493 expertMode=True)
494 self.addOption ('noEffSFForIso', False, type=bool,
495 info="disables the calculation of isolation efficiencies and scale factors. "
496 "Experimental! only useful to test a new WP for which scale "
497 "factors are not available.",
498 expertMode=True)
499 self.addOption ('saveDetailedSF', True, type=bool,
500 info="save all the independent detailed object scale factors.")
501 self.addOption ('saveCombinedSF', False, type=bool,
502 info="save the combined object scale factor.")
503 self.addOption ('forceFullSimConfigForID', False, type=bool,
504 info="whether to force the ID tool to use the configuration meant "
505 "for full simulation samples. Only for testing purposes.")
506 self.addOption ('forceFullSimConfigForIso', False, type=bool,
507 info="whether to force the isolation tool to use the configuration meant "
508 "for full simulation samples. Only for testing purposes.")
509
510 def instanceName (self) :
511 """Return the instance name for this block"""
512 if self.postfix is not None :
513 return self.containerName + '_' + self.selectionName + self.postfix
514 return self.containerName + '_' + self.selectionName
515
516 def makeAlgs (self, config) :
517
518 # The setup below is inappropriate for Run 1
519 if config.geometry() is LHCPeriod.Run1:
520 raise ValueError ("Can't set up the PhotonWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
521
523 warnings.warn_explicit(
524 "You are running PhotonWorkingPointConfig forcing"
525 " full sim config for ID."
526 " This is only intended to be used for testing purposes.",
527 TestingOnlyWarning, filename='', lineno=0)
528
530 warnings.warn_explicit(
531 "You are running PhotonWorkingPointConfig forcing"
532 " full sim config for Iso."
533 " This is only intended to be used for testing purposes.",
534 TestingOnlyWarning, filename='', lineno=0)
535
536 postfix = self.postfix
537 if postfix is None :
538 postfix = self.selectionName
539 if postfix != '' and postfix[0] != '_' :
540 postfix = '_' + postfix
541
542 sfList = []
543 # Set up the ID/reco photon efficiency correction algorithm:
544 if config.dataType() is not DataType.Data and not self.noEffSFForID:
545 alg = config.createAlgorithm( 'CP::PhotonEfficiencyCorrectionAlg',
546 'PhotonEfficiencyCorrectionAlgID' )
547 config.addPrivateTool( 'efficiencyCorrectionTool',
548 'AsgPhotonEfficiencyCorrectionTool' )
549 alg.scaleFactorDecoration = 'ph_id_effSF' + postfix + '_%SYS%'
550 if config.dataType() is DataType.FastSim:
551 alg.efficiencyCorrectionTool.ForceDataType = (
552 PATCore.ParticleDataType.Full if self.forceFullSimConfigForID else
553 PATCore.ParticleDataType.Fast)
554 elif config.dataType() is DataType.FullSim:
555 alg.efficiencyCorrectionTool.ForceDataType = \
556 PATCore.ParticleDataType.Full
557 if config.geometry() >= LHCPeriod.Run2:
558 alg.efficiencyCorrectionTool.MapFilePath = 'PhotonEfficiencyCorrection/2015_2025/rel22.2/2026_Run3Consolidated_Recommendation_v1/map0.txt'
559 alg.outOfValidity = 2 #silent
560 alg.outOfValidityDeco = 'ph_id_bad_eff' + postfix
561 alg.photons = config.readName (self.containerName)
562 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
564 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
565 'id_effSF' + postfix)
566 sfList += [alg.scaleFactorDecoration]
567
568 # Set up the ISO photon efficiency correction algorithm:
569 if config.dataType() is not DataType.Data and self.isolationWP != 'NonIso' and not self.noEffSFForIso:
570 alg = config.createAlgorithm( 'CP::PhotonEfficiencyCorrectionAlg',
571 'PhotonEfficiencyCorrectionAlgIsol' )
572 config.addPrivateTool( 'efficiencyCorrectionTool',
573 'AsgPhotonEfficiencyCorrectionTool' )
574 alg.scaleFactorDecoration = 'ph_isol_effSF' + postfix + '_%SYS%'
575 if config.dataType() is DataType.FastSim:
576 alg.efficiencyCorrectionTool.ForceDataType = (
577 PATCore.ParticleDataType.Full if self.forceFullSimConfigForIso else
578 PATCore.ParticleDataType.Fast)
579 elif config.dataType() is DataType.FullSim:
580 alg.efficiencyCorrectionTool.ForceDataType = \
581 PATCore.ParticleDataType.Full
582 alg.efficiencyCorrectionTool.IsoKey = self.isolationWP.replace("FixedCut","")
583 if config.geometry() >= LHCPeriod.Run2:
584 alg.efficiencyCorrectionTool.MapFilePath = 'PhotonEfficiencyCorrection/2015_2025/rel22.2/2022_Summer_Prerecom_v1/map1.txt'
585 alg.outOfValidity = 2 #silent
586 alg.outOfValidityDeco = 'ph_isol_bad_eff' + postfix
587 alg.photons = config.readName (self.containerName)
588 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
589 if self.saveDetailedSF:
590 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
591 'isol_effSF' + postfix)
592 sfList += [alg.scaleFactorDecoration]
593
594 doCombEffSF = not self.noEffSFForID or not self.noEffSFForIso
595 if config.dataType() is not DataType.Data and doCombEffSF and self.saveCombinedSF:
596 alg = config.createAlgorithm( 'CP::AsgObjectScaleFactorAlg',
597 'PhotonCombinedEfficiencyScaleFactorAlg' )
598 alg.particles = config.readName (self.containerName)
599 alg.inScaleFactors = sfList
600 alg.outScaleFactor = 'effSF' + postfix + '_%SYS%'
601 config.addOutputVar (self.containerName, alg.outScaleFactor, 'effSF' + postfix)
602
603
604@groupBlocks
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition hcg.cxx:312