obdeect 0.7.0__tar.gz → 0.7.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.
Files changed (103) hide show
  1. {obdeect-0.7.0 → obdeect-0.7.1}/AGENTS.md +2 -3
  2. {obdeect-0.7.0 → obdeect-0.7.1}/PKG-INFO +3 -1
  3. {obdeect-0.7.0 → obdeect-0.7.1}/README.md +2 -0
  4. obdeect-0.7.1/docs/changes/31.bugfix.md +1 -0
  5. {obdeect-0.7.0 → obdeect-0.7.1}/docs/optical-models.md +5 -6
  6. {obdeect-0.7.0 → obdeect-0.7.1}/python/obdeect/compiler.py +8 -15
  7. {obdeect-0.7.0 → obdeect-0.7.1}/python/tests/test_axes_offset.py +29 -3
  8. {obdeect-0.7.0 → obdeect-0.7.1}/.clang-format +0 -0
  9. {obdeect-0.7.0 → obdeect-0.7.1}/.clang-tidy +0 -0
  10. {obdeect-0.7.0 → obdeect-0.7.1}/.editorconfig +0 -0
  11. {obdeect-0.7.0 → obdeect-0.7.1}/.gitattributes +0 -0
  12. {obdeect-0.7.0 → obdeect-0.7.1}/.github/workflows/ci.yml +0 -0
  13. {obdeect-0.7.0 → obdeect-0.7.1}/.github/workflows/docs.yml +0 -0
  14. {obdeect-0.7.0 → obdeect-0.7.1}/.github/workflows/pypi.yml +0 -0
  15. {obdeect-0.7.0 → obdeect-0.7.1}/.github/workflows/tests.yml +0 -0
  16. {obdeect-0.7.0 → obdeect-0.7.1}/.gitignore +0 -0
  17. {obdeect-0.7.0 → obdeect-0.7.1}/.pre-commit-config.yaml +0 -0
  18. {obdeect-0.7.0 → obdeect-0.7.1}/CHANGELOG.md +0 -0
  19. {obdeect-0.7.0 → obdeect-0.7.1}/CITATION.cff +0 -0
  20. {obdeect-0.7.0 → obdeect-0.7.1}/CMakeLists.txt +0 -0
  21. {obdeect-0.7.0 → obdeect-0.7.1}/CMakePresets.json +0 -0
  22. {obdeect-0.7.0 → obdeect-0.7.1}/LICENSE +0 -0
  23. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/bench/bench_trace.cpp +0 -0
  24. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/bench/replay.cpp +0 -0
  25. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/bench/replay_compare.hpp +0 -0
  26. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/bindings/README.md +0 -0
  27. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/bindings/core.cpp +0 -0
  28. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/bounds.hpp +0 -0
  29. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/bunches.hpp +0 -0
  30. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/json.hpp +0 -0
  31. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/model.hpp +0 -0
  32. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/model_json.hpp +0 -0
  33. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/obscurers.hpp +0 -0
  34. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/random.hpp +0 -0
  35. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/source.hpp +0 -0
  36. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/surfaces.hpp +0 -0
  37. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/table.hpp +0 -0
  38. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/trace.hpp +0 -0
  39. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/include/obdeect/vec.hpp +0 -0
  40. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/tests/check.hpp +0 -0
  41. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/tests/data/make_polynomial_reference.py +0 -0
  42. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/tests/legacy_asphere.hpp +0 -0
  43. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/tests/test_asphere_steps.cpp +0 -0
  44. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/tests/test_bounds.cpp +0 -0
  45. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/tests/test_kernels.cpp +0 -0
  46. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/tests/test_model_json.cpp +0 -0
  47. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/tests/test_replay_compare.cpp +0 -0
  48. {obdeect-0.7.0 → obdeect-0.7.1}/cpp/tests/test_trace.cpp +0 -0
  49. {obdeect-0.7.0 → obdeect-0.7.1}/docs/VALIDATION.md +0 -0
  50. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/figures/baffle_area.png +0 -0
  51. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/figures/mst_shadowing.png +0 -0
  52. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/figures/telescope_lst.png +0 -0
  53. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/figures/telescope_mst_flashcam.png +0 -0
  54. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/figures/telescope_mst_nectarcam.png +0 -0
  55. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/figures/telescope_sct.png +0 -0
  56. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/figures/telescope_sst.png +0 -0
  57. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/figures/telescopes-provenance.json +0 -0
  58. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/figures/validation_psf_area.png +0 -0
  59. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/images/obdeect_21ratio_large.jpg +0 -0
  60. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/images/obdeect_21ratio_small.jpg +0 -0
  61. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/images/obdeect_large.jpg +0 -0
  62. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/images/obdeect_logo.jpg +0 -0
  63. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/images/obdeect_small.jpg +0 -0
  64. {obdeect-0.7.0 → obdeect-0.7.1}/docs/_static/obdeect.css +0 -0
  65. {obdeect-0.7.0 → obdeect-0.7.1}/docs/air-shower-photons.md +0 -0
  66. {obdeect-0.7.0 → obdeect-0.7.1}/docs/api.md +0 -0
  67. {obdeect-0.7.0 → obdeect-0.7.1}/docs/batch-tracing.md +0 -0
  68. {obdeect-0.7.0 → obdeect-0.7.1}/docs/build_versions.py +0 -0
  69. {obdeect-0.7.0 → obdeect-0.7.1}/docs/changelog.md +0 -0
  70. {obdeect-0.7.0 → obdeect-0.7.1}/docs/changes/.gitkeep +0 -0
  71. {obdeect-0.7.0 → obdeect-0.7.1}/docs/changes/27.feature.md +0 -0
  72. {obdeect-0.7.0 → obdeect-0.7.1}/docs/changes/28.maintenance.md +0 -0
  73. {obdeect-0.7.0 → obdeect-0.7.1}/docs/conf.py +0 -0
  74. {obdeect-0.7.0 → obdeect-0.7.1}/docs/figures/make_figures.py +0 -0
  75. {obdeect-0.7.0 → obdeect-0.7.1}/docs/figures/telescopes.yml +0 -0
  76. {obdeect-0.7.0 → obdeect-0.7.1}/docs/index.md +0 -0
  77. {obdeect-0.7.0 → obdeect-0.7.1}/docs/installation.md +0 -0
  78. {obdeect-0.7.0 → obdeect-0.7.1}/docs/light-sources.md +0 -0
  79. {obdeect-0.7.0 → obdeect-0.7.1}/docs/limitations.md +0 -0
  80. {obdeect-0.7.0 → obdeect-0.7.1}/docs/optical-elements.md +0 -0
  81. {obdeect-0.7.0 → obdeect-0.7.1}/docs/optics.md +0 -0
  82. {obdeect-0.7.0 → obdeect-0.7.1}/docs/python-guide.md +0 -0
  83. {obdeect-0.7.0 → obdeect-0.7.1}/docs/shadowing.md +0 -0
  84. {obdeect-0.7.0 → obdeect-0.7.1}/docs/telescopes.md +0 -0
  85. {obdeect-0.7.0 → obdeect-0.7.1}/docs/usage.md +0 -0
  86. {obdeect-0.7.0 → obdeect-0.7.1}/docs/user-guide.md +0 -0
  87. {obdeect-0.7.0 → obdeect-0.7.1}/pyproject.toml +0 -0
  88. {obdeect-0.7.0 → obdeect-0.7.1}/python/obdeect/__init__.py +0 -0
  89. {obdeect-0.7.0 → obdeect-0.7.1}/python/obdeect/cli.py +0 -0
  90. {obdeect-0.7.0 → obdeect-0.7.1}/python/obdeect/formats.py +0 -0
  91. {obdeect-0.7.0 → obdeect-0.7.1}/python/obdeect/model.py +0 -0
  92. {obdeect-0.7.0 → obdeect-0.7.1}/python/obdeect/plot.py +0 -0
  93. {obdeect-0.7.0 → obdeect-0.7.1}/python/obdeect/propagation.py +0 -0
  94. {obdeect-0.7.0 → obdeect-0.7.1}/python/obdeect/psf.py +0 -0
  95. {obdeect-0.7.0 → obdeect-0.7.1}/python/obdeect/repository.py +0 -0
  96. {obdeect-0.7.0 → obdeect-0.7.1}/python/obdeect/simtel_tables.py +0 -0
  97. {obdeect-0.7.0 → obdeect-0.7.1}/python/tests/telescopes.py +0 -0
  98. {obdeect-0.7.0 → obdeect-0.7.1}/python/tests/test_bunches.py +0 -0
  99. {obdeect-0.7.0 → obdeect-0.7.1}/python/tests/test_compiler.py +0 -0
  100. {obdeect-0.7.0 → obdeect-0.7.1}/python/tests/test_docs_versions.py +0 -0
  101. {obdeect-0.7.0 → obdeect-0.7.1}/python/tests/test_formats.py +0 -0
  102. {obdeect-0.7.0 → obdeect-0.7.1}/python/tests/test_model.py +0 -0
  103. {obdeect-0.7.0 → obdeect-0.7.1}/python/tests/test_propagation.py +0 -0
@@ -51,9 +51,8 @@ scope. Read the relevant code and tests first; use `docs/VALIDATION.md` and
51
51
  ## Changes and validation
52
52
 
53
53
  - Make the narrowest complete change and preserve unrelated work.
54
- - Do not add anything under `docs/changes/` for current work. Leave that
55
- directory untouched unless the user explicitly asks for a changelog or
56
- release-note change.
54
+ - Add an appropriate Towncrier fragment under `docs/changes/` for changes
55
+ that should appear in the release notes.
57
56
  - Add or update a focused test for every behavioural change. Establish analytic
58
57
  kernel and primitive tests before optical-model, cross-tool, or performance tests.
59
58
  - Preserve results across photon ordering, blocks, and thread counts. Record
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: obdeect
3
- Version: 0.7.0
3
+ Version: 0.7.1
4
4
  Summary: Ray tracing for imaging atmospheric Cherenkov telescopes
5
5
  Author: obdeect contributors
6
6
  License: BSD 3-Clause License
@@ -64,6 +64,8 @@ Description-Content-Type: text/markdown
64
64
  <img src="docs/_static/images/obdeect_21ratio_small.jpg" alt="Obdeect optical ray tracing for imaging atmospheric Cherenkov telescopes" width="800">
65
65
  </p>
66
66
 
67
+ [![DOI](https://zenodo.org/badge/1376945125.svg)](https://doi.org/10.5281/zenodo.23942573)
68
+
67
69
  `obdeect` is a C++20 optical ray tracer for imaging atmospheric Cherenkov
68
70
  telescopes. The C++ core uses only the standard library. Python compiles
69
71
  optical models from the simulation models, traces photons, and plots results;
@@ -4,6 +4,8 @@
4
4
  <img src="docs/_static/images/obdeect_21ratio_small.jpg" alt="Obdeect optical ray tracing for imaging atmospheric Cherenkov telescopes" width="800">
5
5
  </p>
6
6
 
7
+ [![DOI](https://zenodo.org/badge/1376945125.svg)](https://doi.org/10.5281/zenodo.23942573)
8
+
7
9
  `obdeect` is a C++20 optical ray tracer for imaging atmospheric Cherenkov
8
10
  telescopes. The C++ core uses only the standard library. Python compiles
9
11
  optical models from the simulation models, traces photons, and plots results;
@@ -0,0 +1 @@
1
+ Accept two components for axes offsets (as expected by the simulation models).
@@ -31,15 +31,14 @@ path of the first error. Surface and obscurer ids are unique.
31
31
 
32
32
  Some telescopes have the optical origin displaced from the rotation axes. The
33
33
  sim_telarray `axes_offsets` record gives the horizontal offset between the
34
- azimuth and altitude axes, in the record's units (cm in sim_telarray). The
35
- first component is that offset; a non-zero second component (displacement
36
- perpendicular to the optical axis) is rejected at compile time.
34
+ azimuth and altitude axes, followed by the altitude-to-optical-axis offset,
35
+ in the record's units (cm in sim_telarray).
37
36
 
38
37
  The compiler stores the offset in the telescope frame, in metres, as
39
38
  `optics.axes_offset_m` = [x, y, z]. The key is written only when the offset is
40
- non-zero, and it needs the zenith angle of the compilation. The offset is
41
- `(O cos(theta), 0, O sin(theta))` for an along-axis offset `O` at zenith
42
- `theta`. The loader requires three numbers.
39
+ non-zero, and it needs the zenith angle of the compilation. For components
40
+ `(O, T)` and zenith `theta`, the offset is
41
+ `(O cos(theta) - T, 0, O sin(theta))`. The loader requires three numbers.
43
42
 
44
43
  The offset is applied only by `trace_bunches`, which subtracts it from its frame
45
44
  offset (see [Batch tracing](batch-tracing.md)). `trace`, `star_photons` and the
@@ -170,13 +170,11 @@ def compile_model(
170
170
  def _axes_offset_m(p: _Parameters, zenith_deg: float | None) -> list[float]:
171
171
  """Offset of the optical origin from the telescope sphere centre, telescope frame [m].
172
172
 
173
- The ``axes_offsets`` record (sim_telarray ``axes_offset``, cm) holds the horizontal offset
174
- of the altitude axis from the azimuth axis, measured along x of the azimuth-rotated frame,
175
- and the displacement perpendicular to the optical axis, which must be zero. A photon at
176
- ground point P is then at R (P - O) in the telescope frame, where R maps the ground frame to
177
- the telescope frame. R O does not depend on the azimuth, so the offset is stored in the
178
- telescope frame at the compiled zenith angle: (O cos(theta), 0, O sin(theta)). Zero when the
179
- record is absent or zero; a non-zero offset needs the zenith angle.
173
+ The ``axes_offsets`` record (sim_telarray ``axes_offset``) holds the horizontal offset
174
+ between the azimuth and altitude axes followed by the altitude-to-optical-axis offset.
175
+ The first component transforms to (along*cos(theta), 0, along*sin(theta)); the second is
176
+ subtracted along telescope-frame x. Zero when the record is absent or zero; a non-zero
177
+ offset needs the zenith angle.
180
178
  """
181
179
  if "axes_offsets" not in p:
182
180
  return [0.0, 0.0, 0.0]
@@ -184,22 +182,17 @@ def _axes_offset_m(p: _Parameters, zenith_deg: float | None) -> list[float]:
184
182
  if len(values) > 2:
185
183
  raise ModelError("axes_offsets has more than two components")
186
184
  along = number(values[0], "axes_offsets") * length_unit_m(units[0], "axes_offsets")
187
- perpendicular = (
185
+ transverse = (
188
186
  number(values[1], "axes_offsets") * length_unit_m(units[1], "axes_offsets")
189
187
  if len(values) == 2
190
188
  else 0.0
191
189
  )
192
- if perpendicular != 0.0:
193
- raise NotImplementedError(
194
- "a non-zero second axes_offsets component (displacement perpendicular to the optical "
195
- "axis) is not supported"
196
- )
197
- if along == 0.0:
190
+ if along == 0.0 and transverse == 0.0:
198
191
  return [0.0, 0.0, 0.0]
199
192
  if zenith_deg is None:
200
193
  raise ModelError("axes_offsets needs the telescope zenith angle")
201
194
  theta = math.radians(zenith_deg)
202
- return [along * math.cos(theta), 0.0, along * math.sin(theta)]
195
+ return [along * math.cos(theta) - transverse, 0.0, along * math.sin(theta)]
203
196
 
204
197
 
205
198
  def _sampling(p: _Parameters, optics: dict[str, Any]) -> dict[str, float]:
@@ -116,9 +116,35 @@ def test_axes_offset_follows_the_zenith_angle():
116
116
  assert model.axes_offset_m == pytest.approx((1.6, 0.0, 0.0))
117
117
 
118
118
 
119
- def test_non_zero_perpendicular_component_is_not_supported():
120
- with pytest.raises(NotImplementedError, match="perpendicular"):
121
- compiled([OFFSET_CM, 10.0])
119
+ def test_second_axes_offset_component_is_transverse_to_the_optical_axis():
120
+ model = compiled([OFFSET_CM, 10.0])
121
+ assert model.axes_offset_m == pytest.approx((
122
+ OFFSET_CM / 100 * COS - 0.1,
123
+ 0.0,
124
+ OFFSET_CM / 100 * SIN,
125
+ ))
126
+
127
+
128
+ def test_equal_offsets_align_optical_and_azimuth_axes_at_zenith():
129
+ model = compiled([OFFSET_CM, OFFSET_CM], zenith_deg=0.0)
130
+ assert model.axes_offset_m == pytest.approx(ORIGIN)
131
+
132
+
133
+ def test_transverse_offset_matches_explicit_frame_transform(reference):
134
+ model = compiled([OFFSET_CM, 10.0])
135
+ bunches = ground_bunches(3000)
136
+ # sim_telarray translates by -O before rotation and +T in telescope x.
137
+ frame_offset = -ROTATION @ GROUND_OFFSET_M + np.array([0.1, 0.0, 0.0])
138
+ expected = trace_ground(reference, bunches, 30.0, offset_m=frame_offset)
139
+ actual = trace_ground(model, bunches, 30.0)
140
+ assert len(actual["weight"]) > 0
141
+ for name in ("bunch_index", "position_m", "direction", "time_ns", "weight"):
142
+ np.testing.assert_allclose(actual[name], expected[name], atol=1e-9)
143
+
144
+
145
+ def test_second_axes_offset_component_needs_the_zenith_angle():
146
+ with pytest.raises(ModelError, match="zenith"):
147
+ compiled([0.0, 10.0], zenith_deg=None)
122
148
 
123
149
 
124
150
  def test_axes_offset_needs_the_zenith_angle():
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes