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,240 @@
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
+ Absorption Kernel for GridInhomogeneousModel
36
+
37
+ GPU kernel for applying Beer-Lambert absorption using 3D grid-based LUT.
38
+ """
39
+
40
+ import math
41
+
42
+ # GPU support is optional
43
+ try:
44
+ from numba import cuda
45
+
46
+ HAS_CUDA = True
47
+ except ImportError:
48
+
49
+ class _FakeCuda:
50
+ """Fake cuda module for when numba is not installed."""
51
+
52
+ class devicearray:
53
+ """Fake devicearray submodule."""
54
+
55
+ DeviceNDArray = object
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
+ @cuda.jit(device=True)
85
+ def _device_trilinear_interpolate(
86
+ x: float,
87
+ y: float,
88
+ z: float,
89
+ grid,
90
+ x_min: float,
91
+ y_min: float,
92
+ z_min: float,
93
+ dx: float,
94
+ dy: float,
95
+ dz: float,
96
+ nx: int,
97
+ ny: int,
98
+ nz: int,
99
+ ) -> float:
100
+ """Trilinear interpolation in 3D grid."""
101
+ xn = (x - x_min) / dx
102
+ yn = (y - y_min) / dy
103
+ zn = (z - z_min) / dz
104
+
105
+ xn = max(0.0, min(xn, nx - 1.001))
106
+ yn = max(0.0, min(yn, ny - 1.001))
107
+ zn = max(0.0, min(zn, nz - 1.001))
108
+
109
+ x0 = int(xn)
110
+ y0 = int(yn)
111
+ z0 = int(zn)
112
+
113
+ x1 = min(x0 + 1, nx - 1)
114
+ y1 = min(y0 + 1, ny - 1)
115
+ z1 = min(z0 + 1, nz - 1)
116
+
117
+ xd = xn - x0
118
+ yd = yn - y0
119
+ zd = zn - z0
120
+
121
+ c000 = grid[x0, y0, z0]
122
+ c001 = grid[x0, y0, z1]
123
+ c010 = grid[x0, y1, z0]
124
+ c011 = grid[x0, y1, z1]
125
+ c100 = grid[x1, y0, z0]
126
+ c101 = grid[x1, y0, z1]
127
+ c110 = grid[x1, y1, z0]
128
+ c111 = grid[x1, y1, z1]
129
+
130
+ c00 = c000 * (1 - xd) + c100 * xd
131
+ c01 = c001 * (1 - xd) + c101 * xd
132
+ c10 = c010 * (1 - xd) + c110 * xd
133
+ c11 = c011 * (1 - xd) + c111 * xd
134
+
135
+ c0 = c00 * (1 - yd) + c10 * yd
136
+ c1 = c01 * (1 - yd) + c11 * yd
137
+
138
+ return c0 * (1 - zd) + c1 * zd
139
+
140
+
141
+ @cuda.jit
142
+ def _kernel_absorption_grid(
143
+ positions,
144
+ directions,
145
+ active,
146
+ intensities,
147
+ optical_depth,
148
+ step_size: float,
149
+ num_steps: int,
150
+ x_min: float,
151
+ y_min: float,
152
+ z_min: float,
153
+ grid_dx: float,
154
+ grid_dy: float,
155
+ grid_dz: float,
156
+ nx: int,
157
+ ny: int,
158
+ nz: int,
159
+ alpha_grid,
160
+ ):
161
+ """
162
+ Apply Beer-Lambert absorption for GridInhomogeneousModel.
163
+
164
+ Integrates absorption along the ray path using trilinear interpolation
165
+ in 3D absorption grid.
166
+
167
+ Parameters
168
+ ----------
169
+ positions : device array, shape (N, 3)
170
+ Ray positions (after propagation)
171
+ directions : device array, shape (N, 3)
172
+ Ray directions (unit vectors)
173
+ active : device array, shape (N,)
174
+ Boolean mask for active rays
175
+ intensities : device array, shape (N,)
176
+ Ray intensities (modified in-place)
177
+ optical_depth : device array, shape (N,)
178
+ Accumulated optical depth (modified in-place)
179
+ step_size : float
180
+ Integration step size in meters
181
+ num_steps : int
182
+ Number of steps taken in propagation
183
+ x_min, y_min, z_min : float
184
+ Grid origin
185
+ grid_dx, grid_dy, grid_dz : float
186
+ Grid spacing in each dimension
187
+ nx, ny, nz : int
188
+ Grid dimensions
189
+ alpha_grid : device array, shape (nx, ny, nz)
190
+ 3D absorption coefficient grid
191
+ """
192
+ idx = cuda.grid(1)
193
+ if idx >= positions.shape[0]:
194
+ return
195
+ if not active[idx]:
196
+ return
197
+
198
+ # Get current position and direction
199
+ x = positions[idx, 0]
200
+ y = positions[idx, 1]
201
+ z = positions[idx, 2]
202
+ dx_dir = directions[idx, 0]
203
+ dy_dir = directions[idx, 1]
204
+ dz_dir = directions[idx, 2]
205
+
206
+ # Integrate absorption along path by stepping backwards
207
+ delta_tau = 0.0
208
+
209
+ for step in range(num_steps):
210
+ # Position at this step (walking backwards from current)
211
+ back_dist = step * step_size
212
+ px = x - dx_dir * back_dist
213
+ py = y - dy_dir * back_dist
214
+ pz = z - dz_dir * back_dist
215
+
216
+ # Look up absorption coefficient (trilinear interpolation)
217
+ alpha = _device_trilinear_interpolate(
218
+ px,
219
+ py,
220
+ pz,
221
+ alpha_grid,
222
+ x_min,
223
+ y_min,
224
+ z_min,
225
+ grid_dx,
226
+ grid_dy,
227
+ grid_dz,
228
+ nx,
229
+ ny,
230
+ nz,
231
+ )
232
+
233
+ # Accumulate optical depth for this step
234
+ delta_tau += alpha * step_size
235
+
236
+ # Apply Beer-Lambert decay: I = I₀ * exp(-τ)
237
+ intensities[idx] *= math.exp(-delta_tau)
238
+
239
+ # Accumulate optical depth
240
+ optical_depth[idx] += delta_tau
@@ -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
+ Absorption Kernel for SimpleInhomogeneousModel
36
+
37
+ GPU kernel for applying Beer-Lambert absorption using 1D altitude-based LUT.
38
+ Called separately after propagation to update ray intensities.
39
+ """
40
+
41
+ import math
42
+
43
+ # GPU support is optional
44
+ try:
45
+ from numba import cuda
46
+
47
+ HAS_CUDA = True
48
+ except ImportError:
49
+
50
+ class _FakeCuda:
51
+ """Fake cuda module for when numba is not installed."""
52
+
53
+ class devicearray:
54
+ """Fake devicearray submodule."""
55
+
56
+ DeviceNDArray = object
57
+
58
+ @staticmethod
59
+ def jit(*args, **kwargs):
60
+ """Return a no-op decorator."""
61
+
62
+ def decorator(func):
63
+ return func
64
+
65
+ if args and callable(args[0]):
66
+ return args[0]
67
+ return decorator
68
+
69
+ @staticmethod
70
+ def is_available():
71
+ return False
72
+
73
+ @staticmethod
74
+ def grid(n):
75
+ return 0
76
+
77
+ @staticmethod
78
+ def synchronize():
79
+ pass
80
+
81
+ cuda = _FakeCuda() # type: ignore[assignment]
82
+ HAS_CUDA = False
83
+
84
+
85
+ @cuda.jit(device=True)
86
+ def _device_lut_interpolate(
87
+ value: float,
88
+ lut: cuda.devicearray.DeviceNDArray,
89
+ min_val: float,
90
+ delta: float,
91
+ lut_size: int,
92
+ ) -> float:
93
+ """
94
+ Linear interpolation in 1D lookup table.
95
+
96
+ Parameters
97
+ ----------
98
+ value : float
99
+ Value to interpolate at
100
+ lut : device array
101
+ 1D lookup table
102
+ min_val : float
103
+ Minimum value in table
104
+ delta : float
105
+ Spacing between table entries
106
+ lut_size : int
107
+ Number of entries in table
108
+
109
+ Returns
110
+ -------
111
+ float
112
+ Interpolated value
113
+ """
114
+ idx_float = (value - min_val) / delta
115
+
116
+ if idx_float <= 0.0:
117
+ return lut[0]
118
+ if idx_float >= lut_size - 1:
119
+ return lut[lut_size - 1]
120
+
121
+ idx = int(idx_float)
122
+ frac = idx_float - idx
123
+
124
+ return lut[idx] + frac * (lut[idx + 1] - lut[idx])
125
+
126
+
127
+ @cuda.jit
128
+ def _kernel_absorption_simple(
129
+ positions,
130
+ directions,
131
+ active,
132
+ intensities,
133
+ optical_depth,
134
+ step_size: float,
135
+ num_steps: int,
136
+ center_x: float,
137
+ center_y: float,
138
+ center_z: float,
139
+ ref_radius: float,
140
+ min_alt: float,
141
+ delta_h: float,
142
+ lut_size: int,
143
+ lut_alpha,
144
+ ):
145
+ """
146
+ Apply Beer-Lambert absorption for SimpleInhomogeneousModel.
147
+
148
+ This kernel applies absorption decay to ray intensities based on
149
+ the distance traveled through the medium. It integrates α along
150
+ the ray path by stepping backwards from the current position and
151
+ sampling α at each step.
152
+
153
+ The kernel computes:
154
+ τ = ∫ α(s) ds (integrated along path)
155
+ I_new = I_old * exp(-τ)
156
+
157
+ Parameters
158
+ ----------
159
+ positions : device array, shape (N, 3)
160
+ Ray positions (after propagation)
161
+ directions : device array, shape (N, 3)
162
+ Ray directions (unit vectors)
163
+ active : device array, shape (N,)
164
+ Boolean mask for active rays
165
+ intensities : device array, shape (N,)
166
+ Ray intensities (modified in-place)
167
+ optical_depth : device array, shape (N,)
168
+ Accumulated optical depth (modified in-place)
169
+ step_size : float
170
+ Integration step size in meters
171
+ num_steps : int
172
+ Number of steps taken in propagation
173
+ center_x, center_y, center_z : float
174
+ Center of spherical symmetry
175
+ ref_radius : float
176
+ Reference radius (e.g., Earth radius)
177
+ min_alt : float
178
+ Minimum altitude in LUT
179
+ delta_h : float
180
+ Altitude spacing in LUT
181
+ lut_size : int
182
+ Number of entries in LUT
183
+ lut_alpha : device array
184
+ 1D lookup table for absorption coefficient α(altitude)
185
+ """
186
+ idx = cuda.grid(1)
187
+ if idx >= positions.shape[0]:
188
+ return
189
+ if not active[idx]:
190
+ return
191
+
192
+ # Get current position and direction
193
+ x = positions[idx, 0]
194
+ y = positions[idx, 1]
195
+ z = positions[idx, 2]
196
+ dx_dir = directions[idx, 0]
197
+ dy_dir = directions[idx, 1]
198
+ dz_dir = directions[idx, 2]
199
+
200
+ # Integrate absorption along path by stepping backwards
201
+ # Start at current position, step back along -direction
202
+ delta_tau = 0.0
203
+
204
+ for step in range(num_steps):
205
+ # Position at this step (walking backwards from current)
206
+ # At step=0, we're at the current position
207
+ # At step=num_steps-1, we're at the starting position
208
+ back_dist = step * step_size
209
+ px = x - dx_dir * back_dist
210
+ py = y - dy_dir * back_dist
211
+ pz = z - dz_dir * back_dist
212
+
213
+ # Compute altitude
214
+ rx = px - center_x
215
+ ry = py - center_y
216
+ rz = pz - center_z
217
+ r = math.sqrt(rx * rx + ry * ry + rz * rz)
218
+ altitude = r - ref_radius
219
+ if altitude < 0.0:
220
+ altitude = 0.0
221
+
222
+ # Look up absorption coefficient at this position
223
+ alpha = _device_lut_interpolate(altitude, lut_alpha, min_alt, delta_h, lut_size)
224
+
225
+ # Accumulate optical depth for this step
226
+ delta_tau += alpha * step_size
227
+
228
+ # Apply Beer-Lambert decay: I = I₀ * exp(-τ)
229
+ intensities[idx] *= math.exp(-delta_tau)
230
+
231
+ # Accumulate optical depth
232
+ optical_depth[idx] += delta_tau