4 from AnalysisAlgorithmsConfig.ConfigBlock
import ConfigBlock
6 from AthenaConfiguration.Enums
import LHCPeriod
7 from AnalysisAlgorithmsConfig.ConfigAccumulator
import DataType
8 from TrigGlobalEfficiencyCorrection.TriggerLeg_DictHelpers
import TriggerDict, MapKeysDict
9 from Campaigns.Utils
import Campaign
10 from AthenaCommon.Logging
import logging
13 from xAODEgamma.xAODEgammaParameters
import xAOD
19 """the ConfigBlock for the electron four-momentum correction"""
22 super (ElectronCalibrationConfig, self).__init__ ()
23 self.setBlockName(
'Electrons')
24 self.addOption (
'inputContainer',
'', type=str,
25 info=
"select electron input container, by default set to Electrons")
26 self.addOption (
'containerName', containerName, type=str,
28 info=
"the name of the output container after calibration.")
29 self.addOption (
'ESModel',
'', type=str,
30 info=
"flag of egamma calibration recommendation.")
31 self.addOption (
'decorrelationModel',
'1NP_v1', type=str,
32 info=
"egamma energy scale decorrelationModel. The default is 1NP_v1. "
33 "Supported Model: 1NP_v1, FULL_v1.")
34 self.addOption (
'postfix',
'', type=str,
35 info=
"a postfix to apply to decorations and algorithm names. Typically "
36 "not needed here since the calibration is common to all electrons.")
37 self.addOption (
'crackVeto',
False, type=bool,
38 info=
"whether to perform LAr crack veto based on the cluster eta, "
39 "i.e. remove electrons within 1.37<|eta|<1.52. The default "
41 self.addOption (
'isolationCorrection',
False, type=bool,
42 info=
"whether or not to perform isolation corrections (leakage "
43 "corrections), i.e. set up an instance of "
44 "CP::EgammaIsolationCorrectionAlg.")
45 self.addOption (
'recalibratePhyslite',
True, type=bool,
46 info=
"whether to run the CP::EgammaCalibrationAndSmearingAlg on "
47 "PHYSLITE derivations. The default is True.")
48 self.addOption (
'minPt', 4.5*GeV, type=float,
49 info=
"the minimum pT cut to apply to calibrated electrons. "
50 "The default is 4.5 GeV.")
51 self.addOption (
'maxEta', 2.47, type=float,
52 info=
"maximum electron |eta| (float). The default is 2.47.")
53 self.addOption (
'forceFullSimConfig',
False, type=bool,
54 info=
"whether to force the tool to use the configuration meant for "
55 "full simulation samples. Only for testing purposes. The default "
58 self.addOption (
'splitCalibrationAndSmearing',
False, type=bool,
59 info=
"EXPERIMENTAL: This splits the EgammaCalibrationAndSmearingTool "
60 " into two steps. The first step applies a baseline calibration that "
61 "is not affected by systematics. The second step then applies the "
62 "systematics dependent corrections. The net effect is that the "
63 "slower first step only has to be run once, while the second is run "
64 "once per systematic. ATLASG-2358")
66 self.addOption (
'decorateTruth',
False, type=bool,
67 info=
"decorate truth particle information on the reconstructed one")
68 self.addOption (
'decorateCaloClusterEta',
False, type=bool,
69 info=
"decorate the calo cluster eta on the reconstructed one")
70 self.addOption (
'writeTrackD0Z0',
False, type = bool,
71 info=
"save the d0 significance and z0sinTheta variables so they can be written out")
72 self.addOption (
'decorateEmva',
False, type=bool,
73 info=
"decorate E_mva_only on the objects (needed for columnar tools/PHYSLITE)")
75 self.addOption (
'decorateSamplingPattern',
False, type=bool,
76 info=
"add samplingPattern decorations to clusters as part of PHYSLITE")
80 """Create the calibration and smearing algorithm
82 Factoring this out into its own function, as we want to
83 instantiate it in multiple places"""
84 log = logging.getLogger(
'ElectronCalibrationConfig')
87 alg = config.createAlgorithm(
'CP::EgammaCalibrationAndSmearingAlg', name + self.postfix )
88 config.addPrivateTool(
'calibrationAndSmearingTool',
89 'CP::EgammaCalibrationAndSmearingTool' )
92 alg.calibrationAndSmearingTool.ESModel = self.ESModel
94 if config.geometry()
is LHCPeriod.Run2:
95 alg.calibrationAndSmearingTool.ESModel =
'es2023_R22_Run2_v1'
96 elif config.geometry()
is LHCPeriod.Run3:
97 alg.calibrationAndSmearingTool.ESModel =
'es2022_R22_PRE'
98 elif config.geometry()
is LHCPeriod.Run4:
99 log.warning(
"No ESModel set for Run4, using Run 3 model instead")
100 alg.calibrationAndSmearingTool.ESModel =
'es2022_R22_PRE'
102 raise ValueError (f
"Can't set up the ElectronCalibrationConfig with {config.geometry().value}, "
103 "there must be something wrong!")
105 alg.calibrationAndSmearingTool.decorrelationModel = self.decorrelationModel
106 alg.calibrationAndSmearingTool.useFastSim = (
107 0
if self.forceFullSimConfig
108 else int( config.dataType()
is DataType.FastSim ))
109 alg.calibrationAndSmearingTool.decorateEmva = self.decorateEmva
110 alg.egammas = config.readName (self.containerName)
111 alg.egammasOut = config.copyName (self.containerName)
112 alg.preselection = config.getPreselection (self.containerName,
'')
118 log = logging.getLogger(
'ElectronCalibrationConfig')
120 if self.forceFullSimConfig:
121 log.warning(
"You are running ElectronCalibrationConfig forcing full sim config")
122 log.warning(
" This is only intended to be used for testing purposes")
124 inputContainer =
"AnalysisElectrons" if config.isPhyslite()
else "Electrons"
125 if self.inputContainer:
126 inputContainer = self.inputContainer
127 config.setSourceName (self.containerName, inputContainer)
130 if self.decorateCaloClusterEta:
131 alg = config.createAlgorithm(
'CP::EgammaCaloClusterEtaAlg',
132 'ElectronEgammaCaloClusterEtaAlg' + self.postfix,
134 alg.particles = config.readName(self.containerName)
135 config.addOutputVar (self.containerName,
'caloEta2',
'caloEta2', noSys=
True)
137 if self.decorateSamplingPattern:
138 config.createAlgorithm(
'CP::EgammaSamplingPatternDecoratorAlg',
'EgammaSamplingPatternDecoratorAlg' + self.postfix )
141 if config.wantCopy (self.containerName) :
142 alg = config.createAlgorithm(
'CP::AsgShallowCopyAlg',
'ElectronShallowCopyAlg' + self.postfix )
143 alg.input = config.readName (self.containerName)
144 alg.output = config.copyName (self.containerName)
147 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronEtaCutAlg' + self.postfix )
148 alg.selectionDecoration =
'selectEta' + self.postfix +
',as_bits'
149 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
150 alg.selectionTool.maxEta = self.maxEta
152 alg.selectionTool.etaGapLow = 1.37
153 alg.selectionTool.etaGapHigh = 1.52
154 alg.selectionTool.useClusterEta =
True
155 alg.particles = config.readName (self.containerName)
156 alg.preselection = config.getPreselection (self.containerName,
'')
157 config.addSelection (self.containerName,
'', alg.selectionDecoration)
160 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronObjectQualityAlg' + self.postfix )
161 alg.selectionDecoration =
'goodOQ' + self.postfix +
',as_bits'
162 config.addPrivateTool(
'selectionTool',
'CP::EgammaIsGoodOQSelectionTool' )
163 alg.selectionTool.Mask = xAOD.EgammaParameters.BADCLUSELECTRON
164 alg.particles = config.readName (self.containerName)
165 alg.preselection = config.getPreselection (self.containerName,
'')
166 config.addSelection (self.containerName,
'', alg.selectionDecoration)
168 if not self.splitCalibrationAndSmearing :
171 if config.isPhyslite()
and not self.recalibratePhyslite :
172 alg.skipNominal =
True
193 alg.noToolSystematics =
True
195 alg.calibrationAndSmearingTool.doSmearing =
False
200 alg.calibrationAndSmearingTool.doScaleCorrection =
False
201 alg.calibrationAndSmearingTool.useMVACalibration =
False
202 alg.calibrationAndSmearingTool.decorateEmva =
False
206 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronPtCutAlg' + self.postfix )
207 alg.selectionDecoration =
'selectPt' + self.postfix +
',as_bits'
208 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
209 alg.selectionTool.minPt = self.minPt
210 alg.particles = config.readName (self.containerName)
211 alg.preselection = config.getPreselection (self.containerName,
'')
212 config.addSelection (self.containerName,
'', alg.selectionDecoration,
216 if self.isolationCorrection:
217 alg = config.createAlgorithm(
'CP::EgammaIsolationCorrectionAlg',
218 'ElectronIsolationCorrectionAlg' + self.postfix )
219 config.addPrivateTool(
'isolationCorrectionTool',
220 'CP::IsolationCorrectionTool' )
221 alg.isolationCorrectionTool.IsMC = config.dataType()
is not DataType.Data
222 alg.isolationCorrectionTool.AFII_corr = (
223 0
if self.forceFullSimConfig
224 else config.dataType()
is DataType.FastSim)
225 alg.egammas = config.readName (self.containerName)
226 alg.egammasOut = config.copyName (self.containerName)
227 alg.preselection = config.getPreselection (self.containerName,
'')
230 if self.writeTrackD0Z0:
231 alg = config.createAlgorithm(
'CP::AsgLeptonTrackDecorationAlg',
232 'LeptonTrackDecorator' + self.containerName + self.postfix,
234 alg.particles = config.readName (self.containerName)
236 alg = config.createAlgorithm(
'CP::AsgEnergyDecoratorAlg',
'EnergyDecorator' + self.containerName + self.postfix )
237 alg.particles = config.readName(self.containerName)
239 config.addOutputVar (self.containerName,
'pt',
'pt')
240 config.addOutputVar (self.containerName,
'eta',
'eta', noSys=
True)
241 config.addOutputVar (self.containerName,
'phi',
'phi', noSys=
True)
242 config.addOutputVar (self.containerName,
'e_%SYS%',
'e')
243 config.addOutputVar (self.containerName,
'charge',
'charge', noSys=
True)
245 if self.writeTrackD0Z0:
246 config.addOutputVar (self.containerName,
'd0_%SYS%',
'd0', noSys=
True)
247 config.addOutputVar (self.containerName,
'd0sig_%SYS%',
'd0sig', noSys=
True)
248 config.addOutputVar (self.containerName,
'z0sintheta_%SYS%',
'z0sintheta', noSys=
True)
249 config.addOutputVar (self.containerName,
'z0sinthetasig_%SYS%',
'z0sinthetasig', noSys=
True)
252 if self.decorateTruth
and config.dataType()
is not DataType.Data:
253 config.addOutputVar (self.containerName,
"truthType",
"truth_type", noSys=
True)
254 config.addOutputVar (self.containerName,
"truthOrigin",
"truth_origin", noSys=
True)
256 config.addOutputVar (self.containerName,
"firstEgMotherPdgId",
"truth_firstEgMotherPdgId", noSys=
True)
257 config.addOutputVar (self.containerName,
"firstEgMotherTruthOrigin",
"truth_firstEgMotherTruthOrigin", noSys=
True)
258 config.addOutputVar (self.containerName,
"firstEgMotherTruthType",
"truth_firstEgMotherTruthType", noSys=
True)
262 """the ConfigBlock for the electron working point
264 This may at some point be split into multiple blocks (29 Aug 22)."""
266 def __init__ (self, containerName='', selectionName='') :
267 super (ElectronWorkingPointConfig, self).__init__ ()
268 self.addOption (
'containerName', containerName, type=str,
270 info=
"the name of the input container.")
271 self.addOption (
'selectionName', selectionName, type=str,
273 info=
"the name of the electron selection to define (e.g. tight or "
275 self.addOption (
'postfix',
None, type=str,
276 info=
"a postfix to apply to decorations and algorithm names. "
277 "Typically not needed here as selectionName is used internally.")
278 self.addOption (
'trackSelection',
True, type=bool,
279 info=
"whether or not to set up an instance of "
280 "CP::AsgLeptonTrackSelectionAlg, with the recommended d_0 and "
281 "z_0 sin(theta) cuts. The default is True.")
282 self.addOption (
'maxD0Significance', 5, type=float,
283 info=
"maximum d0 significance used for the trackSelection"
285 self.addOption (
'maxDeltaZ0SinTheta', 0.5, type=float,
286 info=
"maximum z0sinTheta in mm used for the trackSelection"
287 "The default is 0.5 mm")
288 self.addOption (
'identificationWP',
None, type=str,
289 info=
"the ID WP (string) to use. Supported ID WPs: TightLH, "
290 "MediumLH, LooseBLayerLH, TightDNN, MediumDNN, LooseDNN, "
291 "TightNoCFDNN, MediumNoCFDNN, VeryLooseNoCF97DNN.")
292 self.addOption (
'isolationWP',
None, type=str,
293 info=
"the isolation WP (string) to use. Supported isolation WPs: "
294 "HighPtCaloOnly, Loose_VarRad, Tight_VarRad, TightTrackOnly_"
295 "VarRad, TightTrackOnly_FixedRad, NonIso.")
296 self.addOption (
'addSelectionToPreselection',
True, type=bool,
297 info=
"whether to retain only electrons satisfying the working point "
298 "requirements. The default is True.")
299 self.addOption (
'closeByCorrection',
False, type=bool,
300 info=
"whether to use close-by-corrected isolation working points")
301 self.addOption (
'recomputeID',
False, type=bool,
302 info=
"whether to rerun the ID LH/DNN. The default is False, i.e. to use "
304 self.addOption (
'chargeIDSelectionRun2',
False, type=bool,
305 info=
"whether to run the ECIDS tool. Only available for run 2. "
306 "The default is False.")
307 self.addOption (
'recomputeChargeID',
False, type=bool,
308 info=
"whether to rerun the ECIDS. The default is False, i.e. to use "
310 self.addOption (
'doFSRSelection',
False, type=bool,
311 info=
"whether to accept additional electrons close to muons for "
312 "the purpose of FSR corrections to these muons. Expert feature "
313 "requested by the H4l analysis running on PHYSLITE. "
314 "The default is False.")
315 self.addOption (
'noEffSF',
False, type=bool,
316 info=
"disables the calculation of efficiencies and scale factors. "
317 "Experimental! only useful to test a new WP for which scale "
318 "factors are not available. The default is False.")
319 self.addOption (
'saveDetailedSF',
True, type=bool,
320 info=
"save all the independent detailed object scale factors. "
321 "The default is True.")
322 self.addOption (
'saveCombinedSF',
False, type=bool,
323 info=
"save the combined object scale factor. "
324 "The default is False.")
325 self.addOption (
'forceFullSimConfig',
False, type=bool,
326 info=
"whether to force the tool to use the configuration meant for "
327 "full simulation samples. Only for testing purposes. "
328 "The default is False.")
329 self.addOption (
'correlationModelId',
'SIMPLIFIED', type=str,
330 info=
"the correlation model (string) to use for ID scale factors "
331 "Supported models: SIMPLIFIED (default), FULL, TOTAL, TOYS")
332 self.addOption (
'correlationModelIso',
'SIMPLIFIED', type=str,
333 info=
"the correlation model (string) to use for isolation scale factors "
334 "Supported models: SIMPLIFIED (default), FULL, TOTAL, TOYS")
335 self.addOption (
'correlationModelReco',
'SIMPLIFIED', type=str,
336 info=
"the correlation model (string) to use for reconstruction scale factors "
337 "Supported models: SIMPLIFIED (default), FULL, TOTAL, TOYS")
342 log = logging.getLogger(
'ElectronWorkingPointConfig')
344 if self.forceFullSimConfig:
345 log.warning(
"You are running ElectronWorkingPointConfig forcing full sim config")
346 log.warning(
"This is only intended to be used for testing purposes")
348 selectionPostfix = self.selectionName
349 if selectionPostfix !=
'' and selectionPostfix[0] !=
'_' :
350 selectionPostfix =
'_' + selectionPostfix
353 if config.geometry()
is LHCPeriod.Run1:
354 raise ValueError (
"Can't set up the ElectronWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
356 postfix = self.postfix
358 postfix = self.selectionName
359 if postfix !=
'' and postfix[0] !=
'_' :
360 postfix =
'_' + postfix
363 if self.trackSelection :
364 alg = config.createAlgorithm(
'CP::AsgLeptonTrackSelectionAlg',
365 'ElectronTrackSelectionAlg' + postfix,
367 alg.selectionDecoration =
'trackSelection' + postfix +
',as_bits'
368 alg.maxD0Significance = self.maxD0Significance
369 alg.maxDeltaZ0SinTheta = self.maxDeltaZ0SinTheta
370 alg.particles = config.readName (self.containerName)
371 alg.preselection = config.getPreselection (self.containerName,
'')
372 if self.trackSelection :
373 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
374 preselection=self.addSelectionToPreselection)
376 if 'LH' in self.identificationWP:
379 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronLikelihoodAlg' + postfix )
380 alg.selectionDecoration =
'selectLikelihood' + selectionPostfix +
',as_char'
383 config.addPrivateTool(
'selectionTool',
'AsgElectronLikelihoodTool' )
384 alg.selectionTool.primaryVertexContainer =
'PrimaryVertices'
387 if config.geometry() >= LHCPeriod.Run3:
388 alg.selectionTool.WorkingPoint = self.identificationWP.
replace(
"BLayer",
"BL") +
'Electron'
389 elif config.geometry()
is LHCPeriod.Run2:
390 alg.selectionTool.WorkingPoint = self.identificationWP.
replace(
"BLayer",
"BL") +
'Electron_Run2'
393 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
394 dfFlag =
"DFCommonElectronsLH" + self.identificationWP.
split(
'LH')[0]
395 dfFlag = dfFlag.replace(
"BLayer",
"BL")
396 alg.selectionTool.selectionFlags = [dfFlag]
397 elif 'SiHit' in self.identificationWP:
399 algVeto = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronLikelihoodAlgVeto' + postfix +
'Veto')
400 algVeto.selectionDecoration =
'selectLikelihoodVeto' + postfix +
',as_char'
401 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
402 algVeto.selectionTool.selectionFlags = [
"DFCommonElectronsLHLoose"]
403 algVeto.selectionTool.invertFlags = [
True]
404 algVeto.particles = config.readName (self.containerName)
405 algVeto.preselection = config.getPreselection (self.containerName, self.selectionName)
407 config.addSelection (self.containerName, self.selectionName, algVeto.selectionDecoration,
408 preselection=self.addSelectionToPreselection)
411 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronLikelihoodAlg' + postfix )
412 alg.selectionDecoration =
'selectSiHit' + selectionPostfix +
',as_char'
414 config.addPrivateTool(
'selectionTool',
'CP::AsgMaskSelectionTool' )
415 dfVar =
"DFCommonElectronsLHLooseBLIsEMValue"
416 alg.selectionTool.selectionVars = [dfVar]
417 mask =
int( 0 | 0x1 << 1 | 0x1 << 2)
418 alg.selectionTool.selectionMasks = [mask]
419 elif 'DNN' in self.identificationWP:
420 if self.chargeIDSelectionRun2:
421 raise ValueError(
'DNN is not intended to be used with '
422 '`chargeIDSelectionRun2` option as there are '
423 'DNN WPs containing charge flip rejection.')
425 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronDNNAlg' + postfix )
426 alg.selectionDecoration =
'selectDNN' + selectionPostfix +
',as_char'
429 config.addPrivateTool(
'selectionTool',
'AsgElectronSelectorTool' )
431 if config.geometry()
is LHCPeriod.Run3:
432 raise ValueError (
"DNN working points are not available for Run 3 yet.")
434 alg.selectionTool.WorkingPoint = self.identificationWP +
'Electron'
437 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
438 dfFlag =
"DFCommonElectronsDNN" + self.identificationWP.
split(
'DNN')[0]
439 alg.selectionTool.selectionFlags = [dfFlag]
441 alg.particles = config.readName (self.containerName)
442 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
443 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
444 preselection=self.addSelectionToPreselection)
447 if 'SiHit' in self.identificationWP:
449 algDec = config.createAlgorithm(
'CP::ElectronSiHitDecAlg',
'ElectronSiHitDecAlg' + postfix )
450 selDec =
'siHitEvtHasLeptonPair' + selectionPostfix +
',as_char'
451 algDec.selectionName = selDec.split(
",")[0]
452 algDec.ElectronContainer = config.readName (self.containerName)
454 algDec.RequireTwoLeptons =
True
455 config.addSelection (self.containerName, self.selectionName, selDec,
456 preselection=self.addSelectionToPreselection)
459 if self.doFSRSelection :
461 wpFlag = alg.selectionDecoration.split(
",")[0]
462 alg = config.createAlgorithm(
'CP::EgammaFSRForMuonsCollectorAlg',
'EgammaFSRForMuonsCollectorAlg' + postfix )
463 alg.selectionDecoration = wpFlag
464 alg.ElectronOrPhotonContKey = config.readName (self.containerName)
468 if 'SiHit' in self.identificationWP:
472 if self.isolationWP !=
'NonIso' :
473 alg = config.createAlgorithm(
'CP::EgammaIsolationSelectionAlg',
474 'ElectronIsolationSelectionAlg' + postfix )
475 alg.selectionDecoration =
'isolated' + selectionPostfix +
',as_char'
476 config.addPrivateTool(
'selectionTool',
'CP::IsolationSelectionTool' )
477 alg.selectionTool.ElectronWP = self.isolationWP
478 if self.closeByCorrection:
479 alg.selectionTool.IsoDecSuffix =
"CloseByCorr"
480 alg.egammas = config.readName (self.containerName)
481 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
482 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
483 preselection=self.addSelectionToPreselection)
485 if self.chargeIDSelectionRun2
and config.geometry() >= LHCPeriod.Run3:
486 log.warning(
"ECIDS is only available for Run 2 and will not have effect in run 3.")
489 if self.chargeIDSelectionRun2
and config.geometry() < LHCPeriod.Run3:
490 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
491 'ElectronChargeIDSelectionAlg' + postfix )
492 alg.selectionDecoration =
'chargeID' + selectionPostfix +
',as_char'
493 if self.recomputeChargeID:
495 config.addPrivateTool(
'selectionTool',
496 'AsgElectronChargeIDSelectorTool' )
497 alg.selectionTool.TrainingFile = \
498 'ElectronPhotonSelectorTools/ChargeID/ECIDS_20180731rel21Summer2018.root'
499 alg.selectionTool.WorkingPoint =
'Loose'
500 alg.selectionTool.CutOnBDT = -0.337671
503 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
504 alg.selectionTool.selectionFlags = [
"DFCommonElectronsECIDS"]
506 alg.particles = config.readName (self.containerName)
507 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
508 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
509 preselection=self.addSelectionToPreselection)
511 correlationModels = [
"SIMPLIFIED",
"FULL",
"TOTAL",
"TOYS"]
515 if config.dataType()
is not DataType.Data
and not self.noEffSF:
517 if config.geometry()
is LHCPeriod.Run2:
518 raise ValueError(
'Run 2 does not yet have efficiency correction, '
519 'please disable it by setting `noEffSF` to True.')
520 if 'DNN' in self.identificationWP:
521 raise ValueError(
'DNN does not yet have efficiency correction, '
522 'please disable it by setting `noEffSF` to True.')
524 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
525 'ElectronEfficiencyCorrectionAlgReco' + postfix )
526 config.addPrivateTool(
'efficiencyCorrectionTool',
527 'AsgElectronEfficiencyCorrectionTool' )
528 alg.scaleFactorDecoration =
'el_reco_effSF' + selectionPostfix +
'_%SYS%'
529 alg.efficiencyCorrectionTool.RecoKey =
"Reconstruction"
530 if self.correlationModelReco
not in correlationModels:
531 raise ValueError(
'Invalid correlation model for reconstruction efficiency, '
532 f
'has to be one of: {", ".join(correlationModels)}')
533 if config.geometry() >= LHCPeriod.Run3
and self.correlationModelReco !=
"TOTAL":
534 log.warning(
"Only TOTAL correlation model is currently supported "
535 "for reconstruction efficiency correction in Run 3.")
536 alg.efficiencyCorrectionTool.CorrelationModel =
"TOTAL"
538 alg.efficiencyCorrectionTool.CorrelationModel = self.correlationModelReco
539 if config.dataType()
is DataType.FastSim:
540 alg.efficiencyCorrectionTool.ForceDataType = (
541 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
542 else PATCore.ParticleDataType.Fast)
543 elif config.dataType()
is DataType.FullSim:
544 alg.efficiencyCorrectionTool.ForceDataType = \
545 PATCore.ParticleDataType.Full
546 alg.outOfValidity = 2
547 alg.outOfValidityDeco =
'el_reco_bad_eff' + selectionPostfix
548 alg.electrons = config.readName (self.containerName)
549 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
550 if self.saveDetailedSF:
551 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
552 'reco_effSF' + postfix)
553 sfList += [alg.scaleFactorDecoration]
556 if config.dataType()
is not DataType.Data
and not self.noEffSF:
557 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
558 'ElectronEfficiencyCorrectionAlgID' + postfix )
559 config.addPrivateTool(
'efficiencyCorrectionTool',
560 'AsgElectronEfficiencyCorrectionTool' )
561 alg.scaleFactorDecoration =
'el_id_effSF' + selectionPostfix +
'_%SYS%'
562 alg.efficiencyCorrectionTool.IdKey = self.identificationWP.
replace(
"LH",
"")
563 if self.correlationModelId
not in correlationModels:
564 raise ValueError(
'Invalid correlation model for identification efficiency, '
565 f
'has to be one of: {", ".join(correlationModels)}')
566 alg.efficiencyCorrectionTool.CorrelationModel = self.correlationModelId
567 if config.dataType()
is DataType.FastSim:
568 alg.efficiencyCorrectionTool.ForceDataType = (
569 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
570 else PATCore.ParticleDataType.Fast)
571 elif config.dataType()
is DataType.FullSim:
572 alg.efficiencyCorrectionTool.ForceDataType = \
573 PATCore.ParticleDataType.Full
574 alg.outOfValidity = 2
575 alg.outOfValidityDeco =
'el_id_bad_eff' + selectionPostfix
576 alg.electrons = config.readName (self.containerName)
577 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
578 if self.saveDetailedSF:
579 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
580 'id_effSF' + postfix)
581 sfList += [alg.scaleFactorDecoration]
584 if config.dataType()
is not DataType.Data
and self.isolationWP !=
'NonIso' and not self.noEffSF:
585 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
586 'ElectronEfficiencyCorrectionAlgIsol' + postfix )
587 config.addPrivateTool(
'efficiencyCorrectionTool',
588 'AsgElectronEfficiencyCorrectionTool' )
589 alg.scaleFactorDecoration =
'el_isol_effSF' + selectionPostfix +
'_%SYS%'
590 alg.efficiencyCorrectionTool.IdKey = self.identificationWP.
replace(
"LH",
"")
591 alg.efficiencyCorrectionTool.IsoKey = self.isolationWP
592 if self.correlationModelIso
not in correlationModels:
593 raise ValueError(
'Invalid correlation model for isolation efficiency, '
594 f
'has to be one of: {", ".join(correlationModels)}')
595 if config.geometry() >= LHCPeriod.Run3:
596 log.warning(
"Only TOTAL correlation model is currently supported "
597 "for isolation efficiency correction in Run 3.")
598 alg.efficiencyCorrectionTool.CorrelationModel =
"TOTAL"
600 alg.efficiencyCorrectionTool.CorrelationModel = self.correlationModelIso
601 if config.dataType()
is DataType.FastSim:
602 alg.efficiencyCorrectionTool.ForceDataType = (
603 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
604 else PATCore.ParticleDataType.Fast)
605 elif config.dataType()
is DataType.FullSim:
606 alg.efficiencyCorrectionTool.ForceDataType = \
607 PATCore.ParticleDataType.Full
608 alg.outOfValidity = 2
609 alg.outOfValidityDeco =
'el_isol_bad_eff' + selectionPostfix
610 alg.electrons = 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)
615 sfList += [alg.scaleFactorDecoration]
619 if self.chargeIDSelectionRun2:
624 if config.dataType()
is not DataType.Data
and not self.noEffSF
and self.saveCombinedSF:
625 alg = config.createAlgorithm(
'CP::AsgObjectScaleFactorAlg',
626 'ElectronCombinedEfficiencyScaleFactorAlg' + postfix )
627 alg.particles = config.readName (self.containerName)
628 alg.inScaleFactors = sfList
629 alg.outScaleFactor =
'effSF' + postfix +
'_%SYS%'
630 config.addOutputVar (self.containerName, alg.outScaleFactor,
'effSF' + postfix)
636 super (ElectronTriggerAnalysisSFBlock, self).__init__ ()
638 self.addOption (
'triggerChainsPerYear', {}, type=
None,
639 info=
"a dictionary with key (string) the year and value (list of "
640 "strings) the trigger chains. The default is {} (empty dictionary).")
641 self.addOption (
'electronID',
'', type=str,
642 info=
"the electron ID WP (string) to use.")
643 self.addOption (
'electronIsol',
'', type=str,
644 info=
"the electron isolation WP (string) to use.")
645 self.addOption (
'saveEff',
False, type=bool,
646 info=
"define whether we decorate also the trigger scale efficiency "
647 "The default is false.")
648 self.addOption (
'prefixSF',
'trigEffSF', type=str,
649 info=
"the decoration prefix for trigger scale factors, "
650 "the default is 'trigEffSF'")
651 self.addOption (
'prefixEff',
'trigEff', type=str,
652 info=
"the decoration prefix for MC trigger efficiencies, "
653 "the default is 'trigEff'")
654 self.addOption (
'includeAllYears',
False, type=bool,
655 info=
"if True, all configured years will be included in all jobs. "
656 "The default is False.")
657 self.addOption (
'removeHLTPrefix',
True, type=bool,
658 info=
"remove the HLT prefix from trigger chain names, "
659 "The default is True.")
660 self.addOption (
'useToolKeyAsOutput',
False, type=bool,
661 info=
"use tool trigger key as output, "
662 "The default is False.")
663 self.addOption (
'containerName',
'', type=str,
664 info=
"the input electron container, with a possible selection, in "
665 "the format container or container.selection.")
669 if config.dataType()
is not DataType.Data:
670 log = logging.getLogger(
'ElectronTriggerSFConfig')
672 if self.includeAllYears
and not self.useToolKeyAsOutput:
673 log.warning(
'`includeAllYears` is set to True, but `useToolKeyAsOutput` is set to False. '
674 'This will cause multiple branches to be written out with the same content.')
682 version_Run2 =
"2015_2018/rel21.2/Precision_Summer2020_v1"
683 version_Run3 =
"2015_2025/rel22.2/2022_Summer_Prerecom_v1"
685 version = version_Run2
if config.geometry()
is LHCPeriod.Run2
else version_Run3
692 def filterConfFromMap(conf, electronMapKeys):
694 raise ValueError(
"No configuration found for trigger chain.")
699 if c
in electronMapKeys:
704 if self.includeAllYears:
705 years = [
int(year)
for year
in self.triggerChainsPerYear.
keys()]
706 if any(year
in years
for year
in [2015, 2016, 2017, 2018]) \
707 and any(year
in years
for year
in [2022, 2023, 2024, 2025]):
708 raise ValueError(
"Mixing years from Run 2 and Run 3 in the same job is currently not supported.")
709 elif config.campaign()
is Campaign.MC20a:
711 elif config.campaign()
is Campaign.MC20d:
713 elif config.campaign()
is Campaign.MC20e:
715 elif config.campaign()
in [Campaign.MC21a, Campaign.MC23a]:
717 elif config.campaign()
in [Campaign.MC23c, Campaign.MC23d]:
722 triggerChainsPerYear_Run2 = {}
723 triggerChainsPerYear_Run3 = {}
724 for year, chains
in self.triggerChainsPerYear.
items():
726 log.warning(
"No trigger chains configured for year %s. "
727 "Assuming this is intended, no Electron trigger SF will be computed.", year)
730 chains_split = [chain.replace(
"HLT_",
"").
replace(
" || ",
"_OR_")
for chain
in chains]
731 if int(year) >= 2022:
732 triggerChainsPerYear_Run3[
str(year)] =
' || '.
join(chains_split)
734 triggerChainsPerYear_Run2[
str(year)] =
' || '.
join(chains_split)
735 electronMapKeys_Run2 = ROOT.std.map(
"string",
"string")()
736 electronMapKeys_Run3 = ROOT.std.map(
"string",
"string")()
738 sc_Run2 = ROOT.TrigGlobalEfficiencyCorrectionTool.suggestElectronMapKeys(triggerChainsPerYear_Run2, version_Run2, electronMapKeys_Run2)
739 sc_Run3 = ROOT.TrigGlobalEfficiencyCorrectionTool.suggestElectronMapKeys(triggerChainsPerYear_Run3, version_Run3, electronMapKeys_Run3)
740 if sc_Run2.code() != 2
or sc_Run3.code() != 2:
741 raise RuntimeError(
"Failed to suggest electron map keys")
742 electronMapKeys = dict(electronMapKeys_Run2) | dict(electronMapKeys_Run3)
747 triggerChains = self.triggerChainsPerYear.
get(
int(year), self.triggerChainsPerYear.
get(
str(year), []))
748 for chain
in triggerChains:
749 chain = chain.replace(
" || ",
"_OR_")
750 chain_noHLT = chain.replace(
"HLT_",
"")
751 chain_out = chain_noHLT
if self.removeHLTPrefix
else chain
752 legs = triggerDict[chain_noHLT]
754 if chain_noHLT[0] ==
'e' and chain_noHLT[1].isdigit:
755 chain_key = f
"{year}_{chain_noHLT}"
756 chain_conf = mapKeysDict[chain_key][0]
757 triggerConfigs[chain_conf
if self.useToolKeyAsOutput
else chain_out] = chain_conf
760 if leg[0] ==
'e' and leg[1].isdigit:
761 leg_out = leg
if self.removeHLTPrefix
else f
"HLT_{leg}"
762 leg_key = f
"{year}_{leg}"
763 leg_conf = filterConfFromMap(mapKeysDict[leg_key], electronMapKeys)
764 triggerConfigs[leg_conf
if self.useToolKeyAsOutput
else leg_out] = leg_conf
766 decorations = [self.prefixSF]
768 decorations += [self.prefixEff]
770 for label, conf
in triggerConfigs.items():
771 for deco
in decorations:
772 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
773 'EleTrigEfficiencyCorrectionsAlg' + deco +
775 config.addPrivateTool(
'efficiencyCorrectionTool',
776 'AsgElectronEfficiencyCorrectionTool' )
779 alg.efficiencyCorrectionTool.MapFilePath =
"ElectronEfficiencyCorrection/" + version +
"/map4.txt"
780 alg.efficiencyCorrectionTool.IdKey = self.electronID.
replace(
"LH",
"")
781 alg.efficiencyCorrectionTool.IsoKey = self.electronIsol
782 alg.efficiencyCorrectionTool.TriggerKey = (
783 (
"Eff_" if deco == self.prefixEff
else "") + conf)
784 alg.efficiencyCorrectionTool.CorrelationModel =
"TOTAL"
785 alg.efficiencyCorrectionTool.ForceDataType = \
786 PATCore.ParticleDataType.Full
788 alg.scaleFactorDecoration = f
"el_{deco}_{label}_%SYS%"
790 alg.outOfValidity = 2
791 alg.outOfValidityDeco = f
"bad_eff_ele{deco}_{label}"
792 alg.electrons = config.readName (self.containerName)
793 alg.preselection = config.getPreselection (self.containerName,
"")
794 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, f
"{deco}_{label}")
799 super (ElectronLRTMergedConfig, self).__init__ ()
801 'inputElectrons',
'Electrons', type=str,
803 info=
"the name of the input electron container."
806 'inputLRTElectrons',
'LRTElectrons', type=str,
808 info=
"the name of the input LRT electron container."
811 'containerName',
'Electrons_LRTMerged', type=str,
813 info=
"the name of the output container after LRT merging."
819 if config.isPhyslite() :
820 raise(RuntimeError(
"Electron LRT merging is not available in Physlite mode"))
822 alg = config.createAlgorithm(
"CP::ElectronLRTMergingAlg",
"ElectronLRTMergingAlg" + self.containerName )
823 alg.PromptElectronLocation = self.inputElectrons
824 alg.LRTElectronLocation = self.inputLRTElectrons
825 alg.OutputCollectionName = self.containerName
826 alg.CreateViewCollection =
False