gammasimtools 0.18.0__py3-none-any.whl → 0.20.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.18.0.dist-info → gammasimtools-0.20.0.dist-info}/METADATA +24 -69
- gammasimtools-0.20.0.dist-info/RECORD +395 -0
- {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/entry_points.txt +11 -4
- {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/licenses/LICENSE +1 -1
- simtools/_version.py +16 -3
- simtools/applications/calculate_incident_angles.py +182 -0
- simtools/applications/convert_all_model_parameters_from_simtel.py +4 -3
- simtools/applications/convert_geo_coordinates_of_array_elements.py +3 -3
- simtools/applications/db_add_simulation_model_from_repository_to_db.py +17 -14
- simtools/applications/db_add_value_from_json_to_db.py +8 -10
- simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +8 -13
- simtools/applications/db_generate_compound_indexes.py +65 -0
- simtools/applications/db_get_file_from_db.py +12 -24
- simtools/applications/db_get_parameter_from_db.py +4 -4
- simtools/applications/db_inspect_databases.py +20 -10
- simtools/applications/derive_mirror_rnda.py +17 -11
- simtools/applications/derive_psf_parameters.py +59 -309
- simtools/applications/derive_trigger_rates.py +91 -0
- simtools/applications/docs_produce_array_element_report.py +1 -1
- simtools/applications/docs_produce_calibration_reports.py +1 -1
- simtools/applications/docs_produce_model_parameter_reports.py +1 -1
- simtools/applications/docs_produce_simulation_configuration_report.py +1 -1
- simtools/applications/generate_corsika_histograms.py +1 -1
- simtools/applications/generate_default_metadata.py +8 -24
- simtools/applications/generate_simtel_event_data.py +11 -11
- simtools/applications/maintain_simulation_model_add_production_table.py +71 -0
- simtools/applications/maintain_simulation_model_compare_productions.py +98 -0
- simtools/applications/{verify_simulation_model_production_tables.py → maintain_simulation_model_verify_production_tables.py} +9 -1
- simtools/applications/merge_tables.py +2 -2
- simtools/applications/plot_array_layout.py +3 -3
- simtools/applications/plot_simtel_events.py +421 -0
- simtools/applications/plot_tabular_data.py +9 -2
- simtools/applications/plot_tabular_data_for_model_parameter.py +2 -1
- simtools/applications/print_version.py +8 -9
- simtools/applications/production_derive_corsika_limits.py +6 -7
- simtools/applications/production_derive_statistics.py +1 -1
- simtools/applications/production_generate_grid.py +2 -2
- simtools/applications/production_merge_corsika_limits.py +214 -0
- simtools/applications/run_application.py +47 -113
- simtools/applications/simulate_calibration_events.py +166 -0
- simtools/applications/simulate_flasher.py +141 -0
- simtools/applications/{simulate_light_emission.py → simulate_illuminator.py} +35 -99
- simtools/applications/simulate_prod.py +6 -24
- simtools/applications/simulate_prod_htcondor_generator.py +7 -0
- simtools/applications/submit_array_layouts.py +2 -1
- simtools/applications/submit_model_parameter_from_external.py +1 -1
- simtools/applications/validate_camera_efficiency.py +30 -12
- simtools/applications/validate_camera_fov.py +1 -1
- simtools/applications/validate_cumulative_psf.py +1 -1
- simtools/applications/validate_file_using_schema.py +9 -5
- simtools/applications/validate_optics.py +1 -1
- simtools/camera/camera_efficiency.py +61 -45
- simtools/camera/single_photon_electron_spectrum.py +1 -1
- simtools/configuration/commandline_parser.py +46 -11
- simtools/configuration/configurator.py +4 -4
- simtools/corsika/corsika_config.py +45 -25
- simtools/corsika/corsika_histograms.py +6 -5
- simtools/data_model/data_reader.py +2 -3
- simtools/data_model/metadata_collector.py +32 -36
- simtools/data_model/metadata_model.py +15 -12
- simtools/data_model/model_data_writer.py +13 -32
- simtools/data_model/schema.py +74 -24
- simtools/data_model/validate_data.py +42 -12
- simtools/db/db_handler.py +125 -62
- simtools/db/db_model_upload.py +14 -19
- simtools/dependencies.py +98 -30
- simtools/io/ascii_handler.py +279 -0
- simtools/{io_operations → io}/io_handler.py +25 -3
- simtools/job_execution/htcondor_script_generator.py +15 -4
- simtools/layout/array_layout.py +1 -1
- simtools/layout/array_layout_utils.py +51 -12
- simtools/model/array_model.py +41 -5
- simtools/model/flasher_model.py +106 -0
- simtools/model/model_parameter.py +4 -4
- simtools/model/model_repository.py +197 -2
- simtools/model/site_model.py +25 -0
- simtools/model/telescope_model.py +3 -1
- simtools/production_configuration/derive_corsika_limits.py +336 -427
- simtools/production_configuration/derive_production_statistics_handler.py +7 -6
- simtools/production_configuration/generate_production_grid.py +9 -11
- simtools/production_configuration/merge_corsika_limits.py +528 -0
- simtools/ray_tracing/incident_angles.py +706 -0
- simtools/ray_tracing/mirror_panel_psf.py +1 -0
- simtools/ray_tracing/psf_parameter_optimisation.py +792 -0
- simtools/ray_tracing/ray_tracing.py +6 -2
- simtools/reporting/docs_read_parameters.py +150 -62
- simtools/runners/corsika_runner.py +1 -1
- simtools/runners/corsika_simtel_runner.py +14 -5
- simtools/runners/runner_services.py +10 -5
- simtools/runners/simtools_runner.py +267 -0
- simtools/schemas/application_workflow.metaschema.yml +101 -68
- simtools/schemas/input/MST_mirror_2f_measurements.schema.yml +1 -1
- simtools/schemas/input/single_pe_spectrum.schema.yml +1 -1
- simtools/schemas/metadata.metaschema.yml +577 -3
- simtools/schemas/model_parameter.metaschema.yml +6 -6
- simtools/schemas/model_parameter_and_data_schema.metaschema.yml +4 -4
- simtools/schemas/model_parameters/adjust_gain.schema.yml +1 -1
- simtools/schemas/model_parameters/altitude.schema.yml +1 -1
- simtools/schemas/model_parameters/array_coordinates.schema.yml +1 -1
- simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +1 -1
- simtools/schemas/model_parameters/array_element_position_ground.schema.yml +1 -1
- simtools/schemas/model_parameters/array_element_position_utm.schema.yml +1 -1
- simtools/schemas/model_parameters/array_layouts.schema.yml +1 -1
- simtools/schemas/model_parameters/array_triggers.schema.yml +1 -1
- simtools/schemas/model_parameters/array_window.schema.yml +1 -1
- simtools/schemas/model_parameters/asum_clipping.schema.yml +1 -1
- simtools/schemas/model_parameters/asum_offset.schema.yml +1 -1
- simtools/schemas/model_parameters/asum_shaping.schema.yml +1 -1
- simtools/schemas/model_parameters/asum_threshold.schema.yml +1 -1
- simtools/schemas/model_parameters/atmospheric_profile.schema.yml +1 -1
- simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +1 -1
- simtools/schemas/model_parameters/axes_offsets.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_body_diameter.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_body_shape.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_config_file.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_config_rotate.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_degraded_map.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_depth.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_filter.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_pixels.schema.yml +1 -1
- simtools/schemas/model_parameters/camera_transmission.schema.yml +1 -1
- simtools/schemas/model_parameters/channels_per_chip.schema.yml +1 -1
- simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_observation_level.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +1 -1
- simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +3 -3
- simtools/schemas/model_parameters/dark_events.schema.yml +1 -1
- simtools/schemas/model_parameters/default_trigger.schema.yml +1 -1
- simtools/schemas/model_parameters/design_model.schema.yml +1 -1
- simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +1 -1
- simtools/schemas/model_parameters/disc_bins.schema.yml +1 -1
- simtools/schemas/model_parameters/disc_start.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_threshold.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +1 -1
- simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +1 -1
- simtools/schemas/model_parameters/dish_shape_length.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_clipping.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_offset.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_pedsub.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_prescale.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_presum_max.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_shaping.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +1 -1
- simtools/schemas/model_parameters/dsum_threshold.schema.yml +2 -2
- simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +1 -1
- simtools/schemas/model_parameters/effective_focal_length.schema.yml +1 -1
- simtools/schemas/model_parameters/epsg_code.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_amplitude.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_bins.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_long_event_threshold.schema.yml +35 -0
- simtools/schemas/model_parameters/fadc_long_sum_bins.schema.yml +41 -0
- simtools/schemas/model_parameters/fadc_long_sum_offset.schema.yml +38 -0
- simtools/schemas/model_parameters/fadc_max_signal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_max_sum.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_mhz.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_noise.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +1 -1
- simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +1 -1
- simtools/schemas/model_parameters/fake_mirror_list.schema.yml +1 -1
- simtools/schemas/model_parameters/flatfielding.schema.yml +1 -1
- simtools/schemas/model_parameters/focal_length.schema.yml +1 -1
- simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +1 -1
- simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +1 -1
- simtools/schemas/model_parameters/focus_offset.schema.yml +1 -1
- simtools/schemas/model_parameters/gain_variation.schema.yml +1 -1
- simtools/schemas/model_parameters/geomag_horizontal.schema.yml +1 -1
- simtools/schemas/model_parameters/geomag_rotation.schema.yml +1 -1
- simtools/schemas/model_parameters/geomag_vertical.schema.yml +1 -1
- simtools/schemas/model_parameters/hg_lg_variation.schema.yml +1 -1
- simtools/schemas/model_parameters/iobuf_maximum.schema.yml +1 -1
- simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +1 -1
- simtools/schemas/model_parameters/laser_events.schema.yml +1 -1
- simtools/schemas/model_parameters/laser_external_trigger.schema.yml +1 -1
- simtools/schemas/model_parameters/laser_photons.schema.yml +1 -1
- simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +1 -1
- simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +1 -1
- simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +1 -1
- simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +1 -1
- simtools/schemas/model_parameters/laser_var_photons.schema.yml +1 -1
- simtools/schemas/model_parameters/laser_wavelength.schema.yml +1 -1
- simtools/schemas/model_parameters/led_events.schema.yml +1 -1
- simtools/schemas/model_parameters/led_photons.schema.yml +1 -1
- simtools/schemas/model_parameters/led_pulse_offset.schema.yml +1 -1
- simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +1 -1
- simtools/schemas/model_parameters/led_var_photons.schema.yml +1 -1
- simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +1 -1
- simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +50 -1
- simtools/schemas/model_parameters/min_photoelectrons.schema.yml +1 -1
- simtools/schemas/model_parameters/min_photons.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_class.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_focal_length.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_list.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_offset.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_panel_2f_measurements.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +1 -1
- simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +1 -1
- simtools/schemas/model_parameters/multiplicity_offset.schema.yml +1 -1
- simtools/schemas/model_parameters/muon_mono_threshold.schema.yml +1 -1
- simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +1 -1
- simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +1 -1
- simtools/schemas/model_parameters/nsb_offaxis.schema.yml +1 -1
- simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +1 -1
- simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +2 -2
- simtools/schemas/model_parameters/nsb_reference_value.schema.yml +1 -1
- simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +1 -1
- simtools/schemas/model_parameters/nsb_sky_map.schema.yml +1 -1
- simtools/schemas/model_parameters/nsb_spectrum.schema.yml +23 -30
- simtools/schemas/model_parameters/num_gains.schema.yml +1 -1
- simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +1 -1
- simtools/schemas/model_parameters/optics_properties.schema.yml +1 -1
- simtools/schemas/model_parameters/parabolic_dish.schema.yml +1 -1
- simtools/schemas/model_parameters/pedestal_events.schema.yml +1 -1
- simtools/schemas/model_parameters/photon_delay.schema.yml +1 -1
- simtools/schemas/model_parameters/photons_per_run.schema.yml +1 -1
- simtools/schemas/model_parameters/pixel_cells.schema.yml +1 -1
- simtools/schemas/model_parameters/pixels_parallel.schema.yml +1 -1
- simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +1 -1
- simtools/schemas/model_parameters/pm_average_gain.schema.yml +1 -1
- simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +1 -1
- simtools/schemas/model_parameters/pm_gain_index.schema.yml +1 -1
- simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +1 -1
- simtools/schemas/model_parameters/pm_transit_time.schema.yml +1 -1
- simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +1 -1
- simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +1 -1
- simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +1 -1
- simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +1 -1
- simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +11 -1
- simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +1 -1
- simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +1 -1
- simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +1 -1
- simtools/schemas/model_parameters/qe_variation.schema.yml +1 -1
- simtools/schemas/model_parameters/quantum_efficiency.schema.yml +1 -1
- simtools/schemas/model_parameters/random_focal_length.schema.yml +1 -1
- simtools/schemas/model_parameters/random_generator.schema.yml +1 -1
- simtools/schemas/model_parameters/random_mono_probability.schema.yml +1 -1
- simtools/schemas/model_parameters/reference_point_altitude.schema.yml +1 -1
- simtools/schemas/model_parameters/reference_point_latitude.schema.yml +1 -1
- simtools/schemas/model_parameters/reference_point_longitude.schema.yml +1 -1
- simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +1 -1
- simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +1 -1
- simtools/schemas/model_parameters/sampled_output.schema.yml +1 -1
- simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +11 -1
- simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +11 -1
- simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +1 -1
- simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +1 -1
- simtools/schemas/model_parameters/stars.schema.yml +2 -2
- simtools/schemas/model_parameters/store_photoelectrons.schema.yml +1 -1
- simtools/schemas/model_parameters/tailcut_scale.schema.yml +1 -1
- simtools/schemas/model_parameters/telescope_axis_height.schema.yml +1 -1
- simtools/schemas/model_parameters/telescope_random_angle.schema.yml +1 -1
- simtools/schemas/model_parameters/telescope_random_error.schema.yml +1 -1
- simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +1 -1
- simtools/schemas/model_parameters/telescope_transmission.schema.yml +1 -1
- simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +1 -1
- simtools/schemas/model_parameters/teltrig_min_time.schema.yml +1 -1
- simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +1 -1
- simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +1 -1
- simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +1 -1
- simtools/schemas/model_parameters/transit_time_error.schema.yml +1 -1
- simtools/schemas/model_parameters/transit_time_jitter.schema.yml +1 -1
- simtools/schemas/model_parameters/trigger_current_limit.schema.yml +1 -1
- simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +1 -1
- simtools/schemas/model_parameters/trigger_pixels.schema.yml +1 -1
- simtools/schemas/plot_configuration.metaschema.yml +5 -2
- simtools/schemas/production_configuration_metrics.schema.yml +12 -2
- simtools/schemas/production_tables.schema.yml +7 -2
- simtools/simtel/simtel_config_reader.py +2 -2
- simtools/simtel/simtel_config_writer.py +33 -23
- simtools/simtel/simtel_io_event_histograms.py +483 -0
- simtools/simtel/simtel_io_event_reader.py +65 -43
- simtools/simtel/simtel_io_event_writer.py +40 -20
- simtools/simtel/simtel_io_metadata.py +1 -1
- simtools/simtel/simtel_table_reader.py +95 -13
- simtools/simtel/simulator_array.py +138 -10
- simtools/simtel/simulator_camera_efficiency.py +32 -23
- simtools/simtel/simulator_light_emission.py +437 -271
- simtools/simtel/simulator_ray_tracing.py +1 -1
- simtools/simulator.py +105 -147
- simtools/telescope_trigger_rates.py +119 -0
- simtools/testing/configuration.py +24 -26
- simtools/testing/helpers.py +2 -2
- simtools/testing/log_inspector.py +52 -0
- simtools/testing/validate_output.py +87 -37
- simtools/utils/general.py +125 -255
- simtools/utils/geometry.py +56 -0
- simtools/utils/names.py +1 -1
- simtools/visualization/legend_handlers.py +180 -264
- simtools/visualization/plot_array_layout.py +20 -8
- simtools/visualization/plot_incident_angles.py +431 -0
- simtools/visualization/plot_pixels.py +1 -1
- simtools/visualization/plot_simtel_event_histograms.py +376 -0
- simtools/visualization/plot_simtel_events.py +816 -0
- simtools/visualization/plot_tables.py +133 -37
- simtools/visualization/visualize.py +1 -100
- gammasimtools-0.18.0.dist-info/RECORD +0 -376
- simtools/applications/calculate_trigger_rate.py +0 -187
- simtools/applications/generate_sim_telarray_histograms.py +0 -196
- simtools/production_configuration/derive_corsika_limits_grid.py +0 -232
- simtools/simtel/simtel_io_histogram.py +0 -621
- simtools/simtel/simtel_io_histograms.py +0 -552
- {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/WHEEL +0 -0
- {gammasimtools-0.18.0.dist-info → gammasimtools-0.20.0.dist-info}/top_level.txt +0 -0
- /simtools/{io_operations → io}/hdf5_handler.py +0 -0
- /simtools/{io_operations → io}/legacy_data_handler.py +0 -0
- /simtools/{io_operations/io_table_handler.py → io/table_handler.py} +0 -0
|
@@ -0,0 +1,792 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PSF parameter optimisation and fitting routines for mirror alignment and reflection parameters.
|
|
3
|
+
|
|
4
|
+
This module provides functions for loading PSF data, generating random parameter sets,
|
|
5
|
+
running PSF simulations, calculating RMSD, and finding the best-fit parameters for a given
|
|
6
|
+
telescope model.
|
|
7
|
+
|
|
8
|
+
PSF (Point Spread Function) describes how a point source of light is spread out by the
|
|
9
|
+
optical system, and RMSD (Root Mean Squared Deviation) is used as the optimization metric
|
|
10
|
+
to quantify the difference between measured and simulated PSF curves.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import logging
|
|
14
|
+
from collections import OrderedDict
|
|
15
|
+
|
|
16
|
+
import astropy.units as u
|
|
17
|
+
import matplotlib.pyplot as plt
|
|
18
|
+
import numpy as np
|
|
19
|
+
from matplotlib.backends.backend_pdf import PdfPages
|
|
20
|
+
|
|
21
|
+
from simtools.data_model import model_data_writer as writer
|
|
22
|
+
from simtools.model import model_utils
|
|
23
|
+
from simtools.ray_tracing.ray_tracing import RayTracing
|
|
24
|
+
from simtools.utils import general as gen
|
|
25
|
+
from simtools.visualization import visualize
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
# Constants
|
|
30
|
+
RADIUS_CM = "Radius [cm]"
|
|
31
|
+
CUMULATIVE_PSF = "Cumulative PSF"
|
|
32
|
+
|
|
33
|
+
MRRA_RANGE_DEFAULT = 0.004 # Mirror reflection random angle range
|
|
34
|
+
MRF_RANGE_DEFAULT = 0.1 # Mirror reflection fraction range
|
|
35
|
+
MRRA2_RANGE_DEFAULT = 0.03 # Second mirror reflection random angle range
|
|
36
|
+
MAR_RANGE_DEFAULT = 0.005 # Mirror alignment random range
|
|
37
|
+
MAX_OFFSET_DEFAULT = 4.5 # Maximum off-axis angle in degrees
|
|
38
|
+
OFFSET_STEPS_DEFAULT = 0.1 # Step size for off-axis angle sampling
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_psf_data(data_file):
|
|
42
|
+
"""
|
|
43
|
+
Load data from a text file containing cumulative PSF measurements.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
data_file : str
|
|
48
|
+
Name of the data file with the measured cumulative PSF.
|
|
49
|
+
Expected format:
|
|
50
|
+
Column 0: radial distance in mm
|
|
51
|
+
Column 2: cumulative PSF values
|
|
52
|
+
|
|
53
|
+
Returns
|
|
54
|
+
-------
|
|
55
|
+
numpy.ndarray
|
|
56
|
+
Loaded and processed data with radius in cm and normalized cumulative PSF.
|
|
57
|
+
"""
|
|
58
|
+
d_type = {"names": (RADIUS_CM, CUMULATIVE_PSF), "formats": ("f8", "f8")}
|
|
59
|
+
data = np.loadtxt(data_file, dtype=d_type, usecols=(0, 2))
|
|
60
|
+
data[RADIUS_CM] *= 0.1 # Convert from mm to cm
|
|
61
|
+
data[CUMULATIVE_PSF] /= np.max(np.abs(data[CUMULATIVE_PSF])) # Normalize to max = 1.0
|
|
62
|
+
return data
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def calculate_rmsd(data, sim):
|
|
66
|
+
"""Calculate Root Mean Squared Deviation to be used as metric to find the best parameters."""
|
|
67
|
+
return np.sqrt(np.mean((data - sim) ** 2))
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def add_parameters(
|
|
71
|
+
all_parameters,
|
|
72
|
+
mirror_reflection,
|
|
73
|
+
mirror_align,
|
|
74
|
+
mirror_reflection_fraction=0.15,
|
|
75
|
+
mirror_reflection_2=0.035,
|
|
76
|
+
):
|
|
77
|
+
"""
|
|
78
|
+
Transform and add parameters to the all_parameters list.
|
|
79
|
+
|
|
80
|
+
Parameters
|
|
81
|
+
----------
|
|
82
|
+
mirror_reflection : float
|
|
83
|
+
The random angle of mirror reflection.
|
|
84
|
+
mirror_align : float
|
|
85
|
+
The random angle for mirror alignment (both horizontal and vertical).
|
|
86
|
+
mirror_reflection_fraction : float, optional
|
|
87
|
+
The fraction of the mirror reflection. Default is 0.15.
|
|
88
|
+
mirror_reflection_2 : float, optional
|
|
89
|
+
A secondary random angle for mirror reflection. Default is 0.035.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
None
|
|
94
|
+
Updates the all_parameters list in place.
|
|
95
|
+
"""
|
|
96
|
+
pars = {
|
|
97
|
+
"mirror_reflection_random_angle": [
|
|
98
|
+
mirror_reflection,
|
|
99
|
+
mirror_reflection_fraction,
|
|
100
|
+
mirror_reflection_2,
|
|
101
|
+
],
|
|
102
|
+
"mirror_align_random_horizontal": [mirror_align, 28.0, 0.0, 0.0],
|
|
103
|
+
"mirror_align_random_vertical": [mirror_align, 28.0, 0.0, 0.0],
|
|
104
|
+
}
|
|
105
|
+
all_parameters.append(pars)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_previous_values(tel_model):
|
|
109
|
+
"""
|
|
110
|
+
Retrieve previous parameter values from the telescope model.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
tel_model : TelescopeModel
|
|
115
|
+
Telescope model object.
|
|
116
|
+
|
|
117
|
+
Returns
|
|
118
|
+
-------
|
|
119
|
+
tuple
|
|
120
|
+
Tuple containing the previous values of mirror_reflection_random_angle (first entry),
|
|
121
|
+
mirror_reflection_fraction, second entry), mirror_reflection_random_angle (third entry),
|
|
122
|
+
and mirror_align_random_horizontal/vertical.
|
|
123
|
+
"""
|
|
124
|
+
split_par = tel_model.get_parameter_value("mirror_reflection_random_angle")
|
|
125
|
+
mrra_0, mfr_0, mrra2_0 = split_par[0], split_par[1], split_par[2]
|
|
126
|
+
mar_0 = tel_model.get_parameter_value("mirror_align_random_horizontal")[0]
|
|
127
|
+
logger.debug(
|
|
128
|
+
"Previous parameter values:\n"
|
|
129
|
+
f"MRRA = {mrra_0!s}\n"
|
|
130
|
+
f"MRF = {mfr_0!s}\n"
|
|
131
|
+
f"MRRA2 = {mrra2_0!s}\n"
|
|
132
|
+
f"MAR = {mar_0!s}\n"
|
|
133
|
+
)
|
|
134
|
+
return mrra_0, mfr_0, mrra2_0, mar_0
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def generate_random_parameters(
|
|
138
|
+
all_parameters, n_runs, args_dict, mrra_0, mfr_0, mrra2_0, mar_0, tel_model
|
|
139
|
+
):
|
|
140
|
+
"""
|
|
141
|
+
Generate random parameters for tuning.
|
|
142
|
+
|
|
143
|
+
The parameter ranges around the previous values are configurable via module constants.
|
|
144
|
+
|
|
145
|
+
Parameters
|
|
146
|
+
----------
|
|
147
|
+
all_parameters : list
|
|
148
|
+
List to store all parameter sets.
|
|
149
|
+
n_runs : int
|
|
150
|
+
Number of random parameter combinations to test.
|
|
151
|
+
args_dict : dict
|
|
152
|
+
Dictionary containing parsed command-line arguments.
|
|
153
|
+
mrra_0 : float
|
|
154
|
+
Initial value of mirror_reflection_random_angle.
|
|
155
|
+
mfr_0 : float
|
|
156
|
+
Initial value of mirror_reflection_fraction.
|
|
157
|
+
mrra2_0 : float
|
|
158
|
+
Initial value of the second mirror_reflection_random_angle.
|
|
159
|
+
mar_0 : float
|
|
160
|
+
Initial value of mirror_align_random_horizontal/vertical.
|
|
161
|
+
tel_model : TelescopeModel
|
|
162
|
+
Telescope model object to check if it's a dual mirror telescope.
|
|
163
|
+
"""
|
|
164
|
+
if args_dict["fixed"]:
|
|
165
|
+
logger.debug("fixed=True - First entry of mirror_reflection_random_angle is kept fixed.")
|
|
166
|
+
|
|
167
|
+
is_dual_mirror = model_utils.is_two_mirror_telescope(tel_model.name)
|
|
168
|
+
if is_dual_mirror:
|
|
169
|
+
mar_fixed_value = 0.0
|
|
170
|
+
else:
|
|
171
|
+
mar_fixed_value = None
|
|
172
|
+
|
|
173
|
+
for _ in range(n_runs):
|
|
174
|
+
mrra_range = MRRA_RANGE_DEFAULT if not args_dict["fixed"] else 0
|
|
175
|
+
mrf_range = MRF_RANGE_DEFAULT
|
|
176
|
+
mrra2_range = MRRA2_RANGE_DEFAULT
|
|
177
|
+
mar_range = MAR_RANGE_DEFAULT
|
|
178
|
+
rng = np.random.default_rng(seed=args_dict.get("random_seed"))
|
|
179
|
+
mrra = rng.uniform(max(mrra_0 - mrra_range, 0), mrra_0 + mrra_range)
|
|
180
|
+
mrf = rng.uniform(max(mfr_0 - mrf_range, 0), mfr_0 + mrf_range)
|
|
181
|
+
mrra2 = rng.uniform(max(mrra2_0 - mrra2_range, 0), mrra2_0 + mrra2_range)
|
|
182
|
+
|
|
183
|
+
# Set mar to 0 for dual mirror telescopes, otherwise use random value
|
|
184
|
+
if mar_fixed_value is not None:
|
|
185
|
+
mar = mar_fixed_value
|
|
186
|
+
else:
|
|
187
|
+
mar = rng.uniform(max(mar_0 - mar_range, 0), mar_0 + mar_range)
|
|
188
|
+
|
|
189
|
+
add_parameters(all_parameters, mrra, mar, mrf, mrra2)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _run_ray_tracing_simulation(tel_model, site_model, args_dict, pars):
|
|
193
|
+
"""
|
|
194
|
+
Run a ray tracing simulation with the given telescope parameters.
|
|
195
|
+
|
|
196
|
+
Parameters
|
|
197
|
+
----------
|
|
198
|
+
tel_model : TelescopeModel
|
|
199
|
+
Telescope model object.
|
|
200
|
+
site_model : SiteModel
|
|
201
|
+
Site model object.
|
|
202
|
+
args_dict : dict
|
|
203
|
+
Dictionary containing parsed command-line arguments.
|
|
204
|
+
pars : dict
|
|
205
|
+
Parameter set dictionary.
|
|
206
|
+
|
|
207
|
+
Returns
|
|
208
|
+
-------
|
|
209
|
+
tuple
|
|
210
|
+
(d80, simulated_data) - D80 value and simulated data from ray tracing.
|
|
211
|
+
"""
|
|
212
|
+
if pars is not None:
|
|
213
|
+
tel_model.change_multiple_parameters(**pars)
|
|
214
|
+
else:
|
|
215
|
+
raise ValueError("No best parameters found")
|
|
216
|
+
|
|
217
|
+
ray = RayTracing(
|
|
218
|
+
telescope_model=tel_model,
|
|
219
|
+
site_model=site_model,
|
|
220
|
+
simtel_path=args_dict["simtel_path"],
|
|
221
|
+
zenith_angle=args_dict["zenith"] * u.deg,
|
|
222
|
+
source_distance=args_dict["src_distance"] * u.km,
|
|
223
|
+
off_axis_angle=[0.0] * u.deg,
|
|
224
|
+
)
|
|
225
|
+
ray.simulate(test=args_dict.get("test", False), force=True)
|
|
226
|
+
ray.analyze(force=True, use_rx=False)
|
|
227
|
+
im = ray.images()[0]
|
|
228
|
+
d80 = im.get_psf()
|
|
229
|
+
|
|
230
|
+
return d80, im
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _create_psf_simulation_plot(data_to_plot, pars, d80, rmsd, is_best, pdf_pages):
|
|
234
|
+
"""
|
|
235
|
+
Create a plot for PSF simulation results.
|
|
236
|
+
|
|
237
|
+
Parameters
|
|
238
|
+
----------
|
|
239
|
+
data_to_plot : dict
|
|
240
|
+
Data dictionary for plotting.
|
|
241
|
+
pars : dict
|
|
242
|
+
Parameter set dictionary.
|
|
243
|
+
d80 : float
|
|
244
|
+
D80 value.
|
|
245
|
+
rmsd : float
|
|
246
|
+
RMSD value.
|
|
247
|
+
is_best : bool
|
|
248
|
+
Whether this is the best parameter set.
|
|
249
|
+
pdf_pages : PdfPages
|
|
250
|
+
PDF pages object for saving plots.
|
|
251
|
+
"""
|
|
252
|
+
fig = visualize.plot_1d(
|
|
253
|
+
data_to_plot,
|
|
254
|
+
plot_difference=True,
|
|
255
|
+
no_markers=True,
|
|
256
|
+
)
|
|
257
|
+
ax = fig.get_axes()[0]
|
|
258
|
+
ax.set_ylim(0, 1.05)
|
|
259
|
+
ax.set_ylabel(CUMULATIVE_PSF)
|
|
260
|
+
|
|
261
|
+
title_prefix = "* " if is_best else ""
|
|
262
|
+
ax.set_title(
|
|
263
|
+
f"{title_prefix}refl_rnd = "
|
|
264
|
+
f"{pars['mirror_reflection_random_angle'][0]:.5f}, "
|
|
265
|
+
f"{pars['mirror_reflection_random_angle'][1]:.5f}, "
|
|
266
|
+
f"{pars['mirror_reflection_random_angle'][2]:.5f}\n"
|
|
267
|
+
f"align_rnd = {pars['mirror_align_random_vertical'][0]:.5f}, "
|
|
268
|
+
f"{pars['mirror_align_random_vertical'][1]:.5f}, "
|
|
269
|
+
f"{pars['mirror_align_random_vertical'][2]:.5f}, "
|
|
270
|
+
f"{pars['mirror_align_random_vertical'][3]:.5f}"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
d80_color = "red" if is_best else "black"
|
|
274
|
+
d80_weight = "bold" if is_best else "normal"
|
|
275
|
+
d80_text = f"D80 = {d80:.5f} cm"
|
|
276
|
+
|
|
277
|
+
ax.text(
|
|
278
|
+
0.5,
|
|
279
|
+
0.3,
|
|
280
|
+
f"{d80_text}\nRMSD = {rmsd:.4f}",
|
|
281
|
+
verticalalignment="center",
|
|
282
|
+
horizontalalignment="left",
|
|
283
|
+
transform=ax.transAxes,
|
|
284
|
+
color=d80_color,
|
|
285
|
+
weight=d80_weight,
|
|
286
|
+
bbox={"boxstyle": "round,pad=0.3", "facecolor": "yellow", "alpha": 0.7}
|
|
287
|
+
if is_best
|
|
288
|
+
else None,
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
if is_best:
|
|
292
|
+
fig.text(
|
|
293
|
+
0.02,
|
|
294
|
+
0.02,
|
|
295
|
+
"* Best parameter set (lowest RMSD)",
|
|
296
|
+
fontsize=8,
|
|
297
|
+
style="italic",
|
|
298
|
+
color="red",
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
pdf_pages.savefig(fig, bbox_inches="tight")
|
|
302
|
+
plt.clf()
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def run_psf_simulation(
|
|
306
|
+
tel_model,
|
|
307
|
+
site_model,
|
|
308
|
+
args_dict,
|
|
309
|
+
pars,
|
|
310
|
+
data_to_plot,
|
|
311
|
+
radius,
|
|
312
|
+
pdf_pages=None,
|
|
313
|
+
is_best=False,
|
|
314
|
+
return_simulated_data=False,
|
|
315
|
+
):
|
|
316
|
+
"""
|
|
317
|
+
Run the simulation for one set of parameters and return D80, RMSD.
|
|
318
|
+
|
|
319
|
+
Parameters
|
|
320
|
+
----------
|
|
321
|
+
tel_model : TelescopeModel
|
|
322
|
+
Telescope model object.
|
|
323
|
+
site_model : SiteModel
|
|
324
|
+
Site model object.
|
|
325
|
+
args_dict : dict
|
|
326
|
+
Dictionary containing parsed command-line arguments.
|
|
327
|
+
pars : dict
|
|
328
|
+
Parameter set dictionary.
|
|
329
|
+
data_to_plot : dict
|
|
330
|
+
Data dictionary for plotting.
|
|
331
|
+
radius : array-like
|
|
332
|
+
Radius data.
|
|
333
|
+
pdf_pages : PdfPages, optional
|
|
334
|
+
PDF pages object for plotting. If None, no plotting is done.
|
|
335
|
+
is_best : bool, optional
|
|
336
|
+
Whether this is the best parameter set for highlighting in plots.
|
|
337
|
+
return_simulated_data : bool, optional
|
|
338
|
+
If True, returns simulated data as third element in return tuple.
|
|
339
|
+
|
|
340
|
+
Returns
|
|
341
|
+
-------
|
|
342
|
+
tuple
|
|
343
|
+
(d80, rmsd) if return_simulated_data=False
|
|
344
|
+
(d80, rmsd, simulated_data) if return_simulated_data=True
|
|
345
|
+
"""
|
|
346
|
+
d80, im = _run_ray_tracing_simulation(tel_model, site_model, args_dict, pars)
|
|
347
|
+
|
|
348
|
+
if radius is None:
|
|
349
|
+
raise ValueError("Radius data is not available.")
|
|
350
|
+
|
|
351
|
+
simulated_data = im.get_cumulative_data(radius * u.cm)
|
|
352
|
+
rmsd = calculate_rmsd(data_to_plot["measured"][CUMULATIVE_PSF], simulated_data[CUMULATIVE_PSF])
|
|
353
|
+
|
|
354
|
+
# Handle plotting if requested
|
|
355
|
+
if pdf_pages is not None and args_dict.get("plot_all", False):
|
|
356
|
+
data_to_plot["simulated"] = simulated_data
|
|
357
|
+
_create_psf_simulation_plot(data_to_plot, pars, d80, rmsd, is_best, pdf_pages)
|
|
358
|
+
del data_to_plot["simulated"]
|
|
359
|
+
|
|
360
|
+
return (d80, rmsd, simulated_data) if return_simulated_data else (d80, rmsd)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def load_and_process_data(args_dict):
|
|
364
|
+
"""
|
|
365
|
+
Load and process data if specified in the command-line arguments.
|
|
366
|
+
|
|
367
|
+
Returns
|
|
368
|
+
-------
|
|
369
|
+
- data_to_plot: OrderedDict containing loaded and processed data.
|
|
370
|
+
- radius: Radius data from loaded data (if available).
|
|
371
|
+
"""
|
|
372
|
+
data_to_plot = OrderedDict()
|
|
373
|
+
radius = None
|
|
374
|
+
if args_dict["data"] is not None:
|
|
375
|
+
data_file = gen.find_file(args_dict["data"], args_dict["model_path"])
|
|
376
|
+
data_to_plot["measured"] = load_psf_data(data_file)
|
|
377
|
+
radius = data_to_plot["measured"][RADIUS_CM]
|
|
378
|
+
return data_to_plot, radius
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
def _create_plot_for_parameters(pars, rmsd, d80, simulated_data, data_to_plot, is_best, pdf_pages):
|
|
382
|
+
"""
|
|
383
|
+
Create a single plot for a parameter set.
|
|
384
|
+
|
|
385
|
+
Parameters
|
|
386
|
+
----------
|
|
387
|
+
pars : dict
|
|
388
|
+
Parameter set dictionary
|
|
389
|
+
rmsd : float
|
|
390
|
+
RMSD value for this parameter set
|
|
391
|
+
d80 : float
|
|
392
|
+
D80 value for this parameter set
|
|
393
|
+
simulated_data : array
|
|
394
|
+
Simulated data for plotting
|
|
395
|
+
data_to_plot : dict
|
|
396
|
+
Data dictionary for plotting
|
|
397
|
+
is_best : bool
|
|
398
|
+
Whether this is the best parameter set
|
|
399
|
+
pdf_pages : PdfPages
|
|
400
|
+
PDF pages object to save the plot
|
|
401
|
+
"""
|
|
402
|
+
original_simulated = data_to_plot.get("simulated")
|
|
403
|
+
data_to_plot["simulated"] = simulated_data
|
|
404
|
+
|
|
405
|
+
fig = visualize.plot_1d(
|
|
406
|
+
data_to_plot,
|
|
407
|
+
plot_difference=True,
|
|
408
|
+
no_markers=True,
|
|
409
|
+
)
|
|
410
|
+
ax = fig.get_axes()[0]
|
|
411
|
+
ax.set_ylim(0, 1.05)
|
|
412
|
+
ax.set_ylabel(CUMULATIVE_PSF)
|
|
413
|
+
|
|
414
|
+
title_prefix = "* " if is_best else ""
|
|
415
|
+
|
|
416
|
+
ax.set_title(
|
|
417
|
+
f"{title_prefix}reflection = "
|
|
418
|
+
f"{pars['mirror_reflection_random_angle'][0]:.5f}, "
|
|
419
|
+
f"{pars['mirror_reflection_random_angle'][1]:.5f}, "
|
|
420
|
+
f"{pars['mirror_reflection_random_angle'][2]:.5f}\n"
|
|
421
|
+
f"align_vertical = {pars['mirror_align_random_vertical'][0]:.5f}, "
|
|
422
|
+
f"{pars['mirror_align_random_vertical'][1]:.5f}, "
|
|
423
|
+
f"{pars['mirror_align_random_vertical'][2]:.5f}, "
|
|
424
|
+
f"{pars['mirror_align_random_vertical'][3]:.5f}\n"
|
|
425
|
+
f"align_horizontal = {pars['mirror_align_random_horizontal'][0]:.5f}, "
|
|
426
|
+
f"{pars['mirror_align_random_horizontal'][1]:.5f}, "
|
|
427
|
+
f"{pars['mirror_align_random_horizontal'][2]:.5f}, "
|
|
428
|
+
f"{pars['mirror_align_random_horizontal'][3]:.5f}"
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
d80_color = "red" if is_best else "black"
|
|
432
|
+
d80_weight = "bold" if is_best else "normal"
|
|
433
|
+
|
|
434
|
+
ax.text(
|
|
435
|
+
0.5,
|
|
436
|
+
0.3,
|
|
437
|
+
f"D80 = {d80:.5f} cm\nRMSD = {rmsd:.4f}",
|
|
438
|
+
verticalalignment="center",
|
|
439
|
+
horizontalalignment="left",
|
|
440
|
+
transform=ax.transAxes,
|
|
441
|
+
color=d80_color,
|
|
442
|
+
weight=d80_weight,
|
|
443
|
+
bbox={"boxstyle": "round,pad=0.3", "facecolor": "yellow", "alpha": 0.7}
|
|
444
|
+
if is_best
|
|
445
|
+
else None,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
if is_best:
|
|
449
|
+
fig.text(
|
|
450
|
+
0.02,
|
|
451
|
+
0.02,
|
|
452
|
+
"* Best parameter set (lowest RMSD)",
|
|
453
|
+
fontsize=8,
|
|
454
|
+
style="italic",
|
|
455
|
+
color="red",
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
pdf_pages.savefig(fig, bbox_inches="tight")
|
|
459
|
+
plt.clf()
|
|
460
|
+
|
|
461
|
+
if original_simulated is not None:
|
|
462
|
+
data_to_plot["simulated"] = original_simulated
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def _create_all_plots(results, best_pars, data_to_plot, pdf_pages):
|
|
466
|
+
"""
|
|
467
|
+
Create plots for all parameter sets if requested.
|
|
468
|
+
|
|
469
|
+
Parameters
|
|
470
|
+
----------
|
|
471
|
+
results : list
|
|
472
|
+
List of (pars, rmsd, d80, simulated_data) tuples
|
|
473
|
+
best_pars : dict
|
|
474
|
+
Best parameter set for highlighting
|
|
475
|
+
data_to_plot : dict
|
|
476
|
+
Data dictionary for plotting
|
|
477
|
+
pdf_pages : PdfPages
|
|
478
|
+
PDF pages object to save plots
|
|
479
|
+
"""
|
|
480
|
+
logger.info("Creating plots for all parameter sets...")
|
|
481
|
+
|
|
482
|
+
for i, (pars, rmsd, d80, simulated_data) in enumerate(results):
|
|
483
|
+
is_best = pars is best_pars
|
|
484
|
+
logger.info(f"Creating plot {i + 1}/{len(results)}{' (BEST)' if is_best else ''}")
|
|
485
|
+
|
|
486
|
+
_create_plot_for_parameters(
|
|
487
|
+
pars, rmsd, d80, simulated_data, data_to_plot, is_best, pdf_pages
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def find_best_parameters(
|
|
492
|
+
all_parameters, tel_model, site_model, args_dict, data_to_plot, radius, pdf_pages=None
|
|
493
|
+
):
|
|
494
|
+
"""
|
|
495
|
+
Find the best parameters by running simulations for all parameter sets.
|
|
496
|
+
|
|
497
|
+
Loop over all parameter sets, run the simulation, compute RMSD,
|
|
498
|
+
and return the best parameters and their RMSD.
|
|
499
|
+
"""
|
|
500
|
+
best_rmsd = float("inf")
|
|
501
|
+
best_pars = None
|
|
502
|
+
best_d80 = None
|
|
503
|
+
results = [] # Store (pars, rmsd, d80, simulated_data)
|
|
504
|
+
|
|
505
|
+
logger.info(f"Running {len(all_parameters)} simulations...")
|
|
506
|
+
|
|
507
|
+
for i, pars in enumerate(all_parameters):
|
|
508
|
+
try:
|
|
509
|
+
logger.info(f"Running simulation {i + 1}/{len(all_parameters)}")
|
|
510
|
+
d80, rmsd, simulated_data = run_psf_simulation(
|
|
511
|
+
tel_model,
|
|
512
|
+
site_model,
|
|
513
|
+
args_dict,
|
|
514
|
+
pars,
|
|
515
|
+
data_to_plot,
|
|
516
|
+
radius,
|
|
517
|
+
return_simulated_data=True,
|
|
518
|
+
pdf_pages=None,
|
|
519
|
+
)
|
|
520
|
+
except (ValueError, RuntimeError) as e:
|
|
521
|
+
logger.warning(f"Simulation failed for parameters {pars}: {e}")
|
|
522
|
+
continue
|
|
523
|
+
|
|
524
|
+
results.append((pars, rmsd, d80, simulated_data))
|
|
525
|
+
if rmsd < best_rmsd:
|
|
526
|
+
best_rmsd = rmsd
|
|
527
|
+
best_pars = pars
|
|
528
|
+
best_d80 = d80
|
|
529
|
+
|
|
530
|
+
logger.info(f"Best RMSD found: {best_rmsd:.5f}")
|
|
531
|
+
|
|
532
|
+
# Create all plots if requested
|
|
533
|
+
if pdf_pages is not None and args_dict.get("plot_all", False) and results:
|
|
534
|
+
_create_all_plots(results, best_pars, data_to_plot, pdf_pages)
|
|
535
|
+
|
|
536
|
+
return best_pars, best_d80, results
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def create_d80_vs_offaxis_plot(tel_model, site_model, args_dict, best_pars, output_dir):
|
|
540
|
+
"""
|
|
541
|
+
Create D80 vs off-axis angle plot using the best parameters.
|
|
542
|
+
|
|
543
|
+
Parameters
|
|
544
|
+
----------
|
|
545
|
+
tel_model : TelescopeModel
|
|
546
|
+
Telescope model object.
|
|
547
|
+
site_model : SiteModel
|
|
548
|
+
Site model object.
|
|
549
|
+
args_dict : dict
|
|
550
|
+
Dictionary containing parsed command-line arguments.
|
|
551
|
+
best_pars : dict
|
|
552
|
+
Best parameter set.
|
|
553
|
+
output_dir : Path
|
|
554
|
+
Output directory for saving plots.
|
|
555
|
+
"""
|
|
556
|
+
logger.info("Creating D80 vs off-axis angle plot with best parameters...")
|
|
557
|
+
|
|
558
|
+
# Apply best parameters to telescope model
|
|
559
|
+
tel_model.change_multiple_parameters(**best_pars)
|
|
560
|
+
|
|
561
|
+
# Create off-axis angle array
|
|
562
|
+
max_offset = args_dict.get("max_offset", MAX_OFFSET_DEFAULT)
|
|
563
|
+
offset_steps = args_dict.get("offset_steps", OFFSET_STEPS_DEFAULT)
|
|
564
|
+
off_axis_angles = np.linspace(
|
|
565
|
+
0,
|
|
566
|
+
max_offset,
|
|
567
|
+
int(max_offset / offset_steps) + 1,
|
|
568
|
+
)
|
|
569
|
+
|
|
570
|
+
ray = RayTracing(
|
|
571
|
+
telescope_model=tel_model,
|
|
572
|
+
site_model=site_model,
|
|
573
|
+
simtel_path=args_dict["simtel_path"],
|
|
574
|
+
zenith_angle=args_dict["zenith"] * u.deg,
|
|
575
|
+
source_distance=args_dict["src_distance"] * u.km,
|
|
576
|
+
off_axis_angle=off_axis_angles * u.deg,
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
logger.info(f"Running ray tracing for {len(off_axis_angles)} off-axis angles...")
|
|
580
|
+
ray.simulate(test=args_dict.get("test", False), force=True)
|
|
581
|
+
ray.analyze(force=True)
|
|
582
|
+
|
|
583
|
+
for key in ["d80_cm", "d80_deg"]:
|
|
584
|
+
plt.figure(figsize=(10, 6), tight_layout=True)
|
|
585
|
+
|
|
586
|
+
ray.plot(key, marker="o", linestyle="-", color="blue", linewidth=2, markersize=6)
|
|
587
|
+
|
|
588
|
+
plt.title(
|
|
589
|
+
f"PSF D80 vs Off-axis Angle - {tel_model.name}\n"
|
|
590
|
+
f"Best Parameters: \n"
|
|
591
|
+
f"reflection=[{best_pars['mirror_reflection_random_angle'][0]:.4f},"
|
|
592
|
+
f"{best_pars['mirror_reflection_random_angle'][1]:.4f},"
|
|
593
|
+
f"{best_pars['mirror_reflection_random_angle'][2]:.4f}],\n"
|
|
594
|
+
f"align_horizontal={best_pars['mirror_align_random_horizontal'][0]:.4f}\n"
|
|
595
|
+
f"align_vertical={best_pars['mirror_align_random_vertical'][0]:.4f}\n"
|
|
596
|
+
)
|
|
597
|
+
plt.xlabel("Off-axis Angle (degrees)")
|
|
598
|
+
plt.ylabel("D80 (cm)" if key == "d80_cm" else "D80 (degrees)")
|
|
599
|
+
plt.ylim(bottom=0)
|
|
600
|
+
plt.xticks(rotation=45)
|
|
601
|
+
plt.xlim(0, max_offset)
|
|
602
|
+
plt.grid(True, alpha=0.3)
|
|
603
|
+
|
|
604
|
+
plot_file_name = f"tune_psf_{tel_model.name}_best_params_{key}.pdf"
|
|
605
|
+
plot_file = output_dir.joinpath(plot_file_name)
|
|
606
|
+
visualize.save_figure(plt, plot_file, log_title=f"D80 vs off-axis ({key})")
|
|
607
|
+
|
|
608
|
+
plt.close("all")
|
|
609
|
+
|
|
610
|
+
|
|
611
|
+
def write_tested_parameters_to_file(results, best_pars, best_d80, output_dir, tel_model):
|
|
612
|
+
"""
|
|
613
|
+
Write all tested parameters and their metrics to a text file.
|
|
614
|
+
|
|
615
|
+
Parameters
|
|
616
|
+
----------
|
|
617
|
+
results : list
|
|
618
|
+
List of (pars, rmsd, d80, simulated_data) tuples
|
|
619
|
+
best_pars : dict
|
|
620
|
+
Best parameter set
|
|
621
|
+
best_d80 : float
|
|
622
|
+
Best D80 value
|
|
623
|
+
output_dir : Path
|
|
624
|
+
Output directory path
|
|
625
|
+
tel_model : TelescopeModel
|
|
626
|
+
Telescope model object for filename generation
|
|
627
|
+
"""
|
|
628
|
+
param_file = output_dir.joinpath(f"psf_optimization_{tel_model.name}.log")
|
|
629
|
+
with open(param_file, "w", encoding="utf-8") as f:
|
|
630
|
+
f.write("# PSF Parameter Optimization Log\n")
|
|
631
|
+
f.write(f"# Telescope: {tel_model.name}\n")
|
|
632
|
+
f.write(f"# Total parameter sets tested: {len(results)}\n")
|
|
633
|
+
f.write("#" + "=" * 60 + "\n\n")
|
|
634
|
+
|
|
635
|
+
f.write("PARAMETER TESTING RESULTS:\n")
|
|
636
|
+
for i, (pars, rmsd, d80, _) in enumerate(results):
|
|
637
|
+
is_best = pars is best_pars
|
|
638
|
+
status = "BEST" if is_best else "TESTED"
|
|
639
|
+
f.write(f"[{status}] Set {i + 1:03d}: RMSD={rmsd:.5f}, D80={d80:.5f} cm\n")
|
|
640
|
+
for par, value in pars.items():
|
|
641
|
+
f.write(f" {par}: {value}\n")
|
|
642
|
+
f.write("\n")
|
|
643
|
+
|
|
644
|
+
f.write("OPTIMIZATION SUMMARY:\n")
|
|
645
|
+
f.write(f"Best RMSD: {min(result[1] for result in results):.5f}\n")
|
|
646
|
+
f.write(f"Best D80: {best_d80:.5f} cm\n")
|
|
647
|
+
f.write("\nOPTIMIZED PARAMETERS:\n")
|
|
648
|
+
for par, value in best_pars.items():
|
|
649
|
+
f.write(f"{par}: {value}\n")
|
|
650
|
+
return param_file
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def _add_units_to_psf_parameters(best_pars):
|
|
654
|
+
"""
|
|
655
|
+
Add proper astropy units to PSF parameters based on their schemas.
|
|
656
|
+
|
|
657
|
+
Parameters
|
|
658
|
+
----------
|
|
659
|
+
best_pars : dict
|
|
660
|
+
Dictionary with PSF parameter names as keys and values as lists
|
|
661
|
+
|
|
662
|
+
Returns
|
|
663
|
+
-------
|
|
664
|
+
dict
|
|
665
|
+
Dictionary with same keys but values converted to astropy quantities with units
|
|
666
|
+
"""
|
|
667
|
+
psf_pars_with_units = {}
|
|
668
|
+
|
|
669
|
+
for param_name, param_values in best_pars.items():
|
|
670
|
+
if param_name == "mirror_reflection_random_angle":
|
|
671
|
+
psf_pars_with_units[param_name] = [
|
|
672
|
+
param_values[0] * u.deg,
|
|
673
|
+
param_values[1] * u.dimensionless_unscaled,
|
|
674
|
+
param_values[2] * u.deg,
|
|
675
|
+
]
|
|
676
|
+
elif param_name in ["mirror_align_random_horizontal", "mirror_align_random_vertical"]:
|
|
677
|
+
psf_pars_with_units[param_name] = [
|
|
678
|
+
param_values[0] * u.deg,
|
|
679
|
+
param_values[1] * u.deg,
|
|
680
|
+
param_values[2] * u.dimensionless_unscaled,
|
|
681
|
+
param_values[3] * u.dimensionless_unscaled,
|
|
682
|
+
]
|
|
683
|
+
else:
|
|
684
|
+
psf_pars_with_units[param_name] = param_values
|
|
685
|
+
|
|
686
|
+
return psf_pars_with_units
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def export_psf_parameters(best_pars, tel_model, parameter_version, output_dir):
|
|
690
|
+
"""
|
|
691
|
+
Export PSF parameters as simulation model parameter files.
|
|
692
|
+
|
|
693
|
+
Parameters
|
|
694
|
+
----------
|
|
695
|
+
best_pars : dict
|
|
696
|
+
Best parameter set
|
|
697
|
+
tel_model : TelescopeModel
|
|
698
|
+
Telescope model object
|
|
699
|
+
parameter_version : str
|
|
700
|
+
Parameter version string
|
|
701
|
+
output_dir : Path
|
|
702
|
+
Output directory path
|
|
703
|
+
"""
|
|
704
|
+
try:
|
|
705
|
+
logger.info("Exporting best PSF parameters as simulation model parameter files")
|
|
706
|
+
psf_pars_with_units = _add_units_to_psf_parameters(best_pars)
|
|
707
|
+
parameter_output_path = output_dir / tel_model.name
|
|
708
|
+
for parameter_name, parameter_value in psf_pars_with_units.items():
|
|
709
|
+
writer.ModelDataWriter.dump_model_parameter(
|
|
710
|
+
parameter_name=parameter_name,
|
|
711
|
+
value=parameter_value,
|
|
712
|
+
instrument=tel_model.name,
|
|
713
|
+
parameter_version=parameter_version,
|
|
714
|
+
output_file=f"{parameter_name}-{parameter_version}.json",
|
|
715
|
+
output_path=parameter_output_path,
|
|
716
|
+
use_plain_output_path=True,
|
|
717
|
+
)
|
|
718
|
+
logger.info(f"simulation model parameter files exported to {output_dir}")
|
|
719
|
+
except ImportError as e:
|
|
720
|
+
logger.warning(f"Could not export simulation parameters: {e}")
|
|
721
|
+
except (ValueError, KeyError, OSError) as e:
|
|
722
|
+
logger.error(f"Error exporting simulation parameters: {e}")
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
def run_psf_optimization_workflow(tel_model, site_model, args_dict, output_dir):
|
|
726
|
+
"""
|
|
727
|
+
Run the complete PSF parameter optimization workflow.
|
|
728
|
+
|
|
729
|
+
This function consolidates the main optimization logic to make the application lighter.
|
|
730
|
+
|
|
731
|
+
Parameters
|
|
732
|
+
----------
|
|
733
|
+
tel_model : TelescopeModel
|
|
734
|
+
Telescope model object
|
|
735
|
+
site_model : SiteModel
|
|
736
|
+
Site model object
|
|
737
|
+
args_dict : dict
|
|
738
|
+
Dictionary containing parsed command-line arguments
|
|
739
|
+
output_dir : Path
|
|
740
|
+
Output directory path
|
|
741
|
+
|
|
742
|
+
Returns
|
|
743
|
+
-------
|
|
744
|
+
None
|
|
745
|
+
All results are saved to files and printed to console
|
|
746
|
+
"""
|
|
747
|
+
# Generate parameter sets
|
|
748
|
+
all_parameters = []
|
|
749
|
+
mrra_0, mfr_0, mrra2_0, mar_0 = get_previous_values(tel_model)
|
|
750
|
+
|
|
751
|
+
n_runs = args_dict.get("n_runs")
|
|
752
|
+
generate_random_parameters(
|
|
753
|
+
all_parameters, n_runs, args_dict, mrra_0, mfr_0, mrra2_0, mar_0, tel_model
|
|
754
|
+
)
|
|
755
|
+
|
|
756
|
+
data_to_plot, radius = load_and_process_data(args_dict)
|
|
757
|
+
|
|
758
|
+
# Preparing figure name and PDF pages for plotting
|
|
759
|
+
plot_file_name = "_".join(("tune_psf", tel_model.name + ".pdf"))
|
|
760
|
+
plot_file = output_dir.joinpath(plot_file_name)
|
|
761
|
+
pdf_pages = PdfPages(plot_file)
|
|
762
|
+
|
|
763
|
+
# Find best parameters
|
|
764
|
+
best_pars, best_d80, results = find_best_parameters(
|
|
765
|
+
all_parameters, tel_model, site_model, args_dict, data_to_plot, radius, pdf_pages
|
|
766
|
+
)
|
|
767
|
+
|
|
768
|
+
plt.close()
|
|
769
|
+
pdf_pages.close()
|
|
770
|
+
|
|
771
|
+
# Write all tested parameters and their metrics to a file
|
|
772
|
+
param_file = write_tested_parameters_to_file(
|
|
773
|
+
results, best_pars, best_d80, output_dir, tel_model
|
|
774
|
+
)
|
|
775
|
+
print(f"\nParameter results written to {param_file}")
|
|
776
|
+
|
|
777
|
+
# Automatically create D80 vs off-axis angle plot for best parameters
|
|
778
|
+
create_d80_vs_offaxis_plot(tel_model, site_model, args_dict, best_pars, output_dir)
|
|
779
|
+
print("D80 vs off-axis angle plots created successfully")
|
|
780
|
+
|
|
781
|
+
print("\nBest parameters:")
|
|
782
|
+
for par, value in best_pars.items():
|
|
783
|
+
print(f"{par} = {value}")
|
|
784
|
+
|
|
785
|
+
# Export best parameters as simulation model parameter files (if flag is provided)
|
|
786
|
+
if args_dict.get("write_psf_parameters", False):
|
|
787
|
+
export_psf_parameters(
|
|
788
|
+
best_pars,
|
|
789
|
+
tel_model,
|
|
790
|
+
args_dict.get("parameter_version", "0.0.0"),
|
|
791
|
+
output_dir.parent,
|
|
792
|
+
)
|