Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions core/opengate_core/g4_bindings/pyG4GDMLParser.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include <pybind11/pybind11.h>

#ifdef USE_GDML

#include "G4GDMLParser.hh"

namespace py = pybind11;

void init_G4GDMLParser(py::module &m) {
py::class_<G4GDMLParser>(m, "G4GDMLParser")
.def(py::init<>())

.def(
"Read",
[](G4GDMLParser &parser, const G4String &filename, G4bool validate) {
parser.Read(filename, validate);
},
py::arg("filename"), py::arg("validate") = true)

.def("GetWorldVolume", &G4GDMLParser::GetWorldVolume,
py::arg("setup_name") = "Default",
py::return_value_policy::reference_internal)

.def("SetStripFlag", &G4GDMLParser::SetStripFlag, py::arg("strip"))

.def("SetOverlapCheck", &G4GDMLParser::SetOverlapCheck, py::arg("check"));
}

#endif
7 changes: 7 additions & 0 deletions core/opengate_core/opengate_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -566,8 +566,15 @@ void init_GateVolumeVoxelizer(py::module &);

void init_GateImageBox(py::module &m);

#ifdef USE_GDML
void init_G4GDMLParser(py::module &);
#endif

PYBIND11_MODULE(opengate_core, m) {

#ifdef USE_GDML
init_G4GDMLParser(m);
#endif
init_G4ThreeVector(m);
init_G4AffineTransform(m);
init_G4String(m);
Expand Down
59 changes: 59 additions & 0 deletions docs/source/user_guide/user_guide_reference_volumes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,65 @@ Reference
.. autoclass:: opengate.geometry.volumes.TubsVolume


GDML volumes
------------

Description
~~~~~~~~~~~

A GDML volume imports a complete geometry described with the Geant4
Geometry Description Markup Language. The logical volume associated with
the selected GDML setup is placed as a subtree inside the specified GATE
mother volume. The regular GATE world volume is therefore preserved.

Solids, logical volumes, placements, and materials contained in the GDML
file are constructed directly by Geant4. The ``material`` parameter of
the GATE volume is ignored because the materials are read from the GDML
file.

GDML support requires ``opengate_core`` and Geant4 to have been compiled
with GDML support.

A basic example is:

.. code:: python

import opengate as gate

sim = gate.Simulation()

gdml = sim.add_volume("GDML", name="ImportedGeometry")
gdml.file_name = "geometry.gdml"
gdml.mother = "world"
gdml.setup_name = "Default"
gdml.validate = False
gdml.strip_names = False
gdml.parser_overlap_check = False

The standard volume parameters ``translation`` and ``rotation`` control
the placement of the imported GDML root inside its GATE mother volume.

The GDML-specific parameters are:

- ``file_name``: path to the GDML input file.
- ``setup_name``: GDML setup to import, ``"Default"`` by default.
- ``validate``: enable XML schema validation while reading the file.
- ``strip_names``: strip Geant4 pointer suffixes from imported names.
- ``parser_overlap_check``: enable overlap checking during GDML parsing.

Actors attached to the GDML volume are propagated to its imported logical
volume subtree in the same way as for native GATE volumes.

See
`test113_gdml_volume <https://github.com/OpenGATE/opengate/blob/master/opengate/tests/src/geometry/test113_gdml_volume.py>`_
for an example simulation.

Reference
~~~~~~~~~

.. autoclass:: opengate.geometry.volumes.GDMLVolume


Tesselated (mesh) volumes
-------------------------

Expand Down
156 changes: 156 additions & 0 deletions opengate/geometry/volumes.py
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,161 @@ class TesselatedVolume(RepeatableVolume, solids.TesselatedSolid):
"""


class GDMLVolume(VolumeBase):
"""
Volume importing a complete GDML geometry as a subtree.

The logical volume associated with the GDML world is placed inside
the OpenGATE mother volume. The OpenGATE world is therefore preserved.
"""

user_info_defaults = {
"file_name": (
"",
{
"doc": "Path to the GDML input file.",
"is_input_file": True,
},
),
"setup_name": (
"Default",
{
"doc": "Name of the GDML setup whose world volume is imported.",
},
),
"validate": (
False,
{
"doc": "Enable XML schema validation while reading the GDML file.",
"type": bool,
},
),
"strip_names": (
False,
{
"doc": "Strip Geant4 pointer suffixes from imported GDML names.",
"type": bool,
},
),
"parser_overlap_check": (
False,
{
"doc": "Enable overlap checking while the GDML parser creates placements.",
"type": bool,
},
),
"material": (
None,
{
"doc": "Ignored. Materials are read directly from the GDML file.",
"override": True,
},
),
}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

# Keep the parser alive for as long as the imported geometry is used.
self.g4_gdml_parser = None
self.g4_imported_world_physical_volume = None
self.g4_solid = None

def __getstate__(self):
return_dict = super().__getstate__()
return_dict["g4_gdml_parser"] = None
return_dict["g4_imported_world_physical_volume"] = None
return_dict["g4_solid"] = None
return return_dict

def release_g4_references(self):
super().release_g4_references()
self.g4_imported_world_physical_volume = None
self.g4_gdml_parser = None
self.g4_solid = None

def construct(self):
if self._is_constructed:
return

if not hasattr(g4, "G4GDMLParser"):
fatal(
"GDML support is unavailable in opengate_core. "
"Geant4 and opengate_core must be compiled with GDML support."
)

if self.mother is None:
fatal(
f"GDMLVolume '{self.name}' cannot be used as the OpenGATE world. "
"Assign it to an existing OpenGATE mother volume."
)

gdml_file_name = ensure_filename_is_str(self.file_name)

if not gdml_file_name:
fatal(
f"No GDML file was provided for GDMLVolume '{self.name}'. "
"Set its 'file_name' property."
)

if not os.path.isfile(gdml_file_name):
fatal(
f"GDML file '{gdml_file_name}' does not exist "
f"for GDMLVolume '{self.name}'."
)

parser = g4.G4GDMLParser()
parser.SetStripFlag(self.strip_names)
parser.SetOverlapCheck(self.parser_overlap_check)
parser.Read(gdml_file_name, self.validate)

imported_world = parser.GetWorldVolume(self.setup_name)

if imported_world is None:
fatal(
f"Unable to retrieve GDML setup '{self.setup_name}' "
f"from file '{gdml_file_name}'."
)

imported_logical_volume = imported_world.GetLogicalVolume()

if imported_logical_volume is None:
fatal(
f"The world physical volume from GDML setup "
f"'{self.setup_name}' has no logical volume."
)

self.g4_gdml_parser = parser
self.g4_imported_world_physical_volume = imported_world
self.g4_logical_volume = imported_logical_volume
self.g4_solid = imported_logical_volume.GetSolid()
self.g4_material = imported_logical_volume.GetMaterial()

if self.build_physical_volume:
self.construct_physical_volume()

self._is_constructed = True

def _make_physical_volume(self, volume_name, g4_transform, copy_index=0):
mother_volume = self.mother_volume

if mother_volume is None or mother_volume.g4_logical_volume is None:
fatal(
f"Unable to retrieve the constructed mother logical volume "
f"for GDMLVolume '{self.name}'."
)

return g4.G4PVPlacement(
g4_transform,
self.g4_logical_volume,
volume_name,
mother_volume.g4_logical_volume,
False,
copy_index,
self.volume_manager.simulation.check_volumes_overlap,
)


class RepeatParametrisedVolume(VolumeBase):
"""
Volume created from another volume via translations.
Expand Down Expand Up @@ -1440,4 +1595,5 @@ def __getstate__(self):
process_cls(TubsVolume)
process_cls(RepeatParametrisedVolume)
process_cls(ImageVolume)
process_cls(GDMLVolume)
process_cls(TesselatedVolume)
2 changes: 2 additions & 0 deletions opengate/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@
BoxVolume,
ConsVolume,
EllipsoidVolume,
GDMLVolume,
HexagonVolume,
ImageVolume,
ParallelWorldVolume,
Expand Down Expand Up @@ -1387,6 +1388,7 @@ class VolumeManager(GateObject):
"TrdVolume": TrdVolume,
"BooleanVolume": BooleanVolume,
"RepeatParametrisedVolume": RepeatParametrisedVolume,
"GDMLVolume": GDMLVolume,
"TesselatedVolume": TesselatedVolume,
}

Expand Down
Loading
Loading