gammasimtools 0.14.0__py3-none-any.whl → 0.16.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 (223) hide show
  1. {gammasimtools-0.14.0.dist-info → gammasimtools-0.16.0.dist-info}/METADATA +2 -32
  2. {gammasimtools-0.14.0.dist-info → gammasimtools-0.16.0.dist-info}/RECORD +222 -214
  3. {gammasimtools-0.14.0.dist-info → gammasimtools-0.16.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.14.0.dist-info → gammasimtools-0.16.0.dist-info}/entry_points.txt +5 -2
  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_psf_parameters.py +12 -9
  10. simtools/applications/docs_produce_array_element_report.py +3 -3
  11. simtools/applications/docs_produce_calibration_reports.py +49 -0
  12. simtools/applications/docs_produce_simulation_configuration_report.py +50 -0
  13. simtools/applications/{generate_simtel_array_histograms.py → generate_sim_telarray_histograms.py} +2 -2
  14. simtools/applications/production_derive_corsika_limits.py +63 -10
  15. simtools/applications/production_derive_statistics.py +125 -0
  16. simtools/applications/production_generate_grid.py +197 -0
  17. simtools/applications/production_generate_simulation_config.py +0 -10
  18. simtools/applications/simulate_light_emission.py +5 -13
  19. simtools/applications/simulate_prod.py +16 -4
  20. simtools/applications/validate_cumulative_psf.py +6 -4
  21. simtools/applications/validate_file_using_schema.py +7 -3
  22. simtools/applications/validate_optics.py +5 -4
  23. simtools/camera/camera_efficiency.py +14 -39
  24. simtools/configuration/commandline_parser.py +4 -3
  25. simtools/configuration/configurator.py +10 -0
  26. simtools/corsika/corsika_config.py +103 -5
  27. simtools/data_model/format_checkers.py +9 -0
  28. simtools/data_model/model_data_writer.py +3 -0
  29. simtools/data_model/schema.py +27 -16
  30. simtools/data_model/validate_data.py +27 -7
  31. simtools/db/db_handler.py +10 -4
  32. simtools/layout/array_layout.py +1 -0
  33. simtools/model/array_model.py +63 -29
  34. simtools/model/model_parameter.py +76 -51
  35. simtools/model/model_utils.py +43 -1
  36. simtools/model/site_model.py +3 -2
  37. simtools/model/telescope_model.py +4 -4
  38. simtools/production_configuration/calculate_statistical_errors_grid_point.py +0 -4
  39. simtools/production_configuration/{event_scaler.py → derive_production_statistics.py} +24 -20
  40. simtools/production_configuration/derive_production_statistics_handler.py +119 -0
  41. simtools/production_configuration/generate_production_grid.py +364 -0
  42. simtools/production_configuration/generate_simulation_config.py +9 -9
  43. simtools/production_configuration/interpolation_handler.py +16 -11
  44. simtools/ray_tracing/mirror_panel_psf.py +16 -20
  45. simtools/ray_tracing/psf_analysis.py +2 -2
  46. simtools/ray_tracing/ray_tracing.py +5 -1
  47. simtools/reporting/docs_read_parameters.py +361 -58
  48. simtools/runners/corsika_runner.py +11 -1
  49. simtools/runners/corsika_simtel_runner.py +80 -89
  50. simtools/runners/runner_services.py +17 -4
  51. simtools/runners/simtel_runner.py +27 -10
  52. simtools/schemas/model_parameter.metaschema.yml +4 -0
  53. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +1 -0
  54. simtools/schemas/model_parameters/adjust_gain.schema.yml +2 -2
  55. simtools/schemas/model_parameters/array_element_position_ground.schema.yml +2 -2
  56. simtools/schemas/model_parameters/array_element_position_utm.schema.yml +2 -2
  57. simtools/schemas/model_parameters/array_window.schema.yml +2 -2
  58. simtools/schemas/model_parameters/asum_offset.schema.yml +2 -2
  59. simtools/schemas/model_parameters/asum_shaping.schema.yml +2 -2
  60. simtools/schemas/model_parameters/asum_threshold.schema.yml +2 -2
  61. simtools/schemas/model_parameters/axes_offsets.schema.yml +2 -2
  62. simtools/schemas/model_parameters/camera_body_diameter.schema.yml +2 -2
  63. simtools/schemas/model_parameters/camera_body_shape.schema.yml +2 -2
  64. simtools/schemas/model_parameters/camera_config_file.schema.yml +2 -2
  65. simtools/schemas/model_parameters/camera_config_rotate.schema.yml +2 -2
  66. simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +2 -2
  67. simtools/schemas/model_parameters/camera_degraded_map.schema.yml +2 -2
  68. simtools/schemas/model_parameters/camera_depth.schema.yml +2 -2
  69. simtools/schemas/model_parameters/camera_filter.schema.yml +2 -2
  70. simtools/schemas/model_parameters/camera_pixels.schema.yml +2 -2
  71. simtools/schemas/model_parameters/camera_transmission.schema.yml +2 -2
  72. simtools/schemas/model_parameters/channels_per_chip.schema.yml +2 -2
  73. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +2 -2
  74. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +90 -1
  75. simtools/schemas/model_parameters/default_trigger.schema.yml +2 -2
  76. simtools/schemas/model_parameters/design_model.schema.yml +2 -2
  77. simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +2 -2
  78. simtools/schemas/model_parameters/disc_bins.schema.yml +2 -2
  79. simtools/schemas/model_parameters/disc_start.schema.yml +2 -2
  80. simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +2 -2
  81. simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +2 -2
  82. simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +2 -2
  83. simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +2 -2
  84. simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +2 -2
  85. simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +2 -2
  86. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +2 -2
  87. simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +2 -2
  88. simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +2 -2
  89. simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +2 -2
  90. simtools/schemas/model_parameters/discriminator_threshold.schema.yml +2 -2
  91. simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +2 -2
  92. simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +2 -2
  93. simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +2 -2
  94. simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +2 -2
  95. simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +2 -2
  96. simtools/schemas/model_parameters/dish_shape_length.schema.yml +2 -2
  97. simtools/schemas/model_parameters/dsum_clipping.schema.yml +2 -2
  98. simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +2 -2
  99. simtools/schemas/model_parameters/dsum_offset.schema.yml +2 -2
  100. simtools/schemas/model_parameters/dsum_pedsub.schema.yml +2 -2
  101. simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +2 -2
  102. simtools/schemas/model_parameters/dsum_prescale.schema.yml +2 -2
  103. simtools/schemas/model_parameters/dsum_presum_max.schema.yml +2 -2
  104. simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +2 -2
  105. simtools/schemas/model_parameters/dsum_shaping.schema.yml +2 -2
  106. simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +2 -2
  107. simtools/schemas/model_parameters/dsum_threshold.schema.yml +3 -3
  108. simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +2 -2
  109. simtools/schemas/model_parameters/effective_focal_length.schema.yml +2 -2
  110. simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +2 -2
  111. simtools/schemas/model_parameters/fadc_amplitude.schema.yml +2 -2
  112. simtools/schemas/model_parameters/fadc_bins.schema.yml +2 -2
  113. simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +2 -2
  114. simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +2 -2
  115. simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +2 -2
  116. simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +2 -2
  117. simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +2 -2
  118. simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +2 -2
  119. simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +2 -2
  120. simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +2 -2
  121. simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +2 -2
  122. simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +2 -2
  123. simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +2 -2
  124. simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +2 -2
  125. simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +2 -2
  126. simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +2 -2
  127. simtools/schemas/model_parameters/fadc_max_signal.schema.yml +2 -2
  128. simtools/schemas/model_parameters/fadc_mhz.schema.yml +2 -2
  129. simtools/schemas/model_parameters/fadc_noise.schema.yml +2 -2
  130. simtools/schemas/model_parameters/fadc_pedestal.schema.yml +2 -2
  131. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +2 -2
  132. simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +2 -2
  133. simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +2 -2
  134. simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +2 -2
  135. simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +2 -2
  136. simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +2 -2
  137. simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +2 -2
  138. simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
  139. simtools/schemas/model_parameters/flatfielding.schema.yml +2 -2
  140. simtools/schemas/model_parameters/focal_length.schema.yml +2 -2
  141. simtools/schemas/model_parameters/focus_offset.schema.yml +2 -2
  142. simtools/schemas/model_parameters/gain_variation.schema.yml +2 -2
  143. simtools/schemas/model_parameters/hg_lg_variation.schema.yml +2 -2
  144. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +2 -2
  145. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +2 -2
  146. simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
  147. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +2 -2
  148. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +2 -2
  149. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +2 -2
  150. simtools/schemas/model_parameters/min_photons.schema.yml +2 -2
  151. simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +2 -2
  152. simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +2 -2
  153. simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +2 -2
  154. simtools/schemas/model_parameters/mirror_class.schema.yml +2 -2
  155. simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +2 -2
  156. simtools/schemas/model_parameters/mirror_focal_length.schema.yml +2 -2
  157. simtools/schemas/model_parameters/mirror_list.schema.yml +2 -2
  158. simtools/schemas/model_parameters/mirror_offset.schema.yml +2 -2
  159. simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +2 -2
  160. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +2 -2
  161. simtools/schemas/model_parameters/multiplicity_offset.schema.yml +2 -2
  162. simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +2 -2
  163. simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +2 -2
  164. simtools/schemas/model_parameters/nsb_offaxis.schema.yml +2 -2
  165. simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +2 -2
  166. simtools/schemas/model_parameters/num_gains.schema.yml +2 -2
  167. simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +2 -2
  168. simtools/schemas/model_parameters/optics_properties.schema.yml +2 -2
  169. simtools/schemas/model_parameters/pedestal_events.schema.yml +7 -3
  170. simtools/schemas/model_parameters/photon_delay.schema.yml +2 -2
  171. simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +2 -2
  172. simtools/schemas/model_parameters/pm_average_gain.schema.yml +2 -2
  173. simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +2 -2
  174. simtools/schemas/model_parameters/pm_gain_index.schema.yml +2 -2
  175. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +2 -2
  176. simtools/schemas/model_parameters/pm_transit_time.schema.yml +2 -2
  177. simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +2 -2
  178. simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +2 -2
  179. simtools/schemas/model_parameters/qe_variation.schema.yml +2 -2
  180. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +2 -2
  181. simtools/schemas/model_parameters/random_focal_length.schema.yml +2 -2
  182. simtools/schemas/model_parameters/random_generator.schema.yml +2 -2
  183. simtools/schemas/model_parameters/random_mono_probability.schema.yml +2 -2
  184. simtools/schemas/model_parameters/sampled_output.schema.yml +2 -2
  185. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +2 -2
  186. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +2 -2
  187. simtools/schemas/model_parameters/tailcut_scale.schema.yml +2 -2
  188. simtools/schemas/model_parameters/telescope_axis_height.schema.yml +2 -2
  189. simtools/schemas/model_parameters/telescope_random_angle.schema.yml +2 -2
  190. simtools/schemas/model_parameters/telescope_random_error.schema.yml +2 -2
  191. simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +2 -2
  192. simtools/schemas/model_parameters/telescope_transmission.schema.yml +2 -2
  193. simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +2 -2
  194. simtools/schemas/model_parameters/teltrig_min_time.schema.yml +2 -2
  195. simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +2 -2
  196. simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +2 -2
  197. simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +2 -2
  198. simtools/schemas/model_parameters/transit_time_error.schema.yml +2 -2
  199. simtools/schemas/model_parameters/transit_time_jitter.schema.yml +2 -2
  200. simtools/schemas/model_parameters/trigger_current_limit.schema.yml +2 -2
  201. simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +2 -2
  202. simtools/schemas/model_parameters/trigger_pixels.schema.yml +2 -2
  203. simtools/simtel/simtel_config_reader.py +21 -17
  204. simtools/simtel/simtel_config_writer.py +237 -65
  205. simtools/simtel/simtel_io_file_info.py +57 -0
  206. simtools/simtel/simtel_io_histogram.py +10 -14
  207. simtools/simtel/simtel_io_histograms.py +2 -2
  208. simtools/simtel/simtel_io_metadata.py +91 -0
  209. simtools/simtel/simulator_array.py +26 -12
  210. simtools/simtel/simulator_camera_efficiency.py +12 -6
  211. simtools/simtel/simulator_light_emission.py +6 -11
  212. simtools/simtel/simulator_ray_tracing.py +14 -4
  213. simtools/simulator.py +230 -66
  214. simtools/testing/configuration.py +12 -6
  215. simtools/testing/helpers.py +18 -0
  216. simtools/testing/sim_telarray_metadata.py +212 -0
  217. simtools/testing/validate_output.py +12 -5
  218. simtools/utils/general.py +18 -27
  219. simtools/utils/names.py +27 -5
  220. simtools/visualization/visualize.py +2 -2
  221. simtools/applications/production_scale_events.py +0 -185
  222. {gammasimtools-0.14.0.dist-info → gammasimtools-0.16.0.dist-info}/licenses/LICENSE +0 -0
  223. {gammasimtools-0.14.0.dist-info → gammasimtools-0.16.0.dist-info}/top_level.txt +0 -0
@@ -163,6 +163,7 @@ class Configurator:
163
163
  self.config["activity_id"] = str(uuid.uuid4())
164
164
  if self.config["label"] is None:
165
165
  self.config["label"] = self.label
166
+ self._initialize_model_versions()
166
167
 
167
168
  self._initialize_io_handler()
168
169
  if output:
@@ -329,6 +330,15 @@ class Configurator:
329
330
 
330
331
  self._fill_from_config_dict(_env_dict)
331
332
 
333
+ def _initialize_model_versions(self):
334
+ """Initialize model versions."""
335
+ if (
336
+ self.config.get("model_version", None)
337
+ and isinstance(self.config["model_version"], list)
338
+ and len(self.config["model_version"]) == 1
339
+ ):
340
+ self.config["model_version"] = self.config["model_version"][0]
341
+
332
342
  def _initialize_io_handler(self):
333
343
  """Initialize IOHandler with input and output paths."""
334
344
  _io_handler = io_handler.IOHandler()
@@ -124,9 +124,15 @@ class CorsikaConfig:
124
124
  if db_config is None: # all following parameter require DB
125
125
  return config
126
126
 
127
- db_model_parameters = ModelParameter(
128
- mongo_db_config=db_config, model_version=args_dict["model_version"]
129
- )
127
+ # If the user provided multiple model versions, we take the first one
128
+ # because for CORSIKA config we need only one and it doesn't matter which
129
+ model_versions = args_dict.get("model_version", None)
130
+ if not isinstance(model_versions, list):
131
+ model_versions = [model_versions]
132
+ self.assert_corsika_configurations_match(model_versions, db_config=db_config)
133
+ model_version = model_versions[0]
134
+ self._logger.debug(f"Using model version {model_version} for CORSIKA parameters")
135
+ db_model_parameters = ModelParameter(mongo_db_config=db_config, model_version=model_version)
130
136
  parameters_from_db = db_model_parameters.get_simulation_software_parameters("corsika")
131
137
 
132
138
  config["INTERACTION_FLAGS"] = self._corsika_configuration_interaction_flags(
@@ -140,6 +146,60 @@ class CorsikaConfig:
140
146
 
141
147
  return config
142
148
 
149
+ def assert_corsika_configurations_match(self, model_versions, db_config=None):
150
+ """
151
+ Assert that CORSIKA configurations match across all model versions.
152
+
153
+ Parameters
154
+ ----------
155
+ model_versions : list
156
+ List of model versions to check.
157
+ db_config : dict, optional
158
+ Database configuration.
159
+
160
+ Raises
161
+ ------
162
+ InvalidCorsikaInputError
163
+ If CORSIKA parameters are not identical across all model versions.
164
+ """
165
+ if len(model_versions) < 2:
166
+ return
167
+
168
+ parameters_from_db_list = []
169
+
170
+ # Get parameters for all model versions
171
+ for model_version in model_versions:
172
+ db_model_parameters = ModelParameter(
173
+ mongo_db_config=db_config, model_version=model_version
174
+ )
175
+ parameters_from_db_list.append(
176
+ db_model_parameters.get_simulation_software_parameters("corsika")
177
+ )
178
+
179
+ # Parameters that can differ between model versions (e.g., i/o buffer size)
180
+ skip_parameters = ["corsika_iact_io_buffer", "corsika_iact_split_auto"]
181
+
182
+ # Check if all parameters match
183
+ for i in range(len(parameters_from_db_list) - 1):
184
+ for key in parameters_from_db_list[i]:
185
+ if key in skip_parameters:
186
+ continue
187
+
188
+ current_value = parameters_from_db_list[i][key]["value"]
189
+ next_value = parameters_from_db_list[i + 1][key]["value"]
190
+
191
+ if current_value != next_value:
192
+ self._logger.warning(
193
+ f"Parameter '{key}' mismatch between model versions:\n"
194
+ f" {model_versions[i]}: {current_value}\n"
195
+ f" {model_versions[i + 1]}: {next_value}"
196
+ )
197
+ raise InvalidCorsikaInputError(
198
+ f"CORSIKA parameter '{key}' differs between model versions "
199
+ f"{model_versions[i]} and {model_versions[i + 1]}. "
200
+ f"Values are {current_value} and {next_value} respectively."
201
+ )
202
+
143
203
  def _corsika_configuration_from_user_input(self, args_dict):
144
204
  """
145
205
  Get CORSIKA configuration from user input.
@@ -236,7 +296,45 @@ class CorsikaConfig:
236
296
 
237
297
  def _input_config_corsika_starting_grammage(self, entry):
238
298
  """Return FIXCHI parameter CORSIKA format."""
239
- return f"{entry['value'] * u.Unit(entry['unit']).to('g/cm2')}"
299
+ value = self._get_starting_grammage_value(entry["value"])
300
+ return f"{value * u.Unit(entry['unit']).to('g/cm2')}"
301
+
302
+ def _get_starting_grammage_value(self, value_entry):
303
+ """
304
+ Get appropriate starting grammage value from entry values.
305
+
306
+ Parameters
307
+ ----------
308
+ value_entry : float or list
309
+ Value or list of grammage configurations
310
+
311
+ Returns
312
+ -------
313
+ float
314
+ Selected grammage value
315
+ """
316
+ if not isinstance(value_entry, list):
317
+ return value_entry
318
+
319
+ tel_types = {tel.design_model for tel in self.array_model.telescope_model.values()}
320
+ particle = self.primary_particle.name
321
+ matched_values = self._get_matching_grammage_values(value_entry, tel_types, particle)
322
+
323
+ return min(matched_values) if matched_values else 0
324
+
325
+ def _get_matching_grammage_values(self, configs, tel_types, particle):
326
+ """Get list of matching grammage values for particle and telescope types."""
327
+ matched = []
328
+ defaults = []
329
+
330
+ for config in configs:
331
+ if config.get("instrument") is None or config.get("instrument") in tel_types:
332
+ if config["primary_particle"] == particle:
333
+ matched.append(config["value"])
334
+ elif config["primary_particle"] == "default":
335
+ defaults.append(config["value"])
336
+
337
+ return matched if matched else defaults
240
338
 
241
339
  def _input_config_corsika_particle_kinetic_energy_cutoff(self, entry):
242
340
  """Return ECUTS parameter CORSIKA format."""
@@ -587,7 +685,7 @@ class CorsikaConfig:
587
685
  str
588
686
  Output file name.
589
687
  """
590
- sub_dir = "corsika_simtel" if use_multipipe else "corsika"
688
+ sub_dir = "corsika_sim_telarray" if use_multipipe else "corsika"
591
689
  config_file_name = self.get_corsika_config_file_name(file_type="config")
592
690
  file_directory = self.io_handler.get_output_directory(label=self.label, sub_dir=sub_dir)
593
691
  self._logger.debug(f"Creating directory {file_directory}")
@@ -5,6 +5,7 @@ import re
5
5
  import astropy.units as u
6
6
  import jsonschema
7
7
 
8
+ from simtools.corsika.primary_particle import PrimaryParticle
8
9
  from simtools.utils import names
9
10
 
10
11
  format_checker = jsonschema.FormatChecker()
@@ -50,3 +51,11 @@ def check_array_triggers_name(name):
50
51
  raise ValueError(f"Array trigger name '{name}' does not match pattern '{pattern}'")
51
52
  names.validate_array_element_type(re.match(pattern, name).group(1))
52
53
  return True
54
+
55
+
56
+ @format_checker.checks("common_particle_name")
57
+ def check_common_particle_name(name):
58
+ """Validate common particle names for jsonschema."""
59
+ if name in PrimaryParticle.particle_names() or name == "default":
60
+ return True
61
+ raise ValueError(f"Invalid common particle name: '{name}'")
@@ -270,6 +270,9 @@ class ModelDataWriter:
270
270
  "type": self._get_parameter_type(),
271
271
  "file": self._parameter_is_a_file(),
272
272
  "meta_parameter": False,
273
+ "model_parameter_schema_version": self.schema_dict.get(
274
+ "model_parameter_schema_version", "0.1.0"
275
+ ),
273
276
  }
274
277
  return self.validate_and_transform(
275
278
  product_data_dict=data_dict,
@@ -18,7 +18,7 @@ from simtools.utils import names
18
18
  _logger = logging.getLogger(__name__)
19
19
 
20
20
 
21
- def get_get_model_parameter_schema_files(schema_directory=MODEL_PARAMETER_SCHEMA_PATH):
21
+ def get_model_parameter_schema_files(schema_directory=MODEL_PARAMETER_SCHEMA_PATH):
22
22
  """
23
23
  Return list of parameters and schema files located in schema file directory.
24
24
 
@@ -35,7 +35,8 @@ def get_get_model_parameter_schema_files(schema_directory=MODEL_PARAMETER_SCHEMA
35
35
  raise FileNotFoundError(f"No schema files found in {schema_directory}")
36
36
  parameters = []
37
37
  for schema_file in schema_files:
38
- schema_dict = gen.collect_data_from_file(file_name=schema_file)
38
+ # reading parameter 'name' only - first document in schema file should be ok
39
+ schema_dict = gen.collect_data_from_file(file_name=schema_file, yaml_document=0)
39
40
  parameters.append(schema_dict.get("name"))
40
41
  return parameters, schema_files
41
42
 
@@ -182,6 +183,15 @@ def load_schema(schema_file=None, schema_version=None):
182
183
  return schema
183
184
 
184
185
 
186
+ def _get_array_element_list():
187
+ """Build complete list of array elements including design types."""
188
+ elements = set(names.array_elements().keys())
189
+ for array_element in names.array_elements():
190
+ for design_type in names.array_element_design_types(array_element):
191
+ elements.add(f"{array_element}-{design_type}")
192
+ return sorted(elements)
193
+
194
+
185
195
  def _add_array_elements(key, schema):
186
196
  """
187
197
  Add list of array elements to schema.
@@ -200,22 +210,23 @@ def _add_array_elements(key, schema):
200
210
  -------
201
211
  dict
202
212
  Schema dictionary with added array elements.
203
-
204
213
  """
205
- _list_of_array_elements = sorted(names.array_elements().keys())
206
-
207
- def recursive_search(sub_schema, key):
208
- if key in sub_schema:
209
- if "enum" in sub_schema[key] and isinstance(sub_schema[key]["enum"], list):
210
- sub_schema[key]["enum"] = list(
211
- set(sub_schema[key]["enum"] + _list_of_array_elements)
212
- )
213
- else:
214
- sub_schema[key]["enum"] = _list_of_array_elements
214
+ array_elements = _get_array_element_list()
215
+
216
+ def update_enum(sub_schema):
217
+ if "enum" in sub_schema and isinstance(sub_schema["enum"], list):
218
+ sub_schema["enum"] = list(set(sub_schema["enum"] + array_elements))
215
219
  else:
216
- for _, v in sub_schema.items():
217
- if isinstance(v, dict):
218
- recursive_search(v, key)
220
+ sub_schema["enum"] = array_elements
221
+
222
+ def recursive_search(sub_schema, target_key):
223
+ if target_key in sub_schema:
224
+ update_enum(sub_schema[target_key])
225
+ return
226
+
227
+ for v in sub_schema.values():
228
+ if isinstance(v, dict):
229
+ recursive_search(v, target_key)
219
230
 
220
231
  recursive_search(schema, key)
221
232
  return schema
@@ -177,7 +177,11 @@ class DataValidator:
177
177
  if is_model_parameter:
178
178
  self._prepare_model_parameter()
179
179
 
180
- self._data_description = self._read_validation_schema(self.schema_file_name)
180
+ self._data_description = self._read_validation_schema(
181
+ self.schema_file_name,
182
+ # use 0.1.0 as default; this corresponds to the first definition of the schema
183
+ self.data_dict.get("model_parameter_schema_version", "0.1.0"),
184
+ )
181
185
 
182
186
  value_as_list, unit_as_list = self._get_value_and_units_as_lists()
183
187
 
@@ -701,14 +705,20 @@ class DataValidator:
701
705
 
702
706
  return False
703
707
 
704
- def _read_validation_schema(self, schema_file):
708
+ def _read_validation_schema(self, schema_file, schema_version=None):
705
709
  """
706
710
  Read validation schema from file.
707
711
 
712
+ The schema file can be a yaml file with several documents, each document
713
+ describing a different schema version. Returns first document if no
714
+ schema version is requested.
715
+
708
716
  Parameters
709
717
  ----------
710
718
  schema_file: Path
711
719
  Schema file describing input data.
720
+ schema_version: str
721
+ Version of the schema to be used.
712
722
 
713
723
  Returns
714
724
  -------
@@ -719,11 +729,21 @@ class DataValidator:
719
729
  ------
720
730
  KeyError
721
731
  if 'data' can not be read from dict in schema file
722
- """
723
- try:
724
- return gen.collect_data_from_file(file_name=schema_file)["data"]
725
- except KeyError as exc:
726
- raise KeyError(f"Error reading validation schema from {schema_file}") from exc
732
+ ValueError
733
+ if schema version is not found in schema file
734
+ """
735
+ schema_data = gen.collect_data_from_file(file_name=schema_file)
736
+ entries = schema_data if isinstance(schema_data, list) else [schema_data]
737
+
738
+ for entry in entries:
739
+ if not schema_version or entry.get("version") == schema_version:
740
+ try:
741
+ return entry["data"]
742
+ except KeyError as exc:
743
+ raise KeyError(f"Error reading validation schema from {schema_file}") from exc
744
+ raise ValueError(
745
+ f"Schema version {schema_version} not found in schema file {schema_file}. "
746
+ )
727
747
 
728
748
  def _get_data_description(self, column_name=None, status_test=False):
729
749
  """
simtools/db/db_handler.py CHANGED
@@ -122,9 +122,11 @@ class DatabaseHandler:
122
122
  port=self.mongo_db_config["db_api_port"],
123
123
  username=self.mongo_db_config["db_api_user"],
124
124
  password=self.mongo_db_config["db_api_pw"],
125
- authSource=self.mongo_db_config.get("db_api_authentication_database")
126
- if self.mongo_db_config.get("db_api_authentication_database")
127
- else "admin",
125
+ authSource=(
126
+ self.mongo_db_config.get("db_api_authentication_database")
127
+ if self.mongo_db_config.get("db_api_authentication_database")
128
+ else "admin"
129
+ ),
128
130
  directConnection=direct_connection,
129
131
  ssl=not direct_connection,
130
132
  tlsallowinvalidhostnames=True,
@@ -211,6 +213,10 @@ class DatabaseHandler:
211
213
  """
212
214
  collection_name = names.get_collection_name_from_parameter_name(parameter)
213
215
  if model_version:
216
+ if isinstance(model_version, list):
217
+ raise ValueError(
218
+ "Only one model version can be passed to get_model_parameter, not a list."
219
+ )
214
220
  production_table = self._read_production_table_from_mongo_db(
215
221
  collection_name, model_version
216
222
  )
@@ -678,7 +684,7 @@ class DatabaseHandler:
678
684
  model_version=model_version,
679
685
  collection="configuration_corsika",
680
686
  )
681
- if simulation_software == "simtel":
687
+ if simulation_software == "sim_telarray":
682
688
  return (
683
689
  self.get_model_parameters(
684
690
  site,
@@ -642,6 +642,7 @@ class ArrayLayout:
642
642
  "type": "float64",
643
643
  "file": False,
644
644
  "meta_parameter": False,
645
+ "model_parameter_schema_version": "0.1.0",
645
646
  }
646
647
 
647
648
  def get_number_of_telescopes(self):
@@ -36,6 +36,8 @@ class ArrayModel:
36
36
  array_elements: Union[str, Path, List[str]], optional
37
37
  Array element definitions (list of array element or path to file with
38
38
  the array element positions).
39
+ sim_telarray_seeds : dict, optional
40
+ Dictionary with configuration for sim_telarray random instrument setup.
39
41
  """
40
42
 
41
43
  def __init__(
@@ -46,6 +48,7 @@ class ArrayModel:
46
48
  site: str | None = None,
47
49
  layout_name: str | None = None,
48
50
  array_elements: str | Path | list[str] | None = None,
51
+ sim_telarray_seeds: dict | None = None,
49
52
  ):
50
53
  """Initialize ArrayModel."""
51
54
  self._logger = logging.getLogger(__name__)
@@ -65,6 +68,7 @@ class ArrayModel:
65
68
 
66
69
  self._telescope_model_files_exported = False
67
70
  self._array_model_file_exported = False
71
+ self.sim_telarray_seeds = sim_telarray_seeds
68
72
 
69
73
  def _initialize(self, site: str, array_elements_config: str | Path | list[str]):
70
74
  """
@@ -116,6 +120,26 @@ class ArrayModel:
116
120
  telescope_model = self._build_telescope_models(site_model, array_elements)
117
121
  return array_elements, site_model, telescope_model
118
122
 
123
+ @property
124
+ def config_file_path(self) -> Path:
125
+ """
126
+ Return the path of the array config file for sim_telarray.
127
+
128
+ Returns
129
+ -------
130
+ Path
131
+ Path of the exported config file for sim_telarray.
132
+ """
133
+ if self._config_file_path is None:
134
+ config_file_name = names.simtel_config_file_name(
135
+ array_name=self.layout_name,
136
+ site=self.site_model.site,
137
+ model_version=self.model_version,
138
+ label=self.label,
139
+ )
140
+ self._config_file_path = self.get_config_directory().joinpath(config_file_name)
141
+ return self._config_file_path
142
+
119
143
  @property
120
144
  def number_of_telescopes(self) -> int:
121
145
  """
@@ -140,6 +164,34 @@ class ArrayModel:
140
164
  """
141
165
  return self.site_model.site
142
166
 
167
+ @property
168
+ def model_version(self):
169
+ """Model version."""
170
+ return self._model_version
171
+
172
+ @model_version.setter
173
+ def model_version(self, model_version):
174
+ """
175
+ Set model version.
176
+
177
+ Parameters
178
+ ----------
179
+ _model_version: str or list
180
+ Model version (e.g., "6.0.0").
181
+ If a list is passed, it must contain exactly one element,
182
+ and only that element will be used.
183
+
184
+ Raises
185
+ ------
186
+ ValueError
187
+ If more than one model version is passed.
188
+ """
189
+ if isinstance(model_version, list):
190
+ raise ValueError(
191
+ f"Only one model version can be passed to {self.__class__.__name__}, not a list."
192
+ )
193
+ self._model_version = model_version
194
+
143
195
  def _build_telescope_models(self, site_model: SiteModel, array_elements: dict) -> dict:
144
196
  """
145
197
  Build the the telescope models for all telescopes of this array.
@@ -187,7 +239,7 @@ class ArrayModel:
187
239
  )
188
240
  if name not in exported_models:
189
241
  self._logger.debug(f"Exporting configuration file for telescope {name}")
190
- tel_model.export_config_file()
242
+ tel_model.write_sim_telarray_config_file()
191
243
  exported_models.append(name)
192
244
  else:
193
245
  self._logger.debug(
@@ -196,19 +248,11 @@ class ArrayModel:
196
248
 
197
249
  self._telescope_model_files_exported = True
198
250
 
199
- def export_simtel_array_config_file(self):
251
+ def export_sim_telarray_config_file(self):
200
252
  """Export sim_telarray configuration file for the array into the model directory."""
201
- # Setting file name and the location
202
- config_file_name = names.simtel_config_file_name(
203
- array_name=self.layout_name,
204
- site=self.site_model.site,
205
- model_version=self.model_version,
206
- label=self.label,
207
- )
208
- self._config_file_path = self.get_config_directory().joinpath(config_file_name)
253
+ self.site_model.export_model_files()
209
254
 
210
- # Writing parameters to the file
211
- self._logger.info(f"Writing array configuration file into {self._config_file_path}")
255
+ self._logger.info(f"Writing array configuration file into {self.config_file_path}")
212
256
  simtel_writer = SimtelConfigWriter(
213
257
  site=self.site_model.site,
214
258
  layout_name=self.layout_name,
@@ -216,9 +260,10 @@ class ArrayModel:
216
260
  label=self.label,
217
261
  )
218
262
  simtel_writer.write_array_config_file(
219
- config_file_path=self._config_file_path,
263
+ config_file_path=self.config_file_path,
220
264
  telescope_model=self.telescope_model,
221
265
  site_model=self.site_model,
266
+ sim_telarray_seeds=self.sim_telarray_seeds,
222
267
  )
223
268
  self._array_model_file_exported = True
224
269
 
@@ -231,21 +276,7 @@ class ArrayModel:
231
276
  if not self._telescope_model_files_exported:
232
277
  self.export_simtel_telescope_config_files()
233
278
  if not self._array_model_file_exported:
234
- self.export_simtel_array_config_file()
235
-
236
- def get_config_file(self) -> Path:
237
- """
238
- Return the path of the array config file for sim_telarray.
239
-
240
- A new config file is produced if the file is not updated.
241
-
242
- Returns
243
- -------
244
- Path
245
- Path of the exported config file for sim_telarray.
246
- """
247
- self.export_all_simtel_config_files()
248
- return self._config_file_path
279
+ self.export_sim_telarray_config_file()
249
280
 
250
281
  def get_config_directory(self) -> Path:
251
282
  """
@@ -257,7 +288,9 @@ class ArrayModel:
257
288
  Path of the config directory path for sim_telarray.
258
289
  """
259
290
  if self._config_file_directory is None:
260
- self._config_file_directory = self.io_handler.get_output_directory(self.label, "model")
291
+ self._config_file_directory = self.io_handler.get_output_directory(
292
+ self.label, f"model/{self.model_version}"
293
+ )
261
294
  return self._config_file_directory
262
295
 
263
296
  def _load_array_element_positions_from_file(
@@ -331,6 +364,7 @@ class ArrayModel:
331
364
  "type": "float64",
332
365
  "file": False,
333
366
  "meta_parameter": False,
367
+ "model_parameter_schema_version": "0.1.0",
334
368
  }
335
369
 
336
370
  def _get_array_elements_from_list(self, array_elements_list: list[str]) -> dict: