Warning, /eic-opticks/docs/geometry-requirements.md is written in an unsupported language. File is not indexed.
0001 # Geometry guide
0002
0003 Simphony converts a Geant4 detector into geometry that NVIDIA OptiX can
0004 intersect on the GPU. This guide shows how to try that conversion, explains
0005 which shapes use exact analytic intersections or triangle meshes, and gives a
0006 practical workflow for bringing in a detector of your own.
0007
0008 The most useful rule to remember is simple: keep optical boundaries analytic
0009 when that is practical, and use triangles for shapes that are genuinely
0010 faceted or are not available on the analytic path.
0011
0012 ## Try a known geometry first
0013
0014 After building or installing Simphony, run the small raindrop example from the
0015 repository root:
0016
0017 ```shell
0018 simg4ox \
0019 -g tests/geom/opticks_raindrop.gdml \
0020 -c dev \
0021 -m tests/run.mac \
0022 -s 42
0023 ```
0024
0025 The `-g` option selects the GDML detector, while `config/dev.json` defines 100
0026 optical photons aimed through it. `simg4ox` gives the same initial photons to
0027 Geant4 and to Simphony. This makes it a useful first check of both geometry
0028 conversion and optical transport. Event and hit files are written under the
0029 `event.output_dir` from the JSON configuration; see [Simulation inputs and
0030 outputs](inputs-outputs.md#output-protocol) for their layouts.
0031
0032 To check that a GDML file converts and to save the converted geometry without
0033 running an event, use `consgeo`:
0034
0035 ```shell
0036 consgeo -g detector.gdml -o geometry-output
0037 ```
0038
0039 For an input named `detector.gdml`, this creates:
0040
0041 ```text
0042 geometry-output/detector/
0043 ├── origin.gdml
0044 └── CSGFoundry/
0045 ├── meshname.txt
0046 ├── SSim/
0047 │ ├── stree/
0048 │ └── scene/
0049 └── ... NumPy geometry arrays
0050 ```
0051
0052 `origin.gdml` is Geant4's re-export of the loaded world. `CSGFoundry` contains
0053 the analytic CSG data, triangle scene, material and surface data, transforms,
0054 and instance metadata. If no CUDA device is available, conversion and saving
0055 still proceed; only creation of the in-memory OptiX geometry is skipped.
0056
0057 Use a new output directory for each conversion. The current writer does not
0058 clear an existing `CSGFoundry` directory first, so reusing a directory can
0059 leave files from an older conversion mixed with the new output.
0060
0061 ## Accepted geometry inputs
0062
0063 The command-line examples and the integration API enter the same conversion
0064 pipeline in different ways.
0065
0066 | Input | How it enters Simphony | When to use it |
0067 |---|---|---|
0068 | GDML file | `-g detector.gdml` on `simg4ox`, `GPURaytrace`, `GPUCerenkov`, and the other example applications | The simplest route for standalone studies |
0069 | Live Geant4 world | `G4CXOpticks::SetGeometry(world)` with a `G4VPhysicalVolume*` | Embedding Simphony in a Geant4 application |
0070 | DD4hep compact geometry | DD4hep builds the Geant4 world, then the Simphony DD4hep plugin passes that world into the same API | Experiments that already use DD4hep |
0071 | Saved `CSGFoundry` | Lower-level `G4CXOpticks` workflows load it through the configured geometry base | Reusing a deliberately persisted conversion |
0072
0073 The standalone applications do not read DD4hep compact XML directly, and
0074 their `-g` option does not load a saved `CSGFoundry`. It always means a GDML
0075 file. See [`dd4hepplugins/examples`](../dd4hepplugins/examples) for working
0076 DD4hep integrations.
0077
0078 ## What conversion does
0079
0080 The conversion starts from a complete Geant4 world, not from a bare mesh. It
0081 collects the volume hierarchy, placements, materials, optical surfaces, and
0082 solids before it builds the GPU geometry.
0083
0084 ```mermaid
0085 flowchart LR
0086 A[GDML file] --> B[Geant4 world]
0087 C[DD4hep or application-built geometry] --> B
0088 B --> D[U4Tree and stree]
0089 D --> E{Route each logical-volume solid}
0090 E -->|R: global analytic| F[Analytic CSG]
0091 E -->|F: repeated analytic| F
0092 E -->|T: global triangles| G[Geant4 polyhedron mesh]
0093 F --> H[CSGFoundry and SScene]
0094 G --> H
0095 H --> I[OptiX geometry acceleration structures]
0096 ```
0097
0098 The letters in the diagram are internal labels that are useful in diagnostic
0099 output:
0100
0101 - `R` is the analytic, non-instanced remainder. It includes the world and
0102 other geometry that was not factored as a repeated subtree.
0103 - `F` is an analytic repeated subtree. Simphony stores one copy of its shape
0104 and many placement transforms.
0105 - `T` is the global triangle group. It contains all placements selected for
0106 triangle intersection.
0107
0108 There is currently no triangulated instance type. If a triangle-selected solid
0109 occurs in a repeated subtree, that subtree is removed from analytic
0110 factorization and every affected placement goes into `T`. The geometry remains
0111 usable, but it can require substantially more host memory, GPU memory, and
0112 acceleration-structure build time.
0113
0114 ### Selection is by solid, not by placement
0115
0116 A logical volume points to one Geant4 solid, and Simphony chooses one GPU route
0117 for that whole solid. All placements of the logical volume therefore follow
0118 the same route. You cannot triangulate one placement while leaving another
0119 placement of the same logical volume analytic.
0120
0121 The same rule applies inside a compound solid. If a Boolean or multi-union
0122 contains a tessellated constituent, Simphony selects the enclosing
0123 logical-volume solid for triangles. It does not mix analytic and triangle
0124 intersection within that one compound solid.
0125
0126 ### Every recognized solid gets a mesh
0127
0128 During conversion, `U4Mesh` asks Geant4 to create a `G4Polyhedron` for every
0129 recognized solid and stores indexed vertices and triangles in the scene. The
0130 route classification decides whether OptiX actually intersects that solid as
0131 analytic CSG or as triangles.
0132
0133 For a direct `G4TessellatedSolid`, the polyhedron comes from its authored
0134 facets. Quads are split into triangles. For curved primitives and compound
0135 solids, the polyhedron is Geant4's polygonal approximation. The optical
0136 simulation computes a face normal from the three vertices of the intersected
0137 triangle, so facet size and winding can directly affect reflection and
0138 refraction.
0139
0140 ## Supported Geant4 solids
0141
0142 The following table describes the current conversion in `u4/U4Solid.h`. A
0143 shape listed as analytic can still be deliberately sent to the triangle route
0144 by name.
0145
0146 | Geant4 solid | Default GPU route | Current behavior |
0147 |---|---|---|
0148 | `G4Box`, `G4Orb` | Analytic | Direct box and sphere primitives |
0149 | `G4Sphere` | Analytic | Supports radial shells, theta cuts, and partial-phi intervals |
0150 | `G4Tubs` | Analytic | Supports hollow tubes and partial-phi intervals |
0151 | `G4Ellipsoid` | Analytic | Represented by a scaled sphere; top and bottom z cuts are handled |
0152 | `G4Cons` | Analytic | Full-phi cones and conical shells only; see the partial-phi warning below |
0153 | `G4Polycone` | Analytic | Full-phi polycones are decomposed into analytic sections |
0154 | `G4Trap`, `G4Trd` | Analytic | Converted to convex polyhedra from their eight vertices |
0155 | `G4UnionSolid`, `G4IntersectionSolid`, `G4SubtractionSolid` | Analytic | Converted recursively when every constituent is supported |
0156 | `G4MultiUnion` | Analytic or triangles | Primitive members can remain analytic; tessellated or internally unsupported content selects the enclosing solid for triangles |
0157 | `G4Torus` | Triangles | Recognized and selected automatically because there is no active analytic torus intersection path |
0158 | `G4CutTubs` | Triangles | Recognized and selected automatically; current conversion has the restriction noted below |
0159 | `G4TessellatedSolid` | Triangles | Detected automatically, including inside Boolean, displaced, and multi-union solids |
0160
0161 `G4DisplacedSolid` is also handled as the transform wrapper used for a
0162 constituent of a Boolean or multi-union. It is not an additional visible
0163 volume.
0164
0165 ### Limits that geometry authors need to know
0166
0167 - `G4Hype` and Geant4 solid types not recognized by `U4Solid` do not currently
0168 convert. Manual triangulation does not bypass this step, so adding the name
0169 to `stree__force_triangulate_solid` will not make an unknown type work.
0170 - The current `G4Cons` conversion does not apply its start-phi or delta-phi
0171 parameters. Use only full-phi `G4Cons` solids; a partial-phi cone would
0172 otherwise become a full cone on the GPU.
0173 - Partial-phi `G4Polycone` conversion stops by default. An experimental
0174 `U4Polycone__ENABLE_PHICUT=1` path exists, but it is not the recommended
0175 starting point for production geometry. Prefer a tested decomposition or a
0176 supported tessellated representation.
0177 - `G4CutTubs` conversion currently expects both end-plane normals to have a
0178 zero local y component. The completed volume may still be placed and rotated
0179 normally, but an arbitrarily oriented cut in the solid's local frame will
0180 fail conversion.
0181 - A Boolean expects the usual Geant4/GDML form in which any constituent
0182 displacement is on the second operand. A displaced first operand or nested
0183 `G4DisplacedSolid` wrappers are not supported by the current converter.
0184
0185 If a required solid falls outside these limits, first try to express it as a
0186 small combination of supported primitives. Use a native tessellated solid when
0187 the shape is truly faceted. A code change is required for a completely unknown
0188 Geant4 solid because the converter still needs a placeholder, bounds, and
0189 boundary metadata before the triangle scene can be built.
0190
0191 ## Choosing analytic geometry or triangles
0192
0193 Analytic CSG is usually the best default for optical volumes. It keeps curved
0194 surfaces exact, computes analytic normals, and allows large repeated detector
0195 structures to be instanced efficiently.
0196
0197 Triangles are a good fit for:
0198
0199 - CAD-like parts that are intentionally faceted.
0200 - Native GDML `<tessellated>` solids.
0201 - Tori, cut tubes, and other recognized shapes that Simphony automatically
0202 routes away from analytic intersection.
0203 - A small number of named solids for which the analytic conversion is known to
0204 be unsuitable.
0205
0206 Triangles are a less attractive fit for highly repeated photosensors or for
0207 curved, optical-critical interfaces. Those cases lose triangle instancing and
0208 introduce a resolution-dependent approximation.
0209
0210 ### Native tessellated input
0211
0212 A GDML `<tessellated>` element becomes a `G4TessellatedSolid`, so no selection
0213 environment variable is needed. Before using it for optical transport, check
0214 that:
0215
0216 - The facets form a closed, watertight solid.
0217 - Facet vertices have consistent outward winding.
0218 - There are no zero-area or duplicate facets.
0219 - The mesh resolution is fine enough at every optical boundary.
0220 - Geant4 reports the expected volume and extent before Simphony conversion.
0221
0222 When a tessellated constituent is nested inside another solid, Geant4 creates
0223 the polyhedron for the enclosing solid. The resulting triangles therefore
0224 describe that complete Boolean or multi-union result, not only the nested
0225 tessellated constituent.
0226
0227 The repository's native tessellation tests construct a closed tetrahedron at
0228 runtime. There is not yet a GDML fixture that runs a native tessellated solid
0229 through GPU optical intersection in CI. Treat a new native tessellated detector
0230 as requiring its own end-to-end GPU validation.
0231
0232 ## Selecting additional solids for triangles
0233
0234 Set `stree__force_triangulate_solid` before launching the application. Its
0235 value is a comma-separated list of exact imported solid names:
0236
0237 ```shell
0238 export stree__force_triangulate_solid='SupportRing,ComplexEnvelope'
0239 simg4ox -g detector.gdml -c detector -m run.mac -s 42
0240 ```
0241
0242 These are solid names, not logical-volume or physical-volume names. `U4Tree`
0243 strips generated pointer-like `0x...` suffixes and adds `_0`, `_1`, and similar
0244 suffixes when needed to make imported names unique. Stable, unique GDML solid
0245 names avoid surprises.
0246
0247 For a large list, use one solid name per line in a file:
0248
0249 ```text
0250 # triangle-solids.txt
0251 SupportRing
0252 ComplexEnvelope
0253 ```
0254
0255 Then point the environment variable at an absolute path:
0256
0257 ```shell
0258 export stree__force_triangulate_solid='filepath:/data/my-detector/triangle-solids.txt'
0259 ```
0260
0261 Blank lines and lines beginning with `#` are ignored. To discover the exact
0262 imported names, first save an analytic conversion and inspect
0263 `CSGFoundry/meshname.txt`:
0264
0265 ```shell
0266 consgeo -g detector.gdml -o geometry-analytic
0267 rg 'Support|Envelope' geometry-analytic/detector/CSGFoundry/meshname.txt
0268 ```
0269
0270 A requested name that is not found prints
0271 `stree::FindForceTriangulateLVID name not found`; it does not stop the
0272 application. Treat that warning as a configuration error. For a short
0273 selection summary during conversion, enable the supported diagnostics:
0274
0275 ```shell
0276 SSim__stree_level=1 \
0277 stree__findForceTriangulateLVID_DUMP=1 \
0278 stree__force_triangulate_solid='SupportRing' \
0279 consgeo -g detector.gdml -o geometry-triangles
0280 ```
0281
0282 Selection happens while the live Geant4 world is converted. The standalone
0283 applications repeat this conversion each time they read GDML, so the variables
0284 must be set before each launch. If you explicitly save and later load a
0285 `CSGFoundry`, the selected logical-volume IDs and generated meshes are already
0286 part of that saved geometry; changing the variables does not rewrite the
0287 cache.
0288
0289 ## Controlling mesh resolution
0290
0291 Geant4 uses a configurable number of rotation steps when it polygonizes curved
0292 solids. Simphony can set this resolution for a Geant4 solid type, one exact raw
0293 solid name, or a group of raw names that share a prefix:
0294
0295 ```shell
0296 # Apply to every torus.
0297 export U4Mesh__NumberOfRotationSteps_entityType_G4Torus=48
0298
0299 # Apply to raw solid names that begin with SupportRing.
0300 export U4Mesh__NumberOfRotationSteps_solidName_STARTING_pfx_0=SupportRing
0301 export U4Mesh__NumberOfRotationSteps_solidName_STARTING_val_0=96
0302 ```
0303
0304 The prefix form is useful when a GDML reader or an embedding application keeps
0305 a generated `0x...` suffix in the Geant4 solid name. Choose a prefix that is
0306 long enough to identify only the intended solids. Up to three prefix and value
0307 pairs can be defined with indices `0`, `1`, and `2`. The first matching prefix
0308 is used.
0309
0310 Mesh resolution and forced triangulation use different forms of a solid name.
0311 `U4Tree` removes a generated `0x...` suffix when it writes
0312 `CSGFoundry/meshname.txt` and resolves `stree__force_triangulate_solid`.
0313 `U4Mesh` uses the raw `G4VSolid::GetName()` instead. For example, a raw name of
0314 `SupportRing0x123abc` appears as `SupportRing` in `meshname.txt`. An exact mesh
0315 override ending in only `SupportRing` will not match the raw name. Include the
0316 full suffix when targeting that one exact raw name:
0317
0318 ```shell
0319 export U4Mesh__NumberOfRotationSteps_solidName_SupportRing0x123abc=96
0320 ```
0321
0322 After a baseline conversion, inspect the raw names recorded in the mesh
0323 metadata rather than copying normalized names from `meshname.txt`:
0324
0325 ```shell
0326 rg '^(solidName|numberOfRotationSteps):' \
0327 geometry-analytic/detector/CSGFoundry/SSim/stree/mesh/*/NPFold_meta.txt
0328 ```
0329
0330 The `solidName` field shows the raw name used for matching. After adding an
0331 override and making a fresh conversion, confirm that the intended mesh also
0332 records the requested `numberOfRotationSteps`. The field is absent when no
0333 override matched.
0334
0335 When more than one setting matches, the exact raw name takes precedence. The
0336 first matching prefix is next, followed by the entity type. Without a match,
0337 Geant4 uses its default of 24 rotation steps. Only the triangle route uses the
0338 polygonized surface for GPU intersections. Changing mesh resolution does not
0339 alter an analytic intersection.
0340
0341 Treat a resolution change as a geometry change. Generate a fresh saved output,
0342 record the setting with the study, and repeat the optical comparison. Curved
0343 triangle geometry is ready only when the observables of interest are stable as
0344 the rotation-step count increases.
0345
0346 ## Designing the volume hierarchy
0347
0348 ### Use clear, stable names
0349
0350 Give solids, logical volumes, and physical volumes different naming patterns so
0351 logs and saved arrays are easy to interpret. A useful convention is:
0352
0353 ```text
0354 solid: PMTWindowSolid
0355 logical volume: PMTWindowLV
0356 physical volume: PMTWindowPV_0042
0357 ```
0358
0359 The exact names are an experiment choice; the important part is that a solid
0360 targeted for special handling keeps a stable, unique name between exports.
0361
0362 ### Preserve real repetition
0363
0364 Build a repeated detector module as one logical-volume subtree placed many
0365 times. Simphony finds repeated subtrees by their structural digest. By default,
0366 a subtree must occur at least 500 times to become an analytic factor; the
0367 conversion-time environment variable `stree__FREQ_CUT` controls that
0368 threshold.
0369
0370 The factorizer keeps the largest qualifying repeated subtree and disqualifies
0371 qualifying repeats contained inside it. This avoids nested instances. If any
0372 solid in a candidate subtree is selected for triangles, the whole candidate is
0373 also disqualified from analytic factorization.
0374
0375 Lowering `stree__FREQ_CUT` is an advanced performance choice, not a requirement
0376 for correctness. Measure conversion time, GPU memory, and propagation time for
0377 the complete detector before adopting a non-default value.
0378
0379 ### Keep optical interfaces explicit
0380
0381 Each optical medium should be a distinct logical volume with the correct
0382 material. Put windows, coupling layers, photocathodes, scintillators, and WLS
0383 regions in separate volumes when their interfaces matter to the physics.
0384
0385 Avoid unintended overlaps, microscopic gaps, sliver volumes, and coincident
0386 sibling faces. Simphony's command-line applications do not provide a dedicated
0387 overlap-checking option, so run the geometry checks supplied by Geant4 or by the
0388 geometry-authoring framework before GPU validation.
0389
0390 Simphony imports both logical border surfaces and logical skin surfaces.
0391 Because a border surface is directional in Geant4, define both directions when
0392 the reverse crossing needs an explicit surface. Material `RINDEX` tables and
0393 surface-property tables must cover the energy range of the photons being
0394 simulated. See [Physics](physics.md) for the exact GPU interpretation of
0395 `model`, `finish`, `type`, `EFFICIENCY`, and `REFLECTIVITY`.
0396
0397 For `simg4ox` CPU/GPU hit comparisons, mark a detector logical volume with a
0398 `SensDet` GDML auxiliary and give its optical surface an `EFFICIENCY` property.
0399 The repository fixtures use this form:
0400
0401 ```xml
0402 <volume name="SensorLV">
0403 <materialref ref="SensorMaterial"/>
0404 <solidref ref="SensorSolid"/>
0405 <auxiliary auxtype="SensDet" auxvalue="PhotonDetector"/>
0406 </volume>
0407 ```
0408
0409 The complete surface definition is shown in
0410 [`tests/geom/opticks_raindrop.gdml`](../tests/geom/opticks_raindrop.gdml).
0411
0412 ### Keep Boolean trees understandable
0413
0414 Booleans are supported, but deep trees with nearly coincident surfaces are
0415 difficult to validate and can magnify numerical boundary problems. Prefer a
0416 small number of well-named constituents and move optically irrelevant
0417 mechanical detail into separate volumes. This also makes it possible to send a
0418 complex support part to triangles without changing a nearby optical volume.
0419
0420 ## Validate a new detector in stages
0421
0422 ### 1. Check host-side conversion
0423
0424 Save a fresh conversion with `consgeo`. Investigate any unhandled-solid
0425 message, assertion, missing solid name, or unexpected output extent before
0426 running photons. A successful GDML parse alone is not enough: the complete
0427 `U4Tree`, triangle scene, and `CSGFoundry` conversion must finish.
0428
0429 The two native tessellation unit tests do not require GPU intersection:
0430
0431 ```shell
0432 ctest --test-dir build \
0433 -R '^U4Test\.(TessellatedSolidTest|U4TreeTessellatedTest)$' \
0434 --output-on-failure
0435 ```
0436
0437 They check tessellated-solid dispatch, bounds, transformed placement,
0438 recursive detection, and the handling of repeated placements.
0439
0440 ### 2. Check a small optical source
0441
0442 Aim a small torch source through one known interface. Confirm that the source
0443 position is inside the expected material, its direction reaches the detector,
0444 and its wavelength lies inside every relevant material and surface table.
0445
0446 Use `simg4ox` to compare Geant4 and GPU hit counts and distributions. The two
0447 engines start with the same photons, but their random decisions are not
0448 expected to produce identical photon histories. Compare statistical results,
0449 boundary-state populations, and spatial distributions rather than requiring a
0450 photon-for-photon match.
0451
0452 ### 3. Compare analytic and triangle routes
0453
0454 For a solid that supports both routes, hold the source and random seed fixed,
0455 then run once analytically and once with name-based triangulation. The
0456 repository provides two GPU regressions that demonstrate this method:
0457
0458 ```shell
0459 tests/test_triangulated.sh
0460 tests/test_triangulated_multi.sh
0461 ```
0462
0463 The first compares a curved sphere using analytic and triangle intersections.
0464 The second verifies that selecting either of two sphere solid names affects
0465 only that solid. Both scripts require a built installation on `PATH` and an
0466 NVIDIA GPU.
0467
0468 ### 4. Perform a resolution study
0469
0470 For curved triangle geometry, repeat the comparison with increasing
0471 `U4Mesh__NumberOfRotationSteps_*` values. Record hit efficiency, reflection and
0472 transmission fractions, hit-position distributions, conversion time, and GPU
0473 memory. Stop increasing the resolution only when the quantities relevant to
0474 the study have converged.
0475
0476 ### 5. Scale to the full detector
0477
0478 Only after the small source behaves correctly should you raise photon count and
0479 load the full hierarchy. Recheck conversion time and memory whenever a
0480 triangle-selected solid belongs to a repeated module.
0481
0482 ## Troubleshooting geometry conversion
0483
0484 | Symptom | Likely cause | What to check |
0485 |---|---|---|
0486 | `UNHANDLED SOLID TYPE` or an assertion in `U4Solid` | The solid type or one of its constituents has no current conversion | Compare the geometry with the support table; forcing triangles cannot rescue an unknown type |
0487 | `name not found [...]` | The forced-triangulation name does not match the normalized imported solid name | Generate a baseline with `consgeo` and copy the name from `CSGFoundry/meshname.txt` |
0488 | Conversion or GPU setup becomes much larger after selecting one solid | The solid belongs to a repeated subtree that lost analytic factorization | Check the hierarchy and triangle only an isolated support volume when possible |
0489 | Triangle results move as rotation steps change | The curved mesh is too coarse for the observable | Increase resolution and document a convergence study |
0490 | Many `MISS` or `NAN_ABORT` terminal flags | Source placement, overlaps, ambiguous boundaries, or malformed geometry may be involved | Start with one interface, inspect records, and follow [Performance and debugging](performance-and-debugging.md) |
0491 | Geant4 and GPU have very different hit counts | Geometry may differ, but source, material tables, surfaces, or sensitive-detector setup can also cause it | Confirm the source and wavelength first, then compare boundary states and surface definitions |
0492
0493 ## Authoring checklist
0494
0495 Before using a geometry for a physics result, confirm that:
0496
0497 - The complete Geant4 world converts, not only the GDML parser stage.
0498 - Every solid type and special parameter combination appears in the support
0499 table above.
0500 - Important solids have stable, unique names.
0501 - Optical media and interfaces are represented by clear volume boundaries.
0502 - Material and surface tables cover the simulated photon energies.
0503 - Border surfaces have the intended direction or directions.
0504 - Sensitive detector volumes are configured for the chosen application.
0505 - Geant4 or the authoring framework reports no unintended overlaps.
0506 - Native tessellated solids are closed, consistently wound, and nondegenerate.
0507 - Forced-triangulation names resolve without warnings.
0508 - Mesh-resolution overrides match the intended raw solid name, stable prefix,
0509 or entity type.
0510 - Curved triangle geometry passes a mesh-resolution study.
0511 - Repeated triangle-selected volumes fit within the available host and GPU
0512 memory.
0513 - Geant4 and GPU optical results agree statistically for representative
0514 sources.
0515
0516 For most detectors, a good first production design is analytic CSG for repeated
0517 sensor modules and optical-critical curved interfaces, with triangles reserved
0518 for isolated mechanical parts and genuinely faceted solids.