Loading [MathJax]/extensions/tex2jax.js
ATLAS Offline Software
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
AsgAnalysisConfig.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
2 
3 # AnaAlgorithm import(s):
4 from AnalysisAlgorithmsConfig.ConfigBlock import ConfigBlock
5 from AthenaConfiguration.Enums import LHCPeriod
6 from AnalysisAlgorithmsConfig.ConfigAccumulator import DataType
7 from enum import Enum
8 
10  JETS = ['JET_']
11  JER = ['JET_JER']
12  ELECTRONS = ['EG_', 'EL_']
13  MUONS = ['MUON_']
14  PHOTONS = ['EG_', 'PH_']
15  TAUS = ['TAUS_']
16  MET = ['MET_']
17  EVENT = ['GEN_', 'PRW_']
18  FTAG = ['FT_']
19 
20 class CommonServicesConfig (ConfigBlock) :
21  """the ConfigBlock for common services
22 
23  The idea here is that all algorithms need some common services, and I should
24  provide configuration blocks for those. For now there is just a single
25  block, but in the future I might break out e.g. the systematics service.
26  """
27 
28  def __init__ (self) :
29  super (CommonServicesConfig, self).__init__ ()
30  self.addOption ('runSystematics', None, type=bool,
31  info="whether to turn on the computation of systematic variations. "
32  "The default is to run them on MC.")
33  self.addOption ('filterSystematics', None, type=str,
34  info="a regexp string against which the systematics names will be "
35  "matched. Only positive matches are retained and used in the evaluation "
36  "of the various algorithms.")
37  self.addOption ('onlySystematicsCategories', None, type=list,
38  info="a list of strings defining categories of systematics to enable "
39  "(only recommended for studies / partial ntuple productions). Choose amongst: "
40  "jets, electrons, muons, photons, taus, met, ftag, event. This option is overridden "
41  "by 'filterSystematics'.")
42  self.addOption ('systematicsHistogram', None , type=str,
43  info="the name (string) of the histogram to which a list of executed "
44  "systematics will be printed. The default is None (don't write out "
45  "the histogram).")
46 
47  def makeAlgs (self, config) :
48 
49  sysService = config.createService( 'CP::SystematicsSvc', 'SystematicsSvc' )
50 
51  if self.runSystematics is not None :
52  runSystematics = self.runSystematics
53  elif config.noSystematics() is not None :
54  # if option not set:
55  # check to see if set in config accumulator
56  self.runSystematics = not config.noSystematics()
57  runSystematics = self.runSystematics
58  else :
59  runSystematics = True
60 
61  if runSystematics :
62  sysService.sigmaRecommended = 1
63  if config.dataType() is DataType.Data:
64  # Only one type of allowed systematics on data: the JER variations!
66  if self.onlySystematicsCategories is not None:
67  # Convert strings to enums and validate
68  requested_categories = []
69  for category_str in self.onlySystematicsCategories:
70  try:
71  category_enum = SystematicsCategories[category_str.upper()]
72  requested_categories += category_enum.value
73  except KeyError:
74  raise ValueError(f"Invalid systematics category passed to option 'onlySystematicsCategories': {category_str}. Must be one of {', '.join(category.name for category in SystematicsCategories)}")
75  # Construct regex pattern as logical-OR of category names
76  if len(requested_categories):
77  sysService.systematicsRegex = "^(?=.*(" + "|".join(requested_categories) + ")|$).*"
78  if self.filterSystematics is not None:
79  sysService.systematicsRegex = self.filterSystematics
80  config.createService( 'CP::SelectionNameSvc', 'SelectionNameSvc')
81 
82  if self.systematicsHistogram is not None:
83  sysDumper = config.createAlgorithm( 'CP::SysListDumperAlg', 'SystematicsPrinter' )
84  sysDumper.histogramName = self.systematicsHistogram
85 
86 
87 class IOStatsBlock(ConfigBlock):
88  """Print what branches are used in analysis"""
89 
90  def __init__(self):
91  super(IOStatsBlock, self).__init__()
92  self.addOption("printOption", "Summary", type=str,
93  info='option to pass the standard ROOT printing function. Can be "Summary", "ByEntries" or "ByBytes".')
94 
95  def makeAlgs(self, config):
96  alg = config.createAlgorithm('CP::IOStatsAlg', 'IOStatsAlg')
97  alg.printOption = self.printOption
98 
99 
100 class PileupReweightingBlock (ConfigBlock):
101  """the ConfigBlock for pileup reweighting"""
102 
103  def __init__ (self) :
104  super (PileupReweightingBlock, self).__init__ ()
105  self.addOption ('campaign', None, type=None,
106  info="the MC campaign for the PRW auto-configuration.")
107  self.addOption ('files', None, type=None,
108  info="the input files being processed (list of strings). "
109  "Alternative to auto-configuration.")
110  self.addOption ('useDefaultConfig', True, type=bool,
111  info="whether to use the central PRW files. The default is True.")
112  self.addOption ('userLumicalcFiles', None, type=None,
113  info="user-provided lumicalc files (list of strings). Alternative "
114  "to auto-configuration.")
115  self.addOption ('userLumicalcFilesPerCampaign', None, type=None,
116  info="user-provided lumicalc files (dictionary of list of strings, "
117  "with MC campaigns as the keys). Alternative to auto-configuration.")
118  self.addOption ('userPileupConfigs', None, type=None,
119  info="user-provided PRW files (list of strings). Alternative to "
120  "auto-configuration. Alternative to auto-configuration.")
121  self.addOption ('userPileupConfigsPerCampaign', None, type=None,
122  info="user-provided PRW files (dictionary of list of strings, with "
123  "MC campaigns as the keys)")
124  self.addOption ('postfix', '', type=str,
125  info="a postfix to apply to decorations and algorithm names. "
126  "Typically not needed unless several instances of PileupReweighting are scheduled.")
127  self.addOption ('alternativeConfig', False, type=bool,
128  info="whether this is used as an additional alternative config for PileupReweighting. "
129  "Will only store the alternative pile up weight in that case.")
130 
131 
132  def makeAlgs (self, config) :
133 
134  from Campaigns.Utils import Campaign
135 
136  try:
137  from AthenaCommon.Logging import logging
138  except ImportError:
139  import logging
140  log = logging.getLogger('makePileupAnalysisSequence')
141 
142  eventInfoVar = ['runNumber', 'eventNumber', 'actualInteractionsPerCrossing', 'averageInteractionsPerCrossing']
143  if config.dataType() is not DataType.Data:
144  eventInfoVar += ['mcChannelNumber']
145 
146  if config.isPhyslite() and not self.alternativeConfig:
147  # PHYSLITE already has these variables defined, just need to copy them to the output
148  log.info(f'Physlite does not need pileup reweighting. Variables will be copied from input instead. {config.isPhyslite}')
149  for var in eventInfoVar:
150  config.addOutputVar ('EventInfo', var, var, noSys=True)
151 
152  if config.dataType() is not DataType.Data:
153  config.addOutputVar ('EventInfo', 'PileupWeight_%SYS%', 'weight_pileup')
154  if config.geometry() is LHCPeriod.Run2:
155  config.addOutputVar ('EventInfo', 'beamSpotWeight', 'weight_beamspot', noSys=True)
156  return
157 
158  # check files from autoconfig flags
159  if self.files is None and config.autoconfigFlags() is not None:
160  self.files = config.autoconfigFlags().Input.Files
161 
162  campaign = self.campaign
163  # if user didn't explicitly configure campaign, let's try setting it from metadata
164  # only needed on MC
165  if config.dataType() is not DataType.Data and self.campaign is None:
166  # if we used autoconfigflags, campaign is auto-determined
167  if config.campaign() is not None and config.campaign() is not Campaign.Unknown:
168  campaign = config.campaign()
169  log.info(f'Auto-configuring campaign for PRW from flags: {campaign.value}')
170  else:
171  # we try to determine campaign from files if above failed
172  if self.files is not None:
173  from Campaigns.Utils import getMCCampaign
174  campaign = getMCCampaign(self.files)
175  if campaign and campaign is not Campaign.Unknown:
176  log.info(f'Auto-configuring campaign for PRW from files: {campaign.value}')
177  else:
178  log.info('Campaign could not be determined.')
179 
180 
181  toolConfigFiles = []
182  toolLumicalcFiles = []
183 
184  # PRW config files should only be configured if we run on MC
185  # Run 4 not supported yet
186  if (config.dataType() is not DataType.Data and
187  config.geometry() is not LHCPeriod.Run4):
188  # check if user provides per-campaign pileup config list
189  if self.userPileupConfigs is not None and self.userPileupConfigsPerCampaign is not None:
190  raise ValueError('Both userPileupConfigs and userPileupConfigsPerCampaign specified, '
191  'use only one of the options!')
192  if self.userPileupConfigsPerCampaign is not None:
193  if not campaign:
194  raise Exception('userPileupConfigsPerCampaign requires campaign to be configured!')
195  if campaign is Campaign.Unknown:
196  raise Exception('userPileupConfigsPerCampaign used, but campaign = Unknown!')
197  try:
198  toolConfigFiles = self.userPileupConfigsPerCampaign[campaign.value][:]
199  log.info('Using user provided per-campaign PRW configuration')
200  except KeyError as e:
201  raise KeyError(f'Unconfigured campaign {e} for userPileupConfigsPerCampaign!')
202 
203  elif self.userPileupConfigs is not None:
204  toolConfigFiles = self.userPileupConfigs[:]
205  log.info('Using user provided PRW configuration')
206 
207  else:
208  if self.useDefaultConfig and self.files is None:
209  raise ValueError('useDefaultConfig requires files to be configured! '
210  'Either pass them as an option or use flags.')
211 
212  from PileupReweighting.AutoconfigurePRW import getConfigurationFiles
213  if campaign and campaign is not Campaign.Unknown:
214  toolConfigFiles = getConfigurationFiles(campaign=campaign,
215  files=self.files,
216  useDefaultConfig=self.useDefaultConfig,
217  data_type=config.dataType())
218  if self.useDefaultConfig:
219  log.info('Auto-configuring universal/default PRW config')
220  else:
221  log.info('Auto-configuring per-sample PRW config files based on input files')
222  else:
223  log.info('No campaign specified, no PRW config files configured')
224 
225  # check if user provides per-campaign lumical config list
226  if self.userLumicalcFilesPerCampaign is not None and self.userLumicalcFiles is not None:
227  raise ValueError('Both userLumicalcFiles and userLumicalcFilesYear specified, '
228  'use only one of the options!')
229  if self.userLumicalcFilesPerCampaign is not None:
230  try:
231  toolLumicalcFiles = self.userLumicalcFilesPerCampaign[campaign.value][:]
232  log.info('Using user-provided per-campaign lumicalc files')
233  except KeyError as e:
234  raise KeyError(f'Unconfigured campaign {e} for userLumicalcFilesPerCampaign!')
235  elif self.userLumicalcFiles is not None:
236  toolLumicalcFiles = self.userLumicalcFiles[:]
237  log.info('Using user-provided lumicalc files')
238  else:
239  if campaign and campaign is not Campaign.Unknown:
240  from PileupReweighting.AutoconfigurePRW import getLumicalcFiles
241  toolLumicalcFiles = getLumicalcFiles(campaign)
242  log.info('Using auto-configured lumicalc files')
243  else:
244  log.info('No campaign specified, no lumicalc files configured for PRW')
245  else:
246  log.info('Data needs no lumicalc and PRW configuration files')
247 
248  # Set up the only algorithm of the sequence:
249  if config.geometry() is LHCPeriod.Run4:
250  log.warning ('Pileup reweighting is not yet supported for Run 4 geometry')
251  alg = config.createAlgorithm( 'CP::EventDecoratorAlg', 'EventDecoratorAlg'+self.postfix )
252  alg.uint32Decorations = { 'RandomRunNumber' :
253  config.autoconfigFlags().Input.RunNumbers[0] }
254 
255  else:
256  alg = config.createAlgorithm( 'CP::PileupReweightingAlg',
257  'PileupReweightingAlg'+self.postfix )
258  config.addPrivateTool( 'pileupReweightingTool', 'CP::PileupReweightingTool' )
259  alg.pileupReweightingTool.ConfigFiles = toolConfigFiles
260  if not toolConfigFiles and config.dataType() is not DataType.Data:
261  log.info("No PRW config files provided. Disabling reweighting")
262  # Setting the weight decoration to the empty string disables the reweighting
263  alg.pileupWeightDecoration = ""
264  else:
265  alg.pileupWeightDecoration = "PileupWeight" + self.postfix + "_%SYS%"
266  alg.pileupReweightingTool.LumiCalcFiles = toolLumicalcFiles
267 
268  if not self.alternativeConfig:
269  for var in eventInfoVar:
270  config.addOutputVar ('EventInfo', var, var, noSys=True)
271 
272  if config.dataType() is not DataType.Data and config.geometry() is LHCPeriod.Run2:
273  config.addOutputVar ('EventInfo', 'beamSpotWeight', 'weight_beamspot', noSys=True)
274 
275  if config.dataType() is not DataType.Data and toolConfigFiles:
276  config.addOutputVar ('EventInfo', 'PileupWeight' + self.postfix + '_%SYS%',
277  'weight_pileup'+self.postfix)
278 
279 
280 class GeneratorAnalysisBlock (ConfigBlock):
281  """the ConfigBlock for generator algorithms"""
282 
283  def __init__ (self) :
284  super (GeneratorAnalysisBlock, self).__init__ ()
285  self.addOption ('saveCutBookkeepers', True, type=bool,
286  info="whether to save the cut bookkeepers information into the "
287  "output file. The default is True.")
288  self.addOption ('runNumber', None, type=int,
289  info="the MC runNumber (int). The default is None (autoconfigure "
290  "from metadata).")
291  self.addOption ('cutBookkeepersSystematics', None, type=bool,
292  info="whether to also save the cut bookkeepers systematics. The "
293  "default is None (follows the global systematics flag). Set to "
294  "False or True to override.")
295  self.addOption ('histPattern', None, type=str,
296  info="the histogram name pattern for the cut-bookkeeper histogram names")
297  self.addOption ('streamName', 'ANALYSIS', type=str,
298  info="name of the output stream to save the cut bookkeeper in. "
299  "The default is ANALYSIS.")
300 
301  def makeAlgs (self, config) :
302 
303  if config.dataType() is DataType.Data:
304  # there are no generator weights in data!
305  return
306 
307  if self.runNumber is None:
308  self.runNumber = config.runNumber()
309 
310  if self.saveCutBookkeepers and not self.runNumber:
311  raise ValueError ("invalid run number: " + str(self.runNumber))
312 
313  # Set up the CutBookkeepers algorithm:
314  if self.saveCutBookkeepers:
315  alg = config.createAlgorithm('CP::AsgCutBookkeeperAlg', 'CutBookkeeperAlg' + self.streamName)
316  alg.RootStreamName = self.streamName
317  alg.runNumber = self.runNumber
318  if self.cutBookkeepersSystematics:
319  alg.enableSystematics = self.cutBookkeepersSystematics
320  else:
321  alg.enableSystematics = not config.noSystematics()
322  if self.histPattern:
323  alg.histPattern = self.histPattern
324  config.addPrivateTool( 'truthWeightTool', 'PMGTools::PMGTruthWeightTool' )
325 
326  # Set up the weights algorithm:
327  alg = config.createAlgorithm( 'CP::PMGTruthWeightAlg', 'PMGTruthWeightAlg' + self.streamName )
328  config.addPrivateTool( 'truthWeightTool', 'PMGTools::PMGTruthWeightTool' )
329  alg.decoration = 'generatorWeight_%SYS%'
330  config.addOutputVar ('EventInfo', 'generatorWeight_%SYS%', 'weight_mc')
331 
332 
333 class PtEtaSelectionBlock (ConfigBlock):
334  """the ConfigBlock for a pt-eta selection"""
335 
336  def __init__ (self, containerName='', selectionName='') :
337  super (PtEtaSelectionBlock, self).__init__ ()
338  self.addOption ('containerName', containerName, type=str,
339  noneAction='error',
340  info="the name of the input container.")
341  self.addOption ('selectionName', selectionName, type=str,
342  noneAction='error',
343  info="the name of the selection to append this to. The default is "
344  "'' (empty string), meaning that the cuts are applied to every "
345  "object within the container. Specifying a name (e.g. loose) "
346  "applies the cut only to those object who also pass that selection.")
347  self.addOption ('minPt', None, type=float,
348  info="minimum pT value to cut on, in MeV. No default value.")
349  self.addOption ('maxPt', None, type=float,
350  info="maximum pT value to cut on, in MeV. No default value.")
351  self.addOption ('minEta', None, type=float,
352  info="minimum |eta| value to cut on. No default value.")
353  self.addOption ('maxEta', None, type=float,
354  info="maximum |eta| value to cut on. No default value.")
355  self.addOption ('etaGapLow', None, type=float,
356  info="low end of the |eta| gap. No default value.")
357  self.addOption ('etaGapHigh', None, type=float,
358  info="high end of the |eta| gap. No default value.")
359  self.addOption ('selectionDecoration', None, type=str,
360  info="the name of the decoration to set. If 'None', will be set "
361  "to 'selectPtEta' followed by the selection name.")
362  self.addOption ('useClusterEta', False, type=bool,
363  info="whether to use the cluster eta (etaBE(2)) instead of the object "
364  "eta (for electrons and photons). The default is False.")
365  self.addOption ('useDressedProperties', False, type=bool,
366  info="whether to use the dressed kinematic properties "
367  "(for truth particles only). The default is False.")
368 
369 
370  def makeAlgs (self, config) :
371 
372  alg = config.createAlgorithm( 'CP::AsgSelectionAlg', 'PtEtaSelectionAlg' + self.containerName + self.selectionName )
373  config.addPrivateTool( 'selectionTool', 'CP::AsgPtEtaSelectionTool' )
374  if self.minPt is not None :
375  alg.selectionTool.minPt = self.minPt
376  if self.maxPt is not None:
377  alg.selectionTool.maxPt = self.maxPt
378  if self.minEta is not None:
379  alg.selectionTool.minEta = self.minEta
380  if self.maxEta is not None :
381  alg.selectionTool.maxEta = self.maxEta
382  if self.etaGapLow is not None:
383  alg.selectionTool.etaGapLow = self.etaGapLow
384  if self.etaGapHigh is not None:
385  alg.selectionTool.etaGapHigh = self.etaGapHigh
386  if self.selectionDecoration is None:
387  self.selectionDecoration = 'selectPtEta' + (f'_{self.selectionName}' if self.selectionName else '')
388  alg.selectionTool.useClusterEta = self.useClusterEta
389  alg.selectionTool.useDressedProperties = self.useDressedProperties
390  alg.selectionDecoration = self.selectionDecoration
391  alg.particles = config.readName (self.containerName)
392  alg.preselection = config.getPreselection (self.containerName, '')
393  config.addSelection (self.containerName, self.selectionName, alg.selectionDecoration)
394 
395 
396 
397 class ObjectCutFlowBlock (ConfigBlock):
398  """the ConfigBlock for an object cutflow"""
399 
400  def __init__ (self, containerName='', selectionName='') :
401  super (ObjectCutFlowBlock, self).__init__ ()
402  self.addOption ('containerName', containerName, type=str,
403  noneAction='error',
404  info="the name of the input container.")
405  self.addOption ('selectionName', selectionName, type=str,
406  noneAction='error',
407  info="the name of the selection to perform the cutflow for. The "
408  "default is '' (empty string), meaning that the cutflow is "
409  "performed for every object within the container. Specifying a "
410  "name (e.g. loose) generates the cutflow only for those object "
411  "that also pass that selection.")
412  self.addOption ('forceCutSequence', False, type=bool,
413  info="whether to force the cut sequence and not accept objects "
414  "if previous cuts failed. The default is False.")
415 
416  def makeAlgs (self, config) :
417 
418  alg = config.createAlgorithm( 'CP::ObjectCutFlowHistAlg', 'CutFlowDumperAlg_' + self.containerName + '_' + self.selectionName )
419  alg.histPattern = 'cflow_' + self.containerName + "_" + self.selectionName + '_%SYS%'
420  alg.selections = config.getSelectionCutFlow (self.containerName, self.selectionName)
421  alg.input = config.readName (self.containerName)
422  alg.histTitle = "Object Cutflow: " + self.containerName + "." + self.selectionName
423  alg.forceCutSequence = self.forceCutSequence
424 
425 
426 class EventCutFlowBlock (ConfigBlock):
427  """the ConfigBlock for an event-level cutflow"""
428 
429  def __init__ (self, containerName='', selectionName='') :
430  super (EventCutFlowBlock, self).__init__ ()
431  self.addOption ('containerName', containerName, type=str,
432  noneAction='error',
433  info="the name of the input container, typically EventInfo.")
434  self.addOption ('selectionName', selectionName, type=str,
435  noneAction='error',
436  info="the name of an optional selection decoration to use.")
437  self.addOption ('customSelections', [], type=None,
438  info="the selections for which to generate cutflow histograms. If "
439  "a single string, corresponding to a particular event selection, "
440  "the event cutflow for that selection will be looked up. If a list "
441  "of strings, will use explicitly those selections. If left blank, "
442  "all selections attached to the container will be looked up.")
443  self.addOption ('postfix', '', type=str,
444  info="a postfix to apply in the naming of cutflow histograms. Set "
445  "it when defining multiple cutflows.")
446 
447  def makeAlgs (self, config) :
448 
449  postfix = self.postfix
450  if postfix != '' and postfix[0] != '_' :
451  postfix = '_' + postfix
452 
453  alg = config.createAlgorithm( 'CP::EventCutFlowHistAlg', 'CutFlowDumperAlg_' + self.containerName + '_' + self.selectionName + postfix )
454  alg.histPattern = 'cflow_' + self.containerName + "_" + self.selectionName + postfix + '_%SYS%'
455  # find out which selection decorations to use
456  if isinstance(self.customSelections, str):
457  # user provides a dynamic reference to selections, corresponding to an EventSelection alg
458  alg.selections = config.getEventCutFlow(self.customSelections)
459  elif len(self.customSelections) > 0:
460  # user provides a list of hardcoded selections
461  alg.selections = self.customSelections
462  else:
463  # user provides nothing: get all available selections from EventInfo directly
464  alg.selections = config.getSelectionCutFlow (self.containerName, self.selectionName)
465  alg.selections = [sel+',as_char' for sel in alg.selections]
466  if self.selectionName:
467  alg.preselection = self.selectionName + '_%SYS%'
468  alg.eventInfo = config.readName (self.containerName)
469  alg.histTitle = "Event Cutflow: " + self.containerName + "." + self.selectionName
470 
471 
472 class OutputThinningBlock (ConfigBlock):
473  """the ConfigBlock for output thinning"""
474 
475  def __init__ (self, containerName='', configName='') :
476  # configName is not used. To be removed.
477  super (OutputThinningBlock, self).__init__ ()
478  self.addOption ('containerName', containerName, type=str,
479  noneAction='error',
480  info="the name of the input container.")
481  self.addOption ('postfix', '', type=str,
482  info="a postfix to apply to decorations and algorithm names. "
483  "Typically not needed here.")
484  self.addOption ('selection', '', type=str,
485  info="the name of an optional selection decoration to use.")
486  self.addOption ('selectionName', '', type=str,
487  info="the name of the selection to append this to. The default is "
488  "'' (empty string), meaning that the cuts are applied to every "
489  "object within the container. Specifying a name (e.g. loose) "
490  "applies the cut only to those object who also pass that selection.")
491  self.addOption ('outputName', None, type=str,
492  info="an optional name for the output container.")
493  # TODO: add info string
494  self.addOption ('deepCopy', False, type=bool,
495  info="")
496  self.addOption ('sortPt', False, type=bool,
497  info="whether to sort objects in pt")
498  # TODO: add info string
499  self.addOption ('noUniformSelection', False, type=bool,
500  info="")
501 
502  def makeAlgs (self, config) :
503 
504  postfix = self.postfix
505  if postfix != '' and postfix[0] != '_' :
506  postfix = '_' + postfix
507 
508  selection = config.getFullSelection (self.containerName, self.selectionName)
509  if selection == '' :
510  selection = self.selection
511  elif self.selection != '' :
512  selection = selection + '&&' + self.selection
513 
514  if selection != '' and not self.noUniformSelection :
515  alg = config.createAlgorithm( 'CP::AsgUnionSelectionAlg', 'UnionSelectionAlg' + self.containerName + postfix)
516  alg.preselection = selection
517  alg.particles = config.readName (self.containerName)
518  alg.selectionDecoration = 'outputSelect' + postfix
519  config.addSelection (self.containerName, alg.selectionDecoration, selection)
520  selection = 'outputSelect' + postfix
521 
522  alg = config.createAlgorithm( 'CP::AsgViewFromSelectionAlg', 'DeepCopyAlg' + self.containerName + postfix )
523  alg.input = config.readName (self.containerName)
524  if self.outputName is not None :
525  alg.output = self.outputName + '_%SYS%'
526  config.addOutputContainer (self.containerName, self.outputName)
527  else :
528  alg.output = config.copyName (self.containerName)
529  if selection != '' :
530  alg.selection = [selection]
531  else :
532  alg.selection = []
533  alg.deepCopy = self.deepCopy
534  if self.sortPt and not config.noSystematics() :
535  raise ValueError ("Sorting by pt is not supported with systematics")
536  alg.sortPt = self.sortPt
537 
538 
539 class IFFLeptonDecorationBlock (ConfigBlock):
540  """the ConfigBlock for the IFF classification of leptons"""
541 
542  def __init__ (self, containerName='') :
543  super (IFFLeptonDecorationBlock, self).__init__()
544  self.addOption ('containerName', containerName, type=str,
545  noneAction='error',
546  info="the name of the input electron or muon container.")
547  self.addOption ('separateChargeFlipElectrons', True, type=bool,
548  info="whether to consider charged-flip electrons as a separate class. "
549  "The default is True (recommended).")
550  self.addOption ('decoration', 'IFFClass_%SYS%', type=str,
551  info="the name (str) of the decoration set by the IFF "
552  "TruthClassificationTool. The default is 'IFFClass_%SYS%'.")
553  # Always skip on data
554  self.setOptionValue('skipOnData', True)
555 
556  def makeAlgs (self, config) :
557  particles = config.readName(self.containerName)
558 
559  alg = config.createAlgorithm( 'CP::AsgClassificationDecorationAlg', 'IFFClassifierAlg' + self.containerName )
560  # the IFF classification tool
561  config.addPrivateTool( 'tool', 'TruthClassificationTool')
562  # label charge-flipped electrons as such
563  alg.tool.separateChargeFlipElectrons = self.separateChargeFlipElectrons
564  alg.decoration = self.decoration
565  alg.particles = particles
566 
567  # write the decoration only once to the output
568  config.addOutputVar(self.containerName, alg.decoration, alg.decoration.split("_%SYS%")[0], noSys=True)
569 
570 
571 class MCTCLeptonDecorationBlock (ConfigBlock):
572 
573  def __init__ (self, containerName="") :
574  super (MCTCLeptonDecorationBlock, self).__init__ ()
575 
576  self.addOption ("containerName", containerName, type=str,
577  noneAction='error',
578  info="the input lepton container, with a possible selection, "
579  "in the format container or container.selection.")
580  self.addOption ("prefix", 'MCTC_', type=str,
581  info="the prefix (str) of the decorations based on the MCTC "
582  "classification. The default is 'MCTC_'.")
583  # Always skip on data
584  self.setOptionValue('skipOnData', True)
585 
586  def makeAlgs (self, config) :
587  particles, selection = config.readNameAndSelection(self.containerName)
588 
589  alg = config.createAlgorithm ("CP::MCTCDecorationAlg", f"MCTCDecorationAlg{self.containerName}")
590  alg.particles = particles
591  alg.preselection = selection
592  alg.affectingSystematicsFilter = '.*'
593  config.addOutputVar (self.containerName, "MCTC_isPrompt", f"{self.prefix}isPrompt", noSys=True)
594  config.addOutputVar (self.containerName, "MCTC_fromHadron", f"{self.prefix}fromHadron", noSys=True)
595  config.addOutputVar (self.containerName, "MCTC_fromBSM", f"{self.prefix}fromBSM", noSys=True)
596  config.addOutputVar (self.containerName, "MCTC_fromTau", f"{self.prefix}fromTau", noSys=True)
597 
598 
599 class PerEventSFBlock (ConfigBlock):
600  """the ConfigBlock for the AsgEventScaleFactorAlg"""
601 
602  def __init__ (self, algoName=''):
603  super(PerEventSFBlock, self).__init__()
604  self.addOption('algoName', algoName, type=str,
605  noneAction='error',
606  info="unique name given to the underlying algorithm computing the "
607  "per-event scale factors")
608  self.addOption('particles', '', type=str,
609  info="the input object container, with a possible selection, in the "
610  "format container or container.selection.")
611  self.addOption('objectSF', '', type=str,
612  info="the name of the per-object SF decoration to be used.")
613  self.addOption('eventSF', '', type=str,
614  info="the name of the per-event SF decoration.")
615 
616  def makeAlgs(self, config):
617  if config.dataType() is DataType.Data:
618  return
619  particles, selection = config.readNameAndSelection(self.particles)
620  alg = config.createAlgorithm('CP::AsgEventScaleFactorAlg', self.algoName)
621  alg.particles = particles
622  alg.preselection = selection
623  alg.scaleFactorInputDecoration = self.objectSF
624  alg.scaleFactorOutputDecoration = self.eventSF
625 
626  config.addOutputVar('EventInfo', alg.scaleFactorOutputDecoration,
627  alg.scaleFactorOutputDecoration.split("_%SYS%")[0])
628 
629 
630 class SelectionDecorationBlock (ConfigBlock):
631  """the ConfigBlock to add selection decoration to a container"""
632 
633  def __init__ (self, containers='') :
634  super (SelectionDecorationBlock, self).__init__ ()
635  # TODO: add info string
636  self.addOption('containers', containers, type=list,
637  noneAction='error',
638  info="")
639 
640  def makeAlgs(self, config):
641  for container in self.containers:
642  originContainerName = config.getOutputContainerOrigin(container)
643  selectionNames = config.getSelectionNames(originContainerName)
644  for selectionName in selectionNames:
645  # skip default selection
646  if selectionName == '':
647  continue
648  alg = config.createAlgorithm(
649  'CP::AsgSelectionAlg',
650  f'SelectionDecoration_{originContainerName}_{selectionName}')
651  selectionDecoration = f'baselineSelection_{selectionName}_%SYS%'
652  alg.selectionDecoration = f'{selectionDecoration},as_char'
653  alg.particles = config.readName (originContainerName)
654  alg.preselection = config.getFullSelection (originContainerName,
655  selectionName)
656  config.addOutputVar(
657  originContainerName, selectionDecoration, selectionName)
658 
659 def makeEventCutFlowConfig(seq, containerName,
660  *, postfix=None, selectionName, customSelections=None):
661  """Create an event-level cutflow config
662 
663  Keyword arguments:
664  containerName -- name of the container
665  postfix -- a postfix to apply to decorations and algorithm names.
666  selectionName -- the name of the selection to do the cutflow for
667  customSelections -- a list of decorations to use in the cutflow, to override the retrieval of all decorations
668  """
669 
670  config = EventCutFlowBlock(containerName, selectionName)
671  config.setOptionValue('postfix', postfix)
672  config.setOptionValue('customSelections', customSelections)
673  seq.append(config)
python.AsgAnalysisConfig.GeneratorAnalysisBlock
Definition: AsgAnalysisConfig.py:280
python.AsgAnalysisConfig.PileupReweightingBlock
Definition: AsgAnalysisConfig.py:100
python.AsgAnalysisConfig.IOStatsBlock
Definition: AsgAnalysisConfig.py:87
python.AsgAnalysisConfig.PtEtaSelectionBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:370
python.AsgAnalysisConfig.PtEtaSelectionBlock.selectionDecoration
selectionDecoration
Definition: AsgAnalysisConfig.py:387
python.AsgAnalysisConfig.ObjectCutFlowBlock.__init__
def __init__(self, containerName='', selectionName='')
Definition: AsgAnalysisConfig.py:400
python.AsgAnalysisConfig.makeEventCutFlowConfig
def makeEventCutFlowConfig(seq, containerName, *postfix=None, selectionName, customSelections=None)
Definition: AsgAnalysisConfig.py:659
python.AsgAnalysisConfig.SelectionDecorationBlock.__init__
def __init__(self, containers='')
Definition: AsgAnalysisConfig.py:633
python.AsgAnalysisConfig.ObjectCutFlowBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:416
python.AsgAnalysisConfig.SelectionDecorationBlock
Definition: AsgAnalysisConfig.py:630
python.AsgAnalysisConfig.EventCutFlowBlock
Definition: AsgAnalysisConfig.py:426
python.AsgAnalysisConfig.PerEventSFBlock
Definition: AsgAnalysisConfig.py:599
python.AsgAnalysisConfig.CommonServicesConfig.runSystematics
runSystematics
Definition: AsgAnalysisConfig.py:56
python.AsgAnalysisConfig.IFFLeptonDecorationBlock.__init__
def __init__(self, containerName='')
Definition: AsgAnalysisConfig.py:542
python.AsgAnalysisConfig.IOStatsBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:95
python.AsgAnalysisConfig.SystematicsCategories
Definition: AsgAnalysisConfig.py:9
python.AsgAnalysisConfig.EventCutFlowBlock.__init__
def __init__(self, containerName='', selectionName='')
Definition: AsgAnalysisConfig.py:429
python.AsgAnalysisConfig.MCTCLeptonDecorationBlock.__init__
def __init__(self, containerName="")
Definition: AsgAnalysisConfig.py:573
python.AsgAnalysisConfig.OutputThinningBlock
Definition: AsgAnalysisConfig.py:472
python.AsgAnalysisConfig.GeneratorAnalysisBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:301
python.AsgAnalysisConfig.OutputThinningBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:502
python.AsgAnalysisConfig.PileupReweightingBlock.files
files
Definition: AsgAnalysisConfig.py:160
python.AsgAnalysisConfig.CommonServicesConfig.__init__
def __init__(self)
Definition: AsgAnalysisConfig.py:28
python.AutoconfigurePRW.getConfigurationFiles
def getConfigurationFiles(campaign=None, dsid=None, data_type=None, files=None, useDefaultConfig=False)
Definition: AutoconfigurePRW.py:127
python.AsgAnalysisConfig.PerEventSFBlock.__init__
def __init__(self, algoName='')
Definition: AsgAnalysisConfig.py:602
python.AsgAnalysisConfig.PerEventSFBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:616
python.AsgAnalysisConfig.PtEtaSelectionBlock.__init__
def __init__(self, containerName='', selectionName='')
Definition: AsgAnalysisConfig.py:336
python.AsgAnalysisConfig.GeneratorAnalysisBlock.__init__
def __init__(self)
Definition: AsgAnalysisConfig.py:283
python.AsgAnalysisConfig.PileupReweightingBlock.__init__
def __init__(self)
Definition: AsgAnalysisConfig.py:103
python.AsgAnalysisConfig.ObjectCutFlowBlock
Definition: AsgAnalysisConfig.py:397
python.AsgAnalysisConfig.OutputThinningBlock.__init__
def __init__(self, containerName='', configName='')
Definition: AsgAnalysisConfig.py:475
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
python.AsgAnalysisConfig.CommonServicesConfig
Definition: AsgAnalysisConfig.py:20
python.AsgAnalysisConfig.MCTCLeptonDecorationBlock
Definition: AsgAnalysisConfig.py:571
python.AsgAnalysisConfig.IOStatsBlock.__init__
def __init__(self)
Definition: AsgAnalysisConfig.py:90
python.AsgAnalysisConfig.CommonServicesConfig.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:47
python.AsgAnalysisConfig.EventCutFlowBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:447
python.AsgAnalysisConfig.IFFLeptonDecorationBlock
Definition: AsgAnalysisConfig.py:539
python.AsgAnalysisConfig.IFFLeptonDecorationBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:556
str
Definition: BTagTrackIpAccessor.cxx:11
python.AsgAnalysisConfig.GeneratorAnalysisBlock.runNumber
runNumber
Definition: AsgAnalysisConfig.py:308
python.AutoconfigurePRW.getLumicalcFiles
def getLumicalcFiles(campaign)
Definition: AutoconfigurePRW.py:6
python.AsgAnalysisConfig.PileupReweightingBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:132
python.AsgAnalysisConfig.SelectionDecorationBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:640
python.AsgAnalysisConfig.MCTCLeptonDecorationBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:586
python.AsgAnalysisConfig.CommonServicesConfig.onlySystematicsCategories
onlySystematicsCategories
Definition: AsgAnalysisConfig.py:65
python.Utils.getMCCampaign
def getMCCampaign(files)
Definition: Tools/Campaigns/python/Utils.py:38
python.AsgAnalysisConfig.PtEtaSelectionBlock
Definition: AsgAnalysisConfig.py:333