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,527 @@
|
|
|
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 Kernels for GridInhomogeneousModel
|
|
36
|
+
|
|
37
|
+
CUDA kernels for GPU-accelerated ray propagation through
|
|
38
|
+
3D grid-based inhomogeneous materials using trilinear interpolation.
|
|
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
|
+
@staticmethod
|
|
54
|
+
def jit(*args, **kwargs):
|
|
55
|
+
"""Return a no-op decorator."""
|
|
56
|
+
|
|
57
|
+
def decorator(func):
|
|
58
|
+
return func
|
|
59
|
+
|
|
60
|
+
if args and callable(args[0]):
|
|
61
|
+
return args[0]
|
|
62
|
+
return decorator
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def is_available():
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
@staticmethod
|
|
69
|
+
def grid(n):
|
|
70
|
+
return 0
|
|
71
|
+
|
|
72
|
+
@staticmethod
|
|
73
|
+
def synchronize():
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
cuda = _FakeCuda() # type: ignore[assignment]
|
|
77
|
+
HAS_CUDA = False
|
|
78
|
+
|
|
79
|
+
from ..device_functions import device_euler_step
|
|
80
|
+
from ..registry import PropagationKernelID, register_kernel
|
|
81
|
+
|
|
82
|
+
SPEED_OF_LIGHT = 299792458.0
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@cuda.jit(device=True)
|
|
86
|
+
def _device_trilinear_interpolate(
|
|
87
|
+
x: float,
|
|
88
|
+
y: float,
|
|
89
|
+
z: float,
|
|
90
|
+
grid,
|
|
91
|
+
x_min: float,
|
|
92
|
+
y_min: float,
|
|
93
|
+
z_min: float,
|
|
94
|
+
dx: float,
|
|
95
|
+
dy: float,
|
|
96
|
+
dz: float,
|
|
97
|
+
nx: int,
|
|
98
|
+
ny: int,
|
|
99
|
+
nz: int,
|
|
100
|
+
) -> float:
|
|
101
|
+
"""Trilinear interpolation in 3D grid."""
|
|
102
|
+
xn = (x - x_min) / dx
|
|
103
|
+
yn = (y - y_min) / dy
|
|
104
|
+
zn = (z - z_min) / dz
|
|
105
|
+
|
|
106
|
+
xn = max(0.0, min(xn, nx - 1.001))
|
|
107
|
+
yn = max(0.0, min(yn, ny - 1.001))
|
|
108
|
+
zn = max(0.0, min(zn, nz - 1.001))
|
|
109
|
+
|
|
110
|
+
x0 = int(xn)
|
|
111
|
+
y0 = int(yn)
|
|
112
|
+
z0 = int(zn)
|
|
113
|
+
|
|
114
|
+
x1 = min(x0 + 1, nx - 1)
|
|
115
|
+
y1 = min(y0 + 1, ny - 1)
|
|
116
|
+
z1 = min(z0 + 1, nz - 1)
|
|
117
|
+
|
|
118
|
+
xd = xn - x0
|
|
119
|
+
yd = yn - y0
|
|
120
|
+
zd = zn - z0
|
|
121
|
+
|
|
122
|
+
c000 = grid[x0, y0, z0]
|
|
123
|
+
c001 = grid[x0, y0, z1]
|
|
124
|
+
c010 = grid[x0, y1, z0]
|
|
125
|
+
c011 = grid[x0, y1, z1]
|
|
126
|
+
c100 = grid[x1, y0, z0]
|
|
127
|
+
c101 = grid[x1, y0, z1]
|
|
128
|
+
c110 = grid[x1, y1, z0]
|
|
129
|
+
c111 = grid[x1, y1, z1]
|
|
130
|
+
|
|
131
|
+
c00 = c000 * (1 - xd) + c100 * xd
|
|
132
|
+
c01 = c001 * (1 - xd) + c101 * xd
|
|
133
|
+
c10 = c010 * (1 - xd) + c110 * xd
|
|
134
|
+
c11 = c011 * (1 - xd) + c111 * xd
|
|
135
|
+
|
|
136
|
+
c0 = c00 * (1 - yd) + c10 * yd
|
|
137
|
+
c1 = c01 * (1 - yd) + c11 * yd
|
|
138
|
+
|
|
139
|
+
return c0 * (1 - zd) + c1 * zd
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@cuda.jit(device=True)
|
|
143
|
+
def _device_grid_n_and_gradient(
|
|
144
|
+
x: float,
|
|
145
|
+
y: float,
|
|
146
|
+
z: float,
|
|
147
|
+
n_grid,
|
|
148
|
+
grad_grid,
|
|
149
|
+
x_min: float,
|
|
150
|
+
y_min: float,
|
|
151
|
+
z_min: float,
|
|
152
|
+
dx: float,
|
|
153
|
+
dy: float,
|
|
154
|
+
dz: float,
|
|
155
|
+
nx: int,
|
|
156
|
+
ny: int,
|
|
157
|
+
nz: int,
|
|
158
|
+
) -> tuple[float, float, float, float]:
|
|
159
|
+
"""Get n and gradient from GridInhomogeneousModel."""
|
|
160
|
+
n = _device_trilinear_interpolate(
|
|
161
|
+
x, y, z, n_grid, x_min, y_min, z_min, dx, dy, dz, nx, ny, nz
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
# Normalize coordinates for gradient interpolation
|
|
165
|
+
xn = (x - x_min) / dx
|
|
166
|
+
yn = (y - y_min) / dy
|
|
167
|
+
zn = (z - z_min) / dz
|
|
168
|
+
|
|
169
|
+
xn = max(0.0, min(xn, nx - 1.001))
|
|
170
|
+
yn = max(0.0, min(yn, ny - 1.001))
|
|
171
|
+
zn = max(0.0, min(zn, nz - 1.001))
|
|
172
|
+
|
|
173
|
+
x0 = int(xn)
|
|
174
|
+
y0 = int(yn)
|
|
175
|
+
z0 = int(zn)
|
|
176
|
+
|
|
177
|
+
x1 = min(x0 + 1, nx - 1)
|
|
178
|
+
y1 = min(y0 + 1, ny - 1)
|
|
179
|
+
z1 = min(z0 + 1, nz - 1)
|
|
180
|
+
|
|
181
|
+
xd = xn - x0
|
|
182
|
+
yd = yn - y0
|
|
183
|
+
zd = zn - z0
|
|
184
|
+
|
|
185
|
+
# Interpolate grad_x
|
|
186
|
+
c000 = grad_grid[x0, y0, z0, 0]
|
|
187
|
+
c001 = grad_grid[x0, y0, z1, 0]
|
|
188
|
+
c010 = grad_grid[x0, y1, z0, 0]
|
|
189
|
+
c011 = grad_grid[x0, y1, z1, 0]
|
|
190
|
+
c100 = grad_grid[x1, y0, z0, 0]
|
|
191
|
+
c101 = grad_grid[x1, y0, z1, 0]
|
|
192
|
+
c110 = grad_grid[x1, y1, z0, 0]
|
|
193
|
+
c111 = grad_grid[x1, y1, z1, 0]
|
|
194
|
+
c00 = c000 * (1 - xd) + c100 * xd
|
|
195
|
+
c01 = c001 * (1 - xd) + c101 * xd
|
|
196
|
+
c10 = c010 * (1 - xd) + c110 * xd
|
|
197
|
+
c11 = c011 * (1 - xd) + c111 * xd
|
|
198
|
+
c0 = c00 * (1 - yd) + c10 * yd
|
|
199
|
+
c1 = c01 * (1 - yd) + c11 * yd
|
|
200
|
+
grad_x = c0 * (1 - zd) + c1 * zd
|
|
201
|
+
|
|
202
|
+
# Interpolate grad_y
|
|
203
|
+
c000 = grad_grid[x0, y0, z0, 1]
|
|
204
|
+
c001 = grad_grid[x0, y0, z1, 1]
|
|
205
|
+
c010 = grad_grid[x0, y1, z0, 1]
|
|
206
|
+
c011 = grad_grid[x0, y1, z1, 1]
|
|
207
|
+
c100 = grad_grid[x1, y0, z0, 1]
|
|
208
|
+
c101 = grad_grid[x1, y0, z1, 1]
|
|
209
|
+
c110 = grad_grid[x1, y1, z0, 1]
|
|
210
|
+
c111 = grad_grid[x1, y1, z1, 1]
|
|
211
|
+
c00 = c000 * (1 - xd) + c100 * xd
|
|
212
|
+
c01 = c001 * (1 - xd) + c101 * xd
|
|
213
|
+
c10 = c010 * (1 - xd) + c110 * xd
|
|
214
|
+
c11 = c011 * (1 - xd) + c111 * xd
|
|
215
|
+
c0 = c00 * (1 - yd) + c10 * yd
|
|
216
|
+
c1 = c01 * (1 - yd) + c11 * yd
|
|
217
|
+
grad_y = c0 * (1 - zd) + c1 * zd
|
|
218
|
+
|
|
219
|
+
# Interpolate grad_z
|
|
220
|
+
c000 = grad_grid[x0, y0, z0, 2]
|
|
221
|
+
c001 = grad_grid[x0, y0, z1, 2]
|
|
222
|
+
c010 = grad_grid[x0, y1, z0, 2]
|
|
223
|
+
c011 = grad_grid[x0, y1, z1, 2]
|
|
224
|
+
c100 = grad_grid[x1, y0, z0, 2]
|
|
225
|
+
c101 = grad_grid[x1, y0, z1, 2]
|
|
226
|
+
c110 = grad_grid[x1, y1, z0, 2]
|
|
227
|
+
c111 = grad_grid[x1, y1, z1, 2]
|
|
228
|
+
c00 = c000 * (1 - xd) + c100 * xd
|
|
229
|
+
c01 = c001 * (1 - xd) + c101 * xd
|
|
230
|
+
c10 = c010 * (1 - xd) + c110 * xd
|
|
231
|
+
c11 = c011 * (1 - xd) + c111 * xd
|
|
232
|
+
c0 = c00 * (1 - yd) + c10 * yd
|
|
233
|
+
c1 = c01 * (1 - yd) + c11 * yd
|
|
234
|
+
grad_z = c0 * (1 - zd) + c1 * zd
|
|
235
|
+
|
|
236
|
+
return n, grad_x, grad_y, grad_z
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@cuda.jit(device=True)
|
|
240
|
+
def _device_grid_euler_step(
|
|
241
|
+
x: float,
|
|
242
|
+
y: float,
|
|
243
|
+
z: float,
|
|
244
|
+
dir_x: float,
|
|
245
|
+
dir_y: float,
|
|
246
|
+
dir_z: float,
|
|
247
|
+
step_size: float,
|
|
248
|
+
n_grid,
|
|
249
|
+
grad_grid,
|
|
250
|
+
x_min: float,
|
|
251
|
+
y_min: float,
|
|
252
|
+
z_min: float,
|
|
253
|
+
dx: float,
|
|
254
|
+
dy: float,
|
|
255
|
+
dz: float,
|
|
256
|
+
nx: int,
|
|
257
|
+
ny: int,
|
|
258
|
+
nz: int,
|
|
259
|
+
) -> tuple[float, float, float, float, float, float, float]:
|
|
260
|
+
"""Euler step for GridInhomogeneousModel."""
|
|
261
|
+
n, grad_x, grad_y, grad_z = _device_grid_n_and_gradient(
|
|
262
|
+
x, y, z, n_grid, grad_grid, x_min, y_min, z_min, dx, dy, dz, nx, ny, nz
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
new_x, new_y, new_z, new_dx, new_dy, new_dz = device_euler_step(
|
|
266
|
+
x, y, z, dir_x, dir_y, dir_z, n, grad_x, grad_y, grad_z, step_size
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
return new_x, new_y, new_z, new_dx, new_dy, new_dz, n
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
@cuda.jit(device=True)
|
|
273
|
+
def _device_grid_rk4_step(
|
|
274
|
+
x: float,
|
|
275
|
+
y: float,
|
|
276
|
+
z: float,
|
|
277
|
+
dir_x: float,
|
|
278
|
+
dir_y: float,
|
|
279
|
+
dir_z: float,
|
|
280
|
+
step_size: float,
|
|
281
|
+
n_grid,
|
|
282
|
+
grad_grid,
|
|
283
|
+
x_min: float,
|
|
284
|
+
y_min: float,
|
|
285
|
+
z_min: float,
|
|
286
|
+
grid_dx: float,
|
|
287
|
+
grid_dy: float,
|
|
288
|
+
grid_dz: float,
|
|
289
|
+
nx: int,
|
|
290
|
+
ny: int,
|
|
291
|
+
nz: int,
|
|
292
|
+
) -> tuple[float, float, float, float, float, float, float]:
|
|
293
|
+
"""RK4 step for GridInhomogeneousModel."""
|
|
294
|
+
h = step_size
|
|
295
|
+
h2 = h / 2.0
|
|
296
|
+
|
|
297
|
+
def get_n_and_kappa(px, py, pz, dx, dy, dz):
|
|
298
|
+
n, gx, gy, gz = _device_grid_n_and_gradient(
|
|
299
|
+
px,
|
|
300
|
+
py,
|
|
301
|
+
pz,
|
|
302
|
+
n_grid,
|
|
303
|
+
grad_grid,
|
|
304
|
+
x_min,
|
|
305
|
+
y_min,
|
|
306
|
+
z_min,
|
|
307
|
+
grid_dx,
|
|
308
|
+
grid_dy,
|
|
309
|
+
grid_dz,
|
|
310
|
+
nx,
|
|
311
|
+
ny,
|
|
312
|
+
nz,
|
|
313
|
+
)
|
|
314
|
+
dot = dx * gx + dy * gy + dz * gz
|
|
315
|
+
kx = (gx - dot * dx) / n
|
|
316
|
+
ky = (gy - dot * dy) / n
|
|
317
|
+
kz = (gz - dot * dz) / n
|
|
318
|
+
return n, kx, ky, kz
|
|
319
|
+
|
|
320
|
+
def normalize(dx, dy, dz):
|
|
321
|
+
norm = math.sqrt(dx * dx + dy * dy + dz * dz)
|
|
322
|
+
if norm < 1e-12:
|
|
323
|
+
norm = 1.0
|
|
324
|
+
return dx / norm, dy / norm, dz / norm
|
|
325
|
+
|
|
326
|
+
# k1
|
|
327
|
+
n0, kx1, ky1, kz1 = get_n_and_kappa(x, y, z, dir_x, dir_y, dir_z)
|
|
328
|
+
k1_rx, k1_ry, k1_rz = dir_x, dir_y, dir_z
|
|
329
|
+
k1_dx, k1_dy, k1_dz = kx1, ky1, kz1
|
|
330
|
+
|
|
331
|
+
# k2
|
|
332
|
+
px = x + h2 * k1_rx
|
|
333
|
+
py = y + h2 * k1_ry
|
|
334
|
+
pz = z + h2 * k1_rz
|
|
335
|
+
dx, dy, dz = normalize(dir_x + h2 * k1_dx, dir_y + h2 * k1_dy, dir_z + h2 * k1_dz)
|
|
336
|
+
n1, kx2, ky2, kz2 = get_n_and_kappa(px, py, pz, dx, dy, dz)
|
|
337
|
+
k2_rx, k2_ry, k2_rz = dx, dy, dz
|
|
338
|
+
k2_dx, k2_dy, k2_dz = kx2, ky2, kz2
|
|
339
|
+
|
|
340
|
+
# k3
|
|
341
|
+
px = x + h2 * k2_rx
|
|
342
|
+
py = y + h2 * k2_ry
|
|
343
|
+
pz = z + h2 * k2_rz
|
|
344
|
+
dx, dy, dz = normalize(dir_x + h2 * k2_dx, dir_y + h2 * k2_dy, dir_z + h2 * k2_dz)
|
|
345
|
+
n2, kx3, ky3, kz3 = get_n_and_kappa(px, py, pz, dx, dy, dz)
|
|
346
|
+
k3_rx, k3_ry, k3_rz = dx, dy, dz
|
|
347
|
+
k3_dx, k3_dy, k3_dz = kx3, ky3, kz3
|
|
348
|
+
|
|
349
|
+
# k4
|
|
350
|
+
px = x + h * k3_rx
|
|
351
|
+
py = y + h * k3_ry
|
|
352
|
+
pz = z + h * k3_rz
|
|
353
|
+
dx, dy, dz = normalize(dir_x + h * k3_dx, dir_y + h * k3_dy, dir_z + h * k3_dz)
|
|
354
|
+
n3, kx4, ky4, kz4 = get_n_and_kappa(px, py, pz, dx, dy, dz)
|
|
355
|
+
|
|
356
|
+
# Final RK4 combination
|
|
357
|
+
new_x = x + (h / 6.0) * (k1_rx + 2 * k2_rx + 2 * k3_rx + dx)
|
|
358
|
+
new_y = y + (h / 6.0) * (k1_ry + 2 * k2_ry + 2 * k3_ry + dy)
|
|
359
|
+
new_z = z + (h / 6.0) * (k1_rz + 2 * k2_rz + 2 * k3_rz + dz)
|
|
360
|
+
|
|
361
|
+
new_dx = dir_x + (h / 6.0) * (k1_dx + 2 * k2_dx + 2 * k3_dx + kx4)
|
|
362
|
+
new_dy = dir_y + (h / 6.0) * (k1_dy + 2 * k2_dy + 2 * k3_dy + ky4)
|
|
363
|
+
new_dz = dir_z + (h / 6.0) * (k1_dz + 2 * k2_dz + 2 * k3_dz + kz4)
|
|
364
|
+
|
|
365
|
+
new_dx, new_dy, new_dz = normalize(new_dx, new_dy, new_dz)
|
|
366
|
+
|
|
367
|
+
n_avg = (n0 + 4 * n1 + n2) / 6.0
|
|
368
|
+
|
|
369
|
+
return new_x, new_y, new_z, new_dx, new_dy, new_dz, n_avg
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
@register_kernel(PropagationKernelID.GRID_EULER)
|
|
373
|
+
@cuda.jit
|
|
374
|
+
def _kernel_grid_inhomogeneous_euler(
|
|
375
|
+
positions,
|
|
376
|
+
directions,
|
|
377
|
+
active,
|
|
378
|
+
geo_path,
|
|
379
|
+
opt_path,
|
|
380
|
+
acc_time,
|
|
381
|
+
step_size: float,
|
|
382
|
+
num_steps: int,
|
|
383
|
+
x_min: float,
|
|
384
|
+
y_min: float,
|
|
385
|
+
z_min: float,
|
|
386
|
+
grid_dx: float,
|
|
387
|
+
grid_dy: float,
|
|
388
|
+
grid_dz: float,
|
|
389
|
+
nx: int,
|
|
390
|
+
ny: int,
|
|
391
|
+
nz: int,
|
|
392
|
+
n_grid,
|
|
393
|
+
grad_grid,
|
|
394
|
+
wavelength: float,
|
|
395
|
+
):
|
|
396
|
+
"""GPU kernel for GridInhomogeneousModel using Euler integration."""
|
|
397
|
+
c = SPEED_OF_LIGHT
|
|
398
|
+
|
|
399
|
+
idx = cuda.grid(1)
|
|
400
|
+
if idx >= positions.shape[0]:
|
|
401
|
+
return
|
|
402
|
+
if not active[idx]:
|
|
403
|
+
return
|
|
404
|
+
|
|
405
|
+
x = positions[idx, 0]
|
|
406
|
+
y = positions[idx, 1]
|
|
407
|
+
z = positions[idx, 2]
|
|
408
|
+
dx = directions[idx, 0]
|
|
409
|
+
dy = directions[idx, 1]
|
|
410
|
+
dz = directions[idx, 2]
|
|
411
|
+
gp = geo_path[idx]
|
|
412
|
+
op = opt_path[idx]
|
|
413
|
+
at = acc_time[idx]
|
|
414
|
+
|
|
415
|
+
for _ in range(num_steps):
|
|
416
|
+
x, y, z, dx, dy, dz, n = _device_grid_euler_step(
|
|
417
|
+
x,
|
|
418
|
+
y,
|
|
419
|
+
z,
|
|
420
|
+
dx,
|
|
421
|
+
dy,
|
|
422
|
+
dz,
|
|
423
|
+
step_size,
|
|
424
|
+
n_grid,
|
|
425
|
+
grad_grid,
|
|
426
|
+
x_min,
|
|
427
|
+
y_min,
|
|
428
|
+
z_min,
|
|
429
|
+
grid_dx,
|
|
430
|
+
grid_dy,
|
|
431
|
+
grid_dz,
|
|
432
|
+
nx,
|
|
433
|
+
ny,
|
|
434
|
+
nz,
|
|
435
|
+
)
|
|
436
|
+
gp += step_size
|
|
437
|
+
op += n * step_size
|
|
438
|
+
at += n * step_size / c
|
|
439
|
+
|
|
440
|
+
positions[idx, 0] = x
|
|
441
|
+
positions[idx, 1] = y
|
|
442
|
+
positions[idx, 2] = z
|
|
443
|
+
directions[idx, 0] = dx
|
|
444
|
+
directions[idx, 1] = dy
|
|
445
|
+
directions[idx, 2] = dz
|
|
446
|
+
geo_path[idx] = gp
|
|
447
|
+
opt_path[idx] = op
|
|
448
|
+
acc_time[idx] = at
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
@register_kernel(PropagationKernelID.GRID_RK4)
|
|
452
|
+
@cuda.jit
|
|
453
|
+
def _kernel_grid_inhomogeneous_rk4(
|
|
454
|
+
positions,
|
|
455
|
+
directions,
|
|
456
|
+
active,
|
|
457
|
+
geo_path,
|
|
458
|
+
opt_path,
|
|
459
|
+
acc_time,
|
|
460
|
+
step_size: float,
|
|
461
|
+
num_steps: int,
|
|
462
|
+
x_min: float,
|
|
463
|
+
y_min: float,
|
|
464
|
+
z_min: float,
|
|
465
|
+
grid_dx: float,
|
|
466
|
+
grid_dy: float,
|
|
467
|
+
grid_dz: float,
|
|
468
|
+
nx: int,
|
|
469
|
+
ny: int,
|
|
470
|
+
nz: int,
|
|
471
|
+
n_grid,
|
|
472
|
+
grad_grid,
|
|
473
|
+
wavelength: float,
|
|
474
|
+
):
|
|
475
|
+
"""GPU kernel for GridInhomogeneousModel using RK4 integration."""
|
|
476
|
+
c = SPEED_OF_LIGHT
|
|
477
|
+
|
|
478
|
+
idx = cuda.grid(1)
|
|
479
|
+
if idx >= positions.shape[0]:
|
|
480
|
+
return
|
|
481
|
+
if not active[idx]:
|
|
482
|
+
return
|
|
483
|
+
|
|
484
|
+
x = positions[idx, 0]
|
|
485
|
+
y = positions[idx, 1]
|
|
486
|
+
z = positions[idx, 2]
|
|
487
|
+
dx = directions[idx, 0]
|
|
488
|
+
dy = directions[idx, 1]
|
|
489
|
+
dz = directions[idx, 2]
|
|
490
|
+
gp = geo_path[idx]
|
|
491
|
+
op = opt_path[idx]
|
|
492
|
+
at = acc_time[idx]
|
|
493
|
+
|
|
494
|
+
for _ in range(num_steps):
|
|
495
|
+
x, y, z, dx, dy, dz, n_avg = _device_grid_rk4_step(
|
|
496
|
+
x,
|
|
497
|
+
y,
|
|
498
|
+
z,
|
|
499
|
+
dx,
|
|
500
|
+
dy,
|
|
501
|
+
dz,
|
|
502
|
+
step_size,
|
|
503
|
+
n_grid,
|
|
504
|
+
grad_grid,
|
|
505
|
+
x_min,
|
|
506
|
+
y_min,
|
|
507
|
+
z_min,
|
|
508
|
+
grid_dx,
|
|
509
|
+
grid_dy,
|
|
510
|
+
grid_dz,
|
|
511
|
+
nx,
|
|
512
|
+
ny,
|
|
513
|
+
nz,
|
|
514
|
+
)
|
|
515
|
+
gp += step_size
|
|
516
|
+
op += n_avg * step_size
|
|
517
|
+
at += n_avg * step_size / c
|
|
518
|
+
|
|
519
|
+
positions[idx, 0] = x
|
|
520
|
+
positions[idx, 1] = y
|
|
521
|
+
positions[idx, 2] = z
|
|
522
|
+
directions[idx, 0] = dx
|
|
523
|
+
directions[idx, 1] = dy
|
|
524
|
+
directions[idx, 2] = dz
|
|
525
|
+
geo_path[idx] = gp
|
|
526
|
+
opt_path[idx] = op
|
|
527
|
+
acc_time[idx] = at
|
|
@@ -0,0 +1,105 @@
|
|
|
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
|
+
Propagation Kernel Protocol
|
|
36
|
+
|
|
37
|
+
Defines the interface for material propagation kernels that integrate
|
|
38
|
+
rays through inhomogeneous materials.
|
|
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 PropagationKernelProtocol(Protocol):
|
|
51
|
+
"""
|
|
52
|
+
Protocol for material propagation kernels.
|
|
53
|
+
|
|
54
|
+
Propagation kernels integrate rays through materials with spatially-varying
|
|
55
|
+
refractive index. They update ray positions, directions, and accumulated
|
|
56
|
+
path lengths based on the material's optical properties.
|
|
57
|
+
|
|
58
|
+
The kernel signature varies depending on the material type:
|
|
59
|
+
- Simple: 1D LUT interpolation (altitude only)
|
|
60
|
+
- Spectral: 2D LUT interpolation (altitude × wavelength)
|
|
61
|
+
- Grid: 3D trilinear interpolation
|
|
62
|
+
|
|
63
|
+
Common Parameters
|
|
64
|
+
-----------------
|
|
65
|
+
positions : DeviceNDArray, shape (N, 3)
|
|
66
|
+
Ray positions in meters (in/out)
|
|
67
|
+
directions : DeviceNDArray, shape (N, 3)
|
|
68
|
+
Ray direction unit vectors (in/out)
|
|
69
|
+
active : DeviceNDArray, shape (N,)
|
|
70
|
+
Boolean mask of active rays
|
|
71
|
+
geo_path : DeviceNDArray, shape (N,)
|
|
72
|
+
Accumulated geometric path length in meters (in/out)
|
|
73
|
+
opt_path : DeviceNDArray, shape (N,)
|
|
74
|
+
Accumulated optical path length (n·ds integral) (in/out)
|
|
75
|
+
acc_time : DeviceNDArray, shape (N,)
|
|
76
|
+
Accumulated travel time in seconds (in/out)
|
|
77
|
+
step_size : float
|
|
78
|
+
Integration step size in meters
|
|
79
|
+
num_steps : int
|
|
80
|
+
Number of integration steps to perform
|
|
81
|
+
|
|
82
|
+
Notes
|
|
83
|
+
-----
|
|
84
|
+
Kernels are registered with the central registry using @register_kernel
|
|
85
|
+
decorator and PropagationKernelID enum values.
|
|
86
|
+
|
|
87
|
+
The actual signature includes material-specific parameters (LUT arrays,
|
|
88
|
+
center coordinates, etc.) that vary by kernel type.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
def __call__(
|
|
92
|
+
self,
|
|
93
|
+
positions: DeviceNDArray,
|
|
94
|
+
directions: DeviceNDArray,
|
|
95
|
+
active: DeviceNDArray,
|
|
96
|
+
geo_path: DeviceNDArray,
|
|
97
|
+
opt_path: DeviceNDArray,
|
|
98
|
+
acc_time: DeviceNDArray,
|
|
99
|
+
step_size: float,
|
|
100
|
+
num_steps: int,
|
|
101
|
+
*args,
|
|
102
|
+
**kwargs,
|
|
103
|
+
) -> None:
|
|
104
|
+
"""Execute the propagation kernel."""
|
|
105
|
+
...
|