Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions Detectors/CADSupport/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ loaded in ROOT on its own, or injected into `o2-sim` as a passive module or as a
detector. Injection is data-driven: a JSON file tells `o2-sim` which macro to load, where to anchor
it and, for detectors, which volumes produce hits. Nothing is recompiled.

The tutorial `doc/tutorial/index.html` walks through the whole route on the shipped `ExcavatorArm.step`
model. This file is the option reference.
The tutorial in `doc/tutorial/` walks through the whole route on the shipped `ExcavatorArm.step`
model, and takes the ITS out to STEP and back as a worked example. This file is the option
reference.

## Software setup

Expand Down
1 change: 1 addition & 0 deletions Detectors/CADSupport/doc/tutorial/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
site/
50 changes: 50 additions & 0 deletions Detectors/CADSupport/doc/tutorial/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# CAD to Simulation — the `Detectors/CADSupport` tutorial

Start at **[docs/index.md](docs/index.md)**, or read the pages in order:

**Start**

1. [Install the software](docs/install.md)
2. [Convert your first model](docs/first-conversion.md)

**Converting**

3. [How a part is represented](docs/representation.md)
4. [Convert only part of a model](docs/partial.md)
5. [Give it materials](docs/materials.md)
6. [Field and cuts](docs/field-and-cuts.md)
7. [The geom.C file](docs/geom-c.md)

**Simulating**

8. [Add passive geometry](docs/passive.md)
9. [Make it produce hits](docs/hits.md)
10. [Grow it into a real detector](docs/real-detector.md)

**Worked example**

11. [The ITS, out and back again](docs/its-round-trip.md)

**Reference**

12. [Check your geometry](docs/checks.md)
13. [Limits and pain points](docs/limits.md)

## Reading it

Every page is plain Markdown and renders correctly in the GitHub file view: alerts use GitHub's own
`> [!NOTE]` syntax, the diagrams are ```mermaid fences, and the figures are ordinary images in
`docs/images/`. Nothing has to be published for someone to read this.

## Building the site

The same sources build a browsable site with search and a sidebar:

```bash
pip install mkdocs-material
mkdocs serve # http://127.0.0.1:8000
mkdocs build # static site in ./site
```

`hooks/github_alerts.py` turns the GitHub alerts into Material admonitions at build time, so the
Markdown stays GitHub-native and no extra plugin is needed.
65 changes: 65 additions & 0 deletions Detectors/CADSupport/doc/tutorial/docs/checks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Check your geometry

Before trusting any physics that came out of a conversion, it is worth spending a few minutes on four
checks. They are ordered cheapest first, and in practice the first two catch most problems.

## 1 · Read the cascade table

The converter already told you what it decided for every part, and wrote the same information to
`csg_report.json`. A part that declined CSG says which test it failed and by how much, which is often
enough to see that a model is nearly-but-not-quite a primitive. A large tessellated count on a model
you expected to be analytic is the signal to look at `--recognize-surfaces` and the surface report
below.

## 2 · Look for overlaps

Run `build_and_export("geom.root", true, true)` to get `CheckOverlaps`; zero illegal overlaps is what
you want to see. A non-zero count is worth taking seriously, but do not assume it is the conversion's
fault: engineering assemblies are drawn for manufacture, not for particle transport, and slightly
interpenetrating parts are common in perfectly good CAD models.

## 3 · Confirm the exact solids really load

Successfully extracting a solid's surfaces does not guarantee the result is a usable, watertight body.
This macro loads every `surfaces_*.bin` in a directory the same way the transport does, and reports
closure, orientation consistency and enclosed volume:

```bash
# $O2_SRC is your AliceO2 source directory
root -l -b -q "$O2_SRC/Detectors/CADSupport/test/checkSurfaceSidecars.macro(\"cad_out/excavator\")"
```

```text
OK surfaces_Bucket_0_1_1_6.bin surfaces= 97 closed=1 orient=1 capacity=58.3121
OK surfaces_Base_0_1_1_3.bin surfaces= 44 closed=1 orient=1 capacity=241.281
...
SUMMARY cad_out/excavator
sidecars found : 13
loaded : 13
rejected by the reader : 0
loaded but not IsClosed() : 0
orientation inconsistent : 0
```

`closed=1` means the solid is a watertight manifold, which is precisely what navigation requires. Any
non-zero number on the last three summary lines identifies a part that will not transport correctly.

## 4 · Find out what the geometry really is

A subtlety worth knowing: the surface type stored in a STEP file describes the *exporter*, not the
geometry. CAD kernels routinely write an exact cylinder as a rational B-spline, which is an exact
representation rather than an approximation — but dispatching on the stored type would throw that
exactness away. The converter therefore classifies faces by their actual shape, and its surface report
shows the effect:

```bash
# a per-face classification, written alongside a normal conversion
--surface-report cad_out/mydet/surface_report.json
```

## Going further

`Detectors/CADSupport/validation/` holds the tools the development of this system is validated with:
an acceptance gate that scores converted parts against the OpenCascade oracle, an overlap census, a
round-trip report, and the closure test that the [ITS example](its-round-trip.md) follows. They are
not installed — run them from the source tree. `README.md` lists them all.
42 changes: 42 additions & 0 deletions Detectors/CADSupport/doc/tutorial/docs/field-and-cuts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Field and cuts

There is one place where the converter cannot give you everything, and it is worth being explicit
about rather than discovering later. A CAD file describes a *part*. It cannot describe how you want
that part simulated — how the magnetic field should be integrated through it, how long a step may be,
which secondaries are worth producing. Those are simulation choices, and no CAD format has anywhere
to record them.

## Magnetic field

For the field there is a clean answer. Pass `--in-field` when the module sits inside the magnet, and
the emitted macro will ask the **live** field for its integration method and maximum field strength
at the moment the geometry is built — which is exactly what a hand-written O2 detector does from its
own `createMaterials()`. Nothing is baked into the file:

`geom.C · emitted`

```cpp
int cad_ifield = 2;
float cad_fieldm = 10;
cadFieldTrackingParams(cad_ifield, cad_fieldm); // queries the loaded field
med_Stainless_Steel->SetParam(1, cad_ifield); // ifield, from the live field
med_Stainless_Steel->SetParam(2, cad_fieldm); // fieldm, from the live field
```

The `2,10` you see there is only a seed, used if no field happens to be loaded, and `--in-field 1,5.5`
overrides it. To confirm that the query really happened, check `fieldm` rather than `ifield`:
`ifield = 2` is also the seed value and therefore proves nothing, whereas a `fieldm` the seed could
not have produced — ALICE reports 15 — proves the live field answered.

## Step control and physics cuts

> [!WARNING]
> **These silently default to nothing**
>
> Without `--in-field`, a CAD-authored medium is built through ROOT's three-argument `TGeoMedium`
> constructor, which **zeroes every parameter** — including `ifield`, meaning no field tracking at
> all. Step control (`tmaxfd stemax deemax epsil stmin`) stays at the transport default in every
> case, and special physics cuts are never applied, because there is no `simcuts.dat` for a module
> with no detector directory to hold one. None of this is loud: the simulation runs and the numbers
> look plausible. So set `--in-field` deliberately, and treat cuts as a known open item until your
> study grows into a [real detector](real-detector.md), which is where they come back.
92 changes: 92 additions & 0 deletions Detectors/CADSupport/doc/tutorial/docs/first-conversion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Convert your first model

Rather than start on your own detector, it is worth converting something small and known-good first,
so that anything odd later is clearly your model and not your installation. A toy excavator arm is
committed to the repository for exactly this purpose:

```text
$O2_ROOT/share/CADSupport/examples/ExcavatorArm.step # 13 leaf solids, ~500 kB
```

It converts in seconds and is varied enough to be interesting: the hydraulic rams and pivot pins are
plain cylinders, the boom and stick are machined bodies full of concave features, and the bucket has
a torus in it. Run the converter over it, asking for all three representations at once — we come back
to what those are in the next section:

```bash
mkdir -p cad_out/excavator
o2-cad-to-tgeo \
$O2_ROOT/share/CADSupport/examples/ExcavatorArm.step \
--output-folder cad_out/excavator \
-o geom.C \
--step-unit auto \
--csg auto --exact-surfaces auto --mesh --mesh-prec 0.05
```

That takes about thirteen seconds. Along the way the converter prints three lines worth reading on
*every* run, because each one catches a different common mistake:

```text
Detected STEP length unit: mm (scale to cm = 0.1)
Placement check: 13 leaf placement(s), all at distinct world transforms.
Emitting 13/13 logical volumes as exact O2BVHSurfaceSolid
```

The unit line bites hardest. TGeo works in centimetres and most CAD systems export millimetres, so a
silent unit error gives you a detector ten times too big and a simulation that still looks almost
plausible. `--step-unit auto` reads the declaration in the file; pass `--step-unit mm` explicitly when
the file declares something you do not believe. The placement line then tells you whether two leaves
landed on the same world transform, which almost always means a duplicated part in the CAD model
rather than a real coincidence.

Finally the converter prints what it decided for each part, ending in a one-line summary:

```text
=== REPRESENTATION CASCADE (per leaf solid) ===
volume carried by evidence
BasePin csg TGeoTube(rmin=0, rmax=1, dz=5) [tier1-tube], dV_sym=0 cm^3
Base surface declined CSG: 7 axis clusters: beyond the recogniser's scope ...
BoomCylinderOuter csg TGeoTube(0.6,1,7.991) u TGeoTube(0.7,1.5,1.5), dV_sym=0 cm^3
...
tiers: CSG 7, exact surfaces 6, tessellated 0 (of 13 leaf solids)
```

Seven parts came out as ordinary ROOT shapes, six as exact surface solids, and none had to fall back
to an approximate mesh. The `dV_sym=0` is the reassuring part: it is the symmetric-difference volume
between what was emitted and the original CAD solid, so zero means the conversion is exact rather
than merely close.

## Look at what you made

Numbers in a terminal are no substitute for seeing the thing. The macro can build the geometry and
write it out as an ordinary ROOT file:

```bash
cd cad_out/excavator
root -l -b -q -e '.L geom.C' -e 'build_and_export("geom.root");'
```

![A shaded render of the converted excavator arm: bucket, stick, boom and hydraulic rams, seen from above and to the side.](images/excavator_render.png)

*The converted model, drawn by casting one ray per pixel through the TGeo navigator — so this is the
geometry as the transport sees it, not a separate preview mesh.*

The simplest interactive way to inspect the result is ROOT's own web display, which renders the
geometry with JSROOT in your browser and lets you rotate it, hide volumes and click through the tree:

```bash
root --web geom.root
```

If you are on a remote machine where opening a browser is awkward, export the geometry as a JSROOT
document instead and open that file locally. It is a self-contained 32 kB for this model, and can be
dragged straight onto [root.cern/js](https://root.cern/js/):

```bash
root -l -b -q -e 'TGeoManager::Import("geom.root");' \
-e 'TBufferJSON::ExportToFile("excavator.json.gz", gGeoManager);'
```

Spend a minute here. Turning the model around is the fastest way to notice that a subassembly is
missing, that something sits at the wrong scale, or that the part you care about was quietly filtered
out.
34 changes: 34 additions & 0 deletions Detectors/CADSupport/doc/tutorial/docs/geom-c.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# The geom.C file

Everything the converter does ends up in one ROOT macro, and it is the artefact worth caring about.
It exports two functions: `get_builder_hook_unchecked()`, which is what `o2-sim` calls when it loads
your geometry, and `build_and_export()`, which you already used to look at the model on its own.

Alongside it, the output folder holds the binary payloads the macro reads — `facets_*.bin` for meshed
parts, `surfaces_*.bin` for exact ones and `flatcsg_*.bin` for flat CSG solids — plus
`csg_report.json`, which records what each part became and why.

> [!WARNING]
> **The macro and its binaries travel together**
>
> `geom.C` loads those `.bin` files **relative to its own location**. Move or copy the macro without
> the rest of its folder and it will build an empty geometry without complaining. Always move the
> directory.

`build_and_export()` runs `CheckOverlaps` only when asked, because on large models it is slow:

```bash
root -l -b -q -e '.L geom.C' -e 'build_and_export("geom.root", true, true);'
```

```text
Info in <TGeoManager::CloseGeometry>: 14 nodes/ 14 volume UID's in geom
Info in <TGeoNodeMatrix::CheckOverlaps>: Checking overlaps for Assembly and daughters within 0.1
Info in <TGeoNodeMatrix::CheckOverlaps>: Number of illegal overlaps/extrusions : 0
```

Finally, a structural point that shapes how you organise your work: each converted directory holds
exactly one `geom.C`, and each `geom.C` describes one thing you hook into the simulation. If your
study involves three CAD subsystems, you run the converter three times into three folders. They
coexist without trouble, because the loader compiles each macro into its own namespace at run time,
so the identical function names inside them never collide.
Loading