Source code for CustomSimulation.Configuration

###############################################################################
# (c) Copyright 2021 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.                                       #
###############################################################################
from Gaudi.Configuration import ConfigurableUser
from Gaudi.Configuration import log
import Configurables

__author__ = "Michal Mazurek"
__email__ = "michal.mazurek@cern.ch"


[docs]class CustomSimulation(ConfigurableUser): """This class provides all the necessary tools that are needed to create a custom simulation model in a specific region and attach the corrsponding custom simulation physics. Custom simulation model is chosen via the (``Model``) property (don't forget to specify the ``Type`` i.e. the factory name). The model will be then specified in a region created based on the properties in ``Region`` property. Finally, each custom simulation model needs a dedicated physics factory where you specify what kind of particles will be tracked in that model. :var Model: Properties of the custom simulation model used. :vartype Model: dict, required :var Region: Properties of the G4Region where the custom simulation will take place. :vartype Region: dict, required :var Physics: Properties of the physics factory used for all the custom simulation models in that creator. :vartype Physics: dict, required :Example: .. highlight:: python .. code-block:: python from Gaussino.Simulation import GaussinoSimulation GaussinoSimulation().CustomSimulation = "MyCustomSimulationCreator" from Configurables import CustomSimulation custom = CustomSimulation("MyCustomSimulationCreator") customsim.Model = { 'MyCustomSimModel': { 'Type': 'ImmediateDepositModel', } } custsim.Region = { 'VacuumCubeImmediateDeposit': { 'SensitiveDetectorName': 'VacuumCubeSDet', } } custsim.Physics = { 'ParticlePIDs': [22], } """ __slots__ = { "Model": {}, # # ex. ImmediateDepositModel # # "MyImmediateDepositModel": { # -> type of the factory model # "Type": "ImmediateDepositModel", # -> name of the model (optional, done automatically) # "Name": "MySuperModel", # -> name of the region (optional, found automatically) # "RegionName": "MySuperRegion", # + other properties used by the factory # e.g. OutputLevel etc. # }, "Region": {}, # # ex. # # "MyImmediateDepositModel": { # -> type of the region factory (optional, default value) # "Type": "CustomSimulationRegionFactory", # -> name (optional, done automatically) # "Name": "MySuperRegion", # -> name of the sensitive detector # "SensitiveDetectorName": "DetectorSDet", # -> list of the logical volumes # (optional, if SensitiveDetectorName is not specified) # "Volumes": ["VolumeA", "VolumeB"], # + other properties used by the factory # e.g. OutputLevel etc. # }, "Physics": {}, # # -> type of the physics factory (optional, default value) # "Type": "DefaultCustomPhysics", # -> list of PIDs of particles to custom-simulate # "ParticlePIDs": [22, 11], # -> list of the parallel worlds correspoinding to the particles # (optional, done automatically, empty string for the mass geo) # "ParticleWorlds": ["ParallelWorld1", "ParallelWorld1"], # + other properties used by the factory # e.g. OutputLevel etc. # }
[docs] def create(self, dettool): """Takes care of setting up the right tools and factories responsible for setting up the custom simulation model and it's region. It is based on the properties provided in ``Model`` and ``Region``. Properties correspond to the properites used by each factory. :param dettool: Tool responsible for detector construction in Geant4. In Gaussino, it is ``GiGaMTDetectorConstructionFAC``. """ if not dettool: raise RuntimeError("Detector Construction tool not provided") models_props = self.getProp("Model") if type(models_props) is not dict: raise RuntimeError("Model property must be a dict") regions_props = self.getProp("Region") if type(regions_props) is not dict: raise RuntimeError("Region property must be a dict") for name, props in models_props.items(): self._check_props(name, props) if "Name" not in props: props["Name"] = name # setting up the region region_props = regions_props.get(name) self._check_props( name + "Region", region_props, required=[] ) # there are required, but have to be handled a bit differently if not region_props.get("SensitiveDetectorName") and not region_props.get( "Volumes" ): raise RuntimeError( "Property 'SensitiveDetectorName' or 'Volumes' must be provided for " + name ) if not region_props.get("Type"): log.info("Assigning default region factory to " + name) region_props["Type"] = "CustomSimulationRegionFactory" if not region_props.get("Name"): region_props["Name"] = name + "Region" region_conf = getattr(Configurables, region_props["Type"]) region_tool = region_conf( region_props["Name"], **self._refine_props(region_props) ) dettool.addTool(region_tool, name=region_props["Name"]) dettool.CustomSimulationRegionFactories.append( getattr(dettool, region_props["Name"]) ) log.info( "Registered a custom simulation region tool {} of type {}.".format( region_props["Name"], region_props["Type"] ) ) # setting up the model if not props.get("RegionName"): props["RegionName"] = region_props["Name"] model_name = props["Name"] + "Model" model_conf = getattr(Configurables, props["Type"]) model_tool = model_conf(model_name, **self._refine_props(props)) dettool.addTool(model_tool, name=model_name) dettool.CustomSimulationModelFactories.append(getattr(dettool, model_name)) log.info( "Registered a custom simulation model tool {} of type {}.".format( model_name, props["Type"] ) )
[docs] def attach_physics(self, modular_list, world_name=""): """Takes care of setting up the right tools and factories responsible for creating the physics factory for all the custom simulation models. All the properties should be provided in the ``Physics`` property. :param modular_list: Modular physics list tool, should be ``GiGaMTModularPhysListFAC`` :param world_name: world where the physics will take action, empty means the mass world """ phys_props = self.getProp("Physics") name = world_name + "CustomPhysics" self._check_props(name, phys_props, required=["ParticlePIDs"]) factype = phys_props.get("Type") if not factype: log.info( "No factory type specified for {}. Using default physics world factory".format( name ) ) factype = "DefaultCustomPhysics" if factype == "DefaultCustomPhysics": if not phys_props.get("ParticleWorlds"): # if no parallel worlds fill the list with empty strings phys_props["ParticleWorlds"] = [world_name] * len( phys_props["ParticlePIDs"] ) fac_conf = getattr(Configurables, factype) fsph = fac_conf(name, **self._refine_props(phys_props)) modular_list.addTool(fsph) modular_list.PhysicsConstructors.append(getattr(modular_list, name))
[docs] def _check_props(self, name, props, required=["Type"]): if type(props) is not dict: raise RuntimeError( "ERROR: Dictionary of {} properties not provided.".format(name) ) for req in required: if not props.get(req): raise RuntimeError( "ERROR: Property {} for {} not provided.".format(req, name) )
[docs] def _refine_props(self, props, keys_to_refine=["Type"]): return {key: prop for key, prop in props.items() if key not in keys_to_refine}
[docs] def _register_prop(self, props, key, prop): if not props.get(key): props[key] = prop