###############################################################################
# (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 ExternalDetectorEmbedder(ConfigurableUser):
"""This class sets up the provides all the necessary tools that are needed
to embed volumes (``Shapes``) and make them sensitive (``Sensitive``).
Moreover, it is possible to attach hit extraction (``Hit``) and monitoring
algorithms (``Moni``). It can also serve as a geometry service that can
work on its own by providing the ``World`` property. Finally, it gives a
possibility to create custom materials through ``Materials`` property.
:var Shapes: Properties of the volumes used.
:vartype Shapes: dict, optional
:var Sensitive: Properties of the sensitive detectors associated with
the previosuly defined volumes.
:vartype Sensitive: dict, optional
:var Hit: Properties of the hit exteraction algorithms associated
with the previosuly defined sensitive detectors.
:vartype Hit: dict, optional
:var Moni: Properties of the monitoring algorithms associated
with the previosuly defined sensitive detectors.
:vartype Moni: dict, optional
:var World: Properties of the world volume. Set this on if you
want ``ExternalDetector`` pacage to work in a standalone mode
:vartype World: dict, optional
:var Materials: Properties of the materials used by the world or
other volumes created by the ``ExternalDetector``.
:vartype Materials: dict, optional
:Example:
.. highlight:: python
.. code-block:: python
from Gaussino.Simulation import SimPhase
SimPhase().ExternalDetectorEmbedder = "MyEmbedder"
from Configurables import ExternalDetectorEmbedder
external = ExternalDetectorEmbedder("MyEmbedder")
external.Shapes = {
"MyPlane": {
# -> type of the embedder
"Type": "Cuboid",
# -> x position of its center
"xPos": 0 * m,
# -> y position of its center
"yPos": 0 * m,
# -> z position of its center
"zPos": 0 * m,
# -> length of the x side
"xSize": 1. * m,
# -> length of the y side
"ySize": 1. * m,
# -> length of the z side
"zSize": 1. * m,
# + other properties used by the CuboidEmbedder
e.g. OutputLevel etc.
},
}
external.Sensitive = {
# -> important! you have to specify what volume
"MyPlane": {
# -> required, type of the sensitive detector factory
"Type": "MCCollectorSensDet",
# + other properties used by the GiGaMTG4SensDetFactory
e.g. OutputLevel etc.
},
}
external.Hit = {
# -> important! you have to specify what sensitive volume
"MyPlane": {
# -> required, type of the hit extraction algorithm
"Type": "GetMCCollectorHitsAlg",
# + other properties used by the Gaudi::Functional
# algorithm e.g. OutputLevel etc.
},
}
external.Moni = {
# -> important! you have to specify what sensitive volume
"MyPlane": {
# -> required, type of the monitoring algorithm
"Type": "SomeMonitoringAlgorithm",
# + other properties used by the Gaudi::Functional
# algorithm e.g. OutputLevel etc.
},
}
"""
__slots__ = {
'Shapes': {},
#
# ex. Cuboid
#
# "MyPlane": {
# -> type of the embedder
# "Type": "Cuboid",
# -> x position of its center
# "xPos": 0 * m,
# -> y position of its center
# "yPos": 0 * m,
# -> z position of its center
# "zPos": 0 * m,
# -> length of the x side
# "xSize": 1. * m,
# -> length of the y side
# "ySize": 1. * m,
# -> length of the z side
# "zSize": 1. * m,
# + other properties used by the CuboidEmbedder
# e.g. OutputLevel etc.
# },
#
#
#
'Sensitive': {},
#
# ex. MCCollectorSensDet
#
# -> important! you have to specify what volume
# "MyPlane": {
# -> required, type of the sensitive detector factory
# "Type": "MCCollectorSensDet",
# + other properties used by the GiGaMTG4SensDetFactory
# e.g. OutputLevel etc.
# },
#
#
#
'Hit': {},
#
# ex. GetMCCollectorHitsAlg
#
# -> important! you have to specify what volume
# "MyPlane": {
# -> required, type of the hit extraction algorithm
# "Type": "GetMCCollectorHitsAlg",
# + other properties used by the Gaudi::Functional
# algorithm e.g. OutputLevel etc.
# },
#
#
#
'Moni': {},
#
# ex. SomeMonitoringAlgorithm
#
# -> important! you have to specify what volume
# "MyPlane": {
# -> required, type of the hit extraction algorithm
# "Type": "SomeMonitoringAlgorithm",
# + other properties used by the Gaudi::Functional
# algorithm e.g. OutputLevel etc.
# },
#
#
#
'Materials': {},
#
# ex. 1
#
# 'Pb': {
# 'Type': 'MaterialFromElements',
# -> type of material factory
# 'Symbols': ['Pb'],
# -> required, list of the elements' symbols
# 'AtomicNumbers': [82.],
# -> required, list of atomic numbers per element
# 'MassNumbers': [207.2 * g / mole],
# -> required, list of mass numbers per element
# 'MassFractions': [1.],
# -> required, list of mass fractions in 0. - 1., total <= 1.
# 'Density': 11.29 * g / cm3,
# -> required, density of the material [g / cm3]
# 'State': 'Solid',
# -> optional, state: ['Gas', 'Liquid', 'Solid'],
# default: 'Undefined'
# + other properties used by the MaterialFactory
# e.g. OutputLevel etc.
# },
#
# ex. 2
#
# "OuterSpace": {
# "Type": "MaterialFromChemicalPropertiesFactory"
# -> type of material factory
# "AtomicNumber": 1.,
# -> required, atomic number
# "MassNumber": 1.01 * g / mole,
# -> required, mass number [g / mole]
# "Density": 1.e-25 * g / cm3,
# -> required, density of the material [g / cm3]
# "Pressure": 3.e-18 * pascal,
# -> optional, pressure [Pa], default: 1 atm
# "Temperature": 2.73 * kelvin,
# -> optional, state: ['Gas', 'Liquid', 'Solid'],
# default: 'Undefined'
# + other properties used by the MaterialFactory
# e.g. OutputLevel etc.
# },
#
#
#
# only if you want to implement a custom, external world
# this is mostly for testing purposes
'World': {},
#
# ex.
#
# 'World': {
# "Type": "ExternalWorldCreator",
# -> type of the factory used to build the world
# -> see: Sim/ExternalDetector/WorldFactory.h
# if you need to create a custom one
# "WorldMaterial": "SomeG4Material",
# -> material used by the world, it is the name of the G4Material
# -> that must be defined before
# -> see: Sim/ExternalDetector/MaterialFactory.h
# + other properties used by the WorldFactory
# e.g. OutputLevel etc.
# }
}
_added_dets = []
_added_hits_algs = []
[docs] def embed(self, geo):
""" Takes care of setting up the right tools and factories responsible
for the geometry. It is based on the properties provided in ``Shapes``,
``Sensitive``, ``Materials``, and ``World``. Properties correspond to
the properites used by each factory.
:param geo: Tool responsible for detector construction in Geant4.
In Gaussino, it is ``GiGaMTDetectorConstructionFAC``.
"""
if not geo:
raise RuntimeError("ERROR: GeoService not provided")
for name, props in self.getProp("Materials").items():
self._check_props(name, props, required=[])
if 'Type' not in props:
props['Type'] = 'MaterialFromChemicalProperties'
if 'Name' not in props:
props['Name'] = name
tool_conf = getattr(Configurables, props['Type'])
tool = tool_conf(props['Name'], **self._refine_props(props))
geo.addTool(tool, name=props['Name'])
geo.ExternalMaterials.append(props['Type'] + '/' + props['Name'])
log.info("Registered external material tool {} of type {}.".format(
props['Name'], props['Type']))
for name, props in self.getProp("Shapes").items():
self._check_props(name, props)
tool_name = props['Type'] + 'Embedder'
self._embedding_tool(name, geo, tool_name, props)
geo.ExternalDetectors.append(tool_name + '/' + name)
log.info("Registered external detector {} of type {}.".format(
name, tool_name))
self._added_dets.append(name)
world = self.getProp('World')
if world:
self._check_props(
'World', world, required=['Type', 'WorldMaterial'])
svc_conf = getattr(Configurables, world['Type'])
svc_conf(**self._refine_props(world))
geo.GiGaMTGeoSvc = world['Type']
log.info("Registered external world service of type {}.".format(
world['Type']))
[docs] def activate_hits_alg(self, slot=""):
""" Takes care of setting up the right hit extraction algorithms.
It is based on the properties provided in ``Hit``, but the volume must
be created before as mentioned by ``Shapes``. Properties correspond to
the properites used by each hit extraction factory.
:param slot: additional naming for spill-over, not working for now
"""
algs = []
hit_algs = self.getProp('Hit')
if type(hit_algs) is dict:
for det_name, hit_alg_props in hit_algs.items():
if det_name not in self._added_dets:
log.warning("External geometry not set for " + det_name)
continue
self._check_props(det_name, hit_alg_props)
alg_conf = getattr(Configurables, hit_alg_props['Type'])
hit_alg_name = 'Get' + det_name + 'Hits' + slot
alg = alg_conf(
hit_alg_name,
MCHitsLocation='MC/' + det_name + '/Hits',
CollectionName=det_name + 'SDet/Hits',
**self._refine_props(hit_alg_props))
log.info("Registered external hit extraction " + hit_alg_name)
self._added_hits_algs.append(det_name)
algs.append(alg)
return algs
[docs] def activate_moni_alg(self, slot=""):
""" Takes care of setting up the right monitoring algorithms.
It is based on the properties provided in ``Moni``, but the volume must
be created before as mentioned by ``Shapes``. Properties correspond to
the properites used by each hit extraction factory.
:param slot: additional naming for spill-over, not working for now
"""
algs = []
moni_algs = self.getProp('Moni')
if type(moni_algs) is dict:
for det_name, moni_alg_props in moni_algs.items():
if det_name not in self._added_hits_algs:
log.warning("External hit algorithm not set for " +
det_name)
continue
self._check_props(
det_name,
moni_alg_props,
required=['Type', 'HitsPropertyName'])
moni_alg_props[moni_alg_props[
'HitsPropertyName']] = 'MC/' + det_name + '/Hits'
alg_conf = getattr(Configurables, moni_alg_props['Type'])
moni_alg_name = det_name + moni_alg_props['Type'] + slot
alg = alg_conf(
moni_alg_name,
**self._refine_props(
moni_alg_props,
keys_to_refine=['Type', 'HitsPropertyName']))
log.info("Registered external monitoring " + moni_alg_name)
algs.append(alg)
return algs
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))
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
}
def _register_prop(self, props, key, prop):
if not props.get(key):
props[key] = prop
def _embedding_tool(self, name, geo, tool_name, props):
log.info("Registering external {} as {}".format(name, tool_name))
tool_conf = getattr(Configurables, tool_name)
sens_det_props = self.getProp('Sensitive').get(name)
self._register_prop(props, 'LogicalVolumeName', name + "LVol")
self._register_prop(props, 'PhysicalVolumeName', name + "PVol")
if sens_det_props:
self._check_props(name, sens_det_props)
sens_det_conf = getattr(Configurables, sens_det_props['Type'])
self._register_prop(sens_det_props, 'SensDetName', name + 'SDet')
self._register_prop(
props, 'SensDet',
sens_det_props['Type'] + '/' + sens_det_props['SensDetName'])
sens_det_tool = sens_det_conf(
sens_det_props['SensDetName'],
**self._refine_props(sens_det_props, ['Type', 'SensDetName']))
tool = tool_conf(name, **self._refine_props(props))
if sens_det_tool:
tool.addTool(sens_det_tool)
geo.addTool(tool, name=name)