|
| 1 | +# CAD support: STEP to TGeo and back |
| 2 | + |
| 3 | +`Detectors/CADSupport` converts CAD geometry exported as STEP into ROOT TGeo geometry for |
| 4 | +simulation. It also exports TGeo geometry back to STEP. |
| 5 | + |
| 6 | +The converter writes one ROOT macro, `geom.C`, together with its binary payloads. The macro can be |
| 7 | +loaded in ROOT on its own, or injected into `o2-sim` as a passive module or as a sensitive external |
| 8 | +detector. Injection is data-driven: a JSON file tells `o2-sim` which macro to load, where to anchor |
| 9 | +it and, for detectors, which volumes produce hits. Nothing is recompiled. |
| 10 | + |
| 11 | +The tutorial `doc/tutorial/index.html` walks through the whole route on the shipped `ExcavatorArm.step` |
| 12 | +model. This file is the option reference. |
| 13 | + |
| 14 | +## Software setup |
| 15 | + |
| 16 | +The converter needs pythonOCC, which is a separate aliBuild package: |
| 17 | + |
| 18 | +```bash |
| 19 | +aliBuild build pythonOCC --defaults o2 --no-system SWIG |
| 20 | +alienv enter O2sim/latest,pythonOCC/latest |
| 21 | +o2-cad-to-tgeo --help |
| 22 | +o2-cad-to-tgeo --self-test |
| 23 | +``` |
| 24 | + |
| 25 | +The installed wrappers `o2-cad-to-tgeo` and `o2-tgeo-to-cad` run |
| 26 | +`$O2_ROOT/share/CADSupport/tools/O2_CADtoTGeo.py` and `O2_TGeoToCAD.py`. The example models are |
| 27 | +installed in `$O2_ROOT/share/CADSupport/examples/`. The Geant4 NIST material table is |
| 28 | +`$O2_ROOT/share/CADSupport/tools/g4_nist_database/G4_NIST_DB.json`. The legacy names |
| 29 | +`O2_CADtoTGeo.py` and `O2_TGeoToCAD.py` are installed alongside them and work the same way. |
| 30 | + |
| 31 | +Outside the ALICE stack, a conda environment with `pythonocc-core` also works. There, run the |
| 32 | +script from the source tree: |
| 33 | + |
| 34 | +```bash |
| 35 | +conda create -n occ -c conda-forge python=3.10 pythonocc-core -y |
| 36 | +conda activate occ |
| 37 | +python3 $O2_SRC/Detectors/CADSupport/tools/O2_CADtoTGeo.py --help |
| 38 | +``` |
| 39 | + |
| 40 | +## Convert a STEP file |
| 41 | + |
| 42 | +```bash |
| 43 | +mkdir -p cad_out/excavator |
| 44 | +o2-cad-to-tgeo $O2_ROOT/share/CADSupport/examples/ExcavatorArm.step \ |
| 45 | + --output-folder cad_out/excavator -o geom.C --step-unit auto \ |
| 46 | + --csg auto --exact-surfaces auto --mesh --mesh-prec 0.05 |
| 47 | +``` |
| 48 | + |
| 49 | +Each leaf solid is carried by the first representation that accepts it: |
| 50 | + |
| 51 | +| representation | flag | shape class | payload | |
| 52 | +| --- | --- | --- | --- | |
| 53 | +| native ROOT CSG, or a flat CSG solid | `--csg auto\|required` | `TGeoBBox`, `TGeoTube`, ..., `TGeoCompositeShape`, `O2FlatCSG` | `shape_*.root`, `flatcsg_*.bin` | |
| 54 | +| exact trimmed surfaces | `--exact-surfaces auto\|required` | `O2BVHSurfaceSolid` | `surfaces_*.bin` | |
| 55 | +| triangle mesh | `--mesh` | `O2Tessellated` (`--mesh-solid o2`, default) | `facets_*.bin` | |
| 56 | + |
| 57 | +`off` is the default for `--csg` and `--exact-surfaces`. `auto` uses a tier where it is accepted |
| 58 | +and falls through elsewhere. `required` stops with a report if any leaf cannot use it. Without |
| 59 | +`--mesh`, the fallback tier emits bounding boxes. |
| 60 | + |
| 61 | +`--mesh-prec` sets both the linear and the angular deflection of the OCCT mesher; the default is |
| 62 | +0.1. `--mesh-solid tgeo` emits ROOT's `TGeoTessellated`, which does not implement navigation; |
| 63 | +use it only for a macro that must load outside O2. |
| 64 | + |
| 65 | +The output folder holds: |
| 66 | + |
| 67 | +- `geom.C`; |
| 68 | +- the payloads above; |
| 69 | +- `csg_report.json` (with `--csg`); |
| 70 | +- `brep_*.brep` (with `--dump-brep`); |
| 71 | +- `surface_report.json` (with `--surface-report PATH`). |
| 72 | + |
| 73 | +The macro loads its payloads relative to its own location, so move the folder as a whole. |
| 74 | + |
| 75 | +`geom.C` exports `get_builder_hook_unchecked()`, which `o2-sim` calls, and |
| 76 | +`build_and_export(const char* out_root = "geom.root", bool check = true, bool checkOverlaps = false)` |
| 77 | +for standalone use: |
| 78 | + |
| 79 | +```bash |
| 80 | +(cd cad_out/excavator && root -l -b -q -e '.L geom.C' -e 'build_and_export("geom.root");') # build and export |
| 81 | +(cd cad_out/excavator && root -l -b -q -e '.L geom.C' -e 'build_and_export("geom.root", true, true);') # also CheckOverlaps |
| 82 | +``` |
| 83 | + |
| 84 | +Other conversion options: |
| 85 | + |
| 86 | +| option | meaning | |
| 87 | +| --- | --- | |
| 88 | +| `--step-unit auto\|mm\|cm\|m\|in\|ft` | STEP length unit; `auto` reads the file's declaration | |
| 89 | +| `--recognize-surfaces exact\|off` | recover exact planes, spheres, cylinders and cones stored as NURBS (default `exact`) | |
| 90 | +| `--surface-report PATH` | per-face classification and exact-conversion eligibility, as JSON | |
| 91 | +| `--csg-report PATH` | where to write `csg_report.json` | |
| 92 | +| `--max-cells N`, `--max-splits N`, `--decompose-timeout S` | raise the CSG decomposition budgets (defaults 64, 256, 60 s) | |
| 93 | +| `--print-tree` | print the assembly tree and exit | |
| 94 | +| `--in-field [IFIELD,FIELDM]` | take field tracking parameters from the live field (seed `2,10`) | |
| 95 | + |
| 96 | +## Convert part of a model |
| 97 | + |
| 98 | +`--include-name RE` and `--exclude-name RE` select CAD labels by regular expression. Both may be |
| 99 | +repeated, and a matching assembly includes its whole subtree. Matching is case-insensitive unless |
| 100 | +`--name-filter-case-sensitive` is given. |
| 101 | + |
| 102 | +`--clip-box XMIN YMIN ZMIN XMAX YMAX ZMAX` keeps only the geometry inside an axis-aligned box. The |
| 103 | +box is given in STEP file units, in the assembly's world frame, with each minimum below its |
| 104 | +maximum. |
| 105 | + |
| 106 | +- Solids fully outside the box are dropped. |
| 107 | +- Solids fully inside are kept. |
| 108 | +- Solids that straddle the boundary are intersected with the box. |
| 109 | +- Assemblies left with no children are removed. |
| 110 | + |
| 111 | +`--clip-deduplicate intact` (the default) reuses shared definitions for subtrees fully inside the |
| 112 | +box. `none` makes one volume per surviving occurrence. |
| 113 | + |
| 114 | +## Materials |
| 115 | + |
| 116 | +A bill-of-materials CSV assigns materials and, where masses and CAD volumes are both available, |
| 117 | +effective densities. Material names are matched against the Geant4 NIST table: |
| 118 | + |
| 119 | +```bash |
| 120 | +o2-cad-to-tgeo $O2_ROOT/share/CADSupport/examples/ExcavatorArm.step \ |
| 121 | + --output-folder cad_out/excavator -o geom.C --csg auto --exact-surfaces auto --mesh \ |
| 122 | + --materials-csv $O2_ROOT/share/CADSupport/examples/ExcavatorArm_MATERIALS.csv \ |
| 123 | + --bom-mass-unit kg \ |
| 124 | + --g4-nist-json $O2_ROOT/share/CADSupport/tools/g4_nist_database/G4_NIST_DB.json |
| 125 | +``` |
| 126 | + |
| 127 | +Rows are read when the first two columns are `CAD,Mechanical/Part`. Their layout is |
| 128 | +`CAD,Mechanical/Part,<PartNumber>,<Revision>,<Name>,<Mass>,<Material>,...`. |
| 129 | + |
| 130 | +An ambiguous or missing match falls back to a simple material and leaves a comment in `geom.C`. The |
| 131 | +matching is tuned by `--mat-min-score`, `--mat-ambiguity-delta`, `--mat-w-token`, |
| 132 | +`--mat-w-density`, `--mat-max-log-density-diff` and `--mat-compound-penalty`. |
| 133 | + |
| 134 | +Geometry that came out of TGeo with `o2-tgeo-to-cad` should instead use `--media-json`. That |
| 135 | +rebuilds the original media verbatim and takes precedence over the BOM. |
| 136 | + |
| 137 | +Without `--in-field`, a CAD medium has all tracking parameters zero, including `ifield`. |
| 138 | + |
| 139 | +## Passive geometry in `o2-sim` |
| 140 | + |
| 141 | +`externalGeometry.json`: |
| 142 | + |
| 143 | +```json |
| 144 | +{ |
| 145 | + "externalModules": [ |
| 146 | + { |
| 147 | + "name": "EXCV", |
| 148 | + "title": "Excavator support structure from CAD", |
| 149 | + "macro": "cad_out/excavator/geom.C", |
| 150 | + "anchor": "barrel", |
| 151 | + "placement": { "translation": [21.01, -13.22, -19.66], "rotation_deg": [0.0, 0.0, 0.0] } |
| 152 | + } |
| 153 | + ] |
| 154 | +} |
| 155 | +``` |
| 156 | + |
| 157 | +`detectorlist.json`: |
| 158 | + |
| 159 | +```json |
| 160 | +{ "EXTCAD": ["EXCV"] } |
| 161 | +``` |
| 162 | + |
| 163 | +```bash |
| 164 | +o2-sim -n 1 -g boxgen --detectorList EXTCAD:detectorlist.json --extGeomFile externalGeometry.json |
| 165 | +``` |
| 166 | + |
| 167 | +A module is added only when its `name` is in the active module list. `anchor` must be an existing |
| 168 | +volume; `barrel` sits at (0, −30, 0) in the cave. `placement` is given in cm and degrees in the |
| 169 | +anchor's frame. Several modules, each from its own `geom.C`, can be listed together: the loader compiles |
| 170 | +each macro into its own namespace, so their identical function names do not collide. |
| 171 | + |
| 172 | +## Sensitive external detectors |
| 173 | + |
| 174 | +Use an `externalDetectors` array. It takes the same fields as a module, plus `detID` and at least |
| 175 | +one of `sensitiveVolumes` or `sensitiveMedia`: |
| 176 | + |
| 177 | +```json |
| 178 | +{ |
| 179 | + "externalDetectors": [ |
| 180 | + { |
| 181 | + "name": "EXCV", |
| 182 | + "title": "Excavator as a sensitive detector", |
| 183 | + "macro": "cad_out/excavator/geom.C", |
| 184 | + "anchor": "barrel", |
| 185 | + "detID": "TST", |
| 186 | + "sensitiveVolumes": ["Bucket"], |
| 187 | + "placement": { "translation": [21.01, -13.22, -19.66] } |
| 188 | + } |
| 189 | + ] |
| 190 | +} |
| 191 | +``` |
| 192 | + |
| 193 | +- `sensitiveVolumes` and `sensitiveMedia` match **substrings** of TGeo volume and medium names. |
| 194 | + `"Bucket"` above selects five volumes. |
| 195 | +- `detID` is an existing detector identity that no active built-in detector uses. The default is |
| 196 | + `ITS`. It decides the hit file, for example `o2sim_HitsTST.root`. The branch keeps the module |
| 197 | + name, here `EXCVHit`. |
| 198 | +- Without `sensitiveMacro`, the built-in action records one entrance/exit hit per charged track in |
| 199 | + `o2::ext::Hit`. |
| 200 | +- A custom action is a macro, named by `sensitiveMacro` and `sensitiveFunction`, that returns an |
| 201 | + `o2::ext::ExternalDetector::SensitiveFcn`. It is compiled at run time and can use |
| 202 | + `TVirtualMC::GetMC()`, `currentSensorID()`, `currentTrackID()` and `addHit()`. See |
| 203 | + `Detectors/External/macro/sensitiveActionExample.macro`. |
| 204 | + |
| 205 | +In parallel mode, the hit merger reads the same `--extGeomFile` and persists the external hits. |
| 206 | + |
| 207 | +`run/SimExamples/External_Sensitive_Detectors` defines two detectors, `ACYL` and `BDISK`, from |
| 208 | +hand-written macros. It needs no CAD input; run `./run.sh` there. |
| 209 | + |
| 210 | +## TGeo to STEP |
| 211 | + |
| 212 | +```bash |
| 213 | +o2-tgeo-to-cad geometry.root out.step [--top VOLUME] [--include-name RE] [--carve-mothers] \ |
| 214 | + [--media-json out_media.json] [--report report.json] |
| 215 | +``` |
| 216 | + |
| 217 | +`o2-tgeo-to-cad --help` lists the remaining options. Converting the resulting STEP back with |
| 218 | +`--media-json` closes the round trip. |
| 219 | + |
| 220 | +## Checks and validation tools |
| 221 | + |
| 222 | +`validation/` is not installed. Run its scripts from `$O2_SRC/Detectors/CADSupport/validation/`. |
| 223 | + |
| 224 | +- `root -l -b -q "$O2_SRC/Detectors/CADSupport/test/checkSurfaceSidecars.macro(\"cad_out/excavator\")"` |
| 225 | + loads every `surfaces_*.bin` in a folder and reports closure, orientation and capacity. |
| 226 | +- `--surface-report PATH` shows which faces are exact, recognised or unsupported. |
| 227 | +- `validation/makeTestPartDB.py` builds a database of parts held both as surfaces and as meshes. |
| 228 | + `o2-bench-cadsupport-solid-harness` validates and times them. See |
| 229 | + `doc/reference/SolidNavigationHarness.md`. |
| 230 | +- `validation/runOracleGate.py` is the acceptance gate: it converts models, samples each part and |
| 231 | + scores it against the OpenCascade oracle. `compareGateRuns.py` compares two gate reports. |
| 232 | +- The oracles answer from OpenCascade: `occtOracle.py` per solid, `xrayOracle.py` as crossing lists |
| 233 | + for the X-ray benchmark (`runXRayBench.py`), and `assemblyOracle.py` volume by volume along a ray |
| 234 | + through an assembly. `checkKnownSource.py` scores a part against the `TGeoShape` it came from. |
| 235 | +- `validation/overlapCensus.py` sorts every pair of placed solids in a STEP assembly into |
| 236 | + disjoint, touching or interpenetrating. |
| 237 | +- `validation/roundTripReport.py` reports what the TGeo → STEP → TGeo round trip made of each part; |
| 238 | + `exportSourceShapes.py` exports the source shapes it compares against. |
| 239 | +- `validation/renderTGeo.py` raytraces a TGeo geometry through the navigator into a PNG, coloured |
| 240 | + by representation with `--csg-report`. |
| 241 | +- `validation/closure/` runs the same events through a TGeo module and through its STEP round trip |
| 242 | + and compares the hits (`run_closure.sh`). |
| 243 | +- `validation/demo/` converts ExcavatorArm into exact and tessellated geometry and compares `o2-sim` runs |
| 244 | + over both (`convert_all.sh`, then `run_all.sh`). |
| 245 | + |
| 246 | +Tests and benchmarks built with the module: |
| 247 | + |
| 248 | +| binary | what | |
| 249 | +| --- | --- | |
| 250 | +| `o2-test-cadsupport-BVHSurfaceSolid` | unit tests of `O2BVHSurfaceSolid` and the sidecar reader | |
| 251 | +| `o2-test-cadsupport-BVHAssembly` | unit tests of `O2BVHAssembly` | |
| 252 | +| `o2-test-cadsupport-FlatCSG` | unit tests of `O2FlatCSG` | |
| 253 | +| `o2-bench-cadsupport-solid-harness` | per-part validation and timing | |
| 254 | +| `o2-bench-cadsupport-xray` | X-ray transport benchmark over a part database | |
| 255 | +| `o2-bench-cadsupport-overlap` | overlap census of a placed geometry | |
| 256 | + |
| 257 | +## Reference documents |
| 258 | + |
| 259 | +`doc/reference/`: |
| 260 | + |
| 261 | +- `BVHSurfaceSolid.md`: the exact-surface solid and its sidecar format. |
| 262 | +- `Design_FlatCSGSolid.md`: the flat CSG solid and its sidecar format. |
| 263 | +- `CSG_Pipeline.md`: CSG recognition and acceptance. |
| 264 | +- `TolerancePolicy.md`: every tolerance, with its value and reason. |
| 265 | +- `SolidNavigationHarness.md`: the validation harness. |
| 266 | +- `Roadmap.md`: deferred work. |
| 267 | + |
| 268 | +Beside them in `doc/`: |
| 269 | + |
| 270 | +- `known-issues.md`: open defects and limitations. |
| 271 | +- `ideas.md`: proposals that are not yet decided. |
0 commit comments