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,364 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Module defines the `GridGeneration` class.
|
|
3
|
+
|
|
4
|
+
Used to generate a grid of simulation points based on flexible axes definitions such
|
|
5
|
+
azimuth, zenith angle, night-sky background, and camera offset.
|
|
6
|
+
The module handles axis binning, scaling and interpolation of energy thresholds, viewcone,
|
|
7
|
+
and radius limits from a lookup table.
|
|
8
|
+
Additionally, it allows for converting between Altitude/Azimuth and Right Ascension
|
|
9
|
+
Declination coordinates. The resulting grid points are saved to a file.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import logging
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from astropy import units as u
|
|
17
|
+
from astropy.coordinates import AltAz, EarthLocation, SkyCoord
|
|
18
|
+
from astropy.table import Table
|
|
19
|
+
from astropy.units import Quantity
|
|
20
|
+
from scipy.interpolate import griddata
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class GridGeneration:
|
|
24
|
+
"""
|
|
25
|
+
Defines and generates a grid of simulation points based on flexible axes definitions.
|
|
26
|
+
|
|
27
|
+
This class generates a grid of points for a simulation based on parameters such as
|
|
28
|
+
azimuth, zenith angle, night-sky background, and camera offset,
|
|
29
|
+
taking into account axis definitions, scaling, and units and interpolating values
|
|
30
|
+
for simulations from a lookup table.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
axes: dict,
|
|
36
|
+
coordinate_system: str = "zenith_azimuth",
|
|
37
|
+
observing_location=None,
|
|
38
|
+
observing_time=None,
|
|
39
|
+
lookup_table: str | None = None,
|
|
40
|
+
telescope_ids: list | None = None,
|
|
41
|
+
):
|
|
42
|
+
"""
|
|
43
|
+
Initialize the grid with the given axes and coordinate system.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
axes : dict
|
|
48
|
+
Dictionary where each key is the axis name and the value is a dictionary
|
|
49
|
+
defining the axis properties (range, binning, scaling, etc.).
|
|
50
|
+
coordinate_system : str, optional
|
|
51
|
+
The coordinate system for the grid generation (default is 'zenith_azimuth').
|
|
52
|
+
observing_location : EarthLocation, optional
|
|
53
|
+
The location of the observation (latitude, longitude, height).
|
|
54
|
+
observing_time : Time, optional
|
|
55
|
+
The time of the observation. If None, coordinate conversion to RA/Dec not working.
|
|
56
|
+
lookup_table : str, optional
|
|
57
|
+
Path to the lookup table file (ECSV format).
|
|
58
|
+
telescope_ids : list of int, optional
|
|
59
|
+
List of telescope IDs to get the limits for.
|
|
60
|
+
"""
|
|
61
|
+
self._logger = logging.getLogger(__name__)
|
|
62
|
+
|
|
63
|
+
self.axes = axes["axes"] if "axes" in axes else axes
|
|
64
|
+
self.coordinate_system = coordinate_system
|
|
65
|
+
self.observing_location = (
|
|
66
|
+
observing_location
|
|
67
|
+
if observing_location is not None
|
|
68
|
+
else EarthLocation(lat=0.0 * u.deg, lon=0.0 * u.deg, height=0 * u.m)
|
|
69
|
+
)
|
|
70
|
+
self.observing_time = observing_time
|
|
71
|
+
self.lookup_table = lookup_table
|
|
72
|
+
self.telescope_ids = telescope_ids
|
|
73
|
+
|
|
74
|
+
# Store target values for each axis
|
|
75
|
+
self.target_values = self._generate_target_values()
|
|
76
|
+
|
|
77
|
+
if self.lookup_table:
|
|
78
|
+
self._apply_lookup_table_limits()
|
|
79
|
+
|
|
80
|
+
def _generate_target_values(self):
|
|
81
|
+
"""
|
|
82
|
+
Generate target axis values and store them as Quantities.
|
|
83
|
+
|
|
84
|
+
Returns
|
|
85
|
+
-------
|
|
86
|
+
dict
|
|
87
|
+
Dictionary of target values for each axis, stored as Quantity objects.
|
|
88
|
+
"""
|
|
89
|
+
target_values = {}
|
|
90
|
+
for axis_name, axis in self.axes.items():
|
|
91
|
+
axis_range = axis["range"]
|
|
92
|
+
binning = axis["binning"]
|
|
93
|
+
scaling = axis.get("scaling", "linear")
|
|
94
|
+
units = axis.get("units", None)
|
|
95
|
+
|
|
96
|
+
if axis_name == "azimuth":
|
|
97
|
+
# Use circular binning for azimuth
|
|
98
|
+
values = self.create_circular_binning(axis_range, binning)
|
|
99
|
+
elif scaling == "log":
|
|
100
|
+
# Log scaling
|
|
101
|
+
values = np.logspace(np.log10(axis_range[0]), np.log10(axis_range[1]), binning)
|
|
102
|
+
elif scaling == "1/cos":
|
|
103
|
+
# 1/cos scaling
|
|
104
|
+
cos_min = np.cos(np.radians(axis_range[0]))
|
|
105
|
+
cos_max = np.cos(np.radians(axis_range[1]))
|
|
106
|
+
inv_cos_values = np.linspace(1 / cos_min, 1 / cos_max, binning)
|
|
107
|
+
values = np.degrees(np.arccos(1 / inv_cos_values))
|
|
108
|
+
else:
|
|
109
|
+
# Linear scaling
|
|
110
|
+
values = np.linspace(axis_range[0], axis_range[1], binning)
|
|
111
|
+
|
|
112
|
+
if units:
|
|
113
|
+
values = values * u.Unit(units)
|
|
114
|
+
|
|
115
|
+
target_values[axis_name] = values
|
|
116
|
+
|
|
117
|
+
return target_values
|
|
118
|
+
|
|
119
|
+
def _apply_lookup_table_limits(self):
|
|
120
|
+
"""Apply limits from the lookup table and interpolate values."""
|
|
121
|
+
lookup_table = Table.read(self.lookup_table, format="ascii.ecsv")
|
|
122
|
+
|
|
123
|
+
matching_rows = [
|
|
124
|
+
row for row in lookup_table if set(self.telescope_ids) == set(row["telescope_ids"])
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
if not matching_rows:
|
|
128
|
+
raise ValueError(
|
|
129
|
+
f"No matching rows in the lookup table for telescope_ids: {self.telescope_ids}"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
def extract_array(field, transform=lambda x: x):
|
|
133
|
+
return np.array([transform(row[field]) for row in matching_rows])
|
|
134
|
+
|
|
135
|
+
zeniths = extract_array("zenith")
|
|
136
|
+
azimuths = extract_array("azimuth", lambda x: x % 360)
|
|
137
|
+
nsb_values = extract_array("nsb", lambda x: 1 if x == "dark" else 5)
|
|
138
|
+
lower_energy_thresholds = extract_array("lower_energy_threshold")
|
|
139
|
+
upper_radius_thresholds = extract_array("upper_radius_threshold")
|
|
140
|
+
viewcone_radii = extract_array("viewcone_radius")
|
|
141
|
+
|
|
142
|
+
# Wrap azimuths and repeat others
|
|
143
|
+
azimuths_wrapped = np.concatenate([azimuths + shift for shift in (0, 360, -360)])
|
|
144
|
+
|
|
145
|
+
def repeat_3(arr):
|
|
146
|
+
"""Repeat an array three times."""
|
|
147
|
+
return np.tile(arr, 3)
|
|
148
|
+
|
|
149
|
+
points = np.column_stack(
|
|
150
|
+
(
|
|
151
|
+
repeat_3(zeniths),
|
|
152
|
+
azimuths_wrapped,
|
|
153
|
+
repeat_3(nsb_values),
|
|
154
|
+
)
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
target_grid = (
|
|
158
|
+
np.array(
|
|
159
|
+
np.meshgrid(
|
|
160
|
+
self.target_values["zenith_angle"].value,
|
|
161
|
+
self.target_values["azimuth"].value,
|
|
162
|
+
self.target_values["nsb"].value,
|
|
163
|
+
indexing="ij",
|
|
164
|
+
)
|
|
165
|
+
)
|
|
166
|
+
.reshape(3, -1)
|
|
167
|
+
.T
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
def interpolate(values):
|
|
171
|
+
return griddata(
|
|
172
|
+
points, repeat_3(values), target_grid, method="linear", fill_value=np.nan
|
|
173
|
+
).reshape(
|
|
174
|
+
len(self.target_values["zenith_angle"]),
|
|
175
|
+
len(self.target_values["azimuth"]),
|
|
176
|
+
len(self.target_values["nsb"]),
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
self.interpolated_limits = {
|
|
180
|
+
"energy": interpolate(lower_energy_thresholds),
|
|
181
|
+
"radius": interpolate(upper_radius_thresholds),
|
|
182
|
+
"viewcone": interpolate(viewcone_radii),
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
def create_circular_binning(self, azimuth_range, num_bins):
|
|
186
|
+
"""
|
|
187
|
+
Create bin centers for azimuth angles, handling circular wraparound (0 deg to 360 deg).
|
|
188
|
+
|
|
189
|
+
Parameters
|
|
190
|
+
----------
|
|
191
|
+
azimuth_range : tuple
|
|
192
|
+
(min_azimuth, max_azimuth), can wrap around 0 deg.
|
|
193
|
+
num_bins : int
|
|
194
|
+
Number of bins.
|
|
195
|
+
|
|
196
|
+
Returns
|
|
197
|
+
-------
|
|
198
|
+
np.ndarray
|
|
199
|
+
Array of bin centers.
|
|
200
|
+
"""
|
|
201
|
+
azimuth_min, azimuth_max = azimuth_range
|
|
202
|
+
azimuth_min %= 360 # Normalize to [0, 360)
|
|
203
|
+
azimuth_max %= 360
|
|
204
|
+
|
|
205
|
+
clockwise_distance = (azimuth_max - azimuth_min) % 360
|
|
206
|
+
counterclockwise_distance = (azimuth_min - azimuth_max) % 360
|
|
207
|
+
|
|
208
|
+
if clockwise_distance <= counterclockwise_distance:
|
|
209
|
+
bin_centers = (
|
|
210
|
+
np.linspace(azimuth_min, azimuth_min + clockwise_distance, num_bins, endpoint=True)
|
|
211
|
+
% 360
|
|
212
|
+
)
|
|
213
|
+
else:
|
|
214
|
+
bin_centers = (
|
|
215
|
+
np.linspace(
|
|
216
|
+
azimuth_min, azimuth_min - counterclockwise_distance, num_bins, endpoint=True
|
|
217
|
+
)
|
|
218
|
+
% 360
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
return bin_centers
|
|
222
|
+
|
|
223
|
+
def generate_grid(self) -> list[dict]:
|
|
224
|
+
"""
|
|
225
|
+
Generate the grid based on the required axes and include interpolated limits.
|
|
226
|
+
|
|
227
|
+
Takes energy threshold, viewcone, and radius from the interpolated lookup table.
|
|
228
|
+
|
|
229
|
+
Returns
|
|
230
|
+
-------
|
|
231
|
+
list of dict
|
|
232
|
+
A list of grid points, each represented as a dictionary with axis names
|
|
233
|
+
as keys and axis values as values. Axis values may include units where defined.
|
|
234
|
+
"""
|
|
235
|
+
value_arrays = [value.value for value in self.target_values.values()]
|
|
236
|
+
units = [value.unit for value in self.target_values.values()]
|
|
237
|
+
grid = np.meshgrid(*value_arrays, indexing="ij")
|
|
238
|
+
combinations = np.vstack(list(map(np.ravel, grid))).T
|
|
239
|
+
grid_points = []
|
|
240
|
+
for combination in combinations:
|
|
241
|
+
grid_point = {
|
|
242
|
+
key: Quantity(combination[i], units[i])
|
|
243
|
+
for i, key in enumerate(self.target_values.keys())
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if "energy" in self.interpolated_limits:
|
|
247
|
+
zenith_idx = np.searchsorted(
|
|
248
|
+
self.target_values["zenith_angle"].value, grid_point["zenith_angle"].value
|
|
249
|
+
)
|
|
250
|
+
azimuth_idx = np.searchsorted(
|
|
251
|
+
self.target_values["azimuth"].value, grid_point["azimuth"].value
|
|
252
|
+
)
|
|
253
|
+
nsb_idx = np.searchsorted(self.target_values["nsb"].value, grid_point["nsb"].value)
|
|
254
|
+
energy_lower = self.interpolated_limits["energy"][zenith_idx, azimuth_idx, nsb_idx]
|
|
255
|
+
grid_point["energy_threshold"] = {"lower": energy_lower * u.TeV}
|
|
256
|
+
|
|
257
|
+
if "radius" in self.interpolated_limits:
|
|
258
|
+
radius_value = self.interpolated_limits["radius"][zenith_idx, azimuth_idx, nsb_idx]
|
|
259
|
+
grid_point["radius"] = radius_value * u.m
|
|
260
|
+
|
|
261
|
+
if "viewcone" in self.interpolated_limits:
|
|
262
|
+
viewcone_value = self.interpolated_limits["viewcone"][
|
|
263
|
+
zenith_idx, azimuth_idx, nsb_idx
|
|
264
|
+
]
|
|
265
|
+
grid_point["viewcone"] = viewcone_value * u.deg
|
|
266
|
+
|
|
267
|
+
grid_points.append(grid_point)
|
|
268
|
+
|
|
269
|
+
return grid_points
|
|
270
|
+
|
|
271
|
+
def convert_altaz_to_radec(self, alt, az):
|
|
272
|
+
"""
|
|
273
|
+
Convert Altitude/Azimuth (AltAz) coordinates to Right Ascension/Declination (RA/Dec).
|
|
274
|
+
|
|
275
|
+
Parameters
|
|
276
|
+
----------
|
|
277
|
+
alt : float
|
|
278
|
+
Altitude angle in degrees.
|
|
279
|
+
az : float
|
|
280
|
+
Azimuth angle in degrees.
|
|
281
|
+
|
|
282
|
+
Returns
|
|
283
|
+
-------
|
|
284
|
+
SkyCoord
|
|
285
|
+
SkyCoord object containing the RA/Dec coordinates.
|
|
286
|
+
|
|
287
|
+
Raises
|
|
288
|
+
------
|
|
289
|
+
ValueError
|
|
290
|
+
If observing_time is not set.
|
|
291
|
+
"""
|
|
292
|
+
if self.observing_time is None:
|
|
293
|
+
raise ValueError(
|
|
294
|
+
"Observing time is not set. "
|
|
295
|
+
"Please provide an observing_time to convert coordinates."
|
|
296
|
+
)
|
|
297
|
+
|
|
298
|
+
alt_rad = alt.to(u.rad)
|
|
299
|
+
az_rad = az.to(u.rad)
|
|
300
|
+
aa = AltAz(
|
|
301
|
+
alt=alt_rad,
|
|
302
|
+
az=az_rad,
|
|
303
|
+
location=self.observing_location,
|
|
304
|
+
obstime=self.observing_time,
|
|
305
|
+
)
|
|
306
|
+
skycoord = SkyCoord(aa)
|
|
307
|
+
return skycoord.icrs # Return RA/Dec in ICRS frame
|
|
308
|
+
|
|
309
|
+
def convert_coordinates(self, grid_points: list[dict]) -> list[dict]:
|
|
310
|
+
"""
|
|
311
|
+
Convert the grid points to RA/Dec coordinates if necessary.
|
|
312
|
+
|
|
313
|
+
Parameters
|
|
314
|
+
----------
|
|
315
|
+
grid_points : list of dict
|
|
316
|
+
List of grid points, each represented as a dictionary with axis
|
|
317
|
+
names as keys and values.
|
|
318
|
+
|
|
319
|
+
Returns
|
|
320
|
+
-------
|
|
321
|
+
list of dict
|
|
322
|
+
The grid points with converted RA/Dec coordinates.
|
|
323
|
+
"""
|
|
324
|
+
if self.coordinate_system == "ra_dec":
|
|
325
|
+
for point in grid_points:
|
|
326
|
+
if "zenith_angle" in point and "azimuth" in point:
|
|
327
|
+
alt = (90.0 * u.deg) - point.pop("zenith_angle")
|
|
328
|
+
az = point.pop("azimuth")
|
|
329
|
+
radec = self.convert_altaz_to_radec(alt, az)
|
|
330
|
+
point["ra"] = radec.ra.deg * u.deg
|
|
331
|
+
point["dec"] = radec.dec.deg * u.deg
|
|
332
|
+
return grid_points
|
|
333
|
+
|
|
334
|
+
def serialize_grid_points(self, grid_points, output_file=None):
|
|
335
|
+
"""Serialize the grid output and save to a file or print to the console."""
|
|
336
|
+
cleaned_points = []
|
|
337
|
+
|
|
338
|
+
for point in grid_points:
|
|
339
|
+
cleaned_point = {}
|
|
340
|
+
for key, value in point.items():
|
|
341
|
+
if isinstance(value, dict):
|
|
342
|
+
# Nested dictionaries
|
|
343
|
+
cleaned_point[key] = {k: self.serialize_quantity(v) for k, v in value.items()}
|
|
344
|
+
else:
|
|
345
|
+
cleaned_point[key] = self.serialize_quantity(value)
|
|
346
|
+
|
|
347
|
+
cleaned_points.append(cleaned_point)
|
|
348
|
+
|
|
349
|
+
output_data = json.dumps(cleaned_points, indent=4)
|
|
350
|
+
|
|
351
|
+
if output_file:
|
|
352
|
+
with open(output_file, "w", encoding="utf-8") as f:
|
|
353
|
+
f.write(output_data)
|
|
354
|
+
self._logger.info(f"Output saved to {output_file}")
|
|
355
|
+
else:
|
|
356
|
+
self._logger.info(output_data)
|
|
357
|
+
return output_data
|
|
358
|
+
|
|
359
|
+
def serialize_quantity(self, value):
|
|
360
|
+
"""Serialize Quantity."""
|
|
361
|
+
if isinstance(value, u.Quantity):
|
|
362
|
+
return {"value": value.value, "unit": str(value.unit)}
|
|
363
|
+
self._logger.warning(f"Unsupported type {type(value)} for serialization. Returning as is.")
|
|
364
|
+
return value
|