gammasimtools 0.10.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.10.0.dist-info → gammasimtools-0.11.0.dist-info}/METADATA +3 -1
- {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/RECORD +84 -77
- {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/WHEEL +1 -1
- {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/entry_points.txt +4 -0
- simtools/_version.py +9 -4
- simtools/applications/convert_all_model_parameters_from_simtel.py +0 -1
- simtools/applications/convert_model_parameter_from_simtel.py +0 -1
- simtools/applications/db_add_file_to_db.py +0 -1
- simtools/applications/db_get_parameter_from_db.py +7 -28
- simtools/applications/derive_mirror_rnda.py +1 -2
- 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_corsika_histograms.py +2 -2
- simtools/applications/generate_regular_arrays.py +4 -2
- simtools/applications/production_derive_limits.py +95 -0
- simtools/applications/production_generate_simulation_config.py +15 -29
- simtools/applications/production_scale_events.py +2 -7
- simtools/applications/run_application.py +165 -0
- simtools/applications/simulate_light_emission.py +0 -4
- simtools/applications/submit_model_parameter_from_external.py +11 -6
- simtools/applications/validate_file_using_schema.py +3 -3
- simtools/configuration/commandline_parser.py +29 -0
- simtools/configuration/configurator.py +8 -10
- simtools/corsika/corsika_config.py +11 -10
- simtools/corsika/corsika_histograms.py +4 -6
- simtools/corsika/corsika_histograms_visualize.py +2 -4
- simtools/data_model/metadata_collector.py +18 -9
- simtools/data_model/model_data_writer.py +67 -15
- simtools/data_model/schema.py +10 -3
- simtools/data_model/validate_data.py +70 -24
- simtools/db/db_handler.py +42 -12
- simtools/dependencies.py +112 -0
- simtools/layout/array_layout.py +5 -4
- simtools/model/model_parameter.py +35 -2
- simtools/production_configuration/calculate_statistical_errors_grid_point.py +5 -6
- simtools/production_configuration/event_scaler.py +3 -19
- simtools/production_configuration/generate_simulation_config.py +4 -12
- simtools/production_configuration/interpolation_handler.py +2 -5
- simtools/production_configuration/limits_calculation.py +202 -0
- simtools/reporting/docs_read_parameters.py +310 -0
- simtools/runners/corsika_simtel_runner.py +1 -3
- simtools/schemas/{integration_tests_config.metaschema.yml → application_workflow.metaschema.yml} +51 -27
- simtools/schemas/array_elements.yml +8 -0
- simtools/schemas/model_parameter.metaschema.yml +96 -0
- simtools/schemas/model_parameter_and_data_schema.metaschema.yml +2 -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 +2 -0
- 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/iobuf_maximum.schema.yml +1 -1
- simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +1 -1
- 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/store_photoelectrons.schema.yml +1 -1
- simtools/schemas/model_parameters/tailcut_scale.schema.yml +1 -1
- simtools/schemas/production_tables.schema.yml +1 -1
- simtools/simtel/simtel_config_reader.py +1 -2
- simtools/simtel/simtel_config_writer.py +1 -2
- simtools/simtel/simtel_io_histogram.py +0 -1
- simtools/simtel/simtel_io_histograms.py +2 -4
- simtools/simtel/simulator_camera_efficiency.py +1 -3
- simtools/simtel/simulator_light_emission.py +2 -5
- simtools/simtel/simulator_ray_tracing.py +1 -3
- simtools/testing/configuration.py +2 -1
- simtools/testing/validate_output.py +23 -13
- simtools/utils/general.py +12 -2
- simtools/utils/names.py +290 -152
- simtools/utils/value_conversion.py +17 -13
- simtools/version.py +2 -2
- simtools/visualization/legend_handlers.py +2 -0
- {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/LICENSE +0 -0
- {gammasimtools-0.10.0.dist-info → gammasimtools-0.11.0.dist-info}/top_level.txt +0 -0
|
@@ -152,7 +152,7 @@ r"""
|
|
|
152
152
|
.. code-block:: console
|
|
153
153
|
|
|
154
154
|
simtools-generate-corsika-histograms --iact_file /workdir/external/simtools/\\
|
|
155
|
-
|
|
155
|
+
tests/resources/tel_output_10GeV-2-gamma-20deg-CTAO-South.corsikaio \\
|
|
156
156
|
--pdf --hdf5 \\
|
|
157
157
|
--event_2d_histograms zenith azimuth --event_1d_histograms total_energy
|
|
158
158
|
|
|
@@ -208,7 +208,7 @@ def _parse(label, description):
|
|
|
208
208
|
|
|
209
209
|
config.parser.add_argument(
|
|
210
210
|
"--telescope_indices",
|
|
211
|
-
help="
|
|
211
|
+
help="List of telescope indices to be considered in the generation of the histograms",
|
|
212
212
|
type=str,
|
|
213
213
|
required=False,
|
|
214
214
|
nargs="+",
|
|
@@ -77,7 +77,9 @@ def main():
|
|
|
77
77
|
# Single telescope at the center
|
|
78
78
|
if array_name[0] == "1":
|
|
79
79
|
tel_name.append(
|
|
80
|
-
names.
|
|
80
|
+
names.generate_array_element_name_from_type_site_id(
|
|
81
|
+
tel_size, args_dict["site"], "01"
|
|
82
|
+
)
|
|
81
83
|
)
|
|
82
84
|
pos_x.append(0 * u.m)
|
|
83
85
|
pos_y.append(0 * u.m)
|
|
@@ -86,7 +88,7 @@ def main():
|
|
|
86
88
|
else:
|
|
87
89
|
for i in range(1, 5):
|
|
88
90
|
tel_name.append(
|
|
89
|
-
names.
|
|
91
|
+
names.generate_array_element_name_from_type_site_id(
|
|
90
92
|
tel_size, args_dict["site"], f"0{i}"
|
|
91
93
|
)
|
|
92
94
|
)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
|
|
3
|
+
r"""
|
|
4
|
+
Derives the limits for energy, radial distance, and viewcone to be used in CORSIKA simulations.
|
|
5
|
+
|
|
6
|
+
The limits are derived based on the event loss fraction specified by the user.
|
|
7
|
+
|
|
8
|
+
Command line arguments
|
|
9
|
+
----------------------
|
|
10
|
+
event_data_file (str, required)
|
|
11
|
+
Path to the file containing the event data.
|
|
12
|
+
loss_fraction (float, required)
|
|
13
|
+
Fraction of events to be lost.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
Example
|
|
17
|
+
-------
|
|
18
|
+
Derive limits for a given file with a specified loss fraction.
|
|
19
|
+
|
|
20
|
+
.. code-block:: console
|
|
21
|
+
|
|
22
|
+
simtools-production-derive-limits\\
|
|
23
|
+
--event_data_file path/to/event_data_file.hdf5 \\
|
|
24
|
+
--loss_fraction 1e-6
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
|
|
29
|
+
import simtools.utils.general as gen
|
|
30
|
+
from simtools.configuration import configurator
|
|
31
|
+
from simtools.io_operations.hdf5_handler import read_hdf5
|
|
32
|
+
from simtools.production_configuration.limits_calculation import LimitCalculator
|
|
33
|
+
|
|
34
|
+
_logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _parse():
|
|
38
|
+
"""
|
|
39
|
+
Parse command line configuration.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
event_data_file: str
|
|
44
|
+
The event data file.
|
|
45
|
+
loss_fraction: float
|
|
46
|
+
Loss fraction of events for limit derivation.
|
|
47
|
+
|
|
48
|
+
Returns
|
|
49
|
+
-------
|
|
50
|
+
CommandLineParser
|
|
51
|
+
Command line parser object
|
|
52
|
+
|
|
53
|
+
"""
|
|
54
|
+
config = configurator.Configurator(
|
|
55
|
+
description="Derive limits for energy, radial distance, and viewcone."
|
|
56
|
+
)
|
|
57
|
+
config.parser.add_argument(
|
|
58
|
+
"--event_data_file",
|
|
59
|
+
type=str,
|
|
60
|
+
required=True,
|
|
61
|
+
help="Path to the event data file containing the event data.",
|
|
62
|
+
)
|
|
63
|
+
config.parser.add_argument(
|
|
64
|
+
"--loss_fraction", type=float, required=True, help="Fraction of events to be lost."
|
|
65
|
+
)
|
|
66
|
+
return config.initialize(db_config=False)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def main():
|
|
70
|
+
"""Derive limits for energy, radial distance, and viewcone."""
|
|
71
|
+
args_dict, _ = _parse()
|
|
72
|
+
|
|
73
|
+
logger = logging.getLogger()
|
|
74
|
+
logger.setLevel(gen.get_log_level_from_user(args_dict["log_level"]))
|
|
75
|
+
|
|
76
|
+
event_data_file_path = args_dict["event_data_file"]
|
|
77
|
+
loss_fraction = args_dict["loss_fraction"]
|
|
78
|
+
|
|
79
|
+
_logger.info(f"Loading event data file: {event_data_file_path}")
|
|
80
|
+
tables = read_hdf5(event_data_file_path)
|
|
81
|
+
|
|
82
|
+
calculator = LimitCalculator(tables)
|
|
83
|
+
|
|
84
|
+
lower_energy_limit = calculator.compute_lower_energy_limit(loss_fraction)
|
|
85
|
+
_logger.info(f"Lower energy threshold: {lower_energy_limit}")
|
|
86
|
+
|
|
87
|
+
upper_radial_distance = calculator.compute_upper_radial_distance(loss_fraction)
|
|
88
|
+
_logger.info(f"Upper radius threshold: {upper_radial_distance}")
|
|
89
|
+
|
|
90
|
+
viewcone = calculator.compute_viewcone(loss_fraction)
|
|
91
|
+
_logger.info(f"Viewcone radius: {viewcone}")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
if __name__ == "__main__":
|
|
95
|
+
main()
|
|
@@ -18,28 +18,25 @@ The configuration parameters are derived according to the required precision. Th
|
|
|
18
18
|
* statistical uncertainty on the determination of the effective area as function of primary energy
|
|
19
19
|
* fraction of lost events to the selected core scatter and view cone radius (to be implemented)
|
|
20
20
|
* statistical uncertainty of the energy migration matrix as function of primary energy
|
|
21
|
-
|
|
21
|
+
(to be implemented)
|
|
22
22
|
|
|
23
23
|
Command line arguments
|
|
24
24
|
----------------------
|
|
25
25
|
azimuth (float, required)
|
|
26
|
-
|
|
26
|
+
Azimuth angle in degrees.
|
|
27
27
|
elevation (float, required)
|
|
28
|
-
|
|
28
|
+
Elevation angle in degrees.
|
|
29
29
|
nsb (float, required)
|
|
30
|
-
|
|
31
|
-
ctao_data_level (str, required)
|
|
32
|
-
The data level for the simulation (e.g., 'A', 'B', 'C').
|
|
33
|
-
science_case (str, required)
|
|
34
|
-
The science case for the simulation.
|
|
30
|
+
Night sky background value.
|
|
35
31
|
file_path (str, required)
|
|
36
|
-
|
|
32
|
+
Path to file with MC events at CTAO DL2 data level.
|
|
33
|
+
Used for statistical uncertainty evaluation.
|
|
37
34
|
file_type (str, required)
|
|
38
|
-
|
|
39
|
-
metrics (str,
|
|
40
|
-
|
|
35
|
+
Type of the DL2 MC event file ('point-like' or 'cone').
|
|
36
|
+
metrics (str, required)
|
|
37
|
+
Path to a YAML file containing metrics for evaluation.
|
|
41
38
|
site (str, required)
|
|
42
|
-
|
|
39
|
+
The observatory site (North or South).
|
|
43
40
|
|
|
44
41
|
Example
|
|
45
42
|
-------
|
|
@@ -47,11 +44,10 @@ To run the simulation configuration, execute the script as follows:
|
|
|
47
44
|
|
|
48
45
|
.. code-block:: console
|
|
49
46
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
--metrics_file tests/resources/production_simulation_config_metrics.yml --site North
|
|
47
|
+
simtools-production-generate-simulation-config --azimuth 60.0 --elevation 45.0 \
|
|
48
|
+
--nsb 0.3 --file_path tests/resources/production_dl2_fits/dl2_mc_events_file.fits \
|
|
49
|
+
--file_type "point-like" \
|
|
50
|
+
--metrics_file tests/resources/production_simulation_config_metrics.yml --site North
|
|
55
51
|
|
|
56
52
|
The output will show the derived simulation parameters.
|
|
57
53
|
"""
|
|
@@ -86,12 +82,6 @@ def _parse(label):
|
|
|
86
82
|
config.parser.add_argument(
|
|
87
83
|
"--nsb", type=float, required=True, help="Night sky background in units of 1/(sr*ns*cm**2)."
|
|
88
84
|
)
|
|
89
|
-
config.parser.add_argument(
|
|
90
|
-
"--ctao_data_level", type=str, required=True, help="Data level (e.g., 'A', 'B', 'C')."
|
|
91
|
-
)
|
|
92
|
-
config.parser.add_argument(
|
|
93
|
-
"--science_case", type=str, required=True, help="Science case for the simulation."
|
|
94
|
-
)
|
|
95
85
|
config.parser.add_argument(
|
|
96
86
|
"--file_path", type=str, required=True, help="Path to MC event file in DL2 format."
|
|
97
87
|
)
|
|
@@ -140,17 +130,13 @@ def main():
|
|
|
140
130
|
"night_sky_background": args_dict["nsb"],
|
|
141
131
|
}
|
|
142
132
|
|
|
143
|
-
metrics = (
|
|
144
|
-
gen.collect_data_from_file(args_dict["metrics_file"]) if "metrics_file" in args_dict else {}
|
|
145
|
-
)
|
|
133
|
+
metrics = gen.collect_data_from_file(args_dict["metrics_file"])
|
|
146
134
|
schema.validate_dict_using_schema(
|
|
147
135
|
data=metrics, schema_file="production_configuration_metrics.schema.yml"
|
|
148
136
|
)
|
|
149
137
|
|
|
150
138
|
simulation_config = SimulationConfig(
|
|
151
139
|
grid_point=grid_point_config,
|
|
152
|
-
ctao_data_level=args_dict["ctao_data_level"],
|
|
153
|
-
science_case=args_dict["science_case"],
|
|
154
140
|
file_path=args_dict["file_path"],
|
|
155
141
|
file_type=args_dict["file_type"],
|
|
156
142
|
metrics=metrics,
|
|
@@ -28,7 +28,7 @@ To evaluate statistical uncertainties and perform interpolation, run the command
|
|
|
28
28
|
|
|
29
29
|
simtools-production-scale-events --base_path tests/resources/production_dl2_fits/ \
|
|
30
30
|
--zeniths 20 52 40 60 --offsets 0 --interpolate --query_point 1 180 30 0 0 \
|
|
31
|
-
--
|
|
31
|
+
--metrics_file "path/to/metrics.yaml"
|
|
32
32
|
|
|
33
33
|
|
|
34
34
|
The output will display the scaled events for the specified grid point.
|
|
@@ -96,9 +96,6 @@ def _parse(label, description):
|
|
|
96
96
|
default="production_simulation_config_metrics.yml",
|
|
97
97
|
help="Metrics definition file. (default: production_simulation_config_metrics.yml)",
|
|
98
98
|
)
|
|
99
|
-
config.parser.add_argument(
|
|
100
|
-
"--science_case", type=str, required=True, help="Science case for the simulation."
|
|
101
|
-
)
|
|
102
99
|
return config.initialize(db_config=False)
|
|
103
100
|
|
|
104
101
|
|
|
@@ -147,9 +144,7 @@ def main():
|
|
|
147
144
|
logger.warning(f"Offsets: {args_dict['offsets']}")
|
|
148
145
|
|
|
149
146
|
# Perform interpolation for the given query point
|
|
150
|
-
interpolation_handler = InterpolationHandler(
|
|
151
|
-
evaluator_instances, science_case=args_dict["science_case"], metrics=metrics
|
|
152
|
-
)
|
|
147
|
+
interpolation_handler = InterpolationHandler(evaluator_instances, metrics=metrics)
|
|
153
148
|
query_points = np.array([args_dict["query_point"]])
|
|
154
149
|
scaled_events = interpolation_handler.interpolate(query_points)
|
|
155
150
|
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Run simtools applications from configuration files for setting workflows.
|
|
5
|
+
|
|
6
|
+
Allows to run several simtools applications with a single configuration file, which includes
|
|
7
|
+
both the name of the simtools application and the configuration for the application.
|
|
8
|
+
|
|
9
|
+
Strong assumption on the directory structure for input and output files of applications.
|
|
10
|
+
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
import subprocess
|
|
15
|
+
import tempfile
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
import yaml
|
|
19
|
+
|
|
20
|
+
import simtools.utils.general as gen
|
|
21
|
+
from simtools import dependencies
|
|
22
|
+
from simtools.configuration import configurator
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _parse(label, description, usage):
|
|
26
|
+
"""
|
|
27
|
+
Parse command line configuration.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
label : str
|
|
32
|
+
Label describing the application.
|
|
33
|
+
description : str
|
|
34
|
+
Description of the application.
|
|
35
|
+
usage : str
|
|
36
|
+
Example on how to use the application.
|
|
37
|
+
|
|
38
|
+
Returns
|
|
39
|
+
-------
|
|
40
|
+
CommandLineParser
|
|
41
|
+
Command line parser object.
|
|
42
|
+
"""
|
|
43
|
+
config = configurator.Configurator(label=label, description=description, usage=usage)
|
|
44
|
+
|
|
45
|
+
config.parser.add_argument(
|
|
46
|
+
"--configuration_file",
|
|
47
|
+
help="Application configuration.",
|
|
48
|
+
type=str,
|
|
49
|
+
required=True,
|
|
50
|
+
default=None,
|
|
51
|
+
)
|
|
52
|
+
return config.initialize(db_config=False)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def run_application(application, configuration, logger):
|
|
56
|
+
"""Run a simtools application and return stdout and stderr."""
|
|
57
|
+
with tempfile.NamedTemporaryFile(mode="w", delete=True, suffix=".yml") as temp_config:
|
|
58
|
+
yaml.dump(configuration, temp_config, default_flow_style=False)
|
|
59
|
+
temp_config.flush()
|
|
60
|
+
configuration_file = Path(temp_config.name)
|
|
61
|
+
try:
|
|
62
|
+
result = subprocess.run(
|
|
63
|
+
[application, "--config", configuration_file],
|
|
64
|
+
check=True,
|
|
65
|
+
capture_output=True,
|
|
66
|
+
text=True,
|
|
67
|
+
)
|
|
68
|
+
except subprocess.CalledProcessError as exc:
|
|
69
|
+
logger.error(f"Error running application {application}: {exc.stderr}")
|
|
70
|
+
raise exc
|
|
71
|
+
return result.stdout, result.stderr
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_subdirectory_name(path):
|
|
75
|
+
"""Get the first subdirectory name under 'input'."""
|
|
76
|
+
path = Path(path).resolve()
|
|
77
|
+
try:
|
|
78
|
+
input_index = path.parts.index("input")
|
|
79
|
+
return path.parts[input_index], path.parts[input_index + 1]
|
|
80
|
+
except (ValueError, IndexError) as exc:
|
|
81
|
+
raise ValueError(f"Could not find subdirectory under 'input': {exc}") from exc
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def read_application_configuration(configuration_file, logger):
|
|
85
|
+
"""
|
|
86
|
+
Read application configuration from file and modify for setting workflows.
|
|
87
|
+
|
|
88
|
+
Strong assumptions on the structure of input and output files:
|
|
89
|
+
|
|
90
|
+
- configuration file is expected to be in './input/<workflow directory>/<yaml file>'
|
|
91
|
+
- output files will be written out to './output/<workflow directory>/'
|
|
92
|
+
|
|
93
|
+
Replaces the placeholders in the configuration file with the actual values.
|
|
94
|
+
Sets 'USE_PLAIN_OUTPUT_PATH' to True for all applications.
|
|
95
|
+
|
|
96
|
+
Parameters
|
|
97
|
+
----------
|
|
98
|
+
configuration_file : str
|
|
99
|
+
Configuration file name.
|
|
100
|
+
logger : Logger
|
|
101
|
+
Logger object.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
dict
|
|
106
|
+
Application configuration.
|
|
107
|
+
Path
|
|
108
|
+
Path to the log file.
|
|
109
|
+
|
|
110
|
+
"""
|
|
111
|
+
application_config = gen.collect_data_from_file(configuration_file).get("CTA_SIMPIPE")
|
|
112
|
+
place_holder = "__SETTING_WORKFLOW__"
|
|
113
|
+
workflow_dir, setting_workflow = get_subdirectory_name(configuration_file)
|
|
114
|
+
output_path = str(workflow_dir).replace("input", "output") + setting_workflow
|
|
115
|
+
logger.info(f"Setting workflow output path to {output_path}")
|
|
116
|
+
log_file = (
|
|
117
|
+
Path(application_config.get("LOG_PATH", "./").replace(place_holder, setting_workflow))
|
|
118
|
+
/ "simtools.log"
|
|
119
|
+
)
|
|
120
|
+
log_file.parent.mkdir(parents=True, exist_ok=True)
|
|
121
|
+
configurations = application_config.get("APPLICATIONS")
|
|
122
|
+
for config in configurations:
|
|
123
|
+
for key, value in config.get("CONFIGURATION", {}).items():
|
|
124
|
+
if isinstance(value, str):
|
|
125
|
+
config["CONFIGURATION"][key] = value.replace(place_holder, setting_workflow)
|
|
126
|
+
if isinstance(value, list):
|
|
127
|
+
config["CONFIGURATION"][key] = [
|
|
128
|
+
item.replace(place_holder, setting_workflow) for item in value
|
|
129
|
+
]
|
|
130
|
+
config["CONFIGURATION"]["USE_PLAIN_OUTPUT_PATH"] = True
|
|
131
|
+
config["OUTPUT_PATH"] = output_path
|
|
132
|
+
|
|
133
|
+
return configurations, log_file
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def main(): # noqa: D103
|
|
137
|
+
args_dict, _ = _parse(
|
|
138
|
+
Path(__file__).stem,
|
|
139
|
+
description="Run simtools applications from configuration file.",
|
|
140
|
+
usage="simtools-run-application --config_file config_file_name",
|
|
141
|
+
)
|
|
142
|
+
logger = logging.getLogger()
|
|
143
|
+
logger.setLevel(gen.get_log_level_from_user(args_dict["log_level"]))
|
|
144
|
+
|
|
145
|
+
configurations, log_file = read_application_configuration(
|
|
146
|
+
args_dict["configuration_file"], logger
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
with log_file.open("w", encoding="utf-8") as file:
|
|
150
|
+
file.write("Running simtools applications\n")
|
|
151
|
+
file.write(dependencies.get_version_string())
|
|
152
|
+
for config in configurations:
|
|
153
|
+
logger.info(f"Running application: {config.get('APPLICATION')}")
|
|
154
|
+
config = gen.change_dict_keys_case(config, False)
|
|
155
|
+
stdout, stderr = run_application(
|
|
156
|
+
config.get("APPLICATION"), config.get("CONFIGURATION"), logger
|
|
157
|
+
)
|
|
158
|
+
file.write("=" * 80 + "\n")
|
|
159
|
+
file.write(f"Application: {config.get('APPLICATION')}\n")
|
|
160
|
+
file.write("STDOUT:\n" + stdout)
|
|
161
|
+
file.write("STDERR:\n" + stderr)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
if __name__ == "__main__":
|
|
165
|
+
main()
|
|
@@ -117,7 +117,6 @@ r"""
|
|
|
117
117
|
|
|
118
118
|
"""
|
|
119
119
|
|
|
120
|
-
|
|
121
120
|
import logging
|
|
122
121
|
import subprocess
|
|
123
122
|
from pathlib import Path
|
|
@@ -390,7 +389,6 @@ def main():
|
|
|
390
389
|
run_script = light_source.prepare_script(generate_postscript=True, **args_dict)
|
|
391
390
|
log_file = f"{light_source.output_directory}/logfile.log"
|
|
392
391
|
with open(log_file, "w", encoding="utf-8") as log_file:
|
|
393
|
-
|
|
394
392
|
subprocess.run(
|
|
395
393
|
run_script,
|
|
396
394
|
shell=False,
|
|
@@ -430,7 +428,6 @@ def main():
|
|
|
430
428
|
)
|
|
431
429
|
|
|
432
430
|
elif args_dict["light_source_setup"] == "layout":
|
|
433
|
-
|
|
434
431
|
light_source = SimulatorLightEmission(
|
|
435
432
|
telescope_model=telescope_model,
|
|
436
433
|
calibration_model=calibration_model,
|
|
@@ -443,7 +440,6 @@ def main():
|
|
|
443
440
|
run_script = light_source.prepare_script(generate_postscript=True, **args_dict)
|
|
444
441
|
log_file = f"{light_source.output_directory}/logfile.log"
|
|
445
442
|
with open(log_file, "w", encoding="utf-8") as log_file:
|
|
446
|
-
|
|
447
443
|
subprocess.run(
|
|
448
444
|
run_script, shell=False, check=False, text=True, stdout=log_file, stderr=log_file
|
|
449
445
|
)
|
|
@@ -73,7 +73,6 @@ def _parse(label, description):
|
|
|
73
73
|
config.parser.add_argument(
|
|
74
74
|
"--parameter_version", type=str, required=True, help="Parameter version"
|
|
75
75
|
)
|
|
76
|
-
|
|
77
76
|
config.parser.add_argument(
|
|
78
77
|
"--value",
|
|
79
78
|
type=str,
|
|
@@ -84,18 +83,22 @@ def _parse(label, description):
|
|
|
84
83
|
'Examples: "--value=5", "--value=\'5 km\'", "--value=\'5 cm, 0.5 deg\'"'
|
|
85
84
|
),
|
|
86
85
|
)
|
|
87
|
-
|
|
88
86
|
config.parser.add_argument(
|
|
89
87
|
"--input_meta",
|
|
90
88
|
help="meta data file associated to input data",
|
|
91
89
|
type=str,
|
|
92
90
|
required=False,
|
|
93
91
|
)
|
|
94
|
-
|
|
92
|
+
config.parser.add_argument(
|
|
93
|
+
"--check_parameter_version",
|
|
94
|
+
help="Check if the parameter version exists in the database",
|
|
95
|
+
action="store_true",
|
|
96
|
+
)
|
|
97
|
+
return config.initialize(output=True, db_config=True)
|
|
95
98
|
|
|
96
99
|
|
|
97
100
|
def main(): # noqa: D103
|
|
98
|
-
args_dict,
|
|
101
|
+
args_dict, db_config = _parse(
|
|
99
102
|
label=Path(__file__).stem,
|
|
100
103
|
description="Submit and validate a model parameters).",
|
|
101
104
|
)
|
|
@@ -104,19 +107,21 @@ def main(): # noqa: D103
|
|
|
104
107
|
logger.setLevel(gen.get_log_level_from_user(args_dict["log_level"]))
|
|
105
108
|
|
|
106
109
|
output_path = (
|
|
107
|
-
Path(args_dict["output_path"]) / args_dict["
|
|
110
|
+
Path(args_dict["output_path"]) / args_dict["instrument"] / args_dict["parameter"]
|
|
108
111
|
if args_dict.get("output_path")
|
|
109
112
|
else None
|
|
110
113
|
)
|
|
114
|
+
|
|
111
115
|
writer.ModelDataWriter.dump_model_parameter(
|
|
112
116
|
parameter_name=args_dict["parameter"],
|
|
113
117
|
value=args_dict["value"],
|
|
114
118
|
instrument=args_dict["instrument"],
|
|
115
119
|
parameter_version=args_dict["parameter_version"],
|
|
116
|
-
output_file=Path(args_dict["parameter"]
|
|
120
|
+
output_file=Path(args_dict["parameter"] + "-" + args_dict["parameter_version"] + ".json"),
|
|
117
121
|
output_path=output_path,
|
|
118
122
|
use_plain_output_path=args_dict.get("use_plain_output_path"),
|
|
119
123
|
metadata_input_dict=args_dict,
|
|
124
|
+
db_config=db_config if args_dict.get("check_parameter_version") else None,
|
|
120
125
|
)
|
|
121
126
|
|
|
122
127
|
|
|
@@ -170,9 +170,9 @@ def validate_data_file(args_dict, logger):
|
|
|
170
170
|
data_file=args_dict["file_name"],
|
|
171
171
|
check_exact_data_type=args_dict["require_exact_data_type"],
|
|
172
172
|
)
|
|
173
|
-
data_validator.validate_and_transform(
|
|
174
|
-
|
|
175
|
-
|
|
173
|
+
data_validator.validate_and_transform(
|
|
174
|
+
is_model_parameter=(args_dict["data_type"].lower() == "model_parameter")
|
|
175
|
+
)
|
|
176
176
|
|
|
177
177
|
logger.info(f"Successful validation of data file {args_dict['file_name']}")
|
|
178
178
|
|
|
@@ -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")
|
|
@@ -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
|
|
@@ -232,25 +232,25 @@ class CorsikaConfig:
|
|
|
232
232
|
|
|
233
233
|
def _input_config_first_interaction_height(self, entry):
|
|
234
234
|
"""Return FIXHEI parameter CORSIKA format."""
|
|
235
|
-
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"]
|
|
236
236
|
|
|
237
237
|
def _input_config_corsika_starting_grammage(self, entry):
|
|
238
238
|
"""Return FIXCHI parameter CORSIKA format."""
|
|
239
|
-
return f"{entry['value']*u.Unit(entry['unit']).to('g/cm2')}"
|
|
239
|
+
return f"{entry['value'] * u.Unit(entry['unit']).to('g/cm2')}"
|
|
240
240
|
|
|
241
241
|
def _input_config_corsika_particle_kinetic_energy_cutoff(self, entry):
|
|
242
242
|
"""Return ECUTS parameter CORSIKA format."""
|
|
243
243
|
e_cuts = entry["value"]
|
|
244
244
|
return [
|
|
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')}"
|
|
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')}"
|
|
249
249
|
]
|
|
250
250
|
|
|
251
251
|
def _input_config_corsika_longitudinal_parameters(self, entry):
|
|
252
252
|
"""Return LONGI parameter CORSIKA format."""
|
|
253
|
-
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"]
|
|
254
254
|
|
|
255
255
|
def _corsika_configuration_cherenkov_parameters(self, parameters_from_db):
|
|
256
256
|
"""
|
|
@@ -279,8 +279,8 @@ class CorsikaConfig:
|
|
|
279
279
|
"""Return CWAVLG parameter CORSIKA format."""
|
|
280
280
|
wavelength_range = entry["value"]
|
|
281
281
|
return [
|
|
282
|
-
f"{wavelength_range[0]*u.Unit(entry['unit']).to('nm')}",
|
|
283
|
-
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')}",
|
|
284
284
|
]
|
|
285
285
|
|
|
286
286
|
def _corsika_configuration_iact_parameters(self, parameters_from_db):
|
|
@@ -311,7 +311,8 @@ class CorsikaConfig:
|
|
|
311
311
|
return {
|
|
312
312
|
"DEBUG": ["F", 6, "F", 1000000],
|
|
313
313
|
"DATBAS": ["yes"],
|
|
314
|
-
"DIRECT": ["
|
|
314
|
+
"DIRECT": ["./"],
|
|
315
|
+
"PAROUT": ["F", "F"],
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
def _input_config_io_buff(self, entry):
|