gammasimtools 0.9.0__py3-none-any.whl → 0.11.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.9.0.dist-info → gammasimtools-0.11.0.dist-info}/METADATA +4 -2
- {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/RECORD +133 -117
- {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/WHEEL +1 -1
- {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/entry_points.txt +6 -1
- simtools/_version.py +9 -4
- simtools/applications/calculate_trigger_rate.py +15 -38
- simtools/applications/convert_all_model_parameters_from_simtel.py +9 -29
- simtools/applications/convert_geo_coordinates_of_array_elements.py +47 -45
- simtools/applications/convert_model_parameter_from_simtel.py +2 -3
- simtools/applications/db_add_file_to_db.py +1 -3
- simtools/applications/db_add_simulation_model_from_repository_to_db.py +110 -0
- simtools/applications/db_add_value_from_json_to_db.py +1 -2
- simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +6 -6
- simtools/applications/db_get_file_from_db.py +11 -12
- simtools/applications/db_get_parameter_from_db.py +26 -35
- simtools/applications/derive_mirror_rnda.py +1 -2
- simtools/applications/derive_photon_electron_spectrum.py +99 -0
- simtools/applications/derive_psf_parameters.py +1 -0
- simtools/applications/docs_produce_array_element_report.py +71 -0
- simtools/applications/docs_produce_model_parameter_reports.py +63 -0
- simtools/applications/generate_array_config.py +17 -17
- simtools/applications/generate_corsika_histograms.py +2 -2
- simtools/applications/generate_regular_arrays.py +19 -17
- simtools/applications/generate_simtel_array_histograms.py +11 -48
- simtools/applications/production_derive_limits.py +95 -0
- simtools/applications/production_generate_simulation_config.py +37 -33
- simtools/applications/production_scale_events.py +4 -9
- simtools/applications/run_application.py +165 -0
- simtools/applications/simulate_light_emission.py +0 -4
- simtools/applications/simulate_prod.py +1 -1
- simtools/applications/simulate_prod_htcondor_generator.py +26 -26
- simtools/applications/submit_data_from_external.py +12 -4
- simtools/applications/submit_model_parameter_from_external.py +18 -11
- simtools/applications/validate_camera_efficiency.py +2 -2
- simtools/applications/validate_file_using_schema.py +26 -22
- simtools/camera/single_photon_electron_spectrum.py +168 -0
- simtools/configuration/commandline_parser.py +37 -1
- simtools/configuration/configurator.py +8 -10
- simtools/constants.py +10 -3
- simtools/corsika/corsika_config.py +19 -17
- simtools/corsika/corsika_histograms.py +5 -7
- simtools/corsika/corsika_histograms_visualize.py +2 -4
- simtools/data_model/data_reader.py +0 -3
- simtools/data_model/metadata_collector.py +20 -12
- simtools/data_model/metadata_model.py +8 -124
- simtools/data_model/model_data_writer.py +81 -75
- simtools/data_model/schema.py +220 -0
- simtools/data_model/validate_data.py +79 -68
- simtools/db/db_handler.py +350 -492
- simtools/db/db_model_upload.py +139 -0
- simtools/dependencies.py +112 -0
- simtools/io_operations/hdf5_handler.py +54 -24
- simtools/layout/array_layout.py +38 -32
- simtools/model/array_model.py +13 -7
- simtools/model/model_parameter.py +55 -54
- simtools/model/site_model.py +2 -2
- simtools/production_configuration/calculate_statistical_errors_grid_point.py +119 -145
- simtools/production_configuration/event_scaler.py +9 -35
- simtools/production_configuration/generate_simulation_config.py +9 -44
- simtools/production_configuration/interpolation_handler.py +9 -15
- simtools/production_configuration/limits_calculation.py +202 -0
- simtools/reporting/docs_read_parameters.py +310 -0
- simtools/runners/corsika_simtel_runner.py +4 -4
- simtools/schemas/{integration_tests_config.metaschema.yml → application_workflow.metaschema.yml} +61 -27
- simtools/schemas/array_elements.yml +8 -0
- simtools/schemas/input/MST_mirror_2f_measurements.schema.yml +39 -0
- simtools/schemas/input/single_pe_spectrum.schema.yml +38 -0
- simtools/schemas/model_parameter.metaschema.yml +103 -2
- simtools/schemas/model_parameter_and_data_schema.metaschema.yml +4 -1
- simtools/schemas/model_parameters/array_element_position_utm.schema.yml +1 -1
- simtools/schemas/model_parameters/array_window.schema.yml +37 -0
- simtools/schemas/model_parameters/asum_clipping.schema.yml +0 -4
- simtools/schemas/model_parameters/channels_per_chip.schema.yml +1 -1
- simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +2 -0
- simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +2 -0
- simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +2 -0
- simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +4 -2
- simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +2 -0
- simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +2 -0
- simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +2 -0
- simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +2 -0
- simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +2 -0
- simtools/schemas/model_parameters/dsum_clipping.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_offset.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_pedsub.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_prescale.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_presum_max.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_shaping.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_threshold.schema.yml +0 -2
- simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +0 -2
- simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_noise.schema.yml +3 -3
- simtools/schemas/model_parameters/fake_mirror_list.schema.yml +33 -0
- simtools/schemas/model_parameters/iobuf_maximum.schema.yml +1 -1
- simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +1 -1
- simtools/schemas/model_parameters/laser_photons.schema.yml +2 -2
- simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +1 -1
- simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +1 -1
- simtools/schemas/model_parameters/min_photoelectrons.schema.yml +1 -1
- simtools/schemas/model_parameters/min_photons.schema.yml +1 -1
- simtools/schemas/model_parameters/random_generator.schema.yml +1 -1
- simtools/schemas/model_parameters/sampled_output.schema.yml +1 -1
- simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +1 -1
- simtools/schemas/model_parameters/store_photoelectrons.schema.yml +1 -1
- simtools/schemas/model_parameters/tailcut_scale.schema.yml +1 -1
- simtools/schemas/production_configuration_metrics.schema.yml +68 -0
- simtools/schemas/production_tables.schema.yml +41 -0
- simtools/simtel/simtel_config_reader.py +1 -2
- simtools/simtel/simtel_config_writer.py +6 -8
- simtools/simtel/simtel_io_histogram.py +32 -68
- simtools/simtel/simtel_io_histograms.py +17 -34
- simtools/simtel/simulator_array.py +2 -1
- simtools/simtel/simulator_camera_efficiency.py +6 -3
- simtools/simtel/simulator_light_emission.py +5 -6
- simtools/simtel/simulator_ray_tracing.py +3 -4
- simtools/testing/configuration.py +2 -1
- simtools/testing/helpers.py +6 -13
- simtools/testing/validate_output.py +141 -47
- simtools/utils/general.py +114 -14
- simtools/utils/names.py +299 -157
- simtools/utils/value_conversion.py +17 -13
- simtools/version.py +2 -2
- simtools/visualization/legend_handlers.py +2 -0
- simtools/applications/db_add_model_parameters_from_repository_to_db.py +0 -176
- simtools/db/db_array_elements.py +0 -130
- {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/LICENSE +0 -0
- {gammasimtools-0.9.0.dist-info → gammasimtools-0.11.0.dist-info}/top_level.txt +0 -0
- /simtools/{camera_efficiency.py → camera/camera_efficiency.py} +0 -0
|
@@ -36,12 +36,10 @@ import logging
|
|
|
36
36
|
import re
|
|
37
37
|
from pathlib import Path
|
|
38
38
|
|
|
39
|
-
import jsonschema
|
|
40
|
-
|
|
41
39
|
import simtools.utils.general as gen
|
|
42
40
|
from simtools.configuration import configurator
|
|
43
41
|
from simtools.constants import MODEL_PARAMETER_SCHEMA_PATH
|
|
44
|
-
from simtools.data_model import metadata_collector,
|
|
42
|
+
from simtools.data_model import metadata_collector, schema, validate_data
|
|
45
43
|
|
|
46
44
|
|
|
47
45
|
def _parse(label, description):
|
|
@@ -116,7 +114,20 @@ def _get_schema_file_name(args_dict, data_dict=None):
|
|
|
116
114
|
return schema_file
|
|
117
115
|
|
|
118
116
|
|
|
119
|
-
def
|
|
117
|
+
def _get_json_file_list(file_directory=None, file_name=None):
|
|
118
|
+
"""Return list of json files in a directory."""
|
|
119
|
+
file_list = []
|
|
120
|
+
if file_directory is not None:
|
|
121
|
+
file_list = list(Path(file_directory).rglob("*.json"))
|
|
122
|
+
if not file_list:
|
|
123
|
+
raise FileNotFoundError(f"No files found in {file_directory}")
|
|
124
|
+
elif file_name is not None:
|
|
125
|
+
file_list = [file_name]
|
|
126
|
+
|
|
127
|
+
return file_list
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def validate_dict_using_schema(args_dict, logger):
|
|
120
131
|
"""
|
|
121
132
|
Validate a schema file (or several files) given in yaml or json format.
|
|
122
133
|
|
|
@@ -124,33 +135,26 @@ def validate_schema(args_dict, logger):
|
|
|
124
135
|
the metadata section of the data dictionary.
|
|
125
136
|
|
|
126
137
|
"""
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
file_list = [args_dict["file_name"]]
|
|
131
|
-
for file_name in file_list:
|
|
138
|
+
for file_name in _get_json_file_list(
|
|
139
|
+
args_dict.get("file_directory"), args_dict.get("file_name")
|
|
140
|
+
):
|
|
132
141
|
try:
|
|
133
142
|
data = gen.collect_data_from_file(file_name=file_name)
|
|
134
143
|
except FileNotFoundError as exc:
|
|
135
144
|
logger.error(f"Error reading schema file from {file_name}")
|
|
136
145
|
raise exc
|
|
137
|
-
|
|
138
|
-
metadata_model.validate_schema(data, _get_schema_file_name(args_dict, data))
|
|
139
|
-
except jsonschema.exceptions.ValidationError as exc:
|
|
140
|
-
logger.error(f"Failed validation of file {file_name}")
|
|
141
|
-
raise exc
|
|
146
|
+
schema.validate_dict_using_schema(data, _get_schema_file_name(args_dict, data))
|
|
142
147
|
logger.info(f"Successful validation of file {file_name}")
|
|
143
148
|
|
|
144
149
|
|
|
145
150
|
def validate_data_files(args_dict, logger):
|
|
146
151
|
"""Validate data files."""
|
|
147
|
-
|
|
148
|
-
if file_directory is not None:
|
|
152
|
+
if args_dict.get("file_directory") is not None:
|
|
149
153
|
tmp_args_dict = {}
|
|
150
|
-
for file_name in
|
|
154
|
+
for file_name in _get_json_file_list(args_dict.get("file_directory")):
|
|
151
155
|
tmp_args_dict["file_name"] = file_name
|
|
152
156
|
parameter_name = re.sub(r"-\d+\.\d+\.\d+", "", file_name.stem)
|
|
153
|
-
schema_file =
|
|
157
|
+
schema_file = schema.get_model_parameter_schema_file(f"{parameter_name}")
|
|
154
158
|
tmp_args_dict["schema"] = schema_file
|
|
155
159
|
tmp_args_dict["data_type"] = "model_parameter"
|
|
156
160
|
tmp_args_dict["require_exact_data_type"] = args_dict["require_exact_data_type"]
|
|
@@ -166,9 +170,9 @@ def validate_data_file(args_dict, logger):
|
|
|
166
170
|
data_file=args_dict["file_name"],
|
|
167
171
|
check_exact_data_type=args_dict["require_exact_data_type"],
|
|
168
172
|
)
|
|
169
|
-
data_validator.validate_and_transform(
|
|
170
|
-
|
|
171
|
-
|
|
173
|
+
data_validator.validate_and_transform(
|
|
174
|
+
is_model_parameter=(args_dict["data_type"].lower() == "model_parameter")
|
|
175
|
+
)
|
|
172
176
|
|
|
173
177
|
logger.info(f"Successful validation of data file {args_dict['file_name']}")
|
|
174
178
|
|
|
@@ -192,7 +196,7 @@ def main(): # noqa: D103
|
|
|
192
196
|
if args_dict["data_type"].lower() == "metadata":
|
|
193
197
|
validate_metadata(args_dict, logger)
|
|
194
198
|
elif args_dict["data_type"].lower() == "schema":
|
|
195
|
-
|
|
199
|
+
validate_dict_using_schema(args_dict, logger)
|
|
196
200
|
else:
|
|
197
201
|
validate_data_files(args_dict, logger)
|
|
198
202
|
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""Single photon electron spectral analysis."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import re
|
|
5
|
+
import subprocess
|
|
6
|
+
import tempfile
|
|
7
|
+
from io import BytesIO
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from astropy.table import Table
|
|
11
|
+
|
|
12
|
+
import simtools.data_model.model_data_writer as writer
|
|
13
|
+
from simtools.constants import SCHEMA_PATH
|
|
14
|
+
from simtools.data_model import validate_data
|
|
15
|
+
from simtools.data_model.metadata_collector import MetadataCollector
|
|
16
|
+
from simtools.io_operations import io_handler
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SinglePhotonElectronSpectrum:
|
|
20
|
+
"""
|
|
21
|
+
Single photon electron spectral analysis.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
args_dict: dict
|
|
26
|
+
Dictionary with input arguments.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
prompt_column = "frequency (prompt)"
|
|
30
|
+
prompt_plus_afterpulse_column = "frequency (prompt+afterpulsing)"
|
|
31
|
+
afterpulse_column = "frequency (afterpulsing)"
|
|
32
|
+
|
|
33
|
+
input_schema = SCHEMA_PATH / "input" / "single_pe_spectrum.schema.yml"
|
|
34
|
+
|
|
35
|
+
def __init__(self, args_dict):
|
|
36
|
+
"""Initialize SinglePhotonElectronSpectrum class."""
|
|
37
|
+
self._logger = logging.getLogger(__name__)
|
|
38
|
+
self._logger.debug("Initialize SinglePhotonElectronSpectrum class.")
|
|
39
|
+
|
|
40
|
+
self.args_dict = args_dict
|
|
41
|
+
# default output is of ecsv format
|
|
42
|
+
self.args_dict["output_file"] = str(
|
|
43
|
+
Path(self.args_dict["output_file"]).with_suffix(".ecsv")
|
|
44
|
+
)
|
|
45
|
+
self.io_handler = io_handler.IOHandler()
|
|
46
|
+
self.data = "" # Single photon electron spectrum data (as string)
|
|
47
|
+
self.metadata = MetadataCollector(args_dict=self.args_dict)
|
|
48
|
+
|
|
49
|
+
def derive_single_pe_spectrum(self):
|
|
50
|
+
"""Derive single photon electron spectrum."""
|
|
51
|
+
if self.args_dict.get("use_norm_spe"):
|
|
52
|
+
return self._derive_spectrum_norm_spe()
|
|
53
|
+
|
|
54
|
+
raise NotImplementedError(
|
|
55
|
+
"Derivation of single photon electron spectrum using a simtool is not yet implemented."
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def write_single_pe_spectrum(self):
|
|
59
|
+
"""
|
|
60
|
+
Write single photon electron spectrum plus metadata to disk.
|
|
61
|
+
|
|
62
|
+
Includes writing in simtel and simtools (ecsv) formats.
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
simtel_file = self.io_handler.get_output_directory() / Path(
|
|
66
|
+
self.args_dict["output_file"]
|
|
67
|
+
).with_suffix(".dat")
|
|
68
|
+
self._logger.debug(f"norm_spe output file: {simtel_file}")
|
|
69
|
+
with open(simtel_file, "w", encoding="utf-8") as simtel:
|
|
70
|
+
simtel.write(self.data)
|
|
71
|
+
|
|
72
|
+
cleaned_data = re.sub(r"%%%.+", "", self.data) # remove norm_spe row metadata
|
|
73
|
+
table = Table.read(
|
|
74
|
+
BytesIO(cleaned_data.encode("utf-8")),
|
|
75
|
+
format="ascii.no_header",
|
|
76
|
+
comment="#",
|
|
77
|
+
delimiter="\t",
|
|
78
|
+
)
|
|
79
|
+
table.rename_columns(
|
|
80
|
+
["col1", "col2", "col3"],
|
|
81
|
+
["amplitude", self.prompt_column, self.prompt_plus_afterpulse_column],
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
writer.ModelDataWriter.dump(
|
|
85
|
+
args_dict=self.args_dict,
|
|
86
|
+
metadata=self.metadata.top_level_meta,
|
|
87
|
+
product_data=table,
|
|
88
|
+
validate_schema_file=None,
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
def _derive_spectrum_norm_spe(self):
|
|
92
|
+
"""
|
|
93
|
+
Derive single photon electron spectrum using sim_telarray tool 'norm_spe'.
|
|
94
|
+
|
|
95
|
+
Returns
|
|
96
|
+
-------
|
|
97
|
+
int
|
|
98
|
+
Return code of the executed command
|
|
99
|
+
|
|
100
|
+
Raises
|
|
101
|
+
------
|
|
102
|
+
subprocess.CalledProcessError
|
|
103
|
+
If the command execution fails.
|
|
104
|
+
"""
|
|
105
|
+
tmp_input_file = self._get_input_data(
|
|
106
|
+
input_file=self.args_dict["input_spectrum"],
|
|
107
|
+
frequency_column=self.prompt_column,
|
|
108
|
+
)
|
|
109
|
+
tmp_ap_file = self._get_input_data(
|
|
110
|
+
input_file=self.args_dict.get("afterpulse_spectrum"),
|
|
111
|
+
frequency_column=self.afterpulse_column,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
command = [
|
|
115
|
+
f"{self.args_dict['simtel_path']}/sim_telarray/bin/norm_spe",
|
|
116
|
+
"-r",
|
|
117
|
+
f"{self.args_dict['step_size']},{self.args_dict['max_amplitude']}",
|
|
118
|
+
tmp_input_file.name,
|
|
119
|
+
]
|
|
120
|
+
if tmp_ap_file:
|
|
121
|
+
command.insert(1, "-a")
|
|
122
|
+
command.insert(2, f"{tmp_ap_file.name}")
|
|
123
|
+
|
|
124
|
+
self._logger.debug(f"Running norm_spe command: {' '.join(command)}")
|
|
125
|
+
try:
|
|
126
|
+
result = subprocess.run(command, capture_output=True, text=True, check=True)
|
|
127
|
+
except subprocess.CalledProcessError as exc:
|
|
128
|
+
self._logger.error(f"Error running norm_spe: {exc}")
|
|
129
|
+
self._logger.error(f"stderr: {exc.stderr}")
|
|
130
|
+
raise exc
|
|
131
|
+
finally:
|
|
132
|
+
for tmp_file in [tmp_input_file, tmp_ap_file]:
|
|
133
|
+
try:
|
|
134
|
+
Path(tmp_file.name).unlink()
|
|
135
|
+
except (AttributeError, FileNotFoundError):
|
|
136
|
+
pass
|
|
137
|
+
|
|
138
|
+
self.data = result.stdout
|
|
139
|
+
return result.returncode
|
|
140
|
+
|
|
141
|
+
def _get_input_data(self, input_file, frequency_column):
|
|
142
|
+
"""
|
|
143
|
+
Return input data for norm_spe command.
|
|
144
|
+
|
|
145
|
+
Input data need to be space separated values of the amplitude spectrum.
|
|
146
|
+
"""
|
|
147
|
+
input_data = ""
|
|
148
|
+
if not input_file:
|
|
149
|
+
return None
|
|
150
|
+
input_file = Path(input_file)
|
|
151
|
+
|
|
152
|
+
if input_file.suffix == ".ecsv":
|
|
153
|
+
data_validator = validate_data.DataValidator(
|
|
154
|
+
schema_file=self.input_schema, data_file=input_file
|
|
155
|
+
)
|
|
156
|
+
table = data_validator.validate_and_transform()
|
|
157
|
+
input_data = "\n".join(f"{row['amplitude']} {row[frequency_column]}" for row in table)
|
|
158
|
+
else:
|
|
159
|
+
with open(input_file, encoding="utf-8") as f:
|
|
160
|
+
input_data = (
|
|
161
|
+
f.read().replace(",", " ")
|
|
162
|
+
if frequency_column == self.prompt_column
|
|
163
|
+
else f.read()
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
with tempfile.NamedTemporaryFile(delete=False, mode="w", encoding="utf-8") as tmpfile:
|
|
167
|
+
tmpfile.write(input_data)
|
|
168
|
+
return tmpfile
|
|
@@ -70,6 +70,7 @@ class CommandLineParser(argparse.ArgumentParser):
|
|
|
70
70
|
self.initialize_output_arguments()
|
|
71
71
|
self.initialize_config_files()
|
|
72
72
|
self.initialize_application_execution_arguments()
|
|
73
|
+
self.initialize_user_arguments()
|
|
73
74
|
|
|
74
75
|
def initialize_config_files(self):
|
|
75
76
|
"""Initialize configuration files."""
|
|
@@ -175,6 +176,34 @@ class CommandLineParser(argparse.ArgumentParser):
|
|
|
175
176
|
"--version", action="version", version=f"%(prog)s {simtools.version.__version__}"
|
|
176
177
|
)
|
|
177
178
|
|
|
179
|
+
def initialize_user_arguments(self):
|
|
180
|
+
"""Initialize user arguments."""
|
|
181
|
+
_job_group = self.add_argument_group("user")
|
|
182
|
+
_job_group.add_argument(
|
|
183
|
+
"--user_name",
|
|
184
|
+
help="user name",
|
|
185
|
+
type=str,
|
|
186
|
+
required=False,
|
|
187
|
+
)
|
|
188
|
+
_job_group.add_argument(
|
|
189
|
+
"--user_organization",
|
|
190
|
+
help="user organization",
|
|
191
|
+
type=str,
|
|
192
|
+
required=False,
|
|
193
|
+
)
|
|
194
|
+
_job_group.add_argument(
|
|
195
|
+
"--user_email",
|
|
196
|
+
help="user email",
|
|
197
|
+
type=str,
|
|
198
|
+
required=False,
|
|
199
|
+
)
|
|
200
|
+
_job_group.add_argument(
|
|
201
|
+
"--user_orcid",
|
|
202
|
+
help="user ORCID",
|
|
203
|
+
type=str,
|
|
204
|
+
required=False,
|
|
205
|
+
)
|
|
206
|
+
|
|
178
207
|
def initialize_db_config_arguments(self):
|
|
179
208
|
"""Initialize DB configuration parameters."""
|
|
180
209
|
_job_group = self.add_argument_group("database configuration")
|
|
@@ -239,7 +268,14 @@ class CommandLineParser(argparse.ArgumentParser):
|
|
|
239
268
|
if "model_version" in model_options:
|
|
240
269
|
_job_group.add_argument(
|
|
241
270
|
"--model_version",
|
|
242
|
-
help="model version",
|
|
271
|
+
help="production model version",
|
|
272
|
+
type=str,
|
|
273
|
+
default=None,
|
|
274
|
+
)
|
|
275
|
+
if "parameter_version" in model_options:
|
|
276
|
+
_job_group.add_argument(
|
|
277
|
+
"--parameter_version",
|
|
278
|
+
help="model parameter version",
|
|
243
279
|
type=str,
|
|
244
280
|
default=None,
|
|
245
281
|
)
|
|
@@ -287,16 +287,14 @@ class Configurator:
|
|
|
287
287
|
)
|
|
288
288
|
# yaml parser adds \n in multiline strings, remove them
|
|
289
289
|
_config_dict = gen.remove_substring_recursively_from_dict(_config_dict, substring="\n")
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
except KeyError:
|
|
299
|
-
self._logger.info(f"No CTA_SIMPIPE:CONFIGURATION dict found in {config_file}.")
|
|
290
|
+
# read configuration for first application
|
|
291
|
+
if "CONFIGURATION" in _config_dict.get("CTA_SIMPIPE", {}).get("APPLICATIONS", [{}])[0]:
|
|
292
|
+
self._fill_from_config_dict(
|
|
293
|
+
input_dict=gen.change_dict_keys_case(
|
|
294
|
+
_config_dict["CTA_SIMPIPE"]["APPLICATIONS"][0]["CONFIGURATION"],
|
|
295
|
+
),
|
|
296
|
+
overwrite=True,
|
|
297
|
+
)
|
|
300
298
|
else:
|
|
301
299
|
self._fill_from_config_dict(
|
|
302
300
|
input_dict=gen.change_dict_keys_case(_config_dict), overwrite=True
|
simtools/constants.py
CHANGED
|
@@ -2,8 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
from importlib.resources import files
|
|
4
4
|
|
|
5
|
+
# Schema path
|
|
6
|
+
SCHEMA_PATH = files("simtools") / "schemas"
|
|
5
7
|
# Path to metadata jsonschema
|
|
6
|
-
METADATA_JSON_SCHEMA =
|
|
7
|
-
|
|
8
|
+
METADATA_JSON_SCHEMA = SCHEMA_PATH / "metadata.metaschema.yml"
|
|
9
|
+
# Path to model parameter metaschema
|
|
10
|
+
MODEL_PARAMETER_METASCHEMA = SCHEMA_PATH / "model_parameter.metaschema.yml"
|
|
11
|
+
# Path to model parameter description metaschema
|
|
12
|
+
MODEL_PARAMETER_DESCRIPTION_METASCHEMA = (
|
|
13
|
+
SCHEMA_PATH / "model_parameter_and_data_schema.metaschema.yml"
|
|
14
|
+
)
|
|
8
15
|
# Path to model parameter schema files
|
|
9
|
-
MODEL_PARAMETER_SCHEMA_PATH =
|
|
16
|
+
MODEL_PARAMETER_SCHEMA_PATH = SCHEMA_PATH / "model_parameters"
|
|
@@ -6,7 +6,6 @@ from pathlib import Path
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
from astropy import units as u
|
|
8
8
|
|
|
9
|
-
import simtools.utils.general as gen
|
|
10
9
|
from simtools.corsika.primary_particle import PrimaryParticle
|
|
11
10
|
from simtools.io_operations import io_handler
|
|
12
11
|
from simtools.model.model_parameter import ModelParameter
|
|
@@ -111,8 +110,6 @@ class CorsikaConfig:
|
|
|
111
110
|
if args_dict is None:
|
|
112
111
|
return {}
|
|
113
112
|
|
|
114
|
-
self._logger.debug("Setting CORSIKA parameters ")
|
|
115
|
-
|
|
116
113
|
self._is_file_updated = False
|
|
117
114
|
self.azimuth_angle = int(args_dict["azimuth_angle"].to("deg").value)
|
|
118
115
|
self.zenith_angle = args_dict["zenith_angle"].to("deg").value
|
|
@@ -235,25 +232,25 @@ class CorsikaConfig:
|
|
|
235
232
|
|
|
236
233
|
def _input_config_first_interaction_height(self, entry):
|
|
237
234
|
"""Return FIXHEI parameter CORSIKA format."""
|
|
238
|
-
return [f"{entry['value']*u.Unit(entry['unit']).to('cm'):.2f}", "0"]
|
|
235
|
+
return [f"{entry['value'] * u.Unit(entry['unit']).to('cm'):.2f}", "0"]
|
|
239
236
|
|
|
240
237
|
def _input_config_corsika_starting_grammage(self, entry):
|
|
241
238
|
"""Return FIXCHI parameter CORSIKA format."""
|
|
242
|
-
return f"{entry['value']*u.Unit(entry['unit']).to('g/cm2')}"
|
|
239
|
+
return f"{entry['value'] * u.Unit(entry['unit']).to('g/cm2')}"
|
|
243
240
|
|
|
244
241
|
def _input_config_corsika_particle_kinetic_energy_cutoff(self, entry):
|
|
245
242
|
"""Return ECUTS parameter CORSIKA format."""
|
|
246
|
-
e_cuts =
|
|
243
|
+
e_cuts = entry["value"]
|
|
247
244
|
return [
|
|
248
|
-
f"{e_cuts[0]*u.Unit(entry['unit']).to('GeV')} "
|
|
249
|
-
f"{e_cuts[1]*u.Unit(entry['unit']).to('GeV')} "
|
|
250
|
-
f"{e_cuts[2]*u.Unit(entry['unit']).to('GeV')} "
|
|
251
|
-
f"{e_cuts[3]*u.Unit(entry['unit']).to('GeV')}"
|
|
245
|
+
f"{e_cuts[0] * u.Unit(entry['unit']).to('GeV')} "
|
|
246
|
+
f"{e_cuts[1] * u.Unit(entry['unit']).to('GeV')} "
|
|
247
|
+
f"{e_cuts[2] * u.Unit(entry['unit']).to('GeV')} "
|
|
248
|
+
f"{e_cuts[3] * u.Unit(entry['unit']).to('GeV')}"
|
|
252
249
|
]
|
|
253
250
|
|
|
254
251
|
def _input_config_corsika_longitudinal_parameters(self, entry):
|
|
255
252
|
"""Return LONGI parameter CORSIKA format."""
|
|
256
|
-
return ["T", f"{entry['value']*u.Unit(entry['unit']).to('g/cm2')}", "F", "F"]
|
|
253
|
+
return ["T", f"{entry['value'] * u.Unit(entry['unit']).to('g/cm2')}", "F", "F"]
|
|
257
254
|
|
|
258
255
|
def _corsika_configuration_cherenkov_parameters(self, parameters_from_db):
|
|
259
256
|
"""
|
|
@@ -280,10 +277,10 @@ class CorsikaConfig:
|
|
|
280
277
|
|
|
281
278
|
def _input_config_corsika_cherenkov_wavelength(self, entry):
|
|
282
279
|
"""Return CWAVLG parameter CORSIKA format."""
|
|
283
|
-
wavelength_range =
|
|
280
|
+
wavelength_range = entry["value"]
|
|
284
281
|
return [
|
|
285
|
-
f"{wavelength_range[0]*u.Unit(entry['unit']).to('nm')}",
|
|
286
|
-
f"{wavelength_range[1]*u.Unit(entry['unit']).to('nm')}",
|
|
282
|
+
f"{wavelength_range[0] * u.Unit(entry['unit']).to('nm')}",
|
|
283
|
+
f"{wavelength_range[1] * u.Unit(entry['unit']).to('nm')}",
|
|
287
284
|
]
|
|
288
285
|
|
|
289
286
|
def _corsika_configuration_iact_parameters(self, parameters_from_db):
|
|
@@ -314,12 +311,17 @@ class CorsikaConfig:
|
|
|
314
311
|
return {
|
|
315
312
|
"DEBUG": ["F", 6, "F", 1000000],
|
|
316
313
|
"DATBAS": ["yes"],
|
|
317
|
-
"DIRECT": ["
|
|
314
|
+
"DIRECT": ["./"],
|
|
315
|
+
"PAROUT": ["F", "F"],
|
|
318
316
|
}
|
|
319
317
|
|
|
320
318
|
def _input_config_io_buff(self, entry):
|
|
321
|
-
"""Return IO_BUFFER parameter CORSIKA format."""
|
|
322
|
-
|
|
319
|
+
"""Return IO_BUFFER parameter CORSIKA format (Byte or MB required)."""
|
|
320
|
+
value = entry["value"] * u.Unit(entry["unit"]).to("Mbyte")
|
|
321
|
+
# check if value is integer-like
|
|
322
|
+
if value.is_integer():
|
|
323
|
+
return f"{int(value)}MB"
|
|
324
|
+
return f"{int(entry['value'] * u.Unit(entry['unit']).to('byte'))}"
|
|
323
325
|
|
|
324
326
|
def _rotate_azimuth_by_180deg(self, az, correct_for_geomagnetic_field_alignment=True):
|
|
325
327
|
"""
|
|
@@ -678,7 +678,7 @@ class CorsikaHistograms:
|
|
|
678
678
|
----------
|
|
679
679
|
new_individual_telescopes: bool
|
|
680
680
|
if False, the histograms are supposed to be filled for all telescopes.
|
|
681
|
-
if True, one histogram is set for each telescope
|
|
681
|
+
if True, one histogram is set for each telescope separately.
|
|
682
682
|
"""
|
|
683
683
|
if new_individual_telescopes is None:
|
|
684
684
|
self._individual_telescopes = False
|
|
@@ -944,10 +944,10 @@ class CorsikaHistograms:
|
|
|
944
944
|
|
|
945
945
|
hist_2d_values_list, x_position_list, y_position_list = self.get_2d_photon_position_distr()
|
|
946
946
|
|
|
947
|
-
for i_hist,
|
|
947
|
+
for i_hist, x_pos in enumerate(x_position_list):
|
|
948
948
|
hist_1d, bin_edges_1d = convert_2d_to_radial_distr(
|
|
949
949
|
hist_2d_values_list[i_hist],
|
|
950
|
-
|
|
950
|
+
x_pos,
|
|
951
951
|
y_position_list[i_hist],
|
|
952
952
|
bins=bins,
|
|
953
953
|
max_dist=max_dist,
|
|
@@ -1248,8 +1248,7 @@ class CorsikaHistograms:
|
|
|
1248
1248
|
meta_data=self._meta_dict,
|
|
1249
1249
|
)
|
|
1250
1250
|
self._logger.info(
|
|
1251
|
-
f"Writing 1D histogram with name {hdf5_table_name} to "
|
|
1252
|
-
f"{self.hdf5_file_name}."
|
|
1251
|
+
f"Writing 1D histogram with name {hdf5_table_name} to {self.hdf5_file_name}."
|
|
1253
1252
|
)
|
|
1254
1253
|
# overwrite takes precedence over append
|
|
1255
1254
|
if overwrite is True:
|
|
@@ -1382,8 +1381,7 @@ class CorsikaHistograms:
|
|
|
1382
1381
|
)
|
|
1383
1382
|
|
|
1384
1383
|
self._logger.info(
|
|
1385
|
-
f"Writing 2D histogram with name {hdf5_table_name} to "
|
|
1386
|
-
f"{self.hdf5_file_name}."
|
|
1384
|
+
f"Writing 2D histogram with name {hdf5_table_name} to {self.hdf5_file_name}."
|
|
1387
1385
|
)
|
|
1388
1386
|
# Always appending to table due to the file previously created
|
|
1389
1387
|
# by self._export_1d_histograms.
|
|
@@ -93,8 +93,7 @@ def _kernel_plot_2d_photons(histograms_instance, property_name, log_z=False):
|
|
|
93
93
|
all_figs.append(fig)
|
|
94
94
|
if histograms_instance.individual_telescopes is False:
|
|
95
95
|
ax.set_title(
|
|
96
|
-
f"{histograms_instance.dict_2d_distributions[property_name]['file name']}"
|
|
97
|
-
"_all_tels"
|
|
96
|
+
f"{histograms_instance.dict_2d_distributions[property_name]['file name']}_all_tels"
|
|
98
97
|
)
|
|
99
98
|
else:
|
|
100
99
|
ax.text(
|
|
@@ -285,8 +284,7 @@ def _kernel_plot_1d_photons(histograms_instance, property_name, log_y=True):
|
|
|
285
284
|
ax.set_yscale("log")
|
|
286
285
|
if histograms_instance.individual_telescopes is False:
|
|
287
286
|
ax.set_title(
|
|
288
|
-
f"{histograms_instance.dict_1d_distributions[property_name]['file name']}"
|
|
289
|
-
"_all_tels"
|
|
287
|
+
f"{histograms_instance.dict_1d_distributions[property_name]['file name']}_all_tels"
|
|
290
288
|
)
|
|
291
289
|
else:
|
|
292
290
|
ax.set_title(
|
|
@@ -112,9 +112,6 @@ def read_value_from_file(file_name, schema_file=None, validate=False):
|
|
|
112
112
|
_logger.info("Reading data from %s", file_name)
|
|
113
113
|
|
|
114
114
|
if validate:
|
|
115
|
-
if schema_file is None and "meta_schema_url" in data:
|
|
116
|
-
schema_file = data["meta_schema_url"]
|
|
117
|
-
_logger.debug(f"Using schema from meta_schema_url: {schema_file}")
|
|
118
115
|
if schema_file is None:
|
|
119
116
|
_collector = MetadataCollector(None, metadata_file_name=file_name)
|
|
120
117
|
schema_file = _collector.get_data_model_schema_file_name()
|
|
@@ -6,17 +6,16 @@ implementation of the observatory metadata model.
|
|
|
6
6
|
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
|
-
import datetime
|
|
10
9
|
import getpass
|
|
11
10
|
import logging
|
|
12
11
|
import uuid
|
|
13
|
-
from importlib.resources import files
|
|
14
12
|
from pathlib import Path
|
|
15
13
|
|
|
16
14
|
import simtools.constants
|
|
17
15
|
import simtools.utils.general as gen
|
|
18
16
|
import simtools.version
|
|
19
|
-
from simtools.
|
|
17
|
+
from simtools.constants import METADATA_JSON_SCHEMA
|
|
18
|
+
from simtools.data_model import metadata_model, schema
|
|
20
19
|
from simtools.io_operations import io_handler
|
|
21
20
|
from simtools.utils import names
|
|
22
21
|
|
|
@@ -97,9 +96,9 @@ class MetadataCollector:
|
|
|
97
96
|
|
|
98
97
|
"""
|
|
99
98
|
try:
|
|
100
|
-
self.top_level_meta[self.observatory]["activity"][
|
|
101
|
-
|
|
102
|
-
|
|
99
|
+
self.top_level_meta[self.observatory]["activity"]["end"] = (
|
|
100
|
+
gen.now_date_time_in_isoformat()
|
|
101
|
+
)
|
|
103
102
|
except KeyError:
|
|
104
103
|
pass
|
|
105
104
|
return self.top_level_meta
|
|
@@ -135,7 +134,7 @@ class MetadataCollector:
|
|
|
135
134
|
# from data model name
|
|
136
135
|
if self.data_model_name:
|
|
137
136
|
self._logger.debug(f"Schema file from data model name: {self.data_model_name}")
|
|
138
|
-
return
|
|
137
|
+
return str(schema.get_model_parameter_schema_file(self.data_model_name))
|
|
139
138
|
|
|
140
139
|
# from input metadata
|
|
141
140
|
try:
|
|
@@ -200,8 +199,17 @@ class MetadataCollector:
|
|
|
200
199
|
contact_dict: dict
|
|
201
200
|
Dictionary for contact metadata fields.
|
|
202
201
|
"""
|
|
203
|
-
|
|
202
|
+
contact_dict["name"] = contact_dict.get("name") or self.args_dict.get("user_name")
|
|
203
|
+
if contact_dict["name"] is None:
|
|
204
|
+
self._logger.warning("No user name provided, take user info from system level.")
|
|
204
205
|
contact_dict["name"] = getpass.getuser()
|
|
206
|
+
meta_dict = {
|
|
207
|
+
"email": "user_mail",
|
|
208
|
+
"orcid": "user_orcid",
|
|
209
|
+
"organization": "user_organization",
|
|
210
|
+
}
|
|
211
|
+
for key, value in meta_dict.items():
|
|
212
|
+
contact_dict[key] = contact_dict.get(key) or self.args_dict.get(value)
|
|
205
213
|
|
|
206
214
|
def _fill_context_meta(self, context_dict):
|
|
207
215
|
"""
|
|
@@ -267,7 +275,7 @@ class MetadataCollector:
|
|
|
267
275
|
self._logger.error("Unknown metadata file format: %s", metadata_file_name)
|
|
268
276
|
raise gen.InvalidConfigDataError
|
|
269
277
|
|
|
270
|
-
|
|
278
|
+
schema.validate_dict_using_schema(_input_metadata, schema_file=METADATA_JSON_SCHEMA)
|
|
271
279
|
|
|
272
280
|
return gen.change_dict_keys_case(
|
|
273
281
|
self._process_metadata_from_file(_input_metadata),
|
|
@@ -327,7 +335,7 @@ class MetadataCollector:
|
|
|
327
335
|
self.schema_dict = self.get_data_model_schema_dict()
|
|
328
336
|
|
|
329
337
|
product_dict["id"] = str(uuid.uuid4())
|
|
330
|
-
product_dict["creation_time"] =
|
|
338
|
+
product_dict["creation_time"] = gen.now_date_time_in_isoformat()
|
|
331
339
|
product_dict["description"] = self.schema_dict.get("description", None)
|
|
332
340
|
|
|
333
341
|
# DATA:CATEGORY
|
|
@@ -367,7 +375,7 @@ class MetadataCollector:
|
|
|
367
375
|
)
|
|
368
376
|
if instrument_dict["ID"]:
|
|
369
377
|
instrument_dict["class"] = names.get_collection_name_from_array_element_name(
|
|
370
|
-
instrument_dict["ID"]
|
|
378
|
+
instrument_dict["ID"], False
|
|
371
379
|
)
|
|
372
380
|
|
|
373
381
|
def _fill_process_meta(self, process_dict):
|
|
@@ -395,7 +403,7 @@ class MetadataCollector:
|
|
|
395
403
|
activity_dict["name"] = self.args_dict.get("label", None)
|
|
396
404
|
activity_dict["type"] = "software"
|
|
397
405
|
activity_dict["id"] = self.args_dict.get("activity_id", "UNDEFINED_ACTIVITY_ID")
|
|
398
|
-
activity_dict["start"] =
|
|
406
|
+
activity_dict["start"] = gen.now_date_time_in_isoformat()
|
|
399
407
|
activity_dict["end"] = activity_dict["start"]
|
|
400
408
|
activity_dict["software"]["name"] = "simtools"
|
|
401
409
|
activity_dict["software"]["version"] = simtools.version.__version__
|