From 28a4b99bb80cb14731a7e55463253c560318352e Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Sat, 12 Sep 2026 20:31:43 +0200 Subject: [PATCH] Publish the CAD support tutorial as MkDocs sources This converts the single-page tutorial to Markdown, adds a worked example on the ITS and updates the README pointer. - doc/tutorial/index.html is replaced by a MkDocs project: mkdocs.yml, one page per section under docs/, and the figures as files in docs/images/. - The pages use GitHub's own alert and fence syntax, so they render in the repository file view without a published site. - hooks/github_alerts.py turns those alerts into Material admonitions at build time, so the site needs only mkdocs-material. - docs/its-round-trip.md is new: the ITS through o2-tgeo-to-cad and back, then placed as a sensitive external detector to produce hits. - The tutorial pointer in README.md now names the directory. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_017gNpas1pZ9DNBhVesoM5QZ --- Detectors/CADSupport/README.md | 5 +- Detectors/CADSupport/doc/tutorial/.gitignore | 1 + Detectors/CADSupport/doc/tutorial/README.md | 50 + .../CADSupport/doc/tutorial/docs/checks.md | 65 + .../doc/tutorial/docs/field-and-cuts.md | 42 + .../doc/tutorial/docs/first-conversion.md | 92 ++ .../CADSupport/doc/tutorial/docs/geom-c.md | 34 + .../CADSupport/doc/tutorial/docs/hits.md | 114 ++ .../docs/images/excavator_cascade.png | Bin 0 -> 41077 bytes .../docs/images/excavator_mesh_only.png | Bin 0 -> 23119 bytes .../tutorial/docs/images/excavator_render.png | Bin 0 -> 42209 bytes .../CADSupport/doc/tutorial/docs/index.md | 59 + .../CADSupport/doc/tutorial/docs/install.md | 70 + .../doc/tutorial/docs/its-round-trip.md | 207 +++ .../CADSupport/doc/tutorial/docs/limits.md | 28 + .../CADSupport/doc/tutorial/docs/materials.md | 55 + .../CADSupport/doc/tutorial/docs/partial.md | 39 + .../CADSupport/doc/tutorial/docs/passive.md | 58 + .../doc/tutorial/docs/real-detector.md | 35 + .../doc/tutorial/docs/representation.md | 77 + .../doc/tutorial/hooks/github_alerts.py | 51 + Detectors/CADSupport/doc/tutorial/index.html | 1436 ----------------- Detectors/CADSupport/doc/tutorial/mkdocs.yml | 63 + 23 files changed, 1143 insertions(+), 1438 deletions(-) create mode 100644 Detectors/CADSupport/doc/tutorial/.gitignore create mode 100644 Detectors/CADSupport/doc/tutorial/README.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/checks.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/field-and-cuts.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/first-conversion.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/geom-c.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/hits.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/images/excavator_cascade.png create mode 100644 Detectors/CADSupport/doc/tutorial/docs/images/excavator_mesh_only.png create mode 100644 Detectors/CADSupport/doc/tutorial/docs/images/excavator_render.png create mode 100644 Detectors/CADSupport/doc/tutorial/docs/index.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/install.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/its-round-trip.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/limits.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/materials.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/partial.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/passive.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/real-detector.md create mode 100644 Detectors/CADSupport/doc/tutorial/docs/representation.md create mode 100644 Detectors/CADSupport/doc/tutorial/hooks/github_alerts.py delete mode 100644 Detectors/CADSupport/doc/tutorial/index.html create mode 100644 Detectors/CADSupport/doc/tutorial/mkdocs.yml diff --git a/Detectors/CADSupport/README.md b/Detectors/CADSupport/README.md index c40af97139c97..72081a2244a97 100644 --- a/Detectors/CADSupport/README.md +++ b/Detectors/CADSupport/README.md @@ -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 diff --git a/Detectors/CADSupport/doc/tutorial/.gitignore b/Detectors/CADSupport/doc/tutorial/.gitignore new file mode 100644 index 0000000000000..45ddf0ae39707 --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/.gitignore @@ -0,0 +1 @@ +site/ diff --git a/Detectors/CADSupport/doc/tutorial/README.md b/Detectors/CADSupport/doc/tutorial/README.md new file mode 100644 index 0000000000000..0d915048cc242 --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/README.md @@ -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. diff --git a/Detectors/CADSupport/doc/tutorial/docs/checks.md b/Detectors/CADSupport/doc/tutorial/docs/checks.md new file mode 100644 index 0000000000000..615398cc2c3cb --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/checks.md @@ -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. diff --git a/Detectors/CADSupport/doc/tutorial/docs/field-and-cuts.md b/Detectors/CADSupport/doc/tutorial/docs/field-and-cuts.md new file mode 100644 index 0000000000000..ebf460bf5e96b --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/field-and-cuts.md @@ -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. diff --git a/Detectors/CADSupport/doc/tutorial/docs/first-conversion.md b/Detectors/CADSupport/doc/tutorial/docs/first-conversion.md new file mode 100644 index 0000000000000..2a34a93e53055 --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/first-conversion.md @@ -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. diff --git a/Detectors/CADSupport/doc/tutorial/docs/geom-c.md b/Detectors/CADSupport/doc/tutorial/docs/geom-c.md new file mode 100644 index 0000000000000..0f246d87f33de --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/geom-c.md @@ -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 : 14 nodes/ 14 volume UID's in geom +Info in : Checking overlaps for Assembly and daughters within 0.1 +Info in : 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. diff --git a/Detectors/CADSupport/doc/tutorial/docs/hits.md b/Detectors/CADSupport/doc/tutorial/docs/hits.md new file mode 100644 index 0000000000000..f71a78f07680e --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/hits.md @@ -0,0 +1,114 @@ +# Make it produce hits + +Passive geometry answers questions about material budget. To ask whether your detector is actually +hit, and how often, some of its volumes need to be sensitive. This is the fastest route from a CAD +file to plottable hits, and it still needs no detector class and no rebuild — we simply change the +array name to `externalDetectors` and say which volumes should record: + +`externalGeometry.json` + +```json +{ + "externalDetectors": [ + { + "name": "EXCV", + "title": "Excavator as a sensitive detector", + "macro": "cad_out/excavator/geom.C", + "anchor": "barrel", + "detID": "TST", + "sensitiveVolumes": ["Bucket"], + "placement": { "translation": [21.01, -13.22, -19.66] } + } + ] +} +``` + +## Choosing the sensitive volumes + +There are two ways of selecting them, and you may use either or both as long as at least one is +non-empty. `sensitiveVolumes` matches against TGeo volume names, and `sensitiveMedia` matches against +medium names — the latter being a convenient way to make every silicon part in an assembly sensitive +at once, however the parts happen to be named. + +> [!WARNING] +> **Both match substrings, not whole names** +> +> This catches people out. On the excavator model, `"sensitiveVolumes": ["Bucket"]` selects **five** +> volumes rather than one — `Bucket`, `BucketLink1`, `BucketLink2`, `BucketCylinderInner` and +> `BucketCylinderOuter`. The startup log prints every volume it registered, so read it and tighten +> the string if that was not what you meant. + +## Choosing a DetID + +The `detID` field ties your detector to an existing O2 detector identity, which is what determines +where the hits are filed. Pick a slot no active built-in detector is using: + +- `TST` is the general-purpose test slot, and the right default for a quick study. +- An upgrade study normally borrows the slot it stands in for — `TRK` for an ALICE 3 tracker, for + instance — because it is semantically honest and keeps downstream tooling happy. + +The hit branch keeps *your* module name rather than the borrowed one, so the configuration above +produces a branch called `EXCVHit`. + +## Running it + +```bash +o2-sim-serial -n 3 -g boxgen --seed 42 \ + --detectorList EXTCAD:detectorlist.json \ + --extGeomFile externalGeometry.json \ + --configKeyValues 'BoxGun.number=500;BoxGun.pdg=211;BoxGun.eta[0]=-1;BoxGun.eta[1]=1;BoxGun.prange[0]=2.0;BoxGun.prange[1]=5.0' +``` + +```text +External detector EXCV: 5 sensitive volume(s) selected +External detector EXCV: registered sensitive volume 'Bucket' (MC volID 8, sensor 0) +CREATING BRANCH EXCVHit +External detector EXCV EndOfEvent: 681 sensitive step(s) -> 94 hit(s) +External detector EXCV EndOfEvent: 402 sensitive step(s) -> 59 hit(s) +External detector EXCV EndOfEvent: 927 sensitive step(s) -> 124 hit(s) +``` + +The hits land in `o2sim.root`, one entry per event: + +```bash +root -l -b -q -e 'TFile f("o2sim.root"); TTree *t=(TTree*)f.Get("o2sim"); + t->Draw("EXCVHit@.size()");' +``` + +> [!NOTE] +> **Zero hits is usually aim, not breakage** +> +> The most common first result is `0 sensitive step(s)`, and the instinct is to suspect the +> conversion. Check where the particles are going first. The run above produces nothing at all at +> the default multiplicity of 10, simply because the excavator is a 40 cm object sitting 40 cm +> off-axis and is a small target. Raise the multiplicity or aim the gun. To rule out the geometry +> independently, shoot a ray through it in ROOT with `gGeoManager->FindNextBoundaryAndStep()` and +> print the volume names you cross — if they appear, navigation is fine and the problem is aim. + +## Custom sensitive actions + +With no further configuration, every sensitive volume records a charged-track entrance and exit hit in +the generic `o2::ext::Hit` format: position in and out, momentum, energy loss, PDG code and track +length. That is enough for occupancy, acceptance and material studies, which covers most first +questions. + +When you need something else — a different hit definition, a cut applied at scoring time, extra +quantities — you can point at a macro returning an `o2::ext::ExternalDetector::SensitiveFcn`. It is +compiled at run time and can query `TVirtualMC::GetMC()` and call helpers such as `currentSensorID()`, +`currentTrackID()` and `addHit()`: + +`externalGeometry.json · fragment` + +```json +"sensitiveMedia": ["Silicon"], +"sensitiveMacro": "sensitive_action.macro", +"sensitiveFunction": "sensitiveAction()" +``` + +> [!NOTE] +> **A worked example that needs no CAD file** +> +> `run/SimExamples/External_Sensitive_Detectors` defines two artificial detectors entirely from data +> — one using the built-in action, one with a custom action compiled at run time — from hand-written +> macros that mimic converter output. Running `./run.sh` in that directory shows both hit branches +> appearing. diff --git a/Detectors/CADSupport/doc/tutorial/docs/images/excavator_cascade.png b/Detectors/CADSupport/doc/tutorial/docs/images/excavator_cascade.png new file mode 100644 index 0000000000000000000000000000000000000000..be16f319eba19908c6122f1fa8a110c990d47385 GIT binary patch literal 41077 zcmcG$cT|&U+b@cWii&^(3J3^@B2BtMx}buzL68G#wq?S*?3_ z4C(0TU((SXyT@=8{2zfMw@c~h-YaR{Q8V&OUncuMb(puB*i#*Wt~MDD1lIJwGr#`5 zLd>El;t|)#Fv5;e=8DXtXLM4M{NZAmSKd6TK9r81&`r`3Jay_w`>{uSSNK&QhOnrI z$9__7PjcuSe5jLzxL$%9S}fgin@^wjpNsQcHPkVksP`WFxYLPx;=R>0ucUdeR)tJf zna~U%9S3KkvkmI6WvBfMT_DeAM%rh4jO_{9r@e;cG4SzJN+9J3_-sx&*G2o^wp|P` z+NUIpUWWF`Arr<)`;6iI|L1j?X=}Rj7u?iLDntlJZ=m(!?J0GKqo}CRlUaL2US85L z*jrMA5U9F(gRWtsS2IK@U7qF3dDb=sA;Z)JWODH-C-6kz+0Tp|^+lMa{Fe3fSDk_} z-Ym|n&i3JNOoMXJL{HV7dChWl+AY|@bES>Jy?3s%YA7&xn;uM6=i>G^u)E18FiPM7 z-J@Zgl46>dD4!g+Z5x86^c|92Y(|sX7pJ1JheN(0JhW#Rz%y*7%G(dr9K>IC2o75x zO!<^pE9?-k6w=8MC~cpAz&?%BycEa#nNkMd=|3^4^K23^KcZ`;r!xZAc@LZ4lrjV2 zpW{n8^4x3Y18?jcQ)=x$jP=1D0<4k49KHK;DXxG`l1&V-=4Q*SA2tjk2Vn(|TFhR& zg*=0NmK3_0u!T-z*?AP?SBz@lte^e)ZV!C7ZAl&ry()%JDHs(=h+rMS)E4f^S%>TmQD0=(oCH+94fYT4-hIaV zdYk9e6enw&U`*wys#Cca|2@c-5{%ggoHO!XAPj|0N-!-tgBLi&!Z>^4?Rj@MCc9Kv zO}k_(CEP@G;>!+sLwz_*0lk}<&UHN%ZP;(1KDnQHc98oL595OMb7z}I?&@?v3thf2 z&ho_DBwA`V_l`JZb3*<8?NH%y-c3CrlMe}@xjCm0?~1b^ABk(8DKw_O80wg z7&|=$MUaxxYPru}Au&JPT)5Sg1dxEpF~>hM1`e;0_S~NnbXgL;I8yog1-VTQve^vk zsHwYS7Tu9N_&-$2(Ufex06N(@#5+G zbE4E$s!Zb&IHE0280U6201Ao7S$WoEU6pwOhD3-uqr;P6%gnV9$*&0r?tz<)e$^F^ zCnKmEWsD0`fOcjtY$KJgx!cGZd=LMNPhD?T2PX0=uDdE|gx*^dLqIV5`k`KbWk`Wu zW|?1;cW>?S+$|PUUACq7_Z)7!*u?Zdt}c4j+b;7*lw#<>E!&JIwjS8|cZ0LHD=<`@ zs^YXhc(&Yz&zsH?f@5v`{z7k~c zVsWc6Nz%+a_XRzUPW`bK@JC{fsY$lFJzfdj>q7;<@L~~0RGtbGdaa!(DSk>Aom*G$ z=k#7j|gq0n8>r`+GVL{Ygc9r^rX&K9Z1<2sz{{9H&3o#~RQ9n2@}& zfeCVQ#11rg>xqOUDz&^qB8|w7?`oSRN{TP>v5d4BE=rPzJs37)~8Gu#(jS=$0;sdzaKHaFE_X_>Knsj91#*#-M7sxiR|b3ubXH) zK!*!ToKY|KN#ABK_cc#LCy2V6HqaE_ zWvfBrW$Ck!%#|z1SDj+*!>)TRsk1T?L-?l4;btM*JHAh%F+9}FI1QPTErxAC`0pq% zR1O+C38Prl{itnNYW~*>NkFJe=ei^l=T>)Zy?&>C7*~&2o8%e~+5BRm%OZ};;#zE~ zeyw{f2Cmozdx&mp+}xw4h_e&D#Xc_&lMc8HfZNci)~{l0DOwnwpw2*Q^6i#C=vUpJ z>@5RO{fAH}^2C}Az03=Z$2rgC#(kD&uA|nwTK4*GH}#$u=zBj!>;_WpDmAf-$4(LE z7SCi6_u2w|myfBQBPsrq1P`lqZMlamAH$?zn*--FJO>qAO7ybG^QuxEvjAf^0hx(rPS;x-5%)mP43d;PeVE893J@D6(Fb+%6&2#|up5Wgy{%4?E{5c)*LWebU`TL$jzbN!bs*?Uo-^ zbM@Y^cgzfLx0#6OSS1m~J~^$g*c_1B8$tm2^=Ra0nxM8CE*Ok|^7^&=iEAR{_m3Mz zI?02$KHl2iFKC1Vdbhc^TxrPd+^FNimiy`>siu@ynWDTYu^k>9nu3hFERH!Q9eu_8nNMV5sglFkMe0-+kYE7`i4TDL_Zd_ro3PCQ=i`Gj;+ z^xArr3oR=XJG*7wl%Hq!Rp$JiHB76WJnwCrL5*Lc7Z5H)hh_U6? zNe!2hN)zqV!l*aaev@R>eolsZD$X)#SoZp5Xwr|$1;WfGi4uQK;u0O*&nO01ips$% z<#8?Pmzf))1Fh-Sxbmw?F2*;8qM`T-ZZRrUupA8=vHvwhnhw}|_4NR=P|dAyxKeA# zG%fV#gnL=GH;-Zx+LfEt0s#qn#UBnaWjQ05;rthGKgy2Kh*)`FwfSBWFcR7S? z>5U}o#~hX(RV8{#sCdsMvS)Z6F!9nvIkC#xf1NcrZnJhYHk+)eeGqpiv98?cRQ8W> z1F4Pjs<@?@rnqh2uj9CI{H3PFf%Q?{Pi10JfWGz#)-^d@ic9I<5VYsqno^{LlZ;Lp z6#e$9H?kjL=nFyP_REI=?Dw1m;u0r2ovu1bm^qBr&+uQ^%QaAayt(*c+@-<095YR< z=PKkA{-Ljxlmq9k0s|byde$S-8(xT4VPB34%)lk9zzkI7>P%C zu!Vm(6$8<5xoX@C1l1FBgGIH|HOaxr!c8yIM6G_aW9YzQJa5}-XK>Z#gh!pnBGrw$j+pST5t{nod$+1fVaYW~ z-UR_eq!GKiB_=XRLY=Cj4!Ni9Tl7v=W*ioDG51u$pkYT2>ClP) zOSFw&{eY$ux@%ar$_Y~?hI=QgQz52>zEXv&M+~~=ICB+U#y=WALlFW|yY3YcSc>$? ziH&PsMw~k$gEF(k9Jw){nKZ6+-=Z_@F9OOiOR&1wh1Jbyo3o+^ErxqjI{1~Dj$?cd z3qOtR{2?zvXsOHMoS`Y5xKldUl(2z6wCDfwN^#s7-IznDE_&q8o1-U#gq=Ba&5H0? z3wOh$a|mN~FV)7D(ub8E&j-f($4lEtVNc-N7qlxRu01hg;lV6y+h21d2%4p-%Jm1X zNFrb1rih~YAydN*tN4>sq{IDS;2wZ@)U1>qIG`Ry!3J7Tw(X78wjd!2;f(6F@Y=;W zRuEwYxj4$>-d#K4ty)tL z=nJ>{=^tBinzJ2KdosYb+^XoyTU&1TRTT_pv2HwUJD0(GJ)B{l>8{Md`UioM@w;f) zua~~#3iHxPDEt8uwPi|8WSMHN(o{6A5JNl75wagvdrdyoS^SK#pQP9W+h(OA`-p!m-cKXC0&28TiKajSrLZAm2@rnG(zD zsyw;i8_`yWp&^J}W)Q@bD>G7?WewJHb@P9;Ou0y8Qs&T|$>Q8MA)^a#gYie9aIzzd zu>SbLuOj^r!tmze$hgcK3(ME9Mb*FUqtH5Kp`Mfx{gZaZdIgmx9}_z)ReYt;zyp-k zi5GK%ISJ@ogrfPw9rX{&wVdNOzwo%@(zFKep#umR9rUfLfaZ^K9!5Gn0Zvt-@LAvW z$@dA&HlEAvHM}wuv zUU_) zWnfW)XYcYKVs0a4htBr+prKp+GI@=vqoHEyirg!z1hU0EW^Go?DOU?%Uo8R>)`59e zT50O8vQa0dS5av!1h{O7PxGdcAq@ZvS|t42#X`gtosBa($rntMla{K|svr!{gHJMB zdiHJkHVyHptQ&f?1tDoM+zfw;o%65M=h9gWQpT19rJbn<`8EOkS{D!Etd;u%w=!{y za17_Adhs8=vgyifxJxl`X70MZ)m8RfoW{9l@9OmyZ=1R5N2x;z2$-PTfsXOF0$ox{CE&$${}75lM>ohQYq>Krc7be`RGjy-8k z;yTX898+0dQEO>a;sAxkq>icjZfy;XZ#-?JAVim?Ktvwzl;*Q+8~313X3zdXbXjm~ z-uF*j?#hps(;__GnMfLPqdU(;yPX*4nNH3Uf#aVfqE^Tq{^^u@;ozZ`-e7TEE`MS* zZkXeR9Sm2m$Lih}OLHkc{o#U*JQr_Dm6N%0P09^I5EeRo@14fGZb4Ko zce0Ken+EDfxSFLfN%e_`B{Kl1^5gBZ_Rib^6U_exLRi%k;w*ut`f}fVvehval;kQc z|M-4s3U1pf(9`TAy$3NIWaAPD@&}9YS+3Y-;um1&(p<#TUo$b*^wivNiZN1{;5Sj- zsc+H3ZPZR!%o+Y@}$vzOqnP?|Iu>}4E1OGc%MSY_* zwLSG(=%jD^HZ3j5)zk5^P|8YxQzGAI64xgY;!W8sObtFc-Ld>;wW3~Orun7cs;*_=OxPvP{_cS2B+ui;GD7M+CMD0SuS2~|g9TRp)${R==h=z*?QQ)DT;c+zP$pa^{6%&Y&OIKD} zHD-KwdRjnkEM&7DnK0PNj%S`p!v%ORj4pM$hlpC#35C}_lw?&;ZzS~=6J3pxO(im& z#EPLBm%65I51oC4Q0#5o)dBLA~kHYu;^Nx08W&u zZ7F`&qd0aO#OtxM5hxi6?Di;@^8Bq5a5$RhfVpKlIlg6sB$QvIwrg~>%8hv}?;~4l z8~rA`m?Wy}{X7i-kP(Yu>OSTGf}ZVdadkGf&UKk)w1fvB7M_;$FHWd zs{2jW+#p$B3b@B$5KLBu$6j&Cg9>ONak)fYE}Y@J1d&;x*Lt?F`MGSN{ho=vwT-eY z|3!Q3$iWtfE>A=NbJIF`5PI*U)xkUi`m1~6Vx$F}`O<=YObFWAN`czmft=7zo^QZn zTce0R2OJwiJD0k?_*6i<1Fwa%kMFGMhgt3yBl3DN+Oig}VLEHNsH&x6Zc}YLHepn{ z_m(-rgF@h?kVBisXj#4Go>H=fo&$-Hnkcl zFy@A3*M30pbE7YvkecQQ7M|^yK_YnhX;7ZZDFt_1O%dN_tlhEug0j`uN|hk%OcXa zF>|>PHSdKA`^|U_#KI_7XFfJV{46YeI|!-fGt!=YVipx6g+7?izPVqDCELUGo7OhVlvKXK zfe@KR13$T3QB&GF5;2Hx+D*`lw;!5lq<$M1v){}>cW%Iw@r{Ox8!J+!k1dB=RrmeN zJX;!8NsZet@C^K~NB!-$Mjrw<4E7FUkr9j5k{A9uYseB|APli%G1X?KY-Uk5RS)H! z`~L%A?JQ8!qlOC}5_Xm8Nb0r>MDO)j4`)u6?2^v>xS2e8< z;e$hD<;J3Rf@`EtV^vaS#g#THfFF$e+Az0pZBL1OMe9Fs-qnSRG}s)Yc4i?%rr93 z7}F&Sr9kJs*?dj^rKO3TuZa+C7gVL8xyV&i<0OWefuEKP;IhGvedirf{-XQRr_FWb zv#UMtv<4h|c%A>tXN$7V7h#nOTu7~)RA6YeuZpy(aU{S+?ASs`)ctvh%(~xuS!5hS z6+m{g0|-199=}LDkL@&+6D^;FaHxf)t((WjoK0VlmKBq?k8MvYO z-*K5suF2MZul&aP5}v%okIx2D z9C++AWBk7h%CdX*0>X(q-B-89Cn{Rs#>gXLjLe0->r@EWKf-TM1!s0tn@SZmtu^jo zwh8gd7z$2xzn*34_sTW2M!r{)?7PahSI}C8@7iY#IU+p@*|^W>$jly4hqDfsRrh^I zLO1|#tAI3l8Io2)wNY$7Be+1t{Dki#>@^|KOL*(uCYy_GX^o=&CXQ z7Yed;_2Owl*Nd=REyIrqIX5BCQZtaz*}raw&VR*YL*@$O$g4giK(L-_cDRUbR9E8) zIneFObO1q7F|uJ%G{2(SLUl7|hul@h?5(Um!^&xOGte)P^u(lAC4OZ}2i~|d;JJ7I z4JvUjV*CD^0o1HU(`w_H>0iy0x39U?txaOro|DHwuyhL;`0zdy5vampBTgfi13#lR@GpQXq>MxJgSRw~lNA3|chB;5aOYYY8 zV4+VWrb&DdRrHr zCalx7z^-+zOEn?CG!HQ9ZcBcH_Ddb~?Rc1A9q zDO$s5-!y#N zYcOC-q%IjQIOAJ60=?Fj^8Z!xnqOaHk9roH5uTK*g)$O(9;28}SK=9nn0O+2o*P$gZo*X6=GxdY}?3DJx=Pb%IeQN8Zz6QY92X5}v z{^;)z->)aLpAWqTthkZ+Hm0?_brCxLm4jEMcZn;ZG-P9gJXXZcp6L%|@c23BQlatg z#=8H$mWsaW!o}0lGICsZdtXqKRedL8(fX7YcV>5GK;>a6ao%{Hh*=;<%Z*%<_uAF; zC_mJx54OmVeU2qKMm|mOM}&J*VS+QRj|NshQuDF3qXh#QRcdUjie5j5k@_m@H6f1l zIzZ?jL`8854qeJRwyzKb4d?v+p&}a z9}bTSi&qF|%4{|;zD9WQ8Dg=EtG!{S-t-gUD0^0V&y(ACQNNy3r!~7V>-%Wz_V;rV zz>Mfhhk;{SXB~dX_3h3dVSusTc;}IW>2%mTfhHKc*G-9Xv0GdDQ~Jnb4ll+JM}Y_Y z!d;bUrJ!?BYuHr~ErZg&n5<*R(T*~FEGC9aAW>aH*U{8p6R{2;4{dvNWra6L>NCtO z_`TP1J8*Iueeg4J8H*)xpFAcL`t3_Ax>a{RvOJ{fnffMnr)kM(jby}nxE_^ha2Jot zVJ2trs#5n;^HsJRrmaAcK7}5nMZyVdUBIxz4DYG7K=<}$;&ywy%arzBQGCO!#{Vr{ zXj($W8KgblBTkSG#E(_(5;#eBfnD_uaRPs{E$VQgZ-gfxSm=&0@cQ}t18dFb?(d0R z(9|IT%E782+jd{%<=3Wgsg0IEx$5t{{;%tS35jtzf~e}7U>Q#?tMgt;zN#~yl$H8E zCmkO;JMtGwd?9%_uh#o-ZZd>*EbDUIo!IaVeMsrryGCjI`n3s^@+Bd_`x2_FxfZ%9 z_0EHXtLwyhypK`X(uX)HD5iXKfZ9*J9toRllcQn8$N3Acn5E*It72LgwVbLAMUq=? zo^G+7_p~8JjYrv_g<8M!@O6RsM zOJ=T=OA!LEjhcKGvudi`Or+MvTQCl!Uc>zHsg}^~!~Xj?n1Y3{v51E}Q%kv#_(?mn zk4ED=y(H?W>fX+@^$kFn8$eDH`U8OQi552Ef|4xPA_PW^2W*r^>_mz%+v&eot4Y+Q z;MwK?PhXV_JU3NVjbq|vpXtOun%o?eSo+sNu`s0ykZyPX(loLA z#Y-cbVDo!KCfJg=;`PGfk++KOUa7I$#@R$e*$3h{ll8bKs6WxN2r9j*${DfNf&vg? z!@Co3gs%A|FyODYL%;4EmpR$q6y;$k*p)WmwQx9VUH<8QwYQ{;<<0ZFb(9Kh1I{*S zL?QF6aAKA69&?E8iy_{eV{$gEUG2TDSJSSyp=bT_47*v)QQU1FEGN`oR;Ct|*7GgbSFhJVJed+lQ2VRjgT8ANEIDzB_^?;(&>#cKiA@-aS%? z-{=xBg10r>7~_BS9E99YRZhp{CYa}UvLo!hr`@f^6A|W8MHwMdDT=NlC&Sh)L7Rw< zj9Q^IwIqM2sTdmttyx_p-ZF%4Rn6xbl6I$gS7zJ#ER+@fmb?`hv+JJF!kJD~>JuP_ z-8yiTA!_5I$Pd8AtiZke7=SQ7TiikJakE& znD<$%s4S%e^InO)+7QZ;7$zCCr4!miuE;ov9zDAgYzqyu159ki#3 zH%Nh+E29r9vH#!_b5fruSpcpAGr8`1RXjQS=AcF(2^rhI-a;TI+k|cpsmQNh1qSUi zD0okGVWvOqe)|s4CO!95BQA}7;umG^{0O_;S(wPnuyt3n%(L1uc9u-pW+y)wF3w)N zL<)gUPc13xJB&LRo`vK2Z=dhjgb(ggRd{O(FPIQ?Hw99*Fouq5=Jh+WvVm zY+vX#7~aYvMWZ{}tyMQ>fs6HbXBf#R+U{W?-$fv2x~CVcr1tCq08V_@a@f*=gQB`o z=Bey=r__Z}xC&TFee3yd>)_VUT<$Wn(DUu;FOTF`peOzb$WN>h<~dujyCO3ly|~x5 zct}`0oa%^(cqsG@HF%$8zC}pk@l4Mv3%8(M_rp|=GSH>R$ar2xH~l$@+@}CQGs@$g z;5TO-;-Wh5Ti3qG_V}IPTf(zVH6V>*?4q+F3b%Hmx`Ci>$n3h!!M3RNsG@~}*Jwi* zbg7~r;&bsgWZ)*Xqhw-Z{)FFWr{YYW+S%dvY!RLym%?4fd-|V~YJ9o6@s%Lc6gqdv zN}ZbCYf#x$4SD44v9O_fz}w#7_qQr1*bM@C$NLtEdG8Dj3m^H1tbLE$SzGPQfPq{+ z^CO%_PJLs*g5@-UxH|DMGa&B?Im5KE;&q3bQ+u5l6x08;C7{3d@!}vebyu=f$$M>i z(HC3tsr(|TC>)$6&ob@$SAY1lZk>_v3y<8Y?QXe?v9mg703!GE>`Wjb@vtTXE2tHw zc9$*oH88Q~0d(rjUMGoI1s1VA89l=pcP5K6n_=^4DL!wg5VLJL`(?#62zoP+FOA5X zAfm>BlnmG)st$iXDp4dw9WI$5+qc#}8R>6B-zL>sJD;!8hI{0Dnf&qVfF^dvNU1Z- zr|Y3%Xkr0qb4ZfNP@x{W2f;>ZF@J!%Wb32UhK+{oW2SL|=LMwSa8&nJhY`<%oBHOB zz6!i#I4mS=LwQI=PC%S3gUEx-+_+3e|9Sc8t}b>I!-zxLPORM9EOY|{kCXgo?Fn4z4}XU z`yJoxgO`iFDW!fsGu>QPuxS5%8IVpy$pZE6zGJ|4=h>;-_AwB1;j7`lyW!9zNPIwv ziIugLk{o^k4rqB3(6aOM&Y>1hA`rJD91dW8h6>{M1C^f{%Crjhp<#}`1ETLdo@q2l zPm)U$o#Yweh_b_VQtZJgV6X*2xFrTk zTIYnXk^P#!$Jiyy6!-y8J|Vf9OHV;Hk9J9H6YgJuosTV}3eRhvG5!b?Q zGG-TuW=5!SoUnTz+nZ;s;3=+y&h0HWbt%&6+{o{4+_kvPIRQM-eNa-MPiFDzB@AERyLI>6%wx1SUWYmFFYdy2`uH`Z@3IzDb3G1YU&jwM2k z`-A~U`;s>NE<6Xsu^?w2UuodpZ}L$z`2uD$n(Fa~? zlLx;zZ^Hy*O6{Nk$WMT`5&-I@hprWFpMjOukLPKTr&5oURNhh=86F^oSe)ycdVe0D z&3C%hWJ^pUBemJF#6gPf3#jkN#%okz>KiK_&$SFuaCFt6YxyaysVR)xZzmKnMKl=q zVEwAmjjU+q=IA z_rjN~%+V?iw^-BtI{RKJnY|;d79{qWjtV>M6bGHx5O-p-Lmi(YYG|%D@|QrKKY65W zXRRP(&=UnmCUu8(D+QAf)(S-fMV&=D?BdGgvJ*b*V1r*E&}W_bO;){<$oUQR&9>>6 zRAyqF<%mc^q8^7c4})+z&YDc%t+bhL;`-cwVT!0Q)VQ;<6)mbBwMu~YRIRz%FLcr8 zxnyEG36-hL$Dx)KTGUX>%K$<=kdxAq^6U)MF~wG4cm8Sh$xin3SspwH6N0D}RBn%t zi0L^Gpe2-uA!>tWoKP;2HP?(B--!gVAzgVq%zvxcSRp#Q+(rCegV*4t(^DHC_;9&U zcOz4iWC?MVtxpp?JI9XDm9vB97UAu&tq+eu$lLgi1Hx$W70pTtx_|OnLWg3qAP(k@ zs(*}uP+53!8iMq0=(cA%b*t+ZO;Bv{4QyX7|UjYX@J-4TcpmGeFrV*TB74 zyd*tTphVo8YO=B!Jv&{)=%k*wo9It_}7m3KWm7=rrKNVzpM=pG|OG)ltpYBfh z2V@(%@|=c?<(!KK5_O{WDTnTH2hTtO{5BABwv*Tm{l)T8a>k>*7J}(bXD7xJ-hx}- zSn%~U4*_u{kGn>JE81>faKbf-9cTVEBUMti-HZA7cY+u~aLc@;zOmkqGKI$Ozw%IJ z0qLEiAf9_@#le>tYeiZs_LwvZn%Vy#p2z+LT|G^ztMYKVE8@^m@+{WFG5h@uebq%- zISo6ONWnjAesW5?8mob*s;)2Jj0jy@`?F#LFIX{K@x+@B!LR41q4fb$De|&uZJS|! zgFc(3T=9wZ!kR8HNOiY@Mc*jLaW#&#W=BMV8@{jR4@eDnED{b|OhXoS+R3*-h8ncf z>qhy|@g?f!__cLU6tDKZU?8NnT^flq)|Z43?yO@WT6*3P!SU3%b{*Z>6(Ai#0oS_o--sG@%>$;wZ_UlzSwgV%9eJMAm2Ou?=ZO>QAXa+|9cq`a2a|-Vn07vCHxDzXu?~^ z-p-hFtZAt@cn}It2`TcrK>rM);gskLF?T^C^U3Wl@g$Vm+v*^dBI{#%%dB=08CEU zzIh2Bk>2+6nVM_>VWlPPx#1)2jAyZ2Q&5g@1CHZ@44~_&1SK-JFe2*$LDFdFv`1Lj zz*F1d?FV;&(G?Ek1fDgx_>XC#@eZb{&dm6OuusN$<=DGh#hy&w+>!nZVydgU!f%1F zyQbyg0&=R&t?DXwXYkZ*Ki-p|x&DsiF-bAd;KmuKq!@IdHF8f^UfSc)R5uh3wB^DJ zHMR(U_2{Flk?kp~ANZhYC|s^VQJHv|SSEINxh&*iW85*iW-l-Uz#%?Gtd5b( z#OjEu7rtjpGwkgH8UhT+`y1s_U2)k=u~Hh|kzqe%aql$1K`gAy_FJ&KyBt}^>7Fit z{(H&Ow1c=SDFzJ1e%BsL$eF4|cT1AXgZaEUyk9HIJnJ168q3Ug@4MpEzgPvhxd$va zffTz2#w1cM03pb^tu&G()sA){VVhL2GZ`*CcgKV?W|d>PfKrST%@VrPI|*Xn=NBZd zN!C0gJN`P9p4H8`aX8h{0#pckfzc? z0$!r(gPhiMu)B)W0RS%qfp$0h(F7r8ZTUCi8seP~5I%L*lPj6+05aJkGX8H$umwbk zmh5@zaTzeOc8WMasz-e){}|)u4LLCe&p4h@$Cp7!mHPisfOWS2yuOC4_Y-VS>uEoa z2L+4U=7$`(&_`Wd`=kUy)c99G7!7F` z5L;WOrNTp;pnkoJu8hO*41~GDnXp^)86amFoQ!fp6N}3RP1uuFsc5YHz9z^dNrPD* zU10jEW3FcM1kdYzH^e2FqtL5w_g7x=x#+;#9Z^-^bl`(|xGTJ;E4(}=Y3b)*yhrGs zYTX7@e?`-Q{r{l@N|{wBAUlg3s(9sJ2Hks`@rp8QbG=D(akd%vD&YKgzmxmWC4_I4S+&Z+qF7S}sk z_M-8W#cLXWmN){I=ufE+ulAiHuI-Eto0*+6tXAL}GQyj)stu#iJ>l&fISRMcT+?7$ zIR+LbCF0`2lUbob3DGC%Z2w^aB;=fek~FySugAUZD-7jmyeoFOe6J_%Bd^BT*9^{* zSi%axF}*1Neb6Rivxl1Cnw>gA#~}eEr|^!_(wNZne_Mgq*{ccNz#&y=-OMpr5c>uY zjF9RH1(pfc1k}zvGjwG+nwL8I?E*7hGY5@(qdI4|kw>qSHqayJbon!#e+XnizVqbx1xG8oislDZ+bo~I4!z;`C zg4bM_mo~he17$o;bS%NWt0X!OniLhC(VeVZbJT+_WG+ECcpUFY)3k0PU^<+F88>20-ARBXHb! zwsbmMqD|oTJ;8&-b^`Pb@7=M~d{Tuh0)pHEk;oK?rnco?G}+S8Y0%(%h^DX%H0;M* zg?>ykEb>aiABW26h9VZaI7%un_%DD)Lanqa8eaTI?#tO3>6*U*b}v7niK~xN17)(o z1b@A@tNL{|lewo_R4lyh&YPQzEs`}_C``v;EFNgf51 z4`f(#wE#0POcNml$UhHW>vE^odJaNGGpiyOYNk@9Tq46BIVA~d<(U5m^t%A)7Ze@; zGaN>@%}v|=#$V8nIM44;g!a(eD!y?p*ZZP3M_Lbuy%AjKrCy(yldN-5)j%6c;&3C5 z*&QUH8P5a0UAhb4`a?T9$9F8HxL;3R8s#_*h12kk2ACQP+d$(5L9OIJIDhufi>JVg znHu25$u{)TDz*vo7w0of@nPFgxJw#WD~J~y`$RJ2lTlED;~>}-{C7O+`fb28F%ei$|N$5+o|Z=bOs5%^n}9VVQ7OQ(`1ti z-5`^`6e^kP4Ys}owl;eXB<^<9Utxb}>@=|GYKdJdzQLaRKtZNmsARoc!v&E9dbQ9P zb1SG>0If@aBN}WCrdjC9PthnYoXJzS5m`CR*x_;la$&95Shf?%Wr0H904{mbRbZlB z`b|tO{72N=F9T5rGa_`B7ihdb2Yj6g#YOhy2==^D;fyN0!V+U&Tr>gd!R8JB6Q~ef z2SLml-I+xtPH^v@{@-sO`TO>3%*OTaPX@k}Si$`lT*oBIRVW!+)INhk1Ar~DwkQ<0 zvh)M}8;ZZrAEhh54Q~AF6nOQ-C46bhzPtvH3@pBErxHsGM& zyU!wEETun@2PL=;7Htin5_RC+s#KDS@(i z1B@y3$N>tta{WCB(}c%u6ZaFd-@d$kr3lYBzgDcQi#?Bzfy%I5(Lu{>`*nkZLdJzR!XL=XdNk z0R+^s*`4R*{RyT{X>lZ-PdMQF^H{hhsa_abmU~AY$(@dY+2MHf|L0G|bPOoE0zc`=QudrHdrQl+Wuh)UA zrQrL9qJ3|o9IR1YEy=jr?U!Y$Jssd9cVYVmeZUaLHd1F)^;bW@^ij$x}Fk$2v zSL@-eMH8jymhJg$uWDE(1_ydfWfxc4bab@$1#T&E`CJ)sT!^qK zu(7;gT^Sih4_vIOD3}4qA1O5B{(pmfnwQXQ4ZM}7=ZNUF;vFNONei2?U7Ud2#Mn;B zeAYhvzZHUf=Z(-y#s7y&*jVnae+{W2C4!Li-Vr}aPzj5~ z;?aab&>N^F}&%{@156nJ))uICImkZ!mYD}Mi9hq1_vo|!3l zd}5lgW*(b&mNzV_9vUcq@1Rx&V0-1mS?rS@H);k{aw7T_ue$VY=Q* zOM!xvS@q%j)6iwCWkdN)i3_L^hYk|c=GrB@bl}5(<75@K-0J#Tn%*q-WO4KlGq}=~ zpQJ4@rE3qmAhjF>euU(is0>+_rU^-UJkow<9XLpw`@?1R)4iGbHX!>})XC0nL!Pty zgx;f=-hmx^Iu->)j@5Y)q=jzdu07|O`87}N5iAtGy97f0DQLfy!WlpWOcDUTd9;{L z8ZJoaTMKayov-g2z<{X` z>`>$?se-uc@-BPXc1^LOQD%w2Q3+=ey*z7MAVt&alfryrsT2d36?_d zOWzY9+QPz#M%vnYZP=ZBpIYF9)0`Sx=jFG4)xAOums^*=@3%QjMhL%X^MIGw?StT> z2;}sh3yA^Ow5!h-nv~q_3{1>KfScRBPB*HuLAxWXP)Q=e&!8Yp|sTcTYBWWJK3a{ zSYTvbU6HESR|$3RH!bQ+PO{!>|I9yDKN3p{=?Ww@ZHQA{Gd%UFeY{+sOODV5gC9z0 z?ia{B`{no1k(C-O&BHSJ<6$-U?Qglf@XD!<-%(j?+US;_Z$x*S&o2*AW^FjOga7lY zxSA5zJ7I-4B_rOxsy{P4yA|L~o_liNf%<)-c?m@gKID<+JD0)v#Wj_Xr;#Th4lx7`mz>HZZ5`C3rQg4jv-}#ripJyU zY(;1?q0g78mor@x3O5japsiiC_WPG<=HVTQp${L<{fV6Ewppf#3H4xLAz|g-ptoP8 zFQxxM*5d5xM?m-=(~Q37OZbCD+89ZcVd_qI)xRQWS|`0ICOJ|AJmEYFYMe2Ds@t>* zcdh9&L?D=8+mM{gxc)k}Iu%V+X13l7#o?QNqs|qx07pOsTruB38L2Qdu~EJu&J6Q! z(`E2D?1{G_r#DUoIyurmeRUW7paly)uj_^{2Cq0@yft2h^?$S)huLm>yd+2HVfaf+ zA@Inz_Ltt}hfw%Rv_9T-Czd9+tWT9;bWe{4(!Nx-Pj=oJkzJaO1p)7^Hb&sVeS|-- zv)>cdrc8tR3h)IRAXQ@l_~i_U+Lo*KjaJeEDYk|@gFowYmF_75#I|(e(SDFg$cm0q z&@PHH4wP8Hobw1>Q#RH^8nLXiEalq^P89zfwBx@wW$Vdn%gbR8RJ$!e7jx#k4Bai& zf=s=HBNuS=-oTYO32y&1t-5*pY$8-N;mV zNAUX+>!WXvfyuhvM0f5*lA)~Z(Pr6funULMNBGD)u?*&I-ZNkl2z_#zNCpkLM#2+& z89w)i^8W}0ICS%41LdU%VSEHA94N82gQ04k)Pr=$exZH?rL=uBh%QBeHXzap@?7j| z%g-EPp_Zx99WrW0=G_<_29Ke@14fuG{^Zlb+koVf3ZU<1zDLhD+(Z85L&VX-1YdET zcn$B_7C9_o;<%jNLs!VLzdVn*xaRs`DD-QqITt?)4M7YpfhkaH%J9EnngWsoPNxxP zqCwYO&^uaN_`rsez(UA#%BijhSU7A;&K#LMo|?%__aGcF!Zy7i^DI~2#t~bQ`(QL7 zhIIhlYagHje)fUJ04>1jdnw@A_&D0LhWC}Z{>hs^);yB~Xq0lt2!A4aVUJF75@_So z?{}5B_-J2V$w@p%Ct2s{#{HlFq&>?7o(-ctn@Zza-mw8Rza4FxqqJ=nZg06>qiw_b zlD5tMedUo;w3X;-tVnS)!tXcJNGS`ntl3TS)0$YpvJ9Bx0s|2U@WUyH7=pPlFdbm7 zhI6Rp&oU9T%ezTF!^IL(9D5Fa-x{8CMFqbT}2rg-s6v8nF7^*Ua|s>e5_%#v(|9B zZ*}FxzL#cC+Bpu`rmgVzcUV}3SZDNNw1rU#3ECZ(6HoEG1r6Cxn|sR9?qxZQ0Z0swt0ibx}+N!^!Y!TuQC;yPR>0!Zu%!ABM z=T0PL5P&hCMN{omqp-=)#nHx?D(1FY3Xh}A*1Z+8@q!tTH9EkjdLS(~u(kY(-1(Y=S$2*mP`djLIz z-A@(Gla@My#L;#Y0BKXY_x6@KYSoY_*J^_aVhn)fU*}3<=;&NaNptOsg>WOKoN3PU zNqSB`OYA$B>NSXc-vH4$q=0yWXW|y2(}$u1L)bjg18@Hn zxIer6FS_159_shqA0JY*QWR38MV7M7G>W1WB9tYvuQO-{i6Oh*Ey`N4(|p^+YI6^YW;A2$Yi)S+jBkjrKH&Vgc2+u zxVUxAP+kvP+s)B-gg>PQi|l@`4#+gHkAX3*fsF%>AP3x!5PNUQP_1kCB9uuV&`yE9dQpXPoT8$=!Dh z#A`upvPp(UlKLjyA@AWd?J=wTTR(gE>r~=79A5d28f=qc040Qjd>bF&OYocBs zP$h*MLb>xl%@*6oxn2FUEvmtT9}fpfL)uBpA_%8k6;$KF&=EN-MVSneMB3iAEzyaLXJ?ST1aUva=GNG6Z7Hmzl37d$sH z05HCgE{R}z`72kNQAnMRbbRNB9p=<-1>!SM&a7d>x64MD8irh(U0L@UkSTt2YJWKF z;{j-Hk4w$hQ^_WAr=7^joknBTOCB=ugqmw7h|6c5y9szSqSP@-{JS%v2It;vMQ|>S z4BQ0T*#+n**sf7(S4!(OIe$GZiHZl!y(~yE=fVf6rIA~eR?dopUd@hU}c{qtXUexQ9H>23FO-l92MII{;MTs-nU ztLdhgjF>2WT$Vg&O{E<5*W@|JU4X;`Wt zj0-R+0k2H^=ZRP0mMSaFv&(fSU!mxr$cKuiqd`|!FBO&Dq#u6QMlZsdDId=vO z5fZF^%()(_uY)rs*IzEuUac)>9Ib60w;BWN32W+aTU`La1CZkx-?BygR zXyWfF6ptC%O+(Q$ycO(*+P|Q5QjieKSHc4{xPEnf>G|T2odvWiq&^cYKM!Dy{AVtS zj!>&YuU$fBh>NRTI|lkX+wXQ&Q0D5Oj?oGuy7DET{HzB#qc`vqBn%U`RQA2);koJBVpbCtetWV>!VUISxnCu8^i;8Xb zu{%j8%4J-#AjrAZ#nc*|4y`pp!fmYo(eZqrm+W7(9##-LYSEt!hT@!QyS)R*LT7~z zRQca_^<$6esfPUlhG`MSr;gIRie-vb&<_B-oMhjtv} zNKFfQDJ3S!srT(h#{0m+{;I!KM4A$ne}o46X6-yohT72sRS6diK=zbx+%6Kd7*(td ziJZT(q|_In3hM^MYhX}cZ_u^{43XsU_b;`snahK>~nJ_9b!&&?MmDytfp|lJ+Azo<_)^BvQI+Mbb`{#N-oApuXz;)lsEdm>i~!20fmoSTnI`|WwYez01zf8CfUb{E){9?>eYu`|sbhi<}&@EMqD;xR!j@0^X>; z#PI~uplbg<7zd=kEL`toe3ZIRt@iAw%;!|Y;P@KlPZDX$`)rz_z7uZwWng#1 z1vcy3klhNpKq1l^N1Ygzf4uY0ZG-Q;UV+~~98+tVd%bO6jV0?G%8GezY4p`_K3)wC z6hNf#H2aQKt_iJn&0&@EUiL$EXr5Y$g>??<rc1$U83t8a>vLhz1(_i3n@Iw!O~zFDpV$Q~ff0J1B50myE1Z>l4` z7cEvXehi)@;*e=rMZ=aCixJtnl~~8sLTPYuM1G2{y)`bJkP?YL7lEH;tbupP9FExj)U_RB5jf9Egxv#Lhtob2tR@T};VLOdf ztz)J2VOg%De^{`tHwv#WNlpIo%O7Axo>Gv^jE~##yr^2+L9)wCT4^p{*6bh|-^3eJ z7tz2j;H<8JeZB?lbInnIdw5rJ!eDMf5m6B3)>vq$C0NNUqPSbZx?`a=dvhDu)dgpC z9W`?Qg31ldRzut;ZCNgk8ex`9{mJwqnz)odxoVECTq337n@;ugGDYf;h{VXhq^# zKObHkxc6vvHZp|%!;euXwxALd@&h><2#NhcXzw1NfFW8otoPKg)76qlH`JOC9t~Us ztd!R8p%oQ~vmn#Xb=e`6{zO!l{0f?wWs2xeB!gXEsRB5XH353)YMu}Ern|!S4d2Vd z1g40j2(@HJL`9&T%eo8NX5&xgl)Y-_D=2Bo^U${#4wT*zU&6ci|pOM(r^WR z+WY5iN|mJ8=q=Jeqr9K1WMJ9{A(<1X@*lMFd#wS_@Zg*N?X!=a?PC+`d%68(v~$Jb zL*IX_?3J*0Bj7tTw_WO;SpovViLjaoESz{aT5u%di3#H?`Y(hCn>J+hf_^ zD*E)=37F@8)@On&;q6;0qGo;6+TWpR<>2#3=>M-5mlxxUmJ(ifeo{FT8>N)Io!`H% zkJusm4H~$0=RavG;<5Tq`XI$CJHz@vYaiEh=PXtnI1Bz&6A9YT;>q6Z6W~bIP)NuZ z0t?OoDXJj0DLEo-U~%wiPjp&!9)L>|F8}46g?&7G%=^!&VawW^7Qm+ZdgHVGD>j$1 zT}6)f_(QygRSUjsHE$pg12!BJyM1pRG|fH?7~{7m3$iM;z{uNt?QFb|LL(%w2~gn* zNToMvFqaB0?YIfySk7A9)O3smtiWPHddX9vNrc0yEjonn?+er`Tu}vDiIP zXVv{!`^CokVI2IhKCm_xE0;~SpZb-!(eIkNU}?G z3P4XeSidTM1Y01@q9V4dKfwcI>DM_SJLJ?s@WUO}iQ7TJJ0o&+tboM*yBzx3TPJvc z!iI=!VL`j)nILFp#p|%lLr<>$t9E2fpab{U4hd7YyVs799uVsj#5AQ$**kl3kB~Q-oT+qG7o&AooYszy1Q(>6Llj zNY|+)#+uMJTA&H`kqAky^~Sq|B?rYx^o$6lA`qe{mNjCx=7-=Vm}lv)@3h=IiVL+~ z8S*k;DpfY~g7Xi-%Ax2Dru{+gfD^VHi(DAkn-Zw!H&e;8rL?t{x8LauEb}O9D8mU8 zRXUEyDG(itR(yqh(q9f0Jjd%u_{akr=?bBN(p zB(vtZZmR%rfM(yoE~~BT=Vbx_;052RxnWK0`MU24@e1>$P9J}l|E))3l|qwR|7y7I zGd_b@`qV-DO`oM(DL)GR-TECEo5xj__FlKx&Ehl$?wp29&pFAK7rz|H084&y;V@he zkO+@EEdunv`WW=8oviWPr8J|xC7bpu5i6HFMb?(en-W)~w|{K$3{(!E-D2J`X1AV) zrk22mg$u>#kWCDeCv;JzLW7&#cZPeWteA`S%XJkGDaZomFA`ggA?slDyV{+eLUv-8 z>pN2VyQ{iq?_-TyTsG$d}= zWdv(*N%L6xEikwu(%hoE)gK`)#=;}9#D(VSrIxMFqW#BfezO#S#fKHPHRtQ+lEBVP z6FTns`7dy9SK5Hz?OCqMwS;ZC2mb;pRixs562v6`U*E!KLP=ZdgIl%&#k*tyk25$S zD^sV4`%fh&fu;q37^oRet*zCItd{SnmSN@PARpM1jQ_Ls+{^#L&!+C~s*+{Q*#vg> zX-(FYj2C<9BNJ;0w$T|XJ`E%UT`OG#L8 znjJ}z$(4AchuEzSzM& zs(N-pRf);loK;PU7Idwz%)p~Haxix_U))LFXP}ta+T%?TU2Dpr+M0qJ#DCEBVcdBA zWuBvZEn~%XYk6CFoO7paDWpPzDraM5wzg0PCLdhr$(TTZGlxooCZnBt0ss~aN2&{@ z6$XrL4kdF^=04wD5k=3;h0RR%yb?ZAV-IB(YEGxxB9EM9_#^VdKx=3rJApCl!Z%?2 zJCI)Qm=;S191y_O0vJAz>K@Um4JY5aKG0QB?jn%(U`FD^fUno6$|l)HMLuXZ6h6V; z{F#hb?rFA3Rg%FuBbyHEgw;EeVnGAh<@nvA3$G&>epfB$Jd5@Mj>Jm$RPrEIj(D(} z2nh@9cvjU$HV~rz>aT$e{moAm{(4RshRNU3dDLRhN16dqRz;78sI=NWvRrQz@r?g!3co~WuqnCEDMM)otu|d0NmKY)aKI>M@sMeb0dk`f?f_L$-eI*~`+Qk#Ft)?=OaUM|FCskz*>V&>#LB*pZ{$!4D2`~e5qT*f z6u!Hlul+1&R^C=)(D!ZgV21H4Td@Yk7cogVZ}6u8K$OzQ7iAZjcuucopPwzL^3U&} zKN|Vm3Y$Lg7ez~OoSCs7@RjNPMD=QZ?xM5)pl%nuOOjA(OT-sR_Z2LUlo?nFw}e<= z(tNbQr2^3ATT6{u`Mo!g#>`0bHsh-P%yv$>HjVjp1LjdOl2#UqHv3WQsxM6*P%;&K zT=@i2%)f1kJx@#ifF{}^8JCTr@Vx5I*v5jBu^FUCacJ_Kdwh97RZ@;pr1#iUa-lrC zFDI0*fEm;8J5JxncqDCtqf@>88M*%6`S!PB=|NZeQ9|`yh2gd3x((^(-QY7Q zCf34-i2+oJbh&nn!CLfQ^eFxUwl0wwmQ|eSoRgy&4SpJpevokxg z>Uw3KN;Rh*QSIJ;w}8%1M;YCSulDI``(9i0-W?E`E$@ST0c`{YuM<(%Wr@98NT+T) z^3w_$(>^SOB59@~lm%Q=Mq7DtNy!SmI5_ap*~F#hJg{VQ5koALo+z(=2MUlE|4$TTrR=kR3dQTT= z-WST&p(r+hPgXh?%Zz4+eG{okR+TyhY-$?l_Wf!kGufUP;^1LuWTV+AwT;IuZD2+| zH~0g57!wrG)-fJMrS$S!+;@)JBl-_Uq;IWYz@!ZTY|Xw+6Ybzl5SXIbun}qVE`=FK zl9|FkDXX|0kH(qHxuL}TJjMK$Ll{dSB@z0$p~7pxgke7LDWO;f9e(3JDkAVt?fV;w7ccW_tK#?BI zde281HmIOCD#Ahga@v36mS>=JQ{>=svL~`n)p$Miu!RzB-g~91-}ghrKh8PX->b$g z#LP!6jC`3*n<}C~5)dQ<^9_w~@Gx(4yX0!MZ%9`XZj=7x&l=;=?~tBzBYKmV`^1lh|U_mjND&i(7F9pY!-vrZhMZsk%7Mdk83iZUMvx)f$ z%wIVVbRAC}R!L^_JqR6<%pHByUE(jau7P_w03*`qGkH*VRO`zCw7Q5}1dx#X;Gf^$ z;kaO>btaasC>#3|FD0GXUnPz^fxELw=ijyR2A0Xvh|`yvvaBWxWp=5pry6R#0Q}O? zpazI;R9vQ&<=;l(mTRhx)0fOBY%`ul=YW=CkPpJ0D9i>#C_DVLfhTEt2^@}4ZGppg z<(*?l zJ-Q9JF<{eb4pqy~{H*W*gOw=jUq6Wwt1@&RU_OW(hgKtlKC=BLi^y2Ex?DR$lc-#SME&^FmiZKw(-p2|U-rTkzg^_lLGaW?Jc`Of;1$B;v z6n)C^T&t5iFIe+56;w;W-dOd|gw~0+EysBU*ZPV+jnO~MN!a;%y?rG2TIoJOb`Jt3 zi*7Vh&lE_ZF1(2o?gN0%8E3TV#8rS!ESHPNv#~%!SZfyD&(Dn7Sj?1JFgN0(&4;7K z_AC_V+(N}P!G5#6jm?J?cx3)+B2*|auvhfyD}9NKF#2POms#{mqTw!ZUjeXAF>i@( z4?X4+&g=IqdF>o1(nU&@2`(HwA@=SILXN%U7^DNPsu1>Og_2OsKJlQJi+-3E*6gJo z^!uz%u+2)JL_FI#3r}0SgCKG!x7Y!m@mll%y156?{8cs)1s8sWX-OR?eD3;QUylA; zs03@}@9Q-MH*h6-BjXuB1QU48}sw0(qL6$7Uw3yziii$F@J-J>9~|l5Y2tSS+7bO>FuL6; zxQd`-rZy{(`SpniAc0QX*C&iRBkj4^sb~5^)xeL<;7mmJ>c{s#_Ef} z$p>8%pj)j$m)!QcU~{C*Q0(a)4Pj5G0opxnY6~}FA-A6O8Cq)Q(g%bbZU$SS%%CD_yFoY+E*q}y!5Sg;6 zkq`FWlWxL4?35;qxGyMi5_ZcxdncWX!0Z>jf!fA8QF787TNL1;O52aGK!Oc{oiR-j z7XFj(`&3M$EPS-XmhmLoDu#+ zh9zk8(SYMZv6%wPP_jHw3|fk-(zFFfD&tl6FhX~qEJ&gmS|U2wRDfT^4=uGPcL@4( z>n+}4#qZBX>hGU-7Hg)61$yYf=ITDAzal-zjA=LBxz@Wfi0rQ0k#J-K67;Ki(h3X1 zRQTZX08!sQ^{U3>13JoQ2EjcD^UdC8o~_jGWmLxKbG>fqm(e$oW4l4rFPT_koA!aU zV)tDzF{8*4dEDZ`l+vM#6GPL$-Sd7+^`cQ%W(asJ0}*X1LBjFuo6!UA9<=Z^qds_M z8;#!~o;iDSv~Mhdse5r@W~Ige2Fuv%3Gcvd+#;afvPrOARt^1|@)D%jn zT+pm1tS78yfMxQ#8>!!v9CQbInxrzKm~$wQeCU>8^J~96S`)QG80XcS#Zg)r}! zWFdVM49883<5roAdYc8JX2_`9S4w~m{cwI%Vz#EXBDQhUk{o^ZLYkjCGCR;Rd3ut-lV z-Er46D~aNHJ_PMQo_B}t_VW`Rt3H^W;5~S}{f65>Ay3s^M6~|Rwq8523_2Cf@i#A6 zb0Yg{UcVE)@>32RulI;iKJZ;S;V{TrRc*a4uV3NDjko-?32S}LRH0G`G*EeZh-LD%NjXIJnzy3f=Pu~SzShXof1CRs$eJfegy zVb?Oa;R2kxV`}7U6K-36cn_-X&y%kSY~~!GCjE0em!dDy^bLi9worPs9-f z_vT(n)bofkIoKTHqK;Ywls*j?Q#fkgL7^c`(Y#&YE6&5wkA48%f9k6Fx;8vtQ^C%w zOsaqwY;{wlr0sEP4dg^N7Xu6ZND`a9} zi|<#MBC1?6eqALS;b8Xu;i=_By~YZc%tAv`C%fFr?PZ(GdqKG%YfgKP?`rO)9B9;K~4*sH6J8W(}z9a5ebU(ADggUnjM%({OrjaUJAp#Z<*@u zZv;Q`v!+59=-dEyET+A0`NsxWq+SQ@;?H(^83P2y70w$#cB!wxW*+Vj ztI1`7*{*NFspvaO6@U6l3z*p{;g)X2`(@>hVn_xWn}YBkdaJ~_DGn0@q;Vj+eW88K zr_Tam24_zPW=n~HL&HZ;U5(KvfFltP0b?J>?jXuj*_yX`I7K|Nf#vk%v@6%5zbC#t z`4$&THO<10famF88?B9@=VANe<;yP8&ZV#qb$P-)ljJLv3E6aIIH|C$OXXqLsxW)a z1C}}n6(@v8Nk0RTGme44c)@cob>@IQDO86a>7y3XMWG2nRnVYzYzBONUE~iQXR@9? zF&nwGPJ8Nln3m3`fAdW2Bp+?pwk%8x>Tn9W5|Y{@o(;gOtBDJw=jwy^QCJb=`5R-G z;i6>?qf$V8)vdP2`-gqGKr0XH-Cs_O1#;Om>bbqaH)REt=t`ww&KA;NqN_d-dPgalPq0vL&y{WPzn9kjA~xMw8Ww?Q$~mWgMXAZ?D}w=N*U zQS*wm`Rdm=uXQ=0XC|0Qb0Vn;L>a#iNqUrW!G$EWsYKf6Mx<8>Tf#1APnQuP=*^yG zAS#{t8s_LE_kkmvgb{wr!Q z`}6`&ju*(aGENtf;ZraHmf?Ow%op*ef~5ZV*OHtI(t{K!bgpkTNQ85VhUlP~>hM@@ znz1@D0TLHT5tJs7kctTT8ZbM)E+L)GyQ}l zG+-gvU5rz(lS5buLV{es%J;Vt5%$1jN6NP7(^fr5LcGI-LeY?6IdygZSJ*)yNwZMe z6}B@3ohHhCC5D^k<#}Ba6Ap9QPqXTxi7Va(;`tV6t1B#9ktSBui*?*096V8VxT&{4 z?4fg=Hu&5xr3R(|Y*#SkYbc5noNom_5q!oYcEB&ok>bMfqOX-Ut~StEf&_&l+Fu`| zYouA<6e!)nJ=z~ex=fdj$Egy;n~6tii$l=*dn8N`!zGH)$WtFN(Bjq{<)hIB>mhBi zXR$X4-7iG#Rf7YfAPdHQerm{E7?9kr7N7442OVS`v#`e!YJk1Hd#z$t8*ZFlxNOrg z>%(IGVHRp|Qf%8biKC1s zux?S-i@OsrD(3%}=1-5(*94YNkd0j%>bV3N+i81X(rWh(0(m5f#1CHTz&b0W#QX2~KKNpWpxx>0y z8RVLacuZ9viZYcxQPiOAbr9GSx0zPIv`ek~67I8*{=*^YQk(r~K2YZ_1eLYr>JNwZQC)j$rT4bawS~#g|0%H zcmdw1(UI5fji4+E649S~#0eI9g9|DT*H(i=TQqR$GKxobzVTWhdh*a$pD;>+u<<)LVddXR@J z9HT*Q#?}L$N?)sk#-+`w)VO;4x#IS9izN%`q5;LOS!=BEH&Yy3?{r~ql*tr=OmFoA zp%X_YFZAKXr>Q+3CDKAWsPl7S0nQ3`T@oErY51s_gC}~&r$nkSe%x$tZnF&9%Qv^) zHsyHrE78DV!yLFUHH+pWwS_?|egT|aK2}oO5_o2+gHHtejz>+7+bK8Qh6Br1f=tJa zj{ZGP=~1B02Dqr43pZivP?-%(R>IKQkKXHq2Fdorfki6`%x3%g+6NPYE+@Ieb(J(wSVj>mQpmg1U#UlKr&o+GykSea#H)h3FY(2j_aPcjWDz!wo=(eu>V8Mgiq#kGSV;OaRVMgs z`YCxR-N}Du-BoDJ4%YoQvq?GV0Vfnaljn_p^3ERc4i29PGw$cqvDUU8@UA|#tVI`u zjNsD)I&$n^a)sgtB?vEd$^_N2Y%^o%y)m^mGKXv4u(s@Udxjuy%lJFnp@NrMYLcNt z;QA_1if<0OZ=6V-@1FE<0rfBqAET!iOqcnxgZ3Z zyTTUa%*tYF{*HS7vT;Tg4^72`=P0kFT}->amgpD1nb*84ml3mdp8Fu}qpDHg<2M=M|Ko5YoT~M&%BT@l~BMGA5Fr*w1bPv`WmG_rB9{G}F7zY6C|Db|JCWQTg9bMxS z!-F&OQzVP>2J8&iD^KyhZko6mC!r0TP!^(`a7XOi$ZP7;w{q8#8`Jew`LfSJc%aJW z5B~bf_P4L}75geXurw~7U-WeH{{FDKRzK9@OHPKsumS5nBXDkA->HFXg8XRUa?q1@ zs;GZJD}q<^Upx??ddCOYr5!jR!>72%FOD0H1)3rWP077asrs*=3mObd_a~S`upr1i zz2i8T*Meub^PC=8E1`{>J0c|IJUqf(`tr#x?E=8eV zc*n^vy($=2VC2KmEF+(0A{(^Dl5*HS->jZwl-6r9^S>fI+kGz_!yUjyi=bLeRr5@2 zyOtAa0?i=?F3$z?efaH^$l~81FB3RZtRN5bAOM4{0%y@$k@ZsRI|$5_uk3K<@hC~e zj49p>j;LOhQo)+8f;zPsrieGPL(9p3zO8&c;3VubE6`7HymrPDkE385tGq}pGpQz3 zO<0|aO$PWzK{bU_f2)>Yn*#kGx1L1$jQL}-@a`6EIsMJ!A)e}Oms+(cwI*`xHw67@ zzJqe97j|@uL3UX{piD6MMsU5u%r6nO%Tt8b@%bqHpQMQ}eXbr5l$#13k4c+T<_ciZAzrl7x)|2tfIV{h_FZhG3#TAQ%76I z%|vOqB*)Qm07%DZN%l;l?K!w)hJzwZz;<5KUJ1w6R%kNS`U`<1l| z&)+dVg_(KXwDkFfi;slmGix?-Cq!}$5K{UkzN~<6<0>3FU7{>g&IcT{L>aL_>@mV% z^3IbR0J;)Y`jvCh$<%6ntFInJCQn&~cR@))oBw4(?foxL@zK5j|Cw~~gf~qaW3We~ z`Vnl3r3PNyK?uFE6WRC{+EP9G*H9ijF%U{OR`me>;R;^!8&=)g_V4Z{6w}QZBu0dN zzb_RD>VOLXe)a-63PpZ{ydXe>+yiwd*CVv&i0$iKZ%++lF@J)cjj85tF(z^e`SAa3 z>k|G-;`fa%-BPL#ruZmZ`vKHcIpn)0(73_?qZtSdJ6+Bb!cma*)S7T!(IsCvb@i!P zbTc#p0so;-O7#T+FmO#0I8BC%%b!yCRL8|1m?$UYSJHBDgr2hZJ!>xYwpl>+oCZ*o zz*&}ZDC##w2!%4oENQ``OZJMPS!T?guCa?EX2~TTa??_!XRPe0vD+0os> z5+s1rlk-3YwuPNah-JO)RxpN}#t(&ppyNkRE?OT0?X@zbkz|F-^Bj?DpTd~`9Sx4@ z6o;Z!3nSsYsvd{`V<}DraQwRfop86`SY=}Vl6OBIojZ==Bh7^Ys53_Y_qTH?w4317 ztn%d`y>Z_y4Vh+@&8U_fGTm$pd7Q1i*DkJ$3S4hhE{P2NM8y6u)~O1k%}$|@Jr`Cw zeb=$KNPeuWSq?|R6Y4=g+}U}BaXn^oxa9eHgtK8 zBT}6|C6Hz^m9s$iI(pRKA*jmnZu3(fAX%Z&nHlX`!C^2qbym7S_M|^Idb;v@yDy^) z3pZ1e9t0N+lmQ9k1@MTXB2dsIcA*5X@a_!Ph|kC3)i)y_1HV0WnH86I6kd z{(-=Zbk-g}L98)F`oR@JF)jZK6qEusLY-A4D8{Zvt||f*`8QpiUpLZ0QM?DSj(_vaavMwkNGJ5m?K>pkNZDkLU2ht6zwdlf>?h+Wwd(v zeG?Yk&19hfh&BB{?iToYTs#@TOZXcXeeAn=#)7VB??Y?o-9+0ixYT<>z`9q&oJ7!- zzpnn*SpaZY^p`E?t_N4bjt7HSY)SzD6xe^`LJ$yTfD}~CIBVn8oqzUV`!^z)4ptz! zhLj-`6_~h1ndJB6TtHNRuoue!UZR1P6B^E*_BT>OdGXQeb)EVZY1sb9AmAJe4R6!_ zw#>!jytu?Yc%eQEw#;$aZr+=DUtrw0N<(BhJ6&u(aLgYcpVrX@B!P3OEj5_cRVgXqZBPzUOzf^>MDTR+scEBZ~lA0el87oUVMk8Kt4ci%&GXp zBj8BX!`F;%x?7wjFmnM48>=Uo5!+4xX8YjZ*r3oW1a0exBQIDnDaD>N;IK>C`ChgI zzNQBbzDHE?`a?pP?#3@p)Y%eje~A)!GY zYJLZe4{X;*me%!zTWN1~Uqn=2*?VIZ2hP1`tqI7PRxNK9hcCzl^|R{0#JUVY?WI}& zAtg}XdlAy=*M~vm(Z|!Gs0P9DY2%6Lzd>12&VJ}7R-gcQ?SB$!(#qR}mfi+!R{^%6 zN=N@Q02H_LO~J7xo)M?)^K8 zxqWP4dmyO}lIy;hkl%NtCnxfN=HZ>qp4?lGcU%0{?ExH`e^)^Yi60T787c!j(AgGf zZyU&04ALy-3`N_%!I49)lt2p&X-Rs}iP8hbOJ)F27iOykmhA;=#Gl(Wa3A_$WIRCV zX92#b9>i5FmIRO?Bk*V4=Yoqph& zbay~;3RrjA3tuCQr}8dujm%0}ZT_l06oUr$(m(mi0d8Y#H@#1a*=pRLbMW=P3RC5p zY=ahg23(~(m_Vaown~jxsz)Ay7$yl^cf!RpRylc;Ad|iVia3BCQ@G@`28OyDuw2CY zXWm3TmVc~o>U2KVNWXu*qgpKEkiIf=Wqa{J7x%Oe6qW={d|dY=IA7Ve+q;RosRhMW z3;;Xkc6=)_QLvjzcS^f`*>B80aI5-Ji`B+(@sFaTaSl&PY(Xo#x*}H*lGV@c87&#O zUcNP2J;Trh)o`pbwREAIZPXi&oijOpwX@fx?`TUmzOiI3W@2{Y!OTA< z`EAs3gO-YJVcw|trjQe2zkfAmEhu;lDh$@d4bCN3F9)uytp3~jl5kWhhls!U&?F$H zr|d1WirfPV4~a*ZaviESCDf9s;3l%E)L1#0h$8m{K5$PVcbsxhX@+Y?!Lku@Y<6p0 zX6Cmzt8O@)`%oL>a5uR^xV?|Mn;($SY-oR;NNqG-h1@N_g9J)Oz&_ zju7&h`u8&medhMwE(RqCJgFBj)y_tnS++;27n(~q43v+wUz$ohUNgvL&Cfb+667Y3 zzL-!V?i?w(!2YFFw=PG3t-aBSIURZ77r39DiRDHW?tTJ*0GS(Se--K9Q`>McE~QeM zPy?n8GbEL*+q>qA*}ISbJ&_mQeG2QR%s&2M`>&VaMaQAYv+X^-$@R^T(H3JmQW-Hm zQ~q8#PFCg}`Q6#CVt)iKHY<)L7BvHAa8O%9yhQBZI29Q9An;bu1WVm_I79-Kql{O~ zx;|VE;7wp4!X2<);NG$c54fcFQg~NV$=|jh4_XF|M%}NQJU+rV`(Wkr~H30wF^Czk6;#ACx@YXNMbq9~BAo=gY zW!+c2-DVcmM9ajaDNjtcteP+Nt<_#SJ8x=I&D0 zo4$k5kOzp_$WFwR>WUJMdGFx2#0Bjw81fBNiDbQJpDd__@(?&yGB-qP^{nxsAOT)p?t%wG0$?ENtOS4vVu(l*?JlO3C!Ty zkh}hST(Cy5LQD|`RY#F4NB3%2o+Q(IskrU=huPY95IZ2jv3@@@I~VpO+#XcpDL=Z+ zkO=_tTfNH6dd5RqR%^SIks~lV-UTo<@0=wZXkD2^ADbq_%KsJQaK+r=P=Fo+(?ABqgJfy4?XT_Xx_NPe zO{AUVDX=6i<*t{9Yfj<2Uo{ebZC=Yh^z;lbq#Sq0AlF?tC9-1-C)?e(3)kyp0=6cK ztJWi$+d7&__^Nx@vzR(W-}fI0+i^uqZ+f%hefFA>KdxYzYs5>^+nSo5CpErbo-JH_ z0Im=p!k>`F%K|1F?Kg#brc!{lOk;KpqcOWR)o;8y%G+|piRvQn-{$Fj-mI(Yo}Y!^ zYv)Sc5OjAFYn>)%KqBpl4yr+UHF~Pbz?c!Se0+Bk*~WZ#YWLIp1F9Jd(M=#Ju60-2 zHfDZYDR2$^E8851uUcH9_3^{Ua>yntba~MGckPn zOFVJte*kq!_tUoRzJbcBuebruV=7(vp3u`~OFFtBXE}-<8r3&{_QT-lb*tb*;?JH} z+%)6YmwmPz>9|`$_rbu$R|@BUtFhNz^X&VYsX5Z?X0K3Sd+BS)$i1jDO=b6b`|<7* zq44sfB%=>K-=`Dd

hza#I>rnVh%yopGt?2f(FWHgHYZtUZ&d?P`Sknda?DF~C@FE%7k#<`#Slh-3^uHhK!Y}IhxWY;U#F><8e2=ov7fHw4j7+FL)UGZ$hkA;^qPi>04027|9 zKv+S+xxA+)ddKVT9jPn*9BC9-y`EeiciXn&zP!?W=>Z(jk$%fWu@6;U%wY}70k5B8{C>aj{4Vo{-&_JOlf6q}wodyD7d$i2kP%TS zd^EZ=P*=P#tI88oK5A99Y}1gu-jW)a({Pn|xM$v?so-QwW5GM&inTbDN%D#jMP@lV z=j(z!sO(@iU~(s8vjVG1iArJ6MyIOf%4GS-X!iB`3oLR z0nVv5`B8wEe7RI|&q1%z;b!ypWSw5Jro*!SpSr>htv(XB|Hl0Lti+2RZ_X8@jx#LI z%}@7AhB7whd;9p~*ZTOCimBXIs34GE%WOqfw`1>ICc~5QkxC1i4$@97XOeo-uS8QE z+C9%YWjIOcm`-S@6qS_^_>4T0y6c(LH(=@CsbOU3FcfT~*muBn-8Qe%YijVAHn+_# zm~2tOv(RtUpKq|4?+enuYnmo`g$73qSH~hw;Q%ala4#hW8kvDzFq&A>(w$kiDM`E)*LTjj<@Ahh`{ZdSB~3H_ zg5aTx5w#b#bRNwrRG0mll1ZsbIBX*9WjbTw?iDc4cyglKeS09*DlO31xC5;n{Ktpv z1VW*%?o(GOv)1*P4|zqy^vS}=i_T}Aq;$>P2=$sr>og6|m18+b%yJ6dPna9{_QT**#k?iRz}H8%^#Y>h-_U{*wFa%;J}#g*S5U%?7atQ z19?vR?h^6Ia&FoHl*()1KbWN|%3OSVogC687_ACUk3lZ}I(g1_rswRF z`|GyJsD2k$Cz}ayF+k{;bhZD(i47kcWLW(Hnq+U-C@=7EI!1jO=0vW1WE5@tOHN7S z_+?})%Ss;onl3*ZiRvmX!?xbwrb((hAQBA(*@7m#e71+fE3o)mBh%N>v*Af(XC;RK{^Pl(vM%62E7Jr)K2!Psm3RIBOs{QxQsN+e@x*+85UZ{1 zU^q1)9Z}lhMDm)%VN*=yb@D9-#i1uwUK0D(C>`coXlhc%@+E3!ScDlxQOqQVQD)Em zIX$n=^TYEWJilDqUa!yRzVGY0-`D%P?t7olb$4i2l9H{#frT{IsVK#rz|HGhbp+jw z9|ee`#g^B{5#I|#WgxlI;oZ1HmQ@W?oXIYyE>Y3DZ3>qT8j zyCG-js8t|As7TtVbozwD6WsCeb6=LnHn^RaeT>iiZFPiuO5AqixY3snI_a*|-h)tt z|y}a;KKX?6!oU3qHtuZG2G^e|b#B|@sLG)EWOkrtn63~MP&B^m0*qwZ=%|hW- z_%#Ap!uB}Ln3g=Mpm3^1YNryJo>8QHZsPmYH1nFIf1q^%t@to7LgnCl6?xd;e8McU z$mzoC)p?6Bh9vss^HyO#!B@U3M3n?e^l{3c$%sa8m>mX&IQ-j}l^fI92)->MYx+0E z?N0tQ3ah97q~6X4Fa`n)nBYx6MPE@KDva!n}jX8E3}9vm&yEA zE6Z5u#T8B)O)*xPq=803;6auqoaQ835m|*7NnJ-W7!hEic=k>WbK+PV6^EaI%c9G1 z*!g>pDX_bt;H5I2e`&1a#B#kYkz!M|Q2&lr)W)>O<^2!vO3y`TrI{fr>#$h>yp0Gp zc8)mI()WDoI(dT08~8P7KX}kv0Fnlt{|L!I-vs~w=3@tBE&UE04cYCh^+L2Wi99C_ zXkUyDT$GiMw5h7L?a~ZChKPu7W=b)PTM8{w7DL zU|+CcE0_!z2AUH0@9kAN*MY%dWi%l53+}*cfBd@*HGR7FIpA9Ik>+}tKQG*K8ss1_ z5vf*yGurDjarpLgNQ-B;MwQF6MaoD?CDk)McWF}OQXaFT#~$L8ubq>qNWrDvzq|d~ z`B!N;>Gv!%;q{vilZ55_j{{UJ%z$nV9{HGS*s$*ax4=p-W5bFeE0kVw2s;27Lc2ES z{tCI9deBP(%B4|Fjzo-g?rAa>O38Z{e3Kl`#*Pno_s$G(t7pq1>nJ9QixSPtcOaVT zJ>fJTNt2JSNMe%qL<-yP(aQR-if~aKvi3UbnP7>-4k1++8aA0%GSLhsvG56oHUt-T zSu1- z>P)=sb5D9~O$=T{w7&(}_;NyAz{pUq>CISTUblB&;cP%)WgcfZrZQI8(d;9)t;a-r zq!t3pr27`tk0d5gKTL)tr34f^8Yy z4#IqlE_na)a2HVW8eY9**+(@J~h^gY3;Vh9ot`7t{Cyl!ZtyVMj7woE} z64z}spbCliL6K9lviOHH-kA3Yi;S{X%x0g_1t|(L3p;Zyp34gd={L-x_Tv(Rub2NY z@6U?aY&ZS&guu6nAk~)rEI~3&;<3w~k|URLL4bwb?QJfu>c{mxs9=Lr5zPQu2^Xqf z&FJqYCDOcmfj zMK>R{i)61!0|;f%!cJ*W7bRKskBGQ#T)ls1z~L%X0HA&TT3Z$3Gag>4*%cJ#F#%z+ z_jCdS^Up2^ORaRX6HQP5{M2zfXa&(l-R1k2D{f`UT~aP8rx_NiTJDf*+`y_jJbhyd zfKcB@)E*mCxBnd)+kteEU9MVC4&)Dc{&=HUgW8sIG7NUDwx0?3z;=!a{d$B)wiaDWileD><#CNqC zkbk6UkDU?Qb#+UXqX_}p3`;;Qr5$^joO>G-qcIKTg0RT#{ao;I mX%+`s`)|Vi|M)Idsz{%_OedeXn*po<^ZF5Yu=+qq+P?uSP)g?j literal 0 HcmV?d00001 diff --git a/Detectors/CADSupport/doc/tutorial/docs/images/excavator_mesh_only.png b/Detectors/CADSupport/doc/tutorial/docs/images/excavator_mesh_only.png new file mode 100644 index 0000000000000000000000000000000000000000..02077135dfe0e3af2c58660db27a015388f888ae GIT binary patch literal 23119 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

%wP!RLH@LCst)&J<6^~P*PaSvt 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 bQaVG}syVVO(-RppWA>O@?Rsc@^uoUYm^Q>f literal 0 HcmV?d00001 diff --git a/Detectors/CADSupport/doc/tutorial/docs/images/excavator_render.png b/Detectors/CADSupport/doc/tutorial/docs/images/excavator_render.png new file mode 100644 index 0000000000000000000000000000000000000000..55e6d4b55da59827761359acea0ff60b163ad7b1 GIT binary patch literal 42209 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 fl&b&#Te~XN)K-}4dq>L(lz_6ccD5=% [!WARNING] +> **pythonOCC is not part of O2sim** +> +> It is a separate aliBuild package, and it is **not** pulled in when you build or load `O2sim`. +> If you have never built it, that is genuinely step one — no amount of loading `O2sim` will +> conjure it up. + +So we build it first. This pulls in OpenCascade itself as a dependency, and takes a while the first +time: + +```bash +cd ~/alisw +aliBuild build pythonOCC --defaults o2 --no-system SWIG +``` + +The `--no-system SWIG` is worth keeping even when aliBuild tells you the system SWIG will do. The +recipe asks for SWIG 4.2.1 and several distributions ship 4.2.0, which is close enough to be picked +up and not close enough to build. Forcing aliBuild to build its own costs a few minutes once and +saves a confusing failure later. + +With that in place, everything happens in a single shell. We load `pythonOCC` together with `O2sim`, +because the converter needs ROOT as well as OpenCascade — and the same environment then runs `o2-sim` +afterwards, so there is no need to switch shells between converting and simulating: + +```bash +alienv enter O2sim/latest,pythonOCC/latest +``` + +Two quick checks confirm the environment is sound. The first proves the CAD bindings import at all; +the second runs the converter's own self-test, which builds its test cases in memory and needs no +input file: + +```bash +python3 -c "import OCC.Core.Bnd; print('OCC import OK')" +o2-cad-to-tgeo --self-test +``` + +```text +OCC import OK +... +20/20 in-field media checks passed +``` + +The two commands you will use throughout are `o2-cad-to-tgeo`, which takes STEP to TGeo, and +`o2-tgeo-to-cad`, which takes TGeo back to STEP. They are also installed under their older names, +`O2_CADtoTGeo.py` and `O2_TGeoToCAD.py`, which work identically. + +> [!NOTE] +> **If the import fails with “No module named 'OCC'”** +> +> Some `pythonOCC` installations carry a modulefile that puts the `OCC` package directory itself on +> `PYTHONPATH`, rather than the `site-packages` directory containing it — so Python looks inside the +> package and never finds it. The cure is to drop the trailing `/OCC` from the +> `prepend-path PYTHONPATH` line in `$PYTHONOCC_ROOT/etc/modulefiles/pythonOCC`. A recipe fix is on +> its way to alidist. + +## Outside the ALICE stack + +A conda environment with `pythonocc-core` also works. There, run the script from the source tree: + +```bash +conda create -n occ -c conda-forge python=3.10 pythonocc-core -y +conda activate occ +python3 $O2_SRC/Detectors/CADSupport/tools/O2_CADtoTGeo.py --help +``` diff --git a/Detectors/CADSupport/doc/tutorial/docs/its-round-trip.md b/Detectors/CADSupport/doc/tutorial/docs/its-round-trip.md new file mode 100644 index 0000000000000..4dc95accaa762 --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/its-round-trip.md @@ -0,0 +1,207 @@ +# The ITS, out and back again + +Everything so far started from a CAD file. This example starts from ALICE itself: we take the ITS as +O2 builds it, export it to STEP, convert it back, and simulate hits in the result. It is the most +realistic thing you can do with the tools, because the answer is known — the same detector, +transported by the same Geant, is sitting right next to it. + +It is also the standard way of testing the converter on a part you do not have a CAD file for. Any +O2 module works the same way. + +The four steps are: + +```mermaid +flowchart LR + A["o2-sim -m ITS
o2sim_geometry.root"] --> B["o2-tgeo-to-cad
ITS.step + media sidecar"] + B --> C["o2-cad-to-tgeo
conv/geom.C"] + C --> D["o2-sim
external detector → hits"] +``` + +## 1 · The source geometry + +`-n 0` builds the geometry, writes it and transports nothing: + +```bash +mkdir -p its_roundtrip && cd its_roundtrip +o2-sim-serial -n 0 -g boxgen -m ITS -o o2sim +``` + +That leaves `o2sim_geometry.root`, which is the input to the export. + +## 2 · TGeo to STEP + +```bash +o2-tgeo-to-cad o2sim_geometry.root ITS.step \ + --top barrel \ + --hollow-volume barrel --hollow-tag ITS \ + --media-json ITS_media.json \ + --report ITS_writer_report.json +``` + +```text +Step File Name : ITS.step(278254 ents) Write Done +261 solids, 84 volumes with daughters, 29 pure assemblies, 1996 components, 2 volumes declined +capacity check: max relative deviation 2.012e-02, median 3.365e-16 +report: ITS_writer_report.json (28.22 s, 16.17 MB) +media: ITS_media.json (33 media over 261 parts) +``` + +Three of those options deserve a word. + +`--top barrel` converts the subtree under `barrel`, which is where `o2-sim` hangs the ITS. Converting +from the world root instead would drag the experiment hall along with it. + +`--hollow-volume barrel` emits `barrel` as a pure assembly: its daughters keep their own transforms, +but the volume itself contributes no body. This matters because `o2-sim` always builds `cave`, +`barrel` and `caveRB24` itself, whatever module list it is given — shipping a second copy would put +two coincident air boxes in the world. `--hollow-tag ITS` then suffixes the hollowed name, so two +modules exported from the same world do not collide when they are placed together. + +`--media-json` is the sidecar that makes this a *round trip* rather than a one-way conversion. It +records every medium as O2 built it, so the back-conversion can rebuild them verbatim instead of +guessing materials from part names. + +The `capacity check` line is the writer's own verification: it compares the volume of each solid it +wrote against the volume ROOT reports for the original shape. A median deviation of 3.4e-16 is machine +precision. + +## 3 · STEP back to TGeo + +```bash +o2-cad-to-tgeo ITS.step -o geom.C --output-folder conv \ + --csg auto --exact-surfaces auto --mesh \ + --media-json ITS_media.json +``` + +This one takes about five minutes — the ITS is 261 solids, several of which are deep boolean +constructions. + +```text +Detected STEP length unit: mm (scale to cm = 0.1) +Placement check: 296716 leaf placement(s), all at distinct world transforms. + tessellation is EXACT (every face a planar polygon) for 142 of 261 part(s) -- 54.4 % + tiers: CSG 252, exact surfaces 9, tessellated 0 (of 261 leaf solids) +Media from sidecar: 261/261 volumes carry their source medium +Wrote ROOT macro: .../conv/geom.C +``` + +Two lines to read carefully. `tiers: CSG 252, exact surfaces 9, tessellated 0` says the whole ITS came +back exactly: 252 parts as ordinary ROOT shapes, nine as exact surface solids, and nothing at all fell +through to the approximate mesh. `Media from sidecar: 261/261` says every volume got its original +medium back rather than a placeholder. + +You can check the media independently: + +```bash +python3 $O2_SRC/Detectors/CADSupport/validation/closure/check_media.py \ + --original o2sim_geometry.root --macro conv/geom.C --rtol 1e-6 \ + --writer-report ITS_writer_report.json +``` + +```text +converted volumes with a medium: 261 + media identical to the source: 261 + left on the Default placeholder (transparent): 0 + disagreeing with the source: 0 +VERDICT: every volume carries its source medium +``` + +> [!NOTE] +> **One shell or two** +> +> The converter and `o2-sim` share one `alienv enter O2sim/latest,pythonOCC/latest` shell. If your +> `pythonOCC` modulefile still has the `PYTHONPATH` defect described in +> [Install the software](install.md), that same path makes `o2-sim` segfault at startup — run the +> converter in a shell of its own until the modulefile is fixed. + +## 4 · Hits from the converted ITS + +Now hook it in. The sensitive volumes are the seven ITS sensor volumes, `ITSUSensor0` … `ITSUSensor6`, +which one substring selects. Because the geometry was converted from `barrel` with `barrel` hollowed, +it goes back into the real `barrel` with no placement at all — every part lands at exactly the +transform the source geometry gave it: + +`externalGeometry.json` + +```json +{ + "externalDetectors": [ + { + "name": "CITS", + "title": "CAD round-tripped ITS", + "macro": "conv/geom.C", + "anchor": "barrel", + "detID": "ITS", + "sensitiveVolumes": ["ITSUSensor"] + } + ] +} +``` + +`detectorlist.json` + +```json +{ "CADITS": ["CITS"] } +``` + +```bash +o2-sim-serial -n 3 -g boxgen --seed 42 \ + --detectorList CADITS:detectorlist.json \ + --extGeomFile externalGeometry.json \ + --configKeyValues 'SimCutParams.trackSeed=true;BoxGun.number=100;BoxGun.pdg=211;BoxGun.eta[0]=-1;BoxGun.eta[1]=1;BoxGun.prange[0]=2.0;BoxGun.prange[1]=5.0' +``` + +```text +External detector CITS: 7 sensitive volume(s) selected +External detector CITS: registered sensitive volume 'ITSUSensor0' (MC volID 13, sensor 0) +External detector CITS: registered sensitive volume 'ITSUSensor1' (MC volID 61, sensor 1) +... +External detector CITS: registered sensitive volume 'ITSUSensor6' (MC volID 264, sensor 6) +CREATING BRANCH CITSHit +External detector CITS EndOfEvent: 1825 sensitive step(s) -> 849 hit(s) +External detector CITS EndOfEvent: 1862 sensitive step(s) -> 887 hit(s) +External detector CITS EndOfEvent: 1754 sensitive step(s) -> 869 hit(s) +``` + +The ITS that came back from CAD is producing hits, on the `ITS` DetID slot, in a branch called +`CITSHit`. No detector class was written and nothing was recompiled. + +## Is it the same detector? + +The cheapest answer is the radius of the hits. Run the native ITS with the same gun and the same seed + +```bash +o2-sim-serial -n 3 -g boxgen --seed 42 -m ITS -o native \ + --configKeyValues 'SimCutParams.trackSeed=true;BoxGun.number=100;BoxGun.pdg=211;BoxGun.eta[0]=-1;BoxGun.eta[1]=1;BoxGun.prange[0]=2.0;BoxGun.prange[1]=5.0' +``` + +and histogram the hit radius on both sides: + +```cpp +sqrt(ITSHit.mPos.fCoordinates.fX**2 + ITSHit.mPos.fCoordinates.fY**2) // native, in native_HitsITS.root +sqrt(CITSHit.mPos.fCoordinates.fX**2 + CITSHit.mPos.fCoordinates.fY**2) // CAD, in o2sim.root +``` + +| r (cm) | 1.9 | 2.6 | 3.4 | 4.1 | 19.1 | 19.9 | 24.4 | 25.1 | 34.1 | 34.9 | 38.6 | 39.4 | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | +| native | 14 | 158 | 171 | 185 | 69 | 201 | 265 | 73 | 273 | 101 | 58 | 299 | +| CAD | 74 | 270 | 346 | 362 | 166 | 207 | 348 | 53 | 197 | 187 | — | 395 | + +Every populated radius is populated on both sides, and no hit appears anywhere else: the three inner +barrel layers and the four outer ones are exactly where the native ITS puts them, to the bin. That is +the geometry check, and it passes. + +The *counts* are not the same, and should not be read as one. The two runs are not on identical +physics: a module loaded through the JSON mechanism has no detector directory and therefore no +`simcuts.dat`, so its production cuts are not the ones the ITS sets for itself, and it makes more +low-energy secondaries. Carrying the cuts across takes a cut dump from the baseline, a probe run to +learn the CAD run's own medium indices, and a remap by medium name — which is exactly what +`validation/closure/` does: + +```bash +$O2_SRC/Detectors/CADSupport/validation/closure/run_closure.sh +``` + +It runs PIPE, ITS, TPC and MAG through the same round trip, remaps the cuts, and then compares hits +and material budget between the two sides properly. Use it when you need a number; use the radius +histogram above when you need to know, in a minute, that your geometry arrived where it should. diff --git a/Detectors/CADSupport/doc/tutorial/docs/limits.md b/Detectors/CADSupport/doc/tutorial/docs/limits.md new file mode 100644 index 0000000000000..8de1c34ffa23b --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/limits.md @@ -0,0 +1,28 @@ +# Limits and pain points + +The honest list. These are the things known to catch people today, roughly in order of how often they +do it. None is a reason not to use the system, but all of them are cheaper to read about here than to +rediscover in a result. + +| What | Why it happens | What to do | +| --- | --- | --- | +| One `geom.C` per hooked thing | The macro exports a single builder hook, and that hook is what the JSON refers to. | Run the converter once per subsystem, into its own folder. They coexist happily in one JSON. | +| Media, cuts and field default to zero | A CAD file carries a material, never a medium, and the emitter uses a three-argument `TGeoMedium` which zeroes every parameter. | Pass `--in-field`. Accept transport defaults for step control, and treat production cuts as unset until you write a real detector. | +| The anchor volume must already exist | Placement is expressed inside the frame of an existing O2 volume. | Use `barrel` unless you have a reason not to, and remember it sits at cave `(0, -30, 0)`. | +| Free-form surfaces stay tessellated | Genuine B-spline *surfaces* are not supported by the exact tier at all. | Check the surface report. Recognition already recovers quadrics written as NURBS, which is the large majority of them. | +| Illegal overlaps in the CAD model | Engineering assemblies are not drawn as legal transport worlds, and parts routinely interpenetrate. | Read `CheckOverlaps`, then fix in CAD or clip the offending region. | +| Degenerate facets at coarse precision | `O2Tessellated` drops triangles that collapse to a line. | Treat it as a mesh-quality signal: lower `--mesh-prec`, or move the part onto an exact tier. | +| A surprisingly huge output directory | Meshing a metre-scale curved part at a fine chord tolerance. | Convert large models without `--mesh`, and never use the default `--mesh-prec` on something metre-sized. | +| `o2-sim` complains about a missing `externalModules` array | Cosmetic. The message is emitted even when your JSON correctly contains only `externalDetectors`. | Ignore it. | + +## One rule that is not a preference + +Run `--csg auto` conversions **strictly serially**. Parallel runs race each other and silently lose +shapes, which produces a geometry that looks complete and is not — the worst possible failure mode, +and the hardest to notice afterwards. + +--- + +Deeper material lives in `Detectors/CADSupport`: `README.md` for the complete option reference, +`doc/reference/` for the exact-surface solid, its file format and the CSG pipeline, and +`doc/known-issues.md` for open defects. diff --git a/Detectors/CADSupport/doc/tutorial/docs/materials.md b/Detectors/CADSupport/doc/tutorial/docs/materials.md new file mode 100644 index 0000000000000..129311a160971 --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/materials.md @@ -0,0 +1,55 @@ +# Give it materials + +So far the geometry has shape but no substance. Without material information every volume is assigned +a dummy medium called `Default`, which is fine while you are checking that things are in the right +place and quite wrong the moment you want physics out of it. + +The normal route is the **bill of materials** that the CAD system can export alongside the geometry. +We hand that to the converter as a CSV and it matches each part's material name against a Geant4 NIST +database. The rows it looks for are mechanical part rows in this shape: + +`detector_bom.csv` + +```csv +Type,...,Part Number,Version,Name,Mass (kg),Material +CAD,Mechanical/Part,Base,AA.01,Base,,Stainless Steel +CAD,Mechanical/Part,BasePin,AA.01,BasePin,,Stainless Steel +``` + +Adding both files to the conversion is all that is required: + +```bash +o2-cad-to-tgeo my.step \ + --output-folder cad_out/mydet -o geom.C \ + --csg auto --exact-surfaces auto --mesh --mesh-prec 0.05 \ + --materials-csv detector_bom.csv \ + --bom-mass-unit kg \ + --g4-nist-json $O2_ROOT/share/CADSupport/tools/g4_nist_database/G4_NIST_DB.json +``` + +```text +Loaded Geant4 NIST DB with 309 materials from: .../G4_NIST_DB.json +Loaded 13 BOM entries from: detector_bom.csv +``` + +Matching uses a combined score of name similarity and density plausibility, which handles the fact +that engineers write “Stainless Steel” where Geant4 says `G4_STAINLESS-STEEL`. A confident match +becomes a real `TGeoMixture` carrying its element composition, radiation length and interaction +length. An ambiguous or missing one falls back to a simple material and leaves a comment in `geom.C` +naming the part — so unresolved materials stay visible and greppable rather than silently wrong. The +scoring thresholds are adjustable (`--mat-min-score`, `--mat-ambiguity-delta` and a few others), but +the defaults are usually right, and it is better to fix an ambiguous name in the BOM than to loosen +the matcher. + +One nice consequence of feeding in the BOM: where both a part mass and a CAD volume are available, +the converter derives an effective density from them. That is how a perforated bracket or a +partly-filled cable tray ends up with an honest average density instead of the density of solid +metal. + +> [!NOTE] +> **If your model came from TGeo in the first place** +> +> Geometry exported out of ALICE with `o2-tgeo-to-cad` and coming back should use `--media-json` +> instead. That rebuilds the original media verbatim, field by field, rather than guessing them from +> names, and takes precedence over the BOM for every part it names. The +> [ITS worked example](its-round-trip.md) does exactly this. diff --git a/Detectors/CADSupport/doc/tutorial/docs/partial.md b/Detectors/CADSupport/doc/tutorial/docs/partial.md new file mode 100644 index 0000000000000..0554528c05a13 --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/partial.md @@ -0,0 +1,39 @@ +# Convert only part of a model + +Real engineering assemblies contain far more than you want to simulate — the mounting frame, the +trolley it sits on, sometimes the building. Converting all of it wastes time and fills your geometry +with volumes no particle will ever reach, so the converter offers two independent ways of cutting a +model down. They combine freely. + +## Selecting by name + +The first is by name. `--include-name` and `--exclude-name` take regular expressions matched against +the part name stored in the CAD file, case-insensitively, and either may be repeated. Matching an +assembly takes its whole subtree along with it, which is usually what you want: + +```bash +--include-name 'Bucket' --exclude-name '^SOLID\b' +``` + +Add `--name-filter-case-sensitive` if you need the matching to respect case. + +## Selecting by region + +The second is geometric. `--clip-box` restricts the conversion to an axis-aligned box, given as +`xmin ymin zmin xmax ymax zmax` in the assembly's global frame. Note that these are **STEP file +units**, before the conversion to centimetres — so if your file is in millimetres, so is your clip +box: + +```bash +--clip-box -50 -50 -20 50 50 20 +``` + +Every solid is then classified against that box before any meshing happens. Solids fully outside are +dropped; solids fully inside are kept unchanged; and solids straddling the boundary are cut against +it with a boolean intersection, so only the part inside survives. Assemblies left with no surviving +children disappear from the output tree altogether. + +By default, subtrees that end up entirely inside the box keep their shared logical definitions, which +keeps the output compact when a part is repeated many times. If you need one distinct volume per +surviving occurrence instead — say because you want to name them individually later — pass +`--clip-deduplicate none`. diff --git a/Detectors/CADSupport/doc/tutorial/docs/passive.md b/Detectors/CADSupport/doc/tutorial/docs/passive.md new file mode 100644 index 0000000000000..ef42b03435787 --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/passive.md @@ -0,0 +1,58 @@ +# Add passive geometry + +With a macro in hand we can put the geometry into ALICE. The mechanism is deliberately data-driven: +two small JSON files, no code and no rebuild. We start with the simpler case — passive material such +as supports, cooling or cabling, which should scatter particles but does not record anything. That +goes into an `externalModules` array: + +`externalGeometry.json` + +```json +{ + "externalModules": [ + { + "name": "EXCV", + "title": "Excavator support structure from CAD", + "macro": "cad_out/excavator/geom.C", + "anchor": "barrel", + "placement": { + "translation": [21.01, -13.22, -19.66], + "rotation_deg": [0.0, 0.0, 0.0] + } + } + ] +} +``` + +| field | meaning | +| --- | --- | +| `name` | a short tag for the module. It must also appear in the module list below, or the module is silently skipped. | +| `macro` | the path to the `geom.C` you produced. | +| `anchor` | a volume that already exists in the ALICE geometry. `barrel` is the usual choice, and it sits at cave coordinates `(0, -30, 0)`. | +| `placement` | translation and rotation **within the anchor's frame**, in centimetres and degrees. | + +The second file is the module list, which is what actually switches the module on. The split exists +so that you can describe several modules in one geometry file and enable them individually: + +`detectorlist.json` + +```json +{ "EXTCAD": ["EXCV"] } +``` + +Then run the simulation, pointing at both: + +```bash +o2-sim-serial -n 1 -g boxgen \ + --detectorList EXTCAD:detectorlist.json \ + --extGeomFile externalGeometry.json +``` + +```text +Configured external module 'EXCV' from macro 'cad_out/excavator/geom.C' anchored to volume 'barrel' +Activating EXCV module +Setting special cuts for passive module EXCV +``` + +Those three lines mean your CAD geometry is in the simulation and particles are being transported +through it. You can list as many modules in the same array as you like. diff --git a/Detectors/CADSupport/doc/tutorial/docs/real-detector.md b/Detectors/CADSupport/doc/tutorial/docs/real-detector.md new file mode 100644 index 0000000000000..767badab898a2 --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/real-detector.md @@ -0,0 +1,35 @@ +# Grow it into a real detector + +> [!WARNING] +> **Not yet exercised end to end** +> +> Everything before this page has been run, with its output pasted from a real terminal. This route +> follows from how `ExternalDetector` and the built-in detectors are written, but no detector has +> yet been built this way. Treat it as a design rather than a recipe, and expect to debug it. + +The external-detector route deliberately trades flexibility for speed: you get one generic hit type +and a borrowed `DetID`, and in exchange you get results the same afternoon. Once a study turns into a +real subdetector you will want your own hit class, your own digitisation and a `DetID` of your own — +and none of that requires giving up the CAD import. The generated geometry simply becomes one step +inside an ordinary O2 detector. + +Three changes to a normal detector implementation are involved: + +1. **Build the geometry from the macro instead of by hand.** Copy `geom.C` into your detector's + simulation directory and call its builder hook from `ConstructGeometry()`, in place of the + `new TGeoTube(...)` code you would otherwise write. Keep the `.bin` payloads beside it and install + them with the detector's data files, since the macro resolves them relative to itself. +2. **Register your own sensitive volumes.** Call `AddSensitiveVolume()` for the volumes the macro + created, using the names the converter derived from the CAD part names. Print them once from + `geom.root` and pin them down in code, because a rename in CAD would otherwise quietly unregister a + sensor. +3. **Write your own hits.** Implement `ProcessHits()` with your own hit class and your own `DetID`, + exactly as any hand-written detector does. Nothing about the geometry's CAD origin constrains this. + +Two things come back the moment you take this step, both of which the external-detector route cannot +offer: `initFieldTrackingParams()` called from your own `createMaterials()`, and +`SetSpecialPhysicsCuts()` reading a real `simcuts.dat` from your detector's data directory. That +closes the gap described under [Field and cuts](field-and-cuts.md). + +The payoff is that re-running the converter after a CAD change regenerates only the geometry. Your +detector code stays untouched, which is the whole point of importing rather than transcribing. diff --git a/Detectors/CADSupport/doc/tutorial/docs/representation.md b/Detectors/CADSupport/doc/tutorial/docs/representation.md new file mode 100644 index 0000000000000..08be02c80ad02 --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/docs/representation.md @@ -0,0 +1,77 @@ +# How a part is represented + +You have just run a conversion where every part came out exact, which is a good outcome but not an +automatic one. It is worth understanding what the converter was choosing between, because on a real +detector those choices decide both how faithful your simulation is and how fast it runs. + +The difficulty is that CAD and TGeo describe solids in different languages. CAD describes a body by +its boundary surfaces — this face is a piece of a cylinder, trimmed by these curves. TGeo describes a +body by combining primitives — a tube minus a box, say. Neither language is a superset of the other, +so there is no single translation that always works. The converter therefore carries three different +answers and picks the best available one **for each leaf solid independently**. + +```mermaid +flowchart TD + A["my.step
CAD assembly"] --> B["o2-cad-to-tgeo
per leaf solid"] + B --> C["1 · CSG primitives
TGeoTube, booleans — exact"] + B --> D["2 · Exact surfaces
O2BVHSurfaceSolid — exact"] + B --> E["3 · Triangle mesh
O2Tessellated — fallback"] + C --> F["geom.C
+ binary payloads"] + D --> F + E --> F +``` + +The three are complementary rather than competing, and all of them end up in the same `geom.C`. +Nothing is ever lost along the way: a part that resists exact description still ships as a mesh, so a +conversion always produces a complete geometry. + +| Tier | What it is | Exact | Covers | Flag | +| --- | --- | --- | --- | --- | +| **CSG** | Native ROOT shapes — `TGeoTube`, `TGeoBBox`, `TGeoCone` and booleans of them | Yes | Mechanical parts that really are primitives. Fastest to navigate and smallest on disk, so it is tried first. | `--csg auto` | +| **Surfaces** | The part's real trimmed boundary faces carried into TGeo as `O2BVHSurfaceSolid`, with a bounding-volume hierarchy for ray queries | Yes | Anything whose faces are planes, cylinders, cones, spheres or tori, however complicatedly trimmed. | `--exact-surfaces auto` | +| **Mesh** | A triangle mesh as `O2Tessellated` | No | Everything else, as the fallback. Genuinely free-form surfaces end up here. | `--mesh` | + +The difference is easiest to see rather than describe. Below, the same model is converted twice: once +to triangles alone at a coarse tolerance, and once with the full cascade, coloured by which tier +carried each part. + +| Tessellated only | The cascade, by tier | +| --- | --- | +| ![The excavator arm converted to triangles only, showing faceted, polygonal silhouettes on the cylindrical rams.](images/excavator_mesh_only.png) | ![The same model with the full cascade: hydraulic rams and pins in green for CSG, machined bodies in blue for exact surfaces.](images/excavator_cascade.png) | + +On the left the cylinders have visibly polygonal silhouettes and flat shading bands — that is the +approximation you are accepting. On the right the rams and pivot pins were recognised as unions of +tubes and the machined bodies carried as their exact trimmed surfaces, so the curves are curves. Both +images are cast through the TGeo navigator with the same camera. + +In practice one asks for all three and lets the converter decide, which is what the `auto` values in +the earlier command did. Each of `--csg` and `--exact-surfaces` accepts three settings, and the third +is more useful than it looks: + +- `off` — never use this tier. This is the default for both, so a bare conversion gives you meshes + only, which is the left-hand picture above. +- `auto` — use it wherever it is accepted, and fall through quietly elsewhere. +- `required` — stop with a report if any part cannot be represented this way. Use it when you want to + *know* your geometry is exact rather than hope so. + +One thing to trust here: a part is only accepted as CSG when OpenCascade's symmetric-difference volume +against the original solid falls inside the model's own tolerance. The recogniser is never allowed to +be approximately right, which is why `dV_sym=0` keeps appearing in the evidence column. + +## Mesh precision, and one way to fill a disk + +When a part does fall through to the mesh tier, `--mesh-prec` sets both the linear deflection (in +model units) and the angular deflection (in radians) of the mesher: lower is finer and slower. For a +desk-scale part `0.05` is a reasonable default. For anything metre-scale you should be careful, +because the cost grows quickly with size — the default `0.1` applied to a two-metre sphere has +produced a **22.9 GB** output directory. The right move for large models is to leave `--mesh` off +entirely and let the two exact tiers carry them. + +> [!WARNING] +> **`--mesh-solid tgeo` does not navigate** +> +> The mesh tier defaults to `--mesh-solid o2`, which emits `o2::base::O2Tessellated` and needs the +> O2 environment to load. The alternative, `--mesh-solid tgeo`, emits ROOT's own `TGeoTessellated`, +> which implements none of `Contains`, `DistFromInside`, `DistFromOutside` or `Safety`. Every such +> volume is then transported as its **filled bounding box**, silently and with no warning. Only +> reach for it when the macro must load outside O2 and will never have a particle sent through it. diff --git a/Detectors/CADSupport/doc/tutorial/hooks/github_alerts.py b/Detectors/CADSupport/doc/tutorial/hooks/github_alerts.py new file mode 100644 index 0000000000000..621f51521739a --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/hooks/github_alerts.py @@ -0,0 +1,51 @@ +# Copyright 2019-2026 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. +# Author: Sandro Wenzel +# Since: 2026-09 + +"""Render GitHub alert blockquotes as Material admonitions. + +The pages are written with GitHub's own `> [!NOTE]` syntax so that they read correctly +when someone simply clicks the file in the repository. MkDocs does not know that syntax, +so this hook rewrites it into `!!! note` before the Markdown is parsed. The optional bold +line directly under the marker becomes the admonition title. +""" + +import re + +KIND = {"NOTE": "note", "TIP": "tip", "IMPORTANT": "info", + "WARNING": "warning", "CAUTION": "danger"} + + +def on_page_markdown(markdown, **kwargs): + lines, out, i = markdown.split("\n"), [], 0 + while i < len(lines): + m = re.match(r"^> \[!(\w+)\]\s*$", lines[i]) + if not m or m.group(1) not in KIND: + out.append(lines[i]) + i += 1 + continue + kind = KIND[m.group(1)] + i += 1 + body = [] + while i < len(lines) and lines[i].startswith(">"): + body.append(lines[i][2:] if lines[i].startswith("> ") else lines[i][1:]) + i += 1 + title = "" + if body and re.match(r"^\*\*.+\*\*$", body[0].strip()): + title = body.pop(0).strip()[2:-2] + while body and not body[0].strip(): + body.pop(0) + out.append(f'!!! {kind} "{title}"' if title else f"!!! {kind}") + out.append("") + out.extend(" " + b if b.strip() else "" for b in body) + out.append("") + return "\n".join(out) diff --git a/Detectors/CADSupport/doc/tutorial/index.html b/Detectors/CADSupport/doc/tutorial/index.html deleted file mode 100644 index d4f938f6ec8f8..0000000000000 --- a/Detectors/CADSupport/doc/tutorial/index.html +++ /dev/null @@ -1,1436 +0,0 @@ - - - - - - -CAD to Simulation - - - - - - -

- - - -
- - -
- Introduction -

Simulating ALICE geometries that come from CAD

- -

- Detectors are designed in CAD, but Geant transports particles through ROOT's TGeo geometry. - This guide is about crossing that gap automatically — taking an engineering model as it comes - out of the design office and turning it into something particles can be simulated through, all - the way to hits you can plot. -

- -

- The usual way of crossing that gap is to read the drawings and write the geometry again by hand, - in C++, volume by volume. That works, and most of ALICE was built this way, but it is slow, it is - easy to get subtly wrong, and every time the engineers move a bracket the translation has to be - redone. For a detector that is still being designed — which is exactly the situation during an - upgrade study — the hand-written geometry is out of date almost as soon as it is written. -

- -

- So instead we convert the CAD file directly. You export the assembly as STEP, run one converter - over it, and you get a ROOT macro that builds the geometry. From there a small JSON file tells - o2-sim to load that macro and place it in the ALICE world. Nothing is recompiled at - any point, so the loop from a new CAD revision to a new simulation takes minutes rather than - weeks. -

- -

- Getting the geometry in is only half of it, though. A shape that particles fly through is a - passive obstacle; to do physics you want it to record something. The second half of this - guide is therefore about the external-detector mechanism, which lets you declare parts of your - imported geometry sensitive and have them write hits — again with no detector class and no - rebuild. That is usually enough to answer the first questions an upgrade study asks: does this - thing get hit, how often, and where. -

- -

What you will be able to do by the end

-
    -
  1. Install the converter and check that it works.
  2. -
  3. Convert a STEP assembly and look at the result.
  4. -
  5. Understand and control how faithfully each part is represented.
  6. -
  7. Attach materials, and know what the magnetic field and physics cuts will and will not do.
  8. -
  9. Place the geometry inside ALICE as passive material.
  10. -
  11. Make parts of it sensitive, run a simulation, and count hits.
  12. -
  13. Know where the system's limits are, so you do not discover them in your results.
  14. -
- -

- We assume you can run o2-sim, and nothing more. No CAD experience is needed, and no - knowledge of OpenCascade, which does the heavy lifting underneath but never has to be addressed - directly. -

-
- - -
- Start -

Install the software

- -

- The converter is a Python script, but it leans on OpenCascade — the CAD kernel that reads STEP - files — through its Python bindings, pythonOCC. That is the one piece you have to - provide yourself. -

- -
- pythonOCC is not part of O2sim -

- It is a separate aliBuild package, and it is not pulled in when you build or - load O2sim. If you have never built it, that is genuinely step one — no amount of - loading O2sim will conjure it up. -

-
- -

- So we build it first. This pulls in OpenCascade itself as a dependency, and takes a while the - first time: -

- -
-
bash
-
cd ~/alisw
-aliBuild build pythonOCC --defaults o2 --no-system SWIG
-
- -

- The --no-system SWIG is worth keeping even when aliBuild tells you the system SWIG - will do. The recipe asks for SWIG 4.2.1 and several distributions ship 4.2.0, which is close - enough to be picked up and not close enough to build. Forcing aliBuild to build its own costs a - few minutes once and saves a confusing failure later. -

- -

- With that in place, everything happens in a single shell. We load pythonOCC together - with O2sim, because the converter needs ROOT as well as OpenCascade — and the same - environment then runs o2-sim afterwards, so there is no need to switch shells - between converting and simulating: -

- -
-
bash
-
alienv enter O2sim/latest,pythonOCC/latest
-
- -

- Two quick checks confirm the environment is sound. The first proves the CAD bindings import at - all; the second runs the converter's own self-test, which builds its test cases in memory and - needs no input file: -

- -
-
bash
-
python3 -c "import OCC.Core.Bnd; print('OCC import OK')"
-o2-cad-to-tgeo --self-test
-
- -
-
output
-
OCC import OK
-...
-20/20 in-field media checks passed
-
- -

- o2-cad-to-tgeo is also installed as O2_CADtoTGeo.py, and - o2-tgeo-to-cad as O2_TGeoToCAD.py, for the older command names. -

- -
- If the import fails with “No module named 'OCC'” -

- Some pythonOCC installations carry a modulefile that puts the OCC - package directory itself on PYTHONPATH, rather than the site-packages - directory containing it — so Python looks inside the package and never finds it. The cure is to - drop the trailing /OCC from the prepend-path PYTHONPATH line in - $PYTHONOCC_ROOT/etc/modulefiles/pythonOCC. A recipe fix is on its way to alidist. -

-
-
- - -
- Start -

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: -

- -
-
the example model
-
$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: -

- -
-
output
-
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: -

- -
-
output
-
=== 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. -
- 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: -

- -
-
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. -

-
- - -
- Converting -

How a part is represented

- -

- You have just run a conversion where every part came out exact, which is a good outcome but not an - automatic one. It is worth understanding what the converter was choosing between, because on a - real detector those choices decide both how faithful your simulation is and how fast it runs. -

- -

- The difficulty is that CAD and TGeo describe solids in different languages. CAD describes a body - by its boundary surfaces — this face is a piece of a cylinder, trimmed by these curves. TGeo - describes a body by combining primitives — a tube minus a box, say. Neither language is a superset - of the other, so there is no single translation that always works. The converter therefore carries - three different answers and picks the best available one for each leaf solid - independently. -

- -
- - - - - - - - - my.step - CAD assembly - - - - O2_CADtoTGeo - per leaf solid - - - - - - - 1  CSG primitives - TGeoTube, booleans — exact - - - 2  Exact surfaces - O2BVHSurfaceSolid — exact - - - 3  Triangle mesh - O2Tessellated — fallback - - - - - - - geom.C - + binary payloads - - -
- -

- The three are complementary rather than competing, and all of them end up in the same - geom.C. Nothing is ever lost along the way: a part that resists exact description - still ships as a mesh, so a conversion always produces a complete geometry. -

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - -
TierWhat it isExactCoversFlag
CSGNative ROOT shapes — TGeoTube, TGeoBBox, - TGeoCone and booleans of themYesMechanical parts that really are primitives. Fastest to navigate and smallest on disk, - so it is tried first.--csg auto
SurfacesThe part's real trimmed boundary faces carried into TGeo as - O2BVHSurfaceSolid, with a bounding-volume hierarchy for ray queriesYesAnything whose faces are planes, cylinders, cones, spheres or tori, however - complicatedly trimmed.--exact-surfaces auto
MeshA triangle mesh as O2TessellatedNoEverything else, as the fallback. Genuinely free-form surfaces end up here.--mesh
-
- -

- The difference is easiest to see rather than describe. Below, the same model is converted twice: - once to triangles alone at a coarse tolerance, and once with the full cascade, coloured by which - tier carried each part. -

- -
-
-
- Tessellated only - The excavator arm converted to triangles only, showing faceted, polygonal silhouettes on the cylindrical rams. -
-
- The cascade, by tier - The same model with the full cascade: hydraulic rams and pins in green for CSG, machined bodies in blue for exact surfaces. -
-
-
    -
  • CSG primitives
  • -
  • Exact surfaces
  • -
  • Triangle mesh
  • -
-
- On the left the cylinders have visibly polygonal silhouettes and flat shading bands — that is - the approximation you are accepting. On the right the rams and pivot pins were recognised as - unions of tubes and the machined bodies carried as their exact trimmed surfaces, so the curves - are curves. Both images are cast through the TGeo navigator with the same camera. -
-
- -

- In practice one asks for all three and lets the converter decide, which is what the - auto values in the earlier command did. Each of --csg and - --exact-surfaces accepts three settings, and the third is more useful than it looks: -

- -
    -
  • off — never use this tier. This is the default for both, so a bare conversion - gives you meshes only, which is the left-hand picture above.
  • -
  • auto — use it wherever it is accepted, and fall through quietly elsewhere.
  • -
  • required — stop with a report if any part cannot be represented this way. Use it - when you want to know your geometry is exact rather than hope so.
  • -
- -

- One thing to trust here: a part is only accepted as CSG when OpenCascade's symmetric-difference - volume against the original solid falls inside the model's own tolerance. The recogniser is never - allowed to be approximately right, which is why dV_sym=0 keeps appearing in the - evidence column. -

- -

Mesh precision, and one way to fill a disk

- -

- When a part does fall through to the mesh tier, --mesh-prec sets both the linear - deflection (in model units) and the angular deflection (in radians) of the mesher: lower is finer - and slower. For a desk-scale part 0.05 is a reasonable - default. For anything metre-scale you should be careful, because the cost grows quickly with size - — the default 0.1 applied to a two-metre sphere has produced a 22.9 GB - output directory. The right move for large models is to leave --mesh off entirely and - let the two exact tiers carry them. -

- -
- --mesh-solid tgeo does not navigate -

- The mesh tier defaults to --mesh-solid o2, which emits - o2::base::O2Tessellated and needs the O2 environment to load. The alternative, - --mesh-solid tgeo, emits ROOT's own TGeoTessellated, which implements - none of Contains, DistFromInside, DistFromOutside or - Safety. Every such volume is then transported as its filled bounding box, - silently and with no warning. Only reach for it when the macro must load outside O2 and will - never have a particle sent through it. -

-
-
- - -
- Converting -

Convert only part of a model

- -

- Real engineering assemblies contain far more than you want to simulate — the mounting frame, the - trolley it sits on, sometimes the building. Converting all of it wastes time and fills your - geometry with volumes no particle will ever reach, so the converter offers two independent ways of - cutting a model down. They combine freely. -

- -

Selecting by name

- -

- The first is by name. --include-name and --exclude-name take regular - expressions matched against the part name stored in the CAD file, case-insensitively, and either - may be repeated. Matching an assembly takes its whole subtree along with it, which is usually what - you want: -

- -
-
bash
-
--include-name 'Bucket' --exclude-name '^SOLID\b'
-
- -

Selecting by region

- -

- The second is geometric. --clip-box restricts the conversion to an axis-aligned box, - given as xmin ymin zmin xmax ymax zmax in the assembly's global frame. Note that these - are STEP file units, before the conversion to centimetres — so if your file is in - millimetres, so is your clip box: -

- -
-
bash
-
--clip-box -50 -50 -20 50 50 20
-
- -

- Every solid is then classified against that box before any meshing happens. Solids fully outside - are dropped; solids fully inside are kept unchanged; and solids straddling the boundary are cut - against it with a boolean intersection, so only the part inside survives. Assemblies left with no - surviving children disappear from the output tree altogether. -

- -

- By default, subtrees that end up entirely inside the box keep their shared logical definitions, - which keeps the output compact when a part is repeated many times. If you need one distinct volume - per surviving occurrence instead — say because you want to name them individually later — pass - --clip-deduplicate none. -

-
- - -
- Converting -

Give it materials

- -

- So far the geometry has shape but no substance. Without material information every volume is - assigned a dummy medium called Default, which is fine while you are checking that - things are in the right place and quite wrong the moment you want physics out of it. -

- -

- The normal route is the bill of materials that the CAD system can export alongside - the geometry. We hand that to the converter as a CSV and it matches each part's material name - against a Geant4 NIST database. The rows it looks for are mechanical part rows in this shape: -

- -
-
detector_bom.csv
-
Type,...,Part Number,Version,Name,Mass (kg),Material
-CAD,Mechanical/Part,Base,AA.01,Base,,Stainless Steel
-CAD,Mechanical/Part,BasePin,AA.01,BasePin,,Stainless Steel
-
- -

Adding both files to the conversion is all that is required:

- -
-
bash
-
o2-cad-to-tgeo my.step \
-    --output-folder cad_out/mydet -o geom.C \
-    --csg auto --exact-surfaces auto --mesh --mesh-prec 0.05 \
-    --materials-csv detector_bom.csv \
-    --bom-mass-unit kg \
-    --g4-nist-json $O2_ROOT/share/CADSupport/tools/g4_nist_database/G4_NIST_DB.json
-
- -
-
output
-
Loaded Geant4 NIST DB with 309 materials from: .../G4_NIST_DB.json
-Loaded 13 BOM entries from: detector_bom.csv
-
- -

- Matching uses a combined score of name similarity and density plausibility, which handles the fact - that engineers write “Stainless Steel” where Geant4 says G4_STAINLESS-STEEL. A - confident match becomes a real TGeoMixture carrying its element composition, radiation - length and interaction length. An ambiguous or missing one falls back to a simple material and - leaves a comment in geom.C naming the part — so unresolved materials stay visible and - greppable rather than silently wrong. The scoring thresholds are adjustable - (--mat-min-score, --mat-ambiguity-delta and a few others), but the - defaults are usually right, and it is better to fix an ambiguous name in the BOM than to loosen the - matcher. -

- -

- One nice consequence of feeding in the BOM: where both a part mass and a CAD volume are available, - the converter derives an effective density from them. That is how a perforated bracket or a - partly-filled cable tray ends up with an honest average density instead of the density of solid - metal. -

- -
- If your model came from TGeo in the first place -

- Geometry exported out of ALICE with O2_TGeoToCAD.py and coming back should use - --media-json instead. That rebuilds the original media verbatim, field by field, - rather than guessing them from names, and takes precedence over the BOM for every part it names. -

-
-
- - -
- Converting -

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
-
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

- -
- 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, which is where they come back. -

-
-
- - -
- Converting -

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 and surfaces_*.bin for exact ones — plus - csg_report.json, which records what each part became and why. -

- -
- 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);'
-
- -
-
output
-
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. -

-
- - -
- Simulating -

Add passive geometry

- -

- With a macro in hand we can put the geometry into ALICE. The mechanism is deliberately data-driven: - two small JSON files, no code and no rebuild. We start with the simpler case — passive material such - as supports, cooling or cabling, which should scatter particles but does not record anything. That - goes into an externalModules array: -

- -
-
externalGeometry.json
-
{
-  "externalModules": [
-    {
-      "name":  "EXCV",
-      "title": "Excavator support structure from CAD",
-      "macro": "cad_out/excavator/geom.C",
-      "anchor": "barrel",
-      "placement": {
-        "translation": [21.01, -13.22, -19.66],
-        "rotation_deg": [0.0, 0.0, 0.0]
-      }
-    }
-  ]
-}
-
- -
-
name
a short tag for the module. It must also appear in the module list below, or the module is silently skipped.
-
macro
the path to the geom.C you produced.
-
anchor
a volume that already exists in the ALICE geometry. barrel is the usual choice, and it sits at cave coordinates (0, -30, 0).
-
placement
translation and rotation within the anchor's frame, in centimetres and degrees.
-
- -

- The second file is the module list, which is what actually switches the module on. The split exists - so that you can describe several modules in one geometry file and enable them individually: -

- -
-
detectorlist.json
-
{ "EXTCAD": ["EXCV"] }
-
- -

Then run the simulation, pointing at both:

- -
-
bash
-
o2-sim-serial -n 1 -g boxgen \
-    --detectorList EXTCAD:detectorlist.json \
-    --extGeomFile externalGeometry.json
-
- -
-
output
-
Configured external module 'EXCV' from macro 'cad_out/excavator/geom.C' anchored to volume 'barrel'
-Activating EXCV module
-Setting special cuts for passive module EXCV
-
- -

- Those three lines mean your CAD geometry is in the simulation and particles are being transported - through it. You can list as many modules in the same array as you like. -

-
- - -
- Simulating -

Make it produce hits

- -

- Passive geometry answers questions about material budget. To ask whether your detector is actually - hit, and how often, some of its volumes need to be sensitive. This is the fastest route from a CAD - file to plottable hits, and it still needs no detector class and no rebuild — we simply change the - array name to externalDetectors and say which volumes should record: -

- -
-
externalGeometry.json
-
{
-  "externalDetectors": [
-    {
-      "name":  "EXCV",
-      "title": "Excavator as a sensitive detector",
-      "macro": "cad_out/excavator/geom.C",
-      "anchor": "barrel",
-      "detID": "TST",
-      "sensitiveVolumes": ["Bucket"],
-      "placement": { "translation": [21.01, -13.22, -19.66] }
-    }
-  ]
-}
-
- -

Choosing the sensitive volumes

- -

- There are two ways of selecting them, and you may use either or both as long as at least one is - non-empty. sensitiveVolumes matches against TGeo volume names, and - sensitiveMedia matches against medium names — the latter being a convenient way to make - every silicon part in an assembly sensitive at once, however the parts happen to be named. -

- -
- Both match substrings, not whole names -

- This catches people out. On the excavator model, - "sensitiveVolumes": ["Bucket"] selects five volumes rather than one - — Bucket, BucketLink1, BucketLink2, - BucketCylinderInner and BucketCylinderOuter. The startup log prints - every volume it registered, so read it and tighten the string if that was not what you meant. -

-
- -

Choosing a DetID

- -

- The detID field ties your detector to an existing O2 detector identity, which is what - determines where the hits are filed. Pick a slot no active built-in detector is using: -

-
    -
  • TST is the general-purpose test slot, and the right default for a quick study.
  • -
  • An upgrade study normally borrows the slot it stands in for — TRK for an ALICE 3 - tracker, for instance — because it is semantically honest and keeps downstream tooling happy.
  • -
-

- The hit branch keeps your module name rather than the borrowed one, so the configuration - above produces a branch called EXCVHit. -

- -

Running it

- -
-
bash
-
o2-sim-serial -n 3 -g boxgen --seed 42 \
-    --detectorList EXTCAD:detectorlist.json \
-    --extGeomFile externalGeometry.json \
-    --configKeyValues 'BoxGun.number=500;BoxGun.pdg=211;BoxGun.eta[0]=-1;BoxGun.eta[1]=1;BoxGun.prange[0]=2.0;BoxGun.prange[1]=5.0'
-
- -
-
output
-
External detector EXCV: 5 sensitive volume(s) selected
-External detector EXCV: registered sensitive volume 'Bucket' (MC volID 8, sensor 0)
-CREATING BRANCH EXCVHit
-External detector EXCV EndOfEvent: 681 sensitive step(s) -> 94 hit(s)
-External detector EXCV EndOfEvent: 402 sensitive step(s) -> 59 hit(s)
-External detector EXCV EndOfEvent: 927 sensitive step(s) -> 124 hit(s)
-
- -

The hits land in o2sim.root, one entry per event:

- -
-
bash
-
root -l -b -q -e 'TFile f("o2sim.root"); TTree *t=(TTree*)f.Get("o2sim");
-                  t->Draw("EXCVHit@.size()");'
-
- -
- Zero hits is usually aim, not breakage -

- The most common first result is 0 sensitive step(s), and the instinct is to suspect - the conversion. Check where the particles are going first. The run above produces nothing at all - at the default multiplicity of 10, simply because the excavator is a 40 cm object sitting 40 cm - off-axis and is a small target. Raise the multiplicity or aim the gun. To rule out the geometry - independently, shoot a ray through it in ROOT with - gGeoManager->FindNextBoundaryAndStep() and print the volume names you cross — if - they appear, navigation is fine and the problem is aim. -

-
- -

Custom sensitive actions

- -

- With no further configuration, every sensitive volume records a charged-track entrance and exit hit - in the generic o2::ext::Hit format: position in and out, momentum, energy loss, PDG code - and track length. That is enough for occupancy, acceptance and material studies, which covers most - first questions. -

- -

- When you need something else — a different hit definition, a cut applied at scoring time, extra - quantities — you can point at a macro returning a - o2::ext::ExternalDetector::SensitiveFcn. It is compiled at run time and can query - TVirtualMC::GetMC() and call helpers such as currentSensorID(), - currentTrackID() and addHit(): -

- -
-
externalGeometry.json · fragment
-
"sensitiveMedia": ["Silicon"],
-"sensitiveMacro": "sensitive_action.macro",
-"sensitiveFunction": "sensitiveAction()"
-
- -
- A worked example that needs no CAD file -

- run/SimExamples/External_Sensitive_Detectors defines two artificial detectors - entirely from data — one using the built-in action, one with a custom action compiled at run time - — from hand-written macros that mimic converter output. Running ./run.sh in that - directory shows both hit branches appearing. -

-
-
- - -
- Simulating -

Grow it into a real detector

- -
- Not yet exercised end to end -

- Everything above this section has been run, with its output pasted from a real terminal. This - route follows from how ExternalDetector and the built-in detectors are written, but no - detector has yet been built this way. Treat it as a design rather than a recipe, and expect to - debug it. -

-
- -

- The external-detector route deliberately trades flexibility for speed: you get one generic hit type - and a borrowed DetID, and in exchange you get results the same afternoon. Once a study - turns into a real subdetector you will want your own hit class, your own digitisation and a - DetID of your own — and none of that requires giving up the CAD import. The generated - geometry simply becomes one step inside an ordinary O2 detector. -

- -

Three changes to a normal detector implementation are involved:

- -
    -
  1. - Build the geometry from the macro instead of by hand. Copy geom.C - into your detector's simulation directory and call its builder hook from - ConstructGeometry(), in place of the new TGeoTube(...) code you would - otherwise write. Keep the .bin payloads beside it and install them with the detector's - data files, since the macro resolves them relative to itself. -
  2. -
  3. - Register your own sensitive volumes. Call AddSensitiveVolume() for - the volumes the macro created, using the names the converter derived from the CAD part names. - Print them once from geom.root and pin them down in code, because a rename in CAD - would otherwise quietly unregister a sensor. -
  4. -
  5. - Write your own hits. Implement ProcessHits() with your own hit class - and your own DetID, exactly as any hand-written detector does. Nothing about the - geometry's CAD origin constrains this. -
  6. -
- -

- Two things come back the moment you take this step, both of which the external-detector route cannot - offer: initFieldTrackingParams() called from your own createMaterials(), - and SetSpecialPhysicsCuts() reading a real simcuts.dat from your detector's - data directory. That closes the gap described under Field and cuts. -

- -

- The payoff is that re-running the converter after a CAD change regenerates only the geometry. Your - detector code stays untouched, which is the whole point of importing rather than transcribing. -

-
- - -
- Reference -

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\")"
-
- -
-
output
-
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
-
-
- - -
- Reference -

Limits and pain points

- -

- Finally, the honest list. These are the things known to catch people today, roughly in order of how - often they do it. None is a reason not to use the system, but all of them are cheaper to read about - here than to rediscover in a result. -

- -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
WhatWhy it happensWhat to do
One geom.C per hooked thingThe macro exports a single builder hook, and that hook is what the JSON refers to.Run the converter once per subsystem, into its own folder. They coexist happily in one JSON.
Media, cuts and field default to zeroA CAD file carries a material, never a medium, and the emitter uses a three-argument - TGeoMedium which zeroes every parameter.Pass --in-field. Accept transport defaults for step control, and treat - production cuts as unset until you write a real detector.
The anchor volume must already existPlacement is expressed inside the frame of an existing O2 volume.Use barrel unless you have a reason not to, and remember it sits at cave - (0, -30, 0).
Free-form surfaces stay tessellatedGenuine B-spline surfaces are not supported by the exact tier at all.Check the surface report. Recognition already recovers quadrics written as NURBS, which is - the large majority of them.
Illegal overlaps in the CAD modelEngineering assemblies are not drawn as legal transport worlds, and parts routinely - interpenetrate.Read CheckOverlaps, then fix in CAD or clip the offending region.
Degenerate facets at coarse precisionO2Tessellated drops triangles that collapse to a line.Treat it as a mesh-quality signal: lower --mesh-prec, or move the part onto an - exact tier.
A surprisingly huge output directoryMeshing a metre-scale curved part at a fine chord tolerance.Convert large models without --mesh, and never use the default - --mesh-prec on something metre-sized.
o2-sim complains about a missing externalModules arrayCosmetic. The message is emitted even when your JSON correctly contains only - externalDetectors.Ignore it.
-
- -

One rule that is not a preference

- -

- Run --csg auto conversions strictly serially. Parallel runs race each - other and silently lose shapes, which produces a geometry that looks complete and is not — the worst - possible failure mode, and the hardest to notice afterwards. -

- -
-

- Deeper material lives in Detectors/CADSupport: README.md for the - complete option reference, and doc/reference/ for the exact-surface solid, its file - format and the CSG pipeline. -

-
-
- -
-
- - - - diff --git a/Detectors/CADSupport/doc/tutorial/mkdocs.yml b/Detectors/CADSupport/doc/tutorial/mkdocs.yml new file mode 100644 index 0000000000000..4a3c2988d8f3f --- /dev/null +++ b/Detectors/CADSupport/doc/tutorial/mkdocs.yml @@ -0,0 +1,63 @@ +site_name: CAD to Simulation +site_description: Turning CAD models into ALICE O2 simulation geometry +docs_dir: docs + +theme: + name: material + features: + - navigation.sections + - navigation.top + - content.code.copy + - toc.follow + palette: + - media: "(prefers-color-scheme: light)" + scheme: default + primary: indigo + toggle: + icon: material/weather-night + name: Dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: indigo + toggle: + icon: material/weather-sunny + name: Light mode + +hooks: + - hooks/github_alerts.py + +markdown_extensions: + - admonition + - attr_list + - md_in_html + - tables + - toc: + permalink: true + - pymdownx.details + - pymdownx.highlight + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + +nav: + - Introduction: index.md + - Start: + - Install the software: install.md + - Convert your first model: first-conversion.md + - Converting: + - How a part is represented: representation.md + - Convert only part of a model: partial.md + - Give it materials: materials.md + - Field and cuts: field-and-cuts.md + - The geom.C file: geom-c.md + - Simulating: + - Add passive geometry: passive.md + - Make it produce hits: hits.md + - Grow it into a real detector: real-detector.md + - Worked example: + - The ITS, out and back again: its-round-trip.md + - Reference: + - Check your geometry: checks.md + - Limits and pain points: limits.md