###############################################################################
# (c) Copyright 2022 CERN for the benefit of the LHCb and FCC Collaborations #
# #
# This software is distributed under the terms of the Apache License #
# version 2 (Apache-2.0), copied verbatim in the file "COPYING". #
# #
# In applying this licence, CERN does not waive the privileges and immunities #
# granted to it by virtue of its status as an Intergovernmental Organization #
# or submit itself to any jurisdiction. #
###############################################################################
__author__ = "Dominik Muller, Michal Mazurek, and Gloria Corti"
__email__ = "lhcb-simulation@cern.ch"
import os
# Configurables (do NOT use 'from Configurables' here)
from ExternalDetector.Configuration import ExternalDetectorEmbedder
from Gaudi.Configuration import log
from GaudiKernel.ConfigurableMeta import ConfigurableMeta
from Gaussino.Utilities import (
GaussinoConfigurable,
add_constructors_with_names,
get_set_configurable,
)
from ParallelGeometry.Configuration import ParallelGeometry
[docs]
class GaussinoGeometry(GaussinoConfigurable):
"""Configurable for the geometry in Gaussino.
**Main**
:var GeometryService: default: ``""``, name of the geometry service, if
not provided then some custom geometry must be provided or using the
external detector package
:vartype GeometryService: str, optional
:var SensDetMap: default: ``{}``, additional map of sensitive volumes
to volumes added on top of any geometry service
:vartype SensDetMap: dict, optional
:var ExtraGeoTools: default: ``[]``, additional list of tools related to
the geometry
:vartype ExtraGeoTools: list, optional
**Monitoring**
:var DetailedTimingOpts: default: ``{}``, activates and passed the property to
the detailed timing user action to measure the time spent by simulation
in each volume. The absolute minimum is to provide a property
called "Detectors" with a list of detector volumes (can be just a substring
of the whole path in Geant4)
:vartype DetailedTimingOpts: dict, optional
**Handling GDML files**
:var ExportGDML: default: ``{}``
:vartype ExportGDML: dict, optional
:var ImportGDML: default: ``[]``
:vartype ImportGDML: list, optional
**External Detector**
:var ExternalDetectorEmbedder: default: ``""``, name of the embedder used
when creating external geometry
:vartype ExternalDetectorEmbedder: str, optional
"""
__slots__ = {
# MAIN
"GeometryService": "",
"SensDetMap": {},
"ExtraGeoTools": [],
# MONITORING
"DetailedTimingOpts": {},
# HANDLING GDML FILES
"ExportGDML": {},
"ImportGDML": [],
# EXTERNAL DETECTOR
"ExternalDetectorEmbedder": "",
}
# internal options to be set by Gaussino
only_generation_phase = False
""" options set internally by Gaussino() """
[docs]
def __apply_configuration__(self):
"""Main configuration method for the geometry to be used in Gaussino.
It applies the properties of the geoemtry right after the simulation configurable
:class:`GaussinoSimulation <Gaussino.Simulation.GaussinoSimulation>`, generation
:class:`GaussinoGeneration <Gaussino.Generation.GaussinoGeneration>` and the main configurable:
:class:`Gaussino <Gaussino.Configuration.Gaussino>`.
"""
from Configurables import Gaussino
log.debug("Configuring GaussinoGeometry")
if "Simulation" not in Gaussino().getProp("Phases"):
log.debug("-> Only the generation phase, skipping.")
return
from Configurables import GiGaMT, GiGaMTDetectorConstructionFAC
giga = GiGaMT()
dettool = giga.addTool(
GiGaMTDetectorConstructionFAC(),
name="DetConst",
)
giga.DetectorConstruction = getattr(giga, "DetConst")
dettool.GiGaMTGeoSvc = self.getProp("GeometryService")
dettool.SensDetVolumeMap = self.getProp("SensDetMap")
extra_tools = self.getProp("ExtraGeoTools")
dettool.AfterGeoConstructionTools = extra_tools
add_constructors_with_names(dettool, extra_tools)
algs = []
# CUSTOM SIMULATION AND GEOEMTRY
algs += self._set_external_detector(dettool)
algs += self._set_parallel_geometry(dettool)
self._set_custom_simulation_regions(dettool)
# GDML
self._set_gdml_import(dettool)
self._set_gdml_export(dettool)
# MONITORING
self._set_detailed_timing()
from Configurables import ApplicationMgr
ApplicationMgr().TopAlg += algs
[docs]
def _set_external_detector(self, dettool: ConfigurableMeta) -> list:
"""Sets up the external detector package if requested.
See more info in a dedicated section below.
Args:
dettool (ConfigurableMeta): detector constructor
``GiGaMTDetectorConstructionFAC("DetConst")``
Returns:
list: list of algorithms
"""
# Add external detectors geometries
# TODO: external geometry was prepared to operate with spillover
# but it is not available yet
# so for now there are no 'slot' param in the algos
embedder_name = self.getProp("ExternalDetectorEmbedder")
if not embedder_name:
return []
log.debug(f"-> Configuring external detector: {embedder_name}")
algs = []
embedder = ExternalDetectorEmbedder(embedder_name)
embedder.embed(dettool)
algs += embedder.activate_hits_alg() # no slot for now!
algs += embedder.activate_moni_alg() # no slot for now!
return algs
[docs]
def _set_parallel_geometry(self, dettool: ConfigurableMeta) -> list:
"""Sets up the parallel geoemtry package if requested. It works only
with the external detector package. See more info in a dedicated section below.
Args:
dettool (ConfigurableMeta): detector constructor
``GiGaMTDetectorConstructionFAC("DetConst")``
Returns:
list: list of algorithms
"""
par_geo = ParallelGeometry()
if not par_geo.getProp("ParallelWorlds"):
return []
log.debug("-> Configuring geometry in parallel worlds")
return par_geo.attach(dettool)
[docs]
def _set_custom_simulation_regions(self, dettool: ConfigurableMeta):
"""Sets up the fast simulation interface that will assign models to
the regions.
Args:
dettool (ConfigurableMeta): detector constructor
``GiGaMTDetectorConstructionFAC("DetConst")``
"""
from Configurables import GaussinoSimulation
cust_sim_creator_name = GaussinoSimulation().getProp("CustomSimulation")
if cust_sim_creator_name:
from Configurables import CustomSimulation
CustomSimulation(cust_sim_creator_name).create(dettool)
[docs]
def _set_gdml_export(self, dettool: ConfigurableMeta):
"""Sets up the properties needed to export the geometry to a GDML file.
See more info in a dedicated section below.
Args:
dettool (ConfigurableMeta): detector constructor
``GiGaMTDetectorConstructionFAC("DetConst")``
Raises:
RuntimeError: if ``ExportGDML`` not a dictionary
RuntimeError: if options of ``ExportGDML`` do not start with `GDML*`
"""
gdml_export = self.getProp("ExportGDML")
if type(gdml_export) is not dict:
raise RuntimeError("ExportGDML should be a dictionary of options")
for name, value in gdml_export.items():
if name.startswith("GDML"):
setattr(dettool, name, value)
else:
raise RuntimeError("GDML options start with GDML")
[docs]
def _set_gdml_import(self, dettool):
"""Sets up the properties needed to import geometry from a GDML file.
The geometry can be added to the geometry created with a geometry service.
See more info in a dedicated section below.
Args:
dettool (ConfigurableMeta): detector constructor
``GiGaMTDetectorConstructionFAC("DetConst")``
Raises:
RuntimeError: if ``ImportGDML`` not a dictionary
RuntimeError: if values of ``ImportGDML`` are not dictionaries either
"""
gdml_imports = self.getProp("ImportGDML")
if type(gdml_imports) is not list:
raise RuntimeError("ImportGDML should be a list of dicts")
from Configurables import GDMLReader
for gdml_import in gdml_imports:
if type(gdml_import) is not dict:
raise RuntimeError("Elements of ImportGDML should be dicts")
name = gdml_import["GDMLFileName"] + "Reader"
reader = GDMLReader(name, **gdml_import)
dettool.addTool(reader, name=name)
dettool.GDMLReaders.append("GDMLReader/" + name)
[docs]
def _set_detailed_timing(self):
"""Sets up the detailed timing feature if requested.
It will compute the time spent by Geant4 in the provided volume names.
The output is in CSV files, as well as in the standard output.
If no DetectorPatterns are provided, then the timing is measured for
all Geant4 volumes.
Raises:
NotADirectoryError: if the output directory does not exist
:Example:
.. highlight:: python
.. code-block:: python
from Configurables import GaussinoGeometry
GaussinoGeometry().DetailedTimingOpts = {
"DetectorPatterns": {
"ExampleTracker": [
"ExampleTrackerG4Path1",
"ExampleTrackerG4Path2",
],
"ExampleCalorimeter": ["ExampleCalorimeterG4Path"],
},
"OutputCSVDir": "./",
}
"""
props = self.getProp("DetailedTimingOpts")
if not props:
return
log.debug("-> Configuring detailed timing")
csv_dir = props.setdefault("OutputCSVDir", "./")
if not os.path.isdir(csv_dir):
raise NotADirectoryError(f"OutputCSVDir {csv_dir} is not a directory!")
from Configurables import GiGaMT, Gsino__DetailedTimingActionFactory
actioninit = get_set_configurable(GiGaMT(), "ActionInitializer")
factory = Gsino__DetailedTimingActionFactory(**props)
actioninit.addTool(factory, name="DetailedTiming")
actioninit.SteppingActions.append(getattr(actioninit, "DetailedTiming"))