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,247 @@
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
+ Cell-Based Geometry Container
36
+
37
+ Extends Geometry with explicit cell-based material assignment for
38
+ complex geometries where surfaces intersect.
39
+ """
40
+
41
+ from __future__ import annotations
42
+
43
+ from dataclasses import dataclass
44
+ from typing import TYPE_CHECKING
45
+
46
+ import numpy as np
47
+
48
+ from .cell import Cell
49
+ from .geometry import Geometry
50
+
51
+ if TYPE_CHECKING:
52
+ from numpy.typing import NDArray
53
+
54
+ from ..materials import MaterialField
55
+
56
+
57
+ @dataclass(frozen=True)
58
+ class CellGeometry(Geometry):
59
+ """
60
+ Geometry with explicit cell-based material assignment.
61
+
62
+ Extends the standard Geometry with cells that define material regions
63
+ by the intersection of half-spaces. Use this when surfaces intersect
64
+ and the simple front/back model cannot describe all regions.
65
+
66
+ Parameters
67
+ ----------
68
+ surfaces : tuple of Surface
69
+ All optical/absorber surfaces in the geometry.
70
+ detectors : tuple of Surface
71
+ All detector surfaces in the geometry.
72
+ background_material : MaterialField
73
+ The background/ambient material.
74
+ media : dict
75
+ Mapping from medium name to MaterialField.
76
+ surface_names : dict
77
+ Mapping from surface name to index in surfaces tuple.
78
+ detector_names : dict
79
+ Mapping from detector name to index in detectors tuple.
80
+ cells : tuple of Cell
81
+ Cells defining material regions by half-space intersection.
82
+
83
+ Examples
84
+ --------
85
+ >>> # Created by GeometryBuilder in cell mode
86
+ >>> builder = GeometryBuilder()
87
+ >>> builder.register_medium("air", AIR)
88
+ >>> builder.register_medium("water", WATER)
89
+ >>> builder.set_background("air")
90
+ >>> builder.add_surface_only(plane_x)
91
+ >>> builder.add_surface_only(plane_y)
92
+ >>> builder.add_cell("air", ("plane_x", True), ("plane_y", True))
93
+ >>> builder.add_cell("water", ("plane_x", False), ("plane_y", True))
94
+ >>> geometry = builder.build() # Returns CellGeometry
95
+ >>>
96
+ >>> # Query material at positions
97
+ >>> positions = np.array([[1, 1, 0], [-1, 1, 0]])
98
+ >>> materials = geometry.get_material_at(positions)
99
+ """
100
+
101
+ cells: tuple[Cell, ...]
102
+
103
+ def get_material_at(
104
+ self,
105
+ positions: NDArray[np.float64],
106
+ ) -> list[tuple[MaterialField, NDArray[np.bool_]]]:
107
+ """
108
+ Determine material for each position based on cell membership.
109
+
110
+ Parameters
111
+ ----------
112
+ positions : NDArray, shape (N, 3)
113
+ Positions to query.
114
+
115
+ Returns
116
+ -------
117
+ list of (MaterialField, NDArray[bool])
118
+ List of (material, mask) pairs where mask indicates which
119
+ positions have that material. A position may have no material
120
+ if it doesn't fall into any defined cell.
121
+
122
+ Notes
123
+ -----
124
+ Cells are checked in order. The first cell that contains a position
125
+ determines its material. Positions not in any cell get the background
126
+ material.
127
+ """
128
+ positions = np.asarray(positions, dtype=np.float64)
129
+ n_positions = len(positions)
130
+
131
+ # Compute signed distances for all surfaces
132
+ signed_distances: dict[str, NDArray[np.float64]] = {}
133
+ for surface in self.surfaces:
134
+ if surface.gpu_capable:
135
+ sd = surface.signed_distance(positions.astype(np.float32))
136
+ signed_distances[surface.name] = sd.astype(np.float64)
137
+
138
+ # Track which positions are assigned
139
+ assigned = np.zeros(n_positions, dtype=np.bool_)
140
+ results: list[tuple[MaterialField, NDArray[np.bool_]]] = []
141
+
142
+ # Check each cell
143
+ for cell in self.cells:
144
+ # Get material for this cell
145
+ material = self.media.get(cell.medium_name)
146
+ if material is None:
147
+ continue
148
+
149
+ # Find positions in this cell that aren't already assigned
150
+ in_cell = cell.contains(signed_distances)
151
+ new_in_cell = in_cell & ~assigned
152
+
153
+ if np.any(new_in_cell):
154
+ results.append((material, new_in_cell))
155
+ assigned |= new_in_cell
156
+
157
+ # Assign background material to remaining positions
158
+ remaining = ~assigned
159
+ if np.any(remaining):
160
+ results.append((self.background_material, remaining))
161
+
162
+ return results
163
+
164
+ def get_material_index_at(
165
+ self,
166
+ positions: NDArray[np.float64],
167
+ ) -> NDArray[np.int32]:
168
+ """
169
+ Get cell index for each position (-1 for background).
170
+
171
+ Parameters
172
+ ----------
173
+ positions : NDArray, shape (N, 3)
174
+ Positions to query.
175
+
176
+ Returns
177
+ -------
178
+ NDArray[np.int32], shape (N,)
179
+ Cell index for each position, or -1 for positions in
180
+ the background (not in any cell).
181
+ """
182
+ positions = np.asarray(positions, dtype=np.float64)
183
+ n_positions = len(positions)
184
+
185
+ # Compute signed distances for all surfaces
186
+ signed_distances: dict[str, NDArray[np.float64]] = {}
187
+ for surface in self.surfaces:
188
+ if surface.gpu_capable:
189
+ sd = surface.signed_distance(positions.astype(np.float32))
190
+ signed_distances[surface.name] = sd.astype(np.float64)
191
+
192
+ # Initialize with -1 (background)
193
+ cell_indices = np.full(n_positions, -1, dtype=np.int32)
194
+
195
+ # Check each cell in order
196
+ for i, cell in enumerate(self.cells):
197
+ in_cell = cell.contains(signed_distances)
198
+ # Only assign if not already assigned
199
+ unassigned_in_cell = in_cell & (cell_indices == -1)
200
+ cell_indices[unassigned_in_cell] = i
201
+
202
+ return cell_indices
203
+
204
+ def get_cell(self, index: int) -> Cell:
205
+ """
206
+ Get cell by index.
207
+
208
+ Parameters
209
+ ----------
210
+ index : int
211
+ Cell index.
212
+
213
+ Returns
214
+ -------
215
+ Cell
216
+ The cell at the given index.
217
+
218
+ Raises
219
+ ------
220
+ IndexError
221
+ If index is out of range.
222
+ """
223
+ return self.cells[index]
224
+
225
+ def get_cell_by_name(self, name: str) -> Cell | None:
226
+ """
227
+ Get cell by name.
228
+
229
+ Parameters
230
+ ----------
231
+ name : str
232
+ Cell name.
233
+
234
+ Returns
235
+ -------
236
+ Cell or None
237
+ The cell with the given name, or None if not found.
238
+ """
239
+ for cell in self.cells:
240
+ if cell.name == name:
241
+ return cell
242
+ return None
243
+
244
+ @property
245
+ def num_cells(self) -> int:
246
+ """Number of cells in the geometry."""
247
+ return len(self.cells)