gammasimtools 0.24.0__py3-none-any.whl → 0.26.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/METADATA +2 -1
- {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/RECORD +134 -130
- {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/entry_points.txt +3 -1
- {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/licenses/LICENSE +1 -1
- simtools/_version.py +2 -2
- simtools/application_control.py +78 -0
- simtools/applications/calculate_incident_angles.py +0 -2
- simtools/applications/convert_geo_coordinates_of_array_elements.py +1 -2
- simtools/applications/db_add_file_to_db.py +1 -1
- simtools/applications/db_add_simulation_model_from_repository_to_db.py +1 -1
- simtools/applications/db_add_value_from_json_to_db.py +1 -1
- simtools/applications/db_generate_compound_indexes.py +1 -1
- simtools/applications/db_get_array_layouts_from_db.py +2 -6
- simtools/applications/db_get_file_from_db.py +1 -1
- simtools/applications/db_get_parameter_from_db.py +1 -1
- simtools/applications/db_inspect_databases.py +1 -1
- simtools/applications/db_upload_model_repository.py +1 -1
- simtools/applications/derive_ctao_array_layouts.py +1 -2
- simtools/applications/derive_mirror_rnda.py +1 -3
- simtools/applications/derive_psf_parameters.py +5 -1
- simtools/applications/derive_pulse_shape_parameters.py +194 -0
- simtools/applications/derive_trigger_rates.py +1 -1
- simtools/applications/docs_produce_array_element_report.py +2 -8
- simtools/applications/docs_produce_calibration_reports.py +1 -3
- simtools/applications/docs_produce_model_parameter_reports.py +0 -2
- simtools/applications/docs_produce_simulation_configuration_report.py +1 -3
- simtools/applications/generate_array_config.py +0 -1
- simtools/applications/generate_corsika_histograms.py +48 -235
- simtools/applications/generate_regular_arrays.py +5 -35
- simtools/applications/generate_simtel_event_data.py +2 -2
- simtools/applications/maintain_simulation_model_add_production.py +2 -2
- simtools/applications/maintain_simulation_model_write_array_element_positions.py +87 -0
- simtools/applications/plot_array_layout.py +64 -108
- 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 -4
- simtools/applications/simulate_illuminator.py +0 -1
- simtools/applications/simulate_pedestals.py +2 -6
- simtools/applications/simulate_prod.py +9 -28
- simtools/applications/simulate_prod_htcondor_generator.py +8 -1
- simtools/applications/submit_array_layouts.py +7 -7
- simtools/applications/submit_model_parameter_from_external.py +1 -3
- simtools/applications/validate_camera_efficiency.py +0 -1
- simtools/applications/validate_camera_fov.py +0 -1
- simtools/applications/validate_cumulative_psf.py +0 -2
- simtools/applications/validate_file_using_schema.py +49 -123
- simtools/applications/validate_optics.py +0 -13
- simtools/camera/camera_efficiency.py +1 -6
- simtools/camera/single_photon_electron_spectrum.py +2 -1
- simtools/configuration/commandline_parser.py +43 -8
- simtools/configuration/configurator.py +6 -11
- simtools/corsika/corsika_config.py +204 -99
- simtools/corsika/corsika_histograms.py +411 -1735
- simtools/corsika/primary_particle.py +1 -1
- simtools/data_model/metadata_collector.py +5 -2
- simtools/data_model/metadata_model.py +0 -4
- simtools/data_model/model_data_writer.py +27 -17
- simtools/data_model/schema.py +112 -5
- simtools/data_model/validate_data.py +80 -48
- simtools/db/db_handler.py +19 -8
- simtools/db/db_model_upload.py +2 -1
- simtools/db/mongo_db.py +133 -42
- simtools/dependencies.py +83 -44
- simtools/io/ascii_handler.py +4 -2
- simtools/io/table_handler.py +1 -1
- simtools/job_execution/htcondor_script_generator.py +0 -2
- simtools/layout/array_layout.py +4 -12
- simtools/layout/array_layout_utils.py +227 -58
- simtools/model/array_model.py +37 -18
- simtools/model/calibration_model.py +0 -4
- simtools/model/legacy_model_parameter.py +134 -0
- simtools/model/model_parameter.py +24 -14
- simtools/model/model_repository.py +18 -5
- simtools/model/model_utils.py +1 -6
- simtools/model/site_model.py +0 -4
- simtools/model/telescope_model.py +6 -11
- simtools/production_configuration/derive_corsika_limits.py +6 -11
- simtools/production_configuration/interpolation_handler.py +16 -16
- simtools/ray_tracing/incident_angles.py +5 -11
- simtools/ray_tracing/mirror_panel_psf.py +3 -7
- simtools/ray_tracing/psf_analysis.py +29 -27
- simtools/ray_tracing/psf_parameter_optimisation.py +822 -680
- simtools/ray_tracing/ray_tracing.py +6 -15
- simtools/reporting/docs_auto_report_generator.py +8 -13
- simtools/reporting/docs_read_parameters.py +70 -16
- simtools/runners/corsika_runner.py +15 -10
- simtools/runners/corsika_simtel_runner.py +9 -8
- simtools/runners/runner_services.py +17 -7
- simtools/runners/simtel_runner.py +11 -58
- simtools/runners/simtools_runner.py +2 -4
- simtools/schemas/model_parameters/flasher_pulse_exp_decay.schema.yml +2 -0
- simtools/schemas/model_parameters/flasher_pulse_shape.schema.yml +50 -0
- simtools/schemas/model_parameters/flasher_pulse_width.schema.yml +2 -0
- simtools/schemas/simulation_models_info.schema.yml +2 -0
- simtools/settings.py +154 -0
- simtools/sim_events/file_info.py +128 -0
- 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 +273 -0
- simtools/simtel/simtel_config_writer.py +146 -22
- simtools/simtel/simtel_table_reader.py +6 -4
- simtools/simtel/simulator_array.py +62 -23
- simtools/simtel/simulator_camera_efficiency.py +4 -6
- simtools/simtel/simulator_light_emission.py +101 -19
- simtools/simtel/simulator_ray_tracing.py +4 -10
- simtools/simulator.py +360 -353
- simtools/telescope_trigger_rates.py +3 -4
- simtools/testing/assertions.py +115 -8
- simtools/testing/configuration.py +2 -3
- simtools/testing/helpers.py +2 -3
- simtools/testing/log_inspector.py +5 -1
- simtools/testing/sim_telarray_metadata.py +1 -1
- simtools/testing/validate_output.py +69 -23
- simtools/utils/general.py +37 -0
- simtools/utils/geometry.py +0 -77
- simtools/utils/names.py +7 -9
- simtools/version.py +37 -0
- simtools/visualization/legend_handlers.py +21 -10
- simtools/visualization/plot_array_layout.py +312 -41
- simtools/visualization/plot_corsika_histograms.py +143 -605
- simtools/visualization/plot_mirrors.py +834 -0
- simtools/visualization/plot_pixels.py +2 -4
- simtools/visualization/plot_psf.py +0 -1
- simtools/visualization/plot_simtel_event_histograms.py +4 -4
- simtools/visualization/plot_simtel_events.py +6 -11
- simtools/visualization/plot_tables.py +8 -19
- simtools/visualization/visualize.py +22 -2
- simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +0 -160
- simtools/applications/print_version.py +0 -53
- simtools/io/hdf5_handler.py +0 -139
- simtools/simtel/simtel_io_file_info.py +0 -62
- {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/WHEEL +0 -0
- {gammasimtools-0.24.0.dist-info → gammasimtools-0.26.0.dist-info}/top_level.txt +0 -0
|
@@ -6,13 +6,12 @@ from pathlib import Path
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
from astropy import units as u
|
|
8
8
|
|
|
9
|
+
from simtools import settings
|
|
9
10
|
from simtools.corsika.primary_particle import PrimaryParticle
|
|
10
11
|
from simtools.io import io_handler
|
|
11
12
|
from simtools.model.model_parameter import ModelParameter
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class InvalidCorsikaInputError(Exception):
|
|
15
|
-
"""Exception for invalid corsika input."""
|
|
13
|
+
from simtools.sim_events import file_info
|
|
14
|
+
from simtools.utils import general as gen
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
class CorsikaConfig:
|
|
@@ -30,8 +29,6 @@ class CorsikaConfig:
|
|
|
30
29
|
Array model.
|
|
31
30
|
args_dict : dict
|
|
32
31
|
Configuration dictionary.
|
|
33
|
-
db_config : dict
|
|
34
|
-
MongoDB configuration.
|
|
35
32
|
label : str
|
|
36
33
|
Instance label.
|
|
37
34
|
dummy_simulations : bool
|
|
@@ -39,66 +36,91 @@ class CorsikaConfig:
|
|
|
39
36
|
(e.g., sim_telarray requires for some run modes a valid CORSIKA input file).
|
|
40
37
|
"""
|
|
41
38
|
|
|
42
|
-
def __init__(self, array_model, args_dict,
|
|
39
|
+
def __init__(self, array_model, args_dict, label=None, dummy_simulations=False):
|
|
43
40
|
"""Initialize CorsikaConfig."""
|
|
44
41
|
self._logger = logging.getLogger(__name__)
|
|
45
42
|
self._logger.debug("Init CorsikaConfig")
|
|
46
43
|
|
|
47
44
|
self.label = label
|
|
48
|
-
self.
|
|
49
|
-
self.azimuth_angle = None
|
|
45
|
+
self.shower_events = self.mc_events = None
|
|
46
|
+
self.zenith_angle = self.azimuth_angle = None
|
|
47
|
+
self.curved_atmosphere_min_zenith_angle = None
|
|
50
48
|
self._run_number = None
|
|
51
49
|
self.config_file_path = None
|
|
52
50
|
self.primary_particle = args_dict # see setter for primary_particle
|
|
51
|
+
self.use_curved_atmosphere = args_dict # see setter for use_curved_atmosphere
|
|
53
52
|
self.dummy_simulations = dummy_simulations
|
|
54
53
|
|
|
55
54
|
self.io_handler = io_handler.IOHandler()
|
|
56
55
|
self.array_model = array_model
|
|
57
|
-
self.config = self.
|
|
56
|
+
self.config = self._fill_corsika_configuration(args_dict)
|
|
57
|
+
self._initialize_from_config(args_dict)
|
|
58
58
|
self.is_file_updated = False
|
|
59
59
|
|
|
60
|
-
def __repr__(self):
|
|
61
|
-
"""CorsikaConfig class representation."""
|
|
62
|
-
return (
|
|
63
|
-
f"<class {self.__class__.__name__}> "
|
|
64
|
-
f"(site={self.array_model.site}, "
|
|
65
|
-
f"layout={self.array_model.layout_name}, label={self.label})"
|
|
66
|
-
)
|
|
67
|
-
|
|
68
60
|
@property
|
|
69
61
|
def primary_particle(self):
|
|
70
62
|
"""Primary particle."""
|
|
71
63
|
return self._primary_particle
|
|
72
64
|
|
|
73
65
|
@primary_particle.setter
|
|
74
|
-
def primary_particle(self,
|
|
66
|
+
def primary_particle(self, args):
|
|
75
67
|
"""
|
|
76
|
-
Set primary particle from input dictionary.
|
|
68
|
+
Set primary particle from input dictionary or CORSIKA 7 particle ID.
|
|
77
69
|
|
|
78
70
|
This is to make sure that when setting the primary particle,
|
|
79
71
|
we get the full PrimaryParticle object expected.
|
|
80
72
|
|
|
81
73
|
Parameters
|
|
82
74
|
----------
|
|
83
|
-
|
|
75
|
+
args: dict, corsika particle ID, or None
|
|
84
76
|
Configuration dictionary
|
|
85
77
|
"""
|
|
86
|
-
|
|
78
|
+
if (
|
|
79
|
+
isinstance(args, dict)
|
|
80
|
+
and args.get("primary_id_type") is not None
|
|
81
|
+
and args.get("primary") is not None
|
|
82
|
+
):
|
|
83
|
+
self._primary_particle = PrimaryParticle(
|
|
84
|
+
particle_id_type=args.get("primary_id_type"), particle_id=args.get("primary")
|
|
85
|
+
)
|
|
86
|
+
elif isinstance(args, int):
|
|
87
|
+
self._primary_particle = PrimaryParticle(
|
|
88
|
+
particle_id_type="corsika7_id", particle_id=args
|
|
89
|
+
)
|
|
90
|
+
else:
|
|
91
|
+
self._primary_particle = PrimaryParticle()
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def use_curved_atmosphere(self):
|
|
95
|
+
"""Check if zenith angle condition for curved atmosphere usage for CORSIKA is met."""
|
|
96
|
+
return self._use_curved_atmosphere
|
|
97
|
+
|
|
98
|
+
@use_curved_atmosphere.setter
|
|
99
|
+
def use_curved_atmosphere(self, args):
|
|
100
|
+
"""Check if zenith angle condition for curved atmosphere usage for CORSIKA is met."""
|
|
101
|
+
self._use_curved_atmosphere = False
|
|
102
|
+
if isinstance(args, bool):
|
|
103
|
+
self._use_curved_atmosphere = args
|
|
104
|
+
elif isinstance(args, dict):
|
|
105
|
+
try:
|
|
106
|
+
self._use_curved_atmosphere = (
|
|
107
|
+
args.get("zenith_angle", 0.0 * u.deg).to("deg").value
|
|
108
|
+
> args["curved_atmosphere_min_zenith_angle"].to("deg").value
|
|
109
|
+
)
|
|
110
|
+
except KeyError:
|
|
111
|
+
self._use_curved_atmosphere = False
|
|
87
112
|
|
|
88
|
-
def
|
|
113
|
+
def _fill_corsika_configuration(self, args_dict):
|
|
89
114
|
"""
|
|
90
115
|
Fill CORSIKA configuration.
|
|
91
116
|
|
|
92
|
-
Dictionary keys are CORSIKA parameter names.
|
|
93
|
-
|
|
94
|
-
|
|
117
|
+
Dictionary keys are CORSIKA parameter names. Values are converted to
|
|
118
|
+
CORSIKA-consistent units.
|
|
95
119
|
|
|
96
120
|
Parameters
|
|
97
121
|
----------
|
|
98
122
|
args_dict : dict
|
|
99
123
|
Configuration dictionary.
|
|
100
|
-
db_config: dict
|
|
101
|
-
Database configuration.
|
|
102
124
|
|
|
103
125
|
Returns
|
|
104
126
|
-------
|
|
@@ -108,32 +130,36 @@ class CorsikaConfig:
|
|
|
108
130
|
if args_dict is None:
|
|
109
131
|
return {}
|
|
110
132
|
|
|
111
|
-
self.is_file_updated = False
|
|
112
|
-
self.azimuth_angle = int(args_dict.get("azimuth_angle", 0.0 * u.deg).to("deg").value)
|
|
113
|
-
self.zenith_angle = int(args_dict.get("zenith_angle", 0.0 * u.deg).to("deg").value)
|
|
114
|
-
|
|
115
|
-
self._logger.debug(
|
|
116
|
-
f"Setting CORSIKA parameters from database ({args_dict['model_version']})"
|
|
117
|
-
)
|
|
118
|
-
|
|
119
133
|
config = {}
|
|
120
134
|
if self.dummy_simulations:
|
|
121
|
-
config["USER_INPUT"] = self._corsika_configuration_for_dummy_simulations()
|
|
135
|
+
config["USER_INPUT"] = self._corsika_configuration_for_dummy_simulations(args_dict)
|
|
136
|
+
elif args_dict.get("corsika_file", None) is not None:
|
|
137
|
+
config["USER_INPUT"] = self._corsika_configuration_from_corsika_file(
|
|
138
|
+
args_dict["corsika_file"]
|
|
139
|
+
)
|
|
122
140
|
else:
|
|
123
141
|
config["USER_INPUT"] = self._corsika_configuration_from_user_input(args_dict)
|
|
124
142
|
|
|
125
|
-
|
|
143
|
+
config.update(
|
|
144
|
+
self._fill_corsika_configuration_from_db(
|
|
145
|
+
gen.ensure_iterable(args_dict.get("model_version"))
|
|
146
|
+
)
|
|
147
|
+
)
|
|
148
|
+
return config
|
|
149
|
+
|
|
150
|
+
def _fill_corsika_configuration_from_db(self, model_versions):
|
|
151
|
+
"""Fill CORSIKA configuration from database."""
|
|
152
|
+
config = {}
|
|
153
|
+
# all following parameters require DB
|
|
154
|
+
if settings.config.db_config is None or not model_versions:
|
|
126
155
|
return config
|
|
127
156
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
model_versions = args_dict.get("model_version", None)
|
|
131
|
-
if not isinstance(model_versions, list):
|
|
132
|
-
model_versions = [model_versions]
|
|
133
|
-
self.assert_corsika_configurations_match(model_versions, db_config=db_config)
|
|
157
|
+
# For multiple model versions, check that CORSIKA parameters are identical
|
|
158
|
+
self.assert_corsika_configurations_match(model_versions)
|
|
134
159
|
model_version = model_versions[0]
|
|
135
|
-
|
|
136
|
-
|
|
160
|
+
|
|
161
|
+
self._logger.debug(f"Using model version {model_version} for CORSIKA parameters from DB")
|
|
162
|
+
db_model_parameters = ModelParameter(model_version=model_version)
|
|
137
163
|
parameters_from_db = db_model_parameters.get_simulation_software_parameters("corsika")
|
|
138
164
|
|
|
139
165
|
config["INTERACTION_FLAGS"] = self._corsika_configuration_interaction_flags(
|
|
@@ -144,10 +170,42 @@ class CorsikaConfig:
|
|
|
144
170
|
)
|
|
145
171
|
config["DEBUGGING_OUTPUT_PARAMETERS"] = self._corsika_configuration_debugging_parameters()
|
|
146
172
|
config["IACT_PARAMETERS"] = self._corsika_configuration_iact_parameters(parameters_from_db)
|
|
147
|
-
|
|
148
173
|
return config
|
|
149
174
|
|
|
150
|
-
def
|
|
175
|
+
def _initialize_from_config(self, args_dict):
|
|
176
|
+
"""
|
|
177
|
+
Initialize additional parameters either from command line args or from derived config.
|
|
178
|
+
|
|
179
|
+
Takes into account that in the case of a given CORSIKA input file, some parameters are read
|
|
180
|
+
from the file instead of the command line args.
|
|
181
|
+
|
|
182
|
+
"""
|
|
183
|
+
self.primary_particle = int(self.config.get("USER_INPUT", {}).get("PRMPAR", [1])[0])
|
|
184
|
+
self.shower_events = int(self.config.get("USER_INPUT", {}).get("NSHOW", [0])[0])
|
|
185
|
+
self.mc_events = int(
|
|
186
|
+
self.shower_events * self.config.get("USER_INPUT", {}).get("CSCAT", [1])[0]
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
if args_dict.get("corsika_file", None) is not None:
|
|
190
|
+
azimuth = self._rotate_azimuth_by_180deg(
|
|
191
|
+
0.5 * (self.config["USER_INPUT"]["PHIP"][0] + self.config["USER_INPUT"]["PHIP"][1]),
|
|
192
|
+
invert_operation=True,
|
|
193
|
+
)
|
|
194
|
+
zenith = 0.5 * (
|
|
195
|
+
self.config["USER_INPUT"]["THETAP"][0] + self.config["USER_INPUT"]["THETAP"][1]
|
|
196
|
+
)
|
|
197
|
+
else:
|
|
198
|
+
azimuth = args_dict.get("azimuth_angle", 0.0 * u.deg).to("deg").value
|
|
199
|
+
zenith = args_dict.get("zenith_angle", 20.0 * u.deg).to("deg").value
|
|
200
|
+
|
|
201
|
+
self.azimuth_angle = round(azimuth)
|
|
202
|
+
self.zenith_angle = round(zenith)
|
|
203
|
+
|
|
204
|
+
self.curved_atmosphere_min_zenith_angle = (
|
|
205
|
+
args_dict.get("curved_atmosphere_min_zenith_angle", 90.0 * u.deg).to("deg").value
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
def assert_corsika_configurations_match(self, model_versions):
|
|
151
209
|
"""
|
|
152
210
|
Assert that CORSIKA configurations match across all model versions.
|
|
153
211
|
|
|
@@ -155,8 +213,6 @@ class CorsikaConfig:
|
|
|
155
213
|
----------
|
|
156
214
|
model_versions : list
|
|
157
215
|
List of model versions to check.
|
|
158
|
-
db_config : dict, optional
|
|
159
|
-
Database configuration.
|
|
160
216
|
|
|
161
217
|
Raises
|
|
162
218
|
------
|
|
@@ -170,7 +226,7 @@ class CorsikaConfig:
|
|
|
170
226
|
|
|
171
227
|
# Get parameters for all model versions
|
|
172
228
|
for model_version in model_versions:
|
|
173
|
-
db_model_parameters = ModelParameter(
|
|
229
|
+
db_model_parameters = ModelParameter(model_version=model_version)
|
|
174
230
|
parameters_from_db_list.append(
|
|
175
231
|
db_model_parameters.get_simulation_software_parameters("corsika")
|
|
176
232
|
)
|
|
@@ -193,13 +249,13 @@ class CorsikaConfig:
|
|
|
193
249
|
f" {model_versions[i]}: {current_value}\n"
|
|
194
250
|
f" {model_versions[i + 1]}: {next_value}"
|
|
195
251
|
)
|
|
196
|
-
raise
|
|
252
|
+
raise ValueError(
|
|
197
253
|
f"CORSIKA parameter '{key}' differs between model versions "
|
|
198
254
|
f"{model_versions[i]} and {model_versions[i + 1]}. "
|
|
199
255
|
f"Values are {current_value} and {next_value} respectively."
|
|
200
256
|
)
|
|
201
257
|
|
|
202
|
-
def _corsika_configuration_for_dummy_simulations(self):
|
|
258
|
+
def _corsika_configuration_for_dummy_simulations(self, args_dict):
|
|
203
259
|
"""
|
|
204
260
|
Return CORSIKA configuration for dummy simulations.
|
|
205
261
|
|
|
@@ -211,18 +267,73 @@ class CorsikaConfig:
|
|
|
211
267
|
dict
|
|
212
268
|
Dictionary with CORSIKA parameters for dummy simulations.
|
|
213
269
|
"""
|
|
270
|
+
theta, phi = self._get_corsika_theta_phi(args_dict)
|
|
214
271
|
return {
|
|
215
272
|
"EVTNR": [1],
|
|
216
273
|
"NSHOW": [1],
|
|
217
274
|
"PRMPAR": [1], # CORSIKA ID 1 for primary gamma
|
|
218
275
|
"ESLOPE": [-2.0],
|
|
219
276
|
"ERANGE": [0.1, 0.1],
|
|
220
|
-
"THETAP": [
|
|
221
|
-
"PHIP": [
|
|
277
|
+
"THETAP": [theta, theta],
|
|
278
|
+
"PHIP": [phi, phi],
|
|
222
279
|
"VIEWCONE": [0.0, 0.0],
|
|
223
280
|
"CSCAT": [1, 0.0, 10.0],
|
|
224
281
|
}
|
|
225
282
|
|
|
283
|
+
def _corsika_configuration_from_corsika_file(self, corsika_input_file):
|
|
284
|
+
"""
|
|
285
|
+
Get CORSIKA configuration run header of provided input files.
|
|
286
|
+
|
|
287
|
+
Reads configuration from the run and event headers from the CORSIKA input file
|
|
288
|
+
(unfortunately quite fine tuned to the pycorsikaio run and event
|
|
289
|
+
header implementation).
|
|
290
|
+
|
|
291
|
+
Parameters
|
|
292
|
+
----------
|
|
293
|
+
corsika_input_file : str, path
|
|
294
|
+
Path to the CORSIKA input file.
|
|
295
|
+
|
|
296
|
+
Returns
|
|
297
|
+
-------
|
|
298
|
+
dict
|
|
299
|
+
Dictionary with CORSIKA parameters from input file.
|
|
300
|
+
"""
|
|
301
|
+
run_header, event_header = file_info.get_corsika_run_and_event_headers(corsika_input_file)
|
|
302
|
+
self._logger.debug(f"CORSIKA run header from {corsika_input_file}")
|
|
303
|
+
|
|
304
|
+
def to_float32(value):
|
|
305
|
+
"""Convert value to numpy float32."""
|
|
306
|
+
return np.float32(value) if value is not None else 0.0
|
|
307
|
+
|
|
308
|
+
def to_int32(value):
|
|
309
|
+
"""Convert value to numpy int32."""
|
|
310
|
+
return np.int32(value) if value is not None else 0
|
|
311
|
+
|
|
312
|
+
if run_header["n_observation_levels"] > 0:
|
|
313
|
+
self._check_altitude_and_site(run_header["observation_height"][0])
|
|
314
|
+
|
|
315
|
+
return {
|
|
316
|
+
"EVTNR": [to_int32(event_header["event_number"])],
|
|
317
|
+
"NSHOW": [to_int32(run_header["n_showers"])],
|
|
318
|
+
"PRMPAR": [to_int32(event_header["particle_id"])],
|
|
319
|
+
"ESLOPE": [to_float32(run_header["energy_spectrum_slope"])],
|
|
320
|
+
"ERANGE": [to_float32(run_header["energy_min"]), to_float32(run_header["energy_max"])],
|
|
321
|
+
"THETAP": [
|
|
322
|
+
to_float32(event_header["theta_min"]),
|
|
323
|
+
to_float32(event_header["theta_max"]),
|
|
324
|
+
],
|
|
325
|
+
"PHIP": [to_float32(event_header["phi_min"]), to_float32(event_header["phi_max"])],
|
|
326
|
+
"VIEWCONE": [
|
|
327
|
+
to_float32(event_header["viewcone_inner_angle"]),
|
|
328
|
+
to_float32(event_header["viewcone_outer_angle"]),
|
|
329
|
+
],
|
|
330
|
+
"CSCAT": [
|
|
331
|
+
to_int32(event_header["n_reuse"]),
|
|
332
|
+
to_float32(event_header["reuse_x"]),
|
|
333
|
+
to_float32(event_header["reuse_y"]),
|
|
334
|
+
],
|
|
335
|
+
}
|
|
336
|
+
|
|
226
337
|
def _corsika_configuration_from_user_input(self, args_dict):
|
|
227
338
|
"""
|
|
228
339
|
Get CORSIKA configuration from user input.
|
|
@@ -237,6 +348,7 @@ class CorsikaConfig:
|
|
|
237
348
|
dict
|
|
238
349
|
Dictionary with CORSIKA parameters.
|
|
239
350
|
"""
|
|
351
|
+
theta, phi = self._get_corsika_theta_phi(args_dict)
|
|
240
352
|
return {
|
|
241
353
|
"EVTNR": [args_dict["event_number_first_shower"]],
|
|
242
354
|
"NSHOW": [args_dict["nshow"]],
|
|
@@ -246,24 +358,8 @@ class CorsikaConfig:
|
|
|
246
358
|
args_dict["energy_range"][0].to("GeV").value,
|
|
247
359
|
args_dict["energy_range"][1].to("GeV").value,
|
|
248
360
|
],
|
|
249
|
-
"THETAP": [
|
|
250
|
-
|
|
251
|
-
float(args_dict["zenith_angle"].to("deg").value),
|
|
252
|
-
],
|
|
253
|
-
"PHIP": [
|
|
254
|
-
self._rotate_azimuth_by_180deg(
|
|
255
|
-
args_dict["azimuth_angle"].to("deg").value,
|
|
256
|
-
correct_for_geomagnetic_field_alignment=args_dict[
|
|
257
|
-
"correct_for_b_field_alignment"
|
|
258
|
-
],
|
|
259
|
-
),
|
|
260
|
-
self._rotate_azimuth_by_180deg(
|
|
261
|
-
args_dict["azimuth_angle"].to("deg").value,
|
|
262
|
-
correct_for_geomagnetic_field_alignment=args_dict[
|
|
263
|
-
"correct_for_b_field_alignment"
|
|
264
|
-
],
|
|
265
|
-
),
|
|
266
|
-
],
|
|
361
|
+
"THETAP": [theta, theta],
|
|
362
|
+
"PHIP": [phi, phi],
|
|
267
363
|
"VIEWCONE": [
|
|
268
364
|
args_dict["view_cone"][0].to("deg").value,
|
|
269
365
|
args_dict["view_cone"][1].to("deg").value,
|
|
@@ -275,6 +371,26 @@ class CorsikaConfig:
|
|
|
275
371
|
],
|
|
276
372
|
}
|
|
277
373
|
|
|
374
|
+
def _check_altitude_and_site(self, observation_height):
|
|
375
|
+
"""Check that observation height from CORSIKA file matches site model."""
|
|
376
|
+
site_altitude = self.array_model.site_model.get_parameter_value("corsika_observation_level")
|
|
377
|
+
if not np.isclose(observation_height / 1.0e2, site_altitude, atol=1.0):
|
|
378
|
+
raise ValueError(
|
|
379
|
+
"Observatory altitude does not match CORSIKA file observation height: "
|
|
380
|
+
f"{site_altitude} m (site model) != {observation_height / 1.0e2} m (CORSIKA file)"
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
def _get_corsika_theta_phi(self, args_dict):
|
|
384
|
+
"""Get CORSIKA theta and phi angles from args_dict."""
|
|
385
|
+
theta = args_dict.get("zenith_angle", 20.0 * u.deg).to("deg").value
|
|
386
|
+
phi = self._rotate_azimuth_by_180deg(
|
|
387
|
+
args_dict.get("azimuth_angle", 0.0 * u.deg).to("deg").value,
|
|
388
|
+
correct_for_geomagnetic_field_alignment=args_dict.get(
|
|
389
|
+
"correct_for_b_field_alignment", True
|
|
390
|
+
),
|
|
391
|
+
)
|
|
392
|
+
return theta, phi
|
|
393
|
+
|
|
278
394
|
def _corsika_configuration_interaction_flags(self, parameters_from_db):
|
|
279
395
|
"""
|
|
280
396
|
Return CORSIKA interaction flags / parameters.
|
|
@@ -298,7 +414,8 @@ class CorsikaConfig:
|
|
|
298
414
|
parameters_from_db["corsika_starting_grammage"]
|
|
299
415
|
)
|
|
300
416
|
]
|
|
301
|
-
|
|
417
|
+
if not self.use_curved_atmosphere:
|
|
418
|
+
parameters["TSTART"] = ["T"]
|
|
302
419
|
parameters["ECUTS"] = self._input_config_corsika_particle_kinetic_energy_cutoff(
|
|
303
420
|
parameters_from_db["corsika_particle_kinetic_energy_cutoff"]
|
|
304
421
|
)
|
|
@@ -444,16 +561,23 @@ class CorsikaConfig:
|
|
|
444
561
|
return f"{int(value)}MB"
|
|
445
562
|
return f"{int(entry['value'] * u.Unit(entry['unit']).to('byte'))}"
|
|
446
563
|
|
|
447
|
-
def _rotate_azimuth_by_180deg(
|
|
564
|
+
def _rotate_azimuth_by_180deg(
|
|
565
|
+
self, az, correct_for_geomagnetic_field_alignment=True, invert_operation=False
|
|
566
|
+
):
|
|
448
567
|
"""
|
|
449
568
|
Convert azimuth angle to the CORSIKA coordinate system.
|
|
450
569
|
|
|
570
|
+
Corresponds to a rotation by 180 degrees, and optionally a correction for the
|
|
571
|
+
for the differences between the geographic and geomagnetic north pole.
|
|
572
|
+
|
|
451
573
|
Parameters
|
|
452
574
|
----------
|
|
453
575
|
az: float
|
|
454
576
|
Azimuth angle in degrees.
|
|
455
577
|
correct_for_geomagnetic_field_alignment: bool
|
|
456
578
|
Whether to correct for the geomagnetic field alignment.
|
|
579
|
+
invert_operation: bool
|
|
580
|
+
Whether to invert the operation (i.e., convert from CORSIKA to geographic system).
|
|
457
581
|
|
|
458
582
|
Returns
|
|
459
583
|
-------
|
|
@@ -463,6 +587,8 @@ class CorsikaConfig:
|
|
|
463
587
|
b_field_declination = 0
|
|
464
588
|
if correct_for_geomagnetic_field_alignment:
|
|
465
589
|
b_field_declination = self.array_model.site_model.get_parameter_value("geomag_rotation")
|
|
590
|
+
if invert_operation:
|
|
591
|
+
return (az - 180 - b_field_declination) % 360
|
|
466
592
|
return (az + 180 + b_field_declination) % 360
|
|
467
593
|
|
|
468
594
|
@property
|
|
@@ -470,27 +596,6 @@ class CorsikaConfig:
|
|
|
470
596
|
"""Primary particle name."""
|
|
471
597
|
return self.primary_particle.name
|
|
472
598
|
|
|
473
|
-
def _set_primary_particle(self, args_dict):
|
|
474
|
-
"""
|
|
475
|
-
Set primary particle from input dictionary.
|
|
476
|
-
|
|
477
|
-
Parameters
|
|
478
|
-
----------
|
|
479
|
-
args_dict: dict
|
|
480
|
-
Input dictionary.
|
|
481
|
-
|
|
482
|
-
Returns
|
|
483
|
-
-------
|
|
484
|
-
PrimaryParticle
|
|
485
|
-
Primary particle.
|
|
486
|
-
|
|
487
|
-
"""
|
|
488
|
-
if not args_dict or args_dict.get("primary_id_type") is None:
|
|
489
|
-
return PrimaryParticle()
|
|
490
|
-
return PrimaryParticle(
|
|
491
|
-
particle_id_type=args_dict.get("primary_id_type"), particle_id=args_dict.get("primary")
|
|
492
|
-
)
|
|
493
|
-
|
|
494
599
|
def get_config_parameter(self, par_name):
|
|
495
600
|
"""
|
|
496
601
|
Get value of CORSIKA configuration parameter.
|
|
@@ -511,7 +616,7 @@ class CorsikaConfig:
|
|
|
511
616
|
Value(s) of the parameter.
|
|
512
617
|
"""
|
|
513
618
|
par_value = []
|
|
514
|
-
for
|
|
619
|
+
for values in self.config.values():
|
|
515
620
|
if par_name in values:
|
|
516
621
|
par_value = values[par_name]
|
|
517
622
|
if len(par_value) == 0:
|
|
@@ -674,7 +779,7 @@ class CorsikaConfig:
|
|
|
674
779
|
|
|
675
780
|
base_name = (
|
|
676
781
|
f"{self.primary_particle.name}_{run_number_in_file_name}"
|
|
677
|
-
f"za{int(self.get_config_parameter('THETAP')[0]):
|
|
782
|
+
f"za{int(self.get_config_parameter('THETAP')[0]):02}deg_"
|
|
678
783
|
f"azm{self.azimuth_angle:03}deg{view_cone}_"
|
|
679
784
|
f"{self.array_model.site}_{self.array_model.layout_name}_"
|
|
680
785
|
f"{self.array_model.model_version}{file_label}"
|
|
@@ -685,7 +790,7 @@ class CorsikaConfig:
|
|
|
685
790
|
if file_type == "config":
|
|
686
791
|
return f"corsika_config_{base_name}.input"
|
|
687
792
|
if file_type == "output_generic":
|
|
688
|
-
return f"{base_name}.zst"
|
|
793
|
+
return f"{base_name}.corsika.zst"
|
|
689
794
|
if file_type == "multipipe":
|
|
690
795
|
return f"multi_cta-{self.array_model.site}-{self.array_model.layout_name}.cfg"
|
|
691
796
|
|