ATLAS Offline Software
AsgAnalysisConfig.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2022 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 
413  def makeAlgs (self, config) :
414 
415  alg = config.createAlgorithm( 'CP::ObjectCutFlowHistAlg', 'CutFlowDumperAlg_' + self.containerName + '_' + self.selectionName )
416  alg.histPattern = 'cflow_' + self.containerName + "_" + self.selectionName + '_%SYS%'
417  alg.selections = config.getSelectionCutFlow (self.containerName, self.selectionName)
418  alg.input = config.readName (self.containerName)
419  alg.histTitle = "Object Cutflow: " + self.containerName + "." + self.selectionName
420 
421 
422 class EventCutFlowBlock (ConfigBlock):
423  """the ConfigBlock for an event-level cutflow"""
424 
425  def __init__ (self, containerName='', selectionName='') :
426  super (EventCutFlowBlock, self).__init__ ()
427  self.addOption ('containerName', containerName, type=str,
428  noneAction='error',
429  info="the name of the input container, typically EventInfo.")
430  self.addOption ('selectionName', selectionName, type=str,
431  noneAction='error',
432  info="the name of an optional selection decoration to use.")
433  self.addOption ('customSelections', [], type=None,
434  info="the selections for which to generate cutflow histograms. If "
435  "a single string, corresponding to a particular event selection, "
436  "the event cutflow for that selection will be looked up. If a list "
437  "of strings, will use explicitly those selections. If left blank, "
438  "all selections attached to the container will be looked up.")
439  self.addOption ('postfix', '', type=str,
440  info="a postfix to apply in the naming of cutflow histograms. Set "
441  "it when defining multiple cutflows.")
442 
443  def makeAlgs (self, config) :
444 
445  postfix = self.postfix
446  if postfix != '' and postfix[0] != '_' :
447  postfix = '_' + postfix
448 
449  alg = config.createAlgorithm( 'CP::EventCutFlowHistAlg', 'CutFlowDumperAlg_' + self.containerName + '_' + self.selectionName + postfix )
450  alg.histPattern = 'cflow_' + self.containerName + "_" + self.selectionName + postfix + '_%SYS%'
451  # find out which selection decorations to use
452  if isinstance(self.customSelections, str):
453  # user provides a dynamic reference to selections, corresponding to an EventSelection alg
454  alg.selections = config.getEventCutFlow(self.customSelections)
455  elif len(self.customSelections) > 0:
456  # user provides a list of hardcoded selections
457  alg.selections = self.customSelections
458  else:
459  # user provides nothing: get all available selections from EventInfo directly
460  alg.selections = config.getSelectionCutFlow (self.containerName, self.selectionName)
461  alg.selections = [sel+',as_char' for sel in alg.selections]
462  if self.selectionName:
463  alg.preselection = self.selectionName + '_%SYS%'
464  alg.eventInfo = config.readName (self.containerName)
465  alg.histTitle = "Event Cutflow: " + self.containerName + "." + self.selectionName
466 
467 
468 class OutputThinningBlock (ConfigBlock):
469  """the ConfigBlock for output thinning"""
470 
471  def __init__ (self, containerName='', configName='') :
472  # configName is not used. To be removed.
473  super (OutputThinningBlock, self).__init__ ()
474  self.addOption ('containerName', containerName, type=str,
475  noneAction='error',
476  info="the name of the input container.")
477  self.addOption ('postfix', '', type=str,
478  info="a postfix to apply to decorations and algorithm names. "
479  "Typically not needed here.")
480  self.addOption ('selection', '', type=str,
481  info="the name of an optional selection decoration to use.")
482  self.addOption ('selectionName', '', type=str,
483  info="the name of the selection to append this to. The default is "
484  "'' (empty string), meaning that the cuts are applied to every "
485  "object within the container. Specifying a name (e.g. loose) "
486  "applies the cut only to those object who also pass that selection.")
487  self.addOption ('outputName', None, type=str,
488  info="an optional name for the output container.")
489  # TODO: add info string
490  self.addOption ('deepCopy', False, type=bool,
491  info="")
492  self.addOption ('sortPt', False, type=bool,
493  info="whether to sort objects in pt")
494  # TODO: add info string
495  self.addOption ('noUniformSelection', False, type=bool,
496  info="")
497 
498  def makeAlgs (self, config) :
499 
500  postfix = self.postfix
501  if postfix != '' and postfix[0] != '_' :
502  postfix = '_' + postfix
503 
504  selection = config.getFullSelection (self.containerName, self.selectionName)
505  if selection == '' :
506  selection = self.selection
507  elif self.selection != '' :
508  selection = selection + '&&' + self.selection
509 
510  if selection != '' and not self.noUniformSelection :
511  alg = config.createAlgorithm( 'CP::AsgUnionSelectionAlg', 'UnionSelectionAlg' + self.containerName + postfix)
512  alg.preselection = selection
513  alg.particles = config.readName (self.containerName)
514  alg.selectionDecoration = 'outputSelect' + postfix
515  selection = 'outputSelect' + postfix
516 
517  alg = config.createAlgorithm( 'CP::AsgViewFromSelectionAlg', 'DeepCopyAlg' + self.containerName + postfix )
518  alg.input = config.readName (self.containerName)
519  if self.outputName is not None :
520  alg.output = self.outputName + '_%SYS%'
521  config.addOutputContainer (self.containerName, self.outputName)
522  else :
523  alg.output = config.copyName (self.containerName)
524  if selection != '' :
525  alg.selection = [selection]
526  else :
527  alg.selection = []
528  alg.deepCopy = self.deepCopy
529  if self.sortPt and not config.noSystematics() :
530  raise ValueError ("Sorting by pt is not supported with systematics")
531  alg.sortPt = self.sortPt
532 
533 
534 class IFFLeptonDecorationBlock (ConfigBlock):
535  """the ConfigBlock for the IFF classification of leptons"""
536 
537  def __init__ (self, containerName='') :
538  super (IFFLeptonDecorationBlock, self).__init__()
539  self.addOption ('containerName', containerName, type=str,
540  noneAction='error',
541  info="the name of the input electron or muon container.")
542  self.addOption ('separateChargeFlipElectrons', True, type=bool,
543  info="whether to consider charged-flip electrons as a separate class. "
544  "The default is True (recommended).")
545  self.addOption ('decoration', 'IFFClass_%SYS%', type=str,
546  info="the name (str) of the decoration set by the IFF "
547  "TruthClassificationTool. The default is 'IFFClass_%SYS%'.")
548  # Always skip on data
549  self.setOptionValue('skipOnData', True)
550 
551  def makeAlgs (self, config) :
552  particles = config.readName(self.containerName)
553 
554  alg = config.createAlgorithm( 'CP::AsgClassificationDecorationAlg', 'IFFClassifierAlg' + self.containerName )
555  # the IFF classification tool
556  config.addPrivateTool( 'tool', 'TruthClassificationTool')
557  # label charge-flipped electrons as such
558  alg.tool.separateChargeFlipElectrons = self.separateChargeFlipElectrons
559  alg.decoration = self.decoration
560  alg.particles = particles
561 
562  # write the decoration only once to the output
563  config.addOutputVar(self.containerName, alg.decoration, alg.decoration.split("_%SYS%")[0], noSys=True)
564 
565 
566 class MCTCLeptonDecorationBlock (ConfigBlock):
567 
568  def __init__ (self, containerName="") :
569  super (MCTCLeptonDecorationBlock, self).__init__ ()
570 
571  self.addOption ("containerName", containerName, type=str,
572  noneAction='error',
573  info="the input lepton container, with a possible selection, "
574  "in the format container or container.selection.")
575  self.addOption ("prefix", 'MCTC_', type=str,
576  info="the prefix (str) of the decorations based on the MCTC "
577  "classification. The default is 'MCTC_'.")
578  # Always skip on data
579  self.setOptionValue('skipOnData', True)
580 
581  def makeAlgs (self, config) :
582  particles, selection = config.readNameAndSelection(self.containerName)
583 
584  alg = config.createAlgorithm ("CP::MCTCDecorationAlg", f"MCTCDecorationAlg{self.containerName}")
585  alg.particles = particles
586  alg.preselection = selection
587  alg.affectingSystematicsFilter = '.*'
588  config.addOutputVar (self.containerName, "MCTC_isPrompt", f"{self.prefix}isPrompt", noSys=True)
589  config.addOutputVar (self.containerName, "MCTC_fromHadron", f"{self.prefix}fromHadron", noSys=True)
590  config.addOutputVar (self.containerName, "MCTC_fromBSM", f"{self.prefix}fromBSM", noSys=True)
591  config.addOutputVar (self.containerName, "MCTC_fromTau", f"{self.prefix}fromTau", noSys=True)
592 
593 
594 class PerEventSFBlock (ConfigBlock):
595  """the ConfigBlock for the AsgEventScaleFactorAlg"""
596 
597  def __init__ (self, algoName=''):
598  super(PerEventSFBlock, self).__init__()
599  self.addOption('algoName', algoName, type=str,
600  noneAction='error',
601  info="unique name given to the underlying algorithm computing the "
602  "per-event scale factors")
603  self.addOption('particles', '', type=str,
604  info="the input object container, with a possible selection, in the "
605  "format container or container.selection.")
606  self.addOption('objectSF', '', type=str,
607  info="the name of the per-object SF decoration to be used.")
608  self.addOption('eventSF', '', type=str,
609  info="the name of the per-event SF decoration.")
610 
611  def makeAlgs(self, config):
612  if config.dataType() is DataType.Data:
613  return
614  particles, selection = config.readNameAndSelection(self.particles)
615  alg = config.createAlgorithm('CP::AsgEventScaleFactorAlg', self.algoName)
616  alg.particles = particles
617  alg.preselection = selection
618  alg.scaleFactorInputDecoration = self.objectSF
619  alg.scaleFactorOutputDecoration = self.eventSF
620 
621  config.addOutputVar('EventInfo', alg.scaleFactorOutputDecoration,
622  alg.scaleFactorOutputDecoration.split("_%SYS%")[0])
623 
624 
625 class SelectionDecorationBlock (ConfigBlock):
626  """the ConfigBlock to add selection decoration to a container"""
627 
628  def __init__ (self, containers='') :
629  super (SelectionDecorationBlock, self).__init__ ()
630  # TODO: add info string
631  self.addOption('containers', containers, type=list,
632  noneAction='error',
633  info="")
634 
635  def makeAlgs(self, config):
636  for container in self.containers:
637  originContainerName = config.getOutputContainerOrigin(container)
638  selectionNames = config.getSelectionNames(originContainerName)
639  for selectionName in selectionNames:
640  # skip default selection
641  if selectionName == '':
642  continue
643  alg = config.createAlgorithm(
644  'CP::AsgSelectionAlg',
645  f'SelectionDecoration_{originContainerName}_{selectionName}')
646  selectionDecoration = f'baselineSelection_{selectionName}_%SYS%'
647  alg.selectionDecoration = f'{selectionDecoration},as_char'
648  alg.particles = config.readName (originContainerName)
649  alg.preselection = config.getFullSelection (originContainerName,
650  selectionName)
651  config.addOutputVar(
652  originContainerName, selectionDecoration, selectionName)
653 
654 
656  """Create the common services config"""
657 
658  seq.append (CommonServicesConfig ())
659 
660 
661 
662 def makePileupReweightingConfig( seq, campaign=None, files=None, useDefaultConfig=None, userLumicalcFiles=None, userPileupConfigs=None ):
663  """Create a PRW analysis config
664 
665  Keyword arguments:
666  """
667  # TO DO: add explanation of the keyword arguments, left to experts
668 
669  config = PileupReweightingBlock ()
670  config.setOptionValue ('campaign', campaign)
671  config.setOptionValue ('files', files)
672  config.setOptionValue ('useDefaultConfig', useDefaultConfig)
673  config.setOptionValue ('userLumicalcFiles', userLumicalcFiles)
674  config.setOptionValue ('userPileupConfigs', userPileupConfigs)
675  seq.append (config)
676 
677 
678 
680  saveCutBookkeepers=None,
681  runNumber=None,
682  cutBookkeepersSystematics=None ):
683  """Create a generator analysis algorithm sequence
684 
685  Keyword arguments:
686  saveCutBookkeepers -- save cut bokkeepers information into output file
687  runNumber -- MC run number
688  cutBookkeepersSystematics -- store CutBookkeepers systematics
689  """
690 
691  config = GeneratorAnalysisBlock ()
692  config.setOptionValue ('saveCutBookkeepers', saveCutBookkeepers)
693  config.setOptionValue ('runNumber', runNumber)
694  config.setOptionValue ('cutBookkeepersSystematics', cutBookkeepersSystematics)
695  seq.append (config)
696 
697 
698 
699 def makeEventCutFlowConfig( seq, containerName,
700  *, postfix = None, selectionName, customSelections = None):
701  """Create an event-level cutflow config
702 
703  Keyword arguments:
704  containerName -- name of the container
705  postfix -- a postfix to apply to decorations and algorithm names.
706  selectionName -- the name of the selection to do the cutflow for
707  customSelections -- a list of decorations to use in the cutflow, to override the retrieval of all decorations
708  """
709 
710  config = EventCutFlowBlock (containerName, selectionName)
711  config.setOptionValue ('postfix', postfix)
712  config.setOptionValue ('customSelections', customSelections)
713  seq.append (config)
714 
715 
716 def makeOutputThinningConfig( seq, containerName,
717  *, postfix = None, selection = None, selectionName = None, outputName = None, configName='Thinning'):
718  """Create an output thinning config
719 
720  This will do a consistent selection of output containers (if there
721  is a preselection or a selection specified) and then creates a set
722  of view containers (or deep copies) based on that selection.
723 
724  Keyword arguments:
725  containerName -- name of the container
726  postfix -- a postfix to apply to decorations and algorithm
727  names. this is mostly used/needed when using this
728  sequence with multiple working points to ensure all
729  names are unique.
730  selection -- the name of an optional selection decoration to use
731  outputName -- an optional name for the output container
732 
733  """
734 
735  config = OutputThinningBlock (containerName, configName)
736  config.setOptionValue ('postfix', postfix)
737  config.setOptionValue ('selection', selection)
738  config.setOptionValue ('selectionName', selectionName)
739  config.setOptionValue ('outputName', outputName)
740  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:699
python.AsgAnalysisConfig.SelectionDecorationBlock.__init__
def __init__(self, containers='')
Definition: AsgAnalysisConfig.py:628
python.AsgAnalysisConfig.ObjectCutFlowBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:413
python.AsgAnalysisConfig.SelectionDecorationBlock
Definition: AsgAnalysisConfig.py:625
python.AsgAnalysisConfig.EventCutFlowBlock
Definition: AsgAnalysisConfig.py:422
python.AsgAnalysisConfig.PerEventSFBlock
Definition: AsgAnalysisConfig.py:594
python.AsgAnalysisConfig.CommonServicesConfig.runSystematics
runSystematics
Definition: AsgAnalysisConfig.py:56
python.AsgAnalysisConfig.IFFLeptonDecorationBlock.__init__
def __init__(self, containerName='')
Definition: AsgAnalysisConfig.py:537
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:425
python.AsgAnalysisConfig.MCTCLeptonDecorationBlock.__init__
def __init__(self, containerName="")
Definition: AsgAnalysisConfig.py:568
python.AsgAnalysisConfig.OutputThinningBlock
Definition: AsgAnalysisConfig.py:468
python.AsgAnalysisConfig.makeOutputThinningConfig
def makeOutputThinningConfig(seq, containerName, *postfix=None, selection=None, selectionName=None, outputName=None, configName='Thinning')
Definition: AsgAnalysisConfig.py:716
python.AsgAnalysisConfig.GeneratorAnalysisBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:301
python.AsgAnalysisConfig.OutputThinningBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:498
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:597
python.AsgAnalysisConfig.PerEventSFBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:611
python.AsgAnalysisConfig.PtEtaSelectionBlock.__init__
def __init__(self, containerName='', selectionName='')
Definition: AsgAnalysisConfig.py:336
python.AsgAnalysisConfig.makeGeneratorAnalysisConfig
def makeGeneratorAnalysisConfig(seq, saveCutBookkeepers=None, runNumber=None, cutBookkeepersSystematics=None)
Definition: AsgAnalysisConfig.py:679
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:471
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.makeCommonServicesConfig
def makeCommonServicesConfig(seq)
Definition: AsgAnalysisConfig.py:655
python.AsgAnalysisConfig.makePileupReweightingConfig
def makePileupReweightingConfig(seq, campaign=None, files=None, useDefaultConfig=None, userLumicalcFiles=None, userPileupConfigs=None)
Definition: AsgAnalysisConfig.py:662
python.AsgAnalysisConfig.CommonServicesConfig
Definition: AsgAnalysisConfig.py:20
python.AsgAnalysisConfig.MCTCLeptonDecorationBlock
Definition: AsgAnalysisConfig.py:566
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:443
python.AsgAnalysisConfig.IFFLeptonDecorationBlock
Definition: AsgAnalysisConfig.py:534
python.AsgAnalysisConfig.IFFLeptonDecorationBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:551
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:635
python.AsgAnalysisConfig.MCTCLeptonDecorationBlock.makeAlgs
def makeAlgs(self, config)
Definition: AsgAnalysisConfig.py:581
python.AsgAnalysisConfig.CommonServicesConfig.onlySystematicsCategories
onlySystematicsCategories
Definition: AsgAnalysisConfig.py:65
python.Utils.getMCCampaign
def getMCCampaign(files)
Definition: Tools/Campaigns/python/Utils.py:27
python.AsgAnalysisConfig.PtEtaSelectionBlock
Definition: AsgAnalysisConfig.py:333