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
@@ -9,9 +9,11 @@ from pathlib import Path
9
9
  import astropy.units as u
10
10
  import numpy as np
11
11
 
12
+ from simtools import settings
12
13
  from simtools.io import io_handler
13
14
  from simtools.model.model_utils import initialize_simulation_models
14
15
  from simtools.runners.simtel_runner import SimtelRunner
16
+ from simtools.simtel.simtel_config_writer import SimtelConfigWriter
15
17
  from simtools.utils.general import clear_default_sim_telarray_cfg_directories
16
18
  from simtools.utils.geometry import fiducial_radius_from_shape
17
19
 
@@ -30,21 +32,18 @@ class SimulatorLightEmission(SimtelRunner):
30
32
  Label for the simulation
31
33
  """
32
34
 
33
- def __init__(self, light_emission_config, db_config=None, label=None):
35
+ def __init__(self, light_emission_config, label=None):
34
36
  """Initialize SimulatorLightEmission."""
35
37
  self._logger = logging.getLogger(__name__)
36
38
  self.io_handler = io_handler.IOHandler()
37
39
 
38
- super().__init__(
39
- simtel_path=light_emission_config.get("simtel_path"), label=label, corsika_config=None
40
- )
40
+ super().__init__(label=label, corsika_config=None)
41
41
 
42
42
  self.output_directory = self.io_handler.get_output_directory()
43
43
 
44
44
  self.telescope_model, self.site_model, self.calibration_model = (
45
45
  initialize_simulation_models(
46
46
  label=label,
47
- db_config=db_config,
48
47
  site=light_emission_config.get("site"),
49
48
  telescope_name=light_emission_config.get("telescope"),
50
49
  calibration_device_name=light_emission_config.get("light_source"),
@@ -67,7 +66,7 @@ class SimulatorLightEmission(SimtelRunner):
67
66
  config["flasher_photons"] = (
68
67
  self.calibration_model.get_parameter_value("flasher_photons")
69
68
  if not config.get("test", False)
70
- else 1e8
69
+ else 1e5
71
70
  )
72
71
 
73
72
  if config.get("light_source_position") is not None:
@@ -291,7 +290,7 @@ class SimulatorLightEmission(SimtelRunner):
291
290
  "corsika_observation_level"
292
291
  )
293
292
 
294
- parts = [str(self._simtel_path / "sim_telarray/LightEmission") + f"/{app_name}"]
293
+ parts = [str(settings.config.sim_telarray_path / "LightEmission") + f"/{app_name}"]
295
294
  parts.extend(self._get_site_command(app_name, config_directory, corsika_observation_level))
296
295
  parts.extend(self._get_light_source_command())
297
296
  if self.light_emission_config["light_source_type"] == "illuminator":
@@ -311,7 +310,7 @@ class SimulatorLightEmission(SimtelRunner):
311
310
  atmo_id = self._prepare_flasher_atmosphere_files(config_directory)
312
311
  return [
313
312
  "-I.",
314
- f"-I{self._simtel_path / 'sim_telarray/cfg'}",
313
+ f"-I{settings.config.sim_telarray_path / 'cfg'}",
315
314
  f"-I{config_directory}",
316
315
  f"--altitude {corsika_observation_level.to(u.m).value}",
317
316
  f"--atmosphere {atmo_id}",
@@ -335,18 +334,57 @@ class SimulatorLightEmission(SimtelRunner):
335
334
  def _add_flasher_command_options(self):
336
335
  """Add flasher options for all telescope types (ff-1m style)."""
337
336
  flasher_xyz = self.calibration_model.get_parameter_value_with_unit("flasher_position")
338
- camera_radius = fiducial_radius_from_shape(
337
+ camera_diam_cm = (
339
338
  self.telescope_model.get_parameter_value_with_unit("camera_body_diameter")
340
339
  .to(u.cm)
341
- .value,
342
- self.telescope_model.get_parameter_value("camera_body_shape"),
340
+ .value
343
341
  )
342
+ camera_shape = self.telescope_model.get_parameter_value("camera_body_shape")
343
+ camera_radius = fiducial_radius_from_shape(camera_diam_cm, camera_shape)
344
344
  flasher_wavelength = self.calibration_model.get_parameter_value_with_unit(
345
345
  "flasher_wavelength"
346
346
  )
347
347
  dist_cm = self.calculate_distance_focal_plane_calibration_device().to(u.cm).value
348
348
  angular_distribution = self._get_angular_distribution_string_for_sim_telarray()
349
349
 
350
+ # Build pulse table for ff-1m using unified list parameter [shape, width, exp]
351
+ pulse_shape_value = self.calibration_model.get_parameter_value("flasher_pulse_shape")
352
+ shape_name = pulse_shape_value[0]
353
+ width_ns = pulse_shape_value[1]
354
+ exp_ns = pulse_shape_value[2]
355
+ pulse_arg = self._get_pulse_shape_string_for_sim_telarray()
356
+
357
+ if shape_name == "Gauss-Exponential":
358
+ if width_ns <= 0 or exp_ns <= 0:
359
+ raise ValueError(
360
+ "Gauss-Exponential pulse shape requires positive width"
361
+ " and exponential decay values"
362
+ )
363
+ try:
364
+ base_dir = self.io_handler.get_output_directory("pulse_shapes")
365
+
366
+ def _sanitize_name(value):
367
+ return "".join(
368
+ ch if (ch.isalnum() or ch in ("-", "_")) else "_" for ch in str(value)
369
+ )
370
+
371
+ tel = self.light_emission_config.get("telescope") or "telescope"
372
+ cal = self.light_emission_config.get("light_source") or "calibration"
373
+ fname = f"flasher_pulse_shape_{_sanitize_name(tel)}_{_sanitize_name(cal)}.dat"
374
+ table_path = base_dir / fname
375
+ fadc_bins = self.telescope_model.get_parameter_value("fadc_sum_bins")
376
+
377
+ SimtelConfigWriter.write_light_pulse_table_gauss_exp_conv(
378
+ file_path=table_path,
379
+ width_ns=width_ns,
380
+ exp_decay_ns=exp_ns,
381
+ fadc_sum_bins=fadc_bins,
382
+ time_margin_ns=5.0,
383
+ )
384
+ pulse_arg = str(table_path)
385
+ except (ValueError, OSError) as err:
386
+ self._logger.warning(f"Failed to write pulse shape table, using token: {err}")
387
+
350
388
  return [
351
389
  f"--events {self.light_emission_config['number_of_events']}",
352
390
  f"--photons {self.light_emission_config['flasher_photons']}",
@@ -355,7 +393,7 @@ class SimulatorLightEmission(SimtelRunner):
355
393
  f"--distance {dist_cm}",
356
394
  f"--camera-radius {camera_radius}",
357
395
  f"--spectrum {int(flasher_wavelength.to(u.nm).value)}",
358
- f"--lightpulse {self._get_pulse_shape_string_for_sim_telarray()}",
396
+ f"--lightpulse {pulse_arg}",
359
397
  f"--angular-distribution {angular_distribution}",
360
398
  ]
361
399
 
@@ -398,7 +436,7 @@ class SimulatorLightEmission(SimtelRunner):
398
436
  """
399
437
  theta, phi = self._get_telescope_pointing()
400
438
 
401
- simtel_bin = self._simtel_path.joinpath("sim_telarray/bin/sim_telarray/")
439
+ simtel_bin = str(settings.config.sim_telarray_exe)
402
440
 
403
441
  parts = [
404
442
  f"{simtel_bin}",
@@ -466,6 +504,27 @@ class SimulatorLightEmission(SimtelRunner):
466
504
  )
467
505
  return focal_length - flasher_z
468
506
 
507
+ def _generate_lambertian_angular_distribution_table(self):
508
+ """Generate Lambertian angular distribution table via config writer and return path.
509
+
510
+ Uses a pure cosine profile normalized to 1 at 0 deg and spans 0..90 deg by default.
511
+ """
512
+ base_dir = self.io_handler.get_output_directory("angular_distributions")
513
+
514
+ def _sanitize_name(value):
515
+ return "".join(ch if (ch.isalnum() or ch in ("-", "_")) else "_" for ch in str(value))
516
+
517
+ tel = self.light_emission_config.get("telescope") or "telescope"
518
+ cal = self.light_emission_config.get("light_source") or "calibration"
519
+ fname = f"flasher_angular_distribution_{_sanitize_name(tel)}_{_sanitize_name(cal)}.dat"
520
+ table_path = base_dir / fname
521
+ SimtelConfigWriter.write_angular_distribution_table_lambertian(
522
+ file_path=table_path,
523
+ max_angle_deg=90.0,
524
+ n_samples=100,
525
+ )
526
+ return str(table_path)
527
+
469
528
  def _get_angular_distribution_string_for_sim_telarray(self):
470
529
  """
471
530
  Get the angular distribution string for sim_telarray.
@@ -475,9 +534,18 @@ class SimulatorLightEmission(SimtelRunner):
475
534
  str
476
535
  The angular distribution string.
477
536
  """
478
- option_string = self.calibration_model.get_parameter_value(
479
- "flasher_angular_distribution"
480
- ).lower()
537
+ opt = self.calibration_model.get_parameter_value("flasher_angular_distribution")
538
+ option_string = str(opt).lower() if opt is not None else ""
539
+ if option_string == "lambertian":
540
+ try:
541
+ return self._generate_lambertian_angular_distribution_table()
542
+ except (OSError, ValueError) as err:
543
+ self._logger.warning(
544
+ f"Failed to write Lambertian angular distribution table: {err};"
545
+ f" using token instead."
546
+ )
547
+ return option_string
548
+
481
549
  width = self.calibration_model.get_parameter_value_with_unit(
482
550
  "flasher_angular_distribution_width"
483
551
  )
@@ -492,6 +560,20 @@ class SimulatorLightEmission(SimtelRunner):
492
560
  str
493
561
  The pulse shape string.
494
562
  """
495
- option_string = self.calibration_model.get_parameter_value("flasher_pulse_shape").lower()
496
- width = self.calibration_model.get_parameter_value_with_unit("flasher_pulse_width")
497
- return f"{option_string}:{width.to(u.ns).value}" if width is not None else option_string
563
+ opt = self.calibration_model.get_parameter_value("flasher_pulse_shape")
564
+ shape = opt[0].lower()
565
+ # Map internal shapes to sim_telarray expected tokens
566
+ # 'tophat' corresponds to a simple (flat) pulse in sim_telarray.
567
+ shape_token_map = {
568
+ "tophat": "simple",
569
+ }
570
+ shape_out = shape_token_map.get(shape, shape)
571
+ width = opt[1]
572
+ expv = opt[2]
573
+ if shape_out == "gauss-exponential" and width is not None and expv is not None:
574
+ return f"{shape_out}:{float(width)}:{float(expv)}"
575
+ if shape_out in ("gauss", "simple") and width is not None:
576
+ return f"{shape_out}:{float(width)}"
577
+ if shape_out == "exponential" and expv is not None:
578
+ return f"{shape_out}:{float(expv)}"
579
+ return shape_out
@@ -5,6 +5,7 @@ from collections import namedtuple
5
5
 
6
6
  import astropy.units as u
7
7
 
8
+ from simtools import settings
8
9
  from simtools.io import io_handler
9
10
  from simtools.runners.simtel_runner import SimtelRunner
10
11
  from simtools.utils import names
@@ -27,8 +28,6 @@ class SimulatorRayTracing(SimtelRunner):
27
28
  site model
28
29
  label: str
29
30
  label used for output file naming.
30
- simtel_path: str or Path
31
- Location of sim_telarray installation.
32
31
  config_data: namedtuple
33
32
  namedtuple containing the configurable parameters as values (expected units in
34
33
  brackets): zenith_angle (deg), off_axis_angle (deg), source_distance (km),
@@ -43,7 +42,6 @@ class SimulatorRayTracing(SimtelRunner):
43
42
  telescope_model,
44
43
  site_model,
45
44
  label=None,
46
- simtel_path=None,
47
45
  config_data=None,
48
46
  force_simulate=False,
49
47
  test=False,
@@ -52,7 +50,7 @@ class SimulatorRayTracing(SimtelRunner):
52
50
  self._logger = logging.getLogger(__name__)
53
51
  self._logger.debug("Init SimulatorRayTracing")
54
52
 
55
- super().__init__(label=label, simtel_path=simtel_path)
53
+ super().__init__(label=label)
56
54
 
57
55
  self.telescope_model = telescope_model
58
56
  self.site_model = site_model
@@ -83,10 +81,6 @@ class SimulatorRayTracing(SimtelRunner):
83
81
  force_simulate: bool
84
82
  Remove existing files and force re-running of the ray-tracing simulation.
85
83
  """
86
- # This file is not actually needed and does not exist in simtools.
87
- # It is required as CORSIKA input file to sim_telarray
88
- self._corsika_file = self._simtel_path.joinpath("run9991.corsika.gz")
89
-
90
84
  # Loop to define and remove existing files.
91
85
  # Files will be named _base_file = self.__dict__['_' + base + 'File']
92
86
  for base_name in ["stars", "photons", "log"]:
@@ -153,7 +147,7 @@ class SimulatorRayTracing(SimtelRunner):
153
147
  )
154
148
 
155
149
  # RayTracing
156
- command = str(self._simtel_path.joinpath("sim_telarray/bin/sim_telarray"))
150
+ command = str(settings.config.sim_telarray_exe)
157
151
  command += f" -c {self.telescope_model.config_file_path}"
158
152
  command += f" -I{self.telescope_model.config_file_directory}"
159
153
  command += super().get_config_option("random_state", "none")
@@ -197,7 +191,7 @@ class SimulatorRayTracing(SimtelRunner):
197
191
  command += super().get_config_option("parabolic_dish", "0")
198
192
  command += super().get_config_option("mirror_align_random_distance", "0.")
199
193
  command += super().get_config_option("mirror_align_random_vertical", "0.,28.,0.,0.")
200
- command += " " + str(self._corsika_file)
194
+ command += " " + str(settings.config.corsika_dummy_file)
201
195
 
202
196
  return clear_default_sim_telarray_cfg_directories(command), self._log_file, self._log_file
203
197