Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d43b18d
docs: update README
plexoos Aug 1, 2025
c22acd1
move code from esi-g4ox and rename files
plexoos Aug 1, 2025
752f10a
build: add new executable simg4oxmt
plexoos Aug 1, 2025
7940314
add run_performance.py script
plexoos Aug 1, 2025
22ee1ae
add geom file pfrich_min_FINAL.gdml from esi-g4ox
plexoos Aug 1, 2025
9ac2772
fix paths in performance script
plexoos Aug 1, 2025
a017d95
make run_performance to executable
plexoos Aug 1, 2025
234bff2
Apply suggestions from code review
plexoos Aug 4, 2025
efe9b80
set output dir to /tmp
plexoos Aug 4, 2025
0ef017d
refactor(run_performance): use argparse and pathlib for I/O path conf…
plexoos Aug 5, 2025
eb82acd
Update tests/geom/pfrich_min_FINAL.gdml
plexoos Dec 5, 2025
e4ef18b
Update src/simg4oxmt.cpp
plexoos Dec 5, 2025
f3f7a16
Update src/simg4oxmt.cpp
plexoos Dec 5, 2025
7aa127d
Update README
plexoos Dec 8, 2025
472b72b
Add OPTICKS_EVENT_MODE environment variable
ggalgoczi Dec 8, 2025
ab81134
Make run_performance.py use OPTICKS_HOME environment variable so it r…
ggalgoczi Dec 11, 2025
f3d977e
Move particle gun to create photons in raindrop geom
ggalgoczi Dec 12, 2025
3a4bc9c
Reduce number of beamOn events from 50000 to 500 to be sure to make i…
ggalgoczi Dec 12, 2025
4096e4e
Add log file for performance run outputs
ggalgoczi Dec 12, 2025
0c4da79
Update README with user-defined inputs
ggalgoczi Dec 12, 2025
22bf1e0
Update README with setStackPhotons and GDML details
ggalgoczi Dec 12, 2025
5e4b12b
Change default GDML path raindrop in run_performance.py
ggalgoczi Dec 12, 2025
13729f3
Enhance README with performance studies details
ggalgoczi Dec 12, 2025
a76e557
Merge branch 'main' into performance-studies
plexoos Dec 15, 2025
96e1e8d
docs(README): fix formatting/line-wrapping
plexoos Dec 15, 2025
ff34c93
style(opticks_raindrop.gdml): whitespace and indentation cleanup only
plexoos Dec 15, 2025
04f4d4a
workaround for incorrect ray intersections with open cones
plexoos Jul 30, 2025
8be5074
Revert "Move particle gun to create photons in raindrop geom"
plexoos Dec 15, 2025
6e16acb
Revert "workaround for incorrect ray intersections with open cones"
plexoos Dec 16, 2025
8936907
workaround for incorrect ray intersections with open cones - 2
plexoos Dec 16, 2025
a4bb33f
Revert "workaround for incorrect ray intersections with open cones - 2"
plexoos Dec 18, 2025
683cf3a
Update position in GeneratePrimaries method
ggalgoczi Dec 23, 2025
4101e08
Reorder physvolref elements in opticks_raindrop.gdml
ggalgoczi Jan 6, 2026
7fd2968
Log number of PhotonHits at end of event
ggalgoczi Jan 6, 2026
1715f2e
Change kinetic energy of particle from 5 GeV to 0.005 GeV
ggalgoczi Jan 7, 2026
b327ff2
Revert "Change kinetic energy of particle from 5 GeV to 0.005 GeV"
plexoos Jan 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions CSG/csg_intersect_leaf_newcone.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,5 +119,3 @@ void intersect_leaf_newcone( bool& valid_isect, float4& isect, const quad& q0, c
isect.w = t_cand ;
}
}


63 changes: 63 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,66 @@ Process](https://geant4-userdoc.web.cern.ch/UsersGuides/ForApplicationDeveloper/
...
</gdml>
```


## User/developer defined inputs

### Defining primary particles

There are certain user defined inputs that the user/developer has to define. In
the ```src/simg4oxmt``` example that imports ```src/g4appmt.h``` we provide
a working example with a simple geometry. The User/developer has to change the
following details: **Number of primary particles** to simulate in a macro file
and the **number of G4 threads**. For example:

```
/run/numberOfThreads {threads}
/run/verbose 1
/process/optical/cerenkov/setStackPhotons {flag}
/run/initialize
/run/beamOn 500
```

Here setStackPhotons defines **whether G4 will propagate optical photons or
not**. In production Opticks (GPU) takes care of the optical photon propagation.
Additionally the user has to define the **starting position**, **momentum** etc
of the primary particles define in the **GeneratePrimaries** function in
``src/g4appmt.h```. The hits of the optical photons are returned in the
**EndOfRunAction** function. If more photons are simulated than can fit in the
GPU RAM the execution of a GPU call should be moved to **EndOfEventAction**
together with retriving the hits.

### Loading in geometry into EIC-Opticks

EIC-Opticks can import geometries with GDML format automatically. There are
about 10 primitives supported now, eg. G4Box. G4Trd or G4Trap are not supported
yet, we are working on them. ```src/simg4oxmt``` takes GDML files through
arguments, eg. ```src/simg4oxmt -g mygdml.gdml```.

The GDML must define all optical properties of surfaces of materials including:
- Efficiency (used by EIC-Opticks to specify detection efficiency and assign
sensitive surfaces)
- Refractive index
- Group velocity
- Reflectivity
- Etc.


## Performance studies

In order to quantify the speed-up achieved by EIC-Opticks compared to G4 we
provide a python code that runs the same G4 simulation with and without tracking
optical photons in G4. The difference of the runs will yield the time required
to simulate photons. Meanwhile the same photons are simulated on GPU with
EIC-Opticks and the simulation time is saved.

```
mkdir -p /tmp/out/dev
mkdir -p /tmp/out/rel

docker build -t eic-opticks:perf-dev --target=develop
docker run --rm -t -v /tmp/out:/tmp/out eic-opticks:perf-dev run-performance -o /tmp/out/dev

docker build -t eic-opticks:perf-rel --target=release
docker run --rm -t -v /tmp/out:/tmp/out eic-opticks:perf-rel run-performance -o /tmp/out/rel
```
114 changes: 114 additions & 0 deletions optiphy/tools/run_performance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import argparse
import subprocess
import re
import os
from pathlib import Path

run_mac_template = """
/run/numberOfThreads {threads}
/run/verbose 1
/process/optical/cerenkov/setStackPhotons {flag}
/run/initialize
/run/beamOn 500

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this number reduced? What small GPU RAM are you referring to? For consistency, we should be able to reproduce the published results using the same number of events.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this repository, the event count is set to a conservative default because the goal of this example is to help users get a first successful run with EIC-Opticks and understand the workflow, not to serve as a paper quality reproduction test. Since users run on a wide range of hardware, a high default can fail on GPUs with limited VRAM and lead to crashes/segfaults that are frustrating to debug as a first step.

Also, because we swapped the geometry, the performance and scaling will not match the paper one-to-one anyway, the achievable speedup depends on geometry and workload characteristics.

To keep things consistent and reproducible, the README explicitly notes that users should set the event count themselves.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, what kind of speedup do you see with this geometry? Is it far from the public numbers?

If I use pfrich_min_FINAL.gdml and the patch for open cones, should I expect to reproduce the numbers in the paper?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I did not check. Can you check how much speedup you get?

  2. Yes. I tested it and sent you the example result Dec 8. 3:58 PM in mattermost chat.

"""

os.environ["OPTICKS_EVENT_MODE"] = "Minimal"

def get_opticks_home():
"""Get OPTICKS_HOME from environment, warn if not set."""
opticks_home = os.environ.get("OPTICKS_HOME")
if opticks_home is None:
print("Warning: $OPTICKS_HOME is not defined, so this script should be called from the eic-opticks directory.")
return Path(".")
return Path(opticks_home)

def parse_real_time(time_str):
# Parses 'real\t0m41.149s' to seconds
match = re.search(r'real\s+(\d+)m([\d.]+)s', time_str)
if match:
minutes = int(match.group(1))
seconds = float(match.group(2))
return minutes * 60 + seconds
return None

def parse_sim_time(output):
match = re.search(r"Simulation time:\s*([\d.]+)\s*seconds", output)
if match:
return float(match.group(1))
return None

def main():
opticks_home = get_opticks_home()

parser = argparse.ArgumentParser()
parser.add_argument('-g', '--gdml', type=Path, default=None, help="Path to a custom GDML geometry file (relative to current directory)")
parser.add_argument('-o', '--outpath', type=Path, default=Path('./'), help="Path where the output file will be saved")
args = parser.parse_args()

# If gdml not provided, use default path relative to OPTICKS_HOME
if args.gdml is None:
gdml_path = opticks_home / 'tests/geom/opticks_raindrop.gdml'
else:
# User-provided path is used as-is (relative to current directory or absolute)
gdml_path = args.gdml

# run.mac is created in OPTICKS_HOME
run_mac_path = opticks_home / "run.mac"

geant_file = args.outpath / "timing_geant.txt"
optix_file = args.outpath / "timing_optix.txt"
log_file = args.outpath / "g4logs.txt"

with open(geant_file, "w") as gfile, open(optix_file, "w") as ofile, open(log_file, "w") as logfile:
for threads in range(50, 0, -1):
times = {}
sim_time_true = None
for flag in ['true', 'false']:
# Write run.mac with current flag
with open(run_mac_path, "w") as rm:
rm.write(run_mac_template.format(threads=threads, flag=flag))

# Run with time in bash to capture real/user/sys
cmd = f"time simg4oxmt -g {gdml_path} -m {run_mac_path}"
print(f"Running {threads} threads: {cmd}")
result = subprocess.run(
["bash", "-c", cmd],
capture_output=True, text=True
)
stdout = result.stdout
stderr = result.stderr
# Save full output to log file
logfile.write(f"\n{'='*60}\n")
logfile.write(f"Threads: {threads}, StackPhotons: {flag}\n")
logfile.write(f"{'='*60}\n")
logfile.write(f"--- STDOUT ---\n{stdout}\n")
logfile.write(f"--- STDERR ---\n{stderr}\n")
logfile.flush()

# Save simulation time for true run only
if flag == 'true':
sim_time_true = parse_sim_time(stdout + stderr)
if sim_time_true is not None:
ofile.write(f"{threads} {sim_time_true}\n")
ofile.flush()

# Extract real time
real_match = re.search(r"real\s+\d+m[\d.]+s", stderr)
if real_match:
real_sec = parse_real_time(real_match.group())
times[flag] = real_sec
else:
print(f"[!] Could not find 'real' time for threads={threads} flag={flag}")

# Write the difference to timing_geant.txt (true - false)
if 'true' in times and 'false' in times:
diff = times['true'] - times['false']
gfile.write(f"{threads} {diff}\n")
gfile.flush()
else:
print(f"[!] Missing times for threads={threads}")

print("Done.")

if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ dependencies = [
generate-input-photons = "optiphy.tools.generate_input_photons:main"
plot-csg = "optiphy.tools.plot_csg:main"
serve-path = "optiphy.tools.serve_path:main"
run-performance = "optiphy.tools.run_performance:main"

[build-system]
requires = ["setuptools", "wheel"]
Expand Down
10 changes: 9 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ target_include_directories(simg4ox PRIVATE
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
)

# simg4ox runs Geant4 and OptiX simulations
add_executable(simg4oxmt simg4oxmt.cpp g4appmt.h)
target_link_libraries(simg4oxmt gphox)
target_include_directories(simg4oxmt PRIVATE
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
)

# simtox creates a numpy file with initial photons for simulation
add_executable(simtox simtox.cpp)

Expand All @@ -42,7 +50,7 @@ target_include_directories(simtox PRIVATE
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
)

install(TARGETS consgeo simg4ox simtox gphox
install(TARGETS consgeo simg4ox simg4oxmt simtox gphox
EXPORT ${PROJECT_NAME}Targets
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
Expand Down
Loading