gammasimtools 0.25.0__py3-none-any.whl → 0.26.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 (125) hide show
  1. {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/METADATA +2 -1
  2. {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/RECORD +122 -121
  3. {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/entry_points.txt +2 -1
  4. {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/licenses/LICENSE +1 -1
  5. simtools/_version.py +2 -2
  6. simtools/application_control.py +35 -7
  7. simtools/applications/calculate_incident_angles.py +0 -2
  8. simtools/applications/convert_geo_coordinates_of_array_elements.py +1 -2
  9. simtools/applications/db_add_file_to_db.py +1 -1
  10. simtools/applications/db_add_simulation_model_from_repository_to_db.py +1 -1
  11. simtools/applications/db_add_value_from_json_to_db.py +1 -1
  12. simtools/applications/db_generate_compound_indexes.py +1 -1
  13. simtools/applications/db_get_array_layouts_from_db.py +2 -6
  14. simtools/applications/db_get_file_from_db.py +1 -1
  15. simtools/applications/db_get_parameter_from_db.py +1 -1
  16. simtools/applications/db_inspect_databases.py +1 -1
  17. simtools/applications/db_upload_model_repository.py +1 -1
  18. simtools/applications/derive_ctao_array_layouts.py +1 -2
  19. simtools/applications/derive_mirror_rnda.py +1 -3
  20. simtools/applications/derive_psf_parameters.py +0 -1
  21. simtools/applications/derive_pulse_shape_parameters.py +0 -1
  22. simtools/applications/derive_trigger_rates.py +1 -1
  23. simtools/applications/docs_produce_array_element_report.py +2 -8
  24. simtools/applications/docs_produce_calibration_reports.py +1 -3
  25. simtools/applications/docs_produce_model_parameter_reports.py +0 -2
  26. simtools/applications/docs_produce_simulation_configuration_report.py +1 -3
  27. simtools/applications/generate_array_config.py +0 -1
  28. simtools/applications/generate_corsika_histograms.py +48 -235
  29. simtools/applications/generate_regular_arrays.py +5 -35
  30. simtools/applications/generate_simtel_event_data.py +2 -2
  31. simtools/applications/maintain_simulation_model_add_production.py +2 -2
  32. simtools/applications/maintain_simulation_model_write_array_element_positions.py +87 -0
  33. simtools/applications/plot_array_layout.py +5 -111
  34. simtools/applications/plot_simulated_event_distributions.py +57 -0
  35. simtools/applications/plot_tabular_data.py +0 -1
  36. simtools/applications/plot_tabular_data_for_model_parameter.py +1 -6
  37. simtools/applications/production_derive_corsika_limits.py +1 -1
  38. simtools/applications/production_generate_grid.py +0 -1
  39. simtools/applications/run_application.py +1 -1
  40. simtools/applications/simulate_flasher.py +0 -2
  41. simtools/applications/simulate_illuminator.py +0 -1
  42. simtools/applications/simulate_pedestals.py +1 -5
  43. simtools/applications/simulate_prod.py +1 -5
  44. simtools/applications/simulate_prod_htcondor_generator.py +1 -1
  45. simtools/applications/submit_array_layouts.py +2 -4
  46. simtools/applications/submit_model_parameter_from_external.py +1 -3
  47. simtools/applications/validate_camera_efficiency.py +0 -1
  48. simtools/applications/validate_camera_fov.py +0 -1
  49. simtools/applications/validate_cumulative_psf.py +0 -2
  50. simtools/applications/validate_optics.py +0 -13
  51. simtools/camera/camera_efficiency.py +1 -6
  52. simtools/camera/single_photon_electron_spectrum.py +2 -1
  53. simtools/configuration/commandline_parser.py +35 -2
  54. simtools/configuration/configurator.py +6 -11
  55. simtools/corsika/corsika_config.py +16 -21
  56. simtools/corsika/corsika_histograms.py +411 -1735
  57. simtools/corsika/primary_particle.py +1 -1
  58. simtools/data_model/metadata_collector.py +5 -2
  59. simtools/data_model/metadata_model.py +0 -4
  60. simtools/data_model/model_data_writer.py +13 -15
  61. simtools/data_model/validate_data.py +1 -3
  62. simtools/db/db_handler.py +19 -8
  63. simtools/dependencies.py +81 -38
  64. simtools/io/ascii_handler.py +4 -2
  65. simtools/io/table_handler.py +1 -1
  66. simtools/layout/array_layout.py +4 -12
  67. simtools/layout/array_layout_utils.py +226 -57
  68. simtools/model/array_model.py +1 -13
  69. simtools/model/calibration_model.py +0 -4
  70. simtools/model/legacy_model_parameter.py +134 -0
  71. simtools/model/model_parameter.py +24 -13
  72. simtools/model/model_utils.py +1 -6
  73. simtools/model/site_model.py +0 -4
  74. simtools/model/telescope_model.py +6 -11
  75. simtools/production_configuration/derive_corsika_limits.py +6 -11
  76. simtools/production_configuration/interpolation_handler.py +16 -16
  77. simtools/ray_tracing/incident_angles.py +5 -11
  78. simtools/ray_tracing/mirror_panel_psf.py +3 -7
  79. simtools/ray_tracing/psf_analysis.py +18 -19
  80. simtools/ray_tracing/psf_parameter_optimisation.py +0 -1
  81. simtools/ray_tracing/ray_tracing.py +6 -15
  82. simtools/reporting/docs_auto_report_generator.py +8 -13
  83. simtools/reporting/docs_read_parameters.py +2 -8
  84. simtools/runners/corsika_runner.py +5 -9
  85. simtools/runners/corsika_simtel_runner.py +3 -8
  86. simtools/runners/simtel_runner.py +0 -5
  87. simtools/runners/simtools_runner.py +2 -4
  88. simtools/settings.py +154 -0
  89. simtools/{io/eventio_handler.py → sim_events/file_info.py} +3 -3
  90. simtools/{simtel/simtel_io_event_histograms.py → sim_events/histograms.py} +25 -15
  91. simtools/{simtel/simtel_io_event_reader.py → sim_events/reader.py} +20 -17
  92. simtools/{simtel/simtel_io_event_writer.py → sim_events/writer.py} +84 -25
  93. simtools/simtel/pulse_shapes.py +7 -2
  94. simtools/simtel/simtel_config_writer.py +79 -36
  95. simtools/simtel/simtel_table_reader.py +6 -4
  96. simtools/simtel/simulator_array.py +4 -11
  97. simtools/simtel/simulator_camera_efficiency.py +4 -6
  98. simtools/simtel/simulator_light_emission.py +69 -24
  99. simtools/simtel/simulator_ray_tracing.py +4 -10
  100. simtools/simulator.py +7 -14
  101. simtools/telescope_trigger_rates.py +3 -4
  102. simtools/testing/assertions.py +84 -33
  103. simtools/testing/configuration.py +1 -2
  104. simtools/testing/helpers.py +2 -3
  105. simtools/testing/log_inspector.py +1 -0
  106. simtools/testing/sim_telarray_metadata.py +1 -1
  107. simtools/testing/validate_output.py +34 -23
  108. simtools/utils/general.py +37 -0
  109. simtools/utils/geometry.py +0 -77
  110. simtools/utils/names.py +5 -5
  111. simtools/visualization/legend_handlers.py +7 -6
  112. simtools/visualization/plot_array_layout.py +91 -16
  113. simtools/visualization/plot_corsika_histograms.py +143 -605
  114. simtools/visualization/plot_mirrors.py +1 -4
  115. simtools/visualization/plot_pixels.py +2 -4
  116. simtools/visualization/plot_psf.py +0 -1
  117. simtools/visualization/plot_simtel_event_histograms.py +4 -4
  118. simtools/visualization/plot_simtel_events.py +6 -11
  119. simtools/visualization/plot_tables.py +8 -19
  120. simtools/visualization/visualize.py +22 -2
  121. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +0 -160
  122. simtools/applications/print_version.py +0 -53
  123. simtools/io/hdf5_handler.py +0 -139
  124. {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/WHEEL +0 -0
  125. {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/top_level.txt +0 -0
@@ -185,7 +185,7 @@ class PrimaryParticle:
185
185
  "silicon": {"corsika7_id": 2814},
186
186
  "iron": {"corsika7_id": 5626},
187
187
  }
188
- for _, ids in particles.items():
188
+ for ids in particles.values():
189
189
  ids["pdg_id"] = Corsika7ID(ids["corsika7_id"]).to_pdgid().numerator
190
190
  ids["pdg_name"] = Particle.findall(pdgid=ids["pdg_id"])[0].name
191
191
 
@@ -360,6 +360,9 @@ class MetadataCollector:
360
360
  "Metadata extraction from sim_telarray files is not supported yet."
361
361
  )
362
362
  continue
363
+ elif Path(metadata_file).name.endswith((".corsika.zst", ".corsika")):
364
+ self._logger.warning("Metadata extraction from CORSIKA files is not supported yet.")
365
+ continue
363
366
  else:
364
367
  raise ValueError(f"Unknown metadata file format: {metadata_file}")
365
368
 
@@ -422,7 +425,7 @@ class MetadataCollector:
422
425
  product_dict["creation_time"] = gen.now_date_time_in_isoformat()
423
426
  product_dict["description"] = self.schema_dict.get("description", None)
424
427
 
425
- # DATA:CATEGORY
428
+ # metadata DATA:CATEGORY
426
429
  product_dict["data"]["category"] = "SIM"
427
430
  product_dict["data"]["level"] = "R1"
428
431
  product_dict["data"]["type"] = "Service"
@@ -431,7 +434,7 @@ class MetadataCollector:
431
434
  except KeyError:
432
435
  pass
433
436
 
434
- # DATA:MODEL
437
+ # metadata DATA:MODEL
435
438
  product_dict["data"]["model"]["name"] = (
436
439
  self.schema_dict.get("name")
437
440
  or self.args_dict.get("metadata_product_data_name")
@@ -8,13 +8,9 @@ Follows CTAO top-level data model definition.
8
8
 
9
9
  """
10
10
 
11
- import logging
12
-
13
11
  import simtools.data_model.schema
14
12
  import simtools.utils.general as gen
15
13
 
16
- _logger = logging.getLogger(__name__)
17
-
18
14
 
19
15
  def get_default_metadata_dict(
20
16
  schema_file=None, observatory="CTA", schema_version="latest", lower_case=True
@@ -98,10 +98,10 @@ class ModelDataWriter:
98
98
  output_file,
99
99
  output_path=None,
100
100
  metadata_input_dict=None,
101
- db_config=None,
102
101
  unit=None,
103
102
  meta_parameter=False,
104
103
  model_parameter_schema_version=None,
104
+ check_db_for_existing_parameter=True,
105
105
  ):
106
106
  """
107
107
  Generate DB-style model parameter dict and write it to json file.
@@ -122,14 +122,14 @@ class ModelDataWriter:
122
122
  Path to output file.
123
123
  metadata_input_dict: dict
124
124
  Input to metadata collector.
125
- db_config: dict
126
- Database configuration. If not None, check if parameter with the same version exists.
127
125
  unit: str
128
126
  Unit of the parameter value (if applicable and value is not of type astropy Quantity).
129
127
  meta_parameter: bool
130
128
  Setting for meta parameter flag.
131
129
  model_parameter_schema_version: str, None
132
130
  Version of the model parameter schema (if None, use schema version from schema dict).
131
+ check_db_for_existing_parameter: bool
132
+ If True, check if parameter with same version exists in DB before writing.
133
133
 
134
134
  Returns
135
135
  -------
@@ -142,10 +142,8 @@ class ModelDataWriter:
142
142
  args_dict=None,
143
143
  output_path=output_path,
144
144
  )
145
- if db_config is not None:
146
- writer.check_db_for_existing_parameter(
147
- parameter_name, instrument, parameter_version, db_config
148
- )
145
+ if check_db_for_existing_parameter:
146
+ writer.check_db_for_existing_parameter(parameter_name, instrument, parameter_version)
149
147
 
150
148
  unique_id = None
151
149
  if metadata_input_dict is not None:
@@ -170,9 +168,7 @@ class ModelDataWriter:
170
168
  writer.write_dict_to_model_parameter_json(output_file, _json_dict)
171
169
  return _json_dict
172
170
 
173
- def check_db_for_existing_parameter(
174
- self, parameter_name, instrument, parameter_version, db_config
175
- ):
171
+ def check_db_for_existing_parameter(self, parameter_name, instrument, parameter_version):
176
172
  """
177
173
  Check if a parameter with the same version exists in the simulation model database.
178
174
 
@@ -184,15 +180,15 @@ class ModelDataWriter:
184
180
  Name of the instrument.
185
181
  parameter_version: str
186
182
  Version of the parameter.
187
- db_config: dict
188
- Database configuration.
189
183
 
190
184
  Raises
191
185
  ------
192
186
  ValueError
193
187
  If parameter with the same version exists in the database.
194
188
  """
195
- db = db_handler.DatabaseHandler(db_config=db_config)
189
+ db = db_handler.DatabaseHandler()
190
+ if not db.is_configured():
191
+ return
196
192
  try:
197
193
  db.get_model_parameter(
198
194
  parameter=parameter_name,
@@ -373,8 +369,10 @@ class ModelDataWriter:
373
369
  """
374
370
  try:
375
371
  unit_list = []
376
- for data in self.schema_dict["data"]:
377
- unit_list.append(data["unit"] if data["unit"] != "dimensionless" else None)
372
+ unit_list = [
373
+ data["unit"] if data["unit"] != "dimensionless" else None
374
+ for data in self.schema_dict["data"]
375
+ ]
378
376
  return unit_list if len(unit_list) > 1 else unit_list[0]
379
377
  except (KeyError, IndexError):
380
378
  pass
@@ -928,9 +928,7 @@ class DataValidator:
928
928
  """Return value as sorted list."""
929
929
  return [value] if isinstance(value, str) else sorted(value)
930
930
 
931
- instrument_sites = []
932
- for inst in instruments:
933
- instrument_sites.append(names.get_site_from_array_element_name(inst))
931
+ instrument_sites = [names.get_site_from_array_element_name(inst) for inst in instruments]
934
932
  # names.get_site_from_array_element_name might return a list
935
933
  flat_sites = [
936
934
  s
simtools/db/db_handler.py CHANGED
@@ -4,6 +4,7 @@ import logging
4
4
  from collections import defaultdict
5
5
  from pathlib import Path
6
6
 
7
+ from simtools import settings
7
8
  from simtools.data_model import validate_data
8
9
  from simtools.db.mongo_db import MongoDBHandler
9
10
  from simtools.io import io_handler
@@ -20,11 +21,6 @@ class DatabaseHandler:
20
21
 
21
22
  - db_simulation_model_version (from db_config): version of the simulation model database
22
23
  - model_version (from production_tables): version of the model contained in the database
23
-
24
- Parameters
25
- ----------
26
- db_config: dict
27
- Dictionary with the DB configuration.
28
24
  """
29
25
 
30
26
  ALLOWED_FILE_EXTENSIONS = [".dat", ".txt", ".lis", ".cfg", ".yml", ".yaml", ".ecsv"]
@@ -33,13 +29,17 @@ class DatabaseHandler:
33
29
  model_parameters_cached = {}
34
30
  model_versions_cached = {}
35
31
 
36
- def __init__(self, db_config=None):
32
+ def __init__(self):
37
33
  """Initialize the DatabaseHandler class."""
38
34
  self._logger = logging.getLogger(__name__)
39
35
 
40
- self.db_config = MongoDBHandler.validate_db_config(db_config)
36
+ self.db_config = (
37
+ MongoDBHandler.validate_db_config(dict(settings.config.db_config))
38
+ if settings.config.db_config
39
+ else None
40
+ )
41
41
  self.io_handler = io_handler.IOHandler()
42
- self.mongo_db_handler = MongoDBHandler(db_config) if self.db_config else None
42
+ self.mongo_db_handler = MongoDBHandler(self.db_config) if self.db_config else None
43
43
 
44
44
  self.db_name = (
45
45
  MongoDBHandler.get_db_name(
@@ -50,6 +50,17 @@ class DatabaseHandler:
50
50
  else None
51
51
  )
52
52
 
53
+ def is_configured(self):
54
+ """
55
+ Check if the DatabaseHandler is configured.
56
+
57
+ Returns
58
+ -------
59
+ bool
60
+ True if the DatabaseHandler is configured, False otherwise.
61
+ """
62
+ return self.mongo_db_handler is not None
63
+
53
64
  def get_db_name(self, db_name=None, db_simulation_model_version=None, model_name=None):
54
65
  """Build DB name from configuration."""
55
66
  if db_name:
simtools/dependencies.py CHANGED
@@ -9,27 +9,26 @@ This modules provides two main functionalities:
9
9
  """
10
10
 
11
11
  import logging
12
- import os
13
12
  import re
14
13
  import subprocess
15
14
  from pathlib import Path
16
15
 
17
16
  import yaml
18
17
 
18
+ from simtools import settings
19
19
  from simtools.io import ascii_handler
20
+ from simtools.utils import general as gen
20
21
  from simtools.version import __version__
21
22
 
22
23
  _logger = logging.getLogger(__name__)
23
24
 
24
25
 
25
- def get_version_string(db_config=None, run_time=None):
26
+ def get_version_string(run_time=None):
26
27
  """
27
28
  Print the versions of the dependencies.
28
29
 
29
30
  Parameters
30
31
  ----------
31
- db_config : dict, optional
32
- Database configuration dictionary.
33
32
  run_time : list, optional
34
33
  Runtime environment command (e.g., Docker).
35
34
 
@@ -40,10 +39,13 @@ def get_version_string(db_config=None, run_time=None):
40
39
 
41
40
  """
42
41
  return (
43
- f"Database name: {get_database_version_or_name(db_config, version=False)}\n"
44
- f"Database version: {get_database_version_or_name(db_config, version=True)}\n"
42
+ f"Database name: {get_database_version_or_name(version=False)}\n"
43
+ f"Database version: {get_database_version_or_name(version=True)}\n"
45
44
  f"sim_telarray version: {get_sim_telarray_version(run_time)}\n"
45
+ "sim_telarray exe: "
46
+ f"{settings.config.sim_telarray_exe if settings.config.sim_telarray_exe else 'None'}\n"
46
47
  f"CORSIKA version: {get_corsika_version(run_time)}\n"
48
+ f"CORSIKA exe: {settings.config.corsika_exe if settings.config.corsika_exe else 'None'}\n"
47
49
  f"Build options: {get_build_options(run_time)}\n"
48
50
  f"Runtime environment: {run_time if run_time else 'None'}\n"
49
51
  )
@@ -73,14 +75,12 @@ def get_software_version(software):
73
75
  raise ValueError(f"Unknown software: {software}") from exc
74
76
 
75
77
 
76
- def get_database_version_or_name(db_config, version=True):
78
+ def get_database_version_or_name(version=True):
77
79
  """
78
80
  Get the version or name of the simulation model data base used.
79
81
 
80
82
  Parameters
81
83
  ----------
82
- db_config : dict
83
- Dictionary containing the database configuration.
84
84
  version : bool
85
85
  If True, return the version of the database. If False, return the name.
86
86
 
@@ -91,8 +91,10 @@ def get_database_version_or_name(db_config, version=True):
91
91
 
92
92
  """
93
93
  if version:
94
- return db_config and db_config.get("db_simulation_model_version")
95
- return db_config and db_config.get("db_simulation_model")
94
+ return settings.config.db_config and settings.config.db_config.get(
95
+ "db_simulation_model_version"
96
+ )
97
+ return settings.config.db_config and settings.config.db_config.get("db_simulation_model")
96
98
 
97
99
 
98
100
  def get_sim_telarray_version(run_time=None):
@@ -111,16 +113,13 @@ def get_sim_telarray_version(run_time=None):
111
113
  str
112
114
  Version of the sim_telarray package.
113
115
  """
114
- sim_telarray_path = os.getenv("SIMTOOLS_SIMTEL_PATH")
115
- if sim_telarray_path is None:
116
- _logger.warning("Environment variable SIMTOOLS_SIMTEL_PATH is not set.")
116
+ if settings.config.sim_telarray_exe is None:
117
+ _logger.warning("sim_telarray environment not configured.")
117
118
  return None
118
- sim_telarray_path = Path(sim_telarray_path) / "sim_telarray" / "bin" / "sim_telarray"
119
-
120
119
  if run_time is None:
121
- command = [str(sim_telarray_path), "--version"]
120
+ command = [str(settings.config.sim_telarray_exe), "--version"]
122
121
  else:
123
- command = [*run_time, str(sim_telarray_path), "--version"]
122
+ command = [*run_time, str(settings.config.sim_telarray_exe), "--version"]
124
123
 
125
124
  _logger.debug(f"Running command: {command}")
126
125
  result = subprocess.run(command, capture_output=True, text=True, check=False)
@@ -151,19 +150,15 @@ def get_corsika_version(run_time=None):
151
150
  str
152
151
  Version of the CORSIKA package.
153
152
  """
154
- corsika_path = os.getenv("SIMTOOLS_SIMTEL_PATH")
155
- if corsika_path is None:
156
- _logger.warning("Environment variable SIMTOOLS_SIMTEL_PATH is not set.")
153
+ if settings.config.corsika_exe is None:
154
+ _logger.warning("CORSIKA environment not configured.")
157
155
  return None
158
- corsika_command = Path(corsika_path) / "corsika-run" / "corsika"
159
156
 
160
157
  if run_time is None:
161
- command = [str(corsika_command)]
158
+ command = [str(settings.config.corsika_exe)]
162
159
  else:
163
- command = [*run_time, str(corsika_command)]
160
+ command = [*run_time, str(settings.config.corsika_exe)]
164
161
 
165
- # Below I do not use the standard context manager because
166
- # it makes mocking in the tests significantly more difficult
167
162
  process = subprocess.Popen( # pylint: disable=consider-using-with
168
163
  command,
169
164
  stdout=subprocess.PIPE,
@@ -197,9 +192,14 @@ def get_corsika_version(run_time=None):
197
192
 
198
193
  def get_build_options(run_time=None):
199
194
  """
200
- Return CORSIKA / sim_telarray build options.
195
+ Return CORSIKA / sim_telarray config and build options.
196
+
197
+ For CORSIKA / sim_telarray build for simtools version >0.25.0:
198
+ expects build_opts.yml file in each CORSIKA and sim_telarray
199
+ directories.
201
200
 
202
- Expects a build_opts.yml file in the sim_telarray directory.
201
+ For CORSIKA / sim_telarray build for simtools version <=0.25.0:
202
+ expects a build_opts.yml file in the sim_telarray directory.
203
203
 
204
204
  Parameters
205
205
  ----------
@@ -209,28 +209,71 @@ def get_build_options(run_time=None):
209
209
  Returns
210
210
  -------
211
211
  dict
212
- Build options from build_opts.yml file.
212
+ CORSIKA / sim_telarray build options.
213
213
  """
214
- sim_telarray_path = os.getenv("SIMTOOLS_SIMTEL_PATH")
215
- if sim_telarray_path is None:
216
- raise ValueError("SIMTOOLS_SIMTEL_PATH not defined.")
217
-
218
- build_opts_path = Path(sim_telarray_path) / "build_opts.yml"
219
-
214
+ build_opts = {}
215
+ for package in ["corsika", "sim_telarray"]:
216
+ path = _get_package_path(package)
217
+ if not path:
218
+ continue
219
+ try:
220
+ build_opts.update(_get_build_options_from_file(path / "build_opts.yml", run_time))
221
+ except (FileNotFoundError, TypeError, ValueError):
222
+ # legacy fallback only for sim_telarray
223
+ if package == "sim_telarray":
224
+ try:
225
+ legacy_path = path.parent / "build_opts.yml"
226
+ build_opts.update(_get_build_options_from_file(legacy_path, run_time))
227
+ except (FileNotFoundError, TypeError, ValueError):
228
+ _logger.debug(f"No build options found for {package}.")
229
+ if not build_opts:
230
+ raise FileNotFoundError("No build option file found.")
231
+
232
+ return build_opts
233
+
234
+
235
+ def _get_package_path(package):
236
+ """Get the package path from settings or environment variables."""
237
+ path = getattr(settings.config, f"{package}_path")
238
+ if path is None:
239
+ path = gen.load_environment_variables().get(f"{package}_path")
240
+ return Path(path) if path else None
241
+
242
+
243
+ def _get_build_options_from_file(build_opts_path, run_time=None):
244
+ """Read build options from file."""
220
245
  if run_time is None:
221
246
  try:
222
247
  return ascii_handler.collect_data_from_file(build_opts_path)
223
248
  except FileNotFoundError as exc:
224
- raise FileNotFoundError("No build_opts.yml file found.") from exc
249
+ raise FileNotFoundError("No build option file found.") from exc
225
250
 
226
251
  command = [*run_time, "cat", str(build_opts_path)]
227
- _logger.debug(f"Reading build_opts.yml with command: {command}")
252
+ _logger.debug(f"Reading build option with command: {command}")
228
253
 
229
254
  result = subprocess.run(command, capture_output=True, text=True, check=False)
230
255
  if result.returncode:
231
- raise FileNotFoundError(f"No build_opts.yml file found in container: {result.stderr}")
256
+ raise FileNotFoundError(f"No build option file found in container: {result.stderr}")
232
257
 
233
258
  try:
234
259
  return yaml.safe_load(result.stdout)
235
260
  except yaml.YAMLError as exc:
236
261
  raise ValueError(f"Error parsing build_opts.yml from container: {exc}") from exc
262
+
263
+
264
+ def export_build_info(output_file, run_time=None):
265
+ """
266
+ Export build and version information to a file.
267
+
268
+ Parameters
269
+ ----------
270
+ output_file : str
271
+ Path to the output file.
272
+ run_time : list, optional
273
+ Runtime environment command (e.g., Docker).
274
+ """
275
+ build_info = get_build_options(run_time)
276
+ build_info["simtools"] = __version__
277
+ build_info["database_name"] = get_database_version_or_name(version=False)
278
+ build_info["database_version"] = get_database_version_or_name(version=True)
279
+ ascii_handler.write_data_to_file(data=build_info, output_file=Path(output_file))
@@ -208,9 +208,11 @@ def write_data_to_file(data, output_file, sort_keys=False, numpy_types=False):
208
208
  """
209
209
  output_file = Path(output_file)
210
210
  if output_file.suffix.lower() == ".json":
211
- return _write_to_json(data, output_file, sort_keys, numpy_types)
211
+ _write_to_json(data, output_file, sort_keys, numpy_types)
212
+ return
212
213
  if output_file.suffix.lower() in [".yml", ".yaml"]:
213
- return _write_to_yaml(data, output_file, sort_keys)
214
+ _write_to_yaml(data, output_file, sort_keys)
215
+ return
214
216
 
215
217
  raise ValueError(
216
218
  f"Unsupported file type {output_file.suffix}. Only .json, .yml, and .yaml are supported."
@@ -295,7 +295,7 @@ def write_table_in_hdf5(table, output_file, table_name):
295
295
  None
296
296
  """
297
297
  for col in table.colnames:
298
- if table[col].dtype.kind == "U": # hd5 does not support unicode
298
+ if table[col].dtype.kind == "U": # hdf5 does not support unicode
299
299
  table[col] = table[col].astype("S")
300
300
 
301
301
  with h5py.File(output_file, "a") as f:
@@ -32,8 +32,6 @@ class ArrayLayout:
32
32
 
33
33
  Parameters
34
34
  ----------
35
- db_config: dict
36
- Database configuration.
37
35
  site: str
38
36
  Site name or location (e.g., North/South or LaPalma/Paranal)
39
37
  model_version: str
@@ -52,7 +50,6 @@ class ArrayLayout:
52
50
 
53
51
  def __init__(
54
52
  self,
55
- db_config,
56
53
  site,
57
54
  model_version,
58
55
  label=None,
@@ -67,7 +64,6 @@ class ArrayLayout:
67
64
  self.model_version = model_version
68
65
  self.label = label
69
66
  self.name = name
70
- self.db_config = db_config
71
67
  self.site = None if site is None else names.validate_site_name(site)
72
68
  self.site_model = None
73
69
  self.io_handler = io_handler.IOHandler()
@@ -95,14 +91,11 @@ class ArrayLayout:
95
91
  def _initialize_site_parameters_from_db(self):
96
92
  """Initialize site parameters required for transformations using the database."""
97
93
  self._logger.debug("Initialize parameters from DB")
98
- if self.db_config is None:
99
- raise ValueError("No database configuration provided")
100
94
 
101
- self.site_model = SiteModel(
102
- site=self.site,
103
- model_version=self.model_version,
104
- db_config=self.db_config,
105
- )
95
+ try:
96
+ self.site_model = SiteModel(site=self.site, model_version=self.model_version)
97
+ except RuntimeError as e:
98
+ raise ValueError("No database configuration provided") from e
106
99
  self._corsika_observation_level = self.site_model.get_corsika_site_parameters().get(
107
100
  "corsika_observation_level", None
108
101
  )
@@ -419,7 +412,6 @@ class ArrayLayout:
419
412
  site=self.site,
420
413
  telescope_name=telescope_name,
421
414
  model_version=self.model_version,
422
- db_config=self.db_config,
423
415
  label=self.label,
424
416
  )
425
417