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.
Files changed (387) hide show
  1. batch/compile_prenight_metadata_cache.sh +34 -0
  2. batch/run_auxtel_prenight_sims.sh +163 -0
  3. batch/run_prenight_sims.sh +239 -0
  4. docs/.gitignore +3 -0
  5. docs/Makefile +32 -0
  6. docs/api.rst +25 -0
  7. docs/conf.py +8 -0
  8. docs/data-api.rst +12 -0
  9. docs/data-download.rst +70 -0
  10. docs/developer.rst +14 -0
  11. docs/documenteer.toml +11 -0
  12. docs/index.rst +22 -0
  13. docs/installation.rst +76 -0
  14. docs/introduction.rst +37 -0
  15. docs/maf-api-batches.rst +12 -0
  16. docs/maf-api-db.rst +12 -0
  17. docs/maf-api-maf-contrib.rst +12 -0
  18. docs/maf-api-maps.rst +12 -0
  19. docs/maf-api-metricbundles.rst +12 -0
  20. docs/maf-api-metrics.rst +12 -0
  21. docs/maf-api-plots.rst +12 -0
  22. docs/maf-api-run-comparison.rst +12 -0
  23. docs/maf-api-slicers.rst +12 -0
  24. docs/maf-api-stackers.rst +12 -0
  25. docs/maf-api-utils.rst +12 -0
  26. docs/maf-api.rst +22 -0
  27. docs/maf.rst +60 -0
  28. docs/metric_list.py +49 -0
  29. docs/moving-objects-api.rst +12 -0
  30. docs/moving-objects.rst +22 -0
  31. docs/phot-utils-api.rst +12 -0
  32. docs/phot-utils.rst +11 -0
  33. docs/prenight.rst +117 -0
  34. docs/protoarchive.rst +276 -0
  35. docs/satellite-constellations-api.rst +12 -0
  36. docs/satellite-constellations.rst +13 -0
  37. docs/selfcal-api.rst +12 -0
  38. docs/selfcal.rst +15 -0
  39. docs/sim-archive-api.rst +12 -0
  40. docs/skybrightness-api.rst +12 -0
  41. docs/skybrightness.rst +23 -0
  42. docs/user-guide.rst +28 -0
  43. docs/vseqarchive-architecture.rst +438 -0
  44. docs/vseqarchive-setup.rst +238 -0
  45. docs/vseqarchive.rst +225 -0
  46. rubin_sim/__init__.py +7 -0
  47. rubin_sim/data/__init__.py +1 -0
  48. rubin_sim/data/rs_download_data.py +155 -0
  49. rubin_sim/maf/__init__.py +34 -0
  50. rubin_sim/maf/batches/__init__.py +18 -0
  51. rubin_sim/maf/batches/altaz_batch.py +161 -0
  52. rubin_sim/maf/batches/col_map_dict.py +274 -0
  53. rubin_sim/maf/batches/common.py +388 -0
  54. rubin_sim/maf/batches/ddf_batch.py +728 -0
  55. rubin_sim/maf/batches/filterchange_batch.py +182 -0
  56. rubin_sim/maf/batches/glance_batch.py +591 -0
  57. rubin_sim/maf/batches/hourglass_batch.py +79 -0
  58. rubin_sim/maf/batches/info_batch.py +114 -0
  59. rubin_sim/maf/batches/metadata_batch.py +558 -0
  60. rubin_sim/maf/batches/moving_objects_batch.py +1587 -0
  61. rubin_sim/maf/batches/openshutter_batch.py +100 -0
  62. rubin_sim/maf/batches/radar_limited.py +1100 -0
  63. rubin_sim/maf/batches/science_radar_batch.py +1910 -0
  64. rubin_sim/maf/batches/skycoverage.py +155 -0
  65. rubin_sim/maf/batches/slew_batch.py +146 -0
  66. rubin_sim/maf/batches/srd_batch.py +637 -0
  67. rubin_sim/maf/batches/time_batch.py +757 -0
  68. rubin_sim/maf/batches/time_sci_batch.py +105 -0
  69. rubin_sim/maf/batches/visitdepth_batch.py +485 -0
  70. rubin_sim/maf/db/__init__.py +2 -0
  71. rubin_sim/maf/db/add_run.py +34 -0
  72. rubin_sim/maf/db/results_db.py +1074 -0
  73. rubin_sim/maf/db/tracking_db.py +332 -0
  74. rubin_sim/maf/ddf_dir.py +53 -0
  75. rubin_sim/maf/generate_ss.py +218 -0
  76. rubin_sim/maf/glance_dir.py +58 -0
  77. rubin_sim/maf/maf_contrib/__init__.py +25 -0
  78. rubin_sim/maf/maf_contrib/calculate_lsst_field_visibility_astropy.py +170 -0
  79. rubin_sim/maf/maf_contrib/depth_limited_num_gal_metric.py +109 -0
  80. rubin_sim/maf/maf_contrib/example_new_metrics.py +40 -0
  81. rubin_sim/maf/maf_contrib/filter_pair_t_gaps_metric.py +208 -0
  82. rubin_sim/maf/maf_contrib/grb_transient_metric.py +249 -0
  83. rubin_sim/maf/maf_contrib/gw170817_det_metric.py +72 -0
  84. rubin_sim/maf/maf_contrib/intervals_between_obs_metric.py +68 -0
  85. rubin_sim/maf/maf_contrib/kne_metrics.py +473 -0
  86. rubin_sim/maf/maf_contrib/lss_metrics.py +45 -0
  87. rubin_sim/maf/maf_contrib/lss_obs_strategy/__init__.py +3 -0
  88. rubin_sim/maf/maf_contrib/lss_obs_strategy/constants_for_pipeline.py +73 -0
  89. rubin_sim/maf/maf_contrib/lss_obs_strategy/galaxy_counts_metric_extended.py +205 -0
  90. rubin_sim/maf/maf_contrib/lss_obs_strategy/galaxy_counts_with_pixel_calibration.py +171 -0
  91. rubin_sim/maf/maf_contrib/lv_dwarfs/__init__.py +1 -0
  92. rubin_sim/maf/maf_contrib/lv_dwarfs/lv_dwarfs_metrics.py +388 -0
  93. rubin_sim/maf/maf_contrib/microlensing_metric.py +593 -0
  94. rubin_sim/maf/maf_contrib/num_obs_in_survey_time_overlap_metric.py +43 -0
  95. rubin_sim/maf/maf_contrib/periodic_metric.py +51 -0
  96. rubin_sim/maf/maf_contrib/periodic_star_metric.py +186 -0
  97. rubin_sim/maf/maf_contrib/periodic_star_modulation_metric.py +236 -0
  98. rubin_sim/maf/maf_contrib/presto_color_kne_pop_metric.py +441 -0
  99. rubin_sim/maf/maf_contrib/selfcal_uniformity_metric.py +171 -0
  100. rubin_sim/maf/maf_contrib/star_count_mass_metric.py +64 -0
  101. rubin_sim/maf/maf_contrib/star_count_metric.py +56 -0
  102. rubin_sim/maf/maf_contrib/star_counts/__init__.py +8 -0
  103. rubin_sim/maf/maf_contrib/star_counts/abs_mag.py +324 -0
  104. rubin_sim/maf/maf_contrib/star_counts/coords.py +99 -0
  105. rubin_sim/maf/maf_contrib/star_counts/readme.txt +1 -0
  106. rubin_sim/maf/maf_contrib/star_counts/spec_type.py +91 -0
  107. rubin_sim/maf/maf_contrib/star_counts/starcount.py +30 -0
  108. rubin_sim/maf/maf_contrib/star_counts/starcount_bymass.py +93 -0
  109. rubin_sim/maf/maf_contrib/star_counts/stellardensity.py +59 -0
  110. rubin_sim/maf/maf_contrib/static_probes_fom_summary_metric.py +386 -0
  111. rubin_sim/maf/maf_contrib/tdes_pop_metric.py +362 -0
  112. rubin_sim/maf/maf_contrib/transient_ascii_sed_metric.py +652 -0
  113. rubin_sim/maf/maf_contrib/triplet_metric.py +156 -0
  114. rubin_sim/maf/maf_contrib/var_depth_metric.py +157 -0
  115. rubin_sim/maf/maf_contrib/xrb_metrics.py +523 -0
  116. rubin_sim/maf/maf_contrib/young_stellar_objects_metric.py +200 -0
  117. rubin_sim/maf/maf_night_report.py +200 -0
  118. rubin_sim/maf/make_fbs_tracking_db.py +90 -0
  119. rubin_sim/maf/maps/__init__.py +8 -0
  120. rubin_sim/maf/maps/base_map.py +72 -0
  121. rubin_sim/maf/maps/create_gaia_density_map.py +158 -0
  122. rubin_sim/maf/maps/dust_map.py +66 -0
  123. rubin_sim/maf/maps/dust_map_3d.py +190 -0
  124. rubin_sim/maf/maps/ebv_3d_hp.py +170 -0
  125. rubin_sim/maf/maps/ebv_hp.py +64 -0
  126. rubin_sim/maf/maps/gal_coords_map.py +22 -0
  127. rubin_sim/maf/maps/galactic_plane_priority_maps.py +210 -0
  128. rubin_sim/maf/maps/stellar_density_map.py +73 -0
  129. rubin_sim/maf/maps/trilegal_map.py +82 -0
  130. rubin_sim/maf/metadata_dir.py +90 -0
  131. rubin_sim/maf/metric_bundles/__init__.py +3 -0
  132. rubin_sim/maf/metric_bundles/metric_bundle.py +816 -0
  133. rubin_sim/maf/metric_bundles/metric_bundle_group.py +834 -0
  134. rubin_sim/maf/metric_bundles/mo_metric_bundle.py +646 -0
  135. rubin_sim/maf/metrics/__init__.py +52 -0
  136. rubin_sim/maf/metrics/agn_time_lag_metric.py +118 -0
  137. rubin_sim/maf/metrics/agnstructure.py +141 -0
  138. rubin_sim/maf/metrics/area_summary_metrics.py +120 -0
  139. rubin_sim/maf/metrics/base_metric.py +215 -0
  140. rubin_sim/maf/metrics/brown_dwarf_metric.py +162 -0
  141. rubin_sim/maf/metrics/cadence_metrics.py +400 -0
  142. rubin_sim/maf/metrics/calibration_metrics.py +618 -0
  143. rubin_sim/maf/metrics/chip_vendor_metric.py +43 -0
  144. rubin_sim/maf/metrics/color_slope_metrics.py +189 -0
  145. rubin_sim/maf/metrics/cosmology_summary_metrics.py +1212 -0
  146. rubin_sim/maf/metrics/coverage_metric.py +48 -0
  147. rubin_sim/maf/metrics/crowding_metric.py +255 -0
  148. rubin_sim/maf/metrics/cumulative_metric.py +46 -0
  149. rubin_sim/maf/metrics/dcr_metric.py +93 -0
  150. rubin_sim/maf/metrics/exgal_m5.py +57 -0
  151. rubin_sim/maf/metrics/fft_metric.py +27 -0
  152. rubin_sim/maf/metrics/galactic_plane_metrics.py +375 -0
  153. rubin_sim/maf/metrics/galplane_time_sampling_metrics.py +236 -0
  154. rubin_sim/maf/metrics/hourglass_metric.py +130 -0
  155. rubin_sim/maf/metrics/incremental_template_metric.py +255 -0
  156. rubin_sim/maf/metrics/kuiper_metrics.py +26 -0
  157. rubin_sim/maf/metrics/mo_metrics.py +1496 -0
  158. rubin_sim/maf/metrics/mo_summary_metrics.py +464 -0
  159. rubin_sim/maf/metrics/night_pointing_metric.py +70 -0
  160. rubin_sim/maf/metrics/optimal_m5_metric.py +94 -0
  161. rubin_sim/maf/metrics/pair_metric.py +69 -0
  162. rubin_sim/maf/metrics/periodic_detect_metric.py +133 -0
  163. rubin_sim/maf/metrics/phase_gap_metric.py +172 -0
  164. rubin_sim/maf/metrics/qso_number_counts_metric.py +138 -0
  165. rubin_sim/maf/metrics/scaling_metrics.py +148 -0
  166. rubin_sim/maf/metrics/schedview_metrics.py +39 -0
  167. rubin_sim/maf/metrics/season_metrics.py +344 -0
  168. rubin_sim/maf/metrics/simple_metrics.py +546 -0
  169. rubin_sim/maf/metrics/sky_sat_metric.py +18 -0
  170. rubin_sim/maf/metrics/sn_cadence_metric.py +104 -0
  171. rubin_sim/maf/metrics/sn_n_sn_metric.py +1364 -0
  172. rubin_sim/maf/metrics/sn_sl_metric.py +216 -0
  173. rubin_sim/maf/metrics/sn_snr_metric.py +715 -0
  174. rubin_sim/maf/metrics/snr_weight.py +24 -0
  175. rubin_sim/maf/metrics/star_density.py +77 -0
  176. rubin_sim/maf/metrics/string_count_metric.py +95 -0
  177. rubin_sim/maf/metrics/summary_metrics.py +201 -0
  178. rubin_sim/maf/metrics/surfb_metric.py +175 -0
  179. rubin_sim/maf/metrics/technical_metrics.py +271 -0
  180. rubin_sim/maf/metrics/tgaps.py +412 -0
  181. rubin_sim/maf/metrics/tomography_models.py +2034 -0
  182. rubin_sim/maf/metrics/transient_metrics.py +268 -0
  183. rubin_sim/maf/metrics/uniformity_metrics.py +418 -0
  184. rubin_sim/maf/metrics/use_metrics.py +55 -0
  185. rubin_sim/maf/metrics/vector_metrics.py +190 -0
  186. rubin_sim/maf/metrics/visit_groups_metric.py +341 -0
  187. rubin_sim/maf/metrics/weak_lensing_systematics_metric.py +210 -0
  188. rubin_sim/maf/plots/__init__.py +14 -0
  189. rubin_sim/maf/plots/hg_plotters.py +1117 -0
  190. rubin_sim/maf/plots/hourglass_plotters.py +120 -0
  191. rubin_sim/maf/plots/mo_plotters.py +358 -0
  192. rubin_sim/maf/plots/nd_plotters.py +220 -0
  193. rubin_sim/maf/plots/neo_distance_plotter.py +140 -0
  194. rubin_sim/maf/plots/night_pointing_plotter.py +96 -0
  195. rubin_sim/maf/plots/oned_plotters.py +114 -0
  196. rubin_sim/maf/plots/perceptual_rainbow.py +30 -0
  197. rubin_sim/maf/plots/plot_handler.py +766 -0
  198. rubin_sim/maf/plots/skyproj_plotters.py +540 -0
  199. rubin_sim/maf/plots/spatial_plotters.py +1029 -0
  200. rubin_sim/maf/plots/special_plotters.py +256 -0
  201. rubin_sim/maf/plots/two_d_plotters.py +177 -0
  202. rubin_sim/maf/plots/xyplotter.py +37 -0
  203. rubin_sim/maf/run_comparison/__init__.py +5 -0
  204. rubin_sim/maf/run_comparison/archive.py +584 -0
  205. rubin_sim/maf/run_comparison/gather_summaries.py +125 -0
  206. rubin_sim/maf/run_comparison/microlensing_compare.py +514 -0
  207. rubin_sim/maf/run_comparison/radar_plot.py +202 -0
  208. rubin_sim/maf/run_comparison/summary_plots.py +710 -0
  209. rubin_sim/maf/run_moving_calc.py +239 -0
  210. rubin_sim/maf/run_moving_fractions.py +128 -0
  211. rubin_sim/maf/run_moving_join.py +103 -0
  212. rubin_sim/maf/run_selfcal_metric.py +92 -0
  213. rubin_sim/maf/scimaf_dir.py +87 -0
  214. rubin_sim/maf/show_maf.py +227 -0
  215. rubin_sim/maf/slicers/__init__.py +13 -0
  216. rubin_sim/maf/slicers/base_slicer.py +513 -0
  217. rubin_sim/maf/slicers/base_spatial_slicer.py +202 -0
  218. rubin_sim/maf/slicers/healpix_sdss_slicer.py +118 -0
  219. rubin_sim/maf/slicers/healpix_slicer.py +189 -0
  220. rubin_sim/maf/slicers/healpix_subset_slicer.py +293 -0
  221. rubin_sim/maf/slicers/hourglass_slicer.py +37 -0
  222. rubin_sim/maf/slicers/mo_slicer.py +171 -0
  223. rubin_sim/maf/slicers/movie_slicer.py +288 -0
  224. rubin_sim/maf/slicers/nd_slicer.py +147 -0
  225. rubin_sim/maf/slicers/one_d_slicer.py +210 -0
  226. rubin_sim/maf/slicers/time_interval_slicers.py +283 -0
  227. rubin_sim/maf/slicers/uni_slicer.py +57 -0
  228. rubin_sim/maf/slicers/user_points_slicer.py +141 -0
  229. rubin_sim/maf/stackers/__init__.py +13 -0
  230. rubin_sim/maf/stackers/base_stacker.py +221 -0
  231. rubin_sim/maf/stackers/coord_stackers.py +224 -0
  232. rubin_sim/maf/stackers/date_stackers.py +192 -0
  233. rubin_sim/maf/stackers/general_stackers.py +683 -0
  234. rubin_sim/maf/stackers/get_col_info.py +104 -0
  235. rubin_sim/maf/stackers/label_stackers.py +114 -0
  236. rubin_sim/maf/stackers/m5_optimal_stacker.py +126 -0
  237. rubin_sim/maf/stackers/mo_phase.py +265 -0
  238. rubin_sim/maf/stackers/mo_stackers.py +486 -0
  239. rubin_sim/maf/stackers/n_follow_stacker.py +187 -0
  240. rubin_sim/maf/stackers/neo_dist_stacker.py +126 -0
  241. rubin_sim/maf/stackers/sdss_stackers.py +30 -0
  242. rubin_sim/maf/stackers/sn_stacker.py +152 -0
  243. rubin_sim/maf/stackers/teff_stacker.py +154 -0
  244. rubin_sim/maf/utils/__init__.py +6 -0
  245. rubin_sim/maf/utils/astrometry_utils.py +93 -0
  246. rubin_sim/maf/utils/generate_fov_map.py +58 -0
  247. rubin_sim/maf/utils/get_date_version.py +28 -0
  248. rubin_sim/maf/utils/maf_utils.py +212 -0
  249. rubin_sim/maf/utils/opsim_utils.py +259 -0
  250. rubin_sim/maf/utils/output_utils.py +34 -0
  251. rubin_sim/maf/utils/sn_n_sn_utils.py +894 -0
  252. rubin_sim/maf/utils/sn_utils.py +369 -0
  253. rubin_sim/maf/utils/stellar_mags.py +197 -0
  254. rubin_sim/maf/web/__init__.py +2 -0
  255. rubin_sim/maf/web/favicon.ico +0 -0
  256. rubin_sim/maf/web/maf_run_results.py +645 -0
  257. rubin_sim/maf/web/maf_tracking.py +139 -0
  258. rubin_sim/maf/web/sorttable.js +496 -0
  259. rubin_sim/maf/web/templates/allmetricresults.html +72 -0
  260. rubin_sim/maf/web/templates/configs.html +28 -0
  261. rubin_sim/maf/web/templates/macros.html +243 -0
  262. rubin_sim/maf/web/templates/master.html +294 -0
  263. rubin_sim/maf/web/templates/metricselect.html +91 -0
  264. rubin_sim/maf/web/templates/multicolor.html +147 -0
  265. rubin_sim/maf/web/templates/results.html +82 -0
  266. rubin_sim/maf/web/templates/runselect.html +58 -0
  267. rubin_sim/maf/web/templates/stats.html +50 -0
  268. rubin_sim/moving_objects/__init__.py +8 -0
  269. rubin_sim/moving_objects/base_obs.py +468 -0
  270. rubin_sim/moving_objects/cheby_fits.py +689 -0
  271. rubin_sim/moving_objects/cheby_values.py +231 -0
  272. rubin_sim/moving_objects/chebyshev_utils.py +365 -0
  273. rubin_sim/moving_objects/direct_obs.py +376 -0
  274. rubin_sim/moving_objects/make_lsst_obs.py +282 -0
  275. rubin_sim/moving_objects/ooephemerides.py +643 -0
  276. rubin_sim/moving_objects/orbits.py +507 -0
  277. rubin_sim/moving_objects/pre_generate.py +37 -0
  278. rubin_sim/moving_objects/utils.py +61 -0
  279. rubin_sim/phot_utils/__init__.py +7 -0
  280. rubin_sim/phot_utils/bandpass.py +425 -0
  281. rubin_sim/phot_utils/photometric_parameters.py +449 -0
  282. rubin_sim/phot_utils/physical_parameters.py +50 -0
  283. rubin_sim/phot_utils/predicted_zeropoints.py +245 -0
  284. rubin_sim/phot_utils/sed.py +1773 -0
  285. rubin_sim/phot_utils/sed_utils.py +52 -0
  286. rubin_sim/phot_utils/signaltonoise.py +695 -0
  287. rubin_sim/phot_utils/spectral_resampling.py +161 -0
  288. rubin_sim/satellite_constellations/__init__.py +3 -0
  289. rubin_sim/satellite_constellations/basis_function.py +40 -0
  290. rubin_sim/satellite_constellations/model_observatory.py +157 -0
  291. rubin_sim/satellite_constellations/sat_utils.py +488 -0
  292. rubin_sim/selfcal/__init__.py +4 -0
  293. rubin_sim/selfcal/generate_catalog.py +188 -0
  294. rubin_sim/selfcal/offsets.py +119 -0
  295. rubin_sim/selfcal/solver.py +143 -0
  296. rubin_sim/selfcal/star_tools.py +42 -0
  297. rubin_sim/sim_archive/__init__.py +14 -0
  298. rubin_sim/sim_archive/conda_packages.py +65 -0
  299. rubin_sim/sim_archive/future_vsarch.py +480 -0
  300. rubin_sim/sim_archive/make_snapshot.py +255 -0
  301. rubin_sim/sim_archive/prenight.py +216 -0
  302. rubin_sim/sim_archive/prenightindex.py +229 -0
  303. rubin_sim/sim_archive/prototype.py +1029 -0
  304. rubin_sim/sim_archive/sim_archive.py +680 -0
  305. rubin_sim/sim_archive/tempdb.py +56 -0
  306. rubin_sim/sim_archive/util.py +146 -0
  307. rubin_sim/sim_archive/vseqarchive.py +965 -0
  308. rubin_sim/sim_archive/vseqmetadata.py +1721 -0
  309. rubin_sim/skybrightness/__init__.py +5 -0
  310. rubin_sim/skybrightness/allsky_db.py +93 -0
  311. rubin_sim/skybrightness/data/ESO_Spectra/eso_tools.py +505 -0
  312. rubin_sim/skybrightness/data/solarSpec/package.py +22 -0
  313. rubin_sim/skybrightness/generate_hdf5.py +243 -0
  314. rubin_sim/skybrightness/interp_components.py +798 -0
  315. rubin_sim/skybrightness/sky_model.py +771 -0
  316. rubin_sim/skybrightness/twilight_func.py +83 -0
  317. rubin_sim/skybrightness/utils.py +91 -0
  318. rubin_sim-2.6.1.dev2.dist-info/METADATA +95 -0
  319. rubin_sim-2.6.1.dev2.dist-info/RECORD +387 -0
  320. rubin_sim-2.6.1.dev2.dist-info/WHEEL +5 -0
  321. rubin_sim-2.6.1.dev2.dist-info/entry_points.txt +24 -0
  322. rubin_sim-2.6.1.dev2.dist-info/licenses/LICENSE +674 -0
  323. rubin_sim-2.6.1.dev2.dist-info/top_level.txt +5 -0
  324. sql/make_vseqmeta.sql +251 -0
  325. tests/data/test_data.py +26 -0
  326. tests/data/test_ddf_grid.py +17 -0
  327. tests/maf/test_3x2fom.py +68 -0
  328. tests/maf/test_archive.py +162 -0
  329. tests/maf/test_basemetrics.py +99 -0
  330. tests/maf/test_batchcommon.py +36 -0
  331. tests/maf/test_batches.py +159 -0
  332. tests/maf/test_cadencemetrics.py +388 -0
  333. tests/maf/test_calibrationmetrics.py +242 -0
  334. tests/maf/test_color_slopes.py +71 -0
  335. tests/maf/test_cosmology_summaries.py +20 -0
  336. tests/maf/test_gathersummaries.py +48 -0
  337. tests/maf/test_healpixslicer.py +439 -0
  338. tests/maf/test_healpixsubsetslicer.py +273 -0
  339. tests/maf/test_hgplotters.py +145 -0
  340. tests/maf/test_hourglassmetric.py +33 -0
  341. tests/maf/test_io.py +187 -0
  342. tests/maf/test_json.py +169 -0
  343. tests/maf/test_maps.py +199 -0
  344. tests/maf/test_metricbundle.py +69 -0
  345. tests/maf/test_mometrics.py +422 -0
  346. tests/maf/test_movieslicer.py +216 -0
  347. tests/maf/test_ndslicer.py +240 -0
  348. tests/maf/test_neodistanceplotter.py +41 -0
  349. tests/maf/test_onedslicer.py +245 -0
  350. tests/maf/test_opsimutils.py +80 -0
  351. tests/maf/test_plotters.py +134 -0
  352. tests/maf/test_resultsdb.py +145 -0
  353. tests/maf/test_schedviewmetrics.py +14 -0
  354. tests/maf/test_simplemetrics.py +200 -0
  355. tests/maf/test_snmetrics.py +335 -0
  356. tests/maf/test_snmetrics_nsn.py +123 -0
  357. tests/maf/test_stackers.py +423 -0
  358. tests/maf/test_stellarmags.py +50 -0
  359. tests/maf/test_stringcount.py +26 -0
  360. tests/maf/test_summary_plots.py +197 -0
  361. tests/maf/test_summarymetrics.py +82 -0
  362. tests/maf/test_technicalmetrics.py +267 -0
  363. tests/maf/test_templatemetrics.py +86 -0
  364. tests/maf/test_timeintervalslicers.py +97 -0
  365. tests/maf/test_trackingdb.py +88 -0
  366. tests/maf/test_unislicer.py +116 -0
  367. tests/maf/test_vectormetrics.py +225 -0
  368. tests/maf/test_visitgroupsmetric.py +181 -0
  369. tests/moving_objects/test_camera.py +39 -0
  370. tests/moving_objects/test_chebyfits.py +188 -0
  371. tests/moving_objects/test_chebyshevutils.py +76 -0
  372. tests/moving_objects/test_chebyvalues.py +245 -0
  373. tests/moving_objects/test_ephemerides.py +219 -0
  374. tests/moving_objects/test_orbits.py +170 -0
  375. tests/moving_objects/test_sed_data.py +61 -0
  376. tests/phot_utils/test_approximatebandpasses.py +91 -0
  377. tests/phot_utils/test_photometricparameters.py +268 -0
  378. tests/phot_utils/test_photometry.py +61 -0
  379. tests/phot_utils/test_predicted_zeropoints.py +53 -0
  380. tests/phot_utils/test_read_bandpasses.py +29 -0
  381. tests/phot_utils/test_sed.py +368 -0
  382. tests/phot_utils/test_snr.py +370 -0
  383. tests/satellite_constellations/test_satellites.py +36 -0
  384. tests/sim_archive/test_make_snapshot.py +24 -0
  385. tests/sim_archive/test_prototype_sim_archive.py +204 -0
  386. tests/sim_archive/test_vseqarchive.py +947 -0
  387. tests/skybrightness/test_skymodel.py +359 -0
@@ -0,0 +1,683 @@
1
+ __all__ = (
2
+ "NormAirmassStacker",
3
+ "ParallaxFactorStacker",
4
+ "HourAngleStacker",
5
+ "ZenithDistStacker",
6
+ "ParallacticAngleStacker",
7
+ "DcrStacker",
8
+ "FiveSigmaStacker",
9
+ "SaturationStacker",
10
+ "OverheadStacker",
11
+ )
12
+
13
+ import warnings
14
+
15
+ import astropy.units as u
16
+ import numpy as np
17
+ from astropy.coordinates import GCRS, SkyCoord
18
+ from astropy.time import Time
19
+ from rubin_scheduler.utils import Site, m5_flat_sed
20
+
21
+ from rubin_sim.maf.utils import load_inst_zeropoints
22
+
23
+ from .base_stacker import BaseStacker
24
+ from .date_stackers import DayObsMJDStacker
25
+
26
+
27
+ class SaturationStacker(BaseStacker):
28
+ """Adds a calculated point-source saturation limit for each visit.
29
+
30
+ Assumes Gaussian PSF.
31
+
32
+ Parameters
33
+ ----------
34
+ pixscale : `float`, optional
35
+ Arcsec per pixel.
36
+ saturation_e : `float`, optional
37
+ The saturation level in electrons.
38
+ zeropoints : dict-like, optional
39
+ The zeropoints for the telescope.
40
+ Keys should be str with filter names, values in mags.
41
+ Default of None, will use Rubin calculated zeropoints.
42
+ km : dict-like, optional
43
+ Atmospheric extinction values.
44
+ Keys should be str with filter names.
45
+ If None, will use Rubin calculated atmospheric extinction values.
46
+ """
47
+
48
+ cols_added = ["saturation_mag"]
49
+
50
+ def __init__(
51
+ self,
52
+ seeing_col="seeingFwhmEff",
53
+ skybrightness_col="skyBrightness",
54
+ exptime_col="visitExposureTime",
55
+ nexp_col="numExposures",
56
+ filter_col="band",
57
+ airmass_col="airmass",
58
+ saturation_e=150e3,
59
+ zeropoints=None,
60
+ km=None,
61
+ pixscale=0.2,
62
+ ):
63
+ self.units = ["mag"]
64
+ self.cols_req = [
65
+ seeing_col,
66
+ skybrightness_col,
67
+ exptime_col,
68
+ nexp_col,
69
+ filter_col,
70
+ airmass_col,
71
+ ]
72
+ self.seeing_col = seeing_col
73
+ self.skybrightness_col = skybrightness_col
74
+ self.exptime_col = exptime_col
75
+ self.nexp_col = nexp_col
76
+ self.filter_col = filter_col
77
+ self.airmass_col = airmass_col
78
+ self.saturation_e = saturation_e
79
+ self.pixscale = pixscale
80
+ self.zeropoints = zeropoints
81
+ self.km = km
82
+
83
+ def _run(self, sim_data, cols_present=False):
84
+ if self.zeropoints is None:
85
+ zp_inst, k_atm = load_inst_zeropoints()
86
+ self.zeropoints = zp_inst
87
+ if self.km is None:
88
+ self.km = k_atm
89
+ for filtername in np.unique(sim_data[self.filter_col]):
90
+ in_filt = np.where(sim_data[self.filter_col] == filtername)[0]
91
+ # Calculate the length of the on-sky time per EXPOSURE
92
+ exptime = sim_data[self.exptime_col][in_filt] / sim_data[self.nexp_col][in_filt]
93
+ # Calculate sky counts per pixel per second
94
+ # from skybrightness + zeropoint (e/1s)
95
+ sky_counts = (
96
+ 10.0 ** (0.4 * (self.zeropoints[filtername] - sim_data[self.skybrightness_col][in_filt]))
97
+ * self.pixscale**2
98
+ )
99
+ # Total sky counts in each exposure
100
+ sky_counts = sky_counts * exptime
101
+ # The counts available to the source (at peak) in each exposure is
102
+ # the difference between saturation and sky
103
+ remaining_counts_peak = self.saturation_e - sky_counts
104
+ # Now to figure out how many counts there would be total, if there
105
+ # are that many in the peak
106
+ sigma = sim_data[self.seeing_col][in_filt] / 2.354
107
+ source_counts = remaining_counts_peak * 2.0 * np.pi * (sigma / self.pixscale) ** 2
108
+ # source counts = counts per exposure (expTimeCol / nexp)
109
+ # Translate to counts per second, to apply zeropoint
110
+ count_rate = source_counts / exptime
111
+ sim_data["saturation_mag"][in_filt] = -2.5 * np.log10(count_rate) + self.zeropoints[filtername]
112
+ # Airmass correction
113
+ sim_data["saturation_mag"][in_filt] -= self.km[filtername] * (
114
+ sim_data[self.airmass_col][in_filt] - 1.0
115
+ )
116
+ # Explicitly make sure if sky has saturated we return NaN
117
+ sim_data["saturation_mag"][np.where(remaining_counts_peak < 0)] = np.nan
118
+
119
+ return sim_data
120
+
121
+
122
+ class NoteRootStacker(BaseStacker):
123
+ """Strip off things after a comma in the scheduler_note"""
124
+
125
+ cols_added = ["scheduler_note_root"]
126
+
127
+ def __init__(self, note_col="scheduler_note"):
128
+ self.units = ["mag"]
129
+ self.cols_req = [note_col]
130
+ self.note_col = note_col
131
+ self.cols_added_dtypes = ["<U50"]
132
+
133
+ def _run(self, sim_data, cols_present=False):
134
+ if cols_present:
135
+ # Column already present in data; assume it is fine.
136
+ return sim_data
137
+ new_note = [note.split(",")[0] for note in sim_data[self.note_col]]
138
+ sim_data["scheduler_note_root"] = new_note
139
+ return sim_data
140
+
141
+
142
+ class FiveSigmaStacker(BaseStacker):
143
+ """Calculate the 5-sigma limiting depth for a point source in the given
144
+ conditions.
145
+
146
+ This is generally not needed, unless the m5 parameters have been updated
147
+ or m5 was not previously calculated.
148
+
149
+ Parameters
150
+ ----------
151
+ airmass_col : `str`, optional
152
+ Name of the airmass column in the data.
153
+ seeing_col : `str`, optional
154
+ Name of the seeing column in the data.
155
+ (FWHM of the single-Gaussian PSF)
156
+ skybrightness_col : `str`, optional
157
+ Name of the skybrightness column in the data.
158
+ filter_col : `str`, optional
159
+ Name of the filter bandpass column in the data.
160
+ exptime_col : `str`, optional
161
+ Name of the on-sky exposure time column in the data.
162
+ """
163
+
164
+ cols_added = ["m5_simsUtils"]
165
+
166
+ def __init__(
167
+ self,
168
+ airmass_col="airmass",
169
+ seeing_col="seeingFwhmEff",
170
+ skybrightness_col="skyBrightness",
171
+ filter_col="band",
172
+ exptime_col="visitExposureTime",
173
+ ):
174
+ self.units = ["mag"]
175
+ self.cols_req = [
176
+ airmass_col,
177
+ seeing_col,
178
+ skybrightness_col,
179
+ filter_col,
180
+ exptime_col,
181
+ ]
182
+ self.airmass_col = airmass_col
183
+ self.seeing_col = seeing_col
184
+ self.skybrightness_col = skybrightness_col
185
+ self.filter_col = filter_col
186
+ self.exptime_col = exptime_col
187
+
188
+ def _run(self, sim_data, cols_present=False):
189
+ if cols_present:
190
+ # Column already present in data; assume it is fine.
191
+ return sim_data
192
+ filts = np.unique(sim_data[self.filter_col])
193
+ for filtername in filts:
194
+ infilt = np.where(sim_data[self.filter_col] == filtername)
195
+ sim_data["m5_simsUtils"][infilt] = m5_flat_sed(
196
+ filtername,
197
+ sim_data[infilt][self.skybrightness_col],
198
+ sim_data[infilt][self.seeing_col],
199
+ sim_data[infilt][self.exptime_col],
200
+ sim_data[infilt][self.airmass_col],
201
+ )
202
+ return sim_data
203
+
204
+
205
+ class NormAirmassStacker(BaseStacker):
206
+ """Adds a calculated normairmass for each pointing.
207
+
208
+ The normalized airmass is the airmass divided by the minimum airmass
209
+ achievable at each pointing (which is defined by the declination of
210
+ the field).
211
+
212
+ Parameters
213
+ ----------
214
+ airmass_col : `str`, optional
215
+ The name of the airmass column in the data.
216
+ dec_col : `str`, optional
217
+ The name of the declination column in the data.
218
+ degrees : `bool`, optional
219
+ If True, angle columns are assumed to be in degrees and returned in
220
+ degrees. If False, uses and calculates radians.
221
+ telescope_lat : `float`, optional
222
+ The latitude of the telescope, in degrees.
223
+ """
224
+
225
+ cols_added = ["normairmass"]
226
+
227
+ def __init__(
228
+ self,
229
+ airmass_col="airmass",
230
+ dec_col="fieldDec",
231
+ degrees=True,
232
+ telescope_lat=-30.2446388,
233
+ ):
234
+ self.units = ["X / Xmin"]
235
+ self.cols_req = [airmass_col, dec_col]
236
+ self.airmass_col = airmass_col
237
+ self.dec_col = dec_col
238
+ self.telescope_lat = telescope_lat
239
+ self.degrees = degrees
240
+
241
+ def _run(self, sim_data, cols_present=False):
242
+ # Run method is required to calculate column.
243
+ # Driver runs getColInfo to know what columns are needed from db &
244
+ # which are calculated, then gets data from db and then calculates
245
+ # additional columns (via run methods here).
246
+ if cols_present:
247
+ # Column already present in data; assume it is correct
248
+ # and does not need recalculating.
249
+ return sim_data
250
+ dec = sim_data[self.dec_col]
251
+ if self.degrees:
252
+ dec = np.radians(dec)
253
+ min_z_possible = np.abs(dec - np.radians(self.telescope_lat))
254
+ min_airmass_possible = 1.0 / np.cos(min_z_possible)
255
+ sim_data["normairmass"] = sim_data[self.airmass_col] / min_airmass_possible
256
+ return sim_data
257
+
258
+
259
+ class ZenithDistStacker(BaseStacker):
260
+ """Adds a calculated zenithDistance value for each pointing.
261
+
262
+ Parameters
263
+ ----------
264
+ alt_col : `str`, optional
265
+ The name of the altitude column in the data.
266
+ degrees : `bool`, optional
267
+ If True, data in alt_col is in degrees, and values for zenithDistance
268
+ will be in degrees. (Default).
269
+ If False, data in alt_col is in radians and zenithDistance values
270
+ will be in radians.
271
+ """
272
+
273
+ cols_added = ["zenithDistance"]
274
+
275
+ def __init__(self, alt_col="altitude", degrees=True):
276
+ self.alt_col = alt_col
277
+ self.degrees = degrees
278
+ if self.degrees:
279
+ self.units = ["degrees"]
280
+ else:
281
+ self.unit = ["radians"]
282
+ self.cols_req = [self.alt_col]
283
+
284
+ def _run(self, sim_data, cols_present=False):
285
+ """Calculate new column for zenith distance."""
286
+ if cols_present:
287
+ # Column already present in data; assume it is correct and does not
288
+ # need recalculating.
289
+ return sim_data
290
+ if self.degrees:
291
+ sim_data["zenithDistance"] = 90.0 - sim_data[self.alt_col]
292
+ else:
293
+ sim_data["zenithDistance"] = np.pi / 2.0 - sim_data[self.alt_col]
294
+ return sim_data
295
+
296
+
297
+ class ParallaxFactorStacker(BaseStacker):
298
+ """Add a parallax factor (in arcseconds) column for each visit.
299
+
300
+ Parameters
301
+ ----------
302
+ ra_col : `str`, optional
303
+ Name of the RA column in the data.
304
+ dec_col : `str`, optional
305
+ Name of the declination column in the data.
306
+ date_col : `str`, optional
307
+ Name of the exposure start time column in the data.
308
+ Date should be in units of MJD.
309
+ degrees : `bool`, optional
310
+ If true, assumes angles are in degrees. If False, radians.
311
+ """
312
+
313
+ cols_added = ["ra_pi_amp", "dec_pi_amp"]
314
+
315
+ def __init__(
316
+ self,
317
+ ra_col="fieldRA",
318
+ dec_col="fieldDec",
319
+ date_col="observationStartMJD",
320
+ degrees=True,
321
+ ):
322
+ self.ra_col = ra_col
323
+ self.dec_col = dec_col
324
+ self.date_col = date_col
325
+ self.units = ["arcsec", "arcsec"]
326
+ self.cols_req = [ra_col, dec_col, date_col]
327
+ self.degrees = degrees
328
+
329
+ def _gnomonic_project_toxy(self, ra1, dec1, r_acen, deccen):
330
+ # ra, dec values in RADIANS
331
+ # also used in Global Telescope Network website
332
+ cosc = np.sin(deccen) * np.sin(dec1) + np.cos(deccen) * np.cos(dec1) * np.cos(ra1 - r_acen)
333
+ x = np.cos(dec1) * np.sin(ra1 - r_acen) / cosc
334
+ y = (np.cos(deccen) * np.sin(dec1) - np.sin(deccen) * np.cos(dec1) * np.cos(ra1 - r_acen)) / cosc
335
+ return x, y
336
+
337
+ def _run(self, sim_data, cols_present=False):
338
+ if cols_present:
339
+ # Column already present in data; assume it is correct
340
+ # and does not need recalculating.
341
+ return sim_data
342
+ ra_pi_amp = np.zeros(np.size(sim_data), dtype=[("ra_pi_amp", "float")])
343
+ dec_pi_amp = np.zeros(np.size(sim_data), dtype=[("dec_pi_amp", "float")])
344
+ ra = sim_data[self.ra_col]
345
+ dec = sim_data[self.dec_col]
346
+ if self.degrees:
347
+ ra = np.radians(ra)
348
+ dec = np.radians(dec)
349
+
350
+ times = Time(sim_data[self.date_col], format="mjd")
351
+ c = SkyCoord(ra * u.rad, dec * u.rad, obstime=times)
352
+ geo_far = c.transform_to(GCRS)
353
+ c_near = SkyCoord(ra * u.rad, dec * u.rad, distance=1 * u.pc, obstime=times)
354
+ geo_near = c_near.transform_to(GCRS)
355
+
356
+ x_geo1, y_geo1 = self._gnomonic_project_toxy(geo_near.ra.rad, geo_near.dec.rad, ra, dec)
357
+ x_geo, y_geo = self._gnomonic_project_toxy(geo_far.ra.rad, geo_far.dec.rad, ra, dec)
358
+
359
+ # Return ra_pi_amp and dec_pi_amp in arcseconds.
360
+ ra_pi_amp[:] = np.degrees(x_geo1 - x_geo) * 3600.0
361
+ dec_pi_amp[:] = np.degrees(y_geo1 - y_geo) * 3600.0
362
+ sim_data["ra_pi_amp"] = ra_pi_amp
363
+ sim_data["dec_pi_amp"] = dec_pi_amp
364
+ return sim_data
365
+
366
+
367
+ class DcrStacker(BaseStacker):
368
+ """Add columns representing the expected RA/Dec offsets expected for
369
+ an object due to differential chromatic refraction, per visit.
370
+
371
+ For DCR calculation, we also need zenithDistance, HA, and PA -- but these
372
+ will be explicitly handled within this stacker so that setup is consistent
373
+ and they run in order. If those values have already been calculated
374
+ elsewhere, they will not be overwritten.
375
+
376
+ Parameters
377
+ ----------
378
+ filter_col : `str`, optional
379
+ The name of the column with filter names. Default 'filter'.
380
+ altCol : `str`, optional
381
+ Name of the column with altitude info. Default 'altitude'.
382
+ ra_col : `str`, optional
383
+ Name of the column with RA. Default 'fieldRA'.
384
+ dec_col : `str`, optional
385
+ Name of the column with Dec. Default 'fieldDec'.
386
+ lstCol : `str`, optional
387
+ Name of the column with local sidereal time. Default
388
+ 'observationStartLST'.
389
+ site : `str` or `rubin_scheduler.utils.Site`, optional
390
+ Name of the observory or a rubin_scheduler.utils.Site object.
391
+ Default 'LSST'.
392
+ mjdCol : `str`, optional
393
+ Name of column with modified julian date.
394
+ Default 'observationStartMJD'
395
+ dcr_magnitudes : dict
396
+ Magnitude of the DCR offset for each filter at an
397
+ altitude/zenith distance of 45 degrees.
398
+ Defaults u=0.07, g=0.07, r=0.50, i=0.045, z=0.042, y=0.04
399
+ (all values should be in arcseconds).
400
+
401
+ Returns
402
+ -------
403
+ data : `numpy.array`
404
+ Returns array with additional columns 'ra_dcr_amp' and 'dec_dcr_amp'
405
+ with the DCR offsets for each observation. Also runs ZenithDistStacker
406
+ and ParallacticAngleStacker.
407
+ """
408
+
409
+ cols_added = ["ra_dcr_amp", "dec_dcr_amp"] # zenithDist, HA, PA
410
+
411
+ def __init__(
412
+ self,
413
+ filter_col="band",
414
+ alt_col="altitude",
415
+ degrees=True,
416
+ ra_col="fieldRA",
417
+ dec_col="fieldDec",
418
+ lst_col="observationStartLST",
419
+ site="LSST",
420
+ mjd_col="observationStartMJD",
421
+ dcr_magnitudes=None,
422
+ ):
423
+ self.units = ["arcsec", "arcsec"]
424
+ if dcr_magnitudes is None:
425
+ # DCR amplitudes are in arcseconds.
426
+ self.dcr_magnitudes = {
427
+ "u": 0.07,
428
+ "g": 0.07,
429
+ "r": 0.050,
430
+ "i": 0.045,
431
+ "z": 0.042,
432
+ "y": 0.04,
433
+ }
434
+ else:
435
+ self.dcr_magnitudes = dcr_magnitudes
436
+ self.zd_col = "zenithDistance"
437
+ self.pa_col = "PA"
438
+ self.filter_col = filter_col
439
+ self.ra_col = ra_col
440
+ self.dec_col = dec_col
441
+ self.degrees = degrees
442
+ self.cols_req = [filter_col, ra_col, dec_col, alt_col, lst_col]
443
+ # 'zenithDist', 'PA', 'HA' are additional columns required, coming
444
+ # from other stackers which must also be configured -- so we handle
445
+ # this explicitly here.
446
+ self.zstacker = ZenithDistStacker(alt_col=alt_col, degrees=self.degrees)
447
+ self.pastacker = ParallacticAngleStacker(
448
+ ra_col=ra_col,
449
+ dec_col=dec_col,
450
+ mjd_col=mjd_col,
451
+ degrees=self.degrees,
452
+ lst_col=lst_col,
453
+ site=site,
454
+ )
455
+ # Note that RA/Dec could be coming from a dither stacker!
456
+ # But we will assume that coord stackers will be handled separately.
457
+
458
+ def _run(self, sim_data, cols_present=False):
459
+ if cols_present:
460
+ # Column already present in data; assume it is correct and does not
461
+ # need recalculating.
462
+ return sim_data
463
+ # Need to make sure the Zenith stacker gets run first Call _run method
464
+ # because already added these columns due to 'colsAdded' line.
465
+ sim_data = self.zstacker.run(sim_data)
466
+ sim_data = self.pastacker.run(sim_data)
467
+ if self.degrees:
468
+ zenith_tan = np.tan(np.radians(sim_data[self.zd_col]))
469
+ parallactic_angle = np.radians(sim_data[self.pa_col])
470
+ else:
471
+ zenith_tan = np.tan(sim_data[self.zd_col])
472
+ parallactic_angle = sim_data[self.pa_col]
473
+ dcr_in_ra = zenith_tan * np.sin(parallactic_angle)
474
+ dcr_in_dec = zenith_tan * np.cos(parallactic_angle)
475
+ for filtername in np.unique(sim_data[self.filter_col]):
476
+ fmatch = np.where(sim_data[self.filter_col] == filtername)
477
+ dcr_in_ra[fmatch] = self.dcr_magnitudes[filtername] * dcr_in_ra[fmatch]
478
+ dcr_in_dec[fmatch] = self.dcr_magnitudes[filtername] * dcr_in_dec[fmatch]
479
+ sim_data["ra_dcr_amp"] = dcr_in_ra
480
+ sim_data["dec_dcr_amp"] = dcr_in_dec
481
+ return sim_data
482
+
483
+
484
+ class HourAngleStacker(BaseStacker):
485
+ """Add the Hour Angle (in decimal hours) for each observation.
486
+
487
+ Parameters
488
+ ----------
489
+ lst_col : `str`, optional
490
+ Name of the LST column in the data.
491
+ ra_col : `str`, optional
492
+ Name of the RA column in the data.
493
+ degrees : `bool`, optional
494
+ If True, assumes angles (RA and LST) are in degrees.
495
+ If False, assumes radians.
496
+ """
497
+
498
+ cols_added = ["HA"]
499
+
500
+ def __init__(self, lst_col="observationStartLST", ra_col="fieldRA", degrees=True):
501
+ self.units = ["Hours"]
502
+ self.cols_req = [lst_col, ra_col]
503
+ self.lst_col = lst_col
504
+ self.ra_col = ra_col
505
+ self.degrees = degrees
506
+
507
+ def _run(self, sim_data, cols_present=False):
508
+ """HA = LST - RA"""
509
+ if cols_present:
510
+ # Column already present in data; assume it is correct and does not
511
+ # need recalculating.
512
+ return sim_data
513
+ if len(sim_data) == 0:
514
+ return sim_data
515
+ if self.degrees:
516
+ ra = np.radians(sim_data[self.ra_col])
517
+ lst = np.radians(sim_data[self.lst_col])
518
+ else:
519
+ ra = sim_data[self.ra_col]
520
+ lst = sim_data[self.lst_col]
521
+ # Check that LST is reasonable
522
+ if (np.min(lst) < 0) | (np.max(lst) > 2.0 * np.pi):
523
+ warnings.warn("LST values are not between 0 and 2 pi")
524
+ # Check that RA is reasonable
525
+ if (np.min(ra) < 0) | (np.max(ra) > 2.0 * np.pi):
526
+ warnings.warn("RA values are not between 0 and 2 pi")
527
+ ha = lst - ra
528
+ # Wrap the results so HA between -pi and pi
529
+ ha = np.where(ha < -np.pi, ha + 2.0 * np.pi, ha)
530
+ ha = np.where(ha > np.pi, ha - 2.0 * np.pi, ha)
531
+ # Convert radians to hours
532
+ sim_data["HA"] = ha * 12 / np.pi
533
+ return sim_data
534
+
535
+
536
+ class ParallacticAngleStacker(BaseStacker):
537
+ """Add the calculated parallactic angle to each visit.
538
+
539
+ Parameters
540
+ ----------
541
+ ra_col : `str`, optional
542
+ Name of the RA column in the data.
543
+ dec_col : `str`, optional
544
+ Name of the declination column in the data.
545
+ degrees : `bool`, optional
546
+ If True, assumes ra and dec in degrees and returns Parallactic Angle
547
+ in degrees. If False, assumes and returns radians.
548
+ mjd_col : `str`, optional
549
+ Name of the observation MJD column in the data.
550
+ lst_col : `str`, optional
551
+ Name of the LST column in the data.
552
+ site : `str` or `rubin_scheduler.utils.Site`, optional
553
+ Name of the observory or a rubin_scheduler.utils.Site object.
554
+ Default 'LSST'.
555
+ """
556
+
557
+ cols_added = ["PA"]
558
+
559
+ def __init__(
560
+ self,
561
+ ra_col="fieldRA",
562
+ dec_col="fieldDec",
563
+ degrees=True,
564
+ mjd_col="observationStartMJD",
565
+ lst_col="observationStartLST",
566
+ site="LSST",
567
+ ):
568
+ self.lst_col = lst_col
569
+ self.ra_col = ra_col
570
+ self.dec_col = dec_col
571
+ self.degrees = degrees
572
+ self.mjd_col = mjd_col
573
+ self.site = Site(name=site)
574
+ self.units = ["radians"]
575
+ self.cols_req = [self.ra_col, self.dec_col, self.mjd_col, self.lst_col]
576
+ self.ha_stacker = HourAngleStacker(lst_col=lst_col, ra_col=ra_col, degrees=self.degrees)
577
+
578
+ def _run(self, sim_data, cols_present=False):
579
+ # Equation from:
580
+ # http://www.gb.nrao.edu/~rcreager/GBTMetrology/140ft/l0058/gbtmemo52/memo52.html
581
+ # or
582
+ # http://www.gb.nrao.edu/GBT/DA/gbtidl/release2pt9/contrib/contrib/parangle.pro
583
+ if cols_present:
584
+ # Column already present in data; assume it is correct and does not
585
+ # need recalculating.
586
+ return sim_data
587
+ # Using the run method (not _run) means that if HA is present, it will
588
+ # not be recalculated.
589
+ sim_data = self.ha_stacker.run(sim_data)
590
+ if self.degrees:
591
+ dec = np.radians(sim_data[self.dec_col])
592
+ else:
593
+ dec = sim_data[self.dec_col]
594
+ sim_data["PA"] = np.arctan2(
595
+ np.sin(sim_data["HA"] * np.pi / 12.0),
596
+ (
597
+ np.cos(dec) * np.tan(self.site.latitude_rad)
598
+ - np.sin(dec) * np.cos(sim_data["HA"] * np.pi / 12.0)
599
+ ),
600
+ )
601
+ if self.degrees:
602
+ sim_data["PA"] = np.degrees(sim_data["PA"])
603
+ return sim_data
604
+
605
+
606
+ class OverheadStacker(BaseStacker):
607
+ """Add time between visits in seconds.
608
+
609
+ Parameters
610
+ ----------
611
+ max_gap : `float`, optional
612
+ The maximum gap between observations, in minutes.
613
+ Assume anything longer the dome has closed.
614
+ Defaults to infinity.
615
+ mjd_col : `str`, optional
616
+ The name of the column with the observation start MJD.
617
+ Defaults to "observationStartMJD".
618
+ visit_time_col : `str`, optional
619
+ The name of the column with the total visit time (on-sky plus
620
+ shutter and other overheads).
621
+ Defaults to "visitTime".
622
+ exposure_time_col : `str`, optional
623
+ The name of the column with the visit on-sky exposure time.
624
+ Defaults to "visitExposureTime."
625
+ """
626
+
627
+ cols_added = ["overhead"]
628
+
629
+ def __init__(
630
+ self,
631
+ max_gap=np.inf,
632
+ mjd_col="observationStartMJD",
633
+ visit_time_col="visitTime",
634
+ exposure_time_col="visitExposureTime",
635
+ ):
636
+ # Set max_gap in minutes to match API of existing BruteOSFMetric
637
+ self.max_gap = max_gap
638
+ self.mjd_col = mjd_col
639
+ self.visit_time_col = visit_time_col
640
+ self.exposure_time_col = exposure_time_col
641
+ self.units = ["seconds"]
642
+ self.day_obs_mjd_stacker = DayObsMJDStacker(self.mjd_col)
643
+ self.cols_req = [self.mjd_col, self.visit_time_col, self.exposure_time_col]
644
+ self.cols_req.extend(self.day_obs_mjd_stacker.cols_req)
645
+ self.cols_req = list(set(self.cols_req))
646
+
647
+ def _run(self, sim_data, cols_present=False):
648
+ if cols_present:
649
+ # Column already present in data; assume it is correct and does not
650
+ # need recalculating.
651
+ return sim_data
652
+
653
+ # Count overhead as any time from the shutter close of the previous
654
+ # visit, to the shutter close of the current visit, minus the
655
+ # current on-sky exposure time.
656
+ # This is all non-exposure time required for this visit --
657
+ # includes slew time to this target, any shutter overheads for this
658
+ # visit, and any readouts not absorbed in slewtime for this visit.
659
+ observation_end_mjd = sim_data[self.mjd_col] + sim_data[self.visit_time_col] / (24 * 60 * 60)
660
+ overhead = (
661
+ np.diff(observation_end_mjd, prepend=np.nan) * 24 * 60 * 60 - sim_data[self.exposure_time_col]
662
+ )
663
+
664
+ # Rough heuristic not to count downtime due to weather or instrument
665
+ # problems as overhead. A more reliable way to do this would be to
666
+ # use other sources of weather data or problem reporting to identify
667
+ # these gaps.
668
+ # We might also explicitly want to look at the gaps that arise from
669
+ # these problems in order to measure time lost to these causes.
670
+ overhead[overhead > self.max_gap * 60] = np.nan
671
+
672
+ # If the gap includes a change of night, it isn't really overhead. For
673
+ # most reasonable finite values of self.max_gap, this is never
674
+ # relevant, but it's sometimes useful to set max_gap to inifinity to
675
+ # catch all delays in the night. (The comment above.) But, in
676
+ # even in this case, we would still not want to include gaps between
677
+ # nights.
678
+ day_obs_mjd = self.day_obs_mjd_stacker.run(sim_data)["day_obs_mjd"]
679
+ different_night = np.diff(day_obs_mjd, prepend=np.nan) != 0
680
+ overhead[different_night] = np.nan
681
+
682
+ sim_data[self.cols_added[0]] = overhead
683
+ return sim_data