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.
- lsurf/__init__.py +471 -0
- lsurf/analysis/__init__.py +107 -0
- lsurf/analysis/healpix_utils.py +418 -0
- lsurf/analysis/sphere_viz.py +1280 -0
- lsurf/cli/__init__.py +48 -0
- lsurf/cli/build.py +398 -0
- lsurf/cli/config_schema.py +318 -0
- lsurf/cli/gui_cmd.py +76 -0
- lsurf/cli/interactive.py +850 -0
- lsurf/cli/main.py +81 -0
- lsurf/cli/run.py +806 -0
- lsurf/detectors/__init__.py +266 -0
- lsurf/detectors/analysis.py +289 -0
- lsurf/detectors/base.py +284 -0
- lsurf/detectors/constant_size_rings.py +485 -0
- lsurf/detectors/directional.py +45 -0
- lsurf/detectors/extended/__init__.py +73 -0
- lsurf/detectors/extended/local_sphere.py +353 -0
- lsurf/detectors/extended/recording_sphere.py +368 -0
- lsurf/detectors/planar.py +45 -0
- lsurf/detectors/protocol.py +187 -0
- lsurf/detectors/recording_spheres.py +63 -0
- lsurf/detectors/results.py +1140 -0
- lsurf/detectors/small/__init__.py +79 -0
- lsurf/detectors/small/directional.py +330 -0
- lsurf/detectors/small/planar.py +401 -0
- lsurf/detectors/small/spherical.py +450 -0
- lsurf/detectors/spherical.py +45 -0
- lsurf/geometry/__init__.py +199 -0
- lsurf/geometry/builder.py +478 -0
- lsurf/geometry/cell.py +228 -0
- lsurf/geometry/cell_geometry.py +247 -0
- lsurf/geometry/detector_arrays.py +1785 -0
- lsurf/geometry/geometry.py +222 -0
- lsurf/geometry/surface_analysis.py +375 -0
- lsurf/geometry/validation.py +91 -0
- lsurf/gui/__init__.py +51 -0
- lsurf/gui/app.py +903 -0
- lsurf/gui/core/__init__.py +39 -0
- lsurf/gui/core/scene.py +343 -0
- lsurf/gui/core/simulation.py +264 -0
- lsurf/gui/renderers/__init__.py +40 -0
- lsurf/gui/renderers/ray_renderer.py +353 -0
- lsurf/gui/renderers/source_renderer.py +505 -0
- lsurf/gui/renderers/surface_renderer.py +477 -0
- lsurf/gui/views/__init__.py +48 -0
- lsurf/gui/views/config_editor.py +3199 -0
- lsurf/gui/views/properties.py +257 -0
- lsurf/gui/views/results.py +291 -0
- lsurf/gui/views/scene_tree.py +180 -0
- lsurf/gui/views/viewport_3d.py +555 -0
- lsurf/gui/views/visualizations.py +712 -0
- lsurf/materials/__init__.py +169 -0
- lsurf/materials/base/__init__.py +64 -0
- lsurf/materials/base/full_inhomogeneous.py +208 -0
- lsurf/materials/base/grid_inhomogeneous.py +319 -0
- lsurf/materials/base/homogeneous.py +342 -0
- lsurf/materials/base/material_field.py +527 -0
- lsurf/materials/base/simple_inhomogeneous.py +418 -0
- lsurf/materials/base/spectral_inhomogeneous.py +497 -0
- lsurf/materials/implementations/__init__.py +120 -0
- lsurf/materials/implementations/data/alpha_values_typical_atmosphere_updated.txt +24 -0
- lsurf/materials/implementations/duct_atmosphere.py +390 -0
- lsurf/materials/implementations/exponential_atmosphere.py +435 -0
- lsurf/materials/implementations/gaussian_lens.py +120 -0
- lsurf/materials/implementations/interpolated_data.py +123 -0
- lsurf/materials/implementations/layered_atmosphere.py +134 -0
- lsurf/materials/implementations/linear_gradient.py +109 -0
- lsurf/materials/implementations/linsley_atmosphere.py +764 -0
- lsurf/materials/implementations/standard_materials.py +126 -0
- lsurf/materials/implementations/turbulent_atmosphere.py +135 -0
- lsurf/materials/implementations/us_standard_atmosphere.py +149 -0
- lsurf/materials/utils/__init__.py +77 -0
- lsurf/materials/utils/constants.py +45 -0
- lsurf/materials/utils/device_functions.py +117 -0
- lsurf/materials/utils/dispersion.py +160 -0
- lsurf/materials/utils/factories.py +142 -0
- lsurf/propagation/__init__.py +91 -0
- lsurf/propagation/detector_gpu.py +67 -0
- lsurf/propagation/gpu_device_rays.py +294 -0
- lsurf/propagation/kernels/__init__.py +175 -0
- lsurf/propagation/kernels/absorption/__init__.py +61 -0
- lsurf/propagation/kernels/absorption/grid.py +240 -0
- lsurf/propagation/kernels/absorption/simple.py +232 -0
- lsurf/propagation/kernels/absorption/spectral.py +410 -0
- lsurf/propagation/kernels/detection/__init__.py +64 -0
- lsurf/propagation/kernels/detection/protocol.py +102 -0
- lsurf/propagation/kernels/detection/spherical.py +255 -0
- lsurf/propagation/kernels/device_functions.py +790 -0
- lsurf/propagation/kernels/fresnel/__init__.py +64 -0
- lsurf/propagation/kernels/fresnel/protocol.py +97 -0
- lsurf/propagation/kernels/fresnel/standard.py +258 -0
- lsurf/propagation/kernels/intersection/__init__.py +79 -0
- lsurf/propagation/kernels/intersection/annular_plane.py +207 -0
- lsurf/propagation/kernels/intersection/bounded_plane.py +205 -0
- lsurf/propagation/kernels/intersection/plane.py +166 -0
- lsurf/propagation/kernels/intersection/protocol.py +95 -0
- lsurf/propagation/kernels/intersection/signed_distance.py +742 -0
- lsurf/propagation/kernels/intersection/sphere.py +190 -0
- lsurf/propagation/kernels/propagation/__init__.py +85 -0
- lsurf/propagation/kernels/propagation/grid.py +527 -0
- lsurf/propagation/kernels/propagation/protocol.py +105 -0
- lsurf/propagation/kernels/propagation/simple.py +460 -0
- lsurf/propagation/kernels/propagation/spectral.py +875 -0
- lsurf/propagation/kernels/registry.py +331 -0
- lsurf/propagation/kernels/surface/__init__.py +72 -0
- lsurf/propagation/kernels/surface/bisection.py +232 -0
- lsurf/propagation/kernels/surface/detection.py +402 -0
- lsurf/propagation/kernels/surface/reduction.py +166 -0
- lsurf/propagation/propagator_protocol.py +222 -0
- lsurf/propagation/propagators/__init__.py +101 -0
- lsurf/propagation/propagators/detector_handler.py +354 -0
- lsurf/propagation/propagators/factory.py +200 -0
- lsurf/propagation/propagators/fresnel_handler.py +305 -0
- lsurf/propagation/propagators/gpu_gradient.py +566 -0
- lsurf/propagation/propagators/gpu_surface_propagator.py +707 -0
- lsurf/propagation/propagators/gradient.py +429 -0
- lsurf/propagation/propagators/intersection_handler.py +327 -0
- lsurf/propagation/propagators/material_propagator.py +398 -0
- lsurf/propagation/propagators/signed_distance_handler.py +522 -0
- lsurf/propagation/propagators/spectral_gpu_gradient.py +553 -0
- lsurf/propagation/propagators/surface_interaction.py +616 -0
- lsurf/propagation/propagators/surface_propagator.py +719 -0
- lsurf/py.typed +1 -0
- lsurf/simulation/__init__.py +70 -0
- lsurf/simulation/config.py +164 -0
- lsurf/simulation/orchestrator.py +462 -0
- lsurf/simulation/result.py +299 -0
- lsurf/simulation/simulation.py +262 -0
- lsurf/sources/__init__.py +128 -0
- lsurf/sources/base.py +264 -0
- lsurf/sources/collimated.py +252 -0
- lsurf/sources/custom.py +409 -0
- lsurf/sources/diverging.py +228 -0
- lsurf/sources/gaussian.py +272 -0
- lsurf/sources/parallel_from_positions.py +197 -0
- lsurf/sources/point.py +172 -0
- lsurf/sources/uniform_diverging.py +258 -0
- lsurf/surfaces/__init__.py +184 -0
- lsurf/surfaces/cpu/__init__.py +50 -0
- lsurf/surfaces/cpu/curved_wave.py +463 -0
- lsurf/surfaces/cpu/gerstner_wave.py +381 -0
- lsurf/surfaces/cpu/wave_params.py +118 -0
- lsurf/surfaces/gpu/__init__.py +72 -0
- lsurf/surfaces/gpu/annular_plane.py +453 -0
- lsurf/surfaces/gpu/bounded_plane.py +390 -0
- lsurf/surfaces/gpu/curved_wave.py +483 -0
- lsurf/surfaces/gpu/gerstner_wave.py +377 -0
- lsurf/surfaces/gpu/multi_curved_wave.py +520 -0
- lsurf/surfaces/gpu/plane.py +299 -0
- lsurf/surfaces/gpu/recording_sphere.py +587 -0
- lsurf/surfaces/gpu/sphere.py +311 -0
- lsurf/surfaces/protocol.py +336 -0
- lsurf/surfaces/registry.py +373 -0
- lsurf/utilities/__init__.py +175 -0
- lsurf/utilities/detector_analysis.py +814 -0
- lsurf/utilities/fresnel.py +628 -0
- lsurf/utilities/interactions.py +1215 -0
- lsurf/utilities/propagation.py +602 -0
- lsurf/utilities/ray_data.py +532 -0
- lsurf/utilities/recording_sphere.py +745 -0
- lsurf/utilities/time_spread.py +463 -0
- lsurf/visualization/__init__.py +329 -0
- lsurf/visualization/absorption_plots.py +334 -0
- lsurf/visualization/atmospheric_plots.py +754 -0
- lsurf/visualization/common.py +348 -0
- lsurf/visualization/detector_plots.py +1350 -0
- lsurf/visualization/detector_sphere_plots.py +1173 -0
- lsurf/visualization/fresnel_plots.py +1061 -0
- lsurf/visualization/ocean_simulation_plots.py +999 -0
- lsurf/visualization/polarization_plots.py +916 -0
- lsurf/visualization/raytracing_plots.py +1521 -0
- lsurf/visualization/ring_detector_plots.py +1867 -0
- lsurf/visualization/time_spread_plots.py +531 -0
- lsurf-1.0.0.dist-info/METADATA +381 -0
- lsurf-1.0.0.dist-info/RECORD +180 -0
- lsurf-1.0.0.dist-info/WHEEL +5 -0
- lsurf-1.0.0.dist-info/entry_points.txt +2 -0
- lsurf-1.0.0.dist-info/licenses/LICENSE +32 -0
- lsurf-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,410 @@
|
|
|
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 Kernels for SpectralInhomogeneousModel
|
|
36
|
+
|
|
37
|
+
GPU kernels for applying Beer-Lambert absorption using 2D altitude×wavelength LUT.
|
|
38
|
+
Supports both scalar wavelength (all rays same λ) and per-ray wavelength modes.
|
|
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_bilinear_interpolate(
|
|
87
|
+
val1: float,
|
|
88
|
+
val2: float,
|
|
89
|
+
lut: cuda.devicearray.DeviceNDArray,
|
|
90
|
+
min1: float,
|
|
91
|
+
delta1: float,
|
|
92
|
+
n1: int,
|
|
93
|
+
min2: float,
|
|
94
|
+
delta2: float,
|
|
95
|
+
n2: int,
|
|
96
|
+
) -> float:
|
|
97
|
+
"""
|
|
98
|
+
Bilinear interpolation in 2D LUT.
|
|
99
|
+
|
|
100
|
+
Parameters
|
|
101
|
+
----------
|
|
102
|
+
val1 : float
|
|
103
|
+
Value in first dimension (e.g., altitude)
|
|
104
|
+
val2 : float
|
|
105
|
+
Value in second dimension (e.g., wavelength)
|
|
106
|
+
lut : device array
|
|
107
|
+
2D lookup table [dim1, dim2]
|
|
108
|
+
min1 : float
|
|
109
|
+
Minimum value in first dimension
|
|
110
|
+
delta1 : float
|
|
111
|
+
Spacing in first dimension
|
|
112
|
+
n1 : int
|
|
113
|
+
Number of entries in first dimension
|
|
114
|
+
min2 : float
|
|
115
|
+
Minimum value in second dimension
|
|
116
|
+
delta2 : float
|
|
117
|
+
Spacing in second dimension
|
|
118
|
+
n2 : int
|
|
119
|
+
Number of entries in second dimension
|
|
120
|
+
|
|
121
|
+
Returns
|
|
122
|
+
-------
|
|
123
|
+
float
|
|
124
|
+
Interpolated value
|
|
125
|
+
"""
|
|
126
|
+
# Normalize coordinates to fractional indices
|
|
127
|
+
idx1 = (val1 - min1) / delta1
|
|
128
|
+
idx2 = (val2 - min2) / delta2
|
|
129
|
+
|
|
130
|
+
# Clamp to valid bounds
|
|
131
|
+
if idx1 < 0.0:
|
|
132
|
+
idx1 = 0.0
|
|
133
|
+
if idx1 > n1 - 1.001:
|
|
134
|
+
idx1 = n1 - 1.001
|
|
135
|
+
if idx2 < 0.0:
|
|
136
|
+
idx2 = 0.0
|
|
137
|
+
if idx2 > n2 - 1.001:
|
|
138
|
+
idx2 = n2 - 1.001
|
|
139
|
+
|
|
140
|
+
# Get integer indices
|
|
141
|
+
i0 = int(idx1)
|
|
142
|
+
j0 = int(idx2)
|
|
143
|
+
i1 = i0 + 1
|
|
144
|
+
j1 = j0 + 1
|
|
145
|
+
|
|
146
|
+
# Clamp upper indices
|
|
147
|
+
if i1 > n1 - 1:
|
|
148
|
+
i1 = n1 - 1
|
|
149
|
+
if j1 > n2 - 1:
|
|
150
|
+
j1 = n2 - 1
|
|
151
|
+
|
|
152
|
+
# Fractional parts
|
|
153
|
+
fi = idx1 - i0
|
|
154
|
+
fj = idx2 - j0
|
|
155
|
+
|
|
156
|
+
# Bilinear interpolation
|
|
157
|
+
c00 = lut[i0, j0]
|
|
158
|
+
c01 = lut[i0, j1]
|
|
159
|
+
c10 = lut[i1, j0]
|
|
160
|
+
c11 = lut[i1, j1]
|
|
161
|
+
|
|
162
|
+
# Interpolate along second dimension first
|
|
163
|
+
c0 = c00 * (1.0 - fj) + c01 * fj
|
|
164
|
+
c1 = c10 * (1.0 - fj) + c11 * fj
|
|
165
|
+
|
|
166
|
+
# Then interpolate along first dimension
|
|
167
|
+
return c0 * (1.0 - fi) + c1 * fi
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@cuda.jit
|
|
171
|
+
def _kernel_absorption_spectral(
|
|
172
|
+
positions,
|
|
173
|
+
directions,
|
|
174
|
+
active,
|
|
175
|
+
intensities,
|
|
176
|
+
optical_depth,
|
|
177
|
+
step_size: float,
|
|
178
|
+
num_steps: int,
|
|
179
|
+
center_x: float,
|
|
180
|
+
center_y: float,
|
|
181
|
+
center_z: float,
|
|
182
|
+
ref_radius: float,
|
|
183
|
+
alt_min: float,
|
|
184
|
+
alt_delta: float,
|
|
185
|
+
n_alt: int,
|
|
186
|
+
wl_min: float,
|
|
187
|
+
wl_delta: float,
|
|
188
|
+
n_wl: int,
|
|
189
|
+
lut_alpha,
|
|
190
|
+
wavelength: float,
|
|
191
|
+
):
|
|
192
|
+
"""
|
|
193
|
+
Apply Beer-Lambert absorption for SpectralInhomogeneousModel.
|
|
194
|
+
|
|
195
|
+
Integrates absorption along the ray path using 2D LUT interpolation
|
|
196
|
+
for wavelength-dependent absorption. All rays use the same wavelength.
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
positions : device array, shape (N, 3)
|
|
201
|
+
Ray positions (after propagation)
|
|
202
|
+
directions : device array, shape (N, 3)
|
|
203
|
+
Ray directions (unit vectors)
|
|
204
|
+
active : device array, shape (N,)
|
|
205
|
+
Boolean mask for active rays
|
|
206
|
+
intensities : device array, shape (N,)
|
|
207
|
+
Ray intensities (modified in-place)
|
|
208
|
+
optical_depth : device array, shape (N,)
|
|
209
|
+
Accumulated optical depth (modified in-place)
|
|
210
|
+
step_size : float
|
|
211
|
+
Integration step size in meters
|
|
212
|
+
num_steps : int
|
|
213
|
+
Number of steps taken in propagation
|
|
214
|
+
center_x, center_y, center_z : float
|
|
215
|
+
Center of spherical symmetry
|
|
216
|
+
ref_radius : float
|
|
217
|
+
Reference radius
|
|
218
|
+
alt_min : float
|
|
219
|
+
Minimum altitude in LUT
|
|
220
|
+
alt_delta : float
|
|
221
|
+
Altitude spacing in LUT
|
|
222
|
+
n_alt : int
|
|
223
|
+
Number of altitude samples
|
|
224
|
+
wl_min : float
|
|
225
|
+
Minimum wavelength in LUT
|
|
226
|
+
wl_delta : float
|
|
227
|
+
Wavelength spacing in LUT
|
|
228
|
+
n_wl : int
|
|
229
|
+
Number of wavelength samples
|
|
230
|
+
lut_alpha : device array, shape (n_alt, n_wl)
|
|
231
|
+
2D lookup table for α(altitude, wavelength)
|
|
232
|
+
wavelength : float
|
|
233
|
+
Wavelength in meters (same for all rays)
|
|
234
|
+
"""
|
|
235
|
+
idx = cuda.grid(1)
|
|
236
|
+
if idx >= positions.shape[0]:
|
|
237
|
+
return
|
|
238
|
+
if not active[idx]:
|
|
239
|
+
return
|
|
240
|
+
|
|
241
|
+
# Get current position and direction
|
|
242
|
+
x = positions[idx, 0]
|
|
243
|
+
y = positions[idx, 1]
|
|
244
|
+
z = positions[idx, 2]
|
|
245
|
+
dx_dir = directions[idx, 0]
|
|
246
|
+
dy_dir = directions[idx, 1]
|
|
247
|
+
dz_dir = directions[idx, 2]
|
|
248
|
+
|
|
249
|
+
# Integrate absorption along path by stepping backwards
|
|
250
|
+
delta_tau = 0.0
|
|
251
|
+
|
|
252
|
+
for step in range(num_steps):
|
|
253
|
+
# Position at this step (walking backwards from current)
|
|
254
|
+
back_dist = step * step_size
|
|
255
|
+
px = x - dx_dir * back_dist
|
|
256
|
+
py = y - dy_dir * back_dist
|
|
257
|
+
pz = z - dz_dir * back_dist
|
|
258
|
+
|
|
259
|
+
# Compute altitude from position
|
|
260
|
+
rx = px - center_x
|
|
261
|
+
ry = py - center_y
|
|
262
|
+
rz = pz - center_z
|
|
263
|
+
r = math.sqrt(rx * rx + ry * ry + rz * rz)
|
|
264
|
+
altitude = r - ref_radius
|
|
265
|
+
if altitude < 0.0:
|
|
266
|
+
altitude = 0.0
|
|
267
|
+
|
|
268
|
+
# Look up absorption coefficient (2D interpolation)
|
|
269
|
+
alpha = _device_bilinear_interpolate(
|
|
270
|
+
altitude,
|
|
271
|
+
wavelength,
|
|
272
|
+
lut_alpha,
|
|
273
|
+
alt_min,
|
|
274
|
+
alt_delta,
|
|
275
|
+
n_alt,
|
|
276
|
+
wl_min,
|
|
277
|
+
wl_delta,
|
|
278
|
+
n_wl,
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# Accumulate optical depth for this step
|
|
282
|
+
delta_tau += alpha * step_size
|
|
283
|
+
|
|
284
|
+
# Apply Beer-Lambert decay: I = I₀ * exp(-τ)
|
|
285
|
+
intensities[idx] *= math.exp(-delta_tau)
|
|
286
|
+
|
|
287
|
+
# Accumulate optical depth
|
|
288
|
+
optical_depth[idx] += delta_tau
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
@cuda.jit
|
|
292
|
+
def _kernel_absorption_spectral_perray(
|
|
293
|
+
positions,
|
|
294
|
+
directions,
|
|
295
|
+
wavelengths,
|
|
296
|
+
active,
|
|
297
|
+
intensities,
|
|
298
|
+
optical_depth,
|
|
299
|
+
step_size: float,
|
|
300
|
+
num_steps: int,
|
|
301
|
+
center_x: float,
|
|
302
|
+
center_y: float,
|
|
303
|
+
center_z: float,
|
|
304
|
+
ref_radius: float,
|
|
305
|
+
alt_min: float,
|
|
306
|
+
alt_delta: float,
|
|
307
|
+
n_alt: int,
|
|
308
|
+
wl_min: float,
|
|
309
|
+
wl_delta: float,
|
|
310
|
+
n_wl: int,
|
|
311
|
+
lut_alpha,
|
|
312
|
+
):
|
|
313
|
+
"""
|
|
314
|
+
Apply Beer-Lambert absorption with per-ray wavelengths.
|
|
315
|
+
|
|
316
|
+
Integrates absorption along the ray path. Each ray has its own
|
|
317
|
+
wavelength, enabling chromatic absorption simulation.
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
positions : device array, shape (N, 3)
|
|
322
|
+
Ray positions (after propagation)
|
|
323
|
+
directions : device array, shape (N, 3)
|
|
324
|
+
Ray directions (unit vectors)
|
|
325
|
+
wavelengths : device array, shape (N,)
|
|
326
|
+
Per-ray wavelengths in meters
|
|
327
|
+
active : device array, shape (N,)
|
|
328
|
+
Boolean mask for active rays
|
|
329
|
+
intensities : device array, shape (N,)
|
|
330
|
+
Ray intensities (modified in-place)
|
|
331
|
+
optical_depth : device array, shape (N,)
|
|
332
|
+
Accumulated optical depth (modified in-place)
|
|
333
|
+
step_size : float
|
|
334
|
+
Integration step size in meters
|
|
335
|
+
num_steps : int
|
|
336
|
+
Number of steps taken in propagation
|
|
337
|
+
center_x, center_y, center_z : float
|
|
338
|
+
Center of spherical symmetry
|
|
339
|
+
ref_radius : float
|
|
340
|
+
Reference radius
|
|
341
|
+
alt_min : float
|
|
342
|
+
Minimum altitude in LUT
|
|
343
|
+
alt_delta : float
|
|
344
|
+
Altitude spacing in LUT
|
|
345
|
+
n_alt : int
|
|
346
|
+
Number of altitude samples
|
|
347
|
+
wl_min : float
|
|
348
|
+
Minimum wavelength in LUT
|
|
349
|
+
wl_delta : float
|
|
350
|
+
Wavelength spacing in LUT
|
|
351
|
+
n_wl : int
|
|
352
|
+
Number of wavelength samples
|
|
353
|
+
lut_alpha : device array, shape (n_alt, n_wl)
|
|
354
|
+
2D lookup table for α(altitude, wavelength)
|
|
355
|
+
"""
|
|
356
|
+
idx = cuda.grid(1)
|
|
357
|
+
if idx >= positions.shape[0]:
|
|
358
|
+
return
|
|
359
|
+
if not active[idx]:
|
|
360
|
+
return
|
|
361
|
+
|
|
362
|
+
# Get current position, direction, and wavelength
|
|
363
|
+
x = positions[idx, 0]
|
|
364
|
+
y = positions[idx, 1]
|
|
365
|
+
z = positions[idx, 2]
|
|
366
|
+
dx_dir = directions[idx, 0]
|
|
367
|
+
dy_dir = directions[idx, 1]
|
|
368
|
+
dz_dir = directions[idx, 2]
|
|
369
|
+
wavelength = wavelengths[idx]
|
|
370
|
+
|
|
371
|
+
# Integrate absorption along path by stepping backwards
|
|
372
|
+
delta_tau = 0.0
|
|
373
|
+
|
|
374
|
+
for step in range(num_steps):
|
|
375
|
+
# Position at this step (walking backwards from current)
|
|
376
|
+
back_dist = step * step_size
|
|
377
|
+
px = x - dx_dir * back_dist
|
|
378
|
+
py = y - dy_dir * back_dist
|
|
379
|
+
pz = z - dz_dir * back_dist
|
|
380
|
+
|
|
381
|
+
# Compute altitude from position
|
|
382
|
+
rx = px - center_x
|
|
383
|
+
ry = py - center_y
|
|
384
|
+
rz = pz - center_z
|
|
385
|
+
r = math.sqrt(rx * rx + ry * ry + rz * rz)
|
|
386
|
+
altitude = r - ref_radius
|
|
387
|
+
if altitude < 0.0:
|
|
388
|
+
altitude = 0.0
|
|
389
|
+
|
|
390
|
+
# Look up absorption coefficient (2D interpolation)
|
|
391
|
+
alpha = _device_bilinear_interpolate(
|
|
392
|
+
altitude,
|
|
393
|
+
wavelength,
|
|
394
|
+
lut_alpha,
|
|
395
|
+
alt_min,
|
|
396
|
+
alt_delta,
|
|
397
|
+
n_alt,
|
|
398
|
+
wl_min,
|
|
399
|
+
wl_delta,
|
|
400
|
+
n_wl,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
# Accumulate optical depth for this step
|
|
404
|
+
delta_tau += alpha * step_size
|
|
405
|
+
|
|
406
|
+
# Apply Beer-Lambert decay: I = I₀ * exp(-τ)
|
|
407
|
+
intensities[idx] *= math.exp(-delta_tau)
|
|
408
|
+
|
|
409
|
+
# Accumulate optical depth
|
|
410
|
+
optical_depth[idx] += delta_tau
|
|
@@ -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
|
+
Detection Kernels
|
|
36
|
+
|
|
37
|
+
CUDA kernels for GPU-accelerated ray detection.
|
|
38
|
+
|
|
39
|
+
This module exports ONLY pure CUDA kernels.
|
|
40
|
+
GPU memory management is handled by the propagator layer (handlers).
|
|
41
|
+
|
|
42
|
+
Kernel Types
|
|
43
|
+
------------
|
|
44
|
+
- SPHERICAL_SINGLE: Single spherical detector
|
|
45
|
+
- SPHERICAL_MULTI: Multiple spherical detectors (scan mode)
|
|
46
|
+
- PLANAR_SINGLE: Single planar detector (TODO)
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
from .protocol import DetectionKernelProtocol
|
|
50
|
+
from .spherical import (
|
|
51
|
+
kernel_spherical_detect_single,
|
|
52
|
+
kernel_spherical_detect_multi,
|
|
53
|
+
HAS_CUDA,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
__all__ = [
|
|
57
|
+
# Protocol
|
|
58
|
+
"DetectionKernelProtocol",
|
|
59
|
+
# Kernels (pure CUDA only)
|
|
60
|
+
"kernel_spherical_detect_single",
|
|
61
|
+
"kernel_spherical_detect_multi",
|
|
62
|
+
# CUDA availability
|
|
63
|
+
"HAS_CUDA",
|
|
64
|
+
]
|
|
@@ -0,0 +1,102 @@
|
|
|
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
|
+
Detection Kernel Protocol
|
|
36
|
+
|
|
37
|
+
Defines the interface for ray detection kernels that find
|
|
38
|
+
rays hitting detector surfaces.
|
|
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 DetectionKernelProtocol(Protocol):
|
|
51
|
+
"""
|
|
52
|
+
Protocol for ray detection kernels.
|
|
53
|
+
|
|
54
|
+
Detection kernels find rays that intersect with detector surfaces
|
|
55
|
+
and compute detection parameters (hit time, position, intensity).
|
|
56
|
+
|
|
57
|
+
Common Parameters
|
|
58
|
+
-----------------
|
|
59
|
+
ray_positions : DeviceNDArray, shape (N, 3)
|
|
60
|
+
Ray positions in meters
|
|
61
|
+
ray_directions : DeviceNDArray, shape (N, 3)
|
|
62
|
+
Ray direction unit vectors
|
|
63
|
+
ray_active : DeviceNDArray, shape (N,)
|
|
64
|
+
Boolean mask of active rays
|
|
65
|
+
ray_times : DeviceNDArray, shape (N,)
|
|
66
|
+
Accumulated ray times in seconds
|
|
67
|
+
ray_intensities : DeviceNDArray, shape (N,)
|
|
68
|
+
Ray intensities
|
|
69
|
+
hit_mask_out : DeviceNDArray, shape (N,)
|
|
70
|
+
Output: boolean mask of rays that hit detector
|
|
71
|
+
hit_distances_out : DeviceNDArray, shape (N,)
|
|
72
|
+
Output: distance to detector
|
|
73
|
+
hit_times_out : DeviceNDArray, shape (N,)
|
|
74
|
+
Output: arrival time at detector
|
|
75
|
+
|
|
76
|
+
Detector-Specific Parameters
|
|
77
|
+
----------------------------
|
|
78
|
+
Additional parameters depend on the detector type:
|
|
79
|
+
- Spherical: center position, radius
|
|
80
|
+
- Planar: normal vector, point on plane
|
|
81
|
+
|
|
82
|
+
Notes
|
|
83
|
+
-----
|
|
84
|
+
Kernels are registered with the central registry using @register_kernel
|
|
85
|
+
decorator and DetectionKernelID enum values.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __call__(
|
|
89
|
+
self,
|
|
90
|
+
ray_positions: DeviceNDArray,
|
|
91
|
+
ray_directions: DeviceNDArray,
|
|
92
|
+
ray_active: DeviceNDArray,
|
|
93
|
+
ray_times: DeviceNDArray,
|
|
94
|
+
ray_intensities: DeviceNDArray,
|
|
95
|
+
hit_mask_out: DeviceNDArray,
|
|
96
|
+
hit_distances_out: DeviceNDArray,
|
|
97
|
+
hit_times_out: DeviceNDArray,
|
|
98
|
+
*args,
|
|
99
|
+
**kwargs,
|
|
100
|
+
) -> None:
|
|
101
|
+
"""Execute the detection kernel."""
|
|
102
|
+
...
|