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.
Files changed (180) hide show
  1. lsurf/__init__.py +471 -0
  2. lsurf/analysis/__init__.py +107 -0
  3. lsurf/analysis/healpix_utils.py +418 -0
  4. lsurf/analysis/sphere_viz.py +1280 -0
  5. lsurf/cli/__init__.py +48 -0
  6. lsurf/cli/build.py +398 -0
  7. lsurf/cli/config_schema.py +318 -0
  8. lsurf/cli/gui_cmd.py +76 -0
  9. lsurf/cli/interactive.py +850 -0
  10. lsurf/cli/main.py +81 -0
  11. lsurf/cli/run.py +806 -0
  12. lsurf/detectors/__init__.py +266 -0
  13. lsurf/detectors/analysis.py +289 -0
  14. lsurf/detectors/base.py +284 -0
  15. lsurf/detectors/constant_size_rings.py +485 -0
  16. lsurf/detectors/directional.py +45 -0
  17. lsurf/detectors/extended/__init__.py +73 -0
  18. lsurf/detectors/extended/local_sphere.py +353 -0
  19. lsurf/detectors/extended/recording_sphere.py +368 -0
  20. lsurf/detectors/planar.py +45 -0
  21. lsurf/detectors/protocol.py +187 -0
  22. lsurf/detectors/recording_spheres.py +63 -0
  23. lsurf/detectors/results.py +1140 -0
  24. lsurf/detectors/small/__init__.py +79 -0
  25. lsurf/detectors/small/directional.py +330 -0
  26. lsurf/detectors/small/planar.py +401 -0
  27. lsurf/detectors/small/spherical.py +450 -0
  28. lsurf/detectors/spherical.py +45 -0
  29. lsurf/geometry/__init__.py +199 -0
  30. lsurf/geometry/builder.py +478 -0
  31. lsurf/geometry/cell.py +228 -0
  32. lsurf/geometry/cell_geometry.py +247 -0
  33. lsurf/geometry/detector_arrays.py +1785 -0
  34. lsurf/geometry/geometry.py +222 -0
  35. lsurf/geometry/surface_analysis.py +375 -0
  36. lsurf/geometry/validation.py +91 -0
  37. lsurf/gui/__init__.py +51 -0
  38. lsurf/gui/app.py +903 -0
  39. lsurf/gui/core/__init__.py +39 -0
  40. lsurf/gui/core/scene.py +343 -0
  41. lsurf/gui/core/simulation.py +264 -0
  42. lsurf/gui/renderers/__init__.py +40 -0
  43. lsurf/gui/renderers/ray_renderer.py +353 -0
  44. lsurf/gui/renderers/source_renderer.py +505 -0
  45. lsurf/gui/renderers/surface_renderer.py +477 -0
  46. lsurf/gui/views/__init__.py +48 -0
  47. lsurf/gui/views/config_editor.py +3199 -0
  48. lsurf/gui/views/properties.py +257 -0
  49. lsurf/gui/views/results.py +291 -0
  50. lsurf/gui/views/scene_tree.py +180 -0
  51. lsurf/gui/views/viewport_3d.py +555 -0
  52. lsurf/gui/views/visualizations.py +712 -0
  53. lsurf/materials/__init__.py +169 -0
  54. lsurf/materials/base/__init__.py +64 -0
  55. lsurf/materials/base/full_inhomogeneous.py +208 -0
  56. lsurf/materials/base/grid_inhomogeneous.py +319 -0
  57. lsurf/materials/base/homogeneous.py +342 -0
  58. lsurf/materials/base/material_field.py +527 -0
  59. lsurf/materials/base/simple_inhomogeneous.py +418 -0
  60. lsurf/materials/base/spectral_inhomogeneous.py +497 -0
  61. lsurf/materials/implementations/__init__.py +120 -0
  62. lsurf/materials/implementations/data/alpha_values_typical_atmosphere_updated.txt +24 -0
  63. lsurf/materials/implementations/duct_atmosphere.py +390 -0
  64. lsurf/materials/implementations/exponential_atmosphere.py +435 -0
  65. lsurf/materials/implementations/gaussian_lens.py +120 -0
  66. lsurf/materials/implementations/interpolated_data.py +123 -0
  67. lsurf/materials/implementations/layered_atmosphere.py +134 -0
  68. lsurf/materials/implementations/linear_gradient.py +109 -0
  69. lsurf/materials/implementations/linsley_atmosphere.py +764 -0
  70. lsurf/materials/implementations/standard_materials.py +126 -0
  71. lsurf/materials/implementations/turbulent_atmosphere.py +135 -0
  72. lsurf/materials/implementations/us_standard_atmosphere.py +149 -0
  73. lsurf/materials/utils/__init__.py +77 -0
  74. lsurf/materials/utils/constants.py +45 -0
  75. lsurf/materials/utils/device_functions.py +117 -0
  76. lsurf/materials/utils/dispersion.py +160 -0
  77. lsurf/materials/utils/factories.py +142 -0
  78. lsurf/propagation/__init__.py +91 -0
  79. lsurf/propagation/detector_gpu.py +67 -0
  80. lsurf/propagation/gpu_device_rays.py +294 -0
  81. lsurf/propagation/kernels/__init__.py +175 -0
  82. lsurf/propagation/kernels/absorption/__init__.py +61 -0
  83. lsurf/propagation/kernels/absorption/grid.py +240 -0
  84. lsurf/propagation/kernels/absorption/simple.py +232 -0
  85. lsurf/propagation/kernels/absorption/spectral.py +410 -0
  86. lsurf/propagation/kernels/detection/__init__.py +64 -0
  87. lsurf/propagation/kernels/detection/protocol.py +102 -0
  88. lsurf/propagation/kernels/detection/spherical.py +255 -0
  89. lsurf/propagation/kernels/device_functions.py +790 -0
  90. lsurf/propagation/kernels/fresnel/__init__.py +64 -0
  91. lsurf/propagation/kernels/fresnel/protocol.py +97 -0
  92. lsurf/propagation/kernels/fresnel/standard.py +258 -0
  93. lsurf/propagation/kernels/intersection/__init__.py +79 -0
  94. lsurf/propagation/kernels/intersection/annular_plane.py +207 -0
  95. lsurf/propagation/kernels/intersection/bounded_plane.py +205 -0
  96. lsurf/propagation/kernels/intersection/plane.py +166 -0
  97. lsurf/propagation/kernels/intersection/protocol.py +95 -0
  98. lsurf/propagation/kernels/intersection/signed_distance.py +742 -0
  99. lsurf/propagation/kernels/intersection/sphere.py +190 -0
  100. lsurf/propagation/kernels/propagation/__init__.py +85 -0
  101. lsurf/propagation/kernels/propagation/grid.py +527 -0
  102. lsurf/propagation/kernels/propagation/protocol.py +105 -0
  103. lsurf/propagation/kernels/propagation/simple.py +460 -0
  104. lsurf/propagation/kernels/propagation/spectral.py +875 -0
  105. lsurf/propagation/kernels/registry.py +331 -0
  106. lsurf/propagation/kernels/surface/__init__.py +72 -0
  107. lsurf/propagation/kernels/surface/bisection.py +232 -0
  108. lsurf/propagation/kernels/surface/detection.py +402 -0
  109. lsurf/propagation/kernels/surface/reduction.py +166 -0
  110. lsurf/propagation/propagator_protocol.py +222 -0
  111. lsurf/propagation/propagators/__init__.py +101 -0
  112. lsurf/propagation/propagators/detector_handler.py +354 -0
  113. lsurf/propagation/propagators/factory.py +200 -0
  114. lsurf/propagation/propagators/fresnel_handler.py +305 -0
  115. lsurf/propagation/propagators/gpu_gradient.py +566 -0
  116. lsurf/propagation/propagators/gpu_surface_propagator.py +707 -0
  117. lsurf/propagation/propagators/gradient.py +429 -0
  118. lsurf/propagation/propagators/intersection_handler.py +327 -0
  119. lsurf/propagation/propagators/material_propagator.py +398 -0
  120. lsurf/propagation/propagators/signed_distance_handler.py +522 -0
  121. lsurf/propagation/propagators/spectral_gpu_gradient.py +553 -0
  122. lsurf/propagation/propagators/surface_interaction.py +616 -0
  123. lsurf/propagation/propagators/surface_propagator.py +719 -0
  124. lsurf/py.typed +1 -0
  125. lsurf/simulation/__init__.py +70 -0
  126. lsurf/simulation/config.py +164 -0
  127. lsurf/simulation/orchestrator.py +462 -0
  128. lsurf/simulation/result.py +299 -0
  129. lsurf/simulation/simulation.py +262 -0
  130. lsurf/sources/__init__.py +128 -0
  131. lsurf/sources/base.py +264 -0
  132. lsurf/sources/collimated.py +252 -0
  133. lsurf/sources/custom.py +409 -0
  134. lsurf/sources/diverging.py +228 -0
  135. lsurf/sources/gaussian.py +272 -0
  136. lsurf/sources/parallel_from_positions.py +197 -0
  137. lsurf/sources/point.py +172 -0
  138. lsurf/sources/uniform_diverging.py +258 -0
  139. lsurf/surfaces/__init__.py +184 -0
  140. lsurf/surfaces/cpu/__init__.py +50 -0
  141. lsurf/surfaces/cpu/curved_wave.py +463 -0
  142. lsurf/surfaces/cpu/gerstner_wave.py +381 -0
  143. lsurf/surfaces/cpu/wave_params.py +118 -0
  144. lsurf/surfaces/gpu/__init__.py +72 -0
  145. lsurf/surfaces/gpu/annular_plane.py +453 -0
  146. lsurf/surfaces/gpu/bounded_plane.py +390 -0
  147. lsurf/surfaces/gpu/curved_wave.py +483 -0
  148. lsurf/surfaces/gpu/gerstner_wave.py +377 -0
  149. lsurf/surfaces/gpu/multi_curved_wave.py +520 -0
  150. lsurf/surfaces/gpu/plane.py +299 -0
  151. lsurf/surfaces/gpu/recording_sphere.py +587 -0
  152. lsurf/surfaces/gpu/sphere.py +311 -0
  153. lsurf/surfaces/protocol.py +336 -0
  154. lsurf/surfaces/registry.py +373 -0
  155. lsurf/utilities/__init__.py +175 -0
  156. lsurf/utilities/detector_analysis.py +814 -0
  157. lsurf/utilities/fresnel.py +628 -0
  158. lsurf/utilities/interactions.py +1215 -0
  159. lsurf/utilities/propagation.py +602 -0
  160. lsurf/utilities/ray_data.py +532 -0
  161. lsurf/utilities/recording_sphere.py +745 -0
  162. lsurf/utilities/time_spread.py +463 -0
  163. lsurf/visualization/__init__.py +329 -0
  164. lsurf/visualization/absorption_plots.py +334 -0
  165. lsurf/visualization/atmospheric_plots.py +754 -0
  166. lsurf/visualization/common.py +348 -0
  167. lsurf/visualization/detector_plots.py +1350 -0
  168. lsurf/visualization/detector_sphere_plots.py +1173 -0
  169. lsurf/visualization/fresnel_plots.py +1061 -0
  170. lsurf/visualization/ocean_simulation_plots.py +999 -0
  171. lsurf/visualization/polarization_plots.py +916 -0
  172. lsurf/visualization/raytracing_plots.py +1521 -0
  173. lsurf/visualization/ring_detector_plots.py +1867 -0
  174. lsurf/visualization/time_spread_plots.py +531 -0
  175. lsurf-1.0.0.dist-info/METADATA +381 -0
  176. lsurf-1.0.0.dist-info/RECORD +180 -0
  177. lsurf-1.0.0.dist-info/WHEEL +5 -0
  178. lsurf-1.0.0.dist-info/entry_points.txt +2 -0
  179. lsurf-1.0.0.dist-info/licenses/LICENSE +32 -0
  180. 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
+ ]