gammasimtools 0.6.1__py3-none-any.whl → 0.8.2__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.8.2.dist-info/METADATA +173 -0
- gammasimtools-0.8.2.dist-info/RECORD +345 -0
- {gammasimtools-0.6.1.dist-info → gammasimtools-0.8.2.dist-info}/WHEEL +1 -1
- gammasimtools-0.8.2.dist-info/entry_points.txt +31 -0
- simtools/_dev_version/__init__.py +9 -0
- simtools/_version.py +2 -2
- simtools/applications/calculate_trigger_rate.py +210 -0
- simtools/applications/convert_all_model_parameters_from_simtel.py +372 -0
- simtools/applications/{print_array_elements.py → convert_geo_coordinates_of_array_elements.py} +58 -63
- simtools/applications/convert_model_parameter_from_simtel.py +119 -0
- simtools/applications/{add_file_to_db.py → db_add_file_to_db.py} +70 -60
- simtools/applications/db_add_model_parameters_from_repository_to_db.py +184 -0
- simtools/applications/db_add_value_from_json_to_db.py +105 -0
- simtools/applications/db_development_tools/write_array_elements_positions_to_repository.py +180 -0
- simtools/applications/db_get_array_layouts_from_db.py +162 -0
- simtools/applications/{get_file_from_db.py → db_get_file_from_db.py} +30 -34
- simtools/applications/db_get_parameter_from_db.py +131 -0
- simtools/applications/db_inspect_databases.py +52 -0
- simtools/applications/derive_mirror_rnda.py +39 -255
- simtools/applications/derive_psf_parameters.py +441 -0
- simtools/applications/generate_array_config.py +82 -0
- simtools/applications/generate_corsika_histograms.py +52 -52
- simtools/applications/generate_default_metadata.py +5 -8
- simtools/applications/generate_regular_arrays.py +117 -0
- simtools/applications/generate_simtel_array_histograms.py +97 -56
- simtools/applications/plot_array_layout.py +345 -115
- simtools/applications/production_generate_simulation_config.py +158 -0
- simtools/applications/production_scale_events.py +168 -0
- simtools/applications/simulate_light_emission.py +478 -0
- simtools/applications/simulate_prod.py +97 -175
- simtools/applications/submit_data_from_external.py +9 -12
- simtools/applications/submit_model_parameter_from_external.py +122 -0
- simtools/applications/validate_camera_efficiency.py +35 -102
- simtools/applications/validate_camera_fov.py +20 -19
- simtools/applications/{compare_cumulative_psf.py → validate_cumulative_psf.py} +45 -44
- simtools/applications/validate_file_using_schema.py +111 -47
- simtools/applications/validate_optics.py +17 -22
- simtools/camera_efficiency.py +193 -202
- simtools/configuration/commandline_parser.py +384 -96
- simtools/configuration/configurator.py +55 -71
- simtools/constants.py +5 -5
- simtools/corsika/corsika_config.py +482 -342
- simtools/corsika/corsika_histograms.py +226 -204
- simtools/corsika/corsika_histograms_visualize.py +23 -24
- simtools/corsika/primary_particle.py +159 -0
- simtools/data_model/data_reader.py +25 -20
- simtools/data_model/format_checkers.py +52 -0
- simtools/data_model/metadata_collector.py +211 -185
- simtools/data_model/metadata_model.py +115 -37
- simtools/data_model/model_data_writer.py +335 -26
- simtools/data_model/validate_data.py +366 -154
- simtools/db/db_array_elements.py +130 -0
- simtools/db/db_from_repo_handler.py +106 -0
- simtools/db/db_handler.py +1246 -0
- simtools/io_operations/hdf5_handler.py +3 -1
- simtools/io_operations/io_handler.py +32 -57
- simtools/job_execution/job_manager.py +82 -69
- simtools/layout/array_layout.py +325 -537
- simtools/layout/geo_coordinates.py +8 -11
- simtools/layout/telescope_position.py +163 -86
- simtools/model/array_model.py +312 -259
- simtools/model/calibration_model.py +50 -0
- simtools/model/camera.py +277 -523
- simtools/model/mirrors.py +68 -49
- simtools/model/model_parameter.py +602 -0
- simtools/model/model_utils.py +11 -39
- simtools/model/site_model.py +161 -0
- simtools/model/telescope_model.py +143 -633
- simtools/production_configuration/calculate_statistical_errors_grid_point.py +454 -0
- simtools/production_configuration/event_scaler.py +146 -0
- simtools/production_configuration/generate_simulation_config.py +193 -0
- simtools/production_configuration/interpolation_handler.py +197 -0
- simtools/ray_tracing/__init__.py +0 -0
- simtools/ray_tracing/mirror_panel_psf.py +280 -0
- simtools/{psf_analysis.py → ray_tracing/psf_analysis.py} +133 -47
- simtools/ray_tracing/ray_tracing.py +646 -0
- simtools/runners/__init__.py +0 -0
- simtools/runners/corsika_runner.py +240 -0
- simtools/runners/corsika_simtel_runner.py +225 -0
- simtools/runners/runner_services.py +307 -0
- simtools/runners/simtel_runner.py +224 -0
- simtools/schemas/array_elements.yml +137 -0
- simtools/schemas/integration_tests_config.metaschema.yml +93 -0
- simtools/schemas/metadata.metaschema.yml +6 -0
- simtools/schemas/model_parameter.metaschema.yml +78 -0
- simtools/schemas/{data.metaschema.yml → model_parameter_and_data_schema.metaschema.yml} +27 -44
- simtools/schemas/model_parameters/adjust_gain.schema.yml +37 -0
- simtools/schemas/model_parameters/altitude.schema.yml +37 -0
- simtools/schemas/model_parameters/array_coordinates.schema.yml +33 -0
- simtools/schemas/model_parameters/array_coordinates_UTM.schema.yml +77 -0
- simtools/schemas/model_parameters/array_element_position_ground.schema.yml +39 -0
- simtools/schemas/model_parameters/array_element_position_utm.schema.yml +39 -0
- simtools/schemas/model_parameters/array_layouts.schema.yml +48 -0
- simtools/schemas/model_parameters/array_triggers.schema.yml +93 -0
- simtools/schemas/model_parameters/asum_clipping.schema.yml +38 -0
- simtools/schemas/model_parameters/asum_offset.schema.yml +35 -0
- simtools/schemas/model_parameters/asum_shaping.schema.yml +35 -0
- simtools/schemas/model_parameters/asum_threshold.schema.yml +38 -0
- simtools/schemas/model_parameters/atmospheric_profile.schema.yml +32 -0
- simtools/schemas/model_parameters/atmospheric_transmission.schema.yml +35 -0
- simtools/schemas/model_parameters/axes_offsets.schema.yml +53 -0
- simtools/schemas/model_parameters/camera_body_diameter.schema.yml +40 -0
- simtools/schemas/model_parameters/camera_body_shape.schema.yml +45 -0
- simtools/schemas/model_parameters/camera_config_file.schema.yml +40 -0
- simtools/schemas/model_parameters/camera_config_rotate.schema.yml +36 -0
- simtools/schemas/model_parameters/camera_degraded_efficiency.schema.yml +43 -0
- simtools/schemas/model_parameters/camera_degraded_map.schema.yml +42 -0
- simtools/schemas/model_parameters/camera_depth.schema.yml +42 -0
- simtools/schemas/model_parameters/camera_filter.schema.yml +45 -0
- simtools/schemas/model_parameters/camera_filter_incidence_angle.schema.yml +29 -0
- simtools/schemas/model_parameters/camera_pixels.schema.yml +36 -0
- simtools/schemas/model_parameters/camera_transmission.schema.yml +41 -0
- simtools/schemas/model_parameters/channels_per_chip.schema.yml +36 -0
- simtools/schemas/model_parameters/correct_nsb_spectrum_to_telescope_altitude.schema.yml +35 -0
- simtools/schemas/model_parameters/corsika_cherenkov_photon_bunch_size.schema.yml +27 -0
- simtools/schemas/model_parameters/corsika_cherenkov_photon_wavelength_range.schema.yml +38 -0
- simtools/schemas/model_parameters/corsika_first_interaction_height.schema.yml +28 -0
- simtools/schemas/model_parameters/corsika_iact_io_buffer.schema.yml +23 -0
- simtools/schemas/model_parameters/corsika_iact_max_bunches.schema.yml +27 -0
- simtools/schemas/model_parameters/corsika_iact_split_auto.schema.yml +28 -0
- simtools/schemas/model_parameters/corsika_longitudinal_shower_development.schema.yml +27 -0
- simtools/schemas/model_parameters/corsika_observation_level.schema.yml +38 -0
- simtools/schemas/model_parameters/corsika_particle_kinetic_energy_cutoff.schema.yml +52 -0
- simtools/schemas/model_parameters/corsika_starting_grammage.schema.yml +27 -0
- simtools/schemas/model_parameters/dark_events.schema.yml +32 -0
- simtools/schemas/model_parameters/default_trigger.schema.yml +35 -0
- simtools/schemas/model_parameters/design_model.schema.yml +31 -0
- simtools/schemas/model_parameters/disc_ac_coupled.schema.yml +32 -0
- simtools/schemas/model_parameters/disc_bins.schema.yml +39 -0
- simtools/schemas/model_parameters/disc_start.schema.yml +41 -0
- simtools/schemas/model_parameters/discriminator_amplitude.schema.yml +42 -0
- simtools/schemas/model_parameters/discriminator_fall_time.schema.yml +41 -0
- simtools/schemas/model_parameters/discriminator_gate_length.schema.yml +41 -0
- simtools/schemas/model_parameters/discriminator_hysteresis.schema.yml +39 -0
- simtools/schemas/model_parameters/discriminator_output_amplitude.schema.yml +40 -0
- simtools/schemas/model_parameters/discriminator_output_var_percent.schema.yml +41 -0
- simtools/schemas/model_parameters/discriminator_pulse_shape.schema.yml +33 -0
- simtools/schemas/model_parameters/discriminator_rise_time.schema.yml +42 -0
- simtools/schemas/model_parameters/discriminator_scale_threshold.schema.yml +37 -0
- simtools/schemas/model_parameters/discriminator_sigsum_over_threshold.schema.yml +44 -0
- simtools/schemas/model_parameters/discriminator_threshold.schema.yml +36 -0
- simtools/schemas/model_parameters/discriminator_time_over_threshold.schema.yml +45 -0
- simtools/schemas/model_parameters/discriminator_var_gate_length.schema.yml +40 -0
- simtools/schemas/model_parameters/discriminator_var_sigsum_over_threshold.schema.yml +41 -0
- simtools/schemas/model_parameters/discriminator_var_threshold.schema.yml +38 -0
- simtools/schemas/model_parameters/discriminator_var_time_over_threshold.schema.yml +38 -0
- simtools/schemas/model_parameters/dish_shape_length.schema.yml +41 -0
- simtools/schemas/model_parameters/dsum_clipping.schema.yml +38 -0
- simtools/schemas/model_parameters/dsum_ignore_below.schema.yml +38 -0
- simtools/schemas/model_parameters/dsum_offset.schema.yml +37 -0
- simtools/schemas/model_parameters/dsum_pedsub.schema.yml +33 -0
- simtools/schemas/model_parameters/dsum_pre_clipping.schema.yml +39 -0
- simtools/schemas/model_parameters/dsum_prescale.schema.yml +44 -0
- simtools/schemas/model_parameters/dsum_presum_max.schema.yml +38 -0
- simtools/schemas/model_parameters/dsum_presum_shift.schema.yml +45 -0
- simtools/schemas/model_parameters/dsum_shaping.schema.yml +44 -0
- simtools/schemas/model_parameters/dsum_shaping_renormalize.schema.yml +32 -0
- simtools/schemas/model_parameters/dsum_threshold.schema.yml +43 -0
- simtools/schemas/model_parameters/dsum_zero_clip.schema.yml +42 -0
- simtools/schemas/model_parameters/effective_focal_length.schema.yml +61 -0
- simtools/schemas/model_parameters/epsg_code.schema.yml +37 -0
- simtools/schemas/model_parameters/fadc_ac_coupled.schema.yml +35 -0
- simtools/schemas/model_parameters/fadc_amplitude.schema.yml +46 -0
- simtools/schemas/model_parameters/fadc_bins.schema.yml +40 -0
- simtools/schemas/model_parameters/fadc_compensate_pedestal.schema.yml +50 -0
- simtools/schemas/model_parameters/fadc_dev_pedestal.schema.yml +38 -0
- simtools/schemas/model_parameters/fadc_err_compensate_pedestal.schema.yml +42 -0
- simtools/schemas/model_parameters/fadc_err_pedestal.schema.yml +49 -0
- simtools/schemas/model_parameters/fadc_lg_amplitude.schema.yml +47 -0
- simtools/schemas/model_parameters/fadc_lg_compensate_pedestal.schema.yml +51 -0
- simtools/schemas/model_parameters/fadc_lg_dev_pedestal.schema.yml +37 -0
- simtools/schemas/model_parameters/fadc_lg_err_compensate_pedestal.schema.yml +43 -0
- simtools/schemas/model_parameters/fadc_lg_err_pedestal.schema.yml +49 -0
- simtools/schemas/model_parameters/fadc_lg_max_signal.schema.yml +43 -0
- simtools/schemas/model_parameters/fadc_lg_max_sum.schema.yml +39 -0
- simtools/schemas/model_parameters/fadc_lg_noise.schema.yml +42 -0
- simtools/schemas/model_parameters/fadc_lg_pedestal.schema.yml +40 -0
- simtools/schemas/model_parameters/fadc_lg_sensitivity.schema.yml +50 -0
- simtools/schemas/model_parameters/fadc_lg_sysvar_pedestal.schema.yml +42 -0
- simtools/schemas/model_parameters/fadc_lg_var_pedestal.schema.yml +41 -0
- simtools/schemas/model_parameters/fadc_lg_var_sensitivity.schema.yml +42 -0
- simtools/schemas/model_parameters/fadc_max_signal.schema.yml +43 -0
- simtools/schemas/model_parameters/fadc_max_sum.schema.yml +39 -0
- simtools/schemas/model_parameters/fadc_mhz.schema.yml +31 -0
- simtools/schemas/model_parameters/fadc_noise.schema.yml +41 -0
- simtools/schemas/model_parameters/fadc_pedestal.schema.yml +40 -0
- simtools/schemas/model_parameters/fadc_pulse_shape.schema.yml +39 -0
- simtools/schemas/model_parameters/fadc_sensitivity.schema.yml +50 -0
- simtools/schemas/model_parameters/fadc_sum_bins.schema.yml +43 -0
- simtools/schemas/model_parameters/fadc_sum_offset.schema.yml +43 -0
- simtools/schemas/model_parameters/fadc_sysvar_pedestal.schema.yml +42 -0
- simtools/schemas/model_parameters/fadc_var_pedestal.schema.yml +41 -0
- simtools/schemas/model_parameters/fadc_var_sensitivity.schema.yml +42 -0
- simtools/schemas/model_parameters/flatfielding.schema.yml +37 -0
- simtools/schemas/model_parameters/focal_length.schema.yml +45 -0
- simtools/schemas/model_parameters/focal_surface_parameters.schema.yml +158 -0
- simtools/schemas/model_parameters/focal_surface_ref_radius.schema.yml +29 -0
- simtools/schemas/model_parameters/focus_offset.schema.yml +66 -0
- simtools/schemas/model_parameters/gain_variation.schema.yml +43 -0
- simtools/schemas/model_parameters/geomag_horizontal.schema.yml +34 -0
- simtools/schemas/model_parameters/geomag_rotation.schema.yml +37 -0
- simtools/schemas/model_parameters/geomag_vertical.schema.yml +34 -0
- simtools/schemas/model_parameters/hg_lg_variation.schema.yml +36 -0
- simtools/schemas/model_parameters/iobuf_maximum.schema.yml +34 -0
- simtools/schemas/model_parameters/iobuf_output_maximum.schema.yml +34 -0
- simtools/schemas/model_parameters/laser_events.schema.yml +36 -0
- simtools/schemas/model_parameters/laser_external_trigger.schema.yml +35 -0
- simtools/schemas/model_parameters/laser_photons.schema.yml +32 -0
- simtools/schemas/model_parameters/laser_pulse_exptime.schema.yml +34 -0
- simtools/schemas/model_parameters/laser_pulse_offset.schema.yml +34 -0
- simtools/schemas/model_parameters/laser_pulse_sigtime.schema.yml +33 -0
- simtools/schemas/model_parameters/laser_pulse_twidth.schema.yml +33 -0
- simtools/schemas/model_parameters/laser_var_photons.schema.yml +33 -0
- simtools/schemas/model_parameters/laser_wavelength.schema.yml +33 -0
- simtools/schemas/model_parameters/led_events.schema.yml +34 -0
- simtools/schemas/model_parameters/led_photons.schema.yml +34 -0
- simtools/schemas/model_parameters/led_pulse_offset.schema.yml +32 -0
- simtools/schemas/model_parameters/led_pulse_sigtime.schema.yml +33 -0
- simtools/schemas/model_parameters/led_var_photons.schema.yml +34 -0
- simtools/schemas/model_parameters/lightguide_efficiency_vs_incidence_angle.schema.yml +41 -0
- simtools/schemas/model_parameters/lightguide_efficiency_vs_wavelength.schema.yml +43 -0
- simtools/schemas/model_parameters/min_photoelectrons.schema.yml +35 -0
- simtools/schemas/model_parameters/min_photons.schema.yml +32 -0
- simtools/schemas/model_parameters/mirror_align_random_distance.schema.yml +36 -0
- simtools/schemas/model_parameters/mirror_align_random_horizontal.schema.yml +64 -0
- simtools/schemas/model_parameters/mirror_align_random_vertical.schema.yml +64 -0
- simtools/schemas/model_parameters/mirror_class.schema.yml +41 -0
- simtools/schemas/model_parameters/mirror_degraded_reflection.schema.yml +51 -0
- simtools/schemas/model_parameters/mirror_focal_length.schema.yml +42 -0
- simtools/schemas/model_parameters/mirror_list.schema.yml +38 -0
- simtools/schemas/model_parameters/mirror_offset.schema.yml +41 -0
- simtools/schemas/model_parameters/mirror_panel_2f_measurements.schema.yml +39 -0
- simtools/schemas/model_parameters/mirror_reflection_random_angle.schema.yml +61 -0
- simtools/schemas/model_parameters/mirror_reflectivity.schema.yml +40 -0
- simtools/schemas/model_parameters/multiplicity_offset.schema.yml +46 -0
- simtools/schemas/model_parameters/nsb_autoscale_airmass.schema.yml +51 -0
- simtools/schemas/model_parameters/nsb_gain_drop_scale.schema.yml +37 -0
- simtools/schemas/model_parameters/nsb_offaxis.schema.yml +79 -0
- simtools/schemas/model_parameters/nsb_pixel_rate.schema.yml +47 -0
- simtools/schemas/model_parameters/nsb_reference_spectrum.schema.yml +34 -0
- simtools/schemas/model_parameters/nsb_reference_value.schema.yml +33 -0
- simtools/schemas/model_parameters/nsb_scaling_factor.schema.yml +35 -0
- simtools/schemas/model_parameters/nsb_skymap.schema.yml +39 -0
- simtools/schemas/model_parameters/nsb_spectrum.schema.yml +50 -0
- simtools/schemas/model_parameters/num_gains.schema.yml +34 -0
- simtools/schemas/model_parameters/only_triggered_telescopes.schema.yml +33 -0
- simtools/schemas/model_parameters/optics_properties.schema.yml +31 -0
- simtools/schemas/model_parameters/parabolic_dish.schema.yml +32 -0
- simtools/schemas/model_parameters/pedestal_events.schema.yml +32 -0
- simtools/schemas/model_parameters/photon_delay.schema.yml +38 -0
- simtools/schemas/model_parameters/photons_per_run.schema.yml +33 -0
- simtools/schemas/model_parameters/pixel_cells.schema.yml +35 -0
- simtools/schemas/model_parameters/pixels_parallel.schema.yml +54 -0
- simtools/schemas/model_parameters/pixeltrg_time_step.schema.yml +40 -0
- simtools/schemas/model_parameters/pm_average_gain.schema.yml +34 -0
- simtools/schemas/model_parameters/pm_collection_efficiency.schema.yml +40 -0
- simtools/schemas/model_parameters/pm_gain_index.schema.yml +36 -0
- simtools/schemas/model_parameters/pm_photoelectron_spectrum.schema.yml +41 -0
- simtools/schemas/model_parameters/pm_transit_time.schema.yml +63 -0
- simtools/schemas/model_parameters/pm_voltage_variation.schema.yml +39 -0
- simtools/schemas/model_parameters/primary_mirror_degraded_map.schema.yml +42 -0
- simtools/schemas/model_parameters/primary_mirror_diameter.schema.yml +33 -0
- simtools/schemas/model_parameters/primary_mirror_hole_diameter.schema.yml +33 -0
- simtools/schemas/model_parameters/primary_mirror_incidence_angle.schema.yml +29 -0
- simtools/schemas/model_parameters/primary_mirror_parameters.schema.yml +168 -0
- simtools/schemas/model_parameters/primary_mirror_ref_radius.schema.yml +36 -0
- simtools/schemas/model_parameters/primary_mirror_segmentation.schema.yml +34 -0
- simtools/schemas/model_parameters/qe_variation.schema.yml +43 -0
- simtools/schemas/model_parameters/quantum_efficiency.schema.yml +42 -0
- simtools/schemas/model_parameters/random_focal_length.schema.yml +45 -0
- simtools/schemas/model_parameters/random_generator.schema.yml +36 -0
- simtools/schemas/model_parameters/reference_point_altitude.schema.yml +35 -0
- simtools/schemas/model_parameters/reference_point_latitude.schema.yml +36 -0
- simtools/schemas/model_parameters/reference_point_longitude.schema.yml +36 -0
- simtools/schemas/model_parameters/reference_point_utm_east.schema.yml +34 -0
- simtools/schemas/model_parameters/reference_point_utm_north.schema.yml +34 -0
- simtools/schemas/model_parameters/sampled_output.schema.yml +31 -0
- simtools/schemas/model_parameters/save_pe_with_amplitude.schema.yml +34 -0
- simtools/schemas/model_parameters/secondary_mirror_baffle.schema.yml +79 -0
- simtools/schemas/model_parameters/secondary_mirror_degraded_map.schema.yml +42 -0
- simtools/schemas/model_parameters/secondary_mirror_degraded_reflection.schema.yml +41 -0
- simtools/schemas/model_parameters/secondary_mirror_diameter.schema.yml +33 -0
- simtools/schemas/model_parameters/secondary_mirror_hole_diameter.schema.yml +36 -0
- simtools/schemas/model_parameters/secondary_mirror_incidence_angle.schema.yml +29 -0
- simtools/schemas/model_parameters/secondary_mirror_parameters.schema.yml +168 -0
- simtools/schemas/model_parameters/secondary_mirror_ref_radius.schema.yml +36 -0
- simtools/schemas/model_parameters/secondary_mirror_reflectivity.schema.yml +35 -0
- simtools/schemas/model_parameters/secondary_mirror_segmentation.schema.yml +37 -0
- simtools/schemas/model_parameters/secondary_mirror_shadow_diameter.schema.yml +40 -0
- simtools/schemas/model_parameters/secondary_mirror_shadow_offset.schema.yml +40 -0
- simtools/schemas/model_parameters/store_photoelectrons.schema.yml +41 -0
- simtools/schemas/model_parameters/tailcut_scale.schema.yml +40 -0
- simtools/schemas/model_parameters/telescope_axis_height.schema.yml +31 -0
- simtools/schemas/model_parameters/telescope_random_angle.schema.yml +35 -0
- simtools/schemas/model_parameters/telescope_random_error.schema.yml +34 -0
- simtools/schemas/model_parameters/telescope_sphere_radius.schema.yml +37 -0
- simtools/schemas/model_parameters/telescope_transmission.schema.yml +113 -0
- simtools/schemas/model_parameters/teltrig_min_sigsum.schema.yml +41 -0
- simtools/schemas/model_parameters/teltrig_min_time.schema.yml +36 -0
- simtools/schemas/model_parameters/transit_time_calib_error.schema.yml +36 -0
- simtools/schemas/model_parameters/transit_time_compensate_error.schema.yml +37 -0
- simtools/schemas/model_parameters/transit_time_compensate_step.schema.yml +38 -0
- simtools/schemas/model_parameters/transit_time_error.schema.yml +45 -0
- simtools/schemas/model_parameters/transit_time_jitter.schema.yml +36 -0
- simtools/schemas/model_parameters/trigger_current_limit.schema.yml +32 -0
- simtools/schemas/model_parameters/trigger_delay_compensation.schema.yml +53 -0
- simtools/schemas/model_parameters/trigger_pixels.schema.yml +40 -0
- simtools/simtel/simtel_config_reader.py +353 -0
- simtools/simtel/simtel_config_writer.py +244 -63
- simtools/simtel/{simtel_events.py → simtel_io_events.py} +26 -25
- simtools/simtel/simtel_io_histogram.py +661 -0
- simtools/simtel/simtel_io_histograms.py +569 -0
- simtools/simtel/simulator_array.py +145 -0
- simtools/simtel/{simtel_runner_camera_efficiency.py → simulator_camera_efficiency.py} +76 -52
- simtools/simtel/simulator_light_emission.py +473 -0
- simtools/simtel/simulator_ray_tracing.py +262 -0
- simtools/simulator.py +220 -446
- simtools/testing/__init__.py +0 -0
- simtools/testing/assertions.py +151 -0
- simtools/testing/configuration.py +226 -0
- simtools/testing/helpers.py +42 -0
- simtools/testing/validate_output.py +240 -0
- simtools/utils/general.py +340 -437
- simtools/utils/geometry.py +12 -12
- simtools/utils/names.py +257 -644
- simtools/utils/value_conversion.py +176 -0
- simtools/version.py +3 -1
- simtools/visualization/legend_handlers.py +135 -152
- simtools/visualization/plot_camera.py +379 -0
- simtools/visualization/visualize.py +346 -167
- gammasimtools-0.6.1.dist-info/METADATA +0 -180
- gammasimtools-0.6.1.dist-info/RECORD +0 -91
- gammasimtools-0.6.1.dist-info/entry_points.txt +0 -23
- simtools/_dev_version/scm_version.py +0 -10
- simtools/applications/db_development_tools/add_new_parameter_to_db.py +0 -81
- simtools/applications/db_development_tools/add_unit_to_parameter_in_db.py +0 -59
- simtools/applications/db_development_tools/mark_non_optics_parameters_non_applicable.py +0 -102
- simtools/applications/get_parameter.py +0 -92
- simtools/applications/make_regular_arrays.py +0 -160
- simtools/applications/produce_array_config.py +0 -136
- simtools/applications/production.py +0 -313
- simtools/applications/sim_showers_for_trigger_rates.py +0 -187
- simtools/applications/tune_psf.py +0 -334
- simtools/corsika/corsika_default_config.py +0 -282
- simtools/corsika/corsika_runner.py +0 -450
- simtools/corsika_simtel/corsika_simtel_runner.py +0 -197
- simtools/db_handler.py +0 -1480
- simtools/ray_tracing.py +0 -525
- simtools/simtel/simtel_histograms.py +0 -414
- simtools/simtel/simtel_runner.py +0 -244
- simtools/simtel/simtel_runner_array.py +0 -293
- simtools/simtel/simtel_runner_ray_tracing.py +0 -277
- {gammasimtools-0.6.1.dist-info → gammasimtools-0.8.2.dist-info}/LICENSE +0 -0
- {gammasimtools-0.6.1.dist-info → gammasimtools-0.8.2.dist-info}/top_level.txt +0 -0
- /simtools/{corsika_simtel → db}/__init__.py +0 -0
simtools/utils/general.py
CHANGED
|
@@ -1,301 +1,62 @@
|
|
|
1
|
-
"""
|
|
2
|
-
General functions useful across different parts of the code.
|
|
3
|
-
"""
|
|
1
|
+
"""General functions useful across different parts of the code."""
|
|
4
2
|
|
|
5
3
|
import copy
|
|
6
4
|
import json
|
|
7
5
|
import logging
|
|
8
6
|
import os
|
|
9
|
-
import re
|
|
10
7
|
import tempfile
|
|
11
8
|
import time
|
|
12
9
|
import urllib.error
|
|
13
10
|
import urllib.request
|
|
14
|
-
from collections import namedtuple
|
|
15
11
|
from pathlib import Path
|
|
16
12
|
from urllib.parse import urlparse
|
|
17
13
|
|
|
18
|
-
import
|
|
19
|
-
|
|
14
|
+
import numpy as np
|
|
15
|
+
import yaml
|
|
20
16
|
|
|
21
17
|
__all__ = [
|
|
18
|
+
"InvalidConfigDataError",
|
|
22
19
|
"change_dict_keys_case",
|
|
23
|
-
"
|
|
20
|
+
"collect_data_from_file",
|
|
24
21
|
"collect_final_lines",
|
|
25
22
|
"collect_kwargs",
|
|
26
|
-
"
|
|
27
|
-
"InvalidConfigEntry",
|
|
28
|
-
"MissingRequiredConfigEntry",
|
|
29
|
-
"UnableToIdentifyConfigEntry",
|
|
23
|
+
"get_log_excerpt",
|
|
30
24
|
"get_log_level_from_user",
|
|
31
25
|
"remove_substring_recursively_from_dict",
|
|
32
|
-
"separate_args_and_config_data",
|
|
33
26
|
"set_default_kwargs",
|
|
34
|
-
"validate_config_data",
|
|
35
|
-
"get_log_excerpt",
|
|
36
27
|
"sort_arrays",
|
|
37
28
|
]
|
|
38
29
|
|
|
39
30
|
_logger = logging.getLogger(__name__)
|
|
40
31
|
|
|
41
32
|
|
|
42
|
-
class
|
|
43
|
-
"""Exception for unable to indentify configuration entry."""
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
class MissingRequiredConfigEntry(Exception):
|
|
47
|
-
"""Exception for missing required configuration entry."""
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
class InvalidConfigEntry(Exception):
|
|
51
|
-
"""Exception for invalid configuration entry."""
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
class InvalidConfigData(Exception):
|
|
33
|
+
class InvalidConfigDataError(Exception):
|
|
55
34
|
"""Exception for invalid configuration data."""
|
|
56
35
|
|
|
57
36
|
|
|
58
|
-
def
|
|
37
|
+
def join_url_or_path(url_or_path, *args):
|
|
59
38
|
"""
|
|
60
|
-
|
|
61
|
-
given by the parameters dict. The entries will be validated
|
|
62
|
-
in terms of length, units and names.
|
|
63
|
-
|
|
64
|
-
See data/test-data/test_parameters.yml for an example of the structure
|
|
65
|
-
of the parameters dict.
|
|
66
|
-
|
|
67
|
-
Parameters
|
|
68
|
-
----------
|
|
69
|
-
config_data: dict
|
|
70
|
-
Input config data.
|
|
71
|
-
parameters: dict
|
|
72
|
-
Parameter information necessary for validation.
|
|
73
|
-
ignore_unidentified: bool
|
|
74
|
-
If set to True, unidentified parameters provided in config_data are ignored
|
|
75
|
-
and a debug message is printed. Otherwise, an unidentified parameter leads to an error.
|
|
76
|
-
|
|
77
|
-
Raises
|
|
78
|
-
------
|
|
79
|
-
UnableToIdentifyConfigEntry
|
|
80
|
-
When an entry in config_data cannot be identified among the parameters.
|
|
81
|
-
MissingRequiredConfigEntry
|
|
82
|
-
When a parameter without default value is not given in config_data.
|
|
83
|
-
InvalidConfigEntry
|
|
84
|
-
When an entry in config_data is invalid (wrong len, wrong unit, ...).
|
|
85
|
-
|
|
86
|
-
Returns
|
|
87
|
-
-------
|
|
88
|
-
namedtuple:
|
|
89
|
-
Containing the validated config data entries.
|
|
90
|
-
"""
|
|
91
|
-
|
|
92
|
-
# Dict to be filled and returned
|
|
93
|
-
out_data = {}
|
|
94
|
-
|
|
95
|
-
if config_data is None:
|
|
96
|
-
config_data = {}
|
|
97
|
-
|
|
98
|
-
# Collecting all entries given as in config_data.
|
|
99
|
-
for key_data, value_data in config_data.items():
|
|
100
|
-
is_identified = False
|
|
101
|
-
# Searching for the key in the parameters.
|
|
102
|
-
for par_name, par_info in parameters.items():
|
|
103
|
-
names = par_info.get("names", [])
|
|
104
|
-
if key_data != par_name and key_data.lower() not in [n.lower() for n in names]:
|
|
105
|
-
continue
|
|
106
|
-
# Matched parameter
|
|
107
|
-
validated_value = _validate_and_convert_value(par_name, par_info, value_data)
|
|
108
|
-
out_data[par_name] = validated_value
|
|
109
|
-
is_identified = True
|
|
110
|
-
|
|
111
|
-
# Raising error for an unidentified input.
|
|
112
|
-
if not is_identified:
|
|
113
|
-
msg = f"Entry {key_data} in config_data cannot be identified"
|
|
114
|
-
if ignore_unidentified:
|
|
115
|
-
_logger.debug(f"{msg}, ignoring.")
|
|
116
|
-
else:
|
|
117
|
-
_logger.error(f"{msg}, stopping.")
|
|
118
|
-
raise UnableToIdentifyConfigEntry(msg)
|
|
119
|
-
|
|
120
|
-
# Checking for parameters with default option.
|
|
121
|
-
# If it is not given, filling it with the default value.
|
|
122
|
-
for par_name, par_info in parameters.items():
|
|
123
|
-
if par_name in out_data:
|
|
124
|
-
continue
|
|
125
|
-
if "default" in par_info.keys() and par_info["default"] is not None:
|
|
126
|
-
default_value = par_info["default"]
|
|
127
|
-
if not isinstance(default_value, u.Quantity) and "unit" in par_info:
|
|
128
|
-
default_value *= par_info["unit"]
|
|
129
|
-
validated_value = _validate_and_convert_value(par_name, par_info, default_value)
|
|
130
|
-
out_data[par_name] = validated_value
|
|
131
|
-
elif "default" in par_info.keys() and par_info["default"] is None:
|
|
132
|
-
out_data[par_name] = None
|
|
133
|
-
else:
|
|
134
|
-
msg = f"Required entry in config_data {par_name} was not given (there may be more)."
|
|
135
|
-
_logger.error(msg)
|
|
136
|
-
raise MissingRequiredConfigEntry(msg)
|
|
137
|
-
|
|
138
|
-
configuration_data = namedtuple("configuration_data", out_data)
|
|
139
|
-
return configuration_data(**out_data)
|
|
39
|
+
Join URL or path with additional subdirectories and file.
|
|
140
40
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"""
|
|
144
|
-
Validate input user parameter for input values without units.
|
|
41
|
+
This is the equivalent to Path.join(), with extended functionality
|
|
42
|
+
working also for URLs.
|
|
145
43
|
|
|
146
44
|
Parameters
|
|
147
45
|
----------
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
par_name: str
|
|
153
|
-
name of parameter.
|
|
154
|
-
par_info: dict
|
|
155
|
-
dictionary with parameter info.
|
|
46
|
+
url_or_path: str or Path
|
|
47
|
+
URL or path to be extended.
|
|
48
|
+
args: list
|
|
49
|
+
Additional arguments to be added to the URL or path.
|
|
156
50
|
|
|
157
51
|
Returns
|
|
158
52
|
-------
|
|
159
|
-
|
|
160
|
-
|
|
53
|
+
str or Path
|
|
54
|
+
Extended URL or path.
|
|
161
55
|
|
|
162
56
|
"""
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
# Checking if values have unit and raising error, if so.
|
|
167
|
-
if all(isinstance(v, str) for v in value):
|
|
168
|
-
# In case values are string, e.g. mirror_numbers = 'all'
|
|
169
|
-
# This is needed otherwise the elif condition will break
|
|
170
|
-
pass
|
|
171
|
-
elif any(u.Quantity(v).unit != u.dimensionless_unscaled for v in value):
|
|
172
|
-
msg = f"Config entry {par_name} should not have units"
|
|
173
|
-
_logger.error(msg)
|
|
174
|
-
raise InvalidConfigEntry(msg)
|
|
175
|
-
|
|
176
|
-
if value_keys:
|
|
177
|
-
return dict(zip(value_keys, value))
|
|
178
|
-
return value if len(value) > 1 or undefined_length else value[0]
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
def _check_value_entry_length(value, par_name, par_info):
|
|
182
|
-
"""
|
|
183
|
-
Validate length of user input parameters
|
|
184
|
-
|
|
185
|
-
Parameters
|
|
186
|
-
----------
|
|
187
|
-
value: list
|
|
188
|
-
list of user input values
|
|
189
|
-
par_name: str
|
|
190
|
-
name of parameter
|
|
191
|
-
par_info: dict
|
|
192
|
-
dictionary with parameter info
|
|
193
|
-
|
|
194
|
-
Returns
|
|
195
|
-
-------
|
|
196
|
-
value_length: int
|
|
197
|
-
length of input list
|
|
198
|
-
undefined_length: bool
|
|
199
|
-
state of input list
|
|
200
|
-
|
|
201
|
-
"""
|
|
202
|
-
|
|
203
|
-
# Checking the entry length
|
|
204
|
-
value_length = len(value)
|
|
205
|
-
_logger.debug(f"Value len of {par_name}: {value_length}")
|
|
206
|
-
undefined_length = False
|
|
207
|
-
try:
|
|
208
|
-
if par_info["len"] is None:
|
|
209
|
-
undefined_length = True
|
|
210
|
-
elif value_length != par_info["len"]:
|
|
211
|
-
msg = f"Config entry with wrong len: {par_name}"
|
|
212
|
-
_logger.error(msg)
|
|
213
|
-
raise InvalidConfigEntry(msg)
|
|
214
|
-
except KeyError:
|
|
215
|
-
_logger.error("Missing len entry in par_info")
|
|
216
|
-
raise
|
|
217
|
-
|
|
218
|
-
return value_length, undefined_length
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
def _validate_and_convert_value_with_units(value, value_keys, par_name, par_info):
|
|
222
|
-
"""
|
|
223
|
-
Validate input user parameter for input values with units.
|
|
224
|
-
|
|
225
|
-
Parameters
|
|
226
|
-
----------
|
|
227
|
-
value: list
|
|
228
|
-
list of user input values
|
|
229
|
-
value_keys: list
|
|
230
|
-
list of keys if user input was a dict; otherwise None
|
|
231
|
-
par_name: str
|
|
232
|
-
name of parameter
|
|
233
|
-
|
|
234
|
-
Returns
|
|
235
|
-
-------
|
|
236
|
-
list, dict
|
|
237
|
-
validated and converted input data
|
|
238
|
-
|
|
239
|
-
"""
|
|
240
|
-
|
|
241
|
-
value_length, undefined_length = _check_value_entry_length(value, par_name, par_info)
|
|
242
|
-
|
|
243
|
-
par_unit = copy_as_list(par_info["unit"])
|
|
244
|
-
|
|
245
|
-
if undefined_length and len(par_unit) != 1:
|
|
246
|
-
msg = f"Config entry with undefined length should have a single unit: {par_name}"
|
|
247
|
-
_logger.error(msg)
|
|
248
|
-
raise InvalidConfigEntry(msg)
|
|
249
|
-
if len(par_unit) == 1:
|
|
250
|
-
par_unit *= value_length
|
|
251
|
-
|
|
252
|
-
# Checking units and converting them, if needed.
|
|
253
|
-
value_with_units = []
|
|
254
|
-
for arg, unit in zip(value, par_unit):
|
|
255
|
-
# In case a entry is None, None should be returned.
|
|
256
|
-
if unit is None or arg is None:
|
|
257
|
-
value_with_units.append(arg)
|
|
258
|
-
continue
|
|
259
|
-
|
|
260
|
-
# Converting strings to Quantity
|
|
261
|
-
if isinstance(arg, str):
|
|
262
|
-
arg = u.quantity.Quantity(arg)
|
|
263
|
-
|
|
264
|
-
if not isinstance(arg, u.quantity.Quantity):
|
|
265
|
-
msg = f"Config entry given without unit: {par_name}"
|
|
266
|
-
_logger.error(msg)
|
|
267
|
-
raise InvalidConfigEntry(msg)
|
|
268
|
-
if not arg.unit.is_equivalent(unit):
|
|
269
|
-
msg = f"Config entry given with wrong unit: {par_name}"
|
|
270
|
-
_logger.error(msg)
|
|
271
|
-
raise InvalidConfigEntry(msg)
|
|
272
|
-
value_with_units.append(arg.to(unit).value)
|
|
273
|
-
|
|
274
|
-
if value_keys:
|
|
275
|
-
return dict(zip(value_keys, value_with_units))
|
|
276
|
-
|
|
277
|
-
return (
|
|
278
|
-
value_with_units if len(value_with_units) > 1 or undefined_length else value_with_units[0]
|
|
279
|
-
)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
def _validate_and_convert_value(par_name, par_info, value_in):
|
|
283
|
-
"""
|
|
284
|
-
Validate input user parameter and convert it to the right units, if needed.
|
|
285
|
-
Returns the validated arguments in a list.
|
|
286
|
-
"""
|
|
287
|
-
|
|
288
|
-
if isinstance(value_in, dict):
|
|
289
|
-
value = [d for (k, d) in value_in.items()]
|
|
290
|
-
value_keys = [k for (k, d) in value_in.items()]
|
|
291
|
-
else:
|
|
292
|
-
value = copy_as_list(value_in)
|
|
293
|
-
value_keys = None
|
|
294
|
-
|
|
295
|
-
if "unit" not in par_info.keys():
|
|
296
|
-
return _validate_and_convert_value_without_units(value, value_keys, par_name, par_info)
|
|
297
|
-
|
|
298
|
-
return _validate_and_convert_value_with_units(value, value_keys, par_name, par_info)
|
|
57
|
+
if "://" in str(url_or_path):
|
|
58
|
+
return "/".join([url_or_path.rstrip("/"), *args])
|
|
59
|
+
return Path(url_or_path).joinpath(*args)
|
|
299
60
|
|
|
300
61
|
|
|
301
62
|
def is_url(url):
|
|
@@ -313,7 +74,6 @@ def is_url(url):
|
|
|
313
74
|
True if url is a valid URL.
|
|
314
75
|
|
|
315
76
|
"""
|
|
316
|
-
|
|
317
77
|
try:
|
|
318
78
|
result = urlparse(url)
|
|
319
79
|
return all([result.scheme, result.netloc])
|
|
@@ -324,6 +84,7 @@ def is_url(url):
|
|
|
324
84
|
def collect_data_from_http(url):
|
|
325
85
|
"""
|
|
326
86
|
Download yaml or json file from url and return it contents as dict.
|
|
87
|
+
|
|
327
88
|
File is downloaded as a temporary file and deleted afterwards.
|
|
328
89
|
|
|
329
90
|
Parameters
|
|
@@ -340,76 +101,69 @@ def collect_data_from_http(url):
|
|
|
340
101
|
------
|
|
341
102
|
TypeError
|
|
342
103
|
If url is not a valid URL.
|
|
343
|
-
|
|
104
|
+
FileNotFoundError
|
|
344
105
|
If downloading the yaml file fails.
|
|
345
106
|
|
|
346
107
|
"""
|
|
347
|
-
|
|
348
|
-
_logger.debug(f"Downloaded yaml file from {url}")
|
|
349
108
|
try:
|
|
350
|
-
with tempfile.NamedTemporaryFile() as tmp_file:
|
|
109
|
+
with tempfile.NamedTemporaryFile(mode="w+t") as tmp_file:
|
|
351
110
|
urllib.request.urlretrieve(url, tmp_file.name)
|
|
352
111
|
if url.endswith("yml") or url.endswith("yaml"):
|
|
353
|
-
|
|
112
|
+
try:
|
|
113
|
+
data = yaml.safe_load(tmp_file)
|
|
114
|
+
except yaml.constructor.ConstructorError:
|
|
115
|
+
data = _load_yaml_using_astropy(tmp_file)
|
|
354
116
|
elif url.endswith("json"):
|
|
355
117
|
data = json.load(tmp_file)
|
|
118
|
+
elif url.endswith("list"):
|
|
119
|
+
lines = tmp_file.readlines()
|
|
120
|
+
data = [line.strip() for line in lines]
|
|
356
121
|
else:
|
|
357
122
|
msg = f"File extension of {url} not supported (should be json or yaml)"
|
|
358
123
|
_logger.error(msg)
|
|
359
124
|
raise TypeError(msg)
|
|
360
|
-
except TypeError:
|
|
125
|
+
except TypeError as exc:
|
|
361
126
|
msg = "Invalid url {url}"
|
|
362
127
|
_logger.error(msg)
|
|
363
|
-
raise
|
|
364
|
-
except urllib.error.HTTPError:
|
|
365
|
-
msg = f"Failed to download
|
|
128
|
+
raise TypeError(msg) from exc
|
|
129
|
+
except urllib.error.HTTPError as exc:
|
|
130
|
+
msg = f"Failed to download file from {url}"
|
|
366
131
|
_logger.error(msg)
|
|
367
|
-
raise
|
|
132
|
+
raise FileNotFoundError(msg) from exc
|
|
368
133
|
|
|
134
|
+
_logger.debug(f"Downloaded file from {url}")
|
|
369
135
|
return data
|
|
370
136
|
|
|
371
137
|
|
|
372
|
-
def
|
|
138
|
+
def collect_data_from_file(file_name):
|
|
373
139
|
"""
|
|
374
|
-
Collect
|
|
140
|
+
Collect data from file based on its extension.
|
|
375
141
|
|
|
376
142
|
Parameters
|
|
377
143
|
----------
|
|
378
144
|
file_name: str
|
|
379
|
-
Name of the yaml/json file.
|
|
380
|
-
in_dict: dict
|
|
381
|
-
Data as dict.
|
|
382
|
-
allow_empty: bool
|
|
383
|
-
If True, an error won't be raised in case both yaml and dict are None.
|
|
145
|
+
Name of the yaml/json/ascii file.
|
|
384
146
|
|
|
385
147
|
Returns
|
|
386
148
|
-------
|
|
387
|
-
data: dict
|
|
388
|
-
Data as dict.
|
|
389
|
-
"""
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
if in_dict is not None:
|
|
393
|
-
_logger.warning("Both in_dict and file_name were given - file_name will be used")
|
|
394
|
-
if is_url(str(file_name)):
|
|
395
|
-
data = collect_data_from_http(file_name)
|
|
396
|
-
elif Path(file_name).suffix.lower() == ".json":
|
|
397
|
-
with open(file_name, encoding="utf-8") as file:
|
|
398
|
-
data = json.load(file)
|
|
399
|
-
else:
|
|
400
|
-
with open(file_name, encoding="utf-8") as file:
|
|
401
|
-
data = yaml.load(file)
|
|
402
|
-
return data
|
|
403
|
-
if in_dict is not None:
|
|
404
|
-
return dict(in_dict)
|
|
149
|
+
data: dict or list
|
|
150
|
+
Data as dict or list.
|
|
151
|
+
"""
|
|
152
|
+
if is_url(file_name):
|
|
153
|
+
return collect_data_from_http(file_name)
|
|
405
154
|
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
155
|
+
with open(file_name, encoding="utf-8") as file:
|
|
156
|
+
if Path(file_name).suffix.lower() == ".json":
|
|
157
|
+
return json.load(file)
|
|
158
|
+
|
|
159
|
+
if Path(file_name).suffix.lower() == ".list":
|
|
160
|
+
lines = file.readlines()
|
|
161
|
+
return [line.strip() for line in lines]
|
|
410
162
|
|
|
411
|
-
|
|
412
|
-
|
|
163
|
+
try:
|
|
164
|
+
return yaml.safe_load(file)
|
|
165
|
+
except yaml.constructor.ConstructorError:
|
|
166
|
+
return _load_yaml_using_astropy(file)
|
|
413
167
|
|
|
414
168
|
|
|
415
169
|
def collect_kwargs(label, in_kwargs):
|
|
@@ -422,6 +176,7 @@ def collect_kwargs(label, in_kwargs):
|
|
|
422
176
|
Label to be collected in kwargs.
|
|
423
177
|
in_kwargs: dict
|
|
424
178
|
kwargs.
|
|
179
|
+
|
|
425
180
|
Returns
|
|
426
181
|
-------
|
|
427
182
|
dict
|
|
@@ -528,7 +283,6 @@ def get_log_level_from_user(log_level):
|
|
|
528
283
|
logging.LEVEL
|
|
529
284
|
The requested logging level to be used as input to logging.setLevel().
|
|
530
285
|
"""
|
|
531
|
-
|
|
532
286
|
possible_levels = {
|
|
533
287
|
"info": logging.INFO,
|
|
534
288
|
"debug": logging.DEBUG,
|
|
@@ -537,7 +291,10 @@ def get_log_level_from_user(log_level):
|
|
|
537
291
|
"error": logging.ERROR,
|
|
538
292
|
"critical": logging.CRITICAL,
|
|
539
293
|
}
|
|
540
|
-
|
|
294
|
+
try:
|
|
295
|
+
log_level_lower = log_level.lower()
|
|
296
|
+
except AttributeError:
|
|
297
|
+
log_level_lower = log_level
|
|
541
298
|
if log_level_lower not in possible_levels:
|
|
542
299
|
raise ValueError(
|
|
543
300
|
f"'{log_level}' is not a logging level, "
|
|
@@ -569,37 +326,9 @@ def copy_as_list(value):
|
|
|
569
326
|
return [value]
|
|
570
327
|
|
|
571
328
|
|
|
572
|
-
def separate_args_and_config_data(expected_args, **kwargs):
|
|
573
|
-
"""
|
|
574
|
-
Separate kwargs into the arguments expected for instancing a class and the dict to be given as
|
|
575
|
-
config_data. This function is specific for methods from_kwargs in classes which use the
|
|
576
|
-
validate_config_data system.
|
|
577
|
-
|
|
578
|
-
Parameters
|
|
579
|
-
----------
|
|
580
|
-
expected_args: list of str
|
|
581
|
-
List of arguments expected for the class.
|
|
582
|
-
**kwargs
|
|
583
|
-
|
|
584
|
-
Returns
|
|
585
|
-
-------
|
|
586
|
-
dict, dict
|
|
587
|
-
A dict with the args collected and another one with config_data.
|
|
588
|
-
"""
|
|
589
|
-
args = {}
|
|
590
|
-
config_data = {}
|
|
591
|
-
for key, value in kwargs.items():
|
|
592
|
-
if key in expected_args:
|
|
593
|
-
args[key] = value
|
|
594
|
-
else:
|
|
595
|
-
config_data[key] = value
|
|
596
|
-
|
|
597
|
-
return args, config_data
|
|
598
|
-
|
|
599
|
-
|
|
600
329
|
def program_is_executable(program):
|
|
601
330
|
"""
|
|
602
|
-
|
|
331
|
+
Check if program exists and is executable.
|
|
603
332
|
|
|
604
333
|
Follows https://stackoverflow.com/questions/377017/
|
|
605
334
|
|
|
@@ -619,12 +348,31 @@ def program_is_executable(program):
|
|
|
619
348
|
if is_exe(exe_file):
|
|
620
349
|
return exe_file
|
|
621
350
|
except KeyError:
|
|
622
|
-
_logger.
|
|
351
|
+
_logger.warning("PATH environment variable is not set.")
|
|
623
352
|
return None
|
|
624
353
|
|
|
625
354
|
return None
|
|
626
355
|
|
|
627
356
|
|
|
357
|
+
def _search_directory(directory, filename, rec=False):
|
|
358
|
+
if not Path(directory).exists():
|
|
359
|
+
_logger.debug(f"Directory {directory} does not exist")
|
|
360
|
+
return None
|
|
361
|
+
|
|
362
|
+
file = Path(directory).joinpath(filename)
|
|
363
|
+
if file.exists():
|
|
364
|
+
_logger.debug(f"File {filename} found in {directory}")
|
|
365
|
+
return file
|
|
366
|
+
|
|
367
|
+
if rec:
|
|
368
|
+
for subdir in Path(directory).iterdir():
|
|
369
|
+
if subdir.is_dir():
|
|
370
|
+
file = _search_directory(subdir, filename, True)
|
|
371
|
+
if file:
|
|
372
|
+
return file
|
|
373
|
+
return None
|
|
374
|
+
|
|
375
|
+
|
|
628
376
|
def find_file(name, loc):
|
|
629
377
|
"""
|
|
630
378
|
Search for files inside of given directories, recursively, and return its full path.
|
|
@@ -633,7 +381,7 @@ def find_file(name, loc):
|
|
|
633
381
|
----------
|
|
634
382
|
name: str
|
|
635
383
|
File name to be searched for.
|
|
636
|
-
loc: Path
|
|
384
|
+
loc: Path or list of Path
|
|
637
385
|
Location of where to search for the file.
|
|
638
386
|
|
|
639
387
|
Returns
|
|
@@ -646,40 +394,19 @@ def find_file(name, loc):
|
|
|
646
394
|
FileNotFoundError
|
|
647
395
|
If the desired file is not found.
|
|
648
396
|
"""
|
|
649
|
-
|
|
650
|
-
all_locations = copy.copy(loc)
|
|
651
|
-
all_locations = [all_locations] if not isinstance(all_locations, list) else all_locations
|
|
652
|
-
|
|
653
|
-
def _search_directory(directory, filename, rec=False):
|
|
654
|
-
if not Path(directory).exists():
|
|
655
|
-
msg = f"Directory {directory} does not exist"
|
|
656
|
-
_logger.debug(msg)
|
|
657
|
-
return None
|
|
658
|
-
|
|
659
|
-
file = Path(directory).joinpath(filename)
|
|
660
|
-
if file.exists():
|
|
661
|
-
_logger.debug(f"File {filename} found in {directory}")
|
|
662
|
-
return file
|
|
663
|
-
if not rec: # Not recursively
|
|
664
|
-
return None
|
|
665
|
-
|
|
666
|
-
for subdir in Path(directory).iterdir():
|
|
667
|
-
if not subdir.is_dir():
|
|
668
|
-
continue
|
|
669
|
-
file = _search_directory(subdir, filename, True)
|
|
670
|
-
if file is not None:
|
|
671
|
-
return file
|
|
672
|
-
return None
|
|
397
|
+
all_locations = [loc] if not isinstance(loc, list) else loc
|
|
673
398
|
|
|
674
399
|
# Searching file locally
|
|
675
400
|
file = _search_directory(".", name)
|
|
676
|
-
if file
|
|
401
|
+
if file:
|
|
677
402
|
return file
|
|
403
|
+
|
|
678
404
|
# Searching file in given locations
|
|
679
|
-
for
|
|
680
|
-
file = _search_directory(
|
|
681
|
-
if file
|
|
405
|
+
for location in all_locations:
|
|
406
|
+
file = _search_directory(location, name, True)
|
|
407
|
+
if file:
|
|
682
408
|
return file
|
|
409
|
+
|
|
683
410
|
msg = f"File {name} could not be found in {all_locations}"
|
|
684
411
|
_logger.error(msg)
|
|
685
412
|
raise FileNotFoundError(msg)
|
|
@@ -701,7 +428,6 @@ def get_log_excerpt(log_file, n_last_lines=30):
|
|
|
701
428
|
str
|
|
702
429
|
Excerpt from log file with header/footer
|
|
703
430
|
"""
|
|
704
|
-
|
|
705
431
|
return (
|
|
706
432
|
"\n\nRuntime error - See below the relevant part of the log/err file.\n\n"
|
|
707
433
|
f"{log_file}\n"
|
|
@@ -712,9 +438,7 @@ def get_log_excerpt(log_file, n_last_lines=30):
|
|
|
712
438
|
|
|
713
439
|
|
|
714
440
|
def get_file_age(file_path):
|
|
715
|
-
"""
|
|
716
|
-
Get the age of a file in seconds since the last modification.
|
|
717
|
-
"""
|
|
441
|
+
"""Get the age of a file in seconds since the last modification."""
|
|
718
442
|
if not Path(file_path).is_file():
|
|
719
443
|
raise FileNotFoundError(f"'{file_path}' does not exist or is not a file.")
|
|
720
444
|
|
|
@@ -722,14 +446,44 @@ def get_file_age(file_path):
|
|
|
722
446
|
modification_time = file_stats.st_mtime
|
|
723
447
|
current_time = time.time()
|
|
724
448
|
|
|
725
|
-
|
|
726
|
-
|
|
449
|
+
return (current_time - modification_time) / 60
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def _process_dict_keys(input_dict, case_func):
|
|
453
|
+
"""
|
|
454
|
+
Process dictionary keys recursively.
|
|
455
|
+
|
|
456
|
+
Parameters
|
|
457
|
+
----------
|
|
458
|
+
input_dict: dict
|
|
459
|
+
Dictionary to be processed.
|
|
460
|
+
case_func: function
|
|
461
|
+
Function to change case of keys (e.g., str.lower, str.upper).
|
|
462
|
+
|
|
463
|
+
Returns
|
|
464
|
+
-------
|
|
465
|
+
dict
|
|
466
|
+
Processed dictionary with keys changed.
|
|
467
|
+
"""
|
|
468
|
+
output_dict = {}
|
|
469
|
+
for key, value in input_dict.items():
|
|
470
|
+
processed_key = case_func(key)
|
|
471
|
+
if isinstance(value, dict):
|
|
472
|
+
output_dict[processed_key] = _process_dict_keys(value, case_func)
|
|
473
|
+
elif isinstance(value, list):
|
|
474
|
+
processed_list = [
|
|
475
|
+
_process_dict_keys(item, case_func) if isinstance(item, dict) else item
|
|
476
|
+
for item in value
|
|
477
|
+
]
|
|
478
|
+
output_dict[processed_key] = processed_list
|
|
479
|
+
else:
|
|
480
|
+
output_dict[processed_key] = value
|
|
481
|
+
return output_dict
|
|
727
482
|
|
|
728
483
|
|
|
729
484
|
def change_dict_keys_case(data_dict, lower_case=True):
|
|
730
485
|
"""
|
|
731
|
-
Change keys of a dictionary to lower or upper case.
|
|
732
|
-
all keys. Takes into account list of dictionaries, as e.g. found in the top level data model.
|
|
486
|
+
Change keys of a dictionary to lower or upper case recursively.
|
|
733
487
|
|
|
734
488
|
Parameters
|
|
735
489
|
----------
|
|
@@ -737,37 +491,28 @@ def change_dict_keys_case(data_dict, lower_case=True):
|
|
|
737
491
|
Dictionary to be converted.
|
|
738
492
|
lower_case: bool
|
|
739
493
|
Change keys to lower (upper) case if True (False).
|
|
494
|
+
|
|
495
|
+
Returns
|
|
496
|
+
-------
|
|
497
|
+
dict
|
|
498
|
+
Dictionary with keys converted to lower or upper case.
|
|
740
499
|
"""
|
|
500
|
+
# Determine which case function to use
|
|
501
|
+
case_func = str.lower if lower_case else str.upper
|
|
741
502
|
|
|
742
|
-
_return_dict = {}
|
|
743
503
|
try:
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
_key_changed = key.lower()
|
|
747
|
-
else:
|
|
748
|
-
_key_changed = key.upper()
|
|
749
|
-
if isinstance(data_dict[key], dict):
|
|
750
|
-
_return_dict[_key_changed] = change_dict_keys_case(data_dict[key], lower_case)
|
|
751
|
-
elif isinstance(data_dict[key], list):
|
|
752
|
-
_tmp_list = []
|
|
753
|
-
for _list_entry in data_dict[key]:
|
|
754
|
-
if isinstance(_list_entry, dict):
|
|
755
|
-
_tmp_list.append(change_dict_keys_case(_list_entry, lower_case))
|
|
756
|
-
else:
|
|
757
|
-
_tmp_list.append(_list_entry)
|
|
758
|
-
_return_dict[_key_changed] = _tmp_list
|
|
759
|
-
else:
|
|
760
|
-
_return_dict[_key_changed] = data_dict[key]
|
|
761
|
-
except AttributeError:
|
|
504
|
+
return _process_dict_keys(data_dict, case_func)
|
|
505
|
+
except AttributeError as exc:
|
|
762
506
|
_logger.error(f"Input is not a proper dictionary: {data_dict}")
|
|
763
|
-
raise
|
|
764
|
-
return _return_dict
|
|
507
|
+
raise AttributeError from exc
|
|
765
508
|
|
|
766
509
|
|
|
767
510
|
def remove_substring_recursively_from_dict(data_dict, substring="\n"):
|
|
768
511
|
"""
|
|
769
|
-
Remove substrings from all strings in a dictionary.
|
|
770
|
-
|
|
512
|
+
Remove substrings from all strings in a dictionary.
|
|
513
|
+
|
|
514
|
+
Recursively crawls through the dictionary This e.g., allows to remove all newline characters
|
|
515
|
+
from a dictionary.
|
|
771
516
|
|
|
772
517
|
Parameters
|
|
773
518
|
----------
|
|
@@ -790,9 +535,11 @@ def remove_substring_recursively_from_dict(data_dict, substring="\n"):
|
|
|
790
535
|
item.replace(substring, "") if isinstance(item, str) else item for item in value
|
|
791
536
|
]
|
|
792
537
|
modified_items = [
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
538
|
+
(
|
|
539
|
+
remove_substring_recursively_from_dict(item, substring)
|
|
540
|
+
if isinstance(item, dict)
|
|
541
|
+
else item
|
|
542
|
+
)
|
|
796
543
|
for item in modified_items
|
|
797
544
|
]
|
|
798
545
|
data_dict[key] = modified_items
|
|
@@ -804,18 +551,18 @@ def remove_substring_recursively_from_dict(data_dict, substring="\n"):
|
|
|
804
551
|
|
|
805
552
|
|
|
806
553
|
def sort_arrays(*args):
|
|
807
|
-
"""Sort arrays
|
|
554
|
+
"""Sort arrays.
|
|
808
555
|
|
|
809
556
|
Parameters
|
|
810
557
|
----------
|
|
811
558
|
*args
|
|
812
559
|
Arguments to be sorted.
|
|
560
|
+
|
|
813
561
|
Returns
|
|
814
562
|
-------
|
|
815
563
|
list
|
|
816
564
|
Sorted args.
|
|
817
565
|
"""
|
|
818
|
-
|
|
819
566
|
if len(args) == 0:
|
|
820
567
|
return args
|
|
821
568
|
order_array = copy.copy(args[0])
|
|
@@ -826,59 +573,215 @@ def sort_arrays(*args):
|
|
|
826
573
|
return new_args
|
|
827
574
|
|
|
828
575
|
|
|
829
|
-
def
|
|
576
|
+
def user_confirm():
|
|
577
|
+
"""
|
|
578
|
+
Ask the user to enter y or n (case-insensitive) on the command line.
|
|
579
|
+
|
|
580
|
+
Returns
|
|
581
|
+
-------
|
|
582
|
+
bool:
|
|
583
|
+
True if the answer is Y/y.
|
|
584
|
+
|
|
585
|
+
"""
|
|
586
|
+
while True:
|
|
587
|
+
try:
|
|
588
|
+
answer = input("Is this OK? [y/n]").lower()
|
|
589
|
+
return answer == "y"
|
|
590
|
+
except EOFError:
|
|
591
|
+
break
|
|
592
|
+
return False
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def _get_value_dtype(value):
|
|
596
|
+
"""
|
|
597
|
+
Get the data type of the given value.
|
|
598
|
+
|
|
599
|
+
Parameters
|
|
600
|
+
----------
|
|
601
|
+
Value to determine the data type.
|
|
602
|
+
|
|
603
|
+
Returns
|
|
604
|
+
-------
|
|
605
|
+
type:
|
|
606
|
+
Data type of the value.
|
|
607
|
+
"""
|
|
608
|
+
if isinstance(value, (list | np.ndarray)):
|
|
609
|
+
value = np.array(value)
|
|
610
|
+
return value.dtype
|
|
611
|
+
|
|
612
|
+
return type(value)
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
def validate_data_type(reference_dtype, value=None, dtype=None, allow_subtypes=True):
|
|
616
|
+
"""
|
|
617
|
+
Validate data type of value or type object against a reference data type.
|
|
618
|
+
|
|
619
|
+
Allow to check for exact data type or allow subtypes (e.g. uint is accepted for int).
|
|
620
|
+
Take into account 'file' type as used in the model parameter database.
|
|
621
|
+
|
|
622
|
+
Parameters
|
|
623
|
+
----------
|
|
624
|
+
reference_dtype: str
|
|
625
|
+
Reference data type to be checked against.
|
|
626
|
+
value: any, optional
|
|
627
|
+
Value to be checked (if dtype is None).
|
|
628
|
+
dtype: type, optional
|
|
629
|
+
Type object to be checked (if value is None).
|
|
630
|
+
allow_subtypes: bool, optional
|
|
631
|
+
If True, allow subtypes to be accepted.
|
|
632
|
+
|
|
633
|
+
Returns
|
|
634
|
+
-------
|
|
635
|
+
bool:
|
|
636
|
+
True if the data type is valid.
|
|
830
637
|
"""
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
638
|
+
if value is None and dtype is None:
|
|
639
|
+
raise ValueError("Either value or dtype must be given.")
|
|
640
|
+
|
|
641
|
+
if value is not None and dtype is None:
|
|
642
|
+
dtype = _get_value_dtype(value)
|
|
643
|
+
|
|
644
|
+
# Strict comparison
|
|
645
|
+
if not allow_subtypes:
|
|
646
|
+
return np.issubdtype(dtype, reference_dtype)
|
|
647
|
+
|
|
648
|
+
# Allow any sub-type of integer or float for success
|
|
649
|
+
if (np.issubdtype(dtype, np.str_) or np.issubdtype(dtype, "object")) and reference_dtype in (
|
|
650
|
+
"string",
|
|
651
|
+
"str",
|
|
652
|
+
"file",
|
|
653
|
+
):
|
|
654
|
+
return True
|
|
655
|
+
|
|
656
|
+
if np.issubdtype(dtype, np.bool_) and reference_dtype in ("boolean", "bool"):
|
|
657
|
+
return True
|
|
658
|
+
|
|
659
|
+
if np.issubdtype(dtype, np.integer) and (
|
|
660
|
+
np.issubdtype(reference_dtype, np.integer) or np.issubdtype(reference_dtype, np.floating)
|
|
661
|
+
):
|
|
662
|
+
return True
|
|
663
|
+
|
|
664
|
+
if np.issubdtype(dtype, np.floating) and np.issubdtype(reference_dtype, np.floating):
|
|
665
|
+
return True
|
|
666
|
+
|
|
667
|
+
return False
|
|
668
|
+
|
|
669
|
+
|
|
670
|
+
def convert_list_to_string(data, comma_separated=False, shorten_list=False, collapse_list=False):
|
|
834
671
|
"""
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
672
|
+
Convert arrays to string (if required).
|
|
673
|
+
|
|
674
|
+
Parameters
|
|
675
|
+
----------
|
|
676
|
+
data: object
|
|
677
|
+
Object of data to convert (e.g., double or list)
|
|
678
|
+
comma_separated: bool
|
|
679
|
+
If True, returns elements as a comma-separated string (default is space-separated).
|
|
680
|
+
shorten_list: bool
|
|
681
|
+
If True and all elements in the list are identical, returns a summary string
|
|
682
|
+
like "all: value". This is useful to make the configuration files more readable.
|
|
683
|
+
collapse_list: bool
|
|
684
|
+
If True and all elements in the list are identical, returns a single value
|
|
685
|
+
instead of the entire list.
|
|
686
|
+
|
|
687
|
+
Returns
|
|
688
|
+
-------
|
|
689
|
+
object or str:
|
|
690
|
+
Converted data as string (if required)
|
|
691
|
+
|
|
692
|
+
"""
|
|
693
|
+
if data is None or not isinstance(data, list | np.ndarray):
|
|
694
|
+
return data
|
|
695
|
+
if shorten_list and len(data) > 10 and all(np.isclose(item, data[0]) for item in data):
|
|
696
|
+
return f"all: {data[0]}"
|
|
697
|
+
if collapse_list and len(sorted(set(data))) == 1:
|
|
698
|
+
data = [data[0]]
|
|
699
|
+
if comma_separated:
|
|
700
|
+
return ", ".join(str(item) for item in data)
|
|
701
|
+
return " ".join(str(item) for item in data)
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
def convert_string_to_list(data_string, is_float=True):
|
|
705
|
+
"""
|
|
706
|
+
Convert string (as used e.g. in sim_telarray) to list.
|
|
707
|
+
|
|
708
|
+
Allow coma or space separated strings.
|
|
840
709
|
|
|
841
|
-
|
|
842
|
-
|
|
710
|
+
Parameters
|
|
711
|
+
----------
|
|
712
|
+
data_string: object
|
|
713
|
+
String to be converted
|
|
843
714
|
|
|
715
|
+
Returns
|
|
716
|
+
-------
|
|
717
|
+
list, str
|
|
718
|
+
Converted data from string (if required).
|
|
719
|
+
Return data_string if conversion fails.
|
|
844
720
|
|
|
845
|
-
def get_value_unit_type(value):
|
|
846
721
|
"""
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
722
|
+
try:
|
|
723
|
+
if is_float:
|
|
724
|
+
return [float(v) for v in data_string.split()]
|
|
725
|
+
return [int(v) for v in data_string.split()]
|
|
726
|
+
except ValueError:
|
|
727
|
+
pass
|
|
728
|
+
if "," in data_string:
|
|
729
|
+
result = data_string.split(",")
|
|
730
|
+
return [item.strip() for item in result]
|
|
731
|
+
if " " in data_string:
|
|
732
|
+
return data_string.split()
|
|
733
|
+
return data_string
|
|
734
|
+
|
|
852
735
|
|
|
853
|
-
|
|
736
|
+
def _load_yaml_using_astropy(file):
|
|
737
|
+
"""
|
|
738
|
+
Load a yaml file using astropy's yaml loader.
|
|
854
739
|
|
|
855
740
|
Parameters
|
|
856
741
|
----------
|
|
857
|
-
|
|
858
|
-
|
|
742
|
+
file: file
|
|
743
|
+
File to be loaded.
|
|
859
744
|
|
|
860
745
|
Returns
|
|
861
746
|
-------
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
747
|
+
dict
|
|
748
|
+
Dictionary containing the file content.
|
|
749
|
+
"""
|
|
750
|
+
# pylint: disable=import-outside-toplevel
|
|
751
|
+
import astropy.io.misc.yaml as astropy_yaml
|
|
752
|
+
|
|
753
|
+
file.seek(0)
|
|
754
|
+
return astropy_yaml.load(file)
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def read_file_encoded_in_utf_or_latin(file_name):
|
|
865
758
|
"""
|
|
759
|
+
Read a file encoded in UTF-8 or Latin-1.
|
|
866
760
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
761
|
+
Parameters
|
|
762
|
+
----------
|
|
763
|
+
file_name: str
|
|
764
|
+
Name of the file to be read.
|
|
765
|
+
|
|
766
|
+
Returns
|
|
767
|
+
-------
|
|
768
|
+
list
|
|
769
|
+
List of lines read from the file.
|
|
770
|
+
|
|
771
|
+
Raises
|
|
772
|
+
------
|
|
773
|
+
UnicodeDecodeError
|
|
774
|
+
If the file cannot be decoded using UTF-8 or Latin-1.
|
|
775
|
+
"""
|
|
776
|
+
try:
|
|
777
|
+
with open(file_name, encoding="utf-8") as file:
|
|
778
|
+
lines = file.readlines()
|
|
779
|
+
except UnicodeDecodeError:
|
|
780
|
+
logging.debug("Unable to decode file using UTF-8. Trying Latin-1.")
|
|
871
781
|
try:
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
base_unit = _quantity_value.unit.to_string()
|
|
877
|
-
except TypeError:
|
|
878
|
-
base_value = value
|
|
879
|
-
base_type = "str"
|
|
880
|
-
else:
|
|
881
|
-
base_value = value
|
|
882
|
-
base_type = extract_type_of_value(base_value)
|
|
782
|
+
with open(file_name, encoding="latin-1") as file:
|
|
783
|
+
lines = file.readlines()
|
|
784
|
+
except UnicodeDecodeError as exc:
|
|
785
|
+
raise UnicodeDecodeError("Unable to decode file using UTF-8 or Latin-1.") from exc
|
|
883
786
|
|
|
884
|
-
return
|
|
787
|
+
return lines
|