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 AthenaCommon.Logging
import logging
12 from xAODEgamma.xAODEgammaParameters
import xAOD
18 """the ConfigBlock for the electron four-momentum correction"""
21 super (ElectronCalibrationConfig, self).__init__ ()
22 self.setBlockName(
'Electrons')
23 self.addOption (
'inputContainer',
'', type=str,
24 info=
"select electron input container, by default set to Electrons")
25 self.addOption (
'containerName',
'', type=str,
27 info=
"the name of the output container after calibration.")
28 self.addOption (
'ESModel',
'', type=str,
29 info=
"flag of egamma calibration recommendation.")
30 self.addOption (
'decorrelationModel',
'1NP_v1', type=str,
31 info=
"egamma energy scale decorrelationModel. The default is 1NP_v1. "
32 "Supported Model: 1NP_v1, FULL_v1.")
33 self.addOption (
'postfix',
'', type=str,
34 info=
"a postfix to apply to decorations and algorithm names. Typically "
35 "not needed here since the calibration is common to all electrons.")
36 self.addOption (
'crackVeto',
False, type=bool,
37 info=
"whether to perform LAr crack veto based on the cluster eta, "
38 "i.e. remove electrons within 1.37<|eta|<1.52. The default "
40 self.addOption (
'isolationCorrection',
True, type=bool,
41 info=
"whether or not to perform isolation corrections (leakage "
42 "corrections), i.e. set up an instance of "
43 "CP::EgammaIsolationCorrectionAlg.")
44 self.addOption (
'recalibratePhyslite',
True, type=bool,
45 info=
"whether to run the CP::EgammaCalibrationAndSmearingAlg on "
46 "PHYSLITE derivations. The default is True.")
47 self.addOption (
'minPt', 4.5*GeV, type=float,
48 info=
"the minimum pT cut to apply to calibrated electrons. "
49 "The default is 4.5 GeV.")
50 self.addOption (
'maxEta', 2.47, type=float,
51 info=
"maximum electron |eta| (float). The default is 2.47.")
52 self.addOption (
'forceFullSimConfigForP4',
False, type=bool,
53 info=
"whether to force the tool to use the configuration meant for "
54 "full simulation samples for P4 corrections. Only for testing purposes. "
55 "The default is False.")
56 self.addOption (
'forceFullSimConfigForIso',
False, type=bool,
57 info=
"whether to force the tool to use the configuration meant for "
58 "full simulation samples for isolation corrections. Only for testing purposes. "
59 "The default is False.")
60 self.addOption (
'splitCalibrationAndSmearing',
False, type=bool,
61 info=
"EXPERIMENTAL: This splits the EgammaCalibrationAndSmearingTool "
62 " into two steps. The first step applies a baseline calibration that "
63 "is not affected by systematics. The second step then applies the "
64 "systematics dependent corrections. The net effect is that the "
65 "slower first step only has to be run once, while the second is run "
66 "once per systematic. ATLASG-2358",
69 self.addOption (
'decorateTruth',
False, type=bool,
70 info=
"decorate truth particle information on the reconstructed one")
71 self.addOption (
'decorateCaloClusterEta',
False, type=bool,
72 info=
"decorate the calo cluster eta on the reconstructed one")
73 self.addOption (
'writeTrackD0Z0',
False, type = bool,
74 info=
"save the d0 significance and z0sinTheta variables so they can be written out")
75 self.addOption (
'decorateEmva',
False, type=bool,
76 info=
"decorate E_mva_only on the objects (needed for columnar tools/PHYSLITE)")
78 self.addOption (
'decorateSamplingPattern',
False, type=bool,
79 info=
"add samplingPattern decorations to clusters as part of PHYSLITE")
82 """Return the instance name for this block"""
83 return self.containerName + self.postfix
86 """Create the calibration and smearing algorithm
88 Factoring this out into its own function, as we want to
89 instantiate it in multiple places"""
90 log = logging.getLogger(
'ElectronCalibrationConfig')
93 alg = config.createAlgorithm(
'CP::EgammaCalibrationAndSmearingAlg', name )
94 config.addPrivateTool(
'calibrationAndSmearingTool',
95 'CP::EgammaCalibrationAndSmearingTool' )
98 alg.calibrationAndSmearingTool.ESModel = self.ESModel
100 if config.geometry()
is LHCPeriod.Run2:
101 alg.calibrationAndSmearingTool.ESModel =
'es2023_R22_Run2_v1'
102 elif config.geometry()
is LHCPeriod.Run3:
103 alg.calibrationAndSmearingTool.ESModel =
'es2024_Run3_v0'
104 elif config.geometry()
is LHCPeriod.Run4:
105 log.warning(
"No ESModel set for Run4, using Run 3 model instead")
106 alg.calibrationAndSmearingTool.ESModel =
'es2024_Run3_v0'
108 raise ValueError (f
"Can't set up the ElectronCalibrationConfig with {config.geometry().value}, "
109 "there must be something wrong!")
111 alg.calibrationAndSmearingTool.decorrelationModel = self.decorrelationModel
112 alg.calibrationAndSmearingTool.useFastSim = (
113 0
if self.forceFullSimConfigForP4
114 else int( config.dataType()
is DataType.FastSim ))
115 alg.calibrationAndSmearingTool.decorateEmva = self.decorateEmva
116 alg.egammas = config.readName (self.containerName)
117 alg.egammasOut = config.copyName (self.containerName)
118 alg.preselection = config.getPreselection (self.containerName,
'')
124 log = logging.getLogger(
'ElectronCalibrationConfig')
126 if self.forceFullSimConfigForP4:
127 log.warning(
"You are running ElectronCalibrationConfig forcing full sim config")
128 log.warning(
" This is only intended to be used for testing purposes")
130 inputContainer =
"AnalysisElectrons" if config.isPhyslite()
else "Electrons"
131 if self.inputContainer:
132 inputContainer = self.inputContainer
133 config.setSourceName (self.containerName, inputContainer)
136 if self.decorateCaloClusterEta:
137 alg = config.createAlgorithm(
'CP::EgammaCaloClusterEtaAlg',
138 'ElectronEgammaCaloClusterEtaAlg',
140 alg.particles = config.readName(self.containerName)
141 config.addOutputVar (self.containerName,
'caloEta2',
'caloEta2', noSys=
True)
143 if self.decorateSamplingPattern:
144 config.createAlgorithm(
'CP::EgammaSamplingPatternDecoratorAlg',
'EgammaSamplingPatternDecoratorAlg' )
147 if config.wantCopy (self.containerName) :
148 alg = config.createAlgorithm(
'CP::AsgShallowCopyAlg',
'ElectronShallowCopyAlg' )
149 alg.input = config.readName (self.containerName)
150 alg.output = config.copyName (self.containerName)
153 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronEtaCutAlg' )
154 alg.selectionDecoration =
'selectEta' + self.postfix +
',as_bits'
155 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
156 alg.selectionTool.maxEta = self.maxEta
158 alg.selectionTool.etaGapLow = 1.37
159 alg.selectionTool.etaGapHigh = 1.52
160 alg.selectionTool.useClusterEta =
True
161 alg.particles = config.readName (self.containerName)
162 alg.preselection = config.getPreselection (self.containerName,
'')
163 config.addSelection (self.containerName,
'', alg.selectionDecoration)
166 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronObjectQualityAlg' )
167 alg.selectionDecoration =
'goodOQ' + self.postfix +
',as_bits'
168 config.addPrivateTool(
'selectionTool',
'CP::EgammaIsGoodOQSelectionTool' )
169 alg.selectionTool.Mask = xAOD.EgammaParameters.BADCLUSELECTRON
170 alg.particles = config.readName (self.containerName)
171 alg.preselection = config.getPreselection (self.containerName,
'')
172 config.addSelection (self.containerName,
'', alg.selectionDecoration)
174 if not self.splitCalibrationAndSmearing :
177 if config.isPhyslite()
and not self.recalibratePhyslite :
178 alg.skipNominal =
True
199 alg.noToolSystematics =
True
201 alg.calibrationAndSmearingTool.doSmearing =
False
206 alg.calibrationAndSmearingTool.doScaleCorrection =
False
207 alg.calibrationAndSmearingTool.useMVACalibration =
False
208 alg.calibrationAndSmearingTool.decorateEmva =
False
212 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronPtCutAlg' )
213 alg.selectionDecoration =
'selectPt' + self.postfix +
',as_bits'
214 config.addPrivateTool(
'selectionTool',
'CP::AsgPtEtaSelectionTool' )
215 alg.selectionTool.minPt = self.minPt
216 alg.particles = config.readName (self.containerName)
217 alg.preselection = config.getPreselection (self.containerName,
'')
218 config.addSelection (self.containerName,
'', alg.selectionDecoration,
222 if self.isolationCorrection:
223 alg = config.createAlgorithm(
'CP::EgammaIsolationCorrectionAlg',
224 'ElectronIsolationCorrectionAlg' )
225 config.addPrivateTool(
'isolationCorrectionTool',
226 'CP::IsolationCorrectionTool' )
227 alg.isolationCorrectionTool.IsMC = config.dataType()
is not DataType.Data
228 alg.isolationCorrectionTool.AFII_corr = (
229 0
if self.forceFullSimConfigForIso
230 else config.dataType()
is DataType.FastSim)
231 alg.isolationCorrectionTool.FixTimingIssueInCore =
True
232 alg.isolationCorrectionTool.ToolVer =
"REL22"
233 alg.isolationCorrectionTool.CorrFile =
"IsolationCorrections/v6/isolation_ptcorrections_rel22_mc20.root"
234 alg.egammas = config.readName (self.containerName)
235 alg.egammasOut = config.copyName (self.containerName)
236 alg.preselection = config.getPreselection (self.containerName,
'')
238 log.warning(
"You are not applying the isolation corrections")
239 log.warning(
"This is only intended to be used for testing purposes")
242 if self.writeTrackD0Z0:
243 alg = config.createAlgorithm(
'CP::AsgLeptonTrackDecorationAlg',
244 'LeptonTrackDecorator',
246 alg.particles = config.readName (self.containerName)
248 alg = config.createAlgorithm(
'CP::AsgEnergyDecoratorAlg',
'EnergyDecorator' )
249 alg.particles = config.readName(self.containerName)
251 config.addOutputVar (self.containerName,
'pt',
'pt')
252 config.addOutputVar (self.containerName,
'eta',
'eta', noSys=
True)
253 config.addOutputVar (self.containerName,
'phi',
'phi', noSys=
True)
254 config.addOutputVar (self.containerName,
'e_%SYS%',
'e')
255 config.addOutputVar (self.containerName,
'charge',
'charge', noSys=
True)
257 if self.writeTrackD0Z0:
258 config.addOutputVar (self.containerName,
'd0_%SYS%',
'd0', noSys=
True)
259 config.addOutputVar (self.containerName,
'd0sig_%SYS%',
'd0sig', noSys=
True)
260 config.addOutputVar (self.containerName,
'z0sintheta_%SYS%',
'z0sintheta', noSys=
True)
261 config.addOutputVar (self.containerName,
'z0sinthetasig_%SYS%',
'z0sinthetasig', noSys=
True)
264 if self.decorateTruth
and config.dataType()
is not DataType.Data:
265 config.addOutputVar (self.containerName,
"truthType",
"truth_type", noSys=
True)
266 config.addOutputVar (self.containerName,
"truthOrigin",
"truth_origin", noSys=
True)
268 config.addOutputVar (self.containerName,
"firstEgMotherPdgId",
"truth_firstEgMotherPdgId", noSys=
True)
269 config.addOutputVar (self.containerName,
"firstEgMotherTruthOrigin",
"truth_firstEgMotherTruthOrigin", noSys=
True)
270 config.addOutputVar (self.containerName,
"firstEgMotherTruthType",
"truth_firstEgMotherTruthType", noSys=
True)
274 """the ConfigBlock for the electron working point
276 This may at some point be split into multiple blocks (29 Aug 22)."""
279 super (ElectronWorkingPointConfig, self).__init__ ()
280 self.addOption (
'containerName',
'', type=str,
282 info=
"the name of the input container.")
283 self.addOption (
'selectionName',
'', type=str,
285 info=
"the name of the electron selection to define (e.g. tight or "
287 self.addOption (
'postfix',
None, type=str,
288 info=
"a postfix to apply to decorations and algorithm names. "
289 "Typically not needed here as selectionName is used internally.")
290 self.addOption (
'trackSelection',
True, type=bool,
291 info=
"whether or not to set up an instance of "
292 "CP::AsgLeptonTrackSelectionAlg, with the recommended d_0 and "
293 "z_0 sin(theta) cuts. The default is True.")
294 self.addOption (
'maxD0Significance', 5, type=float,
295 info=
"maximum d0 significance used for the trackSelection"
297 self.addOption (
'maxDeltaZ0SinTheta', 0.5, type=float,
298 info=
"maximum z0sinTheta in mm used for the trackSelection"
299 "The default is 0.5 mm")
300 self.addOption (
'identificationWP',
None, type=str,
301 info=
"the ID WP (string) to use. Supported ID WPs: TightLH, "
302 "MediumLH, LooseBLayerLH, TightDNN, MediumDNN, LooseDNN, "
303 "TightNoCFDNN, MediumNoCFDNN, VeryLooseNoCF97DNN, NoID.",
305 self.addOption (
'isolationWP',
None, type=str,
306 info=
"the isolation WP (string) to use. Supported isolation WPs: "
307 "HighPtCaloOnly, Loose_VarRad, Tight_VarRad, TightTrackOnly_"
308 "VarRad, TightTrackOnly_FixedRad, NonIso.")
309 self.addOption (
'convSelection',
None, type=str,
310 info=
"enter additional selection (string) to use. To be used with "
311 "TightLH or will crash. Supported keywords:"
312 "Veto, MatConv, GammaStar.")
313 self.addOption (
'addSelectionToPreselection',
True, type=bool,
314 info=
"whether to retain only electrons satisfying the working point "
315 "requirements. The default is True.")
316 self.addOption (
'closeByCorrection',
False, type=bool,
317 info=
"whether to use close-by-corrected isolation working points")
318 self.addOption (
'recomputeID',
False, type=bool,
319 info=
"whether to rerun the ID LH/DNN. The default is False, i.e. to use "
321 self.addOption (
'chargeIDSelectionRun2',
False, type=bool,
322 info=
"whether to run the ECIDS tool. Only available for run 2. "
323 "The default is False.")
324 self.addOption (
'recomputeChargeID',
False, type=bool,
325 info=
"whether to rerun the ECIDS. The default is False, i.e. to use "
327 self.addOption (
'doFSRSelection',
False, type=bool,
328 info=
"whether to accept additional electrons close to muons for "
329 "the purpose of FSR corrections to these muons. Expert feature "
330 "requested by the H4l analysis running on PHYSLITE. "
331 "The default is False.",
333 self.addOption (
'noEffSF',
False, type=bool,
334 info=
"disables the calculation of efficiencies and scale factors. "
335 "Experimental! only useful to test a new WP for which scale "
336 "factors are not available. The default is False.",
338 self.addOption (
'saveDetailedSF',
True, type=bool,
339 info=
"save all the independent detailed object scale factors. "
340 "The default is True.")
341 self.addOption (
'saveCombinedSF',
False, type=bool,
342 info=
"save the combined object scale factor. "
343 "The default is False.")
344 self.addOption (
'forceFullSimConfig',
False, type=bool,
345 info=
"whether to force the tool to use the configuration meant for "
346 "full simulation samples. Only for testing purposes. "
347 "The default is False.")
348 self.addOption (
'correlationModelId',
'SIMPLIFIED', type=str,
349 info=
"the correlation model (string) to use for ID scale factors "
350 "Supported models: SIMPLIFIED (default), FULL, TOTAL, TOYS")
351 self.addOption (
'correlationModelIso',
'SIMPLIFIED', type=str,
352 info=
"the correlation model (string) to use for isolation scale factors "
353 "Supported models: SIMPLIFIED (default), FULL, TOTAL, TOYS")
354 self.addOption (
'correlationModelReco',
'SIMPLIFIED', type=str,
355 info=
"the correlation model (string) to use for reconstruction scale factors "
356 "Supported models: SIMPLIFIED (default), FULL, TOTAL, TOYS")
357 self.addOption(
'addChargeMisIDSF',
False, type=bool,
358 info=
"Adds scale factors for charge-misID.")
361 """Return the instance name for this block"""
362 if self.postfix
is not None :
363 return self.containerName +
'_' + self.selectionName + self.postfix
364 return self.containerName +
'_' + self.selectionName
368 log = logging.getLogger(
'ElectronWorkingPointConfig')
370 if self.forceFullSimConfig:
371 log.warning(
"You are running ElectronWorkingPointConfig forcing full sim config")
372 log.warning(
"This is only intended to be used for testing purposes")
374 selectionPostfix = self.selectionName
375 if selectionPostfix !=
'' and selectionPostfix[0] !=
'_' :
376 selectionPostfix =
'_' + selectionPostfix
379 if config.geometry()
is LHCPeriod.Run1:
380 raise ValueError (
"Can't set up the ElectronWorkingPointConfig with %s, there must be something wrong!" % config.geometry().value)
382 postfix = self.postfix
384 postfix = self.selectionName
385 if postfix !=
'' and postfix[0] !=
'_' :
386 postfix =
'_' + postfix
389 if self.trackSelection :
390 alg = config.createAlgorithm(
'CP::AsgLeptonTrackSelectionAlg',
391 'ElectronTrackSelectionAlg',
393 alg.selectionDecoration =
'trackSelection' + postfix +
',as_bits'
394 alg.maxD0Significance = self.maxD0Significance
395 alg.maxDeltaZ0SinTheta = self.maxDeltaZ0SinTheta
396 alg.particles = config.readName (self.containerName)
397 alg.preselection = config.getPreselection (self.containerName,
'')
398 if self.trackSelection :
399 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
400 preselection=self.addSelectionToPreselection)
405 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronLikelihoodAlg' )
406 alg.selectionDecoration =
'selectLikelihood' + selectionPostfix +
',as_char'
409 config.addPrivateTool(
'selectionTool',
'AsgElectronLikelihoodTool' )
410 alg.selectionTool.primaryVertexContainer =
'PrimaryVertices'
413 if config.geometry() >= LHCPeriod.Run3:
418 elif config.geometry()
is LHCPeriod.Run2:
422 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
424 dfFlag = dfFlag.replace(
"BLayer",
"BL")
425 alg.selectionTool.selectionFlags = [dfFlag]
428 algVeto = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronLikelihoodAlgVeto')
429 algVeto.selectionDecoration =
'selectLikelihoodVeto' + postfix +
',as_char'
430 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
431 algVeto.selectionTool.selectionFlags = [
"DFCommonElectronsLHLoose"]
432 algVeto.selectionTool.invertFlags = [
True]
433 algVeto.particles = config.readName (self.containerName)
434 algVeto.preselection = config.getPreselection (self.containerName, self.selectionName)
436 config.addSelection (self.containerName, self.selectionName, algVeto.selectionDecoration,
437 preselection=self.addSelectionToPreselection)
440 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronLikelihoodAlg' )
441 alg.selectionDecoration =
'selectSiHit' + selectionPostfix +
',as_char'
443 config.addPrivateTool(
'selectionTool',
'CP::AsgMaskSelectionTool' )
444 dfVar =
"DFCommonElectronsLHLooseBLIsEMValue"
445 alg.selectionTool.selectionVars = [dfVar]
446 mask =
int( 0 | 0x1 << 1 | 0x1 << 2)
447 alg.selectionTool.selectionMasks = [mask]
449 if self.chargeIDSelectionRun2:
450 raise ValueError(
'DNN is not intended to be used with '
451 '`chargeIDSelectionRun2` option as there are '
452 'DNN WPs containing charge flip rejection.')
454 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronDNNAlg' )
455 alg.selectionDecoration =
'selectDNN' + selectionPostfix +
',as_char'
458 config.addPrivateTool(
'selectionTool',
'AsgElectronSelectorTool' )
460 if config.geometry()
is LHCPeriod.Run3:
461 raise ValueError (
"DNN working points are not available for Run 3 yet.")
466 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
468 alg.selectionTool.selectionFlags = [dfFlag]
472 raise ValueError (f
"Electron ID working point '{self.identificationWP}' is not recognised!")
475 alg.particles = config.readName (self.containerName)
476 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
477 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
478 preselection=self.addSelectionToPreselection)
483 algDec = config.createAlgorithm(
'CP::ElectronSiHitDecAlg',
'ElectronSiHitDecAlg' )
484 selDec =
'siHitEvtHasLeptonPair' + selectionPostfix +
',as_char'
485 algDec.selectionName = selDec.split(
",")[0]
486 algDec.ElectronContainer = config.readName (self.containerName)
488 algDec.RequireTwoLeptons =
True
489 config.addSelection (self.containerName, self.selectionName, selDec,
490 preselection=self.addSelectionToPreselection)
496 raise ValueError(f
"convSelection can only be used with TightLH ID, "
497 f
"whereas {self.identificationWP} has been selected. convSelection option will be ignored.")
499 allowedValues = [
"Veto",
"GammaStar",
"MatConv"]
501 raise ValueError(f
"convSelection has been set to {self.convSelection}, which is not a valid option. "
502 f
"convSelection option must be one of {allowedValues}.")
505 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronAmbiguityTypeAlg' )
506 alg.selectionDecoration =
'selectAmbiguityType' + selectionPostfix +
',as_char'
507 config.addPrivateTool(
'selectionTool',
'CP::AsgNumDecorationSelectionToolUInt8' )
508 alg.selectionTool.decorationName =
"ambiguityType"
509 alg.selectionTool.doEqual =
True
510 alg.selectionTool.equal = 0
511 alg.particles = config.readName (self.containerName)
512 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
513 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
514 preselection=self.addSelectionToPreselection)
517 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
'ElectronDFCommonAddAmbiguityAlg' )
518 alg.selectionDecoration =
'selectDFCommonAddAmbiguity' + selectionPostfix +
',as_char'
519 config.addPrivateTool(
'selectionTool',
'CP::AsgNumDecorationSelectionToolInt' )
520 alg.selectionTool.decorationName =
"DFCommonAddAmbiguity"
522 alg.selectionTool.doMax =
True
523 alg.selectionTool.max = 1
525 alg.selectionTool.doEqual =
True
526 alg.selectionTool.equal = 1
528 alg.selectionTool.doEqual =
True
529 alg.selectionTool.equal = 2
530 alg.particles = config.readName (self.containerName)
531 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
532 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
533 preselection=self.addSelectionToPreselection)
536 if self.doFSRSelection :
538 wpFlag = alg.selectionDecoration.split(
",")[0]
539 alg = config.createAlgorithm(
'CP::EgammaFSRForMuonsCollectorAlg',
'EgammaFSRForMuonsCollectorAlg' )
540 alg.selectionDecoration = wpFlag
541 alg.ElectronOrPhotonContKey = config.readName (self.containerName)
549 if self.isolationWP !=
'NonIso' :
550 alg = config.createAlgorithm(
'CP::EgammaIsolationSelectionAlg',
551 'ElectronIsolationSelectionAlg' )
552 alg.selectionDecoration =
'isolated' + selectionPostfix +
',as_char'
553 config.addPrivateTool(
'selectionTool',
'CP::IsolationSelectionTool' )
554 alg.selectionTool.ElectronWP = self.isolationWP
555 if self.closeByCorrection:
556 alg.selectionTool.IsoDecSuffix =
"CloseByCorr"
557 alg.egammas = config.readName (self.containerName)
558 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
559 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
560 preselection=self.addSelectionToPreselection)
562 if self.chargeIDSelectionRun2
and config.geometry() >= LHCPeriod.Run3:
563 log.warning(
"ECIDS is only available for Run 2 and will not have any effect in Run 3.")
566 if self.chargeIDSelectionRun2
and config.geometry() < LHCPeriod.Run3:
567 alg = config.createAlgorithm(
'CP::AsgSelectionAlg',
568 'ElectronChargeIDSelectionAlg' )
569 alg.selectionDecoration =
'chargeID' + selectionPostfix +
',as_char'
570 if self.recomputeChargeID:
572 config.addPrivateTool(
'selectionTool',
573 'AsgElectronChargeIDSelectorTool' )
574 alg.selectionTool.TrainingFile = \
575 'ElectronPhotonSelectorTools/ChargeID/ECIDS_20180731rel21Summer2018.root'
576 alg.selectionTool.WorkingPoint =
'Loose'
577 alg.selectionTool.CutOnBDT = -0.337671
580 config.addPrivateTool(
'selectionTool',
'CP::AsgFlagSelectionTool' )
581 alg.selectionTool.selectionFlags = [
"DFCommonElectronsECIDS"]
583 alg.particles = config.readName (self.containerName)
584 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
585 config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration,
586 preselection=self.addSelectionToPreselection)
588 correlationModels = [
"SIMPLIFIED",
"FULL",
"TOTAL",
"TOYS"]
589 map_file =
'ElectronEfficiencyCorrection/2015_2025/rel22.2/2025_Run2Rel22_Recommendation_v3/map0.txt' \
590 if config.geometry()
is LHCPeriod.Run2
else \
591 'ElectronEfficiencyCorrection/2015_2025/rel22.2/2025_Run3_Consolidated_Prerecom_v3/map1.txt'
594 if config.dataType()
is not DataType.Data
and not self.noEffSF:
596 raise ValueError(
'DNN does not yet have efficiency correction, '
597 'please disable it by setting `noEffSF` to True.')
599 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
600 'ElectronEfficiencyCorrectionAlgReco' )
601 config.addPrivateTool(
'efficiencyCorrectionTool',
602 'AsgElectronEfficiencyCorrectionTool' )
603 alg.scaleFactorDecoration =
'el_reco_effSF' + selectionPostfix +
'_%SYS%'
604 alg.efficiencyCorrectionTool.MapFilePath = map_file
605 alg.efficiencyCorrectionTool.RecoKey =
"Reconstruction"
606 if self.correlationModelReco
not in correlationModels:
607 raise ValueError(
'Invalid correlation model for reconstruction efficiency, '
608 f
'has to be one of: {", ".join(correlationModels)}')
609 if config.geometry() >= LHCPeriod.Run3
and self.correlationModelReco !=
"TOTAL":
610 log.warning(
"Only TOTAL correlation model is currently supported "
611 "for reconstruction efficiency correction in Run 3.")
612 alg.efficiencyCorrectionTool.CorrelationModel =
"TOTAL"
614 alg.efficiencyCorrectionTool.CorrelationModel = self.correlationModelReco
615 if config.dataType()
is DataType.FastSim:
616 alg.efficiencyCorrectionTool.ForceDataType = (
617 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
618 else PATCore.ParticleDataType.Fast)
619 elif config.dataType()
is DataType.FullSim:
620 alg.efficiencyCorrectionTool.ForceDataType = \
621 PATCore.ParticleDataType.Full
622 alg.outOfValidity = 2
623 alg.outOfValidityDeco =
'el_reco_bad_eff' + selectionPostfix
624 alg.electrons = config.readName (self.containerName)
625 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
626 if self.saveDetailedSF:
627 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
628 'reco_effSF' + postfix)
629 sfList += [alg.scaleFactorDecoration]
632 if config.dataType()
is not DataType.Data
and not self.noEffSF
and self.
identificationWP !=
'NoID':
634 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
635 'ElectronEfficiencyCorrectionAlgID' )
636 config.addPrivateTool(
'efficiencyCorrectionTool',
637 'AsgElectronEfficiencyCorrectionTool' )
638 alg.scaleFactorDecoration =
'el_id_effSF' + selectionPostfix +
'_%SYS%'
639 alg.efficiencyCorrectionTool.MapFilePath = map_file
641 if self.correlationModelId
not in correlationModels:
642 raise ValueError(
'Invalid correlation model for identification efficiency, '
643 f
'has to be one of: {", ".join(correlationModels)}')
644 alg.efficiencyCorrectionTool.CorrelationModel = self.correlationModelId
645 if config.dataType()
is DataType.FastSim:
646 alg.efficiencyCorrectionTool.ForceDataType = (
647 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
648 else PATCore.ParticleDataType.Fast)
649 elif config.dataType()
is DataType.FullSim:
650 alg.efficiencyCorrectionTool.ForceDataType = \
651 PATCore.ParticleDataType.Full
652 alg.outOfValidity = 2
653 alg.outOfValidityDeco =
'el_id_bad_eff' + selectionPostfix
654 alg.electrons = config.readName (self.containerName)
655 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
656 if self.saveDetailedSF:
657 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
658 'id_effSF' + postfix)
659 sfList += [alg.scaleFactorDecoration]
662 if config.dataType()
is not DataType.Data
and self.isolationWP !=
'NonIso' and not self.noEffSF:
663 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
664 'ElectronEfficiencyCorrectionAlgIsol' )
665 config.addPrivateTool(
'efficiencyCorrectionTool',
666 'AsgElectronEfficiencyCorrectionTool' )
667 alg.scaleFactorDecoration =
'el_isol_effSF' + selectionPostfix +
'_%SYS%'
668 alg.efficiencyCorrectionTool.MapFilePath = map_file
670 alg.efficiencyCorrectionTool.IsoKey = self.isolationWP
671 if self.correlationModelIso
not in correlationModels:
672 raise ValueError(
'Invalid correlation model for isolation efficiency, '
673 f
'has to be one of: {", ".join(correlationModels)}')
674 if self.correlationModelIso !=
'TOTAL':
675 log.warning(
"Only TOTAL correlation model is currently supported "
676 "for isolation efficiency correction in Run 3.")
677 alg.efficiencyCorrectionTool.CorrelationModel =
"TOTAL"
678 if config.dataType()
is DataType.FastSim:
679 alg.efficiencyCorrectionTool.ForceDataType = (
680 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
681 else PATCore.ParticleDataType.Fast)
682 elif config.dataType()
is DataType.FullSim:
683 alg.efficiencyCorrectionTool.ForceDataType = \
684 PATCore.ParticleDataType.Full
685 alg.outOfValidity = 2
686 alg.outOfValidityDeco =
'el_isol_bad_eff' + selectionPostfix
687 alg.electrons = config.readName (self.containerName)
688 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
689 if self.saveDetailedSF:
690 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
691 'isol_effSF' + postfix)
692 sfList += [alg.scaleFactorDecoration]
694 if (self.chargeIDSelectionRun2
and config.geometry() < LHCPeriod.Run3
and
695 config.dataType()
is not DataType.Data
and not self.noEffSF):
696 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
697 'ElectronEfficiencyCorrectionAlgEcids' )
698 config.addPrivateTool(
'efficiencyCorrectionTool',
699 'AsgElectronEfficiencyCorrectionTool' )
700 alg.scaleFactorDecoration =
'el_ecids_effSF' + selectionPostfix +
'_%SYS%'
701 if self.isolationWP !=
'Tight_VarRad':
702 raise ValueError(
'ECIDS SFs are supported only for Tight_VarRad isolation.')
710 raise ValueError(
'ECIDS SFs are supported only for ID LooseBLayerLH, MediumLH, or TightLH')
712 alg.efficiencyCorrectionTool.CorrelationModel =
"TOTAL"
713 alg.efficiencyCorrectionTool.CorrectionFileNameList = \
714 [f
'ElectronEfficiencyCorrection/2015_2025/rel22.2/2025_Run2Rel22_Recommendation_v2/ecids/efficiencySF.ChargeID.{ecids_lh}_ECIDS_Tight_VarRad.root']
715 if config.dataType()
is DataType.FastSim:
716 alg.efficiencyCorrectionTool.ForceDataType = (
717 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
718 else PATCore.ParticleDataType.Fast)
719 elif config.dataType()
is DataType.FullSim:
720 alg.efficiencyCorrectionTool.ForceDataType = \
721 PATCore.ParticleDataType.Full
722 alg.outOfValidity = 2
723 alg.outOfValidityDeco =
'el_ecids_bad_eff' + selectionPostfix
724 alg.electrons = config.readName (self.containerName)
725 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
726 if self.saveDetailedSF:
727 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
728 'ecids_effSF' + postfix)
729 sfList += [alg.scaleFactorDecoration]
731 if self.addChargeMisIDSF
and config.dataType()
is not DataType.Data
and not self.noEffSF:
732 if config.geometry() >= LHCPeriod.Run3:
733 raise ValueError(
'Run 3 does not yet have charge mis-ID correction, '
734 'please disable it by setting `noEffSF` to False.')
736 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
737 'ElectronEfficiencyCorrectionAlgMisid' )
738 config.addPrivateTool(
'efficiencyCorrectionTool',
739 'CP::ElectronChargeEfficiencyCorrectionTool' )
740 alg.scaleFactorDecoration =
'el_charge_misid_effSF' + selectionPostfix +
'_%SYS%'
741 if self.isolationWP !=
'Tight_VarRad':
742 raise ValueError(
'Charge mis-ID SFs are supported only for Tight_VarRad isolation.')
744 misid_lh =
'LooseAndBLayerLLH'
746 misid_lh =
'MediumLLH'
748 misid_lh =
'TightLLH'
750 raise ValueError(
'Charge mis-ID SFs are supported only for ID LooseBLayerLH, MediumLH, or TightLH')
751 misid_suffix =
'_ECIDSloose' if self.chargeIDSelectionRun2
else ''
753 alg.efficiencyCorrectionTool.CorrectionFileName = \
754 f
'ElectronEfficiencyCorrection/2015_2025/rel22.2/2025_Run2Rel22_Recommendation_v2/charge_misID/chargeEfficiencySF.{misid_lh}_d0z0_TightVarRad{misid_suffix}.root'
755 if config.dataType()
is DataType.FastSim:
756 alg.efficiencyCorrectionTool.ForceDataType = (
757 PATCore.ParticleDataType.Full
if self.forceFullSimConfig
758 else PATCore.ParticleDataType.Fast)
759 elif config.dataType()
is DataType.FullSim:
760 alg.efficiencyCorrectionTool.ForceDataType = \
761 PATCore.ParticleDataType.Full
762 alg.outOfValidity = 2
763 alg.outOfValidityDeco =
'el_misid_bad_eff' + selectionPostfix
764 alg.electrons = config.readName (self.containerName)
765 alg.preselection = config.getPreselection (self.containerName, self.selectionName)
766 if self.saveDetailedSF:
767 config.addOutputVar (self.containerName, alg.scaleFactorDecoration,
768 'charge_misid_effSF' + postfix)
769 sfList += [alg.scaleFactorDecoration]
771 if config.dataType()
is not DataType.Data
and not self.noEffSF
and self.saveCombinedSF:
772 alg = config.createAlgorithm(
'CP::AsgObjectScaleFactorAlg',
773 'ElectronCombinedEfficiencyScaleFactorAlg' )
774 alg.particles = config.readName (self.containerName)
775 alg.inScaleFactors = sfList
776 alg.outScaleFactor =
'effSF' + postfix +
'_%SYS%'
777 config.addOutputVar (self.containerName, alg.outScaleFactor,
'effSF' + postfix)
784 super (ElectronTriggerAnalysisSFBlock, self).__init__ ()
786 self.addOption (
'triggerChainsPerYear', {}, type=
None,
787 info=
"a dictionary with key (string) the year and value (list of "
788 "strings) the trigger chains. The default is {} (empty dictionary).")
789 self.addOption (
'electronID',
'', type=str,
790 info=
"the electron ID WP (string) to use.")
791 self.addOption (
'electronIsol',
'', type=str,
792 info=
"the electron isolation WP (string) to use.")
793 self.addOption (
'saveEff',
False, type=bool,
794 info=
"define whether we decorate also the trigger scale efficiency "
795 "The default is false.")
796 self.addOption (
'prefixSF',
'trigEffSF', type=str,
797 info=
"the decoration prefix for trigger scale factors, "
798 "the default is 'trigEffSF'")
799 self.addOption (
'prefixEff',
'trigEff', type=str,
800 info=
"the decoration prefix for MC trigger efficiencies, "
801 "the default is 'trigEff'")
802 self.addOption (
'includeAllYearsPerRun',
False, type=bool,
803 info=
"if True, all configured years in the LHC run will "
804 "be included in all jobs. The default is False.")
805 self.addOption (
'removeHLTPrefix',
True, type=bool,
806 info=
"remove the HLT prefix from trigger chain names, "
807 "The default is True.")
808 self.addOption (
'useToolKeyAsOutput',
False, type=bool,
809 info=
"use tool trigger key as output, "
810 "The default is False.")
811 self.addOption (
'containerName',
'', type=str,
812 info=
"the input electron container, with a possible selection, in "
813 "the format container or container.selection.")
816 """Return the instance name for this block"""
817 return self.containerName
821 if config.dataType()
is not DataType.Data:
822 log = logging.getLogger(
'ElectronTriggerSFConfig')
824 if self.includeAllYearsPerRun
and not self.useToolKeyAsOutput:
825 log.warning(
'`includeAllYearsPerRun` is set to True, but `useToolKeyAsOutput` is set to False. '
826 'This will cause multiple branches to be written out with the same content.')
834 version_Run2 =
"2015_2018/rel21.2/Precision_Summer2020_v1"
835 map_Run2 = f
"{version_Run2}/map4.txt"
836 version_Run3 =
"2015_2025/rel22.2/2025_Run3_Consolidated_Recommendation_v4"
837 map_Run3 =
"2015_2025/rel22.2/2025_Run3_Consolidated_Recommendation_v4/map2.txt"
839 version = version_Run2
if config.geometry()
is LHCPeriod.Run2
else version_Run3
846 def filterConfFromMap(conf, electronMapKeys):
848 raise ValueError(
"No configuration found for trigger chain.")
853 if c
in electronMapKeys:
858 if self.includeAllYearsPerRun:
859 years = [
int(year)
for year
in self.triggerChainsPerYear.
keys()]
861 from TriggerAnalysisAlgorithms.TriggerAnalysisSFConfig
import (
867 triggerChainsPerYear_Run2 = {}
868 triggerChainsPerYear_Run3 = {}
869 for year, chains
in self.triggerChainsPerYear.
items():
871 log.warning(
"No trigger chains configured for year %s. "
872 "Assuming this is intended, no Electron trigger SF will be computed.", year)
875 chains_split = [chain.replace(
"HLT_",
"").
replace(
" || ",
"_OR_")
for chain
in chains]
876 if int(year) >= 2022:
877 triggerChainsPerYear_Run3[
str(year)] =
' || '.
join(chains_split)
879 triggerChainsPerYear_Run2[
str(year)] =
' || '.
join(chains_split)
880 electronMapKeys_Run2 = ROOT.std.map(
"string",
"string")()
881 electronMapKeys_Run3 = ROOT.std.map(
"string",
"string")()
883 sc_Run2 = ROOT.TrigGlobalEfficiencyCorrectionTool.suggestElectronMapKeys(triggerChainsPerYear_Run2, version_Run2, electronMapKeys_Run2)
884 sc_Run3 = ROOT.TrigGlobalEfficiencyCorrectionTool.suggestElectronMapKeys(triggerChainsPerYear_Run3, version_Run3, electronMapKeys_Run3)
885 if sc_Run2.code() != 2
or sc_Run3.code() != 2:
886 raise RuntimeError(
"Failed to suggest electron map keys")
887 electronMapKeys = dict(electronMapKeys_Run2) | dict(electronMapKeys_Run3)
890 from TriggerAnalysisAlgorithms.TriggerAnalysisConfig
import is_year_in_current_period
896 triggerChains = self.triggerChainsPerYear.
get(
int(year), self.triggerChainsPerYear.
get(
str(year), []))
897 for chain
in triggerChains:
898 chain = chain.replace(
" || ",
"_OR_")
899 chain_noHLT = chain.replace(
"HLT_",
"")
900 chain_out = chain_noHLT
if self.removeHLTPrefix
else chain
901 legs = triggerDict[chain_noHLT]
903 if chain_noHLT[0] ==
'e' and chain_noHLT[1].isdigit:
904 chain_key = f
"{year}_{chain_noHLT}"
905 chain_conf = mapKeysDict[chain_key][0]
906 triggerConfigs[chain_conf
if self.useToolKeyAsOutput
else chain_out] = chain_conf
909 if leg[0] ==
'e' and leg[1].isdigit:
910 leg_out = leg
if self.removeHLTPrefix
else f
"HLT_{leg}"
911 leg_key = f
"{year}_{leg}"
912 leg_conf = filterConfFromMap(mapKeysDict[leg_key], electronMapKeys)
913 triggerConfigs[leg_conf
if self.useToolKeyAsOutput
else leg_out] = leg_conf
915 decorations = [self.prefixSF]
917 decorations += [self.prefixEff]
919 for label, conf
in triggerConfigs.items():
920 for deco
in decorations:
921 alg = config.createAlgorithm(
'CP::ElectronEfficiencyCorrectionAlg',
922 'EleTrigEfficiencyCorrectionsAlg' + deco +
924 config.addPrivateTool(
'efficiencyCorrectionTool',
925 'AsgElectronEfficiencyCorrectionTool' )
928 alg.efficiencyCorrectionTool.MapFilePath =
"ElectronEfficiencyCorrection/" + (map_Run3
if config.geometry()
is LHCPeriod.Run3
else map_Run2)
929 alg.efficiencyCorrectionTool.IdKey = self.electronID.
replace(
"LH",
"")
930 alg.efficiencyCorrectionTool.IsoKey = self.electronIsol
931 alg.efficiencyCorrectionTool.TriggerKey = (
932 (
"Eff_" if deco == self.prefixEff
else "") + conf)
933 alg.efficiencyCorrectionTool.CorrelationModel =
"TOTAL"
934 alg.efficiencyCorrectionTool.ForceDataType = \
935 PATCore.ParticleDataType.Full
937 alg.scaleFactorDecoration = f
"el_{deco}_{label}_%SYS%"
939 alg.outOfValidity = 2
940 alg.outOfValidityDeco = f
"bad_eff_ele{deco}_{label}"
941 alg.electrons = config.readName (self.containerName)
942 alg.preselection = config.getPreselection (self.containerName,
"")
943 config.addOutputVar (self.containerName, alg.scaleFactorDecoration, f
"{deco}_{label}")
948 super (ElectronLRTMergedConfig, self).__init__ ()
950 'inputElectrons',
'Electrons', type=str,
952 info=
"the name of the input electron container."
955 'inputLRTElectrons',
'LRTElectrons', type=str,
957 info=
"the name of the input LRT electron container."
960 'containerName',
'Electrons_LRTMerged', type=str,
962 info=
"the name of the output container after LRT merging."
966 """Return the instance name for this block"""
967 return self.containerName
971 if config.isPhyslite() :
972 raise(RuntimeError(
"Electron LRT merging is not available in Physlite mode"))
974 alg = config.createAlgorithm(
"CP::ElectronLRTMergingAlg",
"ElectronLRTMergingAlg" )
975 alg.PromptElectronLocation = self.inputElectrons
976 alg.LRTElectronLocation = self.inputLRTElectrons
977 alg.OutputCollectionName = self.containerName
978 alg.CreateViewCollection =
False