4 from AnalysisAlgorithmsConfig.ConfigBlock
import ConfigBlock
6 from AthenaConfiguration.Enums
import LHCPeriod
7 from AnalysisAlgorithmsConfig.ConfigAccumulator
import DataType
8 from AthenaCommon.Logging
import logging
13 from xAODEgamma.xAODEgammaParameters
import xAOD
18 """the ConfigBlock for the photon four-momentum correction"""
21 super (PhotonCalibrationConfig, self).__init__ ()
22 self.setBlockName(
'Photons')
23 self.addOption (
'containerName', containerName, type=str,
25 info=
"the name of the output container after calibration.")
26 self.addOption (
'ESModel',
'', type=str,
27 info=
"flag of egamma calibration recommendation.")
28 self.addOption (
'decorrelationModel',
'1NP_v1', type=str,
29 info=
"egamma energy scale decorrelationModel. The default is 1NP_v1. "
30 "Supported Model: 1NP_v1, FULL_v1.")
31 self.addOption (
'postfix',
'', type=str,
32 info=
"a postfix to apply to decorations and algorithm names. "
33 "Typically not needed here since the calibration is common to "
35 self.addOption (
'crackVeto',
False, type=bool,
36 info=
"whether to perform LAr crack veto based on the cluster eta, "
37 "i.e. remove photons within 1.37<|eta|<1.52. "
38 "The default is False.")
39 self.addOption (
'enableCleaning',
True, type=bool,
40 info=
"whether to enable photon cleaning (DFCommonPhotonsCleaning). "
41 "The default is True.")
42 self.addOption (
'cleaningAllowLate',
False, type=bool,
43 info=
"whether to ignore timing information in cleaning "
44 "(DFCommonPhotonsCleaningNoTime). The default is False.")
45 self.addOption (
'recomputeIsEM',
False, type=bool,
46 info=
"whether to recompute the photon shower shape fudge "
47 "corrections (sets up an instance of CP::PhotonShowerShapeFudgeAlg). "
48 "The default is False, i.e. to use derivation variables.")
49 self.addOption (
'recalibratePhyslite',
True, type=bool,
50 info=
"whether to run the CP::EgammaCalibrationAndSmearingAlg on "
51 "PHYSLITE derivations. The default is True.")
52 self.addOption (
'minPt', 10*GeV, type=float,
53 info=
"the minimum pT cut to apply to calibrated photons. "
54 "The default is 10 GeV.")
55 self.addOption (
'maxEta', 2.37, type=float,
56 info=
"maximum photon |eta| (float). The default is 2.37.")
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 P4 corrections. Only for testing purposes. "
60 "The default is False.")
61 self.addOption (
'forceFullSimConfigForIso',
False, type=bool,
62 info=
"whether to force the tool to use the configuration meant for "
63 "full simulation samples for isolation corrections. Only for testing purposes. "
64 "The default is False.")
65 self.addOption (
'applyIsolationCorrection',
True, type=bool,
66 info=
"whether to to apply the isolation corrections "
67 "The default is True.")
68 self.addOption (
'splitCalibrationAndSmearing',
False, type=bool,
69 info=
"EXPERIMENTAL: This splits the EgammaCalibrationAndSmearingTool "
70 " into two steps. The first step applies a baseline calibration that "
71 "is not affected by systematics. The second step then applies the "
72 "systematics dependent corrections. The net effect is that the "
73 "slower first step only has to be run once, while the second is run "
74 "once per systematic. ATLASG-2358")
75 self.addOption (
'decorateTruth',
False, type=bool,
76 info=
"decorate truth particle information on the reconstructed one")
77 self.addOption (
'decorateCaloClusterEta',
False, type=bool,
78 info=
"decorate the calo cluster eta on the reconstructed one")
79 self.addOption (
'decorateEmva',
False, type=bool,
80 info=
"decorate E_mva_only on the objects (needed for columnar tools/PHYSLITE)")
84 """Create the calibration and smearing algorithm
86 Factoring this out into its own function, as we want to
87 instantiate it in multiple places"""
88 log = logging.getLogger(
'PhotonCalibrationConfig')
91 alg = config.createAlgorithm(
'CP::EgammaCalibrationAndSmearingAlg', name + self.postfix )
92 config.addPrivateTool(
'calibrationAndSmearingTool',
93 'CP::EgammaCalibrationAndSmearingTool' )
96 alg.calibrationAndSmearingTool.ESModel = self.ESModel
98 if config.geometry()
is LHCPeriod.Run2:
99 alg.calibrationAndSmearingTool.ESModel =
'es2023_R22_Run2_v1'
100 elif config.geometry()
is LHCPeriod.Run3:
101 alg.calibrationAndSmearingTool.ESModel =
'es2022_R22_PRE'
102 elif config.geometry()
is LHCPeriod.Run4:
103 log.warning(
"No ESModel set for Run4, using Run3 model")
104 alg.calibrationAndSmearingTool.ESModel =
'es2022_R22_PRE'
106 raise ValueError (f
"Can't set up the ElectronCalibrationConfig with {config.geometry().value}, "
107 "there must be something wrong!")
109 alg.calibrationAndSmearingTool.decorrelationModel = self.decorrelationModel
110 alg.calibrationAndSmearingTool.useFastSim = (
111 0
if self.forceFullSimConfigForP4
112 else int( config.dataType()
is DataType.FastSim ))
113 alg.calibrationAndSmearingTool.decorateEmva = self.decorateEmva
114 alg.egammas = config.readName (self.containerName)
115 alg.egammasOut = config.copyName (self.containerName)
116 alg.preselection = config.getPreselection (self.containerName,
'')
122 log = logging.getLogger(
'PhotonCalibrationConfig')
124 postfix = self.postfix
125 if postfix !=
'' and postfix[0] !=
'_' :
126 postfix =
'_' + postfix
128 if self.forceFullSimConfigForP4:
129 log.warning(
"You are running PhotonCalibrationConfig forcing full sim config for P4 corrections")
130 log.warning(
"This is only intended to be used for testing purposes")
132 if config.isPhyslite() :
133 config.setSourceName (self.containerName,
"AnalysisPhotons")
135 config.setSourceName (self.containerName,
"Photons")
137 cleaningWP =
'NoTime' if self.cleaningAllowLate
else ''
140 if self.decorateCaloClusterEta:
141 alg = config.createAlgorithm(
'CP::EgammaCaloClusterEtaAlg',
142 'ElectronEgammaCaloClusterEtaAlg' + self.postfix,
144 alg.particles = config.readName(self.containerName)
145 config.addOutputVar (self.containerName,
'caloEta2',
'caloEta2', noSys=
True)
148 if config.wantCopy (self.containerName) :
149 alg = config.createAlgorithm(
'CP::AsgShallowCopyAlg',
'PhotonShallowCopyAlg' + postfix )
150 alg.input = config.readName (self.containerName)
151 alg.output = config.copyName (self.containerName)
154 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonEtaCutAlg' + postfix )
155 alg.selectionDecoration =
'selectEta' + postfix +
',as_bits'
156 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
157 alg.selectionTool.maxEta = self.maxEta
159 alg.selectionTool.etaGapLow = 1.37
160 alg.selectionTool.etaGapHigh = 1.52
161 alg.selectionTool.useClusterEta =
True
162 alg.particles = config.readName (self.containerName)
163 alg.preselection = config.getPreselection (self.containerName,
'')
164 config.addSelection (self.containerName,
'', alg.selectionDecoration)
167 if self.recomputeIsEM
and config.dataType()
is DataType.FullSim:
168 alg = config.createAlgorithm(
'CP::PhotonShowerShapeFudgeAlg',
169 'PhotonShowerShapeFudgeAlg' + postfix )
170 config.addPrivateTool(
'showerShapeFudgeTool',
171 'ElectronPhotonVariableCorrectionTool' )
172 if config.geometry
is LHCPeriod.Run2:
173 alg.showerShapeFudgeTool.ConfigFile = \
174 'EGammaVariableCorrection/TUNE25/ElPhVariableNominalCorrection.conf'
175 if config.geometry
is LHCPeriod.Run3:
176 alg.showerShapeFudgeTool.ConfigFile = \
177 'EGammaVariableCorrection/TUNE23/ElPhVariableNominalCorrection.conf'
178 alg.photons = config.readName (self.containerName)
179 alg.photonsOut = config.copyName (self.containerName)
180 alg.preselection = config.getPreselection (self.containerName,
'')
183 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonObjectQualityAlg' + postfix )
184 alg.selectionDecoration =
'goodOQ,as_bits'
185 config.addPrivateTool(
'selectionTool',
'CP::EgammaIsGoodOQSelectionTool' )
186 alg.selectionTool.Mask = xAOD.EgammaParameters.BADCLUSPHOTON
187 alg.particles = config.readName (self.containerName)
188 alg.preselection = config.getPreselection (self.containerName,
'')
189 config.addSelection (self.containerName,
'', alg.selectionDecoration)
192 if self.enableCleaning:
193 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonCleaningAlg' + postfix)
194 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
195 alg.selectionDecoration =
'isClean,as_bits'
196 alg.selectionTool.selectionFlags = [
'DFCommonPhotonsCleaning' + cleaningWP]
197 alg.particles = config.readName (self.containerName)
198 alg.preselection = config.getPreselection (self.containerName,
'')
199 config.addSelection (self.containerName,
'', alg.selectionDecoration)
204 alg = config.createAlgorithm(
'CP::PhotonOriginCorrectionAlg',
205 'PhotonOriginCorrectionAlg' + postfix,
207 alg.photons = config.readName (self.containerName)
208 alg.photonsOut = config.copyName (self.containerName)
209 alg.preselection = config.getPreselection (self.containerName,
'')
211 if not self.splitCalibrationAndSmearing :
214 if config.isPhyslite()
and not self.recalibratePhyslite :
215 alg.skipNominal =
True
236 alg.noToolSystematics =
True
238 alg.calibrationAndSmearingTool.doSmearing =
False
243 alg.calibrationAndSmearingTool.doScaleCorrection =
False
244 alg.calibrationAndSmearingTool.useMVACalibration =
False
245 alg.calibrationAndSmearingTool.decorateEmva =
False
247 if not self.applyIsolationCorrection:
248 log.warning(
"You are not applying the isolation corrections")
249 log.warning(
"This is only intended to be used for testing purposes")
254 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonPtCutAlg' + postfix )
255 alg.selectionDecoration =
'selectPt' + postfix +
',as_bits'
256 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
257 alg.selectionTool.minPt = self.minPt
258 alg.particles = config.readName (self.containerName)
259 alg.preselection = config.getPreselection (self.containerName,
'')
260 config.addSelection (self.containerName,
'', alg.selectionDecoration,
264 if self.applyIsolationCorrection:
266 if self.forceFullSimConfigForIso:
267 log.warning(
"You are running PhotonCalibrationConfig forcing full sim config for isolation corrections")
268 log.warning(
"This is only intended to be used for testing purposes")
270 alg = config.createAlgorithm(
'CP::EgammaIsolationCorrectionAlg',
271 'PhotonIsolationCorrectionAlg' + postfix )
272 config.addPrivateTool(
'isolationCorrectionTool',
273 'CP::IsolationCorrectionTool' )
274 alg.isolationCorrectionTool.IsMC = config.dataType()
is not DataType.Data
275 alg.isolationCorrectionTool.AFII_corr = (
276 0
if self.forceFullSimConfigForIso
277 else config.dataType()
is DataType.FastSim)
278 alg.egammas = config.readName (self.containerName)
279 alg.egammasOut = config.copyName (self.containerName)
280 alg.preselection = config.getPreselection (self.containerName,
'')
283 alg = config.createAlgorithm(
'CP::AsgEnergyDecoratorAlg',
'EnergyDecorator' + self.containerName + self.postfix )
284 alg.particles = config.readName (self.containerName)
286 config.addOutputVar (self.containerName,
'pt',
'pt')
287 config.addOutputVar (self.containerName,
'eta',
'eta', noSys=
True)
288 config.addOutputVar (self.containerName,
'phi',
'phi', noSys=
True)
289 config.addOutputVar (self.containerName,
'e_%SYS%',
'e')
292 if self.decorateTruth
and config.dataType()
is not DataType.Data:
293 config.addOutputVar (self.containerName,
"truthType",
"truth_type", noSys=
True)
294 config.addOutputVar (self.containerName,
"truthOrigin",
"truth_origin", noSys=
True)
298 """the ConfigBlock for the photon working point
300 This may at some point be split into multiple blocks (29 Aug 22)."""
302 def __init__ (self, containerName='', selectionName='') :
303 super (PhotonWorkingPointConfig, self).__init__ ()
304 self.addOption (
'containerName', containerName, type=str,
306 info=
"the name of the input container.")
307 self.addOption (
'selectionName', selectionName, type=str,
309 info=
"the name of the photon selection to define (e.g. tight or "
311 self.addOption (
'postfix', selectionName, type=str,
312 info=
"a postfix to apply to decorations and algorithm names. "
313 "Typically not needed here as selectionName is used internally.")
314 self.addOption (
'qualityWP',
None, type=str,
315 info=
"the ID WP (string) to use. Supported ID WPs: Tight, Medium and Loose.")
316 self.addOption (
'isolationWP',
None, type=str,
317 info=
"the ID WP (string) to use. Supported isolation WPs: "
318 "FixedCutLoose, FixedCutTight, TightCaloOnly, NonIso.")
319 self.addOption (
'addSelectionToPreselection',
True, type=bool,
320 info=
"whether to retain only photons satisfying the working point "
321 "requirements. The default is True.")
322 self.addOption (
'closeByCorrection',
False, type=bool,
323 info=
"whether to use close-by-corrected isolation working points")
324 self.addOption (
'recomputeIsEM',
False, type=bool,
325 info=
"whether to rerun the cut-based selection. The default is "
326 "False, i.e. to use derivation flags.")
327 self.addOption (
'doFSRSelection',
False, type=bool,
328 info=
"whether to accept additional photons close to muons for the "
329 "purpose of FSR corrections to these muons. Expert feature "
330 "requested by the H4l analysis running on PHYSLITE. "
331 "The default is False.")
332 self.addOption (
'noEffSFForID',
False, type=bool,
333 info=
"disables the calculation of ID efficiencies and scale factors. "
334 "Experimental! only useful to test a new WP for which scale "
335 "factors are not available. The default is False.")
336 self.addOption (
'noEffSFForIso',
False, type=bool,
337 info=
"disables the calculation of Iso efficiencies and scale factors. "
338 "Experimental! only useful to test a new WP for which scale "
339 "factors are not available. The default is False.")
340 self.addOption (
'saveDetailedSF',
True, type=bool,
341 info=
"save all the independent detailed object scale factors. "
342 "The default is True.")
343 self.addOption (
'saveCombinedSF',
False, type=bool,
344 info=
"save the combined object scale factor. "
345 "The default is False.")
346 self.addOption (
'forceFullSimConfigForID',
False, type=bool,
347 info=
"whether to force the ID tool to use the configuration meant "
348 "for full simulation samples. Only for testing purposes. "
349 "The default is False.")
350 self.addOption (
'forceFullSimConfigForIso',
False, type=bool,
351 info=
"whether to force the Iso tool to use the configuration meant "
352 "for full simulation samples. Only for testing purposes. "
353 "The default is False.")
357 log = logging.getLogger(
'PhotonWorkingPointConfig')
360 if config.geometry()
is LHCPeriod.Run1:
361 raise ValueError (
"Can't set up the PhotonWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
363 if self.forceFullSimConfigForID:
364 log.warning(
"You are running PhotonWorkingPointConfig forcing full sim config for ID")
365 log.warning(
"This is only intended to be used for testing purposes")
367 if self.forceFullSimConfigForIso:
368 log.warning(
"You are running PhotonWorkingPointConfig forcing full sim config for Iso")
369 log.warning(
"This is only intended to be used for testing purposes")
371 postfix = self.postfix
372 if postfix !=
'' and postfix[0] !=
'_' :
373 postfix =
'_' + postfix
376 quality = ROOT.egammaPID.PhotonTight
378 quality = ROOT.egammaPID.PhotonMedium
380 quality = ROOT.egammaPID.PhotonLoose
382 raise Exception (
'unknown photon quality working point "' + self.
qualityWP +
'" should be Tight, Medium or Loose')
385 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonIsEMSelectorAlg' + postfix )
386 alg.selectionDecoration =
'selectEM' + postfix +
',as_char'
387 if self.recomputeIsEM:
389 config.addPrivateTool(
'selectionTool',
'AsgPhotonIsEMSelector' )
390 alg.selectionTool.isEMMask = quality
391 if config.geometry()
is LHCPeriod.Run2:
393 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc20_20240510/PhotonIsEMTightSelectorCutDefs_pTdep_mc20_smooth.conf'
395 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc15_20150712/PhotonIsEMLooseSelectorCutDefs.conf'
397 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc20_20240510/PhotonIsEMMediumSelectorCutDefs_pTdep_smooth.conf'
398 if config.geometry()
is LHCPeriod.Run3:
400 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/20180825/PhotonIsEMTightSelectorCutDefs.conf'
402 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc15_20150712/PhotonIsEMLooseSelectorCutDefs.conf'
404 raise ValueError(
'No Medium menu available for Run-3. Please get in contact with egamma')
407 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
408 dfFlag =
'DFCommonPhotonsIsEM' + self.
qualityWP
409 alg.selectionTool.selectionFlags = [ dfFlag ]
410 alg.particles = config.readName (self.containerName)
411 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
412 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
413 preselection=self.addSelectionToPreselection)
416 if self.doFSRSelection :
418 wpFlag = alg.selectionDecoration.split(
",")[0]
419 alg = config.createAlgorithm(
'CP::EgammaFSRForMuonsCollectorAlg',
'EgammaFSRForMuonsCollectorAlg' + postfix +
'_ph')
420 alg.selectionDecoration = wpFlag
421 alg.ElectronOrPhotonContKey = config.readName (self.containerName)
424 if self.isolationWP !=
'NonIso' :
425 alg = config.createAlgorithm(
'CP::EgammaIsolationSelectionAlg',
426 'PhotonIsolationSelectionAlg' + postfix )
427 alg.selectionDecoration =
'isolated' + postfix +
',as_char'
428 config.addPrivateTool(
'selectionTool',
'CP::IsolationSelectionTool' )
429 alg.selectionTool.PhotonWP = self.isolationWP
430 if self.closeByCorrection:
431 alg.selectionTool.IsoDecSuffix =
"CloseByCorr"
433 alg.egammas = config.readName (self.containerName)
434 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
435 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
436 preselection=self.addSelectionToPreselection)
440 if config.dataType()
is not DataType.Data
and not self.noEffSFForID:
441 alg = config.createAlgorithm(
'CP::PhotonEfficiencyCorrectionAlg',
442 'PhotonEfficiencyCorrectionAlgID' + postfix )
443 config.addPrivateTool(
'efficiencyCorrectionTool',
444 'AsgPhotonEfficiencyCorrectionTool' )
445 alg.scaleFactorDecoration =
'ph_id_effSF' + postfix +
'_%SYS%'
446 if config.dataType()
is DataType.FastSim:
447 alg.efficiencyCorrectionTool.ForceDataType = (
448 PATCore.ParticleDataType.Full
if self.forceFullSimConfigForID
else
449 PATCore.ParticleDataType.Fast)
450 elif config.dataType()
is DataType.FullSim:
451 alg.efficiencyCorrectionTool.ForceDataType = \
452 PATCore.ParticleDataType.Full
453 if config.geometry() >= LHCPeriod.Run2:
454 alg.efficiencyCorrectionTool.MapFilePath =
'PhotonEfficiencyCorrection/2015_2025/rel22.2/2024_FinalRun2_Recommendation_v1/map1.txt'
455 alg.outOfValidity = 2
456 alg.outOfValidityDeco =
'ph_id_bad_eff' + postfix
457 alg.photons = config.readName (self.containerName)
458 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
459 if self.saveDetailedSF:
460 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
461 'id_effSF' + postfix)
462 sfList += [alg.scaleFactorDecoration]
465 if config.dataType()
is not DataType.Data
and self.isolationWP !=
'NonIso' and not self.noEffSFForIso:
466 alg = config.createAlgorithm(
'CP::PhotonEfficiencyCorrectionAlg',
467 'PhotonEfficiencyCorrectionAlgIsol' + postfix )
468 config.addPrivateTool(
'efficiencyCorrectionTool',
469 'AsgPhotonEfficiencyCorrectionTool' )
470 alg.scaleFactorDecoration =
'ph_isol_effSF' + postfix +
'_%SYS%'
471 if config.dataType()
is DataType.FastSim:
472 alg.efficiencyCorrectionTool.ForceDataType = (
473 PATCore.ParticleDataType.Full
if self.forceFullSimConfigForIso
else
474 PATCore.ParticleDataType.Fast)
475 elif config.dataType()
is DataType.FullSim:
476 alg.efficiencyCorrectionTool.ForceDataType = \
477 PATCore.ParticleDataType.Full
478 alg.efficiencyCorrectionTool.IsoKey = self.isolationWP.
replace(
"FixedCut",
"")
479 if config.geometry() >= LHCPeriod.Run2:
480 alg.efficiencyCorrectionTool.MapFilePath =
'PhotonEfficiencyCorrection/2015_2025/rel22.2/2022_Summer_Prerecom_v1/map1.txt'
481 alg.outOfValidity = 2
482 alg.outOfValidityDeco =
'ph_isol_bad_eff' + postfix
483 alg.photons = config.readName (self.containerName)
484 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
485 if self.saveDetailedSF:
486 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
487 'isol_effSF' + postfix)
488 sfList += [alg.scaleFactorDecoration]
490 doCombEffSF =
not self.noEffSFForID
or not self.noEffSFForIso
491 if config.dataType()
is not DataType.Data
and doCombEffSF
and self.saveCombinedSF:
492 alg = config.createAlgorithm(
'CP::AsgObjectScaleFactorAlg',
493 'PhotonCombinedEfficiencyScaleFactorAlg' + postfix )
494 alg.particles = config.readName (self.containerName)
495 alg.inScaleFactors = sfList
496 alg.outScaleFactor =
'effSF' + postfix +
'_%SYS%'
497 config.addOutputVar (self.containerName, alg.outScaleFactor,
'effSF' + postfix)