lsurf 1.0.0__py3-none-any.whl

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 (180) hide show
  1. lsurf/__init__.py +471 -0
  2. lsurf/analysis/__init__.py +107 -0
  3. lsurf/analysis/healpix_utils.py +418 -0
  4. lsurf/analysis/sphere_viz.py +1280 -0
  5. lsurf/cli/__init__.py +48 -0
  6. lsurf/cli/build.py +398 -0
  7. lsurf/cli/config_schema.py +318 -0
  8. lsurf/cli/gui_cmd.py +76 -0
  9. lsurf/cli/interactive.py +850 -0
  10. lsurf/cli/main.py +81 -0
  11. lsurf/cli/run.py +806 -0
  12. lsurf/detectors/__init__.py +266 -0
  13. lsurf/detectors/analysis.py +289 -0
  14. lsurf/detectors/base.py +284 -0
  15. lsurf/detectors/constant_size_rings.py +485 -0
  16. lsurf/detectors/directional.py +45 -0
  17. lsurf/detectors/extended/__init__.py +73 -0
  18. lsurf/detectors/extended/local_sphere.py +353 -0
  19. lsurf/detectors/extended/recording_sphere.py +368 -0
  20. lsurf/detectors/planar.py +45 -0
  21. lsurf/detectors/protocol.py +187 -0
  22. lsurf/detectors/recording_spheres.py +63 -0
  23. lsurf/detectors/results.py +1140 -0
  24. lsurf/detectors/small/__init__.py +79 -0
  25. lsurf/detectors/small/directional.py +330 -0
  26. lsurf/detectors/small/planar.py +401 -0
  27. lsurf/detectors/small/spherical.py +450 -0
  28. lsurf/detectors/spherical.py +45 -0
  29. lsurf/geometry/__init__.py +199 -0
  30. lsurf/geometry/builder.py +478 -0
  31. lsurf/geometry/cell.py +228 -0
  32. lsurf/geometry/cell_geometry.py +247 -0
  33. lsurf/geometry/detector_arrays.py +1785 -0
  34. lsurf/geometry/geometry.py +222 -0
  35. lsurf/geometry/surface_analysis.py +375 -0
  36. lsurf/geometry/validation.py +91 -0
  37. lsurf/gui/__init__.py +51 -0
  38. lsurf/gui/app.py +903 -0
  39. lsurf/gui/core/__init__.py +39 -0
  40. lsurf/gui/core/scene.py +343 -0
  41. lsurf/gui/core/simulation.py +264 -0
  42. lsurf/gui/renderers/__init__.py +40 -0
  43. lsurf/gui/renderers/ray_renderer.py +353 -0
  44. lsurf/gui/renderers/source_renderer.py +505 -0
  45. lsurf/gui/renderers/surface_renderer.py +477 -0
  46. lsurf/gui/views/__init__.py +48 -0
  47. lsurf/gui/views/config_editor.py +3199 -0
  48. lsurf/gui/views/properties.py +257 -0
  49. lsurf/gui/views/results.py +291 -0
  50. lsurf/gui/views/scene_tree.py +180 -0
  51. lsurf/gui/views/viewport_3d.py +555 -0
  52. lsurf/gui/views/visualizations.py +712 -0
  53. lsurf/materials/__init__.py +169 -0
  54. lsurf/materials/base/__init__.py +64 -0
  55. lsurf/materials/base/full_inhomogeneous.py +208 -0
  56. lsurf/materials/base/grid_inhomogeneous.py +319 -0
  57. lsurf/materials/base/homogeneous.py +342 -0
  58. lsurf/materials/base/material_field.py +527 -0
  59. lsurf/materials/base/simple_inhomogeneous.py +418 -0
  60. lsurf/materials/base/spectral_inhomogeneous.py +497 -0
  61. lsurf/materials/implementations/__init__.py +120 -0
  62. lsurf/materials/implementations/data/alpha_values_typical_atmosphere_updated.txt +24 -0
  63. lsurf/materials/implementations/duct_atmosphere.py +390 -0
  64. lsurf/materials/implementations/exponential_atmosphere.py +435 -0
  65. lsurf/materials/implementations/gaussian_lens.py +120 -0
  66. lsurf/materials/implementations/interpolated_data.py +123 -0
  67. lsurf/materials/implementations/layered_atmosphere.py +134 -0
  68. lsurf/materials/implementations/linear_gradient.py +109 -0
  69. lsurf/materials/implementations/linsley_atmosphere.py +764 -0
  70. lsurf/materials/implementations/standard_materials.py +126 -0
  71. lsurf/materials/implementations/turbulent_atmosphere.py +135 -0
  72. lsurf/materials/implementations/us_standard_atmosphere.py +149 -0
  73. lsurf/materials/utils/__init__.py +77 -0
  74. lsurf/materials/utils/constants.py +45 -0
  75. lsurf/materials/utils/device_functions.py +117 -0
  76. lsurf/materials/utils/dispersion.py +160 -0
  77. lsurf/materials/utils/factories.py +142 -0
  78. lsurf/propagation/__init__.py +91 -0
  79. lsurf/propagation/detector_gpu.py +67 -0
  80. lsurf/propagation/gpu_device_rays.py +294 -0
  81. lsurf/propagation/kernels/__init__.py +175 -0
  82. lsurf/propagation/kernels/absorption/__init__.py +61 -0
  83. lsurf/propagation/kernels/absorption/grid.py +240 -0
  84. lsurf/propagation/kernels/absorption/simple.py +232 -0
  85. lsurf/propagation/kernels/absorption/spectral.py +410 -0
  86. lsurf/propagation/kernels/detection/__init__.py +64 -0
  87. lsurf/propagation/kernels/detection/protocol.py +102 -0
  88. lsurf/propagation/kernels/detection/spherical.py +255 -0
  89. lsurf/propagation/kernels/device_functions.py +790 -0
  90. lsurf/propagation/kernels/fresnel/__init__.py +64 -0
  91. lsurf/propagation/kernels/fresnel/protocol.py +97 -0
  92. lsurf/propagation/kernels/fresnel/standard.py +258 -0
  93. lsurf/propagation/kernels/intersection/__init__.py +79 -0
  94. lsurf/propagation/kernels/intersection/annular_plane.py +207 -0
  95. lsurf/propagation/kernels/intersection/bounded_plane.py +205 -0
  96. lsurf/propagation/kernels/intersection/plane.py +166 -0
  97. lsurf/propagation/kernels/intersection/protocol.py +95 -0
  98. lsurf/propagation/kernels/intersection/signed_distance.py +742 -0
  99. lsurf/propagation/kernels/intersection/sphere.py +190 -0
  100. lsurf/propagation/kernels/propagation/__init__.py +85 -0
  101. lsurf/propagation/kernels/propagation/grid.py +527 -0
  102. lsurf/propagation/kernels/propagation/protocol.py +105 -0
  103. lsurf/propagation/kernels/propagation/simple.py +460 -0
  104. lsurf/propagation/kernels/propagation/spectral.py +875 -0
  105. lsurf/propagation/kernels/registry.py +331 -0
  106. lsurf/propagation/kernels/surface/__init__.py +72 -0
  107. lsurf/propagation/kernels/surface/bisection.py +232 -0
  108. lsurf/propagation/kernels/surface/detection.py +402 -0
  109. lsurf/propagation/kernels/surface/reduction.py +166 -0
  110. lsurf/propagation/propagator_protocol.py +222 -0
  111. lsurf/propagation/propagators/__init__.py +101 -0
  112. lsurf/propagation/propagators/detector_handler.py +354 -0
  113. lsurf/propagation/propagators/factory.py +200 -0
  114. lsurf/propagation/propagators/fresnel_handler.py +305 -0
  115. lsurf/propagation/propagators/gpu_gradient.py +566 -0
  116. lsurf/propagation/propagators/gpu_surface_propagator.py +707 -0
  117. lsurf/propagation/propagators/gradient.py +429 -0
  118. lsurf/propagation/propagators/intersection_handler.py +327 -0
  119. lsurf/propagation/propagators/material_propagator.py +398 -0
  120. lsurf/propagation/propagators/signed_distance_handler.py +522 -0
  121. lsurf/propagation/propagators/spectral_gpu_gradient.py +553 -0
  122. lsurf/propagation/propagators/surface_interaction.py +616 -0
  123. lsurf/propagation/propagators/surface_propagator.py +719 -0
  124. lsurf/py.typed +1 -0
  125. lsurf/simulation/__init__.py +70 -0
  126. lsurf/simulation/config.py +164 -0
  127. lsurf/simulation/orchestrator.py +462 -0
  128. lsurf/simulation/result.py +299 -0
  129. lsurf/simulation/simulation.py +262 -0
  130. lsurf/sources/__init__.py +128 -0
  131. lsurf/sources/base.py +264 -0
  132. lsurf/sources/collimated.py +252 -0
  133. lsurf/sources/custom.py +409 -0
  134. lsurf/sources/diverging.py +228 -0
  135. lsurf/sources/gaussian.py +272 -0
  136. lsurf/sources/parallel_from_positions.py +197 -0
  137. lsurf/sources/point.py +172 -0
  138. lsurf/sources/uniform_diverging.py +258 -0
  139. lsurf/surfaces/__init__.py +184 -0
  140. lsurf/surfaces/cpu/__init__.py +50 -0
  141. lsurf/surfaces/cpu/curved_wave.py +463 -0
  142. lsurf/surfaces/cpu/gerstner_wave.py +381 -0
  143. lsurf/surfaces/cpu/wave_params.py +118 -0
  144. lsurf/surfaces/gpu/__init__.py +72 -0
  145. lsurf/surfaces/gpu/annular_plane.py +453 -0
  146. lsurf/surfaces/gpu/bounded_plane.py +390 -0
  147. lsurf/surfaces/gpu/curved_wave.py +483 -0
  148. lsurf/surfaces/gpu/gerstner_wave.py +377 -0
  149. lsurf/surfaces/gpu/multi_curved_wave.py +520 -0
  150. lsurf/surfaces/gpu/plane.py +299 -0
  151. lsurf/surfaces/gpu/recording_sphere.py +587 -0
  152. lsurf/surfaces/gpu/sphere.py +311 -0
  153. lsurf/surfaces/protocol.py +336 -0
  154. lsurf/surfaces/registry.py +373 -0
  155. lsurf/utilities/__init__.py +175 -0
  156. lsurf/utilities/detector_analysis.py +814 -0
  157. lsurf/utilities/fresnel.py +628 -0
  158. lsurf/utilities/interactions.py +1215 -0
  159. lsurf/utilities/propagation.py +602 -0
  160. lsurf/utilities/ray_data.py +532 -0
  161. lsurf/utilities/recording_sphere.py +745 -0
  162. lsurf/utilities/time_spread.py +463 -0
  163. lsurf/visualization/__init__.py +329 -0
  164. lsurf/visualization/absorption_plots.py +334 -0
  165. lsurf/visualization/atmospheric_plots.py +754 -0
  166. lsurf/visualization/common.py +348 -0
  167. lsurf/visualization/detector_plots.py +1350 -0
  168. lsurf/visualization/detector_sphere_plots.py +1173 -0
  169. lsurf/visualization/fresnel_plots.py +1061 -0
  170. lsurf/visualization/ocean_simulation_plots.py +999 -0
  171. lsurf/visualization/polarization_plots.py +916 -0
  172. lsurf/visualization/raytracing_plots.py +1521 -0
  173. lsurf/visualization/ring_detector_plots.py +1867 -0
  174. lsurf/visualization/time_spread_plots.py +531 -0
  175. lsurf-1.0.0.dist-info/METADATA +381 -0
  176. lsurf-1.0.0.dist-info/RECORD +180 -0
  177. lsurf-1.0.0.dist-info/WHEEL +5 -0
  178. lsurf-1.0.0.dist-info/entry_points.txt +2 -0
  179. lsurf-1.0.0.dist-info/licenses/LICENSE +32 -0
  180. lsurf-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,331 @@
1
+ # The Clear BSD License
2
+ #
3
+ # Copyright (c) 2026 Tobias Heibges
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted (subject to the limitations in the disclaimer
8
+ # below) provided that the following conditions are met:
9
+ #
10
+ # * Redistributions of source code must retain the above copyright notice,
11
+ # this list of conditions and the following disclaimer.
12
+ #
13
+ # * Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # * Neither the name of the copyright holder nor the names of its
18
+ # contributors may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
22
+ # THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
23
+ # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25
+ # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29
+ # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+
34
+ """
35
+ Kernel and Propagator Registry
36
+
37
+ Central registry for all kernel types in the ray tracing system:
38
+ - PropagationKernelID: Material propagation kernels
39
+ - IntersectionKernelID: Surface intersection kernels
40
+ - DetectionKernelID: Ray detection kernels
41
+ - FresnelKernelID: Fresnel reflection/refraction kernels
42
+ - PropagatorID: Propagator implementations
43
+
44
+ This module provides:
45
+ - Kernel ID enums for each category
46
+ - Registration decorator and lookup functions
47
+ - Unified registry for all kernel types
48
+ """
49
+
50
+ from enum import Enum, auto
51
+ from typing import Callable
52
+
53
+ __all__ = [
54
+ # Enums
55
+ "PropagationKernelID",
56
+ "IntersectionKernelID",
57
+ "DetectionKernelID",
58
+ "FresnelKernelID",
59
+ "PropagatorID",
60
+ "KernelID",
61
+ # Registration
62
+ "register_kernel",
63
+ "get_kernel",
64
+ "get_registered_kernels",
65
+ "list_kernels",
66
+ ]
67
+
68
+
69
+ # =============================================================================
70
+ # Kernel Identifier Enums
71
+ # =============================================================================
72
+
73
+
74
+ class PropagationKernelID(Enum):
75
+ """
76
+ Identifiers for material propagation kernels.
77
+
78
+ Each kernel corresponds to a specific integration method and material type.
79
+ Materials declare which kernels they support, and propagators use these
80
+ IDs to select the appropriate CUDA kernel.
81
+
82
+ Naming convention: {MATERIAL_TYPE}_{INTEGRATION_METHOD}
83
+
84
+ Examples
85
+ --------
86
+ >>> from lsurf.propagation.kernels import PropagationKernelID
87
+ >>> PropagationKernelID.SIMPLE_RK4
88
+ <PropagationKernelID.SIMPLE_RK4: ...>
89
+ """
90
+
91
+ # Simple inhomogeneous (1D LUT - altitude only)
92
+ SIMPLE_EULER = auto()
93
+ SIMPLE_RK4 = auto()
94
+
95
+ # Spectral inhomogeneous (2D LUT - altitude × wavelength)
96
+ SPECTRAL_EULER = auto()
97
+ SPECTRAL_RK4 = auto()
98
+ SPECTRAL_EULER_PERRAY = auto()
99
+ SPECTRAL_RK4_PERRAY = auto()
100
+
101
+ # Grid inhomogeneous (3D grid with trilinear interpolation)
102
+ GRID_EULER = auto()
103
+ GRID_RK4 = auto()
104
+
105
+
106
+ class IntersectionKernelID(Enum):
107
+ """
108
+ Identifiers for surface intersection kernels.
109
+
110
+ Each kernel corresponds to a specific geometry type and intersection method.
111
+ Surfaces declare which kernels they support for ray-surface intersection.
112
+
113
+ Note: Complex surfaces (waves, curved surfaces) use the generic signed
114
+ distance kernels which dispatch to device functions based on geometry_id.
115
+
116
+ Naming convention: {GEOMETRY_TYPE}_{METHOD}
117
+
118
+ Examples
119
+ --------
120
+ >>> from lsurf.propagation.kernels import IntersectionKernelID
121
+ >>> IntersectionKernelID.PLANE_ANALYTICAL
122
+ <IntersectionKernelID.PLANE_ANALYTICAL: ...>
123
+ """
124
+
125
+ # Analytical (GPU-capable, fast) - specific geometry kernels
126
+ PLANE_ANALYTICAL = auto() # Ray-plane intersection
127
+ SPHERE_ANALYTICAL = auto() # Ray-sphere intersection
128
+ BOUNDED_PLANE_ANALYTICAL = auto() # Ray-bounded-plane intersection
129
+ ANNULAR_PLANE_ANALYTICAL = auto() # Ray-annular-plane intersection
130
+
131
+ # Generic dispatch kernels (GPU-capable, all geometry types)
132
+ SIGNED_DISTANCE_GENERIC = auto() # Signed distance for any surface via geometry_id
133
+ SURFACE_NORMAL_GENERIC = auto() # Surface normal for any surface via geometry_id
134
+
135
+
136
+ class DetectionKernelID(Enum):
137
+ """
138
+ Identifiers for ray detection kernels.
139
+
140
+ Each kernel corresponds to a specific detector geometry.
141
+
142
+ Naming convention: {GEOMETRY_TYPE}_{VARIANT}
143
+
144
+ Examples
145
+ --------
146
+ >>> from lsurf.propagation.kernels import DetectionKernelID
147
+ >>> DetectionKernelID.SPHERICAL_SINGLE
148
+ <DetectionKernelID.SPHERICAL_SINGLE: ...>
149
+ """
150
+
151
+ SPHERICAL_SINGLE = auto() # Single spherical detector
152
+ SPHERICAL_MULTI = auto() # Multiple spherical detectors (scan mode)
153
+ PLANAR_SINGLE = auto() # Single planar detector
154
+
155
+
156
+ class FresnelKernelID(Enum):
157
+ """
158
+ Identifiers for Fresnel reflection/refraction kernels.
159
+
160
+ Each kernel implements a specific approach to computing Fresnel coefficients.
161
+
162
+ Examples
163
+ --------
164
+ >>> from lsurf.propagation.kernels import FresnelKernelID
165
+ >>> FresnelKernelID.STANDARD
166
+ <FresnelKernelID.STANDARD: ...>
167
+ """
168
+
169
+ STANDARD = auto() # Standard Fresnel equations (unpolarized)
170
+ POLARIZED = auto() # Polarization-aware Fresnel equations
171
+
172
+
173
+ class PropagatorID(Enum):
174
+ """
175
+ Identifiers for available propagator implementations.
176
+
177
+ Each propagator ID corresponds to a specific propagator class that handles
178
+ ray propagation through materials.
179
+
180
+ Examples
181
+ --------
182
+ >>> from lsurf.propagation.kernels import PropagatorID
183
+ >>> PropagatorID.GPU_GRADIENT
184
+ <PropagatorID.GPU_GRADIENT: ...>
185
+ """
186
+
187
+ CPU_GRADIENT = auto() # GradientPropagator (CPU, any material)
188
+ GPU_GRADIENT = auto() # GPUGradientPropagator (GPU, scalar wavelength)
189
+ GPU_SPECTRAL = auto() # SpectralGPUGradientPropagator (GPU, per-ray wavelength)
190
+ GPU_SURFACE = auto() # SurfacePropagator (GPU, with intersection detection)
191
+
192
+
193
+ # =============================================================================
194
+ # Type Aliases
195
+ # =============================================================================
196
+
197
+ # Union type for any kernel ID
198
+ KernelID = (
199
+ PropagationKernelID | IntersectionKernelID | DetectionKernelID | FresnelKernelID
200
+ )
201
+
202
+
203
+ # =============================================================================
204
+ # Kernel Registration System
205
+ # =============================================================================
206
+
207
+ # Global registry mapping kernel ID → kernel function
208
+ _KERNEL_REGISTRY: dict[KernelID, Callable] = {}
209
+
210
+
211
+ def register_kernel(kernel_id: KernelID):
212
+ """
213
+ Decorator to register a kernel function with the global registry.
214
+
215
+ Parameters
216
+ ----------
217
+ kernel_id : KernelID
218
+ The unique identifier for this kernel. Can be any kernel ID type:
219
+ PropagationKernelID, IntersectionKernelID, DetectionKernelID,
220
+ or FresnelKernelID.
221
+
222
+ Returns
223
+ -------
224
+ decorator
225
+ A decorator that registers the function and returns it unchanged.
226
+
227
+ Examples
228
+ --------
229
+ >>> @register_kernel(PropagationKernelID.SIMPLE_EULER)
230
+ ... def my_euler_kernel(positions, directions, ...):
231
+ ... ...
232
+
233
+ >>> @register_kernel(DetectionKernelID.SPHERICAL_SINGLE)
234
+ ... def my_detection_kernel(positions, directions, ...):
235
+ ... ...
236
+
237
+ Notes
238
+ -----
239
+ Registration happens at import time. The registered function can be
240
+ retrieved later using get_kernel().
241
+ """
242
+
243
+ def decorator(func: Callable) -> Callable:
244
+ if kernel_id in _KERNEL_REGISTRY:
245
+ existing = _KERNEL_REGISTRY[kernel_id]
246
+ raise ValueError(
247
+ f"Kernel {kernel_id} already registered by {existing.__name__}. "
248
+ f"Cannot register {func.__name__}."
249
+ )
250
+ _KERNEL_REGISTRY[kernel_id] = func
251
+ return func
252
+
253
+ return decorator
254
+
255
+
256
+ def get_kernel(kernel_id: KernelID) -> Callable:
257
+ """
258
+ Retrieve a registered kernel function by ID.
259
+
260
+ Parameters
261
+ ----------
262
+ kernel_id : KernelID
263
+ The identifier of the kernel to retrieve. Can be any kernel ID type.
264
+
265
+ Returns
266
+ -------
267
+ Callable
268
+ The registered kernel function.
269
+
270
+ Raises
271
+ ------
272
+ KeyError
273
+ If the kernel ID is not registered.
274
+
275
+ Examples
276
+ --------
277
+ >>> kernel = get_kernel(PropagationKernelID.SIMPLE_RK4)
278
+ >>> kernel(positions, directions, ...)
279
+
280
+ >>> detect = get_kernel(DetectionKernelID.SPHERICAL_SINGLE)
281
+ >>> detect(positions, directions, ...)
282
+ """
283
+ if kernel_id not in _KERNEL_REGISTRY:
284
+ registered = list(_KERNEL_REGISTRY.keys())
285
+ raise KeyError(
286
+ f"Kernel {kernel_id} not registered. " f"Available kernels: {registered}"
287
+ )
288
+ return _KERNEL_REGISTRY[kernel_id]
289
+
290
+
291
+ def get_registered_kernels() -> dict[KernelID, Callable]:
292
+ """
293
+ Return a copy of all registered kernels.
294
+
295
+ Returns
296
+ -------
297
+ dict
298
+ Dictionary mapping kernel IDs to kernel functions.
299
+ """
300
+ return _KERNEL_REGISTRY.copy()
301
+
302
+
303
+ def list_kernels(kernel_type: type[Enum] | None = None) -> list[KernelID]:
304
+ """
305
+ List all registered kernels, optionally filtered by type.
306
+
307
+ Parameters
308
+ ----------
309
+ kernel_type : type[Enum], optional
310
+ If provided, only return kernels of this enum type.
311
+ For example, PropagationKernelID to list only propagation kernels.
312
+
313
+ Returns
314
+ -------
315
+ list[KernelID]
316
+ List of registered kernel IDs.
317
+
318
+ Examples
319
+ --------
320
+ >>> # List all registered kernels
321
+ >>> all_kernels = list_kernels()
322
+
323
+ >>> # List only propagation kernels
324
+ >>> prop_kernels = list_kernels(PropagationKernelID)
325
+
326
+ >>> # List only detection kernels
327
+ >>> detect_kernels = list_kernels(DetectionKernelID)
328
+ """
329
+ if kernel_type is None:
330
+ return list(_KERNEL_REGISTRY.keys())
331
+ return [k for k in _KERNEL_REGISTRY.keys() if isinstance(k, kernel_type)]
@@ -0,0 +1,72 @@
1
+ # The Clear BSD License
2
+ #
3
+ # Copyright (c) 2026 Tobias Heibges
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted (subject to the limitations in the disclaimer
8
+ # below) provided that the following conditions are met:
9
+ #
10
+ # * Redistributions of source code must retain the above copyright notice,
11
+ # this list of conditions and the following disclaimer.
12
+ #
13
+ # * Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # * Neither the name of the copyright holder nor the names of its
18
+ # contributors may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
22
+ # THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
23
+ # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25
+ # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29
+ # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+
34
+ """
35
+ Surface Detection Kernels
36
+
37
+ GPU kernels for surface intersection detection, bisection refinement,
38
+ and status reduction. These kernels work with GPUDeviceRays to enable
39
+ GPU-resident surface propagation.
40
+
41
+ Kernels
42
+ -------
43
+ kernel_save_prev_positions
44
+ Save current positions before propagation step (for bisection)
45
+ kernel_detect_crossing
46
+ Check all surfaces for sign changes in signed distance
47
+ kernel_bisect_crossing
48
+ Bisection refinement for rays that crossed a surface
49
+ kernel_reduce_status
50
+ Count active rays and check for crossings
51
+ """
52
+
53
+ from .detection import (
54
+ kernel_save_prev_positions,
55
+ kernel_detect_crossing,
56
+ kernel_init_signed_distances,
57
+ kernel_compute_min_surface_distance,
58
+ kernel_compute_adaptive_steps,
59
+ )
60
+ from .bisection import kernel_bisect_crossing
61
+ from .reduction import kernel_reduce_status, kernel_reduce_status_shared
62
+
63
+ __all__ = [
64
+ "kernel_save_prev_positions",
65
+ "kernel_detect_crossing",
66
+ "kernel_init_signed_distances",
67
+ "kernel_compute_min_surface_distance",
68
+ "kernel_compute_adaptive_steps",
69
+ "kernel_bisect_crossing",
70
+ "kernel_reduce_status",
71
+ "kernel_reduce_status_shared",
72
+ ]
@@ -0,0 +1,232 @@
1
+ # The Clear BSD License
2
+ #
3
+ # Copyright (c) 2026 Tobias Heibges
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted (subject to the limitations in the disclaimer
8
+ # below) provided that the following conditions are met:
9
+ #
10
+ # * Redistributions of source code must retain the above copyright notice,
11
+ # this list of conditions and the following disclaimer.
12
+ #
13
+ # * Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # * Neither the name of the copyright holder nor the names of its
18
+ # contributors may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
22
+ # THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
23
+ # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25
+ # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29
+ # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+
34
+ """
35
+ Surface Bisection Refinement Kernel
36
+
37
+ GPU kernel for refining intersection points using bisection method
38
+ after a crossing has been detected.
39
+ """
40
+
41
+ import math
42
+
43
+ # GPU support is optional
44
+ try:
45
+ from numba import cuda
46
+
47
+ HAS_CUDA = cuda.is_available()
48
+ except ImportError:
49
+ HAS_CUDA = False
50
+
51
+ class _FakeCuda:
52
+ @staticmethod
53
+ def jit(*args, **kwargs):
54
+ def decorator(func):
55
+ return func
56
+
57
+ if args and callable(args[0]):
58
+ return args[0]
59
+ return decorator
60
+
61
+ @staticmethod
62
+ def grid(n):
63
+ return 0
64
+
65
+ cuda = _FakeCuda() # type: ignore[assignment]
66
+
67
+
68
+ # Import device functions for signed distance computation
69
+ from ..intersection.signed_distance import (
70
+ _device_plane_sd,
71
+ _device_sphere_sd,
72
+ _device_gerstner_sd,
73
+ _device_curved_wave_sd,
74
+ _device_multi_curved_wave_sd,
75
+ _device_annular_plane_sd,
76
+ )
77
+
78
+
79
+ @cuda.jit(device=True)
80
+ def _device_signed_distance_bisect(
81
+ x: float, y: float, z: float, geometry_id: int, params, param_offset: int
82
+ ) -> float:
83
+ """
84
+ Compute signed distance for bisection.
85
+
86
+ Same as _device_signed_distance but duplicated to avoid circular import.
87
+ """
88
+ if geometry_id == 1:
89
+ return _device_plane_sd(x, y, z, params[param_offset:])
90
+ elif geometry_id == 2:
91
+ return _device_sphere_sd(x, y, z, params[param_offset:])
92
+ elif geometry_id == 3:
93
+ return _device_gerstner_sd(x, y, z, params[param_offset:])
94
+ elif geometry_id == 4:
95
+ return _device_curved_wave_sd(x, y, z, params[param_offset:])
96
+ elif geometry_id == 5:
97
+ return _device_multi_curved_wave_sd(x, y, z, params[param_offset:])
98
+ elif geometry_id == 7:
99
+ return _device_annular_plane_sd(x, y, z, params[param_offset:])
100
+ else:
101
+ return math.inf
102
+
103
+
104
+ @cuda.jit
105
+ def kernel_bisect_crossing(
106
+ prev_positions, # (N, 3) positions before step
107
+ curr_positions, # (N, 3) positions after step
108
+ crossing_mask, # (N,) rays that crossed
109
+ hit_surface_idx, # (N,) which surface was hit
110
+ geometry_ids, # (S,) geometry ID per surface
111
+ surface_params, # (S * MAX_PARAMS,) all surface parameters
112
+ max_params, # int: MAX_SURFACE_PARAMS
113
+ hit_positions, # (N, 3) output: refined hit positions
114
+ active, # (N,) in/out: deactivate rays that hit
115
+ num_iterations, # int: bisection iterations (default 10)
116
+ geo_path=None, # (N,) geometric path length to correct
117
+ opt_path=None, # (N,) optical path length to correct
118
+ acc_time=None, # (N,) accumulated time to correct
119
+ step_sizes=None, # (N,) step size per ray for correction
120
+ n=1.0, # refractive index for correction
121
+ c=299792458.0, # speed of light for correction
122
+ ):
123
+ """
124
+ Bisection refinement for rays that crossed a surface.
125
+
126
+ For each ray that crossed a surface, use bisection to find
127
+ the exact intersection point between prev_positions and curr_positions.
128
+
129
+ Parameters
130
+ ----------
131
+ prev_positions : (N, 3) float32 device array
132
+ Ray positions before propagation step
133
+ curr_positions : (N, 3) float32 device array
134
+ Ray positions after propagation step
135
+ crossing_mask : (N,) bool device array
136
+ True for rays that crossed a surface
137
+ hit_surface_idx : (N,) int32 device array
138
+ Surface index that was crossed
139
+ geometry_ids : (S,) int32 device array
140
+ Geometry ID for each surface
141
+ surface_params : (S * MAX_PARAMS,) float32 device array
142
+ Concatenated surface parameters
143
+ max_params : int
144
+ MAX_SURFACE_PARAMS constant
145
+ hit_positions : (N, 3) float32 device array
146
+ Output: Refined intersection positions
147
+ active : (N,) bool device array
148
+ In/out: Rays are deactivated after intersection
149
+ num_iterations : int
150
+ Number of bisection iterations (typically 10)
151
+ """
152
+ idx = cuda.grid(1)
153
+ if idx >= curr_positions.shape[0] or not crossing_mask[idx]:
154
+ return
155
+
156
+ surf = hit_surface_idx[idx]
157
+ geo_id = geometry_ids[surf]
158
+ param_offset = surf * max_params
159
+
160
+ # Load endpoints
161
+ p0x = prev_positions[idx, 0]
162
+ p0y = prev_positions[idx, 1]
163
+ p0z = prev_positions[idx, 2]
164
+ p1x = curr_positions[idx, 0]
165
+ p1y = curr_positions[idx, 1]
166
+ p1z = curr_positions[idx, 2]
167
+
168
+ # Get signed distance at p0 to know which side is positive
169
+ sd_p0 = _device_signed_distance_bisect(
170
+ p0x, p0y, p0z, geo_id, surface_params, param_offset
171
+ )
172
+
173
+ # Bisection loop
174
+ for _ in range(num_iterations):
175
+ midx = (p0x + p1x) * 0.5
176
+ midy = (p0y + p1y) * 0.5
177
+ midz = (p0z + p1z) * 0.5
178
+
179
+ sd_mid = _device_signed_distance_bisect(
180
+ midx, midy, midz, geo_id, surface_params, param_offset
181
+ )
182
+
183
+ # Check for convergence
184
+ if abs(sd_mid) < 1e-6:
185
+ break
186
+
187
+ # Determine which half contains the crossing
188
+ if (sd_p0 >= 0) != (sd_mid >= 0):
189
+ # Crossing is in first half
190
+ p1x, p1y, p1z = midx, midy, midz
191
+ else:
192
+ # Crossing is in second half
193
+ p0x, p0y, p0z = midx, midy, midz
194
+ sd_p0 = sd_mid
195
+
196
+ # Store hit position (midpoint of final interval)
197
+ hit_x = (p0x + p1x) * 0.5
198
+ hit_y = (p0y + p1y) * 0.5
199
+ hit_z = (p0z + p1z) * 0.5
200
+ hit_positions[idx, 0] = hit_x
201
+ hit_positions[idx, 1] = hit_y
202
+ hit_positions[idx, 2] = hit_z
203
+
204
+ # Correct accumulated path/time for exact intersection distance
205
+ # (only if correction arrays are provided)
206
+ if geo_path is not None and step_sizes is not None:
207
+ # Compute actual distance traveled (prev_pos to hit_pos)
208
+ dx = hit_x - prev_positions[idx, 0]
209
+ dy = hit_y - prev_positions[idx, 1]
210
+ dz = hit_z - prev_positions[idx, 2]
211
+ actual_distance = math.sqrt(dx * dx + dy * dy + dz * dz)
212
+
213
+ # Compute excess distance (step already accumulated - actual)
214
+ excess_distance = step_sizes[idx] - actual_distance
215
+
216
+ # Remove excess from accumulated values
217
+ geo_path[idx] -= excess_distance
218
+ if opt_path is not None:
219
+ opt_path[idx] -= n * excess_distance
220
+ if acc_time is not None:
221
+ acc_time[idx] -= n * excess_distance / c
222
+
223
+ # Update current position to hit position
224
+ curr_positions[idx, 0] = hit_x
225
+ curr_positions[idx, 1] = hit_y
226
+ curr_positions[idx, 2] = hit_z
227
+
228
+ # Mark ray as inactive
229
+ active[idx] = False
230
+
231
+
232
+ __all__ = ["kernel_bisect_crossing"]