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,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
|