gammasimtools 0.26.0__py3-none-any.whl → 0.27.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/METADATA +5 -1
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/RECORD +70 -66
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/WHEEL +1 -1
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/entry_points.txt +1 -1
- simtools/_version.py +2 -2
- simtools/applications/convert_geo_coordinates_of_array_elements.py +2 -1
- simtools/applications/db_get_array_layouts_from_db.py +1 -1
- simtools/applications/{calculate_incident_angles.py → derive_incident_angle.py} +16 -16
- simtools/applications/derive_mirror_rnda.py +111 -177
- simtools/applications/generate_corsika_histograms.py +38 -1
- simtools/applications/generate_regular_arrays.py +73 -36
- simtools/applications/simulate_flasher.py +3 -13
- simtools/applications/simulate_illuminator.py +2 -10
- simtools/applications/simulate_pedestals.py +1 -1
- simtools/applications/simulate_prod.py +8 -7
- simtools/applications/submit_data_from_external.py +2 -1
- simtools/applications/validate_camera_efficiency.py +28 -27
- simtools/applications/validate_cumulative_psf.py +1 -3
- simtools/applications/validate_optics.py +2 -1
- simtools/atmosphere.py +83 -0
- simtools/camera/camera_efficiency.py +171 -48
- simtools/camera/single_photon_electron_spectrum.py +6 -6
- simtools/configuration/commandline_parser.py +47 -9
- simtools/constants.py +5 -0
- simtools/corsika/corsika_config.py +88 -185
- simtools/corsika/corsika_histograms.py +246 -69
- simtools/data_model/model_data_writer.py +46 -49
- simtools/data_model/schema.py +2 -0
- simtools/db/db_handler.py +4 -2
- simtools/db/mongo_db.py +2 -2
- simtools/io/ascii_handler.py +52 -4
- simtools/io/io_handler.py +23 -12
- simtools/job_execution/job_manager.py +154 -79
- simtools/job_execution/process_pool.py +137 -0
- simtools/layout/array_layout.py +0 -1
- simtools/layout/array_layout_utils.py +143 -21
- simtools/model/array_model.py +22 -50
- simtools/model/calibration_model.py +4 -4
- simtools/model/model_parameter.py +123 -73
- simtools/model/model_utils.py +40 -1
- simtools/model/site_model.py +4 -4
- simtools/model/telescope_model.py +4 -5
- simtools/ray_tracing/incident_angles.py +87 -6
- simtools/ray_tracing/mirror_panel_psf.py +337 -217
- simtools/ray_tracing/psf_analysis.py +57 -42
- simtools/ray_tracing/psf_parameter_optimisation.py +3 -2
- simtools/ray_tracing/ray_tracing.py +37 -10
- simtools/runners/corsika_runner.py +52 -191
- simtools/runners/corsika_simtel_runner.py +74 -100
- simtools/runners/runner_services.py +214 -213
- simtools/runners/simtel_runner.py +27 -155
- simtools/runners/simtools_runner.py +9 -69
- simtools/schemas/application_workflow.metaschema.yml +8 -0
- simtools/settings.py +19 -0
- simtools/simtel/simtel_config_writer.py +0 -55
- simtools/simtel/simtel_seeds.py +184 -0
- simtools/simtel/simulator_array.py +115 -103
- simtools/simtel/simulator_camera_efficiency.py +66 -42
- simtools/simtel/simulator_light_emission.py +110 -123
- simtools/simtel/simulator_ray_tracing.py +78 -63
- simtools/simulator.py +135 -346
- simtools/testing/sim_telarray_metadata.py +13 -11
- simtools/testing/validate_output.py +87 -19
- simtools/utils/general.py +6 -17
- simtools/utils/random.py +36 -0
- simtools/visualization/plot_corsika_histograms.py +2 -0
- simtools/visualization/plot_incident_angles.py +48 -1
- simtools/visualization/plot_psf.py +160 -18
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/licenses/LICENSE +0 -0
- {gammasimtools-0.26.0.dist-info → gammasimtools-0.27.1.dist-info}/top_level.txt +0 -0
|
@@ -7,9 +7,7 @@ Main functionalities are: computing centroids, psf containers etc.
|
|
|
7
7
|
|
|
8
8
|
import gzip
|
|
9
9
|
import logging
|
|
10
|
-
import
|
|
11
|
-
import shutil
|
|
12
|
-
import subprocess
|
|
10
|
+
import tempfile
|
|
13
11
|
from math import fabs, pi, sqrt
|
|
14
12
|
from pathlib import Path
|
|
15
13
|
|
|
@@ -18,6 +16,7 @@ import matplotlib.pyplot as plt
|
|
|
18
16
|
import numpy as np
|
|
19
17
|
|
|
20
18
|
from simtools import settings
|
|
19
|
+
from simtools.job_execution import job_manager
|
|
21
20
|
from simtools.utils.general import collect_kwargs, set_default_kwargs
|
|
22
21
|
|
|
23
22
|
|
|
@@ -93,36 +92,49 @@ class PSFImage:
|
|
|
93
92
|
photons_file: str
|
|
94
93
|
Name of sim_telarray file with photon list.
|
|
95
94
|
"""
|
|
95
|
+
rx_command = [
|
|
96
|
+
f"{settings.config.sim_telarray_path}/bin/rx",
|
|
97
|
+
"-f",
|
|
98
|
+
f"{self._containment_fraction:.2f}",
|
|
99
|
+
"-v",
|
|
100
|
+
]
|
|
101
|
+
|
|
96
102
|
try:
|
|
97
|
-
with
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
103
|
+
with tempfile.NamedTemporaryFile(mode="wb", delete=False) as tmp_file:
|
|
104
|
+
with gzip.open(photon_file, "rb") as gz:
|
|
105
|
+
tmp_file.write(gz.read())
|
|
106
|
+
tmp_file_path = tmp_file.name
|
|
107
|
+
|
|
108
|
+
try:
|
|
109
|
+
with open(tmp_file_path, "rb") as tmp_f:
|
|
110
|
+
result = job_manager.submit(rx_command, stdin=tmp_f)
|
|
111
|
+
|
|
112
|
+
data_lines = [
|
|
113
|
+
line.strip()
|
|
114
|
+
for line in result.stdout.strip().split("\n")
|
|
115
|
+
if line.strip() and not line.startswith("#")
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
if not data_lines:
|
|
119
|
+
raise IndexError("No data line found in RX output")
|
|
120
|
+
|
|
121
|
+
rx_output = data_lines[-1].split()
|
|
122
|
+
self.set_psf(
|
|
123
|
+
2 * float(rx_output[0]), fraction=self._containment_fraction, unit="cm"
|
|
124
|
+
)
|
|
125
|
+
self.centroid_x = float(rx_output[1])
|
|
126
|
+
self.centroid_y = float(rx_output[2])
|
|
127
|
+
self._effective_area = float(rx_output[5])
|
|
128
|
+
|
|
129
|
+
finally:
|
|
130
|
+
Path(tmp_file_path).unlink(missing_ok=True)
|
|
131
|
+
|
|
114
132
|
except FileNotFoundError as e:
|
|
115
133
|
raise FileNotFoundError(f"Photon list file not found: {photon_file}") from e
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
self.centroid_y = float(rx_output[2])
|
|
121
|
-
self._effective_area = float(rx_output[5])
|
|
122
|
-
except IndexError as e:
|
|
123
|
-
raise IndexError(f"Unexpected output format from rx: {rx_output}") from e
|
|
124
|
-
except ValueError as e:
|
|
125
|
-
raise ValueError(f"Invalid output format from rx: {rx_output}") from e
|
|
134
|
+
except (IndexError, ValueError) as e:
|
|
135
|
+
raise type(e)(
|
|
136
|
+
f"Invalid RX output format: {locals().get('rx_output', 'unknown')}"
|
|
137
|
+
) from e
|
|
126
138
|
|
|
127
139
|
def read_photon_list_from_simtel_file(self, photons_file):
|
|
128
140
|
"""
|
|
@@ -150,9 +162,9 @@ class PSFImage:
|
|
|
150
162
|
self._process_simtel_line(line)
|
|
151
163
|
|
|
152
164
|
if not self._is_photon_positions_ok():
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
165
|
+
raise RuntimeError(
|
|
166
|
+
f"Problems reading sim_telarray photons file {photons_file} - invalid data"
|
|
167
|
+
)
|
|
156
168
|
|
|
157
169
|
self.centroid_x = np.mean(self.photon_pos_x)
|
|
158
170
|
self.centroid_y = np.mean(self.photon_pos_y)
|
|
@@ -467,9 +479,10 @@ class PSFImage:
|
|
|
467
479
|
ax.hist2d(data["X"], data["Y"], **kwargs_for_image)
|
|
468
480
|
ax.set_aspect("equal", "datalim")
|
|
469
481
|
|
|
470
|
-
# PSF circle (
|
|
482
|
+
# PSF circle (containment fraction)
|
|
483
|
+
fraction = self._containment_fraction if self._containment_fraction is not None else 0.8
|
|
471
484
|
center = (0, 0) if centralized else (self.centroid_x, self.centroid_y)
|
|
472
|
-
circle = plt.Circle(center, self.get_psf(
|
|
485
|
+
circle = plt.Circle(center, self.get_psf(fraction) / 2, **kwargs_for_psf)
|
|
473
486
|
ax.add_artist(circle)
|
|
474
487
|
|
|
475
488
|
ax.axhline(0, color="k", linestyle="--", zorder=3, linewidth=0.5)
|
|
@@ -495,7 +508,8 @@ class PSFImage:
|
|
|
495
508
|
if radius is not None:
|
|
496
509
|
radius_all = radius.to(u.cm).value if isinstance(radius, u.Quantity) else radius
|
|
497
510
|
else:
|
|
498
|
-
|
|
511
|
+
fraction = self._containment_fraction if self._containment_fraction is not None else 0.8
|
|
512
|
+
radius_all = list(np.linspace(0, 1.6 * self.get_psf(fraction), 30))
|
|
499
513
|
intensity = [
|
|
500
514
|
self._sum_photons_in_radius(rad) / self._number_of_detected_photons
|
|
501
515
|
for rad in radius_all
|
|
@@ -510,15 +524,15 @@ class PSFImage:
|
|
|
510
524
|
|
|
511
525
|
return result
|
|
512
526
|
|
|
513
|
-
def plot_cumulative(self, file_name=None,
|
|
527
|
+
def plot_cumulative(self, file_name=None, psf_diameter_cm=None, **kwargs):
|
|
514
528
|
"""Plot cumulative data (intensity vs radius).
|
|
515
529
|
|
|
516
530
|
Parameters
|
|
517
531
|
----------
|
|
518
532
|
file_name: str
|
|
519
533
|
Name of the file to save the plot to.
|
|
520
|
-
|
|
521
|
-
|
|
534
|
+
psf_diameter_cm: float
|
|
535
|
+
PSF diameter value to be marked in the plot (in cm).
|
|
522
536
|
**kwargs:
|
|
523
537
|
Customization of line plot (e.g., color, linestyle, linewidth).
|
|
524
538
|
"""
|
|
@@ -527,9 +541,10 @@ class PSFImage:
|
|
|
527
541
|
ax.set_xlabel("Radius (cm)")
|
|
528
542
|
ax.set_ylabel("Contained light %")
|
|
529
543
|
ax.plot(data[self.__PSF_RADIUS], data[self.__PSF_CUMULATIVE], **kwargs)
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
544
|
+
fraction = self._containment_fraction if self._containment_fraction is not None else 0.8
|
|
545
|
+
ax.axvline(x=self.get_psf(fraction) / 2, color="b", linestyle="--", linewidth=1)
|
|
546
|
+
if psf_diameter_cm is not None:
|
|
547
|
+
ax.axvline(x=psf_diameter_cm / 2.0, color="r", linestyle="--", linewidth=1)
|
|
533
548
|
if file_name is not None:
|
|
534
549
|
fig.savefig(file_name)
|
|
535
550
|
plt.close(fig)
|
|
@@ -857,10 +857,11 @@ def _run_ray_tracing_simulation(tel_model, site_model, args_dict, pars):
|
|
|
857
857
|
if pars is None:
|
|
858
858
|
raise ValueError("No best parameters found")
|
|
859
859
|
|
|
860
|
-
tel_model.overwrite_parameters(pars)
|
|
860
|
+
tel_model.overwrite_parameters(pars, flat_dict=True)
|
|
861
861
|
ray = RayTracing(
|
|
862
862
|
telescope_model=tel_model,
|
|
863
863
|
site_model=site_model,
|
|
864
|
+
label=args_dict.get("label") or getattr(tel_model, "label", None),
|
|
864
865
|
zenith_angle=args_dict["zenith"] * u.deg,
|
|
865
866
|
source_distance=args_dict["src_distance"] * u.km,
|
|
866
867
|
off_axis_angle=[0.0] * u.deg,
|
|
@@ -1381,7 +1382,7 @@ def cleanup_intermediate_files(output_dir):
|
|
|
1381
1382
|
output_dir : Path
|
|
1382
1383
|
Directory containing output files to clean up.
|
|
1383
1384
|
"""
|
|
1384
|
-
patterns = ["*.log", "*.lis*"]
|
|
1385
|
+
patterns = ["*.log", "*.lis*", "*.dat"]
|
|
1385
1386
|
files_removed = 0
|
|
1386
1387
|
|
|
1387
1388
|
for pattern in patterns:
|
|
@@ -51,8 +51,8 @@ class RayTracing:
|
|
|
51
51
|
"""
|
|
52
52
|
|
|
53
53
|
YLABEL = {
|
|
54
|
-
"
|
|
55
|
-
"
|
|
54
|
+
"psf_cm": "PSF",
|
|
55
|
+
"psf_deg": "PSF",
|
|
56
56
|
"eff_area": "Eff. mirror area",
|
|
57
57
|
"eff_flen": "Eff. focal length",
|
|
58
58
|
}
|
|
@@ -209,6 +209,7 @@ class RayTracing:
|
|
|
209
209
|
simtel = SimulatorRayTracing(
|
|
210
210
|
telescope_model=self.telescope_model,
|
|
211
211
|
site_model=self.site_model,
|
|
212
|
+
label=self.label,
|
|
212
213
|
test=test,
|
|
213
214
|
config_data={
|
|
214
215
|
"zenith_angle": self.zenith_angle,
|
|
@@ -468,22 +469,46 @@ class RayTracing:
|
|
|
468
469
|
else:
|
|
469
470
|
self._logger.error("No results to export")
|
|
470
471
|
|
|
472
|
+
def get_psf_mm(self, row_index: int = 0) -> float:
|
|
473
|
+
"""Return PSF diameter from the analysis results in mm.
|
|
474
|
+
|
|
475
|
+
Parameters
|
|
476
|
+
----------
|
|
477
|
+
row_index : int
|
|
478
|
+
Row index into the results table (default: 0).
|
|
479
|
+
|
|
480
|
+
Returns
|
|
481
|
+
-------
|
|
482
|
+
float
|
|
483
|
+
PSF diameter in millimeters.
|
|
484
|
+
"""
|
|
485
|
+
if self._results is None:
|
|
486
|
+
raise RuntimeError("No results available; run analyze() first")
|
|
487
|
+
psf = self._results["psf_cm"][row_index]
|
|
488
|
+
|
|
489
|
+
if isinstance(psf, u.Quantity):
|
|
490
|
+
psf_cm = psf.to_value(u.cm)
|
|
491
|
+
else:
|
|
492
|
+
psf_cm = float(psf)
|
|
493
|
+
|
|
494
|
+
return psf_cm * 10.0
|
|
495
|
+
|
|
471
496
|
def _read_results(self):
|
|
472
497
|
"""Read existing results file and store it in _results."""
|
|
473
498
|
self._results = astropy.io.ascii.read(self._file_results, format="ecsv")
|
|
474
499
|
|
|
475
|
-
def plot(self, key, save=False,
|
|
500
|
+
def plot(self, key, save=False, psf_diameter_cm=None, **kwargs):
|
|
476
501
|
"""
|
|
477
502
|
Plot key vs off-axis angle and save the figure in pdf.
|
|
478
503
|
|
|
479
504
|
Parameters
|
|
480
505
|
----------
|
|
481
506
|
key: str
|
|
482
|
-
|
|
507
|
+
psf_cm, psf_deg, eff_area or eff_flen
|
|
483
508
|
save: bool
|
|
484
509
|
If True, figure will be saved.
|
|
485
|
-
|
|
486
|
-
|
|
510
|
+
psf_diameter_cm: float
|
|
511
|
+
PSF diameter value to be marked in the cumulative PSF plot (in cm).
|
|
487
512
|
**kwargs:
|
|
488
513
|
kwargs for plt.plot
|
|
489
514
|
|
|
@@ -533,7 +558,9 @@ class RayTracing:
|
|
|
533
558
|
image_cumulative_file_name
|
|
534
559
|
)
|
|
535
560
|
self._logger.info(f"Saving cumulative PSF to {image_cumulative_file}")
|
|
536
|
-
image.plot_cumulative(
|
|
561
|
+
image.plot_cumulative(
|
|
562
|
+
file_name=image_cumulative_file, psf_diameter_cm=psf_diameter_cm
|
|
563
|
+
)
|
|
537
564
|
|
|
538
565
|
def plot_histogram(self, key, **kwargs):
|
|
539
566
|
"""
|
|
@@ -542,7 +569,7 @@ class RayTracing:
|
|
|
542
569
|
Parameters
|
|
543
570
|
----------
|
|
544
571
|
key: str
|
|
545
|
-
|
|
572
|
+
psf_cm, psf_deg, eff_area or eff_flen
|
|
546
573
|
**kwargs:
|
|
547
574
|
kwargs for plt.hist
|
|
548
575
|
|
|
@@ -564,7 +591,7 @@ class RayTracing:
|
|
|
564
591
|
Parameters
|
|
565
592
|
----------
|
|
566
593
|
key: str
|
|
567
|
-
|
|
594
|
+
psf_cm, psf_deg, eff_area or eff_flen
|
|
568
595
|
|
|
569
596
|
Returns
|
|
570
597
|
-------
|
|
@@ -588,7 +615,7 @@ class RayTracing:
|
|
|
588
615
|
Parameters
|
|
589
616
|
----------
|
|
590
617
|
key: str
|
|
591
|
-
|
|
618
|
+
psf_cm, psf_deg, eff_area or eff_flen
|
|
592
619
|
|
|
593
620
|
Returns
|
|
594
621
|
-------
|
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
"""Generate run scripts and directories for CORSIKA simulations."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
import
|
|
4
|
+
from pathlib import Path
|
|
5
5
|
|
|
6
6
|
from simtools import settings
|
|
7
|
-
from simtools.io import io_handler
|
|
8
7
|
from simtools.runners.runner_services import RunnerServices
|
|
9
8
|
|
|
10
9
|
|
|
11
|
-
class MissingRequiredEntryInCorsikaConfigError(Exception):
|
|
12
|
-
"""Exception for missing required entry in corsika config."""
|
|
13
|
-
|
|
14
|
-
|
|
15
10
|
class CorsikaRunner:
|
|
16
11
|
"""
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
CorsikaRunner is responsible for configuring and running CORSIKA, using corsika_autoinputs
|
|
20
|
-
provided by the sim_telarray package. CorsikaRunner generates shell scripts to be run
|
|
21
|
-
externally or by the simulator module simulator.
|
|
12
|
+
Prepare and run CORSIKA simulations.
|
|
22
13
|
|
|
23
|
-
|
|
14
|
+
Generate run scripts and directories for CORSIKA simulations. Run simulations if requested.
|
|
24
15
|
|
|
25
16
|
Parameters
|
|
26
17
|
----------
|
|
@@ -28,8 +19,6 @@ class CorsikaRunner:
|
|
|
28
19
|
CORSIKA configuration.
|
|
29
20
|
label: str
|
|
30
21
|
Instance label.
|
|
31
|
-
keep_seeds: bool
|
|
32
|
-
Use seeds based on run number and primary particle. If False, use sim_telarray seeds.
|
|
33
22
|
use_multipipe: bool
|
|
34
23
|
Use multipipe to run CORSIKA and sim_telarray.
|
|
35
24
|
curved_atmosphere_min_zenith_angle: Quantity
|
|
@@ -40,7 +29,6 @@ class CorsikaRunner:
|
|
|
40
29
|
self,
|
|
41
30
|
corsika_config,
|
|
42
31
|
label=None,
|
|
43
|
-
keep_seeds=False,
|
|
44
32
|
use_multipipe=False,
|
|
45
33
|
curved_atmosphere_min_zenith_angle=None,
|
|
46
34
|
):
|
|
@@ -50,73 +38,63 @@ class CorsikaRunner:
|
|
|
50
38
|
self.label = label
|
|
51
39
|
|
|
52
40
|
self.corsika_config = corsika_config
|
|
53
|
-
self._keep_seeds = keep_seeds
|
|
54
41
|
self._use_multipipe = use_multipipe
|
|
55
42
|
self.curved_atmosphere_min_zenith_angle = curved_atmosphere_min_zenith_angle
|
|
56
43
|
|
|
57
|
-
self.
|
|
58
|
-
|
|
59
|
-
self.runner_service = RunnerServices(corsika_config, label)
|
|
60
|
-
self._directory = self.runner_service.load_data_directories("corsika")
|
|
44
|
+
self.runner_service = RunnerServices(corsika_config, run_type="corsika", label=label)
|
|
45
|
+
self.file_list = None
|
|
61
46
|
|
|
62
|
-
def
|
|
63
|
-
self, run_number=None, extra_commands=None, input_file=None, use_pfp=True
|
|
64
|
-
):
|
|
47
|
+
def prepare_run(self, run_number, sub_script, extra_commands=None, corsika_file=None):
|
|
65
48
|
"""
|
|
66
|
-
Prepare
|
|
49
|
+
Prepare CORSIKA run script and run directory.
|
|
50
|
+
|
|
51
|
+
The CORSIKA run directory includes all input files needed for the simulation.
|
|
67
52
|
|
|
68
53
|
Parameters
|
|
69
54
|
----------
|
|
70
|
-
use_pfp: bool
|
|
71
|
-
Whether to use the preprocessor in preparing the CORSIKA input file
|
|
72
55
|
run_number: int
|
|
73
56
|
Run number.
|
|
57
|
+
sub_script: str or Path
|
|
58
|
+
Path to the CORSIKA run script to be created.
|
|
59
|
+
corsika_file: str or Path
|
|
60
|
+
Path to the multipipe script (used only if use_multipipe is True).
|
|
74
61
|
extra_commands: str
|
|
75
62
|
Additional commands for running simulations.
|
|
76
|
-
|
|
77
|
-
Returns
|
|
78
|
-
-------
|
|
79
|
-
Path:
|
|
80
|
-
Full path of the run script file.
|
|
81
63
|
"""
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
91
|
-
corsika_input_file = self.corsika_config.generate_corsika_input_file(
|
|
92
|
-
use_multipipe=self._use_multipipe, use_test_seeds=self._keep_seeds
|
|
64
|
+
self.file_list = self.runner_service.load_files(run_number=run_number)
|
|
65
|
+
|
|
66
|
+
self.corsika_config.generate_corsika_input_file(
|
|
67
|
+
self._use_multipipe,
|
|
68
|
+
self.runner_service.get_file_name("corsika_input", run_number=run_number),
|
|
69
|
+
self.runner_service.get_file_name("corsika_output", run_number=run_number)
|
|
70
|
+
if not self._use_multipipe
|
|
71
|
+
else corsika_file,
|
|
93
72
|
)
|
|
94
73
|
|
|
95
|
-
|
|
96
|
-
corsika_input_tmp_name = self.corsika_config.get_corsika_config_file_name(
|
|
97
|
-
file_type="config_tmp", run_number=self.corsika_config.run_number
|
|
98
|
-
)
|
|
99
|
-
corsika_input_tmp_file = self._directory["inputs"].joinpath(corsika_input_tmp_name)
|
|
100
|
-
# CORSIKA log file naming (temporary and final)
|
|
101
|
-
corsika_log_tmp_file = (
|
|
102
|
-
self._directory["data"]
|
|
103
|
-
.joinpath(f"run{self.corsika_config.run_number:06}")
|
|
104
|
-
.joinpath(f"run{self.corsika_config.run_number}.log")
|
|
105
|
-
)
|
|
106
|
-
corsika_log_file = self.get_file_name(
|
|
107
|
-
file_type="corsika_log", run_number=self.corsika_config.run_number
|
|
108
|
-
)
|
|
74
|
+
self._logger.debug(f"Extra commands to be added to the run script: {extra_commands}")
|
|
109
75
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
self.corsika_config.run_number, corsika_input_tmp_file
|
|
114
|
-
)
|
|
76
|
+
corsika_run_dir = self.runner_service.get_file_name(
|
|
77
|
+
"corsika_output", run_number=run_number
|
|
78
|
+
).parent
|
|
115
79
|
|
|
116
|
-
self.
|
|
117
|
-
self._logger.debug(f"CORSIKA data will be set to {self._directory['data']}")
|
|
80
|
+
self._export_run_script(run_number, sub_script, corsika_run_dir, extra_commands)
|
|
118
81
|
|
|
119
|
-
|
|
82
|
+
def _corsika_executable(self):
|
|
83
|
+
"""Get the CORSIKA executable path."""
|
|
84
|
+
if self.corsika_config.use_curved_atmosphere:
|
|
85
|
+
self._logger.debug("Using curved-atmosphere CORSIKA binary.")
|
|
86
|
+
return Path(settings.config.corsika_exe_curved)
|
|
87
|
+
self._logger.debug("Using flat-atmosphere CORSIKA binary.")
|
|
88
|
+
return Path(settings.config.corsika_exe)
|
|
89
|
+
|
|
90
|
+
def _export_run_script(self, run_number, sub_script, corsika_run_dir, extra_commands):
|
|
91
|
+
"""Export CORSIKA run script."""
|
|
92
|
+
corsika_log_file = self.runner_service.get_file_name(
|
|
93
|
+
"corsika_log", run_number=run_number
|
|
94
|
+
).with_suffix("") # remove .gz from log file
|
|
95
|
+
corsika_input = self.runner_service.get_file_name("corsika_input", run_number=run_number)
|
|
96
|
+
sub_script = Path(sub_script)
|
|
97
|
+
with open(sub_script, "w", encoding="utf-8") as file:
|
|
120
98
|
file.write("#!/usr/bin/env bash\n")
|
|
121
99
|
file.write("set -e\n")
|
|
122
100
|
file.write("set -o pipefail\n")
|
|
@@ -129,136 +107,19 @@ class CorsikaRunner:
|
|
|
129
107
|
file.write(f"{extra_commands}\n")
|
|
130
108
|
file.write("# End of extras\n\n")
|
|
131
109
|
|
|
132
|
-
file.write(f"export CORSIKA_DATA={
|
|
110
|
+
file.write(f"export CORSIKA_DATA={corsika_run_dir}\n")
|
|
133
111
|
file.write('mkdir -p "$CORSIKA_DATA"\n')
|
|
134
112
|
file.write('cd "$CORSIKA_DATA" || exit 2\n')
|
|
135
|
-
if use_pfp:
|
|
136
|
-
file.write("\n# Running pfp\n")
|
|
137
|
-
file.write(pfp_command)
|
|
138
|
-
file.write("\n# Replacing the XXXXXX placeholder with the run number\n")
|
|
139
|
-
file.write(
|
|
140
|
-
f"sed -i 's/XXXXXX/{self.corsika_config.run_number:06}/g' "
|
|
141
|
-
f"{corsika_input_tmp_file}\n"
|
|
142
|
-
)
|
|
143
|
-
else:
|
|
144
|
-
file.write("\n# Copying CORSIKA input file to run location\n")
|
|
145
|
-
file.write(f"cp {corsika_input_file} {corsika_input_tmp_file}")
|
|
146
|
-
file.write("\n# Running corsika_autoinputs\n")
|
|
147
|
-
file.write(autoinputs_command)
|
|
148
|
-
file.write("\n# Moving log files to the corsika log directory\n")
|
|
149
|
-
file.write(f"gzip {corsika_log_tmp_file}\n")
|
|
150
|
-
file.write(f"mv -v {corsika_log_tmp_file}.gz {corsika_log_file}\n")
|
|
151
|
-
|
|
152
|
-
file.write('\necho "RUNTIME: $SECONDS"\n')
|
|
153
|
-
|
|
154
|
-
script_file_path.chmod(script_file_path.stat().st_mode | stat.S_IXUSR | stat.S_IXGRP)
|
|
155
|
-
return script_file_path
|
|
156
113
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
def _get_pfp_command(self, input_tmp_file, corsika_input_file):
|
|
162
|
-
"""
|
|
163
|
-
Get pfp pre-processor command.
|
|
164
|
-
|
|
165
|
-
pfp is a pre-processor tool and part of sim_telarray.
|
|
166
|
-
|
|
167
|
-
Parameters
|
|
168
|
-
----------
|
|
169
|
-
input_tmp_file: Path
|
|
170
|
-
Temporary input file.
|
|
171
|
-
|
|
172
|
-
Returns
|
|
173
|
-
-------
|
|
174
|
-
str
|
|
175
|
-
pfp command.
|
|
176
|
-
"""
|
|
177
|
-
cmd = settings.config.sim_telarray_path / "bin/pfp"
|
|
178
|
-
cmd = str(cmd) + f" -V -DWITHOUT_MULTIPIPE - < {corsika_input_file}"
|
|
179
|
-
cmd += f" > {input_tmp_file} || exit\n"
|
|
180
|
-
return cmd
|
|
181
|
-
|
|
182
|
-
def _get_autoinputs_command(self, run_number, input_tmp_file):
|
|
183
|
-
"""
|
|
184
|
-
Get autoinputs command.
|
|
185
|
-
|
|
186
|
-
corsika_autoinputs is a tool to generate random seeds and user/host dependent
|
|
187
|
-
parameters for CORSIKA configuration.
|
|
188
|
-
|
|
189
|
-
Parameters
|
|
190
|
-
----------
|
|
191
|
-
run_number: int
|
|
192
|
-
Run number.
|
|
193
|
-
input_tmp_file: Path
|
|
194
|
-
Temporary input file.
|
|
195
|
-
|
|
196
|
-
Returns
|
|
197
|
-
-------
|
|
198
|
-
str
|
|
199
|
-
autoinputs command.
|
|
200
|
-
"""
|
|
201
|
-
if self.corsika_config.use_curved_atmosphere:
|
|
202
|
-
corsika_bin_path = settings.config.corsika_exe_curved
|
|
203
|
-
self._logger.debug("Using curved-atmosphere CORSIKA binary.")
|
|
204
|
-
else:
|
|
205
|
-
corsika_bin_path = settings.config.corsika_exe
|
|
206
|
-
self._logger.debug("Using flat-atmosphere CORSIKA binary.")
|
|
207
|
-
|
|
208
|
-
log_file = self.get_file_name(file_type="log", run_number=run_number)
|
|
209
|
-
if self._use_multipipe:
|
|
210
|
-
log_file = log_file.with_name(f"multipipe_{log_file.name}")
|
|
211
|
-
|
|
212
|
-
cmd = settings.config.sim_telarray_path.joinpath("bin/corsika_autoinputs")
|
|
213
|
-
cmd = str(cmd) + f" --run {corsika_bin_path}"
|
|
214
|
-
cmd += f" -R {run_number}"
|
|
215
|
-
cmd += ' -p "$CORSIKA_DATA"'
|
|
216
|
-
if self._keep_seeds:
|
|
217
|
-
logging.warning(
|
|
218
|
-
"Using --keep-seeds option in corsika_autoinputs is not recommended. "
|
|
219
|
-
"It should only be used for testing purposes."
|
|
114
|
+
file.write("\n# Running corsika\n")
|
|
115
|
+
file.write(
|
|
116
|
+
f"{self._corsika_executable()} < {corsika_input} > {corsika_log_file} 2>&1\n"
|
|
220
117
|
)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
cmd += " || exit 1\n"
|
|
224
|
-
return cmd
|
|
118
|
+
file.write("\n# Cleanup\n")
|
|
119
|
+
file.write(f"gzip {corsika_log_file}\n")
|
|
225
120
|
|
|
226
|
-
|
|
227
|
-
self,
|
|
228
|
-
simulation_software="corsika",
|
|
229
|
-
file_type=None,
|
|
230
|
-
run_number=None,
|
|
231
|
-
mode="",
|
|
232
|
-
model_version_index=0,
|
|
233
|
-
):
|
|
234
|
-
"""
|
|
235
|
-
Get the full path of a file for a given run number.
|
|
236
|
-
|
|
237
|
-
Parameters
|
|
238
|
-
----------
|
|
239
|
-
simulation_software: str
|
|
240
|
-
Simulation software.
|
|
241
|
-
file_type: str
|
|
242
|
-
File type.
|
|
243
|
-
run_number: int
|
|
244
|
-
Run number.
|
|
245
|
-
model_version_index: int
|
|
246
|
-
Index of the model version.
|
|
247
|
-
This is used to select the correct simulator_array instance in case
|
|
248
|
-
multiple array models are simulated.
|
|
121
|
+
file.write('\necho "RUNTIME: $SECONDS"\n')
|
|
249
122
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
File name with full path.
|
|
254
|
-
"""
|
|
255
|
-
if simulation_software.lower() != "corsika":
|
|
256
|
-
raise ValueError(
|
|
257
|
-
f"simulation_software ({simulation_software}) is not supported in CorsikaRunner"
|
|
258
|
-
)
|
|
259
|
-
return self.runner_service.get_file_name(
|
|
260
|
-
file_type=file_type,
|
|
261
|
-
run_number=run_number,
|
|
262
|
-
mode=mode,
|
|
263
|
-
_model_version_index=model_version_index,
|
|
264
|
-
)
|
|
123
|
+
def get_resources(self, sub_out_file):
|
|
124
|
+
"""Return computing resources used."""
|
|
125
|
+
return self.runner_service.get_resources(sub_out_file)
|