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