gammasimtools 0.26.0__py3-none-any.whl → 0.27.1__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.
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/METADATA +5 -1
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/RECORD +70 -66
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/WHEEL +1 -1
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/entry_points.txt +1 -1
- simtools/_version.py +2 -2
- simtools/applications/convert_geo_coordinates_of_array_elements.py +2 -1
- simtools/applications/db_get_array_layouts_from_db.py +1 -1
- simtools/applications/{calculate_incident_angles.py → derive_incident_angle.py} +16 -16
- simtools/applications/derive_mirror_rnda.py +111 -177
- simtools/applications/generate_corsika_histograms.py +38 -1
- simtools/applications/generate_regular_arrays.py +73 -36
- simtools/applications/simulate_flasher.py +3 -13
- simtools/applications/simulate_illuminator.py +2 -10
- simtools/applications/simulate_pedestals.py +1 -1
- simtools/applications/simulate_prod.py +8 -7
- simtools/applications/submit_data_from_external.py +2 -1
- simtools/applications/validate_camera_efficiency.py +28 -27
- simtools/applications/validate_cumulative_psf.py +1 -3
- simtools/applications/validate_optics.py +2 -1
- simtools/atmosphere.py +83 -0
- simtools/camera/camera_efficiency.py +171 -48
- simtools/camera/single_photon_electron_spectrum.py +6 -6
- simtools/configuration/commandline_parser.py +47 -9
- simtools/constants.py +5 -0
- simtools/corsika/corsika_config.py +88 -185
- simtools/corsika/corsika_histograms.py +246 -69
- simtools/data_model/model_data_writer.py +46 -49
- simtools/data_model/schema.py +2 -0
- simtools/db/db_handler.py +4 -2
- simtools/db/mongo_db.py +2 -2
- simtools/io/ascii_handler.py +52 -4
- simtools/io/io_handler.py +23 -12
- simtools/job_execution/job_manager.py +154 -79
- simtools/job_execution/process_pool.py +137 -0
- simtools/layout/array_layout.py +0 -1
- simtools/layout/array_layout_utils.py +143 -21
- simtools/model/array_model.py +22 -50
- simtools/model/calibration_model.py +4 -4
- simtools/model/model_parameter.py +123 -73
- simtools/model/model_utils.py +40 -1
- simtools/model/site_model.py +4 -4
- simtools/model/telescope_model.py +4 -5
- simtools/ray_tracing/incident_angles.py +87 -6
- simtools/ray_tracing/mirror_panel_psf.py +337 -217
- simtools/ray_tracing/psf_analysis.py +57 -42
- simtools/ray_tracing/psf_parameter_optimisation.py +3 -2
- simtools/ray_tracing/ray_tracing.py +37 -10
- simtools/runners/corsika_runner.py +52 -191
- simtools/runners/corsika_simtel_runner.py +74 -100
- simtools/runners/runner_services.py +214 -213
- simtools/runners/simtel_runner.py +27 -155
- simtools/runners/simtools_runner.py +9 -69
- simtools/schemas/application_workflow.metaschema.yml +8 -0
- simtools/settings.py +19 -0
- simtools/simtel/simtel_config_writer.py +0 -55
- simtools/simtel/simtel_seeds.py +184 -0
- simtools/simtel/simulator_array.py +115 -103
- simtools/simtel/simulator_camera_efficiency.py +66 -42
- simtools/simtel/simulator_light_emission.py +110 -123
- simtools/simtel/simulator_ray_tracing.py +78 -63
- simtools/simulator.py +135 -346
- simtools/testing/sim_telarray_metadata.py +13 -11
- simtools/testing/validate_output.py +87 -19
- simtools/utils/general.py +6 -17
- simtools/utils/random.py +36 -0
- simtools/visualization/plot_corsika_histograms.py +2 -0
- simtools/visualization/plot_incident_angles.py +48 -1
- simtools/visualization/plot_psf.py +160 -18
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/licenses/LICENSE +0 -0
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/top_level.txt +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
"""Base class for running sim_telarray simulations."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import subprocess
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
from simtools.job_execution import job_manager
|
|
7
6
|
from simtools.runners.runner_services import RunnerServices
|
|
8
7
|
|
|
8
|
+
SIM_TELARRAY_ENV = {
|
|
9
|
+
"SIM_TELARRAY_CONFIG_PATH": "",
|
|
10
|
+
}
|
|
9
11
|
|
|
10
|
-
class SimtelExecutionError(Exception):
|
|
11
|
-
"""Exception for sim_telarray execution error."""
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
"""
|
|
13
|
+
def sim_telarray_env_as_string():
|
|
14
|
+
"""Return the sim_telarray environment variables as a string."""
|
|
15
|
+
return " ".join(f'{key}="{value}" ' for key, value in SIM_TELARRAY_ENV.items())
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class SimtelRunner:
|
|
@@ -26,34 +26,24 @@ class SimtelRunner:
|
|
|
26
26
|
----------
|
|
27
27
|
label: str
|
|
28
28
|
Instance label. Important for output file naming.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
calibration_run_mode: str
|
|
34
|
-
Calibration run mode.
|
|
29
|
+
config: CorsikaConfig or dict
|
|
30
|
+
Configuration parameters.
|
|
31
|
+
is_calibration_run: bool
|
|
32
|
+
Flag to indicate if this is a calibration run.
|
|
35
33
|
"""
|
|
36
34
|
|
|
37
|
-
def __init__(
|
|
38
|
-
self,
|
|
39
|
-
label=None,
|
|
40
|
-
corsika_config=None,
|
|
41
|
-
use_multipipe=False,
|
|
42
|
-
calibration_run_mode=None,
|
|
43
|
-
):
|
|
35
|
+
def __init__(self, label=None, config=None, is_calibration_run=False):
|
|
44
36
|
"""Initialize SimtelRunner."""
|
|
45
37
|
self._logger = logging.getLogger(__name__)
|
|
46
38
|
|
|
47
39
|
self.label = label
|
|
48
40
|
self._base_directory = None
|
|
49
|
-
self.
|
|
41
|
+
self.is_calibration_run = is_calibration_run
|
|
50
42
|
|
|
51
43
|
self.runs_per_set = 1
|
|
52
44
|
|
|
53
|
-
self.runner_service = RunnerServices(
|
|
54
|
-
self.
|
|
55
|
-
"corsika_sim_telarray" if use_multipipe else "sim_telarray"
|
|
56
|
-
)
|
|
45
|
+
self.runner_service = RunnerServices(config, run_type="sim_telarray", label=label)
|
|
46
|
+
self.file_list = None
|
|
57
47
|
|
|
58
48
|
def run(self, test=False, input_file=None, run_number=None):
|
|
59
49
|
"""
|
|
@@ -70,142 +60,24 @@ class SimtelRunner:
|
|
|
70
60
|
"""
|
|
71
61
|
self._logger.debug("Running sim_telarray")
|
|
72
62
|
|
|
73
|
-
command, stdout_file, stderr_file = self.
|
|
63
|
+
command, stdout_file, stderr_file = self.make_run_command(
|
|
74
64
|
run_number=run_number, input_file=input_file
|
|
75
65
|
)
|
|
76
|
-
|
|
77
|
-
if test
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
self._logger.debug(f"Running ({self.runs_per_set}x) with command: {command}")
|
|
82
|
-
for _ in range(self.runs_per_set):
|
|
83
|
-
self._run_simtel_and_check_output(command, stdout_file, stderr_file)
|
|
84
|
-
|
|
85
|
-
self._check_run_result(run_number=run_number)
|
|
86
|
-
|
|
87
|
-
def _check_run_result(self, run_number=None): # pylint: disable=all
|
|
88
|
-
"""Check if simtel output file exists."""
|
|
89
|
-
pass
|
|
90
|
-
|
|
91
|
-
def _raise_simtel_error(self):
|
|
92
|
-
"""
|
|
93
|
-
Raise sim_telarray execution error.
|
|
94
|
-
|
|
95
|
-
Final 30 lines from the log file are collected and printed.
|
|
96
|
-
|
|
97
|
-
Raises
|
|
98
|
-
------
|
|
99
|
-
SimtelExecutionError
|
|
100
|
-
"""
|
|
101
|
-
if hasattr(self, "_log_file"):
|
|
102
|
-
msg = gen.get_log_excerpt(self._log_file)
|
|
103
|
-
else:
|
|
104
|
-
msg = "Simtel log file does not exist."
|
|
105
|
-
raise SimtelExecutionError(msg)
|
|
106
|
-
|
|
107
|
-
def _run_simtel_and_check_output(self, command, stdout_file, stderr_file):
|
|
108
|
-
"""
|
|
109
|
-
Run the sim_telarray command and check the exit code.
|
|
110
|
-
|
|
111
|
-
Raises
|
|
112
|
-
------
|
|
113
|
-
SimtelExecutionError
|
|
114
|
-
if run was not successful.
|
|
115
|
-
"""
|
|
116
|
-
stdout_file = stdout_file if stdout_file else "/dev/null"
|
|
117
|
-
stderr_file = stderr_file if stderr_file else "/dev/null"
|
|
118
|
-
with (
|
|
119
|
-
open(f"{stdout_file}", "w", encoding="utf-8") as stdout,
|
|
120
|
-
open(f"{stderr_file}", "w", encoding="utf-8") as stderr,
|
|
121
|
-
):
|
|
122
|
-
result = subprocess.run(
|
|
66
|
+
runs = 1 if test else self.runs_per_set
|
|
67
|
+
label = "test" if test else f"{self.runs_per_set}x"
|
|
68
|
+
self._logger.info(f"Running ({label}) with command: {command}")
|
|
69
|
+
for _ in range(runs):
|
|
70
|
+
job_manager.submit(
|
|
123
71
|
command,
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
stderr=stderr,
|
|
72
|
+
out_file=stdout_file,
|
|
73
|
+
err_file=stderr_file,
|
|
74
|
+
env=SIM_TELARRAY_ENV,
|
|
128
75
|
)
|
|
129
76
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
return result.returncode
|
|
134
|
-
|
|
135
|
-
def _make_run_command(self, run_number=None, input_file=None):
|
|
136
|
-
self._logger.debug(
|
|
137
|
-
"make_run_command is being called from the base class - "
|
|
138
|
-
"it should be implemented in the sub class"
|
|
139
|
-
)
|
|
140
|
-
input_file = input_file if input_file else "nofile"
|
|
141
|
-
run_number = run_number if run_number else 1
|
|
142
|
-
return f"{input_file}-{run_number}", None, None
|
|
143
|
-
|
|
144
|
-
@staticmethod
|
|
145
|
-
def get_config_option(par, value=None, weak_option=False):
|
|
146
|
-
"""
|
|
147
|
-
Build sim_telarray command.
|
|
148
|
-
|
|
149
|
-
Parameters
|
|
150
|
-
----------
|
|
151
|
-
par: str
|
|
152
|
-
Parameter name.
|
|
153
|
-
value: str
|
|
154
|
-
Parameter value.
|
|
155
|
-
weak_option: bool
|
|
156
|
-
If True, use -W option instead of -C.
|
|
157
|
-
|
|
158
|
-
Returns
|
|
159
|
-
-------
|
|
160
|
-
str
|
|
161
|
-
Command for sim_telarray.
|
|
162
|
-
"""
|
|
163
|
-
option_syntax = "-W" if weak_option else "-C"
|
|
164
|
-
c = f" {option_syntax} {par}"
|
|
165
|
-
c += f"={value}" if value is not None else ""
|
|
166
|
-
return c
|
|
77
|
+
def make_run_command(self, run_number=None, input_file=None):
|
|
78
|
+
"""Make the sim_telarray run command (to implemented in subclasses)."""
|
|
79
|
+
raise NotImplementedError("Must be implemented in concrete subclass")
|
|
167
80
|
|
|
168
81
|
def get_resources(self, run_number=None):
|
|
169
82
|
"""Return computing resources used."""
|
|
170
83
|
return self.runner_service.get_resources(run_number)
|
|
171
|
-
|
|
172
|
-
def get_file_name(
|
|
173
|
-
self,
|
|
174
|
-
simulation_software="sim_telarray",
|
|
175
|
-
file_type=None,
|
|
176
|
-
run_number=None,
|
|
177
|
-
mode="",
|
|
178
|
-
model_version_index=0,
|
|
179
|
-
):
|
|
180
|
-
"""
|
|
181
|
-
Get the full path of a file for a given run number.
|
|
182
|
-
|
|
183
|
-
Parameters
|
|
184
|
-
----------
|
|
185
|
-
simulation_software: str
|
|
186
|
-
Simulation software.
|
|
187
|
-
file_type: str
|
|
188
|
-
File type.
|
|
189
|
-
run_number: int
|
|
190
|
-
Run number.
|
|
191
|
-
model_version_index: int
|
|
192
|
-
Index of the model version.
|
|
193
|
-
This is used to select the correct simulator_array instance in case
|
|
194
|
-
multiple array models are simulated.
|
|
195
|
-
|
|
196
|
-
Returns
|
|
197
|
-
-------
|
|
198
|
-
str
|
|
199
|
-
File name with full path.
|
|
200
|
-
"""
|
|
201
|
-
if simulation_software.lower() != "sim_telarray":
|
|
202
|
-
raise ValueError(
|
|
203
|
-
f"simulation_software ({simulation_software}) is not supported in SimulatorArray"
|
|
204
|
-
)
|
|
205
|
-
return self.runner_service.get_file_name(
|
|
206
|
-
file_type=file_type,
|
|
207
|
-
run_number=run_number,
|
|
208
|
-
mode=mode,
|
|
209
|
-
_model_version_index=model_version_index,
|
|
210
|
-
calibration_run_mode=self.calibration_run_mode,
|
|
211
|
-
)
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"""Tools for running applications in the simtools framework."""
|
|
2
2
|
|
|
3
3
|
import shutil
|
|
4
|
-
import subprocess
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
|
|
7
6
|
import simtools.utils.general as gen
|
|
8
7
|
from simtools import dependencies
|
|
9
8
|
from simtools.io import ascii_handler
|
|
9
|
+
from simtools.job_execution import job_manager
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
def run_applications(args_dict, logger):
|
|
@@ -39,75 +39,15 @@ def run_applications(args_dict, logger):
|
|
|
39
39
|
logger.info(f"Skipping application: {app}")
|
|
40
40
|
continue
|
|
41
41
|
logger.info(f"Running application: {app}")
|
|
42
|
-
|
|
42
|
+
result = job_manager.submit(
|
|
43
|
+
app,
|
|
44
|
+
out_file=None,
|
|
45
|
+
err_file=None,
|
|
46
|
+
configuration=config.get("configuration"),
|
|
47
|
+
runtime_environment=run_time,
|
|
48
|
+
)
|
|
43
49
|
file.write("=" * 80 + "\n")
|
|
44
|
-
file.write(f"Application: {app}\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}\n")
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def run_application(runtime_environment, application, configuration, logger):
|
|
48
|
-
"""
|
|
49
|
-
Run a simtools application and return stdout and stderr.
|
|
50
|
-
|
|
51
|
-
Allow to specify a runtime environment (e.g., Docker) and a working directory.
|
|
52
|
-
|
|
53
|
-
Parameters
|
|
54
|
-
----------
|
|
55
|
-
runtime_environment : list
|
|
56
|
-
Command to run the application in the specified runtime environment.
|
|
57
|
-
application : str
|
|
58
|
-
Name of the application to run.
|
|
59
|
-
configuration : dict
|
|
60
|
-
Configuration for the application.
|
|
61
|
-
logger : logging.Logger
|
|
62
|
-
Logger for logging application output.
|
|
63
|
-
|
|
64
|
-
Returns
|
|
65
|
-
-------
|
|
66
|
-
tuple
|
|
67
|
-
stdout and stderr from the application run.
|
|
68
|
-
|
|
69
|
-
"""
|
|
70
|
-
command = [application, *_convert_dict_to_args(configuration)]
|
|
71
|
-
if runtime_environment:
|
|
72
|
-
command = runtime_environment + command
|
|
73
|
-
try:
|
|
74
|
-
result = subprocess.run(
|
|
75
|
-
command,
|
|
76
|
-
check=True,
|
|
77
|
-
capture_output=True,
|
|
78
|
-
text=True,
|
|
79
|
-
)
|
|
80
|
-
except subprocess.CalledProcessError as exc:
|
|
81
|
-
logger.error(f"Error running application {application}: {exc.stderr}")
|
|
82
|
-
raise exc
|
|
83
|
-
|
|
84
|
-
return result.stdout, result.stderr
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def _convert_dict_to_args(parameters):
|
|
88
|
-
"""
|
|
89
|
-
Convert a dictionary of parameters to a list of command line arguments.
|
|
90
|
-
|
|
91
|
-
Parameters
|
|
92
|
-
----------
|
|
93
|
-
parameters : dict
|
|
94
|
-
Dictionary containing parameters to convert.
|
|
95
|
-
|
|
96
|
-
Returns
|
|
97
|
-
-------
|
|
98
|
-
list
|
|
99
|
-
List of command line arguments.
|
|
100
|
-
"""
|
|
101
|
-
args = []
|
|
102
|
-
for key, value in parameters.items():
|
|
103
|
-
if isinstance(value, bool):
|
|
104
|
-
if value:
|
|
105
|
-
args.append(f"--{key}")
|
|
106
|
-
elif isinstance(value, list):
|
|
107
|
-
args.extend([f"--{key}", *(str(item) for item in value)])
|
|
108
|
-
else:
|
|
109
|
-
args.extend([f"--{key}", str(value)])
|
|
110
|
-
return args
|
|
50
|
+
file.write(f"Application: {app}\nSTDOUT:\n{result.stdout}\nSTDERR:\n{result.stderr}\n")
|
|
111
51
|
|
|
112
52
|
|
|
113
53
|
def _read_application_configuration(configuration_file, steps, logger):
|
|
@@ -142,6 +142,10 @@ definitions:
|
|
|
142
142
|
description: |
|
|
143
143
|
"Allowed tolerance for floating point comparison."
|
|
144
144
|
type: number
|
|
145
|
+
scaling:
|
|
146
|
+
description: |
|
|
147
|
+
"Scaling factor to apply to the model parameter value before comparison."
|
|
148
|
+
type: number
|
|
145
149
|
test_simtel_cfg_files:
|
|
146
150
|
description: |
|
|
147
151
|
"Reference file used for comparison of sim_telarray configuration files."
|
|
@@ -152,6 +156,10 @@ definitions:
|
|
|
152
156
|
type: string
|
|
153
157
|
description: Path to the configuration file for the given version.
|
|
154
158
|
minProperties: 1
|
|
159
|
+
test_output_file:
|
|
160
|
+
description: |
|
|
161
|
+
"Output file generated by the integration test for comparison with the reference file."
|
|
162
|
+
type: string
|
|
155
163
|
tolerance:
|
|
156
164
|
description: "Allowed tolerance for floating point comparison."
|
|
157
165
|
type: number
|
simtools/settings.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Centralized settings object with command line and environment variables."""
|
|
2
2
|
|
|
3
3
|
import os
|
|
4
|
+
import socket
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
from types import MappingProxyType
|
|
6
7
|
|
|
@@ -15,7 +16,10 @@ class _Config:
|
|
|
15
16
|
self._sim_telarray_path = None
|
|
16
17
|
self._sim_telarray_exe = None
|
|
17
18
|
self._corsika_path = None
|
|
19
|
+
self._corsika_interaction_table_path = None
|
|
18
20
|
self._corsika_exe = None
|
|
21
|
+
self.user = os.getenv("USER", "unknown")
|
|
22
|
+
self.hostname = socket.gethostname()
|
|
19
23
|
|
|
20
24
|
def load(self, args=None, db_config=None):
|
|
21
25
|
"""
|
|
@@ -51,6 +55,12 @@ class _Config:
|
|
|
51
55
|
else os.getenv("SIMTOOLS_CORSIKA_PATH")
|
|
52
56
|
)
|
|
53
57
|
|
|
58
|
+
self._corsika_interaction_table_path = (
|
|
59
|
+
args.get("corsika_interaction_table_path")
|
|
60
|
+
if args is not None and "corsika_interaction_table_path" in args
|
|
61
|
+
else os.getenv("SIMTOOLS_CORSIKA_INTERACTION_TABLE_PATH")
|
|
62
|
+
)
|
|
63
|
+
|
|
54
64
|
self._corsika_exe = self._get_corsika_exec() if self._corsika_path is not None else None
|
|
55
65
|
|
|
56
66
|
def _get_corsika_exec(self):
|
|
@@ -118,6 +128,15 @@ class _Config:
|
|
|
118
128
|
"""Path to the CORSIKA installation directory."""
|
|
119
129
|
return Path(self._corsika_path) if self._corsika_path is not None else None
|
|
120
130
|
|
|
131
|
+
@property
|
|
132
|
+
def corsika_interaction_table_path(self):
|
|
133
|
+
"""Path to the CORSIKA interaction table directory."""
|
|
134
|
+
return (
|
|
135
|
+
Path(self._corsika_interaction_table_path)
|
|
136
|
+
if self._corsika_interaction_table_path is not None
|
|
137
|
+
else self.corsika_path
|
|
138
|
+
)
|
|
139
|
+
|
|
121
140
|
@property
|
|
122
141
|
def corsika_exe(self):
|
|
123
142
|
"""Path to the CORSIKA executable."""
|
|
@@ -17,27 +17,6 @@ from simtools.utils import names
|
|
|
17
17
|
logger = logging.getLogger(__name__)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
def sim_telarray_random_seeds(seed, number):
|
|
21
|
-
"""
|
|
22
|
-
Generate random seeds to be used in sim_telarray.
|
|
23
|
-
|
|
24
|
-
Parameters
|
|
25
|
-
----------
|
|
26
|
-
seed: int
|
|
27
|
-
Seed for the random number generator.
|
|
28
|
-
number: int
|
|
29
|
-
Number of random seeds to generate.
|
|
30
|
-
|
|
31
|
-
Returns
|
|
32
|
-
-------
|
|
33
|
-
list
|
|
34
|
-
List of random seeds.
|
|
35
|
-
"""
|
|
36
|
-
rng = np.random.default_rng(seed)
|
|
37
|
-
max_int32 = np.iinfo(np.int32).max # sim_telarray requires 32 bit integers
|
|
38
|
-
return list(rng.integers(low=1, high=max_int32, size=number, dtype=np.int32))
|
|
39
|
-
|
|
40
|
-
|
|
41
20
|
class SimtelConfigWriter:
|
|
42
21
|
"""
|
|
43
22
|
SimtelConfigWriter writes sim_telarray configuration files.
|
|
@@ -71,7 +50,6 @@ class SimtelConfigWriter:
|
|
|
71
50
|
):
|
|
72
51
|
"""Initialize SimtelConfigWriter."""
|
|
73
52
|
self._logger = logging.getLogger(__name__)
|
|
74
|
-
self._logger.debug("Init SimtelConfigWriter")
|
|
75
53
|
|
|
76
54
|
self._site = site
|
|
77
55
|
self._model_version = model_version
|
|
@@ -492,39 +470,6 @@ class SimtelConfigWriter:
|
|
|
492
470
|
file.write(f"# include <{tel_config_file}>\n\n")
|
|
493
471
|
file.write("#endif \n\n") # configuration files need to end with \n\n
|
|
494
472
|
|
|
495
|
-
if additional_metadata and additional_metadata.get("random_instrument_instances"):
|
|
496
|
-
self._write_random_seeds_file(additional_metadata, config_file_directory)
|
|
497
|
-
|
|
498
|
-
def _write_random_seeds_file(self, sim_telarray_seeds, config_file_directory):
|
|
499
|
-
"""
|
|
500
|
-
Write list of random number used to generate random instances of instrument.
|
|
501
|
-
|
|
502
|
-
Parameters
|
|
503
|
-
----------
|
|
504
|
-
random_instrument_instances: int
|
|
505
|
-
Number of random instances of the instrument.
|
|
506
|
-
"""
|
|
507
|
-
self._logger.info(
|
|
508
|
-
"Writing random seed file "
|
|
509
|
-
f"{config_file_directory}/{sim_telarray_seeds['seed_file_name']}"
|
|
510
|
-
f" (global seed {sim_telarray_seeds['seed']})"
|
|
511
|
-
)
|
|
512
|
-
if sim_telarray_seeds["random_instrument_instances"] > 1024:
|
|
513
|
-
raise ValueError("Number of random instances of instrument must be less than 1024")
|
|
514
|
-
random_integers = sim_telarray_random_seeds(
|
|
515
|
-
sim_telarray_seeds["seed"], sim_telarray_seeds["random_instrument_instances"]
|
|
516
|
-
)
|
|
517
|
-
with open(
|
|
518
|
-
config_file_directory / sim_telarray_seeds["seed_file_name"], "w", encoding="utf-8"
|
|
519
|
-
) as file:
|
|
520
|
-
file.write(
|
|
521
|
-
"# Random seeds for instrument configuration generated with seed "
|
|
522
|
-
f"{sim_telarray_seeds['seed']}"
|
|
523
|
-
f" (model version {self._model_version}, site {self._site})\n"
|
|
524
|
-
)
|
|
525
|
-
for number in random_integers:
|
|
526
|
-
file.write(f"{number}\n")
|
|
527
|
-
|
|
528
473
|
def write_single_mirror_list_file(
|
|
529
474
|
self, mirror_number, mirrors, single_mirror_list_file, set_focal_length_to_zero=False
|
|
530
475
|
):
|
|
@@ -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)
|