gammasimtools 0.26.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.26.0.dist-info → gammasimtools-0.27.0.dist-info}/METADATA +5 -1
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.0.dist-info}/RECORD +70 -66
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.0.dist-info}/WHEEL +1 -1
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.0.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 +51 -3
- 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.0.dist-info}/licenses/LICENSE +0 -0
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.0.dist-info}/top_level.txt +0 -0
|
@@ -2,8 +2,6 @@
|
|
|
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
|
|
@@ -11,10 +9,11 @@ import numpy as np
|
|
|
11
9
|
|
|
12
10
|
from simtools import settings
|
|
13
11
|
from simtools.io import io_handler
|
|
12
|
+
from simtools.job_execution import job_manager
|
|
14
13
|
from simtools.model.model_utils import initialize_simulation_models
|
|
15
|
-
from simtools.runners
|
|
14
|
+
from simtools.runners import runner_services
|
|
15
|
+
from simtools.runners.simtel_runner import SimtelRunner, sim_telarray_env_as_string
|
|
16
16
|
from simtools.simtel.simtel_config_writer import SimtelConfigWriter
|
|
17
|
-
from simtools.utils.general import clear_default_sim_telarray_cfg_directories
|
|
18
17
|
from simtools.utils.geometry import fiducial_radius_from_shape
|
|
19
18
|
|
|
20
19
|
|
|
@@ -37,9 +36,10 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
37
36
|
self._logger = logging.getLogger(__name__)
|
|
38
37
|
self.io_handler = io_handler.IOHandler()
|
|
39
38
|
|
|
40
|
-
super().__init__(label=label,
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
42
|
+
)
|
|
43
43
|
|
|
44
44
|
self.telescope_model, self.site_model, self.calibration_model = (
|
|
45
45
|
initialize_simulation_models(
|
|
@@ -77,73 +77,47 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
77
77
|
return config
|
|
78
78
|
|
|
79
79
|
def simulate(self):
|
|
80
|
-
"""
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
"""
|
|
88
|
-
run_script = self.prepare_script()
|
|
89
|
-
log_path = Path(self.output_directory) / "logfile.log"
|
|
90
|
-
with open(log_path, "w", encoding="utf-8") as fh:
|
|
91
|
-
subprocess.run(
|
|
92
|
-
run_script,
|
|
93
|
-
shell=False,
|
|
94
|
-
check=False,
|
|
95
|
-
text=True,
|
|
96
|
-
stdout=fh,
|
|
97
|
-
stderr=fh,
|
|
98
|
-
)
|
|
99
|
-
out = Path(self._get_simulation_output_filename())
|
|
100
|
-
if not out.exists():
|
|
101
|
-
self._logger.warning(f"Expected sim_telarray output not found: {out}")
|
|
102
|
-
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
|
+
)
|
|
103
87
|
|
|
104
|
-
def
|
|
88
|
+
def prepare_run(self):
|
|
105
89
|
"""
|
|
106
|
-
|
|
90
|
+
Prepare the bash run script containing the light-emission command.
|
|
107
91
|
|
|
108
92
|
Returns
|
|
109
93
|
-------
|
|
110
94
|
Path
|
|
111
95
|
Full path of the run script.
|
|
112
96
|
"""
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
app_name = self._get_light_emission_application_name()
|
|
117
|
-
script_file = script_dir / f"{app_name}-light_emission.sh"
|
|
118
|
-
self._logger.debug(f"Run bash script - {script_file}")
|
|
119
|
-
|
|
120
|
-
target_out = Path(self._get_simulation_output_filename())
|
|
121
|
-
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():
|
|
122
100
|
raise FileExistsError(
|
|
123
|
-
f"sim_telarray output file exists, cancelling simulation: {
|
|
101
|
+
f"sim_telarray output file exists, cancelling simulation: {output_file}"
|
|
124
102
|
)
|
|
103
|
+
lines = self.make_run_command()
|
|
104
|
+
script_file.write_text("".join(lines), encoding="utf-8")
|
|
105
|
+
return script_file
|
|
125
106
|
|
|
126
|
-
|
|
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 [
|
|
127
111
|
"#!/usr/bin/env bash\n",
|
|
128
|
-
f"{self.
|
|
112
|
+
f"{self._make_light_emission_command(iact_output)}\n\n",
|
|
129
113
|
(
|
|
130
|
-
f"[ -s '{
|
|
114
|
+
f"[ -s '{iact_output}' ] || "
|
|
131
115
|
f"{{ echo 'LightEmission did not produce IACT file' >&2; exit 1; }}\n\n"
|
|
132
116
|
),
|
|
133
117
|
f"{self._make_simtel_script()}\n\n",
|
|
134
|
-
f"rm -f '{
|
|
118
|
+
f"rm -f '{iact_output}'\n\n",
|
|
135
119
|
]
|
|
136
120
|
|
|
137
|
-
script_file.write_text("".join(lines), encoding="utf-8")
|
|
138
|
-
script_file.chmod(script_file.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP)
|
|
139
|
-
return script_file
|
|
140
|
-
|
|
141
|
-
def _get_prefix(self):
|
|
142
|
-
prefix = self.light_emission_config.get("output_prefix", "")
|
|
143
|
-
if prefix is not None:
|
|
144
|
-
return f"{prefix}_"
|
|
145
|
-
return ""
|
|
146
|
-
|
|
147
121
|
def _get_light_emission_application_name(self):
|
|
148
122
|
"""
|
|
149
123
|
Return the LightEmission application and mode from type.
|
|
@@ -242,7 +216,9 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
242
216
|
radius = self.telescope_model.get_parameter_value_with_unit("telescope_sphere_radius")
|
|
243
217
|
radius = radius.to(u.cm).value # Convert radius to cm
|
|
244
218
|
|
|
245
|
-
telescope_position_file =
|
|
219
|
+
telescope_position_file = (
|
|
220
|
+
self.io_handler.get_output_directory("light_emission") / "telescope_position.dat"
|
|
221
|
+
)
|
|
246
222
|
telescope_position_file.write_text(f"{x_tel} {y_tel} {z_tel} {radius}\n", encoding="utf-8")
|
|
247
223
|
return telescope_position_file
|
|
248
224
|
|
|
@@ -270,13 +246,18 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
270
246
|
self._logger.warning(f"Failed to create atmosphere alias {dst.name}: {copy_err}")
|
|
271
247
|
return model_id
|
|
272
248
|
|
|
273
|
-
def
|
|
249
|
+
def _make_light_emission_command(self, iact_output):
|
|
274
250
|
"""
|
|
275
|
-
Create the light emission
|
|
251
|
+
Create the light emission command to run the light emission package.
|
|
276
252
|
|
|
277
253
|
Require the specified pre-compiled light emission package application
|
|
278
254
|
in the sim_telarray/LightEmission/ path.
|
|
279
255
|
|
|
256
|
+
Parameters
|
|
257
|
+
----------
|
|
258
|
+
iact_output: str or Path
|
|
259
|
+
The output iact file path.
|
|
260
|
+
|
|
280
261
|
Returns
|
|
281
262
|
-------
|
|
282
263
|
str
|
|
@@ -285,24 +266,24 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
285
266
|
config_directory = self.io_handler.get_model_configuration_directory(
|
|
286
267
|
model_version=self.site_model.model_version
|
|
287
268
|
)
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
+
]
|
|
292
277
|
|
|
293
|
-
parts = [str(settings.config.sim_telarray_path / "LightEmission") + f"/{app_name}"]
|
|
294
|
-
parts.extend(self._get_site_command(app_name, config_directory, corsika_observation_level))
|
|
295
|
-
parts.extend(self._get_light_source_command())
|
|
296
278
|
if self.light_emission_config["light_source_type"] == "illuminator":
|
|
297
|
-
|
|
279
|
+
cmd += [
|
|
298
280
|
"-A",
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
f"{self.telescope_model.get_parameter_value('atmospheric_profile')}"
|
|
302
|
-
),
|
|
281
|
+
f"{config_directory}/"
|
|
282
|
+
f"{self.telescope_model.get_parameter_value('atmospheric_profile')}",
|
|
303
283
|
]
|
|
304
|
-
|
|
305
|
-
|
|
284
|
+
|
|
285
|
+
cmd += ["-o", str(iact_output)]
|
|
286
|
+
return " ".join(cmd)
|
|
306
287
|
|
|
307
288
|
def _get_site_command(self, app_name, config_directory, corsika_observation_level):
|
|
308
289
|
"""Return site command with altitude, atmosphere and telescope_position handling."""
|
|
@@ -361,17 +342,12 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
361
342
|
" and exponential decay values"
|
|
362
343
|
)
|
|
363
344
|
try:
|
|
364
|
-
base_dir = self.io_handler.get_output_directory("pulse_shapes")
|
|
365
|
-
|
|
366
|
-
def _sanitize_name(value):
|
|
367
|
-
return "".join(
|
|
368
|
-
ch if (ch.isalnum() or ch in ("-", "_")) else "_" for ch in str(value)
|
|
369
|
-
)
|
|
370
|
-
|
|
371
345
|
tel = self.light_emission_config.get("telescope") or "telescope"
|
|
372
346
|
cal = self.light_emission_config.get("light_source") or "calibration"
|
|
373
|
-
fname =
|
|
374
|
-
|
|
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
|
|
375
351
|
fadc_bins = self.telescope_model.get_parameter_value("fadc_sum_bins")
|
|
376
352
|
|
|
377
353
|
SimtelConfigWriter.write_light_pulse_table_gauss_exp_conv(
|
|
@@ -397,6 +373,9 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
397
373
|
f"--angular-distribution {angular_distribution}",
|
|
398
374
|
]
|
|
399
375
|
|
|
376
|
+
def _sanitize_name(self, value):
|
|
377
|
+
return "".join(ch if (ch.isalnum() or ch in ("-", "_")) else "_" for ch in str(value))
|
|
378
|
+
|
|
400
379
|
def _add_illuminator_command_options(self):
|
|
401
380
|
"""Get illuminator-specific command options for light emission script."""
|
|
402
381
|
pos = self.light_emission_config.get("light_source_position")
|
|
@@ -435,56 +414,52 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
435
414
|
The command to run sim_telarray
|
|
436
415
|
"""
|
|
437
416
|
theta, phi = self._get_telescope_pointing()
|
|
438
|
-
|
|
439
417
|
simtel_bin = str(settings.config.sim_telarray_exe)
|
|
440
418
|
|
|
441
419
|
parts = [
|
|
442
|
-
|
|
420
|
+
simtel_bin,
|
|
443
421
|
f"-I{self.telescope_model.config_file_directory}",
|
|
444
422
|
f"-I{simtel_bin}",
|
|
445
423
|
f"-c {self.telescope_model.config_file_path}",
|
|
446
424
|
"-DNUM_TELESCOPES=1",
|
|
447
|
-
|
|
425
|
+
]
|
|
426
|
+
|
|
427
|
+
options = [
|
|
428
|
+
(
|
|
448
429
|
"altitude",
|
|
449
430
|
self.site_model.get_parameter_value_with_unit("corsika_observation_level")
|
|
450
431
|
.to(u.m)
|
|
451
432
|
.value,
|
|
452
433
|
),
|
|
453
|
-
|
|
434
|
+
(
|
|
454
435
|
"atmospheric_transmission",
|
|
455
436
|
self.site_model.get_parameter_value("atmospheric_transmission"),
|
|
456
437
|
),
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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}"),
|
|
463
444
|
]
|
|
464
445
|
|
|
465
446
|
if self.light_emission_config["light_source_type"] == "flat_fielding":
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
),
|
|
476
|
-
|
|
477
|
-
"histogram_file", f"{self.output_directory}/{pref}{app_name}.ctsim.hdata\n"
|
|
478
|
-
),
|
|
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}"),
|
|
479
458
|
]
|
|
480
459
|
|
|
481
|
-
|
|
460
|
+
parts += [f"-C {key}={value}" for key, value in options]
|
|
482
461
|
|
|
483
|
-
|
|
484
|
-
"""Get the filename of the simulation output."""
|
|
485
|
-
app_name = self._get_light_emission_application_name()
|
|
486
|
-
pref = self._get_prefix()
|
|
487
|
-
return f"{self.output_directory}/{pref}{app_name}.simtel.zst"
|
|
462
|
+
return sim_telarray_env_as_string() + " ".join(parts)
|
|
488
463
|
|
|
489
464
|
def calculate_distance_focal_plane_calibration_device(self):
|
|
490
465
|
"""
|
|
@@ -509,21 +484,14 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
509
484
|
|
|
510
485
|
Uses a pure cosine profile normalized to 1 at 0 deg and spans 0..90 deg by default.
|
|
511
486
|
"""
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
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,
|
|
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,
|
|
523
492
|
max_angle_deg=90.0,
|
|
524
493
|
n_samples=100,
|
|
525
494
|
)
|
|
526
|
-
return str(table_path)
|
|
527
495
|
|
|
528
496
|
def _get_angular_distribution_string_for_sim_telarray(self):
|
|
529
497
|
"""
|
|
@@ -546,6 +514,9 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
546
514
|
)
|
|
547
515
|
return option_string
|
|
548
516
|
|
|
517
|
+
if option_string == "isotropic":
|
|
518
|
+
return option_string
|
|
519
|
+
|
|
549
520
|
width = self.calibration_model.get_parameter_value_with_unit(
|
|
550
521
|
"flasher_angular_distribution_width"
|
|
551
522
|
)
|
|
@@ -577,3 +548,19 @@ class SimulatorLightEmission(SimtelRunner):
|
|
|
577
548
|
if shape_out == "exponential" and expv is not None:
|
|
578
549
|
return f"{shape_out}:{float(expv)}"
|
|
579
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
|
|
@@ -9,7 +9,6 @@ from simtools import settings
|
|
|
9
9
|
from simtools.io import io_handler
|
|
10
10
|
from simtools.runners.simtel_runner import SimtelRunner
|
|
11
11
|
from simtools.utils import names
|
|
12
|
-
from simtools.utils.general import clear_default_sim_telarray_cfg_directories
|
|
13
12
|
|
|
14
13
|
# pylint: disable=no-member
|
|
15
14
|
# The line above is needed because there are members which are created
|
|
@@ -67,6 +66,8 @@ class SimulatorRayTracing(SimtelRunner):
|
|
|
67
66
|
self._rep_number = 0
|
|
68
67
|
self.runs_per_set = 1 if self.config.single_mirror_mode else 20
|
|
69
68
|
self.photons_per_run = 100000 if not test else 5000
|
|
69
|
+
self._single_pixel_camera_file = None
|
|
70
|
+
self._funnel_file = None
|
|
70
71
|
|
|
71
72
|
self._load_required_files(force_simulate)
|
|
72
73
|
|
|
@@ -106,12 +107,13 @@ class SimulatorRayTracing(SimtelRunner):
|
|
|
106
107
|
self.__dict__["_" + base_name + "_file"] = file
|
|
107
108
|
|
|
108
109
|
if not file.exists() or force_simulate:
|
|
110
|
+
config_file_path = self.telescope_model.get_config_file_path(label=self.label)
|
|
109
111
|
# Adding header to photon list file.
|
|
110
112
|
with self._photons_file.open("w", encoding="utf-8") as file:
|
|
111
113
|
file.write(f"#{50 * '='}\n")
|
|
112
114
|
file.write("# List of photons for RayTracing simulations\n")
|
|
113
115
|
file.write(f"#{50 * '='}\n")
|
|
114
|
-
file.write(f"# config_file = {
|
|
116
|
+
file.write(f"# config_file = {config_file_path}\n")
|
|
115
117
|
file.write(f"# zenith_angle [deg] = {self.config.zenith_angle}\n")
|
|
116
118
|
file.write(f"# off_axis_angle [deg] = {self.config.off_axis_angle}\n")
|
|
117
119
|
file.write(f"# source_distance [km] = {self.config.source_distance}\n")
|
|
@@ -130,15 +132,20 @@ class SimulatorRayTracing(SimtelRunner):
|
|
|
130
132
|
|
|
131
133
|
if self.config.single_mirror_mode:
|
|
132
134
|
self._logger.debug("For single mirror mode, need to prepare the single pixel camera.")
|
|
133
|
-
self.
|
|
135
|
+
self._write_out_single_pixel_camera_files()
|
|
134
136
|
|
|
135
|
-
def
|
|
137
|
+
def make_run_command(self, run_number=None, input_file=None): # pylint: disable=unused-argument
|
|
136
138
|
"""
|
|
137
139
|
Generate sim_telarray run command. Export sim_telarray configuration file(s).
|
|
138
140
|
|
|
139
141
|
The run_number and input_file parameters are not relevant for the ray tracing simulation.
|
|
140
142
|
"""
|
|
141
|
-
self.telescope_model.write_sim_telarray_config_file(
|
|
143
|
+
self.telescope_model.write_sim_telarray_config_file(
|
|
144
|
+
additional_models=self.site_model,
|
|
145
|
+
label=self.label,
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
config_file_path = self.telescope_model.get_config_file_path(label=self.label)
|
|
142
149
|
|
|
143
150
|
if self.config.single_mirror_mode:
|
|
144
151
|
# Note: no mirror length defined for dual-mirror telescopes
|
|
@@ -146,54 +153,56 @@ class SimulatorRayTracing(SimtelRunner):
|
|
|
146
153
|
self.telescope_model.get_parameter_value("mirror_focal_length")
|
|
147
154
|
)
|
|
148
155
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
"
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
"
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
command += super().get_config_option("telescope_random_angle", "0")
|
|
169
|
-
command += super().get_config_option("telescope_random_error", "0")
|
|
170
|
-
command += super().get_config_option("convergent_depth", "0")
|
|
171
|
-
command += super().get_config_option("maximum_telescopes", "1")
|
|
172
|
-
command += super().get_config_option("show", "all")
|
|
173
|
-
command += super().get_config_option("camera_filter", "none")
|
|
156
|
+
options = {
|
|
157
|
+
"random_state": "none",
|
|
158
|
+
"IMAGING_LIST": str(self._photons_file),
|
|
159
|
+
"stars": str(self._stars_file),
|
|
160
|
+
"altitude": self.site_model.get_parameter_value("corsika_observation_level"),
|
|
161
|
+
"telescope_theta": self.config.zenith_angle + self.config.off_axis_angle,
|
|
162
|
+
"star_photons": str(self.photons_per_run),
|
|
163
|
+
"telescope_phi": "0",
|
|
164
|
+
"camera_transmission": "1.0",
|
|
165
|
+
"nightsky_background": "all:0.",
|
|
166
|
+
"trigger_current_limit": "1e10",
|
|
167
|
+
"telescope_random_angle": "0",
|
|
168
|
+
"telescope_random_error": "0",
|
|
169
|
+
"convergent_depth": "0",
|
|
170
|
+
"maximum_telescopes": "1",
|
|
171
|
+
"show": "all",
|
|
172
|
+
"camera_filter": "none",
|
|
173
|
+
}
|
|
174
|
+
|
|
174
175
|
if self.config.single_mirror_mode:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
176
|
+
options.update(
|
|
177
|
+
{
|
|
178
|
+
"focus_offset": "all:0.",
|
|
179
|
+
"camera_config_file": str(self._single_pixel_camera_file),
|
|
180
|
+
"camera_pixels": "1",
|
|
181
|
+
"trigger_pixels": "1",
|
|
182
|
+
"camera_body_diameter": "0",
|
|
183
|
+
"mirror_list": self.telescope_model.get_single_mirror_list_file(
|
|
184
|
+
self.config.mirror_numbers, self.config.use_random_focal_length
|
|
185
|
+
),
|
|
186
|
+
"focal_length": self.config.source_distance * u.km.to(u.cm),
|
|
187
|
+
"dish_shape_length": _mirror_focal_length,
|
|
188
|
+
"mirror_focal_length": _mirror_focal_length,
|
|
189
|
+
"parabolic_dish": "0",
|
|
190
|
+
"mirror_align_random_distance": "0.",
|
|
191
|
+
"mirror_align_random_vertical": "0.,28.,0.,0.",
|
|
192
|
+
}
|
|
188
193
|
)
|
|
189
|
-
command += super().get_config_option("dish_shape_length", _mirror_focal_length)
|
|
190
|
-
command += super().get_config_option("mirror_focal_length", _mirror_focal_length)
|
|
191
|
-
command += super().get_config_option("parabolic_dish", "0")
|
|
192
|
-
command += super().get_config_option("mirror_align_random_distance", "0.")
|
|
193
|
-
command += super().get_config_option("mirror_align_random_vertical", "0.,28.,0.,0.")
|
|
194
|
-
command += " " + str(settings.config.corsika_dummy_file)
|
|
195
194
|
|
|
196
|
-
|
|
195
|
+
cmd = [
|
|
196
|
+
str(settings.config.sim_telarray_exe),
|
|
197
|
+
"-c",
|
|
198
|
+
str(config_file_path),
|
|
199
|
+
f"-I{self.telescope_model.config_file_directory}",
|
|
200
|
+
]
|
|
201
|
+
for key, value in options.items():
|
|
202
|
+
cmd.extend(["-C", f"{key}={value}"])
|
|
203
|
+
cmd.append(str(settings.config.corsika_dummy_file))
|
|
204
|
+
|
|
205
|
+
return cmd, self._log_file, self._log_file
|
|
197
206
|
|
|
198
207
|
def _check_run_result(self, run_number=None): # pylint: disable=unused-argument
|
|
199
208
|
"""
|
|
@@ -217,20 +226,19 @@ class SimulatorRayTracing(SimtelRunner):
|
|
|
217
226
|
raise RuntimeError("Photon list is empty.")
|
|
218
227
|
return True
|
|
219
228
|
|
|
220
|
-
def
|
|
221
|
-
"""Write out
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
+
def _write_out_single_pixel_camera_files(self):
|
|
230
|
+
"""Write out per-label single pixel camera + funnel files.
|
|
231
|
+
|
|
232
|
+
These files are referenced by sim_telarray and must not be shared across
|
|
233
|
+
parallel worker processes (otherwise they can be truncated mid-read).
|
|
234
|
+
"""
|
|
235
|
+
self._single_pixel_camera_file = (
|
|
236
|
+
self._base_directory / f"single_pixel_camera_{self.label}.dat"
|
|
237
|
+
)
|
|
238
|
+
self._funnel_file = self._base_directory / f"funnel_perfect_{self.label}.dat"
|
|
229
239
|
|
|
230
|
-
#
|
|
231
|
-
with self.
|
|
232
|
-
"w"
|
|
233
|
-
) as file:
|
|
240
|
+
# Funnel file (referenced by absolute path from the camera file).
|
|
241
|
+
with self._funnel_file.open("w", encoding="utf-8") as file:
|
|
234
242
|
file.write(
|
|
235
243
|
"# Perfect light collection where the angular efficiency of funnels is needed\n"
|
|
236
244
|
)
|
|
@@ -239,6 +247,13 @@ class SimulatorRayTracing(SimtelRunner):
|
|
|
239
247
|
file.write("60 1.0\n")
|
|
240
248
|
file.write("90 1.0\n")
|
|
241
249
|
|
|
250
|
+
# Camera config.
|
|
251
|
+
with self._single_pixel_camera_file.open("w", encoding="utf-8") as file:
|
|
252
|
+
file.write("# Single pixel camera\n")
|
|
253
|
+
file.write(f'PixType 1 0 0 300 1 300 0.00 "{self._funnel_file}"\n')
|
|
254
|
+
file.write("Pixel 0 1 0. 0. 0 0 0 0x00 1\n")
|
|
255
|
+
file.write("Trigger 1 of 0\n")
|
|
256
|
+
|
|
242
257
|
def _config_to_namedtuple(self, data_dict):
|
|
243
258
|
"""Convert dict to namedtuple for configuration."""
|
|
244
259
|
config_data = namedtuple(
|