gammasimtools 0.18.0__py3-none-any.whl → 0.19.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.19.0.dist-info}/METADATA +26 -69
- gammasimtools-0.19.0.dist-info/RECORD +393 -0
- {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/entry_points.txt +9 -2
- {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/licenses/LICENSE +1 -1
- simtools/_version.py +16 -3
- simtools/applications/calculate_trigger_rate.py +1 -1
- 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_value_from_json_to_db.py +2 -1
- simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +8 -13
- simtools/applications/db_generate_compound_indexes.py +61 -0
- simtools/applications/db_get_file_from_db.py +1 -1
- 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/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_sim_telarray_histograms.py +1 -1
- 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 +379 -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 +2 -1
- 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 +29 -0
- 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 +34 -9
- simtools/db/db_handler.py +43 -37
- simtools/db/db_model_upload.py +3 -3
- simtools/dependencies.py +88 -25
- 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 +14 -7
- simtools/model/array_model.py +23 -4
- simtools/model/flasher_model.py +106 -0
- simtools/model/model_parameter.py +4 -4
- simtools/model/model_repository.py +197 -2
- simtools/model/telescope_model.py +3 -1
- simtools/production_configuration/derive_corsika_limits.py +361 -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/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 +2 -2
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +2 -2
- simtools/simtel/simtel_config_reader.py +2 -2
- simtools/simtel/simtel_config_writer.py +16 -4
- simtools/simtel/simtel_io_event_histograms.py +746 -0
- simtools/simtel/simtel_io_event_reader.py +15 -42
- simtools/simtel/simtel_io_event_writer.py +9 -9
- simtools/simtel/simtel_io_histogram.py +3 -1
- simtools/simtel/simtel_io_histograms.py +7 -3
- simtools/simtel/simtel_table_reader.py +92 -10
- 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/testing/configuration.py +24 -26
- simtools/testing/helpers.py +2 -2
- simtools/testing/log_inspector.py +50 -0
- simtools/testing/validate_output.py +87 -37
- simtools/utils/general.py +125 -255
- simtools/utils/geometry.py +36 -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_pixels.py +1 -1
- simtools/visualization/plot_tables.py +133 -37
- simtools/visualization/simtel_event_plots.py +816 -0
- simtools/visualization/visualize.py +4 -101
- gammasimtools-0.18.0.dist-info/RECORD +0 -376
- simtools/production_configuration/derive_corsika_limits_grid.py +0 -232
- {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.0.dist-info}/WHEEL +0 -0
- {gammasimtools-0.18.0.dist-info → gammasimtools-0.19.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,816 @@
|
|
|
1
|
+
#!/usr/bin/python3
|
|
2
|
+
"""Plots for light emission (flasher/calibration) sim_telarray events."""
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import astropy.units as u
|
|
7
|
+
import matplotlib.pyplot as plt
|
|
8
|
+
import numpy as np
|
|
9
|
+
from ctapipe.calib import CameraCalibrator
|
|
10
|
+
from ctapipe.io import EventSource
|
|
11
|
+
from ctapipe.visualization import CameraDisplay
|
|
12
|
+
from scipy import signal as _signal
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"plot_simtel_event_image",
|
|
16
|
+
"plot_simtel_integrated_pedestal_image",
|
|
17
|
+
"plot_simtel_integrated_signal_image",
|
|
18
|
+
"plot_simtel_peak_timing",
|
|
19
|
+
"plot_simtel_step_traces",
|
|
20
|
+
"plot_simtel_time_traces",
|
|
21
|
+
"plot_simtel_waveform_matrix",
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
_logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
# Reusable literal constants (duplicated from visualize to avoid circular deps)
|
|
27
|
+
AXES_FRACTION = "axes fraction"
|
|
28
|
+
NO_R1_WAVEFORMS_MSG = "No R1 waveforms available in event"
|
|
29
|
+
TIME_NS_LABEL = "time [ns]"
|
|
30
|
+
R1_SAMPLES_LABEL = "R1 samples [d.c.]"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _compute_integration_window(
|
|
34
|
+
peak_idx: int, n_samp: int, half_width: int, mode: str, offset: int | None
|
|
35
|
+
) -> tuple[int, int]:
|
|
36
|
+
"""Return [a, b) window bounds for integration for signal/pedestal modes."""
|
|
37
|
+
hw = int(half_width)
|
|
38
|
+
win_len = 2 * hw + 1
|
|
39
|
+
if mode == "signal":
|
|
40
|
+
a = max(0, peak_idx - hw)
|
|
41
|
+
b = min(n_samp, peak_idx + hw + 1)
|
|
42
|
+
return a, b
|
|
43
|
+
|
|
44
|
+
g = int(offset) if offset is not None else 16
|
|
45
|
+
start = peak_idx + g
|
|
46
|
+
if start + win_len <= n_samp:
|
|
47
|
+
return start, start + win_len
|
|
48
|
+
start = max(0, peak_idx - g - win_len)
|
|
49
|
+
a = start
|
|
50
|
+
b = min(n_samp, start + win_len)
|
|
51
|
+
if a >= b:
|
|
52
|
+
return 0, min(n_samp, win_len)
|
|
53
|
+
return a, b
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _format_integrated_title(
|
|
57
|
+
tel_label: str, et_name: str, half_width: int, mode: str, offset: int | None
|
|
58
|
+
) -> str:
|
|
59
|
+
win_len = 2 * int(half_width) + 1
|
|
60
|
+
if mode == "signal":
|
|
61
|
+
return f"{tel_label} integrated signal (win {win_len}) ({et_name})"
|
|
62
|
+
g = int(offset) if offset is not None else 16
|
|
63
|
+
return f"{tel_label} integrated pedestal (win {win_len}, offset {g}) ({et_name})"
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _select_event_by_type(source):
|
|
67
|
+
"""
|
|
68
|
+
Build an event selector for a ctapipe EventSource.
|
|
69
|
+
|
|
70
|
+
Parameters
|
|
71
|
+
----------
|
|
72
|
+
source : ctapipe.io.EventSource
|
|
73
|
+
Iterable event source.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
callable
|
|
78
|
+
A function ``select_event(event_index: int | None) -> Any`` that returns
|
|
79
|
+
the first event (when ``event_index`` is None) or the event at the given
|
|
80
|
+
index. Returns ``None`` if no event is available or the index is out of range.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def select_event(event_index=None):
|
|
84
|
+
if event_index is None:
|
|
85
|
+
for ev in source:
|
|
86
|
+
return ev
|
|
87
|
+
else:
|
|
88
|
+
for idx, ev in enumerate(source):
|
|
89
|
+
if idx == event_index:
|
|
90
|
+
return ev
|
|
91
|
+
_logger.warning("No events available from source or event_index out of range")
|
|
92
|
+
return None
|
|
93
|
+
|
|
94
|
+
return select_event
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _time_axis_from_readout(readout, n_samp):
|
|
98
|
+
"""
|
|
99
|
+
Compute time axis in nanoseconds from a camera readout.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
readout : Any
|
|
104
|
+
Camera readout providing ``sampling_rate`` as an astropy Quantity.
|
|
105
|
+
n_samp : int
|
|
106
|
+
Number of samples per trace.
|
|
107
|
+
|
|
108
|
+
Returns
|
|
109
|
+
-------
|
|
110
|
+
numpy.ndarray
|
|
111
|
+
Array of shape ``(n_samp,)`` with time in nanoseconds.
|
|
112
|
+
"""
|
|
113
|
+
try:
|
|
114
|
+
dt = (1 / readout.sampling_rate).to(u.ns).value
|
|
115
|
+
except (AttributeError, TypeError):
|
|
116
|
+
dt = 1.0
|
|
117
|
+
if not np.isfinite(dt) or dt <= 0:
|
|
118
|
+
dt = 1.0
|
|
119
|
+
return np.arange(int(n_samp)) * float(dt)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def plot_simtel_event_image(filename, distance=None, event_index=None):
|
|
123
|
+
"""
|
|
124
|
+
Read a sim_telarray file and plot the DL1 image for one event via ctapipe.
|
|
125
|
+
|
|
126
|
+
Parameters
|
|
127
|
+
----------
|
|
128
|
+
filename : str | pathlib.Path
|
|
129
|
+
Path to the ``.simtel`` file.
|
|
130
|
+
distance : astropy.units.Quantity | float | None, optional
|
|
131
|
+
Distance to annotate in the plot. If a Quantity, interpreted in meters.
|
|
132
|
+
If not provided, no unit conversion is attempted.
|
|
133
|
+
event_index : int | None, optional
|
|
134
|
+
Zero-based index of the event to plot. If None, the first event is used.
|
|
135
|
+
|
|
136
|
+
Returns
|
|
137
|
+
-------
|
|
138
|
+
matplotlib.figure.Figure | None
|
|
139
|
+
The created figure, or ``None`` if no suitable event/image is available.
|
|
140
|
+
"""
|
|
141
|
+
source = EventSource(filename, max_events=None)
|
|
142
|
+
event = _select_event_by_type(source)(event_index=event_index)
|
|
143
|
+
if not event:
|
|
144
|
+
_logger.warning("No event found in the file.")
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
|
|
148
|
+
if not tel_ids:
|
|
149
|
+
_logger.warning("First event has no R1 telescope data")
|
|
150
|
+
return None
|
|
151
|
+
tel_id = tel_ids[0]
|
|
152
|
+
|
|
153
|
+
calib = CameraCalibrator(subarray=source.subarray)
|
|
154
|
+
calib(event)
|
|
155
|
+
|
|
156
|
+
geometry = source.subarray.tel[tel_id].camera.geometry
|
|
157
|
+
image = event.dl1.tel[tel_id].image
|
|
158
|
+
|
|
159
|
+
fig, ax = plt.subplots(dpi=300)
|
|
160
|
+
tel = source.subarray.tel[tel_id]
|
|
161
|
+
tel_label = getattr(tel, "name", f"CT{tel_id}")
|
|
162
|
+
title = f"{tel_label}, run {event.index.obs_id} event {event.index.event_id}"
|
|
163
|
+
disp = CameraDisplay(geometry, image=image, norm="symlog", ax=ax)
|
|
164
|
+
disp.cmap = "RdBu_r"
|
|
165
|
+
disp.add_colorbar(fraction=0.02, pad=-0.1)
|
|
166
|
+
disp.set_limits_percent(100)
|
|
167
|
+
ax.set_title(title, pad=20)
|
|
168
|
+
|
|
169
|
+
try:
|
|
170
|
+
d_str = f"{distance.to(u.m)}"
|
|
171
|
+
except (AttributeError, TypeError, ValueError):
|
|
172
|
+
d_str = str(distance)
|
|
173
|
+
|
|
174
|
+
ax.annotate(
|
|
175
|
+
f"tel type: {source.subarray.tel[tel_id].type.name}\n"
|
|
176
|
+
f"optics: {source.subarray.tel[tel_id].optics.name}\n"
|
|
177
|
+
f"camera: {source.subarray.tel[tel_id].camera_name}\n"
|
|
178
|
+
f"distance: {d_str}",
|
|
179
|
+
xy=(0, 0),
|
|
180
|
+
xytext=(0.1, 1),
|
|
181
|
+
xycoords=AXES_FRACTION,
|
|
182
|
+
va="top",
|
|
183
|
+
size=7,
|
|
184
|
+
)
|
|
185
|
+
ax.annotate(
|
|
186
|
+
f"dl1 image,\ntotal ADC counts: {np.round(np.sum(image))}\n",
|
|
187
|
+
xy=(0, 0),
|
|
188
|
+
xytext=(0.75, 1),
|
|
189
|
+
xycoords=AXES_FRACTION,
|
|
190
|
+
va="top",
|
|
191
|
+
ha="left",
|
|
192
|
+
size=7,
|
|
193
|
+
)
|
|
194
|
+
ax.set_axis_off()
|
|
195
|
+
fig.tight_layout()
|
|
196
|
+
return fig
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def plot_simtel_time_traces(
|
|
200
|
+
filename,
|
|
201
|
+
tel_id: int | None = None,
|
|
202
|
+
n_pixels: int = 3,
|
|
203
|
+
event_index: int | None = None,
|
|
204
|
+
):
|
|
205
|
+
"""
|
|
206
|
+
Plot R1 time traces for a few pixels of one event.
|
|
207
|
+
|
|
208
|
+
Parameters
|
|
209
|
+
----------
|
|
210
|
+
filename : str | pathlib.Path
|
|
211
|
+
Path to the ``.simtel`` file.
|
|
212
|
+
tel_id : int | None, optional
|
|
213
|
+
Telescope ID to use. If None, the first telescope with R1 data is chosen.
|
|
214
|
+
n_pixels : int, optional
|
|
215
|
+
Number of pixels with highest signal to plot. Default is 3.
|
|
216
|
+
event_index : int | None, optional
|
|
217
|
+
Zero-based index of the event to plot. If None, the first event is used.
|
|
218
|
+
|
|
219
|
+
Returns
|
|
220
|
+
-------
|
|
221
|
+
matplotlib.figure.Figure | None
|
|
222
|
+
The created figure, or ``None`` if R1 waveforms are unavailable.
|
|
223
|
+
"""
|
|
224
|
+
source = EventSource(filename, max_events=None)
|
|
225
|
+
event = _select_event_by_type(source)(event_index=event_index)
|
|
226
|
+
|
|
227
|
+
r1_tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
|
|
228
|
+
if r1_tel_ids:
|
|
229
|
+
tel_id = tel_id or r1_tel_ids[0]
|
|
230
|
+
else:
|
|
231
|
+
dl1_tel_ids = sorted(getattr(event.dl1, "tel", {}).keys())
|
|
232
|
+
tel_id = tel_id or dl1_tel_ids[0]
|
|
233
|
+
|
|
234
|
+
calib = CameraCalibrator(subarray=source.subarray)
|
|
235
|
+
try:
|
|
236
|
+
calib(event)
|
|
237
|
+
image = event.dl1.tel[tel_id].image
|
|
238
|
+
except (RuntimeError, ValueError, KeyError, AttributeError):
|
|
239
|
+
image = None
|
|
240
|
+
|
|
241
|
+
waveforms = getattr(event.r1.tel.get(tel_id, None), "waveform", None)
|
|
242
|
+
if waveforms is None:
|
|
243
|
+
_logger.warning(NO_R1_WAVEFORMS_MSG)
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
w = np.asarray(waveforms)
|
|
247
|
+
if w.ndim == 3:
|
|
248
|
+
w = w[0]
|
|
249
|
+
_, n_samp = w.shape
|
|
250
|
+
|
|
251
|
+
if image is not None:
|
|
252
|
+
pix_ids = np.argsort(image)[-n_pixels:][::-1]
|
|
253
|
+
else:
|
|
254
|
+
integrals = w.sum(axis=1)
|
|
255
|
+
pix_ids = np.argsort(integrals)[-n_pixels:][::-1]
|
|
256
|
+
|
|
257
|
+
readout = source.subarray.tel[tel_id].camera.readout
|
|
258
|
+
t = _time_axis_from_readout(readout, n_samp)
|
|
259
|
+
|
|
260
|
+
fig, ax = plt.subplots(dpi=300)
|
|
261
|
+
for pid in pix_ids:
|
|
262
|
+
ax.plot(t, w[pid], label=f"pix {int(pid)}", drawstyle="steps-mid")
|
|
263
|
+
ax.set_xlabel(TIME_NS_LABEL)
|
|
264
|
+
ax.set_ylabel(R1_SAMPLES_LABEL)
|
|
265
|
+
et_name = getattr(getattr(event.trigger, "event_type", None), "name", "?")
|
|
266
|
+
tel = source.subarray.tel[tel_id]
|
|
267
|
+
tel_label = getattr(tel, "name", f"CT{tel_id}")
|
|
268
|
+
ax.set_title(f"{tel_label} waveforms ({et_name})")
|
|
269
|
+
ax.legend(loc="best", fontsize=7)
|
|
270
|
+
fig.tight_layout()
|
|
271
|
+
return fig
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def plot_simtel_waveform_matrix(
|
|
275
|
+
filename,
|
|
276
|
+
tel_id: int | None = None,
|
|
277
|
+
vmax: float | None = None,
|
|
278
|
+
event_index: int | None = None,
|
|
279
|
+
pixel_step: int | None = None,
|
|
280
|
+
):
|
|
281
|
+
"""
|
|
282
|
+
Create a pseudocolor image of R1 waveforms (sample index vs. pixel id).
|
|
283
|
+
|
|
284
|
+
Parameters
|
|
285
|
+
----------
|
|
286
|
+
filename : str | pathlib.Path
|
|
287
|
+
Path to the ``.simtel`` file.
|
|
288
|
+
tel_id : int | None, optional
|
|
289
|
+
Telescope ID to use. If None, the first telescope with R1 data is chosen.
|
|
290
|
+
vmax : float | None, optional
|
|
291
|
+
Upper limit for color normalization. If None, determined automatically.
|
|
292
|
+
event_index : int | None, optional
|
|
293
|
+
Zero-based index of the event to plot. If None, the first event is used.
|
|
294
|
+
pixel_step : int | None, optional
|
|
295
|
+
Step between plotted pixel ids (e.g., 1 plots all, 2 plots every second pixel).
|
|
296
|
+
|
|
297
|
+
Returns
|
|
298
|
+
-------
|
|
299
|
+
matplotlib.figure.Figure | None
|
|
300
|
+
The created figure, or ``None`` if R1 waveforms are unavailable.
|
|
301
|
+
"""
|
|
302
|
+
source = EventSource(filename, max_events=None)
|
|
303
|
+
event = _select_event_by_type(source)(event_index=event_index)
|
|
304
|
+
|
|
305
|
+
r1_tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
|
|
306
|
+
if r1_tel_ids:
|
|
307
|
+
tel_id = tel_id or r1_tel_ids[0]
|
|
308
|
+
else:
|
|
309
|
+
_logger.warning("Event has no R1 data for waveform plot")
|
|
310
|
+
return None
|
|
311
|
+
|
|
312
|
+
waveforms = getattr(event.r1.tel.get(tel_id, None), "waveform", None)
|
|
313
|
+
if waveforms is None:
|
|
314
|
+
_logger.warning(NO_R1_WAVEFORMS_MSG)
|
|
315
|
+
return None
|
|
316
|
+
|
|
317
|
+
w = np.asarray(waveforms)
|
|
318
|
+
if w.ndim == 3:
|
|
319
|
+
w = w[0]
|
|
320
|
+
n_pix, n_samp = w.shape
|
|
321
|
+
|
|
322
|
+
step = max(1, int(pixel_step)) if pixel_step is not None else 1
|
|
323
|
+
pix_idx = np.arange(n_pix)[::step]
|
|
324
|
+
w_sel = w[pix_idx]
|
|
325
|
+
|
|
326
|
+
readout = source.subarray.tel[tel_id].camera.readout
|
|
327
|
+
t = _time_axis_from_readout(readout, n_samp)
|
|
328
|
+
|
|
329
|
+
fig, ax = plt.subplots(dpi=300)
|
|
330
|
+
mesh = ax.pcolormesh(t, pix_idx, w_sel, shading="auto", vmax=vmax)
|
|
331
|
+
cbar = fig.colorbar(mesh, ax=ax)
|
|
332
|
+
cbar.set_label(R1_SAMPLES_LABEL)
|
|
333
|
+
et_name = getattr(getattr(event.trigger, "event_type", None), "name", "?")
|
|
334
|
+
tel = source.subarray.tel[tel_id]
|
|
335
|
+
tel_label = getattr(tel, "name", f"CT{tel_id}")
|
|
336
|
+
ax.set_title(f"{tel_label} waveform matrix ({et_name})")
|
|
337
|
+
ax.set_xlabel(TIME_NS_LABEL)
|
|
338
|
+
ax.set_ylabel("pixel id")
|
|
339
|
+
fig.tight_layout()
|
|
340
|
+
return fig
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def plot_simtel_step_traces(
|
|
344
|
+
filename,
|
|
345
|
+
tel_id: int | None = None,
|
|
346
|
+
pixel_step: int = 100,
|
|
347
|
+
max_pixels: int | None = None,
|
|
348
|
+
event_index: int | None = None,
|
|
349
|
+
):
|
|
350
|
+
"""
|
|
351
|
+
Plot step-style R1 traces for regularly sampled pixels (0, N, 2N, ...).
|
|
352
|
+
|
|
353
|
+
Parameters
|
|
354
|
+
----------
|
|
355
|
+
filename : str | pathlib.Path
|
|
356
|
+
Path to the ``.simtel`` file.
|
|
357
|
+
tel_id : int | None, optional
|
|
358
|
+
Telescope ID to use. If None, the first telescope with R1 data is chosen.
|
|
359
|
+
pixel_step : int, optional
|
|
360
|
+
Interval between pixel indices to plot. Default is 100.
|
|
361
|
+
max_pixels : int | None, optional
|
|
362
|
+
Maximum number of pixels to plot. If None, plot all selected by ``pixel_step``.
|
|
363
|
+
event_index : int | None, optional
|
|
364
|
+
Zero-based index of the event to plot. If None, the first event is used.
|
|
365
|
+
|
|
366
|
+
Returns
|
|
367
|
+
-------
|
|
368
|
+
matplotlib.figure.Figure | None
|
|
369
|
+
The created figure, or ``None`` if R1 waveforms are unavailable.
|
|
370
|
+
"""
|
|
371
|
+
source = EventSource(filename, max_events=None)
|
|
372
|
+
event = _select_event_by_type(source)(event_index=event_index)
|
|
373
|
+
|
|
374
|
+
r1_tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
|
|
375
|
+
if r1_tel_ids:
|
|
376
|
+
tel_id = tel_id or r1_tel_ids[0]
|
|
377
|
+
else:
|
|
378
|
+
_logger.warning("Event has no R1 data for traces plot")
|
|
379
|
+
return None
|
|
380
|
+
|
|
381
|
+
waveforms = getattr(event.r1.tel.get(tel_id, None), "waveform", None)
|
|
382
|
+
if waveforms is None:
|
|
383
|
+
_logger.warning(NO_R1_WAVEFORMS_MSG)
|
|
384
|
+
return None
|
|
385
|
+
|
|
386
|
+
w = np.asarray(waveforms)
|
|
387
|
+
if w.ndim == 3:
|
|
388
|
+
w = w[0]
|
|
389
|
+
n_pix, n_samp = w.shape
|
|
390
|
+
|
|
391
|
+
readout = source.subarray.tel[tel_id].camera.readout
|
|
392
|
+
t = _time_axis_from_readout(readout, n_samp)
|
|
393
|
+
|
|
394
|
+
pix_ids = np.arange(0, n_pix, max(1, pixel_step))
|
|
395
|
+
if max_pixels is not None:
|
|
396
|
+
pix_ids = pix_ids[:max_pixels]
|
|
397
|
+
|
|
398
|
+
fig, ax = plt.subplots(dpi=300)
|
|
399
|
+
for pid in pix_ids:
|
|
400
|
+
ax.plot(t, w[int(pid)], label=f"pix {int(pid)}", drawstyle="steps-mid")
|
|
401
|
+
ax.set_xlabel(TIME_NS_LABEL)
|
|
402
|
+
ax.set_ylabel(R1_SAMPLES_LABEL)
|
|
403
|
+
et_name = getattr(getattr(event.trigger, "event_type", None), "name", "?")
|
|
404
|
+
tel = source.subarray.tel[tel_id]
|
|
405
|
+
tel_label = getattr(tel, "name", f"CT{tel_id}")
|
|
406
|
+
ax.set_title(f"{tel_label} step traces ({et_name})")
|
|
407
|
+
ax.legend(loc="best", fontsize=7, ncol=2)
|
|
408
|
+
fig.tight_layout()
|
|
409
|
+
return fig
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _detect_peaks(trace, peak_width, signal_mod):
|
|
413
|
+
"""
|
|
414
|
+
Detect peak indices using CWT if available, otherwise ``find_peaks``.
|
|
415
|
+
|
|
416
|
+
Parameters
|
|
417
|
+
----------
|
|
418
|
+
trace : numpy.ndarray
|
|
419
|
+
One-dimensional waveform samples for a single pixel.
|
|
420
|
+
peak_width : int
|
|
421
|
+
Characteristic peak width (samples) for CWT.
|
|
422
|
+
signal_mod : module
|
|
423
|
+
SciPy ``signal``-like module providing ``find_peaks_cwt``/``find_peaks``.
|
|
424
|
+
|
|
425
|
+
Returns
|
|
426
|
+
-------
|
|
427
|
+
numpy.ndarray
|
|
428
|
+
Array of integer indices of detected peaks (possibly empty).
|
|
429
|
+
"""
|
|
430
|
+
peaks = []
|
|
431
|
+
try:
|
|
432
|
+
if hasattr(signal_mod, "find_peaks_cwt"):
|
|
433
|
+
peaks = signal_mod.find_peaks_cwt(trace, widths=np.array([peak_width]))
|
|
434
|
+
if not np.any(peaks):
|
|
435
|
+
peaks, _ = signal_mod.find_peaks(trace, prominence=np.max(trace) * 0.1)
|
|
436
|
+
except (ValueError, TypeError):
|
|
437
|
+
peaks = []
|
|
438
|
+
return np.asarray(peaks, dtype=int) if np.size(peaks) else np.array([], dtype=int)
|
|
439
|
+
|
|
440
|
+
|
|
441
|
+
def _collect_peak_samples(w, sum_threshold, peak_width, signal_mod):
|
|
442
|
+
"""
|
|
443
|
+
Compute peak-sample indices per pixel from waveform matrix.
|
|
444
|
+
|
|
445
|
+
Parameters
|
|
446
|
+
----------
|
|
447
|
+
w : numpy.ndarray
|
|
448
|
+
Waveform array of shape ``(n_pix, n_samples)`` (or ``(1, n_pix, n_samples)``).
|
|
449
|
+
sum_threshold : float
|
|
450
|
+
Minimum sum over samples for a pixel to be considered.
|
|
451
|
+
peak_width : int
|
|
452
|
+
Characteristic peak width (samples) for CWT.
|
|
453
|
+
signal_mod : module
|
|
454
|
+
SciPy ``signal``-like module providing peak finding routines.
|
|
455
|
+
|
|
456
|
+
Returns
|
|
457
|
+
-------
|
|
458
|
+
tuple[numpy.ndarray | None, numpy.ndarray | None, int]
|
|
459
|
+
``(peak_samples, pix_ids, found_count)`` where ``peak_samples`` are the
|
|
460
|
+
selected peak indices per considered pixel, ``pix_ids`` are the pixel
|
|
461
|
+
indices that passed ``sum_threshold``, and ``found_count`` is the number
|
|
462
|
+
of pixels with at least one detected peak. Returns ``(None, None, 0)`` if
|
|
463
|
+
no pixels passed the threshold.
|
|
464
|
+
"""
|
|
465
|
+
n_pix, _ = w.shape
|
|
466
|
+
sums = w.sum(axis=1)
|
|
467
|
+
has_signal = sums > float(sum_threshold)
|
|
468
|
+
pix_ids = np.arange(n_pix)[has_signal]
|
|
469
|
+
if pix_ids.size == 0:
|
|
470
|
+
return None, None, 0
|
|
471
|
+
|
|
472
|
+
peak_samples = []
|
|
473
|
+
found_count = 0
|
|
474
|
+
for pid in pix_ids:
|
|
475
|
+
trace = w[int(pid)]
|
|
476
|
+
pks = _detect_peaks(trace, peak_width, signal_mod)
|
|
477
|
+
if pks.size:
|
|
478
|
+
found_count += 1
|
|
479
|
+
peak_idx = int(pks[np.argmax(trace[pks])])
|
|
480
|
+
else:
|
|
481
|
+
peak_idx = int(np.argmax(trace))
|
|
482
|
+
peak_samples.append(peak_idx)
|
|
483
|
+
|
|
484
|
+
return np.asarray(peak_samples), pix_ids, found_count
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
def _histogram_edges(n_samp, timing_bins):
|
|
488
|
+
"""
|
|
489
|
+
Compute contiguous histogram bin edges for sample indices.
|
|
490
|
+
|
|
491
|
+
Parameters
|
|
492
|
+
----------
|
|
493
|
+
n_samp : int
|
|
494
|
+
Number of samples per trace.
|
|
495
|
+
timing_bins : int | None
|
|
496
|
+
Number of histogram bins. If None, use unit-width bins.
|
|
497
|
+
|
|
498
|
+
Returns
|
|
499
|
+
-------
|
|
500
|
+
numpy.ndarray
|
|
501
|
+
Array of bin edges spanning the sample index range.
|
|
502
|
+
"""
|
|
503
|
+
if timing_bins and timing_bins > 0:
|
|
504
|
+
return np.linspace(-0.5, n_samp - 0.5, int(timing_bins) + 1)
|
|
505
|
+
return np.arange(-0.5, n_samp + 0.5, 1.0)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _draw_peak_hist(
|
|
509
|
+
ax,
|
|
510
|
+
peak_samples,
|
|
511
|
+
edges,
|
|
512
|
+
mean_sample,
|
|
513
|
+
std_sample,
|
|
514
|
+
tel_label,
|
|
515
|
+
et_name,
|
|
516
|
+
considered,
|
|
517
|
+
found_count,
|
|
518
|
+
):
|
|
519
|
+
"""
|
|
520
|
+
Draw a histogram of peak samples with overlays and annotations.
|
|
521
|
+
|
|
522
|
+
Parameters
|
|
523
|
+
----------
|
|
524
|
+
ax : matplotlib.axes.Axes
|
|
525
|
+
Target axes to draw into.
|
|
526
|
+
|
|
527
|
+
Peak sample indices per pixel.
|
|
528
|
+
edges : numpy.ndarray
|
|
529
|
+
Histogram bin edges.
|
|
530
|
+
mean_sample : float
|
|
531
|
+
Mean of peak sample indices.
|
|
532
|
+
std_sample : float
|
|
533
|
+
Standard deviation of peak sample indices.
|
|
534
|
+
tel_label : str
|
|
535
|
+
Telescope label used in the title.
|
|
536
|
+
et_name : str
|
|
537
|
+
Event type name used in the title.
|
|
538
|
+
considered : int
|
|
539
|
+
Number of pixels considered (passed threshold).
|
|
540
|
+
found_count : int
|
|
541
|
+
Number of pixels with at least one detected peak.
|
|
542
|
+
|
|
543
|
+
Returns
|
|
544
|
+
-------
|
|
545
|
+
None
|
|
546
|
+
"""
|
|
547
|
+
counts, edges = np.histogram(peak_samples, bins=edges)
|
|
548
|
+
ax.bar(edges[:-1], counts, width=np.diff(edges), color="#5B90DC", align="edge")
|
|
549
|
+
ax.set_xlim(edges[0], edges[-1])
|
|
550
|
+
ax.set_xlabel("peak sample")
|
|
551
|
+
ax.set_ylabel("N pixels")
|
|
552
|
+
ax.axvline(
|
|
553
|
+
mean_sample,
|
|
554
|
+
color="#D8153C",
|
|
555
|
+
linestyle="--",
|
|
556
|
+
label=f"mean={mean_sample:.2f}",
|
|
557
|
+
)
|
|
558
|
+
ax.axvspan(
|
|
559
|
+
mean_sample - std_sample,
|
|
560
|
+
mean_sample + std_sample,
|
|
561
|
+
color="#D8153C",
|
|
562
|
+
alpha=0.2,
|
|
563
|
+
label=f"std={std_sample:.2f}",
|
|
564
|
+
)
|
|
565
|
+
ax.set_title(f"{tel_label} peak timing ({et_name})")
|
|
566
|
+
ax.text(
|
|
567
|
+
0.98,
|
|
568
|
+
0.95,
|
|
569
|
+
f"considered: {considered}\npeaks found: {found_count}",
|
|
570
|
+
transform=ax.transAxes,
|
|
571
|
+
ha="right",
|
|
572
|
+
va="top",
|
|
573
|
+
fontsize=7,
|
|
574
|
+
bbox={
|
|
575
|
+
"boxstyle": "round,pad=0.2",
|
|
576
|
+
"facecolor": "white",
|
|
577
|
+
"alpha": 0.6,
|
|
578
|
+
"linewidth": 0.0,
|
|
579
|
+
},
|
|
580
|
+
)
|
|
581
|
+
ax.legend(fontsize=7)
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
def plot_simtel_peak_timing(
|
|
585
|
+
filename,
|
|
586
|
+
tel_id: int | None = None,
|
|
587
|
+
sum_threshold: float = 10.0,
|
|
588
|
+
peak_width: int = 8,
|
|
589
|
+
examples: int = 3,
|
|
590
|
+
timing_bins: int | None = None,
|
|
591
|
+
return_stats: bool = False,
|
|
592
|
+
event_index: int | None = None,
|
|
593
|
+
):
|
|
594
|
+
"""
|
|
595
|
+
Peak finding per pixel; report mean/std of peak sample and plot a histogram.
|
|
596
|
+
|
|
597
|
+
Parameters
|
|
598
|
+
----------
|
|
599
|
+
filename : str | pathlib.Path
|
|
600
|
+
Path to the ``.simtel`` file.
|
|
601
|
+
tel_id : int | None, optional
|
|
602
|
+
Telescope ID to use. If None, the first telescope with R1 data is chosen.
|
|
603
|
+
sum_threshold : float, optional
|
|
604
|
+
Minimum sum over samples for a pixel to be considered. Default is 10.0.
|
|
605
|
+
peak_width : int, optional
|
|
606
|
+
Characteristic peak width (samples) for CWT. Default is 8.
|
|
607
|
+
examples : int, optional
|
|
608
|
+
Number of example pixel traces to overlay. Default is 3.
|
|
609
|
+
timing_bins : int | None, optional
|
|
610
|
+
Number of histogram bins. If None, use unit-width bins.
|
|
611
|
+
return_stats : bool, optional
|
|
612
|
+
If True, also return a statistics dictionary. Default is False.
|
|
613
|
+
event_index : int | None, optional
|
|
614
|
+
Zero-based index of the event to plot. If None, the first event is used.
|
|
615
|
+
|
|
616
|
+
Returns
|
|
617
|
+
-------
|
|
618
|
+
matplotlib.figure.Figure | tuple[matplotlib.figure.Figure, dict] | None
|
|
619
|
+
The created figure, or ``None`` if R1 waveforms are unavailable. If
|
|
620
|
+
``return_stats`` is True, a tuple ``(fig, stats)`` is returned, where
|
|
621
|
+
``stats`` has keys ``{"considered", "found", "mean", "std"}``.
|
|
622
|
+
"""
|
|
623
|
+
source = EventSource(filename, max_events=None)
|
|
624
|
+
event = _select_event_by_type(source)(event_index=event_index)
|
|
625
|
+
|
|
626
|
+
r1_tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
|
|
627
|
+
if r1_tel_ids:
|
|
628
|
+
tel_id = tel_id or r1_tel_ids[0]
|
|
629
|
+
else:
|
|
630
|
+
_logger.warning("Event has no R1 data for peak timing plot")
|
|
631
|
+
return None
|
|
632
|
+
|
|
633
|
+
waveforms = getattr(event.r1.tel.get(tel_id, None), "waveform", None)
|
|
634
|
+
if waveforms is None:
|
|
635
|
+
_logger.warning(NO_R1_WAVEFORMS_MSG)
|
|
636
|
+
return None
|
|
637
|
+
|
|
638
|
+
w = np.asarray(waveforms)
|
|
639
|
+
if w.ndim == 3:
|
|
640
|
+
w = w[0]
|
|
641
|
+
_, n_samp = w.shape
|
|
642
|
+
|
|
643
|
+
peak_samples, pix_ids, found_count = _collect_peak_samples(
|
|
644
|
+
w, sum_threshold, peak_width, _signal
|
|
645
|
+
)
|
|
646
|
+
if peak_samples is None or pix_ids is None:
|
|
647
|
+
_logger.warning("No pixels exceeded sum_threshold for peak timing")
|
|
648
|
+
return None
|
|
649
|
+
|
|
650
|
+
mean_sample = float(np.mean(peak_samples))
|
|
651
|
+
std_sample = float(np.std(peak_samples))
|
|
652
|
+
|
|
653
|
+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), dpi=300)
|
|
654
|
+
|
|
655
|
+
edges = _histogram_edges(n_samp, timing_bins)
|
|
656
|
+
et_name = getattr(getattr(event.trigger, "event_type", None), "name", "?")
|
|
657
|
+
tel = source.subarray.tel[tel_id]
|
|
658
|
+
tel_label = getattr(tel, "name", f"CT{tel_id}")
|
|
659
|
+
_draw_peak_hist(
|
|
660
|
+
ax1,
|
|
661
|
+
peak_samples,
|
|
662
|
+
edges,
|
|
663
|
+
mean_sample,
|
|
664
|
+
std_sample,
|
|
665
|
+
tel_label,
|
|
666
|
+
et_name,
|
|
667
|
+
pix_ids.size,
|
|
668
|
+
found_count,
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
readout = source.subarray.tel[tel_id].camera.readout
|
|
672
|
+
t = _time_axis_from_readout(readout, n_samp)
|
|
673
|
+
|
|
674
|
+
ex_ids = pix_ids[: max(1, int(examples))]
|
|
675
|
+
for pid in ex_ids:
|
|
676
|
+
trace = w[int(pid)]
|
|
677
|
+
pks = _detect_peaks(trace, peak_width, _signal)
|
|
678
|
+
ax2.plot(t, trace, drawstyle="steps-mid", label=f"pix {int(pid)}")
|
|
679
|
+
if pks.size:
|
|
680
|
+
ax2.scatter(t[pks], trace[pks], s=10)
|
|
681
|
+
ax2.set_xlabel(TIME_NS_LABEL)
|
|
682
|
+
ax2.set_ylabel(R1_SAMPLES_LABEL)
|
|
683
|
+
ax2.legend(fontsize=7)
|
|
684
|
+
|
|
685
|
+
fig.tight_layout()
|
|
686
|
+
|
|
687
|
+
if return_stats:
|
|
688
|
+
stats = {
|
|
689
|
+
"considered": int(pix_ids.size),
|
|
690
|
+
"found": int(found_count),
|
|
691
|
+
"mean": float(mean_sample),
|
|
692
|
+
"std": float(std_sample),
|
|
693
|
+
}
|
|
694
|
+
return fig, stats
|
|
695
|
+
return fig
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
def _prepare_waveforms_for_image(filename, tel_id, context_no_r1, event_index=None):
|
|
699
|
+
"""
|
|
700
|
+
Fetch R1 waveforms for one event/telescope and return prepared arrays.
|
|
701
|
+
|
|
702
|
+
Parameters
|
|
703
|
+
----------
|
|
704
|
+
filename : str | pathlib.Path
|
|
705
|
+
Path to the ``.simtel`` file.
|
|
706
|
+
tel_id : int | None
|
|
707
|
+
Telescope ID to use. If None, the first telescope with R1 data is chosen.
|
|
708
|
+
context_no_r1 : str
|
|
709
|
+
Short description used in warnings when no R1 data is available.
|
|
710
|
+
event_index : int | None, optional
|
|
711
|
+
Zero-based index of the event to use. If None, the first event is used.
|
|
712
|
+
|
|
713
|
+
Returns
|
|
714
|
+
-------
|
|
715
|
+
tuple | None
|
|
716
|
+
``(w, n_pix, n_samp, source, event, tel_id)`` where ``w`` is a
|
|
717
|
+
``numpy.ndarray`` of shape ``(n_pix, n_samples)``, ``n_pix`` and
|
|
718
|
+
``n_samp`` are integers, and ``source``, ``event`` and ``tel_id`` are
|
|
719
|
+
the ctapipe objects used. Returns ``None`` on failure.
|
|
720
|
+
"""
|
|
721
|
+
source = EventSource(filename, max_events=None)
|
|
722
|
+
event = _select_event_by_type(source)(event_index=event_index)
|
|
723
|
+
|
|
724
|
+
r1_tel_ids = sorted(getattr(event.r1, "tel", {}).keys())
|
|
725
|
+
if r1_tel_ids:
|
|
726
|
+
tel_id = tel_id or r1_tel_ids[0]
|
|
727
|
+
else:
|
|
728
|
+
_logger.warning(f"Event has no R1 data for {context_no_r1}")
|
|
729
|
+
return None
|
|
730
|
+
|
|
731
|
+
waveforms = getattr(event.r1.tel.get(tel_id, None), "waveform", None)
|
|
732
|
+
if waveforms is None:
|
|
733
|
+
_logger.warning(NO_R1_WAVEFORMS_MSG)
|
|
734
|
+
return None
|
|
735
|
+
|
|
736
|
+
w = np.asarray(waveforms)
|
|
737
|
+
if w.ndim == 3:
|
|
738
|
+
w = w[0]
|
|
739
|
+
n_pix, n_samp = w.shape
|
|
740
|
+
return w, n_pix, n_samp, source, event, tel_id
|
|
741
|
+
|
|
742
|
+
|
|
743
|
+
def plot_simtel_integrated_signal_image(
|
|
744
|
+
filename,
|
|
745
|
+
tel_id: int | None = None,
|
|
746
|
+
half_width: int = 8,
|
|
747
|
+
event_index: int | None = None,
|
|
748
|
+
):
|
|
749
|
+
"""Plot camera image of integrated signal per pixel around the flasher peak."""
|
|
750
|
+
return _plot_simtel_integrated_image(
|
|
751
|
+
filename=filename,
|
|
752
|
+
tel_id=tel_id,
|
|
753
|
+
half_width=half_width,
|
|
754
|
+
event_index=event_index,
|
|
755
|
+
mode="signal",
|
|
756
|
+
)
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
def plot_simtel_integrated_pedestal_image(
|
|
760
|
+
filename,
|
|
761
|
+
tel_id: int | None = None,
|
|
762
|
+
half_width: int = 8,
|
|
763
|
+
offset: int = 16,
|
|
764
|
+
event_index: int | None = None,
|
|
765
|
+
):
|
|
766
|
+
"""Plot camera image of integrated pedestal per pixel away from the flasher peak."""
|
|
767
|
+
return _plot_simtel_integrated_image(
|
|
768
|
+
filename=filename,
|
|
769
|
+
tel_id=tel_id,
|
|
770
|
+
half_width=half_width,
|
|
771
|
+
event_index=event_index,
|
|
772
|
+
mode="pedestal",
|
|
773
|
+
offset=offset,
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
def _plot_simtel_integrated_image(
|
|
778
|
+
filename,
|
|
779
|
+
tel_id: int | None,
|
|
780
|
+
half_width: int,
|
|
781
|
+
event_index: int | None,
|
|
782
|
+
mode: str,
|
|
783
|
+
offset: int | None = None,
|
|
784
|
+
):
|
|
785
|
+
"""Shared implementation for integrated signal/pedestal images.
|
|
786
|
+
|
|
787
|
+
mode: "signal" or "pedestal". For "pedestal", ``offset`` is used.
|
|
788
|
+
"""
|
|
789
|
+
context = "integrated-signal image" if mode == "signal" else "integrated-pedestal image"
|
|
790
|
+
prepared = _prepare_waveforms_for_image(filename, tel_id, context, event_index=event_index)
|
|
791
|
+
if prepared is None:
|
|
792
|
+
return None
|
|
793
|
+
|
|
794
|
+
w, n_pix, n_samp, source, event, tel_id = prepared
|
|
795
|
+
img = np.zeros(n_pix, dtype=float)
|
|
796
|
+
|
|
797
|
+
for pid in range(n_pix):
|
|
798
|
+
trace = w[pid]
|
|
799
|
+
peak_idx = int(np.argmax(trace))
|
|
800
|
+
a, b = _compute_integration_window(peak_idx, n_samp, half_width, mode, offset)
|
|
801
|
+
img[pid] = float(np.sum(trace[a:b]))
|
|
802
|
+
|
|
803
|
+
geometry = source.subarray.tel[tel_id].camera.geometry
|
|
804
|
+
fig, ax = plt.subplots(dpi=300)
|
|
805
|
+
disp = CameraDisplay(geometry, image=img, norm="lin", ax=ax)
|
|
806
|
+
disp.cmap = "viridis" if mode == "signal" else "cividis"
|
|
807
|
+
disp.add_colorbar(fraction=0.02, pad=-0.1)
|
|
808
|
+
disp.set_limits_percent(100)
|
|
809
|
+
|
|
810
|
+
et_name = getattr(getattr(event.trigger, "event_type", None), "name", "?")
|
|
811
|
+
tel = source.subarray.tel[tel_id]
|
|
812
|
+
tel_label = getattr(tel, "name", f"CT{tel_id}")
|
|
813
|
+
ax.set_title(_format_integrated_title(tel_label, et_name, half_width, mode, offset), pad=20)
|
|
814
|
+
ax.set_axis_off()
|
|
815
|
+
fig.tight_layout()
|
|
816
|
+
return fig
|