Skip to content

Commit

Permalink
Merge pull request #214 from legend-exp/python
Browse files Browse the repository at this point in the history
setup for running Python scripts in unit tests
  • Loading branch information
gipert authored Dec 31, 2024
2 parents bab9044 + 1906556 commit bcba362
Show file tree
Hide file tree
Showing 10 changed files with 206 additions and 11 deletions.
57 changes: 53 additions & 4 deletions docs/developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,24 +106,73 @@ tarball. This can be downloaded and inspected from the GitHub actions run page:
navigate to "Actions", select the CI run of interest, scroll to the bottom of
the page.

Cheatsheet:
:::{tip} Cheatsheet:

```console
$ ctest --print-labels # see all defined test labels
$ ctest -L vis # run only tests with label "vis"
$ ctest -R basics-mt/print-volumes.mac # run only this test
```

If you want to open a fanci UI to check the output of `vis` tests, you may
achieve it by:
:::

:::{tip} If you want to open a fancy UI to check the output of `vis` tests, you
may achieve it by:

1. `cd` to `test/confinement`
1. edit `macros/_vis.mac` to make sure you load an interactive UI (e.g.
`/vis/open OI`)
1. edit the macro you are interested in and swap `_init.mac` with `_vis.mac` at
the very beginning (after `/control/execute`)
1. run the visualization with
`remage -i -g gdml/geometry.gdml -- macros/themacro.mac`
`remage -i -g gdml/geometry.gdml -- macros/themacro.mac` :::

### Configuring CMake

You may add a new test with the
[`add_test()`](https://cmake.org/cmake/help/latest/command/add_test.html) CMake
command. Use the `category/test-name` convention to name tests. Here are few
examples:

If the test is supposed to run the `remage` executable, you can use the
`REMAGE_PYEXE`, which stores its path:

```cmake
add_test(NAME basics/test COMMAND ${REMAGE_PYEXE} [<arg>...])
```

If you want to use the low-level `remage-cpp` executable, you can directly use
the `remage-cli-cpp` target:

```cmake
add_test(NAME basics/test COMMAND remage-cli-cpp [<arg>...])
```

If you want to run a Python script, the recommended interpreted to use is the
one from the virtual environment set up in the build area by CMake. Its path is
stored in the `PYTHONPATH` variable:

```cmake
add_test(NAME basics/test COMMAND ${PYTHONPATH} script.py)
```

In case a simulation output file needs to be generated with _remage_, before a
series of tests is ran with Python scripts, one can do so with a fixture:

```cmake
add_test(
NAME germanium/gen-output
COMMAND ${REMAGE_PYEXE} -g gdml/geometry.gdml -w -o output.lh5 -- macros/run.mac)
set_tests_properties(germanium/gen-output PROPERTIES FIXTURES_SETUP output-fixture)
add_test(NAME germanium/bremsstrahlung COMMAND ${PYTHONPATH} ./test_brem.py)
set_tests_properties(germanium/bremsstrahlung PROPERTIES FIXTURES_REQUIRED output-fixture)
add_test(NAME germanium/e-range COMMAND ${PYTHONPATH} ./test_e_range.py)
set_tests_properties(germanium/e-range PROPERTIES FIXTURES_REQUIRED output-fixture)
```

For more complete examples, have a look at the existing tests.

## Documentation

Expand Down
25 changes: 20 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ authors = [
{ name = "Luigi Pertoldi", email = "[email protected]" },
]
maintainers = [
{ name = "The LEGEND Collaboration" },
{ name = "The LEGEND Collaboration" },
]
description = "A python wrapper for remage"
readme = "README.md"
Expand All @@ -32,17 +32,32 @@ classifiers = [
]
dynamic = ["version"]
dependencies = [
"legend-pydataobj",
"legend-pydataobj",
]

[project.optional-dependencies]
test = [
"pytest >=6",
"pytest-cov >=3",
"awkward",
"hist[plot]",
"legend-pygeom-optics",
"legend-pygeom-hpges",
"legend-pygeom-tools",
"matplotlib",
"mplhep",
"numpy",
"pandas",
"pyg4ometry",
"numba", # without this, uv somehow fails to pick a good numba version
]
dev = [
"pytest >=6",
"pytest-cov >=3",
docs = [
"sphinx-copybutton",
"sphinx-togglebutton",
"sphinx-inline-tabs",
"exhale",
"breathe",
"myst-parser",
]

[project.scripts]
Expand Down
16 changes: 14 additions & 2 deletions python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,29 @@ add_custom_command(

add_custom_target(python-virtualenv DEPENDS ${VENV_DIR}/bin/uv)

# install the remage wrapper package into the virtual environment with uv (build area)
# store the path to the python executable, needed later in tests
set_target_properties(python-virtualenv PROPERTIES PYTHONPATH ${VENV_DIR}/bin/python)

# install the remage wrapper package into the virtual environment with uv
# (build area), including dependencies for running tests
# NOTE: when uv/pip installs the package and creates the executable for the cli,
# it hardcodes the path to the current python executable (e.g. the one of the
# virtualenv) in the script's shebang

# need more packages if we also want to use this venv later for tests
if(BUILD_TESTING)
set(_pkg_install "${CMAKE_SOURCE_DIR}[test]")
else()
set(_pkg_install ${CMAKE_SOURCE_DIR})
endif()

add_custom_command(
OUTPUT ${VENV_DIR}/bin/remage
COMMAND
cp
${CMAKE_CURRENT_BINARY_DIR}/cpp_config.build.py # now we want to use the cpp_config for the build area
${CMAKE_CURRENT_SOURCE_DIR}/remage/cpp_config.py
COMMAND ${VENV_DIR}/bin/python -m uv -q pip install --reinstall ${CMAKE_SOURCE_DIR}
COMMAND ${VENV_DIR}/bin/python -m uv -q pip install --reinstall ${_pkg_install}
DEPENDS python-virtualenv ${PYTHON_SOURCES}
COMMENT "Installing remage Python wrapper into the virtual environment")

Expand Down
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ project(remage-tests)
# in case the remage-cpp executable is needed, one can just use the
# "remage-cli-cpp" target name (see add_test() docs)
get_target_property(REMAGE_PYEXE remage-cli PYEXE_PATH)
get_target_property(PYTHONPATH python-virtualenv PYTHONPATH)

add_subdirectory(basics)
add_subdirectory(confinement)
add_subdirectory(germanium)
add_subdirectory(internals)
add_subdirectory(output)
add_subdirectory(python)
Expand Down
21 changes: 21 additions & 0 deletions tests/germanium/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
file(
GLOB _aux
RELATIVE ${PROJECT_SOURCE_DIR}
macros/*.mac gdml/*.gdml *.py)

# copy them to the build area
foreach(_file ${_aux})
configure_file(${PROJECT_SOURCE_DIR}/${_file} ${PROJECT_BINARY_DIR}/${_file} COPYONLY)
endforeach()

add_test(NAME germanium/gen-output COMMAND ${REMAGE_PYEXE} -g gdml/geometry.gdml -w -o output.lh5
-- macros/run.mac)
set_tests_properties(germanium/gen-output PROPERTIES LABELS extra FIXTURES_SETUP output-fixture)

add_test(NAME germanium/bremsstrahlung COMMAND ${PYTHONPATH} ./test_brem.py)

add_test(NAME germanium/e-range COMMAND ${PYTHONPATH} ./test_e_range.py)

foreach(_test germanium/bremsstrahlung germanium/e-range)
set_tests_properties(${_test} PROPERTIES LABELS extra FIXTURES_REQUIRED output-fixture)
endforeach()
52 changes: 52 additions & 0 deletions tests/germanium/gdml/geometry.gdml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" ?>
<gdml xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://cern.ch/service-spi/app/releases/GDML/schema/gdml.xsd">
<define/>
<materials>
<isotope name="Ge70" Z="32" N="70">
<atom value="69.924"/>
</isotope>
<isotope name="Ge72" Z="32" N="72">
<atom value="71.922"/>
</isotope>
<isotope name="Ge73" Z="32" N="73">
<atom value="72.923"/>
</isotope>
<isotope name="Ge74" Z="32" N="74">
<atom value="73.921"/>
</isotope>
<isotope name="Ge76" Z="32" N="76">
<atom value="75.921"/>
</isotope>
<element name="ElementNaturalGermanium" formula="NatGe">
<fraction ref="Ge70" n="0.2057"/>
<fraction ref="Ge72" n="0.2745"/>
<fraction ref="Ge73" n="0.0775"/>
<fraction ref="Ge74" n="0.365"/>
<fraction ref="Ge76" n="0.0773"/>
</element>
<material name="NaturalGermanium">
<D value="5.3234"/>
<fraction ref="ElementNaturalGermanium" n="1"/>
</material>
</materials>
<solids>
<orb name="world" r="20" lunit="cm"/>
<orb name="germanium" r="15" lunit="cm"/>
</solids>
<structure>
<volume name="germanium">
<materialref ref="NaturalGermanium"/>
<solidref ref="germanium"/>
</volume>
<volume name="world">
<materialref ref="G4_Galactic"/>
<solidref ref="world"/>
<physvol name="germanium">
<volumeref ref="germanium"/>
</physvol>
</volume>
</structure>
<setup name="Default" version="1.0">
<world ref="world"/>
</setup>
</gdml>
24 changes: 24 additions & 0 deletions tests/germanium/gdml/geometry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from __future__ import annotations

import legendhpges as hpges
import pyg4ometry as pg4

reg = pg4.geant4.Registry()

# create a world volume
world_s = pg4.geant4.solid.Orb("world", 20, registry=reg, lunit="cm")
world_l = pg4.geant4.LogicalVolume(world_s, "G4_Galactic", "world", registry=reg)
reg.setWorld(world_l)

# let's make a germanium balloon
natge = hpges.materials.make_natural_germanium(reg)

ge_s = pg4.geant4.solid.Orb("germanium", 15, registry=reg, lunit="cm")
ge_l = pg4.geant4.LogicalVolume(ge_s, natge, "germanium", registry=reg)
pg4.geant4.PhysicalVolume(
[0, 0, 0], [0, 0, 0], ge_l, "germanium", world_l, registry=reg
)

w = pg4.gdml.Writer()
w.addDetector(reg)
w.write("geometry.gdml")
18 changes: 18 additions & 0 deletions tests/germanium/macros/run.mac
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/RMG/Geometry/RegisterDetector Germanium germanium 001

/run/initialize

/RMG/Generator/Confine Volume
/RMG/Generator/Confinement/Geometrical/AddSolid Sphere
/RMG/Generator/Confinement/Geometrical/Sphere/OuterRadius 10

/RMG/Generator/Confinement/Geometrical/CenterPositionX 0
/RMG/Generator/Confinement/Geometrical/CenterPositionY 0
/RMG/Generator/Confinement/Geometrical/CenterPositionZ 0

/RMG/Generator/Select GPS
/gps/particle e-
/gps/ang/type iso
/gps/energy 1000 keV

/run/beamOn 10000
1 change: 1 addition & 0 deletions tests/germanium/test_brem.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# lh5.read()
1 change: 1 addition & 0 deletions tests/germanium/test_e_range.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# lh5.read()

0 comments on commit bcba362

Please sign in to comment.