rubin-sim 2.6.1.dev2__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.
- batch/compile_prenight_metadata_cache.sh +34 -0
- batch/run_auxtel_prenight_sims.sh +163 -0
- batch/run_prenight_sims.sh +239 -0
- docs/.gitignore +3 -0
- docs/Makefile +32 -0
- docs/api.rst +25 -0
- docs/conf.py +8 -0
- docs/data-api.rst +12 -0
- docs/data-download.rst +70 -0
- docs/developer.rst +14 -0
- docs/documenteer.toml +11 -0
- docs/index.rst +22 -0
- docs/installation.rst +76 -0
- docs/introduction.rst +37 -0
- docs/maf-api-batches.rst +12 -0
- docs/maf-api-db.rst +12 -0
- docs/maf-api-maf-contrib.rst +12 -0
- docs/maf-api-maps.rst +12 -0
- docs/maf-api-metricbundles.rst +12 -0
- docs/maf-api-metrics.rst +12 -0
- docs/maf-api-plots.rst +12 -0
- docs/maf-api-run-comparison.rst +12 -0
- docs/maf-api-slicers.rst +12 -0
- docs/maf-api-stackers.rst +12 -0
- docs/maf-api-utils.rst +12 -0
- docs/maf-api.rst +22 -0
- docs/maf.rst +60 -0
- docs/metric_list.py +49 -0
- docs/moving-objects-api.rst +12 -0
- docs/moving-objects.rst +22 -0
- docs/phot-utils-api.rst +12 -0
- docs/phot-utils.rst +11 -0
- docs/prenight.rst +117 -0
- docs/protoarchive.rst +276 -0
- docs/satellite-constellations-api.rst +12 -0
- docs/satellite-constellations.rst +13 -0
- docs/selfcal-api.rst +12 -0
- docs/selfcal.rst +15 -0
- docs/sim-archive-api.rst +12 -0
- docs/skybrightness-api.rst +12 -0
- docs/skybrightness.rst +23 -0
- docs/user-guide.rst +28 -0
- docs/vseqarchive-architecture.rst +438 -0
- docs/vseqarchive-setup.rst +238 -0
- docs/vseqarchive.rst +225 -0
- rubin_sim/__init__.py +7 -0
- rubin_sim/data/__init__.py +1 -0
- rubin_sim/data/rs_download_data.py +155 -0
- rubin_sim/maf/__init__.py +34 -0
- rubin_sim/maf/batches/__init__.py +18 -0
- rubin_sim/maf/batches/altaz_batch.py +161 -0
- rubin_sim/maf/batches/col_map_dict.py +274 -0
- rubin_sim/maf/batches/common.py +388 -0
- rubin_sim/maf/batches/ddf_batch.py +728 -0
- rubin_sim/maf/batches/filterchange_batch.py +182 -0
- rubin_sim/maf/batches/glance_batch.py +591 -0
- rubin_sim/maf/batches/hourglass_batch.py +79 -0
- rubin_sim/maf/batches/info_batch.py +114 -0
- rubin_sim/maf/batches/metadata_batch.py +558 -0
- rubin_sim/maf/batches/moving_objects_batch.py +1587 -0
- rubin_sim/maf/batches/openshutter_batch.py +100 -0
- rubin_sim/maf/batches/radar_limited.py +1100 -0
- rubin_sim/maf/batches/science_radar_batch.py +1910 -0
- rubin_sim/maf/batches/skycoverage.py +155 -0
- rubin_sim/maf/batches/slew_batch.py +146 -0
- rubin_sim/maf/batches/srd_batch.py +637 -0
- rubin_sim/maf/batches/time_batch.py +757 -0
- rubin_sim/maf/batches/time_sci_batch.py +105 -0
- rubin_sim/maf/batches/visitdepth_batch.py +485 -0
- rubin_sim/maf/db/__init__.py +2 -0
- rubin_sim/maf/db/add_run.py +34 -0
- rubin_sim/maf/db/results_db.py +1074 -0
- rubin_sim/maf/db/tracking_db.py +332 -0
- rubin_sim/maf/ddf_dir.py +53 -0
- rubin_sim/maf/generate_ss.py +218 -0
- rubin_sim/maf/glance_dir.py +58 -0
- rubin_sim/maf/maf_contrib/__init__.py +25 -0
- rubin_sim/maf/maf_contrib/calculate_lsst_field_visibility_astropy.py +170 -0
- rubin_sim/maf/maf_contrib/depth_limited_num_gal_metric.py +109 -0
- rubin_sim/maf/maf_contrib/example_new_metrics.py +40 -0
- rubin_sim/maf/maf_contrib/filter_pair_t_gaps_metric.py +208 -0
- rubin_sim/maf/maf_contrib/grb_transient_metric.py +249 -0
- rubin_sim/maf/maf_contrib/gw170817_det_metric.py +72 -0
- rubin_sim/maf/maf_contrib/intervals_between_obs_metric.py +68 -0
- rubin_sim/maf/maf_contrib/kne_metrics.py +473 -0
- rubin_sim/maf/maf_contrib/lss_metrics.py +45 -0
- rubin_sim/maf/maf_contrib/lss_obs_strategy/__init__.py +3 -0
- rubin_sim/maf/maf_contrib/lss_obs_strategy/constants_for_pipeline.py +73 -0
- rubin_sim/maf/maf_contrib/lss_obs_strategy/galaxy_counts_metric_extended.py +205 -0
- rubin_sim/maf/maf_contrib/lss_obs_strategy/galaxy_counts_with_pixel_calibration.py +171 -0
- rubin_sim/maf/maf_contrib/lv_dwarfs/__init__.py +1 -0
- rubin_sim/maf/maf_contrib/lv_dwarfs/lv_dwarfs_metrics.py +388 -0
- rubin_sim/maf/maf_contrib/microlensing_metric.py +593 -0
- rubin_sim/maf/maf_contrib/num_obs_in_survey_time_overlap_metric.py +43 -0
- rubin_sim/maf/maf_contrib/periodic_metric.py +51 -0
- rubin_sim/maf/maf_contrib/periodic_star_metric.py +186 -0
- rubin_sim/maf/maf_contrib/periodic_star_modulation_metric.py +236 -0
- rubin_sim/maf/maf_contrib/presto_color_kne_pop_metric.py +441 -0
- rubin_sim/maf/maf_contrib/selfcal_uniformity_metric.py +171 -0
- rubin_sim/maf/maf_contrib/star_count_mass_metric.py +64 -0
- rubin_sim/maf/maf_contrib/star_count_metric.py +56 -0
- rubin_sim/maf/maf_contrib/star_counts/__init__.py +8 -0
- rubin_sim/maf/maf_contrib/star_counts/abs_mag.py +324 -0
- rubin_sim/maf/maf_contrib/star_counts/coords.py +99 -0
- rubin_sim/maf/maf_contrib/star_counts/readme.txt +1 -0
- rubin_sim/maf/maf_contrib/star_counts/spec_type.py +91 -0
- rubin_sim/maf/maf_contrib/star_counts/starcount.py +30 -0
- rubin_sim/maf/maf_contrib/star_counts/starcount_bymass.py +93 -0
- rubin_sim/maf/maf_contrib/star_counts/stellardensity.py +59 -0
- rubin_sim/maf/maf_contrib/static_probes_fom_summary_metric.py +386 -0
- rubin_sim/maf/maf_contrib/tdes_pop_metric.py +362 -0
- rubin_sim/maf/maf_contrib/transient_ascii_sed_metric.py +652 -0
- rubin_sim/maf/maf_contrib/triplet_metric.py +156 -0
- rubin_sim/maf/maf_contrib/var_depth_metric.py +157 -0
- rubin_sim/maf/maf_contrib/xrb_metrics.py +523 -0
- rubin_sim/maf/maf_contrib/young_stellar_objects_metric.py +200 -0
- rubin_sim/maf/maf_night_report.py +200 -0
- rubin_sim/maf/make_fbs_tracking_db.py +90 -0
- rubin_sim/maf/maps/__init__.py +8 -0
- rubin_sim/maf/maps/base_map.py +72 -0
- rubin_sim/maf/maps/create_gaia_density_map.py +158 -0
- rubin_sim/maf/maps/dust_map.py +66 -0
- rubin_sim/maf/maps/dust_map_3d.py +190 -0
- rubin_sim/maf/maps/ebv_3d_hp.py +170 -0
- rubin_sim/maf/maps/ebv_hp.py +64 -0
- rubin_sim/maf/maps/gal_coords_map.py +22 -0
- rubin_sim/maf/maps/galactic_plane_priority_maps.py +210 -0
- rubin_sim/maf/maps/stellar_density_map.py +73 -0
- rubin_sim/maf/maps/trilegal_map.py +82 -0
- rubin_sim/maf/metadata_dir.py +90 -0
- rubin_sim/maf/metric_bundles/__init__.py +3 -0
- rubin_sim/maf/metric_bundles/metric_bundle.py +816 -0
- rubin_sim/maf/metric_bundles/metric_bundle_group.py +834 -0
- rubin_sim/maf/metric_bundles/mo_metric_bundle.py +646 -0
- rubin_sim/maf/metrics/__init__.py +52 -0
- rubin_sim/maf/metrics/agn_time_lag_metric.py +118 -0
- rubin_sim/maf/metrics/agnstructure.py +141 -0
- rubin_sim/maf/metrics/area_summary_metrics.py +120 -0
- rubin_sim/maf/metrics/base_metric.py +215 -0
- rubin_sim/maf/metrics/brown_dwarf_metric.py +162 -0
- rubin_sim/maf/metrics/cadence_metrics.py +400 -0
- rubin_sim/maf/metrics/calibration_metrics.py +618 -0
- rubin_sim/maf/metrics/chip_vendor_metric.py +43 -0
- rubin_sim/maf/metrics/color_slope_metrics.py +189 -0
- rubin_sim/maf/metrics/cosmology_summary_metrics.py +1212 -0
- rubin_sim/maf/metrics/coverage_metric.py +48 -0
- rubin_sim/maf/metrics/crowding_metric.py +255 -0
- rubin_sim/maf/metrics/cumulative_metric.py +46 -0
- rubin_sim/maf/metrics/dcr_metric.py +93 -0
- rubin_sim/maf/metrics/exgal_m5.py +57 -0
- rubin_sim/maf/metrics/fft_metric.py +27 -0
- rubin_sim/maf/metrics/galactic_plane_metrics.py +375 -0
- rubin_sim/maf/metrics/galplane_time_sampling_metrics.py +236 -0
- rubin_sim/maf/metrics/hourglass_metric.py +130 -0
- rubin_sim/maf/metrics/incremental_template_metric.py +255 -0
- rubin_sim/maf/metrics/kuiper_metrics.py +26 -0
- rubin_sim/maf/metrics/mo_metrics.py +1496 -0
- rubin_sim/maf/metrics/mo_summary_metrics.py +464 -0
- rubin_sim/maf/metrics/night_pointing_metric.py +70 -0
- rubin_sim/maf/metrics/optimal_m5_metric.py +94 -0
- rubin_sim/maf/metrics/pair_metric.py +69 -0
- rubin_sim/maf/metrics/periodic_detect_metric.py +133 -0
- rubin_sim/maf/metrics/phase_gap_metric.py +172 -0
- rubin_sim/maf/metrics/qso_number_counts_metric.py +138 -0
- rubin_sim/maf/metrics/scaling_metrics.py +148 -0
- rubin_sim/maf/metrics/schedview_metrics.py +39 -0
- rubin_sim/maf/metrics/season_metrics.py +344 -0
- rubin_sim/maf/metrics/simple_metrics.py +546 -0
- rubin_sim/maf/metrics/sky_sat_metric.py +18 -0
- rubin_sim/maf/metrics/sn_cadence_metric.py +104 -0
- rubin_sim/maf/metrics/sn_n_sn_metric.py +1364 -0
- rubin_sim/maf/metrics/sn_sl_metric.py +216 -0
- rubin_sim/maf/metrics/sn_snr_metric.py +715 -0
- rubin_sim/maf/metrics/snr_weight.py +24 -0
- rubin_sim/maf/metrics/star_density.py +77 -0
- rubin_sim/maf/metrics/string_count_metric.py +95 -0
- rubin_sim/maf/metrics/summary_metrics.py +201 -0
- rubin_sim/maf/metrics/surfb_metric.py +175 -0
- rubin_sim/maf/metrics/technical_metrics.py +271 -0
- rubin_sim/maf/metrics/tgaps.py +412 -0
- rubin_sim/maf/metrics/tomography_models.py +2034 -0
- rubin_sim/maf/metrics/transient_metrics.py +268 -0
- rubin_sim/maf/metrics/uniformity_metrics.py +418 -0
- rubin_sim/maf/metrics/use_metrics.py +55 -0
- rubin_sim/maf/metrics/vector_metrics.py +190 -0
- rubin_sim/maf/metrics/visit_groups_metric.py +341 -0
- rubin_sim/maf/metrics/weak_lensing_systematics_metric.py +210 -0
- rubin_sim/maf/plots/__init__.py +14 -0
- rubin_sim/maf/plots/hg_plotters.py +1117 -0
- rubin_sim/maf/plots/hourglass_plotters.py +120 -0
- rubin_sim/maf/plots/mo_plotters.py +358 -0
- rubin_sim/maf/plots/nd_plotters.py +220 -0
- rubin_sim/maf/plots/neo_distance_plotter.py +140 -0
- rubin_sim/maf/plots/night_pointing_plotter.py +96 -0
- rubin_sim/maf/plots/oned_plotters.py +114 -0
- rubin_sim/maf/plots/perceptual_rainbow.py +30 -0
- rubin_sim/maf/plots/plot_handler.py +766 -0
- rubin_sim/maf/plots/skyproj_plotters.py +540 -0
- rubin_sim/maf/plots/spatial_plotters.py +1029 -0
- rubin_sim/maf/plots/special_plotters.py +256 -0
- rubin_sim/maf/plots/two_d_plotters.py +177 -0
- rubin_sim/maf/plots/xyplotter.py +37 -0
- rubin_sim/maf/run_comparison/__init__.py +5 -0
- rubin_sim/maf/run_comparison/archive.py +584 -0
- rubin_sim/maf/run_comparison/gather_summaries.py +125 -0
- rubin_sim/maf/run_comparison/microlensing_compare.py +514 -0
- rubin_sim/maf/run_comparison/radar_plot.py +202 -0
- rubin_sim/maf/run_comparison/summary_plots.py +710 -0
- rubin_sim/maf/run_moving_calc.py +239 -0
- rubin_sim/maf/run_moving_fractions.py +128 -0
- rubin_sim/maf/run_moving_join.py +103 -0
- rubin_sim/maf/run_selfcal_metric.py +92 -0
- rubin_sim/maf/scimaf_dir.py +87 -0
- rubin_sim/maf/show_maf.py +227 -0
- rubin_sim/maf/slicers/__init__.py +13 -0
- rubin_sim/maf/slicers/base_slicer.py +513 -0
- rubin_sim/maf/slicers/base_spatial_slicer.py +202 -0
- rubin_sim/maf/slicers/healpix_sdss_slicer.py +118 -0
- rubin_sim/maf/slicers/healpix_slicer.py +189 -0
- rubin_sim/maf/slicers/healpix_subset_slicer.py +293 -0
- rubin_sim/maf/slicers/hourglass_slicer.py +37 -0
- rubin_sim/maf/slicers/mo_slicer.py +171 -0
- rubin_sim/maf/slicers/movie_slicer.py +288 -0
- rubin_sim/maf/slicers/nd_slicer.py +147 -0
- rubin_sim/maf/slicers/one_d_slicer.py +210 -0
- rubin_sim/maf/slicers/time_interval_slicers.py +283 -0
- rubin_sim/maf/slicers/uni_slicer.py +57 -0
- rubin_sim/maf/slicers/user_points_slicer.py +141 -0
- rubin_sim/maf/stackers/__init__.py +13 -0
- rubin_sim/maf/stackers/base_stacker.py +221 -0
- rubin_sim/maf/stackers/coord_stackers.py +224 -0
- rubin_sim/maf/stackers/date_stackers.py +192 -0
- rubin_sim/maf/stackers/general_stackers.py +683 -0
- rubin_sim/maf/stackers/get_col_info.py +104 -0
- rubin_sim/maf/stackers/label_stackers.py +114 -0
- rubin_sim/maf/stackers/m5_optimal_stacker.py +126 -0
- rubin_sim/maf/stackers/mo_phase.py +265 -0
- rubin_sim/maf/stackers/mo_stackers.py +486 -0
- rubin_sim/maf/stackers/n_follow_stacker.py +187 -0
- rubin_sim/maf/stackers/neo_dist_stacker.py +126 -0
- rubin_sim/maf/stackers/sdss_stackers.py +30 -0
- rubin_sim/maf/stackers/sn_stacker.py +152 -0
- rubin_sim/maf/stackers/teff_stacker.py +154 -0
- rubin_sim/maf/utils/__init__.py +6 -0
- rubin_sim/maf/utils/astrometry_utils.py +93 -0
- rubin_sim/maf/utils/generate_fov_map.py +58 -0
- rubin_sim/maf/utils/get_date_version.py +28 -0
- rubin_sim/maf/utils/maf_utils.py +212 -0
- rubin_sim/maf/utils/opsim_utils.py +259 -0
- rubin_sim/maf/utils/output_utils.py +34 -0
- rubin_sim/maf/utils/sn_n_sn_utils.py +894 -0
- rubin_sim/maf/utils/sn_utils.py +369 -0
- rubin_sim/maf/utils/stellar_mags.py +197 -0
- rubin_sim/maf/web/__init__.py +2 -0
- rubin_sim/maf/web/favicon.ico +0 -0
- rubin_sim/maf/web/maf_run_results.py +645 -0
- rubin_sim/maf/web/maf_tracking.py +139 -0
- rubin_sim/maf/web/sorttable.js +496 -0
- rubin_sim/maf/web/templates/allmetricresults.html +72 -0
- rubin_sim/maf/web/templates/configs.html +28 -0
- rubin_sim/maf/web/templates/macros.html +243 -0
- rubin_sim/maf/web/templates/master.html +294 -0
- rubin_sim/maf/web/templates/metricselect.html +91 -0
- rubin_sim/maf/web/templates/multicolor.html +147 -0
- rubin_sim/maf/web/templates/results.html +82 -0
- rubin_sim/maf/web/templates/runselect.html +58 -0
- rubin_sim/maf/web/templates/stats.html +50 -0
- rubin_sim/moving_objects/__init__.py +8 -0
- rubin_sim/moving_objects/base_obs.py +468 -0
- rubin_sim/moving_objects/cheby_fits.py +689 -0
- rubin_sim/moving_objects/cheby_values.py +231 -0
- rubin_sim/moving_objects/chebyshev_utils.py +365 -0
- rubin_sim/moving_objects/direct_obs.py +376 -0
- rubin_sim/moving_objects/make_lsst_obs.py +282 -0
- rubin_sim/moving_objects/ooephemerides.py +643 -0
- rubin_sim/moving_objects/orbits.py +507 -0
- rubin_sim/moving_objects/pre_generate.py +37 -0
- rubin_sim/moving_objects/utils.py +61 -0
- rubin_sim/phot_utils/__init__.py +7 -0
- rubin_sim/phot_utils/bandpass.py +425 -0
- rubin_sim/phot_utils/photometric_parameters.py +449 -0
- rubin_sim/phot_utils/physical_parameters.py +50 -0
- rubin_sim/phot_utils/predicted_zeropoints.py +245 -0
- rubin_sim/phot_utils/sed.py +1773 -0
- rubin_sim/phot_utils/sed_utils.py +52 -0
- rubin_sim/phot_utils/signaltonoise.py +695 -0
- rubin_sim/phot_utils/spectral_resampling.py +161 -0
- rubin_sim/satellite_constellations/__init__.py +3 -0
- rubin_sim/satellite_constellations/basis_function.py +40 -0
- rubin_sim/satellite_constellations/model_observatory.py +157 -0
- rubin_sim/satellite_constellations/sat_utils.py +488 -0
- rubin_sim/selfcal/__init__.py +4 -0
- rubin_sim/selfcal/generate_catalog.py +188 -0
- rubin_sim/selfcal/offsets.py +119 -0
- rubin_sim/selfcal/solver.py +143 -0
- rubin_sim/selfcal/star_tools.py +42 -0
- rubin_sim/sim_archive/__init__.py +14 -0
- rubin_sim/sim_archive/conda_packages.py +65 -0
- rubin_sim/sim_archive/future_vsarch.py +480 -0
- rubin_sim/sim_archive/make_snapshot.py +255 -0
- rubin_sim/sim_archive/prenight.py +216 -0
- rubin_sim/sim_archive/prenightindex.py +229 -0
- rubin_sim/sim_archive/prototype.py +1029 -0
- rubin_sim/sim_archive/sim_archive.py +680 -0
- rubin_sim/sim_archive/tempdb.py +56 -0
- rubin_sim/sim_archive/util.py +146 -0
- rubin_sim/sim_archive/vseqarchive.py +965 -0
- rubin_sim/sim_archive/vseqmetadata.py +1721 -0
- rubin_sim/skybrightness/__init__.py +5 -0
- rubin_sim/skybrightness/allsky_db.py +93 -0
- rubin_sim/skybrightness/data/ESO_Spectra/eso_tools.py +505 -0
- rubin_sim/skybrightness/data/solarSpec/package.py +22 -0
- rubin_sim/skybrightness/generate_hdf5.py +243 -0
- rubin_sim/skybrightness/interp_components.py +798 -0
- rubin_sim/skybrightness/sky_model.py +771 -0
- rubin_sim/skybrightness/twilight_func.py +83 -0
- rubin_sim/skybrightness/utils.py +91 -0
- rubin_sim-2.6.1.dev2.dist-info/METADATA +95 -0
- rubin_sim-2.6.1.dev2.dist-info/RECORD +387 -0
- rubin_sim-2.6.1.dev2.dist-info/WHEEL +5 -0
- rubin_sim-2.6.1.dev2.dist-info/entry_points.txt +24 -0
- rubin_sim-2.6.1.dev2.dist-info/licenses/LICENSE +674 -0
- rubin_sim-2.6.1.dev2.dist-info/top_level.txt +5 -0
- sql/make_vseqmeta.sql +251 -0
- tests/data/test_data.py +26 -0
- tests/data/test_ddf_grid.py +17 -0
- tests/maf/test_3x2fom.py +68 -0
- tests/maf/test_archive.py +162 -0
- tests/maf/test_basemetrics.py +99 -0
- tests/maf/test_batchcommon.py +36 -0
- tests/maf/test_batches.py +159 -0
- tests/maf/test_cadencemetrics.py +388 -0
- tests/maf/test_calibrationmetrics.py +242 -0
- tests/maf/test_color_slopes.py +71 -0
- tests/maf/test_cosmology_summaries.py +20 -0
- tests/maf/test_gathersummaries.py +48 -0
- tests/maf/test_healpixslicer.py +439 -0
- tests/maf/test_healpixsubsetslicer.py +273 -0
- tests/maf/test_hgplotters.py +145 -0
- tests/maf/test_hourglassmetric.py +33 -0
- tests/maf/test_io.py +187 -0
- tests/maf/test_json.py +169 -0
- tests/maf/test_maps.py +199 -0
- tests/maf/test_metricbundle.py +69 -0
- tests/maf/test_mometrics.py +422 -0
- tests/maf/test_movieslicer.py +216 -0
- tests/maf/test_ndslicer.py +240 -0
- tests/maf/test_neodistanceplotter.py +41 -0
- tests/maf/test_onedslicer.py +245 -0
- tests/maf/test_opsimutils.py +80 -0
- tests/maf/test_plotters.py +134 -0
- tests/maf/test_resultsdb.py +145 -0
- tests/maf/test_schedviewmetrics.py +14 -0
- tests/maf/test_simplemetrics.py +200 -0
- tests/maf/test_snmetrics.py +335 -0
- tests/maf/test_snmetrics_nsn.py +123 -0
- tests/maf/test_stackers.py +423 -0
- tests/maf/test_stellarmags.py +50 -0
- tests/maf/test_stringcount.py +26 -0
- tests/maf/test_summary_plots.py +197 -0
- tests/maf/test_summarymetrics.py +82 -0
- tests/maf/test_technicalmetrics.py +267 -0
- tests/maf/test_templatemetrics.py +86 -0
- tests/maf/test_timeintervalslicers.py +97 -0
- tests/maf/test_trackingdb.py +88 -0
- tests/maf/test_unislicer.py +116 -0
- tests/maf/test_vectormetrics.py +225 -0
- tests/maf/test_visitgroupsmetric.py +181 -0
- tests/moving_objects/test_camera.py +39 -0
- tests/moving_objects/test_chebyfits.py +188 -0
- tests/moving_objects/test_chebyshevutils.py +76 -0
- tests/moving_objects/test_chebyvalues.py +245 -0
- tests/moving_objects/test_ephemerides.py +219 -0
- tests/moving_objects/test_orbits.py +170 -0
- tests/moving_objects/test_sed_data.py +61 -0
- tests/phot_utils/test_approximatebandpasses.py +91 -0
- tests/phot_utils/test_photometricparameters.py +268 -0
- tests/phot_utils/test_photometry.py +61 -0
- tests/phot_utils/test_predicted_zeropoints.py +53 -0
- tests/phot_utils/test_read_bandpasses.py +29 -0
- tests/phot_utils/test_sed.py +368 -0
- tests/phot_utils/test_snr.py +370 -0
- tests/satellite_constellations/test_satellites.py +36 -0
- tests/sim_archive/test_make_snapshot.py +24 -0
- tests/sim_archive/test_prototype_sim_archive.py +204 -0
- tests/sim_archive/test_vseqarchive.py +947 -0
- tests/skybrightness/test_skymodel.py +359 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# cumulative one dimensional movie slicer
|
|
2
|
+
__all__ = ("MovieSlicer",)
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import subprocess
|
|
6
|
+
import warnings
|
|
7
|
+
from functools import wraps
|
|
8
|
+
from subprocess import CalledProcessError
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from rubin_sim.maf.stackers import ColInfo
|
|
13
|
+
from rubin_sim.maf.utils import optimal_bins
|
|
14
|
+
|
|
15
|
+
from .base_slicer import BaseSlicer
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MovieSlicer(BaseSlicer):
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
slice_col_name=None,
|
|
22
|
+
slice_col_units=None,
|
|
23
|
+
bins=None,
|
|
24
|
+
bin_min=None,
|
|
25
|
+
bin_max=None,
|
|
26
|
+
bin_size=None,
|
|
27
|
+
verbose=True,
|
|
28
|
+
badval=0,
|
|
29
|
+
cumulative=True,
|
|
30
|
+
force_no_ffmpeg=False,
|
|
31
|
+
):
|
|
32
|
+
"""The 'movieSlicer' acts similarly to the OneDSlicer
|
|
33
|
+
(slices on one data column).
|
|
34
|
+
However, the data slices from the movieSlicer are intended to be
|
|
35
|
+
fed to another slicer, which then
|
|
36
|
+
(together with a set of Metrics) calculates metric values +
|
|
37
|
+
plots at each slice created by the movieSlicer.
|
|
38
|
+
The job of the movieSlicer is to track those slices and
|
|
39
|
+
put them together into a movie.
|
|
40
|
+
|
|
41
|
+
'slice_col_name' is the name of the data column to use for slicing.
|
|
42
|
+
'slice_col_units' lets the user set the units (for plotting purposes)
|
|
43
|
+
of the slice column.
|
|
44
|
+
'bins' can be a numpy array with the binpoints for sliceCol
|
|
45
|
+
or a single integer value
|
|
46
|
+
(if a single value, this will be used as the number of bins,
|
|
47
|
+
together with data min/max or bin_min/max),
|
|
48
|
+
as in numpy's histogram function.
|
|
49
|
+
If 'bin_size' is used, this will override the bins value and
|
|
50
|
+
will be used together with the data min/max
|
|
51
|
+
or bin_min/Max to set the binpoint values.
|
|
52
|
+
|
|
53
|
+
Bins work like numpy histogram bins: the last 'bin' value is end
|
|
54
|
+
value of last bin; all bins except for last bin are half-open
|
|
55
|
+
([a, b>), the last one is ([a, b]).
|
|
56
|
+
|
|
57
|
+
The movieSlicer stitches individual frames together into a movie
|
|
58
|
+
using ffmpeg. Thus, on instantiation it checks that ffmpeg is
|
|
59
|
+
available and will raise and exception if not.
|
|
60
|
+
This behavior can be overriden using force_no_ffmpeg = True
|
|
61
|
+
(in order to create a movie later perhaps).
|
|
62
|
+
"""
|
|
63
|
+
# Check for ffmpeg.
|
|
64
|
+
if not force_no_ffmpeg:
|
|
65
|
+
try:
|
|
66
|
+
fnull = open(os.devnull, "w")
|
|
67
|
+
subprocess.check_call(["which", "ffmpeg"], stdout=fnull)
|
|
68
|
+
except CalledProcessError:
|
|
69
|
+
raise Exception(
|
|
70
|
+
"Could not find ffmpeg on the system, so will not be able to create movie."
|
|
71
|
+
" Use force_no_ffmpeg=True to override this error and create individual images."
|
|
72
|
+
)
|
|
73
|
+
super().__init__(verbose=verbose, badval=badval)
|
|
74
|
+
self.slice_col_name = slice_col_name
|
|
75
|
+
self.columns_needed = [slice_col_name]
|
|
76
|
+
self.bins = bins
|
|
77
|
+
self.bin_min = bin_min
|
|
78
|
+
self.bin_max = bin_max
|
|
79
|
+
self.bin_size = bin_size
|
|
80
|
+
self.cumulative = cumulative
|
|
81
|
+
if slice_col_units is None:
|
|
82
|
+
co = ColInfo()
|
|
83
|
+
self.slice_col_units = co.get_units(self.slice_col_name)
|
|
84
|
+
self.slicer_init = {
|
|
85
|
+
"slice_col_name": self.slice_col_name,
|
|
86
|
+
"slice_col_units": slice_col_units,
|
|
87
|
+
"badval": badval,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
def setup_slicer(self, sim_data, maps=None):
|
|
91
|
+
"""
|
|
92
|
+
Set up bins in slicer.
|
|
93
|
+
"""
|
|
94
|
+
if self.slice_col_name is None:
|
|
95
|
+
raise Exception("slice_col_name was not defined when slicer instantiated.")
|
|
96
|
+
slice_col = sim_data[self.slice_col_name]
|
|
97
|
+
# Set bin min/max values.
|
|
98
|
+
if self.bin_min is None:
|
|
99
|
+
self.bin_min = slice_col.min()
|
|
100
|
+
if self.bin_max is None:
|
|
101
|
+
self.bin_max = slice_col.max()
|
|
102
|
+
# Give warning if bin_min = bin_max, and do something at
|
|
103
|
+
# least slightly reasonable.
|
|
104
|
+
if self.bin_min == self.bin_max:
|
|
105
|
+
warnings.warn(
|
|
106
|
+
"bin_min = bin_max (maybe your data is single-valued?). "
|
|
107
|
+
"Increasing bin_max by 1 (or 2*bin_size, if bin_size set)."
|
|
108
|
+
)
|
|
109
|
+
if self.bin_size is not None:
|
|
110
|
+
self.bin_max = self.bin_max + 2 * self.bin_size
|
|
111
|
+
else:
|
|
112
|
+
self.bin_max = self.bin_max + 1
|
|
113
|
+
# Set bins.
|
|
114
|
+
# Using bin_size.
|
|
115
|
+
if self.bin_size is not None:
|
|
116
|
+
if self.bins is not None:
|
|
117
|
+
warnings.warn(
|
|
118
|
+
"Both bin_size and bins have been set; Using bin_size %f only." % (self.bin_size)
|
|
119
|
+
)
|
|
120
|
+
self.bins = np.arange(
|
|
121
|
+
self.bin_min,
|
|
122
|
+
self.bin_max + self.bin_size / 2.0,
|
|
123
|
+
float(self.bin_size),
|
|
124
|
+
"float",
|
|
125
|
+
)
|
|
126
|
+
# Using bins value.
|
|
127
|
+
else:
|
|
128
|
+
# Bins was a sequence (np array or list)
|
|
129
|
+
if hasattr(self.bins, "__iter__"):
|
|
130
|
+
self.bins = np.sort(self.bins)
|
|
131
|
+
# Or bins was a single value.
|
|
132
|
+
else:
|
|
133
|
+
if self.bins is None:
|
|
134
|
+
self.bins = optimal_bins(slice_col, self.bin_min, self.bin_max)
|
|
135
|
+
nbins = int(self.bins)
|
|
136
|
+
self.bin_size = (self.bin_max - self.bin_min) / float(nbins)
|
|
137
|
+
self.bins = np.arange(
|
|
138
|
+
self.bin_min,
|
|
139
|
+
self.bin_max + self.bin_size / 2.0,
|
|
140
|
+
self.bin_size,
|
|
141
|
+
"float",
|
|
142
|
+
)
|
|
143
|
+
# Set shape / nbins to be one less than # of bins because
|
|
144
|
+
# last binvalue is RH edge only
|
|
145
|
+
self.nslice = len(self.bins) - 1
|
|
146
|
+
self.shape = self.nslice
|
|
147
|
+
# Set slice_point metadata.
|
|
148
|
+
self.slice_points["sid"] = np.arange(self.nslice)
|
|
149
|
+
self.slice_points["bins"] = self.bins
|
|
150
|
+
# Add metadata from maps.
|
|
151
|
+
self._run_maps(maps)
|
|
152
|
+
# Set up data slicing.
|
|
153
|
+
self.sim_idxs = np.argsort(sim_data[self.slice_col_name])
|
|
154
|
+
simFieldsSorted = np.sort(sim_data[self.slice_col_name])
|
|
155
|
+
# "left" values are location where simdata == bin value
|
|
156
|
+
# Note that this setup will place any the visits beyond
|
|
157
|
+
# the last bin into the last bin.
|
|
158
|
+
self.left = np.searchsorted(simFieldsSorted, self.bins[:-1], "left")
|
|
159
|
+
self.left = np.concatenate(
|
|
160
|
+
(
|
|
161
|
+
self.left,
|
|
162
|
+
np.array(
|
|
163
|
+
[
|
|
164
|
+
len(self.sim_idxs),
|
|
165
|
+
]
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
)
|
|
169
|
+
# Set up _slice_sim_data method for this class.
|
|
170
|
+
if self.cumulative:
|
|
171
|
+
|
|
172
|
+
@wraps(self._slice_sim_data)
|
|
173
|
+
def _slice_sim_data(islice):
|
|
174
|
+
"""
|
|
175
|
+
Slice sim_data on oneD sliceCol, to
|
|
176
|
+
return relevant indexes for slice_point.
|
|
177
|
+
"""
|
|
178
|
+
# this is the important part.
|
|
179
|
+
# The ids here define the pieces of data that get
|
|
180
|
+
# passed on to subsequent slicers
|
|
181
|
+
# cumulative version of 1D slicing
|
|
182
|
+
idxs = self.sim_idxs[0 : self.left[islice + 1]]
|
|
183
|
+
return {
|
|
184
|
+
"idxs": idxs,
|
|
185
|
+
"slice_point": {
|
|
186
|
+
"sid": islice,
|
|
187
|
+
"bin_left": self.bins[0],
|
|
188
|
+
"bin_right": self.bins[islice + 1],
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
setattr(self, "_slice_sim_data", _slice_sim_data)
|
|
193
|
+
else:
|
|
194
|
+
|
|
195
|
+
@wraps(self._slice_sim_data)
|
|
196
|
+
def _slice_sim_data(islice):
|
|
197
|
+
"""
|
|
198
|
+
Slice sim_data on oneD sliceCol,
|
|
199
|
+
to return relevant indexes for slice_point.
|
|
200
|
+
"""
|
|
201
|
+
idxs = self.sim_idxs[self.left[islice] : self.left[islice + 1]]
|
|
202
|
+
return {
|
|
203
|
+
"idxs": idxs,
|
|
204
|
+
"slice_point": {
|
|
205
|
+
"sid": islice,
|
|
206
|
+
"bin_left": self.bins[islice],
|
|
207
|
+
"bin_right": self.bins[islice + 1],
|
|
208
|
+
},
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
setattr(self, "_slice_sim_data", _slice_sim_data)
|
|
212
|
+
|
|
213
|
+
def __eq__(self, other_slicer):
|
|
214
|
+
"""
|
|
215
|
+
Evaluate if slicers are equivalent.
|
|
216
|
+
"""
|
|
217
|
+
if isinstance(other_slicer, MovieSlicer):
|
|
218
|
+
return np.array_equal(other_slicer.slice_points["bins"], self.slice_points["bins"])
|
|
219
|
+
else:
|
|
220
|
+
return False
|
|
221
|
+
|
|
222
|
+
def make_movie(
|
|
223
|
+
self,
|
|
224
|
+
outfileroot,
|
|
225
|
+
sliceformat,
|
|
226
|
+
plot_type,
|
|
227
|
+
fig_format,
|
|
228
|
+
out_dir="Output",
|
|
229
|
+
ips=10.0,
|
|
230
|
+
fps=10.0,
|
|
231
|
+
):
|
|
232
|
+
"""Takes in metric and slicer metadata and calls
|
|
233
|
+
ffmpeg to stitch together output files.
|
|
234
|
+
"""
|
|
235
|
+
if not os.path.isdir(out_dir):
|
|
236
|
+
raise Exception("Cannot find output directory %s with movie input files." % (out_dir))
|
|
237
|
+
# make video
|
|
238
|
+
# ffmpeg -r 30 -i [image names - FilterColors_%03d_SkyMap.png] -r 30
|
|
239
|
+
# -pix_fmt yuv420p -crf 18 -preset slower outfile
|
|
240
|
+
callList = [
|
|
241
|
+
"ffmpeg",
|
|
242
|
+
"-r",
|
|
243
|
+
str(ips),
|
|
244
|
+
"-i",
|
|
245
|
+
os.path.join(
|
|
246
|
+
out_dir,
|
|
247
|
+
"%s_%s_%s.%s" % (outfileroot, sliceformat, plot_type, fig_format),
|
|
248
|
+
),
|
|
249
|
+
"-vf",
|
|
250
|
+
"pad=ceil(iw/2)*2:ceil(ih/2)*2",
|
|
251
|
+
"-r",
|
|
252
|
+
str(fps),
|
|
253
|
+
"-pix_fmt",
|
|
254
|
+
"yuv420p",
|
|
255
|
+
"-crf",
|
|
256
|
+
"18",
|
|
257
|
+
"-preset",
|
|
258
|
+
"slower",
|
|
259
|
+
os.path.join(
|
|
260
|
+
out_dir,
|
|
261
|
+
"%s_%s_%s_%s.mp4" % (outfileroot, plot_type, str(ips), str(fps)),
|
|
262
|
+
),
|
|
263
|
+
]
|
|
264
|
+
print("Attempting to call ffmpeg with:")
|
|
265
|
+
print(" ".join(callList))
|
|
266
|
+
subprocess.check_call(callList)
|
|
267
|
+
# make thumbnail gif
|
|
268
|
+
callList = [
|
|
269
|
+
"ffmpeg",
|
|
270
|
+
"-i",
|
|
271
|
+
os.path.join(
|
|
272
|
+
out_dir,
|
|
273
|
+
"%s_%s_%s_%s.mp4" % (outfileroot, plot_type, str(ips), str(fps)),
|
|
274
|
+
),
|
|
275
|
+
"-vf",
|
|
276
|
+
"pad=ceil(iw/2)*2:ceil(ih/2)*2",
|
|
277
|
+
"-t",
|
|
278
|
+
str(10),
|
|
279
|
+
"-r",
|
|
280
|
+
str(10),
|
|
281
|
+
os.path.join(
|
|
282
|
+
out_dir,
|
|
283
|
+
"%s_%s_%s_%s.gif" % (outfileroot, plot_type, str(ips), str(fps)),
|
|
284
|
+
),
|
|
285
|
+
]
|
|
286
|
+
print("converting to animated gif with:")
|
|
287
|
+
print(" ".join(callList))
|
|
288
|
+
subprocess.check_call(callList)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
__all__ = ("NDSlicer",)
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
4
|
+
import warnings
|
|
5
|
+
from functools import wraps
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
|
|
9
|
+
from rubin_sim.maf.plots.nd_plotters import OneDSubsetData, TwoDSubsetData
|
|
10
|
+
|
|
11
|
+
from .base_slicer import BaseSlicer
|
|
12
|
+
|
|
13
|
+
# nd Slicer slices data on N columns in sim_data
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NDSlicer(BaseSlicer):
|
|
17
|
+
"""Nd slicer (N dimensions)
|
|
18
|
+
|
|
19
|
+
Parameters
|
|
20
|
+
==========
|
|
21
|
+
slice_col_list : `list` of `str`
|
|
22
|
+
Names of the data columns for slicing (e.g. 'airmass`, etc)
|
|
23
|
+
bins_list : `int` or `list` of `int` or `np.ndarray`, opt
|
|
24
|
+
Single integer (for same number of slices in each dimension)
|
|
25
|
+
or a list of integers (matching
|
|
26
|
+
slice_col_list) or list of arrays. Default 100, in all dimensions.
|
|
27
|
+
|
|
28
|
+
All bins are half-open ([a, b)).
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(self, slice_col_list=None, bins_list=100, verbose=True):
|
|
32
|
+
super().__init__(verbose=verbose)
|
|
33
|
+
self.slice_col_list = slice_col_list
|
|
34
|
+
self.columns_needed = self.slice_col_list
|
|
35
|
+
if self.slice_col_list is not None:
|
|
36
|
+
self.n_d = len(self.slice_col_list)
|
|
37
|
+
else:
|
|
38
|
+
self.n_d = None
|
|
39
|
+
self.bins_list = bins_list
|
|
40
|
+
if not (isinstance(bins_list, float) or isinstance(bins_list, int)):
|
|
41
|
+
if len(self.bins_list) != self.n_d:
|
|
42
|
+
raise Exception(
|
|
43
|
+
"bins_list must be same length as slice_col_names, unless it is a single value"
|
|
44
|
+
)
|
|
45
|
+
self.slicer_init = {"slice_col_list": slice_col_list, "bins_list": bins_list}
|
|
46
|
+
self.plot_funcs = [TwoDSubsetData, OneDSubsetData]
|
|
47
|
+
|
|
48
|
+
def setup_slicer(self, sim_data, maps=None):
|
|
49
|
+
"""Set up bins."""
|
|
50
|
+
# Parse input bins choices.
|
|
51
|
+
self.bins = []
|
|
52
|
+
# If we were given a single number for the binsList, convert to list.
|
|
53
|
+
if isinstance(self.bins_list, float) or isinstance(self.bins_list, int):
|
|
54
|
+
self.bins_list = [self.bins_list for c in self.slice_col_list]
|
|
55
|
+
# And then build bins.
|
|
56
|
+
for bl, col in zip(self.bins_list, self.slice_col_list):
|
|
57
|
+
if isinstance(bl, float) or isinstance(bl, int):
|
|
58
|
+
slice_col = sim_data[col]
|
|
59
|
+
bin_min = slice_col.min()
|
|
60
|
+
bin_max = slice_col.max()
|
|
61
|
+
if bin_min == bin_max:
|
|
62
|
+
warnings.warn("bin_min=bin_max for column %s: increasing bin_max by 1." % (col))
|
|
63
|
+
bin_max = bin_max + 1
|
|
64
|
+
bin_size = (bin_max - bin_min) / float(bl)
|
|
65
|
+
bins = np.arange(bin_min, bin_max + bin_size / 2.0, bin_size, "float")
|
|
66
|
+
self.bins.append(bins)
|
|
67
|
+
else:
|
|
68
|
+
self.bins.append(np.sort(bl))
|
|
69
|
+
self.nslice = (np.array(list(map(len, self.bins))) - 1).prod()
|
|
70
|
+
# Count how many bins we have total
|
|
71
|
+
# (not counting last 'RHS' bin values, as in oneDSlicer).
|
|
72
|
+
self.shape = self.nslice
|
|
73
|
+
# Set up slice metadata.
|
|
74
|
+
self.slice_points["sid"] = np.arange(self.nslice)
|
|
75
|
+
# Including multi-D 'leftmost' bin values
|
|
76
|
+
bins_for_iteration = []
|
|
77
|
+
for b in self.bins:
|
|
78
|
+
bins_for_iteration.append(b[:-1])
|
|
79
|
+
biniterator = itertools.product(*bins_for_iteration)
|
|
80
|
+
self.slice_points["bins"] = []
|
|
81
|
+
for b in biniterator:
|
|
82
|
+
self.slice_points["bins"].append(b)
|
|
83
|
+
# and multi-D 'leftmost' bin indexes corresponding to each sid
|
|
84
|
+
self.slice_points["bin_idxs"] = []
|
|
85
|
+
bin_ids_for_iteration = []
|
|
86
|
+
for b in self.bins:
|
|
87
|
+
bin_ids_for_iteration.append(np.arange(len(b[:-1])))
|
|
88
|
+
bin_id_iterator = itertools.product(*bin_ids_for_iteration)
|
|
89
|
+
for bidx in bin_id_iterator:
|
|
90
|
+
self.slice_points["bin_idxs"].append(bidx)
|
|
91
|
+
# Add metadata from maps.
|
|
92
|
+
self._run_maps(maps)
|
|
93
|
+
# Set up indexing for data slicing.
|
|
94
|
+
self.sim_idxs = []
|
|
95
|
+
self.lefts = []
|
|
96
|
+
for slice_col_name, bins in zip(self.slice_col_list, self.bins):
|
|
97
|
+
sim_idxs = np.argsort(sim_data[slice_col_name])
|
|
98
|
+
sim_fields_sorted = np.sort(sim_data[slice_col_name])
|
|
99
|
+
# "left" values are location where simdata == bin value
|
|
100
|
+
left = np.searchsorted(sim_fields_sorted, bins[:-1], "left")
|
|
101
|
+
left = np.concatenate(
|
|
102
|
+
(
|
|
103
|
+
left,
|
|
104
|
+
np.array(
|
|
105
|
+
[
|
|
106
|
+
len(sim_idxs),
|
|
107
|
+
]
|
|
108
|
+
),
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
# Add these calculated values into the class lists of
|
|
112
|
+
# sim_idxs and lefts.
|
|
113
|
+
self.sim_idxs.append(sim_idxs)
|
|
114
|
+
self.lefts.append(left)
|
|
115
|
+
|
|
116
|
+
@wraps(self._slice_sim_data)
|
|
117
|
+
def _slice_sim_data(islice):
|
|
118
|
+
"""Slice sim_data to return relevant indexes for slice_point."""
|
|
119
|
+
# Identify relevant pointings in each dimension.
|
|
120
|
+
sim_idxs_list = []
|
|
121
|
+
# Translate islice into indexes in each bin dimension
|
|
122
|
+
bin_idxs = self.slice_points["bin_idxs"][islice]
|
|
123
|
+
for d, i in zip(list(range(self.n_d)), bin_idxs):
|
|
124
|
+
sim_idxs_list.append(set(self.sim_idxs[d][self.lefts[d][i] : self.lefts[d][i + 1]]))
|
|
125
|
+
idxs = list(set.intersection(*sim_idxs_list))
|
|
126
|
+
return {
|
|
127
|
+
"idxs": idxs,
|
|
128
|
+
"slice_point": {
|
|
129
|
+
"sid": islice,
|
|
130
|
+
"bin_left": self.slice_points["bins"][islice],
|
|
131
|
+
"bin_idxs": self.slice_points["bin_idxs"][islice],
|
|
132
|
+
},
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
setattr(self, "_slice_sim_data", _slice_sim_data)
|
|
136
|
+
|
|
137
|
+
def __eq__(self, other_slicer):
|
|
138
|
+
"""Evaluate if grids are equivalent."""
|
|
139
|
+
if isinstance(other_slicer, NDSlicer):
|
|
140
|
+
if other_slicer.n_d != self.n_d:
|
|
141
|
+
return False
|
|
142
|
+
for i in range(self.n_d):
|
|
143
|
+
if not np.array_equal(other_slicer.slice_points["bins"][i], self.slice_points["bins"][i]):
|
|
144
|
+
return False
|
|
145
|
+
return True
|
|
146
|
+
else:
|
|
147
|
+
return False
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
__all__ = ("OneDSlicer",)
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from functools import wraps
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
from rubin_sim.maf.plots.oned_plotters import OneDBinnedData
|
|
9
|
+
from rubin_sim.maf.stackers import ColInfo
|
|
10
|
+
from rubin_sim.maf.utils import optimal_bins
|
|
11
|
+
|
|
12
|
+
from .base_slicer import BaseSlicer
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class OneDSlicer(BaseSlicer):
|
|
16
|
+
"""OneD Slicer allows the slicing of data into bins in a single dimension.
|
|
17
|
+
|
|
18
|
+
Parameters
|
|
19
|
+
----------
|
|
20
|
+
slice_col_name : `str`
|
|
21
|
+
The name of the data column to base slicing on (i.e. 'airmass', etc.)
|
|
22
|
+
slice_col_units : `str`, optional
|
|
23
|
+
Set a name for the units of the sliceCol. Used for plotting labels.
|
|
24
|
+
bins : np.ndarray, optional
|
|
25
|
+
The data will be sliced into 'bins': this can be defined as an
|
|
26
|
+
array here. Default None.
|
|
27
|
+
bin_min : `float`, optional
|
|
28
|
+
bin_max : `float`, optional
|
|
29
|
+
bin_size : `float`, optional
|
|
30
|
+
If bins is not defined, then bin_min/bin_max/bin_size can be chosen
|
|
31
|
+
to anchor the slice points.
|
|
32
|
+
Default None.
|
|
33
|
+
Priority goes: bins >> bin_min/bin_max/bin_size >> data values
|
|
34
|
+
(if none of the above are chosen).
|
|
35
|
+
|
|
36
|
+
Notes
|
|
37
|
+
-----
|
|
38
|
+
All bins except for the last bin are half-open ([a, b)) while the
|
|
39
|
+
last bin is ([a, b]).
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
slice_col_name=None,
|
|
45
|
+
slice_col_units=None,
|
|
46
|
+
bins=None,
|
|
47
|
+
bin_min=None,
|
|
48
|
+
bin_max=None,
|
|
49
|
+
bin_size=None,
|
|
50
|
+
verbose=True,
|
|
51
|
+
badval=0,
|
|
52
|
+
):
|
|
53
|
+
super().__init__(verbose=verbose, badval=badval)
|
|
54
|
+
if slice_col_name is None:
|
|
55
|
+
raise ValueError("slice_col_name cannot be left None - choose a data column to group data by")
|
|
56
|
+
self.slice_col_name = slice_col_name
|
|
57
|
+
self.columns_needed = [slice_col_name]
|
|
58
|
+
# We could try to set up the self.bins here --
|
|
59
|
+
# but it's also possible that
|
|
60
|
+
# these bin_min/max/size values have not been set and
|
|
61
|
+
# should just be set from the data.
|
|
62
|
+
self.bins = bins
|
|
63
|
+
self.bin_min = bin_min
|
|
64
|
+
self.bin_max = bin_max
|
|
65
|
+
self.bin_size = bin_size
|
|
66
|
+
# Forget binmin/max/stepsize if bins was set
|
|
67
|
+
if self.bins is not None:
|
|
68
|
+
if bin_min is not None or bin_max is not None or bin_size is not None:
|
|
69
|
+
warnings.warning(
|
|
70
|
+
f"Both bins and one of the bin_min/bin_max/bin_size was specified. "
|
|
71
|
+
f"Using bins ({self.bins} values only."
|
|
72
|
+
)
|
|
73
|
+
self.bin_min = None
|
|
74
|
+
self.bin_max = None
|
|
75
|
+
self.bin_size = None
|
|
76
|
+
# Set the column units
|
|
77
|
+
if slice_col_units is not None:
|
|
78
|
+
self.slice_col_units = slice_col_units
|
|
79
|
+
# Try to determine the column units
|
|
80
|
+
else:
|
|
81
|
+
co = ColInfo()
|
|
82
|
+
self.slice_col_units = co.get_units(self.slice_col_name)
|
|
83
|
+
# Set slicer re-initialize values and default plotFunction
|
|
84
|
+
self.slicer_init = {
|
|
85
|
+
"slice_col_name": self.slice_col_name,
|
|
86
|
+
"slice_col_units": slice_col_units,
|
|
87
|
+
"badval": badval,
|
|
88
|
+
"bin_min": self.bin_min,
|
|
89
|
+
"bin_max": self.bin_max,
|
|
90
|
+
"bin_size": self.bin_size,
|
|
91
|
+
"bins": self.bins,
|
|
92
|
+
}
|
|
93
|
+
self.plot_funcs = [
|
|
94
|
+
OneDBinnedData,
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
def setup_slicer(self, sim_data, maps=None):
|
|
98
|
+
"""Set up bins in slicer.
|
|
99
|
+
|
|
100
|
+
This happens AFTER sim_data is defined,
|
|
101
|
+
thus typically in the MetricBundleGroup.
|
|
102
|
+
This maps data into the bins;
|
|
103
|
+
it's not a good idea to reuse a OneDSlicer as a result.
|
|
104
|
+
"""
|
|
105
|
+
if "bins" in self.slice_points:
|
|
106
|
+
warning_msg = "Warning: this OneDSlicer was already set up once. "
|
|
107
|
+
warning_msg += (
|
|
108
|
+
"Re-setting up a OneDSlicer is unpredictable; at the very least, it "
|
|
109
|
+
"will change the mapping of the simulated data into the data slices. "
|
|
110
|
+
"A safer choice is to use a separate OneDSlicer for each MetricBundle."
|
|
111
|
+
)
|
|
112
|
+
warnings.warn(warning_msg)
|
|
113
|
+
slice_col = sim_data[self.slice_col_name]
|
|
114
|
+
# Set bins from data or specified values,
|
|
115
|
+
# if they were previously defined.
|
|
116
|
+
if self.bins is None:
|
|
117
|
+
# Set bin min/max values (could have been set in __init__)
|
|
118
|
+
if self.bin_min is None:
|
|
119
|
+
self.bin_min = np.nanmin(slice_col)
|
|
120
|
+
if self.bin_max is None:
|
|
121
|
+
self.bin_max = np.nanmax(slice_col)
|
|
122
|
+
# Give warning if bin_min = bin_max,
|
|
123
|
+
# and do something at least slightly reasonable.
|
|
124
|
+
if self.bin_min == self.bin_max:
|
|
125
|
+
warnings.warn(
|
|
126
|
+
"bin_min = bin_max (maybe your data is single-valued?). "
|
|
127
|
+
"Increasing bin_max by 1 (or 2*bin_size, if bin_size was set)."
|
|
128
|
+
)
|
|
129
|
+
if self.bin_size is not None:
|
|
130
|
+
self.bin_max = self.bin_max + 2 * self.bin_size
|
|
131
|
+
else:
|
|
132
|
+
self.bin_max = self.bin_max + 1
|
|
133
|
+
if self.bin_size is None:
|
|
134
|
+
bins = optimal_bins(slice_col, self.bin_min, self.bin_max)
|
|
135
|
+
nbins = np.round(bins)
|
|
136
|
+
self.bin_size = (self.bin_max - self.bin_min) / float(nbins)
|
|
137
|
+
# Set bins
|
|
138
|
+
self.bins = np.arange(self.bin_min, self.bin_max + self.bin_size / 2.0, self.bin_size, "float")
|
|
139
|
+
# nslice is used to stop iteration and should
|
|
140
|
+
# reflect the usable length of the bins
|
|
141
|
+
self.nslice = len(self.bins) - 1
|
|
142
|
+
# and "shape" refers to the length of the datavalues,
|
|
143
|
+
# and should be one less than # of bins because last
|
|
144
|
+
# binvalue is RH edge only
|
|
145
|
+
self.shape = self.nslice
|
|
146
|
+
# Set slice_point metadata.
|
|
147
|
+
self.slice_points["sid"] = np.arange(self.nslice)
|
|
148
|
+
self.slice_points["bins"] = self.bins
|
|
149
|
+
# Add metadata from map if needed.
|
|
150
|
+
self._run_maps(maps)
|
|
151
|
+
|
|
152
|
+
indxs = np.argsort(sim_data[self.slice_col_name])
|
|
153
|
+
data_sorted = sim_data[self.slice_col_name][indxs]
|
|
154
|
+
|
|
155
|
+
# Setting up slices such that left_edge <= data < right_edge
|
|
156
|
+
# in each slice.
|
|
157
|
+
left = np.searchsorted(data_sorted, self.bins[0:-1], "left")
|
|
158
|
+
right = np.searchsorted(data_sorted, self.bins[1:], "left")
|
|
159
|
+
|
|
160
|
+
self.sim_idxs = [indxs[le:ri] for le, ri in zip(left, right)]
|
|
161
|
+
|
|
162
|
+
# Set up _slice_sim_data method for this class.
|
|
163
|
+
@wraps(self._slice_sim_data)
|
|
164
|
+
def _slice_sim_data(islice):
|
|
165
|
+
"""Slice sim_data on oneD sliceCol, to return relevant
|
|
166
|
+
indexes for slice_point.
|
|
167
|
+
"""
|
|
168
|
+
idxs = self.sim_idxs[islice]
|
|
169
|
+
bin_left = self.bins[islice]
|
|
170
|
+
bin_right = self.bins[islice + 1]
|
|
171
|
+
return {
|
|
172
|
+
"idxs": idxs,
|
|
173
|
+
"slice_point": {
|
|
174
|
+
"sid": islice,
|
|
175
|
+
"bin_left": bin_left,
|
|
176
|
+
"bin_right": bin_right,
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
setattr(self, "_slice_sim_data", _slice_sim_data)
|
|
181
|
+
|
|
182
|
+
def __eq__(self, other_slicer):
|
|
183
|
+
"""Evaluate if slicers are equivalent."""
|
|
184
|
+
result = False
|
|
185
|
+
if isinstance(other_slicer, OneDSlicer):
|
|
186
|
+
if self.slice_col_name == other_slicer.slice_col_name:
|
|
187
|
+
# If slicer restored from disk or setup,
|
|
188
|
+
# then 'bins' in slice_points dict.
|
|
189
|
+
# This is preferred method to see if slicers are equal.
|
|
190
|
+
if ("bins" in self.slice_points) & ("bins" in other_slicer.slice_points):
|
|
191
|
+
result = np.array_equal(other_slicer.slice_points["bins"], self.slice_points["bins"])
|
|
192
|
+
# However, before we 'setup' the slicer with data,
|
|
193
|
+
# the slicers could be equivalent.
|
|
194
|
+
else:
|
|
195
|
+
if (self.bins is not None) and (other_slicer.bins is not None):
|
|
196
|
+
result = np.array_equal(self.bins, other_slicer.bins)
|
|
197
|
+
elif (
|
|
198
|
+
(self.bin_size is not None)
|
|
199
|
+
and (self.bin_min is not None) & (self.bin_max is not None)
|
|
200
|
+
and (other_slicer.bin_size is not None)
|
|
201
|
+
and (other_slicer.bin_min is not None)
|
|
202
|
+
and (other_slicer.bin_max is not None)
|
|
203
|
+
):
|
|
204
|
+
if (
|
|
205
|
+
(self.bin_size == other_slicer.bin_size)
|
|
206
|
+
and (self.bin_min == other_slicer.bin_min)
|
|
207
|
+
and (self.bin_max == other_slicer.bin_max)
|
|
208
|
+
):
|
|
209
|
+
result = True
|
|
210
|
+
return result
|