Files
minicalosim/CLAUDE.md
T
2026-06-08 11:20:31 +02:00

79 lines
5.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
miniCaloSim is a Geant4-based calorimeter simulator designed for teaching. It exposes a Python API via pybind11 so students can define calorimeter geometries, run batch simulations, and visualise results — all from a Jupyter notebook.
## Build
Requires Geant4 (v11.1.2), pybind11 (submodule at `lib/pybind11`), and Python 3 development headers.
```bash
mkdir build && cd build
cmake ..
make -j$(nproc)
```
This produces two artifacts:
- `build/minicalo.so` — the pybind11 Python module
- `build/exampleB4a` — a standalone Geant4 executable (for reference only)
After building, copy the Python files to the Python path for use outside the `bind/` directory (the Dockerfile shows the canonical install paths):
```bash
cp build/minicalo*.so bind/G4Calo.py bind/minicalo_tools.py bind/minipandas.py /target/python/path/
cp bind/G4Calo_exec.py /usr/local/bin/
```
## Testing
Run the minipandas unit tests (no Geant4 required):
```bash
cd bind && python3 test_minipandas.py
```
Run the TempFileManager unit tests:
```bash
cd bind && python3 -m unittest minicalo_tools.TestTempFileManager
```
There are no automated tests for the full simulation — student usage is the primary validation path.
## Architecture
### C++ / Geant4 layer (`src/`, `include/`)
The simulation is a standard Geant4 application derived from example B4a. The key extension is that geometry is driven externally via `GeometryDescriptor` rather than hardcoded:
- **`GeometryDescriptor`** (`include/GeometryDescriptor.hh`) — the central geometry object. Holds a list of `Layer` objects, each of which contains `Sensor` objects after construction. Sensors accumulate deposited energy during a run. Fully pickle-able via `__getstate__`/`__setstate__`.
- **`Layer`** — describes one slab: material (NIST name), thickness (cm), active/passive, and an `nx × ny` sensor grid.
- **`Sensor`** — stores position, size, and `mutable energy` (reset between events via `GeometryDescriptor::resetSensorEnergies()`).
- **`DetectorConstruction`** (`src/DetectorConstruction.cc`) — reads `GeometryDescriptor` and builds Geant4 volumes. Uses `G4PVParameterised` + `LayerParametrisation` for the sensor grid. Active sensors have their `G4VPhysicalVolume*` assigned back into the `Layer`, enabling lookup in `SteppingAction`.
- **`SteppingAction`** — on each step, maps the physical volume back to a `Sensor` via `GeometryDescriptor::getSensorByVolume()` and accumulates energy.
- **`RunAction`** — writes a ROOT ntuple named `"Hits"` with per-event scalars (`true_energy`, `total_dep_energy`, `N_layers`, ...) and per-sensor arrays (`sensor_energy`, `sensor_x/y/z`, `sensor_dx/dy/dz`, `sensor_layer`, `sensor_copy_number`).
- **`G4System`** (`include/G4System.hh`) — owns the Geant4 run manager lifetime. `init(GeometryDescriptor&, seed)` sets everything up; `run_batch(...)` fires events to a ROOT file. One `G4System` per process — instantiated inside the subprocess (`G4Calo_exec.py`) to avoid Geant4 singleton issues.
### pybind11 bindings (`bind/bindings.cpp`)
Exposes `Sensor`, `Layer`, `GeometryDescriptor`, and `G4System` to Python as the `minicalo` module.
### Python layer (`bind/`)
- **`G4Calo.py`** — the public student API. `run_batch()` parallelises events across CPU cores using `ThreadPoolExecutor`; each worker subprocess calls `G4Calo_exec.py`, which instantiates a fresh `G4System` to avoid Geant4 singleton issues. Results are written as temporary ROOT files in `/dev/shm` (fallback: `/tmp`), read back with `uproot`/`awkward`, and merged into a `MiniFrame`. `display_event()` runs a single event and renders a 3D Plotly figure.
- **`G4Calo_exec.py`** — the worker subprocess entry point. Reads a pickled `GeometryDescriptor` + parameters, runs the simulation, writes a ROOT file, and returns the path. Must be on `$PATH` as an executable.
- **`minicalo_tools.py`** — `TempFileManager`: context manager for `/dev/shm`-backed pickle IPC files between the main process and worker subprocesses.
- **`minipandas.py`** — `MiniFrame`: a minimal NumPy-backed dataframe. Supports scalar columns `(N,)` and fixed-size vector columns `(N, M)`. Converts to `pandas.DataFrame` via `to_pandas()` and serialises with `to_pickle()`/`read_pickle()`.
### Subprocess isolation pattern
Geant4 uses global singletons that cannot be re-initialised within a process. Therefore, each simulation batch (even in "single-core" mode) runs in a fresh subprocess spawned by `G4Calo_exec.py`. Communication uses pickle files via `TempFileManager`; ROOT output files are used for event data.
### Coordinate conventions
Geant4 internal units are mm; sensor positions stored in `Sensor` are in Geant4 internal units. The Plotly visualisation multiplies layer dimensions by 10 to convert from cm to mm and labels axes with `[mm]`.
## Docker
`docker/Dockerfile` builds a JupyterHub-based image with Geant4 v11.1.2 (multithreading disabled, `GEANT4_BUILD_MULTITHREADED=OFF`), the minicalo package, and ML dependencies (PyTorch, PyG). `docker/Dockerfile-cpu` is the CPU-only variant.