ATLAS Offline Software
JetRecConfig.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration
2 
3 """
4 JetRecConfig: A helper module for configuring jet reconstruction
5 
6 The functions defined here turn JetDefinition object into ComponentAccumulator or list of algs fully configured
7 and ready to be inserted in the framework sequence.
8 
9 Author: TJ Khoo, P-A Delsart
10 """
11 
12 
13 from AthenaCommon import Logging
14 jetlog = Logging.logging.getLogger('JetRecConfig')
15 
16 from ROOT import xAODType
17 xAODType.ObjectType
18 
19 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
20 from AthenaConfiguration.ComponentFactory import CompFactory
21 
22 
23 from JetRecConfig.JetDefinition import JetDefinition, JetInputConstitSeq, JetInputConstit, JetInputExternal
24 from JetRecConfig.JetGrooming import GroomingDefinition
25 from JetRecConfig.DependencyHelper import solveDependencies, solveGroomingDependencies, aliasToModDef
26 from JetRecConfig.JetConfigFlags import jetInternalFlags
27 
28 
29 __all__ = ["JetRecCfg", "JetInputCfg"]
30 
31 
32 
33 
36 
37 def JetRecCfg( flags, jetdef, returnConfiguredDef=False):
38  """Top-level function for running jet finding or grooming.
39 
40  This returns a ComponentAccumulator that can be merged with others
41  from elsewhere in the job and which provides everything needed to
42  reconstruct one jet collection.
43 
44  arguments :
45  - jetdef : jet or grooming definition
46  - flags : the configuration flags instance, mainly for input file
47  peeking such that we don't attempt to reproduce stuff that's already
48  in the input file. And also to be able to invoke building of inputs outside of Jet domain during reco from RAW/RDO.
49  - returnConfiguredDef : is for debugging. It will also returns the cloned JetDefinition which contains the calculated dependencies.
50 
51  """
52 
53  sequenceName = jetdef.fullname()
54  jetlog.info("******************")
55  jetlog.info("Setting up to find {0}".format(sequenceName))
56 
57  components = ComponentAccumulator()
58  from AthenaCommon.CFElements import parOR
59  components.addSequence( parOR(sequenceName) )
60 
61  # call the relevant function according to jetdef_i type
62  if isinstance(jetdef, JetDefinition):
63  algs, jetdef_i = getJetDefAlgs(flags, jetdef , True)
64  elif isinstance(jetdef, GroomingDefinition):
65  algs, jetdef_i = getJetGroomAlgs(flags, jetdef, True)
66 
67  # FIXME temporarily reorder for serial running
68  if flags.Concurrency.NumThreads <= 0:
69  jetlog.info("Reordering algorithms in sequence {0}".format(sequenceName))
70  algs, ca = reOrderAlgs(algs)
71  components.merge(ca)
72 
73  for a in algs:
74 
75  if isinstance(a, ComponentAccumulator):
76  components.merge(a )
77  else:
78  components.addEventAlgo( a , sequenceName = sequenceName )
79 
80  if returnConfiguredDef: return components, jetdef_i
81  return components
82 
83 
84 def JetInputCfg(flags,jetOrConstitdef , context="default"):
85  """Returns a ComponentAccumulator containing algs needed to build inputs to jet finding as defined by jetOrConstitdef
86 
87  jetOrConstitdef can either be
88  * a JetDefinition : this happens when called from JetRecCfg, then the jetdef._prereqDic/Order are used.
89  * a JetInputConstit : to allow scheduling the corresponding constituents algs independently of any jet alg.
90 
91  context is only used if jetOrConstitdef is not a JetDefinition and must refer to a context in StandardJetContext
92  """
93  components = ComponentAccumulator()
94 
95  algs = getInputAlgs(jetOrConstitdef, flags, context)
96 
97  for a in algs:
98 
99  if isinstance(a, ComponentAccumulator):
100  components.merge(a)
101  else:
102  components.addEventAlgo(a)
103 
104  return components
105 
106 def PseudoJetCfg(jetdef):
107  """Builds a ComponentAccumulator for creating PseudoJetContainer needed by jetdef.
108  THIS updates jetdef._internalAtt['finalPJContainer']
109  """
110  components = ComponentAccumulator()
111  pjalglist = getPseudoJetAlgs(jetdef)
112  for pjalg in pjalglist:
113  components.addEventAlgo(pjalg)
114  return components
115 
116 
117 
121 
122 
123 def getJetDefAlgs(flags, jetdef , returnConfiguredDef=False, monTool=None):
124  """ Create the algorithms necessary to build the jet collection defined by jetdef.
125 
126  This internally finds all the dependencies declared into jetdef (through input, ghosts & modifiers)
127  and returns a list of all necessary algs.
128 
129  if returnConfiguredDef==True, also returns the fully configured clone of jetdef containing solved dependencies (debugging)
130 
131  monTool is to allow the trigger config to pass a monitoring tool.
132 
133  returns a list containing either algs or ComponentAccumulator
134  (ComponentAccumulator occurs only (?) in reco from RDO/RAW when we need to build externals such as clusters or tracks : in this case we call the main config functions from external packages)
135  """
136 
137  # Scan the dependencies of this jetdef, also converting all aliases it contains
138  # into config objects and returning a fully configured copy.
139 
140  jetdef_i = solveDependencies(jetdef, flags=flags)
141 
142 
143  # check if the conditions are compatible with the inputs & modifiers of this jetdef_i.
144  # if in reco job we will remove whatever is incompatible and still try to run
145  # if not, we raise an exception
146  canrun = removeComponentFailingConditions(jetdef_i, raiseOnFailure= not jetInternalFlags.isRecoJob)
147  if not canrun :
148  if returnConfiguredDef:
149  return [], jetdef_i
150  return []
151 
152  algs = []
153 
154  # With jetdef_i, we can now instantiate the proper c++ tools and algs.
155 
156  # algs needed to build the various inputs (constituents, track selection, event density, ...)
157  algs += getInputAlgs(jetdef_i, flags , monTool=monTool)
158 
159  # algs to create fastjet::PseudoJet objects out of the inputs
160  algs += getPseudoJetAlgs(jetdef_i)
161 
162  # Generate a JetRecAlg to run the jet finding and modifiers
163  algs += [getJetRecAlg(jetdef_i, monTool=monTool)]
164 
165  jetlog.info("Scheduled JetAlgorithm instance \"jetalg_{0}\"".format(jetdef_i.fullname()))
166 
167  if returnConfiguredDef:
168  return algs, jetdef_i
169  return algs
170 
171 def getJetGroomAlgs(flags, groomdef, returnConfiguredDef=False, monTool=None):
172  """Instantiate and schedule all the algorithms needed to run the grooming alg 'groomdef' and
173  add them in the ComponentAccumulator 'components'
174 
175  This function is meant to be called from the top-level JetRecConfig.JetRecCfg
176  (groomdef is expected to be non locked and will be modified).
177 
178  monTool is to allow the trigger config to pass a monitoring tool.
179  """
180 
181  # Find dependencies from modifier aliases and get a fully configured groomdef
182  # ( This also detects input dependencies, see below)
183  groomdef_i = solveGroomingDependencies(groomdef, flags)
184 
185  # Transfer the input & ghost dependencies onto the parent jet alg,
186  # so they are handled when instatiating the parent jet algs
187  for prereq in groomdef_i._prereqOrder:
188  #Protection for some modifiers that have three 'arguments'
189  if len(prereq.split(':')) > 2:
190  continue
191  reqType, reqKey = prereq.split(':')
192  if reqType=='ghost':
193  groomdef_i.ungroomeddef.ghostdefs.append(reqKey)
194  elif reqType.endswith('input:') : # can be extinput or input
195  groomdef_i.ungroomeddef.extrainputs.append(reqKey)
196 
197  jetlog.info("Scheduling parent alg {} for {} ".format(groomdef.ungroomeddef.fullname(), groomdef.fullname()))
198 
199  # Retrieve algs needed to build the parent (ungroomed) jets
200  # (we always want it even if the parent jets are already in the input file because
201  # we need to rebuild the pseudoJet)
202  algs, ungroomeddef_i = getJetDefAlgs(flags, groomdef_i.ungroomeddef , True)
203  groomdef_i._ungroomeddef = ungroomeddef_i # set directly the internal members to avoid complication. This is fine, since we've been cloning definitions.
204 
205  #Filter the modifiers based on the flags
206  removeGroomModifFailingConditions(groomdef_i, flags, raiseOnFailure = not jetInternalFlags.isRecoJob)
207 
208  algs += [ getJetRecGroomAlg(groomdef_i, monTool=monTool) ]
209 
210 
211  jetlog.info("Scheduled JetAlgorithm instance \"jetalg_{0}\"".format(groomdef_i.fullname()))
212 
213  if returnConfiguredDef: return algs, groomdef_i
214  return algs
215 
216 
217 def getJetAlgs(flags, jetdef, returnConfiguredDef=False, monTool=None):
218  # Useful helper function For Run-II config style
219  if isinstance(jetdef, JetDefinition):
220  func = getJetDefAlgs
221  elif isinstance(jetdef, GroomingDefinition):
222  func = getJetGroomAlgs
223 
224  return func(flags, jetdef, returnConfiguredDef, monTool)
225 
226 
227 
232 
233 
234 def getPseudoJetAlgs(jetdef):
235  """ Builds the list of configured PseudoJetAlgorithm needed for this jetdef.
236  THIS updates jetdef._internalAtt['finalPJContainer']
237  (this function is factorized out of PseudoJetCfg so it can be used standalone in the trigger config)
238  """
239 
240  constitpjalg = getConstitPJGAlg(jetdef.inputdef , suffix=None , flags=jetdef._cflags, parent_jetdef = jetdef)
241 
242  finalPJContainer = str(constitpjalg.OutputContainer)
243  pjalglist = [constitpjalg]
244 
245  # Schedule the ghost PseudoJetAlgs
246  ghostlist = [ key for key in jetdef._prereqOrder if key.startswith('ghost:')]
247  if ghostlist != []:
248  # then we need to schedule a PseudoJetAlg for each ghost collections...
249  pjContNames = [finalPJContainer]
250  for ghostkey in sorted(ghostlist):
251  ghostdef = jetdef._prereqDic[ghostkey]
252  ghostpjalg = getGhostPJGAlg( ghostdef, jetdef )
253  pjalglist.append(ghostpjalg)
254  pjContNames.append( str(ghostpjalg.OutputContainer) ) #
255 
256  # .. and merge them together with the input constituents
257  mergeId = mergedPJId( pjContNames )
258  finalPJContainer = str(finalPJContainer)+"_merged"+mergeId
259  mergerName = "PJMerger_id"+mergeId
260  mergeAlg =CompFactory.PseudoJetMerger(
261  mergerName,
262  InputPJContainers = pjContNames,
263  OutputContainer = finalPJContainer,
264  )
265  pjalglist.append(mergeAlg)
266 
267  # set the name of the complete,merged input PseudoJets, so it can be re-used downstream
268  jetdef._internalAtt['finalPJContainer'] = finalPJContainer
269  return pjalglist
270 
271 
272 _mergedPJContainers = dict()
273 def mergedPJId(pjList):
274  """returns a simple unique ID for the list of PseudoJet container in pjList"""
275  t = tuple(str(n) for n in pjList) # make sure it is string (it can be DataHandle in old style config)
276  currentSize = len(_mergedPJContainers)
277  return str(_mergedPJContainers.setdefault(t, currentSize))
278 
279 
280 def getInputAlgs(jetOrConstitdef, flags, context="default", monTool=None):
281  """Returns the list of configured algs needed to build inputs to jet finding as defined by jetOrConstitdef
282 
283  jetOrConstitdef can either be
284  * a JetDefinition : this happens when called from JetRecCfg or getJetDefAlgs then the jetdef._prereqDic/Order are used.
285  * a JetInputConstit : to allow scheduling the corresponding constituents algs independently of any jet alg.
286 
287  context is only used if jetOrConstitdef is not a JetDefinition and must refer to a context in StandardJetContext.
288 
289  The returned list may contain several algs, including constituent modifications algs, track selection, copying of
290  input truth particles and event density calculations
291  It may also contain ComponentAccumulator, only (?) in reco from RDO/RAW when we need to build externals such as clusters or tracks : in this case we call the main config functions from external packages)
292 
293  """
294 
295  from .JetDefinition import JetInputConstit, JetDefinition
296  if isinstance(jetOrConstitdef, JetInputConstit):
297  # technically we need a JetDefinition, so just build an empty one only containing our JetInputConstit
298  jetlog.info("Setting up jet inputs from JetInputConstit : "+jetOrConstitdef.name)
299  jetdef = solveDependencies( JetDefinition('Kt', 0., jetOrConstitdef, context=context), flags )
300  canrun = removeComponentFailingConditions(jetdef, raiseOnFailure = not jetInternalFlags.isRecoJob)
301  if not canrun:
302  return []
303  else:
304  jetdef = jetOrConstitdef
305 
306  jetlog.info("Inspecting input file contents")
307 
308  # We won't prepare an alg if the input already exists in the in input file
309  try:
310  filecontents = jetdef._cflags.Input.Collections
311  except Exception:
312  filecontents = []
313  # local function to check if the container of the JetInputXXXX 'c' is already in filecontents :
314  def isInInput( c ):
315  cname = c.containername if isinstance(c, JetInputConstit) else c.containername(jetdef,c.specs)
316  return cname in filecontents
317 
318  # Loop over all inputs required by jetdefs and get the corresponding algs
319  inputdeps = [ inputkey for inputkey in jetdef._prereqOrder if inputkey.startswith('input:') or inputkey.startswith('extinput:') ]
320  algs = []
321  for inputfull in inputdeps:
322  inputInstance = jetdef._prereqDic[inputfull]
323  if isInInput( inputInstance ):
324  jetlog.info(f"Input container for {inputInstance} already in input file.")
325  continue
326 
327  # Get the input or external alg
328  if isinstance(inputInstance, JetInputConstit):
329  alg = getConstitModAlg(jetdef, inputInstance, monTool=monTool)
330  else: # it must be a JetInputExternal
331  alg = inputInstance.algoBuilder( jetdef, inputInstance.specs )
332 
333  if alg is not None:
334  algs.append(alg)
335 
336  return algs
337 
338 
339 
340 
341 
342 def getPJContName( jetOrConstitdef, suffix=None, parent_jetdef = None):
343  """Construct the name of the PseudoJetContainer defined by the given JetDef or JetInputConstit.
344  This name has to be constructed from various places, so we factorize the definition here.
345  """
346  cdef = jetOrConstitdef if isinstance(jetOrConstitdef, JetInputConstit) else jetOrConstitdef.inputdef
347  _str_containername = cdef.containername(parent_jetdef).split(':')[-1] if callable(cdef.containername) else cdef.containername
348  end = '' if suffix is None else f'_{suffix}'
349  return f'PseudoJet{_str_containername}{end}'
350 
351 def getConstitPJGAlg(constitdef, suffix=None, flags=None, parent_jetdef = None):
352  """returns a configured PseudoJetAlgorithm which converts the inputs defined by constitdef into fastjet::PseudoJet
353 
354  IMPORTANT : constitdef must have its dependencies solved (i.e. it must result from a solveDependencies() call)
355 
356  the flags argument is TEMPORARY and will be removed once further dev on PseudoJetAlgorithm is done (see comment below)
357  """
358  _str_containername = constitdef.containername(parent_jetdef).split(':')[-1] if callable(constitdef.containername) else constitdef.containername
359  jetlog.debug("Getting PseudoJetAlg for label {0} from {1}".format(constitdef.name,constitdef.inputname))
360  end = '' if suffix is None else f'_{suffix}'
361  full_label = constitdef.label + end
362  pjgalg = CompFactory.PseudoJetAlgorithm(
363  "pjgalg_"+_str_containername+end,
364  InputContainer = _str_containername,
365  OutputContainer =getPJContName(constitdef, suffix = suffix, parent_jetdef = parent_jetdef),
366  Label = full_label,
367  SkipNegativeEnergy=True,
368  DoByVertex=constitdef.byVertex
369  )
370 
371  # This is a terrible temporary hack to enable running in cosmic runs.
372  # There should not be any Properties setting here in a helper function.
373  # This will have to be fixed when all the filtering occuring in PseudoJetAlgorithm
374  # is removed and done as part of a JetConstituentModSequence.
375  if flags is not None:
376  from AthenaConfiguration.Enums import BeamType
377  pjgalg.UseChargedPV = (flags.Beam.Type == BeamType.Collisions)
378 
379  if suffix == 'PUSB':
380  pjgalg.UseChargedPV=False
381  pjgalg.UseChargedPUsideband=True
382  elif suffix == 'Neut':
383  pjgalg.UseCharged=False
384  # end of HAck
385 
386  return pjgalg
387 
388 def getGhostPJGAlg(ghostdef, parentjetdef = None):
389  """returns a configured PseudoJetAlgorithm which converts the inputs defined by constitdef into fastjet::PseudoJet
390 
391  The difference for the above is this is dedicated to ghosts which need variations for the Label and the muon segment cases.
392 
393  IMPORTANT : ghostdef must have its dependencies solved (i.e. it must result from a solveDependencies() call)
394  """
395  label = "Ghost"+ghostdef.label # IMPORTANT !! "Ghost" in the label will be interpreted by the C++ side !
396  _container_name = ghostdef.containername(parentjetdef).split(":")[1] if callable(ghostdef.containername) else ghostdef.containername
397  _output_cont_name_suffix = "" if parentjetdef.context == "default" or _container_name.endswith(parentjetdef.context) else ("_" + parentjetdef.context)
398 
399  kwargs = dict(
400  InputContainer = _container_name,
401  OutputContainer= "PseudoJetGhost"+_container_name + _output_cont_name_suffix,
402  Label= label,
403  SkipNegativeEnergy= True,
404  )
405 
406  pjaclass = CompFactory.PseudoJetAlgorithm
407  if ghostdef.basetype=="MuonSegment":
408  # Muon segments have a specialised type
409  pjaclass = CompFactory.MuonSegmentPseudoJetAlgorithm
410  kwargs.update( Pt =1e-20 ) # ??,)
411  kwargs.pop('SkipNegativeEnergy')
412 
413  pjgalg = pjaclass( "pjgalg_" + label + "_" + parentjetdef.context, **kwargs )
414  return pjgalg
415 
416 
417 def getJetRecAlg( jetdef, monTool = None, ftf_suffix = ''):
418  """Returns the configured JetRecAlg instance corresponding to jetdef
419 
420  IMPORTANT : jetdef must have its dependencies solved (i.e. it must result from solveDependencies() )
421  """
422  pjContNames = jetdef._internalAtt['finalPJContainer']
423 
424  kwargs = {
425  "JetAlgorithm": jetdef.algorithm,
426  "JetRadius": jetdef.radius,
427  "PtMin": jetdef.ptmin,
428  "InputPseudoJets": pjContNames,
429  "GhostArea": jetdef.ghostarea,
430  "JetInputType": int(jetdef.inputdef.jetinputtype),
431  "RandomOption": 1,
432  "VariableRMinRadius": jetdef.VRMinRadius,
433  "VariableRMassScale": jetdef.VRMassScale
434  }
435 
436  jetname = jetdef.fullname()
437  if jetdef.byVertex:
438  jclust = CompFactory.JetClustererByVertex(
439  "builder",
440  **kwargs
441  )
442  else:
443  jclust = CompFactory.JetClusterer(
444  "builder",
445  **kwargs
446  )
447 
448  mods = getJetModifierTools(jetdef)
449 
450  jra = CompFactory.JetRecAlg(
451  "jetrecalg_"+jetname+ftf_suffix,
452  Provider = jclust,
453  Modifiers = mods,
454  OutputContainer = jetname+ftf_suffix,
455  )
456  if monTool:
457  # this option can't be set in AnalysisBase -> set only if explicitly asked :
458  jra.MonTool = monTool
459 
460  return jra
461 
462 
463 def getJetRecGroomAlg(groomdef,monTool=None):
464  """Returns a configured JetRecAlg set-up to perform the grooming defined by 'groomdef'
465  ('monTool' is a temporary placeholder, it is expected to be used in the trigger in the future)
466  """
467  jetlog.debug("Configuring grooming alg \"jetalg_{0}\"".format(groomdef.fullname()))
468 
469 
470  # the grooming tool (a IJetProvider instance)
471  groomClass = CompFactory.getComp(groomdef.tooltype)
472  groomer = groomClass(groomdef.groomalg,
473  UngroomedJets = groomdef.ungroomeddef.fullname(),
474  ParentPseudoJets = groomdef.ungroomeddef._internalAtt['finalPJContainer'],
475  **groomdef.properties)
476 
477  # get JetModifier list
478  mods = getJetModifierTools(groomdef)
479 
480  # put everything together in a JetRecAlg
481  jetname = groomdef.fullname()
482  jra = CompFactory.JetRecAlg(
483  "jetrecalg_"+jetname,
484  Provider = groomer,
485  Modifiers = mods,
486  OutputContainer = jetname)
487 
488  if not isAnalysisRelease():
489  jra.MonTool = monTool
490 
491  return jra
492 
493 
494 
495 def getJetCopyAlg(jetsin, jetsoutdef, decorations=[], shallowcopy=True, shallowIO=True, monTool=None):
496  """
497  Get a JetRecAlg set up to copy a jet collection and apply mods
498  In this setup we do not resolve dependencies because typically
499  these may be set up already in the original jet collection
500  In future we may wish to add a toggle.
501 
502  The decoration list can be set in order for the decorations
503  (jet moments) on the original jets to be propagated to the
504  copy collection. Beware of circular dependencies!
505  """
506  jcopy = CompFactory.JetCopier(
507  "copier",
508  InputJets = jetsin,
509  DecorDeps=decorations,
510  ShallowCopy=shallowcopy,
511  ShallowIO=shallowIO)
512 
513  # Convert mod aliases into concrete tools
514  mods = []
515  for mod in jetsoutdef.modifiers:
516  moddef = aliasToModDef(mod,jetsoutdef)
517  mods.append(getModifier(jetsoutdef,moddef,moddef.modspec))
518 
519  jetsoutname = jetsoutdef.fullname()
520  jra = CompFactory.JetRecAlg(
521  "jetrecalg_copy_"+jetsoutname,
522  Provider = jcopy,
523  Modifiers = mods,
524  OutputContainer = jetsoutname)
525  if not isAnalysisRelease():
526  jra.MonTool = monTool
527 
528 
529  return jra
530 
531 
532 
533 def getConstitModAlg(parentjetdef, constitSeq, monTool=None):
534  """returns a configured JetConstituentModSequence or None if constit.modifiers == []
535 
536  The JetConstituentModSequence is determined by the JetInputConstitSeq constitSeq .
537  However, details of the configuration of the JetConstituentModSequence may depends on which JetDefinition
538  this JetConstituentModSequence is intended for. Thus the function also requires a parentjetdef JetDefinition input
539 
540  IMPORTANT : parentjetdef & constitSeq must have their dependencies solved (i.e. they must result from solveDependencies() )
541 
542  See also getConstitModAlg_nojetdef
543  """
544 
545  # JetInputConstit do not need any JetConstituentModSequence
546  # (they are only needed to trigger the building of the source container and a PJ algo)
547  if not isinstance(constitSeq, JetInputConstitSeq): return
548 
549 
550  inputtype = constitSeq.basetype
551 
552  sequence = constitSeq.modifiers
553 
554  modlist = []
555 
556  #if modlist == []: return
557  if constitSeq.inputname == constitSeq.containername: return
558 
559  for step in sequence:
560  modInstance = parentjetdef._prereqDic[ f'cmod:{step}' ]
561  if not modInstance.tooltype: continue
562 
563  toolclass = getattr( CompFactory, modInstance.tooltype)
564 
565  # update the properties : if some of them are function, just replace by calling this func :
566  for k,v in modInstance.properties.items():
567  if callable(v) :
568  modInstance.properties[k ] = v(parentjetdef, constitSeq )
569 
570  tool = toolclass(modInstance.name,**modInstance.properties)
571 
572  if (inputtype == xAODType.FlowElement or inputtype == xAODType.ParticleFlow) and modInstance.tooltype not in ["CorrectPFOTool","ChargedHadronSubtractionTool"]:
573  tool.IgnoreChargedPFO=True
574  tool.ApplyToChargedPFO=False
575  tool.InputType = inputtype
576  modlist.append(tool)
577 
578  sequenceshort = "".join(sequence)
579  seqname = "ConstitMod{0}_{1}".format(sequenceshort,constitSeq.name)
580  inputcontainer = str(constitSeq.inputname)
581  outputcontainer = str(constitSeq.containername)
582 
583  if (inputtype == xAODType.FlowElement or inputtype == xAODType.ParticleFlow):
584  # Tweak PF names because ConstModSequence needs to work with
585  # up to 4 containers
586  def chopPFO(thestring):
587  pfostr = "ParticleFlowObjects"
588  if thestring.endswith(pfostr):
589  return thestring[:-len(pfostr)]
590  return thestring
591  inputcontainer = chopPFO(inputcontainer)
592  outputcontainer = chopPFO(outputcontainer)
593 
594  doByVertex = constitSeq.byVertex
595 
596  inChargedFEDecorKeys = []
597  inNeutralFEDecorKeys = []
598 
599  if doByVertex:
600  # For by-vertex jet reconstruction, we are performing deep copies of neutral PFOs
601  # Need to schedule this algorithm after all decorations have been apllied by using ReadDecorHandleKeys
602 
603  # https://gitlab.cern.ch/atlas/athena/-/blob/main/Reconstruction/PFlow/PFlowUtils/src/PFlowCellCPDataDecoratorAlgorithm.h
604 
605  # https://gitlab.cern.ch/atlas/athena/-/blob/main/Reconstruction/PFlow/PFlowUtils/src/PFlowCalibPFODecoratorAlgorithm.h
606 
607  # https://gitlab.cern.ch/atlas/athena/-/blob/main/Reconstruction/eflowRec/eflowRec/PFEGamFlowElementAssoc.h
608 
609  # https://gitlab.cern.ch/atlas/athena/-/blob/main/Reconstruction/eflowRec/eflowRec/PFMuonFlowElementAssoc.h
610 
611  inChargedFEDecorKeys += ["cellCPData", "FE_ElectronLinks", "FE_PhotonLinks", "FE_MuonLinks"]
612  inNeutralFEDecorKeys += ["calpfo_NLeadingTruthParticleBarcodeEnergyPairs", "FE_ElectronLinks", "FE_PhotonLinks", "FE_MuonLinks"]
613 
614  modseq = CompFactory.JetConstituentModSequence(seqname,
615  InputType=inputtype,
616  OutputContainer = outputcontainer,
617  InputContainer= inputcontainer,
618  InChargedFEDecorKeys = inChargedFEDecorKeys,
619  InNeutralFEDecorKeys = inNeutralFEDecorKeys,
620  Modifiers = modlist,
621  DoByVertex = doByVertex
622  )
623  if monTool:
624  modseq.MonTool = monTool
625 
626  constitmodalg = CompFactory.JetAlgorithm("jetalg_{0}".format(modseq.getName()))
627  constitmodalg.Tools = [modseq]
628 
629  return constitmodalg
630 
631 def getConstitModAlg_nojetdef( constitSeq, flags,context="default", monTool=None):
632  """Same as getConstitModAlg.
633  This is a convenient function to obtain a JetConstituentModSequence when it is certain, no JetDef is needed.
634  This function just builds a dummy JetDefinition then calls getConstitModAlg
635  Needed in the trigger config.
636  """
637  jetdef = solveDependencies( JetDefinition('Kt', 0., constitSeq, context=context) , flags)
638  constitSeq = jetdef._prereqDic['input:'+constitSeq.name] # retrieve the fully configured version of constitSeq
639  return getConstitModAlg(jetdef, constitSeq, monTool=monTool)
640 
641 
642 def getJetModifierTools( jetdef ):
643  """returns the list of configured JetModifier tools needed by this jetdef.
644  This is done by instantiating the actual C++ tool as ordered in jetdef._prereqOrder
645  """
646  modlist = [ key for key in jetdef._prereqOrder if key.startswith('mod:')]
647 
648  mods = []
649  for modkey in modlist:
650  moddef = jetdef._prereqDic[modkey]
651  modkey = modkey[4:] # remove 'mod:'
652  modspec = '' if ':' not in modkey else modkey.split(':',1)[1]
653  mod = getModifier(jetdef,moddef,modspec)
654  mods.append(mod)
655 
656  return mods
657 
658 
659 def getModifier(jetdef, moddef, modspec, flags=None):
660  """Translate JetModifier into a concrete tool"""
661  jetlog.verbose("Retrieving modifier {0}".format(str(moddef)))
662 
663  if flags is not None:
664  # then we are called from non JetRecConfig functions: we must update the context according to flags
665  jetdef = jetdef.clone()
666  jetdef._cflags = flags
667  jetdef._contextDic = flags.Jet.Context[jetdef.context]
668 
669  # Get the modifier tool
670  try:
671  modtool = moddef.createfn(jetdef, modspec)
672  except Exception as e:
673  jetlog.error( f"Unhandled modifier specification {modspec} for mod {moddef} acting on jet def {jetdef.basetype}!")
674  jetlog.error( f"Received exception \"{e}\"" )
675  jetlog.error( f"Helper function is \"{moddef.createfn}\"" )
676  raise ValueError( f"JetModConfig unable to handle mod {moddef} with spec \"{modspec}\"")
677 
678 
679  # now we overwrite the default properties of the tool, by those
680  # set in the moddef :
681  for k,v in moddef.properties.items():
682  if callable(v) :
683  # The value we got is a function : we call it to get the actual value we want to set on the tool
684  v = v(jetdef, modspec)
685  setattr(modtool, k, v)
686 
687  return modtool
688 
689 
690 
691 
692 def removeComponentFailingConditions(jetdef, flags=None, raiseOnFailure=True):
693  """Filters the lists jetdef.modifiers and jetdef.ghosts (and jetdef._prereqOrder), so only the components
694  comptatible with flags are selected.
695  If flags==None : assume jetdef._cflags is properly set (this is done by higher-level functions)
696  The compatibility is ultimately tested using the component 'filterfn' attributes.
697  Internally calls the function isComponentPassingConditions() (see below)
698  """
699  jetlog.info("Standard Reco mode : filtering components in "+str(jetdef))
700 
701  if jetdef._cflags is None:
702  jetdef._cflags = flags
703 
704 
707 
708  # ---------
709  # first check if the input can be obtained. If not return.
710  ok,reason = isComponentPassingConditions( jetdef.inputdef, jetdef._cflags, jetdef)
711  if not ok:
712  if raiseOnFailure:
713  raise Exception(f"JetDefinition {jetdef} can NOT be scheduled. Failure of input {jetdef.inputdef.name} reason={reason}" )
714  jetlog.info(f"IMPORTANT : removing {jetdef} because input incompatible with job conditions. Reason={reason} ")
715  return False
716 
717  if isinstance( jetdef.inputdef, JetInputConstitSeq):
718  # remove ConstitModifiers failing conditions.
719  jetdef.inputdef.modifiers = filterJetDefList(jetdef, jetdef.inputdef.modifiers, 'cmod', raiseOnFailure, jetdef._cflags)
720 
721 
722 
723  # call the helper function to perform filtering :
724  jetdef.ghostdefs = filterJetDefList(jetdef, jetdef.ghostdefs, "ghost", raiseOnFailure, jetdef._cflags)
725  jetdef.modifiers = filterJetDefList(jetdef, jetdef.modifiers, "mod", raiseOnFailure, jetdef._cflags)
726  # finally filter all possible intermediate dependency :
727  filterJetDefList(jetdef, list(jetdef._prereqOrder), "", raiseOnFailure, jetdef._cflags)
728  return True
729 
730 
731 
732 def removeGroomModifFailingConditions(groomdef, flags, raiseOnFailure=True):
733 
734  groomdef.modifiers = filterJetDefList(groomdef, groomdef.modifiers, "mod", raiseOnFailure, flags)
735  filterJetDefList(groomdef, list(groomdef._prereqOrder), "", raiseOnFailure, flags)
736 
737 
738 
739 # define a helper function to filter components from jet definition
740 def filterJetDefList(jetdef, inList, compType, raiseOnFailure, flags):
741 
742  nOut=0
743  outList=[]
744  basekey= compType+':' if compType!="" else ""
745 
746  fullname = jetdef.fullname()
747 
748  # loop over components in the list to be filtered
749  for comp in inList:
750  fullkey = basekey+comp
751  cInstance = jetdef._prereqDic[fullkey]
752  ok, reason = isComponentPassingConditions(cInstance, flags, jetdef)
753  if not ok :
754  if raiseOnFailure:
755  raise Exception("JetDefinition {} can NOT be scheduled. Failure of {} {} reason={}".format(
756  jetdef, compType, comp, reason) )
757 
758  nOut+=1
759  jetlog.info(f"{fullname} : removing {compType} {comp} reason={reason}")
760  if fullkey in jetdef._prereqOrder:
761  jetdef._prereqOrder.remove(fullkey)
762  if compType=='ghost':
763  removeFromList(jetdef._prereqOrder, 'input:'+comp)
764  removeFromList(jetdef._prereqOrder, 'extinput:'+comp)
765  else:
766  outList.append(comp)
767 
768  jetlog.info(" *** Number of {} filtered components = {} final list={}".format(compType, nOut, outList) )
769 
770  return outList
771 
772 
773 
774 
775 def isComponentPassingConditions(component, flags, jetdef):
776  """Test if component is compatible with flags.
777  This is done by calling component.filterfn AND testing all its prereqs.
778  """
779  for req in component.prereqs:
780  _str_req = req(jetdef) if callable(req) else req
781  if _str_req not in jetdef._prereqDic:
782  return False, "prereq "+_str_req+" not available"
783  reqInstance = jetdef._prereqDic[_str_req]
784  ok, reason = isComponentPassingConditions(reqInstance, flags, jetdef)
785  if not ok :
786  return False, "prereq "+str(reqInstance)+" failed because : "+reason
787 
788  ok, reason = component.filterfn(flags)
789  return ok, reason
790 
791 
793  from AthenaConfiguration.Enums import Project
794  return Project.determine() in( Project.AnalysisBase, Project.AthAnalysis)
795 
796 
797 def reOrderAlgs(algs):
798  """In runIII the scheduler automatically orders algs, so the JetRecConfig helpers do not try to enforce the correct ordering.
799  This is not the case in runII config for which this jobO is intended --> This function makes sure some jet-related algs are well ordered.
800  """
801  def _flatten_CA(cfg, sequence_name="AthAlgSeq"):
802  from AthenaConfiguration.ComponentAccumulator import ConfigurationError
803  if not isinstance(cfg, ComponentAccumulator):
804  raise ConfigurationError('It is not allowed to flatten with multiple top sequences')
805 
806  if len(cfg._allSequences) != 1:
807  raise ConfigurationError('It is not allowed to flatten with multiple top sequences')
808 
809  sequence = cfg.getSequence(sequence_name)
810  if sequence.Sequential:
811  raise ConfigurationError('It is not allowed to flatten sequential sequences')
812 
813  members = []
814  for member in sequence.Members:
815  if isinstance(member, CompFactory.AthSequencer):
816  members.extend(_flatten_CA(cfg, member.getName()))
817  else:
818  members.append(member)
819 
820  sequence.Members = members
821  return members
822 
823  algs_tmp = []
824  ca = ComponentAccumulator()
825  for a in algs:
826  if not isinstance(a, ComponentAccumulator) :
827  algs_tmp.append(a)
828  else:
829  _flatten_CA(a)
830  ca_algs = list(a._algorithms.keys())
831  for algo in ca_algs:
832  algs_tmp.append(a.popEventAlgo(algo))
833  ca.merge(a)
834 
835  algs = algs_tmp
836  evtDensityAlgs = [(i, alg) for (i, alg) in enumerate(algs) if alg and alg.getType() == 'EventDensityAthAlg' ]
837  pjAlgs = [(i, alg) for (i, alg) in enumerate(algs) if alg and alg.getType() == 'PseudoJetAlgorithm' ]
838  pairsToswap = []
839  for i, edalg in evtDensityAlgs:
840  edInput = edalg.EventDensityTool.InputContainer
841  for j, pjalg in pjAlgs:
842  if j < i:
843  continue
844  if edInput == str(pjalg.OutputContainer):
845  pairsToswap.append((i, j))
846  for i, j in pairsToswap:
847  algs[i], algs[j] = algs[j], algs[i]
848 
849  return algs, ca
850 
851 
852 
854  """Make the jet collection described by jetdef available as constituents to other jet finding
855 
856  Technically : create JetInputExternal and JetInputConstit and register them in the relevant look-up dictionnaries.
857  the JetInputConstit will have a algoBuilder to generate the JetContainer described by jetdef
858  """
859  from .StandardJetConstits import stdConstitDic, stdInputExtDic
860  jetname = jetdef.fullname()
861 
862  # define a function to generate the CA for this jetdef
863  def jetBuilder(largejetdef,spec):
864  return JetRecCfg(largejetdef._cflags, jetdef)
865 
866  stdInputExtDic[jetname] = JetInputExternal( jetname, jetname, algoBuilder=jetBuilder)
867  stdConstitDic[jetname] = JetInputConstit(jetname, xAODType.Jet, jetname )
868 
869 
870 def removeFromList(l, o):
871  if o in l:
872  l.remove(o)
873 
874 
875 if __name__=="__main__":
876  # Config flags steer the job at various levels
877  from AthenaConfiguration.AllConfigFlags import initConfigFlags
878  flags = initConfigFlags()
879  flags.Input.Files = ["/cvmfs/atlas-nightlies.cern.ch/repo/data/data-art/ASG/mc16_13TeV.410501.PowhegPythia8EvtGen_A14_ttbar_hdamp258p75_nonallhad.merge.AOD.e5458_s3126_r9364_r9315/AOD.11182705._000001.pool.root.1"]
880  flags.Concurrency.NumThreads = 1
881  flags.Concurrency.NumConcurrentEvents = 1
882  flags.lock()
883 
884  # Get a ComponentAccumulator setting up the fundamental Athena job
885  from AthenaConfiguration.MainServicesConfig import MainServicesCfg
886  cfg=MainServicesCfg(flags)
887 
888  # Add the components for reading in pool files
889  from AthenaPoolCnvSvc.PoolReadConfig import PoolReadCfg
890  cfg.merge(PoolReadCfg(flags))
891 
892  # Add the components from our jet reconstruction job
893  from StandardSmallRJets import AntiKt4EMTopo
894  AntiKt4EMTopo.modifiers = ["Calib:T0:mc","Filter:15000","Sort"] + ["JVT"] + ["PartonTruthLabel"]
895  cfg.merge(JetRecCfg(AntiKt4EMTopo,flags,jetnameprefix="New"))
896 
897  cfg.printConfig(withDetails=False,summariseProps=True)
898 
899 
python.JetAnalysisCommon.ComponentAccumulator
ComponentAccumulator
Definition: JetAnalysisCommon.py:302
vtune_athena.format
format
Definition: vtune_athena.py:14
python.JetRecConfig.getInputAlgs
def getInputAlgs(jetOrConstitdef, flags, context="default", monTool=None)
Definition: JetRecConfig.py:280
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
python.JetRecConfig.getConstitModAlg
def getConstitModAlg(parentjetdef, constitSeq, monTool=None)
Definition: JetRecConfig.py:533
python.JetRecConfig.reOrderAlgs
def reOrderAlgs(algs)
Definition: JetRecConfig.py:797
python.JetRecConfig.getJetRecGroomAlg
def getJetRecGroomAlg(groomdef, monTool=None)
Definition: JetRecConfig.py:463
python.JetRecConfig.getModifier
def getModifier(jetdef, moddef, modspec, flags=None)
Definition: JetRecConfig.py:659
python.JetRecConfig.isAnalysisRelease
def isAnalysisRelease()
Definition: JetRecConfig.py:792
python.JetRecConfig.getJetAlgs
def getJetAlgs(flags, jetdef, returnConfiguredDef=False, monTool=None)
Definition: JetRecConfig.py:217
python.JetRecConfig.getJetGroomAlgs
def getJetGroomAlgs(flags, groomdef, returnConfiguredDef=False, monTool=None)
Definition: JetRecConfig.py:171
python.JetRecConfig.filterJetDefList
def filterJetDefList(jetdef, inList, compType, raiseOnFailure, flags)
Definition: JetRecConfig.py:740
python.JetRecConfig.registerAsInputConstit
def registerAsInputConstit(jetdef)
Definition: JetRecConfig.py:853
python.JetRecConfig.removeComponentFailingConditions
def removeComponentFailingConditions(jetdef, flags=None, raiseOnFailure=True)
Definition: JetRecConfig.py:692
python.JetRecConfig.getConstitModAlg_nojetdef
def getConstitModAlg_nojetdef(constitSeq, flags, context="default", monTool=None)
Definition: JetRecConfig.py:631
python.JetAnalysisCommon.parOR
parOR
Definition: JetAnalysisCommon.py:271
python.DependencyHelper.aliasToModDef
def aliasToModDef(alias, parentjetdef)
Definition: DependencyHelper.py:192
python.JetRecConfig.getJetModifierTools
def getJetModifierTools(jetdef)
Definition: JetRecConfig.py:642
python.JetRecConfig.JetRecCfg
def JetRecCfg(flags, jetdef, returnConfiguredDef=False)
Top level functions returning ComponentAccumulator out of JetDefinition.
Definition: JetRecConfig.py:37
python.JetRecConfig.mergedPJId
def mergedPJId(pjList)
Definition: JetRecConfig.py:273
python.MainServicesConfig.MainServicesCfg
def MainServicesCfg(flags, LoopMgr='AthenaEventLoopMgr')
Definition: MainServicesConfig.py:252
python.JetRecConfig.getGhostPJGAlg
def getGhostPJGAlg(ghostdef, parentjetdef=None)
Definition: JetRecConfig.py:388
python.JetRecConfig.getJetRecAlg
def getJetRecAlg(jetdef, monTool=None, ftf_suffix='')
Definition: JetRecConfig.py:417
histSizes.list
def list(name, path='/')
Definition: histSizes.py:38
python.JetRecConfig.getJetDefAlgs
def getJetDefAlgs(flags, jetdef, returnConfiguredDef=False, monTool=None)
Mid level functions returning list of algs out of JetDefinition.
Definition: JetRecConfig.py:123
python.DependencyHelper.solveDependencies
def solveDependencies(jetdef0, flags)
Definition: DependencyHelper.py:20
DerivationFramework::TriggerMatchingUtils::sorted
std::vector< typename T::value_type > sorted(T begin, T end)
Helper function to create a sorted vector from an unsorted one.
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
python.JetRecConfig.getPJContName
def getPJContName(jetOrConstitdef, suffix=None, parent_jetdef=None)
Definition: JetRecConfig.py:342
python.JetRecConfig.getConstitPJGAlg
def getConstitPJGAlg(constitdef, suffix=None, flags=None, parent_jetdef=None)
Definition: JetRecConfig.py:351
python.JetRecConfig.JetInputCfg
def JetInputCfg(flags, jetOrConstitdef, context="default")
Definition: JetRecConfig.py:84
python.JetRecConfig.removeGroomModifFailingConditions
def removeGroomModifFailingConditions(groomdef, flags, raiseOnFailure=True)
Definition: JetRecConfig.py:732
python.JetRecConfig.getJetCopyAlg
def getJetCopyAlg(jetsin, jetsoutdef, decorations=[], shallowcopy=True, shallowIO=True, monTool=None)
Definition: JetRecConfig.py:495
python.PyAthena.v
v
Definition: PyAthena.py:157
python.AllConfigFlags.initConfigFlags
def initConfigFlags()
Definition: AllConfigFlags.py:19
python.JetRecConfig.PseudoJetCfg
def PseudoJetCfg(jetdef)
Definition: JetRecConfig.py:106
python.DependencyHelper.solveGroomingDependencies
def solveGroomingDependencies(groomdef0, flags)
Definition: DependencyHelper.py:64
str
Definition: BTagTrackIpAccessor.cxx:11
python.JetRecConfig.getPseudoJetAlgs
def getPseudoJetAlgs(jetdef)
Mid level functions returning specific type of algs out of JetDefinition functions below assumines th...
Definition: JetRecConfig.py:234
python.JetRecConfig.isComponentPassingConditions
def isComponentPassingConditions(component, flags, jetdef)
Definition: JetRecConfig.py:775
python.PoolReadConfig.PoolReadCfg
def PoolReadCfg(flags)
Definition: PoolReadConfig.py:69
python.JetRecConfig.removeFromList
def removeFromList(l, o)
Definition: JetRecConfig.py:870
Trk::split
@ split
Definition: LayerMaterialProperties.h:38