divtel: Divergent pointing mode for Imaging Atmospheric Cherenkov Telescopes arrays¶
divtel makes toy simulations for the divergent pointing mode of Imaging Atmospheric Cherenkov Telescopes arrays.
Point an array’s telescopes slightly away from one another and it sees a wider patch of sky, but fewer telescopes see any given part of it, and a shower needs at least two of them to be reconstructed stereoscopically. divtel lets you set up that trade-off and measure both sides of it.
Try it in your browser, sliders, no install.
👨💻 Install¶
divtel is on PyPI:
pip install divtel
🚀 Quickstart¶
import astropy.units as u
import matplotlib.pyplot as plt
from divtel.telescope import Telescope, Array
from divtel.visualization import display_hyper_fov
# Four telescopes on a 100 m square, each with a ~5.7 degree camera.
array = Array([
Telescope(x * u.m, y * u.m, 0 * u.m, focal=20 * u.m, camera_radius=1 * u.m)
for x, y in [(100, 0), (0, 100), (-100, 0), (0, -100)]
])
# Point them divergently around a mean direction of alt=70, az=180.
array.divergent_pointing(0.02, 70 * u.deg, 180 * u.deg)
# How much sky does the array see, and how much of it stereoscopically?
covered, patches = array.hyper_fov() # 45.96 deg2
stereo, _ = array.hyper_fov(m_cut=2) # 30.27 deg2, seen by 2+ telescopes
fig, (ground, sky) = plt.subplots(1, 2, figsize=(11, 5))
array.display_2d(projection="xy", ax=ground)
display_hyper_fov(array, ax=sky)
plt.show()
Pointed in parallel (div=0) the same array sees 25.7 deg2, all of it at
multiplicity 4. A div of 0.02 buys 79% more sky, of which 18% more is still
stereoscopic. But a shower now lands on two telescopes where it used to land
on four. That is the whole trade-off, and the
user guide walks through
it properly: the coordinate frame, what div really means, and how the hyper
field of view is computed.
🛠 Development¶
Install from source¶
With uv (recommended):
git clone https://github.com/cta-observatory/divtel.git
cd divtel
uv sync
uv sync creates a virtual environment in .venv, installs divtel in editable
mode, and pulls in the development dependencies (pytest, sphinx, ruff) declared
as PEP 735 dependency groups. Add
--extra examples if you also want to run the notebooks in examples/.
With pip (requires pip >= 25.1 for --group):
git clone https://github.com/cta-observatory/divtel.git
cd divtel
pip install -e . --group dev
Then run the tests:
pytest
Note: install divtel before importing it, even from a source checkout. The version is derived from git by setuptools_scm at install time and written to
divtel/_version.py; importing an uninstalled source tree reports__version__ == "0.0.0".
Building the documentation¶
The docs are published to https://cta-observatory.github.io/divtel/. To build them locally:
uv sync --group docs --extra examples
sphinx-build -b html docs docs/_build/html
GitHub Pages serves static files only, so it cannot preview the result:
file:// will not work, and the interactive demo needs a real HTTP origin.
Serve the build instead:
python -m http.server 8000 -d docs/_build/html
Working on the interactive demo¶
examples/marimo/interactive_display.py is a marimo
notebook. Sphinx exports it to WebAssembly during the build, so the published
page ships its own Python interpreter and runs entirely in the reader’s
browser — sliders included, with no server and nothing to install.
That export shells out to uv, which is why uv
is itself a documentation dependency.
The Jupyter version in examples/notebooks/interactive_display.ipynb is kept
for running locally, but is deliberately not built into the site: its
ipywidgets sliders need a Python kernel, so on a static page they would
render as controls that cannot move.
To work on the demo:
marimo edit examples/marimo/interactive_display.py
🛡 License¶
This project is licensed under the terms of the MIT license. See LICENSE for more details.
📃 Citation¶
@software{vuillaume_2026_22134772,
author = {Vuillaume, Thomas and
Donini, Alice and
Tak, Donggeun},
title = {cta-observatory/divtel: v1.0.0},
month = aug,
year = 2026,
publisher = {Zenodo},
version = {v1.0.0},
doi = {10.5281/zenodo.22134772},
url = {https://doi.org/10.5281/zenodo.22134772},
swhid = {swh:1:dir:5d33fdb6f72092680b3d56a5b5464ad8bc15105a
;origin=https://doi.org/10.5281/zenodo.6415137;vis
it=swh:1:snp:8c5be5e7e6d934ee5128efb62dbc5e4ffae72
248;anchor=swh:1:rel:bf23ef411c70658334678e4345c3a
cb4cbf8ecc6;path=cta-observatory-divtel-c0283b3
},
}