gammasimtools 0.25.0__py3-none-any.whl → 0.27.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.25.0.dist-info → gammasimtools-0.27.0.dist-info}/METADATA +6 -1
  2. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/RECORD +135 -130
  3. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/WHEEL +1 -1
  4. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/entry_points.txt +3 -2
  5. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/licenses/LICENSE +1 -1
  6. simtools/_version.py +2 -2
  7. simtools/application_control.py +35 -7
  8. simtools/applications/convert_geo_coordinates_of_array_elements.py +3 -3
  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 +3 -7
  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/{calculate_incident_angles.py → derive_incident_angle.py} +16 -18
  20. simtools/applications/derive_mirror_rnda.py +112 -180
  21. simtools/applications/derive_psf_parameters.py +0 -1
  22. simtools/applications/derive_pulse_shape_parameters.py +0 -1
  23. simtools/applications/derive_trigger_rates.py +1 -1
  24. simtools/applications/docs_produce_array_element_report.py +2 -8
  25. simtools/applications/docs_produce_calibration_reports.py +1 -3
  26. simtools/applications/docs_produce_model_parameter_reports.py +0 -2
  27. simtools/applications/docs_produce_simulation_configuration_report.py +1 -3
  28. simtools/applications/generate_array_config.py +0 -1
  29. simtools/applications/generate_corsika_histograms.py +79 -229
  30. simtools/applications/generate_regular_arrays.py +76 -69
  31. simtools/applications/generate_simtel_event_data.py +2 -2
  32. simtools/applications/maintain_simulation_model_add_production.py +2 -2
  33. simtools/applications/maintain_simulation_model_write_array_element_positions.py +87 -0
  34. simtools/applications/plot_array_layout.py +5 -111
  35. simtools/applications/plot_simulated_event_distributions.py +57 -0
  36. simtools/applications/plot_tabular_data.py +0 -1
  37. simtools/applications/plot_tabular_data_for_model_parameter.py +1 -6
  38. simtools/applications/production_derive_corsika_limits.py +1 -1
  39. simtools/applications/production_generate_grid.py +0 -1
  40. simtools/applications/run_application.py +1 -1
  41. simtools/applications/simulate_flasher.py +3 -15
  42. simtools/applications/simulate_illuminator.py +2 -11
  43. simtools/applications/simulate_pedestals.py +1 -5
  44. simtools/applications/simulate_prod.py +8 -11
  45. simtools/applications/simulate_prod_htcondor_generator.py +1 -1
  46. simtools/applications/submit_array_layouts.py +2 -4
  47. simtools/applications/submit_data_from_external.py +2 -1
  48. simtools/applications/submit_model_parameter_from_external.py +1 -3
  49. simtools/applications/validate_camera_efficiency.py +28 -28
  50. simtools/applications/validate_camera_fov.py +0 -1
  51. simtools/applications/validate_cumulative_psf.py +1 -5
  52. simtools/applications/validate_optics.py +2 -14
  53. simtools/atmosphere.py +83 -0
  54. simtools/camera/camera_efficiency.py +171 -53
  55. simtools/camera/single_photon_electron_spectrum.py +8 -7
  56. simtools/configuration/commandline_parser.py +82 -11
  57. simtools/configuration/configurator.py +6 -11
  58. simtools/constants.py +5 -0
  59. simtools/corsika/corsika_config.py +100 -202
  60. simtools/corsika/corsika_histograms.py +561 -1708
  61. simtools/corsika/primary_particle.py +1 -1
  62. simtools/data_model/metadata_collector.py +5 -2
  63. simtools/data_model/metadata_model.py +0 -4
  64. simtools/data_model/model_data_writer.py +59 -64
  65. simtools/data_model/schema.py +2 -0
  66. simtools/data_model/validate_data.py +1 -3
  67. simtools/db/db_handler.py +23 -10
  68. simtools/db/mongo_db.py +2 -2
  69. simtools/dependencies.py +81 -38
  70. simtools/io/ascii_handler.py +55 -5
  71. simtools/io/io_handler.py +23 -12
  72. simtools/io/table_handler.py +1 -1
  73. simtools/job_execution/job_manager.py +154 -79
  74. simtools/job_execution/process_pool.py +137 -0
  75. simtools/layout/array_layout.py +4 -13
  76. simtools/layout/array_layout_utils.py +348 -57
  77. simtools/model/array_model.py +23 -63
  78. simtools/model/calibration_model.py +4 -8
  79. simtools/model/legacy_model_parameter.py +134 -0
  80. simtools/model/model_parameter.py +147 -86
  81. simtools/model/model_utils.py +40 -6
  82. simtools/model/site_model.py +4 -8
  83. simtools/model/telescope_model.py +10 -16
  84. simtools/production_configuration/derive_corsika_limits.py +6 -11
  85. simtools/production_configuration/interpolation_handler.py +16 -16
  86. simtools/ray_tracing/incident_angles.py +92 -17
  87. simtools/ray_tracing/mirror_panel_psf.py +338 -222
  88. simtools/ray_tracing/psf_analysis.py +62 -48
  89. simtools/ray_tracing/psf_parameter_optimisation.py +3 -3
  90. simtools/ray_tracing/ray_tracing.py +43 -25
  91. simtools/reporting/docs_auto_report_generator.py +8 -13
  92. simtools/reporting/docs_read_parameters.py +2 -8
  93. simtools/runners/corsika_runner.py +52 -195
  94. simtools/runners/corsika_simtel_runner.py +77 -108
  95. simtools/runners/runner_services.py +214 -213
  96. simtools/runners/simtel_runner.py +27 -160
  97. simtools/runners/simtools_runner.py +11 -73
  98. simtools/schemas/application_workflow.metaschema.yml +8 -0
  99. simtools/settings.py +173 -0
  100. simtools/{io/eventio_handler.py → sim_events/file_info.py} +3 -3
  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 +7 -2
  105. simtools/simtel/simtel_config_writer.py +79 -91
  106. simtools/simtel/simtel_seeds.py +184 -0
  107. simtools/simtel/simtel_table_reader.py +6 -4
  108. simtools/simtel/simulator_array.py +114 -109
  109. simtools/simtel/simulator_camera_efficiency.py +68 -46
  110. simtools/simtel/simulator_light_emission.py +164 -132
  111. simtools/simtel/simulator_ray_tracing.py +80 -71
  112. simtools/simulator.py +137 -355
  113. simtools/telescope_trigger_rates.py +3 -4
  114. simtools/testing/assertions.py +84 -33
  115. simtools/testing/configuration.py +1 -2
  116. simtools/testing/helpers.py +2 -3
  117. simtools/testing/log_inspector.py +1 -0
  118. simtools/testing/sim_telarray_metadata.py +14 -12
  119. simtools/testing/validate_output.py +121 -42
  120. simtools/utils/general.py +43 -17
  121. simtools/utils/geometry.py +0 -77
  122. simtools/utils/names.py +5 -5
  123. simtools/utils/random.py +36 -0
  124. simtools/visualization/legend_handlers.py +7 -6
  125. simtools/visualization/plot_array_layout.py +91 -16
  126. simtools/visualization/plot_corsika_histograms.py +145 -605
  127. simtools/visualization/plot_incident_angles.py +48 -1
  128. simtools/visualization/plot_mirrors.py +1 -4
  129. simtools/visualization/plot_pixels.py +2 -4
  130. simtools/visualization/plot_psf.py +160 -19
  131. simtools/visualization/plot_simtel_event_histograms.py +4 -4
  132. simtools/visualization/plot_simtel_events.py +6 -11
  133. simtools/visualization/plot_tables.py +8 -19
  134. simtools/visualization/visualize.py +22 -2
  135. simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +0 -160
  136. simtools/applications/print_version.py +0 -53
  137. simtools/io/hdf5_handler.py +0 -139
  138. {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,184 @@
1
+ """Seeds for sim_telarray simulations."""
2
+
3
+ import logging
4
+ from pathlib import Path
5
+
6
+ from simtools import settings
7
+ from simtools.constants import SIMTEL_MAX_SEED
8
+ from simtools.io import ascii_handler
9
+ from simtools.utils import names, random
10
+ from simtools.version import semver_to_int
11
+
12
+
13
+ class SimtelSeeds:
14
+ """Manage seeds for sim_telarray simulations."""
15
+
16
+ def __init__(
17
+ self, output_path=None, site=None, model_version=None, zenith_angle=None, azimuth_angle=None
18
+ ):
19
+ """
20
+ Initialize seeds for sim_telarray simulations.
21
+
22
+ Two seeds are set for sim_telarray simulations:
23
+
24
+ - Instrument seed: used to randomize the instrument setup
25
+ - Simulation seed: used for the shower simulation
26
+
27
+ Parameters
28
+ ----------
29
+ output_path : str or Path or None
30
+ Output path for the seed file.
31
+ site : str or None
32
+ Site name.
33
+ model_version : str or None
34
+ Model version.
35
+ zenith_angle : float or None
36
+ Zenith angle.
37
+ azimuth_angle : float or None
38
+ Azimuth angle.
39
+ """
40
+ self._logger = logging.getLogger(__name__)
41
+
42
+ self.instrument_seed = settings.config.args.get("sim_telarray_instrument_seed", None)
43
+ self.instruments = settings.config.args.get(
44
+ "sim_telarray_random_instrument_instances", None
45
+ )
46
+ self.simulation_seed = settings.config.args.get("sim_telarray_seed", None)
47
+ self.seed_file = settings.config.args.get("sim_telarray_seed_file", None)
48
+ if output_path is not None:
49
+ self.seed_file = Path(output_path) / self.seed_file
50
+
51
+ self.seed_string = self.initialize_seeds(site, model_version, zenith_angle, azimuth_angle)
52
+
53
+ def initialize_seeds(self, site, model_version, zenith_angle, azimuth_angle):
54
+ """Initialize seeds based on provided parameters."""
55
+ if isinstance(self.simulation_seed, list):
56
+ return self._set_fixed_seeds()
57
+
58
+ if not self.simulation_seed:
59
+ self.simulation_seed = random.seeds(max_seed=SIMTEL_MAX_SEED)
60
+
61
+ if not self.instruments or self.instruments <= 1:
62
+ return self._generate_seed_pair()
63
+
64
+ return self._generate_seeds_with_file(site, model_version, zenith_angle, azimuth_angle)
65
+
66
+ def _set_fixed_seeds(self):
67
+ """
68
+ Set fixed seeds to be using for testing purposes only.
69
+
70
+ Fixes both instrument and simulation seeds.
71
+ """
72
+ try:
73
+ seed_string = f"{self.simulation_seed[0]},{self.simulation_seed[1]}"
74
+ except IndexError as exc:
75
+ raise IndexError(
76
+ "Two seeds must be provided for testing purposes: "
77
+ "first for instrument, second for shower simulation."
78
+ ) from exc
79
+ self._logger.warning(f"Using fixed test seeds: {seed_string}")
80
+ return seed_string
81
+
82
+ def _generate_seed_pair(self):
83
+ """Generate seed string."""
84
+ if not self.instrument_seed:
85
+ self.instrument_seed = random.seeds(max_seed=SIMTEL_MAX_SEED)
86
+
87
+ self._logger.info(
88
+ f"Generated sim_telarray seeds - Instrument: {self.instrument_seed}, "
89
+ f"Shower simulation: {self.simulation_seed}"
90
+ )
91
+ return f"{self.instrument_seed},{self.simulation_seed}"
92
+
93
+ def _generate_seeds_with_file(self, site, model_version, zenith_angle, azimuth_angle):
94
+ """Generate a seed file for the instrument seeds and return the seed string."""
95
+ self.instrument_seed = self._get_instrument_seed(
96
+ site, model_version, zenith_angle, azimuth_angle
97
+ )
98
+
99
+ self._logger.info(
100
+ f"Writing random instrument seed file {self.seed_file}"
101
+ f" (instrument seed {self.instrument_seed})"
102
+ )
103
+ if self.instruments > 1024:
104
+ raise ValueError("Number of random instances of instrument must be less than 1024")
105
+ random_integers = random.seeds(
106
+ n_seeds=self.instruments,
107
+ max_seed=SIMTEL_MAX_SEED,
108
+ fixed_seed=self.instrument_seed,
109
+ )
110
+ with open(self.seed_file, "w", encoding="utf-8") as file:
111
+ file.write(
112
+ "# Random seeds for instrument configuration generated with seed "
113
+ f"{self.instrument_seed} (model version {model_version}, site {site})\n"
114
+ f"# Zenith angle: {zenith_angle}, Azimuth angle: {azimuth_angle}\n"
115
+ )
116
+ for number in random_integers:
117
+ file.write(f"{number}\n")
118
+
119
+ return f"file-by-run:{self.seed_file},{self.simulation_seed}"
120
+
121
+ def _get_instrument_seed(self, site, model_version, zenith_angle, azimuth_angle):
122
+ """
123
+ Get configuration dependent instrument seed.
124
+
125
+ Three different scenarios are possible:
126
+
127
+ - instrument seed provided through configuration: use it
128
+ - site, model_version, zenith_angle, azimuth_angle provided:
129
+ generate a seed based on these parameters
130
+ - none of the above: generate a random seed
131
+
132
+ Parameters
133
+ ----------
134
+ site : str or None
135
+ Site name.
136
+ model_version : str or None
137
+ Model version.
138
+ zenith_angle : float or None
139
+ Zenith angle.
140
+ azimuth_angle : float or None
141
+ Azimuth angle.
142
+
143
+ Returns
144
+ -------
145
+ int
146
+ Instrument seed.
147
+ """
148
+ # Use the instrument seed from the configuration if provided
149
+ if self.instrument_seed:
150
+ return self.instrument_seed
151
+
152
+ # Generate a seed based on site, model_version, zenith_angle, and azimuth_angle
153
+ if model_version and zenith_angle is not None and azimuth_angle is not None:
154
+ try:
155
+ key_index = next(
156
+ i + 1
157
+ for i, (_, values) in enumerate(names.site_names().items())
158
+ if site.lower() in values
159
+ )
160
+ except StopIteration as exc:
161
+ raise ValueError(f"Unknown site: {site!r}") from exc
162
+
163
+ seed = semver_to_int(model_version) * 10000000
164
+ seed = seed + key_index * 1000000
165
+ seed = seed + int(zenith_angle) * 1000
166
+ return seed + int(azimuth_angle)
167
+
168
+ # Generate a random instrument seed
169
+ return random.seeds(max_seed=SIMTEL_MAX_SEED)
170
+
171
+ def save_seeds(self, path):
172
+ """
173
+ Save the seeds to a file.
174
+
175
+ Parameters
176
+ ----------
177
+ path : str or Path
178
+ Path to the seed file.
179
+ """
180
+ seed_dict = {
181
+ "instrument_seed": self.instrument_seed,
182
+ "simulation_seed": self.simulation_seed,
183
+ }
184
+ ascii_handler.write_data_to_file(path, seed_dict)
@@ -154,14 +154,16 @@ def _data_columns_mirror_reflectivity(n_columns, n_dim):
154
154
  {"name": "wavelength", "description": "Wavelength", "unit": "nm"},
155
155
  ]
156
156
  if n_dim:
157
- for angle in n_dim:
158
- _columns.append(
157
+ _columns.extend(
158
+ [
159
159
  {
160
160
  "name": f"reflectivity_{angle}deg",
161
161
  "description": f"Mirror reflectivity at {angle} deg",
162
162
  "unit": None,
163
- },
164
- )
163
+ }
164
+ for angle in n_dim
165
+ ]
166
+ )
165
167
  else:
166
168
  _columns.append(
167
169
  {
@@ -1,11 +1,10 @@
1
1
  """Simulation runner for array simulations."""
2
2
 
3
- import logging
4
- import stat
3
+ from pathlib import Path
5
4
 
5
+ from simtools import settings
6
6
  from simtools.io import io_handler
7
- from simtools.runners.simtel_runner import InvalidOutputFileError, SimtelRunner
8
- from simtools.utils.general import clear_default_sim_telarray_cfg_directories
7
+ from simtools.runners.simtel_runner import SimtelRunner, sim_telarray_env_as_string
9
8
 
10
9
 
11
10
  class SimulatorArray(SimtelRunner):
@@ -16,68 +15,46 @@ class SimulatorArray(SimtelRunner):
16
15
  ----------
17
16
  corsika_config_data: CorsikaConfig
18
17
  CORSIKA configuration.
19
- simtel_path: str or Path
20
- Location of source of the sim_telarray/CORSIKA package.
21
18
  label: str
22
19
  Instance label.
23
20
  use_multipipe: bool
24
21
  Use multipipe to run CORSIKA and sim_telarray.
25
- sim_telarray_seeds: dict
26
- Dictionary with configuration for sim_telarray random instrument setup.
22
+ is_calibration_run: bool
23
+ Flag to indicate if this is a calibration run.
27
24
  """
28
25
 
29
26
  def __init__(
30
27
  self,
31
28
  corsika_config,
32
- simtel_path,
33
29
  label=None,
34
- use_multipipe=False,
35
- sim_telarray_seeds=None,
36
- calibration_config=None,
30
+ is_calibration_run=False,
37
31
  ):
38
32
  """Initialize SimulatorArray."""
39
- self._logger = logging.getLogger(__name__)
40
- self._logger.debug("Init SimulatorArray")
41
- super().__init__(
42
- label=label,
43
- simtel_path=simtel_path,
44
- corsika_config=corsika_config,
45
- use_multipipe=use_multipipe,
46
- calibration_run_mode=calibration_config.get("run_mode") if calibration_config else None,
47
- )
33
+ super().__init__(label=label, config=corsika_config, is_calibration_run=is_calibration_run)
48
34
 
49
- self.sim_telarray_seeds = sim_telarray_seeds
50
35
  self.corsika_config = corsika_config
51
- self.calibration_config = calibration_config
36
+ self.is_calibration_run = is_calibration_run
52
37
  self.io_handler = io_handler.IOHandler()
53
38
  self._log_file = None
54
39
 
55
- def prepare_run_script(self, test=False, input_file=None, run_number=None, extra_commands=None):
40
+ def prepare_run(self, run_number=None, sub_script=None, corsika_file=None, extra_commands=None):
56
41
  """
57
42
  Build and return the full path of the bash run script containing the sim_telarray command.
58
43
 
59
44
  Parameters
60
45
  ----------
61
- test: bool
62
- Test flag for faster execution.
63
- input_file: str or Path
64
- Full path of the input CORSIKA file.
65
46
  run_number: int
66
47
  Run number.
48
+ corsika_file: str or Path
49
+ Full path of the input CORSIKA file.
67
50
  extra_commands: list[str]
68
51
  Additional commands for running simulations given in config.yml.
69
-
70
- Returns
71
- -------
72
- Path
73
- Full path of the run script.
74
52
  """
75
- script_file_path = self.get_file_name(file_type="sub_script", run_number=run_number)
76
- self._logger.debug(f"Run bash script - {script_file_path}")
53
+ command = self.make_run_command(run_number=run_number, input_file=corsika_file)
54
+ sub_script = Path(sub_script)
55
+ self._logger.debug(f"Run bash script - {sub_script}")
77
56
  self._logger.debug(f"Extra commands to be added to the run script {extra_commands}")
78
-
79
- command = self.make_run_command(run_number=run_number, input_file=input_file)
80
- with script_file_path.open("w", encoding="utf-8") as file:
57
+ with sub_script.open("w", encoding="utf-8") as file:
81
58
  file.write("#!/usr/bin/env bash\n\n")
82
59
  file.write("set -e\n")
83
60
  file.write("set -o pipefail\n")
@@ -89,16 +66,12 @@ class SimulatorArray(SimtelRunner):
89
66
  file.write(f"{line}\n")
90
67
  file.write("# End of extras\n\n")
91
68
 
92
- n = 1 if test else self.runs_per_set
93
- for _ in range(n):
94
- file.write(f"{command}\n\n")
69
+ for _ in range(self.runs_per_set):
70
+ file.write(f"{sim_telarray_env_as_string()} " + " ".join(command) + "\n")
95
71
 
96
72
  file.write('\necho "RUNTIME: $SECONDS"\n')
97
73
 
98
- script_file_path.chmod(script_file_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP)
99
- return script_file_path
100
-
101
- def make_run_command(self, run_number=None, input_file=None, weak_pointing=None):
74
+ def make_run_command(self, run_number=None, input_file=None):
102
75
  """
103
76
  Build and return the command to run sim_telarray.
104
77
 
@@ -108,26 +81,22 @@ class SimulatorArray(SimtelRunner):
108
81
  Full path of the input CORSIKA file
109
82
  run_number: int (optional)
110
83
  run number
111
- weak_pointing: bool (optional)
112
- Specify weak pointing option for sim_telarray.
113
84
 
114
85
  Returns
115
86
  -------
116
- str
87
+ list
117
88
  Command to run sim_telarray.
118
89
  """
119
- command = self._common_run_command(run_number, weak_pointing)
90
+ self.file_list = self.runner_service.load_files(run_number=run_number)
91
+ command = self._common_run_command(run_number)
120
92
 
121
- if self.calibration_config:
93
+ if self.is_calibration_run:
122
94
  command += self._make_run_command_for_calibration_simulations()
123
95
  else:
124
96
  command += self._make_run_command_for_shower_simulations()
125
97
 
126
98
  # "-C show=all" should be the last option
127
- command += super().get_config_option("show", "all")
128
- command += f" {input_file} | gzip > {self._log_file} 2>&1 || exit"
129
-
130
- return clear_default_sim_telarray_cfg_directories(command)
99
+ return [*command, "-C", "show=all", input_file]
131
100
 
132
101
  def _make_run_command_for_shower_simulations(self):
133
102
  """
@@ -138,80 +107,117 @@ class SimulatorArray(SimtelRunner):
138
107
  str
139
108
  Command to run sim_telarray.
140
109
  """
141
- return super().get_config_option(
142
- "power_law",
143
- SimulatorArray.get_power_law_for_sim_telarray_histograms(
144
- self.corsika_config.primary_particle
145
- ),
146
- )
110
+ return [
111
+ "-C",
112
+ "power_law="
113
+ f"{
114
+ SimulatorArray.get_power_law_for_sim_telarray_histograms(
115
+ self.corsika_config.primary_particle
116
+ )
117
+ }",
118
+ ]
147
119
 
148
120
  def _make_run_command_for_calibration_simulations(self):
149
121
  """Build sim_telarray command for calibration simulations."""
150
- cfg = self.calibration_config
122
+ cfg = settings.config.args
151
123
  altitude = self.corsika_config.array_model.site_model.get_parameter_value_with_unit(
152
124
  "reference_point_altitude"
153
125
  ).to_value("m")
154
126
 
155
- command = super().get_config_option("Altitude", altitude)
156
-
127
+ options = {"Altitude": altitude}
157
128
  for key in ("nsb_scaling_factor", "stars"):
158
- if cfg.get(key):
159
- command += super().get_config_option(key, cfg[key])
129
+ if key in cfg:
130
+ options[key] = cfg[key]
160
131
 
161
132
  run_mode = cfg.get("run_mode")
162
133
  if run_mode in ("pedestals", "pedestals_nsb_only"):
163
134
  n_events = cfg.get("number_of_pedestal_events", cfg["number_of_events"])
164
- command += super().get_config_option("pedestal_events", n_events)
135
+ options["pedestal_events"] = n_events
165
136
  if run_mode == "pedestals_nsb_only":
166
- command += self._pedestals_nsb_only_command()
137
+ options.update(self._pedestals_nsb_only_options())
167
138
  if run_mode == "pedestals_dark":
168
139
  n_events = cfg.get("number_of_dark_events", cfg["number_of_events"])
169
- command += super().get_config_option("dark_events", n_events)
140
+ options["dark_events"] = n_events
170
141
  if run_mode == "direct_injection":
171
142
  n_events = cfg.get("number_of_flasher_events", cfg["number_of_events"])
172
- command += super().get_config_option("laser_events", n_events)
143
+ options["laser_events"] = n_events
173
144
 
174
- return command
145
+ cmd = []
146
+ for key, value in options.items():
147
+ cmd.extend(["-C", f"{key}={value}"])
148
+ return cmd
175
149
 
176
- def _common_run_command(self, run_number, weak_pointing=None):
150
+ def _common_run_command(self, run_number):
177
151
  """Build generic run command for sim_telarray."""
152
+ weak_pointing = self._determine_pointing_option()
178
153
  config_dir = self.corsika_config.array_model.get_config_directory()
179
- self._log_file = self.get_file_name(file_type="log", run_number=run_number)
180
- histogram_file = self.get_file_name(file_type="histogram", run_number=run_number)
181
- output_file = self.get_file_name(file_type="simtel_output", run_number=run_number)
182
- self.corsika_config.array_model.export_all_simtel_config_files()
183
-
184
- command = str(self._simtel_path.joinpath("sim_telarray/bin/sim_telarray"))
185
- command += f" -c {self.corsika_config.array_model.config_file_path}"
186
- command += f" -I{config_dir}"
187
- command += super().get_config_option(
188
- "telescope_theta", self.corsika_config.zenith_angle, weak_pointing
154
+ self._log_file = self.runner_service.get_file_name(
155
+ file_type="sim_telarray_log", run_number=run_number
189
156
  )
190
- command += super().get_config_option(
191
- "telescope_phi", self.corsika_config.azimuth_angle, weak_pointing
157
+ histogram_file = self.runner_service.get_file_name(
158
+ file_type="sim_telarray_histogram", run_number=run_number
192
159
  )
193
- command += super().get_config_option("histogram_file", histogram_file)
194
- command += super().get_config_option("random_state", "none")
195
- if self.sim_telarray_seeds and self.sim_telarray_seeds.get("random_instrument_instances"):
196
- command += super().get_config_option(
197
- "random_seed",
198
- f"file-by-run:{config_dir}/{self.sim_telarray_seeds['seed_file_name']},auto",
199
- )
200
- elif self.sim_telarray_seeds and self.sim_telarray_seeds.get("seed"):
201
- command += super().get_config_option("random_seed", self.sim_telarray_seeds["seed"])
202
- command += super().get_config_option("output_file", output_file)
203
-
204
- return command
205
-
206
- def _pedestals_nsb_only_command(self):
160
+ output_file = self.runner_service.get_file_name(
161
+ file_type="sim_telarray_output", run_number=run_number
162
+ )
163
+ self.corsika_config.array_model.export_all_simtel_config_files()
164
+
165
+ cmd = [
166
+ str(settings.config.sim_telarray_exe),
167
+ "-c",
168
+ str(self.corsika_config.array_model.config_file_path),
169
+ f"-I{config_dir}",
170
+ ]
171
+ weak_options = {
172
+ "telescope_theta": self.corsika_config.zenith_angle,
173
+ "telescope_phi": self.corsika_config.azimuth_angle,
174
+ }
175
+ options = {
176
+ "histogram_file": histogram_file,
177
+ "random_state": "none",
178
+ "output_file": output_file,
179
+ }
180
+
181
+ try:
182
+ options["random_seed"] = self.corsika_config.array_model.sim_telarray_seed.seed_string
183
+ except AttributeError as exc:
184
+ raise AttributeError("Error setting sim_telarray seed string") from exc
185
+
186
+ for key, value in options.items():
187
+ cmd.extend(["-C", f"{key}={value}"])
188
+ for key, value in weak_options.items():
189
+ cmd.extend([("-W" if weak_pointing else "-C"), f"{key}={value}"])
190
+ return cmd
191
+
192
+ def _determine_pointing_option(self):
207
193
  """
208
- Generate the command to run sim_telarray for nsb-only pedestal simulations.
194
+ Determine the pointing option for sim_telarray.
195
+
196
+ Parameters
197
+ ----------
198
+ label: str
199
+ Label of the simulation.
209
200
 
210
201
  Returns
211
202
  -------
212
- str
203
+ bool:
204
+ True if weak pointing is to be used.
205
+ """
206
+ if isinstance(self.label, str):
207
+ return any(pointing in self.label for pointing in ["divergent", "convergent"])
208
+ return False
209
+
210
+ def _pedestals_nsb_only_options(self):
211
+ """
212
+ Generate options to run sim_telarray for nsb-only pedestal simulations.
213
+
214
+ Returns
215
+ -------
216
+ dict
213
217
  Command to run sim_telarray.
214
218
  """
219
+ options = {}
220
+
215
221
  null_values = [
216
222
  "fadc_noise",
217
223
  "fadc_lg_noise",
@@ -222,10 +228,8 @@ class SimulatorArray(SimtelRunner):
222
228
  "fadc_sysvar_pedestal",
223
229
  "fadc_dev_pedestal",
224
230
  ]
225
- null_command_parts = []
226
231
  for param in null_values:
227
- null_command_parts.append(super().get_config_option(param, 0.0))
228
- command = " ".join(null_command_parts)
232
+ options[param] = 0.0
229
233
 
230
234
  one_values = [
231
235
  "fadc_lg_var_pedestal",
@@ -233,11 +237,10 @@ class SimulatorArray(SimtelRunner):
233
237
  "fadc_lg_dev_pedestal",
234
238
  "fadc_lg_sysvar_pedestal",
235
239
  ]
236
- one_command_parts = []
237
240
  for param in one_values:
238
- one_command_parts.append(super().get_config_option(param, -1.0))
239
- command += " " + " ".join(one_command_parts)
240
- return command
241
+ options[param] = -1.0
242
+
243
+ return options
241
244
 
242
245
  def _check_run_result(self, run_number=None):
243
246
  """
@@ -255,12 +258,14 @@ class SimulatorArray(SimtelRunner):
255
258
 
256
259
  Raises
257
260
  ------
258
- InvalidOutputFileError
261
+ FileNotFoundError
259
262
  If sim_telarray output file does not exist.
260
263
  """
261
- output_file = self.get_file_name(file_type="simtel_output", run_number=run_number)
264
+ output_file = self.runner_service.get_file_name(
265
+ file_type="simtel_output", run_number=run_number
266
+ )
262
267
  if not output_file.exists():
263
- raise InvalidOutputFileError(f"sim_telarray output file {output_file} does not exist.")
268
+ raise FileNotFoundError(f"sim_telarray output file {output_file} does not exist.")
264
269
  self._logger.debug(f"sim_telarray output file {output_file} exists.")
265
270
  return True
266
271