Source code for Gaussino.Geometry

###############################################################################
# (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"))