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")
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)")
83 """Return the instance name for this block"""
84 return self.containerName + self.postfix
88 """Create the calibration and smearing algorithm
90 Factoring this out into its own function, as we want to
91 instantiate it in multiple places"""
92 log = logging.getLogger(
'PhotonCalibrationConfig')
95 alg = config.createAlgorithm(
'CP::EgammaCalibrationAndSmearingAlg', name )
96 config.addPrivateTool(
'calibrationAndSmearingTool',
97 'CP::EgammaCalibrationAndSmearingTool' )
100 alg.calibrationAndSmearingTool.ESModel = self.ESModel
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 log.warning(
"No ESModel set for Run4, using Run3 model")
108 alg.calibrationAndSmearingTool.ESModel =
'es2024_Run3_v0'
110 raise ValueError (f
"Can't set up the ElectronCalibrationConfig with {config.geometry().value}, "
111 "there must be something wrong!")
113 alg.calibrationAndSmearingTool.decorrelationModel = self.decorrelationModel
114 alg.calibrationAndSmearingTool.useFastSim = (
115 0
if self.forceFullSimConfigForP4
116 else int( config.dataType()
is DataType.FastSim ))
117 alg.calibrationAndSmearingTool.decorateEmva = self.decorateEmva
118 alg.egammas = config.readName (self.containerName)
119 alg.egammasOut = config.copyName (self.containerName)
120 alg.preselection = config.getPreselection (self.containerName,
'')
126 log = logging.getLogger(
'PhotonCalibrationConfig')
128 postfix = self.postfix
129 if postfix !=
'' and postfix[0] !=
'_' :
130 postfix =
'_' + postfix
132 if self.forceFullSimConfigForP4:
133 log.warning(
"You are running PhotonCalibrationConfig forcing full sim config for P4 corrections")
134 log.warning(
"This is only intended to be used for testing purposes")
136 if config.isPhyslite() :
137 config.setSourceName (self.containerName,
"AnalysisPhotons")
139 config.setSourceName (self.containerName,
"Photons")
141 cleaningWP =
'NoTime' if self.cleaningAllowLate
else ''
144 if self.decorateCaloClusterEta:
145 alg = config.createAlgorithm(
'CP::EgammaCaloClusterEtaAlg',
146 'ElectronEgammaCaloClusterEtaAlg',
148 alg.particles = config.readName(self.containerName)
149 config.addOutputVar (self.containerName,
'caloEta2',
'caloEta2', noSys=
True)
152 if config.wantCopy (self.containerName) :
153 alg = config.createAlgorithm(
'CP::AsgShallowCopyAlg',
'PhotonShallowCopyAlg' )
154 alg.input = config.readName (self.containerName)
155 alg.output = config.copyName (self.containerName)
158 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonEtaCutAlg' )
159 alg.selectionDecoration =
'selectEta' + postfix +
',as_bits'
160 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
161 alg.selectionTool.maxEta = self.maxEta
163 alg.selectionTool.etaGapLow = 1.37
164 alg.selectionTool.etaGapHigh = 1.52
165 alg.selectionTool.useClusterEta =
True
166 alg.particles = config.readName (self.containerName)
167 alg.preselection = config.getPreselection (self.containerName,
'')
168 config.addSelection (self.containerName,
'', alg.selectionDecoration)
171 if self.recomputeIsEM
and config.dataType()
is DataType.FullSim:
172 alg = config.createAlgorithm(
'CP::PhotonShowerShapeFudgeAlg',
173 'PhotonShowerShapeFudgeAlg' )
174 config.addPrivateTool(
'showerShapeFudgeTool',
175 'ElectronPhotonVariableCorrectionTool' )
176 if config.geometry
is LHCPeriod.Run2:
177 alg.showerShapeFudgeTool.ConfigFile = \
178 'EGammaVariableCorrection/TUNE25/ElPhVariableNominalCorrection.conf'
179 if config.geometry
is LHCPeriod.Run3:
180 alg.showerShapeFudgeTool.ConfigFile = \
181 'EGammaVariableCorrection/TUNE23/ElPhVariableNominalCorrection.conf'
182 alg.photons = config.readName (self.containerName)
183 alg.photonsOut = config.copyName (self.containerName)
184 alg.preselection = config.getPreselection (self.containerName,
'')
187 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonObjectQualityAlg' )
188 alg.selectionDecoration =
'goodOQ,as_bits'
189 config.addPrivateTool(
'selectionTool',
'CP::EgammaIsGoodOQSelectionTool' )
190 alg.selectionTool.Mask = xAOD.EgammaParameters.BADCLUSPHOTON
191 alg.particles = config.readName (self.containerName)
192 alg.preselection = config.getPreselection (self.containerName,
'')
193 config.addSelection (self.containerName,
'', alg.selectionDecoration)
196 if self.enableCleaning:
197 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonCleaningAlg' )
198 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
199 alg.selectionDecoration =
'isClean,as_bits'
200 alg.selectionTool.selectionFlags = [
'DFCommonPhotonsCleaning' + cleaningWP]
201 alg.particles = config.readName (self.containerName)
202 alg.preselection = config.getPreselection (self.containerName,
'')
203 config.addSelection (self.containerName,
'', alg.selectionDecoration)
208 alg = config.createAlgorithm(
'CP::PhotonOriginCorrectionAlg',
209 'PhotonOriginCorrectionAlg',
211 alg.photons = config.readName (self.containerName)
212 alg.photonsOut = config.copyName (self.containerName)
213 alg.preselection = config.getPreselection (self.containerName,
'')
215 if not self.splitCalibrationAndSmearing :
218 if config.isPhyslite()
and not self.recalibratePhyslite :
219 alg.skipNominal =
True
240 alg.noToolSystematics =
True
242 alg.calibrationAndSmearingTool.doSmearing =
False
247 alg.calibrationAndSmearingTool.doScaleCorrection =
False
248 alg.calibrationAndSmearingTool.useMVACalibration =
False
249 alg.calibrationAndSmearingTool.decorateEmva =
False
251 if not self.applyIsolationCorrection:
252 log.warning(
"You are not applying the isolation corrections")
253 log.warning(
"This is only intended to be used for testing purposes")
258 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonPtCutAlg' )
259 alg.selectionDecoration =
'selectPt' + postfix +
',as_bits'
260 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
261 alg.selectionTool.minPt = self.minPt
262 alg.particles = config.readName (self.containerName)
263 alg.preselection = config.getPreselection (self.containerName,
'')
264 config.addSelection (self.containerName,
'', alg.selectionDecoration,
268 if self.applyIsolationCorrection:
270 if self.forceFullSimConfigForIso:
271 log.warning(
"You are running PhotonCalibrationConfig forcing full sim config for isolation corrections")
272 log.warning(
"This is only intended to be used for testing purposes")
274 alg = config.createAlgorithm(
'CP::EgammaIsolationCorrectionAlg',
275 'PhotonIsolationCorrectionAlg' )
276 config.addPrivateTool(
'isolationCorrectionTool',
277 'CP::IsolationCorrectionTool' )
278 alg.isolationCorrectionTool.IsMC = config.dataType()
is not DataType.Data
279 alg.isolationCorrectionTool.AFII_corr = (
280 0
if self.forceFullSimConfigForIso
281 else config.dataType()
is DataType.FastSim)
282 alg.egammas = config.readName (self.containerName)
283 alg.egammasOut = config.copyName (self.containerName)
284 alg.preselection = config.getPreselection (self.containerName,
'')
287 alg = config.createAlgorithm(
'CP::AsgEnergyDecoratorAlg',
'EnergyDecorator' )
288 alg.particles = config.readName (self.containerName)
290 config.addOutputVar (self.containerName,
'pt',
'pt')
291 config.addOutputVar (self.containerName,
'eta',
'eta', noSys=
True)
292 config.addOutputVar (self.containerName,
'phi',
'phi', noSys=
True)
293 config.addOutputVar (self.containerName,
'e_%SYS%',
'e')
296 if self.decorateTruth
and config.dataType()
is not DataType.Data:
297 config.addOutputVar (self.containerName,
"truthType",
"truth_type", noSys=
True)
298 config.addOutputVar (self.containerName,
"truthOrigin",
"truth_origin", noSys=
True)
302 """the ConfigBlock for the photon working point
304 This may at some point be split into multiple blocks (29 Aug 22)."""
307 super (PhotonWorkingPointConfig, self).__init__ ()
308 self.addOption (
'containerName',
'', type=str,
310 info=
"the name of the input container.")
311 self.addOption (
'selectionName',
'', type=str,
313 info=
"the name of the photon selection to define (e.g. tight or "
315 self.addOption (
'postfix',
None, type=str,
316 info=
"a postfix to apply to decorations and algorithm names. "
317 "Typically not needed here as selectionName is used internally.")
318 self.addOption (
'qualityWP',
None, type=str,
319 info=
"the ID WP (string) to use. Supported ID WPs: Tight, Medium and Loose.")
320 self.addOption (
'isolationWP',
None, type=str,
321 info=
"the ID WP (string) to use. Supported isolation WPs: "
322 "FixedCutLoose, FixedCutTight, TightCaloOnly, NonIso.")
323 self.addOption (
'addSelectionToPreselection',
True, type=bool,
324 info=
"whether to retain only photons satisfying the working point "
325 "requirements. The default is True.")
326 self.addOption (
'closeByCorrection',
False, type=bool,
327 info=
"whether to use close-by-corrected isolation working points")
328 self.addOption (
'recomputeIsEM',
False, type=bool,
329 info=
"whether to rerun the cut-based selection. The default is "
330 "False, i.e. to use derivation flags.")
331 self.addOption (
'doFSRSelection',
False, type=bool,
332 info=
"whether to accept additional photons close to muons for the "
333 "purpose of FSR corrections to these muons. Expert feature "
334 "requested by the H4l analysis running on PHYSLITE. "
335 "The default is False.")
336 self.addOption (
'noEffSFForID',
False, type=bool,
337 info=
"disables the calculation of ID 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 (
'noEffSFForIso',
False, type=bool,
341 info=
"disables the calculation of Iso efficiencies and scale factors. "
342 "Experimental! only useful to test a new WP for which scale "
343 "factors are not available. The default is False.")
344 self.addOption (
'saveDetailedSF',
True, type=bool,
345 info=
"save all the independent detailed object scale factors. "
346 "The default is True.")
347 self.addOption (
'saveCombinedSF',
False, type=bool,
348 info=
"save the combined object scale factor. "
349 "The default is False.")
350 self.addOption (
'forceFullSimConfigForID',
False, type=bool,
351 info=
"whether to force the ID tool to use the configuration meant "
352 "for full simulation samples. Only for testing purposes. "
353 "The default is False.")
354 self.addOption (
'forceFullSimConfigForIso',
False, type=bool,
355 info=
"whether to force the Iso tool to use the configuration meant "
356 "for full simulation samples. Only for testing purposes. "
357 "The default is False.")
360 """Return the instance name for this block"""
361 if self.postfix
is not None :
362 return self.containerName +
'_' + self.selectionName + self.postfix
363 return self.containerName +
'_' + self.selectionName
367 log = logging.getLogger(
'PhotonWorkingPointConfig')
370 if config.geometry()
is LHCPeriod.Run1:
371 raise ValueError (
"Can't set up the PhotonWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
373 if self.forceFullSimConfigForID:
374 log.warning(
"You are running PhotonWorkingPointConfig forcing full sim config for ID")
375 log.warning(
"This is only intended to be used for testing purposes")
377 if self.forceFullSimConfigForIso:
378 log.warning(
"You are running PhotonWorkingPointConfig forcing full sim config for Iso")
379 log.warning(
"This is only intended to be used for testing purposes")
381 postfix = self.postfix
383 postfix = self.selectionName
384 if postfix !=
'' and postfix[0] !=
'_' :
385 postfix =
'_' + postfix
388 quality = ROOT.egammaPID.PhotonTight
390 quality = ROOT.egammaPID.PhotonMedium
392 quality = ROOT.egammaPID.PhotonLoose
394 raise Exception (
'unknown photon quality working point "' + self.
qualityWP +
'" should be Tight, Medium or Loose')
397 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonIsEMSelectorAlg' )
398 alg.selectionDecoration =
'selectEM' + postfix +
',as_char'
399 if self.recomputeIsEM:
401 config.addPrivateTool(
'selectionTool',
'AsgPhotonIsEMSelector' )
402 alg.selectionTool.isEMMask = quality
403 if config.geometry()
is LHCPeriod.Run2:
405 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc20_20240510/PhotonIsEMTightSelectorCutDefs_pTdep_mc20_smooth.conf'
407 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc15_20150712/PhotonIsEMLooseSelectorCutDefs.conf'
409 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc20_20240510/PhotonIsEMMediumSelectorCutDefs_pTdep_smooth.conf'
410 if config.geometry()
is LHCPeriod.Run3:
412 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/20180825/PhotonIsEMTightSelectorCutDefs.conf'
414 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc15_20150712/PhotonIsEMLooseSelectorCutDefs.conf'
416 raise ValueError(
'No Medium menu available for Run-3. Please get in contact with egamma')
419 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
420 dfFlag =
'DFCommonPhotonsIsEM' + self.
qualityWP
421 alg.selectionTool.selectionFlags = [ dfFlag ]
422 alg.particles = config.readName (self.containerName)
423 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
424 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
425 preselection=self.addSelectionToPreselection)
428 if self.doFSRSelection :
430 wpFlag = alg.selectionDecoration.split(
",")[0]
431 alg = config.createAlgorithm(
'CP::EgammaFSRForMuonsCollectorAlg',
'EgammaFSRForMuonsCollectorAlg')
432 alg.selectionDecoration = wpFlag
433 alg.ElectronOrPhotonContKey = config.readName (self.containerName)
436 if self.isolationWP !=
'NonIso' :
437 alg = config.createAlgorithm(
'CP::EgammaIsolationSelectionAlg',
438 'PhotonIsolationSelectionAlg' )
439 alg.selectionDecoration =
'isolated' + postfix +
',as_char'
440 config.addPrivateTool(
'selectionTool',
'CP::IsolationSelectionTool' )
441 alg.selectionTool.PhotonWP = self.isolationWP
442 if self.closeByCorrection:
443 alg.selectionTool.IsoDecSuffix =
"CloseByCorr"
445 alg.egammas = config.readName (self.containerName)
446 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
447 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
448 preselection=self.addSelectionToPreselection)
452 if config.dataType()
is not DataType.Data
and not self.noEffSFForID:
453 alg = config.createAlgorithm(
'CP::PhotonEfficiencyCorrectionAlg',
454 'PhotonEfficiencyCorrectionAlgID' )
455 config.addPrivateTool(
'efficiencyCorrectionTool',
456 'AsgPhotonEfficiencyCorrectionTool' )
457 alg.scaleFactorDecoration =
'ph_id_effSF' + postfix +
'_%SYS%'
458 if config.dataType()
is DataType.FastSim:
459 alg.efficiencyCorrectionTool.ForceDataType = (
460 PATCore.ParticleDataType.Full
if self.forceFullSimConfigForID
else
461 PATCore.ParticleDataType.Fast)
462 elif config.dataType()
is DataType.FullSim:
463 alg.efficiencyCorrectionTool.ForceDataType = \
464 PATCore.ParticleDataType.Full
465 if config.geometry() >= LHCPeriod.Run2:
466 alg.efficiencyCorrectionTool.MapFilePath =
'PhotonEfficiencyCorrection/2015_2025/rel22.2/2024_FinalRun2_Recommendation_v1/map1.txt'
467 alg.outOfValidity = 2
468 alg.outOfValidityDeco =
'ph_id_bad_eff' + postfix
469 alg.photons = config.readName (self.containerName)
470 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
471 if self.saveDetailedSF:
472 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
473 'id_effSF' + postfix)
474 sfList += [alg.scaleFactorDecoration]
477 if config.dataType()
is not DataType.Data
and self.isolationWP !=
'NonIso' and not self.noEffSFForIso:
478 alg = config.createAlgorithm(
'CP::PhotonEfficiencyCorrectionAlg',
479 'PhotonEfficiencyCorrectionAlgIsol' )
480 config.addPrivateTool(
'efficiencyCorrectionTool',
481 'AsgPhotonEfficiencyCorrectionTool' )
482 alg.scaleFactorDecoration =
'ph_isol_effSF' + postfix +
'_%SYS%'
483 if config.dataType()
is DataType.FastSim:
484 alg.efficiencyCorrectionTool.ForceDataType = (
485 PATCore.ParticleDataType.Full
if self.forceFullSimConfigForIso
else
486 PATCore.ParticleDataType.Fast)
487 elif config.dataType()
is DataType.FullSim:
488 alg.efficiencyCorrectionTool.ForceDataType = \
489 PATCore.ParticleDataType.Full
490 alg.efficiencyCorrectionTool.IsoKey = self.isolationWP.
replace(
"FixedCut",
"")
491 if config.geometry() >= LHCPeriod.Run2:
492 alg.efficiencyCorrectionTool.MapFilePath =
'PhotonEfficiencyCorrection/2015_2025/rel22.2/2022_Summer_Prerecom_v1/map1.txt'
493 alg.outOfValidity = 2
494 alg.outOfValidityDeco =
'ph_isol_bad_eff' + postfix
495 alg.photons = config.readName (self.containerName)
496 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
497 if self.saveDetailedSF:
498 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
499 'isol_effSF' + postfix)
500 sfList += [alg.scaleFactorDecoration]
502 doCombEffSF =
not self.noEffSFForID
or not self.noEffSFForIso
503 if config.dataType()
is not DataType.Data
and doCombEffSF
and self.saveCombinedSF:
504 alg = config.createAlgorithm(
'CP::AsgObjectScaleFactorAlg',
505 'PhotonCombinedEfficiencyScaleFactorAlg' )
506 alg.particles = config.readName (self.containerName)
507 alg.inScaleFactors = sfList
508 alg.outScaleFactor =
'effSF' + postfix +
'_%SYS%'
509 config.addOutputVar (self.containerName, alg.outScaleFactor,
'effSF' + postfix)