gammasimtools 0.25.0__py3-none-any.whl → 0.27.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/METADATA +6 -1
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/RECORD +135 -130
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/WHEEL +1 -1
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/entry_points.txt +3 -2
- {gammasimtools-0.25.0.dist-info → gammasimtools-0.27.0.dist-info}/licenses/LICENSE +1 -1
- simtools/_version.py +2 -2
- simtools/application_control.py +35 -7
- simtools/applications/convert_geo_coordinates_of_array_elements.py +3 -3
- 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 +3 -7
- 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/{calculate_incident_angles.py → derive_incident_angle.py} +16 -18
- simtools/applications/derive_mirror_rnda.py +112 -180
- 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 +79 -229
- simtools/applications/generate_regular_arrays.py +76 -69
- 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 +3 -15
- simtools/applications/simulate_illuminator.py +2 -11
- simtools/applications/simulate_pedestals.py +1 -5
- simtools/applications/simulate_prod.py +8 -11
- simtools/applications/simulate_prod_htcondor_generator.py +1 -1
- simtools/applications/submit_array_layouts.py +2 -4
- simtools/applications/submit_data_from_external.py +2 -1
- simtools/applications/submit_model_parameter_from_external.py +1 -3
- simtools/applications/validate_camera_efficiency.py +28 -28
- simtools/applications/validate_camera_fov.py +0 -1
- simtools/applications/validate_cumulative_psf.py +1 -5
- simtools/applications/validate_optics.py +2 -14
- simtools/atmosphere.py +83 -0
- simtools/camera/camera_efficiency.py +171 -53
- simtools/camera/single_photon_electron_spectrum.py +8 -7
- simtools/configuration/commandline_parser.py +82 -11
- simtools/configuration/configurator.py +6 -11
- simtools/constants.py +5 -0
- simtools/corsika/corsika_config.py +100 -202
- simtools/corsika/corsika_histograms.py +561 -1708
- 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 +59 -64
- simtools/data_model/schema.py +2 -0
- simtools/data_model/validate_data.py +1 -3
- simtools/db/db_handler.py +23 -10
- simtools/db/mongo_db.py +2 -2
- simtools/dependencies.py +81 -38
- simtools/io/ascii_handler.py +55 -5
- simtools/io/io_handler.py +23 -12
- simtools/io/table_handler.py +1 -1
- simtools/job_execution/job_manager.py +154 -79
- simtools/job_execution/process_pool.py +137 -0
- simtools/layout/array_layout.py +4 -13
- simtools/layout/array_layout_utils.py +348 -57
- simtools/model/array_model.py +23 -63
- simtools/model/calibration_model.py +4 -8
- simtools/model/legacy_model_parameter.py +134 -0
- simtools/model/model_parameter.py +147 -86
- simtools/model/model_utils.py +40 -6
- simtools/model/site_model.py +4 -8
- simtools/model/telescope_model.py +10 -16
- simtools/production_configuration/derive_corsika_limits.py +6 -11
- simtools/production_configuration/interpolation_handler.py +16 -16
- simtools/ray_tracing/incident_angles.py +92 -17
- simtools/ray_tracing/mirror_panel_psf.py +338 -222
- simtools/ray_tracing/psf_analysis.py +62 -48
- simtools/ray_tracing/psf_parameter_optimisation.py +3 -3
- simtools/ray_tracing/ray_tracing.py +43 -25
- simtools/reporting/docs_auto_report_generator.py +8 -13
- simtools/reporting/docs_read_parameters.py +2 -8
- simtools/runners/corsika_runner.py +52 -195
- simtools/runners/corsika_simtel_runner.py +77 -108
- simtools/runners/runner_services.py +214 -213
- simtools/runners/simtel_runner.py +27 -160
- simtools/runners/simtools_runner.py +11 -73
- simtools/schemas/application_workflow.metaschema.yml +8 -0
- simtools/settings.py +173 -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 -91
- simtools/simtel/simtel_seeds.py +184 -0
- simtools/simtel/simtel_table_reader.py +6 -4
- simtools/simtel/simulator_array.py +114 -109
- simtools/simtel/simulator_camera_efficiency.py +68 -46
- simtools/simtel/simulator_light_emission.py +164 -132
- simtools/simtel/simulator_ray_tracing.py +80 -71
- simtools/simulator.py +137 -355
- 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 +14 -12
- simtools/testing/validate_output.py +121 -42
- simtools/utils/general.py +43 -17
- simtools/utils/geometry.py +0 -77
- simtools/utils/names.py +5 -5
- simtools/utils/random.py +36 -0
- simtools/visualization/legend_handlers.py +7 -6
- simtools/visualization/plot_array_layout.py +91 -16
- simtools/visualization/plot_corsika_histograms.py +145 -605
- simtools/visualization/plot_incident_angles.py +48 -1
- simtools/visualization/plot_mirrors.py +1 -4
- simtools/visualization/plot_pixels.py +2 -4
- simtools/visualization/plot_psf.py +160 -19
- 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.27.0.dist-info}/top_level.txt +0 -0
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import logging
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
+
from simtools import settings
|
|
6
7
|
from simtools.io import ascii_handler
|
|
7
8
|
from simtools.runners.simtel_runner import SimtelRunner
|
|
8
|
-
from simtools.utils import general
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class SimulatorCameraEfficiency(SimtelRunner):
|
|
@@ -20,12 +20,12 @@ class SimulatorCameraEfficiency(SimtelRunner):
|
|
|
20
20
|
Instance of SiteModel class.
|
|
21
21
|
label: str
|
|
22
22
|
Instance label. Important for output file naming.
|
|
23
|
-
simtel_path: str or Path
|
|
24
|
-
Location of sim_telarray installation.
|
|
25
23
|
file_simtel: str or Path
|
|
26
24
|
Location of the sim_telarray testeff tool output file.
|
|
27
25
|
zenith_angle: float
|
|
28
26
|
Zenith angle given in the config to CameraEfficiency.
|
|
27
|
+
x_max: float
|
|
28
|
+
Maximum depth of shower development in g/cm2.
|
|
29
29
|
nsb_spectrum: str or Path
|
|
30
30
|
Path to the nsb spectrum file.
|
|
31
31
|
skip_correction_to_nsb_spectrum: bool
|
|
@@ -37,18 +37,18 @@ class SimulatorCameraEfficiency(SimtelRunner):
|
|
|
37
37
|
telescope_model,
|
|
38
38
|
site_model,
|
|
39
39
|
label=None,
|
|
40
|
-
simtel_path=None,
|
|
41
40
|
file_simtel=None,
|
|
42
41
|
file_log=None,
|
|
43
42
|
zenith_angle=None,
|
|
43
|
+
x_max=None,
|
|
44
44
|
nsb_spectrum=None,
|
|
45
45
|
skip_correction_to_nsb_spectrum=False,
|
|
46
46
|
):
|
|
47
|
-
"""
|
|
47
|
+
"""Camera efficiency simulator initialization."""
|
|
48
48
|
self._logger = logging.getLogger(__name__)
|
|
49
49
|
self._logger.debug("Init SimulatorCameraEfficiency")
|
|
50
50
|
|
|
51
|
-
super().__init__(label=label
|
|
51
|
+
super().__init__(label=label)
|
|
52
52
|
|
|
53
53
|
self._telescope_model = telescope_model
|
|
54
54
|
self._site_model = site_model
|
|
@@ -57,6 +57,7 @@ class SimulatorCameraEfficiency(SimtelRunner):
|
|
|
57
57
|
self._file_simtel = file_simtel
|
|
58
58
|
self._file_log = file_log
|
|
59
59
|
self.zenith_angle = zenith_angle
|
|
60
|
+
self.x_max = x_max
|
|
60
61
|
self.nsb_spectrum = nsb_spectrum
|
|
61
62
|
self.skip_correction_to_nsb_spectrum = skip_correction_to_nsb_spectrum
|
|
62
63
|
|
|
@@ -76,7 +77,7 @@ class SimulatorCameraEfficiency(SimtelRunner):
|
|
|
76
77
|
/ Path(self._site_model.get_parameter_value("nsb_reference_spectrum")).name
|
|
77
78
|
)
|
|
78
79
|
|
|
79
|
-
def
|
|
80
|
+
def make_run_command(self, run_number=None, input_file=None): # pylint: disable=unused-argument
|
|
80
81
|
"""Prepare the command used to run testeff."""
|
|
81
82
|
self._logger.debug("Preparing the command to run testeff")
|
|
82
83
|
|
|
@@ -109,52 +110,73 @@ class SimulatorCameraEfficiency(SimtelRunner):
|
|
|
109
110
|
"mirror_reflectivity", "secondary_mirror_incidence_angle"
|
|
110
111
|
)
|
|
111
112
|
|
|
112
|
-
|
|
113
|
+
cmd = [str(settings.config.sim_telarray_path / "bin/testeff")]
|
|
114
|
+
|
|
113
115
|
if self.skip_correction_to_nsb_spectrum:
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
command += f" -fatm {self._site_model.get_parameter_value('atmospheric_transmission')}"
|
|
122
|
-
command += f" -flen {focal_length}"
|
|
123
|
-
command += f" -fcur {curvature_radius:.3f}"
|
|
124
|
-
command += f" {pixel_shape_cmd} {pixel_diameter}"
|
|
125
|
-
if mirror_class == 0:
|
|
126
|
-
command += f" -fmir {self._telescope_model.get_parameter_value('mirror_list')}"
|
|
127
|
-
if mirror_class == 2:
|
|
128
|
-
command += f" -fmir {self._telescope_model.get_parameter_value('fake_mirror_list')}"
|
|
129
|
-
command += f" -fref {mirror_reflectivity}"
|
|
130
|
-
if mirror_class == 2:
|
|
131
|
-
command += " -m2"
|
|
132
|
-
command += f" -fref2 {mirror_reflectivity_secondary}"
|
|
133
|
-
command += " -teltrans "
|
|
134
|
-
command += f"{self._telescope_model.get_parameter_value('telescope_transmission')[0]}"
|
|
135
|
-
command += f" -camtrans {camera_transmission}"
|
|
136
|
-
command += f" -fflt {camera_filter_file}"
|
|
137
|
-
command += (
|
|
138
|
-
f" -fang {self._telescope_model.camera.get_lightguide_efficiency_angle_file_name()}"
|
|
116
|
+
cmd.append("-nc")
|
|
117
|
+
|
|
118
|
+
cmd.extend(
|
|
119
|
+
[
|
|
120
|
+
"-I",
|
|
121
|
+
f"-I{self._telescope_model.config_file_directory}",
|
|
122
|
+
]
|
|
139
123
|
)
|
|
140
|
-
|
|
141
|
-
|
|
124
|
+
|
|
125
|
+
if self.nsb_spectrum is not None:
|
|
126
|
+
cmd.extend(["-fnsb", str(self.nsb_spectrum)])
|
|
127
|
+
|
|
128
|
+
cmd.extend(
|
|
129
|
+
[
|
|
130
|
+
"-nm",
|
|
131
|
+
"-nsb-extra",
|
|
132
|
+
"-alt",
|
|
133
|
+
str(self._site_model.get_parameter_value("corsika_observation_level")),
|
|
134
|
+
"-fatm",
|
|
135
|
+
str(self._site_model.get_parameter_value("atmospheric_transmission")),
|
|
136
|
+
"-flen",
|
|
137
|
+
str(focal_length),
|
|
138
|
+
"-fcur",
|
|
139
|
+
f"{curvature_radius:.3f}",
|
|
140
|
+
pixel_shape_cmd,
|
|
141
|
+
str(pixel_diameter),
|
|
142
|
+
]
|
|
142
143
|
)
|
|
143
|
-
command += f" -fqe {self._telescope_model.get_parameter_value('quantum_efficiency')}"
|
|
144
|
-
command += " 200 1000" # lmin and lmax
|
|
145
|
-
command += " 300" # Xmax
|
|
146
|
-
command += f" {self._site_model.get_parameter_value('atmospheric_profile')}"
|
|
147
|
-
command += f" {self.zenith_angle}"
|
|
148
144
|
|
|
149
|
-
|
|
150
|
-
|
|
145
|
+
if mirror_class == 0:
|
|
146
|
+
cmd.extend(["-fmir", self._telescope_model.get_parameter_value("mirror_list")])
|
|
147
|
+
elif mirror_class == 2:
|
|
148
|
+
cmd.extend(["-fmir", self._telescope_model.get_parameter_value("fake_mirror_list")])
|
|
151
149
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
150
|
+
cmd.extend(["-fref", mirror_reflectivity])
|
|
151
|
+
|
|
152
|
+
if mirror_class == 2:
|
|
153
|
+
cmd.append("-m2")
|
|
154
|
+
cmd.extend(["-fref2", mirror_reflectivity_secondary])
|
|
155
|
+
|
|
156
|
+
cmd.extend(
|
|
157
|
+
[
|
|
158
|
+
"-teltrans",
|
|
159
|
+
str(self._telescope_model.get_parameter_value("telescope_transmission")[0]),
|
|
160
|
+
"-camtrans",
|
|
161
|
+
str(camera_transmission),
|
|
162
|
+
"-fflt",
|
|
163
|
+
camera_filter_file,
|
|
164
|
+
"-fang",
|
|
165
|
+
self._telescope_model.camera.get_lightguide_efficiency_angle_file_name(),
|
|
166
|
+
"-fwl",
|
|
167
|
+
self._telescope_model.camera.get_lightguide_efficiency_wavelength_file_name(),
|
|
168
|
+
"-fqe",
|
|
169
|
+
str(self._telescope_model.get_parameter_value("quantum_efficiency")),
|
|
170
|
+
"200", # lmin
|
|
171
|
+
"1000", # lmax
|
|
172
|
+
f"{self.x_max:.1f}" if self.x_max is not None else "300",
|
|
173
|
+
str(self._site_model.get_parameter_value("atmospheric_profile")),
|
|
174
|
+
str(self.zenith_angle),
|
|
175
|
+
]
|
|
156
176
|
)
|
|
157
177
|
|
|
178
|
+
return cmd, self._file_simtel, self._file_log
|
|
179
|
+
|
|
158
180
|
def _check_run_result(self, run_number=None): # pylint: disable=unused-argument
|
|
159
181
|
"""Check run results.
|
|
160
182
|
|
|
@@ -2,18 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import shutil
|
|
5
|
-
import stat
|
|
6
|
-
import subprocess
|
|
7
5
|
from pathlib import Path
|
|
8
6
|
|
|
9
7
|
import astropy.units as u
|
|
10
8
|
import numpy as np
|
|
11
9
|
|
|
10
|
+
from simtools import settings
|
|
12
11
|
from simtools.io import io_handler
|
|
12
|
+
from simtools.job_execution import job_manager
|
|
13
13
|
from simtools.model.model_utils import initialize_simulation_models
|
|
14
|
-
from simtools.runners
|
|
14
|
+
from simtools.runners import runner_services
|
|
15
|
+
from simtools.runners.simtel_runner import SimtelRunner, sim_telarray_env_as_string
|
|
15
16
|
from simtools.simtel.simtel_config_writer import SimtelConfigWriter
|
|
16
|
-
from simtools.utils.general import clear_default_sim_telarray_cfg_directories
|
|
17
17
|
from simtools.utils.geometry import fiducial_radius_from_shape
|
|
18
18
|
|
|
19
19
|
|
|
@@ -31,21 +31,19 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
31
31
|
Label for the simulation
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
|
-
def __init__(self, light_emission_config,
|
|
34
|
+
def __init__(self, light_emission_config, label=None):
|
|
35
35
|
"""Initialize SimulatorLightEmission."""
|
|
36
36
|
self._logger = logging.getLogger(__name__)
|
|
37
37
|
self.io_handler = io_handler.IOHandler()
|
|
38
38
|
|
|
39
|
-
super().__init__(
|
|
40
|
-
|
|
39
|
+
super().__init__(label=label, config=light_emission_config)
|
|
40
|
+
self.job_files = runner_services.RunnerServices(
|
|
41
|
+
light_emission_config, run_type="sub", label=label
|
|
41
42
|
)
|
|
42
43
|
|
|
43
|
-
self.output_directory = self.io_handler.get_output_directory()
|
|
44
|
-
|
|
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:
|
|
@@ -79,73 +77,47 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
79
77
|
return config
|
|
80
78
|
|
|
81
79
|
def simulate(self):
|
|
82
|
-
"""
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
"""
|
|
90
|
-
run_script = self.prepare_script()
|
|
91
|
-
log_path = Path(self.output_directory) / "logfile.log"
|
|
92
|
-
with open(log_path, "w", encoding="utf-8") as fh:
|
|
93
|
-
subprocess.run(
|
|
94
|
-
run_script,
|
|
95
|
-
shell=False,
|
|
96
|
-
check=False,
|
|
97
|
-
text=True,
|
|
98
|
-
stdout=fh,
|
|
99
|
-
stderr=fh,
|
|
100
|
-
)
|
|
101
|
-
out = Path(self._get_simulation_output_filename())
|
|
102
|
-
if not out.exists():
|
|
103
|
-
self._logger.warning(f"Expected sim_telarray output not found: {out}")
|
|
104
|
-
return out
|
|
80
|
+
"""Simulate light emission."""
|
|
81
|
+
run_script = self.prepare_run()
|
|
82
|
+
job_manager.submit(
|
|
83
|
+
run_script,
|
|
84
|
+
out_file=self.job_files.get_file_name("sub_out"),
|
|
85
|
+
err_file=self.job_files.get_file_name("sub_err"),
|
|
86
|
+
)
|
|
105
87
|
|
|
106
|
-
def
|
|
88
|
+
def prepare_run(self):
|
|
107
89
|
"""
|
|
108
|
-
|
|
90
|
+
Prepare the bash run script containing the light-emission command.
|
|
109
91
|
|
|
110
92
|
Returns
|
|
111
93
|
-------
|
|
112
94
|
Path
|
|
113
95
|
Full path of the run script.
|
|
114
96
|
"""
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
app_name = self._get_light_emission_application_name()
|
|
119
|
-
script_file = script_dir / f"{app_name}-light_emission.sh"
|
|
120
|
-
self._logger.debug(f"Run bash script - {script_file}")
|
|
121
|
-
|
|
122
|
-
target_out = Path(self._get_simulation_output_filename())
|
|
123
|
-
if target_out.exists():
|
|
97
|
+
script_file = self.job_files.get_file_name(file_type="sub_script")
|
|
98
|
+
output_file = self.runner_service.get_file_name(file_type="sim_telarray_output")
|
|
99
|
+
if output_file.exists():
|
|
124
100
|
raise FileExistsError(
|
|
125
|
-
f"sim_telarray output file exists, cancelling simulation: {
|
|
101
|
+
f"sim_telarray output file exists, cancelling simulation: {output_file}"
|
|
126
102
|
)
|
|
103
|
+
lines = self.make_run_command()
|
|
104
|
+
script_file.write_text("".join(lines), encoding="utf-8")
|
|
105
|
+
return script_file
|
|
127
106
|
|
|
128
|
-
|
|
107
|
+
def make_run_command(self, run_number=None, input_file=None): # pylint: disable=unused-argument
|
|
108
|
+
"""Light emission and sim_telarray run command."""
|
|
109
|
+
iact_output = self.runner_service.get_file_name(file_type="iact_output")
|
|
110
|
+
return [
|
|
129
111
|
"#!/usr/bin/env bash\n",
|
|
130
|
-
f"{self.
|
|
112
|
+
f"{self._make_light_emission_command(iact_output)}\n\n",
|
|
131
113
|
(
|
|
132
|
-
f"[ -s '{
|
|
114
|
+
f"[ -s '{iact_output}' ] || "
|
|
133
115
|
f"{{ echo 'LightEmission did not produce IACT file' >&2; exit 1; }}\n\n"
|
|
134
116
|
),
|
|
135
117
|
f"{self._make_simtel_script()}\n\n",
|
|
136
|
-
f"rm -f '{
|
|
118
|
+
f"rm -f '{iact_output}'\n\n",
|
|
137
119
|
]
|
|
138
120
|
|
|
139
|
-
script_file.write_text("".join(lines), encoding="utf-8")
|
|
140
|
-
script_file.chmod(script_file.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP)
|
|
141
|
-
return script_file
|
|
142
|
-
|
|
143
|
-
def _get_prefix(self):
|
|
144
|
-
prefix = self.light_emission_config.get("output_prefix", "")
|
|
145
|
-
if prefix is not None:
|
|
146
|
-
return f"{prefix}_"
|
|
147
|
-
return ""
|
|
148
|
-
|
|
149
121
|
def _get_light_emission_application_name(self):
|
|
150
122
|
"""
|
|
151
123
|
Return the LightEmission application and mode from type.
|
|
@@ -244,7 +216,9 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
244
216
|
radius = self.telescope_model.get_parameter_value_with_unit("telescope_sphere_radius")
|
|
245
217
|
radius = radius.to(u.cm).value # Convert radius to cm
|
|
246
218
|
|
|
247
|
-
telescope_position_file =
|
|
219
|
+
telescope_position_file = (
|
|
220
|
+
self.io_handler.get_output_directory("light_emission") / "telescope_position.dat"
|
|
221
|
+
)
|
|
248
222
|
telescope_position_file.write_text(f"{x_tel} {y_tel} {z_tel} {radius}\n", encoding="utf-8")
|
|
249
223
|
return telescope_position_file
|
|
250
224
|
|
|
@@ -272,13 +246,18 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
272
246
|
self._logger.warning(f"Failed to create atmosphere alias {dst.name}: {copy_err}")
|
|
273
247
|
return model_id
|
|
274
248
|
|
|
275
|
-
def
|
|
249
|
+
def _make_light_emission_command(self, iact_output):
|
|
276
250
|
"""
|
|
277
|
-
Create the light emission
|
|
251
|
+
Create the light emission command to run the light emission package.
|
|
278
252
|
|
|
279
253
|
Require the specified pre-compiled light emission package application
|
|
280
254
|
in the sim_telarray/LightEmission/ path.
|
|
281
255
|
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
iact_output: str or Path
|
|
259
|
+
The output iact file path.
|
|
260
|
+
|
|
282
261
|
Returns
|
|
283
262
|
-------
|
|
284
263
|
str
|
|
@@ -287,24 +266,24 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
287
266
|
config_directory = self.io_handler.get_model_configuration_directory(
|
|
288
267
|
model_version=self.site_model.model_version
|
|
289
268
|
)
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
269
|
+
obs_level = self.site_model.get_parameter_value_with_unit("corsika_observation_level")
|
|
270
|
+
|
|
271
|
+
app = self._get_light_emission_application_name()
|
|
272
|
+
cmd = [
|
|
273
|
+
str(settings.config.sim_telarray_path / "LightEmission" / app),
|
|
274
|
+
*self._get_site_command(app, config_directory, obs_level),
|
|
275
|
+
*self._get_light_source_command(),
|
|
276
|
+
]
|
|
294
277
|
|
|
295
|
-
parts = [str(self._simtel_path / "sim_telarray/LightEmission") + f"/{app_name}"]
|
|
296
|
-
parts.extend(self._get_site_command(app_name, config_directory, corsika_observation_level))
|
|
297
|
-
parts.extend(self._get_light_source_command())
|
|
298
278
|
if self.light_emission_config["light_source_type"] == "illuminator":
|
|
299
|
-
|
|
279
|
+
cmd += [
|
|
300
280
|
"-A",
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
f"{self.telescope_model.get_parameter_value('atmospheric_profile')}"
|
|
304
|
-
),
|
|
281
|
+
f"{config_directory}/"
|
|
282
|
+
f"{self.telescope_model.get_parameter_value('atmospheric_profile')}",
|
|
305
283
|
]
|
|
306
|
-
|
|
307
|
-
|
|
284
|
+
|
|
285
|
+
cmd += ["-o", str(iact_output)]
|
|
286
|
+
return " ".join(cmd)
|
|
308
287
|
|
|
309
288
|
def _get_site_command(self, app_name, config_directory, corsika_observation_level):
|
|
310
289
|
"""Return site command with altitude, atmosphere and telescope_position handling."""
|
|
@@ -312,7 +291,7 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
312
291
|
atmo_id = self._prepare_flasher_atmosphere_files(config_directory)
|
|
313
292
|
return [
|
|
314
293
|
"-I.",
|
|
315
|
-
f"-I{
|
|
294
|
+
f"-I{settings.config.sim_telarray_path / 'cfg'}",
|
|
316
295
|
f"-I{config_directory}",
|
|
317
296
|
f"--altitude {corsika_observation_level.to(u.m).value}",
|
|
318
297
|
f"--atmosphere {atmo_id}",
|
|
@@ -349,34 +328,32 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
349
328
|
dist_cm = self.calculate_distance_focal_plane_calibration_device().to(u.cm).value
|
|
350
329
|
angular_distribution = self._get_angular_distribution_string_for_sim_telarray()
|
|
351
330
|
|
|
352
|
-
# Build pulse table for ff-1m using
|
|
331
|
+
# Build pulse table for ff-1m using unified list parameter [shape, width, exp]
|
|
332
|
+
pulse_shape_value = self.calibration_model.get_parameter_value("flasher_pulse_shape")
|
|
333
|
+
shape_name = pulse_shape_value[0]
|
|
334
|
+
width_ns = pulse_shape_value[1]
|
|
335
|
+
exp_ns = pulse_shape_value[2]
|
|
353
336
|
pulse_arg = self._get_pulse_shape_string_for_sim_telarray()
|
|
354
|
-
pulse_shape = self.calibration_model.get_parameter_value("flasher_pulse_shape")
|
|
355
|
-
width_q = self.calibration_model.get_parameter_value_with_unit("flasher_pulse_width")
|
|
356
|
-
exp_q = self.calibration_model.get_parameter_value_with_unit("flasher_pulse_exp_decay")
|
|
357
|
-
if (
|
|
358
|
-
isinstance(exp_q, u.Quantity)
|
|
359
|
-
and isinstance(width_q, u.Quantity)
|
|
360
|
-
and pulse_shape == "Gauss-Exponential"
|
|
361
|
-
):
|
|
362
|
-
try:
|
|
363
|
-
base_dir = self.io_handler.get_output_directory("pulse_shapes")
|
|
364
|
-
|
|
365
|
-
def _sanitize_name(value):
|
|
366
|
-
return "".join(
|
|
367
|
-
ch if (ch.isalnum() or ch in ("-", "_")) else "_" for ch in str(value)
|
|
368
|
-
)
|
|
369
337
|
|
|
338
|
+
if shape_name == "Gauss-Exponential":
|
|
339
|
+
if width_ns <= 0 or exp_ns <= 0:
|
|
340
|
+
raise ValueError(
|
|
341
|
+
"Gauss-Exponential pulse shape requires positive width"
|
|
342
|
+
" and exponential decay values"
|
|
343
|
+
)
|
|
344
|
+
try:
|
|
370
345
|
tel = self.light_emission_config.get("telescope") or "telescope"
|
|
371
346
|
cal = self.light_emission_config.get("light_source") or "calibration"
|
|
372
|
-
fname =
|
|
373
|
-
|
|
347
|
+
fname = (
|
|
348
|
+
f"flasher_pulse_shape_{self._sanitize_name(tel)}_{self._sanitize_name(cal)}.dat"
|
|
349
|
+
)
|
|
350
|
+
table_path = self.io_handler.get_output_directory("light_emission") / fname
|
|
374
351
|
fadc_bins = self.telescope_model.get_parameter_value("fadc_sum_bins")
|
|
375
352
|
|
|
376
|
-
SimtelConfigWriter.
|
|
353
|
+
SimtelConfigWriter.write_light_pulse_table_gauss_exp_conv(
|
|
377
354
|
file_path=table_path,
|
|
378
|
-
width_ns=
|
|
379
|
-
exp_decay_ns=
|
|
355
|
+
width_ns=width_ns,
|
|
356
|
+
exp_decay_ns=exp_ns,
|
|
380
357
|
fadc_sum_bins=fadc_bins,
|
|
381
358
|
time_margin_ns=5.0,
|
|
382
359
|
)
|
|
@@ -396,6 +373,9 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
396
373
|
f"--angular-distribution {angular_distribution}",
|
|
397
374
|
]
|
|
398
375
|
|
|
376
|
+
def _sanitize_name(self, value):
|
|
377
|
+
return "".join(ch if (ch.isalnum() or ch in ("-", "_")) else "_" for ch in str(value))
|
|
378
|
+
|
|
399
379
|
def _add_illuminator_command_options(self):
|
|
400
380
|
"""Get illuminator-specific command options for light emission script."""
|
|
401
381
|
pos = self.light_emission_config.get("light_source_position")
|
|
@@ -434,56 +414,52 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
434
414
|
The command to run sim_telarray
|
|
435
415
|
"""
|
|
436
416
|
theta, phi = self._get_telescope_pointing()
|
|
437
|
-
|
|
438
|
-
simtel_bin = self._simtel_path.joinpath("sim_telarray/bin/sim_telarray/")
|
|
417
|
+
simtel_bin = str(settings.config.sim_telarray_exe)
|
|
439
418
|
|
|
440
419
|
parts = [
|
|
441
|
-
|
|
420
|
+
simtel_bin,
|
|
442
421
|
f"-I{self.telescope_model.config_file_directory}",
|
|
443
422
|
f"-I{simtel_bin}",
|
|
444
423
|
f"-c {self.telescope_model.config_file_path}",
|
|
445
424
|
"-DNUM_TELESCOPES=1",
|
|
446
|
-
|
|
425
|
+
]
|
|
426
|
+
|
|
427
|
+
options = [
|
|
428
|
+
(
|
|
447
429
|
"altitude",
|
|
448
430
|
self.site_model.get_parameter_value_with_unit("corsika_observation_level")
|
|
449
431
|
.to(u.m)
|
|
450
432
|
.value,
|
|
451
433
|
),
|
|
452
|
-
|
|
434
|
+
(
|
|
453
435
|
"atmospheric_transmission",
|
|
454
436
|
self.site_model.get_parameter_value("atmospheric_transmission"),
|
|
455
437
|
),
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
438
|
+
("TRIGGER_TELESCOPES", "1"),
|
|
439
|
+
("TELTRIG_MIN_SIGSUM", "2"),
|
|
440
|
+
("PULSE_ANALYSIS", "-30"),
|
|
441
|
+
("MAXIMUM_TELESCOPES", 1),
|
|
442
|
+
("telescope_theta", f"{theta}"),
|
|
443
|
+
("telescope_phi", f"{phi}"),
|
|
462
444
|
]
|
|
463
445
|
|
|
464
446
|
if self.light_emission_config["light_source_type"] == "flat_fielding":
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
),
|
|
475
|
-
|
|
476
|
-
"histogram_file", f"{self.output_directory}/{pref}{app_name}.ctsim.hdata\n"
|
|
477
|
-
),
|
|
447
|
+
options.append(("Bypass_Optics", "1"))
|
|
448
|
+
|
|
449
|
+
input_file = self.runner_service.get_file_name(file_type="iact_output")
|
|
450
|
+
output_file = self.runner_service.get_file_name(file_type="sim_telarray_output")
|
|
451
|
+
histo_file = self.runner_service.get_file_name(file_type="sim_telarray_histogram")
|
|
452
|
+
|
|
453
|
+
options += [
|
|
454
|
+
("power_law", "2.68"),
|
|
455
|
+
("input_file", f"{input_file}"),
|
|
456
|
+
("output_file", f"{output_file}"),
|
|
457
|
+
("histogram_file", f"{histo_file}"),
|
|
478
458
|
]
|
|
479
459
|
|
|
480
|
-
|
|
460
|
+
parts += [f"-C {key}={value}" for key, value in options]
|
|
481
461
|
|
|
482
|
-
|
|
483
|
-
"""Get the filename of the simulation output."""
|
|
484
|
-
app_name = self._get_light_emission_application_name()
|
|
485
|
-
pref = self._get_prefix()
|
|
486
|
-
return f"{self.output_directory}/{pref}{app_name}.simtel.zst"
|
|
462
|
+
return sim_telarray_env_as_string() + " ".join(parts)
|
|
487
463
|
|
|
488
464
|
def calculate_distance_focal_plane_calibration_device(self):
|
|
489
465
|
"""
|
|
@@ -503,6 +479,20 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
503
479
|
)
|
|
504
480
|
return focal_length - flasher_z
|
|
505
481
|
|
|
482
|
+
def _generate_lambertian_angular_distribution_table(self):
|
|
483
|
+
"""Generate Lambertian angular distribution table via config writer and return path.
|
|
484
|
+
|
|
485
|
+
Uses a pure cosine profile normalized to 1 at 0 deg and spans 0..90 deg by default.
|
|
486
|
+
"""
|
|
487
|
+
tel = self._sanitize_name(self.light_emission_config.get("telescope") or "telescope")
|
|
488
|
+
cal = self._sanitize_name(self.light_emission_config.get("light_source") or "calibration")
|
|
489
|
+
fname = f"flasher_angular_distribution_{tel}_{cal}.dat"
|
|
490
|
+
return SimtelConfigWriter.write_angular_distribution_table_lambertian(
|
|
491
|
+
file_path=self.io_handler.get_output_directory("light_emission") / fname,
|
|
492
|
+
max_angle_deg=90.0,
|
|
493
|
+
n_samples=100,
|
|
494
|
+
)
|
|
495
|
+
|
|
506
496
|
def _get_angular_distribution_string_for_sim_telarray(self):
|
|
507
497
|
"""
|
|
508
498
|
Get the angular distribution string for sim_telarray.
|
|
@@ -514,6 +504,19 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
514
504
|
"""
|
|
515
505
|
opt = self.calibration_model.get_parameter_value("flasher_angular_distribution")
|
|
516
506
|
option_string = str(opt).lower() if opt is not None else ""
|
|
507
|
+
if option_string == "lambertian":
|
|
508
|
+
try:
|
|
509
|
+
return self._generate_lambertian_angular_distribution_table()
|
|
510
|
+
except (OSError, ValueError) as err:
|
|
511
|
+
self._logger.warning(
|
|
512
|
+
f"Failed to write Lambertian angular distribution table: {err};"
|
|
513
|
+
f" using token instead."
|
|
514
|
+
)
|
|
515
|
+
return option_string
|
|
516
|
+
|
|
517
|
+
if option_string == "isotropic":
|
|
518
|
+
return option_string
|
|
519
|
+
|
|
517
520
|
width = self.calibration_model.get_parameter_value_with_unit(
|
|
518
521
|
"flasher_angular_distribution_width"
|
|
519
522
|
)
|
|
@@ -529,6 +532,35 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
529
532
|
The pulse shape string.
|
|
530
533
|
"""
|
|
531
534
|
opt = self.calibration_model.get_parameter_value("flasher_pulse_shape")
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
+
shape = opt[0].lower()
|
|
536
|
+
# Map internal shapes to sim_telarray expected tokens
|
|
537
|
+
# 'tophat' corresponds to a simple (flat) pulse in sim_telarray.
|
|
538
|
+
shape_token_map = {
|
|
539
|
+
"tophat": "simple",
|
|
540
|
+
}
|
|
541
|
+
shape_out = shape_token_map.get(shape, shape)
|
|
542
|
+
width = opt[1]
|
|
543
|
+
expv = opt[2]
|
|
544
|
+
if shape_out == "gauss-exponential" and width is not None and expv is not None:
|
|
545
|
+
return f"{shape_out}:{float(width)}:{float(expv)}"
|
|
546
|
+
if shape_out in ("gauss", "simple") and width is not None:
|
|
547
|
+
return f"{shape_out}:{float(width)}"
|
|
548
|
+
if shape_out == "exponential" and expv is not None:
|
|
549
|
+
return f"{shape_out}:{float(expv)}"
|
|
550
|
+
return shape_out
|
|
551
|
+
|
|
552
|
+
def verify_simulations(self):
|
|
553
|
+
"""
|
|
554
|
+
Verify that the simulations were successful.
|
|
555
|
+
|
|
556
|
+
Returns
|
|
557
|
+
-------
|
|
558
|
+
bool
|
|
559
|
+
True if simulations were successful, False otherwise.
|
|
560
|
+
"""
|
|
561
|
+
out = Path(self.runner_service.get_file_name(file_type="sim_telarray_output"))
|
|
562
|
+
if not out.exists():
|
|
563
|
+
self._logger.error(f"Expected sim_telarray output not found: {out}")
|
|
564
|
+
return False
|
|
565
|
+
self._logger.info(f"sim_telarray output found: {out}")
|
|
566
|
+
return True
|