From 4389b3aeeb0adf5073f399e808fc862a2c02d09b Mon Sep 17 00:00:00 2001 From: David Date: Wed, 9 Oct 2024 11:53:20 +0200 Subject: [PATCH 1/8] Enable Position Independent Code and update README Enabled Position Independent Code (PIC) in `CMakeLists.txt` by setting `CMAKE_POSITION_INDEPENDENT_CODE` to ON. Updated the README to address issues related to multithreading and Qt visualization on Windows, Python version compatibility, and added warnings for specific errors users might encounter. --- README.md | 15 +++++++++++---- core/CMakeLists.txt | 8 +++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 319c20fa2..9450f91a4 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ See the [User Guide](https://opengate-python.readthedocs.io/en/latest/user_guide ### How to install (short version) -*Compatible with Python 3.9, 3.10, 3.11, 3.12. On Windows multithreading, Qt visualization and the "spawn new subprocess" are not (yet) available.* +*Compatible with Python 3.9, 3.10, 3.11, 3.12. On Windows multithreading and Qt visualization are not (yet) available.* First, create an environment (not mandatory but highly advised) @@ -23,7 +23,7 @@ source opengate_env/bin/activate or you can use the conda environment. ``` -conda create --name opengate_env python=3.9 +conda create --name opengate_env python=3.12 conda activate opengate_env ``` @@ -33,6 +33,8 @@ pip install --upgrade pip pip install --pre opengate ``` +*Warning* while it is still beta, the `--pre` option is needed. + If you already installed the packages and want to upgrade to the latest version: ``` @@ -44,19 +46,24 @@ Once installed, you can run all tests: opengate_tests ```` -**WARNING** The first time you run this command, the geant4 data and the test data will be downloaded. If the download fails (on some systems), try to add the following command before running opengate_tests: +**WARNING (1)** The first time you run this command, the geant4 data and the test data will be downloaded. If the download fails (on some systems), try to add the following command before running opengate_tests: ```` export GIT_SSL_NO_VERIFY=1 ```` All tests are in the folder [here](https://github.com/OpenGATE/opengate/tree/master/opengate/tests/src). The test data (binary files) are stored, for technical reasons, in this git: https://gitlab.in2p3.fr/opengamgate/gam_tests_data (which is stored as a git submodule). -**WARNING** Some tests (e.g. test034) needs [gaga-phsp](https://github.com/dsarrut/gaga-phsp) which needs [pytorch](https://pytorch.org/) that cannot really be automatically installed by the previous pip install (at least we don't know how to do). So, in order to run those tests, you will have to install both PyTorch and gaga-phsp first with +**WARNING (2)** Some tests (e.g. test034) needs [gaga-phsp](https://github.com/dsarrut/gaga-phsp) which needs [pytorch](https://pytorch.org/) that cannot really be automatically installed by the previous pip install (at least we don't know how to do). So, in order to run those tests, you will have to install both PyTorch and gaga-phsp first with ```` pip install torch pip install gaga-phsp ```` +**WARNING (3)** With some linux systems (not all), you may encounter an error similar to “cannot allocate memory in static TLS block”. In that case, you must add a specific path to the linker as follows: +```` +export LD_PRELOAD=::${LD_PRELOAD} +```` + The documentation is here: https://opengate-python.readthedocs.io/en/latest/user_guide.html The test history can be visualized here: https://opengate.github.io/opengate_tests_results/ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index bd30553d6..f7cb20681 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -14,6 +14,9 @@ project(opengate_core) set(CMAKE_CXX_STANDARD 17) +# Enable Position Independent Code for all targets (-fPIC) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + # Need Geant4 find_package(Geant4 REQUIRED OPTIONAL_COMPONENTS qt) include(${Geant4_USE_FILE}) @@ -103,13 +106,8 @@ if (WIN32) endif () endif () -# correct one on OSX -#target_link_libraries(opengate_core PRIVATE pybind11::module ${Geant4_LIBRARIES} Threads::Threads ${ITK_LIBRARIES}) - # additional utilities target_include_directories(opengate_core PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/external/) #set(CMAKE_VERBOSE_MAKEFILE on) target_link_libraries(opengate_core PRIVATE pybind11::module ${Geant4_LIBRARIES} Threads::Threads ${ITK_LIBRARIES} fmt::fmt-header-only) - -# Do not not add ${PYTHON_LIBRARIES}) here (seg fault) From 9ed8f88880d477e61b2dc6d5b842571a2f6dbc76 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 9 Oct 2024 11:53:32 +0200 Subject: [PATCH 2/8] Restore original overlap check flag after modifications Preserved the original `check_volumes_overlap` flag after temporarily setting it for overlap checking. Additionally, renamed an old, unused function for clarity. --- opengate/contrib/phantoms/nemaiec.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/opengate/contrib/phantoms/nemaiec.py b/opengate/contrib/phantoms/nemaiec.py index 0831c11c6..721a20ae5 100644 --- a/opengate/contrib/phantoms/nemaiec.py +++ b/opengate/contrib/phantoms/nemaiec.py @@ -39,6 +39,7 @@ def add_iec_phantom( create_material(simulation) # check overlap only for debug + original_check_overlap_flag = simulation.check_volumes_overlap simulation.check_volumes_overlap = check_overlap # Outside structure @@ -64,6 +65,7 @@ def add_iec_phantom( simulation, name, thickness_z, sphere_starting_angle, toggle_sphere_order ) + simulation.check_volumes_overlap = original_check_overlap_flag return iec @@ -544,7 +546,7 @@ def get_n_samples_from_ratio(n, ratio): return n_samples -def compute_sphere_centers_and_volumes(sim, name): +def compute_sphere_centers_and_volumes_OLD_NEVER_CALLED(sim, name): spheres_diam = [10, 13, 17, 22, 28, 37] centers = [] volumes = [] From 2518f8ec18300cd9c7d66028d0dad7a60a3120dd Mon Sep 17 00:00:00 2001 From: David Date: Wed, 9 Oct 2024 11:53:46 +0200 Subject: [PATCH 3/8] Add Jaszczak phantom with background source in simulation Introduce functions to define the Jaszczak phantom and its materials in simulations. Enable background source addition and volume configuration for accurate setups in phantom studies. --- opengate/contrib/phantoms/jaszczak.py | 85 +++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 opengate/contrib/phantoms/jaszczak.py diff --git a/opengate/contrib/phantoms/jaszczak.py b/opengate/contrib/phantoms/jaszczak.py new file mode 100644 index 000000000..5f8ba24fc --- /dev/null +++ b/opengate/contrib/phantoms/jaszczak.py @@ -0,0 +1,85 @@ +from opengate.contrib.phantoms.nemaiec import dump_bg_activity +from opengate.utility import g4_units + + +def add_phantom_material(simulation): + elems = ["H", "C", "O"] + w = [0.080538, 0.599848, 0.319614] + gcm3 = g4_units.g_cm3 + simulation.volume_manager.material_database.add_material_weights( + "JASZCZAK_PMMA", elems, w, 1.19 * gcm3 + ) + + +def add_jaszczak_phantom(simulation, name="jaszczak", check_overlap=False): + # https://www.meditest.fr/documentation/4.pdf + # unit + add_phantom_material(simulation) + + # check overlap only for debug + original_check_overlap_flag = simulation.check_volumes_overlap + simulation.check_volumes_overlap = check_overlap + + # Outside structure + jaszczak, internal_cylinder = add_jaszczak_body(simulation, name) + jaszczak.material = "JASZCZAK_PMMA" + + simulation.check_volumes_overlap = original_check_overlap_flag + return jaszczak, internal_cylinder + + +def add_jaszczak_body(sim, name): + cm = g4_units.cm + mm = g4_units.mm + deg = g4_units.deg + white = [1, 1, 1, 1] + thickness = 3.2 * mm + + # main cylinder interior + external_cylinder = sim.add_volume("Tubs", name=f"{name}_cylinder") + external_cylinder.rmax = 21.6 * cm / 2.0 + thickness + external_cylinder.rmin = 0 + external_cylinder.dz = 18.6 * cm / 2 + thickness * 2 + external_cylinder.sphi = 0 * deg + external_cylinder.dphi = 360 * deg + external_cylinder.material = "JASZCZAK_PMMA" + external_cylinder.color = white + + # main cylinder interior + internal_cylinder = sim.add_volume("Tubs", name=f"{name}_internal_cylinder") + internal_cylinder.mother = external_cylinder.name + internal_cylinder.rmax = 21.6 * cm / 2 + internal_cylinder.rmin = 0 + internal_cylinder.dz = 18.6 * cm / 2 + internal_cylinder.sphi = 0 * deg + internal_cylinder.dphi = 360 * deg + internal_cylinder.material = "G4_AIR" + + return external_cylinder, internal_cylinder + + +def add_background_source( + simulation, jaszczak_name, src_name, activity_bqml, verbose=False +): + # source + bg = simulation.add_source("GenericSource", f"{jaszczak_name}_{src_name}") + bg.mother = f"{jaszczak_name}_internal_cylinder" + v = simulation.volume_manager.volumes[bg.mother] + v_info = v.solid_info + # (1 cm3 = 1 mL) + """ + # no need to confine yet, nothing inside the cylinder -> should be done later + bg.position.type = "box" + bg.position.size = simulation.volume_manager.volumes[bg.mother].bounding_box_size + # this source is confined only within the mother volume, it does not include daughter volumes + # it is a tubs inside the box + bg.position.confine = bg.mother + """ + bg.particle = "e+" + bg.energy.type = "F18" + bg.activity = activity_bqml * v_info.cubic_volume + + if verbose: + s = dump_bg_activity(simulation, jaszczak_name, src_name) + print(s) + return bg, v_info.cubic_volume From a0d0ebe7f000c0efd5d886fb666ad262e312bd9b Mon Sep 17 00:00:00 2001 From: David Date: Wed, 9 Oct 2024 11:54:19 +0200 Subject: [PATCH 4/8] linux: workaround to remove TLS bug Added a function to set and verify GLIBC_TUNABLES environment variable, restarting the process if needed. This ensures better configuration management on Linux systems, . --- opengate/__init__.py | 67 +++++++++++++------------------------------- 1 file changed, 19 insertions(+), 48 deletions(-) diff --git a/opengate/__init__.py b/opengate/__init__.py index df806a5e4..8d2e68bd1 100644 --- a/opengate/__init__.py +++ b/opengate/__init__.py @@ -1,56 +1,27 @@ # This file handles the way opengate is imported. +import os +import sys -""" -import colored -import threading -print( - colored.stylize( - f"Importing opengate (thread " f"{threading.get_native_id()}) ... ", - colored.fore("dark_gray"), - ), - end="", - flush=True, -) -print(colored.stylize("done", colored.fore("dark_gray"))) -""" -# the following modules are imported respecting the package structure -# they will be available via -# `import opengate` -# `opengate.xxx.yyy` -# Modules that are mainly for internal use, such as runtiming.py or uisessions.py -# are not automatically imported. If a user needs them, s/he must import -# them specifically, e.g. `import opengate.uisessions` +def restart_with_glibc_tunables(): + """ + Restart the current process with GLIBC_TUNABLES set. + """ + # tunables_value = "glibc.rtld.optional_static_tls=2048000" + tunables_value = "glibc.rtld.optional_static_tls=1500000" -# subpackages -import opengate.sources + # Check if the environment variable is already set correctly + if os.environ.get("GLIBC_TUNABLES") != tunables_value: + # Set the environment variable + new_env = os.environ.copy() + new_env["GLIBC_TUNABLES"] = tunables_value -import opengate.geometry -import opengate.geometry.materials -import opengate.geometry.solids -import opengate.geometry.utility -import opengate.geometry.volumes + # Restart the process with the new environment + os.execve(sys.executable, [sys.executable] + sys.argv, new_env) -import opengate.actors -import opengate.contrib + # Exit the current process + sys.exit() -# modules directly under /opengate/ -import opengate.managers -import opengate.utility -import opengate.logger -import opengate.exception -import opengate.runtiming -import opengate.definitions -import opengate.userhooks -import opengate.image -import opengate.physics -import opengate.base -import opengate.engines -# import opengate.postprocessors - -# These objects are imported at the top level of the package -# because users will frequently use them -from opengate.managers import Simulation -from opengate.managers import create_sim_from_json -from opengate.utility import g4_units +if sys.platform.startswith("linux"): + restart_with_glibc_tunables() From e361f57041033d66cface485dc2576a0f77bc81f Mon Sep 17 00:00:00 2001 From: David Date: Wed, 9 Oct 2024 13:18:22 +0200 Subject: [PATCH 5/8] redo import that were removed by mistake --- opengate/__init__.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/opengate/__init__.py b/opengate/__init__.py index 8d2e68bd1..6dab941ea 100644 --- a/opengate/__init__.py +++ b/opengate/__init__.py @@ -25,3 +25,34 @@ def restart_with_glibc_tunables(): if sys.platform.startswith("linux"): restart_with_glibc_tunables() + +# subpackages +import opengate.sources +import opengate.geometry +import opengate.geometry.materials +import opengate.geometry.solids +import opengate.geometry.utility +import opengate.geometry.volumes +import opengate.actors +import opengate.contrib + +# modules directly under /opengate/ +import opengate.managers +import opengate.utility +import opengate.logger +import opengate.exception +import opengate.runtiming +import opengate.definitions +import opengate.userhooks +import opengate.image +import opengate.physics +import opengate.base +import opengate.engines + +# import opengate.postprocessors + +# These objects are imported at the top level of the package +# because users will frequently use them +from opengate.managers import Simulation +from opengate.managers import create_sim_from_json +from opengate.utility import g4_units From ff8bc7c99409c87bbd41fcc8a42cc36b7363905f Mon Sep 17 00:00:00 2001 From: David Date: Thu, 10 Oct 2024 15:27:51 +0200 Subject: [PATCH 6/8] Set specific random seed and update sum_tolerance Changed the random seed from "auto" to 87456321 for reproducibility. Adjusted the sum_tolerance from 1.0 to 1.1 in the y-axis profile test to increase tolerance limit. --- opengate/tests/src/test015_iec_phantom_4.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/opengate/tests/src/test015_iec_phantom_4.py b/opengate/tests/src/test015_iec_phantom_4.py index 45d9696a9..90ce94b35 100755 --- a/opengate/tests/src/test015_iec_phantom_4.py +++ b/opengate/tests/src/test015_iec_phantom_4.py @@ -22,7 +22,7 @@ # main options sim.check_volumes_overlap = True - sim.random_seed = "auto" # 123456789 + sim.random_seed = 87456321 sim.output_dir = paths.output sim.progress_bar = True @@ -74,7 +74,7 @@ axis="y", tolerance=50, ignore_value=0, - sum_tolerance=1.0, + sum_tolerance=1.1, sad_profile_tolerance=3.0, ) From 36f90902883e63a2e69b52f624c590b229fa634e Mon Sep 17 00:00:00 2001 From: Thomas BAUDIER Date: Mon, 14 Oct 2024 09:04:38 +0200 Subject: [PATCH 7/8] Remove LD_PRELOAD in the CI and in the init --- .github/workflows/main.yml | 4 --- core/opengate_core/__init__.py | 46 ++++++++++++---------------------- 2 files changed, 16 insertions(+), 34 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f7f18c846..aafb2c768 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -397,10 +397,6 @@ jobs: if [[ ${{ matrix.os }} == "ubuntu-latest" ]]; then path=`opengate_library_path.py -p site_packages` export LD_LIBRARY_PATH="${path}/opengate_core.libs":${LD_LIBRARY_PATH} - path=`opengate_library_path.py -p libG4processes` - export LD_PRELOAD=${path}:${LD_PRELOAD} - path=`opengate_library_path.py -p libG4geometry` - export LD_PRELOAD=${path}:${LD_PRELOAD} fi if [[ ${{ matrix.os }} == "windows-latest" ]]; then path=`opengate_library_path.py -p site_packages` diff --git a/core/opengate_core/__init__.py b/core/opengate_core/__init__.py index ecc53ba5d..a28b44d72 100644 --- a/core/opengate_core/__init__.py +++ b/core/opengate_core/__init__.py @@ -41,37 +41,23 @@ def get_libG4_path(lib): "site-packages" in pathCurrentFile ): # opengate_core is installed using wheel (for "pip install -e .", the paths are different) reloadPython = False - if ( - "LD_LIBRARY_PATH" not in os.environ - or os.path.join(get_site_packages_dir(), "opengate_core.libs") - not in os.environ["LD_LIBRARY_PATH"] - ): - reloadPython = True + #if ( + # "LD_LIBRARY_PATH" not in os.environ + # or os.path.join(get_site_packages_dir(), "opengate_core.libs") + # not in os.environ["LD_LIBRARY_PATH"] + #): + # reloadPython = True - if ( - "LD_PRELOAD" not in os.environ - or get_libG4_path("processes") not in os.environ["LD_PRELOAD"] - or get_libG4_path("geometry") not in os.environ["LD_PRELOAD"] - ): - reloadPython = True - - if reloadPython: - print( - "opengate_core is not detected. Be sure to execute these lines before to run python:" - ) - print( - "export LD_LIBRARY_PATH=" - + os.path.join(get_site_packages_dir(), "opengate_core.libs") - + ":${LD_LIBRARY_PATH}" - ) - print( - "export LD_PRELOAD=" - + get_libG4_path("processes") - + ":" - + get_libG4_path("geometry") - + ":${LD_PRELOAD}" - ) - sys.exit(-1) + #if reloadPython: + # print( + # "opengate_core is not detected. Be sure to execute these lines before to run python:" + # ) + # print( + # "export LD_LIBRARY_PATH=" + # + os.path.join(get_site_packages_dir(), "opengate_core.libs") + # + ":${LD_LIBRARY_PATH}" + # ) + # sys.exit(-1) elif sys.platform == "win32": print(os.path.dirname(pathCurrentFile)) os.add_dll_directory(os.path.dirname(pathCurrentFile)) From f48b2ff7cd21efc7e14edd79ee5f46c1094152bb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 07:05:04 +0000 Subject: [PATCH 8/8] [pre-commit.ci] Automatic python and c++ formatting --- core/opengate_core/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/opengate_core/__init__.py b/core/opengate_core/__init__.py index a28b44d72..1ec9fc4fe 100644 --- a/core/opengate_core/__init__.py +++ b/core/opengate_core/__init__.py @@ -41,14 +41,14 @@ def get_libG4_path(lib): "site-packages" in pathCurrentFile ): # opengate_core is installed using wheel (for "pip install -e .", the paths are different) reloadPython = False - #if ( + # if ( # "LD_LIBRARY_PATH" not in os.environ # or os.path.join(get_site_packages_dir(), "opengate_core.libs") # not in os.environ["LD_LIBRARY_PATH"] - #): + # ): # reloadPython = True - #if reloadPython: + # if reloadPython: # print( # "opengate_core is not detected. Be sure to execute these lines before to run python:" # )