gammasimtools 0.24.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 (138) hide show
  1. {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/METADATA +2 -1
  2. {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/RECORD +134 -130
  3. {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/entry_points.txt +3 -1
  4. {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/licenses/LICENSE +1 -1
  5. simtools/_version.py +2 -2
  6. simtools/application_control.py +78 -0
  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 +5 -1
  21. simtools/applications/derive_pulse_shape_parameters.py +194 -0
  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 +64 -108
  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 +3 -4
  41. simtools/applications/simulate_illuminator.py +0 -1
  42. simtools/applications/simulate_pedestals.py +2 -6
  43. simtools/applications/simulate_prod.py +9 -28
  44. simtools/applications/simulate_prod_htcondor_generator.py +8 -1
  45. simtools/applications/submit_array_layouts.py +7 -7
  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_file_using_schema.py +49 -123
  51. simtools/applications/validate_optics.py +0 -13
  52. simtools/camera/camera_efficiency.py +1 -6
  53. simtools/camera/single_photon_electron_spectrum.py +2 -1
  54. simtools/configuration/commandline_parser.py +43 -8
  55. simtools/configuration/configurator.py +6 -11
  56. simtools/corsika/corsika_config.py +204 -99
  57. simtools/corsika/corsika_histograms.py +411 -1735
  58. simtools/corsika/primary_particle.py +1 -1
  59. simtools/data_model/metadata_collector.py +5 -2
  60. simtools/data_model/metadata_model.py +0 -4
  61. simtools/data_model/model_data_writer.py +27 -17
  62. simtools/data_model/schema.py +112 -5
  63. simtools/data_model/validate_data.py +80 -48
  64. simtools/db/db_handler.py +19 -8
  65. simtools/db/db_model_upload.py +2 -1
  66. simtools/db/mongo_db.py +133 -42
  67. simtools/dependencies.py +83 -44
  68. simtools/io/ascii_handler.py +4 -2
  69. simtools/io/table_handler.py +1 -1
  70. simtools/job_execution/htcondor_script_generator.py +0 -2
  71. simtools/layout/array_layout.py +4 -12
  72. simtools/layout/array_layout_utils.py +227 -58
  73. simtools/model/array_model.py +37 -18
  74. simtools/model/calibration_model.py +0 -4
  75. simtools/model/legacy_model_parameter.py +134 -0
  76. simtools/model/model_parameter.py +24 -14
  77. simtools/model/model_repository.py +18 -5
  78. simtools/model/model_utils.py +1 -6
  79. simtools/model/site_model.py +0 -4
  80. simtools/model/telescope_model.py +6 -11
  81. simtools/production_configuration/derive_corsika_limits.py +6 -11
  82. simtools/production_configuration/interpolation_handler.py +16 -16
  83. simtools/ray_tracing/incident_angles.py +5 -11
  84. simtools/ray_tracing/mirror_panel_psf.py +3 -7
  85. simtools/ray_tracing/psf_analysis.py +29 -27
  86. simtools/ray_tracing/psf_parameter_optimisation.py +822 -680
  87. simtools/ray_tracing/ray_tracing.py +6 -15
  88. simtools/reporting/docs_auto_report_generator.py +8 -13
  89. simtools/reporting/docs_read_parameters.py +70 -16
  90. simtools/runners/corsika_runner.py +15 -10
  91. simtools/runners/corsika_simtel_runner.py +9 -8
  92. simtools/runners/runner_services.py +17 -7
  93. simtools/runners/simtel_runner.py +11 -58
  94. simtools/runners/simtools_runner.py +2 -4
  95. simtools/schemas/model_parameters/flasher_pulse_exp_decay.schema.yml +2 -0
  96. simtools/schemas/model_parameters/flasher_pulse_shape.schema.yml +50 -0
  97. simtools/schemas/model_parameters/flasher_pulse_width.schema.yml +2 -0
  98. simtools/schemas/simulation_models_info.schema.yml +2 -0
  99. simtools/settings.py +154 -0
  100. simtools/sim_events/file_info.py +128 -0
  101. simtools/{simtel/simtel_io_event_histograms.py → sim_events/histograms.py} +25 -15
  102. simtools/{simtel/simtel_io_event_reader.py → sim_events/reader.py} +20 -17
  103. simtools/{simtel/simtel_io_event_writer.py → sim_events/writer.py} +84 -25
  104. simtools/simtel/pulse_shapes.py +273 -0
  105. simtools/simtel/simtel_config_writer.py +146 -22
  106. simtools/simtel/simtel_table_reader.py +6 -4
  107. simtools/simtel/simulator_array.py +62 -23
  108. simtools/simtel/simulator_camera_efficiency.py +4 -6
  109. simtools/simtel/simulator_light_emission.py +101 -19
  110. simtools/simtel/simulator_ray_tracing.py +4 -10
  111. simtools/simulator.py +360 -353
  112. simtools/telescope_trigger_rates.py +3 -4
  113. simtools/testing/assertions.py +115 -8
  114. simtools/testing/configuration.py +2 -3
  115. simtools/testing/helpers.py +2 -3
  116. simtools/testing/log_inspector.py +5 -1
  117. simtools/testing/sim_telarray_metadata.py +1 -1
  118. simtools/testing/validate_output.py +69 -23
  119. simtools/utils/general.py +37 -0
  120. simtools/utils/geometry.py +0 -77
  121. simtools/utils/names.py +7 -9
  122. simtools/version.py +37 -0
  123. simtools/visualization/legend_handlers.py +21 -10
  124. simtools/visualization/plot_array_layout.py +312 -41
  125. simtools/visualization/plot_corsika_histograms.py +143 -605
  126. simtools/visualization/plot_mirrors.py +834 -0
  127. simtools/visualization/plot_pixels.py +2 -4
  128. simtools/visualization/plot_psf.py +0 -1
  129. simtools/visualization/plot_simtel_event_histograms.py +4 -4
  130. simtools/visualization/plot_simtel_events.py +6 -11
  131. simtools/visualization/plot_tables.py +8 -19
  132. simtools/visualization/visualize.py +22 -2
  133. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +0 -160
  134. simtools/applications/print_version.py +0 -53
  135. simtools/io/hdf5_handler.py +0 -139
  136. simtools/simtel/simtel_io_file_info.py +0 -62
  137. {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/WHEEL +0 -0
  138. {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/top_level.txt +0 -0
@@ -11,6 +11,7 @@ import simtools.utils.general as gen
11
11
  from simtools.data_model import schema
12
12
  from simtools.db import db_handler
13
13
  from simtools.io import ascii_handler, io_handler
14
+ from simtools.model import legacy_model_parameter
14
15
  from simtools.simtel.simtel_config_writer import SimtelConfigWriter
15
16
  from simtools.utils import names, value_conversion
16
17
 
@@ -28,8 +29,6 @@ class ModelParameter:
28
29
 
29
30
  Parameters
30
31
  ----------
31
- db_config:
32
- Database configuration dictionary.
33
32
  model_version: str
34
33
  Version of the model (ex. 5.0.0).
35
34
  site: str
@@ -51,7 +50,6 @@ class ModelParameter:
51
50
 
52
51
  def __init__(
53
52
  self,
54
- db_config,
55
53
  model_version,
56
54
  site=None,
57
55
  array_element_name=None,
@@ -62,7 +60,9 @@ class ModelParameter:
62
60
  ):
63
61
  self._logger = logging.getLogger(__name__)
64
62
  self.io_handler = io_handler.IOHandler()
65
- self.db = db_handler.DatabaseHandler(db_config=db_config)
63
+ self.db = db_handler.DatabaseHandler()
64
+ if not self.db.is_configured():
65
+ raise RuntimeError("Database is not configured.")
66
66
 
67
67
  self.parameters = {}
68
68
  self._simulation_config_parameters = {sw: {} for sw in names.simulation_software()}
@@ -271,7 +271,6 @@ class ModelParameter:
271
271
  # Setting file name and the location
272
272
  config_file_name = names.simtel_config_file_name(
273
273
  self.site,
274
- self.model_version,
275
274
  telescope_model_name=self.name,
276
275
  label=self.label,
277
276
  )
@@ -328,35 +327,46 @@ class ModelParameter:
328
327
  )
329
328
  if self.overwrite_model_parameters:
330
329
  self.overwrite_parameters_from_file(self.overwrite_model_parameters)
331
- self._check_model_parameter_software_versions(self.parameters.keys())
330
+ self._check_model_parameter_versions(self.parameters)
332
331
 
333
332
  self._load_simulation_software_parameter()
334
333
  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
- )
334
+ self._check_model_parameter_versions(parameters, software_name=software_name)
338
335
 
339
- def _check_model_parameter_software_versions(self, parameter_list, software_name=None):
336
+ def _check_model_parameter_versions(self, parameters, software_name=None):
340
337
  """
341
- Ensure that model parameters are compatible with the installed software versions.
338
+ Ensure parameters follow the latest schema and are compatible with installed software.
342
339
 
343
340
  Compares software versions listed in schema files with the installed software versions
344
341
  (e.g., sim_telarray, CORSIKA).
345
342
 
343
+ For outdated model parameter schemas, legacy update functions are called to update
344
+ the parameters to the latest schema version.
345
+
346
346
  Parameters
347
347
  ----------
348
- parameter_list: list
349
- List containing model parameter names.
348
+ parameters: dict
349
+ Dictionary containing model parameters.
350
350
  software_name: str
351
351
  Name of the software for which the parameters are checked.
352
352
  """
353
- for par_name in parameter_list:
353
+ _legacy_updates = {}
354
+ for par_name, par_data in parameters.items():
354
355
  if par_name in (parameter_schema := names.model_parameters()):
355
356
  schema.validate_deprecation_and_version(
356
357
  data=parameter_schema[par_name],
357
358
  software_name=software_name,
358
359
  ignore_software_version=self.ignore_software_version,
359
360
  )
361
+ _latest_schema_version = parameter_schema[par_name]["schema_version"]
362
+ if par_data["model_parameter_schema_version"] != _latest_schema_version:
363
+ _legacy_updates.update(
364
+ legacy_model_parameter.update_parameter(
365
+ par_name, parameters, _latest_schema_version
366
+ )
367
+ )
368
+
369
+ legacy_model_parameter.apply_legacy_updates_to_parameters(parameters, _legacy_updates)
360
370
 
361
371
  def overwrite_model_parameter(self, par_name, value, parameter_version=None):
362
372
  """
@@ -361,7 +361,7 @@ def _get_changes_to_production(
361
361
  _version_changes, base_model_version = _get_changes_to_production(
362
362
  _changes_dict, simulation_models_path, update_type="full_update"
363
363
  )
364
- changes = _update_two_levels_in_changes_dict(changes, _version_changes)
364
+ changes = _update_two_levels_in_changes_dict(_version_changes, changes)
365
365
  # stop iterative loop after reaching first full version of production tables
366
366
  if _changes_dict.get("model_update", "full_update") == "full_update":
367
367
  break
@@ -461,7 +461,7 @@ def _create_new_model_parameter_entry(telescope, param, param_data, simulation_m
461
461
 
462
462
  param_dir = telescope_dir / param
463
463
  try:
464
- latest_file = _get_latest_model_parameter_file(param_dir, param)
464
+ latest_file = _get_latest_model_parameter_file(param_dir, param, param_data["version"])
465
465
  except FileNotFoundError:
466
466
  latest_file = None
467
467
 
@@ -484,10 +484,11 @@ def _create_new_model_parameter_entry(telescope, param, param_data, simulation_m
484
484
  output_path=param_dir,
485
485
  unit=param_data.get("unit"),
486
486
  meta_parameter=param_data.get("meta_parameter", False),
487
+ model_parameter_schema_version=param_data.get("model_parameter_schema_version", None),
487
488
  )
488
489
 
489
490
 
490
- def _get_latest_model_parameter_file(directory, parameter):
491
+ def _get_latest_model_parameter_file(directory, parameter, max_version):
491
492
  """
492
493
  Get the latest model parameter JSON file for a parameter in the given directory.
493
494
 
@@ -499,11 +500,14 @@ def _get_latest_model_parameter_file(directory, parameter):
499
500
  Path to the directory containing parameter JSON files.
500
501
  parameter: str
501
502
  Name of the parameter to find.
503
+ max_version: str
504
+ Maximum version to consider (inclusive). Files with versions greater than
505
+ this will be excluded.
502
506
 
503
507
  Returns
504
508
  -------
505
509
  str
506
- Path to the latest JSON file for the parameter.
510
+ Path to the latest JSON file for the parameter with version <= max_version.
507
511
 
508
512
  Raises
509
513
  ------
@@ -521,7 +525,16 @@ def _get_latest_model_parameter_file(directory, parameter):
521
525
  # version is part after first '-'
522
526
  return parse_version(path.stem.split("-", 1)[1])
523
527
 
524
- latest_file = max(files, key=extract_version)
528
+ max_ver = parse_version(max_version)
529
+ filtered_files = [f for f in files if extract_version(f) <= max_ver]
530
+
531
+ if not filtered_files:
532
+ raise FileNotFoundError(
533
+ f"No JSON files found for parameter '{parameter}' with version <= {max_version} "
534
+ f"in directory '{directory}'."
535
+ )
536
+
537
+ latest_file = max(filtered_files, key=extract_version)
525
538
  return str(latest_file)
526
539
 
527
540
 
@@ -10,7 +10,7 @@ from simtools.utils import names
10
10
 
11
11
 
12
12
  def initialize_simulation_models(
13
- label, db_config, model_version, site, telescope_name, calibration_device_name=None
13
+ label, model_version, site, telescope_name, calibration_device_name=None
14
14
  ):
15
15
  """
16
16
  Initialize simulation models for a single telescope, site, and calibration device model.
@@ -19,8 +19,6 @@ def initialize_simulation_models(
19
19
  ----------
20
20
  label: str
21
21
  Label for the simulation.
22
- db_config: dict
23
- Database configuration.
24
22
  model_version: str
25
23
  Version of the simulation model
26
24
  site: str
@@ -38,21 +36,18 @@ def initialize_simulation_models(
38
36
  tel_model = TelescopeModel(
39
37
  site=site,
40
38
  telescope_name=telescope_name,
41
- db_config=db_config,
42
39
  model_version=model_version,
43
40
  label=label,
44
41
  )
45
42
  site_model = SiteModel(
46
43
  site=site,
47
44
  model_version=model_version,
48
- db_config=db_config,
49
45
  label=label,
50
46
  )
51
47
  if calibration_device_name is not None:
52
48
  calibration_model = CalibrationModel(
53
49
  site=site,
54
50
  calibration_device_model_name=calibration_device_name,
55
- db_config=db_config,
56
51
  model_version=model_version,
57
52
  label=label,
58
53
  )
@@ -26,8 +26,6 @@ class SiteModel(ModelParameter):
26
26
  ----------
27
27
  site: str
28
28
  Site name (e.g., South or North).
29
- db_config: dict
30
- Database configuration.
31
29
  model_version: str or list
32
30
  Model version or list of model versions (in which case only the first one is used).
33
31
  label: str, optional
@@ -41,7 +39,6 @@ class SiteModel(ModelParameter):
41
39
  def __init__(
42
40
  self,
43
41
  site,
44
- db_config,
45
42
  model_version,
46
43
  label=None,
47
44
  overwrite_model_parameters=None,
@@ -52,7 +49,6 @@ class SiteModel(ModelParameter):
52
49
  self._logger.debug("Init SiteModel for site %s", site)
53
50
  super().__init__(
54
51
  site=site,
55
- db_config=db_config,
56
52
  model_version=model_version,
57
53
  label=label,
58
54
  collection="sites",
@@ -26,8 +26,6 @@ class TelescopeModel(ModelParameter):
26
26
  Site name (e.g., South or North).
27
27
  telescope_name: str
28
28
  Telescope name (ex. LSTN-01, LSTN-design, ...).
29
- db_config: dict
30
- Database configuration.
31
29
  model_version: str
32
30
  Model version.
33
31
  label: str, optional
@@ -42,7 +40,6 @@ class TelescopeModel(ModelParameter):
42
40
  self,
43
41
  site,
44
42
  telescope_name,
45
- db_config,
46
43
  model_version,
47
44
  label=None,
48
45
  overwrite_model_parameters=None,
@@ -52,7 +49,6 @@ class TelescopeModel(ModelParameter):
52
49
  super().__init__(
53
50
  site=site,
54
51
  array_element_name=telescope_name,
55
- db_config=db_config,
56
52
  model_version=model_version,
57
53
  label=label,
58
54
  overwrite_model_parameters=overwrite_model_parameters,
@@ -326,13 +322,12 @@ class TelescopeModel(ModelParameter):
326
322
  average_curve: astropy.table.Table
327
323
  Instance of astropy.table.Table with the averaged curve.
328
324
  """
329
- weights = []
330
- for angle_now in curves["Angle"]:
331
- weights.append(
332
- incidence_angle_dist["Fraction"][
333
- np.nanargmin(np.abs(angle_now - incidence_angle_dist["Incidence angle"].value))
334
- ]
335
- )
325
+ weights = [
326
+ incidence_angle_dist["Fraction"][
327
+ np.nanargmin(np.abs(angle_now - incidence_angle_dist["Incidence angle"].value))
328
+ ]
329
+ for angle_now in curves["Angle"]
330
+ ]
336
331
 
337
332
  return Table(
338
333
  [curves["Wavelength"], np.average(curves["z"], weights=weights, axis=0)],
@@ -10,13 +10,13 @@ from astropy.table import Column, Table
10
10
  from simtools.data_model.metadata_collector import MetadataCollector
11
11
  from simtools.io import ascii_handler, io_handler
12
12
  from simtools.layout.array_layout_utils import get_array_elements_from_db_for_layouts
13
- from simtools.simtel.simtel_io_event_histograms import SimtelIOEventHistograms
13
+ from simtools.sim_events.histograms import EventDataHistograms
14
14
  from simtools.visualization import plot_simtel_event_histograms
15
15
 
16
16
  _logger = logging.getLogger(__name__)
17
17
 
18
18
 
19
- def generate_corsika_limits_grid(args_dict, db_config=None):
19
+ def generate_corsika_limits_grid(args_dict):
20
20
  """
21
21
  Generate CORSIKA limits.
22
22
 
@@ -24,15 +24,12 @@ def generate_corsika_limits_grid(args_dict, db_config=None):
24
24
  ----------
25
25
  args_dict : dict
26
26
  Dictionary containing command line arguments.
27
- db_config : dict, optional
28
- Database configuration dictionary.
29
27
  """
30
28
  if args_dict.get("array_layout_name"):
31
29
  telescope_configs = get_array_elements_from_db_for_layouts(
32
30
  args_dict["array_layout_name"],
33
31
  args_dict.get("site"),
34
32
  args_dict.get("model_version"),
35
- db_config,
36
33
  )
37
34
  else:
38
35
  telescope_configs = ascii_handler.collect_data_from_file(args_dict["telescope_ids"])[
@@ -81,9 +78,7 @@ def _process_file(file_path, array_name, telescope_ids, loss_fraction, plot_hist
81
78
  dict
82
79
  Dictionary containing the computed limits and metadata.
83
80
  """
84
- histograms = SimtelIOEventHistograms(
85
- file_path, array_name=array_name, telescope_list=telescope_ids
86
- )
81
+ histograms = EventDataHistograms(file_path, array_name=array_name, telescope_list=telescope_ids)
87
82
  histograms.fill()
88
83
 
89
84
  limits = {
@@ -257,7 +252,7 @@ def compute_lower_energy_limit(histograms, loss_fraction):
257
252
 
258
253
  Parameters
259
254
  ----------
260
- histograms : SimtelIOEventHistograms
255
+ histograms : EventDataHistograms
261
256
  Histograms.
262
257
  loss_fraction : float
263
258
  Fraction of events to be lost.
@@ -299,7 +294,7 @@ def compute_upper_radius_limit(histograms, loss_fraction):
299
294
 
300
295
  Parameters
301
296
  ----------
302
- histograms : SimtelIOEventHistograms
297
+ histograms : EventDataHistograms
303
298
  Histograms.
304
299
  loss_fraction : float
305
300
  Fraction of events to be lost.
@@ -337,7 +332,7 @@ def compute_viewcone(histograms, loss_fraction):
337
332
 
338
333
  Parameters
339
334
  ----------
340
- histograms : SimtelIOEventHistograms
335
+ histograms : EventDataHistograms
341
336
  Histograms.
342
337
  loss_fraction : float
343
338
  Fraction of events to be lost.
@@ -214,17 +214,15 @@ class InterpolationHandler:
214
214
  np.ndarray
215
215
  Reduced production grid points.
216
216
  """
217
- production_grid_points = []
218
-
219
- for point in self.grid_points_production:
220
- production_grid_points.append(
221
- [
222
- point["azimuth"]["value"],
223
- point["zenith_angle"]["value"],
224
- point["nsb"]["value"],
225
- point["offset"]["value"],
226
- ]
227
- )
217
+ production_grid_points = [
218
+ [
219
+ point["azimuth"]["value"],
220
+ point["zenith_angle"]["value"],
221
+ point["nsb"]["value"],
222
+ point["offset"]["value"],
223
+ ]
224
+ for point in self.grid_points_production
225
+ ]
228
226
 
229
227
  production_grid_points = np.array(production_grid_points)
230
228
 
@@ -280,11 +278,13 @@ class InterpolationHandler:
280
278
  reduced_production_grid_points = self._prepare_production_grid_points()
281
279
  energy_grid = self.energy_grids[0] if self.energy_grids else []
282
280
 
283
- energy_query_grid = []
284
- for energy in energy_grid:
285
- for grid_point in reduced_production_grid_points:
286
- energy_query_grid.append(np.hstack([energy.to(u.TeV).value, grid_point]))
287
-
281
+ energy_query_grid = [
282
+ [
283
+ np.hstack([energy.to(u.TeV).value, grid_point])
284
+ for energy in energy_grid
285
+ for grid_point in reduced_production_grid_points
286
+ ]
287
+ ]
288
288
  energy_query_grid = np.array(energy_query_grid)
289
289
 
290
290
  self._logger.debug(f"Grid points with energy shape: {grid_points_energy.shape}")
@@ -15,6 +15,7 @@ from pathlib import Path
15
15
  import astropy.units as u
16
16
  from astropy.table import QTable
17
17
 
18
+ from simtools import settings
18
19
  from simtools.data_model.metadata_collector import MetadataCollector
19
20
  from simtools.model.model_utils import initialize_simulation_models
20
21
 
@@ -24,10 +25,6 @@ class IncidentAnglesCalculator:
24
25
 
25
26
  Parameters
26
27
  ----------
27
- simtel_path : str or pathlib.Path
28
- Path to the sim_telarray installation directory (containing ``sim_telarray/bin``).
29
- db_config : dict
30
- Database configuration passed to ``initialize_simulation_models``.
31
28
  config_data : dict
32
29
  Simulation configuration (e.g. ``site``, ``telescope``, ``model_version``,
33
30
  ``off_axis_angle``, ``source_distance``, ``number_of_photons``).
@@ -48,15 +45,12 @@ class IncidentAnglesCalculator:
48
45
 
49
46
  def __init__(
50
47
  self,
51
- simtel_path,
52
- db_config,
53
48
  config_data,
54
49
  output_dir,
55
50
  label=None,
56
51
  ):
57
52
  self.logger = logging.getLogger(__name__)
58
53
 
59
- self._simtel_path = Path(simtel_path)
60
54
  self.config_data = config_data
61
55
  self.output_dir = Path(output_dir)
62
56
  self.label = label or f"incident_angles_{config_data['telescope']}"
@@ -82,7 +76,6 @@ class IncidentAnglesCalculator:
82
76
  )
83
77
  self.telescope_model, self.site_model, _ = initialize_simulation_models(
84
78
  label=self.label,
85
- db_config=db_config,
86
79
  site=config_data["site"],
87
80
  telescope_name=config_data["telescope"],
88
81
  model_version=config_data["model_version"],
@@ -216,8 +209,6 @@ class IncidentAnglesCalculator:
216
209
  Path to the generated shell script.
217
210
  """
218
211
  script_path = self.scripts_dir / f"run_incident_angles_{self._label_suffix()}.sh"
219
- simtel_bin = self._simtel_path / "sim_telarray/bin/sim_telarray_debug_trace"
220
- corsika_dummy = self._simtel_path / "sim_telarray/run9991.corsika.gz"
221
212
 
222
213
  theta = self.ZENITH_ANGLE_DEG
223
214
  off = float(self.config_data["off_axis_angle"].to_value(u.deg))
@@ -258,7 +249,10 @@ class IncidentAnglesCalculator:
258
249
  cfg("camera_filter", "none"),
259
250
  ]
260
251
 
261
- command = f"{simtel_bin} {' '.join(opts)} {corsika_dummy}"
252
+ command = (
253
+ f"{settings.config.sim_telarray_exe_debug_trace} {' '.join(opts)} "
254
+ f"{settings.config.corsika_dummy_file}"
255
+ )
262
256
  with script_path.open("w", encoding="utf-8") as sh:
263
257
  sh.write("#!/usr/bin/env bash\n\n")
264
258
  sh.write("set -e\nset -o pipefail\n\n")
@@ -28,17 +28,15 @@ class MirrorPanelPSF:
28
28
  Application label.
29
29
  args_dict: dict
30
30
  Dictionary with input arguments.
31
- db_config:
32
- Dictionary with database configuration.
33
31
  """
34
32
 
35
- def __init__(self, label, args_dict, db_config):
33
+ def __init__(self, label, args_dict):
36
34
  """Initialize the MirrorPanelPSF class."""
37
35
  self._logger = logging.getLogger(__name__)
38
36
  self._logger.debug("Initializing MirrorPanelPSF")
39
37
 
40
38
  self.args_dict = args_dict
41
- self.telescope_model, self.site_model = self._define_telescope_model(label, db_config)
39
+ self.telescope_model, self.site_model = self._define_telescope_model(label)
42
40
 
43
41
  if self.args_dict["test"]:
44
42
  self.args_dict["number_of_mirrors_to_test"] = 2
@@ -56,7 +54,7 @@ class MirrorPanelPSF:
56
54
  self.results_mean = []
57
55
  self.results_sig = []
58
56
 
59
- def _define_telescope_model(self, label, db_config):
57
+ def _define_telescope_model(self, label):
60
58
  """
61
59
  Define telescope model.
62
60
 
@@ -72,7 +70,6 @@ class MirrorPanelPSF:
72
70
  """
73
71
  tel_model, site_model, _ = initialize_simulation_models(
74
72
  label=label,
75
- db_config=db_config,
76
73
  site=self.args_dict["site"],
77
74
  telescope_name=self.args_dict["telescope"],
78
75
  model_version=self.args_dict["model_version"],
@@ -225,7 +222,6 @@ class MirrorPanelPSF:
225
222
  ray = RayTracing(
226
223
  telescope_model=self.telescope_model,
227
224
  site_model=self.site_model,
228
- simtel_path=self.args_dict.get("simtel_path", None),
229
225
  single_mirror_mode=True,
230
226
  mirror_numbers=(
231
227
  list(range(1, self.args_dict["number_of_mirrors_to_test"] + 1))
@@ -17,6 +17,7 @@ import astropy.units as u
17
17
  import matplotlib.pyplot as plt
18
18
  import numpy as np
19
19
 
20
+ from simtools import settings
20
21
  from simtools.utils.general import collect_kwargs, set_default_kwargs
21
22
 
22
23
 
@@ -36,8 +37,6 @@ class PSFImage:
36
37
  Scatter area of all photons in cm^2. If not given, effective area cannot be computed.
37
38
  containment_fraction: float
38
39
  Containment fraction for PSF calculation.
39
- simtel_path: str
40
- Path to sim_telarray installation.
41
40
  """
42
41
 
43
42
  __PSF_RADIUS = "Radius [cm]"
@@ -48,11 +47,9 @@ class PSFImage:
48
47
  focal_length=None,
49
48
  total_scattered_area=None,
50
49
  containment_fraction=None,
51
- simtel_path=None,
52
50
  ):
53
51
  """Initialize PSFImage class."""
54
52
  self._logger = logging.getLogger(__name__)
55
- self.simtel_path = simtel_path
56
53
 
57
54
  self._total_photons = None
58
55
  self._number_of_detected_photons = None
@@ -97,20 +94,23 @@ class PSFImage:
97
94
  Name of sim_telarray file with photon list.
98
95
  """
99
96
  try:
100
- rx_output = subprocess.Popen( # pylint: disable=consider-using-with
97
+ with subprocess.Popen(
101
98
  shlex.split(
102
- f"{self.simtel_path}/sim_telarray/bin/rx -f {self._containment_fraction:.2f} -v"
99
+ f"{settings.config.sim_telarray_path}/bin/rx "
100
+ f"-f {self._containment_fraction:.2f} -v"
103
101
  ),
104
102
  stdin=subprocess.PIPE,
105
103
  stdout=subprocess.PIPE,
106
- )
107
- with gzip.open(photon_file, "rb") as _stdin:
108
- with rx_output.stdin:
109
- shutil.copyfileobj(_stdin, rx_output.stdin)
110
- try:
111
- rx_output = rx_output.communicate()[0].splitlines()[-1:][0].split()
112
- except IndexError as e:
113
- raise IndexError(f"Unexpected output format from rx: {rx_output}") from e
104
+ ) as rx_process:
105
+ with gzip.open(photon_file, "rb") as _stdin:
106
+ with rx_process.stdin:
107
+ shutil.copyfileobj(_stdin, rx_process.stdin)
108
+ try:
109
+ rx_output = rx_process.communicate()[0].splitlines()[-1:][0].split()
110
+ except IndexError as e:
111
+ raise IndexError(
112
+ f"Unexpected output format from rx: {rx_process}"
113
+ ) from e
114
114
  except FileNotFoundError as e:
115
115
  raise FileNotFoundError(f"Photon list file not found: {photon_file}") from e
116
116
 
@@ -384,7 +384,6 @@ class PSFImage:
384
384
  if radius is not found (found_radius is False)
385
385
  """
386
386
  r0, r1 = rad_min, rad_min + dr
387
- s0, s1 = 0, 0
388
387
  found_radius = False
389
388
  while not found_radius:
390
389
  s0, s1 = self._sum_photons_in_radius(r0), self._sum_photons_in_radius(r1)
@@ -497,10 +496,10 @@ class PSFImage:
497
496
  radius_all = radius.to(u.cm).value if isinstance(radius, u.Quantity) else radius
498
497
  else:
499
498
  radius_all = list(np.linspace(0, 1.6 * self.get_psf(0.8), 30))
500
-
501
- intensity = []
502
- for rad in radius_all:
503
- intensity.append(self._sum_photons_in_radius(rad) / self._number_of_detected_photons)
499
+ intensity = [
500
+ self._sum_photons_in_radius(rad) / self._number_of_detected_photons
501
+ for rad in radius_all
502
+ ]
504
503
  d_type = {
505
504
  "names": (self.__PSF_RADIUS, self.__PSF_CUMULATIVE),
506
505
  "formats": ("f8", "f8"),
@@ -516,18 +515,21 @@ class PSFImage:
516
515
 
517
516
  Parameters
518
517
  ----------
518
+ file_name: str
519
+ Name of the file to save the plot to.
520
+ d80: float
521
+ d80 value to be marked in the plot (in cm).
519
522
  **kwargs:
520
- image_* for the histogram plot and psf_* for the psf circle.
523
+ Customization of line plot (e.g., color, linestyle, linewidth).
521
524
  """
522
525
  data = self.get_cumulative_data()
523
- ax = plt.gca()
524
- plt.tight_layout(pad=1.5)
526
+ fig, ax = plt.subplots(constrained_layout=True)
525
527
  ax.set_xlabel("Radius (cm)")
526
528
  ax.set_ylabel("Contained light %")
527
- plt.plot(data[self.__PSF_RADIUS], data[self.__PSF_CUMULATIVE], **kwargs)
528
- plt.axvline(x=self.get_psf(0.8) / 2, color="b", linestyle="--", linewidth=1)
529
+ ax.plot(data[self.__PSF_RADIUS], data[self.__PSF_CUMULATIVE], **kwargs)
530
+ ax.axvline(x=self.get_psf(0.8) / 2, color="b", linestyle="--", linewidth=1)
529
531
  if d80 is not None:
530
- plt.axvline(x=d80 / 2.0, color="r", linestyle="--", linewidth=1)
532
+ ax.axvline(x=d80 / 2.0, color="r", linestyle="--", linewidth=1)
531
533
  if file_name is not None:
532
- plt.savefig(file_name)
533
- plt.close()
534
+ fig.savefig(file_name)
535
+ plt.close(fig)