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 props = {handlename:self, }
182 klass=type('TmpConf', (Configured,), dict(_allowed=self._allowed+[handlename], _propTypes={},
183 type=anaAlg.getType(),_cppclass='none') )
184
185 c=klass(anaAlg.name(), **props)
186 c.assignAllProperties(anaAlg)
187
188 def assignAllProperties(self, anaAlg):
189 """ Transfer all the configuration in self to anaAlg
190 where anaAlg is an AnaAlgorithmConfig."""
191 self._anaAlg = anaAlg
192 for (k,v) in self.properties():
193 self.setPropToAnaAlg(k,v)
194
195 def setPropToAnaAlg(self, k, v):
196 alg=self._anaAlg
197 if isinstance(v , Configured):
198 # it must be a Tool :
199 try:
200 alg.addPrivateTool(v.fullname(), v.type)
201 except RuntimeError:
202 # this means the tool is already declared, nothing to do.
203 pass #
204 v.assignAllProperties(alg)
205 elif isinstance(v, ConfArray ):
206 # it is a Tool array
207 v.assignAllProperties(alg)
208 else:
209 # any other type :
210 cpptype = self._propTypes[k]
211 alg.setProperty[cpptype](self.prefixed(k) , v)
212
213 def getType(self):
214 return self.type
215
216
217def generateConfigured(classname, cppclass, prefix=""):
218 import cppyy
219
220 # get an instance of the class :
221 if issubclass(cppclass, cppyy.gbl.asg.IAsgTool):
222 dummy = cppclass('dummy')
223 else: # then it's an Algorithm
224 dummy = cppclass('dummy', 0)
225
226 # find all the properties of the Tool/Algorithm
227 pm = dummy.getPropertyMgr()
228 propkeys = [str(k) for k,p in pm.getProperties() ]
229 propTypes = dict( (k,propertyType(pm.getProperty(k)) ) for k in propkeys)
230 allowedProp = Configured._allowed + [k for k in propkeys]
231 # generate the class derived from Configured for this Tool/Alg
232 klass=type(classname+'Conf', (Configured,), dict(_allowed=allowedProp, _propTypes=propTypes,
233 type=prefix+classname,_cppclass=cppclass) )
234
235 return klass
236
238 """Guess the type of the TProperty p.
239 p is a C++ instance of a TProperty.
240
241 This simply interpret the cpp name as set by cppyy...
242 """
243 clsname = p.__class__.__cpp_name__
244 # clsname is in the form : 'TProperty<vector<double> >'
245 typ = clsname[10:-1].strip()
246 if 'Handle' in typ:
247 typ='string'
248 return typ
249
250
252 """A namespace able to automatically generate Configured when quering attributes :
253 Used to replace CompFactory so that expressions like :
254 tool = CompFactory.Trk.SomeTrkTool("tname", Prop=2.345)
255 tool = CompFactory.getComp("Trk::SomeTrkTool")("tname", Prop=2.345)
256 works.
257 In the above 2 examples both CompFactory and Trk are ConfNameSpace
258 """
259 def __init__(self, name=""):
260 self.prefix=name+"::" if name !="" else ""
261
262 def __getattr__(self, t):
263 return self.getComp(t)
264
265 def getComp(self, classname):
266 """generates a new Configured class for the C++ class ROOT.classname .
267 This implies there must be a dictionnary for classname.
268 """
269
270 # if we already generated the class, return it :
271 c = self.__dict__.get(classname, None)
272 if c is not None:
273 return c
274
275 # look for the c++ class from ROOT :
276 c=getattr(ROOT, self.prefix+classname, None)
277
278 if c is None:
279 print("JetAnalysisCommon ERROR : ",classname," is not a known C++ tool, alg, or namespace ")
280 raise
281
282 if hasattr(c,'getPropertyMgr'):
283 conf = generateConfigured(classname,c,self.prefix)
284 else:
285 # not a configurable. Must be a namespace
286 conf = ConfNameSpace(self.prefix+classname)
287
288 # save this new class onto self :
289 setattr(self, classname, conf)
290 return conf
291
292 def addNameSpaces(self, *nsList):
293 for ns in nsList:
294 setattr(self, ns, ConfNameSpace(self.prefix+ns))
295
296
297
298# ----------------------------
299# A replacement for CompFactory
300CompFactory = ConfNameSpace()
301# Add known namespaces :
302CompFactory.addNameSpaces( 'Analysis', 'Trk', 'Jet', 'Sim',)
303
304# Make a pseudo-Module :
305ComponentFactory = ModuleType("ComponentFactory")
306ComponentFactory.CompFactory = CompFactory
307
308ComponentFactory.isComponentAccumulatorCfg = lambda : True
309
310
311#*******************************************************************
312# replacements for CFElements
313def parOR(name):
314 pass
315CFElements = ModuleType("CFElements")
316CFElements.parOR = parOR
317
318
319#*******************************************************************
320
322 """Provdide similar interface than AthenaConfiguration.ComponentAccumulator and also very simplistic
323 merging of list of algorithms
324 """
325 def __init__(self, name="ca"):
326 self.name = name
327 self.algs = []
328
329 def __iter__(self):
330 return iter(self.algs)
331
332 def addSequence(self, seqName):
333 pass
334 def addEventAlgo(self, alg, primary=False, sequenceName=""):
335 self.algs +=[alg]
336 setattr(self, alg._name, alg)
337
338 def merge(self, ca, sequenceName=""):
339 myTNs = set( alg.typeAndName() for alg in self.algs)
340 for alg in ca.algs:
341 tn = alg.typeAndName()
342 if tn not in myTNs:
343 self.algs.append(alg)
344 setattr(self, alg._name, alg)
345
346
347ComponentAccumulator = ModuleType("ComponentAccumulator")
348ComponentAccumulator.ComponentAccumulator = ComponentAccumulatorMockUp
349
350
351
352
353#*******************************************************************
354#
355def addManyAlgs(job, algList):
356 """a little configuration function added from the python module JetAnalysisCommon.py to easily schedule
357 # a list of Configured algs as defined by this module."""
358 for alg in algList:
359 job.algsAdd( alg.asAnaAlg() )
360
361ROOT.EL.Job.addManyAlgs = addManyAlgs
362
363
364#*******************************************************************
365# hack the list of modules in sys so that the Athena config modules are found and redirected
366# to what we have defined in this module
367import sys
368JetAnalysisCommon = sys.modules[__name__]
369
370#import AthenaConfiguration, AthenaCommon
371sys.modules['AthenaConfiguration.ComponentFactory'] = JetAnalysisCommon.ComponentFactory
372sys.modules['AthenaConfiguration.ComponentAccumulator'] = JetAnalysisCommon.ComponentAccumulator
373sys.modules['AthenaCommon.CFElements'] = JetAnalysisCommon.CFElements
374
375
377 """Allows to ignore JetRecTools in case this package is not checked out on top of AnalysisBase"""
378 sys.modules['JetRecTools'] = ModuleType('JetRecTools')
379 sys.modules['JetRecTools.JetRecToolsConfig'] = ModuleType('JetRecToolsConfig')
380
381
382
383#*******************************************************************
384# hacks specific to jets
385
386import JetRecConfig.JetRecConfig as JetRecConfig
387
388# In Athena the jet config relies on the automatic scheduling of algorithms
389# which relies on Read/WriteHandles to correctly order them.
390# There's no such automatic ordering in EventLoop.
391# We thus need to add a step in AnalysisBase to make sure inputs algs to jet finder are
392# correctly ordered.
393# For this we redefine the original JetRecConfig.JetRecCfg function.
394
395JetRecConfig.JetRecCfg_original = JetRecConfig.JetRecCfg
396def JetRecCfg_reorder(jetdef, flags, returnFinalJetDef=False):
397 """Builds the algs with JetRecConfig.JetRecCfg and then make sure
398 they are in proper order.
399 Re-ordering is done manually, according to various input alg type.
400 """
401 res = JetRecConfig.JetRecCfg_original(flags, jetdef , returnFinalJetDef)
402
403 acc , _ = res if returnFinalJetDef else (res,None)
404 algs = acc.algs
405
406 # ************
407 # reorder EventDensity and PseudoJetAlg
408 if not hasattr(ROOT, 'EventDensityAthAlg'):
409 return res
410 evtDensityAlgs = [ (i,alg) for (i,alg) in enumerate(algs) if alg._cppclass == ROOT.EventDensityAthAlg ]
411 pjAlgs = [ (i,alg) for (i,alg) in enumerate(algs) if alg._cppclass == ROOT.PseudoJetAlgorithm ]
412 pairsToswap = []
413 for i,edalg in evtDensityAlgs:
414 edInput = edalg.EventDensityTool.InputContainer
415 for j,pjalg in pjAlgs:
416 if j<i: continue
417 if edInput == pjalg.OutputContainer:
418 pairsToswap.append( (i,j) )
419 for (i,j) in pairsToswap:
420 algs[i], algs[j] = algs[j], algs[i]
421
422 # ************
423 # if there were other types of alg which need re-rordering
424 # we could add the specific re-ordering code below ...
425
426 return res
427
428JetRecConfig.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:130
Definition merge.py:1
JetRecCfg_reorder(jetdef, flags, returnFinalJetDef=False)
generateConfigured(classname, cppclass, prefix="")