gammasimtools 0.16.0__py3-none-any.whl → 0.18.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.16.0.dist-info → gammasimtools-0.18.0.dist-info}/METADATA +5 -2
- {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/RECORD +82 -74
- {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/WHEEL +1 -1
- {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/entry_points.txt +4 -1
- simtools/_version.py +2 -2
- simtools/applications/db_add_simulation_model_from_repository_to_db.py +10 -1
- simtools/applications/derive_ctao_array_layouts.py +5 -5
- simtools/applications/derive_mirror_rnda.py +1 -1
- simtools/applications/generate_simtel_event_data.py +128 -46
- simtools/applications/merge_tables.py +102 -0
- simtools/applications/plot_array_layout.py +145 -258
- simtools/applications/plot_tabular_data.py +12 -1
- simtools/applications/plot_tabular_data_for_model_parameter.py +103 -0
- simtools/applications/production_derive_corsika_limits.py +78 -225
- simtools/applications/production_derive_statistics.py +77 -43
- simtools/applications/simulate_light_emission.py +1 -0
- simtools/applications/simulate_prod.py +30 -18
- simtools/applications/simulate_prod_htcondor_generator.py +0 -1
- simtools/applications/submit_array_layouts.py +93 -0
- simtools/applications/verify_simulation_model_production_tables.py +52 -0
- simtools/camera/camera_efficiency.py +3 -3
- simtools/configuration/commandline_parser.py +30 -35
- simtools/configuration/configurator.py +0 -4
- simtools/constants.py +2 -0
- simtools/corsika/corsika_config.py +17 -12
- simtools/corsika/primary_particle.py +46 -13
- simtools/data_model/metadata_collector.py +7 -3
- simtools/data_model/schema.py +15 -1
- simtools/db/db_handler.py +16 -11
- simtools/db/db_model_upload.py +2 -2
- simtools/io_operations/io_handler.py +2 -2
- simtools/io_operations/io_table_handler.py +345 -0
- simtools/job_execution/htcondor_script_generator.py +2 -2
- simtools/job_execution/job_manager.py +7 -121
- simtools/layout/array_layout_utils.py +389 -0
- simtools/model/array_model.py +10 -1
- simtools/model/model_repository.py +134 -0
- simtools/production_configuration/{calculate_statistical_errors_grid_point.py → calculate_statistical_uncertainties_grid_point.py} +101 -112
- simtools/production_configuration/derive_corsika_limits.py +239 -111
- simtools/production_configuration/derive_corsika_limits_grid.py +232 -0
- simtools/production_configuration/derive_production_statistics.py +57 -26
- simtools/production_configuration/derive_production_statistics_handler.py +70 -37
- simtools/production_configuration/interpolation_handler.py +296 -94
- simtools/ray_tracing/ray_tracing.py +7 -6
- simtools/reporting/docs_read_parameters.py +104 -62
- simtools/resources/array-element-ids.json +126 -0
- simtools/runners/corsika_simtel_runner.py +4 -1
- simtools/runners/runner_services.py +5 -4
- simtools/schemas/model_parameter_and_data_schema.metaschema.yml +5 -1
- simtools/schemas/model_parameters/atmospheric_profile.schema.yml +41 -0
- simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +43 -0
- simtools/schemas/model_parameters/camera_filter.schema.yml +10 -0
- simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +10 -0
- simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +31 -0
- simtools/schemas/model_parameters/dsum_threshold.schema.yml +41 -0
- simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +12 -0
- simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +10 -0
- simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +10 -0
- simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +12 -0
- simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +19 -0
- simtools/schemas/model_parameters/quantum_efficiency.schema.yml +10 -0
- simtools/schemas/plot_configuration.metaschema.yml +46 -57
- simtools/schemas/production_configuration_metrics.schema.yml +2 -2
- simtools/simtel/simtel_config_writer.py +34 -14
- simtools/simtel/simtel_io_event_reader.py +301 -194
- simtools/simtel/simtel_io_event_writer.py +237 -221
- simtools/simtel/simtel_io_file_info.py +9 -4
- simtools/simtel/simtel_io_metadata.py +119 -8
- simtools/simtel/simulator_array.py +2 -2
- simtools/simtel/simulator_light_emission.py +79 -34
- simtools/simtel/simulator_ray_tracing.py +2 -2
- simtools/simulator.py +101 -68
- simtools/testing/validate_output.py +4 -1
- simtools/utils/general.py +1 -3
- simtools/utils/names.py +76 -7
- simtools/visualization/plot_array_layout.py +242 -0
- simtools/visualization/plot_pixels.py +680 -0
- simtools/visualization/plot_tables.py +81 -2
- simtools/visualization/visualize.py +3 -219
- simtools/applications/production_generate_simulation_config.py +0 -152
- simtools/layout/ctao_array_layouts.py +0 -172
- simtools/production_configuration/generate_simulation_config.py +0 -158
- {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/licenses/LICENSE +0 -0
- {gammasimtools-0.16.0.dist-info → gammasimtools-0.18.0.dist-info}/top_level.txt +0 -0
- /simtools/{schemas → resources}/array_elements.yml +0 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/python3
|
|
2
2
|
"""Plot tabular data."""
|
|
3
3
|
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
4
6
|
import numpy as np
|
|
5
7
|
from astropy.table import Table
|
|
6
8
|
|
|
7
9
|
import simtools.utils.general as gen
|
|
10
|
+
from simtools.constants import SCHEMA_PATH
|
|
8
11
|
from simtools.db import db_handler
|
|
9
12
|
from simtools.io_operations import legacy_data_handler
|
|
10
13
|
from simtools.visualization import visualize
|
|
@@ -20,6 +23,8 @@ def plot(config, output_file, db_config=None):
|
|
|
20
23
|
Configuration dictionary for plotting.
|
|
21
24
|
output_file: str
|
|
22
25
|
Output file.
|
|
26
|
+
db_config: dict, optional
|
|
27
|
+
Database configuration dictionary for accessing the model parameter database.
|
|
23
28
|
"""
|
|
24
29
|
data = read_table_data(config, db_config)
|
|
25
30
|
|
|
@@ -29,6 +34,8 @@ def plot(config, output_file, db_config=None):
|
|
|
29
34
|
)
|
|
30
35
|
visualize.save_figure(fig, output_file)
|
|
31
36
|
|
|
37
|
+
return output_file
|
|
38
|
+
|
|
32
39
|
|
|
33
40
|
def read_table_data(config, db_config):
|
|
34
41
|
"""
|
|
@@ -69,7 +76,8 @@ def read_table_data(config, db_config):
|
|
|
69
76
|
_config["select_values"]["column_name"],
|
|
70
77
|
_config["select_values"]["value"],
|
|
71
78
|
)
|
|
72
|
-
|
|
79
|
+
label = _config.get("label", f"{_config.get('column_x')} vs {_config.get('column_y')}")
|
|
80
|
+
data[label] = gen.get_structure_array_from_table(
|
|
73
81
|
table,
|
|
74
82
|
[
|
|
75
83
|
_config["column_x"],
|
|
@@ -93,7 +101,7 @@ def _read_table_from_model_database(table_config, db_config):
|
|
|
93
101
|
Returns
|
|
94
102
|
-------
|
|
95
103
|
Table
|
|
96
|
-
Astropy table
|
|
104
|
+
Astropy table
|
|
97
105
|
"""
|
|
98
106
|
db = db_handler.DatabaseHandler(mongo_db_config=db_config)
|
|
99
107
|
return db.export_model_file(
|
|
@@ -109,3 +117,74 @@ def _read_table_from_model_database(table_config, db_config):
|
|
|
109
117
|
def _select_values_from_table(table, column_name, value):
|
|
110
118
|
"""Return a table with only the rows where column_name == value."""
|
|
111
119
|
return table[np.isclose(table[column_name], value)]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def generate_plot_configurations(
|
|
123
|
+
parameter, parameter_version, site, telescope, output_path, plot_type
|
|
124
|
+
):
|
|
125
|
+
"""
|
|
126
|
+
Generate plot configurations for a model parameter from schema files.
|
|
127
|
+
|
|
128
|
+
Parameters
|
|
129
|
+
----------
|
|
130
|
+
parameter: str
|
|
131
|
+
Model parameter name.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
tuple
|
|
136
|
+
Tuple containing a list of plot configurations and a list of output file names.
|
|
137
|
+
Return None, if no plot configurations are found.
|
|
138
|
+
"""
|
|
139
|
+
schema = gen.change_dict_keys_case(
|
|
140
|
+
gen.collect_data_from_file(
|
|
141
|
+
file_name=SCHEMA_PATH / "model_parameters" / f"{parameter}.schema.yml"
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
configs = schema.get("plot_configuration")
|
|
145
|
+
if not configs:
|
|
146
|
+
return None
|
|
147
|
+
if plot_type != "all":
|
|
148
|
+
configs = [config for config in configs if config.get("type") == plot_type]
|
|
149
|
+
if not configs:
|
|
150
|
+
raise ValueError(
|
|
151
|
+
f"No plot configuration found for type '{plot_type}' in parameter '{parameter}'."
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
output_files = []
|
|
155
|
+
for _config in configs:
|
|
156
|
+
for _table in _config.get("tables", []):
|
|
157
|
+
_table["parameter_version"] = parameter_version
|
|
158
|
+
_table["site"] = site
|
|
159
|
+
output_files.append(
|
|
160
|
+
_generate_output_file_name(
|
|
161
|
+
parameter=parameter,
|
|
162
|
+
parameter_version=parameter_version,
|
|
163
|
+
site=site,
|
|
164
|
+
telescope=telescope,
|
|
165
|
+
plot_type=_config.get("type"),
|
|
166
|
+
output_path=output_path,
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return configs, output_files
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def _generate_output_file_name(
|
|
174
|
+
parameter,
|
|
175
|
+
parameter_version,
|
|
176
|
+
site,
|
|
177
|
+
telescope,
|
|
178
|
+
plot_type,
|
|
179
|
+
output_path=None,
|
|
180
|
+
file_extension=".pdf",
|
|
181
|
+
):
|
|
182
|
+
"""Generate output file name based on table file and appendix."""
|
|
183
|
+
parts = [parameter, parameter_version, site]
|
|
184
|
+
if telescope:
|
|
185
|
+
parts.append(telescope)
|
|
186
|
+
if plot_type != parameter:
|
|
187
|
+
parts.append(plot_type)
|
|
188
|
+
filename = "_".join(parts) + file_extension
|
|
189
|
+
|
|
190
|
+
return Path(output_path) / filename if output_path else Path(filename)
|
|
@@ -8,24 +8,16 @@ from collections import OrderedDict
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
10
|
import astropy.units as u
|
|
11
|
-
import matplotlib.patches as mpatches
|
|
12
11
|
import matplotlib.pyplot as plt
|
|
13
|
-
from astropy.table import
|
|
12
|
+
from astropy.table import QTable
|
|
14
13
|
from cycler import cycler
|
|
15
14
|
from matplotlib import gridspec
|
|
16
|
-
from matplotlib.collections import PatchCollection
|
|
17
|
-
|
|
18
|
-
from simtools.utils import geometry as transf
|
|
19
|
-
from simtools.utils import names
|
|
20
|
-
from simtools.visualization import legend_handlers as leg_h
|
|
21
15
|
|
|
22
16
|
__all__ = [
|
|
23
17
|
"get_colors",
|
|
24
18
|
"get_lines",
|
|
25
19
|
"get_markers",
|
|
26
|
-
"get_telescope_patch",
|
|
27
20
|
"plot_1d",
|
|
28
|
-
"plot_array",
|
|
29
21
|
"plot_hist_2d",
|
|
30
22
|
"plot_table",
|
|
31
23
|
"save_figure",
|
|
@@ -635,214 +627,6 @@ def plot_hist_2d(data, **kwargs):
|
|
|
635
627
|
return fig
|
|
636
628
|
|
|
637
629
|
|
|
638
|
-
@u.quantity_input(x=u.m, y=u.m, radius=u.m)
|
|
639
|
-
def get_telescope_patch(name, x, y, radius):
|
|
640
|
-
"""
|
|
641
|
-
Collect the patch of one telescope to be plotted by plot_array.
|
|
642
|
-
|
|
643
|
-
Parameters
|
|
644
|
-
----------
|
|
645
|
-
name: str
|
|
646
|
-
Name of the telescope (type).
|
|
647
|
-
x: astropy.units.Quantity
|
|
648
|
-
x position of the telescope usually in meters.
|
|
649
|
-
y: astropy.units.Quantity
|
|
650
|
-
y position of the telescope usually in meters.
|
|
651
|
-
radius: astropy.units.Quantity
|
|
652
|
-
Radius of the telescope sphere usually in meters.
|
|
653
|
-
|
|
654
|
-
Returns
|
|
655
|
-
-------
|
|
656
|
-
patch
|
|
657
|
-
Instance of mpatches.Circle.
|
|
658
|
-
"""
|
|
659
|
-
tel_obj = leg_h.TelescopeHandler()
|
|
660
|
-
valid_name = names.get_array_element_type_from_name(name)
|
|
661
|
-
fill_flag = False
|
|
662
|
-
|
|
663
|
-
x = x.to(u.m)
|
|
664
|
-
y = y.to(u.m)
|
|
665
|
-
radius = radius.to(u.m)
|
|
666
|
-
|
|
667
|
-
if valid_name.startswith("MST"):
|
|
668
|
-
fill_flag = True
|
|
669
|
-
if valid_name == "SCTS":
|
|
670
|
-
patch = mpatches.Rectangle(
|
|
671
|
-
((x - radius / 2).value, (y - radius / 2).value),
|
|
672
|
-
width=radius.value,
|
|
673
|
-
height=radius.value,
|
|
674
|
-
fill=False,
|
|
675
|
-
color=tel_obj.colors_dict["SCTS"],
|
|
676
|
-
)
|
|
677
|
-
else:
|
|
678
|
-
patch = mpatches.Circle(
|
|
679
|
-
(x.value, y.value),
|
|
680
|
-
radius=radius.value,
|
|
681
|
-
fill=fill_flag,
|
|
682
|
-
color=tel_obj.colors_dict[valid_name],
|
|
683
|
-
)
|
|
684
|
-
return patch
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
@u.quantity_input(rotate_angle=u.deg)
|
|
688
|
-
def plot_array(
|
|
689
|
-
telescopes, rotate_angle=0, show_tel_label=False, axes_range=None, marker_scaling=1.0
|
|
690
|
-
):
|
|
691
|
-
"""
|
|
692
|
-
Plot the array of telescopes.
|
|
693
|
-
|
|
694
|
-
The x axis gives the easting direction and y axis gives the northing direction.
|
|
695
|
-
Note that in order to convert from the CORSIKA coordinate system to the 'conventional' system
|
|
696
|
-
of North/East, a 90 degree rotation is always applied.
|
|
697
|
-
Rotation of the array elements is possible through the 'rotate_angle' given either in degrees,
|
|
698
|
-
or in radians.
|
|
699
|
-
The direction of rotation of the array elements is counterclockwise.
|
|
700
|
-
The rotation does not change Telescope instance attributes.
|
|
701
|
-
|
|
702
|
-
Parameters
|
|
703
|
-
----------
|
|
704
|
-
telescopes: astropy.table
|
|
705
|
-
Table with the telescope position and names. Note the orientation of the axes.
|
|
706
|
-
rotate_angle:
|
|
707
|
-
Angle to rotate the plot. For rotate_angle = 0 the resulting plot will have
|
|
708
|
-
the x-axis pointing towards the east, and the y-axis pointing towards the North.
|
|
709
|
-
show_tel_label: bool
|
|
710
|
-
If True it will print the label of the individual telescopes in the plot.
|
|
711
|
-
While it works well for the smaller arrays, it gets crowded for larger arrays.
|
|
712
|
-
axes_range : float
|
|
713
|
-
Axis range for both axes. Range is from -plot_range to plot_range.
|
|
714
|
-
maker_scaling : float
|
|
715
|
-
Scaling factor for marker size to be plotted.
|
|
716
|
-
|
|
717
|
-
Returns
|
|
718
|
-
-------
|
|
719
|
-
plt.figure
|
|
720
|
-
Instance of plt.figure with the array of telescopes plotted.
|
|
721
|
-
"""
|
|
722
|
-
fig, ax = plt.subplots(1)
|
|
723
|
-
legend_objects = []
|
|
724
|
-
legend_labels = []
|
|
725
|
-
tel_counters = initialize_tel_counters()
|
|
726
|
-
|
|
727
|
-
pos_x_rotated, pos_y_rotated = get_rotated_positions(telescopes, rotate_angle)
|
|
728
|
-
telescopes.add_column(Column(pos_x_rotated, name="pos_x_rotated"))
|
|
729
|
-
telescopes.add_column(Column(pos_y_rotated, name="pos_y_rotated"))
|
|
730
|
-
|
|
731
|
-
fontsize, scale = get_plot_params(len(pos_x_rotated))
|
|
732
|
-
patches = create_patches(
|
|
733
|
-
telescopes, scale, marker_scaling, show_tel_label, ax, fontsize, tel_counters
|
|
734
|
-
)
|
|
735
|
-
|
|
736
|
-
update_legend(ax, tel_counters, legend_objects, legend_labels)
|
|
737
|
-
finalize_plot(ax, patches, x_title="Easting [m]", y_title="Northing [m]", axes_range=axes_range)
|
|
738
|
-
|
|
739
|
-
return fig
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
def initialize_tel_counters():
|
|
743
|
-
return dict.fromkeys(names.get_list_of_array_element_types(), 0)
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
def get_rotated_positions(telescopes, rotate_angle):
|
|
747
|
-
pos_x_rotated = pos_y_rotated = None
|
|
748
|
-
if "position_x" in telescopes.colnames and "position_y" in telescopes.colnames:
|
|
749
|
-
pos_x_rotated, pos_y_rotated = telescopes["position_x"], telescopes["position_y"]
|
|
750
|
-
rotate_angle = rotate_angle + 90.0 * u.deg
|
|
751
|
-
elif "utm_east" in telescopes.colnames and "utm_north" in telescopes.colnames:
|
|
752
|
-
pos_x_rotated, pos_y_rotated = telescopes["utm_east"], telescopes["utm_north"]
|
|
753
|
-
else:
|
|
754
|
-
raise ValueError(
|
|
755
|
-
"Telescopes table must contain either 'position_x'/'position_y'"
|
|
756
|
-
"or 'utm_east'/'utm_north' columns"
|
|
757
|
-
)
|
|
758
|
-
if rotate_angle != 0:
|
|
759
|
-
pos_x_rotated, pos_y_rotated = transf.rotate(pos_x_rotated, pos_y_rotated, rotate_angle)
|
|
760
|
-
return pos_x_rotated, pos_y_rotated
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
def get_plot_params(position_length):
|
|
764
|
-
if position_length > 30:
|
|
765
|
-
return 4, 2
|
|
766
|
-
return 8, 1
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
def create_patches(telescopes, scale, marker_scaling, show_tel_label, ax, fontsize, tel_counters):
|
|
770
|
-
patches = []
|
|
771
|
-
for tel_now in telescopes:
|
|
772
|
-
telescope_name = get_telescope_name(tel_now)
|
|
773
|
-
update_tel_counters(tel_counters, telescope_name)
|
|
774
|
-
sphere_radius = get_sphere_radius(tel_now)
|
|
775
|
-
i_tel_name = names.get_array_element_type_from_name(telescope_name)
|
|
776
|
-
patches.append(
|
|
777
|
-
get_telescope_patch(
|
|
778
|
-
i_tel_name,
|
|
779
|
-
tel_now["pos_x_rotated"],
|
|
780
|
-
tel_now["pos_y_rotated"],
|
|
781
|
-
scale * sphere_radius * marker_scaling,
|
|
782
|
-
)
|
|
783
|
-
)
|
|
784
|
-
if show_tel_label:
|
|
785
|
-
ax.text(
|
|
786
|
-
tel_now["pos_x_rotated"].value,
|
|
787
|
-
tel_now["pos_y_rotated"].value + scale * sphere_radius.value,
|
|
788
|
-
telescope_name,
|
|
789
|
-
horizontalalignment="center",
|
|
790
|
-
verticalalignment="bottom",
|
|
791
|
-
fontsize=fontsize,
|
|
792
|
-
)
|
|
793
|
-
return patches
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
def get_telescope_name(tel_now):
|
|
797
|
-
"""Get the telescope name from the table row."""
|
|
798
|
-
try:
|
|
799
|
-
return tel_now["telescope_name"]
|
|
800
|
-
except KeyError:
|
|
801
|
-
return tel_now["asset_code"] + "-" + tel_now["sequence_number"]
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
def update_tel_counters(tel_counters, telescope_name):
|
|
805
|
-
"""Update the counter for the given telescope type."""
|
|
806
|
-
for tel_type in tel_counters:
|
|
807
|
-
if tel_type in telescope_name:
|
|
808
|
-
tel_counters[tel_type] += 1
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
def get_sphere_radius(tel_now):
|
|
812
|
-
"""Get the sphere radius of the telescope."""
|
|
813
|
-
return 1.0 * u.m if "sphere_radius" not in tel_now.colnames else tel_now["sphere_radius"]
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
def update_legend(ax, tel_counters, legend_objects, legend_labels):
|
|
817
|
-
"""Update the legend with the telescope counts."""
|
|
818
|
-
for one_telescope in names.get_list_of_array_element_types():
|
|
819
|
-
if tel_counters[one_telescope] > 0:
|
|
820
|
-
legend_objects.append(leg_h.all_telescope_objects[one_telescope]())
|
|
821
|
-
legend_labels.append(f"{one_telescope} ({tel_counters[one_telescope]})")
|
|
822
|
-
legend_handler_map = {k: v() for k, v in leg_h.legend_handler_map.items()}
|
|
823
|
-
ax.legend(
|
|
824
|
-
legend_objects,
|
|
825
|
-
legend_labels,
|
|
826
|
-
handler_map=legend_handler_map,
|
|
827
|
-
prop={"size": 11},
|
|
828
|
-
loc="best",
|
|
829
|
-
)
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
def finalize_plot(ax, patches, x_title, y_title, axes_range):
|
|
833
|
-
"""Finalize the plot by adding titles, setting limits, and adding patches."""
|
|
834
|
-
ax.add_collection(PatchCollection(patches, match_original=True))
|
|
835
|
-
ax.set_xlabel(x_title, fontsize=12, labelpad=0)
|
|
836
|
-
ax.set_ylabel(y_title, fontsize=12, labelpad=0)
|
|
837
|
-
ax.tick_params(axis="both", which="major", labelsize=8)
|
|
838
|
-
ax.set_axisbelow(True)
|
|
839
|
-
ax.axis("square")
|
|
840
|
-
if axes_range is not None:
|
|
841
|
-
ax.set_xlim(-axes_range, axes_range)
|
|
842
|
-
ax.set_ylim(-axes_range, axes_range)
|
|
843
|
-
plt.tight_layout()
|
|
844
|
-
|
|
845
|
-
|
|
846
630
|
def plot_simtel_ctapipe(filename, cleaning_args, distance, return_cleaned=False):
|
|
847
631
|
"""
|
|
848
632
|
Read in a sim_telarray file and plots reconstructed photoelectrons via ctapipe.
|
|
@@ -942,7 +726,7 @@ def plot_simtel_ctapipe(filename, cleaning_args, distance, return_cleaned=False)
|
|
|
942
726
|
return fig
|
|
943
727
|
|
|
944
728
|
|
|
945
|
-
def save_figure(fig, output_file, figure_format=None, log_title=""):
|
|
729
|
+
def save_figure(fig, output_file, figure_format=None, log_title="", dpi="figure"):
|
|
946
730
|
"""
|
|
947
731
|
Save figure to output file(s).
|
|
948
732
|
|
|
@@ -960,7 +744,7 @@ def save_figure(fig, output_file, figure_format=None, log_title=""):
|
|
|
960
744
|
figure_format = figure_format or ["pdf", "png"]
|
|
961
745
|
for fmt in figure_format:
|
|
962
746
|
_file = Path(output_file).with_suffix(f".{fmt}")
|
|
963
|
-
fig.savefig(_file, format=fmt, bbox_inches="tight")
|
|
747
|
+
fig.savefig(_file, format=fmt, bbox_inches="tight", dpi=dpi)
|
|
964
748
|
logging.info(f"Saved plot {log_title} to {_file}")
|
|
965
749
|
|
|
966
750
|
fig.clf()
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/python3
|
|
2
|
-
|
|
3
|
-
r"""
|
|
4
|
-
Derive simulation configuration parameters for a simulation production.
|
|
5
|
-
|
|
6
|
-
Derived simulation configuration parameters include:
|
|
7
|
-
|
|
8
|
-
* energy range
|
|
9
|
-
* shower core scatter radius
|
|
10
|
-
* view cone radius
|
|
11
|
-
* total number of events to be simulated
|
|
12
|
-
|
|
13
|
-
Configuration parameters depend on characteristics of the observations, especially elevation,
|
|
14
|
-
azimuth, and night sky background.
|
|
15
|
-
|
|
16
|
-
The configuration parameters are derived according to the required precision. The metrics are:
|
|
17
|
-
|
|
18
|
-
* statistical uncertainty on the determination of the effective area as function of primary energy
|
|
19
|
-
* fraction of lost events to the selected core scatter and view cone radius (to be implemented)
|
|
20
|
-
* statistical uncertainty of the energy migration matrix as function of primary energy
|
|
21
|
-
(to be implemented)
|
|
22
|
-
|
|
23
|
-
Command line arguments
|
|
24
|
-
----------------------
|
|
25
|
-
azimuth (float, required)
|
|
26
|
-
Azimuth angle in degrees.
|
|
27
|
-
elevation (float, required)
|
|
28
|
-
Elevation angle in degrees.
|
|
29
|
-
nsb (float, required)
|
|
30
|
-
Night sky background value.
|
|
31
|
-
file_path (str, required)
|
|
32
|
-
Path to file with MC events at CTAO DL2 data level.
|
|
33
|
-
Used for statistical uncertainty evaluation.
|
|
34
|
-
metrics (str, required)
|
|
35
|
-
Path to a YAML file containing metrics for evaluation.
|
|
36
|
-
site (str, required)
|
|
37
|
-
The observatory site (North or South).
|
|
38
|
-
|
|
39
|
-
Example
|
|
40
|
-
-------
|
|
41
|
-
To run the simulation configuration, execute the script as follows:
|
|
42
|
-
|
|
43
|
-
.. code-block:: console
|
|
44
|
-
|
|
45
|
-
simtools-production-generate-simulation-config --azimuth 60.0 --elevation 45.0 \
|
|
46
|
-
--nsb 0.3 --file_path tests/resources/production_dl2_fits/dl2_mc_events_file.fits \
|
|
47
|
-
--metrics_file tests/resources/production_simulation_config_metrics.yml --site North
|
|
48
|
-
|
|
49
|
-
The output will show the derived simulation parameters.
|
|
50
|
-
"""
|
|
51
|
-
|
|
52
|
-
import json
|
|
53
|
-
import logging
|
|
54
|
-
from pathlib import Path
|
|
55
|
-
|
|
56
|
-
import astropy.units as u
|
|
57
|
-
|
|
58
|
-
import simtools.utils.general as gen
|
|
59
|
-
from simtools.configuration import configurator
|
|
60
|
-
from simtools.data_model import schema
|
|
61
|
-
from simtools.io_operations import io_handler
|
|
62
|
-
from simtools.production_configuration.generate_simulation_config import (
|
|
63
|
-
SimulationConfig,
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def _parse(label):
|
|
68
|
-
"""Parse command-line arguments."""
|
|
69
|
-
config = configurator.Configurator(
|
|
70
|
-
label=label,
|
|
71
|
-
description="Derive simulation configuration parameters for a simulation production.",
|
|
72
|
-
)
|
|
73
|
-
config.parser.add_argument(
|
|
74
|
-
"--azimuth", type=float, required=True, help="Azimuth angle in degrees."
|
|
75
|
-
)
|
|
76
|
-
config.parser.add_argument(
|
|
77
|
-
"--elevation", type=float, required=True, help="Elevation angle in degrees."
|
|
78
|
-
)
|
|
79
|
-
config.parser.add_argument(
|
|
80
|
-
"--nsb", type=float, required=True, help="Night sky background in units of 1/(sr*ns*cm**2)."
|
|
81
|
-
)
|
|
82
|
-
config.parser.add_argument(
|
|
83
|
-
"--file_path", type=str, required=True, help="Path to MC event file in DL2 format."
|
|
84
|
-
)
|
|
85
|
-
config.parser.add_argument(
|
|
86
|
-
"--metrics_file",
|
|
87
|
-
required=True,
|
|
88
|
-
type=str,
|
|
89
|
-
help="Path to YAML file containing metrics and required precision as values.",
|
|
90
|
-
)
|
|
91
|
-
config.parser.add_argument(
|
|
92
|
-
"--site",
|
|
93
|
-
type=str,
|
|
94
|
-
required=True,
|
|
95
|
-
help="Site location for the simulation (e.g., 'North', 'South').",
|
|
96
|
-
)
|
|
97
|
-
config.parser.add_argument(
|
|
98
|
-
"--output_file",
|
|
99
|
-
help="Name of the file to save the configured simulation parameters.",
|
|
100
|
-
type=str,
|
|
101
|
-
required=False,
|
|
102
|
-
default="configured_simulation_params.json",
|
|
103
|
-
)
|
|
104
|
-
|
|
105
|
-
return config.initialize(db_config=False)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def main():
|
|
109
|
-
"""Run the Simulation Config application."""
|
|
110
|
-
label = Path(__file__).stem
|
|
111
|
-
args_dict, _ = _parse(label)
|
|
112
|
-
|
|
113
|
-
logger = logging.getLogger()
|
|
114
|
-
logger.setLevel(gen.get_log_level_from_user(args_dict["log_level"]))
|
|
115
|
-
output_path = io_handler.IOHandler().get_output_directory(label)
|
|
116
|
-
output_filepath = Path(output_path).joinpath(f"{args_dict['output_file']}")
|
|
117
|
-
|
|
118
|
-
grid_point_config = {
|
|
119
|
-
"azimuth": args_dict["azimuth"],
|
|
120
|
-
"elevation": args_dict["elevation"],
|
|
121
|
-
"night_sky_background": args_dict["nsb"],
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
metrics = gen.collect_data_from_file(args_dict["metrics_file"])
|
|
125
|
-
schema.validate_dict_using_schema(
|
|
126
|
-
data=metrics, schema_file="production_configuration_metrics.schema.yml"
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
simulation_config = SimulationConfig(
|
|
130
|
-
grid_point=grid_point_config,
|
|
131
|
-
file_path=args_dict["file_path"],
|
|
132
|
-
metrics=metrics,
|
|
133
|
-
)
|
|
134
|
-
|
|
135
|
-
simulation_params = simulation_config.configure_simulation()
|
|
136
|
-
|
|
137
|
-
serializable_config = {}
|
|
138
|
-
|
|
139
|
-
for key, value in simulation_params.items():
|
|
140
|
-
if isinstance(value, u.Quantity):
|
|
141
|
-
serializable_config[key] = f"{value.value} {value.unit}"
|
|
142
|
-
else:
|
|
143
|
-
serializable_config[key] = value
|
|
144
|
-
|
|
145
|
-
logger.info(f"Simulation configuration: {serializable_config}")
|
|
146
|
-
with open(output_filepath, "w", encoding="utf-8") as f:
|
|
147
|
-
json.dump(serializable_config, f, indent=4)
|
|
148
|
-
logger.info(f"Simulation configuration saved to: {output_filepath}")
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if __name__ == "__main__":
|
|
152
|
-
main()
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
"""Retrieve, merge, and write layouts from CTAO common identifiers repository."""
|
|
2
|
-
|
|
3
|
-
import logging
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
import simtools.utils.general as gen
|
|
7
|
-
from simtools.data_model.metadata_collector import MetadataCollector
|
|
8
|
-
from simtools.data_model.model_data_writer import ModelDataWriter
|
|
9
|
-
from simtools.io_operations import io_handler
|
|
10
|
-
from simtools.utils import names
|
|
11
|
-
|
|
12
|
-
_logger = logging.getLogger(__name__)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def retrieve_array_layouts(site, repository_url, branch_name="main"):
|
|
16
|
-
"""
|
|
17
|
-
Retrieve array layouts from CTAO common identifiers repository.
|
|
18
|
-
|
|
19
|
-
Parameters
|
|
20
|
-
----------
|
|
21
|
-
site : str
|
|
22
|
-
Site identifier.
|
|
23
|
-
repository_url : str
|
|
24
|
-
URL or path to CTAO common identifiers
|
|
25
|
-
branch_name : str
|
|
26
|
-
Repository branch to use for CTAO common identifiers.
|
|
27
|
-
|
|
28
|
-
Returns
|
|
29
|
-
-------
|
|
30
|
-
dict
|
|
31
|
-
Array layouts for all CTAO sites.
|
|
32
|
-
"""
|
|
33
|
-
_logger.info(f"Retrieving array layouts from {repository_url} on branch {branch_name}.")
|
|
34
|
-
|
|
35
|
-
if gen.is_url(repository_url):
|
|
36
|
-
array_element_ids = gen.collect_data_from_http(
|
|
37
|
-
url=f"{repository_url}/{branch_name}/array-element-ids.json"
|
|
38
|
-
)
|
|
39
|
-
sub_arrays = gen.collect_data_from_http(
|
|
40
|
-
url=f"{repository_url}/{branch_name}/subarray-ids.json"
|
|
41
|
-
)
|
|
42
|
-
else:
|
|
43
|
-
array_element_ids = gen.collect_data_from_file(
|
|
44
|
-
Path(repository_url) / "array-element-ids.json"
|
|
45
|
-
)
|
|
46
|
-
sub_arrays = gen.collect_data_from_file(Path(repository_url) / "subarray-ids.json")
|
|
47
|
-
|
|
48
|
-
return _get_layouts_per_site(site, sub_arrays, array_element_ids)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def _get_layouts_per_site(site, sub_arrays, array_element_ids):
|
|
52
|
-
"""
|
|
53
|
-
Get array layouts for CTAO sites.
|
|
54
|
-
|
|
55
|
-
Parameters
|
|
56
|
-
----------
|
|
57
|
-
site : str
|
|
58
|
-
Site identifier.
|
|
59
|
-
sub_arrays : dict
|
|
60
|
-
Sub-array definitions.
|
|
61
|
-
array_element_ids : dict
|
|
62
|
-
Array element definitions.
|
|
63
|
-
|
|
64
|
-
Returns
|
|
65
|
-
-------
|
|
66
|
-
dict
|
|
67
|
-
Array layouts for CTAO sites.
|
|
68
|
-
"""
|
|
69
|
-
layouts_per_site = []
|
|
70
|
-
|
|
71
|
-
for array in sub_arrays.get("subarrays", []):
|
|
72
|
-
elements = []
|
|
73
|
-
for ids in array.get("array_element_ids", []):
|
|
74
|
-
element_name = _get_array_element_name(ids, array_element_ids)
|
|
75
|
-
if names.get_site_from_array_element_name(element_name) != site:
|
|
76
|
-
break
|
|
77
|
-
elements.append(element_name)
|
|
78
|
-
if len(elements) > 0:
|
|
79
|
-
array_layout = {
|
|
80
|
-
"name": array.get("name"),
|
|
81
|
-
"elements": elements,
|
|
82
|
-
}
|
|
83
|
-
layouts_per_site.append(array_layout)
|
|
84
|
-
|
|
85
|
-
_logger.info(f"CTAO array layout definition: {layouts_per_site}")
|
|
86
|
-
return layouts_per_site
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def _get_array_element_name(ids, array_element_ids):
|
|
90
|
-
"""Return array element name for common identifier."""
|
|
91
|
-
for element in array_element_ids.get("array_elements", []):
|
|
92
|
-
if element.get("id") == ids:
|
|
93
|
-
return element.get("name")
|
|
94
|
-
return None
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def merge_array_layouts(layouts_1, layouts_2):
|
|
98
|
-
"""
|
|
99
|
-
Compare array layout dictionaries and merge them.
|
|
100
|
-
|
|
101
|
-
Parameters
|
|
102
|
-
----------
|
|
103
|
-
layouts_1 : dict
|
|
104
|
-
Array layout dictionary 1.
|
|
105
|
-
layouts_2 : dict
|
|
106
|
-
Array layout dictionary 2.
|
|
107
|
-
|
|
108
|
-
Returns
|
|
109
|
-
-------
|
|
110
|
-
dict
|
|
111
|
-
Merged array layout dictionary based on layout_1.
|
|
112
|
-
"""
|
|
113
|
-
merged_layout = layouts_1
|
|
114
|
-
for layout_2 in layouts_2:
|
|
115
|
-
layout_found = False
|
|
116
|
-
for layout_1 in layouts_1.get("value", {}):
|
|
117
|
-
if sorted(layout_1["elements"]) == sorted(layout_2["elements"]):
|
|
118
|
-
print(
|
|
119
|
-
f"Equal telescope list: simtools '{layout_1['name']}' "
|
|
120
|
-
f"and CTAO '{layout_2['name']}'"
|
|
121
|
-
)
|
|
122
|
-
layout_1["name"] = layout_2["name"]
|
|
123
|
-
layout_found = True
|
|
124
|
-
if not layout_found:
|
|
125
|
-
merged_layout["value"].append(
|
|
126
|
-
{
|
|
127
|
-
"name": layout_2["name"],
|
|
128
|
-
"elements": layout_2["elements"],
|
|
129
|
-
}
|
|
130
|
-
)
|
|
131
|
-
_logger.info(f"Adding {layout_2['name']} with {layout_2['elements']}")
|
|
132
|
-
return merged_layout
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
def write_array_layouts(array_layouts, args_dict, db_config):
|
|
136
|
-
"""
|
|
137
|
-
Write array layouts as model parameter.
|
|
138
|
-
|
|
139
|
-
Parameters
|
|
140
|
-
----------
|
|
141
|
-
args_dict : dict
|
|
142
|
-
Command line arguments.
|
|
143
|
-
array_layouts : dict
|
|
144
|
-
Array layouts to be written.
|
|
145
|
-
db_config : dict
|
|
146
|
-
Database configuration.
|
|
147
|
-
"""
|
|
148
|
-
_logger.info(f"Writing updated array layouts to the database for site {args_dict['site']}.")
|
|
149
|
-
|
|
150
|
-
io_handler_instance = io_handler.IOHandler()
|
|
151
|
-
io_handler_instance.set_paths(
|
|
152
|
-
output_path=args_dict["output_path"],
|
|
153
|
-
use_plain_output_path=args_dict["use_plain_output_path"],
|
|
154
|
-
)
|
|
155
|
-
output_file = io_handler_instance.get_output_file(
|
|
156
|
-
f"array-layouts-{args_dict['updated_parameter_version']}.json"
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
ModelDataWriter.dump_model_parameter(
|
|
160
|
-
parameter_name="array_layouts",
|
|
161
|
-
value=array_layouts["value"],
|
|
162
|
-
instrument=args_dict["site"],
|
|
163
|
-
parameter_version=args_dict.get("updated_parameter_version"),
|
|
164
|
-
output_file=output_file,
|
|
165
|
-
use_plain_output_path=args_dict["use_plain_output_path"],
|
|
166
|
-
db_config=db_config,
|
|
167
|
-
)
|
|
168
|
-
MetadataCollector.dump(
|
|
169
|
-
args_dict,
|
|
170
|
-
output_file,
|
|
171
|
-
add_activity_name=True,
|
|
172
|
-
)
|