gammasimtools 0.15.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 (248) hide show
  1. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/METADATA +5 -33
  2. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/RECORD +243 -229
  3. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/entry_points.txt +8 -3
  5. simtools/_version.py +2 -2
  6. simtools/applications/calculate_trigger_rate.py +10 -10
  7. simtools/applications/convert_all_model_parameters_from_simtel.py +16 -16
  8. simtools/applications/convert_model_parameter_from_simtel.py +1 -1
  9. simtools/applications/derive_ctao_array_layouts.py +5 -5
  10. simtools/applications/derive_psf_parameters.py +12 -9
  11. simtools/applications/docs_produce_array_element_report.py +3 -3
  12. simtools/applications/docs_produce_calibration_reports.py +49 -0
  13. simtools/applications/docs_produce_simulation_configuration_report.py +50 -0
  14. simtools/applications/{generate_simtel_array_histograms.py → generate_sim_telarray_histograms.py} +2 -2
  15. simtools/applications/generate_simtel_event_data.py +36 -46
  16. simtools/applications/merge_tables.py +104 -0
  17. simtools/applications/plot_array_layout.py +145 -258
  18. simtools/applications/production_derive_corsika_limits.py +35 -167
  19. simtools/applications/production_derive_statistics.py +159 -0
  20. simtools/applications/production_generate_grid.py +197 -0
  21. simtools/applications/simulate_light_emission.py +6 -13
  22. simtools/applications/simulate_prod.py +45 -21
  23. simtools/applications/simulate_prod_htcondor_generator.py +0 -1
  24. simtools/applications/submit_array_layouts.py +93 -0
  25. simtools/applications/validate_cumulative_psf.py +6 -4
  26. simtools/applications/validate_file_using_schema.py +7 -3
  27. simtools/applications/validate_optics.py +5 -4
  28. simtools/applications/verify_simulation_model_production_tables.py +52 -0
  29. simtools/camera/camera_efficiency.py +17 -42
  30. simtools/configuration/commandline_parser.py +32 -37
  31. simtools/configuration/configurator.py +10 -4
  32. simtools/corsika/corsika_config.py +120 -17
  33. simtools/corsika/primary_particle.py +46 -13
  34. simtools/data_model/format_checkers.py +9 -0
  35. simtools/data_model/metadata_collector.py +7 -3
  36. simtools/data_model/model_data_writer.py +3 -0
  37. simtools/data_model/schema.py +27 -16
  38. simtools/data_model/validate_data.py +27 -7
  39. simtools/db/db_handler.py +21 -15
  40. simtools/db/db_model_upload.py +2 -2
  41. simtools/io_operations/io_handler.py +2 -2
  42. simtools/io_operations/io_table_handler.py +345 -0
  43. simtools/job_execution/htcondor_script_generator.py +2 -2
  44. simtools/job_execution/job_manager.py +7 -121
  45. simtools/layout/array_layout.py +1 -0
  46. simtools/layout/array_layout_utils.py +385 -0
  47. simtools/model/array_model.py +68 -29
  48. simtools/model/model_parameter.py +76 -51
  49. simtools/model/model_repository.py +134 -0
  50. simtools/model/model_utils.py +43 -1
  51. simtools/model/site_model.py +3 -2
  52. simtools/model/telescope_model.py +4 -4
  53. simtools/production_configuration/{calculate_statistical_errors_grid_point.py → calculate_statistical_uncertainties_grid_point.py} +101 -116
  54. simtools/production_configuration/derive_corsika_limits.py +239 -111
  55. simtools/production_configuration/derive_corsika_limits_grid.py +189 -0
  56. simtools/production_configuration/derive_production_statistics.py +155 -0
  57. simtools/production_configuration/derive_production_statistics_handler.py +152 -0
  58. simtools/production_configuration/generate_production_grid.py +364 -0
  59. simtools/production_configuration/interpolation_handler.py +303 -96
  60. simtools/ray_tracing/mirror_panel_psf.py +16 -20
  61. simtools/ray_tracing/psf_analysis.py +2 -2
  62. simtools/ray_tracing/ray_tracing.py +12 -7
  63. simtools/reporting/docs_read_parameters.py +426 -81
  64. simtools/runners/corsika_runner.py +11 -1
  65. simtools/runners/corsika_simtel_runner.py +84 -90
  66. simtools/runners/runner_services.py +22 -8
  67. simtools/runners/simtel_runner.py +27 -10
  68. simtools/schemas/model_parameter.metaschema.yml +4 -0
  69. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +1 -0
  70. simtools/schemas/model_parameters/adjust_gain.schema.yml +2 -2
  71. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +2 -2
  72. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +2 -2
  73. simtools/schemas/model_parameters/array_window.schema.yml +2 -2
  74. simtools/schemas/model_parameters/asum_offset.schema.yml +2 -2
  75. simtools/schemas/model_parameters/asum_shaping.schema.yml +2 -2
  76. simtools/schemas/model_parameters/asum_threshold.schema.yml +2 -2
  77. simtools/schemas/model_parameters/axes_offsets.schema.yml +2 -2
  78. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +2 -2
  79. simtools/schemas/model_parameters/camera_body_shape.schema.yml +2 -2
  80. simtools/schemas/model_parameters/camera_config_file.schema.yml +2 -2
  81. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +2 -2
  82. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +2 -2
  83. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +2 -2
  84. simtools/schemas/model_parameters/camera_depth.schema.yml +2 -2
  85. simtools/schemas/model_parameters/camera_filter.schema.yml +2 -2
  86. simtools/schemas/model_parameters/camera_pixels.schema.yml +2 -2
  87. simtools/schemas/model_parameters/camera_transmission.schema.yml +2 -2
  88. simtools/schemas/model_parameters/channels_per_chip.schema.yml +2 -2
  89. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +2 -2
  90. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +90 -1
  91. simtools/schemas/model_parameters/default_trigger.schema.yml +2 -2
  92. simtools/schemas/model_parameters/design_model.schema.yml +2 -2
  93. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +2 -2
  94. simtools/schemas/model_parameters/disc_bins.schema.yml +2 -2
  95. simtools/schemas/model_parameters/disc_start.schema.yml +2 -2
  96. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +2 -2
  97. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +2 -2
  98. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +2 -2
  99. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +2 -2
  100. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +2 -2
  101. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +2 -2
  102. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +2 -2
  103. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +2 -2
  104. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +2 -2
  105. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +2 -2
  106. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +2 -2
  107. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +2 -2
  108. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +2 -2
  109. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +2 -2
  110. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +2 -2
  111. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +2 -2
  112. simtools/schemas/model_parameters/dish_shape_length.schema.yml +2 -2
  113. simtools/schemas/model_parameters/dsum_clipping.schema.yml +2 -2
  114. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +2 -2
  115. simtools/schemas/model_parameters/dsum_offset.schema.yml +2 -2
  116. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +2 -2
  117. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +2 -2
  118. simtools/schemas/model_parameters/dsum_prescale.schema.yml +2 -2
  119. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +2 -2
  120. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +2 -2
  121. simtools/schemas/model_parameters/dsum_shaping.schema.yml +2 -2
  122. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +2 -2
  123. simtools/schemas/model_parameters/dsum_threshold.schema.yml +44 -3
  124. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +2 -2
  125. simtools/schemas/model_parameters/effective_focal_length.schema.yml +2 -2
  126. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +2 -2
  127. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +2 -2
  128. simtools/schemas/model_parameters/fadc_bins.schema.yml +2 -2
  129. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +2 -2
  130. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +2 -2
  131. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +2 -2
  132. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +2 -2
  133. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +2 -2
  134. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +2 -2
  135. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +2 -2
  136. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +2 -2
  137. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +2 -2
  138. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +2 -2
  139. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +2 -2
  140. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +2 -2
  141. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +2 -2
  142. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +2 -2
  143. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +2 -2
  144. simtools/schemas/model_parameters/fadc_mhz.schema.yml +2 -2
  145. simtools/schemas/model_parameters/fadc_noise.schema.yml +2 -2
  146. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +2 -2
  147. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +2 -2
  148. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +2 -2
  149. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +2 -2
  150. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +2 -2
  151. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +2 -2
  152. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +2 -2
  153. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +2 -2
  154. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
  155. simtools/schemas/model_parameters/flatfielding.schema.yml +2 -2
  156. simtools/schemas/model_parameters/focal_length.schema.yml +2 -2
  157. simtools/schemas/model_parameters/focus_offset.schema.yml +2 -2
  158. simtools/schemas/model_parameters/gain_variation.schema.yml +2 -2
  159. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +2 -2
  160. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +2 -2
  161. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +2 -2
  162. simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
  163. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +2 -2
  164. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +2 -2
  165. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +2 -2
  166. simtools/schemas/model_parameters/min_photons.schema.yml +2 -2
  167. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +2 -2
  168. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +2 -2
  169. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +2 -2
  170. simtools/schemas/model_parameters/mirror_class.schema.yml +2 -2
  171. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +2 -2
  172. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +2 -2
  173. simtools/schemas/model_parameters/mirror_list.schema.yml +2 -2
  174. simtools/schemas/model_parameters/mirror_offset.schema.yml +2 -2
  175. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +2 -2
  176. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +2 -2
  177. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +2 -2
  178. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +2 -2
  179. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +2 -2
  180. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +2 -2
  181. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +2 -2
  182. simtools/schemas/model_parameters/num_gains.schema.yml +2 -2
  183. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +2 -2
  184. simtools/schemas/model_parameters/optics_properties.schema.yml +2 -2
  185. simtools/schemas/model_parameters/pedestal_events.schema.yml +7 -3
  186. simtools/schemas/model_parameters/photon_delay.schema.yml +2 -2
  187. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +2 -2
  188. simtools/schemas/model_parameters/pm_average_gain.schema.yml +2 -2
  189. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +2 -2
  190. simtools/schemas/model_parameters/pm_gain_index.schema.yml +2 -2
  191. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +2 -2
  192. simtools/schemas/model_parameters/pm_transit_time.schema.yml +2 -2
  193. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +2 -2
  194. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +2 -2
  195. simtools/schemas/model_parameters/qe_variation.schema.yml +2 -2
  196. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +2 -2
  197. simtools/schemas/model_parameters/random_focal_length.schema.yml +2 -2
  198. simtools/schemas/model_parameters/random_generator.schema.yml +2 -2
  199. simtools/schemas/model_parameters/random_mono_probability.schema.yml +2 -2
  200. simtools/schemas/model_parameters/sampled_output.schema.yml +2 -2
  201. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +2 -2
  202. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +2 -2
  203. simtools/schemas/model_parameters/tailcut_scale.schema.yml +2 -2
  204. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +2 -2
  205. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +2 -2
  206. simtools/schemas/model_parameters/telescope_random_error.schema.yml +2 -2
  207. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +2 -2
  208. simtools/schemas/model_parameters/telescope_transmission.schema.yml +2 -2
  209. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +2 -2
  210. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +2 -2
  211. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +2 -2
  212. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +2 -2
  213. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +2 -2
  214. simtools/schemas/model_parameters/transit_time_error.schema.yml +2 -2
  215. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +2 -2
  216. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +2 -2
  217. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +2 -2
  218. simtools/schemas/model_parameters/trigger_pixels.schema.yml +2 -2
  219. simtools/schemas/production_configuration_metrics.schema.yml +2 -2
  220. simtools/simtel/simtel_config_reader.py +21 -17
  221. simtools/simtel/simtel_config_writer.py +258 -66
  222. simtools/simtel/simtel_io_event_reader.py +301 -194
  223. simtools/simtel/simtel_io_event_writer.py +207 -227
  224. simtools/simtel/simtel_io_file_info.py +62 -0
  225. simtools/simtel/simtel_io_histogram.py +10 -14
  226. simtools/simtel/simtel_io_histograms.py +2 -2
  227. simtools/simtel/simtel_io_metadata.py +106 -0
  228. simtools/simtel/simulator_array.py +28 -14
  229. simtools/simtel/simulator_camera_efficiency.py +12 -6
  230. simtools/simtel/simulator_light_emission.py +85 -45
  231. simtools/simtel/simulator_ray_tracing.py +16 -6
  232. simtools/simulator.py +286 -89
  233. simtools/testing/configuration.py +5 -0
  234. simtools/testing/helpers.py +18 -0
  235. simtools/testing/sim_telarray_metadata.py +212 -0
  236. simtools/testing/validate_output.py +16 -6
  237. simtools/utils/general.py +18 -27
  238. simtools/utils/names.py +32 -10
  239. simtools/visualization/plot_array_layout.py +242 -0
  240. simtools/visualization/plot_pixels.py +681 -0
  241. simtools/visualization/visualize.py +5 -221
  242. simtools/applications/production_generate_simulation_config.py +0 -162
  243. simtools/applications/production_scale_events.py +0 -185
  244. simtools/layout/ctao_array_layouts.py +0 -172
  245. simtools/production_configuration/event_scaler.py +0 -120
  246. simtools/production_configuration/generate_simulation_config.py +0 -158
  247. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/licenses/LICENSE +0 -0
  248. {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@
3
3
  r"""Class to read and manage relevant model parameters for a given telescope model."""
4
4
 
5
5
  import logging
6
+ import re
6
7
  import textwrap
7
8
  from collections import defaultdict
8
9
  from itertools import groupby
@@ -11,8 +12,11 @@ from pathlib import Path
11
12
  import numpy as np
12
13
 
13
14
  from simtools.db import db_handler
15
+ from simtools.io_operations import io_handler
14
16
  from simtools.model.telescope_model import TelescopeModel
17
+ from simtools.utils import general as gen
15
18
  from simtools.utils import names
19
+ from simtools.visualization import plot_pixels
16
20
 
17
21
  logger = logging.getLogger()
18
22
 
@@ -25,35 +29,98 @@ class ReadParameters:
25
29
  self._logger = logging.getLogger(__name__)
26
30
  self.db = db_handler.DatabaseHandler(mongo_db_config=db_config)
27
31
  self.db_config = db_config
28
- self.array_element = args.get("telescope")
29
- self.site = args.get("site")
32
+ self.array_element = args.get("telescope", None)
33
+ self.site = args.get("site", None)
30
34
  self.model_version = args.get("model_version", None)
31
35
  self.output_path = output_path
32
36
  self.observatory = args.get("observatory")
37
+ self.software = args.get("simulation_software", None)
33
38
 
34
- def _convert_to_md(self, input_file):
39
+ @property
40
+ def model_version(self):
41
+ """Model version."""
42
+ return self._model_version
43
+
44
+ @model_version.setter
45
+ def model_version(self, model_version):
46
+ """
47
+ Set model version.
48
+
49
+ Parameters
50
+ ----------
51
+ _model_version: str or list
52
+ Model version (e.g., "6.0.0").
53
+ If a list is passed, it must contain exactly one element,
54
+ and only that element will be used.
55
+
56
+ Raises
57
+ ------
58
+ ValueError
59
+ If more than one model version is passed.
60
+ """
61
+ if isinstance(model_version, list):
62
+ raise ValueError(
63
+ f"Only one model version can be passed to {self.__class__.__name__}, not a list."
64
+ )
65
+ self._model_version = model_version
66
+
67
+ def _convert_to_md(self, parameter, parameter_version, input_file):
35
68
  """Convert a file to a Markdown file, preserving formatting."""
36
69
  input_file = Path(input_file)
70
+
37
71
  output_data_path = Path(self.output_path / "_data_files")
38
72
  output_data_path.mkdir(parents=True, exist_ok=True)
39
73
  output_file_name = Path(input_file.stem + ".md")
40
74
  output_file = output_data_path / output_file_name
75
+ image_name = f"{self.array_element}_{parameter}_{self.model_version.replace('.', '-')}"
76
+ outpath = Path(io_handler.IOHandler().get_output_directory().parent / "_images")
77
+ outpath.mkdir(parents=True, exist_ok=True)
78
+ image_path = Path(f"{outpath}/{image_name}")
79
+
80
+ if parameter == "camera_config_file" and parameter_version:
81
+ image_path = Path(f"{outpath}/{input_file.stem.replace('.', '-')}")
82
+ if not (image_path.with_suffix(".png")).exists():
83
+ logger.info("Plotting camera configuration file: %s", input_file.name)
84
+ plot_config = {
85
+ "file_name": input_file.name,
86
+ "telescope": self.array_element,
87
+ "parameter_version": parameter_version,
88
+ "site": self.site,
89
+ "model_version": self.model_version,
90
+ "parameter": parameter,
91
+ }
92
+
93
+ plot_pixels.plot(
94
+ config=plot_config,
95
+ output_file=image_path,
96
+ db_config=self.db_config,
97
+ )
98
+ else:
99
+ logger.info(
100
+ "Camera configuration file plot already exists: %s",
101
+ image_path.with_suffix(".png"),
102
+ )
41
103
 
42
104
  try:
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")
55
- outfile.write(file_contents)
56
- outfile.write("\n```")
105
+ # with input_file.open("r", encoding="utf-8") as infile:
106
+ file_contents = gen.read_file_encoded_in_utf_or_latin(input_file)
107
+
108
+ if self.model_version is not None:
109
+ with output_file.open("w", encoding="utf-8") as outfile:
110
+ outfile.write(f"# {input_file.stem}\n")
111
+ outfile.write(f"![Parameter plot.]({image_path}.png)\n\n")
112
+ outfile.write(
113
+ "\n\nThe full file can be found in the Simulation Model repository [here]"
114
+ "(https://gitlab.cta-observatory.org/cta-science/simulations/"
115
+ "simulation-model/simulation-models/-/blob/main/simulation-models/"
116
+ f"model_parameters/Files/{input_file.name}).\n\n"
117
+ )
118
+ outfile.write("\n\n")
119
+ outfile.write("The first 30 lines of the file are:\n")
120
+ outfile.write("```\n")
121
+ first_30_lines = "".join(file_contents[:30])
122
+ outfile.write(first_30_lines)
123
+ outfile.write("\n```")
57
124
 
58
125
  except FileNotFoundError as exc:
59
126
  logger.exception(f"Data file not found: {input_file}.")
@@ -61,11 +128,19 @@ class ReadParameters:
61
128
 
62
129
  return f"_data_files/{output_file_name}"
63
130
 
64
- def _format_parameter_value(self, value_data, unit, file_flag):
131
+ def _format_parameter_value(
132
+ self, parameter, value_data, unit, file_flag, parameter_version=None
133
+ ):
65
134
  """Format parameter value based on type."""
66
135
  if file_flag:
67
136
  input_file_name = f"{self.output_path}/model/{value_data}"
68
- output_file_name = self._convert_to_md(input_file_name)
137
+ if parameter_version is None:
138
+ return (
139
+ f"[{Path(value_data).name}](https://gitlab.cta-observatory.org/"
140
+ "cta-science/simulations/simulation-model/simulation-models/-/blob/main/"
141
+ f"simulation-models/model_parameters/Files/{value_data})"
142
+ ).strip()
143
+ output_file_name = self._convert_to_md(parameter, parameter_version, input_file_name)
69
144
  return f"[{Path(value_data).name}]({output_file_name})".strip()
70
145
  if isinstance(value_data, (str | int | float)):
71
146
  return f"{value_data} {unit}".strip()
@@ -77,6 +152,26 @@ class ReadParameters:
77
152
  else ", ".join(f"{v} {unit}" for v in value_data)
78
153
  ).strip()
79
154
 
155
+ def _wrap_at_underscores(self, text, max_width):
156
+ """Wrap text at underscores to fit within a specified width."""
157
+ parts = text.split("_")
158
+ lines = []
159
+ current = []
160
+
161
+ for part in parts:
162
+ # Predict the new length if we add this part
163
+ next_line = "_".join([*current, part])
164
+ if len(next_line) <= max_width:
165
+ current.append(part)
166
+ else:
167
+ lines.append("_".join(current))
168
+ current = [part]
169
+
170
+ if current:
171
+ lines.append("_".join(current))
172
+
173
+ return " ".join(lines)
174
+
80
175
  def _group_model_versions_by_parameter_version(self, grouped_data):
81
176
  """Group model versions by parameter version and track the parameter values."""
82
177
  result = {}
@@ -152,8 +247,10 @@ class ReadParameters:
152
247
  continue
153
248
 
154
249
  file_flag = parameter_data.get("file", False)
155
- value = self._format_parameter_value(value_data, unit, file_flag)
156
250
  parameter_version = parameter_data.get("parameter_version")
251
+ value = self._format_parameter_value(
252
+ parameter_name, value_data, unit, file_flag, parameter_version=None
253
+ )
157
254
  model_version = version
158
255
 
159
256
  # Group the data by parameter version and store model versions as a list
@@ -168,26 +265,29 @@ class ReadParameters:
168
265
 
169
266
  return self._group_model_versions_by_parameter_version(grouped_data)
170
267
 
171
- def get_all_parameter_descriptions(self):
172
- """
173
- Get descriptions for all model parameters.
268
+ def get_all_parameter_descriptions(self, collection="telescopes"):
269
+ """Get descriptions for all model parameters.
174
270
 
175
271
  Returns
176
272
  -------
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.
273
+ dict
274
+ Nested dictionaries with first key as the parameter name and
275
+ the following dictionary as the value:
276
+ - key: description, value: description of the parameter.
277
+ - key: short_description, value: short description of the parameter.
278
+ - key: inst_class, value: class, for eg. Structure, Camera, etc.
181
279
  """
182
- parameter_description, short_description, inst_class = {}, {}, {}
280
+ parameter_dict = {}
183
281
 
184
- for instrument_class in names.db_collection_to_instrument_class_key("telescopes"):
282
+ for instrument_class in names.db_collection_to_instrument_class_key(collection):
185
283
  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
284
+ parameter_dict[parameter] = {
285
+ "description": details.get("description"),
286
+ "short_description": details.get("short_description"),
287
+ "inst_class": instrument_class,
288
+ }
189
289
 
190
- return parameter_description, short_description, inst_class
290
+ return parameter_dict
191
291
 
192
292
  def get_array_element_parameter_data(self, telescope_model, collection="telescopes"):
193
293
  """
@@ -219,8 +319,6 @@ class ReadParameters:
219
319
 
220
320
  for parameter in filter(all_parameter_data.__contains__, names.model_parameters().keys()):
221
321
  parameter_data = all_parameter_data.get(parameter)
222
- if parameter_data["instrument"] != telescope_model.name:
223
- continue
224
322
  parameter_version = telescope_model.get_parameter_version(parameter)
225
323
  unit = parameter_data.get("unit") or " "
226
324
  value_data = parameter_data.get("value")
@@ -229,11 +327,25 @@ class ReadParameters:
229
327
  continue
230
328
 
231
329
  file_flag = parameter_data.get("file", False)
232
- value = self._format_parameter_value(value_data, unit, file_flag)
330
+ value = self._format_parameter_value(
331
+ parameter, value_data, unit, file_flag, parameter_version
332
+ )
333
+
334
+ description = parameter_descriptions.get(parameter).get("description")
335
+ short_description = (
336
+ parameter_descriptions.get(parameter).get("short_description") or description
337
+ )
338
+ inst_class = parameter_descriptions.get(parameter).get("inst_class")
339
+
340
+ matching_instrument = parameter_data["instrument"] == telescope_model.name
341
+ if not names.is_design_type(telescope_model.name) and matching_instrument:
342
+ parameter = f"***{parameter}***"
343
+ parameter_version = f"***{parameter_version}***"
344
+ if not re.match(r"^\[.*\]\(.*\)$", value.strip()):
345
+ value = f"***{value}***"
346
+ description = f"***{description}***"
347
+ short_description = f"***{short_description}***"
233
348
 
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
349
  data.append(
238
350
  [
239
351
  inst_class,
@@ -247,6 +359,120 @@ class ReadParameters:
247
359
 
248
360
  return data
249
361
 
362
+ def _write_to_file(self, data, file):
363
+ # Write table header and separator row
364
+ file.write(
365
+ "| Parameter Name | Parameter Version "
366
+ "| Values | Short Description |\n"
367
+ "|---------------------|------------------------"
368
+ "|-------------|-----------------------------|\n"
369
+ )
370
+
371
+ # Write table rows
372
+ column_widths = [10, 10, 20, 60]
373
+ for (
374
+ _,
375
+ parameter_name,
376
+ parameter_version,
377
+ value,
378
+ description,
379
+ short_description,
380
+ ) in data:
381
+ text = short_description if short_description else description
382
+ wrapped_text = textwrap.fill(str(text), column_widths[3]).split("\n")
383
+ wrapped_text = " ".join(wrapped_text)
384
+ parameter_name = self._wrap_at_underscores(parameter_name, column_widths[0])
385
+
386
+ file.write(
387
+ f"| {parameter_name:{column_widths[0]}} |"
388
+ f" {parameter_version:{column_widths[1]}} |"
389
+ f" {value:{column_widths[2]}} |"
390
+ f" {wrapped_text:{column_widths[3]}} |\n"
391
+ )
392
+ file.write("\n\n")
393
+
394
+ def get_simulation_configuration_data(self):
395
+ """Get data and descriptions for simulation configuration parameters."""
396
+
397
+ def get_param_data(telescope, site):
398
+ """Retrieve and format parameter data for one telescope-site combo."""
399
+ param_dict = self.db.get_simulation_configuration_parameters(
400
+ simulation_software=self.software,
401
+ site=site,
402
+ array_element_name=telescope,
403
+ model_version=self.model_version,
404
+ )
405
+
406
+ parameter_descriptions = self.get_all_parameter_descriptions(
407
+ collection=f"configuration_{self.software}"
408
+ )
409
+
410
+ model_output_path = Path(f"{self.output_path}/model")
411
+ model_output_path.mkdir(parents=True, exist_ok=True)
412
+ self.db.export_model_files(parameters=param_dict, dest=str(model_output_path))
413
+
414
+ data = []
415
+ for parameter, parameter_data in param_dict.items():
416
+ description = parameter_descriptions.get(parameter).get("description")
417
+ short_description = parameter_descriptions.get(parameter).get(
418
+ "short_description", description
419
+ )
420
+ value_data = parameter_data.get("value")
421
+
422
+ if value_data is None:
423
+ continue
424
+
425
+ unit = parameter_data.get("unit") or " "
426
+ file_flag = parameter_data.get("file", False)
427
+ parameter_version = parameter_data.get("parameter_version")
428
+ value = self._format_parameter_value(
429
+ parameter, value_data, unit, file_flag, parameter_version
430
+ )
431
+
432
+ data.append(
433
+ [
434
+ telescope,
435
+ parameter,
436
+ parameter_version,
437
+ value,
438
+ description,
439
+ short_description,
440
+ ]
441
+ )
442
+ return data
443
+
444
+ if self.software == "corsika":
445
+ return get_param_data(self.array_element, self.site)
446
+
447
+ results = []
448
+ telescopes = self.db.get_array_elements(self.model_version)
449
+ for telescope in telescopes:
450
+ valid_site = names.get_site_from_array_element_name(telescope)
451
+ if not isinstance(valid_site, list):
452
+ results.extend(get_param_data(telescope, valid_site))
453
+ else:
454
+ for site in valid_site:
455
+ results.extend(get_param_data(telescope, site))
456
+ return results
457
+
458
+ def produce_simulation_configuration_report(self):
459
+ """Write simulation configuration report."""
460
+ output_filename = Path(self.output_path / (f"configuration_{self.software}.md"))
461
+ output_filename.parent.mkdir(parents=True, exist_ok=True)
462
+ data = self.get_simulation_configuration_data()
463
+
464
+ with output_filename.open("w", encoding="utf-8") as file:
465
+ file.write(f"# configuration_{self.software}\n")
466
+ file.write("\n\n")
467
+ if self.software == "sim_telarray":
468
+ data.sort(key=lambda x: (x[0], x[1]))
469
+ for telescope, group in groupby(data, key=lambda x: x[0]):
470
+ file.write(f"## [{telescope}]({telescope}.md)\n")
471
+ file.write("\n\n")
472
+ self._write_to_file(group, file)
473
+ else:
474
+ self._write_to_file(data, file)
475
+
250
476
  def produce_array_element_report(self):
251
477
  """
252
478
  Produce a markdown report of all model parameters per array element.
@@ -281,44 +507,20 @@ class ReadParameters:
281
507
  file.write(
282
508
  "The design model can be found here: "
283
509
  f"[{telescope_model.design_model}]"
284
- f"({telescope_model.design_model}.md).\n"
510
+ f"({telescope_model.design_model}.md).\n\n"
511
+ )
512
+ file.write(
513
+ "Parameters shown in ***bold and italics*** are specific to each telescope.\n"
514
+ "Parameters without emphasis are inherited from the design model.\n"
285
515
  )
286
516
  file.write("\n\n")
287
517
 
288
518
  for class_name, group in groupby(data, key=lambda x: x[0]):
289
519
  group = sorted(group, key=lambda x: x[1])
290
520
  file.write(f"## {class_name}\n\n")
521
+ self._write_to_file(group, file)
291
522
 
292
- # Write table header and separator row
293
- file.write(
294
- "| Parameter Name | Parameter Version "
295
- "| Values | Short Description |\n"
296
- "|---------------------|------------------------"
297
- "|-------------|-----------------------------|\n"
298
- )
299
-
300
- # Write table rows
301
- column_widths = [20, 20, 20, 70]
302
- for (
303
- _,
304
- parameter_name,
305
- parameter_version,
306
- value,
307
- description,
308
- short_description,
309
- ) in group:
310
- text = short_description if short_description else description
311
- wrapped_text = textwrap.fill(str(text), column_widths[3]).split("\n")
312
- wrapped_text = " ".join(wrapped_text)
313
- file.write(
314
- f"| {parameter_name:{column_widths[0]}} |"
315
- f" {parameter_version:{column_widths[1]}} |"
316
- f" {value:{column_widths[2]}} |"
317
- f" {wrapped_text} |\n"
318
- )
319
- file.write("\n\n")
320
-
321
- def produce_model_parameter_reports(self):
523
+ def produce_model_parameter_reports(self, collection="telescopes"):
322
524
  """
323
525
  Produce a markdown report per parameter for a given array element.
324
526
 
@@ -334,7 +536,7 @@ class ReadParameters:
334
536
 
335
537
  all_parameter_names = names.model_parameters(None).keys()
336
538
  all_parameter_data = self.db.get_model_parameters_for_all_model_versions(
337
- site=self.site, array_element_name=self.array_element, collection="telescopes"
539
+ site=self.site, array_element_name=self.array_element, collection=collection
338
540
  )
339
541
 
340
542
  comparison_data = self._compare_parameter_across_versions(
@@ -347,7 +549,12 @@ class ReadParameters:
347
549
  continue
348
550
 
349
551
  output_filename = output_path / f"{parameter}.md"
350
- description = self.get_all_parameter_descriptions()[0].get(parameter)
552
+
553
+ parameter_descriptions = self.get_all_parameter_descriptions(collection=collection).get(
554
+ parameter
555
+ ) or self.get_all_parameter_descriptions(collection="telescopes").get(parameter)
556
+
557
+ description = parameter_descriptions.get("description")
351
558
  with output_filename.open("w", encoding="utf-8") as file:
352
559
  # Write header
353
560
  file.write(
@@ -370,16 +577,16 @@ class ReadParameters:
370
577
  file.write(
371
578
  f"| {item['parameter_version']} |"
372
579
  f" {item['model_version']} |"
373
- f"{item['value'].replace('](', '](../')} |\n"
580
+ f"{item['value']} |\n"
374
581
  )
375
582
 
376
583
  file.write("\n")
377
584
  if comparison_data.get(parameter)[0]["file_flag"]:
378
- file.write(f"![Parameter plot.](_images/{self.array_element}_{parameter}.png)")
585
+ file.write(f"![Parameter plot.](/_images/{self.array_element}_{parameter}.png)")
379
586
 
380
587
  def _write_array_layouts_section(self, file, layouts):
381
588
  """Write the array layouts section of the report."""
382
- file.write("\n## Array Layouts {#array-layouts-details}\n\n")
589
+ file.write("\n## Array Layouts\n\n")
383
590
  for layout in layouts:
384
591
  layout_name = layout["name"]
385
592
  elements = layout["elements"]
@@ -388,10 +595,15 @@ class ReadParameters:
388
595
  for element in sorted(elements):
389
596
  file.write(f"| [{element}]({element}.md) |\n")
390
597
  file.write("\n")
598
+ version = self.model_version.replace(".", "-")
599
+ filename = f"OBS-{self.site}_{layout_name}_{version}.png"
600
+ image_path = f"/_images/{filename}"
601
+ file.write(f"![{layout_name} Layout]({image_path})\n\n")
602
+ file.write("\n")
391
603
 
392
604
  def _write_array_triggers_section(self, file, trigger_configs):
393
605
  """Write the array triggers section of the report."""
394
- file.write("\n## Array Trigger Configurations {#array-triggers-details}\n\n")
606
+ file.write("\n## Array Trigger Configurations\n\n")
395
607
  file.write(
396
608
  "| Trigger Name | Multiplicity | Width | Hard Stereo | Min Separation |\n"
397
609
  "|--------------|--------------|--------|-------------|----------------|\n"
@@ -411,24 +623,34 @@ class ReadParameters:
411
623
 
412
624
  def _write_parameters_table(self, file, all_parameter_data):
413
625
  """Write the main parameters table of the report."""
414
- file.write("| Parameter | Value |\n|-----------|--------|\n")
626
+ file.write(
627
+ "| Parameter | Value | Parameter Version |\n"
628
+ "|-----------|--------|-------------------|\n"
629
+ )
415
630
  for param_name, param_data in sorted(all_parameter_data.items()):
416
631
  value = param_data.get("value")
417
632
  unit = param_data.get("unit") or " "
418
633
  file_flag = param_data.get("file", False)
634
+ parameter_version = param_data.get("parameter_version")
419
635
 
420
636
  if value is None:
421
637
  continue
422
638
 
423
639
  if param_name == "array_layouts":
424
- file.write("| array_layouts | [View Array Layouts](#array-layouts-details) |\n")
640
+ file.write(
641
+ "| array_layouts | [View Array Layouts](#array-layouts)"
642
+ f" | {parameter_version} |\n"
643
+ )
425
644
  elif param_name == "array_triggers":
426
645
  file.write(
427
- "| array_triggers | [View Trigger Configurations](#array-triggers-details) |\n"
646
+ "| array_triggers | [View Trigger Configurations]"
647
+ f"(#array-trigger-configurations) | {parameter_version} |\n"
428
648
  )
429
649
  else:
430
- formatted_value = self._format_parameter_value(value, unit, file_flag)
431
- file.write(f"| {param_name} | {formatted_value} |\n")
650
+ formatted_value = self._format_parameter_value(
651
+ param_name, value, unit, file_flag, parameter_version
652
+ )
653
+ file.write(f"| {param_name} | {formatted_value} | {parameter_version} |\n")
432
654
  file.write("\n")
433
655
 
434
656
  def produce_observatory_report(self):
@@ -463,3 +685,126 @@ class ReadParameters:
463
685
  self._write_array_triggers_section(
464
686
  file, all_parameter_data["array_triggers"]["value"]
465
687
  )
688
+
689
+ def get_calibration_data(self, all_parameter_data, array_element):
690
+ """Get calibration data and descriptions for a given array element."""
691
+ calibration_descriptions = self.get_all_parameter_descriptions(
692
+ collection="calibration_devices"
693
+ )
694
+ # get descriptions of array element positions from the telescope collection
695
+ telescope_descriptions = self.get_all_parameter_descriptions(collection="telescopes")
696
+ data = []
697
+ class_grouped_data = {}
698
+
699
+ for parameter in all_parameter_data.keys():
700
+ parameter_descriptions = calibration_descriptions.get(
701
+ parameter
702
+ ) or telescope_descriptions.get(parameter)
703
+
704
+ parameter_data = all_parameter_data.get(parameter)
705
+ parameter_version = parameter_data.get("parameter_version")
706
+ unit = parameter_data.get("unit") or " "
707
+ value_data = parameter_data.get("value")
708
+
709
+ if value_data is None:
710
+ continue
711
+
712
+ file_flag = parameter_data.get("file", False)
713
+ value = self._format_parameter_value(
714
+ parameter, value_data, unit, file_flag, parameter_version
715
+ )
716
+
717
+ description = parameter_descriptions.get("description")
718
+ short_description = parameter_descriptions.get("short_description") or description
719
+
720
+ inst_class = parameter_descriptions.get("inst_class")
721
+
722
+ matching_instrument = parameter_data["instrument"] == array_element
723
+ if not names.is_design_type(array_element) and matching_instrument:
724
+ parameter = f"***{parameter}***"
725
+ parameter_version = f"***{parameter_version}***"
726
+ if not re.match(r"^\[.*\]\(.*\)$", value.strip()):
727
+ value = f"***{value}***"
728
+ description = f"***{description}***"
729
+ short_description = f"***{short_description}***"
730
+
731
+ # Group by class name
732
+ if inst_class not in class_grouped_data:
733
+ class_grouped_data[inst_class] = []
734
+
735
+ class_grouped_data[inst_class].append(
736
+ [
737
+ inst_class,
738
+ parameter,
739
+ parameter_version,
740
+ value,
741
+ description,
742
+ short_description,
743
+ ]
744
+ )
745
+
746
+ data = []
747
+ for class_name in sorted(class_grouped_data.keys(), reverse=True):
748
+ sorted_class_data = sorted(class_grouped_data[class_name], key=lambda x: x[1])
749
+ data.extend(sorted_class_data)
750
+
751
+ return data
752
+
753
+ def produce_calibration_reports(self):
754
+ """Write calibration reports."""
755
+ calibration_array_elements = self.db.get_array_elements(
756
+ self.model_version, collection="calibration_devices"
757
+ )
758
+ array_elements = calibration_array_elements.copy()
759
+ for element in calibration_array_elements:
760
+ design_model = self.db.get_design_model(
761
+ self.model_version, element, "calibration_devices"
762
+ )
763
+ if design_model and design_model not in array_elements:
764
+ array_elements.append(design_model)
765
+
766
+ for calibration_device in array_elements:
767
+ all_parameter_data = self.db.get_model_parameters(
768
+ site=names.get_site_from_array_element_name(calibration_device),
769
+ array_element_name=calibration_device,
770
+ collection="calibration_devices",
771
+ model_version=self.model_version,
772
+ )
773
+
774
+ output_filename = Path(self.output_path / (f"{calibration_device}.md"))
775
+ output_filename.parent.mkdir(parents=True, exist_ok=True)
776
+ data = self.get_calibration_data(all_parameter_data, calibration_device)
777
+
778
+ design_model = self.db.get_design_model(
779
+ self.model_version, calibration_device, "calibration_devices"
780
+ )
781
+
782
+ with output_filename.open("w", encoding="utf-8") as file:
783
+ file.write(f"# {calibration_device}\n")
784
+ file.write("\n\n")
785
+
786
+ if not names.is_design_type(calibration_device):
787
+ file.write(
788
+ "The design model can be found here: "
789
+ f"[{design_model}]"
790
+ f"({design_model}.md).\n\n"
791
+ )
792
+ file.write(
793
+ "Parameters shown in ***bold and italics*** are specific"
794
+ " to each array element.\n"
795
+ "Parameters without emphasis are inherited from the design model.\n"
796
+ )
797
+ file.write("\n\n")
798
+
799
+ for class_name, group in groupby(data, key=lambda x: x[0]):
800
+ group = sorted(group, key=lambda x: x[1])
801
+ file.write(f"## {class_name}\n\n")
802
+ self._write_to_file(group, file)
803
+
804
+ new_output_path = Path(self.output_path).parent.parent / "parameters"
805
+ new_output_path.mkdir(parents=True, exist_ok=True)
806
+ self.output_path = new_output_path
807
+ for calibration_device in array_elements:
808
+ self.site = names.get_site_from_array_element_name(calibration_device)
809
+ self.array_element = calibration_device
810
+ self.produce_model_parameter_reports(collection="calibration_devices")