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,255 @@
1
+ # The Clear BSD License
2
+ #
3
+ # Copyright (c) 2026 Tobias Heibges
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted (subject to the limitations in the disclaimer
8
+ # below) provided that the following conditions are met:
9
+ #
10
+ # * Redistributions of source code must retain the above copyright notice,
11
+ # this list of conditions and the following disclaimer.
12
+ #
13
+ # * Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # * Neither the name of the copyright holder nor the names of its
18
+ # contributors may be used to endorse or promote products derived from this
19
+ # software without specific prior written permission.
20
+ #
21
+ # NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
22
+ # THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
23
+ # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25
+ # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
26
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
29
+ # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
30
+ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31
+ # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ # POSSIBILITY OF SUCH DAMAGE.
33
+
34
+ """
35
+ GPU-Accelerated Detector Operations
36
+
37
+ This module provides GPU-accelerated ray detection for spherical and planar
38
+ detectors, enabling fast parallel detection of many rays against many detector
39
+ positions simultaneously.
40
+
41
+ This module contains ONLY pure CUDA kernels - no GPU memory management.
42
+ The propagator layer is responsible for cuda.to_device() and copy_to_host().
43
+ """
44
+
45
+ import math
46
+
47
+ from ..registry import DetectionKernelID, register_kernel
48
+
49
+ # GPU support is optional
50
+ try:
51
+ from numba import cuda
52
+
53
+ HAS_CUDA = True
54
+ except ImportError:
55
+
56
+ class _FakeCuda:
57
+ """Fake cuda module for when numba is not installed."""
58
+
59
+ @staticmethod
60
+ def jit(*args, **kwargs):
61
+ """Return a no-op decorator."""
62
+
63
+ def decorator(func):
64
+ return func
65
+
66
+ if args and callable(args[0]):
67
+ return args[0]
68
+ return decorator
69
+
70
+ @staticmethod
71
+ def is_available():
72
+ return False
73
+
74
+ @staticmethod
75
+ def grid(n):
76
+ return 0
77
+
78
+ @staticmethod
79
+ def synchronize():
80
+ pass
81
+
82
+ class atomic:
83
+ @staticmethod
84
+ def add(arr, idx, val):
85
+ arr[idx] += val
86
+
87
+ cuda = _FakeCuda() # type: ignore[assignment]
88
+ HAS_CUDA = False
89
+
90
+ # =============================================================================
91
+ # CUDA Kernels for Spherical Detector
92
+ # =============================================================================
93
+
94
+
95
+ @register_kernel(DetectionKernelID.SPHERICAL_SINGLE)
96
+ @cuda.jit
97
+ def kernel_spherical_detect_single(
98
+ ray_positions, # (N, 3) ray positions
99
+ ray_directions, # (N, 3) ray directions
100
+ ray_active, # (N,) active mask
101
+ ray_times, # (N,) accumulated times
102
+ ray_wavelengths, # (N,) wavelengths
103
+ ray_intensities, # (N,) intensities
104
+ detector_center, # (3,) detector center
105
+ detector_radius, # scalar
106
+ hit_mask_out, # (N,) output: did ray hit?
107
+ hit_distances_out, # (N,) output: distance to hit
108
+ hit_times_out, # (N,) output: arrival times
109
+ ):
110
+ """
111
+ CUDA kernel to detect rays hitting a single spherical detector.
112
+ """
113
+ idx = cuda.grid(1)
114
+ if idx >= ray_positions.shape[0]:
115
+ return
116
+
117
+ # Skip inactive rays
118
+ if not ray_active[idx]:
119
+ hit_mask_out[idx] = False
120
+ return
121
+
122
+ # Load ray data
123
+ ox = ray_positions[idx, 0]
124
+ oy = ray_positions[idx, 1]
125
+ oz = ray_positions[idx, 2]
126
+ dx = ray_directions[idx, 0]
127
+ dy = ray_directions[idx, 1]
128
+ dz = ray_directions[idx, 2]
129
+
130
+ # Detector center
131
+ cx = detector_center[0]
132
+ cy = detector_center[1]
133
+ cz = detector_center[2]
134
+
135
+ # Normalize direction
136
+ d_len = math.sqrt(dx * dx + dy * dy + dz * dz)
137
+ if d_len < 1e-10:
138
+ hit_mask_out[idx] = False
139
+ return
140
+ dx /= d_len
141
+ dy /= d_len
142
+ dz /= d_len
143
+
144
+ # Vector from ray origin to sphere center
145
+ ocx = cx - ox
146
+ ocy = cy - oy
147
+ ocz = cz - oz
148
+
149
+ # Project onto ray direction: t_closest = dot(oc, d)
150
+ t_closest = ocx * dx + ocy * dy + ocz * dz
151
+
152
+ # Only forward propagation
153
+ if t_closest < 0:
154
+ hit_mask_out[idx] = False
155
+ return
156
+
157
+ # Closest point on ray to center
158
+ px = ox + t_closest * dx
159
+ py = oy + t_closest * dy
160
+ pz = oz + t_closest * dz
161
+
162
+ # Distance to center
163
+ dist_sq = (px - cx) * (px - cx) + (py - cy) * (py - cy) + (pz - cz) * (pz - cz)
164
+ radius_sq = detector_radius * detector_radius
165
+
166
+ if dist_sq <= radius_sq:
167
+ # Hit!
168
+ hit_mask_out[idx] = True
169
+ hit_distances_out[idx] = t_closest
170
+
171
+ # Compute arrival time (assume n=1 after reflection)
172
+ c = 299792458.0 # Speed of light
173
+ additional_time = t_closest / c
174
+ hit_times_out[idx] = ray_times[idx] + additional_time
175
+ else:
176
+ hit_mask_out[idx] = False
177
+
178
+
179
+ @register_kernel(DetectionKernelID.SPHERICAL_MULTI)
180
+ @cuda.jit
181
+ def kernel_spherical_detect_multi(
182
+ ray_positions, # (N, 3) ray positions
183
+ ray_directions, # (N, 3) ray directions
184
+ ray_active, # (N,) active mask
185
+ ray_times, # (N,) accumulated times
186
+ detector_centers, # (M, 3) detector centers
187
+ detector_radius, # scalar (same for all)
188
+ hit_counts_out, # (M,) output: count of hits per detector
189
+ hit_intensity_out, # (M,) output: sum of intensities per detector
190
+ ray_intensities, # (N,) intensities
191
+ ):
192
+ """
193
+ CUDA kernel to count ray hits across multiple detector positions.
194
+
195
+ This is optimized for the detector scan use case where we want to
196
+ quickly count how many rays hit each detector position.
197
+ """
198
+ # 2D grid: (ray_idx, detector_idx)
199
+ ray_idx = cuda.grid(1)
200
+
201
+ if ray_idx >= ray_positions.shape[0]:
202
+ return
203
+
204
+ # Skip inactive rays
205
+ if not ray_active[ray_idx]:
206
+ return
207
+
208
+ # Load ray data once
209
+ ox = ray_positions[ray_idx, 0]
210
+ oy = ray_positions[ray_idx, 1]
211
+ oz = ray_positions[ray_idx, 2]
212
+ dx = ray_directions[ray_idx, 0]
213
+ dy = ray_directions[ray_idx, 1]
214
+ dz = ray_directions[ray_idx, 2]
215
+ intensity = ray_intensities[ray_idx]
216
+
217
+ # Normalize direction
218
+ d_len = math.sqrt(dx * dx + dy * dy + dz * dz)
219
+ if d_len < 1e-10:
220
+ return
221
+ dx /= d_len
222
+ dy /= d_len
223
+ dz /= d_len
224
+
225
+ radius_sq = detector_radius * detector_radius
226
+
227
+ # Check against all detectors
228
+ for det_idx in range(detector_centers.shape[0]):
229
+ cx = detector_centers[det_idx, 0]
230
+ cy = detector_centers[det_idx, 1]
231
+ cz = detector_centers[det_idx, 2]
232
+
233
+ # Vector from ray origin to detector center
234
+ ocx = cx - ox
235
+ ocy = cy - oy
236
+ ocz = cz - oz
237
+
238
+ # Project onto ray direction
239
+ t_closest = ocx * dx + ocy * dy + ocz * dz
240
+
241
+ # Only forward propagation
242
+ if t_closest < 0:
243
+ continue
244
+
245
+ # Closest point distance squared
246
+ px = ox + t_closest * dx
247
+ py = oy + t_closest * dy
248
+ pz = oz + t_closest * dz
249
+
250
+ dist_sq = (px - cx) ** 2 + (py - cy) ** 2 + (pz - cz) ** 2
251
+
252
+ if dist_sq <= radius_sq:
253
+ # Hit! Atomic add to counters
254
+ cuda.atomic.add(hit_counts_out, det_idx, 1)
255
+ cuda.atomic.add(hit_intensity_out, det_idx, intensity)