gammasimtools 0.15.0__py3-none-any.whl → 0.17.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.15.0.dist-info → gammasimtools-0.17.0.dist-info}/METADATA +5 -33
- {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/RECORD +243 -229
- {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/WHEEL +1 -1
- {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/entry_points.txt +8 -3
- simtools/_version.py +2 -2
- simtools/applications/calculate_trigger_rate.py +10 -10
- simtools/applications/convert_all_model_parameters_from_simtel.py +16 -16
- simtools/applications/convert_model_parameter_from_simtel.py +1 -1
- simtools/applications/derive_ctao_array_layouts.py +5 -5
- simtools/applications/derive_psf_parameters.py +12 -9
- simtools/applications/docs_produce_array_element_report.py +3 -3
- simtools/applications/docs_produce_calibration_reports.py +49 -0
- simtools/applications/docs_produce_simulation_configuration_report.py +50 -0
- simtools/applications/{generate_simtel_array_histograms.py → generate_sim_telarray_histograms.py} +2 -2
- simtools/applications/generate_simtel_event_data.py +36 -46
- simtools/applications/merge_tables.py +104 -0
- simtools/applications/plot_array_layout.py +145 -258
- simtools/applications/production_derive_corsika_limits.py +35 -167
- simtools/applications/production_derive_statistics.py +159 -0
- simtools/applications/production_generate_grid.py +197 -0
- simtools/applications/simulate_light_emission.py +6 -13
- simtools/applications/simulate_prod.py +45 -21
- simtools/applications/simulate_prod_htcondor_generator.py +0 -1
- simtools/applications/submit_array_layouts.py +93 -0
- simtools/applications/validate_cumulative_psf.py +6 -4
- simtools/applications/validate_file_using_schema.py +7 -3
- simtools/applications/validate_optics.py +5 -4
- simtools/applications/verify_simulation_model_production_tables.py +52 -0
- simtools/camera/camera_efficiency.py +17 -42
- simtools/configuration/commandline_parser.py +32 -37
- simtools/configuration/configurator.py +10 -4
- simtools/corsika/corsika_config.py +120 -17
- simtools/corsika/primary_particle.py +46 -13
- simtools/data_model/format_checkers.py +9 -0
- simtools/data_model/metadata_collector.py +7 -3
- simtools/data_model/model_data_writer.py +3 -0
- simtools/data_model/schema.py +27 -16
- simtools/data_model/validate_data.py +27 -7
- simtools/db/db_handler.py +21 -15
- 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.py +1 -0
- simtools/layout/array_layout_utils.py +385 -0
- simtools/model/array_model.py +68 -29
- simtools/model/model_parameter.py +76 -51
- simtools/model/model_repository.py +134 -0
- simtools/model/model_utils.py +43 -1
- simtools/model/site_model.py +3 -2
- simtools/model/telescope_model.py +4 -4
- simtools/production_configuration/{calculate_statistical_errors_grid_point.py → calculate_statistical_uncertainties_grid_point.py} +101 -116
- simtools/production_configuration/derive_corsika_limits.py +239 -111
- simtools/production_configuration/derive_corsika_limits_grid.py +189 -0
- simtools/production_configuration/derive_production_statistics.py +155 -0
- simtools/production_configuration/derive_production_statistics_handler.py +152 -0
- simtools/production_configuration/generate_production_grid.py +364 -0
- simtools/production_configuration/interpolation_handler.py +303 -96
- simtools/ray_tracing/mirror_panel_psf.py +16 -20
- simtools/ray_tracing/psf_analysis.py +2 -2
- simtools/ray_tracing/ray_tracing.py +12 -7
- simtools/reporting/docs_read_parameters.py +426 -81
- simtools/runners/corsika_runner.py +11 -1
- simtools/runners/corsika_simtel_runner.py +84 -90
- simtools/runners/runner_services.py +22 -8
- simtools/runners/simtel_runner.py +27 -10
- simtools/schemas/model_parameter.metaschema.yml +4 -0
- simtools/schemas/model_parameter_and_data_schema.metaschema.yml +1 -0
- simtools/schemas/model_parameters/adjust_gain.schema.yml +2 -2
- simtools/schemas/model_parameters/array_element_position_ground.schema.yml +2 -2
- simtools/schemas/model_parameters/array_element_position_utm.schema.yml +2 -2
- simtools/schemas/model_parameters/array_window.schema.yml +2 -2
- simtools/schemas/model_parameters/asum_offset.schema.yml +2 -2
- simtools/schemas/model_parameters/asum_shaping.schema.yml +2 -2
- simtools/schemas/model_parameters/asum_threshold.schema.yml +2 -2
- simtools/schemas/model_parameters/axes_offsets.schema.yml +2 -2
- simtools/schemas/model_parameters/camera_body_diameter.schema.yml +2 -2
- simtools/schemas/model_parameters/camera_body_shape.schema.yml +2 -2
- simtools/schemas/model_parameters/camera_config_file.schema.yml +2 -2
- simtools/schemas/model_parameters/camera_config_rotate.schema.yml +2 -2
- simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +2 -2
- simtools/schemas/model_parameters/camera_degraded_map.schema.yml +2 -2
- simtools/schemas/model_parameters/camera_depth.schema.yml +2 -2
- simtools/schemas/model_parameters/camera_filter.schema.yml +2 -2
- simtools/schemas/model_parameters/camera_pixels.schema.yml +2 -2
- simtools/schemas/model_parameters/camera_transmission.schema.yml +2 -2
- simtools/schemas/model_parameters/channels_per_chip.schema.yml +2 -2
- simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +2 -2
- simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +90 -1
- simtools/schemas/model_parameters/default_trigger.schema.yml +2 -2
- simtools/schemas/model_parameters/design_model.schema.yml +2 -2
- simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +2 -2
- simtools/schemas/model_parameters/disc_bins.schema.yml +2 -2
- simtools/schemas/model_parameters/disc_start.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_threshold.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +2 -2
- simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +2 -2
- simtools/schemas/model_parameters/dish_shape_length.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_clipping.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_offset.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_pedsub.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_prescale.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_presum_max.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_shaping.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_threshold.schema.yml +44 -3
- simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +2 -2
- simtools/schemas/model_parameters/effective_focal_length.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_amplitude.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_bins.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_max_signal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_mhz.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_noise.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +2 -2
- simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +2 -2
- simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
- simtools/schemas/model_parameters/flatfielding.schema.yml +2 -2
- simtools/schemas/model_parameters/focal_length.schema.yml +2 -2
- simtools/schemas/model_parameters/focus_offset.schema.yml +2 -2
- simtools/schemas/model_parameters/gain_variation.schema.yml +2 -2
- simtools/schemas/model_parameters/hg_lg_variation.schema.yml +2 -2
- simtools/schemas/model_parameters/iobuf_maximum.schema.yml +2 -2
- simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +2 -2
- simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
- simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +2 -2
- simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +2 -2
- simtools/schemas/model_parameters/min_photoelectrons.schema.yml +2 -2
- simtools/schemas/model_parameters/min_photons.schema.yml +2 -2
- simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +2 -2
- simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +2 -2
- simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +2 -2
- simtools/schemas/model_parameters/mirror_class.schema.yml +2 -2
- simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +2 -2
- simtools/schemas/model_parameters/mirror_focal_length.schema.yml +2 -2
- simtools/schemas/model_parameters/mirror_list.schema.yml +2 -2
- simtools/schemas/model_parameters/mirror_offset.schema.yml +2 -2
- simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +2 -2
- simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +2 -2
- simtools/schemas/model_parameters/multiplicity_offset.schema.yml +2 -2
- simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +2 -2
- simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +2 -2
- simtools/schemas/model_parameters/nsb_offaxis.schema.yml +2 -2
- simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +2 -2
- simtools/schemas/model_parameters/num_gains.schema.yml +2 -2
- simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +2 -2
- simtools/schemas/model_parameters/optics_properties.schema.yml +2 -2
- simtools/schemas/model_parameters/pedestal_events.schema.yml +7 -3
- simtools/schemas/model_parameters/photon_delay.schema.yml +2 -2
- simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +2 -2
- simtools/schemas/model_parameters/pm_average_gain.schema.yml +2 -2
- simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +2 -2
- simtools/schemas/model_parameters/pm_gain_index.schema.yml +2 -2
- simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +2 -2
- simtools/schemas/model_parameters/pm_transit_time.schema.yml +2 -2
- simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +2 -2
- simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +2 -2
- simtools/schemas/model_parameters/qe_variation.schema.yml +2 -2
- simtools/schemas/model_parameters/quantum_efficiency.schema.yml +2 -2
- simtools/schemas/model_parameters/random_focal_length.schema.yml +2 -2
- simtools/schemas/model_parameters/random_generator.schema.yml +2 -2
- simtools/schemas/model_parameters/random_mono_probability.schema.yml +2 -2
- simtools/schemas/model_parameters/sampled_output.schema.yml +2 -2
- simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +2 -2
- simtools/schemas/model_parameters/store_photoelectrons.schema.yml +2 -2
- simtools/schemas/model_parameters/tailcut_scale.schema.yml +2 -2
- simtools/schemas/model_parameters/telescope_axis_height.schema.yml +2 -2
- simtools/schemas/model_parameters/telescope_random_angle.schema.yml +2 -2
- simtools/schemas/model_parameters/telescope_random_error.schema.yml +2 -2
- simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +2 -2
- simtools/schemas/model_parameters/telescope_transmission.schema.yml +2 -2
- simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +2 -2
- simtools/schemas/model_parameters/teltrig_min_time.schema.yml +2 -2
- simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +2 -2
- simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +2 -2
- simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +2 -2
- simtools/schemas/model_parameters/transit_time_error.schema.yml +2 -2
- simtools/schemas/model_parameters/transit_time_jitter.schema.yml +2 -2
- simtools/schemas/model_parameters/trigger_current_limit.schema.yml +2 -2
- simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +2 -2
- simtools/schemas/model_parameters/trigger_pixels.schema.yml +2 -2
- simtools/schemas/production_configuration_metrics.schema.yml +2 -2
- simtools/simtel/simtel_config_reader.py +21 -17
- simtools/simtel/simtel_config_writer.py +258 -66
- simtools/simtel/simtel_io_event_reader.py +301 -194
- simtools/simtel/simtel_io_event_writer.py +207 -227
- simtools/simtel/simtel_io_file_info.py +62 -0
- simtools/simtel/simtel_io_histogram.py +10 -14
- simtools/simtel/simtel_io_histograms.py +2 -2
- simtools/simtel/simtel_io_metadata.py +106 -0
- simtools/simtel/simulator_array.py +28 -14
- simtools/simtel/simulator_camera_efficiency.py +12 -6
- simtools/simtel/simulator_light_emission.py +85 -45
- simtools/simtel/simulator_ray_tracing.py +16 -6
- simtools/simulator.py +286 -89
- simtools/testing/configuration.py +5 -0
- simtools/testing/helpers.py +18 -0
- simtools/testing/sim_telarray_metadata.py +212 -0
- simtools/testing/validate_output.py +16 -6
- simtools/utils/general.py +18 -27
- simtools/utils/names.py +32 -10
- simtools/visualization/plot_array_layout.py +242 -0
- simtools/visualization/plot_pixels.py +681 -0
- simtools/visualization/visualize.py +5 -221
- simtools/applications/production_generate_simulation_config.py +0 -162
- simtools/applications/production_scale_events.py +0 -185
- simtools/layout/ctao_array_layouts.py +0 -172
- simtools/production_configuration/event_scaler.py +0 -120
- simtools/production_configuration/generate_simulation_config.py +0 -158
- {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/licenses/LICENSE +0 -0
- {gammasimtools-0.15.0.dist-info → gammasimtools-0.17.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"""Test consistency of sim_telarray metadata."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
from simtools.simtel.simtel_config_reader import SimtelConfigReader
|
|
8
|
+
from simtools.simtel.simtel_config_writer import sim_telarray_random_seeds
|
|
9
|
+
from simtools.simtel.simtel_io_file_info import get_corsika_run_number
|
|
10
|
+
from simtools.simtel.simtel_io_metadata import (
|
|
11
|
+
get_sim_telarray_telescope_id,
|
|
12
|
+
read_sim_telarray_metadata,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
_logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def assert_sim_telarray_metadata(file, array_model):
|
|
19
|
+
"""
|
|
20
|
+
Assert consistency of sim_telarray metadata with given array model.
|
|
21
|
+
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
file: Path
|
|
25
|
+
Path to the sim_telarray file.
|
|
26
|
+
array_model: ArrayModel
|
|
27
|
+
Array model to compare with.
|
|
28
|
+
"""
|
|
29
|
+
global_meta, telescope_meta = read_sim_telarray_metadata(file)
|
|
30
|
+
_logger.info(f"Found metadata in sim_telarray file for {len(telescope_meta)} telescopes")
|
|
31
|
+
site_parameter_mismatch = _assert_model_parameters(global_meta, array_model.site_model)
|
|
32
|
+
sim_telarray_seed_mismatch = _assert_sim_telarray_seed(
|
|
33
|
+
global_meta, array_model.sim_telarray_seeds, file
|
|
34
|
+
)
|
|
35
|
+
if sim_telarray_seed_mismatch:
|
|
36
|
+
site_parameter_mismatch.append(sim_telarray_seed_mismatch)
|
|
37
|
+
|
|
38
|
+
if len(telescope_meta) != len(array_model.telescope_model):
|
|
39
|
+
raise ValueError(
|
|
40
|
+
f"Number of telescopes in sim_telarray file ({len(telescope_meta)}) does not match "
|
|
41
|
+
f"number of telescopes in array model ({len(array_model.telescope_model)})"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
for telescope_name in array_model.telescope_model.keys():
|
|
45
|
+
if not get_sim_telarray_telescope_id(telescope_name, file):
|
|
46
|
+
raise ValueError(f"Telescope {telescope_name} not found in sim_telarray file metadata")
|
|
47
|
+
|
|
48
|
+
telescope_parameter_mismatch = [
|
|
49
|
+
_assert_model_parameters(telescope_meta[i], model)
|
|
50
|
+
for i, model in enumerate(array_model.telescope_model.values(), start=1)
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
# ensure printout of all mismatches, not only those found first
|
|
54
|
+
telescope_parameter_mismatch.append(site_parameter_mismatch)
|
|
55
|
+
if any(len(m) > 0 for m in telescope_parameter_mismatch):
|
|
56
|
+
mismatches = [m for m in telescope_parameter_mismatch if len(m) > 0]
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"Telescope or site model parameters do not match sim_telarray metadata: {mismatches}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _assert_model_parameters(metadata, model):
|
|
63
|
+
"""
|
|
64
|
+
Assert that model parameter values matches the values in the sim_telarray metadata.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
metadata: dict
|
|
69
|
+
Metadata dictionary.
|
|
70
|
+
model: SiteModel, TelescopeModel
|
|
71
|
+
Model to compare with.
|
|
72
|
+
|
|
73
|
+
Returns
|
|
74
|
+
-------
|
|
75
|
+
invalid_parameter_list: list
|
|
76
|
+
List of parameters that do not match.
|
|
77
|
+
|
|
78
|
+
"""
|
|
79
|
+
config_reader = SimtelConfigReader()
|
|
80
|
+
|
|
81
|
+
invalid_parameter_list = []
|
|
82
|
+
|
|
83
|
+
for param in model.parameters:
|
|
84
|
+
sim_telarray_name = _sim_telarray_name_from_parameter_name(param)
|
|
85
|
+
if sim_telarray_name in metadata.keys():
|
|
86
|
+
parameter_type = model.parameters[param]["type"]
|
|
87
|
+
if parameter_type not in ("string", "dict", "boolean"):
|
|
88
|
+
value, _ = config_reader.extract_value_from_sim_telarray_column(
|
|
89
|
+
[metadata[sim_telarray_name]], parameter_type
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
value = metadata[sim_telarray_name]
|
|
93
|
+
value = (int)(value) if value.isnumeric() else value
|
|
94
|
+
|
|
95
|
+
if not is_equal(value, model.parameters[param]["value"], parameter_type):
|
|
96
|
+
invalid_parameter_list.append(
|
|
97
|
+
f"Parameter {param} mismatch between sim_telarray file: {value}, "
|
|
98
|
+
f"and model: {model.parameters[param]['value']}"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return invalid_parameter_list
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _assert_sim_telarray_seed(metadata, sim_telarray_seeds, file=None):
|
|
105
|
+
"""
|
|
106
|
+
Assert that sim_telarray seed matches the values in the sim_telarray metadata.
|
|
107
|
+
|
|
108
|
+
Regenerate seeds using the sim_telarray_random_seeds function and compare with the metadata.
|
|
109
|
+
|
|
110
|
+
Parameters
|
|
111
|
+
----------
|
|
112
|
+
metadata: dict
|
|
113
|
+
Metadata dictionary.
|
|
114
|
+
sim_telarray_seeds: dict
|
|
115
|
+
Dictionary of sim_telarray seeds.
|
|
116
|
+
file : Path
|
|
117
|
+
Path to the sim_telarray file.
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
invalid_parameter_list: list
|
|
122
|
+
Error message if sim_telarray seeds do not match.
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
if not sim_telarray_seeds or not metadata:
|
|
126
|
+
return None
|
|
127
|
+
|
|
128
|
+
if "instrument_seed" in metadata.keys() and "instrument_instances" in metadata.keys():
|
|
129
|
+
if str(metadata.get("instrument_seed")) != str(sim_telarray_seeds.get("seed")):
|
|
130
|
+
return (
|
|
131
|
+
"Parameter instrument_seed mismatch between sim_telarray file: "
|
|
132
|
+
f"{metadata['instrument_seed']}, and model: {sim_telarray_seeds.get('seed')}"
|
|
133
|
+
)
|
|
134
|
+
_logger.info(
|
|
135
|
+
f"sim_telarray_seed in sim_telarray file: {metadata['instrument_seed']}, "
|
|
136
|
+
f"and model: {sim_telarray_seeds.get('seed')}"
|
|
137
|
+
)
|
|
138
|
+
if file:
|
|
139
|
+
run_number_modified = get_corsika_run_number(file) - 1
|
|
140
|
+
test_seeds = sim_telarray_random_seeds(
|
|
141
|
+
int(metadata["instrument_seed"]), int(metadata["instrument_instances"])
|
|
142
|
+
)
|
|
143
|
+
# no +1 as in sim_telarray (as we count from 0)
|
|
144
|
+
seed_used = run_number_modified % int(metadata["instrument_instances"])
|
|
145
|
+
if str(metadata.get("rng_select_seed")) != str(test_seeds[seed_used]):
|
|
146
|
+
return (
|
|
147
|
+
"Parameter rng_select_seed mismatch between sim_telarray file: "
|
|
148
|
+
f"{metadata['rng_select_seed']}, and model: {test_seeds[seed_used]}"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
return None
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def _sim_telarray_name_from_parameter_name(parameter_name):
|
|
155
|
+
"""Return sim_telarray parameter name. Some specific fine tuning."""
|
|
156
|
+
# parameters like "reference_point_latitude"
|
|
157
|
+
sim_telarray_name = parameter_name.replace("reference_point_", "")
|
|
158
|
+
|
|
159
|
+
if sim_telarray_name == "altitude":
|
|
160
|
+
return "corsika_observation_level"
|
|
161
|
+
if sim_telarray_name == "array_triggers":
|
|
162
|
+
return None
|
|
163
|
+
|
|
164
|
+
return sim_telarray_name
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def is_equal(value1, value2, value_type):
|
|
168
|
+
"""
|
|
169
|
+
Check if two values are equal based on their type.
|
|
170
|
+
|
|
171
|
+
The complexity of this function reflects the complexity of the sim_telarray
|
|
172
|
+
metadata output.
|
|
173
|
+
|
|
174
|
+
Parameters
|
|
175
|
+
----------
|
|
176
|
+
value1: any
|
|
177
|
+
First value to compare.
|
|
178
|
+
value2: any
|
|
179
|
+
Second value to compare.
|
|
180
|
+
value_type: str
|
|
181
|
+
Type of the values ('string', 'dict', etc.).
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
bool
|
|
186
|
+
True if the values are equal, False otherwise.
|
|
187
|
+
"""
|
|
188
|
+
value1 = value1[0] if isinstance(value1, tuple) else value1
|
|
189
|
+
value2 = value2[0] if isinstance(value2, tuple) else value2
|
|
190
|
+
if value1 is None or value2 is None:
|
|
191
|
+
if value1 in ("none", None) and value2 in ("none", None):
|
|
192
|
+
return True
|
|
193
|
+
if value_type == "string":
|
|
194
|
+
return str(value1).strip() == str(value2).strip()
|
|
195
|
+
if value_type == "dict":
|
|
196
|
+
return value1 == value2
|
|
197
|
+
if value_type == "boolean":
|
|
198
|
+
return bool(value1) == bool(value2)
|
|
199
|
+
return _is_equal_floats_or_ints(value1, value2)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _is_equal_floats_or_ints(value1, value2):
|
|
203
|
+
"""Check if floats and ints are equal."""
|
|
204
|
+
if isinstance(value1, np.ndarray | list) and isinstance(value2, np.ndarray | list):
|
|
205
|
+
return bool(np.allclose(np.array(value1), np.array(value2), rtol=1e-10))
|
|
206
|
+
if isinstance(value1, list) and isinstance(value2, float | int | np.integer | np.floating):
|
|
207
|
+
if all(x == value1[0] for x in value1):
|
|
208
|
+
return bool(np.isclose(float(value1[0]), float(value2), rtol=1e-10))
|
|
209
|
+
if isinstance(value2, list) and isinstance(value1, float | int | np.integer | np.floating):
|
|
210
|
+
if all(x == value2[0] for x in value2):
|
|
211
|
+
return bool(np.isclose(float(value1), float(value2[0]), rtol=1e-10))
|
|
212
|
+
return bool(np.isclose(float(value1), float(value2), rtol=1e-10))
|
|
@@ -64,11 +64,18 @@ def _validate_output_files(config, integration_test):
|
|
|
64
64
|
|
|
65
65
|
def _test_simtel_cfg_files(config, integration_test, from_command_line, from_config_file):
|
|
66
66
|
"""Test simtel cfg files."""
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
67
|
+
cfg_files = integration_test.get("TEST_SIMTEL_CFG_FILES", {})
|
|
68
|
+
if isinstance(from_command_line, list):
|
|
69
|
+
sources = from_command_line
|
|
70
|
+
elif isinstance(from_config_file, list):
|
|
71
|
+
sources = from_config_file
|
|
72
|
+
else:
|
|
73
|
+
sources = [from_command_line or from_config_file]
|
|
74
|
+
for version in sources:
|
|
75
|
+
cfg = cfg_files.get(version)
|
|
76
|
+
if cfg:
|
|
77
|
+
_validate_simtel_cfg_files(config, cfg)
|
|
78
|
+
break
|
|
72
79
|
|
|
73
80
|
|
|
74
81
|
def _validate_reference_output_file(config, integration_test):
|
|
@@ -95,7 +102,10 @@ def _validate_output_path_and_file(config, integration_file_tests):
|
|
|
95
102
|
|
|
96
103
|
output_file_path = Path(output_path) / file_test["FILE"]
|
|
97
104
|
_logger.info(f"Checking path: {output_file_path}")
|
|
98
|
-
|
|
105
|
+
try:
|
|
106
|
+
assert output_file_path.exists()
|
|
107
|
+
except AssertionError as exc:
|
|
108
|
+
raise AssertionError(f"Output file {output_file_path} does not exist. ") from exc
|
|
99
109
|
|
|
100
110
|
if "EXPECTED_OUTPUT" in file_test:
|
|
101
111
|
assert assertions.check_output_from_sim_telarray(
|
simtools/utils/general.py
CHANGED
|
@@ -133,28 +133,13 @@ def collect_data_from_http(url):
|
|
|
133
133
|
try:
|
|
134
134
|
with tempfile.NamedTemporaryFile(mode="w+t") as tmp_file:
|
|
135
135
|
urllib.request.urlretrieve(url, tmp_file.name)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
except yaml.constructor.ConstructorError:
|
|
140
|
-
data = _load_yaml_using_astropy(tmp_file)
|
|
141
|
-
elif url.endswith("json"):
|
|
142
|
-
data = json.load(tmp_file)
|
|
143
|
-
elif url.endswith("list"):
|
|
144
|
-
lines = tmp_file.readlines()
|
|
145
|
-
data = [line.strip() for line in lines]
|
|
146
|
-
else:
|
|
147
|
-
msg = f"File extension of {url} not supported (should be json or yaml)"
|
|
148
|
-
_logger.error(msg)
|
|
149
|
-
raise TypeError(msg)
|
|
136
|
+
data = _collect_data_from_different_file_types(
|
|
137
|
+
tmp_file, url, Path(url).suffix.lower(), None
|
|
138
|
+
)
|
|
150
139
|
except TypeError as exc:
|
|
151
|
-
|
|
152
|
-
_logger.error(msg)
|
|
153
|
-
raise TypeError(msg) from exc
|
|
140
|
+
raise TypeError(f"Invalid url {url}") from exc
|
|
154
141
|
except urllib.error.HTTPError as exc:
|
|
155
|
-
|
|
156
|
-
_logger.error(msg)
|
|
157
|
-
raise FileNotFoundError(msg) from exc
|
|
142
|
+
raise FileNotFoundError(f"Failed to download file from {url}") from exc
|
|
158
143
|
|
|
159
144
|
_logger.debug(f"Downloaded file from {url}")
|
|
160
145
|
return data
|
|
@@ -182,20 +167,26 @@ def collect_data_from_file(file_name, yaml_document=None):
|
|
|
182
167
|
suffix = Path(file_name).suffix.lower()
|
|
183
168
|
try:
|
|
184
169
|
with open(file_name, encoding="utf-8") as file:
|
|
185
|
-
|
|
186
|
-
return json.load(file)
|
|
187
|
-
if suffix == ".list":
|
|
188
|
-
return [line.strip() for line in file.readlines()]
|
|
189
|
-
if suffix in [".yml", ".yaml"]:
|
|
190
|
-
return _collect_data_from_yaml_file(file, file_name, yaml_document)
|
|
170
|
+
return _collect_data_from_different_file_types(file, file_name, suffix, yaml_document)
|
|
191
171
|
# broad exception to catch all possible errors in reading the file
|
|
192
172
|
except Exception as exc: # pylint: disable=broad-except
|
|
193
173
|
raise type(exc)(f"Failed to read file {file_name}: {exc}") from exc
|
|
194
174
|
return None
|
|
195
175
|
|
|
196
176
|
|
|
177
|
+
def _collect_data_from_different_file_types(file, file_name, suffix, yaml_document):
|
|
178
|
+
"""Collect data from different file types."""
|
|
179
|
+
if suffix == ".json":
|
|
180
|
+
return json.load(file)
|
|
181
|
+
if suffix in (".list", ".txt"):
|
|
182
|
+
return [line.strip() for line in file.readlines()]
|
|
183
|
+
if suffix in [".yml", ".yaml"]:
|
|
184
|
+
return _collect_data_from_yaml_file(file, file_name, yaml_document)
|
|
185
|
+
raise TypeError(f"File type {suffix} not supported.")
|
|
186
|
+
|
|
187
|
+
|
|
197
188
|
def _collect_data_from_yaml_file(file, file_name, yaml_document):
|
|
198
|
-
"""Collect data from a yaml file."""
|
|
189
|
+
"""Collect data from a yaml file (allow for multi-document yaml files)."""
|
|
199
190
|
try:
|
|
200
191
|
return yaml.safe_load(file)
|
|
201
192
|
except yaml.constructor.ConstructorError:
|
simtools/utils/names.py
CHANGED
|
@@ -19,7 +19,11 @@ from pathlib import Path
|
|
|
19
19
|
|
|
20
20
|
import yaml
|
|
21
21
|
|
|
22
|
-
from simtools.constants import
|
|
22
|
+
from simtools.constants import (
|
|
23
|
+
MODEL_PARAMETER_DESCRIPTION_METASCHEMA,
|
|
24
|
+
MODEL_PARAMETER_SCHEMA_PATH,
|
|
25
|
+
SCHEMA_PATH,
|
|
26
|
+
)
|
|
23
27
|
|
|
24
28
|
_logger = logging.getLogger(__name__)
|
|
25
29
|
|
|
@@ -59,6 +63,21 @@ def array_elements():
|
|
|
59
63
|
return yaml.safe_load(file)["data"]
|
|
60
64
|
|
|
61
65
|
|
|
66
|
+
@cache
|
|
67
|
+
def simulation_software():
|
|
68
|
+
"""
|
|
69
|
+
Get simulation software names from the meta schema definition.
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
list
|
|
74
|
+
List of simulation software names.
|
|
75
|
+
"""
|
|
76
|
+
with open(Path(MODEL_PARAMETER_DESCRIPTION_METASCHEMA), encoding="utf-8") as file:
|
|
77
|
+
schema = yaml.safe_load(file)
|
|
78
|
+
return schema["definitions"]["SimulationSoftwareName"]["enum"]
|
|
79
|
+
|
|
80
|
+
|
|
62
81
|
@cache
|
|
63
82
|
def site_names():
|
|
64
83
|
"""
|
|
@@ -129,15 +148,18 @@ def _load_model_parameters():
|
|
|
129
148
|
"""
|
|
130
149
|
Get model parameters properties from schema files.
|
|
131
150
|
|
|
151
|
+
For schema files including multiple schemas, only the first one is returned
|
|
152
|
+
(as this is the most recent definition).
|
|
153
|
+
|
|
132
154
|
Returns
|
|
133
155
|
-------
|
|
134
156
|
dict
|
|
135
157
|
Model parameters definitions for all model parameters.
|
|
136
158
|
"""
|
|
137
159
|
_parameters = {}
|
|
138
|
-
for schema_file in
|
|
160
|
+
for schema_file in Path(MODEL_PARAMETER_SCHEMA_PATH).rglob("*.yml"):
|
|
139
161
|
with open(schema_file, encoding="utf-8") as f:
|
|
140
|
-
data = yaml.
|
|
162
|
+
data = next(yaml.safe_load_all(f))
|
|
141
163
|
_parameters[data["name"]] = data
|
|
142
164
|
return _parameters
|
|
143
165
|
|
|
@@ -512,7 +534,7 @@ def get_collection_name_from_parameter_name(parameter_name):
|
|
|
512
534
|
|
|
513
535
|
def get_simulation_software_name_from_parameter_name(
|
|
514
536
|
parameter_name,
|
|
515
|
-
|
|
537
|
+
software_name="sim_telarray",
|
|
516
538
|
set_meta_parameter=False,
|
|
517
539
|
):
|
|
518
540
|
"""
|
|
@@ -541,7 +563,7 @@ def get_simulation_software_name_from_parameter_name(
|
|
|
541
563
|
|
|
542
564
|
for software in _parameter.get("simulation_software", []):
|
|
543
565
|
if (
|
|
544
|
-
software.get("name") ==
|
|
566
|
+
software.get("name") == software_name
|
|
545
567
|
and software.get("set_meta_parameter", False) is set_meta_parameter
|
|
546
568
|
):
|
|
547
569
|
return software.get("internal_parameter_name", parameter_name)
|
|
@@ -636,7 +658,7 @@ def generate_file_name(
|
|
|
636
658
|
"""
|
|
637
659
|
Generate a file name for output, config, or plotting.
|
|
638
660
|
|
|
639
|
-
Used e.g., to generate
|
|
661
|
+
Used e.g., to generate camera_efficiency and ray_tracing output files.
|
|
640
662
|
|
|
641
663
|
Parameters
|
|
642
664
|
----------
|
|
@@ -668,10 +690,10 @@ def generate_file_name(
|
|
|
668
690
|
str
|
|
669
691
|
File name.
|
|
670
692
|
"""
|
|
671
|
-
name = f"{file_type}
|
|
672
|
-
name += f"
|
|
673
|
-
name += f"
|
|
674
|
-
name += f"
|
|
693
|
+
name = f"{file_type}_{site}_{telescope_model_name}"
|
|
694
|
+
name += f"_d{source_distance:.1f}km" if source_distance is not None else ""
|
|
695
|
+
name += f"_za{float(zenith_angle):.1f}deg"
|
|
696
|
+
name += f"_off{off_axis_angle:.3f}deg" if off_axis_angle is not None else ""
|
|
675
697
|
name += f"_azm{round(azimuth_angle):03}deg" if azimuth_angle is not None else ""
|
|
676
698
|
name += f"_mirror{mirror_number}" if mirror_number is not None else ""
|
|
677
699
|
name += f"_{label}" if label is not None else ""
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
"""Plot array elements for a layout."""
|
|
3
|
+
|
|
4
|
+
from collections import Counter
|
|
5
|
+
|
|
6
|
+
import astropy.units as u
|
|
7
|
+
import matplotlib.patches as mpatches
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
from astropy.table import Column
|
|
10
|
+
from matplotlib.collections import PatchCollection
|
|
11
|
+
|
|
12
|
+
from simtools.utils import geometry as transf
|
|
13
|
+
from simtools.utils import names
|
|
14
|
+
from simtools.visualization import legend_handlers as leg_h
|
|
15
|
+
|
|
16
|
+
__all__ = ["get_telescope_patch", "plot_array_layout"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def plot_array_layout(
|
|
20
|
+
telescopes,
|
|
21
|
+
show_tel_label=False,
|
|
22
|
+
axes_range=None,
|
|
23
|
+
marker_scaling=1.0,
|
|
24
|
+
background_telescopes=None,
|
|
25
|
+
):
|
|
26
|
+
"""
|
|
27
|
+
Plot telescope array layout.
|
|
28
|
+
|
|
29
|
+
Parameters
|
|
30
|
+
----------
|
|
31
|
+
telescopes : Table
|
|
32
|
+
Telescope data table.
|
|
33
|
+
show_tel_label : bool
|
|
34
|
+
Show telescope labels (default False).
|
|
35
|
+
axes_range : float or None
|
|
36
|
+
Axis range, auto if None.
|
|
37
|
+
marker_scaling : float
|
|
38
|
+
Marker size scale factor.
|
|
39
|
+
background_telescopes : Table or None
|
|
40
|
+
Optional background telescope table.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
fig : Figure
|
|
45
|
+
Matplotlib figure object.
|
|
46
|
+
"""
|
|
47
|
+
fig, ax = plt.subplots(1)
|
|
48
|
+
|
|
49
|
+
patches, plot_range = get_patches(ax, telescopes, show_tel_label, axes_range, marker_scaling)
|
|
50
|
+
|
|
51
|
+
if background_telescopes is not None:
|
|
52
|
+
bg_patches, bg_range = get_patches(
|
|
53
|
+
ax, background_telescopes, False, axes_range, marker_scaling
|
|
54
|
+
)
|
|
55
|
+
ax.add_collection(PatchCollection(bg_patches, match_original=True, alpha=0.1))
|
|
56
|
+
if axes_range is None:
|
|
57
|
+
plot_range = max(plot_range, bg_range)
|
|
58
|
+
|
|
59
|
+
update_legend(ax, telescopes)
|
|
60
|
+
finalize_plot(ax, patches, "Easting [m]", "Northing [m]", plot_range)
|
|
61
|
+
|
|
62
|
+
return fig
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_patches(ax, telescopes, show_tel_label, axes_range, marker_scaling):
|
|
66
|
+
"""
|
|
67
|
+
Get plot patches and axis range.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
patches : list
|
|
72
|
+
List of telescope patches.
|
|
73
|
+
axes_range : float
|
|
74
|
+
Calculated or input axis range.
|
|
75
|
+
"""
|
|
76
|
+
pos_x, pos_y = get_positions(telescopes)
|
|
77
|
+
telescopes["pos_x_rotated"] = Column(pos_x)
|
|
78
|
+
telescopes["pos_y_rotated"] = Column(pos_y)
|
|
79
|
+
|
|
80
|
+
patches, radii = create_patches(telescopes, marker_scaling, show_tel_label, ax)
|
|
81
|
+
|
|
82
|
+
if axes_range:
|
|
83
|
+
return patches, axes_range
|
|
84
|
+
|
|
85
|
+
r = max(radii).value
|
|
86
|
+
max_x = max(abs(pos_x.min().value), abs(pos_x.max().value)) + r
|
|
87
|
+
max_y = max(abs(pos_y.min().value), abs(pos_y.max().value)) + r
|
|
88
|
+
updated_axes_range = max(max_x, max_y) * 1.1
|
|
89
|
+
|
|
90
|
+
return patches, updated_axes_range
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@u.quantity_input(x=u.m, y=u.m, radius=u.m)
|
|
94
|
+
def get_telescope_patch(name, x, y, radius):
|
|
95
|
+
"""
|
|
96
|
+
Create patch for a telescope.
|
|
97
|
+
|
|
98
|
+
Returns
|
|
99
|
+
-------
|
|
100
|
+
patch : Patch
|
|
101
|
+
Circle or rectangle patch.
|
|
102
|
+
"""
|
|
103
|
+
tel_obj = leg_h.TelescopeHandler()
|
|
104
|
+
tel_type = names.get_array_element_type_from_name(name)
|
|
105
|
+
x, y, r = x.to(u.m), y.to(u.m), radius.to(u.m)
|
|
106
|
+
|
|
107
|
+
if tel_type == "SCTS":
|
|
108
|
+
return mpatches.Rectangle(
|
|
109
|
+
((x - r / 2).value, (y - r / 2).value),
|
|
110
|
+
width=r.value,
|
|
111
|
+
height=r.value,
|
|
112
|
+
fill=False,
|
|
113
|
+
color=tel_obj.colors_dict[tel_type],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
return mpatches.Circle(
|
|
117
|
+
(x.value, y.value),
|
|
118
|
+
radius=r.value,
|
|
119
|
+
fill=tel_type.startswith("MST"),
|
|
120
|
+
color=tel_obj.colors_dict[tel_type],
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def get_positions(telescopes):
|
|
125
|
+
"""
|
|
126
|
+
Get X/Y positions depending on coordinate system.
|
|
127
|
+
|
|
128
|
+
For ground coordinates, rotates the positions by 90 degrees.
|
|
129
|
+
|
|
130
|
+
Returns
|
|
131
|
+
-------
|
|
132
|
+
x_rot, y_rot : Quantity
|
|
133
|
+
Position coordinates.
|
|
134
|
+
"""
|
|
135
|
+
if "position_x" in telescopes.colnames:
|
|
136
|
+
x, y = telescopes["position_x"], telescopes["position_y"]
|
|
137
|
+
locale_rotate_angle = 90 * u.deg
|
|
138
|
+
elif "utm_east" in telescopes.colnames:
|
|
139
|
+
x, y = telescopes["utm_east"], telescopes["utm_north"]
|
|
140
|
+
locale_rotate_angle = 0 * u.deg
|
|
141
|
+
else:
|
|
142
|
+
raise ValueError("Missing required position columns.")
|
|
143
|
+
|
|
144
|
+
return transf.rotate(x, y, locale_rotate_angle) if locale_rotate_angle != 0 else (x, y)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def create_patches(telescopes, scale, show_label, ax):
|
|
148
|
+
"""
|
|
149
|
+
Create telescope patches and labels.
|
|
150
|
+
|
|
151
|
+
Returns
|
|
152
|
+
-------
|
|
153
|
+
patches : list
|
|
154
|
+
Shape patches.
|
|
155
|
+
radii : list
|
|
156
|
+
Telescope radii.
|
|
157
|
+
"""
|
|
158
|
+
patches, radii = [], []
|
|
159
|
+
fontsize, scale_factor = (4, 2) if len(telescopes) > 30 else (8, 1)
|
|
160
|
+
|
|
161
|
+
for tel in telescopes:
|
|
162
|
+
name = get_telescope_name(tel)
|
|
163
|
+
radius = get_sphere_radius(tel)
|
|
164
|
+
radii.append(radius)
|
|
165
|
+
tel_type = names.get_array_element_type_from_name(name)
|
|
166
|
+
|
|
167
|
+
patches.append(
|
|
168
|
+
get_telescope_patch(
|
|
169
|
+
tel_type,
|
|
170
|
+
tel["pos_x_rotated"],
|
|
171
|
+
tel["pos_y_rotated"],
|
|
172
|
+
scale_factor * radius * scale,
|
|
173
|
+
)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if show_label:
|
|
177
|
+
ax.text(
|
|
178
|
+
tel["pos_x_rotated"].value,
|
|
179
|
+
tel["pos_y_rotated"].value + scale_factor * radius.value,
|
|
180
|
+
name,
|
|
181
|
+
ha="center",
|
|
182
|
+
va="bottom",
|
|
183
|
+
fontsize=fontsize,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
return patches, radii
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def get_telescope_name(tel):
|
|
190
|
+
"""
|
|
191
|
+
Get telescope name.
|
|
192
|
+
|
|
193
|
+
Returns
|
|
194
|
+
-------
|
|
195
|
+
name : str
|
|
196
|
+
Telescope name or fallback identifier.
|
|
197
|
+
"""
|
|
198
|
+
if "telescope_name" in tel.colnames:
|
|
199
|
+
return tel["telescope_name"]
|
|
200
|
+
if "asset_code" in tel.colnames and "sequence_number" in tel.colnames:
|
|
201
|
+
return f"{tel['asset_code']}-{tel['sequence_number']}"
|
|
202
|
+
return f"tel_{tel.index}"
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
def get_sphere_radius(tel):
|
|
206
|
+
"""
|
|
207
|
+
Get telescope sphere radius.
|
|
208
|
+
|
|
209
|
+
Returns
|
|
210
|
+
-------
|
|
211
|
+
radius : Quantity
|
|
212
|
+
Radius with units.
|
|
213
|
+
"""
|
|
214
|
+
return tel["sphere_radius"] if "sphere_radius" in tel.colnames else 10.0 * u.m
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def update_legend(ax, telescopes):
|
|
218
|
+
"""Add legend for telescope types and counts."""
|
|
219
|
+
names_list = [get_telescope_name(tel) for tel in telescopes]
|
|
220
|
+
types = [names.get_array_element_type_from_name(n) for n in names_list]
|
|
221
|
+
counts = Counter(types)
|
|
222
|
+
|
|
223
|
+
objs, labels = [], []
|
|
224
|
+
for t in names.get_list_of_array_element_types():
|
|
225
|
+
if counts[t]:
|
|
226
|
+
objs.append(leg_h.all_telescope_objects[t]())
|
|
227
|
+
labels.append(f"{t} ({counts[t]})")
|
|
228
|
+
|
|
229
|
+
handler_map = {k: v() for k, v in leg_h.legend_handler_map.items()}
|
|
230
|
+
ax.legend(objs, labels, handler_map=handler_map, prop={"size": 11}, loc="best")
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def finalize_plot(ax, patches, x_title, y_title, axes_range):
|
|
234
|
+
"""Finalize plot appearance and limits."""
|
|
235
|
+
ax.add_collection(PatchCollection(patches, match_original=True))
|
|
236
|
+
ax.set(xlabel=x_title, ylabel=y_title)
|
|
237
|
+
ax.tick_params(labelsize=8)
|
|
238
|
+
ax.axis("square")
|
|
239
|
+
if axes_range:
|
|
240
|
+
ax.set_xlim(-axes_range, axes_range)
|
|
241
|
+
ax.set_ylim(-axes_range, axes_range)
|
|
242
|
+
plt.tight_layout()
|