gammasimtools 0.22.0__py3-none-any.whl → 0.24.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 (128) hide show
  1. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/METADATA +2 -1
  2. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/RECORD +128 -125
  3. simtools/_version.py +2 -2
  4. simtools/application_control.py +118 -0
  5. simtools/applications/calculate_incident_angles.py +17 -22
  6. simtools/applications/convert_all_model_parameters_from_simtel.py +28 -43
  7. simtools/applications/convert_geo_coordinates_of_array_elements.py +26 -45
  8. simtools/applications/convert_model_parameter_from_simtel.py +21 -41
  9. simtools/applications/db_add_file_to_db.py +13 -14
  10. simtools/applications/db_add_simulation_model_from_repository_to_db.py +20 -33
  11. simtools/applications/db_add_value_from_json_to_db.py +29 -24
  12. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +20 -35
  13. simtools/applications/db_generate_compound_indexes.py +11 -13
  14. simtools/applications/db_get_array_layouts_from_db.py +20 -40
  15. simtools/applications/db_get_file_from_db.py +15 -17
  16. simtools/applications/db_get_parameter_from_db.py +33 -35
  17. simtools/applications/db_inspect_databases.py +13 -12
  18. simtools/applications/db_upload_model_repository.py +13 -31
  19. simtools/applications/derive_ctao_array_layouts.py +16 -21
  20. simtools/applications/derive_mirror_rnda.py +9 -14
  21. simtools/applications/derive_photon_electron_spectrum.py +7 -10
  22. simtools/applications/derive_psf_parameters.py +13 -20
  23. simtools/applications/derive_trigger_rates.py +6 -9
  24. simtools/applications/docs_produce_array_element_report.py +22 -23
  25. simtools/applications/docs_produce_calibration_reports.py +26 -24
  26. simtools/applications/docs_produce_model_parameter_reports.py +15 -22
  27. simtools/applications/docs_produce_simulation_configuration_report.py +21 -22
  28. simtools/applications/generate_array_config.py +14 -33
  29. simtools/applications/generate_corsika_histograms.py +22 -43
  30. simtools/applications/generate_default_metadata.py +15 -36
  31. simtools/applications/generate_regular_arrays.py +11 -15
  32. simtools/applications/generate_simtel_event_data.py +23 -33
  33. simtools/applications/maintain_simulation_model_add_production.py +20 -37
  34. simtools/applications/maintain_simulation_model_compare_productions.py +10 -12
  35. simtools/applications/maintain_simulation_model_verify_production_tables.py +8 -11
  36. simtools/applications/merge_tables.py +14 -23
  37. simtools/applications/plot_array_layout.py +77 -54
  38. simtools/applications/plot_simtel_events.py +11 -13
  39. simtools/applications/plot_tabular_data.py +17 -38
  40. simtools/applications/plot_tabular_data_for_model_parameter.py +16 -23
  41. simtools/applications/print_version.py +14 -42
  42. simtools/applications/production_derive_corsika_limits.py +5 -9
  43. simtools/applications/production_derive_statistics.py +12 -25
  44. simtools/applications/production_generate_grid.py +20 -48
  45. simtools/applications/production_merge_corsika_limits.py +17 -21
  46. simtools/applications/run_application.py +12 -32
  47. simtools/applications/simulate_flasher.py +21 -25
  48. simtools/applications/simulate_illuminator.py +7 -14
  49. simtools/applications/simulate_pedestals.py +13 -13
  50. simtools/applications/simulate_prod.py +21 -33
  51. simtools/applications/simulate_prod_htcondor_generator.py +11 -25
  52. simtools/applications/submit_array_layouts.py +16 -19
  53. simtools/applications/submit_data_from_external.py +18 -34
  54. simtools/applications/submit_model_parameter_from_external.py +27 -40
  55. simtools/applications/validate_camera_efficiency.py +23 -21
  56. simtools/applications/validate_camera_fov.py +21 -26
  57. simtools/applications/validate_cumulative_psf.py +27 -35
  58. simtools/applications/validate_file_using_schema.py +15 -33
  59. simtools/applications/validate_optics.py +27 -33
  60. simtools/camera/camera_efficiency.py +0 -2
  61. simtools/configuration/commandline_parser.py +39 -13
  62. simtools/configuration/configurator.py +1 -6
  63. simtools/corsika/corsika_config.py +2 -9
  64. simtools/data_model/data_reader.py +0 -2
  65. simtools/data_model/metadata_collector.py +0 -2
  66. simtools/data_model/model_data_writer.py +1 -3
  67. simtools/data_model/schema.py +36 -34
  68. simtools/data_model/validate_data.py +0 -2
  69. simtools/db/db_handler.py +61 -296
  70. simtools/db/db_model_upload.py +1 -1
  71. simtools/db/mongo_db.py +535 -0
  72. simtools/dependencies.py +33 -8
  73. simtools/io/hdf5_handler.py +0 -5
  74. simtools/io/legacy_data_handler.py +0 -5
  75. simtools/job_execution/job_manager.py +0 -3
  76. simtools/layout/array_layout.py +7 -9
  77. simtools/layout/array_layout_utils.py +3 -3
  78. simtools/layout/telescope_position.py +0 -2
  79. simtools/model/array_model.py +38 -71
  80. simtools/model/calibration_model.py +12 -11
  81. simtools/model/camera.py +0 -2
  82. simtools/model/mirrors.py +0 -2
  83. simtools/model/model_parameter.py +200 -140
  84. simtools/model/model_repository.py +159 -35
  85. simtools/model/model_utils.py +3 -8
  86. simtools/model/site_model.py +59 -29
  87. simtools/model/telescope_model.py +21 -15
  88. simtools/production_configuration/calculate_statistical_uncertainties_grid_point.py +0 -2
  89. simtools/production_configuration/derive_production_statistics.py +0 -2
  90. simtools/production_configuration/interpolation_handler.py +0 -2
  91. simtools/ray_tracing/mirror_panel_psf.py +4 -4
  92. simtools/ray_tracing/psf_analysis.py +0 -2
  93. simtools/ray_tracing/psf_parameter_optimisation.py +1 -1
  94. simtools/ray_tracing/ray_tracing.py +0 -2
  95. simtools/reporting/docs_auto_report_generator.py +109 -1
  96. simtools/reporting/docs_read_parameters.py +4 -9
  97. simtools/runners/corsika_runner.py +0 -2
  98. simtools/runners/corsika_simtel_runner.py +0 -2
  99. simtools/runners/simtel_runner.py +0 -2
  100. simtools/schemas/model_parameters/transit_time_random.schema.yml +29 -0
  101. simtools/schemas/simulation_models_info.schema.yml +2 -1
  102. simtools/simtel/simtel_config_reader.py +0 -2
  103. simtools/simtel/simtel_config_writer.py +128 -33
  104. simtools/simtel/simtel_io_metadata.py +3 -3
  105. simtools/simtel/simulator_array.py +9 -21
  106. simtools/simtel/simulator_camera_efficiency.py +0 -2
  107. simtools/simtel/simulator_light_emission.py +1 -3
  108. simtools/simtel/simulator_ray_tracing.py +0 -2
  109. simtools/simulator.py +2 -6
  110. simtools/testing/assertions.py +52 -8
  111. simtools/testing/configuration.py +17 -4
  112. simtools/testing/validate_output.py +4 -8
  113. simtools/utils/general.py +5 -13
  114. simtools/utils/geometry.py +0 -5
  115. simtools/utils/names.py +1 -13
  116. simtools/utils/value_conversion.py +10 -5
  117. simtools/version.py +85 -0
  118. simtools/visualization/plot_array_layout.py +129 -23
  119. simtools/visualization/plot_incident_angles.py +0 -2
  120. simtools/visualization/plot_pixels.py +1 -1
  121. simtools/visualization/plot_psf.py +1 -1
  122. simtools/visualization/plot_simtel_events.py +0 -11
  123. simtools/visualization/plot_tables.py +1 -1
  124. simtools/visualization/visualize.py +0 -12
  125. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/WHEEL +0 -0
  126. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/entry_points.txt +0 -0
  127. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/licenses/LICENSE +0 -0
  128. {gammasimtools-0.22.0.dist-info → gammasimtools-0.24.0.dist-info}/top_level.txt +0 -0
@@ -1,19 +1,18 @@
1
1
  #!/usr/bin/python3
2
- """Base class for simulation model parameters."""
2
+ """Base class for simulation model parameters (e.g., for SiteModel or TelescopeModel)."""
3
3
 
4
4
  import logging
5
5
  import shutil
6
- from copy import copy
6
+ from copy import copy, deepcopy
7
7
 
8
8
  import astropy.units as u
9
9
 
10
10
  import simtools.utils.general as gen
11
+ from simtools.data_model import schema
11
12
  from simtools.db import db_handler
12
13
  from simtools.io import ascii_handler, io_handler
13
14
  from simtools.simtel.simtel_config_writer import SimtelConfigWriter
14
- from simtools.utils import names
15
-
16
- __all__ = ["InvalidModelParameterError", "ModelParameter"]
15
+ from simtools.utils import names, value_conversion
17
16
 
18
17
 
19
18
  class InvalidModelParameterError(Exception):
@@ -29,8 +28,8 @@ class ModelParameter:
29
28
 
30
29
  Parameters
31
30
  ----------
32
- db: DatabaseHandler
33
- Database handler.
31
+ db_config:
32
+ Database configuration dictionary.
34
33
  model_version: str
35
34
  Version of the model (ex. 5.0.0).
36
35
  site: str
@@ -40,35 +39,37 @@ class ModelParameter:
40
39
  collection: str
41
40
  instrument class (e.g. telescopes, calibration_devices)
42
41
  as stored under collection in the DB.
43
- mongo_db_config: dict
44
- MongoDB configuration.
45
42
  label: str
43
+ Instance label. Used for output file naming.
44
+ overwrite_model_parameters: str, optional
45
+ File name to overwrite model parameters from DB with provided values.
46
46
  Instance label. Important for output file naming.
47
-
47
+ ignore_software_version: bool
48
+ If True, ignore software version checks for deprecated parameters.
49
+ Useful for documentation generation.
48
50
  """
49
51
 
50
52
  def __init__(
51
53
  self,
52
- mongo_db_config,
54
+ db_config,
53
55
  model_version,
54
56
  site=None,
55
57
  array_element_name=None,
56
58
  collection="telescopes",
57
- db=None,
58
59
  label=None,
60
+ overwrite_model_parameters=None,
61
+ ignore_software_version=False,
59
62
  ):
60
63
  self._logger = logging.getLogger(__name__)
61
- self._extra_label = None
62
64
  self.io_handler = io_handler.IOHandler()
63
- self.db = (
64
- db if db is not None else db_handler.DatabaseHandler(mongo_db_config=mongo_db_config)
65
- )
65
+ self.db = db_handler.DatabaseHandler(db_config=db_config)
66
66
 
67
- self._parameters = {}
67
+ self.parameters = {}
68
68
  self._simulation_config_parameters = {sw: {} for sw in names.simulation_software()}
69
69
  self.collection = collection
70
70
  self.label = label
71
71
  self.model_version = model_version
72
+ self.ignore_software_version = ignore_software_version
72
73
  self.site = names.validate_site_name(site) if site is not None else None
73
74
  self.name = (
74
75
  names.validate_array_element_name(array_element_name)
@@ -80,56 +81,17 @@ class ModelParameter:
80
81
  )
81
82
  self._config_file_directory = None
82
83
  self._config_file_path = None
83
- self._load_parameters_from_db()
84
-
85
- self.simtel_config_writer = None
84
+ self.overwrite_model_parameters = overwrite_model_parameters
86
85
  self._added_parameter_files = None
87
- self._is_config_file_up_to_date = False
88
86
  self._is_exported_model_files_up_to_date = False
89
87
 
90
- @property
91
- def model_version(self):
92
- """Model version."""
93
- return self._model_version
94
-
95
- @model_version.setter
96
- def model_version(self, model_version):
97
- """
98
- Set model version.
99
-
100
- Parameters
101
- ----------
102
- model_version: str or list
103
- Model version (e.g., "6.0.0").
104
- If a list is passed, it must contain exactly one element,
105
- and only that element will be used.
106
-
107
- Raises
108
- ------
109
- ValueError
110
- If more than one model version is passed.
111
- """
112
- if isinstance(model_version, list):
113
- raise ValueError(
114
- f"Only one model version can be passed to {self.__class__.__name__}, not a list."
115
- )
116
- self._model_version = model_version
117
-
118
- @property
119
- def parameters(self):
120
- """
121
- Model parameters dictionary.
88
+ self._load_parameters_from_db()
122
89
 
123
- Returns
124
- -------
125
- dict
126
- Dictionary containing all model parameters
127
- """
128
- return self._parameters
90
+ self.simtel_config_writer = None
129
91
 
130
92
  def _get_parameter_dict(self, par_name):
131
93
  """
132
- Get model parameter dictionary as stored in the DB.
94
+ Get model parameter dictionary for a specific parameter as stored in the DB.
133
95
 
134
96
  No conversion to values are applied for the use in simtools
135
97
  (e.g., no conversion from the string representation of lists
@@ -157,7 +119,7 @@ class ModelParameter:
157
119
  f"Parameter {par_name} was not found in the model {self.name}, {self.site}."
158
120
  ) from e
159
121
 
160
- def get_parameter_value(self, par_name, parameter_dict=None):
122
+ def get_parameter_value(self, par_name):
161
123
  """
162
124
  Get the value of a model parameter.
163
125
 
@@ -168,9 +130,6 @@ class ModelParameter:
168
130
  ----------
169
131
  par_name: str
170
132
  Name of the parameter.
171
- parameter_dict: dict
172
- Dictionary with complete DB entry for the given parameter
173
- (including the 'value', 'units' fields).
174
133
 
175
134
  Returns
176
135
  -------
@@ -178,25 +137,28 @@ class ModelParameter:
178
137
 
179
138
  Raises
180
139
  ------
181
- KeyError
140
+ InvalidModelParameterError
182
141
  If par_name does not match any parameter in this model.
183
142
  """
184
- parameter_dict = parameter_dict if parameter_dict else self._get_parameter_dict(par_name)
185
143
  try:
186
- _parameter = parameter_dict["value"]
144
+ value = self._get_parameter_dict(par_name)["value"]
187
145
  except KeyError as exc:
188
- self._logger.error(f"Parameter {par_name} does not have a value")
189
- raise exc
190
- if isinstance(_parameter, str):
191
- _is_float = False
146
+ raise InvalidModelParameterError(f"Parameter {par_name} does not have a value") from exc
147
+
148
+ if isinstance(value, str):
192
149
  try:
193
150
  _is_float = self.get_parameter_type(par_name).startswith("float")
194
- except (InvalidModelParameterError, TypeError): # float - in case we don't know
151
+ except (
152
+ InvalidModelParameterError,
153
+ TypeError,
154
+ AttributeError,
155
+ ): # float - in case we don't know
195
156
  _is_float = True
196
- _parameter = gen.convert_string_to_list(_parameter, is_float=_is_float)
197
- _parameter = _parameter if len(_parameter) > 1 else _parameter[0]
157
+ value = gen.convert_string_to_list(value, is_float=_is_float)
158
+ if len(value) == 1:
159
+ value = value[0]
198
160
 
199
- return _parameter
161
+ return value
200
162
 
201
163
  def get_parameter_value_with_unit(self, par_name):
202
164
  """
@@ -214,7 +176,7 @@ class ModelParameter:
214
176
 
215
177
  """
216
178
  _parameter = self._get_parameter_dict(par_name)
217
- _value = self.get_parameter_value(par_name, _parameter)
179
+ _value = self.get_parameter_value(par_name)
218
180
 
219
181
  try:
220
182
  if isinstance(_parameter.get("unit"), str):
@@ -226,13 +188,14 @@ class ModelParameter:
226
188
  if (isinstance(_value, (int | float))) or (len(_value) > len(_unit)):
227
189
  return _value * u.Unit(_unit[0])
228
190
 
229
- # entries with 'null' units should be returned as dimensionless
230
- _astropy_units = [
231
- u.Unit(item) if item != "null" else u.dimensionless_unscaled for item in _unit
191
+ # Create list of quantities for multiple values with different units
192
+ return [
193
+ value_conversion.get_value_as_quantity(
194
+ _value[i], _unit[i] if i < len(_unit) else None
195
+ )
196
+ for i in range(len(_value))
232
197
  ]
233
198
 
234
- return [_value[i] * _astropy_units[i] for i in range(len(_value))]
235
-
236
199
  except (KeyError, TypeError, AttributeError) as exc:
237
200
  self._logger.debug(
238
201
  f"{exc} encountered for parameter {par_name}, returning only value without units."
@@ -250,13 +213,11 @@ class ModelParameter:
250
213
 
251
214
  Returns
252
215
  -------
253
- str or None
254
- type of the parameter (None if no type is defined)
255
-
216
+ str
217
+ type of the parameter
256
218
  """
257
- parameter_dict = self._get_parameter_dict(par_name)
258
219
  try:
259
- return parameter_dict["type"]
220
+ return self._get_parameter_dict(par_name)["type"]
260
221
  except KeyError:
261
222
  self._logger.debug(f"Parameter {par_name} does not have a type.")
262
223
  return None
@@ -276,9 +237,8 @@ class ModelParameter:
276
237
  True if file flag is set.
277
238
 
278
239
  """
279
- parameter_dict = self._get_parameter_dict(par_name)
280
240
  try:
281
- return parameter_dict["file"]
241
+ return self._get_parameter_dict(par_name)["file"]
282
242
  except KeyError:
283
243
  self._logger.debug(f"Parameter {par_name} does not have a file associated with it.")
284
244
  return False
@@ -299,11 +259,6 @@ class ModelParameter:
299
259
  """
300
260
  return self._get_parameter_dict(par_name)["parameter_version"]
301
261
 
302
- def print_parameters(self):
303
- """Print parameters and their values for debugging purposes."""
304
- for par in self.parameters:
305
- print(f"{par} = {self.get_parameter_value(par)}")
306
-
307
262
  def _set_config_file_directory_and_name(self):
308
263
  """Set and create the directory and the name of the config file."""
309
264
  if self.name is None and self.site is None:
@@ -319,7 +274,6 @@ class ModelParameter:
319
274
  self.model_version,
320
275
  telescope_model_name=self.name,
321
276
  label=self.label,
322
- extra_label=self._extra_label,
323
277
  )
324
278
  self._config_file_path = self.config_file_directory.joinpath(config_file_name)
325
279
 
@@ -357,34 +311,71 @@ class ModelParameter:
357
311
  pass
358
312
 
359
313
  def _load_parameters_from_db(self):
360
- """Read parameters from DB and store them in _parameters."""
314
+ """
315
+ Read parameters from Database.
316
+
317
+ This is the main function to load the model parameters from the DB.
318
+ """
361
319
  if self.db is None:
362
320
  return
363
321
 
364
322
  if self.name or self.site:
365
- self._parameters = self.db.get_model_parameters(
366
- self.site, self.name, self.collection, self.model_version
323
+ # copy parameters dict, is it may be modified later on
324
+ self.parameters = deepcopy(
325
+ self.db.get_model_parameters(
326
+ self.site, self.name, self.collection, self.model_version
327
+ )
367
328
  )
329
+ if self.overwrite_model_parameters:
330
+ self.overwrite_parameters_from_file(self.overwrite_model_parameters)
331
+ self._check_model_parameter_software_versions(self.parameters.keys())
368
332
 
369
333
  self._load_simulation_software_parameter()
334
+ for software_name, parameters in self._simulation_config_parameters.items():
335
+ self._check_model_parameter_software_versions(
336
+ parameters.keys(), software_name=software_name
337
+ )
370
338
 
371
- @property
372
- def extra_label(self):
373
- """Return the extra label if defined, if not return ''."""
374
- return self._extra_label if self._extra_label is not None else ""
339
+ def _check_model_parameter_software_versions(self, parameter_list, software_name=None):
340
+ """
341
+ Ensure that model parameters are compatible with the installed software versions.
342
+
343
+ Compares software versions listed in schema files with the installed software versions
344
+ (e.g., sim_telarray, CORSIKA).
345
+
346
+ Parameters
347
+ ----------
348
+ parameter_list: list
349
+ List containing model parameter names.
350
+ software_name: str
351
+ Name of the software for which the parameters are checked.
352
+ """
353
+ for par_name in parameter_list:
354
+ if par_name in (parameter_schema := names.model_parameters()):
355
+ schema.validate_deprecation_and_version(
356
+ data=parameter_schema[par_name],
357
+ software_name=software_name,
358
+ ignore_software_version=self.ignore_software_version,
359
+ )
375
360
 
376
- def change_parameter(self, par_name, value):
361
+ def overwrite_model_parameter(self, par_name, value, parameter_version=None):
377
362
  """
378
- Change the value of an existing parameter.
363
+ Overwrite the parameter dictionary for a specific parameter in the model.
364
+
365
+ This function does not modify the DB, it affects only the current instance of
366
+ the model parameter dictionary.
379
367
 
380
- This function does not modify the DB, it affects only the current instance.
368
+ If the parameter version is given only, the parameter dictionary is updated
369
+ from the database for the given version.
381
370
 
382
371
  Parameters
383
372
  ----------
384
373
  par_name: str
385
374
  Name of the parameter.
386
375
  value:
387
- Value of the parameter.
376
+ New value for the parameter.
377
+ parameter_version: str, optional
378
+ New version for the parameter.
388
379
 
389
380
  Raises
390
381
  ------
@@ -394,31 +385,48 @@ class ModelParameter:
394
385
  if par_name not in self.parameters:
395
386
  raise InvalidModelParameterError(f"Parameter {par_name} not in the model")
396
387
 
397
- value = gen.convert_string_to_list(value) if isinstance(value, str) else value
398
-
399
- par_type = self.get_parameter_type(par_name)
400
- if not gen.validate_data_type(
401
- reference_dtype=par_type,
402
- value=value,
403
- dtype=None,
404
- allow_subtypes=True,
405
- ):
406
- raise ValueError(f"Could not cast {value} of type {type(value)} to {par_type}.")
388
+ if value is None and parameter_version:
389
+ _para_dict = self.db.get_model_parameter(
390
+ parameter=par_name,
391
+ site=self.site,
392
+ array_element_name=self.name,
393
+ parameter_version=parameter_version,
394
+ )
395
+ if _para_dict:
396
+ self.parameters[par_name] = _para_dict.get(par_name)
397
+ self._logger.debug(
398
+ f"Changing parameter {par_name} to version {parameter_version} with value "
399
+ f"{self.parameters[par_name]['value']}"
400
+ )
401
+ else:
402
+ value = gen.convert_string_to_list(value) if isinstance(value, str) else value
403
+
404
+ par_type = self.get_parameter_type(par_name)
405
+ if not gen.validate_data_type(
406
+ reference_dtype=par_type,
407
+ value=value,
408
+ dtype=None,
409
+ allow_subtypes=True,
410
+ ):
411
+ raise ValueError(f"Could not cast {value} of type {type(value)} to {par_type}.")
407
412
 
408
- self._logger.debug(
409
- f"Changing parameter {par_name} from {self.get_parameter_value(par_name)} to {value}"
410
- )
411
- self.parameters[par_name]["value"] = value
413
+ self._logger.debug(
414
+ f"Changing parameter {par_name} from {self.get_parameter_value(par_name)} "
415
+ f"to {value}"
416
+ )
417
+ self.parameters[par_name]["value"] = value
418
+ if parameter_version:
419
+ self.parameters[par_name]["parameter_version"] = parameter_version
412
420
 
413
421
  # In case parameter is a file, the model files will be outdated
414
422
  if self.get_parameter_file_flag(par_name):
415
423
  self._is_exported_model_files_up_to_date = False
416
424
 
417
- self._is_config_file_up_to_date = False
418
-
419
- def change_multiple_parameters_from_file(self, file_name):
425
+ def overwrite_parameters_from_file(self, file_name):
420
426
  """
421
- Change values of multiple existing parameters in the model from a file.
427
+ Overwrite parameters from a file.
428
+
429
+ File is expected to follow the format described in 'simulation_models_info.schema.yml'.
422
430
 
423
431
  This function does not modify the DB, it affects only the current instance.
424
432
  This feature is intended for developers and lacks validation.
@@ -428,34 +436,85 @@ class ModelParameter:
428
436
  file_name: str
429
437
  File containing the parameters to be changed.
430
438
  """
431
- self._logger.warning(
432
- "Changing multiple parameters from file is a feature for developers."
433
- "Insufficient validation of parameters."
439
+ changes_data = schema.validate_dict_using_schema(
440
+ data=ascii_handler.collect_data_from_file(file_name=file_name),
441
+ schema_file="simulation_models_info.schema.yml",
442
+ ).get("changes", {})
443
+
444
+ key_for_changes = self._get_key_for_parameter_changes(self.site, self.name, changes_data)
445
+ self.overwrite_parameters(changes_data.get(key_for_changes, {}) if key_for_changes else {})
446
+
447
+ def _get_key_for_parameter_changes(self, site, array_element_name, changes_data):
448
+ """
449
+ Get the key for parameter changes based on site and array element name.
450
+
451
+ For array elements, the following cases are taken into account:
452
+
453
+ - array element name in changes_data: specific array element is returned
454
+ - design type in changes_data: specific design type is returned if array
455
+ element matches this design
456
+
457
+ Parameters
458
+ ----------
459
+ site: str
460
+ Site name.
461
+ array_element_name: str
462
+ Array element name.
463
+ changes_data: dict
464
+ Dictionary containing the changes data.
465
+
466
+ Returns
467
+ -------
468
+ str
469
+ Key for parameter changes.
470
+ """
471
+ if site and not array_element_name:
472
+ return f"OBS-{site}"
473
+
474
+ if array_element_name in changes_data:
475
+ return array_element_name
476
+
477
+ design_type = self.db.get_design_model(
478
+ model_version=self.model_version,
479
+ array_element_name=array_element_name,
480
+ collection=self.collection,
434
481
  )
435
- self._logger.debug(f"Changing parameters from file {file_name}")
436
- self.change_multiple_parameters(**ascii_handler.collect_data_from_file(file_name=file_name))
482
+ if design_type in changes_data:
483
+ return design_type
437
484
 
438
- def change_multiple_parameters(self, **kwargs):
485
+ return None
486
+
487
+ def overwrite_parameters(self, changes):
439
488
  """
440
489
  Change the value of multiple existing parameters in the model.
441
490
 
442
491
  This function does not modify the DB, it affects only the current instance.
443
492
 
493
+ Allows for two types of 'changes' dictionary:
494
+
495
+ - simple: '{parameter_name: new_value, ...}'
496
+ - model repository style:
497
+ '{parameter_name: {"value": new_value, "version": new_version}, ...}'
498
+
444
499
  Parameters
445
500
  ----------
446
- **kwargs
447
- Parameters should be passed as parameter_name=value.
501
+ changes: dict
502
+ Parameters to be changed.
503
+ """
504
+ for par_name, par_value in changes.items():
505
+ if par_name in self.parameters:
506
+ if isinstance(par_value, dict) and ("value" in par_value or "version" in par_value):
507
+ self.overwrite_model_parameter(
508
+ par_name, par_value.get("value"), par_value.get("version")
509
+ )
510
+ else:
511
+ self.overwrite_model_parameter(par_name, par_value)
448
512
 
513
+ def overwrite_model_file(self, par_name, file_path):
449
514
  """
450
- for par, value in kwargs.items():
451
- if par in self.parameters:
452
- self.change_parameter(par, value)
515
+ Overwrite the existing model file in the config file directory.
453
516
 
454
- self._is_config_file_up_to_date = False
455
-
456
- def export_parameter_file(self, par_name, file_path):
457
- """
458
- Export a file to the config file directory.
517
+ Keeps track of updated model file with '_added_parameter_files' attribute.
459
518
 
460
519
  Parameters
461
520
  ----------
@@ -470,7 +529,7 @@ class ModelParameter:
470
529
 
471
530
  def export_model_files(self, destination_path=None, update_if_necessary=False):
472
531
  """
473
- Export the model files into the config file directory.
532
+ Export model files from the database into the config file directory.
474
533
 
475
534
  Parameters
476
535
  ----------
@@ -550,6 +609,7 @@ class ModelParameter:
550
609
  self.simtel_config_writer = SimtelConfigWriter(
551
610
  site=self.site,
552
611
  telescope_model_name=self.name,
612
+ telescope_design_model=self.design_model,
553
613
  model_version=self.model_version,
554
614
  label=self.label,
555
615
  )