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