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