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,488 @@
|
|
|
1
|
+
__all__ = (
|
|
2
|
+
"create_constellation",
|
|
3
|
+
"starlink_tles_v1",
|
|
4
|
+
"starlink_tles_v2",
|
|
5
|
+
"oneweb_tles",
|
|
6
|
+
"Constellation",
|
|
7
|
+
"sun_alt_limits",
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
from astropy import constants as const
|
|
12
|
+
from astropy import units as u
|
|
13
|
+
from rubin_scheduler.utils import SURVEY_START_MJD, Site, gnomonic_project_toxy, point_to_line_distance
|
|
14
|
+
from shapely.geometry import LineString, Point
|
|
15
|
+
from skyfield.api import EarthSatellite, load, wgs84
|
|
16
|
+
|
|
17
|
+
MJDOFFSET = 2400000.5
|
|
18
|
+
mjd0 = SURVEY_START_MJD
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def sun_alt_limits():
|
|
22
|
+
"""Return sun altitude limits (degrees) at which zero illuminated
|
|
23
|
+
satellites above 20 degrees altitude result.
|
|
24
|
+
|
|
25
|
+
Different constellations have different limits at which zero illumination
|
|
26
|
+
above 20 degrees occurs.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
sun_alt_limits : `dict` [`str`: `float`]
|
|
31
|
+
Dict with satellite constellation name keys,
|
|
32
|
+
altitude limits values (degrees).
|
|
33
|
+
"""
|
|
34
|
+
# Estimated in sun_alts_limits.ipynb
|
|
35
|
+
result = {"slv1": -36.0, "slv2": -36.0, "oneweb": -53.0}
|
|
36
|
+
return result
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def satellite_mean_motion(altitude, mu=const.GM_earth, r_earth=const.R_earth):
|
|
40
|
+
"""Calculate mean motion of satellites at a given altitude in Earth's
|
|
41
|
+
gravitational field.
|
|
42
|
+
See https://en.wikipedia.org/wiki/Mean_motion#Formulae
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
altitude : `float`
|
|
47
|
+
Altitude of the satellite.
|
|
48
|
+
Should be a float with astropy units attached.
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
mean_motion : `float`
|
|
53
|
+
"""
|
|
54
|
+
no = np.sqrt(4.0 * np.pi**2 * (altitude + r_earth) ** 3 / mu).to(u.day)
|
|
55
|
+
return 1 / no
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def tle_from_orbital_parameters(sat_name, sat_nr, epoch, inclination, raan, mean_anomaly, mean_motion):
|
|
59
|
+
"""Generate TLE strings from orbital parameters.
|
|
60
|
+
|
|
61
|
+
Parameters
|
|
62
|
+
----------
|
|
63
|
+
sat_name : `str`
|
|
64
|
+
Satellite name
|
|
65
|
+
sat_nr : `float`
|
|
66
|
+
Satellite nr
|
|
67
|
+
epoch : `float`
|
|
68
|
+
Epoch
|
|
69
|
+
inclination : `float`
|
|
70
|
+
Inclination, probably with astropy units attached
|
|
71
|
+
raan : `float`
|
|
72
|
+
RA of the ascending node. Assumes astropy units.
|
|
73
|
+
mean_anomaly : `float`
|
|
74
|
+
Mean anomaly.
|
|
75
|
+
mean_motion : `float`
|
|
76
|
+
Mean motion.
|
|
77
|
+
|
|
78
|
+
Returns
|
|
79
|
+
-------
|
|
80
|
+
tle : `str`
|
|
81
|
+
|
|
82
|
+
Notes
|
|
83
|
+
-----
|
|
84
|
+
epoch has the format: first two digits are the year,
|
|
85
|
+
next three digits are the day from beginning of year,
|
|
86
|
+
then fraction of a day is given, e.g.
|
|
87
|
+
20180.25 would be 2020, day 180, 6 hours (UT?)
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
# Note: RAAN = right ascention (or longitude) of ascending node
|
|
91
|
+
# I suspect this is filling in 0 eccentricity everywhere.
|
|
92
|
+
|
|
93
|
+
def checksum(line):
|
|
94
|
+
s = 0
|
|
95
|
+
for c in line[:-1]:
|
|
96
|
+
if c.isdigit():
|
|
97
|
+
s += int(c)
|
|
98
|
+
if c == "-":
|
|
99
|
+
s += 1
|
|
100
|
+
return "{:s}{:1d}".format(line[:-1], s % 10)
|
|
101
|
+
|
|
102
|
+
tle0 = sat_name
|
|
103
|
+
tle1 = checksum(
|
|
104
|
+
"1 {:05d}U 20001A {:14.8f} .00000000 00000-0 50000-4 " "0 0X".format(sat_nr, epoch)
|
|
105
|
+
)
|
|
106
|
+
tle2 = checksum(
|
|
107
|
+
"2 {:05d} {:8.4f} {:8.4f} 0001000 0.0000 {:8.4f} "
|
|
108
|
+
"{:11.8f} 0X".format(
|
|
109
|
+
sat_nr,
|
|
110
|
+
inclination.to_value(u.deg),
|
|
111
|
+
raan.to_value(u.deg),
|
|
112
|
+
mean_anomaly.to_value(u.deg),
|
|
113
|
+
mean_motion.to_value(1 / u.day),
|
|
114
|
+
)
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
return "\n".join([tle0, tle1, tle2])
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def create_constellation(
|
|
121
|
+
altitudes,
|
|
122
|
+
inclinations,
|
|
123
|
+
nplanes,
|
|
124
|
+
sats_per_plane,
|
|
125
|
+
epoch=23274.0,
|
|
126
|
+
name="Test",
|
|
127
|
+
seed=42,
|
|
128
|
+
):
|
|
129
|
+
"""Create a set of orbital elements for a satellite constellation then
|
|
130
|
+
convert them to TLEs.
|
|
131
|
+
|
|
132
|
+
Parameters
|
|
133
|
+
----------
|
|
134
|
+
altitudes : `np.ndarray`, (N,)
|
|
135
|
+
Altitudes (degrees).
|
|
136
|
+
inclinations : `np.ndarray`, (N,)
|
|
137
|
+
Inclinations (degrees).
|
|
138
|
+
nplanes : `np.ndarray`, (N,)
|
|
139
|
+
Number of satellite planes.
|
|
140
|
+
sats_per_plane : `np.ndarray`, (N,)
|
|
141
|
+
Number of satellites per orbital plane.
|
|
142
|
+
epoch : `float`
|
|
143
|
+
Epoch.
|
|
144
|
+
name : `str`
|
|
145
|
+
Satellite name.
|
|
146
|
+
seed : `float`
|
|
147
|
+
Random number seed.
|
|
148
|
+
|
|
149
|
+
Returns
|
|
150
|
+
-------
|
|
151
|
+
my_sat_tles : `list` of `str`
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
rng = np.random.default_rng(seed)
|
|
155
|
+
|
|
156
|
+
my_sat_tles = []
|
|
157
|
+
sat_nr = 8000
|
|
158
|
+
for alt, inc, n, s in zip(altitudes, inclinations, nplanes, sats_per_plane):
|
|
159
|
+
if s == 1:
|
|
160
|
+
# random placement for lower orbits
|
|
161
|
+
mas = rng.uniform(0, 360, n) * u.deg
|
|
162
|
+
raans = rng.uniform(0, 360, n) * u.deg
|
|
163
|
+
else:
|
|
164
|
+
mas = np.linspace(0.0, 360.0, s, endpoint=False) * u.deg
|
|
165
|
+
mas += rng.uniform(0, 360, 1) * u.deg
|
|
166
|
+
raans = np.linspace(0.0, 360.0, n, endpoint=False) * u.deg
|
|
167
|
+
mas, raans = np.meshgrid(mas, raans)
|
|
168
|
+
mas, raans = mas.flatten(), raans.flatten()
|
|
169
|
+
|
|
170
|
+
mm = satellite_mean_motion(alt)
|
|
171
|
+
for ma, raan in zip(mas, raans):
|
|
172
|
+
my_sat_tles.append(
|
|
173
|
+
tle_from_orbital_parameters(name + " {:d}".format(sat_nr), sat_nr, epoch, inc, raan, ma, mm)
|
|
174
|
+
)
|
|
175
|
+
sat_nr += 1
|
|
176
|
+
|
|
177
|
+
return my_sat_tles
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def starlink_tles_v1():
|
|
181
|
+
"""Create a list of satellite TLE's, appropriate for
|
|
182
|
+
Starlink v1 (as of July 2022). Should create 4,408 orbits
|
|
183
|
+
|
|
184
|
+
Returns
|
|
185
|
+
-------
|
|
186
|
+
my_sat_tles : `list` [`str`]
|
|
187
|
+
"""
|
|
188
|
+
altitudes = np.array([550, 540, 570, 560, 560]) * u.km
|
|
189
|
+
inclinations = np.array([53, 53.2, 70, 97.6, 97.6]) * u.deg
|
|
190
|
+
nplanes = np.array([72, 72, 36, 6, 4])
|
|
191
|
+
sats_per_plane = np.array([22, 22, 20, 58, 43])
|
|
192
|
+
|
|
193
|
+
my_sat_tles = create_constellation(altitudes, inclinations, nplanes, sats_per_plane, name="starV1")
|
|
194
|
+
|
|
195
|
+
return my_sat_tles
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def starlink_tles_v2():
|
|
199
|
+
"""Create a list of satellite TLE's appropriate for
|
|
200
|
+
Starlink v2 (as of July 2022). Should create 29,988 orbits
|
|
201
|
+
|
|
202
|
+
Returns
|
|
203
|
+
-------
|
|
204
|
+
my_sat_tles : `list` [`str`]
|
|
205
|
+
"""
|
|
206
|
+
altitudes = np.array([340, 345, 350, 360, 525, 530, 535, 604, 614]) * u.km
|
|
207
|
+
inclinations = np.array([53, 46, 38, 96.9, 53, 43, 33, 148, 115.7]) * u.deg
|
|
208
|
+
nplanes = np.array([48, 48, 48, 30, 28, 28, 28, 12, 18])
|
|
209
|
+
sats_per_plane = np.array([110, 110, 110, 120, 120, 120, 120, 12, 18])
|
|
210
|
+
|
|
211
|
+
my_sat_tles = create_constellation(altitudes, inclinations, nplanes, sats_per_plane, name="starV2")
|
|
212
|
+
|
|
213
|
+
return my_sat_tles
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def oneweb_tles():
|
|
217
|
+
"""Create a list of satellite TLE's appropriate
|
|
218
|
+
for OneWeb plans (as of July 2022). Should create 6,372 orbits
|
|
219
|
+
|
|
220
|
+
Returns
|
|
221
|
+
-------
|
|
222
|
+
my_sat_tles : `list` [`str`]
|
|
223
|
+
"""
|
|
224
|
+
altitudes = np.array([1200, 1200, 1200]) * u.km
|
|
225
|
+
inclinations = np.array([87.9, 40, 55]) * u.deg
|
|
226
|
+
nplanes = np.array([49, 72, 72])
|
|
227
|
+
sats_per_plane = np.array([36, 32, 32])
|
|
228
|
+
|
|
229
|
+
my_sat_tles = create_constellation(altitudes, inclinations, nplanes, sats_per_plane, name="oneWe")
|
|
230
|
+
|
|
231
|
+
return my_sat_tles
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class Constellation:
|
|
235
|
+
"""Holds the constellation TLEs and calculates their appearance
|
|
236
|
+
in a series of observations.
|
|
237
|
+
|
|
238
|
+
Parameters
|
|
239
|
+
----------
|
|
240
|
+
sat_tle_list : `list` [`str`]
|
|
241
|
+
A list of satellite TLEs to be used
|
|
242
|
+
alt_limit : `float`
|
|
243
|
+
Altitude limit below which satellites can be ignored (degrees)
|
|
244
|
+
fov : `float`
|
|
245
|
+
The field of view diameter (degrees)
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
def __init__(self, sat_tle_list, alt_limit=20.0, fov=3.5):
|
|
249
|
+
self.alt_limit_rad = np.radians(alt_limit)
|
|
250
|
+
self.fov_radius_rad = np.radians(fov / 2.0)
|
|
251
|
+
|
|
252
|
+
# Load ephemeris for sun position
|
|
253
|
+
self.eph = load("de421.bsp")
|
|
254
|
+
|
|
255
|
+
self.sat_list = []
|
|
256
|
+
self.ts = load.timescale()
|
|
257
|
+
for tle in sat_tle_list:
|
|
258
|
+
name, line1, line2 = tle.split("\n")
|
|
259
|
+
self.sat_list.append(EarthSatellite(line1, line2, name, self.ts))
|
|
260
|
+
|
|
261
|
+
self._make_location()
|
|
262
|
+
|
|
263
|
+
def _make_location(self):
|
|
264
|
+
telescope = Site(name="LSST")
|
|
265
|
+
|
|
266
|
+
self.observatory_site = wgs84.latlon(telescope.latitude, telescope.longitude, telescope.height)
|
|
267
|
+
|
|
268
|
+
def update_mjd(self, mjd):
|
|
269
|
+
"""Calculate and record the alt/az position and illumination status
|
|
270
|
+
for all the satellites at a given time.
|
|
271
|
+
|
|
272
|
+
Parameters
|
|
273
|
+
----------
|
|
274
|
+
mjd : `float`
|
|
275
|
+
New MJD.
|
|
276
|
+
"""
|
|
277
|
+
jd = mjd + MJDOFFSET
|
|
278
|
+
t = self.ts.ut1_jd(jd)
|
|
279
|
+
|
|
280
|
+
self.altitudes_rad = []
|
|
281
|
+
self.azimuth_rad = []
|
|
282
|
+
self.illum = []
|
|
283
|
+
for sat in self.sat_list:
|
|
284
|
+
current_sat = sat.at(t)
|
|
285
|
+
illum = current_sat.is_sunlit(self.eph)
|
|
286
|
+
self.illum.append(illum.copy())
|
|
287
|
+
if illum:
|
|
288
|
+
topo = current_sat - self.observatory_site.at(t)
|
|
289
|
+
# this returns an Angle object
|
|
290
|
+
alt, az, dist = topo.altaz()
|
|
291
|
+
self.altitudes_rad.append(alt.radians + 0)
|
|
292
|
+
self.azimuth_rad.append(az.radians + 0)
|
|
293
|
+
else:
|
|
294
|
+
self.altitudes_rad.append(np.nan)
|
|
295
|
+
self.azimuth_rad.append(np.nan)
|
|
296
|
+
|
|
297
|
+
self.altitudes_rad = np.array(self.altitudes_rad)
|
|
298
|
+
self.azimuth_rad = np.array(self.azimuth_rad)
|
|
299
|
+
self.illum = np.array(self.illum)
|
|
300
|
+
# Keep track of the ones that are up and illuminated
|
|
301
|
+
self.visible = np.where((self.altitudes_rad >= self.alt_limit_rad) & self.illum)[0]
|
|
302
|
+
|
|
303
|
+
def paths_array(self, mjds):
|
|
304
|
+
"""Calculate and return the RA/Dec/Alt and illumination status
|
|
305
|
+
for all the satellites at an array of times.
|
|
306
|
+
|
|
307
|
+
Parameters
|
|
308
|
+
----------
|
|
309
|
+
mjds : `np.ndarray`, (N,)
|
|
310
|
+
Modified Julian Dates.
|
|
311
|
+
|
|
312
|
+
Returns
|
|
313
|
+
-------
|
|
314
|
+
ras : `np.ndarray`, (N,)
|
|
315
|
+
RAs at each MJD
|
|
316
|
+
decs : `np.ndarray`, (N,)
|
|
317
|
+
Decs at each MJD
|
|
318
|
+
alts : `np.ndarray`, (N,)
|
|
319
|
+
Altitudes at each MJD
|
|
320
|
+
illums : `np.ndarray`, (N,)
|
|
321
|
+
Array of bools for if satellite is illuminated
|
|
322
|
+
"""
|
|
323
|
+
|
|
324
|
+
jd = mjds + MJDOFFSET
|
|
325
|
+
t = self.ts.ut1_jd(jd)
|
|
326
|
+
|
|
327
|
+
ras = []
|
|
328
|
+
decs = []
|
|
329
|
+
alts = []
|
|
330
|
+
illums = []
|
|
331
|
+
for sat in self.sat_list:
|
|
332
|
+
current_sat = sat.at(t)
|
|
333
|
+
illum = current_sat.is_sunlit(self.eph)
|
|
334
|
+
illums.append(illum.copy())
|
|
335
|
+
topo = current_sat - self.observatory_site.at(t)
|
|
336
|
+
ra, dec, distance = topo.radec()
|
|
337
|
+
alt, az, dist = topo.altaz()
|
|
338
|
+
ras.append(ra.radians)
|
|
339
|
+
decs.append(dec.radians)
|
|
340
|
+
alts.append(alt.radians)
|
|
341
|
+
return np.vstack(ras), np.vstack(decs), np.vstack(alts), np.vstack(illums)
|
|
342
|
+
|
|
343
|
+
def check_pointings(
|
|
344
|
+
self,
|
|
345
|
+
pointing_ras,
|
|
346
|
+
pointing_decs,
|
|
347
|
+
mjds,
|
|
348
|
+
visit_time,
|
|
349
|
+
fov_radius=1.75,
|
|
350
|
+
test_radius=10.0,
|
|
351
|
+
dt=2.0,
|
|
352
|
+
):
|
|
353
|
+
"""Calculate streak length and number of streaks in a set of visits.
|
|
354
|
+
|
|
355
|
+
Parameters
|
|
356
|
+
----------
|
|
357
|
+
pointing_ras : `np.ndarray`, (N,)
|
|
358
|
+
The RA for each pointing (degrees).
|
|
359
|
+
pointing_decs : `np.ndarray`, (N,)
|
|
360
|
+
The dec for each pointing (degrees).
|
|
361
|
+
mjds : `np.ndarray`, (N,)
|
|
362
|
+
The MJD for the (start) of each pointing (days).
|
|
363
|
+
visit_time : `np.ndarray`, (N,)
|
|
364
|
+
The start to end time for a visit (seconds).
|
|
365
|
+
fov_radius : `float`
|
|
366
|
+
The radius of the science field of view (degrees)
|
|
367
|
+
test_radius : `float`
|
|
368
|
+
The radius to use to see if a streak gets close (degrees).
|
|
369
|
+
Should be large, because satellites can be moving at ~1 deg/s.
|
|
370
|
+
dt : `float`
|
|
371
|
+
The timestep to use for high resolution checking
|
|
372
|
+
if a satellite crossed (seconds).
|
|
373
|
+
|
|
374
|
+
Returns
|
|
375
|
+
-------
|
|
376
|
+
streak length : `float`
|
|
377
|
+
The total length of satellite streaks in the FoV (degrees)
|
|
378
|
+
n_streak : `int`
|
|
379
|
+
The number of streaks that were in the FoV.
|
|
380
|
+
"""
|
|
381
|
+
test_radius_rad = np.radians(test_radius)
|
|
382
|
+
dt = dt / 3600 / 24 # to days
|
|
383
|
+
visit_time = visit_time / 3600.0 / 24.0
|
|
384
|
+
|
|
385
|
+
# Arrays to hold results
|
|
386
|
+
lengths_rad = np.zeros(pointing_ras.size, dtype=float)
|
|
387
|
+
n_streaks = np.zeros(pointing_ras.size, dtype=int)
|
|
388
|
+
|
|
389
|
+
input_id_indx_oned = np.arange(pointing_ras.size, dtype=int)
|
|
390
|
+
|
|
391
|
+
# Convert everything to radians for internal computations
|
|
392
|
+
pointing_ras_rad = np.radians(pointing_ras)
|
|
393
|
+
pointing_decs_rad = np.radians(pointing_decs)
|
|
394
|
+
fov_radius_rad = np.radians(fov_radius)
|
|
395
|
+
|
|
396
|
+
# Note self.paths_array should return an array that is
|
|
397
|
+
# N_sats x N_mjds in shape
|
|
398
|
+
# And all angles in radians.
|
|
399
|
+
sat_ra_1, sat_dec_1, sat_alt_1, sat_illum_1 = self.paths_array(mjds)
|
|
400
|
+
mjd_end = mjds + visit_time
|
|
401
|
+
sat_ra_2, sat_dec_2, sat_alt_2, sat_illum_2 = self.paths_array(mjd_end)
|
|
402
|
+
|
|
403
|
+
# broadcast the pointings to be the same shape as the satellite arrays.
|
|
404
|
+
pointing_ras_rad = np.broadcast_to(pointing_ras_rad, sat_ra_1.shape)
|
|
405
|
+
pointing_decs_rad = np.broadcast_to(pointing_decs_rad, sat_ra_1.shape)
|
|
406
|
+
input_id_indx = np.broadcast_to(input_id_indx_oned, sat_ra_1.shape)
|
|
407
|
+
|
|
408
|
+
# Which satellites are above the altitude limit and illuminated
|
|
409
|
+
# np.where confuses me when used on a 2d array.
|
|
410
|
+
above_illum_indx = np.where(
|
|
411
|
+
((sat_alt_1 > self.alt_limit_rad) | (sat_alt_2 > self.alt_limit_rad))
|
|
412
|
+
& (sat_illum_1 | sat_illum_2)
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
# point_to_line_distance can take arrays,
|
|
416
|
+
# but they all need to be the same shape,
|
|
417
|
+
# thus why we broadcast pointing ra and dec above.
|
|
418
|
+
distances = point_to_line_distance(
|
|
419
|
+
sat_ra_1[above_illum_indx],
|
|
420
|
+
sat_dec_1[above_illum_indx],
|
|
421
|
+
sat_ra_2[above_illum_indx],
|
|
422
|
+
sat_dec_2[above_illum_indx],
|
|
423
|
+
pointing_ras_rad[above_illum_indx],
|
|
424
|
+
pointing_decs_rad[above_illum_indx],
|
|
425
|
+
)
|
|
426
|
+
|
|
427
|
+
close = np.where(distances < test_radius_rad)[0]
|
|
428
|
+
|
|
429
|
+
# Numpy broadcasting is such a dark art
|
|
430
|
+
sat_indx = np.arange(len(self.sat_list), dtype=int)[np.newaxis]
|
|
431
|
+
sat_indx = np.broadcast_to(sat_indx.T, sat_ra_1.shape)
|
|
432
|
+
|
|
433
|
+
mjd_broad = np.broadcast_to(mjds, sat_ra_1.shape)[above_illum_indx][close]
|
|
434
|
+
visit_broad = np.broadcast_to(visit_time, sat_ra_1.shape)[above_illum_indx][close]
|
|
435
|
+
|
|
436
|
+
# ok, this is pretty ugly, but should get the job done
|
|
437
|
+
# Loop over all the potential collisions we have found
|
|
438
|
+
for p_ra, p_dec, ob_indx, mjd, vt, sat_in in zip(
|
|
439
|
+
pointing_ras_rad[above_illum_indx][close],
|
|
440
|
+
pointing_decs_rad[above_illum_indx][close],
|
|
441
|
+
input_id_indx[above_illum_indx][close],
|
|
442
|
+
mjd_broad,
|
|
443
|
+
visit_broad,
|
|
444
|
+
sat_indx[above_illum_indx][close],
|
|
445
|
+
):
|
|
446
|
+
mjd = np.linspace(mjd, mjd + vt, num=np.round(vt / dt).astype(int))
|
|
447
|
+
jd = mjd + MJDOFFSET
|
|
448
|
+
t = self.ts.ut1_jd(jd)
|
|
449
|
+
sat = self.sat_list[sat_in]
|
|
450
|
+
current_sat = sat.at(t)
|
|
451
|
+
topo = current_sat - self.observatory_site.at(t)
|
|
452
|
+
sat_ra, sat_dec, _distance = topo.radec()
|
|
453
|
+
|
|
454
|
+
length = _streak_length(sat_ra.radians, sat_dec.radians, p_ra, p_dec, fov_radius_rad)
|
|
455
|
+
if length > 0:
|
|
456
|
+
lengths_rad[ob_indx] += length
|
|
457
|
+
n_streaks[ob_indx] += 1
|
|
458
|
+
return np.degrees(lengths_rad), n_streaks
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def _streak_length(sat_ras, sat_decs, pointing_ra, pointing_dec, radius):
|
|
462
|
+
"""Calculate streak lengths for satellites in a given (circular) pointing.
|
|
463
|
+
|
|
464
|
+
Parameters
|
|
465
|
+
----------
|
|
466
|
+
sat_ras : `np.ndarray`, (N,)
|
|
467
|
+
RA for each satellite (radians).
|
|
468
|
+
sat_decs : `np.ndarray`, (N,)
|
|
469
|
+
Decs for the satelltes (radians).
|
|
470
|
+
pointing_ra : `float`
|
|
471
|
+
RA of the pointing (radians).
|
|
472
|
+
pointing_dec : `float`
|
|
473
|
+
Dec of the pointing (radians).
|
|
474
|
+
radius : `float`
|
|
475
|
+
Radius of the field of view (radians).
|
|
476
|
+
|
|
477
|
+
Returns
|
|
478
|
+
-------
|
|
479
|
+
length : `float`
|
|
480
|
+
The length of the streak in radians
|
|
481
|
+
"""
|
|
482
|
+
# Hopefully this broadcasts properly
|
|
483
|
+
x, y = gnomonic_project_toxy(sat_ras, sat_decs, pointing_ra, pointing_dec)
|
|
484
|
+
ls = LineString(zip(x, y))
|
|
485
|
+
p = Point(0, 0)
|
|
486
|
+
circle_buffer = p.buffer(radius)
|
|
487
|
+
length = circle_buffer.intersection(ls).length
|
|
488
|
+
return length
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
__all__ = ("generate_catalog",)
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import warnings
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import numpy.lib.recfunctions as rfn
|
|
8
|
+
from scipy.spatial import cKDTree as kdtree # noqa: N813
|
|
9
|
+
|
|
10
|
+
from .offsets import OffsetSNR
|
|
11
|
+
from .star_tools import assign_patches, stars_project
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def wrap_ra(ra):
|
|
15
|
+
"""Wraps RA into 0-360 degrees."""
|
|
16
|
+
ra = ra % 360.0
|
|
17
|
+
return ra
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def cap_dec(dec):
|
|
21
|
+
"""Terminates declination at +/- 90 degrees."""
|
|
22
|
+
dec = np.where(dec > 90, 90, dec)
|
|
23
|
+
dec = np.where(dec < -90, -90, dec)
|
|
24
|
+
return dec
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def treexyz(ra, dec):
|
|
28
|
+
"""Calculate x/y/z values for ra/dec points, ra/dec in radians."""
|
|
29
|
+
# Note ra/dec can be arrays.
|
|
30
|
+
x = np.cos(dec) * np.cos(ra)
|
|
31
|
+
y = np.cos(dec) * np.sin(ra)
|
|
32
|
+
z = np.sin(dec)
|
|
33
|
+
return x, y, z
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def build_tree(ra, dec, leafsize=100):
|
|
37
|
+
"""Build KD tree on RA/dec and set radius (via setRad) for matching.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
ra : `nd.ndarray`, (N,)
|
|
42
|
+
RA values of the tree (in radians)
|
|
43
|
+
dec : `nd.ndarray`, (N,)
|
|
44
|
+
Dec values of the tree (in radians).
|
|
45
|
+
leafsize : `float`, opt
|
|
46
|
+
The number of RA/Dec pointings in each leafnode.
|
|
47
|
+
Default 100.
|
|
48
|
+
"""
|
|
49
|
+
if np.any(np.abs(ra) > np.pi * 2.0) or np.any(np.abs(dec) > np.pi * 2.0):
|
|
50
|
+
raise ValueError("Expecting RA and Dec values to be in radians.")
|
|
51
|
+
x, y, z = treexyz(ra, dec)
|
|
52
|
+
data = list(zip(x, y, z))
|
|
53
|
+
if np.size(data) > 0:
|
|
54
|
+
star_tree = kdtree(data, leafsize=leafsize)
|
|
55
|
+
else:
|
|
56
|
+
raise ValueError("ra and dec should have length greater than 0.")
|
|
57
|
+
return star_tree
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def generate_catalog(
|
|
61
|
+
visits,
|
|
62
|
+
stars_array,
|
|
63
|
+
offsets=None,
|
|
64
|
+
lsst_filter="r",
|
|
65
|
+
n_patches=16,
|
|
66
|
+
radius_fov=1.8,
|
|
67
|
+
seed=42,
|
|
68
|
+
uncert_floor=0.005,
|
|
69
|
+
verbose=True,
|
|
70
|
+
):
|
|
71
|
+
"""
|
|
72
|
+
Generate a catalog of observed stellar magnitudes.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
visits : `np.array`, (N,)
|
|
77
|
+
A numpy array with the properties of the visits.
|
|
78
|
+
Expected columns of fiveSigmaDepth, ra, dec, rotSkyPos (all degrees)
|
|
79
|
+
offsets : `list` of rubin_sim.selfcal.Offset classes
|
|
80
|
+
A list of instatiated classes that will apply offsets to the stars
|
|
81
|
+
lsst_filter : `str`
|
|
82
|
+
Which filter to use for the observed stars.
|
|
83
|
+
n_patches : `int`
|
|
84
|
+
Number of patches to divide the FoV into. Must be an integer squared
|
|
85
|
+
radius_fov : `float`
|
|
86
|
+
Radius of the telescope field of view in degrees
|
|
87
|
+
seed : `float`
|
|
88
|
+
Random number seed
|
|
89
|
+
uncert_floor : `float`
|
|
90
|
+
Value to add in quadrature to magnitude uncertainties (mags)
|
|
91
|
+
verbose : `bool`
|
|
92
|
+
Should we be verbose
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
if offsets is None:
|
|
96
|
+
# Maybe change this to just run with a default SNR offset
|
|
97
|
+
warnings.warn("Warning, no offsets set, returning without running")
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
# For computing what the 'expected' uncertainty on the observation will be
|
|
101
|
+
mag_uncert = OffsetSNR(lsst_filter=lsst_filter)
|
|
102
|
+
|
|
103
|
+
# set the radius for the kdtree
|
|
104
|
+
x0, y0, z0 = (1, 0, 0)
|
|
105
|
+
x1, y1, z1 = treexyz(np.radians(radius_fov), 0)
|
|
106
|
+
tree_radius = np.sqrt((x1 - x0) ** 2 + (y1 - y0) ** 2 + (z1 - z0) ** 2)
|
|
107
|
+
|
|
108
|
+
newcols = [
|
|
109
|
+
"x",
|
|
110
|
+
"y",
|
|
111
|
+
"radius",
|
|
112
|
+
"patch_id",
|
|
113
|
+
"sub_patch",
|
|
114
|
+
"observed_mag",
|
|
115
|
+
"mag_uncert",
|
|
116
|
+
]
|
|
117
|
+
newtypes = [float, float, float, int, int, float, float]
|
|
118
|
+
stars_new = np.zeros(stars_array.size, dtype=list(zip(newcols, newtypes)))
|
|
119
|
+
|
|
120
|
+
# only need to output these columns (saving rmag+ for now for convienence)
|
|
121
|
+
output_cols = [
|
|
122
|
+
"id",
|
|
123
|
+
"patch_id",
|
|
124
|
+
"observed_mag",
|
|
125
|
+
"mag_uncert",
|
|
126
|
+
"%smag" % lsst_filter,
|
|
127
|
+
"ra",
|
|
128
|
+
"decl",
|
|
129
|
+
]
|
|
130
|
+
output_dtypes = [int, int, float, float, float, float, float]
|
|
131
|
+
|
|
132
|
+
stars = rfn.merge_arrays([stars_array, stars_new], flatten=True)
|
|
133
|
+
|
|
134
|
+
# Build a KDTree for the stars
|
|
135
|
+
star_tree = build_tree(np.radians(stars["ra"]), np.radians(stars["decl"]))
|
|
136
|
+
|
|
137
|
+
# XXX--maybe update the way seeding is going on
|
|
138
|
+
np.random.seed(seed)
|
|
139
|
+
|
|
140
|
+
list_of_observed_arrays = []
|
|
141
|
+
|
|
142
|
+
n_visits = np.size(visits)
|
|
143
|
+
|
|
144
|
+
for i, visit in enumerate(visits):
|
|
145
|
+
dmags = {}
|
|
146
|
+
# Calc x,y, radius for each star, crop off stars outside the FoV
|
|
147
|
+
# could replace with code to see where each star falls and get chipID.
|
|
148
|
+
vx, vy, vz = treexyz(np.radians(visit["ra"]), np.radians(visit["dec"]))
|
|
149
|
+
indices = star_tree.query_ball_point((vx, vy, vz), tree_radius)
|
|
150
|
+
stars_in = stars[indices]
|
|
151
|
+
stars_in = stars_project(stars_in, visit)
|
|
152
|
+
|
|
153
|
+
# Assign patchIDs
|
|
154
|
+
stars_in = assign_patches(stars_in, visit, n_patches=n_patches)
|
|
155
|
+
|
|
156
|
+
# Apply the offsets that have been configured
|
|
157
|
+
for offset in offsets:
|
|
158
|
+
dmags[offset.newkey] = offset(stars_in, visit, dmags=dmags)
|
|
159
|
+
|
|
160
|
+
# Total up all the dmag's to make the observed magnitude
|
|
161
|
+
keys = list(dmags.keys())
|
|
162
|
+
obs_mag = stars_in["%smag" % lsst_filter].copy()
|
|
163
|
+
for key in keys:
|
|
164
|
+
obs_mag += dmags[key]
|
|
165
|
+
|
|
166
|
+
# Calculate the uncertainty in the observed mag:
|
|
167
|
+
mag_err = (
|
|
168
|
+
mag_uncert.calc_mag_errors(obs_mag, err_only=True, m5=visit["fiveSigmaDepth"]) ** 2
|
|
169
|
+
+ uncert_floor**2
|
|
170
|
+
) ** 0.5
|
|
171
|
+
|
|
172
|
+
# put values into the right columns
|
|
173
|
+
stars_in["observed_mag"] = obs_mag
|
|
174
|
+
stars_in["mag_uncert"] = mag_err
|
|
175
|
+
# Should shrink this down so we only return the needed columns
|
|
176
|
+
# observed_mag, mag_uncert, patchid, star_id
|
|
177
|
+
sub_cols = np.empty(stars_in.size, dtype=list(zip(output_cols, output_dtypes)))
|
|
178
|
+
for key in output_cols:
|
|
179
|
+
sub_cols[key] = stars_in[key]
|
|
180
|
+
list_of_observed_arrays.append(sub_cols)
|
|
181
|
+
if verbose:
|
|
182
|
+
progress = i / n_visits * 100
|
|
183
|
+
text = "\rprogress = %.2f%%" % progress
|
|
184
|
+
sys.stdout.write(text)
|
|
185
|
+
sys.stdout.flush()
|
|
186
|
+
|
|
187
|
+
result = np.concatenate(list_of_observed_arrays)
|
|
188
|
+
return result
|