gammasimtools 0.10.0__py3-none-any.whl → 0.11.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 (84) hide show
  1. {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/METADATA +3 -1
  2. {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/RECORD +84 -77
  3. {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/entry_points.txt +4 -0
  5. simtools/_version.py +9 -4
  6. simtools/applications/convert_all_model_parameters_from_simtel.py +0 -1
  7. simtools/applications/convert_model_parameter_from_simtel.py +0 -1
  8. simtools/applications/db_add_file_to_db.py +0 -1
  9. simtools/applications/db_get_parameter_from_db.py +7 -28
  10. simtools/applications/derive_mirror_rnda.py +1 -2
  11. simtools/applications/derive_psf_parameters.py +1 -0
  12. simtools/applications/docs_produce_array_element_report.py +71 -0
  13. simtools/applications/docs_produce_model_parameter_reports.py +63 -0
  14. simtools/applications/generate_corsika_histograms.py +2 -2
  15. simtools/applications/generate_regular_arrays.py +4 -2
  16. simtools/applications/production_derive_limits.py +95 -0
  17. simtools/applications/production_generate_simulation_config.py +15 -29
  18. simtools/applications/production_scale_events.py +2 -7
  19. simtools/applications/run_application.py +165 -0
  20. simtools/applications/simulate_light_emission.py +0 -4
  21. simtools/applications/submit_model_parameter_from_external.py +11 -6
  22. simtools/applications/validate_file_using_schema.py +3 -3
  23. simtools/configuration/commandline_parser.py +29 -0
  24. simtools/configuration/configurator.py +8 -10
  25. simtools/corsika/corsika_config.py +11 -10
  26. simtools/corsika/corsika_histograms.py +4 -6
  27. simtools/corsika/corsika_histograms_visualize.py +2 -4
  28. simtools/data_model/metadata_collector.py +18 -9
  29. simtools/data_model/model_data_writer.py +67 -15
  30. simtools/data_model/schema.py +10 -3
  31. simtools/data_model/validate_data.py +70 -24
  32. simtools/db/db_handler.py +42 -12
  33. simtools/dependencies.py +112 -0
  34. simtools/layout/array_layout.py +5 -4
  35. simtools/model/model_parameter.py +35 -2
  36. simtools/production_configuration/calculate_statistical_errors_grid_point.py +5 -6
  37. simtools/production_configuration/event_scaler.py +3 -19
  38. simtools/production_configuration/generate_simulation_config.py +4 -12
  39. simtools/production_configuration/interpolation_handler.py +2 -5
  40. simtools/production_configuration/limits_calculation.py +202 -0
  41. simtools/reporting/docs_read_parameters.py +310 -0
  42. simtools/runners/corsika_simtel_runner.py +1 -3
  43. simtools/schemas/{integration_tests_config.metaschema.yml → application_workflow.metaschema.yml} +51 -27
  44. simtools/schemas/array_elements.yml +8 -0
  45. simtools/schemas/model_parameter.metaschema.yml +96 -0
  46. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +2 -1
  47. simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +1 -1
  48. simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +2 -0
  49. simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +2 -0
  50. simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +2 -0
  51. simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +2 -0
  52. simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +2 -0
  53. simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +2 -0
  54. simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +2 -0
  55. simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +2 -0
  56. simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +2 -0
  57. simtools/schemas/model_parameters/iobuf_maximum.schema.yml +1 -1
  58. simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +1 -1
  59. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +1 -1
  60. simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +1 -1
  61. simtools/schemas/model_parameters/min_photoelectrons.schema.yml +1 -1
  62. simtools/schemas/model_parameters/min_photons.schema.yml +1 -1
  63. simtools/schemas/model_parameters/random_generator.schema.yml +1 -1
  64. simtools/schemas/model_parameters/sampled_output.schema.yml +1 -1
  65. simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +1 -1
  66. simtools/schemas/model_parameters/store_photoelectrons.schema.yml +1 -1
  67. simtools/schemas/model_parameters/tailcut_scale.schema.yml +1 -1
  68. simtools/schemas/production_tables.schema.yml +1 -1
  69. simtools/simtel/simtel_config_reader.py +1 -2
  70. simtools/simtel/simtel_config_writer.py +1 -2
  71. simtools/simtel/simtel_io_histogram.py +0 -1
  72. simtools/simtel/simtel_io_histograms.py +2 -4
  73. simtools/simtel/simulator_camera_efficiency.py +1 -3
  74. simtools/simtel/simulator_light_emission.py +2 -5
  75. simtools/simtel/simulator_ray_tracing.py +1 -3
  76. simtools/testing/configuration.py +2 -1
  77. simtools/testing/validate_output.py +23 -13
  78. simtools/utils/general.py +12 -2
  79. simtools/utils/names.py +290 -152
  80. simtools/utils/value_conversion.py +17 -13
  81. simtools/version.py +2 -2
  82. simtools/visualization/legend_handlers.py +2 -0
  83. {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/LICENSE +0 -0
  84. {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/top_level.txt +0 -0
simtools/utils/names.py CHANGED
@@ -1,4 +1,16 @@
1
- """Validation of names."""
1
+ """Name utilities for array elements, sites, and model parameters.
2
+
3
+ Naming in simtools:
4
+
5
+ * 'site': South or North
6
+ * 'array element': e.g., LSTN-01, MSTN-01, ...
7
+ * 'array element type': e.g., LSTN, MSTN, ...
8
+ * 'array element ID': e.g., 01, 02, ...
9
+ * 'array element design type': e.g., design, test
10
+ * 'instrument class key': e.g., telescope, camera, structure
11
+ * 'db collection': e.g., telescopes, sites, calibration_devices
12
+
13
+ """
2
14
 
3
15
  import logging
4
16
  import re
@@ -15,7 +27,6 @@ __all__ = [
15
27
  "generate_file_name",
16
28
  "get_array_element_type_from_name",
17
29
  "get_site_from_array_element_name",
18
- "layout_telescope_list_file_name",
19
30
  "sanitize_name",
20
31
  "simtel_config_file_name",
21
32
  "simtel_single_mirror_list_file_name",
@@ -24,11 +35,20 @@ __all__ = [
24
35
  "validate_site_name",
25
36
  ]
26
37
 
38
+ # Mapping of db collection names to class keys
39
+ db_collections_to_class_keys = {
40
+ "sites": ["Site"],
41
+ "telescopes": ["Structure", "Camera", "Telescope"],
42
+ "calibration_devices": ["Calibration"],
43
+ "configuration_sim_telarray": ["configuration_sim_telarray"],
44
+ "configuration_corsika": ["configuration_corsika"],
45
+ }
46
+
27
47
 
28
48
  @cache
29
49
  def array_elements():
30
50
  """
31
- Load array elements from reference files and keep in cache.
51
+ Get array elements and their properties.
32
52
 
33
53
  Returns
34
54
  -------
@@ -42,50 +62,143 @@ def array_elements():
42
62
  @cache
43
63
  def site_names():
44
64
  """
45
- Site names from reference file.
65
+ Get site names.
46
66
 
47
- The list of sites is derived from the sites listed in the model parameter
48
- schema files. Return a dictionary for compatibility with the validation routines.
67
+ The list of sites is derived from the sites listed in array element definition file.
68
+ Return a dictionary for compatibility with the validation '_validate_name' routine.
49
69
 
50
70
  Returns
51
71
  -------
52
72
  dict
53
73
  Site names.
54
74
  """
55
- _array_elements = array_elements()
56
- _sites = {entry["site"] for entry in _array_elements.values()}
57
- return {site: [site.lower()] for site in _sites}
75
+ return {
76
+ site: [site.lower()]
77
+ for entry in array_elements().values()
78
+ for site in (entry["site"] if isinstance(entry["site"], list) else [entry["site"]])
79
+ }
58
80
 
59
81
 
60
82
  @cache
61
- def load_model_parameters(class_key_list):
62
- model_parameters = {}
63
- schema_files = list(Path(MODEL_PARAMETER_SCHEMA_PATH).rglob("*.yml"))
64
- for schema_file in schema_files:
83
+ def array_element_design_types(array_element_type):
84
+ """
85
+ Get array element site types (e.g., 'design' or 'flashcam').
86
+
87
+ Default values are ['design', 'test'].
88
+
89
+ Parameters
90
+ ----------
91
+ array_element_type
92
+ Array element type
93
+
94
+ Returns
95
+ -------
96
+ list
97
+ Array element design types.
98
+ """
99
+ default_types = ["design", "test"]
100
+ if array_element_type is None:
101
+ return default_types
102
+ try:
103
+ return array_elements()[array_element_type].get("design_types", default_types)
104
+ except KeyError as exc:
105
+ raise ValueError(f"Invalid name {array_element_type}") from exc
106
+
107
+
108
+ def is_design_type(array_element_name):
109
+ """
110
+ Check if array element is a design type (e.g., "MSTS-FlashCam" or "LSTN-design").
111
+
112
+ Parameters
113
+ ----------
114
+ array_element_name: str
115
+ Array element name.
116
+
117
+ Returns
118
+ -------
119
+ bool
120
+ True if array element is a design type.
121
+ """
122
+ return get_array_element_id_from_name(array_element_name) in array_element_design_types(
123
+ get_array_element_type_from_name(array_element_name)
124
+ )
125
+
126
+
127
+ @cache
128
+ def _load_model_parameters():
129
+ """
130
+ Get model parameters properties from schema files.
131
+
132
+ Returns
133
+ -------
134
+ dict
135
+ Model parameters definitions for all model parameters.
136
+ """
137
+ _parameters = {}
138
+ for schema_file in list(Path(MODEL_PARAMETER_SCHEMA_PATH).rglob("*.yml")):
65
139
  with open(schema_file, encoding="utf-8") as f:
66
140
  data = yaml.safe_load(f)
67
- try:
68
- if data["instrument"]["class"] in class_key_list:
69
- model_parameters[data["name"]] = data
70
- except KeyError:
71
- pass
72
- return model_parameters
141
+ _parameters[data["name"]] = data
142
+ return _parameters
143
+
144
+
145
+ def model_parameters(class_key_list=None):
146
+ """
147
+ Get model parameters and their properties for a given instrument class key.
148
+
149
+ Returns all model parameters if class_key is None.
150
+
151
+ Parameters
152
+ ----------
153
+ class_key: str, None
154
+ Class key (e.g., "telescope", "camera", structure").
155
+
156
+ Returns
157
+ -------
158
+ dict
159
+ Model parameters definitions.
160
+ """
161
+ _parameters = {}
162
+ if class_key_list is None:
163
+ return _load_model_parameters()
164
+ for key, value in _load_model_parameters().items():
165
+ if value.get("instrument", {}).get("class", "") in class_key_list:
166
+ _parameters[key] = value
167
+ return _parameters
73
168
 
74
169
 
75
170
  def site_parameters():
76
- return load_model_parameters(class_key_list="Site")
171
+ """Return site model parameters."""
172
+ return model_parameters(class_key_list=tuple(db_collections_to_class_keys["sites"]))
77
173
 
78
174
 
79
175
  def telescope_parameters():
80
- return load_model_parameters(class_key_list=("Structure", "Camera", "Telescope"))
176
+ """Return telescope model parameters."""
177
+ return model_parameters(class_key_list=tuple(db_collections_to_class_keys["telescopes"]))
178
+
179
+
180
+ def instrument_class_key_to_db_collection(class_name):
181
+ """Convert instrument class key to collection name."""
182
+ for collection, classes in db_collections_to_class_keys.items():
183
+ if class_name in classes:
184
+ return collection
185
+ raise ValueError(f"Class {class_name} not found")
81
186
 
82
187
 
83
- def validate_array_element_id_name(name):
188
+ def db_collection_to_instrument_class_key(collection_name="telescopes"):
189
+ """Return list of instrument classes for a given collection."""
190
+ try:
191
+ return db_collections_to_class_keys[collection_name]
192
+ except KeyError as exc:
193
+ raise KeyError(f"Invalid collection name {collection_name}") from exc
194
+
195
+
196
+ def validate_array_element_id_name(array_element_id, array_element_type=None):
84
197
  """
85
198
  Validate array element ID.
86
199
 
87
200
  Allowed IDs are
88
- - design (for design array elements or testing)
201
+ - design types (for design array elements or testing)
89
202
  - array element ID (e.g., 1, 5, 15)
90
203
  - test (for testing)
91
204
 
@@ -93,6 +206,8 @@ def validate_array_element_id_name(name):
93
206
  ----------
94
207
  name: str or int
95
208
  Array element ID name.
209
+ array_element_type: str
210
+ Array element type (e.g., LSTN, MSTN).
96
211
 
97
212
  Returns
98
213
  -------
@@ -104,23 +219,20 @@ def validate_array_element_id_name(name):
104
219
  ValueError
105
220
  If name is not valid.
106
221
  """
107
- if isinstance(name, int) or name.isdigit():
108
- return f"{int(name):02d}"
109
- if name.lower() in ("design", "test"):
110
- return str(name).lower()
111
-
112
- msg = f"Invalid array element ID name {name}"
113
- _logger.error(msg)
114
- raise ValueError(msg)
222
+ if isinstance(array_element_id, int) or array_element_id.isdigit():
223
+ return f"{int(array_element_id):02d}"
224
+ if array_element_id in array_element_design_types(array_element_type):
225
+ return str(array_element_id)
226
+ raise ValueError(f"Invalid array element ID name {array_element_id}")
115
227
 
116
228
 
117
- def validate_site_name(name):
229
+ def validate_site_name(site_name):
118
230
  """
119
231
  Validate site name.
120
232
 
121
233
  Parameters
122
234
  ----------
123
- name: str
235
+ site_name: str
124
236
  Site name.
125
237
 
126
238
  Returns
@@ -128,7 +240,7 @@ def validate_site_name(name):
128
240
  str
129
241
  Validated name.
130
242
  """
131
- return _validate_name(name, site_names())
243
+ return _validate_name(site_name, site_names())
132
244
 
133
245
 
134
246
  def _validate_name(name, all_names):
@@ -167,13 +279,13 @@ def _validate_name(name, all_names):
167
279
  raise ValueError(msg)
168
280
 
169
281
 
170
- def validate_array_element_type(name):
282
+ def validate_array_element_type(array_element_type):
171
283
  """
172
284
  Validate array element type (e.g., LSTN, MSTN).
173
285
 
174
286
  Parameters
175
287
  ----------
176
- name: str
288
+ array_element_type: str
177
289
  Array element type.
178
290
 
179
291
  Returns
@@ -181,16 +293,18 @@ def validate_array_element_type(name):
181
293
  str
182
294
  Validated name.
183
295
  """
184
- return _validate_name(name, array_elements())
296
+ return _validate_name(array_element_type, array_elements())
185
297
 
186
298
 
187
- def validate_array_element_name(name):
299
+ def validate_array_element_name(array_element_name):
188
300
  """
189
- Validate array element name (e.g., MSTN-design, MSTN-01).
301
+ Validate array element name (e.g., MSTx-NectarCam, MSTN-01).
302
+
303
+ Forgiving validation, is it allows also to give a site name (e.g., OBS-North).
190
304
 
191
305
  Parameters
192
306
  ----------
193
- name: str
307
+ array_element_name: str
194
308
  Array element name.
195
309
 
196
310
  Returns
@@ -199,20 +313,22 @@ def validate_array_element_name(name):
199
313
  Validated name.
200
314
  """
201
315
  try:
202
- _array_element_type, _array_element_id = name.split("-")
316
+ _array_element_type, _array_element_id = array_element_name.split("-")
203
317
  except ValueError as exc:
204
- msg = f"Invalid name {name}"
318
+ msg = f"Invalid name {array_element_name}"
205
319
  raise ValueError(msg) from exc
320
+ if _array_element_type == "OBS":
321
+ return validate_site_name(_array_element_id)
206
322
  return (
207
323
  _validate_name(_array_element_type, array_elements())
208
324
  + "-"
209
- + validate_array_element_id_name(_array_element_id)
325
+ + validate_array_element_id_name(_array_element_id, _array_element_type)
210
326
  )
211
327
 
212
328
 
213
- def get_array_element_name_from_type_site_id(array_element_type, site, array_element_id):
329
+ def generate_array_element_name_from_type_site_id(array_element_type, site, array_element_id):
214
330
  """
215
- Get array element name from type, site and ID.
331
+ Generate a new array element name from array element type, site, and array element ID.
216
332
 
217
333
  Parameters
218
334
  ----------
@@ -229,17 +345,17 @@ def get_array_element_name_from_type_site_id(array_element_type, site, array_ele
229
345
  Array element name.
230
346
  """
231
347
  _short_site = validate_site_name(site)[0]
232
- _val_id = validate_array_element_id_name(array_element_id)
348
+ _val_id = validate_array_element_id_name(array_element_id, array_element_type)
233
349
  return f"{array_element_type}{_short_site}-{_val_id}"
234
350
 
235
351
 
236
- def get_array_element_type_from_name(name):
352
+ def get_array_element_type_from_name(array_element_name):
237
353
  """
238
- Get array element type from name, e.g. "LSTN", "MSTN".
354
+ Get array element type from array element name (e.g "MSTN" from "MSTN-01").
239
355
 
240
356
  Parameters
241
357
  ----------
242
- name: str
358
+ array_element_name: str
243
359
  Array element name
244
360
 
245
361
  Returns
@@ -247,14 +363,36 @@ def get_array_element_type_from_name(name):
247
363
  str
248
364
  Array element type.
249
365
  """
250
- return _validate_name(name.split("-")[0], array_elements())
366
+ return _validate_name(array_element_name.split("-")[0], array_elements())
367
+
368
+
369
+ def get_array_element_id_from_name(array_element_name):
370
+ """
371
+ Get array element ID from array element name, (e.g. "01" from "MSTN-01").
372
+
373
+ Parameters
374
+ ----------
375
+ array_element_name: str
376
+ Array element name
377
+
378
+ Returns
379
+ -------
380
+ str
381
+ Array element ID.
382
+ """
383
+ try:
384
+ return validate_array_element_id_name(
385
+ array_element_name.split("-")[1], array_element_name.split("-")[0]
386
+ )
387
+ except IndexError as exc:
388
+ raise ValueError(f"Invalid name {array_element_name}") from exc
251
389
 
252
390
 
253
391
  def get_list_of_array_element_types(
254
392
  array_element_class="telescopes", site=None, observatory="CTAO"
255
393
  ):
256
394
  """
257
- Get list of array element types.
395
+ Get list of array element types (e.g., ["LSTN", "MSTN"] for the Northern site).
258
396
 
259
397
  Parameters
260
398
  ----------
@@ -268,139 +406,136 @@ def get_list_of_array_element_types(
268
406
  list
269
407
  List of array element types.
270
408
  """
271
- return [
272
- key
273
- for key, value in array_elements().items()
274
- if value["collection"] == array_element_class
275
- and (site is None or value["site"] == site)
276
- and (observatory is None or value["observatory"] == observatory)
277
- ]
409
+ return sorted(
410
+ [
411
+ key
412
+ for key, value in array_elements().items()
413
+ if value["collection"] == array_element_class
414
+ and (site is None or value["site"] == site)
415
+ and (observatory is None or value["observatory"] == observatory)
416
+ ]
417
+ )
278
418
 
279
419
 
280
- def get_site_from_array_element_name(name):
420
+ def get_site_from_array_element_name(array_element_name):
281
421
  """
282
- Get site name from array element name.
422
+ Get site name from array element name (e.g., "South" from "MSTS-01").
283
423
 
284
424
  Parameters
285
425
  ----------
286
- name: str
426
+ array_element_name: str
287
427
  Array element name.
288
428
 
289
429
  Returns
290
430
  -------
291
- str
292
- Site name (South or North).
431
+ str, list
432
+ Site name(s).
293
433
  """
294
- return array_elements()[get_array_element_type_from_name(name)]["site"]
434
+ try: # e.g. instrument is 'North' as given for the site parameters
435
+ return validate_site_name(array_element_name)
436
+ except ValueError: # e.g. instrument is 'LSTN' as given for the array element types
437
+ return array_elements()[get_array_element_type_from_name(array_element_name)]["site"]
295
438
 
296
439
 
297
- def get_collection_name_from_array_element_name(name, array_elements_only=True):
440
+ def get_collection_name_from_array_element_name(array_element_name, array_elements_only=True):
298
441
  """
299
- Get collection name (e.g., telescopes, calibration_devices, sites) of array element from name.
442
+ Get collection name (e.g., telescopes, calibration_devices) of an array element from its name.
300
443
 
301
444
  Parameters
302
445
  ----------
303
- name: str
304
- Array element name.
446
+ array_element_name: str
447
+ Array element name (e.g. LSTN-01)
305
448
  array_elements_only: bool
306
- If True, only array elements are considered.
449
+ If True, only array elements are considered (e.g. "OBS-North" will raise a ValueError).
307
450
 
308
451
  Returns
309
452
  -------
310
453
  str
311
454
  Collection name .
455
+
456
+ Raises
457
+ ------
458
+ ValueError
459
+ If name is not a valid array element name.
312
460
  """
313
461
  try:
314
- return array_elements()[get_array_element_type_from_name(name)]["collection"]
315
- except ValueError:
316
- pass
317
- if name.startswith("OBS"):
318
- return "sites"
319
- try:
320
- validate_site_name(name)
321
- return "sites"
462
+ return array_elements()[get_array_element_type_from_name(array_element_name)]["collection"]
322
463
  except ValueError as exc:
323
464
  if array_elements_only:
324
- raise ValueError(f"Invalid array element name {name}") from exc
325
- if name in (
465
+ raise ValueError(f"Invalid array element name {array_element_name}") from exc
466
+ try:
467
+ if array_element_name.startswith("OBS") or validate_site_name(array_element_name):
468
+ return "sites"
469
+ except ValueError:
470
+ pass
471
+ if array_element_name in {
326
472
  "configuration_sim_telarray",
327
473
  "configuration_corsika",
328
474
  "Files",
329
475
  "Dummy-Telescope",
330
- ):
331
- return name
476
+ }:
477
+ return array_element_name
478
+ raise ValueError(f"Invalid array element name {array_element_name}")
332
479
 
333
- raise ValueError(f"Invalid array element name {name}")
334
480
 
335
-
336
- def get_simulation_software_name_from_parameter_name(
337
- par_name,
338
- simulation_software="sim_telarray",
339
- ):
481
+ def get_collection_name_from_parameter_name(parameter_name):
340
482
  """
341
- Get the name used in the simulation software from the model parameter name.
342
-
343
- Name convention is expected to be defined in the schema.
344
- Returns the parameter name if no simulation software name is found.
483
+ Get the db collection name for a given parameter.
345
484
 
346
485
  Parameters
347
486
  ----------
348
- par_name: str
349
- Model parameter name.
350
- simulation_software: str
351
- Simulation software name.
487
+ parameter_name: str
488
+ Name of the parameter.
352
489
 
353
490
  Returns
354
491
  -------
355
492
  str
356
- Simtel parameter name.
357
- """
358
- _parameter_names = {**telescope_parameters(), **site_parameters()}
493
+ Collection name.
359
494
 
495
+ Raises
496
+ ------
497
+ KeyError
498
+ If the parameter name is not found in the list of model parameters
499
+ """
500
+ _parameter_names = model_parameters()
360
501
  try:
361
- _parameter = _parameter_names[par_name]
362
- except KeyError as err:
363
- _logger.error(f"Parameter {par_name} without schema definition")
364
- raise err
365
-
366
- try:
367
- for software in _parameter.get("simulation_software", []):
368
- if software.get("name") == simulation_software:
369
- return software.get("internal_parameter_name", par_name)
370
- except TypeError: # catches cases for which 'simulation_software' is None
371
- pass
372
- return None
502
+ class_key = _parameter_names[parameter_name].get("instrument", {}).get("class")
503
+ except KeyError as exc:
504
+ raise KeyError(f"Parameter {parameter_name} without schema definition") from exc
505
+ return instrument_class_key_to_db_collection(class_key)
373
506
 
374
507
 
375
- def get_parameter_name_from_simtel_name(simtel_name):
508
+ def get_simulation_software_name_from_parameter_name(
509
+ parameter_name,
510
+ simulation_software="sim_telarray",
511
+ ):
376
512
  """
377
- Get the model parameter name from the simtel parameter name.
513
+ Get the name used in the given simulation software from the model parameter name.
378
514
 
379
- Assumes that both names are equal if not defined otherwise in names.py.
515
+ Name convention is expected to be defined in the model parameter schema.
516
+ Returns the parameter name if no simulation software name is found.
380
517
 
381
518
  Parameters
382
519
  ----------
383
- simtel_name: str
384
- Simtel parameter name.
520
+ parameter_name: str
521
+ Model parameter name.
522
+ simulation_software: str
523
+ Simulation software name.
385
524
 
386
525
  Returns
387
526
  -------
388
527
  str
389
- Model parameter name.
528
+ Simtel parameter name.
390
529
  """
391
- _parameters = {**telescope_parameters(), **site_parameters()}
530
+ _parameter = model_parameters().get(parameter_name)
531
+ if not _parameter:
532
+ raise KeyError(f"Parameter {parameter_name} without schema definition")
392
533
 
393
- for par_name, par_info in _parameters.items():
394
- try:
395
- for software in par_info["simulation_software"]:
396
- if (
397
- software["name"] == "sim_telarray"
398
- and software["internal_parameter_name"] == simtel_name
399
- ):
400
- return par_name
401
- except (KeyError, TypeError): # catches cases for which 'simulation_software' is None
402
- pass
403
- return simtel_name
534
+ for software in _parameter.get("simulation_software", []):
535
+ if software.get("name") == simulation_software:
536
+ return software.get("internal_parameter_name", parameter_name)
537
+
538
+ return None
404
539
 
405
540
 
406
541
  def simtel_config_file_name(
@@ -474,28 +609,6 @@ def simtel_single_mirror_list_file_name(
474
609
  return name
475
610
 
476
611
 
477
- def layout_telescope_list_file_name(name, label):
478
- """
479
- File name for files required at the RayTracing class.
480
-
481
- Parameters
482
- ----------
483
- name: str
484
- Name of the array.
485
- label: str
486
- Instance label.
487
-
488
- Returns
489
- -------
490
- str
491
- File name.
492
- """
493
- file_name = f"telescope_positions-{name}"
494
- file_name += f"_{label}" if label is not None else ""
495
- file_name += ".ecsv"
496
- return file_name
497
-
498
-
499
612
  def generate_file_name(
500
613
  file_type,
501
614
  suffix,
@@ -581,8 +694,7 @@ def sanitize_name(name):
581
694
  if the string name can not be sanitized.
582
695
  """
583
696
  if name is None:
584
- # _logger.info("The string is None and can't be sanitized.")
585
- return name
697
+ return None
586
698
  sanitized = name.lower()
587
699
  sanitized = sanitized.replace(" ", "_")
588
700
  # Remove characters that are not alphanumerics or underscores
@@ -592,3 +704,29 @@ def sanitize_name(name):
592
704
  _logger.error(msg)
593
705
  raise ValueError(msg)
594
706
  return sanitized
707
+
708
+
709
+ def file_name_with_version(file_name, suffix):
710
+ """
711
+ Return a file name including a semantic version with the correct suffix.
712
+
713
+ Replaces 'Path.suffix()', which removes trailing numbers (and therefore version numbers).
714
+
715
+ Parameters
716
+ ----------
717
+ file_name: str
718
+ File name.
719
+ suffix: str
720
+ File suffix.
721
+
722
+ Returns
723
+ -------
724
+ Path
725
+ File name with version number.
726
+ """
727
+ if file_name is None or suffix is None:
728
+ return None
729
+ file_name = str(file_name)
730
+ if bool(re.search(r"\d+\.\d+\.\d+$", file_name)):
731
+ return Path(file_name + suffix)
732
+ return Path(file_name).with_suffix(suffix)