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,258 @@
|
|
|
1
|
+
# The Clear BSD License
|
|
2
|
+
#
|
|
3
|
+
# Copyright (c) 2026 Tobias Heibges
|
|
4
|
+
# All rights reserved.
|
|
5
|
+
#
|
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
|
7
|
+
# modification, are permitted (subject to the limitations in the disclaimer
|
|
8
|
+
# below) provided that the following conditions are met:
|
|
9
|
+
#
|
|
10
|
+
# * Redistributions of source code must retain the above copyright notice,
|
|
11
|
+
# this list of conditions and the following disclaimer.
|
|
12
|
+
#
|
|
13
|
+
# * Redistributions in binary form must reproduce the above copyright
|
|
14
|
+
# notice, this list of conditions and the following disclaimer in the
|
|
15
|
+
# documentation and/or other materials provided with the distribution.
|
|
16
|
+
#
|
|
17
|
+
# * Neither the name of the copyright holder nor the names of its
|
|
18
|
+
# contributors may be used to endorse or promote products derived from this
|
|
19
|
+
# software without specific prior written permission.
|
|
20
|
+
#
|
|
21
|
+
# NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
|
|
22
|
+
# THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
23
|
+
# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
24
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
|
25
|
+
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
26
|
+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
27
|
+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
28
|
+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
29
|
+
# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
|
|
30
|
+
# IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
31
|
+
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
32
|
+
# POSSIBILITY OF SUCH DAMAGE.
|
|
33
|
+
|
|
34
|
+
"""
|
|
35
|
+
Uniform Diverging Beam Source Implementation
|
|
36
|
+
|
|
37
|
+
Provides a diverging beam source with uniform solid angle distribution,
|
|
38
|
+
suitable for modeling sources that emit uniformly over a cone.
|
|
39
|
+
|
|
40
|
+
This differs from DivergingBeam which samples phi uniformly (creating
|
|
41
|
+
higher ray density near the cone axis). UniformDivergingBeam samples
|
|
42
|
+
cos(phi) uniformly, giving uniform ray density per solid angle.
|
|
43
|
+
|
|
44
|
+
Examples
|
|
45
|
+
--------
|
|
46
|
+
>>> from lsurf.sources import UniformDivergingBeam
|
|
47
|
+
>>>
|
|
48
|
+
>>> source = UniformDivergingBeam(
|
|
49
|
+
... origin=(0, 0, 0),
|
|
50
|
+
... mean_direction=(0, 0, 1),
|
|
51
|
+
... divergence_angle=0.1, # ~5.7 degrees
|
|
52
|
+
... num_rays=1000,
|
|
53
|
+
... wavelength=850e-9,
|
|
54
|
+
... power=1.0
|
|
55
|
+
... )
|
|
56
|
+
>>> rays = source.generate()
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
import numpy as np
|
|
60
|
+
|
|
61
|
+
from ..utilities.ray_data import RayBatch
|
|
62
|
+
from .base import RaySource
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class UniformDivergingBeam(RaySource):
|
|
66
|
+
"""
|
|
67
|
+
Beam with angular divergence and uniform solid angle distribution.
|
|
68
|
+
|
|
69
|
+
Generates rays from a single point with directions uniformly distributed
|
|
70
|
+
over the solid angle of a cone around the mean direction. Each element
|
|
71
|
+
of solid angle receives equal ray density.
|
|
72
|
+
|
|
73
|
+
This is physically correct for sources that emit uniformly over a cone
|
|
74
|
+
(e.g., idealized point sources with angular limits).
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
origin : tuple of float
|
|
79
|
+
Source position (x, y, z) in meters.
|
|
80
|
+
mean_direction : tuple of float
|
|
81
|
+
Mean beam direction (dx, dy, dz), will be normalized.
|
|
82
|
+
divergence_angle : float
|
|
83
|
+
Half-angle divergence in radians (cone half-angle).
|
|
84
|
+
Must be in range (0, π/2).
|
|
85
|
+
num_rays : int
|
|
86
|
+
Number of rays to generate.
|
|
87
|
+
wavelength : float or tuple of float
|
|
88
|
+
Single wavelength (m) or (min, max) range.
|
|
89
|
+
power : float, optional
|
|
90
|
+
Total source power in watts. Default is 1.0.
|
|
91
|
+
|
|
92
|
+
Attributes
|
|
93
|
+
----------
|
|
94
|
+
origin : ndarray, shape (3,)
|
|
95
|
+
Source position.
|
|
96
|
+
mean_direction : ndarray, shape (3,)
|
|
97
|
+
Normalized mean beam direction.
|
|
98
|
+
divergence_angle : float
|
|
99
|
+
Cone half-angle in radians.
|
|
100
|
+
solid_angle : float
|
|
101
|
+
Solid angle of the cone in steradians: Ω = 2π(1 - cos(θ))
|
|
102
|
+
|
|
103
|
+
Notes
|
|
104
|
+
-----
|
|
105
|
+
The key difference from DivergingBeam is the sampling of the polar angle:
|
|
106
|
+
|
|
107
|
+
- DivergingBeam: phi ~ Uniform(0, divergence_angle)
|
|
108
|
+
This creates higher ray density near the cone axis.
|
|
109
|
+
|
|
110
|
+
- UniformDivergingBeam: cos(phi) ~ Uniform(cos(divergence_angle), 1)
|
|
111
|
+
This creates uniform ray density per solid angle.
|
|
112
|
+
|
|
113
|
+
The solid angle of a cone with half-angle θ is:
|
|
114
|
+
Ω = 2π(1 - cos(θ))
|
|
115
|
+
|
|
116
|
+
For uniform sampling over this solid angle, we need:
|
|
117
|
+
dN/dΩ = constant
|
|
118
|
+
|
|
119
|
+
This requires sampling cos(phi) uniformly, not phi.
|
|
120
|
+
|
|
121
|
+
Examples
|
|
122
|
+
--------
|
|
123
|
+
>>> # Uniform emission over 10 degree cone
|
|
124
|
+
>>> source = UniformDivergingBeam(
|
|
125
|
+
... origin=(0, 0, 0),
|
|
126
|
+
... mean_direction=(0, 0, 1),
|
|
127
|
+
... divergence_angle=np.radians(10),
|
|
128
|
+
... num_rays=5000,
|
|
129
|
+
... wavelength=1550e-9,
|
|
130
|
+
... power=1e-3
|
|
131
|
+
... )
|
|
132
|
+
|
|
133
|
+
>>> # Compare solid angle coverage
|
|
134
|
+
>>> # Expected: uniform distribution in cos(phi)
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
def __init__(
|
|
138
|
+
self,
|
|
139
|
+
origin: tuple[float, float, float],
|
|
140
|
+
mean_direction: tuple[float, float, float],
|
|
141
|
+
divergence_angle: float,
|
|
142
|
+
num_rays: int,
|
|
143
|
+
wavelength: float | tuple[float, float],
|
|
144
|
+
power: float = 1.0,
|
|
145
|
+
):
|
|
146
|
+
"""
|
|
147
|
+
Initialize uniform diverging beam.
|
|
148
|
+
|
|
149
|
+
Parameters
|
|
150
|
+
----------
|
|
151
|
+
origin : tuple of float
|
|
152
|
+
Source position (x, y, z) in meters.
|
|
153
|
+
mean_direction : tuple of float
|
|
154
|
+
Mean beam direction, will be normalized.
|
|
155
|
+
divergence_angle : float
|
|
156
|
+
Cone half-angle in radians.
|
|
157
|
+
num_rays : int
|
|
158
|
+
Number of rays to generate.
|
|
159
|
+
wavelength : float or tuple of float
|
|
160
|
+
Wavelength in meters or (min, max) range.
|
|
161
|
+
power : float, optional
|
|
162
|
+
Total source power in watts. Default is 1.0.
|
|
163
|
+
|
|
164
|
+
Raises
|
|
165
|
+
------
|
|
166
|
+
ValueError
|
|
167
|
+
If divergence_angle not in (0, π/2).
|
|
168
|
+
"""
|
|
169
|
+
super().__init__(num_rays, wavelength, power)
|
|
170
|
+
self.origin = np.array(origin, dtype=np.float32)
|
|
171
|
+
self.divergence_angle = divergence_angle
|
|
172
|
+
|
|
173
|
+
# Normalize mean direction
|
|
174
|
+
mean_direction_arr = np.array(mean_direction, dtype=np.float32)
|
|
175
|
+
self.mean_direction = mean_direction_arr / np.linalg.norm(mean_direction_arr)
|
|
176
|
+
|
|
177
|
+
if divergence_angle <= 0 or divergence_angle >= np.pi / 2:
|
|
178
|
+
raise ValueError("divergence_angle must be in (0, π/2)")
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
def solid_angle(self) -> float:
|
|
182
|
+
"""
|
|
183
|
+
Solid angle of the cone in steradians.
|
|
184
|
+
|
|
185
|
+
Returns
|
|
186
|
+
-------
|
|
187
|
+
float
|
|
188
|
+
Ω = 2π(1 - cos(θ)) where θ is the divergence half-angle.
|
|
189
|
+
"""
|
|
190
|
+
return 2 * np.pi * (1 - np.cos(self.divergence_angle))
|
|
191
|
+
|
|
192
|
+
def generate(self) -> RayBatch:
|
|
193
|
+
"""
|
|
194
|
+
Generate uniformly diverging beam.
|
|
195
|
+
|
|
196
|
+
Creates rays from the origin with directions uniformly distributed
|
|
197
|
+
over the solid angle of a cone around the mean direction.
|
|
198
|
+
|
|
199
|
+
Returns
|
|
200
|
+
-------
|
|
201
|
+
RayBatch
|
|
202
|
+
Ray batch with uniform solid angle distribution.
|
|
203
|
+
|
|
204
|
+
Notes
|
|
205
|
+
-----
|
|
206
|
+
The sampling uses:
|
|
207
|
+
- theta ~ Uniform(0, 2π) for azimuthal angle
|
|
208
|
+
- cos(phi) ~ Uniform(cos(divergence_angle), 1) for polar angle
|
|
209
|
+
|
|
210
|
+
This ensures dN/dΩ = constant over the cone.
|
|
211
|
+
"""
|
|
212
|
+
rays = self._allocate_rays()
|
|
213
|
+
|
|
214
|
+
# All rays start at origin
|
|
215
|
+
rays.positions[:] = self.origin
|
|
216
|
+
|
|
217
|
+
# Create perpendicular basis for direction perturbation
|
|
218
|
+
v1, v2 = self._create_perpendicular_basis(self.mean_direction)
|
|
219
|
+
|
|
220
|
+
# Generate random angles within cone
|
|
221
|
+
# Azimuthal angle: uniform in [0, 2π)
|
|
222
|
+
theta = np.random.uniform(0, 2 * np.pi, self.num_rays)
|
|
223
|
+
|
|
224
|
+
# Polar angle: sample cos(phi) uniformly for uniform solid angle distribution
|
|
225
|
+
# cos(phi) ~ Uniform(cos(divergence_angle), 1)
|
|
226
|
+
cos_phi = np.random.uniform(np.cos(self.divergence_angle), 1.0, self.num_rays)
|
|
227
|
+
phi = np.arccos(cos_phi)
|
|
228
|
+
|
|
229
|
+
# Compute perturbed directions
|
|
230
|
+
sin_phi = np.sin(phi)
|
|
231
|
+
|
|
232
|
+
# Direction in local coordinates (z = mean_direction)
|
|
233
|
+
local_x = sin_phi * np.cos(theta)
|
|
234
|
+
local_y = sin_phi * np.sin(theta)
|
|
235
|
+
local_z = cos_phi
|
|
236
|
+
|
|
237
|
+
# Transform to global coordinates
|
|
238
|
+
rays.directions[:] = (
|
|
239
|
+
local_x[:, np.newaxis] * v1
|
|
240
|
+
+ local_y[:, np.newaxis] * v2
|
|
241
|
+
+ local_z[:, np.newaxis] * self.mean_direction
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Normalize (should already be normalized, but ensure numerical stability)
|
|
245
|
+
norms = np.linalg.norm(rays.directions, axis=1, keepdims=True)
|
|
246
|
+
rays.directions[:] /= norms
|
|
247
|
+
|
|
248
|
+
self._assign_wavelengths(rays)
|
|
249
|
+
|
|
250
|
+
return rays
|
|
251
|
+
|
|
252
|
+
def __repr__(self) -> str:
|
|
253
|
+
"""Return string representation."""
|
|
254
|
+
return (
|
|
255
|
+
f"UniformDivergingBeam(origin={self.origin.tolist()}, "
|
|
256
|
+
f"divergence_angle={self.divergence_angle:.4f}, "
|
|
257
|
+
f"solid_angle={self.solid_angle:.6f} sr)"
|
|
258
|
+
)
|
|
@@ -0,0 +1,184 @@
|
|
|
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
|
+
Surfaces Module
|
|
36
|
+
|
|
37
|
+
Unified surface interface for ray tracing. All surfaces follow the same
|
|
38
|
+
protocol regardless of GPU capability.
|
|
39
|
+
|
|
40
|
+
Surface Types
|
|
41
|
+
-------------
|
|
42
|
+
GPU-Capable (fast signed distance computation):
|
|
43
|
+
- PlaneSurface: Infinite flat plane (geometry_id=1)
|
|
44
|
+
- SphereSurface: Spherical surface (geometry_id=2)
|
|
45
|
+
- GPUGerstnerWaveSurface: Flat-earth Gerstner wave, single wave (geometry_id=3)
|
|
46
|
+
- GPUCurvedWaveSurface: Curved-earth wave, single wave (geometry_id=4)
|
|
47
|
+
- GPUMultiCurvedWaveSurface: Curved-earth wave, multi-wave up to 8 (geometry_id=5)
|
|
48
|
+
- BoundedPlaneSurface: Bounded rectangular plane (geometry_id=6)
|
|
49
|
+
- AnnularPlaneSurface: Annular (ring-shaped) plane (geometry_id=7)
|
|
50
|
+
|
|
51
|
+
CPU-Only (complex geometry, ray marching, multiple waves):
|
|
52
|
+
- GerstnerWaveSurface: Flat-earth ocean waves (multi-wave)
|
|
53
|
+
- CurvedWaveSurface: Curved-earth ocean waves (multi-wave)
|
|
54
|
+
|
|
55
|
+
Protocol
|
|
56
|
+
--------
|
|
57
|
+
All surfaces provide:
|
|
58
|
+
- role: SurfaceRole (DETECTOR, OPTICAL, ABSORBER)
|
|
59
|
+
- name: Human-readable identifier
|
|
60
|
+
- gpu_capable: Whether GPU acceleration is available
|
|
61
|
+
- geometry_id: Integer ID for GPU kernels (0 for CPU-only)
|
|
62
|
+
- intersect(): CPU ray intersection (always available)
|
|
63
|
+
- normal_at(): CPU surface normals (always available)
|
|
64
|
+
- get_materials(): Return materials for Fresnel calculation
|
|
65
|
+
|
|
66
|
+
GPU-capable surfaces additionally provide:
|
|
67
|
+
- signed_distance(): For GPU kernels
|
|
68
|
+
- get_gpu_parameters(): Parameters for GPU kernels
|
|
69
|
+
|
|
70
|
+
Examples
|
|
71
|
+
--------
|
|
72
|
+
>>> from lsurf.surfaces import PlaneSurface, GerstnerWaveSurface, SurfaceRole
|
|
73
|
+
>>> from lsurf.materials import AIR_STP, WATER
|
|
74
|
+
>>>
|
|
75
|
+
>>> # GPU-capable plane
|
|
76
|
+
>>> detector = PlaneSurface(
|
|
77
|
+
... point=(0, 0, 1000),
|
|
78
|
+
... normal=(0, 0, 1),
|
|
79
|
+
... role=SurfaceRole.DETECTOR,
|
|
80
|
+
... name="detector",
|
|
81
|
+
... )
|
|
82
|
+
>>> print(detector.gpu_capable) # True
|
|
83
|
+
>>>
|
|
84
|
+
>>> # CPU-only wave surface
|
|
85
|
+
>>> from lsurf.surfaces import GerstnerWaveParams
|
|
86
|
+
>>> wave = GerstnerWaveParams(amplitude=1.0, wavelength=50.0)
|
|
87
|
+
>>> ocean = GerstnerWaveSurface(
|
|
88
|
+
... wave_params=[wave],
|
|
89
|
+
... role=SurfaceRole.OPTICAL,
|
|
90
|
+
... name="ocean",
|
|
91
|
+
... material_front=AIR_STP,
|
|
92
|
+
... material_back=WATER,
|
|
93
|
+
... )
|
|
94
|
+
>>> print(ocean.gpu_capable) # False
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
# Protocol and enums
|
|
98
|
+
from .protocol import (
|
|
99
|
+
Surface,
|
|
100
|
+
SurfaceRole,
|
|
101
|
+
GPUSurface, # Alias for backwards compatibility
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Surface type registry
|
|
105
|
+
from .registry import (
|
|
106
|
+
# New names
|
|
107
|
+
SurfaceTypeInfo,
|
|
108
|
+
register_surface_type,
|
|
109
|
+
get_surface_type,
|
|
110
|
+
get_surface_type_id,
|
|
111
|
+
get_surface_class,
|
|
112
|
+
is_gpu_capable,
|
|
113
|
+
list_surface_types,
|
|
114
|
+
surface_type_exists,
|
|
115
|
+
# Backwards compatibility aliases
|
|
116
|
+
GeometryInfo,
|
|
117
|
+
register_geometry,
|
|
118
|
+
get_geometry_info,
|
|
119
|
+
get_geometry_id,
|
|
120
|
+
list_geometries,
|
|
121
|
+
geometry_exists,
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# GPU-capable surfaces
|
|
125
|
+
from .gpu import (
|
|
126
|
+
PlaneSurface,
|
|
127
|
+
SphereSurface,
|
|
128
|
+
RecordingSphereSurface,
|
|
129
|
+
LocalRecordingSphereSurface,
|
|
130
|
+
GPUGerstnerWaveSurface,
|
|
131
|
+
GPUCurvedWaveSurface,
|
|
132
|
+
GPUMultiCurvedWaveSurface,
|
|
133
|
+
BoundedPlaneSurface,
|
|
134
|
+
AnnularPlaneSurface,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# CPU-only surfaces
|
|
138
|
+
from .cpu import (
|
|
139
|
+
GerstnerWaveSurface,
|
|
140
|
+
CurvedWaveSurface,
|
|
141
|
+
GerstnerWaveParams,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
# Constants
|
|
145
|
+
EARTH_RADIUS = 6.371e6 # Earth's mean radius in meters
|
|
146
|
+
|
|
147
|
+
__all__ = [
|
|
148
|
+
# Protocol
|
|
149
|
+
"Surface",
|
|
150
|
+
"SurfaceRole",
|
|
151
|
+
"GPUSurface",
|
|
152
|
+
# Surface type registry
|
|
153
|
+
"SurfaceTypeInfo",
|
|
154
|
+
"register_surface_type",
|
|
155
|
+
"get_surface_type",
|
|
156
|
+
"get_surface_type_id",
|
|
157
|
+
"get_surface_class",
|
|
158
|
+
"is_gpu_capable",
|
|
159
|
+
"list_surface_types",
|
|
160
|
+
"surface_type_exists",
|
|
161
|
+
# Backwards compatibility
|
|
162
|
+
"GeometryInfo",
|
|
163
|
+
"register_geometry",
|
|
164
|
+
"get_geometry_info",
|
|
165
|
+
"get_geometry_id",
|
|
166
|
+
"list_geometries",
|
|
167
|
+
"geometry_exists",
|
|
168
|
+
# GPU-capable surfaces
|
|
169
|
+
"PlaneSurface",
|
|
170
|
+
"SphereSurface",
|
|
171
|
+
"RecordingSphereSurface",
|
|
172
|
+
"LocalRecordingSphereSurface",
|
|
173
|
+
"GPUGerstnerWaveSurface",
|
|
174
|
+
"GPUCurvedWaveSurface",
|
|
175
|
+
"GPUMultiCurvedWaveSurface",
|
|
176
|
+
"BoundedPlaneSurface",
|
|
177
|
+
"AnnularPlaneSurface",
|
|
178
|
+
# CPU-only surfaces
|
|
179
|
+
"GerstnerWaveSurface",
|
|
180
|
+
"CurvedWaveSurface",
|
|
181
|
+
"GerstnerWaveParams",
|
|
182
|
+
# Constants
|
|
183
|
+
"EARTH_RADIUS",
|
|
184
|
+
]
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
CPU-Only Surfaces
|
|
36
|
+
|
|
37
|
+
Surfaces with complex geometry that require CPU-based ray marching.
|
|
38
|
+
These surfaces do not support GPU acceleration but provide the same
|
|
39
|
+
interface as GPU-capable surfaces.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from .gerstner_wave import GerstnerWaveSurface
|
|
43
|
+
from .curved_wave import CurvedWaveSurface
|
|
44
|
+
from .wave_params import GerstnerWaveParams
|
|
45
|
+
|
|
46
|
+
__all__ = [
|
|
47
|
+
"GerstnerWaveSurface",
|
|
48
|
+
"CurvedWaveSurface",
|
|
49
|
+
"GerstnerWaveParams",
|
|
50
|
+
]
|