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,126 @@
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
+ Standard Material Constants
36
+
37
+ Pre-defined materials for common optical media.
38
+
39
+ Standard Materials
40
+ ------------------
41
+ - VACUUM: n = 1.0 (by definition)
42
+ - AIR_STP: n = 1.000293 (standard temperature and pressure)
43
+ - WATER: n = 1.333, with absorption
44
+ - BK7_GLASS: n = 1.5168, Schott optical glass
45
+ """
46
+
47
+ from ..base import HomogeneousMaterial
48
+
49
+
50
+ VACUUM = HomogeneousMaterial(
51
+ name="Vacuum",
52
+ refractive_index=1.0,
53
+ absorption_coef=0.0,
54
+ scattering_coef=0.0,
55
+ anisotropy=0.0,
56
+ )
57
+ """
58
+ Perfect vacuum with n = 1.0 (by definition).
59
+
60
+ The refractive index of vacuum is exactly 1 by definition of the
61
+ speed of light. No absorption or scattering.
62
+ """
63
+
64
+ AIR_STP = HomogeneousMaterial(
65
+ name="Air (STP)",
66
+ refractive_index=1.000293,
67
+ absorption_coef=0.0,
68
+ scattering_coef=0.0,
69
+ anisotropy=0.0,
70
+ )
71
+ """
72
+ Air at standard temperature (273.15 K) and pressure (101.325 kPa).
73
+
74
+ Refractive index from Ciddor (1996) for visible light.
75
+ Dispersion is negligible for most applications.
76
+
77
+ References
78
+ ----------
79
+ .. [1] Ciddor, P. E. (1996). Appl. Opt., 35(9), 1566-1573.
80
+ """
81
+
82
+ WATER = HomogeneousMaterial(
83
+ name="Water",
84
+ refractive_index=1.333,
85
+ absorption_coef=0.0045, # ~0.045 per 10m at 550nm
86
+ scattering_coef=0.0,
87
+ anisotropy=0.0,
88
+ )
89
+ """
90
+ Pure water at 20°C.
91
+
92
+ Refractive index is average for visible wavelengths.
93
+ Absorption coefficient is approximate for green light (550 nm).
94
+ Real water absorption is strongly wavelength-dependent.
95
+
96
+ References
97
+ ----------
98
+ .. [1] Hale & Querry (1973). Appl. Opt., 12(3), 555-563.
99
+ """
100
+
101
+ BK7_GLASS = HomogeneousMaterial(
102
+ name="BK7 Glass",
103
+ refractive_index=1.5168,
104
+ absorption_coef=0.0,
105
+ scattering_coef=0.0,
106
+ anisotropy=0.0,
107
+ )
108
+ """
109
+ Schott N-BK7 borosilicate crown glass.
110
+
111
+ Standard optical glass with excellent homogeneity.
112
+ Refractive index given at 587.6 nm (d-line).
113
+ For accurate work, use Sellmeier coefficients.
114
+
115
+ References
116
+ ----------
117
+ .. [1] Schott AG. Optical Glass Data Sheets.
118
+ """
119
+
120
+
121
+ __all__ = [
122
+ "VACUUM",
123
+ "AIR_STP",
124
+ "WATER",
125
+ "BK7_GLASS",
126
+ ]
@@ -0,0 +1,135 @@
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
+ Turbulent Atmosphere Implementation
36
+
37
+ Atmosphere with Kolmogorov-like turbulence perturbations on a 3D grid.
38
+ """
39
+
40
+ import numpy as np
41
+
42
+ from ..base import GridInhomogeneousModel
43
+
44
+
45
+ class TurbulentAtmosphere(GridInhomogeneousModel):
46
+ """
47
+ Atmosphere with Kolmogorov-like turbulence perturbations.
48
+
49
+ Creates a 3D grid of refractive index values with random turbulent
50
+ perturbations superimposed on an exponential baseline profile.
51
+
52
+ Parameters
53
+ ----------
54
+ bounds : tuple
55
+ ((x_min, x_max), (y_min, y_max), (z_min, z_max)) in meters
56
+ grid_resolution : tuple
57
+ (nx, ny, nz) grid points
58
+ baseline_n : float
59
+ Sea-level refractive index for baseline. Default 1.000293.
60
+ scale_height : float
61
+ Scale height for baseline exponential. Default 8500 m.
62
+ turbulence_strength : float
63
+ Strength of turbulent perturbations (relative). Default 1e-6.
64
+ outer_scale : float
65
+ Outer scale of turbulence in meters. Default 100 m.
66
+ seed : int, optional
67
+ Random seed for reproducibility.
68
+
69
+ Example
70
+ -------
71
+ >>> turb = TurbulentAtmosphere(
72
+ ... bounds=((-5000, 5000), (-5000, 5000), (0, 20000)),
73
+ ... grid_resolution=(64, 64, 32),
74
+ ... turbulence_strength=1e-5,
75
+ ... )
76
+ """
77
+
78
+ def __init__(
79
+ self,
80
+ bounds: tuple[tuple[float, float], ...],
81
+ grid_resolution: tuple[int, int, int] = (64, 64, 32),
82
+ baseline_n: float = 1.000293,
83
+ scale_height: float = 8500.0,
84
+ turbulence_strength: float = 1e-6,
85
+ outer_scale: float = 100.0,
86
+ seed: int | None = None,
87
+ ):
88
+ nx, ny, nz = grid_resolution
89
+ (x_min, x_max), (y_min, y_max), (z_min, z_max) = bounds
90
+
91
+ # Create coordinate grids
92
+ # x = np.linspace(x_min, x_max, nx)
93
+ # y = np.linspace(y_min, y_max, ny)
94
+ z = np.linspace(z_min, z_max, nz)
95
+ Z = np.zeros((nx, ny, nz))
96
+ for k in range(nz):
97
+ Z[:, :, k] = z[k]
98
+
99
+ # Baseline exponential profile
100
+ delta_n = baseline_n - 1.0
101
+ n_grid = 1.0 + delta_n * np.exp(-Z / scale_height)
102
+
103
+ # Add turbulent perturbations
104
+ if seed is not None:
105
+ np.random.seed(seed)
106
+
107
+ # Simple turbulence model using filtered noise
108
+ # (Real Kolmogorov turbulence would use proper power spectrum)
109
+ noise = np.random.randn(nx, ny, nz)
110
+
111
+ # Smooth with Gaussian filter to create correlated structure
112
+ from scipy.ndimage import gaussian_filter
113
+
114
+ dx = (x_max - x_min) / (nx - 1)
115
+ sigma = outer_scale / dx / 2.0 # Filter scale in grid units
116
+ turbulence = gaussian_filter(noise, sigma=sigma, mode="wrap")
117
+
118
+ # Normalize and scale turbulence
119
+ turbulence = turbulence / np.std(turbulence) * turbulence_strength
120
+
121
+ # Add to baseline (scaled by local n-1 to preserve physics)
122
+ n_grid = n_grid + turbulence * (n_grid - 1.0)
123
+
124
+ super().__init__(
125
+ name="Turbulent Atmosphere",
126
+ n_grid=n_grid.astype(np.float32),
127
+ bounds=bounds,
128
+ )
129
+
130
+ self.baseline_n = baseline_n
131
+ self.scale_height = scale_height
132
+ self.turbulence_strength = turbulence_strength
133
+
134
+
135
+ __all__ = ["TurbulentAtmosphere"]
@@ -0,0 +1,149 @@
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
+ US Standard Atmosphere (1976) Implementation
36
+
37
+ Implements a piecewise atmosphere model with:
38
+ - Troposphere (0-11 km): Temperature lapse rate
39
+ - Stratosphere (11-20 km): Isothermal
40
+ - Upper stratosphere (20-32 km): Temperature inversion
41
+ """
42
+
43
+ import math
44
+
45
+ from ..base import SimpleInhomogeneousModel
46
+ from ..utils.constants import EARTH_RADIUS
47
+
48
+
49
+ class USStandardAtmosphere(SimpleInhomogeneousModel):
50
+ """
51
+ US Standard Atmosphere (1976) approximation.
52
+
53
+ Implements a piecewise atmosphere model with:
54
+ - Troposphere (0-11 km): Temperature lapse rate
55
+ - Stratosphere (11-20 km): Isothermal
56
+ - Upper stratosphere (20-32 km): Temperature inversion
57
+
58
+ This is more realistic than a simple exponential model but still
59
+ radially symmetric, making it a good fit for SimpleInhomogeneousModel.
60
+
61
+ Parameters
62
+ ----------
63
+ earth_center : tuple, optional
64
+ Center of Earth. Default is (0, 0, 0).
65
+ earth_radius : float, optional
66
+ Radius of Earth in meters. Default is 6,371,000 m.
67
+
68
+ Example
69
+ -------
70
+ >>> atm = USStandardAtmosphere()
71
+ >>> n = atm.get_refractive_index(0, 0, EARTH_RADIUS + 10000, 532e-9)
72
+ """
73
+
74
+ # US Standard Atmosphere constants
75
+ T0 = 288.15 # Sea level temperature (K)
76
+ P0 = 101325.0 # Sea level pressure (Pa)
77
+ L0 = -0.0065 # Temperature lapse rate in troposphere (K/m)
78
+ H_TROPOPAUSE = 11000.0 # Tropopause altitude (m)
79
+ T_TROPOPAUSE = 216.65 # Tropopause temperature (K)
80
+
81
+ def __init__(
82
+ self,
83
+ earth_center: tuple[float, float, float] = (0.0, 0.0, 0.0),
84
+ earth_radius: float = EARTH_RADIUS,
85
+ ):
86
+ super().__init__(
87
+ name="US Standard Atmosphere",
88
+ center=earth_center,
89
+ reference_radius=earth_radius,
90
+ altitude_range=(0.0, 100_000.0),
91
+ lut_resolution=20000,
92
+ )
93
+ self.earth_center = earth_center
94
+ self.earth_radius = earth_radius
95
+
96
+ def n_at_altitude(self, altitude: float, wavelength: float | None = None) -> float:
97
+ """
98
+ Compute refractive index using US Standard Atmosphere model.
99
+
100
+ Uses the Edlen formula approximation: n - 1 ∝ P/T
101
+ """
102
+ altitude = max(altitude, 0.0)
103
+
104
+ if altitude <= self.H_TROPOPAUSE:
105
+ # Troposphere: linear temperature decrease
106
+ T = self.T0 + self.L0 * altitude
107
+ P = self.P0 * (T / self.T0) ** (-9.80665 / (self.L0 * 287.053))
108
+ elif altitude <= 20000:
109
+ # Lower stratosphere: isothermal
110
+ T = self.T_TROPOPAUSE
111
+ P_trop = self.P0 * (self.T_TROPOPAUSE / self.T0) ** (
112
+ -9.80665 / (self.L0 * 287.053)
113
+ )
114
+ P = P_trop * math.exp(
115
+ -9.80665 * (altitude - self.H_TROPOPAUSE) / (287.053 * T)
116
+ )
117
+ else:
118
+ # Upper stratosphere: simplified
119
+ T = self.T_TROPOPAUSE + 0.001 * (altitude - 20000)
120
+ P_20km = (
121
+ self.P0
122
+ * (self.T_TROPOPAUSE / self.T0) ** (-9.80665 / (self.L0 * 287.053))
123
+ * math.exp(-9.80665 * 9000 / (287.053 * self.T_TROPOPAUSE))
124
+ )
125
+ P = P_20km * math.exp(-9.80665 * (altitude - 20000) / (287.053 * T))
126
+
127
+ # Edlen formula approximation for dry air
128
+ # n - 1 = 0.000293 * (P/P0) * (T0/T)
129
+ refractivity = 0.000293 * (P / self.P0) * (self.T0 / T)
130
+ return 1.0 + refractivity
131
+
132
+ def dn_dh_at_altitude(
133
+ self, altitude: float, wavelength: float | None = None
134
+ ) -> float:
135
+ """Numerical derivative (accurate enough given model complexity)."""
136
+ eps = 10.0 # 10 meter step
137
+ h_plus = max(altitude + eps, 0.0)
138
+ h_minus = max(altitude - eps, 0.0)
139
+
140
+ if h_plus == h_minus:
141
+ return 0.0
142
+
143
+ return (
144
+ self.n_at_altitude(h_plus, wavelength)
145
+ - self.n_at_altitude(h_minus, wavelength)
146
+ ) / (h_plus - h_minus)
147
+
148
+
149
+ __all__ = ["USStandardAtmosphere"]
@@ -0,0 +1,77 @@
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 for Materials Module
36
+
37
+ This submodule contains utility functions for the materials system:
38
+
39
+ - Dispersion functions (Sellmeier, Cauchy)
40
+ - Factory functions for creating dispersive materials
41
+ - GPU device functions for CUDA kernels
42
+ - Physical constants
43
+ """
44
+
45
+ from .constants import (
46
+ EARTH_RADIUS,
47
+ SCALE_HEIGHT_DEFAULT,
48
+ N_SEA_LEVEL,
49
+ )
50
+ from .dispersion import (
51
+ sellmeier_refractive_index,
52
+ cauchy_refractive_index,
53
+ )
54
+ from .factories import (
55
+ create_sellmeier_material,
56
+ create_cauchy_material,
57
+ )
58
+ from .device_functions import (
59
+ device_sellmeier_equation,
60
+ device_cauchy_equation,
61
+ )
62
+
63
+ __all__ = [
64
+ # Constants
65
+ "EARTH_RADIUS",
66
+ "SCALE_HEIGHT_DEFAULT",
67
+ "N_SEA_LEVEL",
68
+ # Dispersion functions
69
+ "sellmeier_refractive_index",
70
+ "cauchy_refractive_index",
71
+ # Factory functions
72
+ "create_sellmeier_material",
73
+ "create_cauchy_material",
74
+ # GPU device functions
75
+ "device_sellmeier_equation",
76
+ "device_cauchy_equation",
77
+ ]
@@ -0,0 +1,45 @@
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
+ Physical Constants for Materials Module
36
+
37
+ Contains commonly used physical constants for atmospheric and optical calculations.
38
+ """
39
+
40
+ # Earth parameters
41
+ EARTH_RADIUS = 6_371_000.0 # meters
42
+
43
+ # Atmospheric constants
44
+ SCALE_HEIGHT_DEFAULT = 8500.0 # meters (typical for troposphere)
45
+ N_SEA_LEVEL = 1.000293 # Refractive index of air at STP
@@ -0,0 +1,117 @@
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 Device Functions (Numba CUDA compatible)
36
+
37
+ Contains functions designed to be used in Numba CUDA kernels.
38
+ These should be compiled with @cuda.jit(device=True) in GPU modules.
39
+ """
40
+
41
+ import math
42
+
43
+
44
+ def device_sellmeier_equation(
45
+ wl_um: float,
46
+ B1: float,
47
+ B2: float,
48
+ B3: float,
49
+ C1: float,
50
+ C2: float,
51
+ C3: float,
52
+ ) -> float:
53
+ """
54
+ GPU-compatible Sellmeier equation.
55
+
56
+ For use in Numba CUDA kernels. Computes refractive index
57
+ from wavelength and Sellmeier coefficients.
58
+
59
+ Parameters
60
+ ----------
61
+ wl_um : float
62
+ Wavelength in micrometers.
63
+ B1, B2, B3 : float
64
+ Sellmeier B coefficients.
65
+ C1, C2, C3 : float
66
+ Sellmeier C coefficients in μm².
67
+
68
+ Returns
69
+ -------
70
+ n : float
71
+ Refractive index.
72
+
73
+ Notes
74
+ -----
75
+ This function is designed to be compiled with @cuda.jit(device=True).
76
+ Import and compile in your GPU module.
77
+ """
78
+ wl2 = wl_um * wl_um
79
+ n2_minus_1 = B1 * wl2 / (wl2 - C1) + B2 * wl2 / (wl2 - C2) + B3 * wl2 / (wl2 - C3)
80
+ return math.sqrt(1.0 + n2_minus_1)
81
+
82
+
83
+ def device_cauchy_equation(
84
+ wl_um: float,
85
+ A: float,
86
+ B: float,
87
+ C: float,
88
+ ) -> float:
89
+ """
90
+ GPU-compatible Cauchy equation.
91
+
92
+ For use in Numba CUDA kernels. Computes refractive index
93
+ from wavelength and Cauchy coefficients.
94
+
95
+ Parameters
96
+ ----------
97
+ wl_um : float
98
+ Wavelength in micrometers.
99
+ A : float
100
+ Constant term.
101
+ B : float
102
+ First-order dispersion coefficient in μm².
103
+ C : float
104
+ Second-order dispersion coefficient in μm⁴.
105
+
106
+ Returns
107
+ -------
108
+ n : float
109
+ Refractive index.
110
+
111
+ Notes
112
+ -----
113
+ This function is designed to be compiled with @cuda.jit(device=True).
114
+ Import and compile in your GPU module.
115
+ """
116
+ wl2 = wl_um * wl_um
117
+ return A + B / wl2 + C / (wl2 * wl2)