Skip to content

Feat/aligned validation#271

Open
ggalgoczi wants to merge 14 commits intomainfrom
feat/aligned-validation
Open

Feat/aligned validation#271
ggalgoczi wants to merge 14 commits intomainfrom
feat/aligned-validation

Conversation

@ggalgoczi
Copy link
Copy Markdown
Contributor

@ggalgoczi ggalgoczi commented Apr 4, 2026

Add infrastructure for photon-by-photon aligned comparison between GPU and G4. StandAloneGeant4Validation provides a CPU-only G4 simulation with --aligned mode using precooked curand sequences via U4Random. Includes AlignedOpticalPhysics with ShimG4Op* wrappers for random number consumption alignment. G4ValidationGenstep enables electron-primary genstep validation. Achieves 97.7% flag match on fresnel_single.gdml.

ggalgoczi and others added 14 commits April 4, 2026 02:35
…hmark

Pure Geant4 optical photon simulation with no opticks/GPU dependencies.
Loads GDML, fires torch photons, collects hits via G4VSensitiveDetector,
saves g4_hits.npy for comparison with GPU output.

Supports multi-threaded mode via G4MTRunManager (-t N flag) by splitting
photons across multiple events. Sequential mode (-t 0) for reproducibility.
Adds photon-by-photon aligned comparison with GPU via U4Random
precooked curand sequences. Includes PhotonFateAccumulator for
indexed g4_photon.npy output, AlignedOpticalPhysics with
InstrumentedG4OpBoundaryProcess, and U4Recorder integration
for SEvt lifecycle and random alignment.
…ess dependency

Use standard G4OpticalPhysics and direct U4Random::SetSequenceIndex.
Only requires linking U4 (for U4Random) — no u4/ source modifications needed.
Uses ShimG4OpAbsorption/ShimG4OpRayleigh instead of standard
G4OpticalPhysics when --aligned. Improves match from 95.7% to 97.7%
by matching the GPU's RNILL random consumption pattern.
Generates Philox random streams matching GPU seeding for U4Random
aligned mode. 113 lines, compiles with nvcc, no opticks dependencies
beyond NP.hh.
Compares GPU and G4 photon.npy arrays element-by-element:
flag match, position match, distributions, divergent photon listing.
Fires configurable electron in LAr, G4 produces scintillation/Cerenkov
photons and tracks them to SiPM detection. For comparison with GPURaytrace
which uses G4CXOpticks to hand gensteps to GPU for optical propagation.

Tested: 1 MeV e- in det.gdml produces ~170 SiPM hits/event.
Relax MaxRecord assert to permit values above sseq::SLOTS (32).
In DebugLite mode, respect OPTICKS_MAX_RECORD env var if set,
falling back to the default record_limit (32) otherwise. This
enables full step history recording (e.g. 1000 steps) for photon
path analysis without affecting production performance.
G4OpticalPhysics defaults to delta time profile for G4OpWLS,
which applies a fixed delay equal to WLSTIMECONSTANT. The
physically correct model is exponential decay sampling:
dt = -WLSTIMECONSTANT * log(u). The GPU implementation already
uses exponential. This fix aligns G4 with GPU and with the
correct stochastic decay physics.
SetWLSTimeProfile in GPURaytrace changed G4OpticalParameters
during physics list initialization, inadvertently altering the
electron tracking (different genstep count). The WLS time profile
setting only matters for StandAloneGeant4Validation where G4
tracks optical photons directly. In GPURaytrace, the GPU handles
optical physics so the G4 time profile is irrelevant.
Update `G4MultiUnion` navigation to use the public Geant4 API and finalize generated multi-unions
with `Voxelize()`.

- remove the `G4MultiUnion`-specific `NoVoxels` path from `sysrap/ssolid.h`
- use the standard `G4VSolid` navigation calls:
  - `Inside`
  - `DistanceToIn`
  - `DistanceToOut`
- enable `Voxelize()` in `U4SolidMaker::GridMultiUnion_`

Geant4 11.4.1 no longer permits external use of `G4MultiUnion::InsideNoVoxels`, which breaks the
build.

Rather than keeping a version-specific workaround, this change aligns with the public Geant4
navigation interface and ensures `G4MultiUnion` instances are finalized for voxel-aware navigation.

This change intentionally removes an older `G4MultiUnion` special case that used Geant4's
`*NoVoxels` navigation methods.

Relevant history in this repo:

- `61b075287` `check G4MultiUnion BoxGrid and OrbGrid` This appears to be the origin of the pattern.
  It introduced a `DistanceMultiUnionNoVoxels_` helper using `InsideNoVoxels`,
  `DistanceToInNoVoxels`, and `DistanceToOutNoVoxels`. In the same change, `GridMultiUnion_` was
  added with `Voxelize()` commented out, which suggests the no-voxel path was deliberately paired
  with partially constructed `G4MultiUnion` test geometry.

- `9bfe9c94f` `pull x4solid.h Distance functions out of X4Intersect for usage from X4Simtrace` This
  propagated the same `G4MultiUnion` no-voxel logic into a reusable utility layer.

- `e614494e3` `generalize Geant4 volume/solid intersect plotting to any level of transforms using
  U4Tree/stree in u4/tests/U4PMTFastSimGeomTest` This copied the same special case into
  `sysrap/ssolid.h`, which is the direct ancestor of the code changed in this PR.

There is also later related context in:

- `079896e04` `enable geometry translation ... to avoid G4 voxelization SEGV` This indicates there
  were broader concerns in the codebase around Geant4 voxelization stability for some geometry
  flows.

Taken together, the historical pattern suggests the `NoVoxels` path was introduced as a pragmatic
workaround for `G4MultiUnion` behavior in older testing and translation workflows, especially when
`Voxelize()` was not always being called.

This PR moves back to the public Geant4 navigation API and restores the expected `Voxelize()` step
for generated `G4MultiUnion` grid solids, which is required for the standard navigation path and
    also avoids reliance on private/internal Geant4 APIs in 11.4.x.
Add U4SolidMakerTest2 to validate G4MultiUnion distance and inside calculations through the standard
voxelized Geant4 navigation API, exercising the code path changed by the removal of the NoVoxels
special case in ssolid.h.

Test OrbOrbMultiUnion1 (3 orbs of radius 50mm at x=-100,0,+100):
- DistanceToIn from outside along Z and X axes into each orb
- DistanceToOut from inside centre and right orbs
- ray entering the leftmost orb along the +X axis
- miss for a ray parallel above all orbs
- Inside() at origin, orb centres, surface, and interior points

Test BoxFourBoxContiguous (45mm cube + 4 flanking boxes):
- DistanceToIn from outside along Z into the centre cube
- DistanceToOut from origin along +Z
- DistanceToIn hitting a flanking +X box from outside
- miss for a far-offset parallel ray
- Inside() at origin, flanking box interior, and far exterior

Test EnsureVoxelizedMultiUnion safety net:
- confirm OrbOrbMultiUnion (no explicit Voxelize() call) navigates correctly through Make(), which
  calls EnsureVoxelizedMultiUnion
- verify Inside() and Distance_ return expected values at the origin
@ggalgoczi ggalgoczi requested a review from plexoos April 4, 2026 02:54
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 4, 2026

Cpp-Linter Report ⚠️

Some files did not pass the configured checks!

clang-format (v20.1.2) reports: 7 file(s) not formatted
  • src/G4ValidationGenstep.cpp
  • src/G4ValidationGenstep.h
  • src/StandAloneGeant4Validation.cpp
  • src/StandAloneGeant4Validation.h
  • sysrap/SEventConfig.cc
  • u4/U4SolidMaker.cc
  • u4/tests/U4SolidMakerTest2.cc

Have any feedback or feature suggestions? Share it here.

@ggalgoczi ggalgoczi mentioned this pull request Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants