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,373 @@
|
|
|
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
|
+
Surface Type Registry
|
|
36
|
+
|
|
37
|
+
A flexible registry for surface types (plane, sphere, wave, etc.).
|
|
38
|
+
Each surface type is registered with a name, capability ("gpu" or "cpu"),
|
|
39
|
+
an integer ID for GPU kernel dispatch, and the surface class itself.
|
|
40
|
+
|
|
41
|
+
The registry ensures:
|
|
42
|
+
- Each surface type name is unique
|
|
43
|
+
- Each (capability, id) combination is unique (no GPU id conflicts)
|
|
44
|
+
- GPU IDs are positive integers (used by GPU kernels)
|
|
45
|
+
- CPU-only surfaces use id=0
|
|
46
|
+
|
|
47
|
+
Usage
|
|
48
|
+
-----
|
|
49
|
+
Register new surface types (typically done in the surface class module):
|
|
50
|
+
|
|
51
|
+
from lsurf.surfaces.registry import register_surface_type
|
|
52
|
+
|
|
53
|
+
@register_surface_type("cylinder", "gpu", 3)
|
|
54
|
+
class CylinderSurface(Surface):
|
|
55
|
+
...
|
|
56
|
+
|
|
57
|
+
Or register manually:
|
|
58
|
+
|
|
59
|
+
register_surface_type("cylinder", "gpu", 3, CylinderSurface)
|
|
60
|
+
|
|
61
|
+
Query the registry:
|
|
62
|
+
|
|
63
|
+
from lsurf.surfaces.registry import get_surface_type, get_surface_class
|
|
64
|
+
|
|
65
|
+
info = get_surface_type("plane")
|
|
66
|
+
# -> SurfaceTypeInfo('plane', 'gpu', id=1, cls=<class 'PlaneSurface'>)
|
|
67
|
+
|
|
68
|
+
cls = get_surface_class("plane")
|
|
69
|
+
# -> <class 'PlaneSurface'>
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
from dataclasses import dataclass
|
|
73
|
+
from typing import Literal, TYPE_CHECKING
|
|
74
|
+
|
|
75
|
+
if TYPE_CHECKING:
|
|
76
|
+
from .protocol import Surface
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass(frozen=True)
|
|
80
|
+
class SurfaceTypeInfo:
|
|
81
|
+
"""Information about a registered surface type."""
|
|
82
|
+
|
|
83
|
+
name: str
|
|
84
|
+
capability: Literal["gpu", "cpu"]
|
|
85
|
+
id: int
|
|
86
|
+
cls: type["Surface"] | None = None
|
|
87
|
+
|
|
88
|
+
def __repr__(self) -> str:
|
|
89
|
+
cls_name = self.cls.__name__ if self.cls else "None"
|
|
90
|
+
return f"SurfaceTypeInfo({self.name!r}, {self.capability!r}, id={self.id}, cls={cls_name})"
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class SurfaceTypeRegistry:
|
|
94
|
+
"""
|
|
95
|
+
Registry for surface types.
|
|
96
|
+
|
|
97
|
+
Ensures uniqueness of:
|
|
98
|
+
- Surface type names
|
|
99
|
+
- (capability, id) combinations for GPU surfaces
|
|
100
|
+
"""
|
|
101
|
+
|
|
102
|
+
def __init__(self) -> None:
|
|
103
|
+
self._by_name: dict[str, SurfaceTypeInfo] = {}
|
|
104
|
+
self._gpu_ids: dict[int, str] = {} # id -> name mapping for GPU surfaces
|
|
105
|
+
self._by_class: dict[type, SurfaceTypeInfo] = {} # class -> info mapping
|
|
106
|
+
|
|
107
|
+
def register(
|
|
108
|
+
self,
|
|
109
|
+
name: str,
|
|
110
|
+
capability: Literal["gpu", "cpu"],
|
|
111
|
+
surface_id: int | None = None,
|
|
112
|
+
cls: type["Surface"] | None = None,
|
|
113
|
+
) -> SurfaceTypeInfo:
|
|
114
|
+
"""
|
|
115
|
+
Register a new surface type.
|
|
116
|
+
|
|
117
|
+
Parameters
|
|
118
|
+
----------
|
|
119
|
+
name : str
|
|
120
|
+
Unique name for the surface type (e.g., "plane", "sphere").
|
|
121
|
+
capability : "gpu" or "cpu"
|
|
122
|
+
Whether this surface supports GPU acceleration.
|
|
123
|
+
surface_id : int, optional
|
|
124
|
+
GPU kernel ID. Required for GPU surfaces (must be > 0).
|
|
125
|
+
CPU surfaces always use id=0.
|
|
126
|
+
cls : type, optional
|
|
127
|
+
The surface class to register.
|
|
128
|
+
|
|
129
|
+
Returns
|
|
130
|
+
-------
|
|
131
|
+
SurfaceTypeInfo
|
|
132
|
+
The registered surface type information.
|
|
133
|
+
|
|
134
|
+
Raises
|
|
135
|
+
------
|
|
136
|
+
ValueError
|
|
137
|
+
If name already exists, or if GPU id is already taken.
|
|
138
|
+
"""
|
|
139
|
+
# Validate name uniqueness
|
|
140
|
+
if name in self._by_name:
|
|
141
|
+
existing = self._by_name[name]
|
|
142
|
+
# Allow re-registration with the same class (for adding class after initial registration)
|
|
143
|
+
if cls is not None and existing.cls is None:
|
|
144
|
+
# Update existing entry with class
|
|
145
|
+
new_info = SurfaceTypeInfo(
|
|
146
|
+
name=existing.name,
|
|
147
|
+
capability=existing.capability,
|
|
148
|
+
id=existing.id,
|
|
149
|
+
cls=cls,
|
|
150
|
+
)
|
|
151
|
+
self._by_name[name] = new_info
|
|
152
|
+
self._by_class[cls] = new_info
|
|
153
|
+
return new_info
|
|
154
|
+
elif cls is None or cls is existing.cls:
|
|
155
|
+
return existing
|
|
156
|
+
else:
|
|
157
|
+
raise ValueError(
|
|
158
|
+
f"Surface type '{name}' already registered as "
|
|
159
|
+
f"({existing.capability}, id={existing.id})"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Handle GPU vs CPU
|
|
163
|
+
if capability == "gpu":
|
|
164
|
+
if surface_id is None:
|
|
165
|
+
raise ValueError("GPU surfaces require a surface_id > 0")
|
|
166
|
+
if surface_id <= 0:
|
|
167
|
+
raise ValueError(f"GPU surface_id must be > 0, got {surface_id}")
|
|
168
|
+
if surface_id in self._gpu_ids:
|
|
169
|
+
existing_name = self._gpu_ids[surface_id]
|
|
170
|
+
raise ValueError(
|
|
171
|
+
f"GPU id {surface_id} already used by '{existing_name}'"
|
|
172
|
+
)
|
|
173
|
+
self._gpu_ids[surface_id] = name
|
|
174
|
+
final_id = surface_id
|
|
175
|
+
else:
|
|
176
|
+
# CPU surfaces always have id=0
|
|
177
|
+
if surface_id is not None and surface_id != 0:
|
|
178
|
+
raise ValueError(
|
|
179
|
+
f"CPU surfaces must have id=0 (or omit it), got {surface_id}"
|
|
180
|
+
)
|
|
181
|
+
final_id = 0
|
|
182
|
+
|
|
183
|
+
info = SurfaceTypeInfo(name=name, capability=capability, id=final_id, cls=cls)
|
|
184
|
+
self._by_name[name] = info
|
|
185
|
+
if cls is not None:
|
|
186
|
+
self._by_class[cls] = info
|
|
187
|
+
return info
|
|
188
|
+
|
|
189
|
+
def get(self, name: str) -> SurfaceTypeInfo:
|
|
190
|
+
"""
|
|
191
|
+
Get surface type info by name.
|
|
192
|
+
|
|
193
|
+
Raises KeyError if not found.
|
|
194
|
+
"""
|
|
195
|
+
if name not in self._by_name:
|
|
196
|
+
available = ", ".join(sorted(self._by_name.keys()))
|
|
197
|
+
raise KeyError(f"Unknown surface type '{name}'. Registered: {available}")
|
|
198
|
+
return self._by_name[name]
|
|
199
|
+
|
|
200
|
+
def get_by_class(self, cls: type) -> SurfaceTypeInfo:
|
|
201
|
+
"""
|
|
202
|
+
Get surface type info by class.
|
|
203
|
+
|
|
204
|
+
Raises KeyError if not found.
|
|
205
|
+
"""
|
|
206
|
+
if cls not in self._by_class:
|
|
207
|
+
raise KeyError(
|
|
208
|
+
f"Class {cls.__name__} not registered in surface type registry"
|
|
209
|
+
)
|
|
210
|
+
return self._by_class[cls]
|
|
211
|
+
|
|
212
|
+
def get_id(self, name: str) -> int:
|
|
213
|
+
"""Get the GPU kernel ID for a surface type."""
|
|
214
|
+
return self.get(name).id
|
|
215
|
+
|
|
216
|
+
def get_class(self, name: str) -> type["Surface"] | None:
|
|
217
|
+
"""Get the surface class for a surface type."""
|
|
218
|
+
return self.get(name).cls
|
|
219
|
+
|
|
220
|
+
def get_capability(self, name: str) -> Literal["gpu", "cpu"]:
|
|
221
|
+
"""Get the capability type for a surface type."""
|
|
222
|
+
return self.get(name).capability
|
|
223
|
+
|
|
224
|
+
def is_gpu_capable(self, name: str) -> bool:
|
|
225
|
+
"""Check if a surface type is GPU-capable."""
|
|
226
|
+
return self.get(name).capability == "gpu"
|
|
227
|
+
|
|
228
|
+
def list_all(self) -> list[SurfaceTypeInfo]:
|
|
229
|
+
"""List all registered surface types."""
|
|
230
|
+
return list(self._by_name.values())
|
|
231
|
+
|
|
232
|
+
def list_by_capability(
|
|
233
|
+
self, capability: Literal["gpu", "cpu"]
|
|
234
|
+
) -> list[SurfaceTypeInfo]:
|
|
235
|
+
"""List surface types filtered by capability."""
|
|
236
|
+
return [
|
|
237
|
+
info for info in self._by_name.values() if info.capability == capability
|
|
238
|
+
]
|
|
239
|
+
|
|
240
|
+
def __contains__(self, name: str) -> bool:
|
|
241
|
+
"""Check if a surface type name is registered."""
|
|
242
|
+
return name in self._by_name
|
|
243
|
+
|
|
244
|
+
def __len__(self) -> int:
|
|
245
|
+
"""Number of registered surface types."""
|
|
246
|
+
return len(self._by_name)
|
|
247
|
+
|
|
248
|
+
def __repr__(self) -> str:
|
|
249
|
+
gpu_count = len(self._gpu_ids)
|
|
250
|
+
cpu_count = len(self._by_name) - gpu_count
|
|
251
|
+
return f"SurfaceTypeRegistry({gpu_count} GPU, {cpu_count} CPU)"
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# Global registry instance
|
|
255
|
+
_registry = SurfaceTypeRegistry()
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
# --- Module-level convenience functions ---
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def register_surface_type(
|
|
262
|
+
name: str,
|
|
263
|
+
capability: Literal["gpu", "cpu"],
|
|
264
|
+
surface_id: int | None = None,
|
|
265
|
+
cls: type["Surface"] | None = None,
|
|
266
|
+
) -> SurfaceTypeInfo | type["Surface"]:
|
|
267
|
+
"""
|
|
268
|
+
Register a new surface type.
|
|
269
|
+
|
|
270
|
+
Can be used as a function or decorator.
|
|
271
|
+
|
|
272
|
+
Parameters
|
|
273
|
+
----------
|
|
274
|
+
name : str
|
|
275
|
+
Unique name for the surface type (e.g., "plane", "sphere").
|
|
276
|
+
capability : "gpu" or "cpu"
|
|
277
|
+
Whether this surface supports GPU acceleration.
|
|
278
|
+
surface_id : int, optional
|
|
279
|
+
GPU kernel ID. Required for GPU surfaces (must be > 0).
|
|
280
|
+
CPU surfaces always use id=0.
|
|
281
|
+
cls : type, optional
|
|
282
|
+
The surface class. If None, returns a decorator.
|
|
283
|
+
|
|
284
|
+
Returns
|
|
285
|
+
-------
|
|
286
|
+
SurfaceTypeInfo or decorator
|
|
287
|
+
If cls is provided, returns SurfaceTypeInfo.
|
|
288
|
+
If cls is None, returns a decorator that registers the class.
|
|
289
|
+
|
|
290
|
+
Examples
|
|
291
|
+
--------
|
|
292
|
+
>>> # Direct registration
|
|
293
|
+
>>> register_surface_type("plane", "gpu", 1, PlaneSurface)
|
|
294
|
+
SurfaceTypeInfo('plane', 'gpu', id=1, cls=PlaneSurface)
|
|
295
|
+
|
|
296
|
+
>>> # As decorator
|
|
297
|
+
>>> @register_surface_type("cylinder", "gpu", 3)
|
|
298
|
+
... class CylinderSurface(Surface):
|
|
299
|
+
... pass
|
|
300
|
+
"""
|
|
301
|
+
if cls is not None:
|
|
302
|
+
return _registry.register(name, capability, surface_id, cls)
|
|
303
|
+
|
|
304
|
+
# Return decorator
|
|
305
|
+
def decorator(cls: type["Surface"]) -> type["Surface"]:
|
|
306
|
+
_registry.register(name, capability, surface_id, cls)
|
|
307
|
+
return cls
|
|
308
|
+
|
|
309
|
+
return decorator
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def get_surface_type(name: str) -> SurfaceTypeInfo:
|
|
313
|
+
"""Get full surface type info by name."""
|
|
314
|
+
return _registry.get(name)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def get_surface_type_id(name: str) -> int:
|
|
318
|
+
"""Get the GPU kernel ID for a surface type."""
|
|
319
|
+
return _registry.get_id(name)
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def get_surface_class(name: str) -> type["Surface"] | None:
|
|
323
|
+
"""Get the surface class for a surface type."""
|
|
324
|
+
return _registry.get_class(name)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def is_gpu_capable(name: str) -> bool:
|
|
328
|
+
"""Check if a surface type supports GPU acceleration."""
|
|
329
|
+
return _registry.is_gpu_capable(name)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def list_surface_types(
|
|
333
|
+
capability: Literal["gpu", "cpu"] | None = None,
|
|
334
|
+
) -> list[SurfaceTypeInfo]:
|
|
335
|
+
"""
|
|
336
|
+
List registered surface types.
|
|
337
|
+
|
|
338
|
+
Parameters
|
|
339
|
+
----------
|
|
340
|
+
capability : "gpu", "cpu", or None
|
|
341
|
+
Filter by capability. None returns all.
|
|
342
|
+
"""
|
|
343
|
+
if capability is None:
|
|
344
|
+
return _registry.list_all()
|
|
345
|
+
return _registry.list_by_capability(capability)
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def surface_type_exists(name: str) -> bool:
|
|
349
|
+
"""Check if a surface type is registered."""
|
|
350
|
+
return name in _registry
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
# --- Backwards compatibility aliases ---
|
|
354
|
+
|
|
355
|
+
register_geometry = register_surface_type
|
|
356
|
+
get_geometry_info = get_surface_type
|
|
357
|
+
get_geometry_id = get_surface_type_id
|
|
358
|
+
list_geometries = list_surface_types
|
|
359
|
+
geometry_exists = surface_type_exists
|
|
360
|
+
GeometryInfo = SurfaceTypeInfo
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
# --- Register built-in surface types ---
|
|
364
|
+
# Note: Classes are registered when their modules are imported.
|
|
365
|
+
# Here we just reserve the names and IDs.
|
|
366
|
+
|
|
367
|
+
# GPU-capable surfaces (used by GPU kernels)
|
|
368
|
+
register_surface_type("plane", "gpu", 1)
|
|
369
|
+
register_surface_type("sphere", "gpu", 2)
|
|
370
|
+
|
|
371
|
+
# CPU-only surfaces (complex shapes requiring ray marching)
|
|
372
|
+
register_surface_type("gerstner_wave", "cpu")
|
|
373
|
+
register_surface_type("curved_wave", "cpu")
|
|
@@ -0,0 +1,175 @@
|
|
|
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
|
+
Utilities Module
|
|
36
|
+
|
|
37
|
+
Core utility functions and data structures for the ray tracing framework.
|
|
38
|
+
This module contains foundational components used throughout the simulation.
|
|
39
|
+
|
|
40
|
+
Submodules
|
|
41
|
+
----------
|
|
42
|
+
ray_data
|
|
43
|
+
Ray batch data structures for GPU processing (RayBatch, create_ray_batch).
|
|
44
|
+
fresnel
|
|
45
|
+
Fresnel equations for reflection/refraction calculations.
|
|
46
|
+
propagation
|
|
47
|
+
Ray propagation engine (straight-line and curved-path).
|
|
48
|
+
recording_sphere
|
|
49
|
+
(Deprecated) Spherical detector for capturing rays at altitude.
|
|
50
|
+
Use lsurf.detectors instead for RecordingSphereDetector.
|
|
51
|
+
|
|
52
|
+
Examples
|
|
53
|
+
--------
|
|
54
|
+
>>> from lsurf.utilities import RayBatch, create_ray_batch
|
|
55
|
+
>>> rays = create_ray_batch(1000)
|
|
56
|
+
>>>
|
|
57
|
+
>>> from lsurf.utilities import fresnel_coefficients
|
|
58
|
+
>>> R, T = fresnel_coefficients(1.0, 1.5, cos_theta)
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
# Import submodules for backwards compatibility attribute access
|
|
62
|
+
from . import fresnel, interactions, propagation, ray_data, recording_sphere
|
|
63
|
+
|
|
64
|
+
# Fresnel equations
|
|
65
|
+
from .fresnel import (
|
|
66
|
+
compute_reflection_direction,
|
|
67
|
+
compute_refraction_direction,
|
|
68
|
+
fresnel_coefficients,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Propagation engine
|
|
72
|
+
from .propagation import (
|
|
73
|
+
IPropagator,
|
|
74
|
+
PropagationConfig,
|
|
75
|
+
PropagationMode,
|
|
76
|
+
StraightLinePropagator,
|
|
77
|
+
)
|
|
78
|
+
from .ray_data import (
|
|
79
|
+
BoolArray,
|
|
80
|
+
Float32Array,
|
|
81
|
+
Int32Array,
|
|
82
|
+
RayBatch,
|
|
83
|
+
RayStatistics,
|
|
84
|
+
compute_statistics,
|
|
85
|
+
create_ray_batch,
|
|
86
|
+
merge_ray_batches,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Recording sphere - kept for backward compatibility
|
|
90
|
+
# New code should use lsurf.detectors.RecordingSphereDetector instead
|
|
91
|
+
from .recording_sphere import (
|
|
92
|
+
LocalRecordingSphere,
|
|
93
|
+
RecordedRays,
|
|
94
|
+
RecordingSphere,
|
|
95
|
+
load_recorded_rays_hdf5,
|
|
96
|
+
load_recorded_rays_numpy,
|
|
97
|
+
save_recorded_rays_hdf5,
|
|
98
|
+
save_recorded_rays_numpy,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Time spread estimation
|
|
102
|
+
from .time_spread import (
|
|
103
|
+
TimeSpreadResult,
|
|
104
|
+
compute_beam_footprint,
|
|
105
|
+
estimate_time_spread,
|
|
106
|
+
estimate_time_spread_bounds,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
# Detector analysis
|
|
110
|
+
from .detector_analysis import (
|
|
111
|
+
analyze_healpix_detector,
|
|
112
|
+
compute_healpix_pareto_front,
|
|
113
|
+
compute_pareto_front,
|
|
114
|
+
find_peak_energy_density,
|
|
115
|
+
find_peak_irradiance_local,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Gradient propagator from propagation module
|
|
119
|
+
from ..propagation import GradientPropagator
|
|
120
|
+
|
|
121
|
+
# Interactions (ray-surface processing)
|
|
122
|
+
from .interactions import (
|
|
123
|
+
process_surface_interaction,
|
|
124
|
+
reflect_rays,
|
|
125
|
+
refract_rays,
|
|
126
|
+
trace_rays_multi_bounce,
|
|
127
|
+
trace_rays_with_splitting,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
__all__ = [
|
|
131
|
+
# Ray data
|
|
132
|
+
"RayBatch",
|
|
133
|
+
"RayStatistics",
|
|
134
|
+
"Float32Array",
|
|
135
|
+
"BoolArray",
|
|
136
|
+
"Int32Array",
|
|
137
|
+
"create_ray_batch",
|
|
138
|
+
"compute_statistics",
|
|
139
|
+
"merge_ray_batches",
|
|
140
|
+
# Fresnel
|
|
141
|
+
"fresnel_coefficients",
|
|
142
|
+
"compute_reflection_direction",
|
|
143
|
+
"compute_refraction_direction",
|
|
144
|
+
# Propagation
|
|
145
|
+
"PropagationMode",
|
|
146
|
+
"PropagationConfig",
|
|
147
|
+
"StraightLinePropagator",
|
|
148
|
+
"GradientPropagator",
|
|
149
|
+
"IPropagator",
|
|
150
|
+
# Recording sphere (backward compatibility - use lsurf.detectors instead)
|
|
151
|
+
"RecordingSphere",
|
|
152
|
+
"LocalRecordingSphere",
|
|
153
|
+
"RecordedRays",
|
|
154
|
+
"save_recorded_rays_hdf5",
|
|
155
|
+
"load_recorded_rays_hdf5",
|
|
156
|
+
"save_recorded_rays_numpy",
|
|
157
|
+
"load_recorded_rays_numpy",
|
|
158
|
+
# Time spread estimation
|
|
159
|
+
"TimeSpreadResult",
|
|
160
|
+
"estimate_time_spread",
|
|
161
|
+
"estimate_time_spread_bounds",
|
|
162
|
+
"compute_beam_footprint",
|
|
163
|
+
# Detector analysis
|
|
164
|
+
"find_peak_energy_density",
|
|
165
|
+
"find_peak_irradiance_local",
|
|
166
|
+
"compute_pareto_front",
|
|
167
|
+
"analyze_healpix_detector",
|
|
168
|
+
"compute_healpix_pareto_front",
|
|
169
|
+
# Interactions
|
|
170
|
+
"process_surface_interaction",
|
|
171
|
+
"reflect_rays",
|
|
172
|
+
"refract_rays",
|
|
173
|
+
"trace_rays_multi_bounce",
|
|
174
|
+
"trace_rays_with_splitting",
|
|
175
|
+
]
|