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
simtools/simulator.py
CHANGED
|
@@ -1,78 +1,65 @@
|
|
|
1
1
|
"""Simulator class for managing simulations of showers and array of telescopes."""
|
|
2
2
|
|
|
3
|
-
import gzip
|
|
4
3
|
import logging
|
|
5
|
-
import re
|
|
6
4
|
import shutil
|
|
7
|
-
from collections import defaultdict
|
|
8
5
|
from pathlib import Path
|
|
9
6
|
|
|
10
7
|
import numpy as np
|
|
11
8
|
from astropy import units as u
|
|
12
9
|
|
|
10
|
+
from simtools import settings
|
|
13
11
|
from simtools.corsika.corsika_config import CorsikaConfig
|
|
14
12
|
from simtools.io import io_handler, table_handler
|
|
15
|
-
from simtools.job_execution
|
|
13
|
+
from simtools.job_execution import job_manager
|
|
16
14
|
from simtools.model.array_model import ArrayModel
|
|
17
|
-
from simtools.runners
|
|
18
|
-
from simtools.
|
|
19
|
-
from simtools.sim_events import file_info
|
|
20
|
-
from simtools.sim_events.writer import EventDataWriter
|
|
15
|
+
from simtools.runners import corsika_runner, corsika_simtel_runner, runner_services, simtel_runner
|
|
16
|
+
from simtools.sim_events import file_info, writer
|
|
21
17
|
from simtools.simtel.simulator_array import SimulatorArray
|
|
22
18
|
from simtools.testing.sim_telarray_metadata import assert_sim_telarray_metadata
|
|
23
|
-
from simtools.utils import general
|
|
24
|
-
from simtools.version import semver_to_int
|
|
19
|
+
from simtools.utils import general
|
|
25
20
|
|
|
26
21
|
|
|
27
22
|
class Simulator:
|
|
28
23
|
"""
|
|
29
|
-
|
|
24
|
+
Simulation of showers and of the array of telescopes.
|
|
30
25
|
|
|
31
|
-
|
|
26
|
+
Interface with the simulation software packages (e.g., CORSIKA or sim_telarray).
|
|
32
27
|
A single run is simulated per instance, possibly for multiple model versions.
|
|
33
28
|
|
|
34
|
-
The configuration is set as a dict corresponding to the command line configuration groups
|
|
35
|
-
(especially simulation_software, simulation_model, simulation_parameters).
|
|
36
|
-
|
|
37
29
|
Parameters
|
|
38
30
|
----------
|
|
39
|
-
args_dict : dict
|
|
40
|
-
Configuration dictionary
|
|
41
|
-
(includes simulation_software, simulation_model, simulation_parameters groups).
|
|
42
31
|
label: str
|
|
43
32
|
Instance label.
|
|
44
33
|
extra_commands: str or list of str
|
|
45
34
|
Extra commands to be added to the run script before the run command.
|
|
46
35
|
"""
|
|
47
36
|
|
|
48
|
-
def __init__(
|
|
49
|
-
self,
|
|
50
|
-
args_dict,
|
|
51
|
-
label=None,
|
|
52
|
-
extra_commands=None,
|
|
53
|
-
):
|
|
37
|
+
def __init__(self, label=None, extra_commands=None):
|
|
54
38
|
"""Initialize Simulator class."""
|
|
55
39
|
self.logger = logging.getLogger(__name__)
|
|
56
40
|
self.label = label
|
|
57
41
|
|
|
58
|
-
self.
|
|
59
|
-
self.
|
|
60
|
-
self.model_version = self.args_dict.get("model_version", None)
|
|
42
|
+
self.site = settings.config.args.get("site", None)
|
|
43
|
+
self.model_version = settings.config.args.get("model_version", None)
|
|
61
44
|
|
|
62
|
-
self.simulation_software =
|
|
63
|
-
|
|
64
|
-
|
|
45
|
+
self.simulation_software = settings.config.args.get(
|
|
46
|
+
"simulation_software", "corsika_sim_telarray"
|
|
47
|
+
)
|
|
48
|
+
self.run_mode = settings.config.args.get("run_mode", None)
|
|
65
49
|
|
|
66
50
|
self.io_handler = io_handler.IOHandler()
|
|
67
51
|
|
|
68
|
-
self.run_number = None
|
|
69
|
-
self._results = defaultdict(list)
|
|
70
|
-
self._test = None
|
|
71
52
|
self._extra_commands = extra_commands
|
|
72
|
-
self.
|
|
73
|
-
|
|
53
|
+
self.run_number = self._initialize_from_tool_configuration()
|
|
54
|
+
|
|
74
55
|
self.array_models, self.corsika_configurations = self._initialize_array_models()
|
|
75
56
|
self._simulation_runner = self._initialize_simulation_runner()
|
|
57
|
+
self.runner_service = runner_services.RunnerServices(
|
|
58
|
+
self._get_first_corsika_config(),
|
|
59
|
+
"sub",
|
|
60
|
+
label,
|
|
61
|
+
)
|
|
62
|
+
self.file_list = self.runner_service.load_files(self.run_number)
|
|
76
63
|
|
|
77
64
|
@property
|
|
78
65
|
def simulation_software(self):
|
|
@@ -101,21 +88,13 @@ class Simulator:
|
|
|
101
88
|
|
|
102
89
|
def _initialize_from_tool_configuration(self):
|
|
103
90
|
"""Initialize simulator from tool configuration."""
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
"seed": self.args_dict.get("sim_telarray_instrument_seeds"),
|
|
107
|
-
"random_instrument_instances": self.args_dict.get(
|
|
108
|
-
"sim_telarray_random_instrument_instances"
|
|
109
|
-
),
|
|
110
|
-
"seed_file_name": "sim_telarray_instrument_seeds.txt", # name only; no directory
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if self.args_dict.get("corsika_file"):
|
|
114
|
-
self.run_number = file_info.get_corsika_run_number(self.args_dict["corsika_file"])
|
|
91
|
+
if settings.config.args.get("corsika_file"):
|
|
92
|
+
run_number = file_info.get_corsika_run_number(settings.config.args["corsika_file"])
|
|
115
93
|
else:
|
|
116
|
-
|
|
117
|
-
"
|
|
118
|
-
)
|
|
94
|
+
run_number = settings.config.args.get(
|
|
95
|
+
"run_number_offset", 0
|
|
96
|
+
) + settings.config.args.get("run_number", 1)
|
|
97
|
+
return runner_services.validate_corsika_run_number(run_number)
|
|
119
98
|
|
|
120
99
|
def _initialize_array_models(self):
|
|
121
100
|
"""
|
|
@@ -123,8 +102,8 @@ class Simulator:
|
|
|
123
102
|
|
|
124
103
|
Returns
|
|
125
104
|
-------
|
|
126
|
-
list
|
|
127
|
-
List of ArrayModel objects.
|
|
105
|
+
list, list
|
|
106
|
+
List of ArrayModel and CorsikaConfig objects.
|
|
128
107
|
"""
|
|
129
108
|
versions = general.ensure_iterable(self.model_version)
|
|
130
109
|
|
|
@@ -132,36 +111,19 @@ class Simulator:
|
|
|
132
111
|
corsika_configurations = []
|
|
133
112
|
|
|
134
113
|
for version in versions:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
overwrite_model_parameters=self.args_dict.get("overwrite_model_parameters"),
|
|
143
|
-
)
|
|
114
|
+
model = ArrayModel(
|
|
115
|
+
label=self.label,
|
|
116
|
+
site=self.site,
|
|
117
|
+
layout_name=settings.config.args.get("array_layout_name"),
|
|
118
|
+
model_version=version,
|
|
119
|
+
calibration_device_types=self._get_calibration_device_types(self.run_mode),
|
|
120
|
+
overwrite_model_parameters=settings.config.args.get("overwrite_model_parameters"),
|
|
144
121
|
)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
dummy_simulations=self._is_calibration_run(self.run_mode),
|
|
151
|
-
)
|
|
152
|
-
)
|
|
153
|
-
array_model[-1].sim_telarray_seeds = {
|
|
154
|
-
"seed": self._get_seed_for_random_instrument_instances(
|
|
155
|
-
self.sim_telarray_seeds["seed"],
|
|
156
|
-
version,
|
|
157
|
-
corsika_configurations[-1].zenith_angle,
|
|
158
|
-
corsika_configurations[-1].azimuth_angle,
|
|
159
|
-
),
|
|
160
|
-
"random_instrument_instances": self.sim_telarray_seeds[
|
|
161
|
-
"random_instrument_instances"
|
|
162
|
-
],
|
|
163
|
-
"seed_file_name": self.sim_telarray_seeds["seed_file_name"],
|
|
164
|
-
}
|
|
122
|
+
cfg = CorsikaConfig(array_model=model, label=self.label, run_number=self.run_number)
|
|
123
|
+
model.initialize_seeds(cfg.zenith_angle, cfg.azimuth_angle)
|
|
124
|
+
|
|
125
|
+
array_model.append(model)
|
|
126
|
+
corsika_configurations.append(cfg)
|
|
165
127
|
|
|
166
128
|
# 'corsika_sim_telarray' allows for multiple model versions (multipipe option)
|
|
167
129
|
corsika_configurations = (
|
|
@@ -172,42 +134,6 @@ class Simulator:
|
|
|
172
134
|
|
|
173
135
|
return array_model, corsika_configurations
|
|
174
136
|
|
|
175
|
-
def _get_seed_for_random_instrument_instances(
|
|
176
|
-
self, seed, model_version, zenith_angle, azimuth_angle
|
|
177
|
-
):
|
|
178
|
-
"""
|
|
179
|
-
Generate seed for random instances of the instrument.
|
|
180
|
-
|
|
181
|
-
Parameters
|
|
182
|
-
----------
|
|
183
|
-
seed : str
|
|
184
|
-
Seed string given through configuration.
|
|
185
|
-
model_version: str
|
|
186
|
-
Model version.
|
|
187
|
-
zenith_angle: float
|
|
188
|
-
Zenith angle of the observation (in degrees).
|
|
189
|
-
azimuth_angle: float
|
|
190
|
-
Azimuth angle of the observation (in degrees).
|
|
191
|
-
|
|
192
|
-
Returns
|
|
193
|
-
-------
|
|
194
|
-
int
|
|
195
|
-
Seed for random instances of the instrument.
|
|
196
|
-
"""
|
|
197
|
-
if seed:
|
|
198
|
-
return int(seed.split(",")[0].strip())
|
|
199
|
-
|
|
200
|
-
def key_index(key):
|
|
201
|
-
try:
|
|
202
|
-
return list(names.site_names()).index(key) + 1
|
|
203
|
-
except ValueError:
|
|
204
|
-
return 1
|
|
205
|
-
|
|
206
|
-
seed = semver_to_int(model_version) * 10000000
|
|
207
|
-
seed = seed + key_index(self.site) * 1000000
|
|
208
|
-
seed = seed + (int)(zenith_angle) * 1000
|
|
209
|
-
return seed + (int)(azimuth_angle)
|
|
210
|
-
|
|
211
137
|
def _initialize_simulation_runner(self):
|
|
212
138
|
"""
|
|
213
139
|
Initialize corsika configuration and simulation runners.
|
|
@@ -218,29 +144,22 @@ class Simulator:
|
|
|
218
144
|
Simulation runner object.
|
|
219
145
|
"""
|
|
220
146
|
runner_class = {
|
|
221
|
-
"corsika": CorsikaRunner,
|
|
147
|
+
"corsika": corsika_runner.CorsikaRunner,
|
|
222
148
|
"sim_telarray": SimulatorArray,
|
|
223
|
-
"corsika_sim_telarray": CorsikaSimtelRunner,
|
|
149
|
+
"corsika_sim_telarray": corsika_simtel_runner.CorsikaSimtelRunner,
|
|
224
150
|
}.get(self.simulation_software)
|
|
225
151
|
|
|
226
152
|
runner_args = {
|
|
227
153
|
"label": self.label,
|
|
228
154
|
"corsika_config": self.corsika_configurations,
|
|
229
|
-
"use_multipipe": runner_class is CorsikaSimtelRunner,
|
|
230
155
|
}
|
|
231
156
|
|
|
232
157
|
if runner_class is not SimulatorArray:
|
|
233
|
-
runner_args["
|
|
234
|
-
runner_args["curved_atmosphere_min_zenith_angle"] = self.args_dict.get(
|
|
158
|
+
runner_args["curved_atmosphere_min_zenith_angle"] = settings.config.args.get(
|
|
235
159
|
"curved_atmosphere_min_zenith_angle", 65 * u.deg
|
|
236
160
|
)
|
|
237
|
-
if runner_class is
|
|
238
|
-
runner_args["
|
|
239
|
-
if runner_class is CorsikaSimtelRunner:
|
|
240
|
-
runner_args["sequential"] = self.args_dict.get("sequential", False)
|
|
241
|
-
runner_args["calibration_config"] = (
|
|
242
|
-
self.args_dict if self._is_calibration_run(self.run_mode) else None
|
|
243
|
-
)
|
|
161
|
+
if runner_class is corsika_simtel_runner.CorsikaSimtelRunner:
|
|
162
|
+
runner_args["sequential"] = settings.config.args.get("sequential", False)
|
|
244
163
|
|
|
245
164
|
return runner_class(**runner_args)
|
|
246
165
|
|
|
@@ -251,25 +170,21 @@ class Simulator:
|
|
|
251
170
|
Writes submission scripts using the simulation runners and submits the
|
|
252
171
|
run script to the job manager. Collects generated files.
|
|
253
172
|
"""
|
|
254
|
-
|
|
173
|
+
self._simulation_runner.prepare_run(
|
|
255
174
|
run_number=self.run_number,
|
|
256
|
-
|
|
175
|
+
corsika_file=self._get_corsika_file(),
|
|
176
|
+
sub_script=self.runner_service.get_file_name("sub_script", self.run_number),
|
|
257
177
|
extra_commands=self._extra_commands,
|
|
258
178
|
)
|
|
179
|
+
self.update_file_lists()
|
|
259
180
|
|
|
260
|
-
job_manager = JobManager(test=self._test)
|
|
261
181
|
job_manager.submit(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
log_file=self._simulation_runner.get_file_name(
|
|
267
|
-
file_type=("log"), run_number=self.run_number
|
|
268
|
-
),
|
|
182
|
+
command=self.runner_service.get_file_name("sub_script", self.run_number),
|
|
183
|
+
out_file=self.runner_service.get_file_name("sub_out", self.run_number),
|
|
184
|
+
err_file=self.runner_service.get_file_name("sub_err", self.run_number),
|
|
185
|
+
env=simtel_runner.SIM_TELARRAY_ENV,
|
|
269
186
|
)
|
|
270
187
|
|
|
271
|
-
self._fill_list_of_generated_files()
|
|
272
|
-
|
|
273
188
|
def _get_corsika_file(self):
|
|
274
189
|
"""
|
|
275
190
|
Get the CORSIKA input file if applicable (for sim_telarray simulations).
|
|
@@ -280,61 +195,12 @@ class Simulator:
|
|
|
280
195
|
Path to the CORSIKA input file.
|
|
281
196
|
"""
|
|
282
197
|
if self.simulation_software == "sim_telarray":
|
|
283
|
-
return
|
|
198
|
+
return settings.config.args.get("corsika_file", None)
|
|
284
199
|
return None
|
|
285
200
|
|
|
286
|
-
def
|
|
287
|
-
"""
|
|
288
|
-
|
|
289
|
-
"simtel_output",
|
|
290
|
-
"sub_out",
|
|
291
|
-
"log",
|
|
292
|
-
"input",
|
|
293
|
-
"histogram",
|
|
294
|
-
"corsika_log",
|
|
295
|
-
"corsika_output",
|
|
296
|
-
"event_data",
|
|
297
|
-
]
|
|
298
|
-
results = {key: [] for key in keys}
|
|
299
|
-
|
|
300
|
-
def get_file_name(name, **kwargs):
|
|
301
|
-
return str(self._simulation_runner.get_file_name(file_type=name, **kwargs))
|
|
302
|
-
|
|
303
|
-
results["sub_out"].append(get_file_name("sub_log", mode="out", run_number=self.run_number))
|
|
304
|
-
|
|
305
|
-
for i in range(len(self.array_models)):
|
|
306
|
-
results["simtel_output"].append(
|
|
307
|
-
get_file_name("simtel_output", run_number=self.run_number, model_version_index=i)
|
|
308
|
-
)
|
|
309
|
-
|
|
310
|
-
if "sim_telarray" in self.simulation_software:
|
|
311
|
-
for file_type in ("log", "histogram", "event_data"):
|
|
312
|
-
results[file_type].append(
|
|
313
|
-
get_file_name(
|
|
314
|
-
file_type,
|
|
315
|
-
simulation_software="sim_telarray",
|
|
316
|
-
run_number=self.run_number,
|
|
317
|
-
model_version_index=i,
|
|
318
|
-
)
|
|
319
|
-
)
|
|
320
|
-
|
|
321
|
-
if "corsika" in self.simulation_software:
|
|
322
|
-
for file_type in ("corsika_output", "corsika_log"):
|
|
323
|
-
results[file_type].append(
|
|
324
|
-
get_file_name(
|
|
325
|
-
file_type,
|
|
326
|
-
simulation_software="corsika",
|
|
327
|
-
run_number=self.run_number,
|
|
328
|
-
model_version_index=i,
|
|
329
|
-
)
|
|
330
|
-
)
|
|
331
|
-
|
|
332
|
-
for key in keys:
|
|
333
|
-
self._results[key].extend(results[key])
|
|
334
|
-
|
|
335
|
-
def get_file_list(self, file_type="simtel_output"):
|
|
336
|
-
"""
|
|
337
|
-
Get list of files generated by simulations.
|
|
201
|
+
def get_files(self, file_type):
|
|
202
|
+
"""
|
|
203
|
+
Get file(s) generated by simulations.
|
|
338
204
|
|
|
339
205
|
Not all file types are available for all simulation types.
|
|
340
206
|
Returns an empty list for an unknown file type.
|
|
@@ -346,34 +212,24 @@ class Simulator:
|
|
|
346
212
|
|
|
347
213
|
Returns
|
|
348
214
|
-------
|
|
349
|
-
list
|
|
350
|
-
|
|
215
|
+
Path or list[Path]:
|
|
216
|
+
File or list with the full path of output files of a certain file type.
|
|
351
217
|
|
|
352
218
|
"""
|
|
353
|
-
return self.
|
|
219
|
+
return self.file_list[file_type]
|
|
354
220
|
|
|
355
|
-
def _make_resources_report(self
|
|
221
|
+
def _make_resources_report(self):
|
|
356
222
|
"""
|
|
357
223
|
Prepare a simple report on computing wall clock time used in the simulations.
|
|
358
224
|
|
|
359
|
-
Parameters
|
|
360
|
-
----------
|
|
361
|
-
input_file_list: str or list of str
|
|
362
|
-
Single file or list of files of shower simulations.
|
|
363
|
-
|
|
364
225
|
Returns
|
|
365
226
|
-------
|
|
366
227
|
str
|
|
367
228
|
string reporting on computing resources
|
|
368
229
|
|
|
369
230
|
"""
|
|
370
|
-
if len(self._results["sub_out"]) == 0 and input_file_list is None:
|
|
371
|
-
return "Mean wall time/run [sec]: np.nan"
|
|
372
|
-
|
|
373
231
|
runtime = []
|
|
374
|
-
|
|
375
|
-
_resources = {}
|
|
376
|
-
_resources = self._simulation_runner.get_resources(run_number=self.run_number)
|
|
232
|
+
_resources = self._simulation_runner.get_resources(self.get_files(file_type="sub_out"))
|
|
377
233
|
if _resources.get("runtime"):
|
|
378
234
|
runtime.append(_resources["runtime"])
|
|
379
235
|
|
|
@@ -385,17 +241,11 @@ class Simulator:
|
|
|
385
241
|
|
|
386
242
|
return resource_summary
|
|
387
243
|
|
|
388
|
-
def report(self
|
|
244
|
+
def report(self):
|
|
389
245
|
"""
|
|
390
246
|
Report on simulations and computing resources used.
|
|
391
247
|
|
|
392
248
|
Includes run time per run only at this point.
|
|
393
|
-
|
|
394
|
-
Parameters
|
|
395
|
-
----------
|
|
396
|
-
input_file_list: str or list of str
|
|
397
|
-
Single file or list of files of shower simulations.
|
|
398
|
-
|
|
399
249
|
"""
|
|
400
250
|
_corsika_config = self._get_first_corsika_config()
|
|
401
251
|
self.logger.info(
|
|
@@ -405,22 +255,23 @@ class Simulator:
|
|
|
405
255
|
f"at {self.site} site, using {self.model_version} model."
|
|
406
256
|
)
|
|
407
257
|
self.logger.info(
|
|
408
|
-
f"Computing for {self.simulation_software} Simulations: "
|
|
409
|
-
f"{self._make_resources_report(input_file_list)}"
|
|
258
|
+
f"Computing for {self.simulation_software} Simulations: {self._make_resources_report()}"
|
|
410
259
|
)
|
|
411
260
|
|
|
412
261
|
def save_file_lists(self):
|
|
413
|
-
"""Save
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
if
|
|
418
|
-
self.logger.info(f"Saving list of {file_type} files to {file_name}")
|
|
419
|
-
with open(file_name, "w", encoding="utf-8") as f:
|
|
420
|
-
for line in self.get_file_list(file_type=file_type):
|
|
421
|
-
f.write(f"{line}\n")
|
|
422
|
-
else:
|
|
262
|
+
"""Save file lists for output and log files."""
|
|
263
|
+
outdir = self.io_handler.get_output_directory()
|
|
264
|
+
|
|
265
|
+
for file_type, files in self.file_list.items():
|
|
266
|
+
if not files or any(f is None for f in files):
|
|
423
267
|
self.logger.debug(f"No files to save for {file_type} files.")
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
path = outdir / f"{file_type}_files.txt"
|
|
271
|
+
self.logger.info(f"Saving list of {file_type} files to {path}")
|
|
272
|
+
|
|
273
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
274
|
+
f.write("\n".join(map(str, files)) + "\n")
|
|
424
275
|
|
|
425
276
|
def save_reduced_event_lists(self):
|
|
426
277
|
"""
|
|
@@ -435,10 +286,10 @@ class Simulator:
|
|
|
435
286
|
)
|
|
436
287
|
return
|
|
437
288
|
|
|
438
|
-
input_files = self.
|
|
439
|
-
output_files = self.
|
|
289
|
+
input_files = self.get_files(file_type="sim_telarray_output")
|
|
290
|
+
output_files = self.get_files(file_type="sim_telarray_event_data")
|
|
440
291
|
for input_file, output_file in zip(input_files, output_files):
|
|
441
|
-
generator = EventDataWriter([input_file])
|
|
292
|
+
generator = writer.EventDataWriter([input_file])
|
|
442
293
|
table_handler.write_tables(
|
|
443
294
|
tables=generator.process_files(),
|
|
444
295
|
output_file=Path(output_file),
|
|
@@ -458,15 +309,14 @@ class Simulator:
|
|
|
458
309
|
|
|
459
310
|
"""
|
|
460
311
|
self.logger.info(
|
|
461
|
-
f"Packing
|
|
312
|
+
f"Packing output files for registering on the grid ({directory_for_grid_upload})"
|
|
462
313
|
)
|
|
463
|
-
output_files = self.
|
|
464
|
-
log_files = self.
|
|
465
|
-
|
|
466
|
-
histogram_files = self.get_file_list(file_type="histogram")
|
|
314
|
+
output_files = self.get_files(file_type="sim_telarray_output")
|
|
315
|
+
log_files = self.get_files(file_type="sim_telarray_log")
|
|
316
|
+
histogram_files = self.get_files(file_type="sim_telarray_histogram")
|
|
467
317
|
reduced_event_files = (
|
|
468
|
-
self.
|
|
469
|
-
if
|
|
318
|
+
self.get_files(file_type="sim_telarray_event_data")
|
|
319
|
+
if settings.config.args.get("save_reduced_event_lists")
|
|
470
320
|
else []
|
|
471
321
|
)
|
|
472
322
|
|
|
@@ -477,36 +327,32 @@ class Simulator:
|
|
|
477
327
|
)
|
|
478
328
|
directory_for_grid_upload.mkdir(parents=True, exist_ok=True)
|
|
479
329
|
|
|
480
|
-
# If there are more than one model version,
|
|
481
|
-
# duplicate the corsika log file to have one for each model version with the "right name".
|
|
482
|
-
if len(self.array_models) > 1 and corsika_log_files:
|
|
483
|
-
self._copy_corsika_log_file_for_all_versions(corsika_log_files)
|
|
484
|
-
|
|
485
330
|
# Group files by model version
|
|
486
331
|
for model in self.array_models:
|
|
487
332
|
model_version = model.model_version
|
|
488
|
-
|
|
333
|
+
model_logs = [f for f in log_files if model_version in str(f)]
|
|
489
334
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
model_hists = [f for f in histogram_files if model_version in f]
|
|
493
|
-
model_corsika_logs = [f for f in corsika_log_files if model_version in f]
|
|
335
|
+
if not model_logs:
|
|
336
|
+
continue
|
|
494
337
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
338
|
+
tar_name = Path(model_logs[0]).name.replace("simtel.log.gz", "log_hist.tar.gz")
|
|
339
|
+
tar_path = directory_for_grid_upload / tar_name
|
|
340
|
+
|
|
341
|
+
files_to_tar = (
|
|
342
|
+
model_logs
|
|
343
|
+
+ [f for f in histogram_files if model_version in str(f)]
|
|
344
|
+
+ [str(self.get_files(file_type="corsika_log"))]
|
|
345
|
+
+ list(general.ensure_iterable(model.pack_model_files()))
|
|
346
|
+
)
|
|
347
|
+
general.pack_tar_file(tar_path, files_to_tar)
|
|
501
348
|
|
|
502
349
|
for file_to_move in output_files + reduced_event_files:
|
|
503
|
-
|
|
504
|
-
destination_file = directory_for_grid_upload / source_file.name
|
|
350
|
+
destination_file = directory_for_grid_upload / Path(file_to_move).name
|
|
505
351
|
if destination_file.exists():
|
|
506
352
|
self.logger.warning(f"Overwriting existing file: {destination_file}")
|
|
507
|
-
shutil.move(
|
|
353
|
+
shutil.move(file_to_move, destination_file)
|
|
508
354
|
|
|
509
|
-
self.logger.info(f"
|
|
355
|
+
self.logger.info(f"Grid output files grid placed in {directory_for_grid_upload!s}")
|
|
510
356
|
|
|
511
357
|
def validate_metadata(self):
|
|
512
358
|
"""Validate metadata in the sim_telarray output files."""
|
|
@@ -515,8 +361,9 @@ class Simulator:
|
|
|
515
361
|
return
|
|
516
362
|
|
|
517
363
|
for model in self.array_models:
|
|
518
|
-
files = self.
|
|
519
|
-
|
|
364
|
+
files = general.ensure_iterable(self.get_files(file_type="sim_telarray_output"))
|
|
365
|
+
|
|
366
|
+
output_file = next((f for f in files if model.model_version in str(f)), None)
|
|
520
367
|
if output_file:
|
|
521
368
|
self.logger.info(f"Validating metadata for {output_file}")
|
|
522
369
|
assert_sim_telarray_metadata(output_file, model)
|
|
@@ -526,76 +373,6 @@ class Simulator:
|
|
|
526
373
|
f"No sim_telarray file found for model version {model.model_version}: {files}"
|
|
527
374
|
)
|
|
528
375
|
|
|
529
|
-
def _copy_corsika_log_file_for_all_versions(self, corsika_log_files):
|
|
530
|
-
"""
|
|
531
|
-
Create copies of the CORSIKA log file for each model version.
|
|
532
|
-
|
|
533
|
-
Adds a header comment to each copy explaining its relationship to the original.
|
|
534
|
-
|
|
535
|
-
Parameters
|
|
536
|
-
----------
|
|
537
|
-
corsika_log_files: list
|
|
538
|
-
List containing the original CORSIKA log file path.
|
|
539
|
-
"""
|
|
540
|
-
original_log = Path(corsika_log_files[0])
|
|
541
|
-
# Find which model version the original log belongs to
|
|
542
|
-
original_version = next(
|
|
543
|
-
model.model_version
|
|
544
|
-
for model in self.array_models
|
|
545
|
-
if re.search(
|
|
546
|
-
rf"(?<![0-9A-Za-z]){re.escape(model.model_version)}(?![0-9A-Za-z])",
|
|
547
|
-
original_log.name,
|
|
548
|
-
)
|
|
549
|
-
)
|
|
550
|
-
|
|
551
|
-
for model in self.array_models:
|
|
552
|
-
if model.model_version == original_version:
|
|
553
|
-
continue
|
|
554
|
-
|
|
555
|
-
new_log = original_log.parent / original_log.name.replace(
|
|
556
|
-
original_version, model.model_version
|
|
557
|
-
)
|
|
558
|
-
|
|
559
|
-
with gzip.open(new_log, "wt", encoding="utf-8") as new_file:
|
|
560
|
-
# Write the header to the new file
|
|
561
|
-
header = (
|
|
562
|
-
f"###############################################################\n"
|
|
563
|
-
f"Copy of CORSIKA log file from model version {original_version}.\n"
|
|
564
|
-
f"Applicable also for {model.model_version} (same CORSIKA configuration,\n"
|
|
565
|
-
f"different sim_telarray model versions in the same run).\n"
|
|
566
|
-
f"###############################################################\n\n"
|
|
567
|
-
)
|
|
568
|
-
new_file.write(header)
|
|
569
|
-
|
|
570
|
-
# Copy the content of the original log file, ignoring invalid characters
|
|
571
|
-
with gzip.open(original_log, "rt", encoding="utf-8", errors="ignore") as orig_file:
|
|
572
|
-
for line in orig_file:
|
|
573
|
-
new_file.write(line)
|
|
574
|
-
|
|
575
|
-
corsika_log_files.append(str(new_log))
|
|
576
|
-
|
|
577
|
-
@staticmethod
|
|
578
|
-
def _is_calibration_run(run_mode):
|
|
579
|
-
"""
|
|
580
|
-
Check if this simulation is a calibration run.
|
|
581
|
-
|
|
582
|
-
Parameters
|
|
583
|
-
----------
|
|
584
|
-
run_mode: str
|
|
585
|
-
Run mode of the simulation.
|
|
586
|
-
|
|
587
|
-
Returns
|
|
588
|
-
-------
|
|
589
|
-
bool
|
|
590
|
-
True if it is a calibration run, False otherwise.
|
|
591
|
-
"""
|
|
592
|
-
return run_mode in [
|
|
593
|
-
"pedestals",
|
|
594
|
-
"pedestals_dark",
|
|
595
|
-
"pedestals_nsb_only",
|
|
596
|
-
"direct_injection",
|
|
597
|
-
]
|
|
598
|
-
|
|
599
376
|
@staticmethod
|
|
600
377
|
def _get_calibration_device_types(run_mode):
|
|
601
378
|
"""
|
|
@@ -659,7 +436,7 @@ class Simulator:
|
|
|
659
436
|
)
|
|
660
437
|
if self.simulation_software == "corsika":
|
|
661
438
|
self._verify_simulated_events_corsika(expected_mc_events)
|
|
662
|
-
if
|
|
439
|
+
if settings.config.args.get("save_reduced_event_lists"):
|
|
663
440
|
self._verify_simulated_events_in_reduced_event_lists(expected_mc_events)
|
|
664
441
|
|
|
665
442
|
def _verify_simulated_events_corsika(self, expected_mc_events, tolerance=1.0e-3):
|
|
@@ -683,24 +460,25 @@ class Simulator:
|
|
|
683
460
|
return abs(a - b) / max(a, b) <= tol
|
|
684
461
|
|
|
685
462
|
event_errors = []
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
)
|
|
695
|
-
|
|
463
|
+
|
|
464
|
+
file = self.get_files(file_type="corsika_output")
|
|
465
|
+
shower_events, _ = file_info.get_simulated_events(file)
|
|
466
|
+
|
|
467
|
+
if shower_events != expected_mc_events:
|
|
468
|
+
if consistent(shower_events, expected_mc_events, tol=tolerance):
|
|
469
|
+
self.logger.warning(
|
|
470
|
+
f"Small mismatch in number of events in: {file}: "
|
|
471
|
+
f"shower events: {shower_events} (expected: {expected_mc_events})"
|
|
472
|
+
)
|
|
473
|
+
else:
|
|
696
474
|
event_errors.append(
|
|
697
475
|
f"Number of simulated MC events ({shower_events}) does not match "
|
|
698
476
|
f"the expected number ({expected_mc_events}) in CORSIKA {file}."
|
|
699
477
|
)
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
478
|
+
else:
|
|
479
|
+
self.logger.info(
|
|
480
|
+
f"Consistent number of events in: {file}: shower events: {shower_events}"
|
|
481
|
+
)
|
|
704
482
|
|
|
705
483
|
if event_errors:
|
|
706
484
|
self.logger.error("Inconsistent event counts found in CORSIKA output:")
|
|
@@ -728,7 +506,7 @@ class Simulator:
|
|
|
728
506
|
If the number of simulated events does not match the expected number.
|
|
729
507
|
"""
|
|
730
508
|
event_errors = []
|
|
731
|
-
for file in self.
|
|
509
|
+
for file in general.ensure_iterable(self.get_files(file_type="sim_telarray_output")):
|
|
732
510
|
shower_events, mc_events = file_info.get_simulated_events(file)
|
|
733
511
|
|
|
734
512
|
if (shower_events, mc_events) != (expected_shower_events, expected_mc_events):
|
|
@@ -767,7 +545,7 @@ class Simulator:
|
|
|
767
545
|
If the number of simulated events does not match the expected number.
|
|
768
546
|
"""
|
|
769
547
|
event_errors = []
|
|
770
|
-
for file in self.
|
|
548
|
+
for file in self.get_files(file_type="sim_telarray_event_data"):
|
|
771
549
|
tables = table_handler.read_tables(file, ["SHOWERS"])
|
|
772
550
|
try:
|
|
773
551
|
mc_events = len(tables["SHOWERS"])
|
|
@@ -793,3 +571,14 @@ class Simulator:
|
|
|
793
571
|
f" - {error}" for error in event_errors
|
|
794
572
|
)
|
|
795
573
|
raise ValueError(error_message)
|
|
574
|
+
|
|
575
|
+
def update_file_lists(self):
|
|
576
|
+
"""
|
|
577
|
+
Update file lists with all data, log, histogram and submission files.
|
|
578
|
+
|
|
579
|
+
Some of these files are generated by the simulation runners.
|
|
580
|
+
"""
|
|
581
|
+
if self.file_list is None:
|
|
582
|
+
self.file_list = self._simulation_runner.file_list
|
|
583
|
+
else:
|
|
584
|
+
self.file_list.update(self._simulation_runner.file_list)
|