lsurf 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lsurf/__init__.py +471 -0
- lsurf/analysis/__init__.py +107 -0
- lsurf/analysis/healpix_utils.py +418 -0
- lsurf/analysis/sphere_viz.py +1280 -0
- lsurf/cli/__init__.py +48 -0
- lsurf/cli/build.py +398 -0
- lsurf/cli/config_schema.py +318 -0
- lsurf/cli/gui_cmd.py +76 -0
- lsurf/cli/interactive.py +850 -0
- lsurf/cli/main.py +81 -0
- lsurf/cli/run.py +806 -0
- lsurf/detectors/__init__.py +266 -0
- lsurf/detectors/analysis.py +289 -0
- lsurf/detectors/base.py +284 -0
- lsurf/detectors/constant_size_rings.py +485 -0
- lsurf/detectors/directional.py +45 -0
- lsurf/detectors/extended/__init__.py +73 -0
- lsurf/detectors/extended/local_sphere.py +353 -0
- lsurf/detectors/extended/recording_sphere.py +368 -0
- lsurf/detectors/planar.py +45 -0
- lsurf/detectors/protocol.py +187 -0
- lsurf/detectors/recording_spheres.py +63 -0
- lsurf/detectors/results.py +1140 -0
- lsurf/detectors/small/__init__.py +79 -0
- lsurf/detectors/small/directional.py +330 -0
- lsurf/detectors/small/planar.py +401 -0
- lsurf/detectors/small/spherical.py +450 -0
- lsurf/detectors/spherical.py +45 -0
- lsurf/geometry/__init__.py +199 -0
- lsurf/geometry/builder.py +478 -0
- lsurf/geometry/cell.py +228 -0
- lsurf/geometry/cell_geometry.py +247 -0
- lsurf/geometry/detector_arrays.py +1785 -0
- lsurf/geometry/geometry.py +222 -0
- lsurf/geometry/surface_analysis.py +375 -0
- lsurf/geometry/validation.py +91 -0
- lsurf/gui/__init__.py +51 -0
- lsurf/gui/app.py +903 -0
- lsurf/gui/core/__init__.py +39 -0
- lsurf/gui/core/scene.py +343 -0
- lsurf/gui/core/simulation.py +264 -0
- lsurf/gui/renderers/__init__.py +40 -0
- lsurf/gui/renderers/ray_renderer.py +353 -0
- lsurf/gui/renderers/source_renderer.py +505 -0
- lsurf/gui/renderers/surface_renderer.py +477 -0
- lsurf/gui/views/__init__.py +48 -0
- lsurf/gui/views/config_editor.py +3199 -0
- lsurf/gui/views/properties.py +257 -0
- lsurf/gui/views/results.py +291 -0
- lsurf/gui/views/scene_tree.py +180 -0
- lsurf/gui/views/viewport_3d.py +555 -0
- lsurf/gui/views/visualizations.py +712 -0
- lsurf/materials/__init__.py +169 -0
- lsurf/materials/base/__init__.py +64 -0
- lsurf/materials/base/full_inhomogeneous.py +208 -0
- lsurf/materials/base/grid_inhomogeneous.py +319 -0
- lsurf/materials/base/homogeneous.py +342 -0
- lsurf/materials/base/material_field.py +527 -0
- lsurf/materials/base/simple_inhomogeneous.py +418 -0
- lsurf/materials/base/spectral_inhomogeneous.py +497 -0
- lsurf/materials/implementations/__init__.py +120 -0
- lsurf/materials/implementations/data/alpha_values_typical_atmosphere_updated.txt +24 -0
- lsurf/materials/implementations/duct_atmosphere.py +390 -0
- lsurf/materials/implementations/exponential_atmosphere.py +435 -0
- lsurf/materials/implementations/gaussian_lens.py +120 -0
- lsurf/materials/implementations/interpolated_data.py +123 -0
- lsurf/materials/implementations/layered_atmosphere.py +134 -0
- lsurf/materials/implementations/linear_gradient.py +109 -0
- lsurf/materials/implementations/linsley_atmosphere.py +764 -0
- lsurf/materials/implementations/standard_materials.py +126 -0
- lsurf/materials/implementations/turbulent_atmosphere.py +135 -0
- lsurf/materials/implementations/us_standard_atmosphere.py +149 -0
- lsurf/materials/utils/__init__.py +77 -0
- lsurf/materials/utils/constants.py +45 -0
- lsurf/materials/utils/device_functions.py +117 -0
- lsurf/materials/utils/dispersion.py +160 -0
- lsurf/materials/utils/factories.py +142 -0
- lsurf/propagation/__init__.py +91 -0
- lsurf/propagation/detector_gpu.py +67 -0
- lsurf/propagation/gpu_device_rays.py +294 -0
- lsurf/propagation/kernels/__init__.py +175 -0
- lsurf/propagation/kernels/absorption/__init__.py +61 -0
- lsurf/propagation/kernels/absorption/grid.py +240 -0
- lsurf/propagation/kernels/absorption/simple.py +232 -0
- lsurf/propagation/kernels/absorption/spectral.py +410 -0
- lsurf/propagation/kernels/detection/__init__.py +64 -0
- lsurf/propagation/kernels/detection/protocol.py +102 -0
- lsurf/propagation/kernels/detection/spherical.py +255 -0
- lsurf/propagation/kernels/device_functions.py +790 -0
- lsurf/propagation/kernels/fresnel/__init__.py +64 -0
- lsurf/propagation/kernels/fresnel/protocol.py +97 -0
- lsurf/propagation/kernels/fresnel/standard.py +258 -0
- lsurf/propagation/kernels/intersection/__init__.py +79 -0
- lsurf/propagation/kernels/intersection/annular_plane.py +207 -0
- lsurf/propagation/kernels/intersection/bounded_plane.py +205 -0
- lsurf/propagation/kernels/intersection/plane.py +166 -0
- lsurf/propagation/kernels/intersection/protocol.py +95 -0
- lsurf/propagation/kernels/intersection/signed_distance.py +742 -0
- lsurf/propagation/kernels/intersection/sphere.py +190 -0
- lsurf/propagation/kernels/propagation/__init__.py +85 -0
- lsurf/propagation/kernels/propagation/grid.py +527 -0
- lsurf/propagation/kernels/propagation/protocol.py +105 -0
- lsurf/propagation/kernels/propagation/simple.py +460 -0
- lsurf/propagation/kernels/propagation/spectral.py +875 -0
- lsurf/propagation/kernels/registry.py +331 -0
- lsurf/propagation/kernels/surface/__init__.py +72 -0
- lsurf/propagation/kernels/surface/bisection.py +232 -0
- lsurf/propagation/kernels/surface/detection.py +402 -0
- lsurf/propagation/kernels/surface/reduction.py +166 -0
- lsurf/propagation/propagator_protocol.py +222 -0
- lsurf/propagation/propagators/__init__.py +101 -0
- lsurf/propagation/propagators/detector_handler.py +354 -0
- lsurf/propagation/propagators/factory.py +200 -0
- lsurf/propagation/propagators/fresnel_handler.py +305 -0
- lsurf/propagation/propagators/gpu_gradient.py +566 -0
- lsurf/propagation/propagators/gpu_surface_propagator.py +707 -0
- lsurf/propagation/propagators/gradient.py +429 -0
- lsurf/propagation/propagators/intersection_handler.py +327 -0
- lsurf/propagation/propagators/material_propagator.py +398 -0
- lsurf/propagation/propagators/signed_distance_handler.py +522 -0
- lsurf/propagation/propagators/spectral_gpu_gradient.py +553 -0
- lsurf/propagation/propagators/surface_interaction.py +616 -0
- lsurf/propagation/propagators/surface_propagator.py +719 -0
- lsurf/py.typed +1 -0
- lsurf/simulation/__init__.py +70 -0
- lsurf/simulation/config.py +164 -0
- lsurf/simulation/orchestrator.py +462 -0
- lsurf/simulation/result.py +299 -0
- lsurf/simulation/simulation.py +262 -0
- lsurf/sources/__init__.py +128 -0
- lsurf/sources/base.py +264 -0
- lsurf/sources/collimated.py +252 -0
- lsurf/sources/custom.py +409 -0
- lsurf/sources/diverging.py +228 -0
- lsurf/sources/gaussian.py +272 -0
- lsurf/sources/parallel_from_positions.py +197 -0
- lsurf/sources/point.py +172 -0
- lsurf/sources/uniform_diverging.py +258 -0
- lsurf/surfaces/__init__.py +184 -0
- lsurf/surfaces/cpu/__init__.py +50 -0
- lsurf/surfaces/cpu/curved_wave.py +463 -0
- lsurf/surfaces/cpu/gerstner_wave.py +381 -0
- lsurf/surfaces/cpu/wave_params.py +118 -0
- lsurf/surfaces/gpu/__init__.py +72 -0
- lsurf/surfaces/gpu/annular_plane.py +453 -0
- lsurf/surfaces/gpu/bounded_plane.py +390 -0
- lsurf/surfaces/gpu/curved_wave.py +483 -0
- lsurf/surfaces/gpu/gerstner_wave.py +377 -0
- lsurf/surfaces/gpu/multi_curved_wave.py +520 -0
- lsurf/surfaces/gpu/plane.py +299 -0
- lsurf/surfaces/gpu/recording_sphere.py +587 -0
- lsurf/surfaces/gpu/sphere.py +311 -0
- lsurf/surfaces/protocol.py +336 -0
- lsurf/surfaces/registry.py +373 -0
- lsurf/utilities/__init__.py +175 -0
- lsurf/utilities/detector_analysis.py +814 -0
- lsurf/utilities/fresnel.py +628 -0
- lsurf/utilities/interactions.py +1215 -0
- lsurf/utilities/propagation.py +602 -0
- lsurf/utilities/ray_data.py +532 -0
- lsurf/utilities/recording_sphere.py +745 -0
- lsurf/utilities/time_spread.py +463 -0
- lsurf/visualization/__init__.py +329 -0
- lsurf/visualization/absorption_plots.py +334 -0
- lsurf/visualization/atmospheric_plots.py +754 -0
- lsurf/visualization/common.py +348 -0
- lsurf/visualization/detector_plots.py +1350 -0
- lsurf/visualization/detector_sphere_plots.py +1173 -0
- lsurf/visualization/fresnel_plots.py +1061 -0
- lsurf/visualization/ocean_simulation_plots.py +999 -0
- lsurf/visualization/polarization_plots.py +916 -0
- lsurf/visualization/raytracing_plots.py +1521 -0
- lsurf/visualization/ring_detector_plots.py +1867 -0
- lsurf/visualization/time_spread_plots.py +531 -0
- lsurf-1.0.0.dist-info/METADATA +381 -0
- lsurf-1.0.0.dist-info/RECORD +180 -0
- lsurf-1.0.0.dist-info/WHEEL +5 -0
- lsurf-1.0.0.dist-info/entry_points.txt +2 -0
- lsurf-1.0.0.dist-info/licenses/LICENSE +32 -0
- lsurf-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,329 @@
|
|
|
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
|
+
Visualization Module
|
|
36
|
+
|
|
37
|
+
Provides plotting functions for ray tracing and detector analysis.
|
|
38
|
+
Functions are organized into single-axis functions (for composability)
|
|
39
|
+
and composite figure builders.
|
|
40
|
+
|
|
41
|
+
Modules
|
|
42
|
+
-------
|
|
43
|
+
common : Shared utilities and styling
|
|
44
|
+
raytracing_plots : Ray path and surface visualization
|
|
45
|
+
detector_plots : Detector and beam analysis visualization
|
|
46
|
+
|
|
47
|
+
Examples
|
|
48
|
+
--------
|
|
49
|
+
Single-axis functions for custom layouts:
|
|
50
|
+
|
|
51
|
+
>>> import matplotlib.pyplot as plt
|
|
52
|
+
>>> from surface_roughness.visualization import (
|
|
53
|
+
... plot_ray_paths_projection,
|
|
54
|
+
... plot_ray_endpoints_scatter,
|
|
55
|
+
... plot_detection_counts,
|
|
56
|
+
... )
|
|
57
|
+
>>> fig, axes = plt.subplots(1, 2)
|
|
58
|
+
>>> plot_ray_paths_projection(axes[0], ray_history, projection="xz")
|
|
59
|
+
>>> plot_ray_endpoints_scatter(axes[1], rays, projection="xy")
|
|
60
|
+
|
|
61
|
+
Composite figures for quick visualization:
|
|
62
|
+
|
|
63
|
+
>>> from surface_roughness.visualization import (
|
|
64
|
+
... create_ray_overview_figure,
|
|
65
|
+
... create_detector_scan_figure,
|
|
66
|
+
... )
|
|
67
|
+
>>> fig = create_ray_overview_figure(rays, surface)
|
|
68
|
+
>>> fig = create_detector_scan_figure(angles, counts, intensities, total)
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
# Common utilities
|
|
72
|
+
from .common import (
|
|
73
|
+
# Style constants
|
|
74
|
+
DEFAULT_FIGSIZE,
|
|
75
|
+
DEFAULT_LINEWIDTH,
|
|
76
|
+
DEFAULT_MARKERSIZE,
|
|
77
|
+
INTENSITY_CMAP,
|
|
78
|
+
LINE_ALPHA,
|
|
79
|
+
SCATTER_ALPHA,
|
|
80
|
+
WAVELENGTH_CMAP,
|
|
81
|
+
add_colorbar,
|
|
82
|
+
create_figure,
|
|
83
|
+
# Utility functions
|
|
84
|
+
get_color_mapping,
|
|
85
|
+
get_projection_config,
|
|
86
|
+
save_figure,
|
|
87
|
+
setup_axis_grid,
|
|
88
|
+
wavelength_to_color,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Detector visualization
|
|
92
|
+
from .detector_plots import (
|
|
93
|
+
create_beam_profile_figure,
|
|
94
|
+
create_detector_scan_figure,
|
|
95
|
+
# Composite builders
|
|
96
|
+
create_wavelength_figure,
|
|
97
|
+
plot_angular_histogram,
|
|
98
|
+
plot_arrival_angle_distribution,
|
|
99
|
+
plot_beam_profile,
|
|
100
|
+
# Single-axis functions
|
|
101
|
+
plot_beam_slice,
|
|
102
|
+
plot_detection_counts,
|
|
103
|
+
plot_detection_efficiency,
|
|
104
|
+
# Production figures
|
|
105
|
+
plot_detector_scan_results,
|
|
106
|
+
plot_mean_arrival_time,
|
|
107
|
+
# Legacy convenience functions
|
|
108
|
+
plot_statistics_evolution,
|
|
109
|
+
plot_timing_distribution,
|
|
110
|
+
plot_wavelength_distribution,
|
|
111
|
+
plot_wavelength_histogram,
|
|
112
|
+
plot_wavelength_intensity_histogram,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
# Ocean simulation visualization
|
|
116
|
+
from .ocean_simulation_plots import (
|
|
117
|
+
create_ocean_simulation_figures,
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Detector sphere visualization
|
|
121
|
+
from .detector_sphere_plots import (
|
|
122
|
+
plot_3d_intersection_scatter,
|
|
123
|
+
plot_arrival_time_distributions,
|
|
124
|
+
plot_energy_density_map,
|
|
125
|
+
plot_geometry_schematic,
|
|
126
|
+
plot_mollweide_detector_projection,
|
|
127
|
+
plot_ocean_intersections_top_view,
|
|
128
|
+
plot_pareto_front,
|
|
129
|
+
plot_time_spread_comparison,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
# Ray tracing visualization
|
|
133
|
+
from .raytracing_plots import (
|
|
134
|
+
# Composite builders
|
|
135
|
+
create_ray_overview_figure,
|
|
136
|
+
plot_bounce_points,
|
|
137
|
+
plot_incoming_rays,
|
|
138
|
+
plot_multi_bounce_paths,
|
|
139
|
+
# Production figures
|
|
140
|
+
plot_production_ray_overview,
|
|
141
|
+
plot_ray_endpoints,
|
|
142
|
+
plot_ray_endpoints_histogram,
|
|
143
|
+
plot_ray_endpoints_scatter,
|
|
144
|
+
# Legacy convenience functions
|
|
145
|
+
plot_ray_paths_2d,
|
|
146
|
+
# Single-axis functions
|
|
147
|
+
plot_ray_paths_projection,
|
|
148
|
+
plot_ray_paths_with_surface,
|
|
149
|
+
plot_reflected_rays,
|
|
150
|
+
plot_surface_profile,
|
|
151
|
+
plot_wave_surface_detail,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Fresnel reflection visualization
|
|
155
|
+
from .fresnel_plots import (
|
|
156
|
+
plot_brewster_validation,
|
|
157
|
+
plot_fresnel_reflection,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
# Polarization visualization
|
|
161
|
+
from .polarization_plots import (
|
|
162
|
+
get_ray_coordinates,
|
|
163
|
+
plot_fresnel_reflectance_curves,
|
|
164
|
+
plot_measured_polarization_reflectance,
|
|
165
|
+
plot_polarization_ellipse,
|
|
166
|
+
plot_polarization_vector_components,
|
|
167
|
+
plot_polarization_vs_elevation,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Time spread visualization
|
|
171
|
+
from .time_spread_plots import (
|
|
172
|
+
create_arrival_time_figure,
|
|
173
|
+
# Composite builders
|
|
174
|
+
create_time_spread_schematic,
|
|
175
|
+
plot_arrival_time_histogram,
|
|
176
|
+
plot_beam_footprint_top,
|
|
177
|
+
plot_footprint_arrival_times,
|
|
178
|
+
# Single-axis functions
|
|
179
|
+
plot_time_spread_geometry_side,
|
|
180
|
+
plot_time_spread_vs_divergence,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Atmospheric refraction visualization
|
|
184
|
+
from .atmospheric_plots import (
|
|
185
|
+
plot_ray_trajectories,
|
|
186
|
+
plot_refractive_index_profile,
|
|
187
|
+
plot_trajectory_offset,
|
|
188
|
+
plot_horizontal_ray_deviation,
|
|
189
|
+
create_atmospheric_refraction_figure,
|
|
190
|
+
create_atmospheric_refraction_figure_with_offset,
|
|
191
|
+
plot_trajectory_comparison,
|
|
192
|
+
save_atmospheric_figure,
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Absorption visualization
|
|
196
|
+
from .absorption_plots import (
|
|
197
|
+
plot_homogeneous_absorption,
|
|
198
|
+
plot_exponential_atmosphere_absorption,
|
|
199
|
+
plot_spectral_atmosphere_absorption,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
# Ring detector visualization
|
|
203
|
+
from .ring_detector_plots import (
|
|
204
|
+
compute_constant_size_bin_stats,
|
|
205
|
+
compute_ring_azimuth_maps,
|
|
206
|
+
compute_ring_spread_stats,
|
|
207
|
+
plot_constant_size_timing_analysis,
|
|
208
|
+
plot_constant_size_timing_distributions,
|
|
209
|
+
plot_geometry_analysis,
|
|
210
|
+
plot_per_ring_timing_distributions,
|
|
211
|
+
plot_polar_irradiance,
|
|
212
|
+
plot_ring_azimuth_heatmap,
|
|
213
|
+
plot_ring_overview,
|
|
214
|
+
plot_ring_side_view,
|
|
215
|
+
plot_spread_analysis,
|
|
216
|
+
plot_time_spread_comparison as plot_ring_time_spread_comparison,
|
|
217
|
+
plot_timing_heatmap,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
__all__ = [
|
|
221
|
+
# Common
|
|
222
|
+
"DEFAULT_FIGSIZE",
|
|
223
|
+
"LINE_ALPHA",
|
|
224
|
+
"SCATTER_ALPHA",
|
|
225
|
+
"DEFAULT_LINEWIDTH",
|
|
226
|
+
"DEFAULT_MARKERSIZE",
|
|
227
|
+
"WAVELENGTH_CMAP",
|
|
228
|
+
"INTENSITY_CMAP",
|
|
229
|
+
"get_color_mapping",
|
|
230
|
+
"get_projection_config",
|
|
231
|
+
"wavelength_to_color",
|
|
232
|
+
"setup_axis_grid",
|
|
233
|
+
"add_colorbar",
|
|
234
|
+
"create_figure",
|
|
235
|
+
"save_figure",
|
|
236
|
+
# Ray Tracing - Single Axis
|
|
237
|
+
"plot_ray_paths_projection",
|
|
238
|
+
"plot_ray_endpoints_scatter",
|
|
239
|
+
"plot_ray_endpoints_histogram",
|
|
240
|
+
"plot_surface_profile",
|
|
241
|
+
"plot_bounce_points",
|
|
242
|
+
"plot_incoming_rays",
|
|
243
|
+
"plot_reflected_rays",
|
|
244
|
+
"plot_multi_bounce_paths",
|
|
245
|
+
# Ray Tracing - Composite
|
|
246
|
+
"create_ray_overview_figure",
|
|
247
|
+
"plot_production_ray_overview",
|
|
248
|
+
"plot_wave_surface_detail",
|
|
249
|
+
"plot_ray_paths_with_surface",
|
|
250
|
+
"plot_fresnel_reflection",
|
|
251
|
+
"plot_brewster_validation",
|
|
252
|
+
# Ray Tracing - Polarization
|
|
253
|
+
"plot_polarization_vector_components",
|
|
254
|
+
"plot_polarization_ellipse",
|
|
255
|
+
"plot_polarization_vs_elevation",
|
|
256
|
+
"plot_fresnel_reflectance_curves",
|
|
257
|
+
"plot_measured_polarization_reflectance",
|
|
258
|
+
"get_ray_coordinates",
|
|
259
|
+
# Ray Tracing - Legacy
|
|
260
|
+
"plot_ray_paths_2d",
|
|
261
|
+
"plot_ray_endpoints",
|
|
262
|
+
# Detector - Single Axis
|
|
263
|
+
"plot_beam_slice",
|
|
264
|
+
"plot_wavelength_histogram",
|
|
265
|
+
"plot_wavelength_intensity_histogram",
|
|
266
|
+
"plot_detection_counts",
|
|
267
|
+
"plot_detection_efficiency",
|
|
268
|
+
"plot_mean_arrival_time",
|
|
269
|
+
"plot_timing_distribution",
|
|
270
|
+
"plot_arrival_angle_distribution",
|
|
271
|
+
"plot_angular_histogram",
|
|
272
|
+
# Detector - Composite
|
|
273
|
+
"create_wavelength_figure",
|
|
274
|
+
"create_beam_profile_figure",
|
|
275
|
+
"create_detector_scan_figure",
|
|
276
|
+
"plot_detector_scan_results",
|
|
277
|
+
# Detector - Legacy
|
|
278
|
+
"plot_statistics_evolution",
|
|
279
|
+
"plot_beam_profile",
|
|
280
|
+
"plot_wavelength_distribution",
|
|
281
|
+
# Ocean Simulation - Complete Suite
|
|
282
|
+
"create_ocean_simulation_figures",
|
|
283
|
+
# Detector Sphere Visualization
|
|
284
|
+
"plot_geometry_schematic",
|
|
285
|
+
"plot_ocean_intersections_top_view",
|
|
286
|
+
"plot_3d_intersection_scatter",
|
|
287
|
+
"plot_pareto_front",
|
|
288
|
+
"plot_energy_density_map",
|
|
289
|
+
"plot_mollweide_detector_projection",
|
|
290
|
+
"plot_time_spread_comparison",
|
|
291
|
+
"plot_arrival_time_distributions",
|
|
292
|
+
# Time Spread - Single Axis
|
|
293
|
+
"plot_time_spread_geometry_side",
|
|
294
|
+
"plot_beam_footprint_top",
|
|
295
|
+
"plot_arrival_time_histogram",
|
|
296
|
+
"plot_footprint_arrival_times",
|
|
297
|
+
"plot_time_spread_vs_divergence",
|
|
298
|
+
# Time Spread - Composite
|
|
299
|
+
"create_time_spread_schematic",
|
|
300
|
+
"create_arrival_time_figure",
|
|
301
|
+
# Atmospheric Refraction
|
|
302
|
+
"plot_ray_trajectories",
|
|
303
|
+
"plot_refractive_index_profile",
|
|
304
|
+
"plot_trajectory_offset",
|
|
305
|
+
"plot_horizontal_ray_deviation",
|
|
306
|
+
"create_atmospheric_refraction_figure",
|
|
307
|
+
"create_atmospheric_refraction_figure_with_offset",
|
|
308
|
+
"plot_trajectory_comparison",
|
|
309
|
+
"save_atmospheric_figure",
|
|
310
|
+
# Absorption
|
|
311
|
+
"plot_homogeneous_absorption",
|
|
312
|
+
"plot_exponential_atmosphere_absorption",
|
|
313
|
+
"plot_spectral_atmosphere_absorption",
|
|
314
|
+
# Ring Detector
|
|
315
|
+
"plot_geometry_analysis",
|
|
316
|
+
"plot_ring_side_view",
|
|
317
|
+
"plot_ring_overview",
|
|
318
|
+
"plot_ring_azimuth_heatmap",
|
|
319
|
+
"plot_timing_heatmap",
|
|
320
|
+
"plot_per_ring_timing_distributions",
|
|
321
|
+
"plot_spread_analysis",
|
|
322
|
+
"plot_polar_irradiance",
|
|
323
|
+
"plot_ring_time_spread_comparison",
|
|
324
|
+
"plot_constant_size_timing_analysis",
|
|
325
|
+
"plot_constant_size_timing_distributions",
|
|
326
|
+
"compute_ring_azimuth_maps",
|
|
327
|
+
"compute_ring_spread_stats",
|
|
328
|
+
"compute_constant_size_bin_stats",
|
|
329
|
+
]
|
|
@@ -0,0 +1,334 @@
|
|
|
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
|
+
Absorption Visualization
|
|
36
|
+
|
|
37
|
+
Plotting utilities for Beer-Lambert absorption analysis.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from pathlib import Path
|
|
41
|
+
from typing import Callable
|
|
42
|
+
|
|
43
|
+
import numpy as np
|
|
44
|
+
import matplotlib.pyplot as plt
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def plot_homogeneous_absorption(
|
|
48
|
+
alpha: float,
|
|
49
|
+
distance: float,
|
|
50
|
+
simulated_intensity: float,
|
|
51
|
+
simulated_optical_depth: float,
|
|
52
|
+
initial_intensity: float = 1.0,
|
|
53
|
+
save_path: str | Path | None = None,
|
|
54
|
+
show: bool = True,
|
|
55
|
+
) -> plt.Figure:
|
|
56
|
+
"""
|
|
57
|
+
Plot Beer-Lambert decay for homogeneous absorbing medium.
|
|
58
|
+
|
|
59
|
+
Parameters
|
|
60
|
+
----------
|
|
61
|
+
alpha : float
|
|
62
|
+
Absorption coefficient in m^-1
|
|
63
|
+
distance : float
|
|
64
|
+
Total propagation distance in m
|
|
65
|
+
simulated_intensity : float
|
|
66
|
+
Simulated final intensity
|
|
67
|
+
simulated_optical_depth : float
|
|
68
|
+
Simulated accumulated optical depth
|
|
69
|
+
initial_intensity : float
|
|
70
|
+
Initial intensity (default 1.0)
|
|
71
|
+
save_path : str or Path, optional
|
|
72
|
+
Path to save figure
|
|
73
|
+
show : bool
|
|
74
|
+
Whether to display the figure
|
|
75
|
+
|
|
76
|
+
Returns
|
|
77
|
+
-------
|
|
78
|
+
plt.Figure
|
|
79
|
+
The matplotlib figure
|
|
80
|
+
"""
|
|
81
|
+
distances = np.linspace(0, distance, 100)
|
|
82
|
+
analytical_intensity = initial_intensity * np.exp(-alpha * distances)
|
|
83
|
+
analytical_tau = alpha * distances
|
|
84
|
+
|
|
85
|
+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
|
|
86
|
+
|
|
87
|
+
# Intensity decay
|
|
88
|
+
ax1.plot(distances / 1000, analytical_intensity, "b-", label="Analytical")
|
|
89
|
+
ax1.scatter(
|
|
90
|
+
[distance / 1000],
|
|
91
|
+
[simulated_intensity],
|
|
92
|
+
c="r",
|
|
93
|
+
s=100,
|
|
94
|
+
label=f"Simulated ({simulated_intensity:.4f})",
|
|
95
|
+
zorder=5,
|
|
96
|
+
)
|
|
97
|
+
ax1.set_xlabel("Distance (km)")
|
|
98
|
+
ax1.set_ylabel("Intensity")
|
|
99
|
+
ax1.set_title("Beer-Lambert Decay")
|
|
100
|
+
ax1.legend()
|
|
101
|
+
ax1.grid(True, alpha=0.3)
|
|
102
|
+
|
|
103
|
+
# Optical depth
|
|
104
|
+
ax2.plot(distances / 1000, analytical_tau, "b-", label="Analytical")
|
|
105
|
+
ax2.scatter(
|
|
106
|
+
[distance / 1000],
|
|
107
|
+
[simulated_optical_depth],
|
|
108
|
+
c="r",
|
|
109
|
+
s=100,
|
|
110
|
+
label=f"Simulated ({simulated_optical_depth:.4f})",
|
|
111
|
+
zorder=5,
|
|
112
|
+
)
|
|
113
|
+
ax2.set_xlabel("Distance (km)")
|
|
114
|
+
ax2.set_ylabel("Optical Depth τ")
|
|
115
|
+
ax2.set_title("Optical Depth Accumulation")
|
|
116
|
+
ax2.legend()
|
|
117
|
+
ax2.grid(True, alpha=0.3)
|
|
118
|
+
|
|
119
|
+
plt.tight_layout()
|
|
120
|
+
|
|
121
|
+
if save_path:
|
|
122
|
+
fig.savefig(save_path, dpi=150)
|
|
123
|
+
|
|
124
|
+
if show:
|
|
125
|
+
plt.show()
|
|
126
|
+
|
|
127
|
+
return fig
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def plot_exponential_atmosphere_absorption(
|
|
131
|
+
alpha_sea_level: float,
|
|
132
|
+
scale_height: float,
|
|
133
|
+
final_altitude: float,
|
|
134
|
+
simulated_intensity: float,
|
|
135
|
+
simulated_optical_depth: float,
|
|
136
|
+
max_altitude: float = 100_000.0,
|
|
137
|
+
save_path: str | Path | None = None,
|
|
138
|
+
show: bool = True,
|
|
139
|
+
) -> plt.Figure:
|
|
140
|
+
"""
|
|
141
|
+
Plot absorption profiles for exponential atmosphere.
|
|
142
|
+
|
|
143
|
+
Parameters
|
|
144
|
+
----------
|
|
145
|
+
alpha_sea_level : float
|
|
146
|
+
Absorption coefficient at sea level in m^-1
|
|
147
|
+
scale_height : float
|
|
148
|
+
Atmospheric scale height in m
|
|
149
|
+
final_altitude : float
|
|
150
|
+
Final ray altitude in m
|
|
151
|
+
simulated_intensity : float
|
|
152
|
+
Simulated final intensity
|
|
153
|
+
simulated_optical_depth : float
|
|
154
|
+
Simulated accumulated optical depth
|
|
155
|
+
max_altitude : float
|
|
156
|
+
Maximum altitude for plots in m
|
|
157
|
+
save_path : str or Path, optional
|
|
158
|
+
Path to save figure
|
|
159
|
+
show : bool
|
|
160
|
+
Whether to display the figure
|
|
161
|
+
|
|
162
|
+
Returns
|
|
163
|
+
-------
|
|
164
|
+
plt.Figure
|
|
165
|
+
The matplotlib figure
|
|
166
|
+
"""
|
|
167
|
+
altitudes = np.linspace(0, max_altitude, 500)
|
|
168
|
+
alpha_profile = alpha_sea_level * np.exp(-altitudes / scale_height)
|
|
169
|
+
tau_profile = (
|
|
170
|
+
alpha_sea_level * scale_height * (1 - np.exp(-altitudes / scale_height))
|
|
171
|
+
)
|
|
172
|
+
intensity_profile = np.exp(-tau_profile)
|
|
173
|
+
|
|
174
|
+
fig, axes = plt.subplots(1, 3, figsize=(15, 5))
|
|
175
|
+
|
|
176
|
+
# Absorption coefficient profile
|
|
177
|
+
axes[0].semilogy(alpha_profile * 1e6, altitudes / 1000, "b-")
|
|
178
|
+
axes[0].set_xlabel("α (×10⁻⁶ m⁻¹)")
|
|
179
|
+
axes[0].set_ylabel("Altitude (km)")
|
|
180
|
+
axes[0].set_title("Absorption Coefficient Profile")
|
|
181
|
+
axes[0].grid(True, alpha=0.3)
|
|
182
|
+
|
|
183
|
+
# Optical depth
|
|
184
|
+
axes[1].plot(tau_profile, altitudes / 1000, "b-", label="Analytical")
|
|
185
|
+
axes[1].scatter(
|
|
186
|
+
[simulated_optical_depth],
|
|
187
|
+
[final_altitude / 1000],
|
|
188
|
+
c="r",
|
|
189
|
+
s=100,
|
|
190
|
+
label="Simulated",
|
|
191
|
+
zorder=5,
|
|
192
|
+
)
|
|
193
|
+
axes[1].set_xlabel("Optical Depth τ")
|
|
194
|
+
axes[1].set_ylabel("Altitude (km)")
|
|
195
|
+
axes[1].set_title("Accumulated Optical Depth")
|
|
196
|
+
axes[1].legend()
|
|
197
|
+
axes[1].grid(True, alpha=0.3)
|
|
198
|
+
|
|
199
|
+
# Intensity
|
|
200
|
+
axes[2].plot(intensity_profile, altitudes / 1000, "b-", label="Analytical")
|
|
201
|
+
axes[2].scatter(
|
|
202
|
+
[simulated_intensity],
|
|
203
|
+
[final_altitude / 1000],
|
|
204
|
+
c="r",
|
|
205
|
+
s=100,
|
|
206
|
+
label="Simulated",
|
|
207
|
+
zorder=5,
|
|
208
|
+
)
|
|
209
|
+
axes[2].set_xlabel("Intensity (I/I₀)")
|
|
210
|
+
axes[2].set_ylabel("Altitude (km)")
|
|
211
|
+
axes[2].set_title("Transmitted Intensity")
|
|
212
|
+
axes[2].legend()
|
|
213
|
+
axes[2].grid(True, alpha=0.3)
|
|
214
|
+
|
|
215
|
+
plt.tight_layout()
|
|
216
|
+
|
|
217
|
+
if save_path:
|
|
218
|
+
fig.savefig(save_path, dpi=150)
|
|
219
|
+
|
|
220
|
+
if show:
|
|
221
|
+
plt.show()
|
|
222
|
+
|
|
223
|
+
return fig
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def plot_spectral_atmosphere_absorption(
|
|
227
|
+
get_extinction: Callable[[float, float], float],
|
|
228
|
+
get_optical_depth: Callable[[float, float, float], float],
|
|
229
|
+
earth_radius: float,
|
|
230
|
+
ray_wavelengths: np.ndarray,
|
|
231
|
+
ray_positions: np.ndarray,
|
|
232
|
+
ray_optical_depths: np.ndarray,
|
|
233
|
+
wavelength_range: tuple[float, float] = (300e-9, 1500e-9),
|
|
234
|
+
altitude_range: tuple[float, float] = (0, 50_000),
|
|
235
|
+
test_altitudes: list[float] | None = None,
|
|
236
|
+
test_wavelengths: list[float] | None = None,
|
|
237
|
+
save_path: str | Path | None = None,
|
|
238
|
+
show: bool = True,
|
|
239
|
+
) -> plt.Figure:
|
|
240
|
+
"""
|
|
241
|
+
Plot spectral absorption characteristics.
|
|
242
|
+
|
|
243
|
+
Parameters
|
|
244
|
+
----------
|
|
245
|
+
get_extinction : callable
|
|
246
|
+
Function(altitude, wavelength) -> extinction coefficient
|
|
247
|
+
get_optical_depth : callable
|
|
248
|
+
Function(z1, z2, wavelength) -> optical depth
|
|
249
|
+
earth_radius : float
|
|
250
|
+
Earth radius in m
|
|
251
|
+
ray_wavelengths : ndarray
|
|
252
|
+
Array of ray wavelengths in m
|
|
253
|
+
ray_positions : ndarray
|
|
254
|
+
Array of ray positions (N, 3)
|
|
255
|
+
ray_optical_depths : ndarray
|
|
256
|
+
Array of simulated optical depths
|
|
257
|
+
wavelength_range : tuple
|
|
258
|
+
(min, max) wavelength in m for plots
|
|
259
|
+
altitude_range : tuple
|
|
260
|
+
(min, max) altitude in m for plots
|
|
261
|
+
test_altitudes : list, optional
|
|
262
|
+
Altitudes for extinction vs wavelength plot
|
|
263
|
+
test_wavelengths : list, optional
|
|
264
|
+
Wavelengths for optical depth vs altitude plot
|
|
265
|
+
save_path : str or Path, optional
|
|
266
|
+
Path to save figure
|
|
267
|
+
show : bool
|
|
268
|
+
Whether to display the figure
|
|
269
|
+
|
|
270
|
+
Returns
|
|
271
|
+
-------
|
|
272
|
+
plt.Figure
|
|
273
|
+
The matplotlib figure
|
|
274
|
+
"""
|
|
275
|
+
if test_altitudes is None:
|
|
276
|
+
test_altitudes = [5000, 10000, 20000, 30000]
|
|
277
|
+
if test_wavelengths is None:
|
|
278
|
+
test_wavelengths = [400e-9, 550e-9, 700e-9, 1000e-9]
|
|
279
|
+
|
|
280
|
+
wavelengths_plot = np.linspace(wavelength_range[0], wavelength_range[1], 100)
|
|
281
|
+
altitudes_range = np.linspace(altitude_range[0], altitude_range[1], 100)
|
|
282
|
+
|
|
283
|
+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 5))
|
|
284
|
+
|
|
285
|
+
# Extinction coefficient vs wavelength at different altitudes
|
|
286
|
+
for h in test_altitudes:
|
|
287
|
+
alpha_vals = [get_extinction(h, wl) for wl in wavelengths_plot]
|
|
288
|
+
ax1.semilogy(wavelengths_plot * 1e9, alpha_vals, label=f"{h/1000:.0f} km")
|
|
289
|
+
|
|
290
|
+
ax1.set_xlabel("Wavelength (nm)")
|
|
291
|
+
ax1.set_ylabel("Extinction coefficient (m⁻¹)")
|
|
292
|
+
ax1.set_title("Extinction vs Wavelength")
|
|
293
|
+
ax1.legend()
|
|
294
|
+
ax1.grid(True, alpha=0.3)
|
|
295
|
+
|
|
296
|
+
# Optical depth vs altitude for different wavelengths
|
|
297
|
+
for wl in test_wavelengths:
|
|
298
|
+
tau_vals = [get_optical_depth(0, h, wl) for h in altitudes_range]
|
|
299
|
+
ax2.plot(tau_vals, altitudes_range / 1000, label=f"{wl*1e9:.0f} nm")
|
|
300
|
+
|
|
301
|
+
# Add simulated points
|
|
302
|
+
for i in range(len(ray_wavelengths)):
|
|
303
|
+
final_h = (
|
|
304
|
+
np.sqrt(
|
|
305
|
+
ray_positions[i, 0] ** 2
|
|
306
|
+
+ ray_positions[i, 1] ** 2
|
|
307
|
+
+ ray_positions[i, 2] ** 2
|
|
308
|
+
)
|
|
309
|
+
- earth_radius
|
|
310
|
+
)
|
|
311
|
+
ax2.scatter(ray_optical_depths[i], final_h / 1000, c="r", s=50, zorder=5)
|
|
312
|
+
|
|
313
|
+
ax2.set_xlabel("Optical Depth τ")
|
|
314
|
+
ax2.set_ylabel("Altitude (km)")
|
|
315
|
+
ax2.set_title("Optical Depth vs Altitude")
|
|
316
|
+
ax2.legend()
|
|
317
|
+
ax2.grid(True, alpha=0.3)
|
|
318
|
+
|
|
319
|
+
plt.tight_layout()
|
|
320
|
+
|
|
321
|
+
if save_path:
|
|
322
|
+
fig.savefig(save_path, dpi=150)
|
|
323
|
+
|
|
324
|
+
if show:
|
|
325
|
+
plt.show()
|
|
326
|
+
|
|
327
|
+
return fig
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
__all__ = [
|
|
331
|
+
"plot_homogeneous_absorption",
|
|
332
|
+
"plot_exponential_atmosphere_absorption",
|
|
333
|
+
"plot_spectral_atmosphere_absorption",
|
|
334
|
+
]
|