Skip to content

fix(CSGOptiX): handle coincident sibling faces - #346

Open
ggalgoczi wants to merge 9 commits into
mainfrom
qbnd-sibling-pair-matline
Open

fix(CSGOptiX): handle coincident sibling faces#346
ggalgoczi wants to merge 9 commits into
mainfrom
qbnd-sibling-pair-matline

Conversation

@ggalgoczi

@ggalgoczi ggalgoczi commented May 29, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR fixes GPU optical-photon propagation where two sibling CSG volumes share the same face, as can happen in segmented radiator bars with glue layers and multi-layer lenses.

At a shared face, OptiX can select a boundary involving the common parent volume instead of the adjacent sibling. The photon may then use the wrong optical material or behave as if it crossed a thin layer of the parent medium, leading to incorrect refraction, total internal reflection, and lost photons.

What changes

  • Carries the photon's current optical material across boundary interactions so absorption and scattering use the correct medium.
  • Prefers the entering-sibling intersection when exit and entry faces are coincident. A 10 µm adjustment is used only for hit ordering; the physical intersection position remains unchanged.
  • Enables the face-ordering adjustment per boundary when the maximum refractive-index contrast exceeds 0.1, using the existing boundary table without detector-specific configuration.
  • Enables the adjustment for high-contrast boundaries such as quartz/air (~0.47) while leaving near-matched boundaries such as aerogel/air (~0.025) unchanged.
  • Updates the dRICH validation example to use the bundled single-sector geometry.

@ggalgoczi ggalgoczi self-assigned this May 29, 2026
@ggalgoczi ggalgoczi added the enhancement New feature or request label May 29, 2026
@ggalgoczi
ggalgoczi marked this pull request as draft May 29, 2026 14:09
@ggalgoczi
ggalgoczi marked this pull request as ready for review May 30, 2026 14:57
@ggalgoczi
ggalgoczi requested a review from plexoos May 30, 2026 14:57
@ggalgoczi
ggalgoczi force-pushed the qbnd-sibling-pair-matline branch from 15e0da7 to 6f9d9bd Compare May 30, 2026 15:00
@ggalgoczi
ggalgoczi force-pushed the qbnd-sibling-pair-matline branch from a212e4c to 325e81f Compare May 30, 2026 15:57
@plexoos
plexoos requested a review from Copilot June 10, 2026 15:05

Copilot AI left a comment

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.

Pull request overview

This PR addresses two OptiX failure modes at CSG sibling primitives with coincident faces by (1) carrying the photon’s “authoritative current-medium” material-line across boundary steps, and (2) biasing the reported intersection distance on the exiting side so OptiX consistently selects the intended “entering sibling” hit in coplanar tie cases.

Changes:

  • Add sctx::current_matline and propagate it across boundary interactions, using it to override m1 in qbnd::fill_state when valid.
  • Introduce per-boundary gating (params.boundary_face_bias) and apply a +10 µm t bias on exit-side coplanar intersections in __intersection__is.
  • Compute the per-boundary face-bias gate from the bnd table in CSGOptiX::initSimulate and plumb it through Params.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
sysrap/sctx.h Adds per-photon carried current_matline state to sctx.
qudarap/qsim.h Updates/propagates current_matline at boundaries and passes it into qbnd::fill_state.
qudarap/qbnd.h Extends fill_state to optionally override m1_line using carried matline.
CSGOptiX/Params.h Adds boundary_face_bias pointer to launch params.
CSGOptiX/Params.cc Initializes boundary_face_bias to nullptr.
CSGOptiX/CSGOptiX7.cu Seeds carried matline at birth and biases optixReportIntersection t for exit-side coplanar sibling faces.
CSGOptiX/CSGOptiX.cc Computes per-boundary face-bias gate from bnd refractive-index contrast and uploads it to device memory.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CSGOptiX/CSGOptiX7.cu Outdated
Comment on lines +442 to +449
// F7: seed carried matline from the genstep's source-medium bnd row
// (gs.q0.u.z == scerenkov/sscint matline). Updated through
// propagate_at_boundary. Guard the -1 sentinel (0xFFFFFFFF) that
// SEvt::setGenstep emits for bad_ck/unmapped mtindex.
{
const unsigned gm = gs.q0.u.z;
ctx.current_matline = (gm == 0xFFFFFFFFu) ? 0u : gm;
}
Comment thread qudarap/qbnd.h Outdated
Comment on lines +213 to +220
// Guards: 0 = "no carry" (legacy callers); 0xFFFFFFFF = the lookup_mtline=-1
// sentinel from SEvt::setGenstep, not a valid table row; and the carried
// slot must be a material row (OMAT/IMAT), not a surface row (OSUR/ISUR).
const unsigned matline_slot = carried_matline & 0x3u;
if (carried_matline != 0u && carried_matline != 0xFFFFFFFFu && (matline_slot == unsigned(OMAT) || matline_slot == unsigned(IMAT)))
{
m1_line = int(carried_matline);
}
Comment thread CSGOptiX/CSGOptiX.cc Outdated
Comment on lines +645 to +656
static unsigned char* s_face_bias = nullptr;
static unsigned s_face_bias_num = 0u;
if (s_face_bias == nullptr)
{
CUDA_CHECK(cudaMalloc((void**)&s_face_bias, num_bnd));
s_face_bias_num = num_bnd;
}
if (s_face_bias_num == num_bnd)
{
CUDA_CHECK(cudaMemcpy(s_face_bias, bits.data(), num_bnd, cudaMemcpyHostToDevice));
params->boundary_face_bias = s_face_bias;
}
Comment thread CSGOptiX/CSGOptiX.cc Outdated
Comment on lines +641 to +644
}
bits[b] = (dnmax > DN_THRESHOLD) ? 1u : 0u;
LOG(LEVEL) << "boundary_face_bias bnd " << b << " dnmax " << dnmax << " gate " << unsigned(bits[b]);
}
@plexoos
plexoos force-pushed the qbnd-sibling-pair-matline branch from c243bd4 to d82daf4 Compare July 28, 2026 22:16
@plexoos plexoos self-assigned this Jul 29, 2026
@plexoos plexoos changed the title qbnd/qsim: carry source-medium matline across CSG sibling-pair faces Fix GPU optical propagation across coincident sibling CSG faces Jul 29, 2026
@plexoos plexoos changed the title Fix GPU optical propagation across coincident sibling CSG faces fix(CSGOptiX): handle coincident sibling faces Jul 29, 2026
plexoos pushed a commit that referenced this pull request Jul 30, 2026
Seed the carried material line only for Cerenkov and scintillation gensteps, whose q0.z field has that contract. Treat UINT_MAX as the unavailable sentinel so material line zero remains valid, and validate carried rows against the uploaded boundary table before property or optical-buffer lookup.

Replace the fixed 10 um, refractive-index-gated exit bias with a simulation-only one-ULP tie-break. This lets an entering sibling win exact coplanar ties without crossing a representable physical gap, while preserving the unbiased intersection for position advancement. Removing the old gate also eliminates its static GPU allocation and per-boundary debug logging.

Add regression coverage for genstep material-line ownership; valid line-zero and inner-material carries; sentinel, surface-row, and out-of-range fallbacks; and the updated Params layout.

Validated with a Release CUDA/OptiX build of CSGOptiX, OpticksGenstepTest, qbnd_boundary_lookup_test, and ParamsTest. All three focused CTest cases pass.

Refs: #346

Refs: #346 (comment)

Refs: #346 (comment)

Refs: #346 (comment)

Refs: #346 (comment)
@plexoos
plexoos force-pushed the qbnd-sibling-pair-matline branch from d82daf4 to f5177fe Compare July 30, 2026 15:28
ggalgoczi and others added 6 commits July 30, 2026 12:37
GBndLib does not enumerate boundary entries for CSG sibling-pair faces (two daughters of one parent sharing a face), so OptiX can pick a boundary whose m1/m2 do not reference the photon's current medium -> absorption/scattering sampled in the wrong material1. Carry the photon's matline (seeded from the genstep at birth in CSGOptiX7.cu, updated each propagate_at_boundary) in sctx::current_matline and use it as m1 in qbnd::fill_state when valid. No-op for correctly-enumerated boundaries (carried == the boundary's own m1_line).
…dent faces

At a face shared by two sibling primitives (segmented radiator bars + glue,
multi-layer CSG lens) OptiX sorts the two coplanar intersections
inconsistently; the "exiting" hit (cosI>0) carries the parent medium as m2,
wrong for a sibling crossing. Bias the t reported to OptiX's closest-hit sort
by +10 um on the exit side so the entering sibling wins. The real unbiased
distance is preserved in the PRD, so photon position advancement is unperturbed.

Gate the bias per boundary by index contrast |n_m1 - n_m2| > 0.1 (max over
wavelength), computed at translation in CSGOptiX::initSimulate from the bnd
table and read in __intersection__is. High-contrast faces (e.g. quartz/air
~0.47) need the entering-sibling selection; low-contrast thin gaps (e.g.
aerogel/air ~0.025) are harmed by it and gate off. No per-detector hardcode;
nullptr => always-on fallback.

Stacks on the matline-carry m1 fix (carry source-medium matline across CSG
sibling-pair faces): that fixes *which material* m1 is sampled; this fixes
*which face* OptiX selects. The two are complementary - on a TIR light-guide
either alone collapses the GPU hit count.

Verified ePIC hpDIRC mu- 3 GeV: GPU/CPU 0.987, bit-identical to the ungated
bias (all glued-sibling faces clear the 0.1 threshold). Low-index-contrast
detectors are unaffected (gate stays off at their thin gaps).
Reflection never changes the photon's medium, so recomputing the carry
from this boundary's own m1 line on the reflect branch could clobber a
sibling-pair override from qbnd::fill_state with the parent medium's
line (e.g. after a Fresnel/TIR reflection at a sibling-coincident face),
poisoning m1 lookups at every subsequent boundary. Leave the carry
untouched on reflect; transmit keeps the entered-medium (m2) line as
before.
The script referenced epic_drich_simple.xml / drich_1sector_simple.xml,
which were never committed, and hardcoded /tmp/simphony as the script
directory. Use the bundled single-sector compact via DRICH_1SECTOR_XML,
matching benchmark_drich.py, and derive the script directory from
__file__ so the example runs from any checkout location.

Note: constructing the 1-sector geometry against epic >= 26.06 still
fails until the bundled radiator XML gains the <coronas> element that
newer epic DRICH plugins require; that applies equally to
benchmark_drich.py and is left to a separate change.
Seed the carried material line only for Cerenkov and scintillation gensteps, whose q0.z field has that contract. Treat UINT_MAX as the unavailable sentinel so material line zero remains valid, and validate carried rows against the uploaded boundary table before property or optical-buffer lookup.

Replace the fixed 10 um, refractive-index-gated exit bias with a simulation-only one-ULP tie-break. This lets an entering sibling win exact coplanar ties without crossing a representable physical gap, while preserving the unbiased intersection for position advancement. Removing the old gate also eliminates its static GPU allocation and per-boundary debug logging.

Add regression coverage for genstep material-line ownership; valid line-zero and inner-material carries; sentinel, surface-row, and out-of-range fallbacks; and the updated Params layout.

Validated with a Release CUDA/OptiX build of CSGOptiX, GenstepTest, qbnd_boundary_lookup_test, and ParamsTest. All three focused CTest cases pass.

Refs: #346

Refs: #346 (comment)

Refs: #346 (comment)

Refs: #346 (comment)

Refs: #346 (comment)
@plexoos
plexoos force-pushed the qbnd-sibling-pair-matline branch from f5177fe to 57340fc Compare July 30, 2026 16:41
Align OpticksGenstep_UsesMaterialLine with the current genstep protocol after #429 retired the five legacy suffixed identifiers. Only the generic Cerenkov, scintillation, and modified G4 Cerenkov producers retain the q0.z material-line contract.

Cover numeric protocol slots 1 through 5 as retired non-material-line gensteps so a future reintroduction cannot silently restore the stale classification.

Validated by rebuilding CSGOptiX7_ptx and OpticksGenstepTest and running SysRapTest.OpticksGenstepTest plus SysRapTest.GenstepTest.

Refs: #346

Refs: #429
@plexoos
plexoos force-pushed the qbnd-sibling-pair-matline branch from effd842 to a9fd8a3 Compare July 30, 2026 17:39
@plexoos
plexoos force-pushed the qbnd-sibling-pair-matline branch from a9fd8a3 to 72ddeaa Compare July 30, 2026 17:42
Exercise the carried material-line lifecycle directly through qsim, checking that transmission records the entered material while reflection preserves the current one.

Share the CUDA-local genstep classification and reported-distance rules with an on-device test covering TORCH/Cerenkov handling and exact entering-versus-exiting intersection ties.

Add a compact touching-sibling GDML fixture and simg4ox CPU/GPU hit comparison so the production propagation path must cross the shared face and retain the correct medium properties to reach the sensor.

Refs: #346
@github-actions

Copy link
Copy Markdown
Contributor

Cpp-Linter Report ⚠️

Some files did not pass the configured checks!

clang-format (v21.1.8) reports: 2 file(s) not formatted
  • CSGOptiX/CSGOptiXHelpers.h
  • qudarap/tests/QSim_MaterialCarryTest.cc

Have any feedback or feature suggestions? Share it here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants