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
|
@@ -8,6 +8,7 @@ from pathlib import Path
|
|
|
8
8
|
import astropy.units as u
|
|
9
9
|
|
|
10
10
|
import simtools.version
|
|
11
|
+
from simtools import constants
|
|
11
12
|
from simtools.utils import names
|
|
12
13
|
|
|
13
14
|
|
|
@@ -289,6 +290,13 @@ class CommandLineParser(argparse.ArgumentParser):
|
|
|
289
290
|
required="--list_available_layouts" not in self._option_string_actions,
|
|
290
291
|
)
|
|
291
292
|
|
|
293
|
+
_job_group.add_argument(
|
|
294
|
+
"--ignore_missing_design_model",
|
|
295
|
+
help="Ignore missing design model definition of DB",
|
|
296
|
+
action="store_true",
|
|
297
|
+
required=False,
|
|
298
|
+
)
|
|
299
|
+
|
|
292
300
|
def initialize_simulation_configuration_arguments(self, simulation_configuration):
|
|
293
301
|
"""
|
|
294
302
|
Initialize default arguments for simulation configuration and simulation software.
|
|
@@ -450,20 +458,34 @@ class CommandLineParser(argparse.ArgumentParser):
|
|
|
450
458
|
def _get_dictionary_with_sim_telarray_configuration():
|
|
451
459
|
"""Return dictionary with sim_telarray configuration parameters."""
|
|
452
460
|
return {
|
|
453
|
-
"
|
|
454
|
-
"help":
|
|
455
|
-
|
|
456
|
-
"If '--sim_telarray_random_instrument_instances' is not set: "
|
|
457
|
-
"use as sim_telarray seed ('random_seed' parameter). Otherwise: "
|
|
458
|
-
"use as base seed to generate the random instrument instance seeds."
|
|
459
|
-
),
|
|
460
|
-
"type": str,
|
|
461
|
+
"sim_telarray_instrument_seed": {
|
|
462
|
+
"help": "Random seed used for sim_telarray instrument setup.",
|
|
463
|
+
"type": CommandLineParser.bounded_int(1, constants.SIMTEL_MAX_SEED),
|
|
461
464
|
"required": False,
|
|
462
465
|
},
|
|
463
466
|
"sim_telarray_random_instrument_instances": {
|
|
464
467
|
"help": "Number of random instrument instances initialized in sim_telarray.",
|
|
465
|
-
"type":
|
|
468
|
+
"type": CommandLineParser.bounded_int(1, 1024),
|
|
466
469
|
"required": False,
|
|
470
|
+
"default": 1,
|
|
471
|
+
},
|
|
472
|
+
"sim_telarray_seed": {
|
|
473
|
+
"help": (
|
|
474
|
+
"Random seed used for sim_telarray simulation. "
|
|
475
|
+
"Single value: seed for event simulation. "
|
|
476
|
+
"Two values: [instrument_seed, simulation_seed] (use for testing only)."
|
|
477
|
+
),
|
|
478
|
+
"type": CommandLineParser.bounded_int(1, constants.SIMTEL_MAX_SEED),
|
|
479
|
+
"nargs": "+",
|
|
480
|
+
"required": False,
|
|
481
|
+
},
|
|
482
|
+
# hidden argument to specify the sim_telarray seeds file name
|
|
483
|
+
# (defined it here for convenience)
|
|
484
|
+
"sim_telarray_seed_file": {
|
|
485
|
+
"help": argparse.SUPPRESS,
|
|
486
|
+
"type": str,
|
|
487
|
+
"required": False,
|
|
488
|
+
"default": "sim_telarray_instrument_seeds.txt",
|
|
467
489
|
},
|
|
468
490
|
}
|
|
469
491
|
|
|
@@ -838,6 +860,22 @@ class CommandLineParser(argparse.ArgumentParser):
|
|
|
838
860
|
|
|
839
861
|
return (int(match.group(1)), u.Quantity(float(match.group(2)), match.group(3)))
|
|
840
862
|
|
|
863
|
+
@staticmethod
|
|
864
|
+
def bounded_int(min_value, max_value):
|
|
865
|
+
"""Argument parser type to check that an integer is within a given interval."""
|
|
866
|
+
|
|
867
|
+
def bounded_int_type(value):
|
|
868
|
+
try:
|
|
869
|
+
int_value = int(value)
|
|
870
|
+
except ValueError as exc:
|
|
871
|
+
raise ValueError(f"expected an integer in [{min_value},{max_value}]") from exc
|
|
872
|
+
|
|
873
|
+
if min_value <= int_value <= max_value:
|
|
874
|
+
return int_value
|
|
875
|
+
raise ValueError(f"{int_value} not in [{min_value},{max_value}]")
|
|
876
|
+
|
|
877
|
+
return bounded_int_type
|
|
878
|
+
|
|
841
879
|
|
|
842
880
|
class BuildInfoAction(argparse.Action):
|
|
843
881
|
"""Custom argparse action to display build information."""
|
simtools/constants.py
CHANGED
|
@@ -23,3 +23,8 @@ MODEL_PARAMETER_SCHEMA_URL = (
|
|
|
23
23
|
)
|
|
24
24
|
# Path to resource files
|
|
25
25
|
RESOURCE_PATH = files("simtools") / "resources"
|
|
26
|
+
|
|
27
|
+
# Maximum value allowed for random seeds in sim_telarray
|
|
28
|
+
SIMTEL_MAX_SEED = 2147483647
|
|
29
|
+
# Maximum value allowed for random seeds in CORSIKA
|
|
30
|
+
CORSIKA_MAX_SEED = 900000000
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
"""CORSIKA configuration."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from collections.abc import Mapping
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
import numpy as np
|
|
7
8
|
from astropy import units as u
|
|
8
9
|
|
|
9
10
|
from simtools import settings
|
|
11
|
+
from simtools.constants import CORSIKA_MAX_SEED
|
|
10
12
|
from simtools.corsika.primary_particle import PrimaryParticle
|
|
11
13
|
from simtools.io import io_handler
|
|
12
14
|
from simtools.model.model_parameter import ModelParameter
|
|
13
15
|
from simtools.sim_events import file_info
|
|
14
16
|
from simtools.utils import general as gen
|
|
17
|
+
from simtools.utils.random import seeds
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
class CorsikaConfig:
|
|
@@ -27,35 +30,31 @@ class CorsikaConfig:
|
|
|
27
30
|
----------
|
|
28
31
|
array_model : ArrayModel
|
|
29
32
|
Array model.
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
run_number : int
|
|
34
|
+
Run number.
|
|
32
35
|
label : str
|
|
33
36
|
Instance label.
|
|
34
|
-
dummy_simulations : bool
|
|
35
|
-
If True, the configuration is generated for dummy simulations
|
|
36
|
-
(e.g., sim_telarray requires for some run modes a valid CORSIKA input file).
|
|
37
37
|
"""
|
|
38
38
|
|
|
39
|
-
def __init__(self, array_model,
|
|
39
|
+
def __init__(self, array_model, run_number, label=None):
|
|
40
40
|
"""Initialize CorsikaConfig."""
|
|
41
41
|
self._logger = logging.getLogger(__name__)
|
|
42
|
-
self._logger.debug("Init CorsikaConfig")
|
|
43
42
|
|
|
44
43
|
self.label = label
|
|
45
44
|
self.shower_events = self.mc_events = None
|
|
46
45
|
self.zenith_angle = self.azimuth_angle = None
|
|
47
46
|
self.curved_atmosphere_min_zenith_angle = None
|
|
48
|
-
self.
|
|
49
|
-
self.
|
|
50
|
-
self.
|
|
51
|
-
self.
|
|
52
|
-
self.dummy_simulations = dummy_simulations
|
|
47
|
+
self.run_number = run_number
|
|
48
|
+
self.primary_particle = settings.config.args # see setter for primary_particle
|
|
49
|
+
self.use_curved_atmosphere = settings.config.args # see setter for use_curved_atmosphere
|
|
50
|
+
self.run_mode = settings.config.args.get("run_mode")
|
|
53
51
|
|
|
54
52
|
self.io_handler = io_handler.IOHandler()
|
|
55
53
|
self.array_model = array_model
|
|
56
|
-
self.
|
|
57
|
-
self.
|
|
58
|
-
self.
|
|
54
|
+
self.corsika_exec = settings.config.corsika_exe
|
|
55
|
+
self.interaction_table_path = settings.config.corsika_interaction_table_path
|
|
56
|
+
self.config = self._fill_corsika_configuration(settings.config.args)
|
|
57
|
+
self._initialize_from_config(settings.config.args)
|
|
59
58
|
|
|
60
59
|
@property
|
|
61
60
|
def primary_particle(self):
|
|
@@ -76,7 +75,7 @@ class CorsikaConfig:
|
|
|
76
75
|
Configuration dictionary
|
|
77
76
|
"""
|
|
78
77
|
if (
|
|
79
|
-
isinstance(args, dict)
|
|
78
|
+
isinstance(args, Mapping) # dict-like (includes mappingproxy)
|
|
80
79
|
and args.get("primary_id_type") is not None
|
|
81
80
|
and args.get("primary") is not None
|
|
82
81
|
):
|
|
@@ -101,7 +100,7 @@ class CorsikaConfig:
|
|
|
101
100
|
self._use_curved_atmosphere = False
|
|
102
101
|
if isinstance(args, bool):
|
|
103
102
|
self._use_curved_atmosphere = args
|
|
104
|
-
elif isinstance(args, dict)
|
|
103
|
+
elif isinstance(args, Mapping): # dict-like (includes mappingproxy)
|
|
105
104
|
try:
|
|
106
105
|
self._use_curved_atmosphere = (
|
|
107
106
|
args.get("zenith_angle", 0.0 * u.deg).to("deg").value
|
|
@@ -110,7 +109,7 @@ class CorsikaConfig:
|
|
|
110
109
|
except KeyError:
|
|
111
110
|
self._use_curved_atmosphere = False
|
|
112
111
|
|
|
113
|
-
def _fill_corsika_configuration(self,
|
|
112
|
+
def _fill_corsika_configuration(self, args):
|
|
114
113
|
"""
|
|
115
114
|
Fill CORSIKA configuration.
|
|
116
115
|
|
|
@@ -119,7 +118,7 @@ class CorsikaConfig:
|
|
|
119
118
|
|
|
120
119
|
Parameters
|
|
121
120
|
----------
|
|
122
|
-
|
|
121
|
+
args: dict
|
|
123
122
|
Configuration dictionary.
|
|
124
123
|
|
|
125
124
|
Returns
|
|
@@ -127,23 +126,24 @@ class CorsikaConfig:
|
|
|
127
126
|
dict
|
|
128
127
|
Dictionary with CORSIKA parameters.
|
|
129
128
|
"""
|
|
130
|
-
if
|
|
129
|
+
if args is None:
|
|
131
130
|
return {}
|
|
132
131
|
|
|
133
132
|
config = {}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
133
|
+
config["RUNNR"] = [self.run_number]
|
|
134
|
+
config["USER"] = [settings.config.user]
|
|
135
|
+
config["HOST"] = [settings.config.hostname]
|
|
136
|
+
if self.is_calibration_run():
|
|
137
|
+
config["USER_INPUT"] = self._corsika_configuration_for_dummy_simulations(args)
|
|
138
|
+
elif args.get("corsika_file", None) is not None:
|
|
137
139
|
config["USER_INPUT"] = self._corsika_configuration_from_corsika_file(
|
|
138
|
-
|
|
140
|
+
args["corsika_file"]
|
|
139
141
|
)
|
|
140
142
|
else:
|
|
141
|
-
config["USER_INPUT"] = self._corsika_configuration_from_user_input(
|
|
143
|
+
config["USER_INPUT"] = self._corsika_configuration_from_user_input(args)
|
|
142
144
|
|
|
143
145
|
config.update(
|
|
144
|
-
self._fill_corsika_configuration_from_db(
|
|
145
|
-
gen.ensure_iterable(args_dict.get("model_version"))
|
|
146
|
-
)
|
|
146
|
+
self._fill_corsika_configuration_from_db(gen.ensure_iterable(args.get("model_version")))
|
|
147
147
|
)
|
|
148
148
|
return config
|
|
149
149
|
|
|
@@ -172,13 +172,17 @@ class CorsikaConfig:
|
|
|
172
172
|
config["IACT_PARAMETERS"] = self._corsika_configuration_iact_parameters(parameters_from_db)
|
|
173
173
|
return config
|
|
174
174
|
|
|
175
|
-
def _initialize_from_config(self,
|
|
175
|
+
def _initialize_from_config(self, args):
|
|
176
176
|
"""
|
|
177
177
|
Initialize additional parameters either from command line args or from derived config.
|
|
178
178
|
|
|
179
179
|
Takes into account that in the case of a given CORSIKA input file, some parameters are read
|
|
180
180
|
from the file instead of the command line args.
|
|
181
181
|
|
|
182
|
+
Parameters
|
|
183
|
+
----------
|
|
184
|
+
args: dict
|
|
185
|
+
Command line arguments.
|
|
182
186
|
"""
|
|
183
187
|
self.primary_particle = int(self.config.get("USER_INPUT", {}).get("PRMPAR", [1])[0])
|
|
184
188
|
self.shower_events = int(self.config.get("USER_INPUT", {}).get("NSHOW", [0])[0])
|
|
@@ -186,7 +190,7 @@ class CorsikaConfig:
|
|
|
186
190
|
self.shower_events * self.config.get("USER_INPUT", {}).get("CSCAT", [1])[0]
|
|
187
191
|
)
|
|
188
192
|
|
|
189
|
-
if
|
|
193
|
+
if args.get("corsika_file", None) is not None:
|
|
190
194
|
azimuth = self._rotate_azimuth_by_180deg(
|
|
191
195
|
0.5 * (self.config["USER_INPUT"]["PHIP"][0] + self.config["USER_INPUT"]["PHIP"][1]),
|
|
192
196
|
invert_operation=True,
|
|
@@ -195,14 +199,14 @@ class CorsikaConfig:
|
|
|
195
199
|
self.config["USER_INPUT"]["THETAP"][0] + self.config["USER_INPUT"]["THETAP"][1]
|
|
196
200
|
)
|
|
197
201
|
else:
|
|
198
|
-
azimuth =
|
|
199
|
-
zenith =
|
|
202
|
+
azimuth = args.get("azimuth_angle", 0.0 * u.deg).to("deg").value
|
|
203
|
+
zenith = args.get("zenith_angle", 20.0 * u.deg).to("deg").value
|
|
200
204
|
|
|
201
205
|
self.azimuth_angle = round(azimuth)
|
|
202
206
|
self.zenith_angle = round(zenith)
|
|
203
207
|
|
|
204
208
|
self.curved_atmosphere_min_zenith_angle = (
|
|
205
|
-
|
|
209
|
+
args.get("curved_atmosphere_min_zenith_angle", 90.0 * u.deg).to("deg").value
|
|
206
210
|
)
|
|
207
211
|
|
|
208
212
|
def assert_corsika_configurations_match(self, model_versions):
|
|
@@ -261,6 +265,7 @@ class CorsikaConfig:
|
|
|
261
265
|
|
|
262
266
|
Settings are such that that the simulations are fast
|
|
263
267
|
and none (or not many) Cherenkov photons are generated.
|
|
268
|
+
This is e.g. used for some calibration run modes in sim_telarray.
|
|
264
269
|
|
|
265
270
|
Returns
|
|
266
271
|
-------
|
|
@@ -268,6 +273,7 @@ class CorsikaConfig:
|
|
|
268
273
|
Dictionary with CORSIKA parameters for dummy simulations.
|
|
269
274
|
"""
|
|
270
275
|
theta, phi = self._get_corsika_theta_phi(args_dict)
|
|
276
|
+
self._logger.info("Using CORSIKA configuration for dummy simulations.")
|
|
271
277
|
return {
|
|
272
278
|
"EVTNR": [1],
|
|
273
279
|
"NSHOW": [1],
|
|
@@ -427,9 +433,25 @@ class CorsikaConfig:
|
|
|
427
433
|
parameters["MAXPRT"] = ["10"]
|
|
428
434
|
parameters["ECTMAP"] = ["1.e6"]
|
|
429
435
|
|
|
436
|
+
if "epos" in str(self.corsika_exec).lower():
|
|
437
|
+
parameters.update(self._epos_flags())
|
|
438
|
+
|
|
430
439
|
self._logger.debug(f"Interaction parameters: {parameters}")
|
|
431
440
|
return parameters
|
|
432
441
|
|
|
442
|
+
def _epos_flags(self):
|
|
443
|
+
"""EPOS interaction model flags."""
|
|
444
|
+
epos_par = {}
|
|
445
|
+
epos_path = Path(self.interaction_table_path)
|
|
446
|
+
epos_par["EPOPAR fname pathnx"] = [f"{epos_path}/"]
|
|
447
|
+
for epos_file in ["inics", "iniev", "inirj", "initl"]:
|
|
448
|
+
epos_par[f"EPOPAR fname {epos_file}"] = [str(epos_path / f"epos.{epos_file}")]
|
|
449
|
+
epos_par["EPOPAR fname hpf"] = [str(epos_path / "urqmd34/tables.dat")]
|
|
450
|
+
for dummy_output in ["check", "histo", "data", "copy"]:
|
|
451
|
+
epos_par[f"EPOPAR fname {dummy_output}"] = ["none"]
|
|
452
|
+
|
|
453
|
+
return epos_par
|
|
454
|
+
|
|
433
455
|
def _input_config_first_interaction_height(self, entry):
|
|
434
456
|
"""Return FIXHEI parameter CORSIKA format."""
|
|
435
457
|
return [f"{entry['value'] * u.Unit(entry['unit']).to('cm'):.2f}", "0"]
|
|
@@ -548,7 +570,7 @@ class CorsikaConfig:
|
|
|
548
570
|
"""Return CORSIKA debugging output parameters."""
|
|
549
571
|
return {
|
|
550
572
|
"DEBUG": ["F", 6, "F", 1000000],
|
|
551
|
-
"DATBAS": ["
|
|
573
|
+
"DATBAS": ["F"],
|
|
552
574
|
"DIRECT": ["./"],
|
|
553
575
|
"PAROUT": ["F", "F"],
|
|
554
576
|
}
|
|
@@ -591,11 +613,6 @@ class CorsikaConfig:
|
|
|
591
613
|
return (az - 180 - b_field_declination) % 360
|
|
592
614
|
return (az + 180 + b_field_declination) % 360
|
|
593
615
|
|
|
594
|
-
@property
|
|
595
|
-
def primary(self):
|
|
596
|
-
"""Primary particle name."""
|
|
597
|
-
return self.primary_particle.name
|
|
598
|
-
|
|
599
616
|
def get_config_parameter(self, par_name):
|
|
600
617
|
"""
|
|
601
618
|
Get value of CORSIKA configuration parameter.
|
|
@@ -647,7 +664,7 @@ class CorsikaConfig:
|
|
|
647
664
|
text += line
|
|
648
665
|
return text
|
|
649
666
|
|
|
650
|
-
def generate_corsika_input_file(self, use_multipipe
|
|
667
|
+
def generate_corsika_input_file(self, use_multipipe, input_file, output_file):
|
|
651
668
|
"""
|
|
652
669
|
Generate a CORSIKA input file.
|
|
653
670
|
|
|
@@ -656,16 +673,14 @@ class CorsikaConfig:
|
|
|
656
673
|
use_multipipe: bool
|
|
657
674
|
Whether to set the CORSIKA Inputs file to pipe
|
|
658
675
|
the output directly to sim_telarray.
|
|
659
|
-
|
|
676
|
+
input_file: Path
|
|
677
|
+
Path to the input file to be generated.
|
|
678
|
+
output_file: Path
|
|
679
|
+
Path to the output file to be generated.
|
|
660
680
|
"""
|
|
661
|
-
|
|
662
|
-
self._logger.debug(f"CORSIKA input file already updated: {self.config_file_path}")
|
|
663
|
-
return self.config_file_path
|
|
664
|
-
self._logger.info(f"Exporting CORSIKA input file to {self.config_file_path}")
|
|
665
|
-
_output_generic_file_name = self.set_output_file_and_directory(use_multipipe=use_multipipe)
|
|
666
|
-
self._logger.info(f"Output generic file name: {_output_generic_file_name}")
|
|
681
|
+
self._logger.info(f"Exporting CORSIKA input file to {input_file}")
|
|
667
682
|
|
|
668
|
-
with open(
|
|
683
|
+
with open(input_file, "w", encoding="utf-8") as file:
|
|
669
684
|
file.write("\n* [ RUN PARAMETERS ]\n")
|
|
670
685
|
text_parameters = self._get_text_single_line(self.config["USER_INPUT"])
|
|
671
686
|
file.write(text_parameters)
|
|
@@ -684,7 +699,7 @@ class CorsikaConfig:
|
|
|
684
699
|
file.write(f"IACT setenv AZM {self.azimuth_angle}\n")
|
|
685
700
|
|
|
686
701
|
file.write("\n* [ SEEDS ]\n")
|
|
687
|
-
self._write_seeds(file
|
|
702
|
+
self._write_seeds(file)
|
|
688
703
|
|
|
689
704
|
file.write("\n* [ TELESCOPES ]\n")
|
|
690
705
|
telescope_list_text = self.get_corsika_telescope_list()
|
|
@@ -693,6 +708,7 @@ class CorsikaConfig:
|
|
|
693
708
|
file.write("\n* [ INTERACTION FLAGS ]\n")
|
|
694
709
|
text_interaction_flags = self._get_text_single_line(self.config["INTERACTION_FLAGS"])
|
|
695
710
|
file.write(text_interaction_flags)
|
|
711
|
+
file.write(f"DATDIR {self.interaction_table_path}\n")
|
|
696
712
|
|
|
697
713
|
file.write("\n* [ CHERENKOV EMISSION PARAMETERS ]\n")
|
|
698
714
|
text_cherenkov = self._get_text_single_line(
|
|
@@ -706,10 +722,9 @@ class CorsikaConfig:
|
|
|
706
722
|
|
|
707
723
|
file.write("\n* [ OUTPUT FILE ]\n")
|
|
708
724
|
if use_multipipe:
|
|
709
|
-
|
|
710
|
-
file.write(f"TELFIL |{run_cta_script!s}\n")
|
|
725
|
+
file.write(f"TELFIL |{output_file!s}\n")
|
|
711
726
|
else:
|
|
712
|
-
file.write(f"TELFIL {
|
|
727
|
+
file.write(f"TELFIL {output_file.name}\n")
|
|
713
728
|
|
|
714
729
|
file.write("\n* [ IACT TUNING PARAMETERS ]\n")
|
|
715
730
|
text_iact = self._get_text_single_line(
|
|
@@ -726,111 +741,22 @@ class CorsikaConfig:
|
|
|
726
741
|
model_directory=self.array_model.get_config_directory()
|
|
727
742
|
)
|
|
728
743
|
|
|
729
|
-
|
|
730
|
-
return self.config_file_path
|
|
731
|
-
|
|
732
|
-
def get_corsika_config_file_name(self, file_type, run_number=None):
|
|
733
|
-
"""
|
|
734
|
-
Get a CORSIKA config style file name for various configuration file types.
|
|
735
|
-
|
|
736
|
-
Parameters
|
|
737
|
-
----------
|
|
738
|
-
file_type: str
|
|
739
|
-
The type of file (determines the file suffix).
|
|
740
|
-
Choices are config_tmp, config or output_generic.
|
|
741
|
-
run_number: int
|
|
742
|
-
Run number.
|
|
743
|
-
|
|
744
|
-
Returns
|
|
745
|
-
-------
|
|
746
|
-
str
|
|
747
|
-
for file_type="config_tmp":
|
|
748
|
-
Return CORSIKA input file name for one specific run.
|
|
749
|
-
This is the input file after being pre-processed by sim_telarray (pfp).
|
|
750
|
-
for file_type="config":
|
|
751
|
-
Return generic CORSIKA config input file name.
|
|
752
|
-
for file_type="output_generic"
|
|
753
|
-
Return generic file name for the TELFIL option in the CORSIKA inputs file.
|
|
754
|
-
for file_type="multipipe"
|
|
755
|
-
Return multipipe "file name" for the TELFIL option in the CORSIKA inputs file.
|
|
756
|
-
|
|
757
|
-
Raises
|
|
758
|
-
------
|
|
759
|
-
ValueError
|
|
760
|
-
If file_type is unknown or if the run number is not given for file_type==config_tmp.
|
|
761
|
-
"""
|
|
762
|
-
if file_type == "config_tmp" and run_number is None:
|
|
763
|
-
raise ValueError("Must provide a run number for a temporary CORSIKA config file")
|
|
764
|
-
|
|
765
|
-
file_label = f"_{self.label}" if self.label is not None else ""
|
|
766
|
-
|
|
767
|
-
_vc_low = self.get_config_parameter("VIEWCONE")[0]
|
|
768
|
-
_vc_high = self.get_config_parameter("VIEWCONE")[1]
|
|
769
|
-
view_cone = (
|
|
770
|
-
f"_cone{int(_vc_low):d}-{int(_vc_high):d}" if _vc_low != 0 or _vc_high != 0 else ""
|
|
771
|
-
)
|
|
772
|
-
|
|
773
|
-
run_number_in_file_name = ""
|
|
774
|
-
if file_type == "output_generic":
|
|
775
|
-
# The XXXXXX will be replaced by the run number after the pfp step with sed
|
|
776
|
-
run_number_in_file_name = "runXXXXXX_"
|
|
777
|
-
if file_type == "config_tmp":
|
|
778
|
-
run_number_in_file_name = f"run{run_number:06}_"
|
|
779
|
-
|
|
780
|
-
base_name = (
|
|
781
|
-
f"{self.primary_particle.name}_{run_number_in_file_name}"
|
|
782
|
-
f"za{int(self.get_config_parameter('THETAP')[0]):02}deg_"
|
|
783
|
-
f"azm{self.azimuth_angle:03}deg{view_cone}_"
|
|
784
|
-
f"{self.array_model.site}_{self.array_model.layout_name}_"
|
|
785
|
-
f"{self.array_model.model_version}{file_label}"
|
|
786
|
-
)
|
|
787
|
-
|
|
788
|
-
if file_type == "config_tmp":
|
|
789
|
-
return f"corsika_config_{base_name}.txt"
|
|
790
|
-
if file_type == "config":
|
|
791
|
-
return f"corsika_config_{base_name}.input"
|
|
792
|
-
if file_type == "output_generic":
|
|
793
|
-
return f"{base_name}.corsika.zst"
|
|
794
|
-
if file_type == "multipipe":
|
|
795
|
-
return f"multi_cta-{self.array_model.site}-{self.array_model.layout_name}.cfg"
|
|
796
|
-
|
|
797
|
-
raise ValueError(f"The requested file type ({file_type}) is unknown")
|
|
798
|
-
|
|
799
|
-
def set_output_file_and_directory(self, use_multipipe=False):
|
|
800
|
-
"""
|
|
801
|
-
Set output file names and directories.
|
|
802
|
-
|
|
803
|
-
Parameters
|
|
804
|
-
----------
|
|
805
|
-
use_multipipe: bool
|
|
806
|
-
Whether to set the CORSIKA Inputs file to pipe
|
|
807
|
-
the output directly to sim_telarray. Defines directory names.
|
|
808
|
-
|
|
809
|
-
Returns
|
|
810
|
-
-------
|
|
811
|
-
str
|
|
812
|
-
Output file name.
|
|
813
|
-
"""
|
|
814
|
-
self.config_file_path = self.io_handler.get_output_file(
|
|
815
|
-
file_name=self.get_corsika_config_file_name(file_type="config"),
|
|
816
|
-
sub_dir="corsika_sim_telarray" if use_multipipe else "corsika",
|
|
817
|
-
)
|
|
818
|
-
return self.get_corsika_config_file_name(file_type="output_generic")
|
|
819
|
-
|
|
820
|
-
def _write_seeds(self, file, use_test_seeds=False):
|
|
744
|
+
def _write_seeds(self, file):
|
|
821
745
|
"""
|
|
822
746
|
Generate and write seeds in the CORSIKA input file.
|
|
823
747
|
|
|
748
|
+
CORSIKA seeds consist of 4 integers.
|
|
749
|
+
|
|
824
750
|
Parameters
|
|
825
751
|
----------
|
|
826
752
|
file: stream
|
|
827
753
|
File where the telescope positions will be written.
|
|
828
754
|
"""
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
if
|
|
833
|
-
|
|
755
|
+
corsika_seeds = settings.config.args.get("corsika_seeds", False)
|
|
756
|
+
if not corsika_seeds:
|
|
757
|
+
corsika_seeds = seeds(n_seeds=4, max_seed=CORSIKA_MAX_SEED)
|
|
758
|
+
if len(corsika_seeds) != 4:
|
|
759
|
+
raise ValueError("Exactly 4 CORSIKA seeds must be provided.")
|
|
834
760
|
for s in corsika_seeds:
|
|
835
761
|
file.write(f"SEED {s} 0 0\n")
|
|
836
762
|
|
|
@@ -857,46 +783,23 @@ class CorsikaConfig:
|
|
|
857
783
|
|
|
858
784
|
return corsika_input_list
|
|
859
785
|
|
|
860
|
-
|
|
861
|
-
def run_number(self):
|
|
862
|
-
"""Set run number."""
|
|
863
|
-
return self._run_number
|
|
864
|
-
|
|
865
|
-
@run_number.setter
|
|
866
|
-
def run_number(self, run_number):
|
|
867
|
-
"""
|
|
868
|
-
Set run number and validate it.
|
|
869
|
-
|
|
870
|
-
Parameters
|
|
871
|
-
----------
|
|
872
|
-
run_number: int
|
|
873
|
-
Run number.
|
|
874
|
-
"""
|
|
875
|
-
self._run_number = self.validate_run_number(run_number)
|
|
876
|
-
|
|
877
|
-
def validate_run_number(self, run_number):
|
|
786
|
+
def is_calibration_run(self):
|
|
878
787
|
"""
|
|
879
|
-
|
|
788
|
+
Check if this simulation is a calibration run.
|
|
880
789
|
|
|
881
790
|
Parameters
|
|
882
791
|
----------
|
|
883
|
-
|
|
884
|
-
Run
|
|
792
|
+
run_mode: str
|
|
793
|
+
Run mode of the simulation.
|
|
885
794
|
|
|
886
795
|
Returns
|
|
887
796
|
-------
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
return self.run_number
|
|
898
|
-
if not float(run_number).is_integer() or run_number < 1 or run_number > 999999:
|
|
899
|
-
raise ValueError(
|
|
900
|
-
f"Invalid type of run number ({run_number}) - it must be an uint < 1000000."
|
|
901
|
-
)
|
|
902
|
-
return run_number
|
|
797
|
+
bool
|
|
798
|
+
True if it is a calibration run, False otherwise.
|
|
799
|
+
"""
|
|
800
|
+
return self.run_mode in [
|
|
801
|
+
"pedestals",
|
|
802
|
+
"pedestals_dark",
|
|
803
|
+
"pedestals_nsb_only",
|
|
804
|
+
"direct_injection",
|
|
805
|
+
]
|