gammasimtools 0.16.0__py3-none-any.whl → 0.18.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 (85) hide show
  1. {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/METADATA +5 -2
  2. {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/RECORD +82 -74
  3. {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/entry_points.txt +4 -1
  5. simtools/_version.py +2 -2
  6. simtools/applications/db_add_simulation_model_from_repository_to_db.py +10 -1
  7. simtools/applications/derive_ctao_array_layouts.py +5 -5
  8. simtools/applications/derive_mirror_rnda.py +1 -1
  9. simtools/applications/generate_simtel_event_data.py +128 -46
  10. simtools/applications/merge_tables.py +102 -0
  11. simtools/applications/plot_array_layout.py +145 -258
  12. simtools/applications/plot_tabular_data.py +12 -1
  13. simtools/applications/plot_tabular_data_for_model_parameter.py +103 -0
  14. simtools/applications/production_derive_corsika_limits.py +78 -225
  15. simtools/applications/production_derive_statistics.py +77 -43
  16. simtools/applications/simulate_light_emission.py +1 -0
  17. simtools/applications/simulate_prod.py +30 -18
  18. simtools/applications/simulate_prod_htcondor_generator.py +0 -1
  19. simtools/applications/submit_array_layouts.py +93 -0
  20. simtools/applications/verify_simulation_model_production_tables.py +52 -0
  21. simtools/camera/camera_efficiency.py +3 -3
  22. simtools/configuration/commandline_parser.py +30 -35
  23. simtools/configuration/configurator.py +0 -4
  24. simtools/constants.py +2 -0
  25. simtools/corsika/corsika_config.py +17 -12
  26. simtools/corsika/primary_particle.py +46 -13
  27. simtools/data_model/metadata_collector.py +7 -3
  28. simtools/data_model/schema.py +15 -1
  29. simtools/db/db_handler.py +16 -11
  30. simtools/db/db_model_upload.py +2 -2
  31. simtools/io_operations/io_handler.py +2 -2
  32. simtools/io_operations/io_table_handler.py +345 -0
  33. simtools/job_execution/htcondor_script_generator.py +2 -2
  34. simtools/job_execution/job_manager.py +7 -121
  35. simtools/layout/array_layout_utils.py +389 -0
  36. simtools/model/array_model.py +10 -1
  37. simtools/model/model_repository.py +134 -0
  38. simtools/production_configuration/{calculate_statistical_errors_grid_point.py → calculate_statistical_uncertainties_grid_point.py} +101 -112
  39. simtools/production_configuration/derive_corsika_limits.py +239 -111
  40. simtools/production_configuration/derive_corsika_limits_grid.py +232 -0
  41. simtools/production_configuration/derive_production_statistics.py +57 -26
  42. simtools/production_configuration/derive_production_statistics_handler.py +70 -37
  43. simtools/production_configuration/interpolation_handler.py +296 -94
  44. simtools/ray_tracing/ray_tracing.py +7 -6
  45. simtools/reporting/docs_read_parameters.py +104 -62
  46. simtools/resources/array-element-ids.json +126 -0
  47. simtools/runners/corsika_simtel_runner.py +4 -1
  48. simtools/runners/runner_services.py +5 -4
  49. simtools/schemas/model_parameter_and_data_schema.metaschema.yml +5 -1
  50. simtools/schemas/model_parameters/atmospheric_profile.schema.yml +41 -0
  51. simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +43 -0
  52. simtools/schemas/model_parameters/camera_filter.schema.yml +10 -0
  53. simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +10 -0
  54. simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +31 -0
  55. simtools/schemas/model_parameters/dsum_threshold.schema.yml +41 -0
  56. simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +12 -0
  57. simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +10 -0
  58. simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +10 -0
  59. simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +12 -0
  60. simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +19 -0
  61. simtools/schemas/model_parameters/quantum_efficiency.schema.yml +10 -0
  62. simtools/schemas/plot_configuration.metaschema.yml +46 -57
  63. simtools/schemas/production_configuration_metrics.schema.yml +2 -2
  64. simtools/simtel/simtel_config_writer.py +34 -14
  65. simtools/simtel/simtel_io_event_reader.py +301 -194
  66. simtools/simtel/simtel_io_event_writer.py +237 -221
  67. simtools/simtel/simtel_io_file_info.py +9 -4
  68. simtools/simtel/simtel_io_metadata.py +119 -8
  69. simtools/simtel/simulator_array.py +2 -2
  70. simtools/simtel/simulator_light_emission.py +79 -34
  71. simtools/simtel/simulator_ray_tracing.py +2 -2
  72. simtools/simulator.py +101 -68
  73. simtools/testing/validate_output.py +4 -1
  74. simtools/utils/general.py +1 -3
  75. simtools/utils/names.py +76 -7
  76. simtools/visualization/plot_array_layout.py +242 -0
  77. simtools/visualization/plot_pixels.py +680 -0
  78. simtools/visualization/plot_tables.py +81 -2
  79. simtools/visualization/visualize.py +3 -219
  80. simtools/applications/production_generate_simulation_config.py +0 -152
  81. simtools/layout/ctao_array_layouts.py +0 -172
  82. simtools/production_configuration/generate_simulation_config.py +0 -158
  83. {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/licenses/LICENSE +0 -0
  84. {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/top_level.txt +0 -0
  85. /simtools/{schemas → resources}/array_elements.yml +0 -0
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/python3
2
+
3
+ r"""
4
+ Submit array-layouts definition and corresponding metadata and validate list of telescopes.
5
+
6
+ Includes validation that all defined telescope exists.
7
+
8
+ Command line arguments
9
+ ----------------------
10
+ array_layouts (str, required)
11
+ Array layouts file.
12
+ updated_parameter_version (str, optional)
13
+ Updated parameter version.
14
+ input_meta (str, optional)
15
+ Input meta data file(s) associated to input data (wildcards or list of files allowed).
16
+ model_version (str, required)
17
+ Model version.
18
+
19
+ Example
20
+ -------
21
+
22
+ Submit a new array layout dictionary:
23
+
24
+ .. code-block:: console
25
+
26
+ simtools-submit-array-layouts \
27
+ --array_layouts array_layouts.json \\
28
+ --model_version 6.0.0 \\
29
+ --updated_parameter_version 0.1.0 \\
30
+ --input_meta array_layouts.metadata.yml
31
+
32
+
33
+ """
34
+
35
+ import logging
36
+ from pathlib import Path
37
+
38
+ import simtools.utils.general as gen
39
+ from simtools.configuration import configurator
40
+ from simtools.db import db_handler
41
+ from simtools.layout.array_layout_utils import validate_array_layouts_with_db, write_array_layouts
42
+
43
+
44
+ def _parse(label, description):
45
+ """Parse command line configuration."""
46
+ config = configurator.Configurator(label=label, description=description)
47
+
48
+ config.parser.add_argument(
49
+ "--array_layouts",
50
+ type=str,
51
+ required=True,
52
+ help="Array layout dictionary file.",
53
+ )
54
+ config.parser.add_argument(
55
+ "--updated_parameter_version",
56
+ help="Updated parameter version.",
57
+ type=str,
58
+ required=False,
59
+ )
60
+ config.parser.add_argument(
61
+ "--input_meta",
62
+ help="meta data file(s) associated to input data (wildcards or list of files allowed)",
63
+ type=str,
64
+ nargs="+",
65
+ required=False,
66
+ )
67
+
68
+ return config.initialize(output=True, db_config=True, simulation_model=["model_version"])
69
+
70
+
71
+ def main(): # noqa: D103
72
+ args_dict, db_config = _parse(
73
+ label=Path(__file__).stem,
74
+ description="Submit and validate array layouts.",
75
+ )
76
+
77
+ logger = logging.getLogger()
78
+ logger.setLevel(gen.get_log_level_from_user(args_dict["log_level"]))
79
+
80
+ db = db_handler.DatabaseHandler(mongo_db_config=db_config)
81
+
82
+ array_layouts = validate_array_layouts_with_db(
83
+ production_table=db.read_production_table_from_mongo_db(
84
+ collection_name="telescopes", model_version=args_dict["model_version"]
85
+ ),
86
+ array_layouts=gen.collect_data_from_file(args_dict["array_layouts"]),
87
+ )
88
+
89
+ write_array_layouts(array_layouts=array_layouts, args_dict=args_dict, db_config=db_config)
90
+
91
+
92
+ if __name__ == "__main__":
93
+ main()
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/python3
2
+
3
+ """
4
+ Verify simulation model production tables and model parameters for completeness.
5
+
6
+ This application is a utility to be used in the CI pipeline of the SimulationModels
7
+ repository. It checks that all model parameters defined in the production tables
8
+ exist in the simulation models repository.
9
+
10
+ """
11
+
12
+ import logging
13
+
14
+ from simtools.configuration import configurator
15
+ from simtools.model import model_repository
16
+ from simtools.utils import general as gen
17
+
18
+
19
+ def _parse():
20
+ """Parse command line arguments."""
21
+ config = configurator.Configurator(
22
+ description=(
23
+ "Verify simulation model production tables and model parameters for completeness. "
24
+ "This application checks that all model parameters defined in the production tables "
25
+ "exist in the simulation models repository."
26
+ )
27
+ )
28
+ config.parser.add_argument(
29
+ "--simulation_models_path",
30
+ help="Path to the simulation models repository.",
31
+ type=str,
32
+ required=True,
33
+ )
34
+ return config.initialize(db_config=False, output=False, paths=False)
35
+
36
+
37
+ def main(): # noqa: D103
38
+ args_dict, _ = _parse()
39
+
40
+ logger = logging.getLogger()
41
+ logger.setLevel(gen.get_log_level_from_user(args_dict["log_level"]))
42
+
43
+ if not model_repository.verify_simulation_model_production_tables(
44
+ simulation_models_path=args_dict["simulation_models_path"]
45
+ ):
46
+ raise RuntimeError(
47
+ "Verification failed: Some model parameters are missing in the repository."
48
+ )
49
+
50
+
51
+ if __name__ == "__main__":
52
+ main()
@@ -114,7 +114,7 @@ class CameraEfficiency:
114
114
  ):
115
115
  file_name = names.generate_file_name(
116
116
  file_type=(
117
- "camera-efficiency-table" if label == "results" else "camera-efficiency"
117
+ "camera_efficiency_table" if label == "results" else "camera_efficiency"
118
118
  ),
119
119
  suffix=suffix,
120
120
  site=self.telescope_model.site,
@@ -126,7 +126,7 @@ class CameraEfficiency:
126
126
 
127
127
  _file[label] = self.io_handler.get_output_directory(
128
128
  label=self.label,
129
- sub_dir="camera-efficiency",
129
+ sub_dir="camera_efficiency",
130
130
  ).joinpath(file_name)
131
131
  return _file
132
132
 
@@ -292,7 +292,7 @@ class CameraEfficiency:
292
292
  self._results, self._file["results"], format="basic", overwrite=True
293
293
  )
294
294
  _results_summary_file = (
295
- str(self._file["results"]).replace(".ecsv", ".txt").replace("-table-", "-summary-")
295
+ str(self._file["results"]).replace(".ecsv", ".txt").replace("_table_", "_summary_")
296
296
  )
297
297
  self._logger.info(f"Exporting summary results to {_results_summary_file}")
298
298
  with open(_results_summary_file, "w", encoding="utf-8") as file:
@@ -37,7 +37,6 @@ class CommandLineParser(argparse.ArgumentParser):
37
37
  simulation_model=None,
38
38
  simulation_configuration=None,
39
39
  db_config=False,
40
- job_submission=False,
41
40
  ):
42
41
  """
43
42
  Initialize default arguments used by all applications (e.g., log level or test flag).
@@ -55,13 +54,9 @@ class CommandLineParser(argparse.ArgumentParser):
55
54
  Dict of simulation software configuration parameters to add to list of args.
56
55
  db_config: bool
57
56
  Add database configuration parameters to list of args.
58
- job_submission: bool
59
- Add job submission configuration parameters to list of args.
60
57
  """
61
58
  self.initialize_simulation_model_arguments(simulation_model)
62
59
  self.initialize_simulation_configuration_arguments(simulation_configuration)
63
- if job_submission:
64
- self.initialize_job_submission_arguments()
65
60
  if db_config:
66
61
  self.initialize_db_config_arguments()
67
62
  if paths:
@@ -228,28 +223,6 @@ class CommandLineParser(argparse.ArgumentParser):
228
223
  default=None,
229
224
  )
230
225
 
231
- def initialize_job_submission_arguments(self):
232
- """Initialize job submission arguments for simulator."""
233
- _job_group = self.add_argument_group("job submission")
234
- _job_group.add_argument(
235
- "--submit_engine",
236
- help="job submission command",
237
- type=str,
238
- required=True,
239
- choices=[
240
- "qsub",
241
- "htcondor",
242
- "local",
243
- ],
244
- default="local",
245
- )
246
- _job_group.add_argument(
247
- "--submit_options",
248
- help="additional options (comma separated) for submission command",
249
- type=str,
250
- required=False,
251
- )
252
-
253
226
  def initialize_simulation_model_arguments(self, model_options):
254
227
  """
255
228
  Initialize default arguments for simulation model definition.
@@ -303,7 +276,7 @@ class CommandLineParser(argparse.ArgumentParser):
303
276
  if "layout" in model_options or "layout_file" in model_options:
304
277
  _job_group = self._add_model_option_layout(
305
278
  job_group=_job_group,
306
- add_layout_file="layout_file" in model_options,
279
+ model_options=model_options,
307
280
  # layout info is always required for layout related tasks with the exception
308
281
  # of listing the available layouts in the DB
309
282
  required="--list_available_layouts" not in self._option_string_actions,
@@ -389,8 +362,14 @@ class CommandLineParser(argparse.ArgumentParser):
389
362
  "type": int,
390
363
  "required": False,
391
364
  },
392
- "run_number_start": {
393
- "help": "Run number for the first run.",
365
+ "run_number_offset": {
366
+ "help": "An offset for the run number to be simulated.",
367
+ "type": int,
368
+ "required": False,
369
+ "default": 0,
370
+ },
371
+ "run_number": {
372
+ "help": "Run number to be simulated.",
394
373
  "type": int,
395
374
  "required": True,
396
375
  "default": 1,
@@ -477,7 +456,7 @@ class CommandLineParser(argparse.ArgumentParser):
477
456
  pass
478
457
 
479
458
  @staticmethod
480
- def _add_model_option_layout(job_group, add_layout_file, required=True):
459
+ def _add_model_option_layout(job_group, model_options, required=True):
481
460
  """
482
461
  Add layout option to the job group.
483
462
 
@@ -485,8 +464,8 @@ class CommandLineParser(argparse.ArgumentParser):
485
464
  ----------
486
465
  job_group: argparse.ArgumentParser
487
466
  Job group
488
- add_layout_file: bool
489
- Add layout file option
467
+ model_options: list
468
+ List of model options.
490
469
 
491
470
  Returns
492
471
  -------
@@ -495,7 +474,8 @@ class CommandLineParser(argparse.ArgumentParser):
495
474
  _layout_group = job_group.add_mutually_exclusive_group(required=required)
496
475
  _layout_group.add_argument(
497
476
  "--array_layout_name",
498
- help="array layout name (e.g., alpha, subsystem_msts)",
477
+ help="array layout name(s) (e.g., alpha, subsystem_msts)",
478
+ nargs="+",
499
479
  type=str,
500
480
  required=False,
501
481
  )
@@ -507,7 +487,7 @@ class CommandLineParser(argparse.ArgumentParser):
507
487
  required=False,
508
488
  default=None,
509
489
  )
510
- if add_layout_file:
490
+ if "layout_file" in model_options:
511
491
  _layout_group.add_argument(
512
492
  "--array_layout_file",
513
493
  help="file(s) with the list of array elements (astropy table format).",
@@ -516,6 +496,21 @@ class CommandLineParser(argparse.ArgumentParser):
516
496
  required=False,
517
497
  default=None,
518
498
  )
499
+ if "layout_parameter_file" in model_options:
500
+ _layout_group.add_argument(
501
+ "--array_layout_parameter_file",
502
+ help="Array layout model parameter file (typically in JSON format).",
503
+ type=str,
504
+ required=False,
505
+ default=None,
506
+ )
507
+ if "plot_all_layouts" in model_options:
508
+ _layout_group.add_argument(
509
+ "--plot_all_layouts",
510
+ help="plot all available layouts",
511
+ action="store_true",
512
+ required=False,
513
+ )
519
514
  return job_group
520
515
 
521
516
  def _add_model_option_site(self, job_group):
@@ -106,7 +106,6 @@ class Configurator:
106
106
  simulation_model=None,
107
107
  simulation_configuration=None,
108
108
  db_config=False,
109
- job_submission=False,
110
109
  ):
111
110
  """
112
111
  Initialize application configuration.
@@ -134,8 +133,6 @@ class Configurator:
134
133
  Dict of simulation software configuration parameters to add to list of args.
135
134
  db_config: bool
136
135
  Add database configuration parameters to list of args.
137
- job_submission: bool
138
- Add job submission configuration to list of args.
139
136
 
140
137
  Returns
141
138
  -------
@@ -151,7 +148,6 @@ class Configurator:
151
148
  simulation_model=simulation_model,
152
149
  simulation_configuration=simulation_configuration,
153
150
  db_config=db_config,
154
- job_submission=job_submission,
155
151
  )
156
152
 
157
153
  self._fill_from_command_line(require_command_line=require_command_line)
simtools/constants.py CHANGED
@@ -21,3 +21,5 @@ MODEL_PARAMETER_SCHEMA_URL = (
21
21
  "https://raw.githubusercontent.com/gammasim/simtools/main/src/simtools/schemas/"
22
22
  "/model_parameters"
23
23
  )
24
+ # Path to resource files
25
+ RESOURCE_PATH = files("simtools") / "resources"
@@ -638,6 +638,9 @@ class CorsikaConfig:
638
638
  ValueError
639
639
  If file_type is unknown or if the run number is not given for file_type==config_tmp.
640
640
  """
641
+ if file_type == "config_tmp" and run_number is None:
642
+ raise ValueError("Must provide a run number for a temporary CORSIKA config file")
643
+
641
644
  file_label = f"_{self.label}" if self.label is not None else ""
642
645
 
643
646
  _vc_low = self.get_config_parameter("VIEWCONE")[0]
@@ -646,25 +649,27 @@ class CorsikaConfig:
646
649
  f"_cone{int(_vc_low):d}-{int(_vc_high):d}" if _vc_low != 0 or _vc_high != 0 else ""
647
650
  )
648
651
 
652
+ run_number_in_file_name = ""
653
+ if file_type == "output_generic":
654
+ # The XXXXXX will be replaced by the run number after the pfp step with sed
655
+ run_number_in_file_name = "runXXXXXX_"
656
+ if file_type == "config_tmp":
657
+ run_number_in_file_name = f"run{run_number:06}_"
658
+
649
659
  base_name = (
650
- f"{self.primary_particle.name}_{self.array_model.site}_{self.array_model.layout_name}_"
651
- f"za{int(self.get_config_parameter('THETAP')[0]):03}-"
652
- f"azm{self.azimuth_angle:03}deg"
653
- f"{view_cone}{file_label}"
660
+ f"{self.primary_particle.name}_{run_number_in_file_name}"
661
+ f"za{int(self.get_config_parameter('THETAP')[0]):03}deg_"
662
+ f"azm{self.azimuth_angle:03}deg{view_cone}_"
663
+ f"{self.array_model.site}_{self.array_model.layout_name}_"
664
+ f"{self.array_model.model_version}{file_label}"
654
665
  )
655
666
 
656
667
  if file_type == "config_tmp":
657
- if run_number is None:
658
- raise ValueError("Must provide a run number for a temporary CORSIKA config file")
659
- return f"corsika_config_run{run_number:06}_{base_name}.txt"
668
+ return f"corsika_config_{base_name}.txt"
660
669
  if file_type == "config":
661
670
  return f"corsika_config_{base_name}.input"
662
671
  if file_type == "output_generic":
663
- # The XXXXXX will be replaced by the run number after the pfp step with sed
664
- return (
665
- f"runXXXXXX_{base_name}_{self.array_model.site}_"
666
- f"{self.array_model.layout_name}{file_label}.zst"
667
- )
672
+ return f"{base_name}.zst"
668
673
  if file_type == "multipipe":
669
674
  return f"multi_cta-{self.array_model.site}-{self.array_model.layout_name}.cfg"
670
675
 
@@ -7,7 +7,7 @@ from particle import Corsika7ID, InvalidParticle, Particle
7
7
 
8
8
  class PrimaryParticle:
9
9
  """
10
- Primary particle definition using CORSIKA7 or PDG ID.
10
+ Primary particle definition using CORSIKA7, eventio, or PDG ID.
11
11
 
12
12
  Uses a dictionary to map particle common names to CORSIKA7 IDs.
13
13
  Particles not found in the dictionary are searched in the PDG particle database.
@@ -20,27 +20,31 @@ class PrimaryParticle:
20
20
  The actual ID of the primary particle.
21
21
  """
22
22
 
23
+ _valid_types = {
24
+ "corsika7_id": "corsika7_id",
25
+ "common_name": "name",
26
+ "pdg_id": "pdg_id",
27
+ "eventio_id": "eventio_id",
28
+ }
29
+
23
30
  def __init__(self, particle_id_type=None, particle_id=None):
24
31
  self._logger = logging.getLogger(__name__)
25
-
26
32
  self._corsika7_id = None
27
33
  self._name = None
28
34
  self._pdg_id = None
29
35
  self._pdg_name = None
36
+ self._eventio_id = None
30
37
 
31
- if bool(particle_id_type) != bool(particle_id):
38
+ if (particle_id_type is None) != (particle_id is None):
32
39
  raise ValueError("Both 'particle_id_type' and 'particle_id' must be provided together.")
33
40
 
34
- valid_id_types = {"corsika7_id", "common_name", "pdg_id"}
35
- if particle_id_type and particle_id_type not in valid_id_types:
36
- raise ValueError(f"Particle ID type must be one of {valid_id_types}")
37
-
38
- if particle_id_type == "corsika7_id":
39
- self.corsika7_id = particle_id
40
- elif particle_id_type == "common_name":
41
- self.name = particle_id
42
- elif particle_id_type == "pdg_id":
43
- self.pdg_id = particle_id
41
+ if particle_id_type:
42
+ try:
43
+ setattr(self, self._valid_types[particle_id_type], particle_id)
44
+ except KeyError as exc:
45
+ raise ValueError(
46
+ f"Particle ID type must be one of {set(self._valid_types)}"
47
+ ) from exc
44
48
 
45
49
  def __str__(self):
46
50
  """Return a string representation of the primary particle."""
@@ -70,6 +74,35 @@ class PrimaryParticle:
70
74
  raise ValueError(f"Invalid CORSIKA7 ID: {value}") from exc
71
75
  self._corsika7_id = int(value)
72
76
 
77
+ @property
78
+ def eventio_id(self):
79
+ """
80
+ EventIO ID of the primary particle.
81
+
82
+ 0 (gamma), 1(e-), 2(mu-), 100*A+Z for nucleons and nuclei, negative for antimatter.
83
+ """
84
+ return self._eventio_id
85
+
86
+ @eventio_id.setter
87
+ def eventio_id(self, value):
88
+ """Set EventIO ID of the primary particle."""
89
+ mapping = {
90
+ 0: 1,
91
+ 1: 3,
92
+ -1: 2,
93
+ 2: 6,
94
+ -2: 5,
95
+ 100: 13,
96
+ 101: 14,
97
+ -101: 15,
98
+ }
99
+
100
+ try:
101
+ self.corsika7_id = mapping.get(value, value)
102
+ except (ValueError, InvalidParticle) as exc:
103
+ raise ValueError(f"Invalid EventIO ID: {value}") from exc
104
+ self._eventio_id = value
105
+
73
106
  @property
74
107
  def name(self):
75
108
  """Common name of the primary particle."""
@@ -13,7 +13,6 @@ from pathlib import Path
13
13
 
14
14
  import yaml
15
15
 
16
- import simtools.constants
17
16
  import simtools.utils.general as gen
18
17
  import simtools.version
19
18
  from simtools.constants import METADATA_JSON_SCHEMA
@@ -209,7 +208,7 @@ class MetadataCollector:
209
208
  url = self.input_metadata[0][self.observatory]["product"]["data"]["model"]["url"]
210
209
  self._logger.debug(f"Schema file from input metadata: {url}")
211
210
  return url
212
- except (KeyError, TypeError):
211
+ except (KeyError, TypeError, IndexError):
213
212
  pass
214
213
 
215
214
  self._logger.warning("No schema file found.")
@@ -254,7 +253,7 @@ class MetadataCollector:
254
253
  )
255
254
  if _site is not None:
256
255
  return names.validate_site_name(_site)
257
- except (KeyError, TypeError):
256
+ except (KeyError, TypeError, IndexError):
258
257
  pass
259
258
  return None
260
259
 
@@ -360,6 +359,11 @@ class MetadataCollector:
360
359
  _input_metadata = self._read_input_metadata_from_yml_or_json(metadata_file)
361
360
  elif Path(metadata_file).suffix == ".ecsv":
362
361
  _input_metadata = self._read_input_metadata_from_ecsv(metadata_file)
362
+ elif Path(metadata_file).name.endswith((".simtel.zst", ".simtel")):
363
+ self._logger.warning(
364
+ "Metadata extraction from sim_telarray files is not supported yet."
365
+ )
366
+ continue
363
367
  else:
364
368
  raise gen.InvalidConfigDataError(f"Unknown metadata file format: {metadata_file}")
365
369
 
@@ -4,6 +4,7 @@ import logging
4
4
  from pathlib import Path
5
5
 
6
6
  import jsonschema
7
+ from referencing import Registry, Resource
7
8
 
8
9
  import simtools.utils.general as gen
9
10
  from simtools.constants import (
@@ -119,8 +120,14 @@ def validate_dict_using_schema(data, schema_file=None, json_schema=None):
119
120
  ), # default version to ensure backward compatibility
120
121
  )
121
122
 
123
+ validator = jsonschema.Draft6Validator(
124
+ schema=json_schema,
125
+ format_checker=format_checkers.format_checker,
126
+ registry=Registry(retrieve=_retrieve_yaml_schema_from_uri),
127
+ )
128
+
122
129
  try:
123
- jsonschema.validate(data, schema=json_schema, format_checker=format_checkers.format_checker)
130
+ validator.validate(instance=data)
124
131
  except jsonschema.exceptions.ValidationError as exc:
125
132
  _logger.error(f"Validation failed using schema: {json_schema} for data: {data}")
126
133
  raise exc
@@ -135,6 +142,13 @@ def validate_dict_using_schema(data, schema_file=None, json_schema=None):
135
142
  return data
136
143
 
137
144
 
145
+ def _retrieve_yaml_schema_from_uri(uri):
146
+ """Load schema from a file URI."""
147
+ path = SCHEMA_PATH / Path(uri.removeprefix("file:/"))
148
+ contents = gen.collect_data_from_file(file_name=path)
149
+ return Resource.from_contents(contents)
150
+
151
+
138
152
  def load_schema(schema_file=None, schema_version=None):
139
153
  """
140
154
  Load parameter schema from file.
simtools/db/db_handler.py CHANGED
@@ -217,7 +217,7 @@ class DatabaseHandler:
217
217
  raise ValueError(
218
218
  "Only one model version can be passed to get_model_parameter, not a list."
219
219
  )
220
- production_table = self._read_production_table_from_mongo_db(
220
+ production_table = self.read_production_table_from_mongo_db(
221
221
  collection_name, model_version
222
222
  )
223
223
  array_element_list = self._get_array_element_list(
@@ -263,7 +263,7 @@ class DatabaseHandler:
263
263
  dict containing the parameters
264
264
  """
265
265
  pars = {}
266
- production_table = self._read_production_table_from_mongo_db(collection, model_version)
266
+ production_table = self.read_production_table_from_mongo_db(collection, model_version)
267
267
  array_element_list = self._get_array_element_list(
268
268
  array_element_name, site, production_table, collection
269
269
  )
@@ -409,6 +409,11 @@ class DatabaseHandler:
409
409
  Version of the model.
410
410
  export_file_as_table: bool
411
411
  If True, export the file as an astropy table (ecsv format).
412
+
413
+ Returns
414
+ -------
415
+ astropy.table.Table or None
416
+ If export_file_as_table is True
412
417
  """
413
418
  parameters = self.get_model_parameter(
414
419
  parameter,
@@ -518,9 +523,9 @@ class DatabaseHandler:
518
523
  parameters[par_now]["entry_date"] = ObjectId(post["_id"]).generation_time
519
524
  return {k: parameters[k] for k in sorted(parameters)}
520
525
 
521
- def _read_production_table_from_mongo_db(self, collection_name, model_version):
526
+ def read_production_table_from_mongo_db(self, collection_name, model_version):
522
527
  """
523
- Read production table from MongoDB.
528
+ Read production table for the given collection from MongoDB.
524
529
 
525
530
  Parameters
526
531
  ----------
@@ -591,7 +596,7 @@ class DatabaseHandler:
591
596
  list
592
597
  Sorted list of all array elements found in collection
593
598
  """
594
- production_table = self._read_production_table_from_mongo_db(collection, model_version)
599
+ production_table = self.read_production_table_from_mongo_db(collection, model_version)
595
600
  return sorted([entry for entry in production_table["parameters"] if "-design" not in entry])
596
601
 
597
602
  def get_design_model(self, model_version, array_element_name, collection="telescopes"):
@@ -613,7 +618,7 @@ class DatabaseHandler:
613
618
  str
614
619
  Design model for a given array element.
615
620
  """
616
- production_table = self._read_production_table_from_mongo_db(collection, model_version)
621
+ production_table = self.read_production_table_from_mongo_db(collection, model_version)
617
622
  try:
618
623
  return production_table["design_model"][array_element_name]
619
624
  except KeyError:
@@ -641,7 +646,7 @@ class DatabaseHandler:
641
646
  list
642
647
  Sorted list of all array element names found in collection
643
648
  """
644
- production_table = self._read_production_table_from_mongo_db(collection, model_version)
649
+ production_table = self.read_production_table_from_mongo_db(collection, model_version)
645
650
  all_array_elements = production_table["parameters"]
646
651
  return sorted(
647
652
  [
@@ -759,7 +764,7 @@ class DatabaseHandler:
759
764
  """
760
765
  db_name = self._get_db_name(db_name)
761
766
  collection = self.get_collection(db_name, "production_tables")
762
- self._logger.info(f"Adding production for {production_table.get('collection')} to to DB")
767
+ self._logger.debug(f"Adding production for {production_table.get('collection')} to to DB")
763
768
  collection.insert_one(production_table)
764
769
  DatabaseHandler.production_table_cached.clear()
765
770
 
@@ -809,13 +814,13 @@ class DatabaseHandler:
809
814
  raise ValueError(f"File is not UTF-8 encoded: {file_path}")
810
815
  files_to_add_to_db.add(f"{file_path}")
811
816
 
812
- self._logger.info(
817
+ self._logger.debug(
813
818
  f"Adding a new entry to DB {db_name} and collection {collection_name}:\n{par_dict}"
814
819
  )
815
820
  collection.insert_one(par_dict)
816
821
 
817
822
  for file_to_insert_now in files_to_add_to_db:
818
- self._logger.info(f"Will also add the file {file_to_insert_now} to the DB")
823
+ self._logger.debug(f"Will also add the file {file_to_insert_now} to the DB")
819
824
  self.insert_file_to_db(file_to_insert_now, db_name)
820
825
 
821
826
  self._reset_parameter_cache()
@@ -964,7 +969,7 @@ class DatabaseHandler:
964
969
  return [array_element_name]
965
970
  if collection == "configuration_sim_telarray":
966
971
  # get design model from 'telescope' or 'calibration_device' production tables
967
- production_table = self._read_production_table_from_mongo_db(
972
+ production_table = self.read_production_table_from_mongo_db(
968
973
  names.get_collection_name_from_array_element_name(array_element_name),
969
974
  production_table["model_version"],
970
975
  )
@@ -27,7 +27,7 @@ def add_values_from_json_to_db(file, collection, db, db_name, file_prefix):
27
27
  Path to location of all additional files to be uploaded.
28
28
  """
29
29
  par_dict = gen.collect_data_from_file(file_name=file)
30
- logger.info(
30
+ logger.debug(
31
31
  f"Adding the following parameter to the DB: {par_dict['parameter']} "
32
32
  f"version {par_dict['parameter_version']} "
33
33
  f"(collection {collection} in database {db_name})"
@@ -120,7 +120,7 @@ def _read_production_table(model_dict, file, model_name):
120
120
  },
121
121
  )
122
122
  parameter_dict = gen.collect_data_from_file(file_name=file)
123
- logger.info(f"Reading production table for {array_element} (collection {collection})")
123
+ logger.debug(f"Reading production table for {array_element} (collection {collection})")
124
124
  try:
125
125
  if array_element in ("configuration_corsika", "configuration_sim_telarray"):
126
126
  model_dict[collection]["parameters"] = parameter_dict["parameters"]