ATLAS Offline Software
Loading...
Searching...
No Matches
JetDefinition.py
Go to the documentation of this file.
1# Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
2
3"""
4
5JetDefinition: A module for classes encoding definitions of jets and
6related objects for configuring jet reconstruction
7
8Various classes encode definitions of different types of components used in Jet Reco.
9They are :
10
11 - JetInputExternal : describes how to build a source container, typically external to the jet domain. This includes input to jet finding (ex: CaloCluster, Track) but also other sources like EventDensity...
12
13 - JetInputConstit : describes specifically an input constituents container (an input to a PseudoJetAlgorithm), thus referring to a JetInputExternal as the primary source.
14 - JetInputConstitSeq : a subclass of JetInputConstit, describing how a constituents container is build from a JetConstituentModSequence (ex: PU or Origin correction).
15 - JetConstitModifier : describes a constituent modifier tool to be used in a JetConstituentModSequence
16
17 - JetDefinition : describes a full jet reco sequence. Uses a JetInputConstit and a list of JetModifier
18 - JetModifier : describes a JetModifier c++ tool.
19
20Author: TJ Khoo, P-A Delsart
21
22"""
23
24__all__ = [ "JetDefinition","xAODType", "JetModifier", "JetConstitModifier" , "JetInputConstitSeq", "JetInputExternal"]
25
26from AthenaCommon import Logging
27jetlog = Logging.logging.getLogger('JetDefinition')
28
29from xAODBase.xAODType import xAODType
30from .Utilities import make_lproperty, onlyAttributesAreProperties, clonable, make_alias
31
32from copy import deepcopy
33
34def formatRvalue(parameter):
35 """Define the convention that we write R truncating the decimal point
36 if R>=1, then we write R*10.
37 (Code from JetRecUtils )
38 """
39 # impose precision limits where there could be ambiguity
40 if int(10*parameter)>=1 and int(100*parameter % 10):
41 #jetlog.warning('Radius parameter {0} exceeds allowable precision of 0.1'.format(parameter))
42 raise ValueError('Bad radius parameter')
43 if int(parameter)>=1:
44 return "{0:.0f}".format(10*parameter)
45 else:
46 return "{0:.1g}".format(10*parameter).replace('.','')
47
48
49# Could also split off a VR name builder
50def buildJetAlgName(finder, mainParam,
51 variableRMassScale= -1.0, variableRMinRadius=-1.0):
52 """variableRMassScale (Rho) in MeV """
53 if ( variableRMassScale >= 0.0 and variableRMinRadius >= 0.0):
54 rmaxstr = formatRvalue(mainParam)
55 rminstr = formatRvalue(variableRMinRadius)
56 return f"{finder}VR{str(int(variableRMassScale/1000))}Rmax{rmaxstr}Rmin{rminstr}"
57 return finder + formatRvalue(mainParam)
58
59
60
61def _condAlwaysPass(condflags):
62 return True,""
63
64from AthenaCommon.SystemOfUnits import MeV
65
66@clonable
67@onlyAttributesAreProperties
69 _allowedattributes = ['_cflags','_contextDic'] # onlyAttributesAreProperties will add all properties to this list.
70 def __init__(self,
71 algorithm, # The fastjet clustering algorithm
72 radius, # The jet radius specifier (clustering cutoff)
73 inputdef, # The input JetConstit
74 ptmin=5e3*MeV, # The pt cutoff for fastjet in MeV
75 ghostdefs=[], # The list of alias to JetGhosts to ghost-associate
76 modifiers=[], # The list of alias to JetModifiers to execute after jet finding
77 extrainputs=[], # The list of additional input types needed for jet finding
78 standardRecoMode = False, #
79 prefix = "", # allows to tune the full JetContainer name
80 suffix = "", # allows to tune the full JetContainer name
81 infix = "", # allows to tune the full JetContainer name
82 context = "default", # describe a context for which this definition will be used. See StandardJetContext
83 VRMinR = -1.0, # Minimum radius for VR jet finding
84 VRMassSc = -1.0, # Mass scale for VR jet finding, in MeV
85 ghostarea = 0.01, # area resolution when evaluating jet area using "ghosts"
86 byVertex = False, # Reconstruct the jets by vertex
87 lock = False, # lock the properties of this instance to avoid accidental overwrite after __init__
88 ):
89
90 self._locked = False # unlock during init
91 # Should add some type checking here
92 # Could use JetContainerInfo conversion
93 if algorithm not in ["Kt","AntiKt","CamKt"]:
94 jetlog.error("FastJet algorithm specification was not one of Kt, AntiKt, CamKt!")
95 raise KeyError("Invalid fastjet algorithm choice: {0}".format(self.algorithm))
96 self._algorithm = algorithm
97
98 self._radius = radius
99 self._inputdef = inputdef
100 self._prefix = prefix
101 self._suffix = suffix
102 self._infix = infix
103 self._context = context
104 self._VRMinRadius = VRMinR
105 self._VRMassScale = VRMassSc
106 self._ghostarea = ghostarea
107 self._defineName()
108
109 self.ptmin = ptmin # The pt down to which FastJet is run
110
111 self.ghostdefs = ghostdefs # Objects to ghost-associate
112 self.modifiers = modifiers # Tools to modify the jet
113 self.extrainputs = extrainputs # Any extra input dependencies
114
115 self.standardRecoMode = standardRecoMode
116
117 # used internally to resolve dependencies
118 self._prereqDic = {}
119 self._prereqOrder = []
120 self._internalAtt = {}
121 self._cflags = None # pointer to AthenaConfiguration.ConfigFlags. Mainly to allow to invoke building of input dependencies which are outside Jet domain during std reco
122 self._contextDic = None # pointer to the context dictionnary. Convenient shortcut used to configure input or modifier dependencies
123 self.byVertex = byVertex
124 self._locked = lock
125
126
127 def __hash__(self):
128 return hash((self.basename,self._inputdef,self.ptmin,str(self.ghostdefs),str(self.modifiers),str(self.extrainputs),self.byVertex))
129
130 def __eq__(self,rhs):
131 return self.__hash__() == rhs.__hash__()
132
133 def __ne__(self,rhs):
134 return (not self.__eq__(rhs))
135
136 def lock(self):
137 if not self._locked:
138 self._locked = True
139
140 # After dependency solving, we hold a reference to the AthConfigFlags,
141 # which if unmodified is meant to function as a singleton throughout the
142 # configuration. A full deep copy of this is expensive, and slows down
143 # the HLT menu generation a lot due to copies in caches.
144 # So we explicitly avoid the deepcopy of the flags here, and further
145 # check that the flags are locked, to prevent accidental unlocking
146 def __deepcopy__(self, memo):
147 cls = self.__class__
148 result = cls.__new__(cls)
149 memo[id(self)] = result
150 set_without_deepcopy = ['_cflags']
151 for k, v in self.__dict__.items():
152 if k in set_without_deepcopy:
153 if v:
154 assert(v.locked())
155 setattr(result, k, v)
156 else:
157 setattr(result, k, deepcopy(v, memo))
158 return result
159
160 # Define core attributes as properties, with
161 # custom setter/getter such that if changed, these
162 # force resetting of the jet name
163 @make_lproperty
164 def algorithm(self): pass
165
166 @algorithm.lsetter
167 def algorithm(self,algorithm):
168 self._algorithm = algorithm
169 self._defineName()
170
171 @make_lproperty
172 def radius(self): pass
173
174 @radius.lsetter
175 def radius(self,radius):
176 self._radius = radius
177 self._defineName()
178
179 @make_lproperty
180 def inputdef(self): pass
181
182 @inputdef.lsetter
183 def inputdef(self,inputdef):
184 self._inputdef = inputdef
185 self._defineName()
186
187 @make_lproperty
188 def prefix(self): pass
189
190 @make_lproperty
191 def suffix(self): pass
192
193 @make_lproperty
194 def infix(self): pass
195
196 @make_lproperty
197 def basename(self): pass
198
199 @basename.lsetter
200 def basename(self,v):
201 raise Exception("Can NOT set property basename of JetDefinition ",self," Change prefix, infix or suffix instead.")
202
203
204 @make_lproperty
205 def ghostdefs(self): pass
206 @make_lproperty
207 def modifiers(self): pass
208 @make_lproperty
209 def extrainputs(self): pass
210 @make_lproperty
211 def standardRecoMode(self): pass
212
213 @make_lproperty
214 def VRMinRadius(self): pass
215 @make_lproperty
216 def VRMassScale(self): pass
217
218 @make_lproperty
219 def context(self): pass
220
221 @make_lproperty
222 def ghostarea(self): pass
223
224
225 def fullname(self):
226 return self.prefix+self.basename+self.infix+"Jets"+self.suffix
227
228 def _defineName(self):
229 self._basename = buildJetAlgName(self.algorithm,self.radius,self.VRMassScale,self.VRMinRadius)+self.inputdef.label # .label
230 if self.inputdef.basetype == xAODType.CaloCluster:
231 # Omit cluster origin correction from jet name
232 # Keep the origin correction explicit because sometimes we may not
233 # wish to apply it, whereas PFlow corrections are applied implicitly
234 self._basename = self.basename.replace("Origin","")
235 pass
236
237 # Define a string conversion for printing
238 def __str__(self):
239 return f"JetDefinition({self.fullname()})"
240 # Need to override __repr__ for printing in lists etc
241 __repr__ = __str__
242
243
244
245
246@clonable
247@onlyAttributesAreProperties
249 """Helper to define the config of a IJetModifier tool.
250 Tools that typically have more complex properties set should have
251 their own dedicated helper 'createfn' functions defined"""
252
253 def __init__(self,tooltype,toolname,
254 createfn=None,
255 filterfn=_condAlwaysPass,
256 prereqs=[],modspec=None,
257 **properties
258 ):
259 # For the easy cases where no helper function is needed.
260 # They will be ignored in the case of a helper,
261 # but are still required such that it's obvious what
262 # the tool name and type are when defining the config.
263 self.tooltype = tooltype
264 self.toolname = toolname
265
266 # The helper function may take 2 parameters:
267 # a "modifier specification" string and the jet
268 # definition
269
270 # The helper function always returns the desired
271 # modifier, and a ComponentAccumulator instance,
272 # in case additional supporting tools/services
273 # need to be set up.
274 if createfn is None:
276 else:
277 self.createfn = createfn
278 self.modspec = modspec
279
280 # Prereqs is normally a list.
281 # However, in special cases, the prereqs may
282 # depend on either or both of modspec and jetdef,
283 # in which case a helper function can be defined.
284 self.prereqs = prereqs
285
286 # a function taking a CondFlags as argument and deciding if this JetModifier is compatible
287 # with the conditions.
288 # The function must return a tuple : (bool, "reason of failure")
289 self.filterfn = filterfn
290
291 # These will be set as the Gaudi properties of the C++ tool
292 self.properties = properties
293
294
295
296
297 @make_lproperty
298 def tooltype(self):pass
299 @make_lproperty
300 def toolname(self):pass
301 @make_lproperty
302 def createfn(self):pass
303 @make_lproperty
304 def modspec(self):pass
305 @make_lproperty
306 def prereqs(self):pass
307 @make_lproperty
308 def filterfn(self):pass
309 @make_lproperty
310 def properties(self):pass
311
312
313
314 def __hash__(self):
315 return hash((self.toolname,self.tooltype,self.createfn.__name__,self.modspec,str(self.prereqs)))
316
317 def __eq__(self,rhs):
318 return self.__hash__() == rhs.__hash__()
319
320 def __ne__(self,rhs):
321 return (not self.__eq__(rhs))
322
323 # Define a string conversion for printing
324 def __str__(self):
325 return "JetModifier({0}/{1})".format(self.tooltype,self.toolname)
326 # Need to override __repr__ for printing in lists etc
327 __repr__ = __str__
328
329 def getGenericModifier(self,jetdef, modspec):
330 """returns a real tool instance accoding to this definition : simply instantiating from
331 class self.tooltype and with name self.toolname ( actually : self.toolname.format(modspec) )
332 Since this function will be called as a callback from JetRecConfig as 'func(jetdef, modspec)', it must accept
333 the jetdef argument, even if unused in this case.
334 """
335 from AthenaConfiguration.ComponentFactory import CompFactory
336 name = self.toolname.format(modspec=modspec)
337 tool = CompFactory.getComp(self.tooltype)(name)
338 return tool
339
340
341
342
343
344
345@clonable
346@onlyAttributesAreProperties
348 """This class allows to declare primary data sources to jet finding which are typically outside of jet domain.
349 Such sources can be container of particles (ex: clusters, selection of tracks,...) but also
350 other object needed by some JetModifier (ex: EventDensity or track-vertex association map).
351
352 The class is mainly here to hold a helper function (algoBuilder) in charge of configuring the proper algorithm to build the source.
353 If this function is None, then we expect the container pre-exists in the evt store.
354
355 Arguments to the constructor :
356 - name : container name in event store
357 - objtype : the xAODType (ex: xAODType.TruthParticle, xAODType.CaloCluster, ...)
358 - algoBuilder [optional] : a function returning a configured algorithm which build the container
359 the function is called as algoBuilder(parentjetdef, specs) where
360 parentjetdef is the JetDefinition for which this input building is called.
361 specs is self.specs
362 If omitted, it is assumed the container pre-exists in the event store.
363 - specs [optional] : a string (or anything) which specifies some options, and passed to the algoBuilder function
364 - filterfn : a function taking a CondFlags as argument and deciding if this JetModifier is compatible
365 with the conditions (same as JetModifier.filterfn )
366 The function must return a tuple : (bool, "reason of failure")
367 - prereqs : a list of prerequisites (str) for this input definition. If any, these str must match the name of other existing JetInputExternal instances.
368 """
369 def __init__(self, name, objtype, algoBuilder=None, specs=None, containername=None, filterfn= _condAlwaysPass, prereqs=[]):
370 self.name = name
371 self.basetype = objtype
372
373 self.algoBuilder = algoBuilder if algoBuilder is not None else buildNothing # buildNothing returns None (see below)
374
375 # In certain cases (EventShape) we need to configure the concrete
376 # output container name based on the jetdef and specs, so can
377 # pass in a (lambda) function to define this.
378 if containername:
379 self.containername = containername
380 else:
381 # Ordinarily we just return the name
382 self.containername = lambda dummyjetdef,dummyspecs : self.name
383
384 self.specs = specs
385 self.filterfn = filterfn
386 self.prereqs = prereqs
387
388 @make_lproperty
389 def name(self): pass
390 @make_lproperty
391 def algoBuilder(self): pass
392 @make_lproperty
393 def basetype(self): pass
394 @make_lproperty
395 def specs(self): pass
396 @make_lproperty
397 def filterfn(self):pass
398 @make_lproperty
399 def prereqs(self):pass
400
401 # make outputname an alias of name so JetInputExternal shares an interface with JetInputConstitSeq.
402 outputname = make_alias("name")
403
404 # Define a string conversion for printing
405 def __str__(self):
406 return f"JetInputExternal({self.name},type={str(self.basetype)})"
407
408 def __hash__(self):
409 return hash((
410 self.name,str(self.basetype),str(self.algoBuilder),
411 self.containername(None,None),str(self.prereqs),
412 str(self.filterfn),str(self.specs)))
413
414 # Need to override __repr__ for printing in lists etc
415 __repr__ = __str__
416
417 def __eq__(self,other):
418 return hash(self) == hash(other)
419
420 def __ne__(self,rhs):
421 return (not self.__eq__(rhs))
422
423
424
425
426
427from enum import IntEnum, auto
428class JetInputType(IntEnum):
429 """We reproduce the Enum from in xAODJet/​JetContainerInfo.h, xAOD::JetInput : loading the C++ library
430 can slow down a lot the configuration.
431 Note : this is different from the xAODType which describes *only* c++ types whereas JetInputType describes
432 categories of inputs to jets.
433 """
434 LCTopo=0
435 EMTopo=auto()
436 TopoTower=auto()
437 Tower=auto()
438 Truth=auto()
439 TruthWZ=auto()
440 Track=auto()
441 PFlow=auto()
442 LCPFlow=auto() # LC PFlow
443 EMPFlow=auto() # EM Pflow at EM scale
444 EMPFlowByVertex=auto() # EM Pflow by vertex
445 EMCPFlow=auto() # EM Pflow calibrated to LC scale
446 Jet=auto()
447 LCTopoOrigin=auto()
448 EMTopoOrigin=auto()
449 TrackCaloCluster=auto()
450 TruthDressedWZ=auto() # Truth jets without prompt e/mu (or dressed photons) or prompt gammas
451 EMTopoOriginSK=auto()
452 EMTopoOriginCS=auto()
453 EMTopoOriginVor=auto()
454 EMTopoOriginCSSK=auto()
455 EMTopoOriginVorSK=auto()
456 LCTopoOriginSK=auto()
457 LCTopoOriginCS=auto()
458 LCTopoOriginVor=auto()
459 LCTopoOriginCSSK=auto()
460 LCTopoOriginVorSK=auto()
461 EMPFlowSK=auto()
462 EMPFlowCS=auto()
463 EMPFlowVor=auto()
464 EMPFlowCSSK=auto()
465 EMPFlowVorSK=auto()
466 TruthCharged=auto() # Truth jets with only charged particles
467 EMTopoOriginTime=auto()
468 EMTopoOriginSKTime=auto()
469 EMTopoOriginCSSKTime=auto()
470 EMTopoOriginVorSKTime=auto()
471 EMPFlowTime=auto()
472 EMPFlowSKTime=auto()
473 EMPFlowCSSKTime=auto()
474 EMPFlowVorSKTime=auto()
475 HI=auto()
476 HIClusters=auto()
477 Other = 100
478 Uncategorized= 1000
479
481 """Returns a default JetInputType for a given xAODType """
482 _xaodTojetinputMap = {
483 xAODType.CaloCluster : JetInputType.LCTopo,
484 xAODType.ParticleFlow : JetInputType.EMPFlow,
485 xAODType.FlowElement : JetInputType.EMPFlow,
486 xAODType.TrackParticle : JetInputType.Track,
487 xAODType.TruthParticle : JetInputType.Truth,
488 xAODType.Jet : JetInputType.Jet,
489 }
490 return _xaodTojetinputMap.get(xt, JetInputType.Other)
491
492@clonable
493@onlyAttributesAreProperties
495 """Configuration for simplest constituents (or ghost constituents) to jets.
496 This describes what can be the input to a PseudoJetAlgorithm.
497 The containername attribute must correspond to an existing JetInputExternal so the system knows how to build this
498 source container (if necessary).
499 """
500
502 self,
503 name, # identifies this constit source, must be unique.
504 objtype, # The type of xAOD object from which to build the jets
505 containername, # The key of the source container in the event store.
506 prereqs=[], # will contain references to JetInputExternal
507 label=None, # used to describe a category for these constits. if None, will default to name
508 jetinputtype=None, # The JetInputType category. Can be passed as a string.
509 # if None, set according to objtype.
510 filterfn=_condAlwaysPass,
511 byVertex=False,
512 lock=False, # lock all properties of this instance
513 ):
514
515 self.name = name
516 self.containername = containername
517 self.prereqs = prereqs
518 self.label = label or name
519
520 self.basetype = objtype
521 self.filterfn = filterfn
522
523 jetinputtype = jetinputtype or JetInputType.fromxAODType(objtype)
524 if isinstance(jetinputtype, str):
525 jetinputtype = JetInputType[jetinputtype]
526 self.jetinputtype = jetinputtype
527 self.byVertex = byVertex
528 self._locked = lock
529
530 def __hash__(self):
531 return hash((self.name,self.containername,self.label,str(self.basetype),str(self.filterfn),str(self.jetinputtype),str(self.byVertex)))
532
533 def __eq__(self,rhs):
534 return self.__hash__() == rhs.__hash__()
535
536 def __ne__(self,rhs):
537 return (not self.__eq__(rhs))
538
539 @make_lproperty
540 def basetype(self): pass
541
542 @make_lproperty
543 def name(self): pass
544
545 @make_lproperty
546 def containername(self): pass
547
548 @make_lproperty
549 def prereqs(self): pass
550
551 @make_lproperty
552 def filterfn(self):pass
553
554 @make_lproperty
555 def jetinputtype(self): pass
556
557 # make an alias on containername so JetInputConstit and JetInputConstitSeq share an interface
558 inputname = make_alias("containername")
559
560 # Define a string conversion for printing
561 def __str__(self):
562 return f"JetInputConstit({self.name},type={str(self.basetype)})"
563 # Need to override __repr__ for printing in lists etc
564 __repr__ = __str__
565
566
567
568@clonable
569@onlyAttributesAreProperties
571 """Configuration for JetConstituentModSequence.
572 Describes the constituents which need to be build with a JetConstituentModSequence.
573 Uses a list of aliases to JetConstitModifier to describe the modif steps.
574 """
575 def __init__(self,
576 name,
577 objtype, # The type of xAOD object from which to build the jets
578 modifiers=[], # Modifications to be applied to constituents prior to jet finding
579 inputname=None, # input collection which will be transformed into the source constituents
580 outputname=None, # output collection, will be set to self.containername
581 prereqs = [], # will contain references to JetInputExternal
582 label = None,
583 jetinputtype=None,
584 filterfn=_condAlwaysPass,
585 byVertex=False,
586 lock = False, # lock all properties of this instance
587 ):
588
589 JetInputConstit.__init__(self,name, objtype, outputname, prereqs=prereqs, jetinputtype=jetinputtype, filterfn=filterfn,label=label,lock=False, finalinit=False, byVertex=byVertex)
590 self.inputname = inputname or name
591 self.modifiers = modifiers
592
593
594 self._locked = lock
595
596 @make_lproperty
597 def modifiers(self): pass
598
599 @make_lproperty
600 def inputname(self): pass
601 @make_lproperty
602 def label(self): pass
603
604
605
606 def __hash__(self):
607 return hash((self._basetype,str(self._modifiers)))
608
609 def __eq__(self,rhs):
610 return self.__hash__() == rhs.__hash__()
611
612 def __ne__(self,rhs):
613 return (not self.__eq__(rhs))
614
615
616 # Define a string conversion for printing
617 def __str__(self):
618 return f"JetInputConstitSeq({self.name}, {self.inputname} , {self.containername})"
619 # Need to override __repr__ for printing in lists etc
620 __repr__ = __str__
621
622
623
624@clonable
625@onlyAttributesAreProperties
627 """Configuration for a constituent modifier tool to be used in a JetConstituentModSequence.
628 See StandardJetConstits.py for usage of this class.
629
630 the properties argument in __init__ defines directly the properties of the final tool :
631 if the tool has the property "PtMin" then passing 'dict(PtMin=10*GeV)' will result in 'tool.PtMin = 10*GeV'
632 IMPORTANT : If a property is itself an other tool, we can pass a function returning the tool like in 'dict(TheSubTool = mySubToolFunc)'
633 The function will be called only when appropriate in the form 'tool.TheSubTool = mySubToolFunc(constitseq)'
634 """
635 def __init__(self,
636 name,
637 tooltype,
638 prereqs= [],
639 properties={},
640 ):
641 self.name = name
642 self.tooltype = tooltype
643 self.properties = properties
644 self.prereqs = prereqs
645 self.filterfn = _condAlwaysPass # we might want to make this a proper attribute in the future
646
647 @make_lproperty
648 def name(self): pass
649 @make_lproperty
650 def tooltype(self): pass
651 @make_lproperty
652 def properties(self): pass
653 @make_lproperty
654 def prereqs(self): pass
655
656
657
658
660 return None
__init__(self, name, tooltype, prereqs=[], properties={})
__init__(self, algorithm, radius, inputdef, ptmin=5e3 *MeV, ghostdefs=[], modifiers=[], extrainputs=[], standardRecoMode=False, prefix="", suffix="", infix="", context="default", VRMinR=-1.0, VRMassSc=-1.0, ghostarea=0.01, byVertex=False, lock=False)
__init__(self, name, objtype, modifiers=[], inputname=None, outputname=None, prereqs=[], label=None, jetinputtype=None, filterfn=_condAlwaysPass, byVertex=False, lock=False)
__init__(self, name, objtype, containername, prereqs=[], label=None, jetinputtype=None, filterfn=_condAlwaysPass, byVertex=False, lock=False)
__init__(self, name, objtype, algoBuilder=None, specs=None, containername=None, filterfn=_condAlwaysPass, prereqs=[])
getGenericModifier(self, jetdef, modspec)
__init__(self, tooltype, toolname, createfn=None, filterfn=_condAlwaysPass, prereqs=[], modspec=None, **properties)
std::string replace(std::string s, const std::string &s2, const std::string &s3)
Definition hcg.cxx:310
_condAlwaysPass(condflags)
buildJetAlgName(finder, mainParam, variableRMassScale=-1.0, variableRMinRadius=-1.0)