Monitoring JUNOSW + Opticks : GPU ray trace accelerated optical photon simulation

Monitoring JUNOSW + Opticks : GPU ray trace accelerated optical photon simulation

Open source, https://github.com/simoncblyth/opticks

Simon C Blyth, IHEP — JUNO Collaboration Meeting, IHEP, Beijing — (21 July 2026)


Outline

newtons-opticks.png


(JUNO) Optical Photon Simulation Problem...

Opticks solves this using GPU ray tracing via NVIDIA OptiX


Four NVIDIA RTX Gen : Ray Tracing : ~2x every ~2 yrs

Gen Model Year VRAM CUDA Cores RT (Ray tracing)
GB GB/s Cores TFLOPS Rise
Turing Quadro RTX 6000 2018 24 672 4,608 72 ~34 [1]
Ampere RTX A6000 2020 48 768 10,752 84 ~76 2.2x
Ada RTX 6000 Ada 2023 48 960 18,176 142 ~211 2.7x
Blackwell RTX PRO 6000 2025 96 1792 24,064 188 ~380 1.8x

NVIDIA RT TFLOPS: synthetic ray trace metric -- Turing -> Blackwell : 11x

(Equivalent FLOPs per Ray Intersection) x (Intersections per clock) x (Core Clock) x (Number of RT Cores)

[1] baseline : 2018 "World's First Ray-Tracing GPU -- 10 Gigarays/sec"


NVIDIA® OptiX™ Ray Tracing Engine -- Accessible GPU Ray Tracing

OptiX makes GPU ray tracing accessible

OptiX features

User provides (Green):

Latest Release : NVIDIA® OptiX™ 9.1.0 (Dec 2025)


Geant4 + Opticks + NVIDIA OptiX : Hybrid Workflow

Opticks enables Geant4 based simulation to offload optical photon simulation to the GPU

NVIDIA GPU ray tracing of billions[1] of rays per second applied to optical simulation

[1] Actual performance depends on geometry and its modelling, JUNO optical simulation speedups > 1000x Geant4 have been measured


Opticks+JUNOSW (OJ) Monitoring

Opticks : fast replacement for Geant4 optical sim, BUT FRAGILE:

Accelerate dev-cycle via automated : Validation + Performance Mon.

OJ + Opticks Monitoring : https://code.ihep.ac.cn/blyth/oj/

"Pipeline" works, NEXT: extend coverage to all important tests


opticks_monitoring_gitlab_ci_20260716_141838.png


opticks_monitoring_grafana_20260715_160444.png

Opticks Grafana Dashboard
  • 1. interactive time window
  • 2. direct links from gitlab-ci

opticks_monitoring_grafana_20260715_155458.png

Web Interface -> Edit Grafana viz of "cxs_min.sh" - GPU time vs photon
  • Viz based on SQL queries from SQLite3 DB tables
  • Grafana very popular => AI very helpful

Opticks+JUNOSW (OJ) Monitoring : Goal

Nightly : DETAILED Validation + Performance Monitoring

Requires, for all important tests:

  1. expand DB schema to hold summary results
    • tables : ~/opticks/sysrap/sreportdb.sql
  2. add reporting stage that writes summary results into DB tables
    • python OR C++ using ~/np/NSQLite.h
    • insert into tables : ~/opticks/sysrap/sreportdb.h
    • control script : ~/opticks/sysrap/tests/sreportdb.sh
  3. add Grafana visualization of results + alerts for deviations
    • use web interface to develop SQL query + configure viz

Preparations for Three Species LS model

Preparations for Three Species LS model


MC Method : Sampling using Cumulative Distribution Function (CDF)

/env/presentation/normal_pdf_cdf.png

Aim : create sample (a set of values) that follows an analytic PDF

Cumulative distribution function (CDF) : Prob( X < x)

Map uniform randoms onto probabilities

  1. integrate PDF -> CDF (mapping PDF domain onto probability [0,1] )
  2. invert the CDF, so domain becomes [0,1]
  3. inverted_CDF(uniform random) -> sample value

Intuitively : throw randoms uniformly "vertically"

CDF "encodes" shape of PDF in convenient form for sampling

https://en.wikipedia.org/wiki/Inverse_transform_sampling


Monte Carlo Method : Modelling Scintillator Re-emission

/env/presentation/inverted_cdf_GScintillatorLib_py.png
reemission_probability(wavelength)
fraction of light absorbed in scintillator re-emitted with different wavelength.
generating re-emitted wavelength
  • expected PDF(wavelength) -> inverted_CDF(probability)

Opticks Re-emission model

float u_reemit = curand_uniform(&rng);
if (u_reemit < reemission_probability )
{
    ...
    p.wavelength = reemission_lookup(curand_uniform(&rng));    # re-emission texture lookup  
    s.flag = BULK_REEMIT ;
    return CONTINUE;
}
else
{
    s.flag = BULK_ABSORB ;
    return BREAK;
}

https://bitbucket.org/simoncblyth/opticks/src/default/optixrap/cu/propagate.h


Scint wavelength generation in triplicate (LAB, bisMSB, PPO)

U4ScintThree.h Geant4 : ScintIntegral::GetEnergy -> ICDF
QTexLayered.h CUDA layered texture from array (layers, height, width, payload )
qscint_three.h wavelength_hd20 tex atlas lookup with random u0 -> wl
QScintThree.h
  • Scint ICDF texture atlas 3*3 layers (3 species, 3 zone LHS/MID/RHS)
  • initialize qscint_three.h on CPU, upload to GPU
 0        e-m   e   e+m  ["HD20": e=.05,m=e/10]   -e-m 1-e 1-e+m        1
 |         |    |    |                             |    |    |          |
 +---LHS-------------+                             +----------RHS-------+
 |         +----:----------------MID--------------------:----+          |
 |              :    |        1 - 2*(e-m)          |    :               |
 |              :    |                             |    :               |
 |        u < e :    |     otherwise MID zone      |    : u > 1 - e     |
 |  x=u/(e+m)   :    | x = (u-(e-m))/(1-2*(e-m))   | x=(u-(1-e-m))/(e+m)|
 

float qscint_three::wavelength_hd20(int species_idx, float u ) const


QScintThree_test_sh_COMP_012_LOGY1_EDGE1_DPI300

1e9 wavelengths - (logscale) G4:Opticks diff not visible - Chi2/ndf < 1.001


QScintThree_test_sh_COMP_012_LOGY0_EDGE1_DPI300

1e9 wavelengths - (linear) G4:Opticks diff not visible - Chi2/ndf < 1.001


QScintThree_test_sh_COMP_012_LOGY1_EDGE1_DPI300_BOFFSET1e7

1e9 wavelengths - (lin+offset) G4:Opticks diff not visible - Chi2/ndf < 1.001


Scaling Opticks

Geant4 + Opticks + NVIDIA OptiX : Production Scaling ?

=> Client-Server architecture


Geant4 + Opticks + NVIDIA OptiX : Hybrid Workflow 2x2 ?

Geant4 + Opticks + NVIDIA OptiX : Monolith x4 ?


Geant4 + Opticks + NVIDIA OptiX : Hybrid Workflow 4x4 ?

Geant4 + Opticks + NVIDIA OptiX : Monolith x16 ?

"Monolithic" scaling : very inefficient use of scarce GPU resources


OpticksClients + OpticksService : Share GPUs


Client.png


Opticks "Optical Core" => Server, "Periphery" => Client

Package Role Client Server
SysRap Geometry and event types, array NP.hh
CSG CPU/GPU geometry model
QUDARap CUDA optical simulation
CSGOptiX OptiX 7+ geometry, GPU ray trace
U4 Geometry convert, collect gensteps, return hits
G4CX Top level interface, acts via SSimulator
  Client Server
depends: NVIDIA GPU + CUDA + OptiX Geant4, U4, G4CX
depends: libcurl 7.76.1+, NP_CURL.h python, FastAPI, nanobind
SSimulator: SOpticksClientSimulator CSGOptiX

Client build from common Opticks codebase with OPTICKS_CONFIG=Client


NP_CURL.h : Array transport via HTTP POST

Basis for Opticks Client - using libcurl 7.76.1+ (2021) - default in many Linux distro

NP* NP_CURL::transformRemote( NP* a, size_t index, size_t count )
HTTP POST array to endpoint, receive array in response, metadata in headers

NP.hh : C++ array with NumPy serialization (Opticks numerical base), NP_CURL.h headers:

HTTP metadata headers Note
x-opticks-shape array shape eg "(10,6,4)" for 10 gensteps
x-opticks-dtype eg "float32"
x-opticks-index eg eventID controlling random number stream offsets
x-opticks-count eg: number of photons in genstep, cost of request
x-opticks-meta general eg geometry root node digest - assert same geometry

HTTP 429, 503 : Too Many Requests, Service Unavailable (temporary downtime)

https://github.com/simoncblyth/np/blob/master/NP_CURL.h -- https://github.com/simoncblyth/np/blob/master/NP.hh


Opticks Server Prototype : python + FastAPI + nanobind + CSGOptiX

CSGOptiX/tests/CSGOptiXService_FastAPI_test/CSGOptiXService_FastAPI_test.sh

Prototype client + service operational

"Roll your own" Prototype Server : Educational, BUT:


NVIDIA Triton Inference Server (aka Dynamo-Triton)

Triton[1] : open-source, designed to accelerate AI deployment at scale

Wrap Opticks as Custom C++ Triton Backend "model(s)" ?

Make request load like inference : smaller, more uniform

Make requests during genstep collection :
  • => tunable max_slots
  • decouple compute from physics
High GPU utilization => concurrency :
  • async CUDA Opticks
  • async CUDA memory pools [2]
Robust Server (MC campaign):
  • static flat VRAM
  • backpressure, signal client retry

[1] https://developer.nvidia.com/dynamo-triton

[2] cudaMallocFromPoolAsync