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',
'', 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",
76 self.addOption (
'decorateTruth',
False, type=bool,
77 info=
"decorate truth particle information on the reconstructed one")
78 self.addOption (
'decorateCaloClusterEta',
False, type=bool,
79 info=
"decorate the calo cluster eta on the reconstructed one")
80 self.addOption (
'decorateEmva',
False, type=bool,
81 info=
"decorate E_mva_only on the objects (needed for columnar tools/PHYSLITE)")
84 """Return the instance name for this block"""
85 return self.containerName + self.postfix
89 """Create the calibration and smearing algorithm
91 Factoring this out into its own function, as we want to
92 instantiate it in multiple places"""
93 log = logging.getLogger(
'PhotonCalibrationConfig')
96 alg = config.createAlgorithm(
'CP::EgammaCalibrationAndSmearingAlg', name )
97 config.addPrivateTool(
'calibrationAndSmearingTool',
98 'CP::EgammaCalibrationAndSmearingTool' )
101 alg.calibrationAndSmearingTool.ESModel = self.ESModel
103 if config.geometry()
is LHCPeriod.Run2:
104 alg.calibrationAndSmearingTool.ESModel =
'es2023_R22_Run2_v1'
105 elif config.geometry()
is LHCPeriod.Run3:
106 alg.calibrationAndSmearingTool.ESModel =
'es2024_Run3_v0'
107 elif config.geometry()
is LHCPeriod.Run4:
108 log.warning(
"No ESModel set for Run4, using Run3 model")
109 alg.calibrationAndSmearingTool.ESModel =
'es2024_Run3_v0'
111 raise ValueError (f
"Can't set up the ElectronCalibrationConfig with {config.geometry().value}, "
112 "there must be something wrong!")
114 alg.calibrationAndSmearingTool.decorrelationModel = self.decorrelationModel
115 alg.calibrationAndSmearingTool.useFastSim = (
116 0
if self.forceFullSimConfigForP4
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,
'')
127 log = logging.getLogger(
'PhotonCalibrationConfig')
129 postfix = self.postfix
130 if postfix !=
'' and postfix[0] !=
'_' :
131 postfix =
'_' + postfix
133 if self.forceFullSimConfigForP4:
134 log.warning(
"You are running PhotonCalibrationConfig forcing full sim config for P4 corrections")
135 log.warning(
"This is only intended to be used for testing purposes")
137 if config.isPhyslite() :
138 config.setSourceName (self.containerName,
"AnalysisPhotons")
140 config.setSourceName (self.containerName,
"Photons")
142 cleaningWP =
'NoTime' if self.cleaningAllowLate
else ''
145 if self.decorateCaloClusterEta:
146 alg = config.createAlgorithm(
'CP::EgammaCaloClusterEtaAlg',
147 'ElectronEgammaCaloClusterEtaAlg',
149 alg.particles = config.readName(self.containerName)
150 config.addOutputVar (self.containerName,
'caloEta2',
'caloEta2', noSys=
True)
153 if config.wantCopy (self.containerName) :
154 alg = config.createAlgorithm(
'CP::AsgShallowCopyAlg',
'PhotonShallowCopyAlg' )
155 alg.input = config.readName (self.containerName)
156 alg.output = config.copyName (self.containerName)
159 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonEtaCutAlg' )
160 alg.selectionDecoration =
'selectEta' + postfix +
',as_bits'
161 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
162 alg.selectionTool.maxEta = self.maxEta
164 alg.selectionTool.etaGapLow = 1.37
165 alg.selectionTool.etaGapHigh = 1.52
166 alg.selectionTool.useClusterEta =
True
167 alg.particles = config.readName (self.containerName)
168 alg.preselection = config.getPreselection (self.containerName,
'')
169 config.addSelection (self.containerName,
'', alg.selectionDecoration)
172 if self.recomputeIsEM
and config.dataType()
is DataType.FullSim:
173 alg = config.createAlgorithm(
'CP::PhotonShowerShapeFudgeAlg',
174 'PhotonShowerShapeFudgeAlg' )
175 config.addPrivateTool(
'showerShapeFudgeTool',
176 'ElectronPhotonVariableCorrectionTool' )
177 if config.geometry
is LHCPeriod.Run2:
178 alg.showerShapeFudgeTool.ConfigFile = \
179 'EGammaVariableCorrection/TUNE25/ElPhVariableNominalCorrection.conf'
180 if config.geometry
is LHCPeriod.Run3:
181 alg.showerShapeFudgeTool.ConfigFile = \
182 'EGammaVariableCorrection/TUNE23/ElPhVariableNominalCorrection.conf'
183 alg.photons = config.readName (self.containerName)
184 alg.photonsOut = config.copyName (self.containerName)
185 alg.preselection = config.getPreselection (self.containerName,
'')
188 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonObjectQualityAlg' )
189 alg.selectionDecoration =
'goodOQ,as_bits'
190 config.addPrivateTool(
'selectionTool',
'CP::EgammaIsGoodOQSelectionTool' )
191 alg.selectionTool.Mask = xAOD.EgammaParameters.BADCLUSPHOTON
192 alg.particles = config.readName (self.containerName)
193 alg.preselection = config.getPreselection (self.containerName,
'')
194 config.addSelection (self.containerName,
'', alg.selectionDecoration)
197 if self.enableCleaning:
198 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonCleaningAlg' )
199 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
200 alg.selectionDecoration =
'isClean,as_bits'
201 alg.selectionTool.selectionFlags = [
'DFCommonPhotonsCleaning' + cleaningWP]
202 alg.particles = config.readName (self.containerName)
203 alg.preselection = config.getPreselection (self.containerName,
'')
204 config.addSelection (self.containerName,
'', alg.selectionDecoration)
209 alg = config.createAlgorithm(
'CP::PhotonOriginCorrectionAlg',
210 'PhotonOriginCorrectionAlg',
212 alg.photons = config.readName (self.containerName)
213 alg.photonsOut = config.copyName (self.containerName)
214 alg.preselection = config.getPreselection (self.containerName,
'')
216 if not self.splitCalibrationAndSmearing :
219 if config.isPhyslite()
and not self.recalibratePhyslite :
220 alg.skipNominal =
True
241 alg.noToolSystematics =
True
243 alg.calibrationAndSmearingTool.doSmearing =
False
248 alg.calibrationAndSmearingTool.doScaleCorrection =
False
249 alg.calibrationAndSmearingTool.useMVACalibration =
False
250 alg.calibrationAndSmearingTool.decorateEmva =
False
252 if not self.applyIsolationCorrection:
253 log.warning(
"You are not applying the isolation corrections")
254 log.warning(
"This is only intended to be used for testing purposes")
259 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonPtCutAlg' )
260 alg.selectionDecoration =
'selectPt' + postfix +
',as_bits'
261 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
262 alg.selectionTool.minPt = self.minPt
263 alg.particles = config.readName (self.containerName)
264 alg.preselection = config.getPreselection (self.containerName,
'')
265 config.addSelection (self.containerName,
'', alg.selectionDecoration,
269 if self.applyIsolationCorrection:
271 if self.forceFullSimConfigForIso:
272 log.warning(
"You are running PhotonCalibrationConfig forcing full sim config for isolation corrections")
273 log.warning(
"This is only intended to be used for testing purposes")
275 alg = config.createAlgorithm(
'CP::EgammaIsolationCorrectionAlg',
276 'PhotonIsolationCorrectionAlg' )
277 config.addPrivateTool(
'isolationCorrectionTool',
278 'CP::IsolationCorrectionTool' )
279 alg.isolationCorrectionTool.IsMC = config.dataType()
is not DataType.Data
280 alg.isolationCorrectionTool.AFII_corr = (
281 0
if self.forceFullSimConfigForIso
282 else config.dataType()
is DataType.FastSim)
283 alg.egammas = config.readName (self.containerName)
284 alg.egammasOut = config.copyName (self.containerName)
285 alg.preselection = config.getPreselection (self.containerName,
'')
288 alg = config.createAlgorithm(
'CP::AsgEnergyDecoratorAlg',
'EnergyDecorator' )
289 alg.particles = config.readName (self.containerName)
291 config.addOutputVar (self.containerName,
'pt',
'pt')
292 config.addOutputVar (self.containerName,
'eta',
'eta', noSys=
True)
293 config.addOutputVar (self.containerName,
'phi',
'phi', noSys=
True)
294 config.addOutputVar (self.containerName,
'e_%SYS%',
'e')
297 if self.decorateTruth
and config.dataType()
is not DataType.Data:
298 config.addOutputVar (self.containerName,
"truthType",
"truth_type", noSys=
True)
299 config.addOutputVar (self.containerName,
"truthOrigin",
"truth_origin", noSys=
True)
303 """the ConfigBlock for the photon working point
305 This may at some point be split into multiple blocks (29 Aug 22)."""
308 super (PhotonWorkingPointConfig, self).__init__ ()
309 self.addOption (
'containerName',
'', type=str,
311 info=
"the name of the input container.")
312 self.addOption (
'selectionName',
'', type=str,
314 info=
"the name of the photon selection to define (e.g. tight or "
316 self.addOption (
'postfix',
None, type=str,
317 info=
"a postfix to apply to decorations and algorithm names. "
318 "Typically not needed here as selectionName is used internally.")
319 self.addOption (
'qualityWP',
None, type=str,
320 info=
"the ID WP (string) to use. Supported ID WPs: Tight, Medium and Loose.")
321 self.addOption (
'isolationWP',
None, type=str,
322 info=
"the ID WP (string) to use. Supported isolation WPs: "
323 "FixedCutLoose, FixedCutTight, TightCaloOnly, NonIso.")
324 self.addOption (
'addSelectionToPreselection',
True, type=bool,
325 info=
"whether to retain only photons satisfying the working point "
326 "requirements. The default is True.")
327 self.addOption (
'closeByCorrection',
False, type=bool,
328 info=
"whether to use close-by-corrected isolation working points")
329 self.addOption (
'recomputeIsEM',
False, type=bool,
330 info=
"whether to rerun the cut-based selection. The default is "
331 "False, i.e. to use derivation flags.")
332 self.addOption (
'doFSRSelection',
False, type=bool,
333 info=
"whether to accept additional photons close to muons for the "
334 "purpose of FSR corrections to these muons. Expert feature "
335 "requested by the H4l analysis running on PHYSLITE. "
336 "The default is False.",
338 self.addOption (
'noEffSFForID',
False, type=bool,
339 info=
"disables the calculation of ID efficiencies and scale factors. "
340 "Experimental! only useful to test a new WP for which scale "
341 "factors are not available. The default is False.",
343 self.addOption (
'noEffSFForIso',
False, type=bool,
344 info=
"disables the calculation of Iso efficiencies and scale factors. "
345 "Experimental! only useful to test a new WP for which scale "
346 "factors are not available. The default is False.",
348 self.addOption (
'saveDetailedSF',
True, type=bool,
349 info=
"save all the independent detailed object scale factors. "
350 "The default is True.")
351 self.addOption (
'saveCombinedSF',
False, type=bool,
352 info=
"save the combined object scale factor. "
353 "The default is False.")
354 self.addOption (
'forceFullSimConfigForID',
False, type=bool,
355 info=
"whether to force the ID tool to use the configuration meant "
356 "for full simulation samples. Only for testing purposes. "
357 "The default is False.")
358 self.addOption (
'forceFullSimConfigForIso',
False, type=bool,
359 info=
"whether to force the Iso tool to use the configuration meant "
360 "for full simulation samples. Only for testing purposes. "
361 "The default is False.")
364 """Return the instance name for this block"""
365 if self.postfix
is not None :
366 return self.containerName +
'_' + self.selectionName + self.postfix
367 return self.containerName +
'_' + self.selectionName
371 log = logging.getLogger(
'PhotonWorkingPointConfig')
374 if config.geometry()
is LHCPeriod.Run1:
375 raise ValueError (
"Can't set up the PhotonWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
377 if self.forceFullSimConfigForID:
378 log.warning(
"You are running PhotonWorkingPointConfig forcing full sim config for ID")
379 log.warning(
"This is only intended to be used for testing purposes")
381 if self.forceFullSimConfigForIso:
382 log.warning(
"You are running PhotonWorkingPointConfig forcing full sim config for Iso")
383 log.warning(
"This is only intended to be used for testing purposes")
385 postfix = self.postfix
387 postfix = self.selectionName
388 if postfix !=
'' and postfix[0] !=
'_' :
389 postfix =
'_' + postfix
392 quality = ROOT.egammaPID.PhotonTight
394 quality = ROOT.egammaPID.PhotonMedium
396 quality = ROOT.egammaPID.PhotonLoose
398 raise Exception (
'unknown photon quality working point "' + self.
qualityWP +
'" should be Tight, Medium or Loose')
401 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonIsEMSelectorAlg' )
402 alg.selectionDecoration =
'selectEM' + postfix +
',as_char'
403 if self.recomputeIsEM:
405 config.addPrivateTool(
'selectionTool',
'AsgPhotonIsEMSelector' )
406 alg.selectionTool.isEMMask = quality
407 if config.geometry()
is LHCPeriod.Run2:
409 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc20_20240510/PhotonIsEMTightSelectorCutDefs_pTdep_mc20_smooth.conf'
411 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc15_20150712/PhotonIsEMLooseSelectorCutDefs.conf'
413 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc20_20240510/PhotonIsEMMediumSelectorCutDefs_pTdep_smooth.conf'
414 if config.geometry()
is LHCPeriod.Run3:
416 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/20180825/PhotonIsEMTightSelectorCutDefs.conf'
418 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc15_20150712/PhotonIsEMLooseSelectorCutDefs.conf'
420 raise ValueError(
'No Medium menu available for Run-3. Please get in contact with egamma')
423 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
424 dfFlag =
'DFCommonPhotonsIsEM' + self.
qualityWP
425 alg.selectionTool.selectionFlags = [ dfFlag ]
426 alg.particles = config.readName (self.containerName)
427 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
428 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
429 preselection=self.addSelectionToPreselection)
432 if self.doFSRSelection :
434 wpFlag = alg.selectionDecoration.split(
",")[0]
435 alg = config.createAlgorithm(
'CP::EgammaFSRForMuonsCollectorAlg',
'EgammaFSRForMuonsCollectorAlg')
436 alg.selectionDecoration = wpFlag
437 alg.ElectronOrPhotonContKey = config.readName (self.containerName)
440 if self.isolationWP !=
'NonIso' :
441 alg = config.createAlgorithm(
'CP::EgammaIsolationSelectionAlg',
442 'PhotonIsolationSelectionAlg' )
443 alg.selectionDecoration =
'isolated' + postfix +
',as_char'
444 config.addPrivateTool(
'selectionTool',
'CP::IsolationSelectionTool' )
445 alg.selectionTool.PhotonWP = self.isolationWP
446 if self.closeByCorrection:
447 alg.selectionTool.IsoDecSuffix =
"CloseByCorr"
449 alg.egammas = config.readName (self.containerName)
450 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
451 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
452 preselection=self.addSelectionToPreselection)
456 if config.dataType()
is not DataType.Data
and not self.noEffSFForID:
457 alg = config.createAlgorithm(
'CP::PhotonEfficiencyCorrectionAlg',
458 'PhotonEfficiencyCorrectionAlgID' )
459 config.addPrivateTool(
'efficiencyCorrectionTool',
460 'AsgPhotonEfficiencyCorrectionTool' )
461 alg.scaleFactorDecoration =
'ph_id_effSF' + postfix +
'_%SYS%'
462 if config.dataType()
is DataType.FastSim:
463 alg.efficiencyCorrectionTool.ForceDataType = (
464 PATCore.ParticleDataType.Full
if self.forceFullSimConfigForID
else
465 PATCore.ParticleDataType.Fast)
466 elif config.dataType()
is DataType.FullSim:
467 alg.efficiencyCorrectionTool.ForceDataType = \
468 PATCore.ParticleDataType.Full
469 if config.geometry() >= LHCPeriod.Run2:
470 alg.efficiencyCorrectionTool.MapFilePath =
'PhotonEfficiencyCorrection/2015_2025/rel22.2/2024_FinalRun2_Recommendation_v1/map1.txt'
471 alg.outOfValidity = 2
472 alg.outOfValidityDeco =
'ph_id_bad_eff' + postfix
473 alg.photons = config.readName (self.containerName)
474 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
475 if self.saveDetailedSF:
476 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
477 'id_effSF' + postfix)
478 sfList += [alg.scaleFactorDecoration]
481 if config.dataType()
is not DataType.Data
and self.isolationWP !=
'NonIso' and not self.noEffSFForIso:
482 alg = config.createAlgorithm(
'CP::PhotonEfficiencyCorrectionAlg',
483 'PhotonEfficiencyCorrectionAlgIsol' )
484 config.addPrivateTool(
'efficiencyCorrectionTool',
485 'AsgPhotonEfficiencyCorrectionTool' )
486 alg.scaleFactorDecoration =
'ph_isol_effSF' + postfix +
'_%SYS%'
487 if config.dataType()
is DataType.FastSim:
488 alg.efficiencyCorrectionTool.ForceDataType = (
489 PATCore.ParticleDataType.Full
if self.forceFullSimConfigForIso
else
490 PATCore.ParticleDataType.Fast)
491 elif config.dataType()
is DataType.FullSim:
492 alg.efficiencyCorrectionTool.ForceDataType = \
493 PATCore.ParticleDataType.Full
494 alg.efficiencyCorrectionTool.IsoKey = self.isolationWP.
replace(
"FixedCut",
"")
495 if config.geometry() >= LHCPeriod.Run2:
496 alg.efficiencyCorrectionTool.MapFilePath =
'PhotonEfficiencyCorrection/2015_2025/rel22.2/2022_Summer_Prerecom_v1/map1.txt'
497 alg.outOfValidity = 2
498 alg.outOfValidityDeco =
'ph_isol_bad_eff' + postfix
499 alg.photons = config.readName (self.containerName)
500 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
501 if self.saveDetailedSF:
502 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
503 'isol_effSF' + postfix)
504 sfList += [alg.scaleFactorDecoration]
506 doCombEffSF =
not self.noEffSFForID
or not self.noEffSFForIso
507 if config.dataType()
is not DataType.Data
and doCombEffSF
and self.saveCombinedSF:
508 alg = config.createAlgorithm(
'CP::AsgObjectScaleFactorAlg',
509 'PhotonCombinedEfficiencyScaleFactorAlg' )
510 alg.particles = config.readName (self.containerName)
511 alg.inScaleFactors = sfList
512 alg.outScaleFactor =
'effSF' + postfix +
'_%SYS%'
513 config.addOutputVar (self.containerName, alg.outScaleFactor,
'effSF' + postfix)