3 from __future__
import print_function
6 Collection of utilities to interface gallery with python.
8 This module requires ROOT.
10 An example of a interactive session counting the number of muons
11 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
14 sampleEvents = galleryUtils.makeEvent("data.root")
15 LArG4tag = ROOT.art.InputTag("largeant")
18 = galleryUtils.make_getValidHandle("std::vector<simb::MCParticle>", sampleEvents)
19 for event in galleryUtils.forEach(sampleEvents):
20 particles = getParticleHandle(LArG4tag).product()
22 nMuons = sum(1 for part in particles if abs(part.PdgCode()) == 13)
23 print("%s: %d muons" % (event.eventAuxiliary().id(), nMuons))
26 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
34 'make_getValidHandle',
41 'startMessageFacility',
42 'ServiceRegistryClass',
43 'ConfigurationHelper',
47 from ROOTutils
import ROOT, expandFileList
55 SourceCode = cppUtils.SourceCode
56 readHeader = cppUtils.readHeader
64 ROOT.art.EventID.__str__ \
65 =
lambda self:
"R:%d S:%d E:%d" % (self.run(), self.subRun(), self.event())
69 """Make the ROOT C++ jit compiler instantiate the
70 Event::getValidHandle member template for template parameter klass.
72 Needs to keep track of what was done already, because Cling will protest if
73 the same class is asked twice.
75 See the documentation of `__call__` for examples of usage of instances of
82 self.
getHandles = ROOT.gallery.Event.getManyByType[klass]
91 klass:
"data product class to prepare for (as a C++ class name string)",
92 event:
"(optional) the event object to retrieve the data products from " =
None,
93 ) ->
"if `event` is specified, bound `getValidHandle<klass>`, unbound otherwise":
96 getHandle = ROOT.gallery.Event.getValidHandle[klass]
97 return (
lambda tag: getHandle(event, tag))
if event
else getHandle
101 klass:
"data product class to prepare for (as a C++ class name string)",
102 event:
"(optional) the event object to retrieve the data products from " =
None,
103 ) ->
"""if `event` is specified, a list (std::vector) of handles,
104 otherwise a callable that when called on an event returns such list""":
106 return getManyByType(event)
if event
else (
lambda event: getManyByType(event))
110 klass:
"data product class to prepare for (as a C++ class name string)",
111 event:
"(optional) the event object to retrieve the data products from " =
None,
112 ) ->
"if `event` is specified, bound `getValidHandle<klass>`, unbound otherwise":
113 """Prepares for reading data products of the specified class.
115 This function causes the instantiation of the `gallery::Event::getValidHandle()`
116 method specialized to read the specified `klass`.
117 It also returns a bound or unbound function to actually retrieve data products.
119 If an `event` instance is specified, the returned value is a bound function
120 that can be used directly.
122 from galleryUtils import makeEvent, make_getValidHandle
125 event = makeEvent("test.root")
126 inputTag = ROOT.art.InputTag("largeant")
128 getParticlesFrom = make_getValidHandle("std::vector<simb::MCParticle>")
129 # note: the following is curretly mostly broken (see below)
130 particles1 = getParticlesFrom(event, inputTag).product()
132 getParticles = make_getValidHandle("std::vector<simb::MCParticle>", event)
133 particles2 = getParticles(inputTag).product()
135 Both `particles1` and `particles2` point to the same data product.
137 Exception (`RuntimeError`) is raised on error.
147 *filePaths:
"a list of input files"
148 ) ->
"a list of files suitable to construct a `gallery.Event object":
149 """Creates a file list suitable for `gallery::Event`.
151 If a file ends with `.root`, it is added directly to the list.
152 Otherwise, it is interpreted as a file list and treated as such
153 (see `ROOTutils.expandFileList()`).
154 File list recursion is disabled.
156 files = ROOT.vector(ROOT.string)()
157 for path
in filePaths:
158 entries = [ path ]
if path.endswith(
'.root')
else expandFileList(path)
159 for entry
in entries: files.push_back(entry)
166 files:
"files or file lists, as supported by `ROOTutils.makeFileList()`",
167 **kwargs:
"additional arguments to `gallery::Event` constructor"
168 ) ->
"a gallery.Event object reading the specified input files":
169 return ROOT.gallery.Event(
makeFileList(files), **kwargs)
175 Simplifies sequential event looping.
177 This function handles the state of `event`, moving the current event around.
178 Therefore, it has side effects.
179 The function returns `event` itself, after it has set to the next available
182 This function is actually a generator.
186 for iEvent, event in enumerate(forEach(event)):
191 while (
not event.atEnd()):
200 Iterator for an event sequence.
202 It can be used directly as:
204 for event in EventIterator(event): ...
206 or it can be plugged in directly in the gallery.Event class, making the latter
209 for event in event: ...
211 (or `for evt in event: ...`).
220 if self.
_index is not None:
223 if self._event.atEnd():
raise StopIteration
235 Applies the `process` function to each and every event from the specified
236 input files, in sequence.
238 The `inputFiles` list may be a single file, or a list (or any iterable object)
239 of files, or a `std::vector<std::string>` object.
241 The `process` callable is executed with two arguments: the number of argument
242 in the loop, and the event itself. No information on which file the event is
243 taken from is provided. If a call returns exactly `False`, it is considered
244 to have failed and an error counter is incremented. Exceptions raised in
245 `process` are not handled.
247 The error counter is returned at the end of the execution.
250 - 'nEvents': number of events to be processed (does not include skipped ones)
251 - 'nSkip': number of events from the beginning of the sample to be skipped
255 nSkip = options.get(
'nSkip', 0)
256 nEvents = options.get(
'nEvents',
None)
259 if not isinstance(inputFiles, ROOT.vector(ROOT.string)):
260 if isinstance(inputFiles, str): inputFiles = [ inputFiles, ]
264 event = ROOT.gallery.Event(inputFiles)
275 if iFile != event.fileEntry():
276 iFile = event.fileEntry()
277 print(
"Opening: '%s'" % inputFiles[iFile])
281 if iEvent < nSkip:
continue
282 if (nEvents
is not None)
and (nProcessedEvents >= nEvents):
break
283 nProcessedEvents += 1
288 res = process(event, iEvent)
289 if isinstance(res, bool)
and not res: nErrors += 1
297 print(
"Encountered %d/%d errors." % (nErrors, nProcessedEvents),file=sys.stderr)
313 if os.path.isfile(configRelPath):
314 return os.path.join(os.getcwd(), str(configRelPath))
315 for path
in extraDirs + os.environ.get(
'FHICL_FILE_PATH',
"").split(
':'):
316 candidate = os.path.join(path, str(configRelPath))
317 if os.path.isfile(candidate):
return candidate
324 pset:
"fhicl.ParameterSet object containing the desired configuration table",
325 key:
"name of the table to be extracted",
326 defValue:
"value returned if the key is not present (None by default)" =
None,
327 type_:
"type to look for (instead of ROOT.fhicl.ParameterSet)" =
None,
328 ) ->
"ParameterSet with `key` in `pset`, or `defValue` if not present":
330 try:
return pset.get(type_
if type_
else ROOT.fhicl.ParameterSet)(key)
331 except Exception:
return defValue
339 Provides more user-friendly interface for the configuration access.
341 While FHiCL C++ interface is friendly enough, some of that is lost in Python
344 This helper class keeps a reference to the FHiCL configuration object, and
345 provides some `get()` overloads to make the interface easier.
349 def get(self, key, default=None, klass=None):
351 Returns the value associated with the specified key.
357 the key of the configuration parameter to be read
359 the value to provide if no key is present; if default is `None`,
360 no default is present and if key is missing a `KeyError` exception will
363 type of object returned (if `None`, it's taken from `default` if
369 An object of type `klass`, initialised with the value associated to `key`
375 `KeyError` if the `key` is not present in the configuration and `default` is
380 assert default
is not None
381 klass = default.__class__
383 try:
return self.config.get[klass](key)
384 except Exception:
pass
386 if default
is None:
raise KeyError(key)
387 return klass(default)
392 Returns whether there is a value associated with the specified key.
398 the key of the configuration parameter to be read
401 return self.config.has(klass)
406 def pset(self):
return self.config
414 SourceCode.loadHeaderFromUPS(
"larcorealg/Geometry/StandaloneBasicSetup.h")
416 if isinstance(configSpec, ConfigurationString):
418 configFile = tempfile.NamedTemporaryFile(
"w+")
419 configFile.write(str(configSpec))
421 configPath = configFile.name
424 configPath = configSpec
429 raise RuntimeError(
"Couldn't find configuration file '%s'" % configPath)
430 return ROOT.lar.standalone.ParseConfiguration(fullPath)
435 """Wrapper to a string that should be considered configuration text."""
445 return self.config.get[ROOT.fhicl.ParameterSet](FHiCLpath)
447 return self.
paramsFor(
"services." + serviceName)
449 return self.
paramsFor(
"physics.producers." + moduleName)
460 def config(self, serviceName):
return self.fullConfig.service(serviceName)
462 def has(self, serviceName):
return serviceName
in self.services
465 self.
services[serviceName] = service
472 def get(self, serviceName):
return self.
services[serviceName]
475 def create(self, serviceName, serviceClass, *otherServiceConstructorArgs):
476 serviceConfig = self.
config(serviceName)
477 if not serviceConfig:
478 raise RuntimeError(
"Couldn't find the configuration for service '%s'" % serviceName)
479 service = serviceClass(serviceConfig, *otherServiceConstructorArgs)
481 raise RuntimeError(
"Failed to create service '%s' (type: %s)" % (serviceName, serviceClass.__class__.__name__))
482 return self.
register(serviceName, service)
490 """Use it as a function: `startMessageFacility(config, applName)`.
492 `config` either is `ConfigurationClass` object, or it is a parameter set
493 with the configuration for the service.
497 if not startMessageFacility.Init: self.
init(config, applName)
498 def init(self, config, applName):
499 if not applName: applName = os.path.basename(sys.argv[0])
500 if isinstance(config, ConfigurationClass): config = config.service(
"message")
501 print(
"Starting message facility for %s..." % applName)
502 ROOT.mf.StartMessageFacility(config, applName)
503 startMessageFacility.Init =
True
def findFHiCL
Infrastructure.
do one_file $F done echo for F in find $TOP name CMakeLists txt print
def registeredServiceNames
auto enumerate(Iterables &&...iterables)
Range-for loop helper tracking the number of iteration.