obdeect
obdeect is a C++20 optical ray tracing prototype for imaging atmospheric
Cherenkov telescopes. The C++ core uses only the standard library. Python
provides plotting, PSF analysis, and model import tools.
Installation and Build
For a released platform wheel, Python 3.10+ is sufficient. The wheel contains the C++20 ray-tracing executables and headers. A source checkout additionally needs a C++20 compiler and CMake 3.20+.
Install the current development distribution from PyPI and run the packaged tracer without a source checkout:
python -m pip install obdeect-dev
obdeect-simtools-raytrace --telescope MST --photons 10000 --output trace.csv
Installation and build steps are as follows:
python -m venv .venv
source .venv/bin/activate
python -m pip install -e '.[dev]'
cmake --preset debug
cmake --build --preset debug
Test your installation with:
ctest --test-dir build/debug --output-on-failure
python -m unittest discover -s python/tests
Simple Telescope Simulation
Trace the simple spherical telescope and make three plots:
./build/debug/obdeect_reference --photons 10000 --output reference.csv
obdeect-plot-reference --telescope reference-mst --view structure --output structure.png
obdeect-plot-reference reference.csv --telescope reference-mst --view rays --output rays.png
obdeect-plot-reference reference.csv --telescope reference-mst --view focal-plane --output focal.png
The structure plot shows the reference mirror, camera, and four supports in two
side projections. The ray plot uses actual CSV vertices. The focal plot shows
detected weight per bin and x/y projections. --max-paths limits displayed
rays; --bins controls the focal histogram. Coordinates are telescope frame
metres. The older --focal-plane flag remains supported.
Artificial calibration sources
The baseline executable uses 400-nm photons and supports three deterministic source models over the same entrance pupil:
# Plane wave from an on/off-axis star; angles are telescope-frame degrees.
./build/obdeect_reference --source star --field-x-deg 0.5 --field-y-deg 0.0
# Finite-distance point flasher, with per-photon inverse-square weights.
./build/obdeect_reference --source illuminator --distance-m 50
# Collimated or finite-divergence calibration laser.
./build/obdeect_reference --source laser --distance-m 50 --divergence-deg 0.1
CSV output stores every traced path vertex plus wavelength, emission time, source weight and the trace's wavelength-independent throughput. The latter is currently one for a detector-surface hit and zero for every loss; a compiled coating/material scene will replace it with wavelength-dependent transport.
CTAO reference models
obdeect_ctao --telescope LST|MST|SST|SCT writes ragged photon paths for the
same Python plotter. The analytic catalogue uses public simulation-models
6.3.0 identifiers as a tested baseline; the production importer accepts an
explicitly selected version from the supplied checkout and records provenance.
It contains optical prescriptions only: it does not load model JSON, facet positions,
detector conversion, alignment, structures, throughput, or the SCT coordinate
transform. LST ideal-paraboloid and MST central-sphere baselines are executable
and tested; SST/SCT are two-mirror prescription scaffolding awaiting their
model-specific geometry validation.
The core now also has a finite-facet and tabulated-coating kernel for use by a future model importer. A facet list is not currently embedded in any reference model, so this must not be interpreted as segmented CTAO telescope support. The explicit status, remaining work, and evidence required before claiming sim_telarray/ROBAST-level coverage are in docs/STATUS.md. Reference manifest and comparison schemas are described in docs/REFERENCE_MANIFEST.md and docs/COMPARISON_CONTRACT.md.
Import a selected simulation-models production
The standard-library importer records every selected parameter record and all declared model-file assets with SHA-256 hashes. It neither downloads nor copies external model data.
obdeect-import-simulation-models /path/to/simulation-models LSTN-design \
--version 6.3.0 --output lstn-design.ir.json
This command selects and hashes the source parameter records and their declared assets. It is provenance input for scene compilation, not a ray-tracing command and does not download, copy, or silently interpret model files.
The emitted obdeect.simulation-models-ir.v1 JSON is the auditable hand-off
from model selection to the future C++ scene compiler. An unresolved parameter
file, missing declared asset, identity mismatch, or unsafe path fails the
import; no field is silently discarded.
Coordinate and model convention
The mirror vertex is at z=0 m; incoming artificial Cherenkov photons begin
at z=20 m and travel in -z. The spherical centre is at z=9.75 m, making
the paraxial screen location z=4.875 m. The simple structure is four
finite-cylinder support legs plus a circular camera face evaluated before the
primary reflection. CSV records source, termination/mirror, and focal-screen
vertices so the plot displays the actual traced path.
Core architecture status
The C++20 core is split by responsibility so a photon kernel never needs Python, YAML, EventIO or a plotting dependency.
CORSIKA 7 EventIO input
The optional C++ EventIO adapter reads CORSIKA IACT TELFIL files directly.
In this workspace it builds against the adjacent EventIO C source checkout;
an installed library can instead be selected with -DEventio_ROOT=/path/to/install.
cmake -S . -B build/eventio -DOBDEECT_BUILD_EVENTIO_INPUT=ON
cmake --build build/eventio --target obdeect_eventio_summary
./build/eventio/obdeect_eventio_summary /path/to/CORSIKA_TELFIL
EventioPhotonReader yields one weighted OpticalPhoton per CORSIKA bunch,
with a PhotonBatchContext for run, event, reused array and telescope. It
supports full, compact and 3D records in both array layouts. A zero wavelength
stays unresolved; CEFFIC photoelectron-like bunches are rejected. Use
resolve_eventio_spectrum() to create deterministic spectral children before
any wavelength-dependent response. AtmosphereTransmissionTable reads the
simulation transmission table and attenuate_eventio_direct_beam() applies
direct-beam extinction between emission and telescope arrival. Check that the
table's observation altitude and CORSIKA production configuration match before
using it. These stages are separate from the file decoder and optical kernel.
The reader's CORSIKA-local arrival rays must be transformed into the selected
telescope scene frame before tracing. See the
input and atmosphere plan for conventions
and remaining production-file validation.
| Component | Current implementation |
|---|---|
math.hpp |
Vec3, dot/cross/norm and checked direction normalisation. |
photon_buffer.hpp, abi.hpp |
SoA input contract, result buffers, status values and validation. |
source.hpp |
Deterministic 400-nm star, point illuminator and laser sources. |
tables.hpp |
Immutable no-extrapolation 1-D response interpolation. |
intersections.hpp, geometry.hpp |
Plane, sphere/cap, disk, finite cylinder and axisymmetric-surface dispatch. |
interactions.hpp |
Checked specular reflection. |
axisymmetric_optics.hpp |
Paraboloid/even-polynomial SC surfaces and forward Newton intersection. |
facets.hpp |
Finite circular facet intersection, nearest-hit selection and tabulated coating response. |
scene.hpp, trace.hpp |
Immutable directed reference-scene compilation and scalar SoA block tracing. |
diagnostics.hpp |
Status/weight closure summary. |
model_import.hpp |
Canonical CTAO model provenance/import target. |
Linting
Install CMake, Ninja, a C++20 compiler, Python 3.10+, and the development tools:
python -m pip install --upgrade pip ruff
python -m pip install .
Examples and tutorials
Tutorials cover sources, optical reference models, field-angle scans, and model provenance. Run the examples with:
python examples/run_examples.py --build build/debug --output out/examples --photons 1000
Each case writes a CSV, structure/ray/focal plots where applicable, and status counts. See examples/README.md for the case list.
Scope
The reference scene is MST inspired; it is not a CTAO production telescope. The
obdeect_ctao command traces analytic LST/MST optical baselines and contains
SST/SCT polynomial prescriptions. Model-derived nominal LST/MST surface tables
are traceable through obdeect-simtools-raytrace --scene-file; run-specific
alignment, secondary structures, obscurers, and wavelength-dependent bindings
remain gated. Current limitations and validation evidence are in status. The current
code review records fixes and remaining duplication.
PSF scan
obdeect-psf scan --executable ./build/debug/obdeect_reference --photons 10000 \
--field-x-deg 0 0.5 1.0 --output-dir out/reference_psf --plot out/reference_psf.png
A scan writes one trace CSV per offset and psf_scan.csv/psf_scan.json.
These metrics describe the selected deterministic reference configuration;
they are not validation of a production telescope model.
The production 7.0.0 sim_telarray reference includes focal-plane PSFs, integration radii, cumulative distributions, and archived photon lists for LST, both MST configurations, and SST across their selected North/South sites. These remain the comparison fixtures for native scene traces.
Model provenance and scene compilation
The Python adapters can select a simulation-models record, hash its declared
assets, and compile a provenance-checked scene hand-off. Compilation retains
mirror-list geometry, derives nominal single-reflector panel normals, and
reports run-specific alignment, structures, and materials as trace blockers.
For nominal LST and MST records it can also emit the strict native surface
table consumed directly by the C++ tracer:
obdeect-import-simulation-models /path/to/simulation-models LSTN-design \
--version 7.0.0 --output lstn.ir.json
obdeect-compile-scene lstn.ir.json --source-root /path/to/simulation-models \
--simtel-root /path/to/sim_telarray --output lstn.scene.json \
--native-output lstn.scene.csv
obdeect-simtools-raytrace --scene-file lstn.scene.csv --source star \
--photons 10000 --output lstn-arrivals.csv
--simtel-root is needed when a camera response table is found in
sim_telarray's cfg/CTA search path rather than simulation-models Files.
The selected file path and SHA-256 hash are recorded in the scene provenance.
CSV trace output includes source weight, wavelength, emission time, terminal status, path length, path vertices, and primary/focal incidence angles. The current executable-level throughput is one for a detector-surface hit and zero for a loss; material and coating kernels remain separately tested primitives until their model bindings are selected.
Project map
| Path | Purpose |
|---|---|
cpp/include/obdeect/ |
Geometry, optics, sources, and tracing |
cpp/tests/ |
Native tests |
python/obdeect/ |
Import, plotting, and PSF commands |
python/tests/ |
Python tests |
examples/ |
Runnable end to end examples |
docs/ |
Tutorials, status, and comparison contracts |
License and Citation
BSD-3-Clause license. Citation metadata is in CITATION.cff.
Generative AI disclosure
Generative AI tools were used to write much of this project; outputs were reviewed and validated by the authors.
Generative AI tools (mostly ChatGPT 5.6) were used to write the entire code of this project. All AI-assisted outputs were reviewed, validated, and, where necessary, modified by the authors to ensure accuracy and reliability.
Release files for obdeect-dev 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| obdeect_dev-0.1.0.tar.gz | 6.8 MB | Details |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| obdeect_dev-0.1.0-py3-none-musllinux_1_2_x86_64.whl | Python 3 | none | Linux musl 1.2+ x86-64 | Details |
| obdeect_dev-0.1.0-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl | Python 3 | none | Linux glibc 2.28+ x86-64, Linux glibc 2.24+ x86-64 | Details |
| obdeect_dev-0.1.0-py3-none-macosx_11_0_arm64.whl | Python 3 | none | macOS 11.0+ ARM64 | Details |
Total release size: 8.3 MB
Release files / obdeect_dev-0.1.0.tar.gz
| Download URL | obdeect_dev-0.1.0.tar.gz |
|---|---|
| Size | 6.8 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6accdd8452d3117d3b33bb7009fe09e942adccd97a430c00f99c0acb43003abb
|
|
BLAKE2b-256 checksum How to use checksums |
e458488d060ca20641ee3778b5f6d44e6a390d11053ce240ab53b17e95c049f1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / obdeect_dev-0.1.0-py3-none-musllinux_1_2_x86_64.whl
| Download URL | obdeect_dev-0.1.0-py3-none-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 1.2 MB |
| Tags | Linux musl 1.2+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
4ebde24795b17c726a2de2b9883875a158446d49a80a08e54ab8163ad0d0bd51
|
|
BLAKE2b-256 checksum How to use checksums |
a4a8e5ff260b6811b347a05ba0a81aa2db874f5871a5eae6e6291fbe62d06bc6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / obdeect_dev-0.1.0-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
| Download URL | obdeect_dev-0.1.0-py3-none-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 152.9 kB |
| Tags | Linux glibc 2.24+ x86-64 Linux glibc 2.28+ x86-64 Python 3 |
|
SHA-256 checksum How to use checksums |
1e34b2e4e36158ffd89063364ed590f59085acc82ccd1537cbcc849d699c57cc
|
|
BLAKE2b-256 checksum How to use checksums |
6eeeffa9060805b4c73c701da09a763e49e25d1bfb88e61a9700e4d214128a32
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / obdeect_dev-0.1.0-py3-none-macosx_11_0_arm64.whl
| Download URL | obdeect_dev-0.1.0-py3-none-macosx_11_0_arm64.whl |
|---|---|
| Size | 150.2 kB |
| Tags | Python 3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
00c7f3d439f0ae71827e4be38224600ec56888a460f9da4ae2d0523a35f21945
|
|
BLAKE2b-256 checksum How to use checksums |
dbc0397e6797928af574d4939520869113871f5a94432076e7fa79cb55aa8626
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log