From 8339b76d269517254f17e3242728e1a83e1effc5 Mon Sep 17 00:00:00 2001 From: Massinissa Date: Fri, 20 Jun 2025 17:13:05 +0200 Subject: [PATCH 01/10] Update engines.py for optical properties in voxelized volumes Modified engines.py so optical properties can be applied to voxelized volumes. It loops on all materials created in the simulations and set the optical properties only if the material is in "voxel_materials" --- opengate/engines.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/opengate/engines.py b/opengate/engines.py index 41b2970c4..9917fc361 100644 --- a/opengate/engines.py +++ b/opengate/engines.py @@ -415,6 +415,25 @@ def initialize_optical_material_properties(self): for ( vol ) in self.simulation_engine.simulation.volume_manager.volumes.values(): + if hasattr(vol,"voxel_materials"): #True if voxelized volume (ImageVolume) + for i in np.array(vol.voxel_materials)[:,2]: #Read all materials contained in voxel_materials + for mat in g4.G4Material.GetMaterialTable: #Loop on all materials created in the simulation + if str(mat.GetName())==i: #If material is in voxel_materials + mat_prop = load_optical_properties_from_xml( + self.physics_manager.optical_properties_file, mat.GetName() + ) + if mat_prop is not None: + self.g4_optical_material_tables[str(mat.GetName())] = ( + create_g4_optical_properties_table(mat_prop) + ) + mat.SetMaterialPropertiesTable( + self.g4_optical_material_tables[str(mat.GetName())] + ) + else: + self.simulation_engine.simulation.warn_user( + f"Could not load the optical material properties for material {mat.GetName()} " + f"found in volume {vol.name} from file {self.physics_manager.optical_properties_file}." + ) material_name = vol.g4_material.GetName() material_properties = load_optical_properties_from_xml( self.physics_manager.optical_properties_file, material_name From 26c07c02ecb38a3f1de465eb22e83c53587ba398 Mon Sep 17 00:00:00 2001 From: Massinissa Abbas Date: Fri, 20 Jun 2025 18:12:34 +0200 Subject: [PATCH 02/10] Update engines.py for optical properties in ImageVolumes Modified engines.py so optical photons can be transported in voxelized volumes (ImageVolumes). Before that, optical properties were only applied to the default material and not those defined in "voxel_materials", preventing the transport of optical photons in ImageVolumes. --- opengate/engines.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/opengate/engines.py b/opengate/engines.py index 9917fc361..caa436f05 100644 --- a/opengate/engines.py +++ b/opengate/engines.py @@ -415,24 +415,24 @@ def initialize_optical_material_properties(self): for ( vol ) in self.simulation_engine.simulation.volume_manager.volumes.values(): - if hasattr(vol,"voxel_materials"): #True if voxelized volume (ImageVolume) - for i in np.array(vol.voxel_materials)[:,2]: #Read all materials contained in voxel_materials - for mat in g4.G4Material.GetMaterialTable: #Loop on all materials created in the simulation - if str(mat.GetName())==i: #If material is in voxel_materials - mat_prop = load_optical_properties_from_xml( + if hasattr(vol,"voxel_materials"): #True if voxelized volume (ImageVolume) + for i in np.array(vol.voxel_materials)[:,2]: #Read all materials contained in voxel_materials + for mat in g4.G4Material.GetMaterialTable: #Loop on all materials created in the simulation + if str(mat.GetName())==i: #If material is in voxel_materials + mat_prop = load_optical_properties_from_xml( self.physics_manager.optical_properties_file, mat.GetName() - ) - if mat_prop is not None: - self.g4_optical_material_tables[str(mat.GetName())] = ( - create_g4_optical_properties_table(mat_prop) ) - mat.SetMaterialPropertiesTable( + if mat_prop is not None: + self.g4_optical_material_tables[str(mat.GetName())] = ( + create_g4_optical_properties_table(mat_prop) + ) + mat.SetMaterialPropertiesTable( self.g4_optical_material_tables[str(mat.GetName())] - ) - else: + ) + else: self.simulation_engine.simulation.warn_user( - f"Could not load the optical material properties for material {mat.GetName()} " - f"found in volume {vol.name} from file {self.physics_manager.optical_properties_file}." + f"Could not load the optical material properties for material {mat.GetName()} " + f"found in volume {vol.name} from file {self.physics_manager.optical_properties_file}." ) material_name = vol.g4_material.GetName() material_properties = load_optical_properties_from_xml( From b9033eb3715cd908f1d3cd0f9f456a674cce1cf3 Mon Sep 17 00:00:00 2001 From: Massinissa Abbas Date: Fri, 20 Jun 2025 18:16:57 +0200 Subject: [PATCH 03/10] Update engines.py --- opengate/engines.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/opengate/engines.py b/opengate/engines.py index caa436f05..899154114 100644 --- a/opengate/engines.py +++ b/opengate/engines.py @@ -420,20 +420,20 @@ def initialize_optical_material_properties(self): for mat in g4.G4Material.GetMaterialTable: #Loop on all materials created in the simulation if str(mat.GetName())==i: #If material is in voxel_materials mat_prop = load_optical_properties_from_xml( - self.physics_manager.optical_properties_file, mat.GetName() + self.physics_manager.optical_properties_file, mat.GetName() ) if mat_prop is not None: self.g4_optical_material_tables[str(mat.GetName())] = ( - create_g4_optical_properties_table(mat_prop) + create_g4_optical_properties_table(mat_prop) ) mat.SetMaterialPropertiesTable( - self.g4_optical_material_tables[str(mat.GetName())] + self.g4_optical_material_tables[str(mat.GetName())] ) else: - self.simulation_engine.simulation.warn_user( - f"Could not load the optical material properties for material {mat.GetName()} " - f"found in volume {vol.name} from file {self.physics_manager.optical_properties_file}." - ) + self.simulation_engine.simulation.warn_user( + f"Could not load the optical material properties for material {mat.GetName()} " + f"found in volume {vol.name} from file {self.physics_manager.optical_properties_file}." + ) material_name = vol.g4_material.GetName() material_properties = load_optical_properties_from_xml( self.physics_manager.optical_properties_file, material_name From bc3d444e51990d1991f18f23e0e3a9122aea33fa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 16:36:39 +0000 Subject: [PATCH 04/10] [pre-commit.ci] Automatic python and c++ formatting --- opengate/engines.py | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/opengate/engines.py b/opengate/engines.py index 899154114..a73e918e6 100644 --- a/opengate/engines.py +++ b/opengate/engines.py @@ -415,25 +415,38 @@ def initialize_optical_material_properties(self): for ( vol ) in self.simulation_engine.simulation.volume_manager.volumes.values(): - if hasattr(vol,"voxel_materials"): #True if voxelized volume (ImageVolume) - for i in np.array(vol.voxel_materials)[:,2]: #Read all materials contained in voxel_materials - for mat in g4.G4Material.GetMaterialTable: #Loop on all materials created in the simulation - if str(mat.GetName())==i: #If material is in voxel_materials - mat_prop = load_optical_properties_from_xml( - self.physics_manager.optical_properties_file, mat.GetName() + if hasattr( + vol, "voxel_materials" + ): # True if voxelized volume (ImageVolume) + for i in np.array(vol.voxel_materials)[ + :, 2 + ]: # Read all materials contained in voxel_materials + for ( + mat + ) in ( + g4.G4Material.GetMaterialTable + ): # Loop on all materials created in the simulation + if ( + str(mat.GetName()) == i + ): # If material is in voxel_materials + mat_prop = load_optical_properties_from_xml( + self.physics_manager.optical_properties_file, + mat.GetName(), ) if mat_prop is not None: - self.g4_optical_material_tables[str(mat.GetName())] = ( - create_g4_optical_properties_table(mat_prop) - ) + self.g4_optical_material_tables[ + str(mat.GetName()) + ] = create_g4_optical_properties_table(mat_prop) mat.SetMaterialPropertiesTable( - self.g4_optical_material_tables[str(mat.GetName())] + self.g4_optical_material_tables[ + str(mat.GetName()) + ] ) else: self.simulation_engine.simulation.warn_user( f"Could not load the optical material properties for material {mat.GetName()} " f"found in volume {vol.name} from file {self.physics_manager.optical_properties_file}." - ) + ) material_name = vol.g4_material.GetName() material_properties = load_optical_properties_from_xml( self.physics_manager.optical_properties_file, material_name From c6d8b982f5f68ffa5e6ec6fadd925917d79076fa Mon Sep 17 00:00:00 2001 From: Thomas BAUDIER Date: Wed, 30 Apr 2025 13:42:32 +0200 Subject: [PATCH 05/10] Add test 092 for optic material issue --- opengate/tests/data | 2 +- opengate/tests/src/test092_optic_material.py | 104 +++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) create mode 100755 opengate/tests/src/test092_optic_material.py diff --git a/opengate/tests/data b/opengate/tests/data index 0200198b2..be81811c9 160000 --- a/opengate/tests/data +++ b/opengate/tests/data @@ -1 +1 @@ -Subproject commit 0200198b2022f89cff342493a09dff73e5669d6b +Subproject commit be81811c9514bfcbff5014987b587f96ca6774e1 diff --git a/opengate/tests/src/test092_optic_material.py b/opengate/tests/src/test092_optic_material.py new file mode 100755 index 000000000..c8ad59a7e --- /dev/null +++ b/opengate/tests/src/test092_optic_material.py @@ -0,0 +1,104 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +Created on Wed Apr 2 09:48:30 2025 + +@author: fava +""" +import opengate as gate +from opengate.utility import g4_units +import opengate.tests.utility as utility +import opengate_core as g4 +import uproot +import numpy as np +from scipy.spatial.transform import Rotation +import SimpleITK as sitk + + +if __name__ == "__main__": + + paths = utility.get_default_test_paths(__file__, output_folder="test092") + + # Define the Simulation object + sim = gate.Simulation() + + # Add a material database + sim.volume_manager.add_material_database(paths.data / "GateMaterials.db") + sim.physics_manager.special_physics_constructors.G4OpticalPhysics = True + paths.test_data = paths.data / "test092" + sim.physics_manager.optical_properties_file = paths.test_data / "Materials.xml" + + # Define the units used in the simulation set-up + m = gate.g4_units.m + cm = gate.g4_units.cm + keV = gate.g4_units.keV + eV = gate.g4_units.eV + Bq = gate.g4_units.Bq + mm = gate.g4_units.mm + sec = gate.g4_units.second + gcm3 = gate.g4_units.g_cm3 + deg = g4_units.deg + + sim.visu = False + sim.visu_type = "vrml" + + world = sim.world + world.size = [3 * m, 3 * m, 3 * m] + world.material = "Air" + + img = sitk.ReadImage( + paths.test_data / "vox_volume.mhd" + ) # Extraction des caractéristiques de l'images - chemin de l'image test (voxels de 1mm) + Spacing = img.GetSpacing() # Taille de voxel en mm + Size = np.array(img.GetSize()) # Taille de l'image en nombre de voxels + Size2 = np.array( + [Size[i] * Spacing[i] for i in range(len(Size))] + ) # Taille de l'image en mm + Center = Size2 / 2 # Pour placer la source + + patient = sim.add_volume("Image", name="patient") + patient.image = paths.test_data / "vox_volume.mhd" # image CT + patient.material = "Air" + patient.dump_label_image = ( + paths.test_data / "labels_vox_volume.mhd" + ) # Vérifier que tout correspond à du muscle + patient.translation = [0, 0, 0] + patient.voxel_materials = [[-2, 2, "Muscle"]] # Valeurs des voxels == 1 + # patient.voxel_materials = [[-500, -49, "Fat"],[-49, 150, "Muscle"]] + + ##################################################################### + + # dose = sim.add_actor("DoseActor", "dose") + # dose.output_filename = "../output/imageVolume_photon_unique.mhd" + # dose.attached_to = patient.name + # dose.size = [Size[0],Size[1],Size[2]] # Même nombre de voxels que l'image + # mm = gate.g4_units.mm + # dose.spacing = [ + # Spacing[i] * mm for i in range(len(Spacing)) + # ] # Même taille de voxel que l'image + # dose.translation = [0, 0, 0] + # dose.edep_uncertainty.active = False + # dose.hit_type = "random" + + ################################################ SOURCE DE PHOTONS OPTIQUES + source = sim.add_source("GenericSource", "mysource") + source.particle = "opticalphoton" + source.energy.type = "mono" + source.energy.mono = 1.913 * eV + # source.position.type = "box" # plane source + source.position.type = "point" + # source.position.size = [5.5 * cm, 2.6 * cm, 0] + source.direction.type = "momentum" + source.direction.momentum = [0, 0, 1] + source.position.translation = [ + 0, + 0, + -Center[2] * mm, + ] # La source est à l'interface entre l'image et l'extérieur + source.n = 1 / sim.number_of_threads + + stats = sim.add_actor("SimulationStatisticsActor", "Stats") + stats.track_types_flag = True + + sim.run() + print(stats) From 2db7eaf680af4324a67ee53728db9fdde606b9fe Mon Sep 17 00:00:00 2001 From: Massinissa Abbas Date: Thu, 10 Jul 2025 14:52:21 +0200 Subject: [PATCH 06/10] Update engines.py --- opengate/engines.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opengate/engines.py b/opengate/engines.py index a73e918e6..6f5b3678c 100644 --- a/opengate/engines.py +++ b/opengate/engines.py @@ -5,6 +5,7 @@ import weakref from box import Box from anytree import PreOrderIter +import numpy as np import opengate_core as g4 from .exception import fatal, warning, GateImplementationError From 2cdfba8d38e5caf4ffff771b15283aa51fef6aed Mon Sep 17 00:00:00 2001 From: David Sarrut Date: Tue, 15 Jul 2025 10:48:51 +0200 Subject: [PATCH 07/10] Update engines.py test, adding the default material --- opengate/engines.py | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/opengate/engines.py b/opengate/engines.py index 6f5b3678c..0535092f1 100644 --- a/opengate/engines.py +++ b/opengate/engines.py @@ -408,28 +408,20 @@ def initialize_physics_biasing(self): def initialize_optical_material_properties(self): # Load optical material properties if special physics constructor "G4OpticalPhysics" # is set to True in PhysicsManager's user info - if ( - self.simulation_engine.simulation.physics_manager.special_physics_constructors.G4OpticalPhysics - is True - ): + if self.simulation_engine.simulation.physics_manager.special_physics_constructors.G4OpticalPhysics is True: # retrieve path to file from physics manager - for ( - vol - ) in self.simulation_engine.simulation.volume_manager.volumes.values(): - if hasattr( - vol, "voxel_materials" - ): # True if voxelized volume (ImageVolume) - for i in np.array(vol.voxel_materials)[ - :, 2 - ]: # Read all materials contained in voxel_materials - for ( - mat - ) in ( - g4.G4Material.GetMaterialTable - ): # Loop on all materials created in the simulation - if ( - str(mat.GetName()) == i - ): # If material is in voxel_materials + for vol in self.simulation_engine.simulation.volume_manager.volumes.values(): + if hasattr(vol, "voxel_materials"): + # True if voxelized volume (ImageVolume) + materials = [m[2] for m in vol.voxel_materials] + materials.append(vol.material) + print(materials) + for i in materials: + # Read all materials contained in voxel_materials + for mat in g4.G4Material.GetMaterialTable: + # Loop on all materials created in the simulation + if str(mat.GetName()) == i: + # If material is in voxel_materials mat_prop = load_optical_properties_from_xml( self.physics_manager.optical_properties_file, mat.GetName(), From 65b3cbb1e5d0dd7800aac4932df46279dd412334 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 08:49:14 +0000 Subject: [PATCH 08/10] [pre-commit.ci] Automatic python and c++ formatting --- opengate/engines.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/opengate/engines.py b/opengate/engines.py index 0535092f1..ef6e02ff2 100644 --- a/opengate/engines.py +++ b/opengate/engines.py @@ -408,15 +408,20 @@ def initialize_physics_biasing(self): def initialize_optical_material_properties(self): # Load optical material properties if special physics constructor "G4OpticalPhysics" # is set to True in PhysicsManager's user info - if self.simulation_engine.simulation.physics_manager.special_physics_constructors.G4OpticalPhysics is True: + if ( + self.simulation_engine.simulation.physics_manager.special_physics_constructors.G4OpticalPhysics + is True + ): # retrieve path to file from physics manager - for vol in self.simulation_engine.simulation.volume_manager.volumes.values(): + for ( + vol + ) in self.simulation_engine.simulation.volume_manager.volumes.values(): if hasattr(vol, "voxel_materials"): # True if voxelized volume (ImageVolume) materials = [m[2] for m in vol.voxel_materials] materials.append(vol.material) print(materials) - for i in materials: + for i in materials: # Read all materials contained in voxel_materials for mat in g4.G4Material.GetMaterialTable: # Loop on all materials created in the simulation From 071d6b2113df82edc4c9ef31e4cfd37df9bfbbe8 Mon Sep 17 00:00:00 2001 From: Thomas BAUDIER Date: Tue, 15 Jul 2025 13:54:40 +0200 Subject: [PATCH 09/10] Update tests --- opengate/tests/src/test092_optic_material.py | 47 ++++++++++---------- 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/opengate/tests/src/test092_optic_material.py b/opengate/tests/src/test092_optic_material.py index c8ad59a7e..2f1e7eb43 100755 --- a/opengate/tests/src/test092_optic_material.py +++ b/opengate/tests/src/test092_optic_material.py @@ -12,7 +12,7 @@ import uproot import numpy as np from scipy.spatial.transform import Rotation -import SimpleITK as sitk +import itk if __name__ == "__main__": @@ -40,47 +40,46 @@ deg = g4_units.deg sim.visu = False - sim.visu_type = "vrml" + sim.visu_type = "qt" + sim.number_of_threads = 1 + sim.random_seed = 123456789 world = sim.world world.size = [3 * m, 3 * m, 3 * m] world.material = "Air" + #world.material = "G4_Galactic" + #world.material = "G4_AIR" - img = sitk.ReadImage( + img = itk.imread( paths.test_data / "vox_volume.mhd" - ) # Extraction des caractéristiques de l'images - chemin de l'image test (voxels de 1mm) - Spacing = img.GetSpacing() # Taille de voxel en mm - Size = np.array(img.GetSize()) # Taille de l'image en nombre de voxels - Size2 = np.array( - [Size[i] * Spacing[i] for i in range(len(Size))] - ) # Taille de l'image en mm - Center = Size2 / 2 # Pour placer la source + ) + spacing = img.GetSpacing() + size = np.array(img.GetLargestPossibleRegion().GetSize()) + center = size*spacing / 2 patient = sim.add_volume("Image", name="patient") - patient.image = paths.test_data / "vox_volume.mhd" # image CT - patient.material = "Air" + patient.image = paths.test_data / "vox_volume.mhd" # CT image + #patient.material = "Air" + patient.material = "G4_AIR" patient.dump_label_image = ( paths.test_data / "labels_vox_volume.mhd" - ) # Vérifier que tout correspond à du muscle + ) patient.translation = [0, 0, 0] - patient.voxel_materials = [[-2, 2, "Muscle"]] # Valeurs des voxels == 1 + patient.voxel_materials = [[-2, 2, "Muscle"]] # patient.voxel_materials = [[-500, -49, "Fat"],[-49, 150, "Muscle"]] ##################################################################### # dose = sim.add_actor("DoseActor", "dose") - # dose.output_filename = "../output/imageVolume_photon_unique.mhd" + # dose.output_filename = paths.output / "imageVolume_photon_unique.mhd" # dose.attached_to = patient.name - # dose.size = [Size[0],Size[1],Size[2]] # Même nombre de voxels que l'image - # mm = gate.g4_units.mm - # dose.spacing = [ - # Spacing[i] * mm for i in range(len(Spacing)) - # ] # Même taille de voxel que l'image + # dose.size = size + # dose.spacing = spacing # dose.translation = [0, 0, 0] # dose.edep_uncertainty.active = False # dose.hit_type = "random" - ################################################ SOURCE DE PHOTONS OPTIQUES + ################################################ Optical photon source source = sim.add_source("GenericSource", "mysource") source.particle = "opticalphoton" source.energy.type = "mono" @@ -93,9 +92,9 @@ source.position.translation = [ 0, 0, - -Center[2] * mm, - ] # La source est à l'interface entre l'image et l'extérieur - source.n = 1 / sim.number_of_threads + -center[2] * mm, + ] #The source is at the border between the image and the exterior + source.n = 100000 / sim.number_of_threads stats = sim.add_actor("SimulationStatisticsActor", "Stats") stats.track_types_flag = True From 765dc13fc5cf2677742ceea7898c45f37057d3de Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 11:55:06 +0000 Subject: [PATCH 10/10] [pre-commit.ci] Automatic python and c++ formatting --- opengate/tests/src/test092_optic_material.py | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/opengate/tests/src/test092_optic_material.py b/opengate/tests/src/test092_optic_material.py index 2f1e7eb43..c4679ee0c 100755 --- a/opengate/tests/src/test092_optic_material.py +++ b/opengate/tests/src/test092_optic_material.py @@ -47,23 +47,19 @@ world = sim.world world.size = [3 * m, 3 * m, 3 * m] world.material = "Air" - #world.material = "G4_Galactic" - #world.material = "G4_AIR" + # world.material = "G4_Galactic" + # world.material = "G4_AIR" - img = itk.imread( - paths.test_data / "vox_volume.mhd" - ) + img = itk.imread(paths.test_data / "vox_volume.mhd") spacing = img.GetSpacing() size = np.array(img.GetLargestPossibleRegion().GetSize()) - center = size*spacing / 2 + center = size * spacing / 2 patient = sim.add_volume("Image", name="patient") patient.image = paths.test_data / "vox_volume.mhd" # CT image - #patient.material = "Air" + # patient.material = "Air" patient.material = "G4_AIR" - patient.dump_label_image = ( - paths.test_data / "labels_vox_volume.mhd" - ) + patient.dump_label_image = paths.test_data / "labels_vox_volume.mhd" patient.translation = [0, 0, 0] patient.voxel_materials = [[-2, 2, "Muscle"]] # patient.voxel_materials = [[-500, -49, "Fat"],[-49, 150, "Muscle"]] @@ -93,7 +89,7 @@ 0, 0, -center[2] * mm, - ] #The source is at the border between the image and the exterior + ] # The source is at the border between the image and the exterior source.n = 100000 / sim.number_of_threads stats = sim.add_actor("SimulationStatisticsActor", "Stats")