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 (
'forceFullSimConfig',
False, type=bool,
56 info=
"whether to force the tool to use the configuration meant for "
57 "full simulation samples. Only for testing purposes. "
58 "The default is False.")
59 self.addOption (
'splitCalibrationAndSmearing',
False, type=bool,
60 info=
"EXPERIMENTAL: This splits the EgammaCalibrationAndSmearingTool "
61 " into two steps. The first step applies a baseline calibration that "
62 "is not affected by systematics. The second step then applies the "
63 "systematics dependent corrections. The net effect is that the "
64 "slower first step only has to be run once, while the second is run "
65 "once per systematic. ATLASG-2358")
69 """Create the calibration and smearing algorithm
71 Factoring this out into its own function, as we want to
72 instantiate it in multiple places"""
74 alg = config.createAlgorithm(
'CP::EgammaCalibrationAndSmearingAlg', name + self.postfix )
75 config.addPrivateTool(
'calibrationAndSmearingTool',
76 'CP::EgammaCalibrationAndSmearingTool' )
79 alg.calibrationAndSmearingTool.ESModel = self.ESModel
81 if config.geometry()
is LHCPeriod.Run2:
82 alg.calibrationAndSmearingTool.ESModel =
'es2023_R22_Run2_v0'
83 elif config.geometry()
is LHCPeriod.Run3:
84 alg.calibrationAndSmearingTool.ESModel =
'es2022_R22_PRE'
85 elif config.geometry()
is LHCPeriod.Run4:
86 logging.warning(
"No ESModel set for Run4, using Run3 model")
87 alg.calibrationAndSmearingTool.ESModel =
'es2022_R22_PRE'
89 raise ValueError (f
"Can't set up the ElectronCalibrationConfig with {config.geometry().value}, "
90 "there must be something wrong!")
92 alg.calibrationAndSmearingTool.decorrelationModel = self.decorrelationModel
93 alg.calibrationAndSmearingTool.useFastSim = (
94 0
if self.forceFullSimConfig
95 else int( config.dataType()
is DataType.FastSim ))
96 alg.egammas = config.readName (self.containerName)
97 alg.egammasOut = config.copyName (self.containerName)
98 alg.preselection = config.getPreselection (self.containerName,
'')
104 log = logging.getLogger(
'PhotonCalibrationConfig')
106 postfix = self.postfix
107 if postfix !=
'' and postfix[0] !=
'_' :
108 postfix =
'_' + postfix
110 if self.forceFullSimConfig:
111 log.warning(
"You are running PhotonCalibrationConfig forcing full sim config")
112 log.warning(
"This is only intended to be used for testing purposes")
114 if config.isPhyslite() :
115 config.setSourceName (self.containerName,
"AnalysisPhotons")
117 config.setSourceName (self.containerName,
"Photons")
119 cleaningWP =
'NoTime' if self.cleaningAllowLate
else ''
122 if config.wantCopy (self.containerName) :
123 alg = config.createAlgorithm(
'CP::AsgShallowCopyAlg',
'PhotonShallowCopyAlg' + postfix )
124 alg.input = config.readName (self.containerName)
125 alg.output = config.copyName (self.containerName)
128 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonEtaCutAlg' + postfix )
129 alg.selectionDecoration =
'selectEta' + postfix +
',as_bits'
130 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
131 alg.selectionTool.maxEta = 2.37
133 alg.selectionTool.etaGapLow = 1.37
134 alg.selectionTool.etaGapHigh = 1.52
135 alg.selectionTool.useClusterEta =
True
136 alg.particles = config.readName (self.containerName)
137 alg.preselection = config.getPreselection (self.containerName,
'')
138 config.addSelection (self.containerName,
'', alg.selectionDecoration)
141 if self.recomputeIsEM
and config.dataType()
is DataType.FullSim:
142 alg = config.createAlgorithm(
'CP::PhotonShowerShapeFudgeAlg',
143 'PhotonShowerShapeFudgeAlg' + postfix )
144 config.addPrivateTool(
'showerShapeFudgeTool',
145 'ElectronPhotonVariableCorrectionTool' )
146 if config.geometry
is LHCPeriod.Run2:
147 alg.showerShapeFudgeTool.ConfigFile = \
148 'EGammaVariableCorrection/TUNE25/ElPhVariableNominalCorrection.conf'
149 if config.geometry
is LHCPeriod.Run3:
150 alg.showerShapeFudgeTool.ConfigFile = \
151 'EGammaVariableCorrection/TUNE23/ElPhVariableNominalCorrection.conf'
152 alg.photons = config.readName (self.containerName)
153 alg.photonsOut = config.copyName (self.containerName)
154 alg.preselection = config.getPreselection (self.containerName,
'')
157 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonObjectQualityAlg' + postfix )
158 alg.selectionDecoration =
'goodOQ,as_bits'
159 config.addPrivateTool(
'selectionTool',
'CP::EgammaIsGoodOQSelectionTool' )
160 alg.selectionTool.Mask = xAOD.EgammaParameters.BADCLUSPHOTON
161 alg.particles = config.readName (self.containerName)
162 alg.preselection = config.getPreselection (self.containerName,
'')
163 config.addSelection (self.containerName,
'', alg.selectionDecoration)
166 if self.enableCleaning:
167 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonCleaningAlg' + postfix)
168 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
169 alg.selectionDecoration =
'isClean,as_bits'
170 alg.selectionTool.selectionFlags = [
'DFCommonPhotonsCleaning' + cleaningWP]
171 alg.particles = config.readName (self.containerName)
172 alg.preselection = config.getPreselection (self.containerName,
'')
173 config.addSelection (self.containerName,
'', alg.selectionDecoration)
178 alg = config.createAlgorithm(
'CP::PhotonOriginCorrectionAlg',
179 'PhotonOriginCorrectionAlg' + postfix )
180 alg.photons = config.readName (self.containerName)
181 alg.photonsOut = config.copyName (self.containerName)
182 alg.preselection = config.getPreselection (self.containerName,
'')
184 if not self.splitCalibrationAndSmearing :
187 if config.isPhyslite()
and not self.recalibratePhyslite :
188 alg.skipNominal =
True
209 alg.noToolSystematics =
True
211 alg.calibrationAndSmearingTool.doSmearing =
False
216 alg.calibrationAndSmearingTool.doScaleCorrection =
False
217 alg.calibrationAndSmearingTool.useMVACalibration =
False
221 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonPtCutAlg' + postfix )
222 alg.selectionDecoration =
'selectPt' + postfix +
',as_bits'
223 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
224 alg.selectionTool.minPt = self.minPt
225 alg.particles = config.readName (self.containerName)
226 alg.preselection = config.getPreselection (self.containerName,
'')
227 config.addSelection (self.containerName,
'', alg.selectionDecoration,
231 alg = config.createAlgorithm(
'CP::EgammaIsolationCorrectionAlg',
232 'PhotonIsolationCorrectionAlg' + postfix )
233 config.addPrivateTool(
'isolationCorrectionTool',
234 'CP::IsolationCorrectionTool' )
235 alg.isolationCorrectionTool.IsMC = config.dataType()
is not DataType.Data
236 alg.isolationCorrectionTool.AFII_corr = (
237 0
if self.forceFullSimConfig
238 else config.dataType()
is DataType.FastSim)
239 alg.egammas = config.readName (self.containerName)
240 alg.egammasOut = config.copyName (self.containerName)
241 alg.preselection = config.getPreselection (self.containerName,
'')
244 alg = config.createAlgorithm(
'CP::AsgEnergyDecoratorAlg',
'EnergyDecorator' + self.containerName + self.postfix )
245 alg.particles = config.readName (self.containerName)
247 config.addOutputVar (self.containerName,
'pt',
'pt')
248 config.addOutputVar (self.containerName,
'eta',
'eta', noSys=
True)
249 config.addOutputVar (self.containerName,
'phi',
'phi', noSys=
True)
250 config.addOutputVar (self.containerName,
'e_%SYS%',
'e')
255 """the ConfigBlock for the photon working point
257 This may at some point be split into multiple blocks (29 Aug 22)."""
259 def __init__ (self, containerName='', selectionName='') :
260 super (PhotonWorkingPointConfig, self).__init__ ()
261 self.addOption (
'containerName', containerName, type=str,
263 info=
"the name of the input container.")
264 self.addOption (
'selectionName', selectionName, type=str,
266 info=
"the name of the photon selection to define (e.g. tight or "
268 self.addOption (
'postfix', selectionName, type=str,
269 info=
"a postfix to apply to decorations and algorithm names. "
270 "Typically not needed here as selectionName is used internally.")
271 self.addOption (
'qualityWP',
None, type=str,
272 info=
"the ID WP (string) to use. Supported ID WPs: Tight, Medium and Loose.")
273 self.addOption (
'isolationWP',
None, type=str,
274 info=
"the ID WP (string) to use. Supported isolation WPs: "
275 "FixedCutLoose, FixedCutTight, TightCaloOnly, NonIso.")
276 self.addOption (
'closeByCorrection',
False, type=bool,
277 info=
"whether to use close-by-corrected isolation working points")
278 self.addOption (
'recomputeIsEM',
False, type=bool,
279 info=
"whether to rerun the cut-based selection. The default is "
280 "False, i.e. to use derivation flags.")
281 self.addOption (
'doFSRSelection',
False, type=bool,
282 info=
"whether to accept additional photons close to muons for the "
283 "purpose of FSR corrections to these muons. Expert feature "
284 "requested by the H4l analysis running on PHYSLITE. "
285 "The default is False.")
286 self.addOption (
'noEffSF',
False, type=bool,
287 info=
"disables the calculation of efficiencies and scale factors. "
288 "Experimental! only useful to test a new WP for which scale "
289 "factors are not available. The default is False.")
290 self.addOption (
'forceFullSimConfig',
False, type=bool,
291 info=
"whether to force the tool to use the configuration meant "
292 "for full simulation samples. Only for testing purposes. "
293 "The default is False.")
297 log = logging.getLogger(
'PhotonWorkingPointConfig')
300 if config.geometry()
is LHCPeriod.Run1:
301 raise ValueError (
"Can't set up the PhotonWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
303 if self.forceFullSimConfig:
304 log.warning(
"You are running PhotonWorkingPointConfig forcing full sim config")
305 log.warning(
"This is only intended to be used for testing purposes")
307 postfix = self.postfix
308 if postfix !=
'' and postfix[0] !=
'_' :
309 postfix =
'_' + postfix
312 quality = ROOT.egammaPID.PhotonTight
314 quality = ROOT.egammaPID.PhotonMedium
316 quality = ROOT.egammaPID.PhotonLoose
318 raise Exception (
'unknown photon quality working point "' + self.
qualityWP +
'" should be Tight, Medium or Loose')
321 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'PhotonIsEMSelectorAlg' + postfix )
322 alg.selectionDecoration =
'selectEM' + postfix +
',as_bits'
323 if self.recomputeIsEM:
325 config.addPrivateTool(
'selectionTool',
'AsgPhotonIsEMSelector' )
326 alg.selectionTool.isEMMask = quality
327 if config.geometry()
is LHCPeriod.Run2:
329 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc20_20240510/PhotonIsEMTightSelectorCutDefs_pTdep_mc20_smooth.conf'
331 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc15_20150712/PhotonIsEMLooseSelectorCutDefs.conf'
333 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc20_20240510/PhotonIsEMMediumSelectorCutDefs_pTdep_smooth.conf'
334 if config.geometry()
is LHCPeriod.Run3:
336 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/20180825/PhotonIsEMTightSelectorCutDefs.conf'
338 alg.selectionTool.ConfigFile =
'ElectronPhotonSelectorTools/offline/mc15_20150712/PhotonIsEMLooseSelectorCutDefs.conf'
340 raise ValueError(
'No Medium menu available for Run-3. Please get in contact with egamma')
343 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
344 dfFlag =
'DFCommonPhotonsIsEM' + self.
qualityWP
345 alg.selectionTool.selectionFlags = [ dfFlag ]
346 alg.particles = config.readName (self.containerName)
347 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
348 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration)
351 if self.doFSRSelection :
353 wpFlag = alg.selectionDecoration.split(
",")[0]
354 alg = config.createAlgorithm(
'CP::EgammaFSRForMuonsCollectorAlg',
'EgammaFSRForMuonsCollectorAlg' + postfix +
'_ph')
355 alg.selectionDecoration = wpFlag
356 alg.ElectronOrPhotonContKey = config.readName (self.containerName)
359 if self.isolationWP !=
'NonIso' :
360 alg = config.createAlgorithm(
'CP::EgammaIsolationSelectionAlg',
361 'PhotonIsolationSelectionAlg' + postfix )
362 alg.selectionDecoration =
'isolated' + postfix +
',as_bits'
363 config.addPrivateTool(
'selectionTool',
'CP::IsolationSelectionTool' )
364 alg.selectionTool.PhotonWP = self.isolationWP
365 if self.closeByCorrection:
366 alg.selectionTool.IsoDecSuffix =
"CloseByCorr"
368 alg.egammas = config.readName (self.containerName)
369 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
370 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration)
373 if config.dataType()
is not DataType.Data
and not self.noEffSF:
374 alg = config.createAlgorithm(
'CP::PhotonEfficiencyCorrectionAlg',
375 'PhotonEfficiencyCorrectionAlgID' + postfix )
376 config.addPrivateTool(
'efficiencyCorrectionTool',
377 'AsgPhotonEfficiencyCorrectionTool' )
378 alg.scaleFactorDecoration =
'ph_id_effSF' + postfix +
'_%SYS%'
379 if config.dataType()
is DataType.FastSim:
380 alg.efficiencyCorrectionTool.ForceDataType = (
381 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
else
382 PATCore.ParticleDataType.Fast)
383 elif config.dataType()
is DataType.FullSim:
384 alg.efficiencyCorrectionTool.ForceDataType = \
385 PATCore.ParticleDataType.Full
386 if config.geometry() >= LHCPeriod.Run2:
387 alg.efficiencyCorrectionTool.MapFilePath =
'PhotonEfficiencyCorrection/2015_2025/rel22.2/2022_Summer_Prerecom_v1/map0.txt'
388 alg.outOfValidity = 2
389 alg.outOfValidityDeco =
'ph_id_bad_eff' + postfix
390 alg.photons = config.readName (self.containerName)
391 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
392 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'id_effSF' + postfix)
395 if config.dataType()
is not DataType.Data
and self.isolationWP !=
'NonIso' and not self.noEffSF:
396 alg = config.createAlgorithm(
'CP::PhotonEfficiencyCorrectionAlg',
397 'PhotonEfficiencyCorrectionAlgIsol' + postfix )
398 config.addPrivateTool(
'efficiencyCorrectionTool',
399 'AsgPhotonEfficiencyCorrectionTool' )
400 alg.scaleFactorDecoration =
'ph_isol_effSF' + postfix +
'_%SYS%'
401 if config.dataType()
is DataType.FastSim:
402 alg.efficiencyCorrectionTool.ForceDataType = (
403 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
else
404 PATCore.ParticleDataType.Fast)
405 elif config.dataType()
is DataType.FullSim:
406 alg.efficiencyCorrectionTool.ForceDataType = \
407 PATCore.ParticleDataType.Full
408 alg.efficiencyCorrectionTool.IsoKey = self.isolationWP.
replace(
"FixedCut",
"")
409 if config.geometry() >= LHCPeriod.Run2:
410 alg.efficiencyCorrectionTool.MapFilePath =
'PhotonEfficiencyCorrection/2015_2025/rel22.2/2022_Summer_Prerecom_v1/map0.txt'
411 alg.outOfValidity = 2
412 alg.outOfValidityDeco =
'ph_isol_bad_eff' + postfix
413 alg.photons = config.readName (self.containerName)
414 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
415 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
'isol_effSF' + postfix)
424 enableCleaning = None,
425 cleaningAllowLate = None,
426 recomputeIsEM = None,
427 forceFullSimConfig = None):
428 """Create photon calibration analysis algorithms
430 This makes all the algorithms that need to be run first befor
431 all working point specific algorithms and that can be shared
432 between the working points.
435 postfix -- a postfix to apply to decorations and algorithm
436 names. this is mostly used/needed when using this
437 sequence with multiple working points to ensure all
439 crackVeto -- Whether or not to perform eta crack veto
440 enableCleaning -- Enable photon cleaning
441 cleaningAllowLate -- Whether to ignore timing information in cleaning.
442 recomputeIsEM -- Whether to rerun the cut-based selection. If not, use derivation flags
443 forceFullSimConfig -- imposes full-sim config for FastSim for testing
446 config = PhotonCalibrationConfig (containerName)
447 config.setOptionValue (
'postfix', postfix)
448 config.setOptionValue (
'crackVeto', crackVeto)
449 config.setOptionValue (
'enableCleaning', enableCleaning)
450 config.setOptionValue (
'cleaningAllowLate', cleaningAllowLate)
451 config.setOptionValue (
'recomputeIsEM', recomputeIsEM)
452 config.setOptionValue (
'forceFullSimConfig', forceFullSimConfig)
458 recomputeIsEM = None,
460 forceFullSimConfig = None):
461 """Create photon analysis algorithms for a single working point
464 workingPoint -- The working point to use
465 selectionName -- a postfix to apply to decorations and algorithm
466 names. this is mostly used/needed when using this
467 sequence with multiple working points to ensure all
469 recomputeIsEM -- Whether to rerun the cut-based selection. If not, use derivation flags
470 noEffSF -- Disables the calculation of efficiencies and scale factors
471 forceFullSimConfig -- imposes full-sim config for FastSim for testing
474 config = PhotonWorkingPointConfig (containerName, selectionName)
475 if workingPoint
is not None :
476 splitWP = workingPoint.split (
'.')
477 if len (splitWP) != 2 :
478 raise ValueError (
'working point should be of format "quality.isolation", not ' + workingPoint)
479 config.setOptionValue (
'qualityWP', splitWP[0])
480 config.setOptionValue (
'isolationWP', splitWP[1])
481 config.setOptionValue (
'recomputeIsEM', recomputeIsEM)
482 config.setOptionValue (
'noEffSF', noEffSF)
483 config.setOptionValue (
'forceFullSimConfig', forceFullSimConfig)