gammasimtools 0.13.0__py3-none-any.whl → 0.15.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 (226) hide show
  1. {gammasimtools-0.13.0.dist-info → gammasimtools-0.15.0.dist-info}/METADATA +3 -3
  2. {gammasimtools-0.13.0.dist-info → gammasimtools-0.15.0.dist-info}/RECORD +224 -217
  3. {gammasimtools-0.13.0.dist-info → gammasimtools-0.15.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.13.0.dist-info → gammasimtools-0.15.0.dist-info}/entry_points.txt +3 -1
  5. simtools/_version.py +2 -2
  6. simtools/applications/db_add_file_to_db.py +15 -0
  7. simtools/applications/db_add_value_from_json_to_db.py +18 -1
  8. simtools/applications/derive_ctao_array_layouts.py +120 -0
  9. simtools/applications/derive_photon_electron_spectrum.py +29 -1
  10. simtools/applications/docs_produce_array_element_report.py +41 -16
  11. simtools/applications/docs_produce_model_parameter_reports.py +28 -8
  12. simtools/applications/{production_extract_mc_event_data.py → generate_simtel_event_data.py} +12 -20
  13. simtools/applications/print_version.py +81 -0
  14. simtools/applications/production_derive_corsika_limits.py +172 -39
  15. simtools/applications/production_scale_events.py +59 -36
  16. simtools/applications/run_application.py +41 -11
  17. simtools/applications/simulate_light_emission.py +115 -247
  18. simtools/applications/simulate_prod_htcondor_generator.py +2 -2
  19. simtools/camera/single_photon_electron_spectrum.py +163 -15
  20. simtools/data_model/model_data_writer.py +7 -6
  21. simtools/db/db_handler.py +14 -8
  22. simtools/dependencies.py +38 -3
  23. simtools/layout/array_layout.py +1 -0
  24. simtools/layout/ctao_array_layouts.py +172 -0
  25. simtools/model/array_model.py +3 -4
  26. simtools/model/model_parameter.py +30 -87
  27. simtools/production_configuration/derive_corsika_limits.py +222 -154
  28. simtools/production_configuration/event_scaler.py +2 -2
  29. simtools/reporting/docs_auto_report_generator.py +217 -0
  30. simtools/reporting/docs_read_parameters.py +192 -110
  31. simtools/schemas/application_workflow.metaschema.yml +3 -0
  32. simtools/schemas/model_parameter.metaschema.yml +13 -0
  33. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +6 -0
  34. simtools/schemas/model_parameters/adjust_gain.schema.yml +1 -1
  35. simtools/schemas/model_parameters/altitude.schema.yml +1 -1
  36. simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +3 -3
  37. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +3 -3
  38. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +3 -3
  39. simtools/schemas/model_parameters/array_window.schema.yml +1 -1
  40. simtools/schemas/model_parameters/asum_clipping.schema.yml +1 -1
  41. simtools/schemas/model_parameters/asum_offset.schema.yml +1 -1
  42. simtools/schemas/model_parameters/asum_threshold.schema.yml +1 -1
  43. simtools/schemas/model_parameters/axes_offsets.schema.yml +2 -2
  44. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +1 -1
  45. simtools/schemas/model_parameters/camera_body_shape.schema.yml +1 -1
  46. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +1 -1
  47. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +1 -1
  48. simtools/schemas/model_parameters/camera_depth.schema.yml +1 -1
  49. simtools/schemas/model_parameters/camera_pixels.schema.yml +1 -1
  50. simtools/schemas/model_parameters/camera_transmission.schema.yml +1 -1
  51. simtools/schemas/model_parameters/channels_per_chip.schema.yml +1 -1
  52. simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +1 -1
  53. simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +1 -1
  54. simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +1 -1
  55. simtools/schemas/model_parameters/corsika_observation_level.schema.yml +1 -1
  56. simtools/schemas/model_parameters/dark_events.schema.yml +1 -1
  57. simtools/schemas/model_parameters/disc_bins.schema.yml +1 -1
  58. simtools/schemas/model_parameters/disc_start.schema.yml +1 -1
  59. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +1 -1
  60. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +1 -1
  61. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +1 -1
  62. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +1 -1
  63. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +1 -1
  64. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +1 -1
  65. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +1 -1
  66. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +1 -1
  67. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +1 -1
  68. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +1 -1
  69. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +1 -1
  70. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +1 -1
  71. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +1 -1
  72. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +1 -1
  73. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +1 -1
  74. simtools/schemas/model_parameters/dish_shape_length.schema.yml +1 -1
  75. simtools/schemas/model_parameters/dsum_clipping.schema.yml +1 -1
  76. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +1 -1
  77. simtools/schemas/model_parameters/dsum_offset.schema.yml +1 -1
  78. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +1 -1
  79. simtools/schemas/model_parameters/dsum_prescale.schema.yml +2 -2
  80. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +1 -1
  81. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +1 -1
  82. simtools/schemas/model_parameters/dsum_threshold.schema.yml +1 -1
  83. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +1 -1
  84. simtools/schemas/model_parameters/effective_focal_length.schema.yml +5 -5
  85. simtools/schemas/model_parameters/epsg_code.schema.yml +1 -1
  86. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +1 -1
  87. simtools/schemas/model_parameters/fadc_bins.schema.yml +1 -1
  88. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +1 -1
  89. simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +1 -1
  90. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +1 -1
  91. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +1 -1
  92. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +1 -1
  93. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +1 -1
  94. simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +1 -1
  95. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +1 -1
  96. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +1 -1
  97. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +1 -1
  98. simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +1 -1
  99. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +1 -1
  100. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +1 -1
  101. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +1 -1
  102. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +1 -1
  103. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +1 -1
  104. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +1 -1
  105. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +1 -1
  106. simtools/schemas/model_parameters/fadc_max_sum.schema.yml +1 -1
  107. simtools/schemas/model_parameters/fadc_mhz.schema.yml +1 -1
  108. simtools/schemas/model_parameters/fadc_noise.schema.yml +1 -1
  109. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +1 -1
  110. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +1 -1
  111. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +1 -1
  112. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +1 -1
  113. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +1 -1
  114. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +1 -1
  115. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +1 -1
  116. simtools/schemas/model_parameters/focal_length.schema.yml +1 -1
  117. simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +20 -20
  118. simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +1 -1
  119. simtools/schemas/model_parameters/focus_offset.schema.yml +4 -4
  120. simtools/schemas/model_parameters/gain_variation.schema.yml +1 -1
  121. simtools/schemas/model_parameters/geomag_horizontal.schema.yml +1 -1
  122. simtools/schemas/model_parameters/geomag_rotation.schema.yml +1 -1
  123. simtools/schemas/model_parameters/geomag_vertical.schema.yml +1 -1
  124. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +1 -1
  125. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +1 -1
  126. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +1 -1
  127. simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
  128. simtools/schemas/model_parameters/laser_external_trigger.schema.yml +1 -1
  129. simtools/schemas/model_parameters/laser_photons.schema.yml +1 -1
  130. simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +1 -1
  131. simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +1 -1
  132. simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +1 -1
  133. simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +1 -1
  134. simtools/schemas/model_parameters/laser_var_photons.schema.yml +1 -1
  135. simtools/schemas/model_parameters/laser_wavelength.schema.yml +1 -1
  136. simtools/schemas/model_parameters/led_events.schema.yml +1 -1
  137. simtools/schemas/model_parameters/led_photons.schema.yml +1 -1
  138. simtools/schemas/model_parameters/led_pulse_offset.schema.yml +1 -1
  139. simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +1 -1
  140. simtools/schemas/model_parameters/led_var_photons.schema.yml +1 -1
  141. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +1 -1
  142. simtools/schemas/model_parameters/min_photons.schema.yml +1 -1
  143. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +1 -1
  144. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +4 -4
  145. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +4 -4
  146. simtools/schemas/model_parameters/mirror_class.schema.yml +1 -1
  147. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +1 -1
  148. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +1 -1
  149. simtools/schemas/model_parameters/mirror_offset.schema.yml +1 -1
  150. simtools/schemas/model_parameters/mirror_panel_2f_measurements.schema.yml +3 -3
  151. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +3 -3
  152. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +1 -1
  153. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +45 -0
  154. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +2 -2
  155. simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +1 -1
  156. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +5 -5
  157. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +1 -1
  158. simtools/schemas/model_parameters/nsb_reference_value.schema.yml +1 -1
  159. simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +1 -1
  160. simtools/schemas/model_parameters/nsb_spectrum.schema.yml +2 -2
  161. simtools/schemas/model_parameters/num_gains.schema.yml +1 -1
  162. simtools/schemas/model_parameters/pedestal_events.schema.yml +1 -1
  163. simtools/schemas/model_parameters/photon_delay.schema.yml +1 -1
  164. simtools/schemas/model_parameters/photons_per_run.schema.yml +1 -1
  165. simtools/schemas/model_parameters/pixel_cells.schema.yml +1 -1
  166. simtools/schemas/model_parameters/pixels_parallel.schema.yml +1 -1
  167. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +1 -1
  168. simtools/schemas/model_parameters/pm_average_gain.schema.yml +1 -1
  169. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +1 -1
  170. simtools/schemas/model_parameters/pm_gain_index.schema.yml +1 -1
  171. simtools/schemas/model_parameters/pm_transit_time.schema.yml +4 -4
  172. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +1 -1
  173. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +7 -3
  174. simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +1 -1
  175. simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +1 -1
  176. simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +20 -20
  177. simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +1 -1
  178. simtools/schemas/model_parameters/qe_variation.schema.yml +1 -1
  179. simtools/schemas/model_parameters/random_focal_length.schema.yml +2 -2
  180. simtools/schemas/model_parameters/random_mono_probability.schema.yml +38 -0
  181. simtools/schemas/model_parameters/reference_point_altitude.schema.yml +1 -1
  182. simtools/schemas/model_parameters/reference_point_latitude.schema.yml +4 -1
  183. simtools/schemas/model_parameters/reference_point_longitude.schema.yml +4 -1
  184. simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +1 -1
  185. simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +1 -1
  186. simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +5 -5
  187. simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +1 -1
  188. simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +1 -1
  189. simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +1 -1
  190. simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +20 -20
  191. simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +1 -1
  192. simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +1 -1
  193. simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +1 -1
  194. simtools/schemas/model_parameters/stars.schema.yml +36 -0
  195. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +1 -1
  196. simtools/schemas/model_parameters/tailcut_scale.schema.yml +1 -1
  197. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +1 -1
  198. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +1 -1
  199. simtools/schemas/model_parameters/telescope_random_error.schema.yml +1 -1
  200. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +1 -1
  201. simtools/schemas/model_parameters/telescope_transmission.schema.yml +6 -6
  202. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +1 -1
  203. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +1 -1
  204. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +1 -1
  205. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +1 -1
  206. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +1 -1
  207. simtools/schemas/model_parameters/transit_time_error.schema.yml +1 -1
  208. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +1 -1
  209. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +1 -1
  210. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +4 -4
  211. simtools/schemas/model_parameters/trigger_pixels.schema.yml +1 -1
  212. simtools/simtel/simtel_config_writer.py +70 -38
  213. simtools/simtel/simtel_io_event_reader.py +278 -0
  214. simtools/simtel/simtel_io_event_writer.py +317 -0
  215. simtools/simtel/simulator_light_emission.py +181 -67
  216. simtools/simulator.py +2 -2
  217. simtools/testing/configuration.py +18 -0
  218. simtools/utils/general.py +33 -7
  219. simtools/utils/geometry.py +19 -0
  220. simtools/utils/names.py +7 -1
  221. simtools/visualization/visualize.py +2 -2
  222. simtools/production_configuration/extract_mc_event_data.py +0 -253
  223. simtools/simtel/simtel_io_events.py +0 -265
  224. {gammasimtools-0.13.0.dist-info → gammasimtools-0.15.0.dist-info/licenses}/LICENSE +0 -0
  225. {gammasimtools-0.13.0.dist-info → gammasimtools-0.15.0.dist-info}/top_level.txt +0 -0
  226. /simtools/schemas/model_parameters/{nsb_skymap.schema.yml → nsb_sky_map.schema.yml} +0 -0
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/python3
2
+
3
+ r"""Class to automate generation of all reports according to command line inputs."""
4
+
5
+ import logging
6
+ from collections.abc import Generator
7
+ from pathlib import Path
8
+
9
+ from simtools.db import db_handler
10
+ from simtools.reporting.docs_read_parameters import ReadParameters
11
+ from simtools.utils import names
12
+
13
+ logger = logging.getLogger()
14
+
15
+
16
+ class ReportGenerator:
17
+ """Automate report generation."""
18
+
19
+ def __init__(self, db_config, args, output_path):
20
+ """Initialise class."""
21
+ self._logger = logging.getLogger(__name__)
22
+ self.db = db_handler.DatabaseHandler(mongo_db_config=db_config)
23
+ self.db_config = db_config
24
+ self.args = args
25
+ self.output_path = output_path
26
+
27
+ def _add_design_models_to_telescopes(self, model_version, telescopes):
28
+ """Add design models to the list of telescopes for a given model version."""
29
+ updated_telescopes = telescopes.copy() # Copy original list to avoid modifying it directly
30
+ for telescope in telescopes:
31
+ design_model = self.db.get_design_model(model_version, telescope) # Get design model
32
+ if design_model and design_model not in updated_telescopes:
33
+ updated_telescopes.append(design_model) # Add design model if not already present
34
+ return updated_telescopes
35
+
36
+ def _filter_telescopes_by_site(self, telescopes, selected_sites):
37
+ """Filter telescopes by selected sites."""
38
+ filtered_telescopes = []
39
+ for telescope in telescopes:
40
+ sites = names.get_site_from_array_element_name(telescope)
41
+ sites = sites if isinstance(sites, list) else [sites]
42
+ if any(site in selected_sites for site in sites):
43
+ filtered_telescopes.append(telescope)
44
+ return filtered_telescopes
45
+
46
+ def _generate_array_element_report_combinations(self):
47
+ """Generate combinations of model versions, telescopes, and sites for array element reports.
48
+
49
+ This generator function yields tuples of (model_version, telescope, site) for array element
50
+ report generation based on the input arguments and flags.
51
+
52
+ The function filters telescopes by site and ensures that design models are included
53
+ when necessary.
54
+
55
+ Yields
56
+ ------
57
+ tuple[str, str, str]: A tuple containing (model_version, telescope, site) for each
58
+ valid combination based on the input arguments.
59
+ """
60
+ all_sites = names.site_names()
61
+
62
+ model_versions = (
63
+ self.db.get_model_versions()
64
+ if self.args.get("all_model_versions")
65
+ else [self.args["model_version"]]
66
+ )
67
+ selected_sites = all_sites if self.args.get("all_sites") else {self.args["site"]}
68
+
69
+ def get_telescopes(model_version):
70
+ """Get list of telescopes depending on input arguments."""
71
+ if not self.args.get("all_telescopes"):
72
+ return [self.args["telescope"]]
73
+ telescopes = self.db.get_array_elements(model_version)
74
+ all_telescopes = self._add_design_models_to_telescopes(model_version, telescopes)
75
+ return self._filter_telescopes_by_site(all_telescopes, selected_sites)
76
+
77
+ def get_valid_sites(telescope):
78
+ sites = names.get_site_from_array_element_name(telescope)
79
+ sites = sites if isinstance(sites, list) else [sites]
80
+ return [site for site in sites if site in selected_sites]
81
+
82
+ def generate_combinations():
83
+ for version in model_versions:
84
+ telescopes = get_telescopes(version)
85
+ for telescope in telescopes:
86
+ for site in get_valid_sites(telescope):
87
+ yield version, telescope, site
88
+
89
+ return generate_combinations()
90
+
91
+ def _generate_single_array_element_report(self, model_version, telescope, site):
92
+ """Generate a single report with given parameters."""
93
+ self.args.update(
94
+ {
95
+ "telescope": telescope,
96
+ "site": site,
97
+ "model_version": model_version,
98
+ }
99
+ )
100
+
101
+ output_path = Path(self.output_path) / str(model_version)
102
+ ReadParameters(self.db_config, self.args, output_path).produce_array_element_report()
103
+
104
+ logger.info(
105
+ f"Markdown report generated for {site} "
106
+ f"Telescope {telescope} (v{model_version}): {output_path}"
107
+ )
108
+
109
+ def auto_generate_array_element_reports(self):
110
+ """Generate all reports based on which --all_* flag is passed."""
111
+ if self.args.get("observatory"):
112
+ self.auto_generate_observatory_reports()
113
+ return
114
+ for params in self._generate_array_element_report_combinations():
115
+ self._generate_single_array_element_report(*params)
116
+
117
+ def _get_telescopes_from_layout(self, site: str) -> set[str]:
118
+ """Get unique telescopes for a given site across all versions."""
119
+ if not self.args.get("all_telescopes"):
120
+ return {self.args["telescope"]}
121
+
122
+ # Get layouts for all versions for this site
123
+ layouts = self.db.get_model_parameters_for_all_model_versions(
124
+ site=site, array_element_name=f"OBS-{site}", collection="sites"
125
+ )
126
+
127
+ # Collect all telescopes from hyper array layouts
128
+ all_telescopes = set()
129
+ for key, version_data in layouts.items():
130
+ layout = version_data.get("array_layouts", {}).get("value", [])
131
+ hyper_array = next(
132
+ (layout["elements"] for layout in layout if layout["name"] == "hyper_array"), []
133
+ )
134
+ all_telescopes.update(hyper_array)
135
+ all_telescopes.update(self._add_design_models_to_telescopes(key, list(all_telescopes)))
136
+
137
+ return all_telescopes
138
+
139
+ def _get_valid_sites_for_telescope(self, telescope: str) -> list[str]:
140
+ """Get valid sites for a given telescope."""
141
+ sites = names.get_site_from_array_element_name(telescope)
142
+ return [sites] if not isinstance(sites, list) else sites
143
+
144
+ def _generate_parameter_report_combinations(self) -> Generator[tuple[str, str], None, None]:
145
+ """Generate combinations of telescopes and sites for report generation."""
146
+ if not self.args.get("all_telescopes"):
147
+ # For a specific telescope, get its valid sites regardless of --all_sites
148
+ telescope = self.args["telescope"]
149
+ for site in self._get_valid_sites_for_telescope(telescope):
150
+ yield telescope, site
151
+ else:
152
+ # For --all_telescopes, use selected sites to get telescopes
153
+ all_sites = names.site_names()
154
+ selected_sites = all_sites if self.args.get("all_sites") else {self.args["site"]}
155
+
156
+ for site in selected_sites:
157
+ telescopes = self._get_telescopes_from_layout(site)
158
+ yield from ((telescope, site) for telescope in telescopes)
159
+
160
+ def auto_generate_parameter_reports(self):
161
+ """Generate parameter reports for all telescope-site combinations."""
162
+ for telescope, site in self._generate_parameter_report_combinations():
163
+ self.args.update(
164
+ {
165
+ "telescope": telescope,
166
+ "site": site,
167
+ }
168
+ )
169
+
170
+ ReadParameters(
171
+ self.db_config, self.args, self.output_path
172
+ ).produce_model_parameter_reports()
173
+
174
+ logger.info(
175
+ f"Markdown report generated for {site} Telescope {telescope}: {self.output_path}"
176
+ )
177
+
178
+ def _generate_observatory_report_combinations(self) -> Generator[tuple[str, str], None, None]:
179
+ """Generate combinations of sites and model versions for observatory reports.
180
+
181
+ Yields
182
+ ------
183
+ tuple[str, str]: A tuple containing (site, model_version) for each valid combination
184
+ based on the input arguments.
185
+ """
186
+ all_sites = names.site_names()
187
+ selected_sites = all_sites if self.args.get("all_sites") else {self.args["site"]}
188
+
189
+ model_versions = (
190
+ self.db.get_model_versions()
191
+ if self.args.get("all_model_versions")
192
+ else [self.args["model_version"]]
193
+ )
194
+
195
+ for site in selected_sites:
196
+ for version in model_versions:
197
+ yield site, version
198
+
199
+ def _generate_single_observatory_report(self, site: str, model_version: str):
200
+ """Generate a single observatory report with given parameters."""
201
+ self.args.update(
202
+ {
203
+ "site": site,
204
+ "model_version": model_version,
205
+ "observatory": True,
206
+ }
207
+ )
208
+
209
+ output_path = Path(self.output_path) / str(model_version)
210
+ ReadParameters(self.db_config, self.args, output_path).produce_observatory_report()
211
+
212
+ logger.info(f"Observatory report generated for {site} (v{model_version}): {output_path}")
213
+
214
+ def auto_generate_observatory_reports(self):
215
+ """Generate all observatory reports based on which --all_* flags are passed."""
216
+ for params in self._generate_observatory_report_combinations():
217
+ self._generate_single_observatory_report(*params)
@@ -11,7 +11,6 @@ from pathlib import Path
11
11
  import numpy as np
12
12
 
13
13
  from simtools.db import db_handler
14
- from simtools.io_operations import io_handler
15
14
  from simtools.model.telescope_model import TelescopeModel
16
15
  from simtools.utils import names
17
16
 
@@ -30,20 +29,10 @@ class ReadParameters:
30
29
  self.site = args.get("site")
31
30
  self.model_version = args.get("model_version", None)
32
31
  self.output_path = output_path
32
+ self.observatory = args.get("observatory")
33
33
 
34
34
  def _convert_to_md(self, input_file):
35
- """
36
- Convert a '.dat' or '.ecsv' file to a Markdown file, preserving formatting.
37
-
38
- Parameters
39
- ----------
40
- input_file: Path, str
41
- Simulation data file (in '.dat' or '.ecsv' format).
42
-
43
- Returns
44
- -------
45
- - Path to the created Markdown file.
46
- """
35
+ """Convert a file to a Markdown file, preserving formatting."""
47
36
  input_file = Path(input_file)
48
37
  output_data_path = Path(self.output_path / "_data_files")
49
38
  output_data_path.mkdir(parents=True, exist_ok=True)
@@ -51,18 +40,20 @@ class ReadParameters:
51
40
  output_file = output_data_path / output_file_name
52
41
 
53
42
  try:
54
- with (
55
- input_file.open("r", encoding="utf-8") as infile,
56
- output_file.open("w", encoding="utf-8") as outfile,
57
- ):
58
- outfile.write(f"# {input_file.stem}")
59
- outfile.write("\n")
60
- outfile.write("```")
61
- outfile.write("\n")
62
- file_contents = infile.read()
43
+ # First try with utf-8
44
+ try:
45
+ with input_file.open("r", encoding="utf-8") as infile:
46
+ file_contents = infile.read()
47
+ except UnicodeDecodeError:
48
+ # If utf-8 fails, try with latin-1 (which can read any byte sequence)
49
+ with input_file.open("r", encoding="latin-1") as infile:
50
+ file_contents = infile.read()
51
+
52
+ with output_file.open("w", encoding="utf-8") as outfile:
53
+ outfile.write(f"# {input_file.stem}\n")
54
+ outfile.write("```\n")
63
55
  outfile.write(file_contents)
64
- outfile.write("\n")
65
- outfile.write("```")
56
+ outfile.write("\n```")
66
57
 
67
58
  except FileNotFoundError as exc:
68
59
  logger.exception(f"Data file not found: {input_file}.")
@@ -70,84 +61,6 @@ class ReadParameters:
70
61
 
71
62
  return f"_data_files/{output_file_name}"
72
63
 
73
- def get_all_parameter_descriptions(self):
74
- """
75
- Get descriptions for all model parameters.
76
-
77
- Returns
78
- -------
79
- tuple: A tuple containing two dictionaries:
80
- - parameter_description: Maps parameter names to their descriptions.
81
- - short_description: Maps parameter names to their short descriptions.
82
- - inst_class: Maps parameter names to their respective class.
83
- """
84
- parameter_description, short_description, inst_class = {}, {}, {}
85
-
86
- for instrument_class in names.db_collection_to_instrument_class_key("telescopes"):
87
- for parameter, details in names.model_parameters(instrument_class).items():
88
- parameter_description[parameter] = details.get("description")
89
- short_description[parameter] = details.get("short_description")
90
- inst_class[parameter] = instrument_class
91
-
92
- return parameter_description, short_description, inst_class
93
-
94
- def get_array_element_parameter_data(self, telescope_model, collection="telescopes"):
95
- """
96
- Get model parameter data and descriptions for a given array element.
97
-
98
- Currently only configured for telescope.
99
-
100
- Parameters
101
- ----------
102
- telescope_model : TelescopeModel
103
- The telescope model instance.
104
-
105
- Returns
106
- -------
107
- list: A list of lists containing parameter names, values with units,
108
- descriptions, and short descriptions.
109
- """
110
- all_params = telescope_model.db.get_model_parameters(
111
- site=telescope_model.site,
112
- array_element_name=telescope_model.name,
113
- collection=collection,
114
- model_version=telescope_model.model_version,
115
- )
116
-
117
- telescope_model.export_model_files()
118
- parameter_descriptions = self.get_all_parameter_descriptions()
119
- data = []
120
-
121
- for parameter in all_params:
122
- if all_params[parameter]["instrument"] != telescope_model.name:
123
- continue
124
- parameter_version = telescope_model.get_parameter_version(parameter)
125
- value = telescope_model.get_parameter_value_with_unit(parameter)
126
- if telescope_model.get_parameter_file_flag(parameter) and value:
127
- input_file_name = telescope_model.config_file_directory / Path(value)
128
- output_file_name = self._convert_to_md(input_file_name)
129
- value = f"[{Path(value).name}]({output_file_name})"
130
- elif isinstance(value, list):
131
- value = ", ".join(str(q) for q in value)
132
- else:
133
- value = str(value)
134
-
135
- description = parameter_descriptions[0].get(parameter)
136
- short_description = parameter_descriptions[1].get(parameter, description)
137
- inst_class = parameter_descriptions[2].get(parameter)
138
- data.append(
139
- [
140
- inst_class,
141
- parameter,
142
- parameter_version,
143
- value,
144
- description,
145
- short_description,
146
- ]
147
- )
148
-
149
- return data
150
-
151
64
  def _format_parameter_value(self, value_data, unit, file_flag):
152
65
  """Format parameter value based on type."""
153
66
  if file_flag:
@@ -216,14 +129,15 @@ class ReadParameters:
216
129
 
217
130
  # Iterate over each model version
218
131
  for version in all_versions:
219
- Path(f"{self.output_path}/model").mkdir(parents=True, exist_ok=True)
132
+ parameter_dict = all_param_data.get(version, {})
133
+ if not parameter_dict:
134
+ continue
220
135
 
136
+ Path(f"{self.output_path}/model").mkdir(parents=True, exist_ok=True)
221
137
  self.db.export_model_files(
222
138
  parameters=all_param_data.get(version), dest=f"{self.output_path}/model"
223
139
  )
224
140
 
225
- parameter_dict = all_param_data.get(version, {})
226
-
227
141
  for parameter_name in filter(parameter_dict.__contains__, all_parameter_names):
228
142
  parameter_data = parameter_dict.get(parameter_name)
229
143
 
@@ -234,7 +148,7 @@ class ReadParameters:
234
148
  unit = parameter_data.get("unit") or " "
235
149
  value_data = parameter_data.get("value")
236
150
 
237
- if not value_data:
151
+ if value_data is None:
238
152
  continue
239
153
 
240
154
  file_flag = parameter_data.get("file", False)
@@ -254,6 +168,85 @@ class ReadParameters:
254
168
 
255
169
  return self._group_model_versions_by_parameter_version(grouped_data)
256
170
 
171
+ def get_all_parameter_descriptions(self):
172
+ """
173
+ Get descriptions for all model parameters.
174
+
175
+ Returns
176
+ -------
177
+ tuple: A tuple containing two dictionaries:
178
+ - parameter_description: Maps parameter names to their descriptions.
179
+ - short_description: Maps parameter names to their short descriptions.
180
+ - inst_class: Maps parameter names to their respective class.
181
+ """
182
+ parameter_description, short_description, inst_class = {}, {}, {}
183
+
184
+ for instrument_class in names.db_collection_to_instrument_class_key("telescopes"):
185
+ for parameter, details in names.model_parameters(instrument_class).items():
186
+ parameter_description[parameter] = details.get("description")
187
+ short_description[parameter] = details.get("short_description")
188
+ inst_class[parameter] = instrument_class
189
+
190
+ return parameter_description, short_description, inst_class
191
+
192
+ def get_array_element_parameter_data(self, telescope_model, collection="telescopes"):
193
+ """
194
+ Get model parameter data and descriptions for a given array element.
195
+
196
+ Currently only configured for telescope.
197
+
198
+ Parameters
199
+ ----------
200
+ telescope_model : TelescopeModel
201
+ The telescope model instance.
202
+
203
+ Returns
204
+ -------
205
+ list: A list of lists containing parameter names, values with units,
206
+ descriptions, and short descriptions.
207
+ """
208
+ all_parameter_data = self.db.get_model_parameters(
209
+ site=telescope_model.site,
210
+ array_element_name=telescope_model.name,
211
+ collection=collection,
212
+ model_version=telescope_model.model_version,
213
+ )
214
+
215
+ Path(f"{self.output_path}/model").mkdir(parents=True, exist_ok=True)
216
+ self.db.export_model_files(parameters=all_parameter_data, dest=f"{self.output_path}/model")
217
+ parameter_descriptions = self.get_all_parameter_descriptions()
218
+ data = []
219
+
220
+ for parameter in filter(all_parameter_data.__contains__, names.model_parameters().keys()):
221
+ parameter_data = all_parameter_data.get(parameter)
222
+ if parameter_data["instrument"] != telescope_model.name:
223
+ continue
224
+ parameter_version = telescope_model.get_parameter_version(parameter)
225
+ unit = parameter_data.get("unit") or " "
226
+ value_data = parameter_data.get("value")
227
+
228
+ if value_data is None:
229
+ continue
230
+
231
+ file_flag = parameter_data.get("file", False)
232
+ value = self._format_parameter_value(value_data, unit, file_flag)
233
+
234
+ description = parameter_descriptions[0].get(parameter)
235
+ short_description = parameter_descriptions[1].get(parameter, description)
236
+ inst_class = parameter_descriptions[2].get(parameter)
237
+ data.append(
238
+ [
239
+ inst_class,
240
+ parameter,
241
+ parameter_version,
242
+ value,
243
+ description,
244
+ short_description,
245
+ ]
246
+ )
247
+
248
+ return data
249
+
257
250
  def produce_array_element_report(self):
258
251
  """
259
252
  Produce a markdown report of all model parameters per array element.
@@ -261,6 +254,10 @@ class ReadParameters:
261
254
  Outputs one markdown report of a given array element listing parameter values,
262
255
  versions, and descriptions.
263
256
  """
257
+ if self.observatory:
258
+ self.produce_observatory_report()
259
+ return
260
+
264
261
  telescope_model = TelescopeModel(
265
262
  site=self.site,
266
263
  telescope_name=self.array_element,
@@ -332,10 +329,8 @@ class ReadParameters:
332
329
  f"Comparing parameters across model versions for Telescope: {self.array_element}"
333
330
  f" and Site: {self.site}."
334
331
  )
335
- io_handler_instance = io_handler.IOHandler()
336
- output_path = io_handler_instance.get_output_directory(
337
- label="reports", sub_dir=f"parameters/{self.array_element}"
338
- )
332
+ output_path = self.output_path / f"{self.array_element}"
333
+ Path(output_path).mkdir(parents=True, exist_ok=True)
339
334
 
340
335
  all_parameter_names = names.model_parameters(None).keys()
341
336
  all_parameter_data = self.db.get_model_parameters_for_all_model_versions(
@@ -381,3 +376,90 @@ class ReadParameters:
381
376
  file.write("\n")
382
377
  if comparison_data.get(parameter)[0]["file_flag"]:
383
378
  file.write(f"![Parameter plot.](_images/{self.array_element}_{parameter}.png)")
379
+
380
+ def _write_array_layouts_section(self, file, layouts):
381
+ """Write the array layouts section of the report."""
382
+ file.write("\n## Array Layouts {#array-layouts-details}\n\n")
383
+ for layout in layouts:
384
+ layout_name = layout["name"]
385
+ elements = layout["elements"]
386
+ file.write(f"### {layout_name}\n\n")
387
+ file.write("| Element |\n|---------|\n")
388
+ for element in sorted(elements):
389
+ file.write(f"| [{element}]({element}.md) |\n")
390
+ file.write("\n")
391
+
392
+ def _write_array_triggers_section(self, file, trigger_configs):
393
+ """Write the array triggers section of the report."""
394
+ file.write("\n## Array Trigger Configurations {#array-triggers-details}\n\n")
395
+ file.write(
396
+ "| Trigger Name | Multiplicity | Width | Hard Stereo | Min Separation |\n"
397
+ "|--------------|--------------|--------|-------------|----------------|\n"
398
+ )
399
+ for config in trigger_configs:
400
+ name = config["name"]
401
+ mult = f"{config['multiplicity']['value']} {config['multiplicity']['unit'] or ''}"
402
+ width = f"{config['width']['value']} {config['width']['unit'] or ''}"
403
+ stereo = "Yes" if config["hard_stereo"]["value"] else "No"
404
+ min_sep = (
405
+ f"{config['min_separation']['value']} {config['min_separation']['unit'] or '-'}"
406
+ )
407
+ file.write(
408
+ f"| {name} | {mult.strip()} | {width.strip()} | {stereo} | {min_sep.strip()} |\n"
409
+ )
410
+ file.write("\n")
411
+
412
+ def _write_parameters_table(self, file, all_parameter_data):
413
+ """Write the main parameters table of the report."""
414
+ file.write("| Parameter | Value |\n|-----------|--------|\n")
415
+ for param_name, param_data in sorted(all_parameter_data.items()):
416
+ value = param_data.get("value")
417
+ unit = param_data.get("unit") or " "
418
+ file_flag = param_data.get("file", False)
419
+
420
+ if value is None:
421
+ continue
422
+
423
+ if param_name == "array_layouts":
424
+ file.write("| array_layouts | [View Array Layouts](#array-layouts-details) |\n")
425
+ elif param_name == "array_triggers":
426
+ file.write(
427
+ "| array_triggers | [View Trigger Configurations](#array-triggers-details) |\n"
428
+ )
429
+ else:
430
+ formatted_value = self._format_parameter_value(value, unit, file_flag)
431
+ file.write(f"| {param_name} | {formatted_value} |\n")
432
+ file.write("\n")
433
+
434
+ def produce_observatory_report(self):
435
+ """Produce a markdown report of all observatory parameters for a given site."""
436
+ output_filename = Path(self.output_path / f"OBS-{self.site}.md")
437
+ output_filename.parent.mkdir(parents=True, exist_ok=True)
438
+
439
+ all_parameter_data = self.db.get_model_parameters(
440
+ site=self.site,
441
+ array_element_name="OBS-" + self.site,
442
+ collection="sites",
443
+ model_version=self.model_version,
444
+ )
445
+
446
+ if not all_parameter_data:
447
+ logger.warning(f"No observatory parameters found for site {self.site}")
448
+ return
449
+
450
+ Path(f"{self.output_path}/model").mkdir(parents=True, exist_ok=True)
451
+ self.db.export_model_files(parameters=all_parameter_data, dest=f"{self.output_path}/model")
452
+
453
+ with output_filename.open("w", encoding="utf-8") as file:
454
+ file.write(f"# Observatory Parameters - {self.site} Site\n\n")
455
+ self._write_parameters_table(file, all_parameter_data)
456
+
457
+ if "array_layouts" in all_parameter_data:
458
+ self._write_array_layouts_section(
459
+ file, all_parameter_data["array_layouts"]["value"]
460
+ )
461
+
462
+ if "array_triggers" in all_parameter_data:
463
+ self._write_array_triggers_section(
464
+ file, all_parameter_data["array_triggers"]["value"]
465
+ )
@@ -48,6 +48,9 @@ definitions:
48
48
  TEST_NAME:
49
49
  type: string
50
50
  description: "Test name. Should be unique for this application."
51
+ SKIP_FOR_PRODUCTION_DB:
52
+ type: boolean
53
+ description: "Skip test if production DB is used."
51
54
  MODEL_VERSION_USE_CURRENT:
52
55
  type: boolean
53
56
  description: |
@@ -14,6 +14,7 @@ definitions:
14
14
  SimtoolsModelParameters:
15
15
  description: ""
16
16
  type: object
17
+ additionalProperties: false
17
18
  properties:
18
19
  _id:
19
20
  type: string
@@ -29,6 +30,12 @@ definitions:
29
30
  - string
30
31
  - "null"
31
32
  description: "Associated instrument."
33
+ meta_parameter:
34
+ type: boolean
35
+ description: "Meta parameter flag for sim_telarray."
36
+ parameter:
37
+ type: string
38
+ description: "Parameter name."
32
39
  site:
33
40
  anyOf:
34
41
  - type: string
@@ -62,6 +69,11 @@ definitions:
62
69
  anyOf:
63
70
  - type: string
64
71
  - type: "null"
72
+ - type: array
73
+ items:
74
+ type:
75
+ - string
76
+ - "null"
65
77
  description: "Unit of the parameter."
66
78
  value:
67
79
  anyOf:
@@ -87,6 +99,7 @@ definitions:
87
99
  required:
88
100
  - file
89
101
  - instrument
102
+ - parameter
90
103
  - parameter_version
91
104
  - schema_version
92
105
  - site
@@ -290,6 +290,10 @@ definitions:
290
290
  type: string
291
291
  description: |-
292
292
  "Naming of parameter as used this simulation software"
293
+ set_meta_parameter:
294
+ type: boolean
295
+ description: |-
296
+ "Add this parameter as sim_telarray meta parameter using option 'set'"
293
297
  required:
294
298
  - name
295
299
  title: SimulationSoftware
@@ -362,8 +366,10 @@ definitions:
362
366
  - double
363
367
  - float64
364
368
  - int
369
+ - int64
365
370
  - string
366
371
  - uint
372
+ - uint64
367
373
  - file
368
374
  title: TableColumnType
369
375
  Unit:
@@ -12,7 +12,7 @@ description: |-
12
12
  short_description: |-
13
13
  Gain adjustment for amplitudes and PMT gain.
14
14
  data:
15
- - type: double
15
+ - type: float64
16
16
  unit: dimensionless
17
17
  default: 1.0
18
18
  allowed_range:
@@ -12,7 +12,7 @@ description: |-
12
12
  (taking `telescope_axes_height` into account).
13
13
  short_description: Altitude above sea level of site centre.
14
14
  data:
15
- - type: double
15
+ - type: float64
16
16
  unit: m
17
17
  default: 1800.
18
18
  allowed_range: