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,1029 @@
1
+ __all__ = (
2
+ "set_color_lims",
3
+ "set_color_map",
4
+ "HealpixSkyMap",
5
+ "HealpixPowerSpectrum",
6
+ "HealpixHistogram",
7
+ "BaseHistogram",
8
+ "BaseSkyMap",
9
+ "HealpixSDSSSkyMap",
10
+ "LambertSkyMap",
11
+ )
12
+
13
+ import copy
14
+ import numbers
15
+ import warnings
16
+
17
+ import healpy as hp
18
+ import matplotlib as mpl
19
+ import matplotlib.cm as cm
20
+ import matplotlib.pyplot as plt
21
+ import numpy as np
22
+ import numpy.ma as ma
23
+ from astropy import units as u
24
+ from astropy.coordinates import SkyCoord
25
+ from matplotlib import colors, ticker
26
+ from matplotlib.collections import PatchCollection
27
+ from matplotlib.patches import Ellipse
28
+ from matplotlib.ticker import FuncFormatter
29
+ from rubin_scheduler.utils import _healbin
30
+
31
+ from rubin_sim.maf.utils import optimal_bins, percentile_clipping
32
+
33
+ from .perceptual_rainbow import make_pr_cmap
34
+ from .plot_handler import BasePlotter, apply_zp_norm
35
+
36
+ perceptual_rainbow = make_pr_cmap()
37
+
38
+ base_default_plot_dict = {
39
+ "title": None,
40
+ "xlabel": None,
41
+ "label": None,
42
+ "log_scale": False,
43
+ "percentile_clip": None,
44
+ "norm_val": None,
45
+ "zp": None,
46
+ "cbar_orientation": "horizontal",
47
+ "cbar_format": None,
48
+ "cmap": perceptual_rainbow,
49
+ "cbar_edge": True,
50
+ "n_ticks": 10,
51
+ "color_min": None,
52
+ "color_max": None,
53
+ "extend": "neither",
54
+ "x_min": None,
55
+ "x_max": None,
56
+ "y_min": None,
57
+ "y_max": None,
58
+ "labelsize": None,
59
+ "fontsize": None,
60
+ "figsize": None,
61
+ "subplot": 111,
62
+ "mask_below": None,
63
+ }
64
+
65
+
66
+ def set_color_lims(metric_value, plot_dict, key_min="color_min", key_max="color_max"):
67
+ """Set up x or color bar limits."""
68
+ # Use plotdict values if available
69
+ color_min = plot_dict[key_min]
70
+ color_max = plot_dict[key_max]
71
+ # If either is not set and we have data:
72
+ if color_min is None or color_max is None:
73
+ if np.size(metric_value.compressed()) > 0:
74
+ # is percentile clipping set?
75
+ if plot_dict["percentile_clip"] is not None:
76
+ pc_min, pc_max = percentile_clipping(
77
+ metric_value.compressed(), percentile=plot_dict["percentile_clip"]
78
+ )
79
+ tempcolor_min = pc_min
80
+ tempcolor_max = pc_max
81
+ # If not, just use the data limits.
82
+ else:
83
+ tempcolor_min = metric_value.compressed().min()
84
+ tempcolor_max = metric_value.compressed().max()
85
+ # But make sure there is some range on the colorbar
86
+ if tempcolor_min == tempcolor_max:
87
+ tempcolor_min = tempcolor_min - 0.5
88
+ tempcolor_max = tempcolor_max + 0.5
89
+ tempcolor_min, tempcolor_max = np.sort([tempcolor_min, tempcolor_max])
90
+ else:
91
+ # There is no metric data to plot, but here we are.
92
+ tempcolor_min = 0
93
+ tempcolor_max = 1
94
+ if color_min is None:
95
+ color_min = tempcolor_min
96
+ if color_max is None:
97
+ color_max = tempcolor_max
98
+ return [color_min, color_max]
99
+
100
+
101
+ def set_color_map(plot_dict):
102
+ cmap = plot_dict["cmap"]
103
+ if cmap is None:
104
+ cmap = "perceptual_rainbow"
105
+ if isinstance(cmap, str):
106
+ cmap = getattr(cm, cmap)
107
+ # Set background and masked pixel colors default healpy white and gray.
108
+ cmap = copy.copy(cmap)
109
+ cmap.set_over(cmap(1.0))
110
+ cmap.set_under("w")
111
+ cmap.set_bad("gray")
112
+ return cmap
113
+
114
+
115
+ class HealpixSkyMap(BasePlotter):
116
+ """
117
+ Generate a sky map of healpix metric values using healpy's mollweide view.
118
+ """
119
+
120
+ def __init__(self):
121
+ super(HealpixSkyMap, self).__init__()
122
+ # Set the plot_type
123
+ self.plot_type = "SkyMap"
124
+ self.object_plotter = False
125
+ # Set up the default plotting parameters.
126
+ self.default_plot_dict = {}
127
+ self.default_plot_dict.update(base_default_plot_dict)
128
+ self.default_plot_dict.update(
129
+ {
130
+ "rot": (0, 0, 0),
131
+ "flip": "astro",
132
+ "coord": "C",
133
+ "nside": 8,
134
+ "reduce_func": np.mean,
135
+ "visufunc": hp.mollview,
136
+ }
137
+ )
138
+ # Note: for alt/az sky maps using the healpix plotter, you can use
139
+ # {'rot': (90, 90, 90), 'flip': 'geo'}
140
+ self.healpy_visufunc_params = {}
141
+ self.ax = None
142
+ self.im = None
143
+
144
+ def __call__(self, metric_value_in, slicer, user_plot_dict, fig=None):
145
+ """
146
+ Parameters
147
+ ----------
148
+ metric_value : `numpy.ma.MaskedArray`
149
+ The metric values from the bundle.
150
+ slicer : `rubin_sim.maf.slicers.TwoDSlicer`
151
+ The slicer.
152
+ user_plot_dict: `dict`
153
+ Dictionary of plot parameters set by user
154
+ (overrides default values).
155
+ fig : `matplotlib.figure.Figure`
156
+ Matplotlib figure number to use. Default = None, starts new figure.
157
+
158
+ Returns
159
+ -------
160
+ fig : `matplotlib.figure.Figure`
161
+ Figure with the plot.
162
+ """
163
+ # Override the default plotting parameters with user specified values.
164
+ plot_dict = {}
165
+ plot_dict.update(self.default_plot_dict)
166
+ plot_dict.update(user_plot_dict)
167
+
168
+ self.healpy_visufunc = plot_dict["visufunc"]
169
+
170
+ # Check if we have a valid HEALpix slicer
171
+ if "Heal" in slicer.slicer_name:
172
+ # Update the metric data with zeropoint or normalization.
173
+ metric_value = apply_zp_norm(metric_value_in, plot_dict)
174
+ else:
175
+ # Bin the values up on a healpix grid.
176
+ metric_value = _healbin(
177
+ slicer.slice_points["ra"],
178
+ slicer.slice_points["dec"],
179
+ metric_value_in.filled(slicer.badval),
180
+ nside=plot_dict["nside"],
181
+ reduce_func=plot_dict["reduce_func"],
182
+ fill_val=slicer.badval,
183
+ )
184
+ mask = np.zeros(metric_value.size)
185
+ mask[np.where(metric_value == slicer.badval)] = 1
186
+
187
+ metric_value = ma.array(metric_value, mask=mask)
188
+ metric_value = apply_zp_norm(metric_value, plot_dict)
189
+
190
+ if plot_dict["mask_below"] is not None:
191
+ to_mask = np.where(metric_value <= plot_dict["mask_below"])[0]
192
+ metric_value.mask[to_mask] = True
193
+ badval = hp.UNSEEN
194
+ else:
195
+ badval = slicer.badval
196
+
197
+ # Generate a full-sky plot.
198
+ if fig is None:
199
+ fig = plt.figure(figsize=plot_dict["figsize"])
200
+ # Set up color bar limits.
201
+ clims = set_color_lims(metric_value, plot_dict)
202
+ cmap = set_color_map(plot_dict)
203
+ # Set log scale?
204
+ norm = None
205
+ if plot_dict["log_scale"]:
206
+ norm = "log"
207
+ # Avoid trying to log scale when zero is in the range.
208
+ if (norm == "log") & ((clims[0] <= 0 <= clims[1]) or (clims[0] >= 0 >= clims[1])):
209
+ # Try something simple
210
+ above = metric_value[np.where(metric_value > 0)]
211
+ if len(above) > 0:
212
+ clims[0] = above.max()
213
+ # If still bad, give up and turn off norm
214
+ if (clims[0] <= 0 <= clims[1]) or (clims[0] >= 0 >= clims[1]):
215
+ norm = None
216
+ warnings.warn(
217
+ "Using norm was set to log, but color limits pass through 0. "
218
+ "Adjusting so plotting doesn't fail"
219
+ )
220
+ if plot_dict["coord"] == "C":
221
+ notext = True
222
+ else:
223
+ notext = False
224
+
225
+ visufunc_params = {
226
+ "title": plot_dict["title"],
227
+ "cbar": False,
228
+ "min": clims[0],
229
+ "max": clims[1],
230
+ "rot": plot_dict["rot"],
231
+ "flip": plot_dict["flip"],
232
+ "coord": plot_dict["coord"],
233
+ "cmap": cmap,
234
+ "norm": norm,
235
+ "sub": plot_dict["subplot"],
236
+ "fig": fig.number,
237
+ "notext": notext,
238
+ }
239
+ # Keys to specify only if present in plot_dict
240
+ for key in (
241
+ "reso",
242
+ "xsize",
243
+ "lamb",
244
+ "reuse_axes",
245
+ "alpha",
246
+ "badcolor",
247
+ "bgcolor",
248
+ ):
249
+ if key in plot_dict:
250
+ visufunc_params[key] = plot_dict[key]
251
+
252
+ visufunc_params.update(self.healpy_visufunc_params)
253
+ self.healpy_visufunc(metric_value.filled(badval), **visufunc_params)
254
+
255
+ # Add colorbar
256
+ # (not using healpy default colorbar because we want more tickmarks).
257
+ self.ax = plt.gca()
258
+ im = self.ax.get_images()[0]
259
+
260
+ # Add a graticule (grid) over the globe.
261
+ if "noGraticule" not in plot_dict:
262
+ hp.graticule(dpar=30, dmer=30)
263
+
264
+ # Add label.
265
+ if plot_dict["label"] is not None:
266
+ plt.figtext(0.8, 0.8, "%s" % (plot_dict["label"]))
267
+ # Make a color bar. Suppress excessive colorbar warnings.
268
+ with warnings.catch_warnings():
269
+ warnings.simplefilter("ignore")
270
+ # The vertical colorbar is primarily aimed at the movie
271
+ # but may be useful for other purposes
272
+ if plot_dict["extend"] != "neither":
273
+ extendrect = False
274
+ else:
275
+ extendrect = True
276
+ if plot_dict["cbar_orientation"].lower() == "vertical":
277
+ cb = plt.colorbar(
278
+ im,
279
+ shrink=0.5,
280
+ extendrect=extendrect,
281
+ extend=plot_dict["extend"],
282
+ location="right",
283
+ format=plot_dict["cbar_format"],
284
+ )
285
+ else:
286
+ # Most of the time we just want a standard horizontal colorbar
287
+ cb = plt.colorbar(
288
+ im,
289
+ shrink=0.75,
290
+ aspect=25,
291
+ pad=0.1,
292
+ orientation="horizontal",
293
+ format=plot_dict["cbar_format"],
294
+ extendrect=extendrect,
295
+ extend=plot_dict["extend"],
296
+ )
297
+ cb.set_label(plot_dict["xlabel"], fontsize=plot_dict["fontsize"])
298
+ if plot_dict["labelsize"] is not None:
299
+ cb.ax.tick_params(labelsize=plot_dict["labelsize"])
300
+ if norm == "log":
301
+ tick_locator = ticker.LogLocator(numticks=plot_dict["n_ticks"])
302
+ cb.locator = tick_locator
303
+ cb.update_ticks()
304
+ if (plot_dict["n_ticks"] is not None) & (norm != "log"):
305
+ tick_locator = ticker.MaxNLocator(nbins=plot_dict["n_ticks"])
306
+ cb.locator = tick_locator
307
+ cb.update_ticks()
308
+ # If outputing to PDF, this fixes the colorbar white stripes
309
+ if plot_dict["cbar_edge"]:
310
+ cb.solids.set_edgecolor("face")
311
+ return fig
312
+
313
+
314
+ class HealpixPowerSpectrum(BasePlotter):
315
+ def __init__(self):
316
+ self.plot_type = "PowerSpectrum"
317
+ self.object_plotter = False
318
+ self.default_plot_dict = {}
319
+ self.default_plot_dict.update(base_default_plot_dict)
320
+ self.default_plot_dict.update({"maxl": None, "removeDipole": True, "linestyle": "-"})
321
+
322
+ def __call__(self, metric_value, slicer, user_plot_dict, fig=None):
323
+ """Generate and plot the power spectrum of metric_values
324
+ (for metrics calculated on a healpix grid).
325
+ """
326
+ if "Healpix" not in slicer.slicer_name:
327
+ raise ValueError("HealpixPowerSpectrum for use with healpix metricBundles.")
328
+ plot_dict = {}
329
+ plot_dict.update(self.default_plot_dict)
330
+ plot_dict.update(user_plot_dict)
331
+
332
+ if fig is None:
333
+ fig = plt.figure(figsize=plot_dict["figsize"])
334
+ if plot_dict["subplot"] != "111":
335
+ fig.add_subplot(plot_dict["subplot"])
336
+ # If the mask is True everywhere (no data), just plot zeros
337
+ if False not in metric_value.mask:
338
+ return None
339
+ if plot_dict["removeDipole"]:
340
+ cl = hp.anafast(
341
+ hp.remove_dipole(metric_value.filled(hp.UNSEEN)),
342
+ lmax=plot_dict["maxl"],
343
+ )
344
+ else:
345
+ cl = hp.anafast(metric_value.filled(hp.UNSEEN), lmax=plot_dict["maxl"])
346
+ ell = np.arange(np.size(cl))
347
+ if plot_dict["removeDipole"]:
348
+ condition = ell > 1
349
+ else:
350
+ condition = ell > 0
351
+ ell = ell[condition]
352
+ cl = cl[condition]
353
+ # Plot the results.
354
+ plt.plot(
355
+ ell,
356
+ (cl * ell * (ell + 1)) / 2.0 / np.pi,
357
+ color=plot_dict["color"],
358
+ linestyle=plot_dict["linestyle"],
359
+ label=plot_dict["label"],
360
+ )
361
+ if cl.max() > 0 and plot_dict["log_scale"]:
362
+ plt.yscale("log")
363
+ plt.xlabel(r"$l$", fontsize=plot_dict["fontsize"])
364
+ plt.ylabel(r"$l(l+1)C_l/(2\pi)$", fontsize=plot_dict["fontsize"])
365
+ if plot_dict["labelsize"] is not None:
366
+ plt.tick_params(axis="x", labelsize=plot_dict["labelsize"])
367
+ plt.tick_params(axis="y", labelsize=plot_dict["labelsize"])
368
+ if plot_dict["title"] is not None:
369
+ plt.title(plot_dict["title"])
370
+ # Return figure number
371
+ # (so we can reuse/add onto/save this figure if desired).
372
+ return fig
373
+
374
+
375
+ class HealpixHistogram(BasePlotter):
376
+ def __init__(self):
377
+ self.plot_type = "Histogram"
378
+ self.object_plotter = False
379
+ self.default_plot_dict = {}
380
+ self.default_plot_dict.update(base_default_plot_dict)
381
+ self.default_plot_dict.update(
382
+ {
383
+ "ylabel": "Area (1000s of square degrees)",
384
+ "bins": None,
385
+ "bin_size": None,
386
+ "cumulative": False,
387
+ "scale": None,
388
+ "linestyle": "-",
389
+ }
390
+ )
391
+ self.base_hist = BaseHistogram()
392
+
393
+ def __call__(self, metric_value, slicer, user_plot_dict, fig=None):
394
+ """Histogram metric_value for all healpix points."""
395
+ if "Healpix" not in slicer.slicer_name:
396
+ raise ValueError("HealpixHistogram is for use with healpix slicer.")
397
+ plot_dict = {}
398
+ plot_dict.update(self.default_plot_dict)
399
+ plot_dict.update(user_plot_dict)
400
+ if plot_dict["scale"] is None:
401
+ plot_dict["scale"] = hp.nside2pixarea(slicer.nside, degrees=True) / 1000.0
402
+ fig = self.base_hist(metric_value, slicer, plot_dict, fig=fig)
403
+ return fig
404
+
405
+
406
+ class BaseHistogram(BasePlotter):
407
+ def __init__(self):
408
+ self.plot_type = "Histogram"
409
+ self.object_plotter = False
410
+ self.default_plot_dict = {}
411
+ self.default_plot_dict.update(base_default_plot_dict)
412
+ self.default_plot_dict.update(
413
+ {
414
+ "ylabel": "Count",
415
+ "bins": None,
416
+ "bin_size": None,
417
+ "cumulative": False,
418
+ "scale": 1.0,
419
+ "yaxisformat": "%.3f",
420
+ "linestyle": "-",
421
+ }
422
+ )
423
+
424
+ def __call__(self, metric_value_in, slicer, user_plot_dict, fig=None):
425
+ """
426
+ Plot a histogram of metric_values
427
+ (such as would come from a spatial slicer).
428
+ """
429
+ # Adjust metric values by zeropoint or norm_val,
430
+ # and use 'compressed' version of masked array.
431
+ plot_dict = {}
432
+ plot_dict.update(self.default_plot_dict)
433
+ plot_dict.update(user_plot_dict)
434
+ metric_value = apply_zp_norm(metric_value_in, plot_dict)
435
+ # Toss any NaNs or infs
436
+ metric_value = metric_value[np.isfinite(metric_value)]
437
+ x_min, x_max = set_color_lims(metric_value, plot_dict, key_min="x_min", key_max="x_max")
438
+ metric_value = metric_value.compressed()
439
+ # Set up the bins for the histogram.
440
+ # User specified 'bins' overrides 'bin_size'.
441
+ # Note that 'bins' could be a single number or an array,
442
+ # simply passed to plt.histogram.
443
+ if plot_dict["bins"] is not None:
444
+ bins = plot_dict["bins"]
445
+ elif plot_dict["bin_size"] is not None:
446
+ # If generating a cumulative histogram,
447
+ # want to use full range of data (but with given bin_size).
448
+ # but if user set histRange to be wider than full range of data,
449
+ # then extend bins to cover this range,
450
+ # so we can make prettier plots.
451
+ if plot_dict["cumulative"]:
452
+ # Potentially, expand the range for the cumulative histogram.
453
+ bmin = np.min([metric_value.min(), x_min])
454
+ bmax = np.max([metric_value.max(), x_max])
455
+ bins = np.arange(bmin, bmax + plot_dict["bin_size"] / 2.0, plot_dict["bin_size"])
456
+ # Otherwise, not cumulative so just use metric values,
457
+ # without potential expansion.
458
+ else:
459
+ bins = np.arange(
460
+ x_min,
461
+ x_max + plot_dict["bin_size"] / 2.0,
462
+ plot_dict["bin_size"],
463
+ )
464
+ # Catch edge-case where there is only 1 bin value
465
+ if bins.size < 2:
466
+ bins = np.arange(
467
+ bins.min() - plot_dict["bin_size"] * 2.0,
468
+ bins.max() + plot_dict["bin_size"] * 2.0,
469
+ plot_dict["bin_size"],
470
+ )
471
+ else:
472
+ # If user did not specify bins or bin_size,
473
+ # then we try to figure out a good number of bins.
474
+ bins = optimal_bins(metric_value)
475
+ # Generate plots.
476
+ if fig is None:
477
+ fig = plt.figure(figsize=plot_dict["figsize"])
478
+ if (
479
+ plot_dict["subplot"] != 111
480
+ and plot_dict["subplot"] != (1, 1, 1)
481
+ and plot_dict["subplot"] is not None
482
+ ):
483
+ ax = fig.add_subplot(plot_dict["subplot"])
484
+ else:
485
+ ax = plt.gca()
486
+ # Check if any data falls within histRange,
487
+ # because otherwise histogram generation will fail.
488
+ if isinstance(bins, np.ndarray):
489
+ condition = (metric_value >= bins.min()) & (metric_value <= bins.max())
490
+ else:
491
+ condition = (metric_value >= x_min) & (metric_value <= x_max)
492
+ plot_value = metric_value[condition]
493
+ if len(plot_value) == 0:
494
+ # No data is within histRange/bins.
495
+ # So let's just make a simple histogram anyway.
496
+ n, b, p = plt.hist(
497
+ metric_value,
498
+ bins=50,
499
+ histtype="step",
500
+ cumulative=plot_dict["cumulative"],
501
+ log=plot_dict["log_scale"],
502
+ label=plot_dict["label"],
503
+ color=plot_dict["color"],
504
+ )
505
+ else:
506
+ # There is data to plot, and we've already ensured
507
+ # x_min/x_max/bins are more than single value.
508
+ n, b, p = plt.hist(
509
+ metric_value,
510
+ bins=bins,
511
+ range=[x_min, x_max],
512
+ histtype="step",
513
+ log=plot_dict["log_scale"],
514
+ cumulative=plot_dict["cumulative"],
515
+ label=plot_dict["label"],
516
+ color=plot_dict["color"],
517
+ )
518
+ hist_ylims = plt.ylim()
519
+ if n.max() > hist_ylims[1]:
520
+ plt.ylim(top=n.max())
521
+ if n.min() < hist_ylims[0] and not plot_dict["log_scale"]:
522
+ plt.ylim(bottom=n.min())
523
+ # Fill in axes labels and limits.
524
+ # Option to use 'scale' to turn y axis into area or other value.
525
+
526
+ def mjr_formatter(y, pos):
527
+ if not isinstance(plot_dict["scale"], numbers.Number):
528
+ raise ValueError('plot_dict["scale"] must be a number to scale the y axis.')
529
+ return plot_dict["yaxisformat"] % (y * plot_dict["scale"])
530
+
531
+ ax.yaxis.set_major_formatter(FuncFormatter(mjr_formatter))
532
+ # Set optional x, y limits.
533
+ if "x_min" in plot_dict:
534
+ plt.xlim(left=plot_dict["x_min"])
535
+ if "x_max" in plot_dict:
536
+ plt.xlim(right=plot_dict["x_max"])
537
+ if "y_min" in plot_dict:
538
+ plt.ylim(bottom=plot_dict["y_min"])
539
+ if "y_max" in plot_dict:
540
+ plt.ylim(top=plot_dict["y_max"])
541
+ # Set/Add various labels.
542
+ plt.xlabel(plot_dict["xlabel"], fontsize=plot_dict["fontsize"])
543
+ plt.ylabel(plot_dict["ylabel"], fontsize=plot_dict["fontsize"])
544
+ plt.title(plot_dict["title"])
545
+ if plot_dict["labelsize"] is not None:
546
+ plt.tick_params(axis="x", labelsize=plot_dict["labelsize"])
547
+ plt.tick_params(axis="y", labelsize=plot_dict["labelsize"])
548
+ # Return figure
549
+ return fig
550
+
551
+
552
+ class BaseSkyMap(BasePlotter):
553
+ def __init__(self):
554
+ self.plot_type = "SkyMap"
555
+ self.object_plotter = False # unless 'metricIsColor' is true..
556
+ self.default_plot_dict = {}
557
+ self.default_plot_dict.update(base_default_plot_dict)
558
+ self.default_plot_dict.update(
559
+ {
560
+ "projection": "aitoff",
561
+ "radius": np.radians(1.75),
562
+ "alpha": 1.0,
563
+ "plotMask": False,
564
+ "metricIsColor": False,
565
+ "cbar": True,
566
+ "raCen": 0.0,
567
+ "mwZone": True,
568
+ "bgcolor": "gray",
569
+ }
570
+ )
571
+
572
+ def _plot_tissot_ellipse(self, lon, lat, radius, **kwargs):
573
+ """Plot Tissot Ellipse/Tissot Indicatrix
574
+
575
+ Parameters
576
+ ----------
577
+ lon : `float` or array_like
578
+ longitude-like of ellipse centers (radians)
579
+ lat : `float` or array_like
580
+ latitude-like of ellipse centers (radians)
581
+ radius : `float` or array_like
582
+ radius of ellipses (radians)
583
+ **kwargs : `dict`
584
+ Keyword argument which will be passed to
585
+ `matplotlib.patches.Ellipse`.
586
+
587
+ Returns
588
+ -------
589
+ ellipses : `list` [ `matplotlib.patches.Ellipse` ]
590
+ List of ellipses to add to the plot.
591
+
592
+ # The code in this method adapted from astroML, which is BSD-licensed.
593
+ # See http: //github.com/astroML/astroML for details.
594
+ """
595
+ # Code adapted from astroML, which is BSD-licensed.
596
+ # See http: //github.com/astroML/astroML for details.
597
+ ellipses = []
598
+ for ll, bb, diam in np.broadcast(lon, lat, radius * 2.0):
599
+ el = Ellipse((ll, bb), diam / np.cos(bb), diam, **kwargs)
600
+ ellipses.append(el)
601
+ return ellipses
602
+
603
+ def _plot_ecliptic(self, ra_cen=0, ax=None):
604
+ """
605
+ Plot a red line at location of ecliptic.
606
+ """
607
+ if ax is None:
608
+ ax = plt.gca()
609
+ ecinc = 23.439291 * (np.pi / 180.0)
610
+ ra_ec = np.arange(0, np.pi * 2.0, (np.pi * 2.0 / 360.0))
611
+ dec_ec = np.sin(ra_ec) * ecinc
612
+ lon = -(ra_ec - ra_cen - np.pi) % (np.pi * 2) - np.pi
613
+ ax.plot(lon, dec_ec, "r.", markersize=1.8, alpha=0.4)
614
+
615
+ def _plot_mw_zone(
616
+ self,
617
+ ra_cen=0,
618
+ peak_width=np.radians(10.0),
619
+ taper_length=np.radians(80.0),
620
+ ax=None,
621
+ ):
622
+ """
623
+ Plot blue lines to mark the milky way galactic exclusion zone.
624
+ """
625
+ if ax is None:
626
+ ax = plt.gca()
627
+ # Calculate galactic coordinates for mw location.
628
+ step = 0.02
629
+ gal_l = np.arange(-np.pi, np.pi + step / 2.0, step)
630
+ val = peak_width * np.cos(gal_l / taper_length * np.pi / 2.0)
631
+ gal_b1 = np.where(np.abs(gal_l) <= taper_length, val, 0)
632
+ gal_b2 = np.where(np.abs(gal_l) <= taper_length, -val, 0)
633
+ # Convert to ra/dec.
634
+ # Convert to lon/lat and plot.
635
+ c = SkyCoord(l=gal_l * u.rad, b=gal_b1 * u.rad, frame="galactic").transform_to("icrs")
636
+ ra = c.ra.rad
637
+ dec = c.dec.rad
638
+ lon = -(ra - ra_cen - np.pi) % (np.pi * 2) - np.pi
639
+ ax.plot(lon, dec, "b.", markersize=1.8, alpha=0.4)
640
+ c = SkyCoord(l=gal_l * u.rad, b=gal_b2 * u.rad, frame="galactic").transform_to("icrs")
641
+ ra = c.ra.rad
642
+ dec = c.dec.rad
643
+ lon = -(ra - ra_cen - np.pi) % (np.pi * 2) - np.pi
644
+ ax.plot(lon, dec, "b.", markersize=1.8, alpha=0.4)
645
+
646
+ def __call__(self, metric_value_in, slicer, user_plot_dict, fig=None):
647
+ """
648
+ Plot the sky map of metric_value for a generic spatial slicer.
649
+ """
650
+ if "ra" not in slicer.slice_points or "dec" not in slicer.slice_points:
651
+ err_message = 'SpatialSlicer must contain "ra" and "dec" in slice_points metadata.'
652
+ err_message += " SlicePoints only contains keys %s." % (slicer.slice_points.keys())
653
+ raise ValueError(err_message)
654
+ plot_dict = {}
655
+ plot_dict.update(self.default_plot_dict)
656
+ plot_dict.update(user_plot_dict)
657
+ metric_value = apply_zp_norm(metric_value_in, plot_dict)
658
+
659
+ if fig is None:
660
+ fig = plt.figure(figsize=plot_dict["figsize"])
661
+ # other projections available include
662
+ # ['aitoff', 'hammer', 'lambert', 'mollweide', 'polar', 'rectilinear']
663
+ ax = fig.add_subplot(plot_dict["subplot"], projection=plot_dict["projection"])
664
+ # Set up valid datapoints and color_min/max values.
665
+ if plot_dict["plotMask"]:
666
+ # Plot all data points.
667
+ good = np.ones(len(metric_value), dtype="bool")
668
+ else:
669
+ # Only plot points which are not masked.
670
+ # Flip numpy ma mask where 'False' == 'good'.
671
+ good = ~metric_value.mask
672
+
673
+ # Add ellipses at RA/Dec locations - but don't add colors yet.
674
+ lon = -(slicer.slice_points["ra"][good] - plot_dict["raCen"] - np.pi) % (np.pi * 2) - np.pi
675
+ ellipses = self._plot_tissot_ellipse(
676
+ lon,
677
+ slicer.slice_points["dec"][good],
678
+ plot_dict["radius"],
679
+ rasterized=True,
680
+ )
681
+ if plot_dict["metricIsColor"]:
682
+ current = None
683
+ for ellipse, mVal in zip(ellipses, metric_value.data[good]):
684
+ if mVal[3] > 1:
685
+ ellipse.set_alpha(1.0)
686
+ ellipse.set_facecolor((mVal[0], mVal[1], mVal[2]))
687
+ ellipse.set_edgecolor("k")
688
+ current = ellipse
689
+ else:
690
+ ellipse.set_alpha(mVal[3])
691
+ ellipse.set_color((mVal[0], mVal[1], mVal[2]))
692
+ ax.add_patch(ellipse)
693
+ if current:
694
+ ax.add_patch(current)
695
+ else:
696
+ # Determine color min/max values.
697
+ # metricValue.compressed = non-masked points.
698
+ clims = set_color_lims(metric_value, plot_dict)
699
+ # Determine whether or not to use auto-log scale.
700
+ if plot_dict["log_scale"] == "auto":
701
+ if clims[0] > 0:
702
+ if np.log10(clims[1]) - np.log10(clims[0]) > 3:
703
+ plot_dict["log_scale"] = True
704
+ else:
705
+ plot_dict["log_scale"] = False
706
+ else:
707
+ plot_dict["log_scale"] = False
708
+ if plot_dict["log_scale"]:
709
+ # Move min/max values to things that can be marked
710
+ # on the colorbar.
711
+ # clims[0] = 10 ** (int(np.log10(clims[0])))
712
+ # clims[1] = 10 ** (int(np.log10(clims[1])))
713
+ norml = colors.LogNorm()
714
+ p = PatchCollection(
715
+ ellipses,
716
+ cmap=plot_dict["cmap"],
717
+ alpha=plot_dict["alpha"],
718
+ linewidth=0,
719
+ edgecolor=None,
720
+ norm=norml,
721
+ rasterized=True,
722
+ )
723
+ else:
724
+ p = PatchCollection(
725
+ ellipses,
726
+ cmap=plot_dict["cmap"],
727
+ alpha=plot_dict["alpha"],
728
+ linewidth=0,
729
+ edgecolor=None,
730
+ rasterized=True,
731
+ )
732
+ p.set_array(metric_value.data[good])
733
+ p.set_clim(clims)
734
+ ax.add_collection(p)
735
+ # Add color bar (with optional setting of limits)
736
+ if plot_dict["cbar"]:
737
+ if plot_dict["cbar_orientation"].lower() == "vertical":
738
+ cb = plt.colorbar(
739
+ p,
740
+ shrink=0.5,
741
+ extendrect=True,
742
+ location="right",
743
+ format=plot_dict["cbar_format"],
744
+ )
745
+ else:
746
+ # Usually we just want a standard horizontal colorbar
747
+ cb = plt.colorbar(
748
+ p,
749
+ shrink=0.75,
750
+ aspect=25,
751
+ pad=0.1,
752
+ orientation="horizontal",
753
+ format=plot_dict["cbar_format"],
754
+ extendrect=True,
755
+ )
756
+ # If outputing to PDF, this fixes the colorbar white stripes
757
+ if plot_dict["cbar_edge"]:
758
+ cb.solids.set_edgecolor("face")
759
+ cb.set_label(plot_dict["xlabel"], fontsize=plot_dict["fontsize"])
760
+ cb.ax.tick_params(labelsize=plot_dict["labelsize"])
761
+ tick_locator = ticker.MaxNLocator(nbins=plot_dict["n_ticks"])
762
+ cb.locator = tick_locator
763
+ cb.update_ticks()
764
+ # Add ecliptic
765
+ self._plot_ecliptic(plot_dict["raCen"], ax=ax)
766
+ if plot_dict["mwZone"]:
767
+ self._plot_mw_zone(plot_dict["raCen"], ax=ax)
768
+ ax.grid(True, zorder=1)
769
+ ax.xaxis.set_ticklabels([])
770
+ if plot_dict["bgcolor"] is not None:
771
+ ax.set_facecolor(plot_dict["bgcolor"])
772
+ # Add label.
773
+ if plot_dict["label"] is not None:
774
+ plt.figtext(0.75, 0.9, "%s" % plot_dict["label"], fontsize=plot_dict["fontsize"])
775
+ if plot_dict["title"] is not None:
776
+ plt.text(
777
+ 0.5,
778
+ 1.09,
779
+ plot_dict["title"],
780
+ horizontalalignment="center",
781
+ transform=ax.transAxes,
782
+ fontsize=plot_dict["fontsize"],
783
+ )
784
+ return fig
785
+
786
+
787
+ class HealpixSDSSSkyMap(BasePlotter):
788
+ def __init__(self):
789
+ self.plot_type = "SkyMap"
790
+ self.object_plotter = False
791
+ self.default_plot_dict = {}
792
+ self.default_plot_dict.update(base_default_plot_dict)
793
+ self.default_plot_dict.update(
794
+ {
795
+ "cbar_format": "%.2f",
796
+ "raMin": -90,
797
+ "raMax": 90,
798
+ "raLen": 45,
799
+ "decMin": -2.0,
800
+ "decMax": 2.0,
801
+ }
802
+ )
803
+
804
+ def __call__(self, metric_value_in, slicer, user_plot_dict, fig=None):
805
+ """
806
+ Plot the sky map of metric_value using healpy cartview plots
807
+ in thin strips.
808
+ raMin: Minimum RA to plot (deg)
809
+ raMax: Max RA to plot (deg).
810
+ Note raMin/raMax define the centers that will be plotted.
811
+ raLen: Length of the plotted strips in degrees
812
+ decMin: minimum dec value to plot
813
+ decMax: max dec value to plot
814
+ metric_value_in: metric values
815
+ """
816
+ plot_dict = {}
817
+ plot_dict.update(self.default_plot_dict)
818
+ plot_dict.update(user_plot_dict)
819
+ metric_value = apply_zp_norm(metric_value_in, plot_dict)
820
+ norm = None
821
+ if plot_dict["log_scale"]:
822
+ norm = "log"
823
+ clims = set_color_lims(metric_value, plot_dict)
824
+ cmap = set_color_map(plot_dict)
825
+ racenters = np.arange(plot_dict["raMin"], plot_dict["raMax"], plot_dict["raLen"])
826
+ nframes = racenters.size
827
+ if fig is None:
828
+ fig = plt.figure(figsize=plot_dict["figsize"])
829
+ # Do not specify or use plot_dict['subplot']
830
+ # because this is done in each call to hp.cartview.
831
+ for i, racenter in enumerate(racenters):
832
+ if i == 0:
833
+ use_title = (
834
+ plot_dict["title"]
835
+ + " /n"
836
+ + "%i < RA < %i" % (racenter - plot_dict["raLen"], racenter + plot_dict["raLen"])
837
+ )
838
+ else:
839
+ use_title = "%i < RA < %i" % (
840
+ racenter - plot_dict["raLen"],
841
+ racenter + plot_dict["raLen"],
842
+ )
843
+ hp.cartview(
844
+ metric_value.filled(slicer.badval),
845
+ title=use_title,
846
+ cbar=False,
847
+ min=clims[0],
848
+ max=clims[1],
849
+ flip="astro",
850
+ rot=(racenter, 0, 0),
851
+ cmap=cmap,
852
+ norm=norm,
853
+ lonra=[-plot_dict["raLen"], plot_dict["raLen"]],
854
+ latra=[plot_dict["decMin"], plot_dict["decMax"]],
855
+ sub=(nframes + 1, 1, i + 1),
856
+ fig=fig,
857
+ )
858
+ hp.graticule(dpar=20, dmer=20, verbose=False)
859
+ # Add colorbar (not using healpy default colorbar)
860
+ ax = fig.add_axes([0.1, 0.15, 0.8, 0.075])
861
+ # Add label.
862
+ if plot_dict["label"] is not None:
863
+ plt.figtext(0.8, 0.9, "%s" % plot_dict["label"])
864
+ # Make the colorbar as a seperate figure,
865
+ # from http: //matplotlib.org/examples/api/colorbar_only.html
866
+ cnorm = colors.Normalize(vmin=clims[0], vmax=clims[1])
867
+ # supress silly colorbar warnings
868
+ with warnings.catch_warnings():
869
+ warnings.simplefilter("ignore")
870
+ cb = mpl.colorbar.ColorbarBase(
871
+ ax,
872
+ cmap=cmap,
873
+ norm=cnorm,
874
+ orientation="horizontal",
875
+ format=plot_dict["cbar_format"],
876
+ )
877
+ cb.set_label(plot_dict["xlabel"])
878
+ cb.ax.tick_params(labelsize=plot_dict["labelsize"])
879
+ if norm == "log":
880
+ tick_locator = ticker.LogLocator(numticks=plot_dict["n_ticks"])
881
+ cb.locator = tick_locator
882
+ cb.update_ticks()
883
+ if (plot_dict["n_ticks"] is not None) & (norm != "log"):
884
+ tick_locator = ticker.MaxNLocator(nbins=plot_dict["n_ticks"])
885
+ cb.locator = tick_locator
886
+ cb.update_ticks()
887
+ # If outputing to PDF, this fixes the colorbar white stripes
888
+ if plot_dict["cbar_edge"]:
889
+ cb.solids.set_edgecolor("face")
890
+ return fig
891
+
892
+
893
+ def project_lambert(longitude, latitude):
894
+ """Project from RA,dec to plane
895
+ https://en.wikipedia.org/wiki/Lambert_azimuthal_equal-area_projection
896
+ """
897
+
898
+ # flipping the sign on latitude goes north pole or south pole centered
899
+ r_polar = 2 * np.cos((np.pi / 2 + latitude) / 2.0)
900
+ # Add pi/2 so north is up
901
+ theta_polar = longitude + np.pi / 2
902
+
903
+ x = r_polar * np.cos(theta_polar)
904
+ y = r_polar * np.sin(theta_polar)
905
+ return x, y
906
+
907
+
908
+ def draw_grat(ax):
909
+ """Draw some graticule lines on an axis"""
910
+ decs = np.radians(90.0 - np.array([20, 40, 60, 80]))
911
+ ra = np.radians(np.arange(0, 361, 1))
912
+ for dec in decs:
913
+ temp_dec = ra * 0 + dec
914
+ x, y = project_lambert(ra, temp_dec)
915
+ ax.plot(x, y, "k--", alpha=0.5)
916
+
917
+ ras = np.radians(np.arange(0, 360 + 45, 45))
918
+ dec = np.radians(90.0 - np.arange(0, 81, 1))
919
+ for ra in ras:
920
+ temp_ra = dec * 0 + ra
921
+ x, y = project_lambert(temp_ra, dec)
922
+ ax.plot(x, y, "k--", alpha=0.5)
923
+
924
+ for dec in decs:
925
+ x, y = project_lambert(np.radians(45.0), dec)
926
+ ax.text(x, y, "%i" % np.round(np.degrees(dec)))
927
+
928
+ return ax
929
+
930
+
931
+ class LambertSkyMap(BasePlotter):
932
+ """
933
+ Use basemap and contour to make a Lambertian projection.
934
+ Note that the plot_dict can include a 'basemap' key with a dictionary of
935
+ arbitrary kwargs to use with the call to Basemap.
936
+ """
937
+
938
+ def __init__(self):
939
+ self.plot_type = "SkyMap"
940
+ self.object_plotter = False
941
+ self.default_plot_dict = {}
942
+ self.default_plot_dict.update(base_default_plot_dict)
943
+ self.default_plot_dict.update(
944
+ {
945
+ "basemap": {
946
+ "projection": "nplaea",
947
+ "boundinglat": 1,
948
+ "lon_0": 180,
949
+ "resolution": None,
950
+ "celestial": False,
951
+ "round": False,
952
+ },
953
+ "levels": 200,
954
+ "cbar_format": "%i",
955
+ "norm": None,
956
+ }
957
+ )
958
+
959
+ def __call__(self, metric_value_in, slicer, user_plot_dict, fig=None):
960
+ if "ra" not in slicer.slice_points or "dec" not in slicer.slice_points:
961
+ err_message = 'SpatialSlicer must contain "ra" and "dec" in slice_points metadata.'
962
+ err_message += " SlicePoints only contains keys %s." % (slicer.slice_points.keys())
963
+ raise ValueError(err_message)
964
+
965
+ plot_dict = {}
966
+ plot_dict.update(self.default_plot_dict)
967
+ plot_dict.update(user_plot_dict)
968
+
969
+ metric_value = apply_zp_norm(metric_value_in, plot_dict)
970
+ clims = set_color_lims(metric_value, plot_dict)
971
+ # Calculate the levels to use for the contour
972
+ if np.size(plot_dict["levels"]) > 1:
973
+ levels = plot_dict["levels"]
974
+ else:
975
+ step = (clims[1] - clims[0]) / plot_dict["levels"]
976
+ levels = np.arange(clims[0], clims[1] + step, step)
977
+
978
+ if fig is None:
979
+ fig = plt.figure(figsize=plot_dict["figsize"])
980
+ ax = fig.add_subplot(plot_dict["subplot"])
981
+
982
+ x, y = project_lambert(slicer.slice_points["ra"], slicer.slice_points["dec"])
983
+ # Contour the plot first to remove any anti-aliasing artifacts.
984
+ # Doesn't seem to work though. See:
985
+ # http://stackoverflow.com/questions/15822159/
986
+ # aliasing-when-saving-matplotlib-filled-contour-plot-to-pdf-or-eps
987
+ # tmpContour = m.contour(np.degrees(slicer.slice_points['ra']),
988
+ # np.degrees(slicer.slice_points['dec']),
989
+ # metric_value.filled(np.min(clims)-1), levels,
990
+ # tri=True,
991
+ # cmap=plot_dict['cmap'], ax=ax, latlon=True,
992
+ # lw=1)
993
+
994
+ # Set masked values to be below the lowest contour level.
995
+ if plot_dict["norm"] == "log":
996
+ z_val = metric_value.filled(np.min(clims) - 0.9)
997
+ norm = colors.LogNorm(vmin=z_val.min(), vmax=z_val.max())
998
+ else:
999
+ norm = plot_dict["norm"]
1000
+ tcf = ax.tricontourf(
1001
+ x,
1002
+ y,
1003
+ metric_value.filled(np.min(clims) - 0.9),
1004
+ levels,
1005
+ cmap=plot_dict["cmap"],
1006
+ norm=norm,
1007
+ )
1008
+ tcf.set_edgecolors("face")
1009
+
1010
+ ax = draw_grat(ax)
1011
+
1012
+ ax.set_xticks([])
1013
+ ax.set_yticks([])
1014
+ alt_limit = 10.0
1015
+ x, y = project_lambert(0, np.radians(alt_limit))
1016
+ max_val = np.max(np.abs([x, y]))
1017
+ ax.set_xlim([-max_val, max_val])
1018
+ ax.set_ylim([-max_val, max_val])
1019
+
1020
+ cb = plt.colorbar(tcf, format=plot_dict["cbar_format"])
1021
+ cb.set_label(plot_dict["xlabel"])
1022
+ if plot_dict["labelsize"] is not None:
1023
+ cb.ax.tick_params(labelsize=plot_dict["labelsize"])
1024
+ # Pop in an extra line to raise the title a bit
1025
+ ax.set_title(plot_dict["title"] + "\n ")
1026
+ # If outputing to PDF, this fixes the colorbar white stripes
1027
+ if plot_dict["cbar_edge"]:
1028
+ cb.solids.set_edgecolor("face")
1029
+ return fig