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,64 @@
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
+ Fresnel Kernels
36
+
37
+ CUDA kernels for GPU-accelerated Fresnel reflection/refraction calculations.
38
+ Computes reflection and transmission coefficients at material interfaces.
39
+
40
+ This module exports ONLY pure CUDA kernels and device functions.
41
+ GPU memory management is handled by the propagator layer (handlers).
42
+
43
+ Kernel Types
44
+ ------------
45
+ - STANDARD: Standard Fresnel equations for unpolarized light
46
+ - POLARIZED: Polarization-aware Fresnel equations (s and p separately)
47
+ """
48
+
49
+ from .protocol import FresnelKernelProtocol
50
+ from .standard import (
51
+ kernel_fresnel_standard,
52
+ kernel_fresnel_polarized,
53
+ HAS_CUDA,
54
+ )
55
+
56
+ __all__ = [
57
+ # Protocol
58
+ "FresnelKernelProtocol",
59
+ # Kernels (pure CUDA only)
60
+ "kernel_fresnel_standard",
61
+ "kernel_fresnel_polarized",
62
+ # CUDA availability
63
+ "HAS_CUDA",
64
+ ]
@@ -0,0 +1,97 @@
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
+ Fresnel Kernel Protocol
36
+
37
+ Defines the interface for Fresnel reflection/refraction kernels that
38
+ compute reflection and transmission coefficients at interfaces.
39
+ """
40
+
41
+ from __future__ import annotations
42
+
43
+ from typing import TYPE_CHECKING, Protocol, runtime_checkable
44
+
45
+ if TYPE_CHECKING:
46
+ from numba.cuda.devicearray import DeviceNDArray
47
+
48
+
49
+ @runtime_checkable
50
+ class FresnelKernelProtocol(Protocol):
51
+ """
52
+ Protocol for Fresnel reflection/refraction kernels.
53
+
54
+ Fresnel kernels compute reflection and transmission coefficients
55
+ at material interfaces based on incident angle, refractive indices,
56
+ and polarization state.
57
+
58
+ Common Parameters
59
+ -----------------
60
+ cos_theta_i : DeviceNDArray, shape (N,)
61
+ Cosine of incident angle (dot product of direction and normal)
62
+ n1 : DeviceNDArray or float
63
+ Refractive index of incident medium
64
+ n2 : DeviceNDArray or float
65
+ Refractive index of transmitted medium
66
+ reflectance_out : DeviceNDArray, shape (N,)
67
+ Output: reflection coefficient (0-1)
68
+ transmittance_out : DeviceNDArray, shape (N,)
69
+ Output: transmission coefficient (0-1)
70
+
71
+ Optional Parameters
72
+ -------------------
73
+ polarization : DeviceNDArray, shape (N,), optional
74
+ Polarization state per ray (0=unpolarized, 1=s, 2=p)
75
+
76
+ Notes
77
+ -----
78
+ Kernels are registered with the central registry using @register_kernel
79
+ decorator and FresnelKernelID enum values.
80
+
81
+ For unpolarized light, the kernel averages s and p polarizations.
82
+ Total internal reflection (TIR) is automatically handled when
83
+ sin(theta_t) > 1.
84
+ """
85
+
86
+ def __call__(
87
+ self,
88
+ cos_theta_i: DeviceNDArray,
89
+ n1: DeviceNDArray | float,
90
+ n2: DeviceNDArray | float,
91
+ reflectance_out: DeviceNDArray,
92
+ transmittance_out: DeviceNDArray,
93
+ *args,
94
+ **kwargs,
95
+ ) -> None:
96
+ """Execute the Fresnel kernel."""
97
+ ...
@@ -0,0 +1,258 @@
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
+ GPU-Accelerated Fresnel Equations
36
+
37
+ Computes reflection and transmission coefficients at interfaces between
38
+ different media using Fresnel equations.
39
+
40
+ This module contains ONLY pure CUDA kernels and device functions - no GPU memory
41
+ management. The propagator layer is responsible for cuda.to_device() and copy_to_host().
42
+
43
+ References
44
+ ----------
45
+ .. [1] Born, M., & Wolf, E. (1999). Principles of Optics (7th ed.).
46
+ Cambridge University Press. Chapter 1.5.
47
+ .. [2] Hecht, E. (2017). Optics (5th ed.). Pearson. Chapter 4.
48
+ """
49
+
50
+ import math
51
+
52
+ from ..registry import FresnelKernelID, register_kernel
53
+
54
+ # GPU support is optional
55
+ try:
56
+ from numba import cuda
57
+
58
+ HAS_CUDA = True
59
+ except ImportError:
60
+
61
+ class _FakeCuda:
62
+ """Fake cuda module for when numba is not installed."""
63
+
64
+ @staticmethod
65
+ def jit(*args, **kwargs):
66
+ """Return a no-op decorator."""
67
+
68
+ def decorator(func):
69
+ return func
70
+
71
+ if args and callable(args[0]):
72
+ return args[0]
73
+ return decorator
74
+
75
+ @staticmethod
76
+ def is_available():
77
+ return False
78
+
79
+ @staticmethod
80
+ def grid(n):
81
+ return 0
82
+
83
+ @staticmethod
84
+ def synchronize():
85
+ pass
86
+
87
+ cuda = _FakeCuda() # type: ignore[assignment]
88
+ HAS_CUDA = False
89
+
90
+
91
+ # =============================================================================
92
+ # CUDA Device Functions
93
+ # =============================================================================
94
+
95
+
96
+ @cuda.jit(device=True)
97
+ def _device_fresnel_rs_rp(
98
+ cos_theta_i: float,
99
+ n1: float,
100
+ n2: float,
101
+ ) -> tuple[float, float, float, bool]:
102
+ """
103
+ Compute Fresnel amplitude reflection coefficients r_s and r_p.
104
+
105
+ Parameters
106
+ ----------
107
+ cos_theta_i : float
108
+ Cosine of incident angle
109
+ n1 : float
110
+ Refractive index of incident medium
111
+ n2 : float
112
+ Refractive index of transmitted medium
113
+
114
+ Returns
115
+ -------
116
+ r_s : float
117
+ s-polarization amplitude reflection coefficient
118
+ r_p : float
119
+ p-polarization amplitude reflection coefficient
120
+ cos_theta_t : float
121
+ Cosine of transmitted angle
122
+ tir : bool
123
+ True if total internal reflection
124
+ """
125
+ # Snell's law: n1*sin(theta_i) = n2*sin(theta_t)
126
+ n_ratio = n1 / n2
127
+ sin_theta_i_sq = 1.0 - cos_theta_i * cos_theta_i
128
+ sin_theta_t_sq = n_ratio * n_ratio * sin_theta_i_sq
129
+
130
+ # Check for total internal reflection
131
+ if sin_theta_t_sq > 1.0:
132
+ return 1.0, 1.0, 0.0, True
133
+
134
+ cos_theta_t = math.sqrt(1.0 - sin_theta_t_sq)
135
+
136
+ # Fresnel equations for amplitude coefficients
137
+ # s-polarization (TE)
138
+ r_s_num = n1 * cos_theta_i - n2 * cos_theta_t
139
+ r_s_den = n1 * cos_theta_i + n2 * cos_theta_t
140
+ r_s = r_s_num / (r_s_den + 1e-10)
141
+
142
+ # p-polarization (TM)
143
+ r_p_num = n2 * cos_theta_i - n1 * cos_theta_t
144
+ r_p_den = n2 * cos_theta_i + n1 * cos_theta_t
145
+ r_p = r_p_num / (r_p_den + 1e-10)
146
+
147
+ return r_s, r_p, cos_theta_t, False
148
+
149
+
150
+ # =============================================================================
151
+ # CUDA Kernels
152
+ # =============================================================================
153
+
154
+
155
+ @register_kernel(FresnelKernelID.STANDARD)
156
+ @cuda.jit
157
+ def kernel_fresnel_standard(
158
+ cos_theta_i, # (N,) cosine of incident angles
159
+ n1, # (N,) or scalar: refractive index of incident medium
160
+ n2, # (N,) or scalar: refractive index of transmitted medium
161
+ reflectance_out, # (N,) output: reflection coefficient
162
+ transmittance_out, # (N,) output: transmission coefficient
163
+ n1_is_scalar, # bool: whether n1 is scalar
164
+ n2_is_scalar, # bool: whether n2 is scalar
165
+ ):
166
+ """
167
+ CUDA kernel for standard Fresnel equations (unpolarized light).
168
+
169
+ Computes reflection and transmission coefficients by averaging
170
+ s and p polarizations.
171
+ """
172
+ idx = cuda.grid(1)
173
+ if idx >= cos_theta_i.shape[0]:
174
+ return
175
+
176
+ cti = cos_theta_i[idx]
177
+ if n1_is_scalar:
178
+ ni1 = n1[0]
179
+ else:
180
+ ni1 = n1[idx]
181
+ if n2_is_scalar:
182
+ ni2 = n2[0]
183
+ else:
184
+ ni2 = n2[idx]
185
+
186
+ r_s, r_p, cos_theta_t, tir = _device_fresnel_rs_rp(cti, ni1, ni2)
187
+
188
+ if tir:
189
+ reflectance_out[idx] = 1.0
190
+ transmittance_out[idx] = 0.0
191
+ else:
192
+ # Intensity reflection coefficients
193
+ R_s = r_s * r_s
194
+ R_p = r_p * r_p
195
+
196
+ # Average for unpolarized light
197
+ R = 0.5 * (R_s + R_p)
198
+ T = 1.0 - R
199
+
200
+ reflectance_out[idx] = R
201
+ transmittance_out[idx] = T
202
+
203
+
204
+ @register_kernel(FresnelKernelID.POLARIZED)
205
+ @cuda.jit
206
+ def kernel_fresnel_polarized(
207
+ cos_theta_i, # (N,) cosine of incident angles
208
+ n1, # (N,) or scalar: refractive index of incident medium
209
+ n2, # (N,) or scalar: refractive index of transmitted medium
210
+ polarization, # (N,) polarization state: 0=unpolarized, 1=s, 2=p
211
+ reflectance_out, # (N,) output: reflection coefficient
212
+ transmittance_out, # (N,) output: transmission coefficient
213
+ n1_is_scalar, # bool: whether n1 is scalar
214
+ n2_is_scalar, # bool: whether n2 is scalar
215
+ ):
216
+ """
217
+ CUDA kernel for polarization-aware Fresnel equations.
218
+
219
+ Computes reflection and transmission coefficients based on
220
+ the specified polarization state per ray.
221
+ """
222
+ idx = cuda.grid(1)
223
+ if idx >= cos_theta_i.shape[0]:
224
+ return
225
+
226
+ cti = cos_theta_i[idx]
227
+ pol = polarization[idx]
228
+ if n1_is_scalar:
229
+ ni1 = n1[0]
230
+ else:
231
+ ni1 = n1[idx]
232
+ if n2_is_scalar:
233
+ ni2 = n2[0]
234
+ else:
235
+ ni2 = n2[idx]
236
+
237
+ r_s, r_p, cos_theta_t, tir = _device_fresnel_rs_rp(cti, ni1, ni2)
238
+
239
+ if tir:
240
+ reflectance_out[idx] = 1.0
241
+ transmittance_out[idx] = 0.0
242
+ else:
243
+ # Intensity reflection coefficients
244
+ R_s = r_s * r_s
245
+ R_p = r_p * r_p
246
+
247
+ # Select based on polarization
248
+ if pol == 1: # s-polarization
249
+ R = R_s
250
+ elif pol == 2: # p-polarization
251
+ R = R_p
252
+ else: # unpolarized
253
+ R = 0.5 * (R_s + R_p)
254
+
255
+ T = 1.0 - R
256
+
257
+ reflectance_out[idx] = R
258
+ transmittance_out[idx] = T
@@ -0,0 +1,79 @@
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
+ Intersection Kernels
36
+
37
+ CUDA kernels for GPU-accelerated ray-surface intersection testing.
38
+ Supports analytical methods for simple geometries (plane, sphere, bounded plane)
39
+ and generic signed distance dispatch for all surface types.
40
+
41
+ This module exports ONLY pure CUDA kernels and device functions.
42
+ GPU memory management is handled by the propagator layer (handlers).
43
+
44
+ Kernel Types
45
+ ------------
46
+ - Analytical: Fast closed-form solutions for simple geometries
47
+ - PLANE_ANALYTICAL: Infinite plane intersection
48
+ - SPHERE_ANALYTICAL: Sphere intersection
49
+ - BOUNDED_PLANE_ANALYTICAL: Bounded rectangular plane intersection
50
+
51
+ - Generic dispatch: GPU-accelerated signed distance for any surface
52
+ - SIGNED_DISTANCE_GENERIC: Signed distance via geometry_id dispatch
53
+ - SURFACE_NORMAL_GENERIC: Surface normal via geometry_id dispatch
54
+ """
55
+
56
+ from .protocol import IntersectionKernelProtocol
57
+ from .plane import kernel_plane_intersect, HAS_CUDA
58
+ from .sphere import kernel_sphere_intersect
59
+ from .bounded_plane import kernel_bounded_plane_intersect
60
+ from .annular_plane import kernel_annular_plane_intersect
61
+ from .signed_distance import kernel_signed_distance, kernel_surface_normal
62
+
63
+ __all__ = [
64
+ # Protocol
65
+ "IntersectionKernelProtocol",
66
+ # Plane intersection kernel
67
+ "kernel_plane_intersect",
68
+ # Sphere intersection kernel
69
+ "kernel_sphere_intersect",
70
+ # Bounded plane intersection kernel
71
+ "kernel_bounded_plane_intersect",
72
+ # Annular plane intersection kernel
73
+ "kernel_annular_plane_intersect",
74
+ # Generic signed distance kernels
75
+ "kernel_signed_distance",
76
+ "kernel_surface_normal",
77
+ # CUDA availability
78
+ "HAS_CUDA",
79
+ ]
@@ -0,0 +1,207 @@
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
+ GPU-Accelerated Annular Plane Intersection
36
+
37
+ Analytical ray-plane intersection with annular (circular) bounds checking.
38
+
39
+ This module contains ONLY pure CUDA kernels - no GPU memory management.
40
+ The propagator layer is responsible for cuda.to_device() and copy_to_host().
41
+ """
42
+
43
+ import math
44
+
45
+ from ..registry import IntersectionKernelID, register_kernel
46
+
47
+ # GPU support is optional
48
+ try:
49
+ from numba import cuda
50
+
51
+ HAS_CUDA = True
52
+ except ImportError:
53
+
54
+ class _FakeCuda:
55
+ """Fake cuda module for when numba is not installed."""
56
+
57
+ @staticmethod
58
+ def jit(*args, **kwargs):
59
+ """Return a no-op decorator."""
60
+
61
+ def decorator(func):
62
+ return func
63
+
64
+ if args and callable(args[0]):
65
+ return args[0]
66
+ return decorator
67
+
68
+ @staticmethod
69
+ def is_available():
70
+ return False
71
+
72
+ @staticmethod
73
+ def grid(n):
74
+ return 0
75
+
76
+ @staticmethod
77
+ def synchronize():
78
+ pass
79
+
80
+ cuda = _FakeCuda() # type: ignore[assignment]
81
+ HAS_CUDA = False
82
+
83
+
84
+ # =============================================================================
85
+ # CUDA Kernels
86
+ # =============================================================================
87
+
88
+
89
+ @register_kernel(IntersectionKernelID.ANNULAR_PLANE_ANALYTICAL)
90
+ @cuda.jit
91
+ def kernel_annular_plane_intersect(
92
+ origins, # (N, 3) ray origins
93
+ directions, # (N, 3) ray directions
94
+ distances_out, # (N,) output distances
95
+ hit_mask_out, # (N,) output hit mask
96
+ normals_out, # (N, 3) output normals at intersection
97
+ # Plane parameters
98
+ plane_normal_x, # Plane normal X component
99
+ plane_normal_y, # Plane normal Y component
100
+ plane_normal_z, # Plane normal Z component
101
+ # Annulus center
102
+ center_x,
103
+ center_y,
104
+ center_z,
105
+ # Local U axis for coordinate computation
106
+ u_axis_x,
107
+ u_axis_y,
108
+ u_axis_z,
109
+ # Local V axis for coordinate computation
110
+ v_axis_x,
111
+ v_axis_y,
112
+ v_axis_z,
113
+ # Squared radii for annular bounds
114
+ inner_radius_sq,
115
+ outer_radius_sq,
116
+ ):
117
+ """
118
+ CUDA kernel for ray-annular-plane intersection.
119
+
120
+ Uses analytical solution for ray-plane intersection, then checks
121
+ if the intersection point lies within the annular bounds.
122
+
123
+ The annular plane is defined by its center point and normal.
124
+ The annular bounds are checked using the squared radial distance
125
+ from the center in the local coordinate frame.
126
+ """
127
+ idx = cuda.grid(1)
128
+ if idx >= origins.shape[0]:
129
+ return
130
+
131
+ # Load ray data
132
+ ox = origins[idx, 0]
133
+ oy = origins[idx, 1]
134
+ oz = origins[idx, 2]
135
+ dx = directions[idx, 0]
136
+ dy = directions[idx, 1]
137
+ dz = directions[idx, 2]
138
+
139
+ # Normalize direction
140
+ d_len = math.sqrt(dx * dx + dy * dy + dz * dz)
141
+ if d_len < 1e-10:
142
+ hit_mask_out[idx] = False
143
+ distances_out[idx] = math.inf
144
+ return
145
+ dx /= d_len
146
+ dy /= d_len
147
+ dz /= d_len
148
+
149
+ # Compute denominator: n . d
150
+ denom = plane_normal_x * dx + plane_normal_y * dy + plane_normal_z * dz
151
+
152
+ # Check if ray is parallel to plane
153
+ if abs(denom) < 1e-10:
154
+ hit_mask_out[idx] = False
155
+ distances_out[idx] = math.inf
156
+ return
157
+
158
+ # Compute numerator: (center - origin) . n
159
+ numer = (
160
+ plane_normal_x * (center_x - ox)
161
+ + plane_normal_y * (center_y - oy)
162
+ + plane_normal_z * (center_z - oz)
163
+ )
164
+
165
+ # Compute intersection distance
166
+ t = numer / denom
167
+
168
+ # Only forward intersections
169
+ if t < 0:
170
+ hit_mask_out[idx] = False
171
+ distances_out[idx] = math.inf
172
+ return
173
+
174
+ # Compute intersection point
175
+ px = ox + t * dx
176
+ py = oy + t * dy
177
+ pz = oz + t * dz
178
+
179
+ # Vector from center to intersection
180
+ rx = px - center_x
181
+ ry = py - center_y
182
+ rz = pz - center_z
183
+
184
+ # Project onto local axes
185
+ u_coord = rx * u_axis_x + ry * u_axis_y + rz * u_axis_z
186
+ v_coord = rx * v_axis_x + ry * v_axis_y + rz * v_axis_z
187
+
188
+ # Check annular bounds (circular)
189
+ r_sq = u_coord * u_coord + v_coord * v_coord
190
+ if r_sq < inner_radius_sq or r_sq > outer_radius_sq:
191
+ hit_mask_out[idx] = False
192
+ distances_out[idx] = math.inf
193
+ return
194
+
195
+ # Valid annular intersection
196
+ hit_mask_out[idx] = True
197
+ distances_out[idx] = t
198
+
199
+ # Normal points toward ray origin (flip if ray hits from behind)
200
+ if denom > 0:
201
+ normals_out[idx, 0] = -plane_normal_x
202
+ normals_out[idx, 1] = -plane_normal_y
203
+ normals_out[idx, 2] = -plane_normal_z
204
+ else:
205
+ normals_out[idx, 0] = plane_normal_x
206
+ normals_out[idx, 1] = plane_normal_y
207
+ normals_out[idx, 2] = plane_normal_z