iactrace 0.10.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iactrace-0.10.1/CHANGELOG.md +116 -0
- iactrace-0.10.1/LICENSE +29 -0
- iactrace-0.10.1/MANIFEST.in +10 -0
- iactrace-0.10.1/PKG-INFO +141 -0
- iactrace-0.10.1/README.md +88 -0
- iactrace-0.10.1/iactrace/__init__.py +59 -0
- iactrace-0.10.1/iactrace/analysis/__init__.py +13 -0
- iactrace-0.10.1/iactrace/analysis/focal_surface.py +216 -0
- iactrace-0.10.1/iactrace/camera/__init__.py +22 -0
- iactrace-0.10.1/iactrace/camera/_hexgeom.py +81 -0
- iactrace-0.10.1/iactrace/camera/camera.py +454 -0
- iactrace-0.10.1/iactrace/camera/detection_chain.py +104 -0
- iactrace-0.10.1/iactrace/camera/detector/__init__.py +11 -0
- iactrace-0.10.1/iactrace/camera/detector/photodetector.py +119 -0
- iactrace-0.10.1/iactrace/camera/detector/pmt.py +154 -0
- iactrace-0.10.1/iactrace/camera/detector/surface.py +138 -0
- iactrace-0.10.1/iactrace/camera/optics/__init__.py +13 -0
- iactrace-0.10.1/iactrace/camera/optics/concentrator.py +74 -0
- iactrace-0.10.1/iactrace/camera/optics/okumura.py +320 -0
- iactrace-0.10.1/iactrace/camera/optics/polygonal.py +279 -0
- iactrace-0.10.1/iactrace/camera/optics/winston.py +220 -0
- iactrace-0.10.1/iactrace/camera/sensor_group.py +473 -0
- iactrace-0.10.1/iactrace/core/__init__.py +87 -0
- iactrace-0.10.1/iactrace/core/_tolerances.py +18 -0
- iactrace-0.10.1/iactrace/core/apertures.py +173 -0
- iactrace-0.10.1/iactrace/core/bsdf.py +162 -0
- iactrace-0.10.1/iactrace/core/coatings.py +136 -0
- iactrace-0.10.1/iactrace/core/interactions.py +385 -0
- iactrace-0.10.1/iactrace/core/intersections.py +449 -0
- iactrace-0.10.1/iactrace/core/obstructions.py +170 -0
- iactrace-0.10.1/iactrace/core/optics.py +190 -0
- iactrace-0.10.1/iactrace/core/ray_bundle.py +204 -0
- iactrace-0.10.1/iactrace/core/render.py +502 -0
- iactrace-0.10.1/iactrace/core/sampling.py +82 -0
- iactrace-0.10.1/iactrace/core/surfaces.py +572 -0
- iactrace-0.10.1/iactrace/core/trajectory.py +53 -0
- iactrace-0.10.1/iactrace/core/transforms.py +111 -0
- iactrace-0.10.1/iactrace/io/__init__.py +28 -0
- iactrace-0.10.1/iactrace/io/adapters.py +1817 -0
- iactrace-0.10.1/iactrace/io/schemas.py +660 -0
- iactrace-0.10.1/iactrace/io/yaml_io.py +243 -0
- iactrace-0.10.1/iactrace/py.typed +0 -0
- iactrace-0.10.1/iactrace/telescope/__init__.py +9 -0
- iactrace-0.10.1/iactrace/telescope/_common.py +21 -0
- iactrace-0.10.1/iactrace/telescope/lenses.py +319 -0
- iactrace-0.10.1/iactrace/telescope/mirrors.py +432 -0
- iactrace-0.10.1/iactrace/telescope/obstructions.py +113 -0
- iactrace-0.10.1/iactrace/telescope/operations.py +578 -0
- iactrace-0.10.1/iactrace/telescope/telescope.py +400 -0
- iactrace-0.10.1/iactrace/viz/__init__.py +14 -0
- iactrace-0.10.1/iactrace/viz/_meshes.py +445 -0
- iactrace-0.10.1/iactrace/viz/_optics.py +194 -0
- iactrace-0.10.1/iactrace/viz/_sensors.py +282 -0
- iactrace-0.10.1/iactrace/viz/_utils.py +16 -0
- iactrace-0.10.1/iactrace/viz/overlays.py +170 -0
- iactrace-0.10.1/iactrace/viz/plotting.py +155 -0
- iactrace-0.10.1/iactrace/viz/scenes.py +273 -0
- iactrace-0.10.1/iactrace.egg-info/PKG-INFO +141 -0
- iactrace-0.10.1/iactrace.egg-info/SOURCES.txt +62 -0
- iactrace-0.10.1/iactrace.egg-info/dependency_links.txt +1 -0
- iactrace-0.10.1/iactrace.egg-info/requires.txt +31 -0
- iactrace-0.10.1/iactrace.egg-info/top_level.txt +1 -0
- iactrace-0.10.1/pyproject.toml +133 -0
- iactrace-0.10.1/setup.cfg +4 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v0.10.1 (2026-08-07)
|
|
4
|
+
|
|
5
|
+
### Fix
|
|
6
|
+
|
|
7
|
+
- **core**: fixed shadowing bug for point sources, switched to 1/d^2 scaling for point source
|
|
8
|
+
- **core**: account for opl if slab is first element, salt downstream of first surface dependent on id now, clarify aspheric convention
|
|
9
|
+
|
|
10
|
+
## v0.10.0 (2026-08-06)
|
|
11
|
+
|
|
12
|
+
### Feat
|
|
13
|
+
|
|
14
|
+
- **trajectory**: unified treatment of trajectories between components, fixed bugs in viz
|
|
15
|
+
- **viz,-core**: added the ability to save trajectories for the optical trace, added methods to visualize traces for optics and detection_chain
|
|
16
|
+
|
|
17
|
+
### Fix
|
|
18
|
+
|
|
19
|
+
- **docs,-core**: fixed documentation and removed bugs from formatting. fixed some problems with normalization and polygonal apertures
|
|
20
|
+
- **core**: fixed issue with surface relocalization
|
|
21
|
+
- **core,-viz**: fixed a bug in the intersection logic, leading to the wrong root being chosen for conics. also made float32 computation more stable by propagating to local object reference. improved visualization of traced rays. refactored telescope3d by breaking up into files
|
|
22
|
+
|
|
23
|
+
### Refactor
|
|
24
|
+
|
|
25
|
+
- **ruff**: reformat and fixes with ruff
|
|
26
|
+
- **ruff,-tests**: reformated with ruff, changed default setting of laye_vs_eager test to not be on pixel boundary
|
|
27
|
+
|
|
28
|
+
## v0.9.1 (2026-08-06)
|
|
29
|
+
|
|
30
|
+
### Fix
|
|
31
|
+
|
|
32
|
+
- **core, docs**: fixed angle conventions, more info
|
|
33
|
+
|
|
34
|
+
## v0.9.0 (2026-07-18)
|
|
35
|
+
|
|
36
|
+
### BREAKING CHANGE
|
|
37
|
+
|
|
38
|
+
- Closes #11
|
|
39
|
+
|
|
40
|
+
### Feat
|
|
41
|
+
|
|
42
|
+
- **core,tests,camera**: refactored tests to be more concentrated and better motivated, refactored to reduce code duplication and increase polymorphism, restructured camera module
|
|
43
|
+
- **camera**: created a combined tracing chain, including photosensor surface, allowing for peeking
|
|
44
|
+
- **core**: added an alive flag to ray bundles, to avoid confusing properties regarding value and hit mask
|
|
45
|
+
- **camera**: added okumura cone prototype
|
|
46
|
+
- **camera,configs,io,viz,tests**: implemented a detection chain that allows for concentrators and complicated photosensors
|
|
47
|
+
- **core**: added angle dependent coatings via tabulation
|
|
48
|
+
|
|
49
|
+
### Fix
|
|
50
|
+
|
|
51
|
+
- **configs**: changed the ordering of the modules to reflective of the way it is done in sim_telarray
|
|
52
|
+
- **core,-surfaces**: added more surface types, fixed shadowing bug for tracing
|
|
53
|
+
- **configs**: improved telescope shadowing (no baffling instead of monolithic camera enclosure, to avoid incorrect self shadowing. also added a best guess okumura cone to LSTCam
|
|
54
|
+
- **configs**: Added winston cones to flashcam, nectarcam and HESS2cam
|
|
55
|
+
- **configs,viz**: added winston cone to HESS1U, fixed relative position of mirrors in the context of coordinate conventions, added visualization of the sensor grid
|
|
56
|
+
- **core**: fix that there is no shadowing applied in the last leg to the focal plane, improved focal plane analysis
|
|
57
|
+
- **camera**: fixed roundtripping issues with camera and coatings
|
|
58
|
+
- **camera,core,io**: fixed bugs regarding mirroring, io and camera rays
|
|
59
|
+
- **io**: fixed bsdf not bein correctly serialized
|
|
60
|
+
|
|
61
|
+
### Refactor
|
|
62
|
+
|
|
63
|
+
- **refactor**: refactored camera module, slimmed tests, better io for surfaces
|
|
64
|
+
- **format,-doc**: ruff formatting and small doc changes regarding conventions
|
|
65
|
+
|
|
66
|
+
## v0.8.0 (2026-06-11)
|
|
67
|
+
|
|
68
|
+
### Feat
|
|
69
|
+
|
|
70
|
+
- **opl**: make opl behave as expected, weighted with index of refraction, add distance to source or initial mirror
|
|
71
|
+
|
|
72
|
+
### Fix
|
|
73
|
+
|
|
74
|
+
- **viz**: fixed 3D visualization to correctly handle transparency in jupyter notebooks
|
|
75
|
+
|
|
76
|
+
## v0.7.0 (2026-05-12)
|
|
77
|
+
|
|
78
|
+
### Feat
|
|
79
|
+
|
|
80
|
+
- **configs**: added configs for the SST telescope with CHECS camera
|
|
81
|
+
- **io,viz**: enabled both apertures types for slabs and lenses for plotting and io
|
|
82
|
+
- **analysis,camera,viz**: improved plotting and combined plotting path for sensors, added analysis tools such as focal plane and fixed multi-sensor groups
|
|
83
|
+
|
|
84
|
+
### Fix
|
|
85
|
+
|
|
86
|
+
- **benchmarks**: fixed benchmark to mvoe from MCIntegrator to direct sampling
|
|
87
|
+
- **surfaces**: bug in aspheric surface formula squared unnecessarily
|
|
88
|
+
|
|
89
|
+
### Refactor
|
|
90
|
+
|
|
91
|
+
- **docs,configs**: adjusted docs to reflect new structure, split configs into telescope/camera, ran ruff
|
|
92
|
+
- **iactrace**: major refactor of architecture, better support for lens optics
|
|
93
|
+
- **utils**: removed utils path, unneccessary for new sampling method
|
|
94
|
+
|
|
95
|
+
## v0.6.1 (2026-05-06)
|
|
96
|
+
|
|
97
|
+
### Fix
|
|
98
|
+
|
|
99
|
+
- **viz**: changed hexshow colorscaling to follow the same logic as squareshow via plt.Normalize
|
|
100
|
+
- **operations**: changed mirror displacement to go along the local mirror z axis, similar to simtel_array
|
|
101
|
+
- **core**: fixed bug in conic intersection
|
|
102
|
+
|
|
103
|
+
### Perf
|
|
104
|
+
|
|
105
|
+
- **core**: bypass NR for pure conics
|
|
106
|
+
|
|
107
|
+
## v0.6.0 (2026-02-09)
|
|
108
|
+
|
|
109
|
+
### BREAKING CHANGE
|
|
110
|
+
|
|
111
|
+
- initial project setup
|
|
112
|
+
|
|
113
|
+
### Feat
|
|
114
|
+
|
|
115
|
+
- add changelog
|
|
116
|
+
- initial release
|
iactrace-0.10.1/LICENSE
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Gerrit Roellinghoff
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
iactrace-0.10.1/PKG-INFO
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iactrace
|
|
3
|
+
Version: 0.10.1
|
|
4
|
+
Summary: JAX-based optical ray tracing for Imaging Atmospheric Cherenkov Telescopes
|
|
5
|
+
Author-email: Gerrit Roellinghoff <gerrit.roellinghoff@fau.de>
|
|
6
|
+
Maintainer-email: Gerrit Roellinghoff <gerrit.roellinghoff@fau.de>
|
|
7
|
+
License: BSD-3-Clause
|
|
8
|
+
Project-URL: Homepage, https://github.com/GerritRo/iactrace
|
|
9
|
+
Project-URL: Documentation, https://gerritro.github.io/iactrace/
|
|
10
|
+
Project-URL: Repository, https://github.com/GerritRo/iactrace.git
|
|
11
|
+
Project-URL: Issues, https://github.com/GerritRo/iactrace/issues
|
|
12
|
+
Keywords: ray-tracing,optics,cherenkov,telescope,jax,differentiable,iact,astronomy,astrophysics
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
17
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.12
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: jax>=0.4.20
|
|
25
|
+
Requires-Dist: jaxlib>=0.4.20
|
|
26
|
+
Requires-Dist: numpy>=1.22.0
|
|
27
|
+
Requires-Dist: matplotlib>=3.5.0
|
|
28
|
+
Requires-Dist: trimesh>=3.15.0
|
|
29
|
+
Requires-Dist: equinox>=0.11.0
|
|
30
|
+
Requires-Dist: pyyaml>=6.0
|
|
31
|
+
Requires-Dist: pydantic>=2.0
|
|
32
|
+
Provides-Extra: dev
|
|
33
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
34
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
35
|
+
Requires-Dist: jupyter>=1.0; extra == "dev"
|
|
36
|
+
Requires-Dist: mypy>=1.0; extra == "dev"
|
|
37
|
+
Requires-Dist: nbclient>=0.7.0; extra == "dev"
|
|
38
|
+
Requires-Dist: types-PyYAML>=6.0; extra == "dev"
|
|
39
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
40
|
+
Requires-Dist: optax>=0.2.6; extra == "dev"
|
|
41
|
+
Requires-Dist: asv>=0.6.0; extra == "dev"
|
|
42
|
+
Requires-Dist: virtualenv>=20.0; extra == "dev"
|
|
43
|
+
Requires-Dist: commitizen>=3.12.0; extra == "dev"
|
|
44
|
+
Provides-Extra: gpu
|
|
45
|
+
Requires-Dist: jax[cuda12]; extra == "gpu"
|
|
46
|
+
Provides-Extra: docs
|
|
47
|
+
Requires-Dist: sphinx; extra == "docs"
|
|
48
|
+
Requires-Dist: furo; extra == "docs"
|
|
49
|
+
Requires-Dist: sphinx-automodapi; extra == "docs"
|
|
50
|
+
Requires-Dist: sphinx-design; extra == "docs"
|
|
51
|
+
Requires-Dist: myst-nb; extra == "docs"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
# IACTrace
|
|
55
|
+
|
|
56
|
+

|
|
57
|
+
[](LICENSE)
|
|
58
|
+
[](http://mypy-lang.org/)
|
|
59
|
+
[](https://github.com/astral-sh/ruff)
|
|
60
|
+
[](https://codecov.io/gh/GerritRo/iactrace)
|
|
61
|
+
|
|
62
|
+
**JAX-based optical ray tracing for Imaging Atmospheric Cherenkov Telescopes**
|
|
63
|
+
|
|
64
|
+
IACTrace is a differentiable ray tracing library for simulating the optical properties of IACT (Imaging Atmospheric Cherenkov Telescope) systems. Built on JAX and Equinox, it supports automatic differentiation for optimization and inverse problems.
|
|
65
|
+
|
|
66
|
+
## Features
|
|
67
|
+
|
|
68
|
+
- Differentiable ray tracing with JAX
|
|
69
|
+
- Multi-stage optical systems: segmented primaries, secondary mirrors, lenses and windows
|
|
70
|
+
- Square and hexagonal sensor arrays with per-pixel light concentrators (Winston / Okumura cones)
|
|
71
|
+
- Physical photodetectors: PMTs, SiPMs can be connected in-line with concentrators
|
|
72
|
+
- Aspheric, Zernike and freeform surfaces, plus error models (roughness, misalignment, figure errors)
|
|
73
|
+
- Obstruction modeling (cylinders, boxes, spheres, oriented boxes, triangles)
|
|
74
|
+
- YAML-based telescope and camera configuration
|
|
75
|
+
- Response matrix calculation
|
|
76
|
+
|
|
77
|
+
## Installation
|
|
78
|
+
```bash
|
|
79
|
+
pip install iactrace
|
|
80
|
+
```
|
|
81
|
+
Or straight from the repository:
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pip install git+https://github.com/GerritRo/iactrace/
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
For development:
|
|
88
|
+
```bash
|
|
89
|
+
git clone https://github.com/GerritRo/iactrace.git
|
|
90
|
+
cd iactrace
|
|
91
|
+
pip install -e ".[dev]"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Quick Start
|
|
95
|
+
|
|
96
|
+
Telescope and camera are configured with separate YAML files and loaded
|
|
97
|
+
independently.
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import jax
|
|
101
|
+
import jax.numpy as jnp
|
|
102
|
+
from iactrace import Telescope, Camera
|
|
103
|
+
|
|
104
|
+
# Random key for Monte Carlo ray sampling.
|
|
105
|
+
key = jax.random.key(0)
|
|
106
|
+
|
|
107
|
+
# Load the telescope (optics + camera frame) and the camera (sensor layout).
|
|
108
|
+
telescope = Telescope.from_yaml("configs/HESS/CT3.yaml", n_samples=256, key=key)
|
|
109
|
+
camera = Camera.from_yaml("configs/HESS/HESS1U.yaml")
|
|
110
|
+
|
|
111
|
+
# Simulate an on-axis astronomical source (parallel light).
|
|
112
|
+
directions = jnp.array([[0.0, 0.0, -1.0]])
|
|
113
|
+
values = jnp.array([1.0])
|
|
114
|
+
|
|
115
|
+
# render() traces rays through the optics and returns a lazy ray bundle;
|
|
116
|
+
# camera.image() folds it into the pixel image.
|
|
117
|
+
ray_bundle = telescope.render(directions, values, source_type="parallel")
|
|
118
|
+
image = camera.image(ray_bundle)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
See the [Quick Start guide](https://gerritro.github.io/iactrace/getting_started/quickstart.html)
|
|
122
|
+
for point sources, off-axis geometry, and visualization.
|
|
123
|
+
|
|
124
|
+
## Documentation
|
|
125
|
+
|
|
126
|
+
Full documentation and examples are available at: **https://gerritro.github.io/iactrace/**
|
|
127
|
+
|
|
128
|
+
### Building Documentation Locally
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
pip install -e ".[docs]"
|
|
132
|
+
cd docs && make html
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
BSD-3-Clause License - see [LICENSE](LICENSE) for details.
|
|
138
|
+
|
|
139
|
+
## Citation
|
|
140
|
+
|
|
141
|
+
If you use IACTrace in your research, please cite this repository.
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# IACTrace
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
[](LICENSE)
|
|
5
|
+
[](http://mypy-lang.org/)
|
|
6
|
+
[](https://github.com/astral-sh/ruff)
|
|
7
|
+
[](https://codecov.io/gh/GerritRo/iactrace)
|
|
8
|
+
|
|
9
|
+
**JAX-based optical ray tracing for Imaging Atmospheric Cherenkov Telescopes**
|
|
10
|
+
|
|
11
|
+
IACTrace is a differentiable ray tracing library for simulating the optical properties of IACT (Imaging Atmospheric Cherenkov Telescope) systems. Built on JAX and Equinox, it supports automatic differentiation for optimization and inverse problems.
|
|
12
|
+
|
|
13
|
+
## Features
|
|
14
|
+
|
|
15
|
+
- Differentiable ray tracing with JAX
|
|
16
|
+
- Multi-stage optical systems: segmented primaries, secondary mirrors, lenses and windows
|
|
17
|
+
- Square and hexagonal sensor arrays with per-pixel light concentrators (Winston / Okumura cones)
|
|
18
|
+
- Physical photodetectors: PMTs, SiPMs can be connected in-line with concentrators
|
|
19
|
+
- Aspheric, Zernike and freeform surfaces, plus error models (roughness, misalignment, figure errors)
|
|
20
|
+
- Obstruction modeling (cylinders, boxes, spheres, oriented boxes, triangles)
|
|
21
|
+
- YAML-based telescope and camera configuration
|
|
22
|
+
- Response matrix calculation
|
|
23
|
+
|
|
24
|
+
## Installation
|
|
25
|
+
```bash
|
|
26
|
+
pip install iactrace
|
|
27
|
+
```
|
|
28
|
+
Or straight from the repository:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install git+https://github.com/GerritRo/iactrace/
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
For development:
|
|
35
|
+
```bash
|
|
36
|
+
git clone https://github.com/GerritRo/iactrace.git
|
|
37
|
+
cd iactrace
|
|
38
|
+
pip install -e ".[dev]"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
Telescope and camera are configured with separate YAML files and loaded
|
|
44
|
+
independently.
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
import jax
|
|
48
|
+
import jax.numpy as jnp
|
|
49
|
+
from iactrace import Telescope, Camera
|
|
50
|
+
|
|
51
|
+
# Random key for Monte Carlo ray sampling.
|
|
52
|
+
key = jax.random.key(0)
|
|
53
|
+
|
|
54
|
+
# Load the telescope (optics + camera frame) and the camera (sensor layout).
|
|
55
|
+
telescope = Telescope.from_yaml("configs/HESS/CT3.yaml", n_samples=256, key=key)
|
|
56
|
+
camera = Camera.from_yaml("configs/HESS/HESS1U.yaml")
|
|
57
|
+
|
|
58
|
+
# Simulate an on-axis astronomical source (parallel light).
|
|
59
|
+
directions = jnp.array([[0.0, 0.0, -1.0]])
|
|
60
|
+
values = jnp.array([1.0])
|
|
61
|
+
|
|
62
|
+
# render() traces rays through the optics and returns a lazy ray bundle;
|
|
63
|
+
# camera.image() folds it into the pixel image.
|
|
64
|
+
ray_bundle = telescope.render(directions, values, source_type="parallel")
|
|
65
|
+
image = camera.image(ray_bundle)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
See the [Quick Start guide](https://gerritro.github.io/iactrace/getting_started/quickstart.html)
|
|
69
|
+
for point sources, off-axis geometry, and visualization.
|
|
70
|
+
|
|
71
|
+
## Documentation
|
|
72
|
+
|
|
73
|
+
Full documentation and examples are available at: **https://gerritro.github.io/iactrace/**
|
|
74
|
+
|
|
75
|
+
### Building Documentation Locally
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install -e ".[docs]"
|
|
79
|
+
cd docs && make html
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
BSD-3-Clause License - see [LICENSE](LICENSE) for details.
|
|
85
|
+
|
|
86
|
+
## Citation
|
|
87
|
+
|
|
88
|
+
If you use IACTrace in your research, please cite this repository.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import jax
|
|
2
|
+
|
|
3
|
+
from .camera import (
|
|
4
|
+
PMT,
|
|
5
|
+
Camera,
|
|
6
|
+
Concentrator,
|
|
7
|
+
ConstantQE,
|
|
8
|
+
DetectionChain,
|
|
9
|
+
HexagonalSensorGroup,
|
|
10
|
+
OkumuraCone,
|
|
11
|
+
PhotoDetector,
|
|
12
|
+
SensorGroup,
|
|
13
|
+
SquareSensorGroup,
|
|
14
|
+
WinstonCone,
|
|
15
|
+
)
|
|
16
|
+
from .core import LazyRayBundle, RayBundle, TraceResult, Trajectory
|
|
17
|
+
from .io import save_camera, save_telescope
|
|
18
|
+
from .telescope import Telescope
|
|
19
|
+
from .viz import (
|
|
20
|
+
add_trajectories,
|
|
21
|
+
show_camera,
|
|
22
|
+
show_image,
|
|
23
|
+
show_sensor_chain,
|
|
24
|
+
show_telescope,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
__version__ = "0.10.1"
|
|
28
|
+
|
|
29
|
+
jax.config.update("jax_default_matmul_precision", "highest")
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"Telescope",
|
|
33
|
+
"Camera",
|
|
34
|
+
"RayBundle",
|
|
35
|
+
"LazyRayBundle",
|
|
36
|
+
"Trajectory",
|
|
37
|
+
"TraceResult",
|
|
38
|
+
"Concentrator",
|
|
39
|
+
"WinstonCone",
|
|
40
|
+
"OkumuraCone",
|
|
41
|
+
"DetectionChain",
|
|
42
|
+
# Sensors
|
|
43
|
+
"SensorGroup",
|
|
44
|
+
"SquareSensorGroup",
|
|
45
|
+
"HexagonalSensorGroup",
|
|
46
|
+
# PhotoDetector
|
|
47
|
+
"PhotoDetector",
|
|
48
|
+
"ConstantQE",
|
|
49
|
+
"PMT",
|
|
50
|
+
# Visualization
|
|
51
|
+
"show_image",
|
|
52
|
+
"show_telescope",
|
|
53
|
+
"show_camera",
|
|
54
|
+
"show_sensor_chain",
|
|
55
|
+
"add_trajectories",
|
|
56
|
+
# I/O
|
|
57
|
+
"save_telescope",
|
|
58
|
+
"save_camera",
|
|
59
|
+
]
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
|
|
5
|
+
import equinox as eqx
|
|
6
|
+
import jax
|
|
7
|
+
import jax.numpy as jnp
|
|
8
|
+
from jax import Array
|
|
9
|
+
|
|
10
|
+
from ..core.intersections import (
|
|
11
|
+
intersect_conic,
|
|
12
|
+
intersect_plane,
|
|
13
|
+
newton_raphson_intersect,
|
|
14
|
+
)
|
|
15
|
+
from ..core.ray_bundle import LazyRayBundle, RayBundle
|
|
16
|
+
from ..core.surfaces import sag_raw
|
|
17
|
+
from ..core.transforms import euler_to_matrix
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class FocalSurfaceHits(eqx.Module):
|
|
21
|
+
"""Result of intersecting a :class:`RayBundle` with a :class:`FocalSurface`.
|
|
22
|
+
|
|
23
|
+
All arrays have leading dimension ``n_rays`` and are aligned with the input
|
|
24
|
+
bundle. Dead rays appear with ``hit_mask = False`` and meaningless values
|
|
25
|
+
in the position/direction arrays: always filter with ``hit_mask`` (or
|
|
26
|
+
:attr:`alive`) before using ``xy_local`` for plotting or statistics.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
xy_local: Tangent-plane coordinates at the hit, in the surface-local
|
|
30
|
+
frame (n_rays, 2).
|
|
31
|
+
z_local: Sag of the surface at the hit (n_rays,). Always 0 for a flat
|
|
32
|
+
focal plane.
|
|
33
|
+
t: Ray parameter at the hit; equals the world-frame distance travelled
|
|
34
|
+
from ``ray_bundle.origins`` to the surface (n_rays,).
|
|
35
|
+
hit_mask: Liveness at the surface (n_rays,): ``True`` for a ray that
|
|
36
|
+
was still alive on arrival **and** crossed this surface. This is
|
|
37
|
+
the input bundle's ``alive`` flag ANDed with a real intersection,
|
|
38
|
+
so a ray lost upstream (shadowed, off-aperture) is ``False`` here
|
|
39
|
+
even if its stale geometry would formally cross the surface.
|
|
40
|
+
directions_local: Ray directions in the surface-local frame (n_rays, 3).
|
|
41
|
+
Useful for chief-ray / angle-of-incidence analysis.
|
|
42
|
+
opl: Per-ray optical path length from the source wavefront to the
|
|
43
|
+
focal surface, in metres (n_rays,). Equals
|
|
44
|
+
``ray_bundle.path_length + t * ray_bundle.n``.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
xy_local: Array
|
|
48
|
+
z_local: Array
|
|
49
|
+
t: Array
|
|
50
|
+
hit_mask: Array
|
|
51
|
+
directions_local: Array
|
|
52
|
+
opl: Array
|
|
53
|
+
values: Array
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def alive(self) -> Array:
|
|
57
|
+
"""Rays that reached the surface carrying light (``hit_mask & values > 0``).
|
|
58
|
+
|
|
59
|
+
The one-stop filter for photometry / spot diagrams: it excludes both
|
|
60
|
+
geometry loss (``hit_mask``) and rays attenuated to zero throughput
|
|
61
|
+
such as absorbed or totally-internally-reflected rays.
|
|
62
|
+
"""
|
|
63
|
+
return self.hit_mask & (self.values > 0)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class FocalSurface(eqx.Module):
|
|
67
|
+
"""Abstract focal surface attached to the camera frame.
|
|
68
|
+
|
|
69
|
+
Subclasses implement :meth:`_intersect_local`, which receives a single ray
|
|
70
|
+
already transformed into the surface-local frame (vertex at origin, +Z
|
|
71
|
+
along the surface normal at the vertex). The base :meth:`intersect`
|
|
72
|
+
handles materialisation of lazy bundles, the world->local transform, and
|
|
73
|
+
vectorisation across rays.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
position: Array # (3,) vertex of the surface in the camera/telescope frame
|
|
77
|
+
rotation: Array # (3,) Euler XYZ degrees; local +Z is the normal at the vertex
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def _intersect_local(
|
|
81
|
+
self,
|
|
82
|
+
o_local: Array,
|
|
83
|
+
d_local: Array,
|
|
84
|
+
) -> tuple[Array, Array, Array, Array]:
|
|
85
|
+
"""Intersect a single local-frame ray with the surface.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
o_local: Ray origin in the surface-local frame (3,).
|
|
89
|
+
d_local: Ray direction in the surface-local frame (3,), normalized.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
Tuple ``(t, xy_local, z_local, valid)`` where:
|
|
93
|
+
- ``t`` is the ray parameter (scalar),
|
|
94
|
+
- ``xy_local`` is the in-plane hit coordinate (2,),
|
|
95
|
+
- ``z_local`` is the surface sag at the hit (scalar),
|
|
96
|
+
- ``valid`` is a bool flag indicating a real intersection.
|
|
97
|
+
"""
|
|
98
|
+
raise NotImplementedError
|
|
99
|
+
|
|
100
|
+
def intersect(
|
|
101
|
+
self,
|
|
102
|
+
ray_bundle: RayBundle | LazyRayBundle,
|
|
103
|
+
) -> FocalSurfaceHits:
|
|
104
|
+
"""Intersect every ray in ``ray_bundle`` with this focal surface.
|
|
105
|
+
|
|
106
|
+
Lazy bundles are materialised first; per-ray analysis cannot fold.
|
|
107
|
+
"""
|
|
108
|
+
if isinstance(ray_bundle, LazyRayBundle):
|
|
109
|
+
ray_bundle = ray_bundle.materialise()
|
|
110
|
+
|
|
111
|
+
rot = euler_to_matrix(self.rotation)
|
|
112
|
+
|
|
113
|
+
o_local = (ray_bundle.origins - self.position) @ rot
|
|
114
|
+
d_local = ray_bundle.directions @ rot
|
|
115
|
+
|
|
116
|
+
t, xy_local, z_local, valid = jax.vmap(self._intersect_local)(
|
|
117
|
+
o_local,
|
|
118
|
+
d_local,
|
|
119
|
+
)
|
|
120
|
+
# Liveness at the surface: only rays that were still alive coming in
|
|
121
|
+
# and land on a real intersection.
|
|
122
|
+
hit_mask = ray_bundle.alive & valid & jnp.isfinite(t)
|
|
123
|
+
|
|
124
|
+
opl = ray_bundle.path_length + jnp.where(
|
|
125
|
+
hit_mask,
|
|
126
|
+
t * ray_bundle.n,
|
|
127
|
+
0.0,
|
|
128
|
+
)
|
|
129
|
+
return FocalSurfaceHits(
|
|
130
|
+
xy_local=xy_local,
|
|
131
|
+
z_local=z_local,
|
|
132
|
+
t=t,
|
|
133
|
+
hit_mask=hit_mask,
|
|
134
|
+
directions_local=d_local,
|
|
135
|
+
opl=opl,
|
|
136
|
+
values=ray_bundle.values,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class FlatFocalPlane(FocalSurface):
|
|
141
|
+
"""A flat focal plane.
|
|
142
|
+
|
|
143
|
+
The plane passes through ``position`` with its normal aligned with the
|
|
144
|
+
local +Z axis defined by ``rotation``. This is the same model
|
|
145
|
+
:func:`intersect_sensor` uses for sensors, just without pixel binning.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
def __init__(
|
|
149
|
+
self,
|
|
150
|
+
position: Array | None = None,
|
|
151
|
+
rotation: Array | None = None,
|
|
152
|
+
) -> None:
|
|
153
|
+
self.position = jnp.zeros(3) if position is None else jnp.asarray(position, dtype=float)
|
|
154
|
+
self.rotation = jnp.zeros(3) if rotation is None else jnp.asarray(rotation, dtype=float)
|
|
155
|
+
|
|
156
|
+
def _intersect_local(self, o_local, d_local):
|
|
157
|
+
center = jnp.zeros(3)
|
|
158
|
+
rot = jnp.eye(3)
|
|
159
|
+
xy, t = intersect_plane(o_local, d_local, center, rot)
|
|
160
|
+
valid = t < 1e10
|
|
161
|
+
z = jnp.zeros((), dtype=xy.dtype)
|
|
162
|
+
return t, xy, z, valid
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
class AsphericFocalSurface(FocalSurface):
|
|
166
|
+
"""A rotationally-symmetric aspheric focal surface.
|
|
167
|
+
|
|
168
|
+
Parameterisation matches :class:`AsphericSurfaceGroup`; the surface sag is
|
|
169
|
+
|
|
170
|
+
z(r) = c r^2 / (1 + sqrt(1 - (1 + k) c^2 r^2))
|
|
171
|
+
+ sum_i a_i r^(2i+4)
|
|
172
|
+
|
|
173
|
+
with ``r^2 = x^2 + y^2`` in the local frame, i.e. ``aspherics[i]``
|
|
174
|
+
multiplies ``r^(2i + 4)`` -- the polynomial starts at ``r^4``, since an
|
|
175
|
+
``r^2`` term would be degenerate with ``curvature``. The vertex sits at
|
|
176
|
+
``position`` and the optical axis points along the local +Z (set by
|
|
177
|
+
``rotation``). Pure conics are recovered with ``aspherics=None``;
|
|
178
|
+
``curvature = 0`` reduces to a flat plane (Newton converges in one step
|
|
179
|
+
from the closed-form conic init).
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
curvature: Array
|
|
183
|
+
conic: Array
|
|
184
|
+
aspherics: Array
|
|
185
|
+
|
|
186
|
+
def __init__(
|
|
187
|
+
self,
|
|
188
|
+
position: Array | None = None,
|
|
189
|
+
rotation: Array | None = None,
|
|
190
|
+
*,
|
|
191
|
+
curvature: float | Array = 0.0,
|
|
192
|
+
conic: float | Array = 0.0,
|
|
193
|
+
aspherics: Array | None = None,
|
|
194
|
+
) -> None:
|
|
195
|
+
self.position = jnp.zeros(3) if position is None else jnp.asarray(position, dtype=float)
|
|
196
|
+
self.rotation = jnp.zeros(3) if rotation is None else jnp.asarray(rotation, dtype=float)
|
|
197
|
+
self.curvature = jnp.asarray(curvature, dtype=float)
|
|
198
|
+
self.conic = jnp.asarray(conic, dtype=float)
|
|
199
|
+
self.aspherics = (
|
|
200
|
+
jnp.zeros((0,)) if aspherics is None else jnp.asarray(aspherics, dtype=float)
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
def _intersect_local(self, o_local, d_local):
|
|
204
|
+
c = self.curvature
|
|
205
|
+
k = self.conic
|
|
206
|
+
a = self.aspherics
|
|
207
|
+
|
|
208
|
+
t_init = intersect_conic(o_local, d_local, c, k)
|
|
209
|
+
t, hit_xy, valid = newton_raphson_intersect(
|
|
210
|
+
lambda x, y: sag_raw(x, y, c, k, a),
|
|
211
|
+
o_local,
|
|
212
|
+
d_local,
|
|
213
|
+
t_init,
|
|
214
|
+
)
|
|
215
|
+
z = sag_raw(hit_xy[0], hit_xy[1], c, k, a)
|
|
216
|
+
return t, hit_xy, z, valid
|