gammasimtools 0.16.0__py3-none-any.whl → 0.17.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 (63) hide show
  1. {gammasimtools-0.16.0.dist-info → gammasimtools-0.17.0.dist-info}/METADATA +4 -2
  2. {gammasimtools-0.16.0.dist-info → gammasimtools-0.17.0.dist-info}/RECORD +60 -54
  3. {gammasimtools-0.16.0.dist-info → gammasimtools-0.17.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.16.0.dist-info → gammasimtools-0.17.0.dist-info}/entry_points.txt +3 -1
  5. simtools/_version.py +2 -2
  6. simtools/applications/derive_ctao_array_layouts.py +5 -5
  7. simtools/applications/generate_simtel_event_data.py +36 -46
  8. simtools/applications/merge_tables.py +104 -0
  9. simtools/applications/plot_array_layout.py +145 -258
  10. simtools/applications/production_derive_corsika_limits.py +35 -220
  11. simtools/applications/production_derive_statistics.py +77 -43
  12. simtools/applications/simulate_light_emission.py +1 -0
  13. simtools/applications/simulate_prod.py +30 -18
  14. simtools/applications/simulate_prod_htcondor_generator.py +0 -1
  15. simtools/applications/submit_array_layouts.py +93 -0
  16. simtools/applications/verify_simulation_model_production_tables.py +52 -0
  17. simtools/camera/camera_efficiency.py +3 -3
  18. simtools/configuration/commandline_parser.py +28 -34
  19. simtools/configuration/configurator.py +0 -4
  20. simtools/corsika/corsika_config.py +17 -12
  21. simtools/corsika/primary_particle.py +46 -13
  22. simtools/data_model/metadata_collector.py +7 -3
  23. simtools/db/db_handler.py +11 -11
  24. simtools/db/db_model_upload.py +2 -2
  25. simtools/io_operations/io_handler.py +2 -2
  26. simtools/io_operations/io_table_handler.py +345 -0
  27. simtools/job_execution/htcondor_script_generator.py +2 -2
  28. simtools/job_execution/job_manager.py +7 -121
  29. simtools/layout/array_layout_utils.py +385 -0
  30. simtools/model/array_model.py +5 -0
  31. simtools/model/model_repository.py +134 -0
  32. simtools/production_configuration/{calculate_statistical_errors_grid_point.py → calculate_statistical_uncertainties_grid_point.py} +101 -112
  33. simtools/production_configuration/derive_corsika_limits.py +239 -111
  34. simtools/production_configuration/derive_corsika_limits_grid.py +189 -0
  35. simtools/production_configuration/derive_production_statistics.py +57 -26
  36. simtools/production_configuration/derive_production_statistics_handler.py +70 -37
  37. simtools/production_configuration/interpolation_handler.py +296 -94
  38. simtools/ray_tracing/ray_tracing.py +7 -6
  39. simtools/reporting/docs_read_parameters.py +104 -62
  40. simtools/runners/corsika_simtel_runner.py +4 -1
  41. simtools/runners/runner_services.py +5 -4
  42. simtools/schemas/model_parameters/dsum_threshold.schema.yml +41 -0
  43. simtools/schemas/production_configuration_metrics.schema.yml +2 -2
  44. simtools/simtel/simtel_config_writer.py +34 -14
  45. simtools/simtel/simtel_io_event_reader.py +301 -194
  46. simtools/simtel/simtel_io_event_writer.py +207 -227
  47. simtools/simtel/simtel_io_file_info.py +9 -4
  48. simtools/simtel/simtel_io_metadata.py +20 -5
  49. simtools/simtel/simulator_array.py +2 -2
  50. simtools/simtel/simulator_light_emission.py +79 -34
  51. simtools/simtel/simulator_ray_tracing.py +2 -2
  52. simtools/simulator.py +101 -68
  53. simtools/testing/validate_output.py +4 -1
  54. simtools/utils/general.py +1 -1
  55. simtools/utils/names.py +5 -5
  56. simtools/visualization/plot_array_layout.py +242 -0
  57. simtools/visualization/plot_pixels.py +681 -0
  58. simtools/visualization/visualize.py +3 -219
  59. simtools/applications/production_generate_simulation_config.py +0 -152
  60. simtools/layout/ctao_array_layouts.py +0 -172
  61. simtools/production_configuration/generate_simulation_config.py +0 -158
  62. {gammasimtools-0.16.0.dist-info → gammasimtools-0.17.0.dist-info}/licenses/LICENSE +0 -0
  63. {gammasimtools-0.16.0.dist-info → gammasimtools-0.17.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,242 @@
1
+ #!/usr/bin/python3
2
+ """Plot array elements for a layout."""
3
+
4
+ from collections import Counter
5
+
6
+ import astropy.units as u
7
+ import matplotlib.patches as mpatches
8
+ import matplotlib.pyplot as plt
9
+ from astropy.table import Column
10
+ from matplotlib.collections import PatchCollection
11
+
12
+ from simtools.utils import geometry as transf
13
+ from simtools.utils import names
14
+ from simtools.visualization import legend_handlers as leg_h
15
+
16
+ __all__ = ["get_telescope_patch", "plot_array_layout"]
17
+
18
+
19
+ def plot_array_layout(
20
+ telescopes,
21
+ show_tel_label=False,
22
+ axes_range=None,
23
+ marker_scaling=1.0,
24
+ background_telescopes=None,
25
+ ):
26
+ """
27
+ Plot telescope array layout.
28
+
29
+ Parameters
30
+ ----------
31
+ telescopes : Table
32
+ Telescope data table.
33
+ show_tel_label : bool
34
+ Show telescope labels (default False).
35
+ axes_range : float or None
36
+ Axis range, auto if None.
37
+ marker_scaling : float
38
+ Marker size scale factor.
39
+ background_telescopes : Table or None
40
+ Optional background telescope table.
41
+
42
+ Returns
43
+ -------
44
+ fig : Figure
45
+ Matplotlib figure object.
46
+ """
47
+ fig, ax = plt.subplots(1)
48
+
49
+ patches, plot_range = get_patches(ax, telescopes, show_tel_label, axes_range, marker_scaling)
50
+
51
+ if background_telescopes is not None:
52
+ bg_patches, bg_range = get_patches(
53
+ ax, background_telescopes, False, axes_range, marker_scaling
54
+ )
55
+ ax.add_collection(PatchCollection(bg_patches, match_original=True, alpha=0.1))
56
+ if axes_range is None:
57
+ plot_range = max(plot_range, bg_range)
58
+
59
+ update_legend(ax, telescopes)
60
+ finalize_plot(ax, patches, "Easting [m]", "Northing [m]", plot_range)
61
+
62
+ return fig
63
+
64
+
65
+ def get_patches(ax, telescopes, show_tel_label, axes_range, marker_scaling):
66
+ """
67
+ Get plot patches and axis range.
68
+
69
+ Returns
70
+ -------
71
+ patches : list
72
+ List of telescope patches.
73
+ axes_range : float
74
+ Calculated or input axis range.
75
+ """
76
+ pos_x, pos_y = get_positions(telescopes)
77
+ telescopes["pos_x_rotated"] = Column(pos_x)
78
+ telescopes["pos_y_rotated"] = Column(pos_y)
79
+
80
+ patches, radii = create_patches(telescopes, marker_scaling, show_tel_label, ax)
81
+
82
+ if axes_range:
83
+ return patches, axes_range
84
+
85
+ r = max(radii).value
86
+ max_x = max(abs(pos_x.min().value), abs(pos_x.max().value)) + r
87
+ max_y = max(abs(pos_y.min().value), abs(pos_y.max().value)) + r
88
+ updated_axes_range = max(max_x, max_y) * 1.1
89
+
90
+ return patches, updated_axes_range
91
+
92
+
93
+ @u.quantity_input(x=u.m, y=u.m, radius=u.m)
94
+ def get_telescope_patch(name, x, y, radius):
95
+ """
96
+ Create patch for a telescope.
97
+
98
+ Returns
99
+ -------
100
+ patch : Patch
101
+ Circle or rectangle patch.
102
+ """
103
+ tel_obj = leg_h.TelescopeHandler()
104
+ tel_type = names.get_array_element_type_from_name(name)
105
+ x, y, r = x.to(u.m), y.to(u.m), radius.to(u.m)
106
+
107
+ if tel_type == "SCTS":
108
+ return mpatches.Rectangle(
109
+ ((x - r / 2).value, (y - r / 2).value),
110
+ width=r.value,
111
+ height=r.value,
112
+ fill=False,
113
+ color=tel_obj.colors_dict[tel_type],
114
+ )
115
+
116
+ return mpatches.Circle(
117
+ (x.value, y.value),
118
+ radius=r.value,
119
+ fill=tel_type.startswith("MST"),
120
+ color=tel_obj.colors_dict[tel_type],
121
+ )
122
+
123
+
124
+ def get_positions(telescopes):
125
+ """
126
+ Get X/Y positions depending on coordinate system.
127
+
128
+ For ground coordinates, rotates the positions by 90 degrees.
129
+
130
+ Returns
131
+ -------
132
+ x_rot, y_rot : Quantity
133
+ Position coordinates.
134
+ """
135
+ if "position_x" in telescopes.colnames:
136
+ x, y = telescopes["position_x"], telescopes["position_y"]
137
+ locale_rotate_angle = 90 * u.deg
138
+ elif "utm_east" in telescopes.colnames:
139
+ x, y = telescopes["utm_east"], telescopes["utm_north"]
140
+ locale_rotate_angle = 0 * u.deg
141
+ else:
142
+ raise ValueError("Missing required position columns.")
143
+
144
+ return transf.rotate(x, y, locale_rotate_angle) if locale_rotate_angle != 0 else (x, y)
145
+
146
+
147
+ def create_patches(telescopes, scale, show_label, ax):
148
+ """
149
+ Create telescope patches and labels.
150
+
151
+ Returns
152
+ -------
153
+ patches : list
154
+ Shape patches.
155
+ radii : list
156
+ Telescope radii.
157
+ """
158
+ patches, radii = [], []
159
+ fontsize, scale_factor = (4, 2) if len(telescopes) > 30 else (8, 1)
160
+
161
+ for tel in telescopes:
162
+ name = get_telescope_name(tel)
163
+ radius = get_sphere_radius(tel)
164
+ radii.append(radius)
165
+ tel_type = names.get_array_element_type_from_name(name)
166
+
167
+ patches.append(
168
+ get_telescope_patch(
169
+ tel_type,
170
+ tel["pos_x_rotated"],
171
+ tel["pos_y_rotated"],
172
+ scale_factor * radius * scale,
173
+ )
174
+ )
175
+
176
+ if show_label:
177
+ ax.text(
178
+ tel["pos_x_rotated"].value,
179
+ tel["pos_y_rotated"].value + scale_factor * radius.value,
180
+ name,
181
+ ha="center",
182
+ va="bottom",
183
+ fontsize=fontsize,
184
+ )
185
+
186
+ return patches, radii
187
+
188
+
189
+ def get_telescope_name(tel):
190
+ """
191
+ Get telescope name.
192
+
193
+ Returns
194
+ -------
195
+ name : str
196
+ Telescope name or fallback identifier.
197
+ """
198
+ if "telescope_name" in tel.colnames:
199
+ return tel["telescope_name"]
200
+ if "asset_code" in tel.colnames and "sequence_number" in tel.colnames:
201
+ return f"{tel['asset_code']}-{tel['sequence_number']}"
202
+ return f"tel_{tel.index}"
203
+
204
+
205
+ def get_sphere_radius(tel):
206
+ """
207
+ Get telescope sphere radius.
208
+
209
+ Returns
210
+ -------
211
+ radius : Quantity
212
+ Radius with units.
213
+ """
214
+ return tel["sphere_radius"] if "sphere_radius" in tel.colnames else 10.0 * u.m
215
+
216
+
217
+ def update_legend(ax, telescopes):
218
+ """Add legend for telescope types and counts."""
219
+ names_list = [get_telescope_name(tel) for tel in telescopes]
220
+ types = [names.get_array_element_type_from_name(n) for n in names_list]
221
+ counts = Counter(types)
222
+
223
+ objs, labels = [], []
224
+ for t in names.get_list_of_array_element_types():
225
+ if counts[t]:
226
+ objs.append(leg_h.all_telescope_objects[t]())
227
+ labels.append(f"{t} ({counts[t]})")
228
+
229
+ handler_map = {k: v() for k, v in leg_h.legend_handler_map.items()}
230
+ ax.legend(objs, labels, handler_map=handler_map, prop={"size": 11}, loc="best")
231
+
232
+
233
+ def finalize_plot(ax, patches, x_title, y_title, axes_range):
234
+ """Finalize plot appearance and limits."""
235
+ ax.add_collection(PatchCollection(patches, match_original=True))
236
+ ax.set(xlabel=x_title, ylabel=y_title)
237
+ ax.tick_params(labelsize=8)
238
+ ax.axis("square")
239
+ if axes_range:
240
+ ax.set_xlim(-axes_range, axes_range)
241
+ ax.set_ylim(-axes_range, axes_range)
242
+ plt.tight_layout()