From 472315217d7e43cc3c0d13bb781e03e4b8f830b3 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 13 Jun 2024 07:23:35 +0200 Subject: [PATCH 01/14] generic source dir relative to volume for iso + angle --- core/CMakeLists.txt | 2 +- .../opengate_lib/GateGenericSource.cpp | 16 ++++++---- .../opengate_lib/GateSPSAngDistribution.cpp | 19 ++++++++++++ .../opengate_lib/GateSPSAngDistribution.h | 30 +++++++++++++++++++ .../opengate_lib/GateSingleParticleSource.cpp | 6 ++-- .../opengate_lib/GateSingleParticleSource.h | 9 +++--- opengate/tests/src/test067_stl_volume.py | 0 7 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 core/opengate_core/opengate_lib/GateSPSAngDistribution.cpp create mode 100644 core/opengate_core/opengate_lib/GateSPSAngDistribution.h mode change 100644 => 100755 opengate/tests/src/test067_stl_volume.py diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 9625df51e..bd30553d6 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -24,7 +24,7 @@ if (Geant4_multithreaded_FOUND) ENDIF () find_package(Threads REQUIRED) -#GDML +# GDML IF (Geant4_gdml_FOUND) message(STATUS "OPENGATE - Geant4 is compiled with GDML") add_definitions(-DUSE_GDML=1) diff --git a/core/opengate_core/opengate_lib/GateGenericSource.cpp b/core/opengate_core/opengate_lib/GateGenericSource.cpp index 3491c00ff..230db67c9 100644 --- a/core/opengate_core/opengate_lib/GateGenericSource.cpp +++ b/core/opengate_core/opengate_lib/GateGenericSource.cpp @@ -30,6 +30,8 @@ GateGenericSource::GateGenericSource() : GateVSource() { fEffectiveEventTime = -1; fEffectiveEventTime = -1; fDirectionRelativeToAttachedVolume = false; + fUserParticleLifeTime = -1; + fBackToBackMode = false; } GateGenericSource::~GateGenericSource() { @@ -90,7 +92,6 @@ void GateGenericSource::InitializeUserInfo(py::dict &user_info) { fCurrentSkippedEvents = 0; fTotalSkippedEvents = 0; fEffectiveEventTime = -1; - fDirectionRelativeToAttachedVolume = DictGetBool(user_info, "direction_relative_to_attached_volume"); } @@ -162,14 +163,13 @@ double GateGenericSource::PrepareNextTime(double current_simulation_time) { } void GateGenericSource::PrepareNextRun() { - // The following compute the global transformation from + // The following function computes the global transformation from // the local volume (mother) to the world GateVSource::PrepareNextRun(); + // This global transformation is given to the SPS that will // generate particles in the correct coordinate system auto &l = fThreadLocalData.Get(); - // auto user_info_pos = py::dict(puser_info["position"]); - // auto pos_init = DictGetG4ThreeVector(user_info_pos, "translation"); auto *pos = fSPS->GetPosDist(); pos->SetCentreCoords(l.fGlobalTranslation); @@ -180,17 +180,23 @@ void GateGenericSource::PrepareNextRun() { pos->SetPosRot1(r1); pos->SetPosRot2(r2); + // For the direction, the orientation may or may not be + // relative to the volume according to user option auto *ang = fSPS->GetAngDist(); - + ang->fDirectionRelativeToAttachedVolume = fDirectionRelativeToAttachedVolume; + ang->fGlobalRotation = l.fGlobalRotation; + ang->fGlobalTranslation = l.fGlobalTranslation; if (fangType == "momentum" && fDirectionRelativeToAttachedVolume) { auto new_d = rotation * fInitializeMomentum; ang->SetParticleMomentumDirection(new_d); + ang->fDirectionRelativeToAttachedVolume = false; } if (fangType == "focused" && fDirectionRelativeToAttachedVolume) { auto vec_f = fInitiliazeFocusPoint - fInitTranslation; auto rot_f = rotation * vec_f; auto new_f = rot_f + l.fGlobalTranslation; ang->SetFocusPoint(new_f); + ang->fDirectionRelativeToAttachedVolume = false; } } diff --git a/core/opengate_core/opengate_lib/GateSPSAngDistribution.cpp b/core/opengate_core/opengate_lib/GateSPSAngDistribution.cpp new file mode 100644 index 000000000..fb12b1307 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateSPSAngDistribution.cpp @@ -0,0 +1,19 @@ +/* -------------------------------------------------- + Copyright (C): OpenGate Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#include "GateSPSAngDistribution.h" +#include "GateHelpers.h" + +G4ThreeVector GateSPSAngDistribution::VGenerateOne() { + // return GenerateOne(); + auto direction = GenerateOne(); + if (fDirectionRelativeToAttachedVolume) { + direction = direction / direction.mag(); + direction = fGlobalRotation * direction; + } + return direction; +} diff --git a/core/opengate_core/opengate_lib/GateSPSAngDistribution.h b/core/opengate_core/opengate_lib/GateSPSAngDistribution.h new file mode 100644 index 000000000..9bdde37f5 --- /dev/null +++ b/core/opengate_core/opengate_lib/GateSPSAngDistribution.h @@ -0,0 +1,30 @@ +/* -------------------------------------------------- + Copyright (C): OpenGATE Collaboration + This software is distributed under the terms + of the GNU Lesser General Public Licence (LGPL) + See LICENSE.md for further details + -------------------------------------------------- */ + +#ifndef GateSPSAngDistribution_h +#define GateSPSAngDistribution_h + +#include "G4ParticleDefinition.hh" +#include "G4SPSAngDistribution.hh" + +class GateSPSAngDistribution : public G4SPSAngDistribution { + +public: + virtual ~GateSPSAngDistribution() = default; + + // Cannot inherit from GenerateOne, so we consider VGenerateOne instead + virtual G4ThreeVector VGenerateOne(); + + // Store the global orientation that may be applied to the direction + // in the GenerateOne function. + // (Must be updated each run) + bool fDirectionRelativeToAttachedVolume; + G4ThreeVector fGlobalTranslation; + G4RotationMatrix fGlobalRotation; +}; + +#endif // GateSPSAngDistribution_h diff --git a/core/opengate_core/opengate_lib/GateSingleParticleSource.cpp b/core/opengate_core/opengate_lib/GateSingleParticleSource.cpp index 2473bbfab..cf3fdd577 100644 --- a/core/opengate_core/opengate_lib/GateSingleParticleSource.cpp +++ b/core/opengate_core/opengate_lib/GateSingleParticleSource.cpp @@ -10,13 +10,12 @@ #include "G4PrimaryVertex.hh" #include "G4RunManager.hh" #include "GateHelpers.h" - #include "GateRandomMultiGauss.h" GateSingleParticleSource::GateSingleParticleSource( std::string /*mother_volume*/) { fPositionGenerator = new GateSPSPosDistribution(); - fDirectionGenerator = new G4SPSAngDistribution(); + fDirectionGenerator = new GateSPSAngDistribution(); fEnergyGenerator = new GateSPSEneDistribution(); // needed @@ -64,7 +63,8 @@ GateSingleParticleSource::GenerateDirectionWithAA(const G4ThreeVector &position, fAAManager->StartAcceptLoop(); while (!accept_angle) { // direction - direction = fDirectionGenerator->GenerateOne(); + direction = fDirectionGenerator->VGenerateOne(); + // accept ? accept_angle = fAAManager->TestIfAccept(position, direction); if (!accept_angle && fAAManager->GetPolicy() == diff --git a/core/opengate_core/opengate_lib/GateSingleParticleSource.h b/core/opengate_core/opengate_lib/GateSingleParticleSource.h index ad75d6863..a68f29ccd 100644 --- a/core/opengate_core/opengate_lib/GateSingleParticleSource.h +++ b/core/opengate_core/opengate_lib/GateSingleParticleSource.h @@ -10,16 +10,15 @@ #include "G4AffineTransform.hh" #include "G4ParticleDefinition.hh" -#include "G4SPSAngDistribution.hh" #include "G4VPrimaryGenerator.hh" #include "GateAcceptanceAngleTester.h" #include "GateAcceptanceAngleTesterManager.h" #include "GateHelpers.h" +#include "GateRandomMultiGauss.h" +#include "GateSPSAngDistribution.h" #include "GateSPSEneDistribution.h" #include "GateSPSPosDistribution.h" -#include "GateRandomMultiGauss.h" - /* Single Particle Source generator. We need to re-implement the one from G4 in order to @@ -37,7 +36,7 @@ class GateSingleParticleSource : public G4VPrimaryGenerator { GateSPSPosDistribution *GetPosDist() { return fPositionGenerator; } - G4SPSAngDistribution *GetAngDist() { return fDirectionGenerator; } + GateSPSAngDistribution *GetAngDist() { return fDirectionGenerator; } GateSPSEneDistribution *GetEneDist() { return fEnergyGenerator; } @@ -62,7 +61,7 @@ class GateSingleParticleSource : public G4VPrimaryGenerator { double fCharge; double fMass; GateSPSPosDistribution *fPositionGenerator; - G4SPSAngDistribution *fDirectionGenerator; + GateSPSAngDistribution *fDirectionGenerator; GateSPSEneDistribution *fEnergyGenerator; G4SPSRandomGenerator *fBiasRndm; bool fAccolinearityFlag; diff --git a/opengate/tests/src/test067_stl_volume.py b/opengate/tests/src/test067_stl_volume.py old mode 100644 new mode 100755 From 529db79b27b3187e4e7e6b6347ac8389ab99c406 Mon Sep 17 00:00:00 2001 From: David Date: Thu, 13 Jun 2024 07:33:00 +0200 Subject: [PATCH 02/14] starting test --- .../src/test075_generic_source_rotation.py | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100755 opengate/tests/src/test075_generic_source_rotation.py diff --git a/opengate/tests/src/test075_generic_source_rotation.py b/opengate/tests/src/test075_generic_source_rotation.py new file mode 100755 index 000000000..064e0e896 --- /dev/null +++ b/opengate/tests/src/test075_generic_source_rotation.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +from opengate.tests import utility +from scipy.spatial.transform import Rotation + +if __name__ == "__main__": + # paths = utility.get_default_test_paths(__file__, "gate_test010_generic_source") + + # create the simulation + sim = gate.Simulation() + + # main options + sim.g4_verbose = False + sim.g4_verbose_level = 1 + sim.visu = True + sim.visu_type = "vrml" + sim.number_of_threads = 1 + + # useful units + MeV = gate.g4_units.MeV + keV = gate.g4_units.keV + Bq = gate.g4_units.Bq + deg = gate.g4_units.deg + mm = gate.g4_units.mm + m = gate.g4_units.m + cm = gate.g4_units.cm + + # set the world size like in the Gate macro + world = sim.world + world.size = [2 * m, 2 * m, 2 * m] + + # add a simple volume + waterbox = sim.add_volume("Box", "waterbox") + waterbox.size = [40 * cm, 40 * cm, 40 * cm] + waterbox.translation = [0 * cm, 0 * cm, 0 * cm] + waterbox.material = "G4_AIR" + + # source box + b = sim.add_volume("Box", "sourcebox") + b.size = [5 * cm, 5 * cm, 5 * cm] + b.translation = [0 * cm, 50 * cm, 0 * cm] + r = Rotation.from_euler("y", -25, degrees=True) + r = r * Rotation.from_euler("x", -35, degrees=True) + b.rotation = r.as_matrix() + + # source + source = sim.add_source("GenericSource", "source1") + source.mother = "sourcebox" + source.particle = "gamma" + source.n = 50 + source.position.type = "disc" + source.position.radius = 0 * mm + source.direction_relative_to_volume = True + source.direction.type = "iso" + source.direction.focus_point = [1 * cm, 2 * cm, 3 * cm] + source.direction.theta = [0 * deg, 10 * deg] + source.direction.phi = [0 * deg, 360 * deg] + + source.energy.type = "mono" + source.energy.mono = 100 * MeV + + # actors + stats_actor = sim.add_actor("SimulationStatisticsActor", "Stats") + + # start simulation + sim.run() + + # get results + stats = sim.output.get_actor("Stats") + print(stats) + + utility.test_ok(False) From fe67ccdac17d16a6c9b68cdcfa05ff8b494d48de Mon Sep 17 00:00:00 2001 From: axelrannou Date: Fri, 14 Jun 2024 09:37:40 +0200 Subject: [PATCH 03/14] update test068, no need for test075 --- opengate/tests/src/test068_rotation_source.py | 21 +++++- .../src/test075_generic_source_rotation.py | 74 ------------------- 2 files changed, 18 insertions(+), 77 deletions(-) delete mode 100755 opengate/tests/src/test075_generic_source_rotation.py diff --git a/opengate/tests/src/test068_rotation_source.py b/opengate/tests/src/test068_rotation_source.py index c1c214cc9..96d9d40b7 100755 --- a/opengate/tests/src/test068_rotation_source.py +++ b/opengate/tests/src/test068_rotation_source.py @@ -8,7 +8,7 @@ import uproot -# The test generates two different generic sources, momentum and focused, which is attached to a plan. +# The test generates three differents generic sources, momentum, focused and iso, which are attached to a plan. # The plan is randomly rotated and we verify that the generated particles have a direction which is in accordance # with the applied rotations and the transmissions. @@ -50,6 +50,7 @@ def test068(tab, nb_run, nb_part, nb_source): keV = gate.g4_units.keV sec = gate.g4_units.s gcm3 = gate.g4_units["g/cm3"] + deg = gate.g4_units.deg # adapt world size world = sim.world @@ -60,7 +61,7 @@ def test068(tab, nb_run, nb_part, nb_source): n = 100 # Plan to attach the source - nb_source = 2 + nb_source = 3 plan = sim.add_volume("Box", "source_plan") plan.mother = world.name plan.material = "G4_Galactic" @@ -93,6 +94,20 @@ def test068(tab, nb_run, nb_part, nb_source): source2.energy.mono = 1 * MeV source2.activity = n * Bq / sim.number_of_threads + source3 = sim.add_source("GenericSource", "photon_source_3") + source3.particle = "gamma" + source3.position.type = "disc" + source3.position.radius = 0 * mm + source3.mother = plan.name + source3.position.size = [10 * cm, 10 * cm, 1 * nm] + source3.direction.type = "iso" + source3.direction.theta = [0 * deg, 10 * deg] + source3.direction.phi = [0 * deg, 360 * deg] + source3.direction_relative_to_volume = True + source3.energy.type = "mono" + source3.energy.mono = 1 * MeV + source3.activity = n * Bq / sim.number_of_threads + # Phase Space phsp_plan = sim.add_volume("Box", "phsp_plan") @@ -171,5 +186,5 @@ def test068(tab, nb_run, nb_part, nb_source): + str(int(+3 * err + nb_run * nb_part * nb_source)) + "]" ) - is_ok = test068(arr, len(sim.run_timing_intervals), nb_part, 2) + is_ok = test068(arr, len(sim.run_timing_intervals), nb_part, nb_source) utility.test_ok(is_ok) diff --git a/opengate/tests/src/test075_generic_source_rotation.py b/opengate/tests/src/test075_generic_source_rotation.py deleted file mode 100755 index 064e0e896..000000000 --- a/opengate/tests/src/test075_generic_source_rotation.py +++ /dev/null @@ -1,74 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import opengate as gate -from opengate.tests import utility -from scipy.spatial.transform import Rotation - -if __name__ == "__main__": - # paths = utility.get_default_test_paths(__file__, "gate_test010_generic_source") - - # create the simulation - sim = gate.Simulation() - - # main options - sim.g4_verbose = False - sim.g4_verbose_level = 1 - sim.visu = True - sim.visu_type = "vrml" - sim.number_of_threads = 1 - - # useful units - MeV = gate.g4_units.MeV - keV = gate.g4_units.keV - Bq = gate.g4_units.Bq - deg = gate.g4_units.deg - mm = gate.g4_units.mm - m = gate.g4_units.m - cm = gate.g4_units.cm - - # set the world size like in the Gate macro - world = sim.world - world.size = [2 * m, 2 * m, 2 * m] - - # add a simple volume - waterbox = sim.add_volume("Box", "waterbox") - waterbox.size = [40 * cm, 40 * cm, 40 * cm] - waterbox.translation = [0 * cm, 0 * cm, 0 * cm] - waterbox.material = "G4_AIR" - - # source box - b = sim.add_volume("Box", "sourcebox") - b.size = [5 * cm, 5 * cm, 5 * cm] - b.translation = [0 * cm, 50 * cm, 0 * cm] - r = Rotation.from_euler("y", -25, degrees=True) - r = r * Rotation.from_euler("x", -35, degrees=True) - b.rotation = r.as_matrix() - - # source - source = sim.add_source("GenericSource", "source1") - source.mother = "sourcebox" - source.particle = "gamma" - source.n = 50 - source.position.type = "disc" - source.position.radius = 0 * mm - source.direction_relative_to_volume = True - source.direction.type = "iso" - source.direction.focus_point = [1 * cm, 2 * cm, 3 * cm] - source.direction.theta = [0 * deg, 10 * deg] - source.direction.phi = [0 * deg, 360 * deg] - - source.energy.type = "mono" - source.energy.mono = 100 * MeV - - # actors - stats_actor = sim.add_actor("SimulationStatisticsActor", "Stats") - - # start simulation - sim.run() - - # get results - stats = sim.output.get_actor("Stats") - print(stats) - - utility.test_ok(False) From 370e67b631c3ab53594f1d82ae1ea5bf847ac309 Mon Sep 17 00:00:00 2001 From: axelrannou Date: Wed, 26 Jun 2024 13:48:52 +0200 Subject: [PATCH 04/14] Add simulation of a c-arm : siemens cios alpha --- opengate/contrib/carm/siemensciosalpha.py | 160 ++++++++++++++++++ .../tests/src/test075_siemens_cios_alpha.py | 65 +++++++ 2 files changed, 225 insertions(+) create mode 100644 opengate/contrib/carm/siemensciosalpha.py create mode 100644 opengate/tests/src/test075_siemens_cios_alpha.py diff --git a/opengate/contrib/carm/siemensciosalpha.py b/opengate/contrib/carm/siemensciosalpha.py new file mode 100644 index 000000000..b3fc22ec2 --- /dev/null +++ b/opengate/contrib/carm/siemensciosalpha.py @@ -0,0 +1,160 @@ +import opengate as gate +from opengate.utility import g4_units, get_contrib_path +from scipy.spatial.transform import Rotation +import numpy as np +import spekpy as sp + +def add_carm(sim, machine_name): + # carm + carm = add_carm_box(sim, machine_name) + add_xray_tank(sim, machine_name) + add_collimators(sim, machine_name) + + return carm + +def add_carm_box(sim, machine_name): + carmbox = sim.volume_manager.create_volume("Box", "carmbox") + carmbox.material = "G4_AIR" + carmbox.size = [200 * cm, 30* cm, 200 * cm] + carmbox.translation = [0 * cm, 0* cm, 0 * cm] + carmbox.color = [1, 1, 1, 0.8] + + # create a t_shape to subtract from carmbox + hole1 = sim.volume_manager.create_volume("Box", "hole") + hole1.size = [191 * cm, 31* cm, 160 * cm] + hole1.color = [1, 1, 1, 0.8] + hole2 = sim.volume_manager.create_volume("Box", "hole2") + hole2.size = [80 * cm, 31* cm, 31 * cm] + hole2.color = [1, 1, 1, 0.8] + hole3 = sim.volume_manager.create_volume("Box", "hole3") + hole3.size = [80 * cm, 31* cm, 31 * cm] + hole3.color = [1, 1, 1, 0.8] + + # unite hole 1 and 2 + hole1and2= gate.geometry.volumes.unite_volumes( + hole1, hole2, translation = [-55.5 * cm, 0 * cm, 95 * cm] + ) + + # t_shape + t_shape= gate.geometry.volumes.unite_volumes( + hole1and2, hole3, translation = [-55.5 * cm, 0 * cm, -95 * cm] + ) + #sim.add_volume(t_shape) + + # create c-arm + carm = gate.geometry.volumes.subtract_volumes( + carmbox, t_shape, new_name = machine_name, translation = [-5 * cm, 0 * cm, -10 * cm] + ) + sim.add_volume(carm) + + return carm + +def add_xray_tank(sim, machine_name): + xray_tank = sim.add_volume("Box", f"{machine_name}_xray_tank") + xray_tank.mother = machine_name + xray_tank.material = "G4_AIR" + xray_tank.size = [40 * cm, 20 * cm, 30 * cm] + xray_tank.translation = [0 * cm, 0, 85 * cm] + xray_tank.color = [1, 1, 1, 0.8] + +def add_carm_source(sim, machine_name, kvp): + # Generate spectrum + s = sp.Spek(kvp, th=10, physics= 'kqp') + s.filter('Al', 3.0).filter('Cu', 0.1) + + # Get the spectrum + energy_bins, weights = s.get_spectrum() + + # source box + b = sim.add_volume("Box", f"{machine_name}_sourcebox") + b.mother = f"{machine_name}_xray_tank" + b.translation = [0* cm,0 * cm, 10 * cm] + b.size = [1 * cm, 1 * cm, 1 * cm] + + # source + source = sim.add_source("GenericSource", f"{machine_name}_source") + source.mother = f"{machine_name}_sourcebox" + source.particle = "gamma" + source.position.type = "disc" + source.position.radius = 0 * mm + + source.direction_relative_to_volume = True + source.direction.type = "iso" + source.direction.theta = [0 * deg, 10 * deg] + source.direction.phi = [0 * deg, 360 * deg] + + source.energy.type = "histogram" + source.energy.histogram_weight = weights + source.energy.histogram_energy = energy_bins + + return source + +def add_collimators(sim, machine_name): + xray_tank = sim.volume_manager.get_volume(f"{machine_name}_xray_tank") + z_xray_tank = xray_tank.size[2] + + collimator1 = sim.add_volume("Box", f"{machine_name}_collimator1") + collimator1.mother = f"{machine_name}_xray_tank" + collimator1.color = red + collimator1.translation = [75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] + collimator1.size = [5* cm, 10 * cm, 1 * mm] + + collimator2 = sim.add_volume("Box", f"{machine_name}_collimator2") + collimator2.mother = f"{machine_name}_xray_tank" + collimator2.color = red + collimator2.translation = [-75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] + collimator2.size = [5* cm, 10 * cm, 1 * mm] + + collimator3 = sim.add_volume("Box", f"{machine_name}_collimator3") + collimator3.mother = f"{machine_name}_xray_tank" + collimator3.color = red + collimator3.translation = [0* cm, 75 * mm , -z_xray_tank / 2 * mm + 3 * mm] + collimator3.size = [10* cm, 5 * cm, 1 * mm] + + collimator4 = sim.add_volume("Box", f"{machine_name}_collimator4") + collimator4.mother = f"{machine_name}_xray_tank" + collimator4.color = red + collimator4.translation = [0* cm, -75 * mm, -z_xray_tank / 2 * mm + 3 * mm] + collimator4.size = [10* cm, 5 * cm, 1 * mm] + + # psycho killer + killer = sim.add_actor("KillActor", f"target_kill") + killer.mother = [collimator1.name, collimator2.name,collimator3.name,collimator4.name] + + +def update_collimation(sim, machine_name, num, num2): + if not 0 <= num <= 50 or not 0 <= num2 <= 50: + raise ValueError("Wrong values for collimation") + + num = 50 - num + num2 = 50 - num2 + + xray_tank = sim.volume_manager.get_volume(f"{machine_name}_xray_tank") + z_xray_tank = xray_tank.size[2] + + collimator1 = sim.volume_manager.get_volume(f"{machine_name}_collimator1") + collimator1.translation = [75 * mm - num, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] + + collimator2 = sim.volume_manager.get_volume(f"{machine_name}_collimator2") + collimator2.translation = [-75 * mm + num, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] + + collimator3 = sim.volume_manager.get_volume(f"{machine_name}_collimator3") + collimator3.translation = [0 * cm, 75 * mm - num2, -z_xray_tank / 2 * mm + 3 * mm] + + collimator4 = sim.volume_manager.get_volume(f"{machine_name}_collimator4") + collimator4.translation = [0 * cm, -75 * mm + num2, -z_xray_tank / 2 * mm + 3 * mm] + +# useful units +MeV = gate.g4_units.MeV +keV = gate.g4_units.keV +Bq = gate.g4_units.Bq +deg = gate.g4_units.deg +nm = gate.g4_units.nm +mm = gate.g4_units.mm +m = gate.g4_units.m +cm = gate.g4_units.cm + +# colors +red = [1, 0.7, 0.7, 0.8] +blue = [0.5, 0.5, 1, 0.8] +green = [0, 1, 0, 1] diff --git a/opengate/tests/src/test075_siemens_cios_alpha.py b/opengate/tests/src/test075_siemens_cios_alpha.py new file mode 100644 index 000000000..7a943411e --- /dev/null +++ b/opengate/tests/src/test075_siemens_cios_alpha.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import opengate as gate +import opengate.contrib.carm.siemensciosalpha as ciosalpha +from opengate.tests import utility +from scipy.spatial.transform import Rotation +import numpy as np +import uproot +import matplotlib.pyplot as plt + +if __name__ == "__main__": + # paths + paths = utility.get_default_test_paths(__file__, output_folder="test075_siemens_cios_alpha") + + # create the simulation + sim = gate.Simulation() + + # main options + sim.g4_verbose = False + sim.visu = True + sim.visu_type = "vrml" + sim.check_volumes_overlap = False + sim.number_of_threads = 1 + sim.random_seed = 12345678 + sim.check_volumes_overlap = True + + # units + m = gate.g4_units.m + cm = gate.g4_units.cm + mm = gate.g4_units.mm + deg = gate.g4_units.deg + + # world + world = sim.world + world.size = [5* m, 5 * m, 5 * m] + world.material = "G4_AIR" + + # add a carm + carm = ciosalpha.add_carm(sim, "cios_alpha") + carm.rotation = Rotation.from_euler("ZYX", [0,20,180], degrees=True).as_matrix() + + # xray tube spectrum parameters + # tube potential [kV] + kvp = 100 + + # add carm source + source = ciosalpha.add_carm_source(sim, carm.name, kvp) + source.n = 1e6 + if sim.visu: + source.n = 1 + + # opening of the collimators [0, 50 *mm] + ciosalpha.update_collimation(sim, carm.name, 20 * mm, 20 * mm) + + # aluminum table + table = sim.add_volume("Box", "aluminum_table") + table.size = [60 * cm, 2 * m, 0.9 * mm ] + table.material = "G4_Al" + table.translation = [0 * m, 0 * cm, 0 * cm] + table.color = [0.8, 0.8, 0.8, 1] + + # start simulation + sim.run() + From 47950fcc3c169616657a35dc335ace1ab6e947d1 Mon Sep 17 00:00:00 2001 From: axelrannou Date: Thu, 27 Jun 2024 14:15:14 +0200 Subject: [PATCH 05/14] create Ciosalpha class --- opengate/contrib/carm/siemensciosalpha.py | 296 +++++++++--------- .../tests/src/test075_siemens_cios_alpha.py | 23 +- 2 files changed, 166 insertions(+), 153 deletions(-) diff --git a/opengate/contrib/carm/siemensciosalpha.py b/opengate/contrib/carm/siemensciosalpha.py index b3fc22ec2..dda021f89 100644 --- a/opengate/contrib/carm/siemensciosalpha.py +++ b/opengate/contrib/carm/siemensciosalpha.py @@ -4,145 +4,161 @@ import numpy as np import spekpy as sp -def add_carm(sim, machine_name): - # carm - carm = add_carm_box(sim, machine_name) - add_xray_tank(sim, machine_name) - add_collimators(sim, machine_name) - - return carm - -def add_carm_box(sim, machine_name): - carmbox = sim.volume_manager.create_volume("Box", "carmbox") - carmbox.material = "G4_AIR" - carmbox.size = [200 * cm, 30* cm, 200 * cm] - carmbox.translation = [0 * cm, 0* cm, 0 * cm] - carmbox.color = [1, 1, 1, 0.8] - - # create a t_shape to subtract from carmbox - hole1 = sim.volume_manager.create_volume("Box", "hole") - hole1.size = [191 * cm, 31* cm, 160 * cm] - hole1.color = [1, 1, 1, 0.8] - hole2 = sim.volume_manager.create_volume("Box", "hole2") - hole2.size = [80 * cm, 31* cm, 31 * cm] - hole2.color = [1, 1, 1, 0.8] - hole3 = sim.volume_manager.create_volume("Box", "hole3") - hole3.size = [80 * cm, 31* cm, 31 * cm] - hole3.color = [1, 1, 1, 0.8] - - # unite hole 1 and 2 - hole1and2= gate.geometry.volumes.unite_volumes( - hole1, hole2, translation = [-55.5 * cm, 0 * cm, 95 * cm] - ) - - # t_shape - t_shape= gate.geometry.volumes.unite_volumes( - hole1and2, hole3, translation = [-55.5 * cm, 0 * cm, -95 * cm] - ) - #sim.add_volume(t_shape) - - # create c-arm - carm = gate.geometry.volumes.subtract_volumes( - carmbox, t_shape, new_name = machine_name, translation = [-5 * cm, 0 * cm, -10 * cm] - ) - sim.add_volume(carm) - - return carm - -def add_xray_tank(sim, machine_name): - xray_tank = sim.add_volume("Box", f"{machine_name}_xray_tank") - xray_tank.mother = machine_name - xray_tank.material = "G4_AIR" - xray_tank.size = [40 * cm, 20 * cm, 30 * cm] - xray_tank.translation = [0 * cm, 0, 85 * cm] - xray_tank.color = [1, 1, 1, 0.8] - -def add_carm_source(sim, machine_name, kvp): - # Generate spectrum - s = sp.Spek(kvp, th=10, physics= 'kqp') - s.filter('Al', 3.0).filter('Cu', 0.1) - - # Get the spectrum - energy_bins, weights = s.get_spectrum() - - # source box - b = sim.add_volume("Box", f"{machine_name}_sourcebox") - b.mother = f"{machine_name}_xray_tank" - b.translation = [0* cm,0 * cm, 10 * cm] - b.size = [1 * cm, 1 * cm, 1 * cm] - - # source - source = sim.add_source("GenericSource", f"{machine_name}_source") - source.mother = f"{machine_name}_sourcebox" - source.particle = "gamma" - source.position.type = "disc" - source.position.radius = 0 * mm - - source.direction_relative_to_volume = True - source.direction.type = "iso" - source.direction.theta = [0 * deg, 10 * deg] - source.direction.phi = [0 * deg, 360 * deg] - - source.energy.type = "histogram" - source.energy.histogram_weight = weights - source.energy.histogram_energy = energy_bins - - return source - -def add_collimators(sim, machine_name): - xray_tank = sim.volume_manager.get_volume(f"{machine_name}_xray_tank") - z_xray_tank = xray_tank.size[2] - - collimator1 = sim.add_volume("Box", f"{machine_name}_collimator1") - collimator1.mother = f"{machine_name}_xray_tank" - collimator1.color = red - collimator1.translation = [75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] - collimator1.size = [5* cm, 10 * cm, 1 * mm] - - collimator2 = sim.add_volume("Box", f"{machine_name}_collimator2") - collimator2.mother = f"{machine_name}_xray_tank" - collimator2.color = red - collimator2.translation = [-75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] - collimator2.size = [5* cm, 10 * cm, 1 * mm] - - collimator3 = sim.add_volume("Box", f"{machine_name}_collimator3") - collimator3.mother = f"{machine_name}_xray_tank" - collimator3.color = red - collimator3.translation = [0* cm, 75 * mm , -z_xray_tank / 2 * mm + 3 * mm] - collimator3.size = [10* cm, 5 * cm, 1 * mm] - - collimator4 = sim.add_volume("Box", f"{machine_name}_collimator4") - collimator4.mother = f"{machine_name}_xray_tank" - collimator4.color = red - collimator4.translation = [0* cm, -75 * mm, -z_xray_tank / 2 * mm + 3 * mm] - collimator4.size = [10* cm, 5 * cm, 1 * mm] - - # psycho killer - killer = sim.add_actor("KillActor", f"target_kill") - killer.mother = [collimator1.name, collimator2.name,collimator3.name,collimator4.name] - - -def update_collimation(sim, machine_name, num, num2): - if not 0 <= num <= 50 or not 0 <= num2 <= 50: - raise ValueError("Wrong values for collimation") - - num = 50 - num - num2 = 50 - num2 - - xray_tank = sim.volume_manager.get_volume(f"{machine_name}_xray_tank") - z_xray_tank = xray_tank.size[2] - - collimator1 = sim.volume_manager.get_volume(f"{machine_name}_collimator1") - collimator1.translation = [75 * mm - num, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] - - collimator2 = sim.volume_manager.get_volume(f"{machine_name}_collimator2") - collimator2.translation = [-75 * mm + num, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] - - collimator3 = sim.volume_manager.get_volume(f"{machine_name}_collimator3") - collimator3.translation = [0 * cm, 75 * mm - num2, -z_xray_tank / 2 * mm + 3 * mm] - - collimator4 = sim.volume_manager.get_volume(f"{machine_name}_collimator4") - collimator4.translation = [0 * cm, -75 * mm + num2, -z_xray_tank / 2 * mm + 3 * mm] +class Ciosalpha: + def __init__(self, sim, kvp): + self.sim = sim + self.machine_name = "ciosalpha" + self.volume = self.add_carm_box() + self.add_xray_tank() + self.add_collimators() + self.source = self.add_carm_source(kvp) + + def add_carm_box(self): + carmbox = self.sim.volume_manager.create_volume("Box", "carmbox") + carmbox.material = "G4_AIR" + carmbox.size = [200 * cm, 30 * cm, 200 * cm] + carmbox.translation = [0 * cm, 0 * cm, 0 * cm] + carmbox.color = [1, 1, 1, 0.8] + + hole1 = self.sim.volume_manager.create_volume("Box", "hole") + hole1.size = [191 * cm, 31 * cm, 160 * cm] + hole1.color = [1, 1, 1, 0.8] + hole2 = self.sim.volume_manager.create_volume("Box", "hole2") + hole2.size = [80 * cm, 31 * cm, 31 * cm] + hole2.color = [1, 1, 1, 0.8] + hole3 = self.sim.volume_manager.create_volume("Box", "hole3") + hole3.size = [80 * cm, 31 * cm, 31 * cm] + hole3.color = [1, 1, 1, 0.8] + + hole1and2 = gate.geometry.volumes.unite_volumes( + hole1, hole2, translation=[-55.5 * cm, 0 * cm, 95 * cm] + ) + + t_shape = gate.geometry.volumes.unite_volumes( + hole1and2, hole3, translation=[-55.5 * cm, 0 * cm, -95 * cm] + ) + + carm = gate.geometry.volumes.subtract_volumes( + carmbox, t_shape, new_name=self.machine_name, translation=[-5 * cm, 0 * cm, -10 * cm] + ) + self.sim.add_volume(carm) + + return carm + + def add_xray_tank(self): + xray_tank = self.sim.add_volume("Box", f"{self.machine_name}_xray_tank") + xray_tank.mother = self.machine_name + xray_tank.material = "G4_AIR" + xray_tank.size = [40 * cm, 20 * cm, 30 * cm] + xray_tank.translation = [0 * cm, 0, 85 * cm] + xray_tank.color = [1, 1, 1, 0.8] + + def add_carm_source(self, kvp): + s = sp.Spek(kvp, th=10, physics='kqp') + s.filter('Al', 3.0).filter('Cu', 0.1) + + energy_bins, weights = s.get_spectrum() + + b = self.sim.add_volume("Box", f"{self.machine_name}_sourcebox") + b.mother = f"{self.machine_name}_xray_tank" + b.translation = [0 * cm, 0 * cm, 10 * cm] + b.size = [1 * cm, 1 * cm, 1 * cm] + + source = self.sim.add_source("GenericSource", f"{self.machine_name}_source") + source.mother = f"{self.machine_name}_sourcebox" + source.particle = "gamma" + source.position.type = "disc" + source.position.radius = 0 * mm + + source.direction_relative_to_volume = True + source.direction.type = "iso" + source.direction.theta = [0 * deg, 10 * deg] + source.direction.phi = [0 * deg, 360 * deg] + + source.energy.type = "histogram" + source.energy.histogram_weight = weights + source.energy.histogram_energy = energy_bins + + return source + + def add_collimators(self): + xray_tank = self.sim.volume_manager.get_volume(f"{self.machine_name}_xray_tank") + z_xray_tank = xray_tank.size[2] + + collimator1 = self.sim.add_volume("Box", f"{self.machine_name}_collimator1") + collimator1.mother = f"{self.machine_name}_xray_tank" + collimator1.color = red + collimator1.translation = [75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] + collimator1.size = [5 * cm, 10 * cm, 1 * mm] + + collimator2 = self.sim.add_volume("Box", f"{self.machine_name}_collimator2") + collimator2.mother = f"{self.machine_name}_xray_tank" + collimator2.color = red + collimator2.translation = [-75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] + collimator2.size = [5 * cm, 10 * cm, 1 * mm] + + collimator3 = self.sim.add_volume("Box", f"{self.machine_name}_collimator3") + collimator3.mother = f"{self.machine_name}_xray_tank" + collimator3.color = red + collimator3.translation = [0 * cm, 75 * mm, -z_xray_tank / 2 * mm + 3 * mm] + collimator3.size = [10 * cm, 5 * cm, 1 * mm] + + collimator4 = self.sim.add_volume("Box", f"{self.machine_name}_collimator4") + collimator4.mother = f"{self.machine_name}_xray_tank" + collimator4.color = red + collimator4.translation = [0 * cm, -75 * mm, -z_xray_tank / 2 * mm + 3 * mm] + collimator4.size = [10 * cm, 5 * cm, 1 * mm] + + killer = self.sim.add_actor("KillActor", f"target_kill") + killer.mother = [collimator1.name, collimator2.name, collimator3.name, collimator4.name] + + def update_collimation(self, num, num2): + if not 0 <= num <= 50 or not 0 <= num2 <= 50: + raise ValueError("Wrong values for collimation") + + num = 50 - num + num2 = 50 - num2 + + xray_tank = self.sim.volume_manager.get_volume(f"{self.machine_name}_xray_tank") + z_xray_tank = xray_tank.size[2] + + collimator1 = self.sim.volume_manager.get_volume(f"{self.machine_name}_collimator1") + collimator1.translation = [75 * mm - num, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] + + collimator2 = self.sim.volume_manager.get_volume(f"{self.machine_name}_collimator2") + collimator2.translation = [-75 * mm + num, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] + + collimator3 = self.sim.volume_manager.get_volume(f"{self.machine_name}_collimator3") + collimator3.translation = [0 * cm, 75 * mm - num2, -z_xray_tank / 2 * mm + 3 * mm] + + collimator4 = self.sim.volume_manager.get_volume(f"{self.machine_name}_collimator4") + collimator4.translation = [0 * cm, -75 * mm + num2, -z_xray_tank / 2 * mm + 3 * mm] + + @property + def collimation(self): + return self._collimation + + @collimation.setter + def collimation(self, value): + self._collimation = value + self.update_collimation(value[0] / mm, value[1] / mm) + + @property + def rotation(self): + return self.volume.rotation + + @rotation.setter + def rotation(self, value): + self.volume.rotation = value + + @property + def translation(self): + return self.volume.translation + + @translation.setter + def translation(self, value): + self.volume.translation = value + # useful units MeV = gate.g4_units.MeV @@ -157,4 +173,4 @@ def update_collimation(sim, machine_name, num, num2): # colors red = [1, 0.7, 0.7, 0.8] blue = [0.5, 0.5, 1, 0.8] -green = [0, 1, 0, 1] +green = [0, 1, 0, 1] \ No newline at end of file diff --git a/opengate/tests/src/test075_siemens_cios_alpha.py b/opengate/tests/src/test075_siemens_cios_alpha.py index 7a943411e..19904a430 100644 --- a/opengate/tests/src/test075_siemens_cios_alpha.py +++ b/opengate/tests/src/test075_siemens_cios_alpha.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- import opengate as gate -import opengate.contrib.carm.siemensciosalpha as ciosalpha +from opengate.contrib.carm.siemensciosalpha import Ciosalpha from opengate.tests import utility from scipy.spatial.transform import Rotation import numpy as np @@ -11,7 +11,7 @@ if __name__ == "__main__": # paths - paths = utility.get_default_test_paths(__file__, output_folder="test075_siemens_cios_alpha") + #paths = utility.get_default_test_paths(__file__, output_folder="test075_siemens_cios_alpha") # create the simulation sim = gate.Simulation() @@ -36,22 +36,19 @@ world.size = [5* m, 5 * m, 5 * m] world.material = "G4_AIR" - # add a carm - carm = ciosalpha.add_carm(sim, "cios_alpha") - carm.rotation = Rotation.from_euler("ZYX", [0,20,180], degrees=True).as_matrix() - # xray tube spectrum parameters # tube potential [kV] kvp = 100 - # add carm source - source = ciosalpha.add_carm_source(sim, carm.name, kvp) - source.n = 1e6 - if sim.visu: - source.n = 1 + # add a carm + carm = Ciosalpha(sim, kvp) + carm.rotation = Rotation.from_euler("ZYX", [0,20,0], degrees=True).as_matrix() + carm.translation = [0 * cm, 0 * cm, 0 * cm] + carm.collimation = [30 * mm, 10 * mm] - # opening of the collimators [0, 50 *mm] - ciosalpha.update_collimation(sim, carm.name, 20 * mm, 20 * mm) + carm.source.n = 1e6 + if sim.visu: + carm.source.n = 1000 # aluminum table table = sim.add_volume("Box", "aluminum_table") From 5afc3bb440e06a5a6ec7e580ec013068a7933ddf Mon Sep 17 00:00:00 2001 From: axelrannou Date: Fri, 28 Jun 2024 09:53:26 +0200 Subject: [PATCH 06/14] enhanced code --- opengate/contrib/carm/siemensciosalpha.py | 112 +++++++++--------- .../tests/src/test075_siemens_cios_alpha.py | 1 + 2 files changed, 54 insertions(+), 59 deletions(-) diff --git a/opengate/contrib/carm/siemensciosalpha.py b/opengate/contrib/carm/siemensciosalpha.py index dda021f89..3c491c345 100644 --- a/opengate/contrib/carm/siemensciosalpha.py +++ b/opengate/contrib/carm/siemensciosalpha.py @@ -1,9 +1,18 @@ import opengate as gate -from opengate.utility import g4_units, get_contrib_path -from scipy.spatial.transform import Rotation +from opengate.utility import g4_units import numpy as np import spekpy as sp +# useful units +MeV = gate.g4_units.MeV +keV = gate.g4_units.keV +Bq = gate.g4_units.Bq +deg = gate.g4_units.deg +nm = gate.g4_units.nm +mm = gate.g4_units.mm +m = gate.g4_units.m +cm = gate.g4_units.cm + class Ciosalpha: def __init__(self, sim, kvp): self.sim = sim @@ -85,54 +94,55 @@ def add_collimators(self): xray_tank = self.sim.volume_manager.get_volume(f"{self.machine_name}_xray_tank") z_xray_tank = xray_tank.size[2] - collimator1 = self.sim.add_volume("Box", f"{self.machine_name}_collimator1") - collimator1.mother = f"{self.machine_name}_xray_tank" - collimator1.color = red - collimator1.translation = [75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] - collimator1.size = [5 * cm, 10 * cm, 1 * mm] - - collimator2 = self.sim.add_volume("Box", f"{self.machine_name}_collimator2") - collimator2.mother = f"{self.machine_name}_xray_tank" - collimator2.color = red - collimator2.translation = [-75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] - collimator2.size = [5 * cm, 10 * cm, 1 * mm] - - collimator3 = self.sim.add_volume("Box", f"{self.machine_name}_collimator3") - collimator3.mother = f"{self.machine_name}_xray_tank" - collimator3.color = red - collimator3.translation = [0 * cm, 75 * mm, -z_xray_tank / 2 * mm + 3 * mm] - collimator3.size = [10 * cm, 5 * cm, 1 * mm] - - collimator4 = self.sim.add_volume("Box", f"{self.machine_name}_collimator4") - collimator4.mother = f"{self.machine_name}_xray_tank" - collimator4.color = red - collimator4.translation = [0 * cm, -75 * mm, -z_xray_tank / 2 * mm + 3 * mm] - collimator4.size = [10 * cm, 5 * cm, 1 * mm] + collimators = [ + { + "translation": [75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], + "size": [5 * cm, 10 * cm, 1 * mm] + }, + { + "translation": [-75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], + "size": [5 * cm, 10 * cm, 1 * mm] + }, + { + "translation": [0 * cm, 75 * mm, -z_xray_tank / 2 * mm + 3 * mm], + "size": [10 * cm, 5 * cm, 1 * mm] + }, + { + "translation": [0 * cm, -75 * mm, -z_xray_tank / 2 * mm + 3 * mm], + "size": [10 * cm, 5 * cm, 1 * mm] + } + ] + + for i, colli in enumerate(collimators): + collimator = self.sim.add_volume("Box", f"{self.machine_name}_collimator{i+1}") + collimator.mother = f"{self.machine_name}_xray_tank" + collimator.color = [1, 0.7, 0.7, 0.8] + collimator.translation = colli["translation"] + collimator.size = colli["size"] killer = self.sim.add_actor("KillActor", f"target_kill") - killer.mother = [collimator1.name, collimator2.name, collimator3.name, collimator4.name] + killer.mother = [f"{self.machine_name}_collimator{i+1}" for i in range(4)] - def update_collimation(self, num, num2): - if not 0 <= num <= 50 or not 0 <= num2 <= 50: - raise ValueError("Wrong values for collimation") + def set_collimation(self, collimation1, collimation2): + if not 0 <= collimation1 <= 50 or not 0 <= collimation2 <= 50: + raise ValueError("Collimation values must be between 0 and 50 mm") - num = 50 - num - num2 = 50 - num2 + collimation1 = 50 - collimation1 + collimation2 = 50 - collimation2 xray_tank = self.sim.volume_manager.get_volume(f"{self.machine_name}_xray_tank") z_xray_tank = xray_tank.size[2] - collimator1 = self.sim.volume_manager.get_volume(f"{self.machine_name}_collimator1") - collimator1.translation = [75 * mm - num, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] - - collimator2 = self.sim.volume_manager.get_volume(f"{self.machine_name}_collimator2") - collimator2.translation = [-75 * mm + num, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm] - - collimator3 = self.sim.volume_manager.get_volume(f"{self.machine_name}_collimator3") - collimator3.translation = [0 * cm, 75 * mm - num2, -z_xray_tank / 2 * mm + 3 * mm] + translations = [ + [75 * mm - collimation1, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], + [-75 * mm + collimation1, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], + [0 * cm, 75 * mm - collimation2, -z_xray_tank / 2 * mm + 3 * mm], + [0 * cm, -75 * mm + collimation2, -z_xray_tank / 2 * mm + 3 * mm] + ] - collimator4 = self.sim.volume_manager.get_volume(f"{self.machine_name}_collimator4") - collimator4.translation = [0 * cm, -75 * mm + num2, -z_xray_tank / 2 * mm + 3 * mm] + for i, translation in enumerate(translations): + collimator = self.sim.volume_manager.get_volume(f"{self.machine_name}_collimator{i+1}") + collimator.translation = translation @property def collimation(self): @@ -141,7 +151,7 @@ def collimation(self): @collimation.setter def collimation(self, value): self._collimation = value - self.update_collimation(value[0] / mm, value[1] / mm) + self.set_collimation(value[0], value[1]) @property def rotation(self): @@ -157,20 +167,4 @@ def translation(self): @translation.setter def translation(self, value): - self.volume.translation = value - - -# useful units -MeV = gate.g4_units.MeV -keV = gate.g4_units.keV -Bq = gate.g4_units.Bq -deg = gate.g4_units.deg -nm = gate.g4_units.nm -mm = gate.g4_units.mm -m = gate.g4_units.m -cm = gate.g4_units.cm - -# colors -red = [1, 0.7, 0.7, 0.8] -blue = [0.5, 0.5, 1, 0.8] -green = [0, 1, 0, 1] \ No newline at end of file + self.volume.translation = value \ No newline at end of file diff --git a/opengate/tests/src/test075_siemens_cios_alpha.py b/opengate/tests/src/test075_siemens_cios_alpha.py index 19904a430..cde0c9d09 100644 --- a/opengate/tests/src/test075_siemens_cios_alpha.py +++ b/opengate/tests/src/test075_siemens_cios_alpha.py @@ -60,3 +60,4 @@ # start simulation sim.run() + # TODO: Test From 60da033976b5605a79fe5fb449636ab424fea354 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 08:05:03 +0000 Subject: [PATCH 07/14] [pre-commit.ci] Automatic python and c++ formatting --- opengate/contrib/carm/siemensciosalpha.py | 32 ++++++++++++------- .../tests/src/test075_siemens_cios_alpha.py | 8 ++--- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/opengate/contrib/carm/siemensciosalpha.py b/opengate/contrib/carm/siemensciosalpha.py index 3c491c345..73309aaa3 100644 --- a/opengate/contrib/carm/siemensciosalpha.py +++ b/opengate/contrib/carm/siemensciosalpha.py @@ -13,6 +13,7 @@ m = gate.g4_units.m cm = gate.g4_units.cm + class Ciosalpha: def __init__(self, sim, kvp): self.sim = sim @@ -48,7 +49,10 @@ def add_carm_box(self): ) carm = gate.geometry.volumes.subtract_volumes( - carmbox, t_shape, new_name=self.machine_name, translation=[-5 * cm, 0 * cm, -10 * cm] + carmbox, + t_shape, + new_name=self.machine_name, + translation=[-5 * cm, 0 * cm, -10 * cm], ) self.sim.add_volume(carm) @@ -63,8 +67,8 @@ def add_xray_tank(self): xray_tank.color = [1, 1, 1, 0.8] def add_carm_source(self, kvp): - s = sp.Spek(kvp, th=10, physics='kqp') - s.filter('Al', 3.0).filter('Cu', 0.1) + s = sp.Spek(kvp, th=10, physics="kqp") + s.filter("Al", 3.0).filter("Cu", 0.1) energy_bins, weights = s.get_spectrum() @@ -97,24 +101,26 @@ def add_collimators(self): collimators = [ { "translation": [75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], - "size": [5 * cm, 10 * cm, 1 * mm] + "size": [5 * cm, 10 * cm, 1 * mm], }, { "translation": [-75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], - "size": [5 * cm, 10 * cm, 1 * mm] + "size": [5 * cm, 10 * cm, 1 * mm], }, { "translation": [0 * cm, 75 * mm, -z_xray_tank / 2 * mm + 3 * mm], - "size": [10 * cm, 5 * cm, 1 * mm] + "size": [10 * cm, 5 * cm, 1 * mm], }, { "translation": [0 * cm, -75 * mm, -z_xray_tank / 2 * mm + 3 * mm], - "size": [10 * cm, 5 * cm, 1 * mm] - } + "size": [10 * cm, 5 * cm, 1 * mm], + }, ] for i, colli in enumerate(collimators): - collimator = self.sim.add_volume("Box", f"{self.machine_name}_collimator{i+1}") + collimator = self.sim.add_volume( + "Box", f"{self.machine_name}_collimator{i+1}" + ) collimator.mother = f"{self.machine_name}_xray_tank" collimator.color = [1, 0.7, 0.7, 0.8] collimator.translation = colli["translation"] @@ -137,11 +143,13 @@ def set_collimation(self, collimation1, collimation2): [75 * mm - collimation1, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], [-75 * mm + collimation1, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], [0 * cm, 75 * mm - collimation2, -z_xray_tank / 2 * mm + 3 * mm], - [0 * cm, -75 * mm + collimation2, -z_xray_tank / 2 * mm + 3 * mm] + [0 * cm, -75 * mm + collimation2, -z_xray_tank / 2 * mm + 3 * mm], ] for i, translation in enumerate(translations): - collimator = self.sim.volume_manager.get_volume(f"{self.machine_name}_collimator{i+1}") + collimator = self.sim.volume_manager.get_volume( + f"{self.machine_name}_collimator{i+1}" + ) collimator.translation = translation @property @@ -167,4 +175,4 @@ def translation(self): @translation.setter def translation(self, value): - self.volume.translation = value \ No newline at end of file + self.volume.translation = value diff --git a/opengate/tests/src/test075_siemens_cios_alpha.py b/opengate/tests/src/test075_siemens_cios_alpha.py index cde0c9d09..e720cd7fd 100644 --- a/opengate/tests/src/test075_siemens_cios_alpha.py +++ b/opengate/tests/src/test075_siemens_cios_alpha.py @@ -11,7 +11,7 @@ if __name__ == "__main__": # paths - #paths = utility.get_default_test_paths(__file__, output_folder="test075_siemens_cios_alpha") + # paths = utility.get_default_test_paths(__file__, output_folder="test075_siemens_cios_alpha") # create the simulation sim = gate.Simulation() @@ -33,7 +33,7 @@ # world world = sim.world - world.size = [5* m, 5 * m, 5 * m] + world.size = [5 * m, 5 * m, 5 * m] world.material = "G4_AIR" # xray tube spectrum parameters @@ -42,7 +42,7 @@ # add a carm carm = Ciosalpha(sim, kvp) - carm.rotation = Rotation.from_euler("ZYX", [0,20,0], degrees=True).as_matrix() + carm.rotation = Rotation.from_euler("ZYX", [0, 20, 0], degrees=True).as_matrix() carm.translation = [0 * cm, 0 * cm, 0 * cm] carm.collimation = [30 * mm, 10 * mm] @@ -52,7 +52,7 @@ # aluminum table table = sim.add_volume("Box", "aluminum_table") - table.size = [60 * cm, 2 * m, 0.9 * mm ] + table.size = [60 * cm, 2 * m, 0.9 * mm] table.material = "G4_Al" table.translation = [0 * m, 0 * cm, 0 * cm] table.color = [0.8, 0.8, 0.8, 1] From 80f742035d874dc6579acc9865fea8962b48b673 Mon Sep 17 00:00:00 2001 From: axelrannou Date: Fri, 28 Jun 2024 14:01:39 +0200 Subject: [PATCH 08/14] change some carm measures --- opengate/contrib/carm/siemensciosalpha.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/opengate/contrib/carm/siemensciosalpha.py b/opengate/contrib/carm/siemensciosalpha.py index 73309aaa3..11a8c0ad1 100644 --- a/opengate/contrib/carm/siemensciosalpha.py +++ b/opengate/contrib/carm/siemensciosalpha.py @@ -26,26 +26,26 @@ def __init__(self, sim, kvp): def add_carm_box(self): carmbox = self.sim.volume_manager.create_volume("Box", "carmbox") carmbox.material = "G4_AIR" - carmbox.size = [200 * cm, 30 * cm, 200 * cm] + carmbox.size = [200 * cm, 30 * cm, 120 * cm] carmbox.translation = [0 * cm, 0 * cm, 0 * cm] carmbox.color = [1, 1, 1, 0.8] hole1 = self.sim.volume_manager.create_volume("Box", "hole") - hole1.size = [191 * cm, 31 * cm, 160 * cm] + hole1.size = [191 * cm, 31 * cm, 80 * cm] hole1.color = [1, 1, 1, 0.8] hole2 = self.sim.volume_manager.create_volume("Box", "hole2") - hole2.size = [80 * cm, 31 * cm, 31 * cm] + hole2.size = [100 * cm, 31 * cm, 31 * cm] hole2.color = [1, 1, 1, 0.8] hole3 = self.sim.volume_manager.create_volume("Box", "hole3") - hole3.size = [80 * cm, 31 * cm, 31 * cm] + hole3.size = [100 * cm, 31 * cm, 31 * cm] hole3.color = [1, 1, 1, 0.8] hole1and2 = gate.geometry.volumes.unite_volumes( - hole1, hole2, translation=[-55.5 * cm, 0 * cm, 95 * cm] + hole1, hole2, translation=[-55.5 * cm, 0 * cm, 55 * cm] ) t_shape = gate.geometry.volumes.unite_volumes( - hole1and2, hole3, translation=[-55.5 * cm, 0 * cm, -95 * cm] + hole1and2, hole3, translation=[-55.5 * cm, 0 * cm, -55 * cm] ) carm = gate.geometry.volumes.subtract_volumes( @@ -62,8 +62,8 @@ def add_xray_tank(self): xray_tank = self.sim.add_volume("Box", f"{self.machine_name}_xray_tank") xray_tank.mother = self.machine_name xray_tank.material = "G4_AIR" - xray_tank.size = [40 * cm, 20 * cm, 30 * cm] - xray_tank.translation = [0 * cm, 0, 85 * cm] + xray_tank.size = [20 * cm, 20 * cm, 30 * cm] + xray_tank.translation = [0 * cm, 0, 45 * cm] xray_tank.color = [1, 1, 1, 0.8] def add_carm_source(self, kvp): From be05da7f93f7a9f3036ea69064815b478e670935 Mon Sep 17 00:00:00 2001 From: axelrannou Date: Fri, 28 Jun 2024 16:24:40 +0200 Subject: [PATCH 09/14] add option to only have source box geometry --- opengate/contrib/carm/siemensciosalpha.py | 34 +++++++++++++++---- .../tests/src/test075_siemens_cios_alpha.py | 3 +- 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/opengate/contrib/carm/siemensciosalpha.py b/opengate/contrib/carm/siemensciosalpha.py index 11a8c0ad1..6b3f68896 100644 --- a/opengate/contrib/carm/siemensciosalpha.py +++ b/opengate/contrib/carm/siemensciosalpha.py @@ -15,9 +15,10 @@ class Ciosalpha: - def __init__(self, sim, kvp): + def __init__(self, sim, kvp, source_only=False): self.sim = sim self.machine_name = "ciosalpha" + self.source_only = source_only self.volume = self.add_carm_box() self.add_xray_tank() self.add_collimators() @@ -34,23 +35,42 @@ def add_carm_box(self): hole1.size = [191 * cm, 31 * cm, 80 * cm] hole1.color = [1, 1, 1, 0.8] hole2 = self.sim.volume_manager.create_volume("Box", "hole2") - hole2.size = [100 * cm, 31 * cm, 31 * cm] + hole2.size = [90 * cm, 31 * cm, 31 * cm] hole2.color = [1, 1, 1, 0.8] hole3 = self.sim.volume_manager.create_volume("Box", "hole3") - hole3.size = [100 * cm, 31 * cm, 31 * cm] + hole3.size = [90 * cm, 31 * cm, 31 * cm] hole3.color = [1, 1, 1, 0.8] hole1and2 = gate.geometry.volumes.unite_volumes( - hole1, hole2, translation=[-55.5 * cm, 0 * cm, 55 * cm] + hole1, hole2, translation=[-50.5 * cm, 0 * cm, 55 * cm] ) - t_shape = gate.geometry.volumes.unite_volumes( - hole1and2, hole3, translation=[-55.5 * cm, 0 * cm, -55 * cm] + subtract_to_carm = gate.geometry.volumes.unite_volumes( + hole1and2, hole3, translation=[-50.5 * cm, 0 * cm, -55 * cm] ) + if self.source_only: + hole4 = self.sim.volume_manager.create_volume("Box", "hole4") + hole4.size = [45 * cm, 31 * cm, 31 * cm] + hole4.color = [1, 1, 1, 0.8] + # hole4 = self.sim.volume_manager.create_volume("Box", "hole4") + # hole4.size = [90 * cm, 31 * cm, 31 * cm] + # hole4.color = [1, 1, 1, 0.8] + hole5 = self.sim.volume_manager.create_volume("Box", "hole5") + hole5.size = [90 * cm, 31 * cm, 121* cm] + hole5.color = [1, 1, 1, 0.8] + + hole4and5 = gate.geometry.volumes.unite_volumes( + hole4, hole5, translation=[55 * cm, 0 * cm, 45 * cm] + ) + + subtract_to_carm = gate.geometry.volumes.unite_volumes( + subtract_to_carm, hole4and5, translation=[5 * cm, 0 * cm, -35* cm] + ) + carm = gate.geometry.volumes.subtract_volumes( carmbox, - t_shape, + subtract_to_carm, new_name=self.machine_name, translation=[-5 * cm, 0 * cm, -10 * cm], ) diff --git a/opengate/tests/src/test075_siemens_cios_alpha.py b/opengate/tests/src/test075_siemens_cios_alpha.py index e720cd7fd..369c8793a 100644 --- a/opengate/tests/src/test075_siemens_cios_alpha.py +++ b/opengate/tests/src/test075_siemens_cios_alpha.py @@ -20,7 +20,6 @@ sim.g4_verbose = False sim.visu = True sim.visu_type = "vrml" - sim.check_volumes_overlap = False sim.number_of_threads = 1 sim.random_seed = 12345678 sim.check_volumes_overlap = True @@ -41,7 +40,7 @@ kvp = 100 # add a carm - carm = Ciosalpha(sim, kvp) + carm = Ciosalpha(sim, kvp, source_only=True) carm.rotation = Rotation.from_euler("ZYX", [0, 20, 0], degrees=True).as_matrix() carm.translation = [0 * cm, 0 * cm, 0 * cm] carm.collimation = [30 * mm, 10 * mm] From 2afcca38d7cff442c8f27553ae981a4575db1907 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 28 Jun 2024 14:24:47 +0000 Subject: [PATCH 10/14] [pre-commit.ci] Automatic python and c++ formatting --- opengate/contrib/carm/siemensciosalpha.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opengate/contrib/carm/siemensciosalpha.py b/opengate/contrib/carm/siemensciosalpha.py index 6b3f68896..09ee371ff 100644 --- a/opengate/contrib/carm/siemensciosalpha.py +++ b/opengate/contrib/carm/siemensciosalpha.py @@ -57,7 +57,7 @@ def add_carm_box(self): # hole4.size = [90 * cm, 31 * cm, 31 * cm] # hole4.color = [1, 1, 1, 0.8] hole5 = self.sim.volume_manager.create_volume("Box", "hole5") - hole5.size = [90 * cm, 31 * cm, 121* cm] + hole5.size = [90 * cm, 31 * cm, 121 * cm] hole5.color = [1, 1, 1, 0.8] hole4and5 = gate.geometry.volumes.unite_volumes( @@ -65,7 +65,7 @@ def add_carm_box(self): ) subtract_to_carm = gate.geometry.volumes.unite_volumes( - subtract_to_carm, hole4and5, translation=[5 * cm, 0 * cm, -35* cm] + subtract_to_carm, hole4and5, translation=[5 * cm, 0 * cm, -35 * cm] ) carm = gate.geometry.volumes.subtract_volumes( From 0ab8cc4b237897d7bb7b6b3381840ceb35984569 Mon Sep 17 00:00:00 2001 From: axelrannou Date: Wed, 10 Jul 2024 11:44:39 +0200 Subject: [PATCH 11/14] Use histogram direction type instead of iso to simulate anode heel effect --- .../GateAcceptanceAngleTesterManager.cpp | 8 +- .../opengate_lib/GateGenericSource.cpp | 4 +- opengate/contrib/carm/anodeheeleffect.npz | Bin 0 -> 1108 bytes opengate/contrib/carm/siemensciosalpha.py | 75 ++++++++++-------- .../tests/src/test075_siemens_cios_alpha.py | 4 +- 5 files changed, 51 insertions(+), 40 deletions(-) create mode 100644 opengate/contrib/carm/anodeheeleffect.npz diff --git a/core/opengate_core/opengate_lib/GateAcceptanceAngleTesterManager.cpp b/core/opengate_core/opengate_lib/GateAcceptanceAngleTesterManager.cpp index 9070fb8e8..d40899b82 100644 --- a/core/opengate_core/opengate_lib/GateAcceptanceAngleTesterManager.cpp +++ b/core/opengate_core/opengate_lib/GateAcceptanceAngleTesterManager.cpp @@ -19,7 +19,7 @@ GateAcceptanceAngleTesterManager::GateAcceptanceAngleTesterManager() { } void GateAcceptanceAngleTesterManager::Initialize(py::dict puser_info, - bool is_iso) { + bool is_valid_type) { fAcceptanceAngleVolumeNames = DictGetVecStr(puser_info, "volumes"); fEnabledFlag = !fAcceptanceAngleVolumeNames.empty(); if (!fEnabledFlag) @@ -39,10 +39,10 @@ void GateAcceptanceAngleTesterManager::Initialize(py::dict puser_info, Fatal(oss.str()); } - // Cannot use SkipEvent with non iso source - if (!is_iso && fPolicy == AASkipEvent) { + // Cannot use SkipEvent with not a valid type of source + if (!is_valid_type && fPolicy == AASkipEvent) { std::ostringstream oss; - oss << "Cannot use 'SkipEvent' mode without 'iso' direction type"; + oss << "Cannot use 'SkipEvent' mode without 'iso' or 'histogram' direction type"; Fatal(oss.str()); } } diff --git a/core/opengate_core/opengate_lib/GateGenericSource.cpp b/core/opengate_core/opengate_lib/GateGenericSource.cpp index 230db67c9..0f380f933 100644 --- a/core/opengate_core/opengate_lib/GateGenericSource.cpp +++ b/core/opengate_core/opengate_lib/GateGenericSource.cpp @@ -449,10 +449,10 @@ void GateGenericSource::InitializeDirection(py::dict puser_info) { // set the angle acceptance volume if needed auto d = py::dict(puser_info["direction"]); auto dd = py::dict(d["acceptance_angle"]); - auto is_iso = ang->GetDistType() == "iso"; + auto is_valid_type = ang->GetDistType() == "iso" || ang->GetDistType() == "user"; auto &l = fThreadLocalDataAA.Get(); l.fAAManager = new GateAcceptanceAngleTesterManager; - l.fAAManager->Initialize(dd, is_iso); + l.fAAManager->Initialize(dd, is_valid_type); fSPS->SetAAManager(l.fAAManager); } diff --git a/opengate/contrib/carm/anodeheeleffect.npz b/opengate/contrib/carm/anodeheeleffect.npz new file mode 100644 index 0000000000000000000000000000000000000000..cc60bb367413b310c678ad8065fb4bca182276d9 GIT binary patch literal 1108 zcmWIWW@Zs#fB;2?)_DQ{H!w0VfG{V62t#6CdQPfdUO^=zg8*0%q!1(t0+anheFGvH z8Oj){)l*W7lZ(`?6x40fEYx)r)YI~dN{SNm;`57AQbFQwi8;loK=I;?#DY{HU&Gi! zM^i_kR)KuL_2tu>iB9<(4(pHVZ+u=)#mH4hOaDt?zAnI2<0@{@{B)mBV3< zfVJL)`5X=rk86D`S8_OrY&($iXcLFS`3~lu?mZk1UD0lw4tp;1aT+}4a426EsB!ZRhl5e&6!)fY91iciucv7p32ZP8x`|`6m9bRkyzbjF~>9FjdTV%4^IUQt| z9Jdyk$mwvSj4OKYEKY|hp1xISOE?|;WhTtzU&rad{r=L99osn_jvW-ek$8~Pp>ayY zcdpZ%4mRnt#kO4GbO=C+>T`k4}8VwHFs~&92UE^@VzS(%{MXr{E_Gce|dt*A~ zxV`7`ZtE%657uT647R(=K~R=zJJ-bbvP_ vlL#}e3=MHLh-?7IFFbprYeP*O$l6{5H6oHqfHx}}NR|l*^MUkvW)Kem$>}^u literal 0 HcmV?d00001 diff --git a/opengate/contrib/carm/siemensciosalpha.py b/opengate/contrib/carm/siemensciosalpha.py index 09ee371ff..b62719a25 100644 --- a/opengate/contrib/carm/siemensciosalpha.py +++ b/opengate/contrib/carm/siemensciosalpha.py @@ -1,7 +1,8 @@ import opengate as gate -from opengate.utility import g4_units +from scipy.spatial.transform import Rotation import numpy as np import spekpy as sp +import pathlib # useful units MeV = gate.g4_units.MeV @@ -13,6 +14,7 @@ m = gate.g4_units.m cm = gate.g4_units.cm +current_path = pathlib.Path(__file__).parent.resolve() class Ciosalpha: def __init__(self, sim, kvp, source_only=False): @@ -27,7 +29,7 @@ def __init__(self, sim, kvp, source_only=False): def add_carm_box(self): carmbox = self.sim.volume_manager.create_volume("Box", "carmbox") carmbox.material = "G4_AIR" - carmbox.size = [200 * cm, 30 * cm, 120 * cm] + carmbox.size = [200 * cm, 10 * cm, 120 * cm] carmbox.translation = [0 * cm, 0 * cm, 0 * cm] carmbox.color = [1, 1, 1, 0.8] @@ -35,7 +37,7 @@ def add_carm_box(self): hole1.size = [191 * cm, 31 * cm, 80 * cm] hole1.color = [1, 1, 1, 0.8] hole2 = self.sim.volume_manager.create_volume("Box", "hole2") - hole2.size = [90 * cm, 31 * cm, 31 * cm] + hole2.size = [101 * cm, 31 * cm, 31 * cm] hole2.color = [1, 1, 1, 0.8] hole3 = self.sim.volume_manager.create_volume("Box", "hole3") hole3.size = [90 * cm, 31 * cm, 31 * cm] @@ -53,11 +55,8 @@ def add_carm_box(self): hole4 = self.sim.volume_manager.create_volume("Box", "hole4") hole4.size = [45 * cm, 31 * cm, 31 * cm] hole4.color = [1, 1, 1, 0.8] - # hole4 = self.sim.volume_manager.create_volume("Box", "hole4") - # hole4.size = [90 * cm, 31 * cm, 31 * cm] - # hole4.color = [1, 1, 1, 0.8] hole5 = self.sim.volume_manager.create_volume("Box", "hole5") - hole5.size = [90 * cm, 31 * cm, 121 * cm] + hole5.size = [100 * cm, 31 * cm, 121 * cm] hole5.color = [1, 1, 1, 0.8] hole4and5 = gate.geometry.volumes.unite_volumes( @@ -82,7 +81,7 @@ def add_xray_tank(self): xray_tank = self.sim.add_volume("Box", f"{self.machine_name}_xray_tank") xray_tank.mother = self.machine_name xray_tank.material = "G4_AIR" - xray_tank.size = [20 * cm, 20 * cm, 30 * cm] + xray_tank.size = [10 * cm, 10 * cm, 30 * cm] xray_tank.translation = [0 * cm, 0, 45 * cm] xray_tank.color = [1, 1, 1, 0.8] @@ -92,26 +91,38 @@ def add_carm_source(self, kvp): energy_bins, weights = s.get_spectrum() - b = self.sim.add_volume("Box", f"{self.machine_name}_sourcebox") - b.mother = f"{self.machine_name}_xray_tank" - b.translation = [0 * cm, 0 * cm, 10 * cm] - b.size = [1 * cm, 1 * cm, 1 * cm] + sourcebox = self.sim.add_volume("Box", f"{self.machine_name}_sourcebox") + sourcebox.mother = f"{self.machine_name}_xray_tank" + sourcebox.translation = [0 * cm, 0 * cm, 10 * cm] + sourcebox.size = [1 * cm, 1 * cm, 1 * cm] + sourcebox.rotation = Rotation.from_euler("ZYX", [0, 90, 90], degrees=True).as_matrix() source = self.sim.add_source("GenericSource", f"{self.machine_name}_source") - source.mother = f"{self.machine_name}_sourcebox" + source.mother = sourcebox.name source.particle = "gamma" source.position.type = "disc" source.position.radius = 0 * mm source.direction_relative_to_volume = True - source.direction.type = "iso" - source.direction.theta = [0 * deg, 10 * deg] - source.direction.phi = [0 * deg, 360 * deg] + source.direction.type = "histogram" + source.direction.histogram_theta_weight = [0, 1] + source.direction.histogram_theta_angle = [85 * deg, 95 * deg] + + # TODO: Need real values for the anode heel effect + data = np.load(current_path / 'anodeheeleffect.npz') + source.direction.histogram_phi_weight = data['weight'] + source.direction.histogram_phi_angle = data['angle'] source.energy.type = "histogram" source.energy.histogram_weight = weights source.energy.histogram_energy = energy_bins + source.direction.acceptance_angle.volumes = [sourcebox.name] + source.direction.acceptance_angle.normal_flag = True + source.direction.acceptance_angle.normal_vector = [1, 0, 0] + source.direction.acceptance_angle.normal_tolerance = 5 * deg + source.direction.acceptance_angle.skip_policy = "SkipEvents" + return source def add_collimators(self): @@ -120,20 +131,20 @@ def add_collimators(self): collimators = [ { - "translation": [75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], - "size": [5 * cm, 10 * cm, 1 * mm], + "translation": [37.5 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], + "size": [2.5 * cm, 5 * cm, 1 * mm], }, { - "translation": [-75 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], - "size": [5 * cm, 10 * cm, 1 * mm], + "translation": [-37.5 * mm, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], + "size": [2.5 * cm, 5 * cm, 1 * mm], }, { - "translation": [0 * cm, 75 * mm, -z_xray_tank / 2 * mm + 3 * mm], - "size": [10 * cm, 5 * cm, 1 * mm], + "translation": [0 * cm, 37.5 * mm, -z_xray_tank / 2 * mm + 3 * mm], + "size": [5 * cm, 2.5 * cm, 1 * mm], }, { - "translation": [0 * cm, -75 * mm, -z_xray_tank / 2 * mm + 3 * mm], - "size": [10 * cm, 5 * cm, 1 * mm], + "translation": [0 * cm, -37.5 * mm, -z_xray_tank / 2 * mm + 3 * mm], + "size": [5 * cm, 2.5 * cm, 1 * mm], }, ] @@ -150,20 +161,20 @@ def add_collimators(self): killer.mother = [f"{self.machine_name}_collimator{i+1}" for i in range(4)] def set_collimation(self, collimation1, collimation2): - if not 0 <= collimation1 <= 50 or not 0 <= collimation2 <= 50: - raise ValueError("Collimation values must be between 0 and 50 mm") + if not 0 <= collimation1 <= 25 or not 0 <= collimation2 <= 25: + raise ValueError("Collimation values must be between 0 and 25 mm") - collimation1 = 50 - collimation1 - collimation2 = 50 - collimation2 + collimation1 = 25 - collimation1 + collimation2 = 25 - collimation2 xray_tank = self.sim.volume_manager.get_volume(f"{self.machine_name}_xray_tank") z_xray_tank = xray_tank.size[2] translations = [ - [75 * mm - collimation1, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], - [-75 * mm + collimation1, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], - [0 * cm, 75 * mm - collimation2, -z_xray_tank / 2 * mm + 3 * mm], - [0 * cm, -75 * mm + collimation2, -z_xray_tank / 2 * mm + 3 * mm], + [37.5 * mm - collimation1, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], + [-37.5 * mm + collimation1, 0 * cm, -z_xray_tank / 2 * mm + 1 * mm], + [0 * cm, 37.5 * mm - collimation2, -z_xray_tank / 2 * mm + 3 * mm], + [0 * cm, -37.5 * mm + collimation2, -z_xray_tank / 2 * mm + 3 * mm], ] for i, translation in enumerate(translations): diff --git a/opengate/tests/src/test075_siemens_cios_alpha.py b/opengate/tests/src/test075_siemens_cios_alpha.py index 369c8793a..6d4bc0ab4 100644 --- a/opengate/tests/src/test075_siemens_cios_alpha.py +++ b/opengate/tests/src/test075_siemens_cios_alpha.py @@ -40,10 +40,10 @@ kvp = 100 # add a carm - carm = Ciosalpha(sim, kvp, source_only=True) + carm = Ciosalpha(sim, kvp, source_only=False) carm.rotation = Rotation.from_euler("ZYX", [0, 20, 0], degrees=True).as_matrix() carm.translation = [0 * cm, 0 * cm, 0 * cm] - carm.collimation = [30 * mm, 10 * mm] + carm.collimation = [25* mm, 25 * mm] carm.source.n = 1e6 if sim.visu: From d611881f5e08cf5a999891b50ea6b49f95183ce3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 09:46:48 +0000 Subject: [PATCH 12/14] [pre-commit.ci] Automatic python and c++ formatting --- .../opengate_lib/GateAcceptanceAngleTesterManager.cpp | 3 ++- core/opengate_core/opengate_lib/GateGenericSource.cpp | 3 ++- opengate/contrib/carm/siemensciosalpha.py | 11 +++++++---- opengate/tests/src/test075_siemens_cios_alpha.py | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/core/opengate_core/opengate_lib/GateAcceptanceAngleTesterManager.cpp b/core/opengate_core/opengate_lib/GateAcceptanceAngleTesterManager.cpp index d40899b82..1b2532043 100644 --- a/core/opengate_core/opengate_lib/GateAcceptanceAngleTesterManager.cpp +++ b/core/opengate_core/opengate_lib/GateAcceptanceAngleTesterManager.cpp @@ -42,7 +42,8 @@ void GateAcceptanceAngleTesterManager::Initialize(py::dict puser_info, // Cannot use SkipEvent with not a valid type of source if (!is_valid_type && fPolicy == AASkipEvent) { std::ostringstream oss; - oss << "Cannot use 'SkipEvent' mode without 'iso' or 'histogram' direction type"; + oss << "Cannot use 'SkipEvent' mode without 'iso' or 'histogram' direction " + "type"; Fatal(oss.str()); } } diff --git a/core/opengate_core/opengate_lib/GateGenericSource.cpp b/core/opengate_core/opengate_lib/GateGenericSource.cpp index 0f380f933..88748a25d 100644 --- a/core/opengate_core/opengate_lib/GateGenericSource.cpp +++ b/core/opengate_core/opengate_lib/GateGenericSource.cpp @@ -449,7 +449,8 @@ void GateGenericSource::InitializeDirection(py::dict puser_info) { // set the angle acceptance volume if needed auto d = py::dict(puser_info["direction"]); auto dd = py::dict(d["acceptance_angle"]); - auto is_valid_type = ang->GetDistType() == "iso" || ang->GetDistType() == "user"; + auto is_valid_type = + ang->GetDistType() == "iso" || ang->GetDistType() == "user"; auto &l = fThreadLocalDataAA.Get(); l.fAAManager = new GateAcceptanceAngleTesterManager; l.fAAManager->Initialize(dd, is_valid_type); diff --git a/opengate/contrib/carm/siemensciosalpha.py b/opengate/contrib/carm/siemensciosalpha.py index b62719a25..362f1c14f 100644 --- a/opengate/contrib/carm/siemensciosalpha.py +++ b/opengate/contrib/carm/siemensciosalpha.py @@ -16,6 +16,7 @@ current_path = pathlib.Path(__file__).parent.resolve() + class Ciosalpha: def __init__(self, sim, kvp, source_only=False): self.sim = sim @@ -95,7 +96,9 @@ def add_carm_source(self, kvp): sourcebox.mother = f"{self.machine_name}_xray_tank" sourcebox.translation = [0 * cm, 0 * cm, 10 * cm] sourcebox.size = [1 * cm, 1 * cm, 1 * cm] - sourcebox.rotation = Rotation.from_euler("ZYX", [0, 90, 90], degrees=True).as_matrix() + sourcebox.rotation = Rotation.from_euler( + "ZYX", [0, 90, 90], degrees=True + ).as_matrix() source = self.sim.add_source("GenericSource", f"{self.machine_name}_source") source.mother = sourcebox.name @@ -109,9 +112,9 @@ def add_carm_source(self, kvp): source.direction.histogram_theta_angle = [85 * deg, 95 * deg] # TODO: Need real values for the anode heel effect - data = np.load(current_path / 'anodeheeleffect.npz') - source.direction.histogram_phi_weight = data['weight'] - source.direction.histogram_phi_angle = data['angle'] + data = np.load(current_path / "anodeheeleffect.npz") + source.direction.histogram_phi_weight = data["weight"] + source.direction.histogram_phi_angle = data["angle"] source.energy.type = "histogram" source.energy.histogram_weight = weights diff --git a/opengate/tests/src/test075_siemens_cios_alpha.py b/opengate/tests/src/test075_siemens_cios_alpha.py index 6d4bc0ab4..8845c457a 100644 --- a/opengate/tests/src/test075_siemens_cios_alpha.py +++ b/opengate/tests/src/test075_siemens_cios_alpha.py @@ -43,7 +43,7 @@ carm = Ciosalpha(sim, kvp, source_only=False) carm.rotation = Rotation.from_euler("ZYX", [0, 20, 0], degrees=True).as_matrix() carm.translation = [0 * cm, 0 * cm, 0 * cm] - carm.collimation = [25* mm, 25 * mm] + carm.collimation = [25 * mm, 25 * mm] carm.source.n = 1e6 if sim.visu: From ec4f93c0665d5061ba0c13ca74a5b75c799e8a06 Mon Sep 17 00:00:00 2001 From: Thomas BAUDIER Date: Mon, 19 Aug 2024 16:58:34 +0200 Subject: [PATCH 13/14] Install spekpy for test075 --- .github/workflows/main.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bba7cc5e4..5c0f229ab 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -394,6 +394,7 @@ jobs: fi pip install SimpleITK pip install gaga_phsp==0.7.2 + pip install spekpy pip install dist/opengate_core-*-${PYTHONFOLDER}-${OSNAME}*_${PLATFORM}64.whl pip install dist/opengate-*.whl export GIT_SSL_NO_VERIFY=1 From 4bf906ca001e4a7a67b699f783b4633dd14383c5 Mon Sep 17 00:00:00 2001 From: Thomas BAUDIER Date: Wed, 21 Aug 2024 10:40:23 +0200 Subject: [PATCH 14/14] For tests 072 coinc, we need the output of test072_coinc_sorter_1.py --- opengate/contrib/carm/siemensciosalpha.py | 2 +- opengate/tests/src/test068_rotation_source.py | 2 +- opengate/tests/src/test072_coinc_sorter_2keepAll.py | 9 +++++++++ .../tests/src/test072_coinc_sorter_2removeMultiples.py | 8 ++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/opengate/contrib/carm/siemensciosalpha.py b/opengate/contrib/carm/siemensciosalpha.py index 362f1c14f..5eb4d271a 100644 --- a/opengate/contrib/carm/siemensciosalpha.py +++ b/opengate/contrib/carm/siemensciosalpha.py @@ -106,7 +106,7 @@ def add_carm_source(self, kvp): source.position.type = "disc" source.position.radius = 0 * mm - source.direction_relative_to_volume = True + source.direction_relative_to_attached_volume = True source.direction.type = "histogram" source.direction.histogram_theta_weight = [0, 1] source.direction.histogram_theta_angle = [85 * deg, 95 * deg] diff --git a/opengate/tests/src/test068_rotation_source.py b/opengate/tests/src/test068_rotation_source.py index 96d9d40b7..9f07368d2 100755 --- a/opengate/tests/src/test068_rotation_source.py +++ b/opengate/tests/src/test068_rotation_source.py @@ -103,7 +103,7 @@ def test068(tab, nb_run, nb_part, nb_source): source3.direction.type = "iso" source3.direction.theta = [0 * deg, 10 * deg] source3.direction.phi = [0 * deg, 360 * deg] - source3.direction_relative_to_volume = True + source3.direction_relative_to_attached_volume = True source3.energy.type = "mono" source3.energy.mono = 1 * MeV source3.activity = n * Bq / sim.number_of_threads diff --git a/opengate/tests/src/test072_coinc_sorter_2keepAll.py b/opengate/tests/src/test072_coinc_sorter_2keepAll.py index 41d576fa9..92d21fc80 100755 --- a/opengate/tests/src/test072_coinc_sorter_2keepAll.py +++ b/opengate/tests/src/test072_coinc_sorter_2keepAll.py @@ -7,6 +7,8 @@ coincidences_sorter, copy_tree_for_dump, ) +import os +import subprocess import uproot if __name__ == "__main__": @@ -15,6 +17,13 @@ __file__, output_folder="test072_coinc_sorter" ) + # The test needs the output of test072_coinc_sorter_1.py + # If the output of test072_coinc_sorter_1.py does not exist, create it + if not os.path.isfile(paths.output / "test72_output_1.root"): + print("---------- Begin of test072_coinc_sorter_1.py ----------") + subprocess.call(["python", paths.current / "test072_coinc_sorter_1.py"]) + print("----------- End of test072_coinc_sorter_1.py -----------") + # open root file root_filename = paths.output / "test72_output_1.root" print(f"Opening {root_filename} ...") diff --git a/opengate/tests/src/test072_coinc_sorter_2removeMultiples.py b/opengate/tests/src/test072_coinc_sorter_2removeMultiples.py index 1d80f07c6..a7ab2a952 100755 --- a/opengate/tests/src/test072_coinc_sorter_2removeMultiples.py +++ b/opengate/tests/src/test072_coinc_sorter_2removeMultiples.py @@ -8,6 +8,7 @@ copy_tree_for_dump, ) import uproot +import subprocess import os if __name__ == "__main__": @@ -16,6 +17,13 @@ __file__, output_folder="test072_coinc_sorter" ) + # The test needs the output of test072_coinc_sorter_1.py + # If the output of test072_coinc_sorter_1.py does not exist, create it + if not os.path.isfile(paths.output / "test72_output_1.root"): + print("---------- Begin of test072_coinc_sorter_1.py ----------") + subprocess.call(["python", paths.current / "test072_coinc_sorter_1.py"]) + print("----------- End of test072_coinc_sorter_1.py -----------") + # open root file root_filename = paths.output / "test72_output_1.root" print(f"Opening {root_filename} ...")