gammasimtools 0.25.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.
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/METADATA +2 -1
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/RECORD +122 -121
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/entry_points.txt +2 -1
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/licenses/LICENSE +1 -1
- simtools/_version.py +2 -2
- simtools/application_control.py +35 -7
- simtools/applications/calculate_incident_angles.py +0 -2
- simtools/applications/convert_geo_coordinates_of_array_elements.py +1 -2
- simtools/applications/db_add_file_to_db.py +1 -1
- simtools/applications/db_add_simulation_model_from_repository_to_db.py +1 -1
- simtools/applications/db_add_value_from_json_to_db.py +1 -1
- simtools/applications/db_generate_compound_indexes.py +1 -1
- simtools/applications/db_get_array_layouts_from_db.py +2 -6
- simtools/applications/db_get_file_from_db.py +1 -1
- simtools/applications/db_get_parameter_from_db.py +1 -1
- simtools/applications/db_inspect_databases.py +1 -1
- simtools/applications/db_upload_model_repository.py +1 -1
- simtools/applications/derive_ctao_array_layouts.py +1 -2
- simtools/applications/derive_mirror_rnda.py +1 -3
- simtools/applications/derive_psf_parameters.py +0 -1
- simtools/applications/derive_pulse_shape_parameters.py +0 -1
- simtools/applications/derive_trigger_rates.py +1 -1
- simtools/applications/docs_produce_array_element_report.py +2 -8
- simtools/applications/docs_produce_calibration_reports.py +1 -3
- simtools/applications/docs_produce_model_parameter_reports.py +0 -2
- simtools/applications/docs_produce_simulation_configuration_report.py +1 -3
- simtools/applications/generate_array_config.py +0 -1
- simtools/applications/generate_corsika_histograms.py +48 -235
- simtools/applications/generate_regular_arrays.py +5 -35
- simtools/applications/generate_simtel_event_data.py +2 -2
- simtools/applications/maintain_simulation_model_add_production.py +2 -2
- simtools/applications/maintain_simulation_model_write_array_element_positions.py +87 -0
- simtools/applications/plot_array_layout.py +5 -111
- simtools/applications/plot_simulated_event_distributions.py +57 -0
- simtools/applications/plot_tabular_data.py +0 -1
- simtools/applications/plot_tabular_data_for_model_parameter.py +1 -6
- simtools/applications/production_derive_corsika_limits.py +1 -1
- simtools/applications/production_generate_grid.py +0 -1
- simtools/applications/run_application.py +1 -1
- simtools/applications/simulate_flasher.py +0 -2
- simtools/applications/simulate_illuminator.py +0 -1
- simtools/applications/simulate_pedestals.py +1 -5
- simtools/applications/simulate_prod.py +1 -5
- simtools/applications/simulate_prod_htcondor_generator.py +1 -1
- simtools/applications/submit_array_layouts.py +2 -4
- simtools/applications/submit_model_parameter_from_external.py +1 -3
- simtools/applications/validate_camera_efficiency.py +0 -1
- simtools/applications/validate_camera_fov.py +0 -1
- simtools/applications/validate_cumulative_psf.py +0 -2
- simtools/applications/validate_optics.py +0 -13
- simtools/camera/camera_efficiency.py +1 -6
- simtools/camera/single_photon_electron_spectrum.py +2 -1
- simtools/configuration/commandline_parser.py +35 -2
- simtools/configuration/configurator.py +6 -11
- simtools/corsika/corsika_config.py +16 -21
- simtools/corsika/corsika_histograms.py +411 -1735
- simtools/corsika/primary_particle.py +1 -1
- simtools/data_model/metadata_collector.py +5 -2
- simtools/data_model/metadata_model.py +0 -4
- simtools/data_model/model_data_writer.py +13 -15
- simtools/data_model/validate_data.py +1 -3
- simtools/db/db_handler.py +19 -8
- simtools/dependencies.py +81 -38
- simtools/io/ascii_handler.py +4 -2
- simtools/io/table_handler.py +1 -1
- simtools/layout/array_layout.py +4 -12
- simtools/layout/array_layout_utils.py +226 -57
- simtools/model/array_model.py +1 -13
- simtools/model/calibration_model.py +0 -4
- simtools/model/legacy_model_parameter.py +134 -0
- simtools/model/model_parameter.py +24 -13
- simtools/model/model_utils.py +1 -6
- simtools/model/site_model.py +0 -4
- simtools/model/telescope_model.py +6 -11
- simtools/production_configuration/derive_corsika_limits.py +6 -11
- simtools/production_configuration/interpolation_handler.py +16 -16
- simtools/ray_tracing/incident_angles.py +5 -11
- simtools/ray_tracing/mirror_panel_psf.py +3 -7
- simtools/ray_tracing/psf_analysis.py +18 -19
- simtools/ray_tracing/psf_parameter_optimisation.py +0 -1
- simtools/ray_tracing/ray_tracing.py +6 -15
- simtools/reporting/docs_auto_report_generator.py +8 -13
- simtools/reporting/docs_read_parameters.py +2 -8
- simtools/runners/corsika_runner.py +5 -9
- simtools/runners/corsika_simtel_runner.py +3 -8
- simtools/runners/simtel_runner.py +0 -5
- simtools/runners/simtools_runner.py +2 -4
- simtools/settings.py +154 -0
- simtools/{io/eventio_handler.py → sim_events/file_info.py} +3 -3
- simtools/{simtel/simtel_io_event_histograms.py → sim_events/histograms.py} +25 -15
- simtools/{simtel/simtel_io_event_reader.py → sim_events/reader.py} +20 -17
- simtools/{simtel/simtel_io_event_writer.py → sim_events/writer.py} +84 -25
- simtools/simtel/pulse_shapes.py +7 -2
- simtools/simtel/simtel_config_writer.py +79 -36
- simtools/simtel/simtel_table_reader.py +6 -4
- simtools/simtel/simulator_array.py +4 -11
- simtools/simtel/simulator_camera_efficiency.py +4 -6
- simtools/simtel/simulator_light_emission.py +69 -24
- simtools/simtel/simulator_ray_tracing.py +4 -10
- simtools/simulator.py +7 -14
- simtools/telescope_trigger_rates.py +3 -4
- simtools/testing/assertions.py +84 -33
- simtools/testing/configuration.py +1 -2
- simtools/testing/helpers.py +2 -3
- simtools/testing/log_inspector.py +1 -0
- simtools/testing/sim_telarray_metadata.py +1 -1
- simtools/testing/validate_output.py +34 -23
- simtools/utils/general.py +37 -0
- simtools/utils/geometry.py +0 -77
- simtools/utils/names.py +5 -5
- simtools/visualization/legend_handlers.py +7 -6
- simtools/visualization/plot_array_layout.py +91 -16
- simtools/visualization/plot_corsika_histograms.py +143 -605
- simtools/visualization/plot_mirrors.py +1 -4
- simtools/visualization/plot_pixels.py +2 -4
- simtools/visualization/plot_psf.py +0 -1
- simtools/visualization/plot_simtel_event_histograms.py +4 -4
- simtools/visualization/plot_simtel_events.py +6 -11
- simtools/visualization/plot_tables.py +8 -19
- simtools/visualization/visualize.py +22 -2
- simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +0 -160
- simtools/applications/print_version.py +0 -53
- simtools/io/hdf5_handler.py +0 -139
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/WHEEL +0 -0
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.26.0.dist-info}/top_level.txt +0 -0
|
@@ -9,6 +9,7 @@ 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
|
|
@@ -31,21 +32,18 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
31
32
|
Label for the simulation
|
|
32
33
|
"""
|
|
33
34
|
|
|
34
|
-
def __init__(self, light_emission_config,
|
|
35
|
+
def __init__(self, light_emission_config, label=None):
|
|
35
36
|
"""Initialize SimulatorLightEmission."""
|
|
36
37
|
self._logger = logging.getLogger(__name__)
|
|
37
38
|
self.io_handler = io_handler.IOHandler()
|
|
38
39
|
|
|
39
|
-
super().__init__(
|
|
40
|
-
simtel_path=light_emission_config.get("simtel_path"), label=label, corsika_config=None
|
|
41
|
-
)
|
|
40
|
+
super().__init__(label=label, corsika_config=None)
|
|
42
41
|
|
|
43
42
|
self.output_directory = self.io_handler.get_output_directory()
|
|
44
43
|
|
|
45
44
|
self.telescope_model, self.site_model, self.calibration_model = (
|
|
46
45
|
initialize_simulation_models(
|
|
47
46
|
label=label,
|
|
48
|
-
db_config=db_config,
|
|
49
47
|
site=light_emission_config.get("site"),
|
|
50
48
|
telescope_name=light_emission_config.get("telescope"),
|
|
51
49
|
calibration_device_name=light_emission_config.get("light_source"),
|
|
@@ -68,7 +66,7 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
68
66
|
config["flasher_photons"] = (
|
|
69
67
|
self.calibration_model.get_parameter_value("flasher_photons")
|
|
70
68
|
if not config.get("test", False)
|
|
71
|
-
else
|
|
69
|
+
else 1e5
|
|
72
70
|
)
|
|
73
71
|
|
|
74
72
|
if config.get("light_source_position") is not None:
|
|
@@ -292,7 +290,7 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
292
290
|
"corsika_observation_level"
|
|
293
291
|
)
|
|
294
292
|
|
|
295
|
-
parts = [str(
|
|
293
|
+
parts = [str(settings.config.sim_telarray_path / "LightEmission") + f"/{app_name}"]
|
|
296
294
|
parts.extend(self._get_site_command(app_name, config_directory, corsika_observation_level))
|
|
297
295
|
parts.extend(self._get_light_source_command())
|
|
298
296
|
if self.light_emission_config["light_source_type"] == "illuminator":
|
|
@@ -312,7 +310,7 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
312
310
|
atmo_id = self._prepare_flasher_atmosphere_files(config_directory)
|
|
313
311
|
return [
|
|
314
312
|
"-I.",
|
|
315
|
-
f"-I{
|
|
313
|
+
f"-I{settings.config.sim_telarray_path / 'cfg'}",
|
|
316
314
|
f"-I{config_directory}",
|
|
317
315
|
f"--altitude {corsika_observation_level.to(u.m).value}",
|
|
318
316
|
f"--atmosphere {atmo_id}",
|
|
@@ -349,16 +347,19 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
349
347
|
dist_cm = self.calculate_distance_focal_plane_calibration_device().to(u.cm).value
|
|
350
348
|
angular_distribution = self._get_angular_distribution_string_for_sim_telarray()
|
|
351
349
|
|
|
352
|
-
# Build pulse table for ff-1m using
|
|
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]
|
|
353
355
|
pulse_arg = self._get_pulse_shape_string_for_sim_telarray()
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
):
|
|
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
|
+
)
|
|
362
363
|
try:
|
|
363
364
|
base_dir = self.io_handler.get_output_directory("pulse_shapes")
|
|
364
365
|
|
|
@@ -373,10 +374,10 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
373
374
|
table_path = base_dir / fname
|
|
374
375
|
fadc_bins = self.telescope_model.get_parameter_value("fadc_sum_bins")
|
|
375
376
|
|
|
376
|
-
SimtelConfigWriter.
|
|
377
|
+
SimtelConfigWriter.write_light_pulse_table_gauss_exp_conv(
|
|
377
378
|
file_path=table_path,
|
|
378
|
-
width_ns=
|
|
379
|
-
exp_decay_ns=
|
|
379
|
+
width_ns=width_ns,
|
|
380
|
+
exp_decay_ns=exp_ns,
|
|
380
381
|
fadc_sum_bins=fadc_bins,
|
|
381
382
|
time_margin_ns=5.0,
|
|
382
383
|
)
|
|
@@ -435,7 +436,7 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
435
436
|
"""
|
|
436
437
|
theta, phi = self._get_telescope_pointing()
|
|
437
438
|
|
|
438
|
-
simtel_bin =
|
|
439
|
+
simtel_bin = str(settings.config.sim_telarray_exe)
|
|
439
440
|
|
|
440
441
|
parts = [
|
|
441
442
|
f"{simtel_bin}",
|
|
@@ -503,6 +504,27 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
503
504
|
)
|
|
504
505
|
return focal_length - flasher_z
|
|
505
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
|
+
|
|
506
528
|
def _get_angular_distribution_string_for_sim_telarray(self):
|
|
507
529
|
"""
|
|
508
530
|
Get the angular distribution string for sim_telarray.
|
|
@@ -514,6 +536,16 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
514
536
|
"""
|
|
515
537
|
opt = self.calibration_model.get_parameter_value("flasher_angular_distribution")
|
|
516
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
|
+
|
|
517
549
|
width = self.calibration_model.get_parameter_value_with_unit(
|
|
518
550
|
"flasher_angular_distribution_width"
|
|
519
551
|
)
|
|
@@ -529,6 +561,19 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
529
561
|
The pulse shape string.
|
|
530
562
|
"""
|
|
531
563
|
opt = self.calibration_model.get_parameter_value("flasher_pulse_shape")
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
simtools/simulator.py
CHANGED
|
@@ -11,12 +11,13 @@ import numpy as np
|
|
|
11
11
|
from astropy import units as u
|
|
12
12
|
|
|
13
13
|
from simtools.corsika.corsika_config import CorsikaConfig
|
|
14
|
-
from simtools.io import
|
|
14
|
+
from simtools.io import io_handler, table_handler
|
|
15
15
|
from simtools.job_execution.job_manager import JobManager
|
|
16
16
|
from simtools.model.array_model import ArrayModel
|
|
17
17
|
from simtools.runners.corsika_runner import CorsikaRunner
|
|
18
18
|
from simtools.runners.corsika_simtel_runner import CorsikaSimtelRunner
|
|
19
|
-
from simtools.
|
|
19
|
+
from simtools.sim_events import file_info
|
|
20
|
+
from simtools.sim_events.writer import EventDataWriter
|
|
20
21
|
from simtools.simtel.simulator_array import SimulatorArray
|
|
21
22
|
from simtools.testing.sim_telarray_metadata import assert_sim_telarray_metadata
|
|
22
23
|
from simtools.utils import general, names
|
|
@@ -42,8 +43,6 @@ class Simulator:
|
|
|
42
43
|
Instance label.
|
|
43
44
|
extra_commands: str or list of str
|
|
44
45
|
Extra commands to be added to the run script before the run command.
|
|
45
|
-
db_config: dict
|
|
46
|
-
Database configuration.
|
|
47
46
|
"""
|
|
48
47
|
|
|
49
48
|
def __init__(
|
|
@@ -51,14 +50,12 @@ class Simulator:
|
|
|
51
50
|
args_dict,
|
|
52
51
|
label=None,
|
|
53
52
|
extra_commands=None,
|
|
54
|
-
db_config=None,
|
|
55
53
|
):
|
|
56
54
|
"""Initialize Simulator class."""
|
|
57
55
|
self.logger = logging.getLogger(__name__)
|
|
58
56
|
self.label = label
|
|
59
57
|
|
|
60
58
|
self.args_dict = args_dict
|
|
61
|
-
self.db_config = db_config
|
|
62
59
|
self.site = self.args_dict.get("site", None)
|
|
63
60
|
self.model_version = self.args_dict.get("model_version", None)
|
|
64
61
|
|
|
@@ -114,7 +111,7 @@ class Simulator:
|
|
|
114
111
|
}
|
|
115
112
|
|
|
116
113
|
if self.args_dict.get("corsika_file"):
|
|
117
|
-
self.run_number =
|
|
114
|
+
self.run_number = file_info.get_corsika_run_number(self.args_dict["corsika_file"])
|
|
118
115
|
else:
|
|
119
116
|
self.run_number = self.args_dict.get("run_number_offset", 0) + self.args_dict.get(
|
|
120
117
|
"run_number", 1
|
|
@@ -140,11 +137,9 @@ class Simulator:
|
|
|
140
137
|
label=self.label,
|
|
141
138
|
site=self.site,
|
|
142
139
|
layout_name=self.args_dict.get("array_layout_name"),
|
|
143
|
-
db_config=self.db_config,
|
|
144
140
|
model_version=version,
|
|
145
141
|
calibration_device_types=self._get_calibration_device_types(self.run_mode),
|
|
146
142
|
overwrite_model_parameters=self.args_dict.get("overwrite_model_parameters"),
|
|
147
|
-
simtel_path=self.args_dict.get("simtel_path"),
|
|
148
143
|
)
|
|
149
144
|
)
|
|
150
145
|
corsika_configurations.append(
|
|
@@ -152,7 +147,6 @@ class Simulator:
|
|
|
152
147
|
array_model=array_model[-1],
|
|
153
148
|
label=self.label,
|
|
154
149
|
args_dict=self.args_dict,
|
|
155
|
-
db_config=self.db_config,
|
|
156
150
|
dummy_simulations=self._is_calibration_run(self.run_mode),
|
|
157
151
|
)
|
|
158
152
|
)
|
|
@@ -232,7 +226,6 @@ class Simulator:
|
|
|
232
226
|
runner_args = {
|
|
233
227
|
"label": self.label,
|
|
234
228
|
"corsika_config": self.corsika_configurations,
|
|
235
|
-
"simtel_path": self.args_dict.get("simtel_path"),
|
|
236
229
|
"use_multipipe": runner_class is CorsikaSimtelRunner,
|
|
237
230
|
}
|
|
238
231
|
|
|
@@ -445,7 +438,7 @@ class Simulator:
|
|
|
445
438
|
input_files = self.get_file_list(file_type="simtel_output")
|
|
446
439
|
output_files = self.get_file_list(file_type="event_data")
|
|
447
440
|
for input_file, output_file in zip(input_files, output_files):
|
|
448
|
-
generator =
|
|
441
|
+
generator = EventDataWriter([input_file])
|
|
449
442
|
table_handler.write_tables(
|
|
450
443
|
tables=generator.process_files(),
|
|
451
444
|
output_file=Path(output_file),
|
|
@@ -691,7 +684,7 @@ class Simulator:
|
|
|
691
684
|
|
|
692
685
|
event_errors = []
|
|
693
686
|
for file in self.get_file_list(file_type="corsika_output"):
|
|
694
|
-
shower_events, _ =
|
|
687
|
+
shower_events, _ = file_info.get_simulated_events(file)
|
|
695
688
|
|
|
696
689
|
if shower_events != expected_mc_events:
|
|
697
690
|
if consistent(shower_events, expected_mc_events, tol=tolerance):
|
|
@@ -736,7 +729,7 @@ class Simulator:
|
|
|
736
729
|
"""
|
|
737
730
|
event_errors = []
|
|
738
731
|
for file in self.get_file_list(file_type="simtel_output"):
|
|
739
|
-
shower_events, mc_events =
|
|
732
|
+
shower_events, mc_events = file_info.get_simulated_events(file)
|
|
740
733
|
|
|
741
734
|
if (shower_events, mc_events) != (expected_shower_events, expected_mc_events):
|
|
742
735
|
event_errors.append(
|
|
@@ -8,13 +8,13 @@ from ctao_cr_spectra.definitions import IRFDOC_PROTON_SPECTRUM
|
|
|
8
8
|
|
|
9
9
|
from simtools.io import ascii_handler, io_handler
|
|
10
10
|
from simtools.layout.array_layout_utils import get_array_elements_from_db_for_layouts
|
|
11
|
-
from simtools.
|
|
11
|
+
from simtools.sim_events.histograms import EventDataHistograms
|
|
12
12
|
from simtools.visualization import plot_simtel_event_histograms
|
|
13
13
|
|
|
14
14
|
_logger = logging.getLogger(__name__)
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
def telescope_trigger_rates(args_dict
|
|
17
|
+
def telescope_trigger_rates(args_dict):
|
|
18
18
|
"""
|
|
19
19
|
Calculate trigger rates for single telescopes or arrays of telescopes.
|
|
20
20
|
|
|
@@ -27,7 +27,6 @@ def telescope_trigger_rates(args_dict, db_config):
|
|
|
27
27
|
args_dict["array_layout_name"],
|
|
28
28
|
args_dict.get("site"),
|
|
29
29
|
args_dict.get("model_version"),
|
|
30
|
-
db_config,
|
|
31
30
|
)
|
|
32
31
|
else:
|
|
33
32
|
telescope_configs = ascii_handler.collect_data_from_file(args_dict["telescope_ids"])[
|
|
@@ -38,7 +37,7 @@ def telescope_trigger_rates(args_dict, db_config):
|
|
|
38
37
|
_logger.info(
|
|
39
38
|
f"Processing file: {args_dict['event_data_file']} with telescope config: {array_name}"
|
|
40
39
|
)
|
|
41
|
-
histograms =
|
|
40
|
+
histograms = EventDataHistograms(
|
|
42
41
|
args_dict["event_data_file"], array_name=array_name, telescope_list=telescope_ids
|
|
43
42
|
)
|
|
44
43
|
histograms.fill()
|
simtools/testing/assertions.py
CHANGED
|
@@ -68,8 +68,7 @@ def assert_n_showers_and_energy_range(file):
|
|
|
68
68
|
simulation_config = {}
|
|
69
69
|
with SimTelFile(file, skip_non_triggered=False) as f:
|
|
70
70
|
simulation_config = f.mc_run_headers[0]
|
|
71
|
-
for event in f
|
|
72
|
-
simulated_energies.append(event["mc_shower"]["energy"])
|
|
71
|
+
simulated_energies.extend(event["mc_shower"]["energy"] for event in f)
|
|
73
72
|
|
|
74
73
|
# The relative tolerance is set to 1% because ~0.5% shower simulations do not
|
|
75
74
|
# succeed, without resulting in an error. This tolerance therefore is not an issue.
|
|
@@ -191,54 +190,94 @@ def check_output_from_sim_telarray(file, file_test):
|
|
|
191
190
|
return assert_n_showers_and_energy_range(file=file) and assert_output and assert_metadata
|
|
192
191
|
|
|
193
192
|
|
|
194
|
-
def _find_patterns(text, patterns):
|
|
195
|
-
"""Find patterns in text."""
|
|
196
|
-
return {p for p in patterns if p in text}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
def _read_log(member, tar):
|
|
200
|
-
"""Read and decode a gzipped log file from a tar archive."""
|
|
201
|
-
with tar.extractfile(member) as gz, gzip.open(gz, "rb") as f:
|
|
202
|
-
return f.read().decode("utf-8", "ignore")
|
|
203
|
-
|
|
204
|
-
|
|
205
193
|
def check_simulation_logs(tar_file, file_test):
|
|
206
194
|
"""
|
|
207
|
-
Check
|
|
195
|
+
Check simulation logs for wanted and forbidden patterns.
|
|
208
196
|
|
|
209
197
|
Parameters
|
|
210
198
|
----------
|
|
211
|
-
tar_file:
|
|
212
|
-
Path to
|
|
213
|
-
file_test: dict
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
199
|
+
tar_file : str
|
|
200
|
+
Path to the tar file.
|
|
201
|
+
file_test : dict
|
|
202
|
+
Dictionary with the test configuration.
|
|
203
|
+
|
|
204
|
+
Returns
|
|
205
|
+
-------
|
|
206
|
+
bool
|
|
207
|
+
True if the logs are correct.
|
|
220
208
|
"""
|
|
221
|
-
|
|
222
|
-
wanted
|
|
223
|
-
forbidden = expected_log.get("forbidden_pattern", [])
|
|
224
|
-
|
|
225
|
-
if not (wanted or forbidden):
|
|
226
|
-
_logger.debug(f"No expected log output provided, skipping checks {file_test}")
|
|
209
|
+
wanted, forbidden = _get_expected_patterns(file_test)
|
|
210
|
+
if wanted is None:
|
|
227
211
|
return True
|
|
228
212
|
|
|
229
213
|
if not tarfile.is_tarfile(tar_file):
|
|
230
|
-
raise ValueError(f"
|
|
214
|
+
raise ValueError(f"{tar_file} is not a tar file")
|
|
231
215
|
|
|
232
|
-
|
|
216
|
+
found_wanted = set()
|
|
217
|
+
found_forbidden = set()
|
|
233
218
|
with tarfile.open(tar_file, "r:*") as tar:
|
|
234
219
|
for member in tar.getmembers():
|
|
235
220
|
if not member.name.endswith(".log.gz"):
|
|
236
221
|
continue
|
|
237
222
|
_logger.info(f"Scanning {member.name}")
|
|
238
223
|
text = _read_log(member, tar)
|
|
239
|
-
|
|
240
|
-
|
|
224
|
+
found_wanted |= _find_patterns(text, wanted)
|
|
225
|
+
found_forbidden |= _find_patterns(text, forbidden)
|
|
226
|
+
|
|
227
|
+
return _validate_patterns(found_wanted, found_forbidden, wanted)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def check_plain_log(log_file, file_test):
|
|
231
|
+
"""
|
|
232
|
+
Check plain log file for wanted and forbidden patterns.
|
|
233
|
+
|
|
234
|
+
Parameters
|
|
235
|
+
----------
|
|
236
|
+
log_file : str
|
|
237
|
+
Path to the log file.
|
|
238
|
+
file_test : dict
|
|
239
|
+
Dictionary with the test configuration.
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
bool
|
|
244
|
+
True if the logs are correct.
|
|
245
|
+
"""
|
|
246
|
+
wanted, forbidden = _get_expected_patterns(file_test)
|
|
247
|
+
if wanted is None:
|
|
248
|
+
return True
|
|
249
|
+
|
|
250
|
+
try:
|
|
251
|
+
with open(log_file, encoding="utf-8") as f:
|
|
252
|
+
text = f.read()
|
|
253
|
+
except FileNotFoundError:
|
|
254
|
+
_logger.error(f"Log file {log_file} not found")
|
|
255
|
+
return False
|
|
256
|
+
|
|
257
|
+
found = _find_patterns(text, wanted)
|
|
258
|
+
bad = _find_patterns(text, forbidden)
|
|
259
|
+
|
|
260
|
+
return _validate_patterns(found, bad, wanted)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _get_expected_patterns(file_test):
|
|
264
|
+
"""Get wanted and forbidden patterns from file test configuration."""
|
|
265
|
+
expected_log = file_test.get("expected_log_output")
|
|
266
|
+
if isinstance(expected_log, dict):
|
|
267
|
+
wanted = expected_log.get("pattern", [])
|
|
268
|
+
forbidden = expected_log.get("forbidden_pattern", [])
|
|
269
|
+
else:
|
|
270
|
+
wanted = file_test.get("pattern", [])
|
|
271
|
+
forbidden = file_test.get("forbidden_pattern", [])
|
|
272
|
+
if not (wanted or forbidden):
|
|
273
|
+
_logger.debug(f"No expected log output provided, skipping checks {file_test}")
|
|
274
|
+
return None, None
|
|
241
275
|
|
|
276
|
+
return wanted, forbidden
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _validate_patterns(found, bad, wanted):
|
|
280
|
+
"""Validate found patterns against wanted and forbidden ones."""
|
|
242
281
|
if bad:
|
|
243
282
|
_logger.error(f"Forbidden patterns found: {list(bad)}")
|
|
244
283
|
return False
|
|
@@ -249,3 +288,15 @@ def check_simulation_logs(tar_file, file_test):
|
|
|
249
288
|
|
|
250
289
|
_logger.debug(f"All expected patterns found: {wanted}")
|
|
251
290
|
return True
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def _find_patterns(text, patterns):
|
|
294
|
+
"""Find patterns in text (case insensitive)."""
|
|
295
|
+
text_lower = text.lower()
|
|
296
|
+
return {p for p in patterns if p.lower() in text_lower}
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _read_log(member, tar):
|
|
300
|
+
"""Read and decode a gzipped log file from a tar archive."""
|
|
301
|
+
with tar.extractfile(member) as gz, gzip.open(gz, "rb") as f:
|
|
302
|
+
return f.read().decode("utf-8", "ignore")
|
|
@@ -76,8 +76,7 @@ def _read_configs_from_files(config_files):
|
|
|
76
76
|
_dict = gen.remove_substring_recursively_from_dict(
|
|
77
77
|
ascii_handler.collect_data_from_file(file_name=config_file), substring="\n"
|
|
78
78
|
)
|
|
79
|
-
|
|
80
|
-
configs.append(application)
|
|
79
|
+
configs.extend(_dict.get("applications", []))
|
|
81
80
|
return configs
|
|
82
81
|
|
|
83
82
|
|
simtools/testing/helpers.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Helper functions for integration testing."""
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
from pathlib import Path
|
|
3
|
+
from simtools import settings
|
|
5
4
|
|
|
6
5
|
|
|
7
6
|
def skip_camera_efficiency(config):
|
|
@@ -21,7 +20,7 @@ def _new_testeff_version():
|
|
|
21
20
|
|
|
22
21
|
This test checks if the new version is used.
|
|
23
22
|
"""
|
|
24
|
-
testeff_path =
|
|
23
|
+
testeff_path = settings.config.sim_telarray_path / "testeff.c"
|
|
25
24
|
try:
|
|
26
25
|
with open(testeff_path, encoding="utf-8") as file:
|
|
27
26
|
file_content = file.read()
|
|
@@ -18,6 +18,7 @@ ERROR_PATTERNS = [
|
|
|
18
18
|
IGNORE_PATTERNS = [
|
|
19
19
|
re.compile(r"Falling back to 'utf-8' with errors='ignore'", re.IGNORECASE),
|
|
20
20
|
re.compile(r"Failed to get user name[^\n]*setting it to UNKNOWN_USER", re.IGNORECASE),
|
|
21
|
+
re.compile(r"adjust_text::Error", re.IGNORECASE),
|
|
21
22
|
]
|
|
22
23
|
|
|
23
24
|
|
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
|
|
5
5
|
import numpy as np
|
|
6
6
|
|
|
7
|
-
from simtools.
|
|
7
|
+
from simtools.sim_events.file_info import get_corsika_run_number
|
|
8
8
|
from simtools.simtel.simtel_config_reader import SimtelConfigReader
|
|
9
9
|
from simtools.simtel.simtel_config_writer import sim_telarray_random_seeds
|
|
10
10
|
from simtools.simtel.simtel_io_metadata import (
|