ATLAS Offline Software
JetAnalysisCommon.py
Go to the documentation of this file.
1 # Copyright (C) 2002-2021 CERN for the benefit of the ATLAS collaboration
2 
3 
4 """
5 JetAnalysisCommon
6 
7 This module defines various components to be used in AnalysisBase releases.
8 These components (classes, submodules...) can be used in place of some Athena components
9 they are replacing, so athena-style configuration can be used unchanged in AnalysisBase
10 
11 This module is thus designed ONLY for AnalysisBase and to run EventLoop jobs.
12 
13 IMPORTANT : this module is only designed so the jet configuration works. There's no guarantee it won't mess up
14 when used with other part of Athena configuration.
15 
16 Internally, the athena-style configurations of algorithms are translated to AnaReentrantAlgorithmConfig or
17 AnaAlgorithmConfig. The configuration of AsgTools are translated to call to AnaAlgorithmConfig.addPrivateTool()
18 or AnaAlgorithmConfig.setPropertyFromString()
19 
20 This module allows to write python config for EventLoop exactly as one would do for Athena :
21 
22 alg1 = CompFactory.MyAlg("algname",
23  InputContainer = "SomeJets".
24  Aparameter=4.305,
25  AprivateTool = CompFactory.SomeTool("tool", AToolparam =0.21), )
26 alg2 = CompFactory.AnOtherAlg( ... )
27 
28 job = ROOT.EL.Jon()
29  ...
30 job.addManyAlgs([alg1, alg2]) # specific call allowed by this module
31 
32 driver = ROOT.EL.DirectDriver()
33 driver.submit( job, "out")
34 
35 """
36 
37 
38 from types import ModuleType
39 import ROOT
40 
41 
42 # #*******************************************************************
43 # reset the base logging class : the one set by xAH breaks AthenaCommon.Logging
44 import logging
45 logging.setLoggerClass( logging.Logger )
46 
47 # #*******************************************************************
48 
49 
50 def stringPropValue( value ):
51  """Helper function producing a string property value"""
52 
53  stringValue = str( value )
54  if isinstance( value, bool ):
55  stringValue = str( int( value ) )
56  pass
57  return stringValue
58 
59 class ConfArray:
60  """A simplistic array of Configured (see below) to replace the ToolHandleArray of Athena """
61  def __init__(self, key, conflist , parent):
62  self.key = key
63  self._parent = parent
64  self.conflist=[]
65  self += conflist
66 
67  def __iadd__(self, conflist):
68  self.conflist += conflist
69  for conf in conflist:
70  conf.setparent(self._parent)
71  conf._name = self.key
72 
73  def __iter__(self):
74  return iter(self.conflist)
75 
76  def append(self, conf):
77  self += [conf]
78 
79  def assignAllProperties(self, anaAlg):
80  self._anaAlg = anaAlg
81  for conf in self.conflist:
82  actualName = anaAlg.createPrivateToolInArray(conf.fullname(), conf.type)
83  conf._name = actualName.split('.')[-1] # because AnaAlgorithmConfig will assign it's own naming scheme
84  conf.assignAllProperties(anaAlg)
85 
86 
87 class Configured:
88  """A replacement for Athena auto-generated Configured python class.
89  Configured has the same interface as its Athena counterpart and can describe both Tools and Algorithms
90 
91  This is a base class. The system replacing CompFactory will generate a derived class for each c++ Tool/Algorithm
92  to hold the configuration of such tool/alg (see generateConfigured())
93  """
94  _properties=set()
95  _parent = None
96  _anaAlg = None
97  # the list of properties and attribute memeber which are allowed to be set.
98  # The full list will be generated for each class when actual classes are build in generateConfigured
99  _allowed = ['_properties', '_name', '_parent', '_anaAlg']
100 
101  def __init__(self, name, **props):
102  self._name = name
103  self._properties = set()
104  for k,v in props.items():
105  setattr(self,k,v)
106 
107  def __setattr__(self, k, v):
108  if k in self._allowed:
109  self._setattrImpl(k,v)
110  else:
111  raise AttributeError("Configuration of Tool {} / {} . can't set attribute : {}".format(self.type, self._name, k) )
112 
113  def _setattrImpl(self, k, v) :
114  super().__setattr__(k,v)
115  if k[0] == '_' :
116  # this is not a Property
117  return
118 
119  if isinstance(v, Configured):
120  if k in self._properties:
121  raise RuntimeError( "Configuring {} / {} : Tool for property {} already exists".format(self.type, self._name, k) )
122  # it's a tool:
123  v.setparent(self)
124  v._name = k
125  elif isinstance(v, (list, tuple) ):
126  if isinstance(v[0], Configured):
127  v = ConfArray(k,v, self)
128  super().__setattr__(k,v)
129  self._properties.add(k)
130  if self._anaAlg:
131  self.setPropToAnaAlg( k , v)
132 
133 
134  def getName(self):
135  return self._name
136 
137  def setparent(self, parent):
138  self._parent = parent
139 
140  def prefixed(self, k):
141  if self._parent is None: return k
142  return self.fullname()+'.'+k
143 
144  def ancestors(self):
145  if self._parent is None : return [self]
146  return self._parent.ancestors()+[self]
147 
148  def fullname(self):
149  parents = self.ancestors()[1:]
150  return '.'.join([p._name for p in parents])
151 
152  def properties(self):
153  return [ (k, getattr(self,k)) for k in self._properties]
154 
155  def typeAndName(self):
156  return self.type+'/'+self._name
157 
158  def asAnaAlg(self):
159  if issubclass(self._cppclass, ROOT.EL.AnaReentrantAlgorithm):
160  alg=ROOT.EL.AnaReentrantAlgorithmConfig()
161  else:
162  alg=ROOT.EL.AnaAlgorithmConfig()
163 
164  alg.setTypeAndName( self.typeAndName() )
165  self.assignAllProperties(alg)
166  return alg
167 
168  def assignAllProperties(self, anaAlg):
169  self._anaAlg = anaAlg
170  for (k,v) in self.properties():
171  self.setPropToAnaAlg(k,v)
172 
173  def setPropToAnaAlg(self, k, v):
174  alg=self._anaAlg
175  if isinstance(v , Configured):
176  # it must be a Tool :
177  alg.createPrivateTool(v.fullname(), v.type)
178  v.assignAllProperties(alg)
179  elif isinstance(v, ConfArray ):
180  # it is a Tool array
181  v.assignAllProperties(alg)
182  else:
183  # any other type :
184  alg.setPropertyFromString(self.prefixed(k) , stringPropValue( v ) )
185 
186  def getType(self):
187  return self.type
188 
189 def generateConfigured(classname, cppclass, prefix=""):
190  import cppyy
191 
192  # get an instance of the class :
193  if issubclass(cppclass, cppyy.gbl.asg.IAsgTool):
194  dummy = cppclass('dummy')
195  else: # then it's an Algorithm
196  dummy = cppclass('dummy', 0)
197 
198  # find all the properties of the Tool/Algorithm
199  allowedProp = Configured._allowed + [k for k,v in dummy.getPropertyMgr().getProperties() ]
200  # generate the class derived from Configured for this Tool/Alg
201  klass=type(classname+'Conf', (Configured,), dict(_allowed=allowedProp,
202  type=prefix+classname,_cppclass=cppclass) )
203 
204  return klass
205 
207  """A namespace able to automatically generate Configured when quering attributes :
208  Used to replace CompFactory so that expressions like :
209  tool = CompFactory.Trk.SomeTrkTool("tname", Prop=2.345)
210  tool = CompFactory.getComp("Trk::SomeTrkTool")("tname", Prop=2.345)
211  works.
212  In the above 2 examples both CompFactory and Trk are ConfNameSpace
213  """
214  def __init__(self, name=""):
215  self.prefix=name+"::" if name !="" else ""
216 
217  def __getattr__(self, t):
218  return self.getComp(t)
219 
220  def getComp(self, classname):
221  """generates a new Configured class for the C++ class ROOT.classname .
222  This implies there must be a dictionnary for classname.
223  """
224 
225  # if we already generated the class, return it :
226  c = self.__dict__.get(classname, None)
227  if c is not None:
228  return c
229 
230  # look for the c++ class from ROOT :
231  c=getattr(ROOT, self.prefix+classname, None)
232 
233  if c is None:
234  print("JetAnalysisCommon ERROR : ",classname," is not a known C++ tool, alg, or namespace ")
235  raise
236 
237  if hasattr(c,'getPropertyMgr'):
238  conf = generateConfigured(classname,c,self.prefix)
239  else:
240  # not a configurable. Must be a namespace
241  conf = ConfNameSpace(self.prefix+classname)
242 
243  # save this new class onto self :
244  setattr(self, classname, conf)
245  return conf
246 
247  def addNameSpaces(self, *nsList):
248  for ns in nsList:
249  setattr(self, ns, ConfNameSpace(self.prefix+ns))
250 
251 
252 
253 # ----------------------------
254 # A replacement for CompFactory
255 CompFactory = ConfNameSpace()
256 # Add known namespaces :
257 CompFactory.addNameSpaces( 'Analysis', 'Trk', 'Jet', 'Sim',)
258 
259 # Make a pseudo-Module :
260 ComponentFactory = ModuleType("ComponentFactory")
261 ComponentFactory.CompFactory = CompFactory
262 
263 ComponentFactory.isComponentAccumulatorCfg = lambda : True
264 
265 
266 #*******************************************************************
267 # replacements for CFElements
268 def parOR(name):
269  pass
270 CFElements = ModuleType("CFElements")
271 CFElements.parOR = parOR
272 
273 
274 #*******************************************************************
275 
277  """Provdide similar interface than AthenaConfiguration.ComponentAccumulator and also very simplistic
278  merging of list of algorithms
279  """
280  def __init__(self, name="ca"):
281  self.name = name
282  self.algs = []
283 
284  def __iter__(self):
285  return iter(self.algs)
286 
287  def addSequence(self, seqName):
288  pass
289  def addEventAlgo(self, alg, primary=False, sequenceName=""):
290  self.algs +=[alg]
291  setattr(self, alg._name, alg)
292 
293  def merge(self, ca, sequenceName=""):
294  myTNs = set( alg.typeAndName() for alg in self.algs)
295  for alg in ca.algs:
296  tn = alg.typeAndName()
297  if tn not in myTNs:
298  self.algs.append(alg)
299  setattr(self, alg._name, alg)
300 
301 
302 ComponentAccumulator = ModuleType("ComponentAccumulator")
303 ComponentAccumulator.ComponentAccumulator = ComponentAccumulatorMockUp
304 
305 
306 
307 
308 #*******************************************************************
309 #
310 def addManyAlgs(job, algList):
311  """a little configuration function added from the python module JetAnalysisCommon.py to easily schedule
312  # a list of Configured algs as defined by this module."""
313  for alg in algList:
314  job.algsAdd( alg.asAnaAlg() )
315 
316 ROOT.EL.Job.addManyAlgs = addManyAlgs
317 
318 
319 #*******************************************************************
320 # hack the list of modules in sys so that the Athena config modules are found and redirected
321 # to what we have defined in this module
322 import sys
323 JetAnalysisCommon = sys.modules[__name__]
324 
325 #import AthenaConfiguration, AthenaCommon
326 sys.modules['AthenaConfiguration.ComponentFactory'] = JetAnalysisCommon.ComponentFactory
327 sys.modules['AthenaConfiguration.ComponentAccumulator'] = JetAnalysisCommon.ComponentAccumulator
328 sys.modules['AthenaCommon.CFElements'] = JetAnalysisCommon.CFElements
329 
330 
332  """Allows to ignore JetRecTools in case this package is not checked out on top of AnalysisBase"""
333  sys.modules['JetRecTools'] = ModuleType('JetRecTools')
334  sys.modules['JetRecTools.JetRecToolsConfig'] = ModuleType('JetRecToolsConfig')
335 
336 
337 
338 #*******************************************************************
339 # hacks specific to jets
340 
341 import JetRecConfig.JetRecConfig as JetRecConfig
342 
343 # In Athena the jet config relies on the automatic scheduling of algorithms
344 # which relies on Read/WriteHandles to correctly order them.
345 # There's no such automatic ordering in EventLoop.
346 # We thus need to add a step in AnalysisBase to make sure inputs algs to jet finder are
347 # correctly ordered.
348 # For this we redefine the original JetRecConfig.JetRecCfg function.
349 
350 JetRecConfig.JetRecCfg_original = JetRecConfig.JetRecCfg
351 def JetRecCfg_reorder(jetdef, flags, returnFinalJetDef=False):
352  """Builds the algs with JetRecConfig.JetRecCfg and then make sure
353  they are in proper order.
354  Re-ordering is done manually, according to various input alg type.
355  """
356  res = JetRecConfig.JetRecCfg_original(flags, jetdef , returnFinalJetDef)
357 
358  acc , _ = res if returnFinalJetDef else (res,None)
359  algs = acc.algs
360 
361  # ************
362  # reorder EventDensity and PseudoJetAlg
363  if not hasattr(ROOT, 'EventDensityAthAlg'):
364  return res
365  evtDensityAlgs = [ (i,alg) for (i,alg) in enumerate(algs) if alg._cppclass == ROOT.EventDensityAthAlg ]
366  pjAlgs = [ (i,alg) for (i,alg) in enumerate(algs) if alg._cppclass == ROOT.PseudoJetAlgorithm ]
367  pairsToswap = []
368  for i,edalg in evtDensityAlgs:
369  edInput = edalg.EventDensityTool.InputContainer
370  for j,pjalg in pjAlgs:
371  if j<i: continue
372  if edInput == pjalg.OutputContainer:
373  pairsToswap.append( (i,j) )
374  for (i,j) in pairsToswap:
375  algs[i], algs[j] = algs[j], algs[i]
376 
377  # ************
378  # if there were other types of alg which need re-rordering
379  # we could add the specific re-ordering code below ...
380 
381  return res
382 
383 JetRecConfig.JetRecCfg = JetRecCfg_reorder
python.JetAnalysisCommon.Configured.asAnaAlg
def asAnaAlg(self)
Definition: JetAnalysisCommon.py:158
python.JetAnalysisCommon.ConfNameSpace
Definition: JetAnalysisCommon.py:206
python.JetAnalysisCommon.Configured
Definition: JetAnalysisCommon.py:87
SCT_Parameters::ModuleType
ModuleType
Enums for module and chip type.
Definition: SCT_ReadoutData.h:33
vtune_athena.format
format
Definition: vtune_athena.py:14
python.JetAnalysisCommon.ConfArray.__iter__
def __iter__(self)
Definition: JetAnalysisCommon.py:73
python.JetAnalysisCommon.Configured.getType
def getType(self)
Definition: JetAnalysisCommon.py:186
python.JetAnalysisCommon.Configured._properties
_properties
Definition: JetAnalysisCommon.py:94
CaloCellPos2Ntuple.int
int
Definition: CaloCellPos2Ntuple.py:24
python.JetAnalysisCommon.Configured.__setattr__
def __setattr__(self, k, v)
Definition: JetAnalysisCommon.py:107
python.JetAnalysisCommon.ConfArray.__iadd__
def __iadd__(self, conflist)
Definition: JetAnalysisCommon.py:67
python.JetAnalysisCommon.Configured.assignAllProperties
def assignAllProperties(self, anaAlg)
Definition: JetAnalysisCommon.py:168
python.JetAnalysisCommon.ComponentAccumulatorMockUp.addEventAlgo
def addEventAlgo(self, alg, primary=False, sequenceName="")
Definition: JetAnalysisCommon.py:289
python.ComponentAccumulator.ComponentAccumulator
Definition: ComponentAccumulator.py:89
python.JetAnalysisCommon.Configured._name
_name
Definition: JetAnalysisCommon.py:102
python.JetAnalysisCommon.ComponentAccumulatorMockUp.__iter__
def __iter__(self)
Definition: JetAnalysisCommon.py:284
python.JetAnalysisCommon.ConfArray.append
def append(self, conf)
Definition: JetAnalysisCommon.py:76
python.JetAnalysisCommon.ConfNameSpace.addNameSpaces
def addNameSpaces(self, *nsList)
Definition: JetAnalysisCommon.py:247
python.JetAnalysisCommon.Configured._allowed
list _allowed
Definition: JetAnalysisCommon.py:99
dumpHVPathFromNtuple.append
bool append
Definition: dumpHVPathFromNtuple.py:91
python.JetAnalysisCommon.Configured.fullname
def fullname(self)
Definition: JetAnalysisCommon.py:148
python.JetAnalysisCommon.ConfArray
Definition: JetAnalysisCommon.py:59
python.JetAnalysisCommon.ConfArray._parent
_parent
Definition: JetAnalysisCommon.py:63
python.JetAnalysisCommon.ConfNameSpace.__init__
def __init__(self, name="")
Definition: JetAnalysisCommon.py:214
python.JetAnalysisCommon.ComponentAccumulatorMockUp.algs
algs
Definition: JetAnalysisCommon.py:282
python.JetAnalysisCommon.ConfArray.conflist
conflist
Definition: JetAnalysisCommon.py:64
python.JetAnalysisCommon.Configured.ancestors
def ancestors(self)
Definition: JetAnalysisCommon.py:144
python.JetAnalysisCommon.ConfNameSpace.getComp
def getComp(self, classname)
Definition: JetAnalysisCommon.py:220
python.JetAnalysisCommon.ComponentAccumulatorMockUp.__init__
def __init__(self, name="ca")
Definition: JetAnalysisCommon.py:280
python.JetAnalysisCommon.ConfArray.key
key
Definition: JetAnalysisCommon.py:62
python.KeyStore.dict
def dict(self)
Definition: KeyStore.py:321
python.JetAnalysisCommon.ConfNameSpace.__getattr__
def __getattr__(self, t)
Definition: JetAnalysisCommon.py:217
python.JetAnalysisCommon.parOR
parOR
Definition: JetAnalysisCommon.py:271
python.JetAnalysisCommon.stringPropValue
def stringPropValue(value)
Definition: JetAnalysisCommon.py:50
python.JetAnalysisCommon.Configured.setparent
def setparent(self, parent)
Definition: JetAnalysisCommon.py:137
python.JetAnalysisCommon.Configured._anaAlg
_anaAlg
Definition: JetAnalysisCommon.py:96
python.JetAnalysisCommon.ComponentAccumulatorMockUp
Definition: JetAnalysisCommon.py:276
python.JetAnalysisCommon.ConfNameSpace.prefix
prefix
Definition: JetAnalysisCommon.py:215
add
bool add(const std::string &hname, TKey *tobj)
Definition: fastadd.cxx:55
python.JetAnalysisCommon.ComponentAccumulatorMockUp.addSequence
def addSequence(self, seqName)
Definition: JetAnalysisCommon.py:287
python.JetAnalysisCommon.Configured._setattrImpl
def _setattrImpl(self, k, v)
Definition: JetAnalysisCommon.py:113
python.JetAnalysisCommon.mock_JetRecTools
def mock_JetRecTools()
Definition: JetAnalysisCommon.py:331
python.JetAnalysisCommon.ConfArray.assignAllProperties
def assignAllProperties(self, anaAlg)
Definition: JetAnalysisCommon.py:79
python.JetAnalysisCommon.ComponentAccumulatorMockUp.name
name
Definition: JetAnalysisCommon.py:281
python.JetAnalysisCommon.ConfArray.__init__
def __init__(self, key, conflist, parent)
Definition: JetAnalysisCommon.py:61
CxxUtils::set
constexpr std::enable_if_t< is_bitmask_v< E >, E & > set(E &lhs, E rhs)
Convenience function to set bits in a class enum bitmask.
Definition: bitmask.h:224
TCS::join
std::string join(const std::vector< std::string > &v, const char c=',')
Definition: Trigger/TrigT1/L1Topo/L1TopoCommon/Root/StringUtils.cxx:10
python.JetAnalysisCommon.Configured.properties
def properties(self)
Definition: JetAnalysisCommon.py:152
python.JetAnalysisCommon.Configured.typeAndName
def typeAndName(self)
Definition: JetAnalysisCommon.py:155
python.JetAnalysisCommon.Configured.__init__
def __init__(self, name, **props)
Definition: JetAnalysisCommon.py:101
python.KeyStore.getProperties
def getProperties(self)
Definition: KeyStore.py:247
python.JetAnalysisCommon.JetRecCfg_reorder
def JetRecCfg_reorder(jetdef, flags, returnFinalJetDef=False)
Definition: JetAnalysisCommon.py:351
python.JetAnalysisCommon.ComponentAccumulatorMockUp.merge
def merge(self, ca, sequenceName="")
Definition: JetAnalysisCommon.py:293
python.CaloScaleNoiseConfig.type
type
Definition: CaloScaleNoiseConfig.py:78
Muon::print
std::string print(const MuPatSegment &)
Definition: MuonTrackSteering.cxx:28
get
T * get(TKey *tobj)
get a TObject* from a TKey* (why can't a TObject be a TKey?)
Definition: hcg.cxx:127
python.JetAnalysisCommon.generateConfigured
def generateConfigured(classname, cppclass, prefix="")
Definition: JetAnalysisCommon.py:189
str
Definition: BTagTrackIpAccessor.cxx:11
python.JetAnalysisCommon.addManyAlgs
def addManyAlgs(job, algList)
Definition: JetAnalysisCommon.py:310
python.JetAnalysisCommon.Configured._parent
_parent
Definition: JetAnalysisCommon.py:95
python.JetAnalysisCommon.Configured.prefixed
def prefixed(self, k)
Definition: JetAnalysisCommon.py:140
python.JetAnalysisCommon.Configured.setPropToAnaAlg
def setPropToAnaAlg(self, k, v)
Definition: JetAnalysisCommon.py:173
python.JetAnalysisCommon.Configured.getName
def getName(self)
Definition: JetAnalysisCommon.py:134
python.JetAnalysisCommon.ConfArray._anaAlg
_anaAlg
Definition: JetAnalysisCommon.py:80