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,1029 @@
|
|
|
1
|
+
"""Tools for maintaining the prototype archive of opsim output and metadata."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"make_sim_archive_dir",
|
|
5
|
+
"transfer_archive_dir",
|
|
6
|
+
"check_opsim_archive_resource",
|
|
7
|
+
"read_archived_sim_metadata",
|
|
8
|
+
"make_sim_archive_cli",
|
|
9
|
+
"compile_sim_metadata",
|
|
10
|
+
"read_sim_metadata_from_hdf",
|
|
11
|
+
"verify_compiled_sim_metadata",
|
|
12
|
+
"compile_sim_archive_metadata_cli",
|
|
13
|
+
"find_latest_prenight_sim_for_nights",
|
|
14
|
+
"fetch_sim_for_nights",
|
|
15
|
+
"fetch_obsloctap_visits",
|
|
16
|
+
"fetch_sim_stats_for_night",
|
|
17
|
+
"export_sim_to_prototype_sim_archive",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
import argparse
|
|
21
|
+
import datetime
|
|
22
|
+
import hashlib
|
|
23
|
+
import logging
|
|
24
|
+
import os
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from tempfile import TemporaryDirectory
|
|
27
|
+
from typing import Any
|
|
28
|
+
from urllib.parse import urlparse
|
|
29
|
+
from uuid import UUID
|
|
30
|
+
|
|
31
|
+
import numpy as np
|
|
32
|
+
import pandas as pd
|
|
33
|
+
import yaml
|
|
34
|
+
from astropy.time import Time
|
|
35
|
+
from lsst.resources import ResourcePath
|
|
36
|
+
from rubin_scheduler.scheduler.utils import SchemaConverter
|
|
37
|
+
from rubin_scheduler.site_models.almanac import Almanac
|
|
38
|
+
|
|
39
|
+
from rubin_sim import maf
|
|
40
|
+
from rubin_sim.maf.utils.opsim_utils import get_sim_data
|
|
41
|
+
from rubin_sim.sim_archive import vseqarchive
|
|
42
|
+
|
|
43
|
+
from .sim_archive import make_sim_data_dir as make_sim_archive_dir
|
|
44
|
+
|
|
45
|
+
LOGGER = logging.getLogger(__name__)
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
from lsst.resources import ResourcePath
|
|
49
|
+
except ModuleNotFoundError:
|
|
50
|
+
LOGGER.error("Module lsst.resources required to use rubin_sim.sim_archive.")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _next_sim_date_and_index(archive_base_uri: str) -> tuple:
|
|
54
|
+
insert_date = datetime.datetime.now(datetime.UTC).date().isoformat()
|
|
55
|
+
insert_date_rpath = ResourcePath(archive_base_uri).join(insert_date, forceDirectory=True)
|
|
56
|
+
if not insert_date_rpath.exists():
|
|
57
|
+
insert_date_rpath.mkdir()
|
|
58
|
+
LOGGER.debug(f"Created {insert_date_rpath}.")
|
|
59
|
+
|
|
60
|
+
# Number the sims in the insert date dir by
|
|
61
|
+
# looing for all the interger directories, and choosing the next one.
|
|
62
|
+
found_ids = []
|
|
63
|
+
for base_dir, found_dirs, found_files in insert_date_rpath.walk():
|
|
64
|
+
if base_dir == insert_date_rpath:
|
|
65
|
+
for found_dir in found_dirs:
|
|
66
|
+
try:
|
|
67
|
+
found_dir_index = found_dir[:-1] if found_dir.endswith("/") else found_dir
|
|
68
|
+
found_ids.append(int(found_dir_index))
|
|
69
|
+
except ValueError:
|
|
70
|
+
pass
|
|
71
|
+
|
|
72
|
+
new_id = max(found_ids) + 1 if len(found_ids) > 0 else 1
|
|
73
|
+
resource_rpath = insert_date_rpath.join(f"{new_id}", forceDirectory=True)
|
|
74
|
+
resource_rpath.mkdir()
|
|
75
|
+
LOGGER.debug(f"Created {resource_rpath}.")
|
|
76
|
+
return insert_date, new_id, resource_rpath
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def transfer_archive_dir(
|
|
80
|
+
archive_dir: str, archive_base_uri: str = "s3://rubin:rubin-scheduler-prenight/opsim/"
|
|
81
|
+
) -> ResourcePath:
|
|
82
|
+
"""Transfer the contents of an archive directory to an resource.
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
archive_dir : `str`
|
|
87
|
+
The path to the archive directory containing the files to be
|
|
88
|
+
transferred.
|
|
89
|
+
archive_base_uri : `str`, optional
|
|
90
|
+
The base URI where the archive files will be transferred to.
|
|
91
|
+
Default is "s3://rubin:rubin-scheduler-prenight/opsim/".
|
|
92
|
+
|
|
93
|
+
Returns
|
|
94
|
+
-------
|
|
95
|
+
resource_rpath : `ResourcePath`
|
|
96
|
+
The destination resource.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
LOGGER.debug(f"Beginning copy of {archive_dir} to {archive_base_uri}.")
|
|
100
|
+
metadata_fname = Path(archive_dir).joinpath("sim_metadata.yaml")
|
|
101
|
+
with open(metadata_fname, "r") as metadata_io:
|
|
102
|
+
sim_metadata = yaml.safe_load(metadata_io)
|
|
103
|
+
LOGGER.debug(f"Completed read of {archive_dir}.")
|
|
104
|
+
|
|
105
|
+
insert_date, new_id, resource_rpath = _next_sim_date_and_index(archive_base_uri)
|
|
106
|
+
|
|
107
|
+
# Include the metadata file itself.
|
|
108
|
+
sim_metadata["files"]["metadata"] = {"name": "sim_metadata.yaml"}
|
|
109
|
+
|
|
110
|
+
for file_info in sim_metadata["files"].values():
|
|
111
|
+
source_fname = Path(archive_dir).joinpath(file_info["name"])
|
|
112
|
+
with open(source_fname, "rb") as source_io:
|
|
113
|
+
content = source_io.read()
|
|
114
|
+
LOGGER.debug(f"Read {source_fname}.")
|
|
115
|
+
|
|
116
|
+
destination_rpath = resource_rpath.join(file_info["name"])
|
|
117
|
+
destination_rpath.write(content)
|
|
118
|
+
|
|
119
|
+
LOGGER.info(f"Copied {source_fname} to {destination_rpath}")
|
|
120
|
+
|
|
121
|
+
return resource_rpath
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def check_opsim_archive_resource(archive_uri: str) -> dict:
|
|
125
|
+
"""Check the contents of an opsim archive resource.
|
|
126
|
+
|
|
127
|
+
Parameters
|
|
128
|
+
----------
|
|
129
|
+
archive_uri : `str`
|
|
130
|
+
The URI of the archive resource to be checked.
|
|
131
|
+
|
|
132
|
+
Returns
|
|
133
|
+
-------
|
|
134
|
+
validity: `dict`
|
|
135
|
+
A dictionary of files checked, and their validity.
|
|
136
|
+
"""
|
|
137
|
+
LOGGER.debug(f"Starting to check file hashes in opsim sim archive {archive_uri}.")
|
|
138
|
+
metadata_path = ResourcePath(archive_uri).join("sim_metadata.yaml")
|
|
139
|
+
with metadata_path.open(mode="r") as metadata_io:
|
|
140
|
+
sim_metadata = yaml.safe_load(metadata_io)
|
|
141
|
+
LOGGER.debug(f"Read sim metadata from {metadata_path}.)")
|
|
142
|
+
|
|
143
|
+
results = {}
|
|
144
|
+
|
|
145
|
+
for file_info in sim_metadata["files"].values():
|
|
146
|
+
resource_path = ResourcePath(archive_uri).join(file_info["name"])
|
|
147
|
+
LOGGER.info(f"Reading {resource_path}.")
|
|
148
|
+
content = resource_path.read()
|
|
149
|
+
|
|
150
|
+
results[file_info["name"]] = file_info["md5"] == hashlib.md5(content).hexdigest()
|
|
151
|
+
if results[file_info["name"]]:
|
|
152
|
+
LOGGER.debug(f"{resource_path} checked and found to match recorded md5.")
|
|
153
|
+
else:
|
|
154
|
+
LOGGER.debug(f"{resource_path} has an md5 that differs from the recorded md5!")
|
|
155
|
+
|
|
156
|
+
return results
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _build_archived_sim_label(base_uri: str, metadata_resource: ResourcePath, metadata: dict) -> str:
|
|
160
|
+
label_base = metadata_resource.dirname().geturl().removeprefix(base_uri).rstrip("/").lstrip("/")
|
|
161
|
+
|
|
162
|
+
# If a label is supplied by the metadata, use it
|
|
163
|
+
if "label" in metadata:
|
|
164
|
+
label = f"{label_base} {metadata['label']}"
|
|
165
|
+
return label
|
|
166
|
+
|
|
167
|
+
try:
|
|
168
|
+
sim_dates = metadata["simulated_dates"]
|
|
169
|
+
first_date = sim_dates["first"]
|
|
170
|
+
last_date = sim_dates["last"]
|
|
171
|
+
label = f"{label_base} of {first_date}"
|
|
172
|
+
if last_date != first_date:
|
|
173
|
+
label = f"{label} through {last_date}"
|
|
174
|
+
except KeyError:
|
|
175
|
+
label = label_base
|
|
176
|
+
|
|
177
|
+
if "scheduler_version" in metadata:
|
|
178
|
+
label = f"{label} with {metadata['scheduler_version']}"
|
|
179
|
+
|
|
180
|
+
return label
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def read_archived_sim_metadata(
|
|
184
|
+
base_uri: str,
|
|
185
|
+
latest: str = None,
|
|
186
|
+
num_nights: int = 5,
|
|
187
|
+
compilation_resource: ResourcePath | str | None = None,
|
|
188
|
+
verify_compilation: bool = False,
|
|
189
|
+
) -> dict:
|
|
190
|
+
"""Read metadata for a time range of archived opsim output.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
base_uri : `str`
|
|
195
|
+
The base URI of the archive resource to be checked.
|
|
196
|
+
latest : `str`, optional
|
|
197
|
+
The date of the latest simulation whose metadata should be loaded.
|
|
198
|
+
This is the date on which the simulations was added to the archive,
|
|
199
|
+
not necessarily the date on which the simulation was run, or any
|
|
200
|
+
of the dates simulated.
|
|
201
|
+
Default is today.
|
|
202
|
+
num_nights : `int`
|
|
203
|
+
The number of nights of the date window to load.
|
|
204
|
+
compilation_resource : `ResourcePath` or `str` or `None`
|
|
205
|
+
The ResourcePath to an hdf5 compilation of metadata.
|
|
206
|
+
verify_compilation : `bool`
|
|
207
|
+
Verify that metadata in compilation corresponds to an existing
|
|
208
|
+
resource, and the all existing resources have metadata. Defaults
|
|
209
|
+
to False, which will work provided that the compilation is complete
|
|
210
|
+
and correct up to the date of the last simulation it includes.
|
|
211
|
+
|
|
212
|
+
Returns
|
|
213
|
+
-------
|
|
214
|
+
sim_metadata: `dict`
|
|
215
|
+
A dictionary of metadata for simulations in the date range.
|
|
216
|
+
"""
|
|
217
|
+
latest_mjd = int(Time.now().mjd if latest is None else Time(latest).mjd)
|
|
218
|
+
earliest_mjd = int(latest_mjd - (num_nights - 1))
|
|
219
|
+
LOGGER.debug(
|
|
220
|
+
f"Looking for simulation metadata with MJD between {earliest_mjd} and {latest_mjd} in {base_uri}."
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
compilation = {}
|
|
224
|
+
compiled_uris_by_date = {}
|
|
225
|
+
max_compiled_date = "1900-01-01"
|
|
226
|
+
if compilation_resource is not None:
|
|
227
|
+
LOGGER.debug(f"Reading metadata cache {compilation_resource}.")
|
|
228
|
+
try:
|
|
229
|
+
compilation.update(read_sim_metadata_from_hdf(compilation_resource))
|
|
230
|
+
for uri in compilation:
|
|
231
|
+
iso_date = Path(urlparse(uri).path).parts[-2]
|
|
232
|
+
if iso_date not in compiled_uris_by_date:
|
|
233
|
+
compiled_uris_by_date[iso_date] = []
|
|
234
|
+
compiled_uris_by_date[iso_date].append(uri)
|
|
235
|
+
max_compiled_date = max(max_compiled_date, iso_date)
|
|
236
|
+
LOGGER.debug(f"Maximum simulation execution date in metadata cache: {max_compiled_date}")
|
|
237
|
+
except FileNotFoundError:
|
|
238
|
+
LOGGER.warning(f"No metadata cache {compilation_resource}, not using cache.")
|
|
239
|
+
pass
|
|
240
|
+
|
|
241
|
+
all_metadata = {}
|
|
242
|
+
for mjd in range(earliest_mjd, latest_mjd + 1):
|
|
243
|
+
iso_date = Time(mjd, format="mjd").iso[:10]
|
|
244
|
+
|
|
245
|
+
# Make the comparison >=, rather than >, so
|
|
246
|
+
# it won't miss sims in which the compilation does not complete
|
|
247
|
+
# the final date.
|
|
248
|
+
if verify_compilation or (iso_date >= max_compiled_date):
|
|
249
|
+
date_resource = ResourcePath(base_uri).join(iso_date, forceDirectory=True)
|
|
250
|
+
if date_resource.exists():
|
|
251
|
+
for base_dir, found_dirs, found_files in date_resource.walk(
|
|
252
|
+
file_filter=r".*sim_metadata.yaml"
|
|
253
|
+
):
|
|
254
|
+
for found_file in found_files:
|
|
255
|
+
found_resource = ResourcePath(base_dir).join(found_file)
|
|
256
|
+
LOGGER.debug(f"Found {found_resource}")
|
|
257
|
+
sim_uri = str(found_resource.dirname())
|
|
258
|
+
if sim_uri in compilation:
|
|
259
|
+
LOGGER.debug(f"Not reading {found_resource}, already in the read compliation.")
|
|
260
|
+
these_metadata = compilation[sim_uri]
|
|
261
|
+
else:
|
|
262
|
+
LOGGER.debug(f"Reading {found_resource} (absent from compilation).")
|
|
263
|
+
these_metadata = yaml.safe_load(found_resource.read().decode("utf-8"))
|
|
264
|
+
these_metadata["label"] = _build_archived_sim_label(
|
|
265
|
+
base_uri, found_resource, these_metadata
|
|
266
|
+
)
|
|
267
|
+
LOGGER.debug(f"Read successfully: {found_resource}")
|
|
268
|
+
if iso_date < max_compiled_date:
|
|
269
|
+
LOGGER.error(
|
|
270
|
+
f"Simulation at {sim_uri} expected but not found in compilation."
|
|
271
|
+
)
|
|
272
|
+
all_metadata[sim_uri] = these_metadata
|
|
273
|
+
else:
|
|
274
|
+
LOGGER.debug(f"No simulations found with generation date of {iso_date}")
|
|
275
|
+
else:
|
|
276
|
+
if iso_date in compiled_uris_by_date:
|
|
277
|
+
for sim_uri in compiled_uris_by_date[iso_date]:
|
|
278
|
+
all_metadata[sim_uri] = compilation[sim_uri]
|
|
279
|
+
|
|
280
|
+
if verify_compilation:
|
|
281
|
+
if iso_date in compiled_uris_by_date:
|
|
282
|
+
for sim_uri in compiled_uris_by_date[iso_date]:
|
|
283
|
+
if sim_uri not in all_metadata:
|
|
284
|
+
message = f"Simulation at {sim_uri} in compiled metadata but not archive."
|
|
285
|
+
print(message)
|
|
286
|
+
LOGGER.error(message)
|
|
287
|
+
else:
|
|
288
|
+
LOGGER.debug(
|
|
289
|
+
f"Date {iso_date} not expected to be in the metadata compilation, not checking for it."
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
if len(all_metadata) == 0:
|
|
293
|
+
earliest_iso = Time(earliest_mjd, format="mjd").iso[:10]
|
|
294
|
+
latest_iso = Time(latest_mjd, format="mjd").iso[:10]
|
|
295
|
+
LOGGER.info(f"No simulations run between {earliest_iso} through {latest_iso} found in {base_uri}")
|
|
296
|
+
|
|
297
|
+
return all_metadata
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def make_sim_archive_cli(*args: Any) -> str:
|
|
301
|
+
parser = argparse.ArgumentParser(description="Add files to sim archive")
|
|
302
|
+
parser.add_argument(
|
|
303
|
+
"label",
|
|
304
|
+
type=str,
|
|
305
|
+
help="A label for the simulation.",
|
|
306
|
+
)
|
|
307
|
+
parser.add_argument(
|
|
308
|
+
"opsim",
|
|
309
|
+
type=str,
|
|
310
|
+
help="File name of opsim database.",
|
|
311
|
+
)
|
|
312
|
+
parser.add_argument("--rewards", type=str, default=None, help="A rewards HDF5 file.")
|
|
313
|
+
parser.add_argument(
|
|
314
|
+
"--scheduler_version",
|
|
315
|
+
type=str,
|
|
316
|
+
default=None,
|
|
317
|
+
help="The version of the scheduler that producte the opsim database.",
|
|
318
|
+
)
|
|
319
|
+
parser.add_argument(
|
|
320
|
+
"--scheduler",
|
|
321
|
+
type=str,
|
|
322
|
+
default=None,
|
|
323
|
+
help="A snapshot of the scheduler used to produce the database, at the start of the simulation.",
|
|
324
|
+
)
|
|
325
|
+
parser.add_argument(
|
|
326
|
+
"--script", type=str, default=None, help="The file name of the script run to create the simulation."
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
notebook_help = "The file name of the notebook run to create the simulation."
|
|
330
|
+
notebook_help = notebook_help + " This can be produced using the %%notebook magic."
|
|
331
|
+
parser.add_argument(
|
|
332
|
+
"--notebook",
|
|
333
|
+
type=str,
|
|
334
|
+
default=None,
|
|
335
|
+
help=notebook_help,
|
|
336
|
+
)
|
|
337
|
+
parser.add_argument(
|
|
338
|
+
"--archive_base_uri",
|
|
339
|
+
type=str,
|
|
340
|
+
default="s3://rubin:rubin-scheduler-prenight/opsim/",
|
|
341
|
+
help="Base URI for the archive",
|
|
342
|
+
)
|
|
343
|
+
parser.add_argument("--tags", type=str, default=[], nargs="*", help="The tags on the simulation.")
|
|
344
|
+
parser.add_argument("--telescope", type=str, default="simonyi", help="The telescope simulated.")
|
|
345
|
+
arg_values = parser.parse_args() if len(args) == 0 else parser.parse_args(args)
|
|
346
|
+
|
|
347
|
+
observations = SchemaConverter().opsim2obs(arg_values.opsim)
|
|
348
|
+
|
|
349
|
+
if arg_values.rewards is not None:
|
|
350
|
+
try:
|
|
351
|
+
reward_df = pd.read_hdf(arg_values.rewards, "reward_df")
|
|
352
|
+
except KeyError:
|
|
353
|
+
reward_df = None
|
|
354
|
+
|
|
355
|
+
try:
|
|
356
|
+
obs_rewards = pd.read_hdf(arg_values.rewards, "obs_rewards")
|
|
357
|
+
except KeyError:
|
|
358
|
+
obs_rewards = None
|
|
359
|
+
else:
|
|
360
|
+
reward_df = None
|
|
361
|
+
obs_rewards = None
|
|
362
|
+
|
|
363
|
+
filename_args = ["scheduler", "script", "notebook"]
|
|
364
|
+
in_files = {}
|
|
365
|
+
for filename_arg in filename_args:
|
|
366
|
+
try:
|
|
367
|
+
filename = getattr(arg_values, filename_arg)
|
|
368
|
+
if filename is not None:
|
|
369
|
+
in_files[filename] = filename
|
|
370
|
+
except AttributeError:
|
|
371
|
+
pass
|
|
372
|
+
|
|
373
|
+
data_path = make_sim_archive_dir(
|
|
374
|
+
observations,
|
|
375
|
+
reward_df,
|
|
376
|
+
obs_rewards,
|
|
377
|
+
in_files,
|
|
378
|
+
tags=arg_values.tags,
|
|
379
|
+
label=arg_values.label,
|
|
380
|
+
opsim_metadata={"telescope": arg_values.telescope},
|
|
381
|
+
)
|
|
382
|
+
LOGGER.info(f"Created simulation archived directory: {data_path.name}")
|
|
383
|
+
|
|
384
|
+
sim_archive_uri = transfer_archive_dir(data_path.name, arg_values.archive_base_uri)
|
|
385
|
+
LOGGER.info(f"Transferred {data_path} to {sim_archive_uri}")
|
|
386
|
+
|
|
387
|
+
return sim_archive_uri
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def compile_sim_metadata(
|
|
391
|
+
archive_uri: str, compilation_resource: str | ResourcePath, num_nights: int = 10000, append: bool = False
|
|
392
|
+
) -> ResourcePath:
|
|
393
|
+
"""Read sim archive metadata and export it to tables in an hdf5 files.
|
|
394
|
+
|
|
395
|
+
Parameters
|
|
396
|
+
----------
|
|
397
|
+
archive_uri : `str`
|
|
398
|
+
URI of the sim archive from which to read metadata.
|
|
399
|
+
compilation_resource : `str` or `ResourcePath`
|
|
400
|
+
Resource for hdf5 file to be written to
|
|
401
|
+
num_nights : `int`, optional
|
|
402
|
+
Number of nights to include, by default 10000
|
|
403
|
+
append : `bool`, optional
|
|
404
|
+
Do not rebuild the whole compilation, but instead reread what is
|
|
405
|
+
there already, and add new metadata after the last date already
|
|
406
|
+
include. Defaults to False.
|
|
407
|
+
|
|
408
|
+
Returns
|
|
409
|
+
-------
|
|
410
|
+
compilation_fname : `ResourcePath`
|
|
411
|
+
The resource to which the hdf5 file was written.
|
|
412
|
+
"""
|
|
413
|
+
LOGGER.debug("Starting compile_sim_metadata.")
|
|
414
|
+
|
|
415
|
+
if append:
|
|
416
|
+
sim_metadata = read_archived_sim_metadata(
|
|
417
|
+
archive_uri, num_nights=num_nights, compilation_resource=compilation_resource
|
|
418
|
+
)
|
|
419
|
+
else:
|
|
420
|
+
sim_metadata = read_archived_sim_metadata(archive_uri, num_nights=num_nights)
|
|
421
|
+
|
|
422
|
+
sim_rows = []
|
|
423
|
+
file_rows = []
|
|
424
|
+
sim_runner_kwargs = []
|
|
425
|
+
tags = []
|
|
426
|
+
for uri, metadata in list(sim_metadata.items()):
|
|
427
|
+
sim_row = {"sim_uri": uri}
|
|
428
|
+
for key, value in metadata.items():
|
|
429
|
+
match key:
|
|
430
|
+
case "files":
|
|
431
|
+
for file_type, file_metadata in value.items():
|
|
432
|
+
this_file = {"sim_uri": uri, "file_type": file_type} | file_metadata
|
|
433
|
+
file_rows.append(this_file)
|
|
434
|
+
case "sim_runner_kwargs":
|
|
435
|
+
these_args = {"sim_uri": uri} | value
|
|
436
|
+
sim_runner_kwargs.append(these_args)
|
|
437
|
+
case "tags":
|
|
438
|
+
for tag in value:
|
|
439
|
+
tags.append({"sim_uri": uri, "tag": tag})
|
|
440
|
+
case "simulated_dates":
|
|
441
|
+
sim_row["first_simulated_date"] = value["start"] if "start" in value else value["first"]
|
|
442
|
+
sim_row["last_simulated_date"] = value["end"] if "end" in value else value["last"]
|
|
443
|
+
case _:
|
|
444
|
+
sim_row[key] = value
|
|
445
|
+
sim_rows.append(sim_row)
|
|
446
|
+
|
|
447
|
+
sim_df = pd.DataFrame(sim_rows).set_index("sim_uri")
|
|
448
|
+
file_df = pd.DataFrame(file_rows).set_index("sim_uri")
|
|
449
|
+
sim_runner_kwargs_df = pd.DataFrame(sim_runner_kwargs).set_index("sim_uri")
|
|
450
|
+
tags_df = pd.DataFrame(tags).set_index("sim_uri")
|
|
451
|
+
|
|
452
|
+
with TemporaryDirectory() as local_data_dir:
|
|
453
|
+
local_hdf_fname = Path(local_data_dir).joinpath("scheduler.pickle.xz")
|
|
454
|
+
sim_df.to_hdf(local_hdf_fname, key="simulations", format="table")
|
|
455
|
+
file_df.to_hdf(local_hdf_fname, key="files", format="table")
|
|
456
|
+
sim_runner_kwargs_df.to_hdf(local_hdf_fname, key="kwargs", format="table")
|
|
457
|
+
tags_df.to_hdf(local_hdf_fname, key="tags", format="table")
|
|
458
|
+
|
|
459
|
+
with open(local_hdf_fname, "rb") as local_hdf_io:
|
|
460
|
+
hdf_bytes = local_hdf_io.read()
|
|
461
|
+
|
|
462
|
+
if isinstance(compilation_resource, str):
|
|
463
|
+
compilation_resource = ResourcePath(compilation_resource)
|
|
464
|
+
|
|
465
|
+
assert isinstance(compilation_resource, ResourcePath)
|
|
466
|
+
LOGGER.info(f"Writing metadata compilation to {compilation_resource}")
|
|
467
|
+
compilation_resource.write(hdf_bytes)
|
|
468
|
+
|
|
469
|
+
return compilation_resource
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def read_sim_metadata_from_hdf(compilation_resource: str | ResourcePath) -> dict:
|
|
473
|
+
"""Read sim archive metadata from an hdf5 file.
|
|
474
|
+
Return a dict as if it were generated by read_archived_sim_metadata.
|
|
475
|
+
|
|
476
|
+
Parameters
|
|
477
|
+
----------
|
|
478
|
+
compilation_fname : `str` or `ResourcePath`
|
|
479
|
+
The source of the hdf5 data to read.
|
|
480
|
+
|
|
481
|
+
Returns
|
|
482
|
+
-------
|
|
483
|
+
sim_archive_metadata: `dict`
|
|
484
|
+
A nested dictionary with the simulation metadata.
|
|
485
|
+
"""
|
|
486
|
+
|
|
487
|
+
if isinstance(compilation_resource, str):
|
|
488
|
+
compilation_resource = ResourcePath(compilation_resource)
|
|
489
|
+
assert isinstance(compilation_resource, ResourcePath)
|
|
490
|
+
|
|
491
|
+
with compilation_resource.as_local() as local_compilation_resource:
|
|
492
|
+
compilation_fname: str = local_compilation_resource.ospath
|
|
493
|
+
LOGGER.debug(f"{compilation_resource} copied to {compilation_fname}.")
|
|
494
|
+
sim_df = pd.read_hdf(compilation_fname, "simulations")
|
|
495
|
+
file_df = pd.read_hdf(compilation_fname, "files")
|
|
496
|
+
sim_runner_kwargs_df = pd.read_hdf(compilation_fname, "kwargs")
|
|
497
|
+
tags_df = pd.read_hdf(compilation_fname, "tags")
|
|
498
|
+
|
|
499
|
+
def make_file_dict(g: pd.Series | pd.DataFrame) -> dict:
|
|
500
|
+
if isinstance(g, pd.Series):
|
|
501
|
+
g = g.to_frame().T
|
|
502
|
+
|
|
503
|
+
return g.set_index("file_type").T.to_dict()
|
|
504
|
+
|
|
505
|
+
sim_df["files"] = file_df.groupby("sim_uri").apply(make_file_dict)
|
|
506
|
+
|
|
507
|
+
def make_kwargs_dict(g):
|
|
508
|
+
sim_kwargs = g.to_dict(orient="records")[0]
|
|
509
|
+
# Keyword args that are not set get recorded as nans.
|
|
510
|
+
# Do not include them in the dictionary.
|
|
511
|
+
nan_keys = []
|
|
512
|
+
for key in sim_kwargs:
|
|
513
|
+
try:
|
|
514
|
+
if np.isnan(sim_kwargs[key]):
|
|
515
|
+
nan_keys.append(key)
|
|
516
|
+
except TypeError:
|
|
517
|
+
pass
|
|
518
|
+
|
|
519
|
+
for key in nan_keys:
|
|
520
|
+
del sim_kwargs[key]
|
|
521
|
+
|
|
522
|
+
return sim_kwargs
|
|
523
|
+
|
|
524
|
+
sim_df["sim_runner_kwargs"] = sim_runner_kwargs_df.groupby("sim_uri").apply(make_kwargs_dict)
|
|
525
|
+
|
|
526
|
+
sim_df["tags"] = tags_df.groupby("sim_uri").agg({"tag": list})
|
|
527
|
+
|
|
528
|
+
sim_metadata = sim_df.to_dict(orient="index")
|
|
529
|
+
|
|
530
|
+
for sim_uri in sim_metadata:
|
|
531
|
+
# Return begin and end date to their nested dict format.
|
|
532
|
+
sim_metadata[sim_uri]["simulated_dates"] = {
|
|
533
|
+
"first": sim_metadata[sim_uri]["first_simulated_date"],
|
|
534
|
+
"last": sim_metadata[sim_uri]["last_simulated_date"],
|
|
535
|
+
}
|
|
536
|
+
del sim_metadata[sim_uri]["first_simulated_date"]
|
|
537
|
+
del sim_metadata[sim_uri]["last_simulated_date"]
|
|
538
|
+
|
|
539
|
+
# Unset keys show up as nans.
|
|
540
|
+
# Do not put them in the resultant dict.
|
|
541
|
+
nan_keys = []
|
|
542
|
+
for key in sim_metadata[sim_uri]:
|
|
543
|
+
try:
|
|
544
|
+
if np.isnan(sim_metadata[sim_uri][key]):
|
|
545
|
+
nan_keys.append(key)
|
|
546
|
+
except TypeError:
|
|
547
|
+
pass
|
|
548
|
+
|
|
549
|
+
for key in nan_keys:
|
|
550
|
+
del sim_metadata[sim_uri][key]
|
|
551
|
+
|
|
552
|
+
return sim_metadata
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
def verify_compiled_sim_metadata(
|
|
556
|
+
archive_uri: str, compilation_resource: str | ResourcePath, num_nights: int = 10000
|
|
557
|
+
) -> list[dict]:
|
|
558
|
+
"""Verify that a compilation of sim archive metadata matches directaly
|
|
559
|
+
read metadata.
|
|
560
|
+
|
|
561
|
+
Parameters
|
|
562
|
+
----------
|
|
563
|
+
archive_uri : `str`
|
|
564
|
+
Archive from which to directly read metadata.
|
|
565
|
+
compilation_resource : `str` or `ResourcePath`
|
|
566
|
+
Resource for the metadata compilation
|
|
567
|
+
num_nights : `int`, optional
|
|
568
|
+
number of nights to check, by default 10000
|
|
569
|
+
|
|
570
|
+
Returns
|
|
571
|
+
-------
|
|
572
|
+
differences : `list[dict]`
|
|
573
|
+
A list of dicts describing differences. If they match, it will return
|
|
574
|
+
and empty list.
|
|
575
|
+
"""
|
|
576
|
+
|
|
577
|
+
direct_sim_metadata = read_archived_sim_metadata(archive_uri, num_nights=num_nights)
|
|
578
|
+
|
|
579
|
+
try:
|
|
580
|
+
# One old sim uses a couple of non-standard keywords, so update them.
|
|
581
|
+
simulated_dates = direct_sim_metadata["s3://rubin:rubin-scheduler-prenight/opsim/2023-12-15/1/"][
|
|
582
|
+
"simulated_dates"
|
|
583
|
+
]
|
|
584
|
+
simulated_dates["first"] = simulated_dates["start"]
|
|
585
|
+
del simulated_dates["start"]
|
|
586
|
+
simulated_dates["last"] = simulated_dates["end"]
|
|
587
|
+
del simulated_dates["end"]
|
|
588
|
+
except KeyError:
|
|
589
|
+
# If the archive doesn't have this old sim, don't worry about it.
|
|
590
|
+
pass
|
|
591
|
+
|
|
592
|
+
compiled_sim_metadata = read_sim_metadata_from_hdf(compilation_resource)
|
|
593
|
+
|
|
594
|
+
# Test that everything in direct_sim_metadata has a corresponding matching
|
|
595
|
+
# entry in the compilation.
|
|
596
|
+
differences = []
|
|
597
|
+
for sim_uri in direct_sim_metadata:
|
|
598
|
+
for key in direct_sim_metadata[sim_uri]:
|
|
599
|
+
try:
|
|
600
|
+
if direct_sim_metadata[sim_uri][key] != compiled_sim_metadata[sim_uri][key]:
|
|
601
|
+
differences.append(
|
|
602
|
+
{
|
|
603
|
+
"sim_uri": sim_uri,
|
|
604
|
+
"key": key,
|
|
605
|
+
"direct_value": direct_sim_metadata[sim_uri][key],
|
|
606
|
+
"compiled_value": compiled_sim_metadata[sim_uri][key],
|
|
607
|
+
}
|
|
608
|
+
)
|
|
609
|
+
except KeyError:
|
|
610
|
+
differences.append(
|
|
611
|
+
{
|
|
612
|
+
"sim_uri": sim_uri,
|
|
613
|
+
"key": key,
|
|
614
|
+
"direct_value": direct_sim_metadata[sim_uri][key],
|
|
615
|
+
"compiled_value": "MISSING",
|
|
616
|
+
}
|
|
617
|
+
)
|
|
618
|
+
|
|
619
|
+
# Test that everything in the compilation has a corresponding matching
|
|
620
|
+
# entry in direct_sim_metadata.
|
|
621
|
+
for sim_uri in compiled_sim_metadata:
|
|
622
|
+
for key in compiled_sim_metadata[sim_uri]:
|
|
623
|
+
if sim_uri not in direct_sim_metadata:
|
|
624
|
+
differences.append(
|
|
625
|
+
{
|
|
626
|
+
"sim_uri": sim_uri,
|
|
627
|
+
"direct_value": "MISSING",
|
|
628
|
+
}
|
|
629
|
+
)
|
|
630
|
+
elif key not in direct_sim_metadata[sim_uri]:
|
|
631
|
+
differences.append(
|
|
632
|
+
{
|
|
633
|
+
"sim_uri": sim_uri,
|
|
634
|
+
"key": key,
|
|
635
|
+
"direct_value": "MISSING",
|
|
636
|
+
"compiled_value": compiled_sim_metadata[sim_uri][key],
|
|
637
|
+
}
|
|
638
|
+
)
|
|
639
|
+
|
|
640
|
+
return differences
|
|
641
|
+
|
|
642
|
+
|
|
643
|
+
def compile_sim_archive_metadata_cli(*args: Any) -> None:
|
|
644
|
+
parser = argparse.ArgumentParser(description="Create a metadata compilation HDF5 file at a URI")
|
|
645
|
+
parser.add_argument(
|
|
646
|
+
"--compilation_uri",
|
|
647
|
+
type=str,
|
|
648
|
+
default=None,
|
|
649
|
+
help="The URI of the metadata archive compilation to write. "
|
|
650
|
+
+ "Defaults to compilation_metadate.h5 in the archive.",
|
|
651
|
+
)
|
|
652
|
+
parser.add_argument(
|
|
653
|
+
"--archive_base_uri",
|
|
654
|
+
type=str,
|
|
655
|
+
default="s3://rubin:rubin-scheduler-prenight/opsim/",
|
|
656
|
+
help="Base URI for the archive",
|
|
657
|
+
)
|
|
658
|
+
parser.add_argument(
|
|
659
|
+
"--append",
|
|
660
|
+
action="store_true",
|
|
661
|
+
help="Do not rebuild the whole compilation, "
|
|
662
|
+
+ "but add new simulations with dates after the last current entry.",
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
log_file = os.environ.get("SIM_ARCHIVE_LOG_FILE", None)
|
|
666
|
+
if log_file is not None:
|
|
667
|
+
logging.basicConfig(
|
|
668
|
+
filename=log_file, format="%(asctime)s: %(message)s", datefmt="%Y-%m-%dT%H:%M:%S%z"
|
|
669
|
+
)
|
|
670
|
+
else:
|
|
671
|
+
logging.basicConfig(level=logging.INFO)
|
|
672
|
+
|
|
673
|
+
arg_values = parser.parse_args() if len(args) == 0 else parser.parse_args(args)
|
|
674
|
+
archive_uri = arg_values.archive_base_uri
|
|
675
|
+
compilation_uri = arg_values.compilation_uri
|
|
676
|
+
append = arg_values.append
|
|
677
|
+
if compilation_uri is None:
|
|
678
|
+
compilation_resource = ResourcePath(archive_uri).join("compiled_metadata_cache.h5")
|
|
679
|
+
else:
|
|
680
|
+
compilation_resource = ResourcePath(compilation_uri)
|
|
681
|
+
|
|
682
|
+
compilation_resource = compile_sim_metadata(archive_uri, compilation_resource, append=append)
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def find_latest_prenight_sim_for_nights(
|
|
686
|
+
first_day_obs: str | None = None,
|
|
687
|
+
last_day_obs: str | None = None,
|
|
688
|
+
tags: tuple[str] = ("ideal", "nominal"),
|
|
689
|
+
telescope: str = "simonyi",
|
|
690
|
+
max_simulation_age: int = 2,
|
|
691
|
+
archive_uri: str = "s3://rubin:rubin-scheduler-prenight/opsim/",
|
|
692
|
+
compilation_uri: str = "s3://rubin:rubin-scheduler-prenight/opsim/compiled_metadata_cache.h5",
|
|
693
|
+
) -> pd.DataFrame:
|
|
694
|
+
"""Find the most recent prenight simulation that covers a night.
|
|
695
|
+
|
|
696
|
+
Parameters
|
|
697
|
+
----------
|
|
698
|
+
first_day_obs : `str` or `None`
|
|
699
|
+
The date of the evening for the first night for which to get
|
|
700
|
+
a simulation. If `None`, then the current date will be used.
|
|
701
|
+
last_day_obs : `str` or `None`
|
|
702
|
+
The date of the evening for the last night for which to get
|
|
703
|
+
a simulation. If `None`, then the current date will be used.
|
|
704
|
+
tags : `tuple[str]`
|
|
705
|
+
A tuple of tags to filter simulations by.
|
|
706
|
+
Defaults to ``('ideal', 'nominal')``.
|
|
707
|
+
telescope : `str`
|
|
708
|
+
The telescope to search for (simonyi or auxtel).
|
|
709
|
+
Defaults to simonyi.
|
|
710
|
+
max_simulation_age : `int`
|
|
711
|
+
The maximum age of simulations to consider, in days.
|
|
712
|
+
Simulations older than ``max_simulation_age`` will not be considered.
|
|
713
|
+
Defaults to 2.
|
|
714
|
+
archive_uri : `str`
|
|
715
|
+
The URI of the archive from which to fetch the simulation.
|
|
716
|
+
Defaults to ``s3://rubin:rubin-scheduler-prenight/opsim/``.
|
|
717
|
+
compilation_uri : `str`
|
|
718
|
+
The URI of the compiled metadata HDF5 file for efficient querying.
|
|
719
|
+
Defaults to
|
|
720
|
+
``s3://rubin:rubin-scheduler-prenight/opsim/compiled_metadata_cache.h5``.
|
|
721
|
+
|
|
722
|
+
Returns
|
|
723
|
+
-------
|
|
724
|
+
sim_metadata : `dict`
|
|
725
|
+
A dictionary with metadata for the simulation.
|
|
726
|
+
"""
|
|
727
|
+
|
|
728
|
+
if first_day_obs is None:
|
|
729
|
+
first_day_obs = Time(Time.now().mjd - 0.5, format="mjd").iso[:10]
|
|
730
|
+
if last_day_obs is None:
|
|
731
|
+
last_day_obs = first_day_obs
|
|
732
|
+
|
|
733
|
+
sim_metadata = read_archived_sim_metadata(
|
|
734
|
+
archive_uri, num_nights=max_simulation_age, compilation_resource=compilation_uri
|
|
735
|
+
)
|
|
736
|
+
LOGGER.debug(f"Total simulations it the last {max_simulation_age} days: {len(sim_metadata)}.")
|
|
737
|
+
|
|
738
|
+
best_sim = None
|
|
739
|
+
for uri, sim in sim_metadata.items():
|
|
740
|
+
sim["uri"] = uri
|
|
741
|
+
sim["exec_date"] = uri.split("/")[-3]
|
|
742
|
+
sim["date_index"] = int(uri.split("/")[-2])
|
|
743
|
+
|
|
744
|
+
if sim["simulated_dates"]["first"] > first_day_obs:
|
|
745
|
+
continue
|
|
746
|
+
if sim["simulated_dates"]["last"] < last_day_obs:
|
|
747
|
+
continue
|
|
748
|
+
if "telescope" in sim and sim["telescope"].lower() != telescope.lower():
|
|
749
|
+
continue
|
|
750
|
+
if not set(tags).issubset(sim["tags"]):
|
|
751
|
+
continue
|
|
752
|
+
if best_sim is not None:
|
|
753
|
+
if sim["exec_date"] < best_sim["exec_date"]:
|
|
754
|
+
continue
|
|
755
|
+
if sim["date_index"] < best_sim["date_index"]:
|
|
756
|
+
continue
|
|
757
|
+
best_sim = sim
|
|
758
|
+
|
|
759
|
+
if best_sim is not None:
|
|
760
|
+
best_sim["opsim_rp"] = (
|
|
761
|
+
ResourcePath(archive_uri)
|
|
762
|
+
.join(best_sim["exec_date"], forceDirectory=True)
|
|
763
|
+
.join(f"{best_sim['date_index']}", forceDirectory=True)
|
|
764
|
+
.join(best_sim["files"]["observations"]["name"])
|
|
765
|
+
)
|
|
766
|
+
LOGGER.info(f"Most recent simulation meeting requested criteria is {best_sim['uri']}.")
|
|
767
|
+
else:
|
|
768
|
+
LOGGER.debug("No simulations met the requested criteria.")
|
|
769
|
+
|
|
770
|
+
return best_sim
|
|
771
|
+
|
|
772
|
+
|
|
773
|
+
def fetch_sim_for_nights(
|
|
774
|
+
first_day_obs: str | None = None,
|
|
775
|
+
last_day_obs: str | None = None,
|
|
776
|
+
which_sim: ResourcePath | str | dict | None = None,
|
|
777
|
+
get_sim_data_kwargs: dict | None = None,
|
|
778
|
+
) -> pd.DataFrame | None:
|
|
779
|
+
"""Fetches visit metadata from an opsim database for specified nights.
|
|
780
|
+
|
|
781
|
+
Parameters
|
|
782
|
+
----------
|
|
783
|
+
first_day_obs : `str` or `None`
|
|
784
|
+
The date of the evening for the first night for which to get
|
|
785
|
+
a simulation. If `None`, then the current date will be used.
|
|
786
|
+
last_day_obs : `str` or `None`
|
|
787
|
+
The date of the evening for the last night for which to get
|
|
788
|
+
a simulation. If `None`, then the current date will be used.
|
|
789
|
+
which_sim : `ResourcePath` or `str` or `dict` on `None`
|
|
790
|
+
The ``resourcePath`` or URL of the opsim file from which to
|
|
791
|
+
load visits, or the arguments to
|
|
792
|
+
``find_latest_prenight_sim_for_nights``
|
|
793
|
+
to use to determine which simulation to load. ``None`` uses
|
|
794
|
+
default arguments to ``find_latest_prenight_sim_for_nights``.
|
|
795
|
+
Defaults to ``None``.
|
|
796
|
+
git_sim_data_kwargs : `dict`
|
|
797
|
+
Additional arguments to ``get_sim_data`` to use to load
|
|
798
|
+
the visits.
|
|
799
|
+
|
|
800
|
+
Returns
|
|
801
|
+
-------
|
|
802
|
+
visits : `pd.DataFrame`
|
|
803
|
+
A pandas DataFrame containing visit parameters.
|
|
804
|
+
"""
|
|
805
|
+
|
|
806
|
+
if first_day_obs is None:
|
|
807
|
+
first_day_obs = Time(Time.now().mjd - 0.5, format="mjd").iso[:10]
|
|
808
|
+
if last_day_obs is None:
|
|
809
|
+
last_day_obs = first_day_obs
|
|
810
|
+
|
|
811
|
+
match which_sim:
|
|
812
|
+
case ResourcePath():
|
|
813
|
+
opsim_rp = which_sim
|
|
814
|
+
case str():
|
|
815
|
+
opsim_rp = ResourcePath(which_sim)
|
|
816
|
+
case dict():
|
|
817
|
+
opsim_rp = find_latest_prenight_sim_for_nights(first_day_obs, last_day_obs, **which_sim)[
|
|
818
|
+
"opsim_rp"
|
|
819
|
+
]
|
|
820
|
+
case None:
|
|
821
|
+
opsim_rp = find_latest_prenight_sim_for_nights(first_day_obs, last_day_obs)["opsim_rp"]
|
|
822
|
+
case _:
|
|
823
|
+
raise NotImplementedError()
|
|
824
|
+
|
|
825
|
+
assert isinstance(opsim_rp, ResourcePath)
|
|
826
|
+
|
|
827
|
+
if get_sim_data_kwargs is None:
|
|
828
|
+
get_sim_data_kwargs = {}
|
|
829
|
+
assert isinstance(get_sim_data_kwargs, dict)
|
|
830
|
+
|
|
831
|
+
# Limit visits returned to the nights we requested
|
|
832
|
+
if "stackers" not in get_sim_data_kwargs:
|
|
833
|
+
get_sim_data_kwargs["stackers"] = []
|
|
834
|
+
dayobsiso_requested = maf.DayObsISOStacker in [s.__class__ for s in get_sim_data_kwargs["stackers"]]
|
|
835
|
+
if not dayobsiso_requested:
|
|
836
|
+
# We want it to filter out dates that were not requested,
|
|
837
|
+
# so add it to the stacker even if it was not requested.
|
|
838
|
+
get_sim_data_kwargs["stackers"].append(maf.DayObsISOStacker())
|
|
839
|
+
|
|
840
|
+
visits = get_sim_data(opsim_rp, **get_sim_data_kwargs)
|
|
841
|
+
|
|
842
|
+
LOGGER.debug(f"Loaded {len(visits)} from {opsim_rp}")
|
|
843
|
+
visits_df = pd.DataFrame(visits)
|
|
844
|
+
visits_df = visits_df.loc[
|
|
845
|
+
(first_day_obs <= visits_df["day_obs_iso8601"]) & (visits_df["day_obs_iso8601"] <= last_day_obs),
|
|
846
|
+
:,
|
|
847
|
+
]
|
|
848
|
+
# If it dayobsiso was not requested, do not return it.
|
|
849
|
+
if not dayobsiso_requested:
|
|
850
|
+
visits_df.drop(columns="day_obs_iso8601", inplace=True)
|
|
851
|
+
|
|
852
|
+
return visits_df
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
def fetch_obsloctap_visits(
|
|
856
|
+
day_obs: str | None = None, nights: int = 2, telescope: str = "simonyi"
|
|
857
|
+
) -> pd.DataFrame:
|
|
858
|
+
"""Return visits from latest nominal prenight briefing simulation.
|
|
859
|
+
|
|
860
|
+
Parameters
|
|
861
|
+
----------
|
|
862
|
+
day_obs : `str`
|
|
863
|
+
The day_obs of the night, in YYYY-MM-DD format (e.g. 2025-03-26).
|
|
864
|
+
Default None will use the date of the next sunset.
|
|
865
|
+
nights : `int`
|
|
866
|
+
The number of nights of observations to return.
|
|
867
|
+
Defaults to 2.
|
|
868
|
+
telescope : `str`
|
|
869
|
+
The telescope to get visits for: "simonyi" or "auxtel".
|
|
870
|
+
Defaults to "simonyi".
|
|
871
|
+
|
|
872
|
+
Returns
|
|
873
|
+
-------
|
|
874
|
+
visits : `pd.DataFrame`
|
|
875
|
+
The visits from the prenight simulation.
|
|
876
|
+
"""
|
|
877
|
+
dbcols = [
|
|
878
|
+
"observationStartMJD",
|
|
879
|
+
"fieldRA",
|
|
880
|
+
"fieldDec",
|
|
881
|
+
"rotSkyPos",
|
|
882
|
+
"band",
|
|
883
|
+
"visitExposureTime",
|
|
884
|
+
"night",
|
|
885
|
+
"target_name",
|
|
886
|
+
]
|
|
887
|
+
|
|
888
|
+
# Start with the first night that starts after the reference time,
|
|
889
|
+
# which is the current time by default.
|
|
890
|
+
# So, if the reference time is during a night, it starts with the
|
|
891
|
+
# following night.
|
|
892
|
+
night_bounds = pd.DataFrame(Almanac().sunsets)
|
|
893
|
+
reference_time = Time.now() if day_obs is None else Time(day_obs, format="iso", scale="utc")
|
|
894
|
+
first_night = night_bounds.query(f"sunset > {reference_time.mjd}").night.min()
|
|
895
|
+
last_night = first_night + nights - 1
|
|
896
|
+
|
|
897
|
+
night_bounds.set_index("night", inplace=True)
|
|
898
|
+
start_mjd = night_bounds.loc[first_night, "sunset"]
|
|
899
|
+
end_mjd = night_bounds.loc[last_night, "sunrise"]
|
|
900
|
+
|
|
901
|
+
first_day_obs = Time(start_mjd - 0.5, format="mjd").iso[:10]
|
|
902
|
+
last_day_obs = Time(end_mjd - 0.5, format="mjd").iso[:10]
|
|
903
|
+
|
|
904
|
+
which_sim = {
|
|
905
|
+
"tags": ("ideal", "nominal"),
|
|
906
|
+
"telescope": telescope,
|
|
907
|
+
"max_simulation_age": int(np.ceil(Time.now().mjd - reference_time.mjd)) + 1,
|
|
908
|
+
}
|
|
909
|
+
get_sim_data_kwargs = {
|
|
910
|
+
"sqlconstraint": (f"observationStartMJD BETWEEN {start_mjd} AND {end_mjd}",),
|
|
911
|
+
"dbcols": dbcols,
|
|
912
|
+
}
|
|
913
|
+
visits = fetch_sim_for_nights(
|
|
914
|
+
first_day_obs, last_day_obs, which_sim=which_sim, get_sim_data_kwargs=get_sim_data_kwargs
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
return visits
|
|
918
|
+
|
|
919
|
+
|
|
920
|
+
def fetch_sim_stats_for_night(day_obs: str | int | None = None) -> dict:
|
|
921
|
+
"""Count the visits on a night in the latest nominal sim for a night.
|
|
922
|
+
|
|
923
|
+
Parameters
|
|
924
|
+
----------
|
|
925
|
+
day_obs : `str` or 'int' or `None`
|
|
926
|
+
Date (in UTC-12hrs timezone) for which to get the count of visits,
|
|
927
|
+
in ISO8601 (YYYY-MM-DD as a string) or int dayobs (int(YYYYMMDD))
|
|
928
|
+
or `None` (day_obs including the evening of yesterday in local time).
|
|
929
|
+
|
|
930
|
+
Returns
|
|
931
|
+
-------
|
|
932
|
+
sim_stats : `dict`
|
|
933
|
+
A dict with statistics for the night.
|
|
934
|
+
Presently, it has one key: `nominal_visits`, the number of visits
|
|
935
|
+
in the latest nominal simulation.
|
|
936
|
+
"""
|
|
937
|
+
|
|
938
|
+
# Maybe we should move schedview.DayObs into rubin_sim so we can use it
|
|
939
|
+
# here without introducing a schedview dependency.
|
|
940
|
+
match day_obs:
|
|
941
|
+
case str():
|
|
942
|
+
pass
|
|
943
|
+
case int():
|
|
944
|
+
day_obs = datetime.datetime.strptime(str(day_obs), "%Y%m%d").date().isoformat()
|
|
945
|
+
case None:
|
|
946
|
+
day_obs = (datetime.date.today() - datetime.timedelta(days=1)).isoformat()
|
|
947
|
+
case _:
|
|
948
|
+
raise NotImplementedError(f"Cannot get day_obs from a {type(day_obs)}.")
|
|
949
|
+
|
|
950
|
+
assert isinstance(day_obs, str)
|
|
951
|
+
|
|
952
|
+
# Initialize the dictionary with the night stats
|
|
953
|
+
night_sim_stats = {}
|
|
954
|
+
|
|
955
|
+
# Count the visits in the latest simulated nominal night
|
|
956
|
+
|
|
957
|
+
# Go far enough back to get the automatic pre-nights for the night
|
|
958
|
+
max_simulation_age = int(np.ceil(Time.now().mjd - Time(day_obs).mjd)) + 3
|
|
959
|
+
|
|
960
|
+
visits = fetch_sim_for_nights(
|
|
961
|
+
first_day_obs=day_obs,
|
|
962
|
+
last_day_obs=day_obs,
|
|
963
|
+
which_sim={"max_simulation_age": max_simulation_age},
|
|
964
|
+
)
|
|
965
|
+
night_sim_stats["nominal_visits"] = len(visits) if isinstance(visits, pd.DataFrame) else 0
|
|
966
|
+
|
|
967
|
+
# We can add whatever additional stats we like here. Possibilities include
|
|
968
|
+
# statistics from multiple simulations, counts for different bands in the
|
|
969
|
+
# nominal simulation, numbers of DDF/WFD/ToO visits, etc.
|
|
970
|
+
|
|
971
|
+
return night_sim_stats
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def export_sim_to_prototype_sim_archive(
|
|
975
|
+
archive_metadata: vseqarchive.VisitSequenceArchiveMetadata, sim_uuid: UUID, proto_sim_archive_url: str
|
|
976
|
+
) -> ResourcePath:
|
|
977
|
+
"""Export a simulation to the prototype simulation archive.
|
|
978
|
+
|
|
979
|
+
Parameters
|
|
980
|
+
----------
|
|
981
|
+
archive_metadata : `vseqarchive.VisitSequenceArchiveMetadata`
|
|
982
|
+
Interface to the visit‑sequence metadata database from
|
|
983
|
+
which the simulation is to be imported.
|
|
984
|
+
sim_uuid : `uuid.UUID`
|
|
985
|
+
UUID of the simulation to be exported.
|
|
986
|
+
proto_sim_archive_url : ``str``
|
|
987
|
+
Base URL of the prototype simulation archive to which
|
|
988
|
+
the simulations is to be exported.
|
|
989
|
+
|
|
990
|
+
Returns
|
|
991
|
+
-------
|
|
992
|
+
sim_rp: `lsst.resources.ResourcePath`
|
|
993
|
+
ResourcePath pointing to the root of the newly created
|
|
994
|
+
prototype archive entry.
|
|
995
|
+
"""
|
|
996
|
+
|
|
997
|
+
insert_date, new_id, proto_sim_rpath = _next_sim_date_and_index(proto_sim_archive_url)
|
|
998
|
+
metadata = yaml.safe_load(archive_metadata.sim_metadata_yaml(sim_uuid))
|
|
999
|
+
|
|
1000
|
+
have_opsim = False
|
|
1001
|
+
for file_type in metadata["files"]:
|
|
1002
|
+
if file_type == "observations":
|
|
1003
|
+
have_opsim = True
|
|
1004
|
+
|
|
1005
|
+
origin = ResourcePath(metadata["files"][file_type]["url"])
|
|
1006
|
+
destination = proto_sim_rpath.join(origin.basename())
|
|
1007
|
+
destination.transfer_from(origin, "copy")
|
|
1008
|
+
metadata["files"][file_type]["url"] = destination.geturl()
|
|
1009
|
+
|
|
1010
|
+
if not have_opsim:
|
|
1011
|
+
visits_url = archive_metadata.get_visitseq_url(sim_uuid)
|
|
1012
|
+
visits_rp = ResourcePath(visits_url)
|
|
1013
|
+
with visits_rp.as_local() as visits_local_rp:
|
|
1014
|
+
with TemporaryDirectory() as temp_dir:
|
|
1015
|
+
opsimdb_fname = str(Path(temp_dir) / "opsim.db")
|
|
1016
|
+
vseqarchive.hdf5_to_opsimdb(visits_local_rp.ospath, opsimdb_fname)
|
|
1017
|
+
opsimdb_rp = proto_sim_rpath.join("opsim.db")
|
|
1018
|
+
opsimdb_rp.transfer_from(ResourcePath(opsimdb_fname), "copy")
|
|
1019
|
+
metadata["files"]["observations"] = {"name": "opsim.db", "url": opsimdb_rp.geturl()}
|
|
1020
|
+
|
|
1021
|
+
metadata["files"]["metadata"] = {"name": "sim_metadata.yaml"}
|
|
1022
|
+
metadata["files"]["metadata"]["url"] = proto_sim_rpath.join(
|
|
1023
|
+
metadata["files"]["metadata"]["name"]
|
|
1024
|
+
).geturl()
|
|
1025
|
+
|
|
1026
|
+
metadata_yaml = yaml.dump(metadata)
|
|
1027
|
+
ResourcePath(metadata["files"]["metadata"]["url"]).write(metadata_yaml.encode("utf-8"))
|
|
1028
|
+
|
|
1029
|
+
return proto_sim_rpath
|