ATLAS Offline Software
ElectronAnalysisSequence.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2024 CERN for the benefit of the ATLAS collaboration
2 
3 # AnaAlgorithm import(s):
4 from AsgAnalysisAlgorithms.AnalysisObjectSharedSequence import makeSharedObjectSequence
5 from AnaAlgorithm.AnaAlgSequence import AnaAlgSequence
6 from AnaAlgorithm.DualUseConfig import createAlgorithm, addPrivateTool
7 
8 # E/gamma import(s).
9 from xAODEgamma.xAODEgammaParameters import xAOD
10 
12 
13 def makeElectronAnalysisSequence( dataType, workingPoint,
14  deepCopyOutput = False,
15  shallowViewOutput = True,
16  postfix = '',
17  recomputeLikelihood = False,
18  chargeIDSelection = False,
19  isolationCorrection = False,
20  crackVeto = False,
21  ptSelectionOutput = False,
22  trackSelection = True,
23  enableCutflow = False,
24  enableKinematicHistograms = False,
25  defineSystObjectLinks = False,
26  forceFullSimConfig = False):
27  """Create an electron analysis algorithm sequence
28 
29  Keyword arguments:
30  dataType -- The data type to run on ("data", "mc" or "afii")
31  workingPoint -- The working point to use
32  deepCopyOutput -- If set to 'True', the output containers will be
33  standalone, deep copies (slower, but needed for xAOD
34  output writing)
35  shallowViewOutput -- Create a view container if required
36  postfix -- a postfix to apply to decorations and algorithm
37  names. this is mostly used/needed when using this
38  sequence with multiple working points to ensure all
39  names are unique.
40  recomputeLikelihood -- Whether to rerun the LH. If not, use derivation flags
41  chargeIDSelection -- Whether or not to perform charge ID/flip selection
42  isolationCorrection -- Whether or not to perform isolation correction
43  crackVeto -- Whether or not to perform eta crack veto
44  ptSelectionOutput -- Whether or not to apply pt selection when creating
45  output containers.
46  trackSelection -- apply selection on tracks (d0, z0, siHits, etc.)
47  enableCutflow -- Whether or not to dump the cutflow
48  enableKinematicHistograms -- Whether or not to dump the kinematic histograms
49  """
50 
51  # Make sure we received a valid data type.
52  if dataType not in [ 'data', 'mc', 'afii' ]:
53  raise ValueError( 'Invalid data type: %s' % dataType )
54 
55  if postfix != '' :
56  postfix = '_' + postfix
57  pass
58 
59  # Create the analysis algorithm sequence object:
60  seq = AnaAlgSequence( "ElectronAnalysisSequence" + postfix )
61 
62  # Variables keeping track of the selections being applied.
63  seq.addMetaConfigDefault ("selectionDecorNames", [])
64  seq.addMetaConfigDefault ("selectionDecorNamesOutput", [])
65  seq.addMetaConfigDefault ("selectionDecorCount", [])
66 
67  makeElectronCalibrationSequence (seq, dataType, postfix=postfix,
68  crackVeto = crackVeto,
69  ptSelectionOutput = ptSelectionOutput,
70  trackSelection = trackSelection,
71  isolationCorrection = isolationCorrection,
72  forceFullSimConfig = forceFullSimConfig)
73  makeElectronWorkingPointSequence (seq, dataType, workingPoint, postfix=postfix,
74  recomputeLikelihood = recomputeLikelihood,
75  chargeIDSelection = chargeIDSelection,
76  forceFullSimConfig = forceFullSimConfig)
77  makeSharedObjectSequence (seq, deepCopyOutput = deepCopyOutput,
78  shallowViewOutput = shallowViewOutput,
79  postfix = '_Electron' + postfix,
80  enableCutflow = enableCutflow,
81  enableKinematicHistograms = enableKinematicHistograms,
82  defineSystObjectLinks = defineSystObjectLinks )
83 
84  # Return the sequence:
85  return seq
86 
87 
88 
89 
90 
91 def makeElectronCalibrationSequence( seq, dataType, postfix = '',
92  crackVeto = False,
93  ptSelectionOutput = False,
94  trackSelection = False,
95  isolationCorrection = False,
96  forceFullSimConfig = False):
97  """Create electron calibration analysis algorithms
98 
99  This makes all the algorithms that need to be run first befor
100  all working point specific algorithms and that can be shared
101  between the working points.
102 
103  Keyword arguments:
104  dataType -- The data type to run on ("data", "mc" or "afii")
105  workingPoint -- The working point to use
106  postfix -- a postfix to apply to decorations and algorithm
107  names. this is mostly used/needed when using this
108  sequence with multiple working points to ensure all
109  names are unique.
110  isolationCorrection -- Whether or not to perform isolation correction
111  ptSelectionOutput -- Whether or not to apply pt selection when creating
112  output containers.
113  """
114 
115  # Make sure we received a valid data type.
116  if dataType not in [ 'data', 'mc', 'afii' ]:
117  raise ValueError( 'Invalid data type: %s' % dataType )
118 
119  if forceFullSimConfig:
120  print("WARNING! You are running makeElectronCalibrationSequence forcing full sim config")
121  print("WARNING! This is only intended to be used for testing purposes")
122 
123  # Set up a shallow copy to decorate
124  alg = createAlgorithm( 'CP::AsgShallowCopyAlg', 'ElectronShallowCopyAlg' + postfix )
125  seq.append( alg, inputPropName = 'input',
126  outputPropName = 'output',
127  stageName = 'prepare')
128 
129  # Set up the eta-cut on all electrons prior to everything else
130  alg = createAlgorithm( 'CP::AsgSelectionAlg', 'ElectronEtaCutAlg' + postfix )
131  alg.selectionDecoration = 'selectEta' + postfix + ',as_bits'
132  addPrivateTool( alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
133  alg.selectionTool.maxEta = 2.47
134  if crackVeto:
135  alg.selectionTool.etaGapLow = 1.37
136  alg.selectionTool.etaGapHigh = 1.52
137  alg.selectionTool.useClusterEta = True
138  seq.append( alg, inputPropName = 'particles',
139  stageName = 'calibration',
140  metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
141  'selectionDecorNamesOutput' : [alg.selectionDecoration],
142  'selectionDecorCount' : [5 if crackVeto else 4]},
143  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNamesOutput"])} )
144 
145  # Set up the track selection algorithm:
146  if trackSelection:
147  alg = createAlgorithm( 'CP::AsgLeptonTrackSelectionAlg',
148  'ElectronTrackSelectionAlg' + postfix )
149  alg.selectionDecoration = 'trackSelection' + postfix + ',as_bits'
150  alg.maxD0Significance = 5
151  alg.maxDeltaZ0SinTheta = 0.5
152  seq.append( alg, inputPropName = 'particles',
153  stageName = 'selection',
154  metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
155  'selectionDecorNamesOutput' : [alg.selectionDecoration],
156  'selectionDecorCount' : [3]},
157  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNamesOutput"])} )
158 
159  # Select electrons only with good object quality.
160  alg = createAlgorithm( 'CP::AsgSelectionAlg', 'ElectronObjectQualityAlg' + postfix )
161  alg.selectionDecoration = 'goodOQ' + postfix + ',as_bits'
162  addPrivateTool( alg, 'selectionTool', 'CP::EgammaIsGoodOQSelectionTool' )
163  alg.selectionTool.Mask = xAOD.EgammaParameters.BADCLUSELECTRON
164  seq.append( alg, inputPropName = 'particles',
165  stageName = 'calibration',
166  metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
167  'selectionDecorNamesOutput' : [alg.selectionDecoration],
168  'selectionDecorCount' : [1]},
169  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNamesOutput"])} )
170 
171  # Set up the calibration and smearing algorithm:
172  alg = createAlgorithm( 'CP::EgammaCalibrationAndSmearingAlg',
173  'ElectronCalibrationAndSmearingAlg' + postfix )
174  addPrivateTool( alg, 'calibrationAndSmearingTool',
175  'CP::EgammaCalibrationAndSmearingTool' )
176  alg.calibrationAndSmearingTool.ESModel = 'es2022_R22_PRE'
177  alg.calibrationAndSmearingTool.decorrelationModel = '1NP_v1'
178  alg.calibrationAndSmearingTool.useFastSim = (0 if forceFullSimConfig
179  else int(dataType == 'afii'))
180  seq.append( alg, inputPropName = 'egammas', outputPropName = 'egammasOut',
181  stageName = 'calibration',
182  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNamesOutput"])} )
183 
184  # Set up the the pt selection
185  alg = createAlgorithm( 'CP::AsgSelectionAlg', 'ElectronPtCutAlg' + postfix )
186  alg.selectionDecoration = 'selectPt' + postfix + ',as_bits'
187  addPrivateTool( alg, 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
188  alg.selectionTool.minPt = 4.5e3
189  seq.append( alg, inputPropName = 'particles',
190  stageName = 'selection',
191  metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
192  'selectionDecorNamesOutput' : [alg.selectionDecoration] if ptSelectionOutput else [],
193  'selectionDecorCount' : [2]},
194  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNamesOutput"])} )
195 
196  # Set up the isolation correction algorithm:
197  if isolationCorrection:
198  alg = createAlgorithm( 'CP::EgammaIsolationCorrectionAlg',
199  'ElectronIsolationCorrectionAlg' + postfix )
200  addPrivateTool( alg, 'isolationCorrectionTool',
201  'CP::IsolationCorrectionTool' )
202  alg.isolationCorrectionTool.IsMC = int(dataType != 'data')
203  alg.isolationCorrectionTool.AFII_corr = (0 if forceFullSimConfig
204  else dataType == 'afii')
205  seq.append( alg, inputPropName = 'egammas', outputPropName = 'egammasOut',
206  stageName = 'calibration',
207  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNamesOutput"])} )
208 
209 
210 
211 
212 
213 def makeElectronWorkingPointSequence( seq, dataType, workingPoint,
214  postfix = '',
215  recomputeLikelihood = False,
216  chargeIDSelection = False,
217  forceFullSimConfig = False ):
218  """Create electron analysis algorithms for a single working point
219 
220  Keyword arguments:
221  dataType -- The data type to run on ("data", "mc" or "afii")
222  workingPoint -- The working point to use
223  postfix -- a postfix to apply to decorations and algorithm
224  names. this is mostly used/needed when using this
225  sequence with multiple working points to ensure all
226  names are unique.
227  recomputeLikelihood -- Whether to rerun the LH. If not, use derivation flags
228  chargeIDSelection -- Whether or not to perform charge ID/flip selection
229  """
230 
231  # Make sure we received a valid data type.
232  if dataType not in [ 'data', 'mc', 'afii' ]:
233  raise ValueError( 'Invalid data type: %s' % dataType )
234 
235  if forceFullSimConfig:
236  print("WARNING! You are running makeElectronWorkingPointSequence forcing full sim config")
237  print("WARNING! This is only intended to be used for testing purposes")
238 
239  splitWP = workingPoint.split ('.')
240  if len (splitWP) != 2 :
241  raise ValueError ('working point should be of format "likelihood.isolation", not ' + workingPoint)
242 
243  likelihoodWP = splitWP[0]
244  isolationWP = splitWP[1]
245 
246  if 'LH' in likelihoodWP:
247  # Set up the likelihood ID selection algorithm
248  # It is safe to do this before calibration, as the cluster E is used
249  alg = createAlgorithm( 'CP::AsgSelectionAlg', 'ElectronLikelihoodAlg' + postfix )
250  alg.selectionDecoration = 'selectLikelihood' + postfix + ',as_bits'
251  if 'SiHits' in likelihoodWP:
252  # Select from Derivation Framework IsEM bits
253  addPrivateTool( alg, 'selectionTool', 'CP::AsgMaskSelectionTool' )
254  dfVar = "DFCommonElectronsLHLooseBLIsEMValue"
255  alg.selectionTool.selectionVars = [dfVar]
256  mask = int( 0 | 0x1 << 1 | 0x1 << 2)
257  alg.selectionTool.selectionMasks = [mask]
258  algDecorCount = 1
259  else:
260  if recomputeLikelihood:
261  # Rerun the likelihood ID
262  addPrivateTool( alg, 'selectionTool', 'AsgElectronLikelihoodTool' )
263  alg.selectionTool.primaryVertexContainer = 'PrimaryVertices'
264  alg.selectionTool.WorkingPoint = likelihoodWP
265  algDecorCount = 7
266  else:
267  # Select from Derivation Framework flags
268  addPrivateTool( alg, 'selectionTool', 'CP::AsgFlagSelectionTool' )
269  dfFlag = "DFCommonElectronsLH" + likelihoodWP.split('LH')[0]
270  dfFlag = dfFlag.replace("BLayer","BL")
271  alg.selectionTool.selectionFlags = [dfFlag]
272  algDecorCount = 1
273  else:
274  # Set up the DNN ID selection algorithm
275  alg = createAlgorithm( 'CP::AsgSelectionAlg', 'ElectronDNNAlg' + postfix )
276  alg.selectionDecoration = 'selectDNN' + postfix + ',as_bits'
277  if recomputeLikelihood:
278  # Rerun the DNN ID
279  addPrivateTool( alg, 'selectionTool', 'AsgElectronSelectorTool' )
280  alg.selectionTool.WorkingPoint = likelihoodWP
281  algDecorCount = 6
282  else:
283  # Select from Derivation Framework flags
284  raise ValueError ( "DNN working points are not available in derivations yet.")
285  seq.append( alg, inputPropName = 'particles',
286  stageName = 'selection',
287  metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
288  'selectionDecorNamesOutput' : [alg.selectionDecoration],
289  'selectionDecorCount' : [algDecorCount]},
290  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNamesOutput"])} )
291 
292  # Set up the isolation selection algorithm:
293  if isolationWP != 'NonIso' :
294  alg = createAlgorithm( 'CP::EgammaIsolationSelectionAlg',
295  'ElectronIsolationSelectionAlg' + postfix )
296  alg.selectionDecoration = 'isolated' + postfix + ',as_bits'
297  addPrivateTool( alg, 'selectionTool', 'CP::IsolationSelectionTool' )
298  alg.selectionTool.ElectronWP = isolationWP
299  seq.append( alg, inputPropName = 'egammas',
300  stageName = 'selection',
301  metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
302  'selectionDecorNamesOutput' : [alg.selectionDecoration],
303  'selectionDecorCount' : [1]},
304  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNamesOutput"])} )
305 
306  # Select electrons only if they don't appear to have flipped their charge.
307  if chargeIDSelection:
308  alg = createAlgorithm( 'CP::AsgSelectionAlg',
309  'ElectronChargeIDSelectionAlg' + postfix )
310  alg.selectionDecoration = 'chargeID' + postfix + ',as_bits'
311  addPrivateTool( alg, 'selectionTool',
312  'AsgElectronChargeIDSelectorTool' )
313  alg.selectionTool.TrainingFile = \
314  'ElectronPhotonSelectorTools/ChargeID/ECIDS_20180731rel21Summer2018.root'
315  alg.selectionTool.WorkingPoint = 'Loose'
316  alg.selectionTool.CutOnBDT = -0.337671 # Loose 97%
317  seq.append( alg, inputPropName = 'particles',
318  stageName = 'selection',
319  metaConfig = {'selectionDecorNames' : [alg.selectionDecoration],
320  'selectionDecorNamesOutput' : [alg.selectionDecoration],
321  'selectionDecorCount' : [1]},
322  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNamesOutput"])} )
323  pass
324 
325  # Set up the electron efficiency correction algorithm:
326  alg = createAlgorithm( 'CP::ElectronEfficiencyCorrectionAlg',
327  'ElectronEfficiencyCorrectionAlg' + postfix )
328  addPrivateTool( alg, 'efficiencyCorrectionTool',
329  'AsgElectronEfficiencyCorrectionTool' )
330  alg.scaleFactorDecoration = 'effSF' + postfix + '_%SYS%'
331  alg.efficiencyCorrectionTool.RecoKey = "Reconstruction"
332  alg.efficiencyCorrectionTool.CorrelationModel = "TOTAL"
333  if dataType == 'afii':
334  alg.efficiencyCorrectionTool.ForceDataType = (
335  PATCore.ParticleDataType.Full if forceFullSimConfig
336  else PATCore.ParticleDataType.Fast)
337  elif dataType == 'mc':
338  alg.efficiencyCorrectionTool.ForceDataType = \
339  PATCore.ParticleDataType.Full
340  pass
341  alg.outOfValidity = 2 #silent
342  alg.outOfValidityDeco = 'bad_eff' + postfix
343  if dataType != 'data':
344  seq.append( alg, inputPropName = 'electrons',
345  stageName = 'efficiency',
346  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNamesOutput"])} )
347  pass
348 
349  # Set up an algorithm used for decorating baseline electron selection:
350  alg = createAlgorithm( 'CP::AsgSelectionAlg',
351  'ElectronSelectionSummary' + postfix )
352  alg.selectionDecoration = 'baselineSelection' + postfix + ',as_char'
353  seq.append( alg, inputPropName = 'particles',
354  stageName = 'selection',
355  dynConfig = {'preselection' : lambda meta : "&&".join (meta["selectionDecorNames"])} )
PATCore::ParticleDataType
Definition: PATCoreEnums.h:21
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
python.DualUseConfig.addPrivateTool
def addPrivateTool(alg, toolName, typeName)
Definition: DualUseConfig.py:180
python.ElectronAnalysisSequence.makeElectronCalibrationSequence
def makeElectronCalibrationSequence(seq, dataType, postfix='', crackVeto=False, ptSelectionOutput=False, trackSelection=False, isolationCorrection=False, forceFullSimConfig=False)
Definition: ElectronAnalysisSequence.py:91
python.DualUseConfig.createAlgorithm
def createAlgorithm(typeName, instanceName)
Definition: DualUseConfig.py:56
Muon::print
std::string print(const MuPatSegment &)
Definition: MuonTrackSteering.cxx:28
python.ElectronAnalysisSequence.makeElectronWorkingPointSequence
def makeElectronWorkingPointSequence(seq, dataType, workingPoint, postfix='', recomputeLikelihood=False, chargeIDSelection=False, forceFullSimConfig=False)
Definition: ElectronAnalysisSequence.py:213
python.ElectronAnalysisSequence.makeElectronAnalysisSequence
def makeElectronAnalysisSequence(dataType, workingPoint, deepCopyOutput=False, shallowViewOutput=True, postfix='', recomputeLikelihood=False, chargeIDSelection=False, isolationCorrection=False, crackVeto=False, ptSelectionOutput=False, trackSelection=True, enableCutflow=False, enableKinematicHistograms=False, defineSystObjectLinks=False, forceFullSimConfig=False)
Definition: ElectronAnalysisSequence.py:13