Skip to content

feat(csg): support partial-phi spheres and tubes - #426

Merged
plexoos merged 3 commits into
mainfrom
support-partial-spheres-tubes
Jul 29, 2026
Merged

feat(csg): support partial-phi spheres and tubes#426
plexoos merged 3 commits into
mainfrom
support-partial-spheres-tubes

Conversation

@plexoos

@plexoos plexoos commented Jul 28, 2026

Copy link
Copy Markdown
Member

Summary

Add analytic phi-wedge support for incomplete G4Sphere and G4Tubs geometries during Geant4-to-CSG conversion.

The conversion now preserves startPhi and deltaPhi on centered ZSphere and Cylinder leaves. Their intersection and signed-distance implementations account for both radial cut faces as well as the existing curved surfaces and end caps.

Add shared host/device phi-interval helpers for curved-surface and cap filtering, radial cut-face intersections and normals, and signed-distance classification. Keep conservative full-radius bounds, preserve the legacy OldCylinder center encoding, and support wrapped intervals and sweeps larger than pi.

Extend the existing quarter-shell sphere regression and add separate primitive and annular G4Tubs GDML tests.

Motivation

Partial-phi geometries were previously unsupported in different ways (see #334):

  • Converting tests/geom/sphere_phicut_quarter_shell.gdml aborted in Debug builds.
  • Release builds terminated before macro execution through the corresponding unsupported-geometry path.
  • Partial G4Tubs geometries silently lost their phi limits and were represented as complete cylinders.

Consequently, a Release build could conceal the sphere assertion without producing a usable partial sphere, while tube conversion could succeed with incorrect geometry.

Implementation

  • Add phi metadata to the existing ZSphere and modern Cylinder leaf encodings.

    • q0.f.x stores startPhi.
    • q0.f.y stores deltaPhi.
    • A zero delta retains the legacy full-solid behavior.
  • Update U4Solid conversion:

    • Convert partial G4Sphere solids to phi-aware ZSphere leaves.
    • Convert partial G4Tubs solids to phi-aware outer and inner Cylinder leaves.
    • Preserve the existing canonical intersection with a complemented inner cylinder for annular tubes.
  • Add shared host/device phi-wedge helpers for:

    • normalized relative azimuth tests;
    • wrapped angular intervals;
    • sweeps both smaller and larger than π;
    • radial cut-face intersections;
    • outward cut-face normals;
    • signed-distance classification.
  • Extend sphere and cylinder intersections to:

    • reject curved-surface and end-cap candidates outside the phi interval;
    • intersect the two radial cut faces;
    • select the nearest valid candidate across all surfaces;
    • return the appropriate outward normal.
  • Preserve compatibility:

    • Full spheres and cylinders retain their existing representation and behavior.
    • Legacy OldCylinder center encoding remains unchanged.
    • Phi-wedged primitives retain conservative full-radius AABBs.

Test coverage

Add a focused primitive-level phi-wedge test covering:

  • quarter-wedge curved-surface intersections;
  • radial start and end cut faces;
  • rays outside the wedge;
  • signed-distance classification;
  • z-cap filtering;
  • wrapped intervals crossing phi zero;
  • sweeps larger than π.

Extend the existing sphere regression to require successful conversion of:

tests/geom/sphere_phicut_quarter_shell.gdml

The test verifies the two expected quarter-phi ZSphere leaves.

Add a separate annular tube fixture and conversion regression:

tests/geom/tube_phicut_quarter_shell.gdml

The tube test verifies:

  • preserved startPhi and deltaPhi;
  • outer and inner cylinder radii;
  • canonical intersection structure;
  • exactly one complemented inner cylinder;
  • conservative bounds.

Scope

This change adds partial-phi support specifically for G4Sphere and G4Tubs. It does not extend other angularly clipped Geant4 solids such as cones, cut tubes, or polycones. Bounds remain deliberately conservative rather than being tightened to the phi wedge.

This PR supersedes #345, which presented an earlier implementation of the same phi-aware ZSphere/Cylinder approach. Thanks to Gabor for the original investigation and groundwork.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 37a194a3b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread u4/U4Solid.h Outdated
Comment on lines +584 to +585
if (has_deltaPhi)
return sn::ZSphere(radius, zmin, zmax, startPhi, deltaPhi);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid coincident phi walls in spherical subtractions

When a partial-phi sphere is subtracted from another sphere with the same phi interval—as in sphere_phicut_quarter_shell.gdml, or for a G4Sphere with a nonzero inner radius—both leaves now report the same radial-wall intersection inside the inner radius. After positivization the outer hit is Enter and the complemented inner hit is Exit at equal t; intersect_tree treats the lhs as closer and returns the outer hit, producing a phantom cut face that seals the hollow region. The phi restriction needs to be represented once around the radial shell rather than baked into both operands.

Useful? React with 👍 / 👎.

Comment thread u4/U4Solid.h Outdated
Comment on lines +993 to +995
sn* inner = has_deltaPhi
? sn::Cylinder(rmin, -(hz + dz), hz + dz, startPhi, deltaPhi)
: sn::Cylinder(rmin, -(hz + dz), hz + dz);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Do not duplicate phi walls on the inner tube

For every partial annular G4Tubs, giving the inner cylinder the same phi wedge creates radial faces exactly coincident with the outer cylinder for radii below rmin; the existing z nudge does not separate these faces. A ray crossing a cut plane through the nominal hole receives equal outer-Enter and complemented-inner-Exit hits, and the CSG intersection lookup returns the outer hit, so the annular hole appears capped by a false phi face. Subtracting a full inner cylinder from the wedged outer cylinder preserves the intended volume without introducing these coincident walls.

Useful? React with 👍 / 👎.

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 adds analytic phi-wedge (partial-phi) support for G4Sphere and G4Tubs during Geant4→CSG conversion by baking startPhi/deltaPhi into centered ZSphere and Cylinder leaves, and updating intersection + signed-distance logic to respect radial phi cut faces.

Changes:

  • Preserve startPhi/deltaPhi when converting partial-phi G4Sphere and G4Tubs into phi-aware sn::ZSphere/sn::Cylinder leaves.
  • Introduce shared host/device phi-wedge helpers and extend leaf intersection/SDF for Cylinder and ZSphere to account for phi walls.
  • Add focused regression and primitive-level tests plus a GDML fixture for quarter-phi tubes.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
u4/U4Solid.h Converts partial-phi spheres/tubes into phi-aware ZSphere/Cylinder leaves.
u4/tests/TubePhiCutQuarterShellTest.cc New regression test validating tube phi metadata + canonical annulus structure.
u4/tests/SpherePhiCutQuarterShellTest.cc Updates regression to require successful partial-phi sphere conversion with baked metadata.
u4/tests/CMakeLists.txt Registers the new tube phi-cut regression test.
tests/geom/tube_phicut_quarter_shell.gdml Adds a minimal GDML quarter-phi annular tube fixture.
sysrap/sn.h Adds Cylinder/ZSphere overloads carrying phi metadata; updates AABB logic for new parameter layout.
CSG/tests/intersect_leaf_phi_wedge_test.cc New low-level test exercising wedge intersections and SDF behavior.
CSG/tests/CMakeLists.txt Registers the new CSG phi-wedge test.
CSG/CSGNode.cc Updates cylinder AABB computation to match new parameter layout (phi metadata in q0).
CSG/csg_intersect_leaf_zsphere.h Adds phi-wedge filtering, phi-wall intersections, and phi-aware SDF for ZSphere.
CSG/csg_intersect_leaf_phi_wedge.h New shared phi-wedge helpers for inclusion tests, wall intersections, and SDF terms.
CSG/csg_intersect_leaf_cylinder.h Adds phi-wedge filtering, phi-wall intersections, and phi-aware SDF for Cylinder.

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

Comment thread CSG/tests/intersect_leaf_phi_wedge_test.cc

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

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

u4/U4Solid.h:983

  • Same issue as in init_Sphere_: has_deltaPhi should be based on the sweep (deltaPhi) rather than startPhi != 0. For a full sweep with non-zero startPhi, this currently stores deltaPhi == 2*pi into the leaf params instead of using the legacy full-cylinder encoding (deltaPhi == 0), and can be misclassified as a wedge on the GPU due to float rounding.
    double startPhi = tubs->GetStartPhiAngle() / CLHEP::radian;
    double deltaPhi = tubs->GetDeltaPhiAngle() / CLHEP::radian;
    bool   has_deltaPhi = startPhi != 0. || deltaPhi != 2. * CLHEP::pi;

    sn* outer = has_deltaPhi
                    ? sn::Cylinder(rmax, -hz, hz, startPhi, deltaPhi)
                    : sn::Cylinder(rmax, -hz, hz);

u4/U4Solid.h:588

  • has_deltaPhi treats startPhi != 0 as evidence of a phi wedge. That can cause full-sweep solids (deltaPhi == 2*pi) to be encoded with deltaPhi stored as 2*pi instead of using the legacy full-phi encoding (deltaPhi == 0). Besides breaking the stated compatibility invariant, storing 2*pi in the float param quad risks the GPU-side deltaPhi < 2*pi test classifying it as a wedge due to float rounding, introducing spurious radial walls.

This issue also appears on line 977 of the same file.

    double startPhi = sphere->GetStartPhiAngle()/CLHEP::radian ;
    double deltaPhi = sphere->GetDeltaPhiAngle()/CLHEP::radian ;
    bool has_deltaPhi = startPhi != 0. || deltaPhi != 2.*CLHEP::pi  ;

    if (has_deltaPhi && layer == 'O')
        return sn::ZSphere(radius, zmin, zmax, startPhi, deltaPhi);

@plexoos

plexoos commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3eefa6308f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@@ -1,11 +1,13 @@
#pragma once

#include "csg_intersect_leaf_phi_wedge.h"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Install the phi-wedge helper header

When consumers build against an installed CSG package, this include cannot be resolved: CSG/CMakeLists.txt installs only files listed in HEADERS, but the new csg_intersect_leaf_phi_wedge.h was not added to INTERSECT_HEADERS. Because the installed csg_intersect_leaf.h includes this cylinder header, downstream compilation fails with a missing-header error; add the helper to the installed header list.

Useful? React with 👍 / 👎.

plexoos added 2 commits July 28, 2026 16:49
Preserve Geant4 startPhi and deltaPhi on centered ZSphere and Cylinder
leaves. Partial G4Sphere conversion previously aborted in Debug and
terminated before macro execution in Release, while partial G4Tubs
conversion silently produced full cylinders.

Add shared host/device phi-interval helpers for curved-surface and cap
filtering, radial cut-face intersections and normals, and
signed-distance classification. Keep conservative full-radius bounds,
preserve the legacy OldCylinder center encoding, and support wrapped
intervals and sweeps larger than pi.

Extend the existing quarter-shell sphere regression and add separate
primitive and annular G4Tubs GDML tests. Validation covered eight
focused and compatibility tests in Debug, three feature tests in
Release, and successful simg4ox runs for both fixtures in both build
types.
Apply partial-phi metadata only to the outer leaf of G4Sphere and G4Tubs
shells. Leaving the complemented inner leaf full-angle prevents equal-t
radial-wall intersections from producing phantom faces across the hollow
region.

For aligned Boolean subtractions whose primitive leaves carry matching
phi intervals, clear the redundant RHS interval before canonicalization.
Accept identity-displaced GDML operands while preserving phi metadata on
genuinely transformed subtractions.

Update the sphere regression to cover both the existing GDML subtraction
and a native G4Sphere with nonzero inner radius, and update the tube
regression to require a wedged outer and full-angle inner cylinder.
@plexoos
plexoos force-pushed the support-partial-spheres-tubes branch from 3eefa63 to 3531505 Compare July 28, 2026 20:53
@plexoos
plexoos force-pushed the support-partial-spheres-tubes branch from 3531505 to cf29586 Compare July 28, 2026 21:39
@plexoos
plexoos merged commit 55847cb into main Jul 29, 2026
23 checks passed
@plexoos
plexoos deleted the support-partial-spheres-tubes branch July 29, 2026 22:33
plexoos added a commit that referenced this pull request Jul 30, 2026
The partial-phi sphere and tube implementation from #426
has focused geometry and intersection tests, but it lacks an end-to-end
regression using a realistic detector that converts those solids and
compares optical-photon detection across Geant4 and Opticks. The dRICH
fixture provides that coverage with one partial sphere, three partial
tubes, and 210 annotated photon-sensor volumes.

Add a deterministic 10,000-photon, 420 nm torch configuration aimed at
a dRICH SiPM patch so the CPU and GPU receive identical optical input
and produce stable nonzero hit samples. Register
Integration.simg4ox.drich through the common CMake helper, allow
hit-validation cases to select a custom macro, and use run_validate.mac
so Geant4 invokes the sensor SDs. The complete geometry is converted
before transport, so the test exercises partial-phi construction even
though the controlled beam targets the sensors directly.

GDML import strips pointer-like suffixes from logical-volume names,
leaving distinct dRICH sensor volumes with duplicate names. Append
a numeric index to each PhotonSD name so G4SDManager collection keys
remain unique and all Geant4 sensor hits are retained for the existing
three-sigma count and distribution comparison.
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.

2 participants