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,189 @@
|
|
|
1
|
+
"""Derive CORSIKA limits for a grid of parameters."""
|
|
2
|
+
|
|
3
|
+
import datetime
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
from astropy.table import Column, Table
|
|
8
|
+
|
|
9
|
+
import simtools.utils.general as gen
|
|
10
|
+
from simtools.data_model.metadata_collector import MetadataCollector
|
|
11
|
+
from simtools.io_operations import io_handler
|
|
12
|
+
from simtools.production_configuration.derive_corsika_limits import LimitCalculator
|
|
13
|
+
|
|
14
|
+
_logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def generate_corsika_limits_grid(args_dict):
|
|
18
|
+
"""
|
|
19
|
+
Generate CORSIKA limits for a grid of parameters.
|
|
20
|
+
|
|
21
|
+
Requires at least one event data file per parameter set.
|
|
22
|
+
|
|
23
|
+
Parameters
|
|
24
|
+
----------
|
|
25
|
+
args_dict : dict
|
|
26
|
+
Dictionary containing command line arguments.
|
|
27
|
+
"""
|
|
28
|
+
event_data_files = gen.collect_data_from_file(args_dict["event_data_files"])["files"]
|
|
29
|
+
telescope_configs = gen.collect_data_from_file(args_dict["telescope_ids"])["telescope_configs"]
|
|
30
|
+
|
|
31
|
+
results = []
|
|
32
|
+
for file_path in event_data_files:
|
|
33
|
+
for array_name, telescope_ids in telescope_configs.items():
|
|
34
|
+
_logger.info(f"Processing file: {file_path} with telescope config: {array_name}")
|
|
35
|
+
result = _process_file(
|
|
36
|
+
file_path,
|
|
37
|
+
array_name,
|
|
38
|
+
telescope_ids,
|
|
39
|
+
args_dict["loss_fraction"],
|
|
40
|
+
args_dict["plot_histograms"],
|
|
41
|
+
)
|
|
42
|
+
result["layout"] = array_name
|
|
43
|
+
results.append(result)
|
|
44
|
+
|
|
45
|
+
write_results(results, args_dict)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _process_file(file_path, array_name, telescope_ids, loss_fraction, plot_histograms):
|
|
49
|
+
"""
|
|
50
|
+
Compute limits for a single file.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
file_path : str
|
|
55
|
+
Path to the event data file.
|
|
56
|
+
array_name : str
|
|
57
|
+
Name of the telescope array configuration.
|
|
58
|
+
telescope_ids : list[int]
|
|
59
|
+
List of telescope IDs to filter the events.
|
|
60
|
+
loss_fraction : float
|
|
61
|
+
Fraction of events to be lost.
|
|
62
|
+
plot_histograms : bool
|
|
63
|
+
Whether to plot histograms.
|
|
64
|
+
|
|
65
|
+
Returns
|
|
66
|
+
-------
|
|
67
|
+
dict
|
|
68
|
+
Dictionary containing the computed limits and metadata.
|
|
69
|
+
"""
|
|
70
|
+
calculator = LimitCalculator(file_path, array_name=array_name, telescope_list=telescope_ids)
|
|
71
|
+
limits = calculator.compute_limits(loss_fraction)
|
|
72
|
+
|
|
73
|
+
if plot_histograms:
|
|
74
|
+
calculator.plot_data(io_handler.IOHandler().get_output_directory())
|
|
75
|
+
|
|
76
|
+
return limits
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def write_results(results, args_dict):
|
|
80
|
+
"""
|
|
81
|
+
Write the computed limits as astropy table to file.
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
results : list[dict]
|
|
86
|
+
List of computed limits.
|
|
87
|
+
args_dict : dict
|
|
88
|
+
Dictionary containing command line arguments.
|
|
89
|
+
"""
|
|
90
|
+
table = _create_results_table(results, args_dict["loss_fraction"])
|
|
91
|
+
|
|
92
|
+
output_dir = io_handler.IOHandler().get_output_directory("corsika_limits")
|
|
93
|
+
output_file = output_dir / args_dict["output_file"]
|
|
94
|
+
|
|
95
|
+
table.write(output_file, format="ascii.ecsv", overwrite=True)
|
|
96
|
+
_logger.info(f"Results saved to {output_file}")
|
|
97
|
+
|
|
98
|
+
MetadataCollector.dump(args_dict, output_file)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _create_results_table(results, loss_fraction):
|
|
102
|
+
"""
|
|
103
|
+
Convert list of simulation results to an astropy Table with metadata.
|
|
104
|
+
|
|
105
|
+
Round values to appropriate precision and add metadata.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
results : list[dict]
|
|
110
|
+
Computed limits per file and telescope configuration.
|
|
111
|
+
loss_fraction : float
|
|
112
|
+
Fraction of lost events (added to metadata).
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
astropy.table.Table
|
|
117
|
+
Table with computed limits.
|
|
118
|
+
"""
|
|
119
|
+
cols = [
|
|
120
|
+
"primary_particle",
|
|
121
|
+
"array_name",
|
|
122
|
+
"telescope_ids",
|
|
123
|
+
"zenith",
|
|
124
|
+
"azimuth",
|
|
125
|
+
"nsb_level",
|
|
126
|
+
"lower_energy_limit",
|
|
127
|
+
"upper_radius_limit",
|
|
128
|
+
"viewcone_radius",
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
columns = {name: [] for name in cols}
|
|
132
|
+
units = {}
|
|
133
|
+
|
|
134
|
+
for res in results:
|
|
135
|
+
_process_result_row(res, cols, columns, units)
|
|
136
|
+
|
|
137
|
+
table_cols = _create_table_columns(cols, columns, units)
|
|
138
|
+
table = Table(table_cols)
|
|
139
|
+
|
|
140
|
+
table.meta.update(
|
|
141
|
+
{
|
|
142
|
+
"created": datetime.datetime.now().isoformat(),
|
|
143
|
+
"description": "Lookup table for CORSIKA limits computed from simulations.",
|
|
144
|
+
"loss_fraction": loss_fraction,
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
return table
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _process_result_row(res, cols, columns, units):
|
|
152
|
+
"""Process a single result row and add values to columns."""
|
|
153
|
+
for k in cols:
|
|
154
|
+
val = res.get(k, None)
|
|
155
|
+
if val is not None:
|
|
156
|
+
val = _round_value(k, val)
|
|
157
|
+
_logger.debug(f"Adding {k}: {val} to column data")
|
|
158
|
+
|
|
159
|
+
if hasattr(val, "unit"):
|
|
160
|
+
columns[k].append(val.value)
|
|
161
|
+
units[k] = val.unit
|
|
162
|
+
else:
|
|
163
|
+
columns[k].append(val)
|
|
164
|
+
if k not in units:
|
|
165
|
+
units[k] = None
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _round_value(key, val):
|
|
169
|
+
"""Round value based on key type."""
|
|
170
|
+
if key == "lower_energy_limit":
|
|
171
|
+
return np.floor(val * 1e3) / 1e3
|
|
172
|
+
if key == "upper_radius_limit":
|
|
173
|
+
return np.ceil(val / 25) * 25
|
|
174
|
+
if key == "viewcone_radius":
|
|
175
|
+
return np.ceil(val / 0.25) * 0.25
|
|
176
|
+
return val
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _create_table_columns(cols, columns, units):
|
|
180
|
+
"""Create table columns with appropriate data types."""
|
|
181
|
+
table_cols = []
|
|
182
|
+
for k in cols:
|
|
183
|
+
col_data = columns[k]
|
|
184
|
+
if any(isinstance(v, list | tuple) for v in col_data):
|
|
185
|
+
col = Column(data=col_data, name=k, unit=units.get(k), dtype=object)
|
|
186
|
+
else:
|
|
187
|
+
col = Column(data=col_data, name=k, unit=units.get(k))
|
|
188
|
+
table_cols.append(col)
|
|
189
|
+
return table_cols
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Calculate the event production statistics based on metrics.
|
|
3
|
+
|
|
4
|
+
Module for calculating the production event statistics based on statistical error metrics.
|
|
5
|
+
Contains the `ProductionStatisticsDerivator` class, which derives the number of events for
|
|
6
|
+
both the entire dataset and specific grid points. Event statistic is calculated using error
|
|
7
|
+
metrics and the evaluator's results.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import astropy.units as u
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
__all__ = ["ProductionStatisticsDerivator"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ProductionStatisticsDerivator:
|
|
17
|
+
"""
|
|
18
|
+
Derives the production statistics based on statistical error metrics.
|
|
19
|
+
|
|
20
|
+
Supports deriving statistics for both the entire dataset and
|
|
21
|
+
specific grid points like energy values.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def __init__(self, evaluator, metrics: dict):
|
|
25
|
+
"""
|
|
26
|
+
Initialize the ProductionStatisticsDerivator with the evaluator and metrics.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
evaluator : StatisticalUncertaintyEvaluator
|
|
31
|
+
The evaluator responsible for calculating metrics and handling event data.
|
|
32
|
+
metrics : dict
|
|
33
|
+
Dictionary containing metrics, including target error for effective area.
|
|
34
|
+
"""
|
|
35
|
+
self.evaluator = evaluator
|
|
36
|
+
self.metrics = metrics
|
|
37
|
+
|
|
38
|
+
def _compute_scaling_factor(self) -> np.ndarray:
|
|
39
|
+
"""
|
|
40
|
+
Compute bin-wise scaling factors based on error metrics.
|
|
41
|
+
|
|
42
|
+
Takes into account the energy range specified in the metrics and
|
|
43
|
+
calculates a separate scaling factor for each energy bin.
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
np.ndarray
|
|
48
|
+
Array of scaling factors for each energy bin.
|
|
49
|
+
"""
|
|
50
|
+
uncertainty_effective_area = self.evaluator.metric_results.get("uncertainty_effective_area")
|
|
51
|
+
relative_uncertainties = uncertainty_effective_area.get("relative_uncertainties")
|
|
52
|
+
energy_range = (
|
|
53
|
+
self.metrics.get("uncertainty_effective_area").get("energy_range").get("value")
|
|
54
|
+
)
|
|
55
|
+
energy_unit = u.Unit(
|
|
56
|
+
self.metrics.get("uncertainty_effective_area").get("energy_range").get("unit")
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
energy_range_converted = np.array(energy_range) * energy_unit
|
|
60
|
+
|
|
61
|
+
bin_edges = self.evaluator.energy_bin_edges
|
|
62
|
+
bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2
|
|
63
|
+
|
|
64
|
+
# Mask for bins within the metric specified energy range
|
|
65
|
+
mask = (bin_centers >= energy_range_converted[0]) & (
|
|
66
|
+
bin_centers <= energy_range_converted[1]
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
scaling_factors = np.zeros_like(relative_uncertainties)
|
|
70
|
+
|
|
71
|
+
target_uncertainty = (
|
|
72
|
+
self.metrics.get("uncertainty_effective_area").get("target_uncertainty").get("value")
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Calculate scaling factor only for bins within the energy range
|
|
76
|
+
# For bins with zero events/uncertainty, use a scaling factor of 0
|
|
77
|
+
valid_bins = mask & (relative_uncertainties > 0)
|
|
78
|
+
scaling_factors[valid_bins] = (relative_uncertainties[valid_bins] / target_uncertainty) ** 2
|
|
79
|
+
|
|
80
|
+
return scaling_factors
|
|
81
|
+
|
|
82
|
+
def derive_statistics(self, return_sum: bool = True) -> u.Quantity:
|
|
83
|
+
"""
|
|
84
|
+
Derive the production statistics based on statistical error metrics.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
return_sum : bool, optional
|
|
89
|
+
If True, returns the sum of production statistics for the entire set of MC events.
|
|
90
|
+
If False, returns the production statistics for each grid point along the energy axis.
|
|
91
|
+
Default is True.
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
u.Quantity
|
|
96
|
+
If 'return_sum' is True, returns the total
|
|
97
|
+
derived production statistics as a u.Quantity.
|
|
98
|
+
If 'return_sum' is False, returns an array of production statistics along the energy
|
|
99
|
+
axis as a u.Quantity.
|
|
100
|
+
"""
|
|
101
|
+
scaling_factors = self._compute_scaling_factor()
|
|
102
|
+
base_events = self._number_of_simulated_events()
|
|
103
|
+
# currently we use the maximum of the scaling factors to scale the events. This is a soft
|
|
104
|
+
# requirement if we want to keep the power law shape of the production statistics.
|
|
105
|
+
scaled_events = base_events * np.max(scaling_factors)
|
|
106
|
+
|
|
107
|
+
if return_sum:
|
|
108
|
+
return np.sum(scaled_events)
|
|
109
|
+
return scaled_events
|
|
110
|
+
|
|
111
|
+
def _number_of_simulated_events(self) -> u.Quantity:
|
|
112
|
+
"""
|
|
113
|
+
Fetch the number of simulated events from the evaluator's data.
|
|
114
|
+
|
|
115
|
+
Returns
|
|
116
|
+
-------
|
|
117
|
+
u.Quantity
|
|
118
|
+
The number of simulated events.
|
|
119
|
+
"""
|
|
120
|
+
return self.evaluator.data.get("simulated_event_histogram")
|
|
121
|
+
|
|
122
|
+
def calculate_production_statistics_at_grid_point(
|
|
123
|
+
self,
|
|
124
|
+
grid_point: tuple,
|
|
125
|
+
) -> u.Quantity:
|
|
126
|
+
"""
|
|
127
|
+
Derive the production statistics for a specific energy grid point.
|
|
128
|
+
|
|
129
|
+
Parameters
|
|
130
|
+
----------
|
|
131
|
+
grid_point : tuple
|
|
132
|
+
The grid point specifying energy, azimuth, zenith, NSB, and offset.
|
|
133
|
+
|
|
134
|
+
Returns
|
|
135
|
+
-------
|
|
136
|
+
float
|
|
137
|
+
The derived production statistics at the specified grid point (energy).
|
|
138
|
+
"""
|
|
139
|
+
energy = grid_point[0]
|
|
140
|
+
bin_edges = self.evaluator.create_bin_edges()
|
|
141
|
+
bin_idx = np.digitize(energy, bin_edges) - 1
|
|
142
|
+
|
|
143
|
+
# Get scaling factors for all bins
|
|
144
|
+
scaling_factors = self._compute_scaling_factor()
|
|
145
|
+
|
|
146
|
+
simulated_event_histogram = self.evaluator.data.get("simulated_event_histogram", [])
|
|
147
|
+
|
|
148
|
+
if bin_idx < 0 or bin_idx >= len(simulated_event_histogram):
|
|
149
|
+
raise ValueError(f"Energy {energy} is outside the range of the simulated events data.")
|
|
150
|
+
|
|
151
|
+
base_events = self._number_of_simulated_events()
|
|
152
|
+
base_event_at_energy = base_events[bin_idx]
|
|
153
|
+
scaling_factor_at_energy = scaling_factors[bin_idx]
|
|
154
|
+
|
|
155
|
+
return base_event_at_energy * scaling_factor_at_energy
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Derives the required statistics for a requested set of production parameters through interpolation.
|
|
3
|
+
|
|
4
|
+
This module provides the `ProductionStatisticsHandler` class, which manages the workflow for
|
|
5
|
+
derivation of required number of events for a simulation production using pre-defined metrics.
|
|
6
|
+
|
|
7
|
+
The module includes functionality to:
|
|
8
|
+
- Initialize evaluators for statistical uncertainty calculations based on input parameters.
|
|
9
|
+
- Perform interpolation using the initialized evaluators to estimate production statistics at a
|
|
10
|
+
query point.
|
|
11
|
+
- Write the results of the interpolation to an output file.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import itertools
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
import astropy.units as u
|
|
20
|
+
|
|
21
|
+
from simtools.production_configuration.calculate_statistical_uncertainties_grid_point import (
|
|
22
|
+
StatisticalUncertaintyEvaluator,
|
|
23
|
+
)
|
|
24
|
+
from simtools.production_configuration.interpolation_handler import InterpolationHandler
|
|
25
|
+
from simtools.utils.general import collect_data_from_file
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ProductionStatisticsHandler:
|
|
29
|
+
"""
|
|
30
|
+
Handles the workflow for deriving production statistics.
|
|
31
|
+
|
|
32
|
+
This class manages the evaluation of statistical uncertainties from DL2 MC event files
|
|
33
|
+
and performs interpolation to estimate the required number of events for a simulation
|
|
34
|
+
production at a specified query point.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, args_dict, output_path):
|
|
38
|
+
"""
|
|
39
|
+
Initialize the manager with the provided arguments.
|
|
40
|
+
|
|
41
|
+
Parameters
|
|
42
|
+
----------
|
|
43
|
+
args_dict : dict
|
|
44
|
+
Dictionary of command-line arguments.
|
|
45
|
+
output_path : Path
|
|
46
|
+
Path to the directory where the event statistics output file will be saved.
|
|
47
|
+
"""
|
|
48
|
+
self.args = args_dict
|
|
49
|
+
self.logger = logging.getLogger(__name__)
|
|
50
|
+
self.output_path = output_path
|
|
51
|
+
self.metrics = collect_data_from_file(self.args["metrics_file"])
|
|
52
|
+
self.evaluator_instances = []
|
|
53
|
+
self.interpolation_handler = None
|
|
54
|
+
self.grid_points_production = self._load_grid_points_production()
|
|
55
|
+
|
|
56
|
+
def _load_grid_points_production(self):
|
|
57
|
+
"""Load grid points from the JSON file."""
|
|
58
|
+
grid_points_production_file = self.args["grid_points_production_file"]
|
|
59
|
+
return collect_data_from_file(grid_points_production_file)
|
|
60
|
+
|
|
61
|
+
def initialize_evaluators(self):
|
|
62
|
+
"""Initialize StatisticalUncertaintyEvaluator instances for the given grid point."""
|
|
63
|
+
if not (
|
|
64
|
+
self.args["base_path"]
|
|
65
|
+
and self.args["zeniths"]
|
|
66
|
+
and self.args["azimuths"]
|
|
67
|
+
and self.args["nsb"]
|
|
68
|
+
and self.args["offsets"]
|
|
69
|
+
):
|
|
70
|
+
self.logger.warning("No files read")
|
|
71
|
+
self.logger.warning(f"Base Path: {self.args['base_path']}")
|
|
72
|
+
self.logger.warning(f"Zeniths: {self.args['zeniths']}")
|
|
73
|
+
self.logger.warning(f"Camera offsets: {self.args['offsets']}")
|
|
74
|
+
return
|
|
75
|
+
|
|
76
|
+
for zenith, azimuth, nsb, offset in itertools.product(
|
|
77
|
+
self.args["zeniths"], self.args["azimuths"], self.args["nsb"], self.args["offsets"]
|
|
78
|
+
):
|
|
79
|
+
file_name = self.args["file_name_template"].format(
|
|
80
|
+
zenith=int(zenith),
|
|
81
|
+
azimuth=azimuth,
|
|
82
|
+
nsb=nsb,
|
|
83
|
+
offset=offset,
|
|
84
|
+
)
|
|
85
|
+
file_path = Path(self.args["base_path"]).joinpath(file_name)
|
|
86
|
+
|
|
87
|
+
if not file_path.exists():
|
|
88
|
+
self.logger.warning(f"File not found: {file_path}. Skipping.")
|
|
89
|
+
continue
|
|
90
|
+
|
|
91
|
+
evaluator = StatisticalUncertaintyEvaluator(
|
|
92
|
+
file_path,
|
|
93
|
+
metrics=self.metrics,
|
|
94
|
+
grid_point=(None, azimuth, zenith, nsb, offset * u.deg),
|
|
95
|
+
)
|
|
96
|
+
evaluator.calculate_metrics()
|
|
97
|
+
self.evaluator_instances.append(evaluator)
|
|
98
|
+
|
|
99
|
+
def perform_interpolation(self):
|
|
100
|
+
"""Perform interpolation for the query point."""
|
|
101
|
+
if not self.evaluator_instances:
|
|
102
|
+
self.logger.error("No evaluators initialized. Cannot perform interpolation.")
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
self.interpolation_handler = InterpolationHandler(
|
|
106
|
+
self.evaluator_instances,
|
|
107
|
+
metrics=self.metrics,
|
|
108
|
+
grid_points_production=self.grid_points_production,
|
|
109
|
+
)
|
|
110
|
+
qrid_points_with_statistics = []
|
|
111
|
+
|
|
112
|
+
interpolated_production_statistics = self.interpolation_handler.interpolate()
|
|
113
|
+
for grid_point, statistics in zip(
|
|
114
|
+
self.grid_points_production, interpolated_production_statistics
|
|
115
|
+
):
|
|
116
|
+
qrid_points_with_statistics.append(
|
|
117
|
+
{
|
|
118
|
+
"grid_point": grid_point,
|
|
119
|
+
"interpolated_production_statistics": float(statistics),
|
|
120
|
+
}
|
|
121
|
+
)
|
|
122
|
+
return qrid_points_with_statistics
|
|
123
|
+
|
|
124
|
+
def write_output(self, production_statistics):
|
|
125
|
+
"""Write the derived event statistics to a file."""
|
|
126
|
+
output_data = (production_statistics,)
|
|
127
|
+
output_filename = self.args["output_file"]
|
|
128
|
+
self.output_path.mkdir(parents=True, exist_ok=True)
|
|
129
|
+
output_file_path = self.output_path.joinpath(output_filename)
|
|
130
|
+
with open(output_file_path, "w", encoding="utf-8") as f:
|
|
131
|
+
json.dump(output_data, f, indent=4)
|
|
132
|
+
self.logger.info(f"Output saved to {self.output_path}")
|
|
133
|
+
|
|
134
|
+
def plot_production_statistics_comparison(self):
|
|
135
|
+
"""Plot the derived event statistics."""
|
|
136
|
+
ax = self.interpolation_handler.plot_comparison()
|
|
137
|
+
plot_path = self.output_path.joinpath("production_statistics_comparison.png")
|
|
138
|
+
plot_path.parent.mkdir(parents=True, exist_ok=True)
|
|
139
|
+
ax.figure.savefig(plot_path)
|
|
140
|
+
self.logger.info(f"Plot saved to {plot_path}")
|
|
141
|
+
|
|
142
|
+
def run(self):
|
|
143
|
+
"""Run the scaling and interpolation workflow."""
|
|
144
|
+
self.logger.info(f"Grid Points File: {self.args['grid_points_production_file']}")
|
|
145
|
+
self.logger.info(f"Metrics File: {self.args['metrics_file']}")
|
|
146
|
+
|
|
147
|
+
self.initialize_evaluators()
|
|
148
|
+
production_statistics = self.perform_interpolation()
|
|
149
|
+
if self.args.get("plot_production_statistics"):
|
|
150
|
+
self.plot_production_statistics_comparison()
|
|
151
|
+
|
|
152
|
+
self.write_output(production_statistics)
|