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,766 @@
1
+ __all__ = ("apply_zp_norm", "PlotHandler", "BasePlotter")
2
+
3
+ import os
4
+ import warnings
5
+
6
+ import matplotlib.pyplot as plt
7
+ import numpy as np
8
+
9
+ import rubin_sim.maf.utils as utils
10
+
11
+
12
+ def apply_zp_norm(metric_value, plot_dict):
13
+ if "zp" in plot_dict:
14
+ if plot_dict["zp"] is not None:
15
+ metric_value = metric_value - plot_dict["zp"]
16
+ if "norm_val" in plot_dict:
17
+ if plot_dict["norm_val"] is not None:
18
+ metric_value = metric_value / plot_dict["norm_val"]
19
+ return metric_value
20
+
21
+
22
+ class BasePlotter:
23
+ """
24
+ Serve as the base type for MAF plotters and example of API.
25
+ """
26
+
27
+ def __init__(self):
28
+ self.plot_type = None
29
+ # This should be included in every subsequent default_plot_dict
30
+ # (assumed to be present).
31
+ self.default_plot_dict = {
32
+ "title": None,
33
+ "xlabel": None,
34
+ "label": None,
35
+ "labelsize": None,
36
+ "fontsize": None,
37
+ "figsize": None,
38
+ }
39
+
40
+ def __call__(self, metric_value, slicer, user_plot_dict, fig=None):
41
+ """
42
+ Parameters
43
+ ----------
44
+ metric_value : `numpy.ma.MaskedArray`
45
+ The metric values from the bundle.
46
+ slicer : `rubin_sim.maf.slicers.TwoDSlicer`
47
+ The slicer.
48
+ user_plot_dict: `dict`
49
+ Dictionary of plot parameters set by user
50
+ (overrides default values).
51
+ fig : `matplotlib.figure.Figure`
52
+ Matplotlib figure number to use. Default = None, starts new figure.
53
+
54
+ Returns
55
+ -------
56
+ fig : `matplotlib.figure.Figure`
57
+ Figure with the plot.
58
+ """
59
+ pass
60
+
61
+
62
+ class PlotHandler:
63
+ """Create plots from a single or series of metric bundles.
64
+
65
+ Parameters
66
+ ----------
67
+ out_dir : `str`, optional
68
+ Directory to save output plots.
69
+ results_db : `rubin_sim.maf.ResultsDb`, optional
70
+ ResultsDb into which to record plot location and information.
71
+ savefig : `bool`, optional
72
+ Flag for saving images to disk (versus create and return
73
+ to caller).
74
+ fig_format : `str`, optional
75
+ Figure format to use to save full-size output. Default PDF.
76
+ dpi : `int`, optional
77
+ DPI to save output figures to disk at. (for matplotlib figures).
78
+ thumbnail : `bool`, optional
79
+ Flag for saving thumbnails (reduced size pngs) to disk.
80
+ trim_whitespace : `bool`, optional
81
+ Flag for trimming whitespace option to matplotlib output figures.
82
+ Default True, usually doesn't need to be changed.
83
+ """
84
+
85
+ def __init__(
86
+ self,
87
+ out_dir=".",
88
+ results_db=None,
89
+ savefig=True,
90
+ fig_format="pdf",
91
+ dpi=600,
92
+ thumbnail=True,
93
+ trim_whitespace=True,
94
+ ):
95
+ self.out_dir = out_dir
96
+ self.results_db = results_db
97
+ self.savefig = savefig
98
+ self.fig_format = fig_format
99
+ self.dpi = dpi
100
+ self.trim_whitespace = trim_whitespace
101
+ self.thumbnail = thumbnail
102
+ self.filtercolors = {
103
+ "u": "cyan",
104
+ "g": "g",
105
+ "r": "y",
106
+ "i": "r",
107
+ "z": "m",
108
+ "y": "k",
109
+ " ": None,
110
+ }
111
+ self.filterorder = {" ": -1, "u": 0, "g": 1, "r": 2, "i": 3, "z": 4, "y": 5}
112
+
113
+ def set_metric_bundles(self, m_bundles):
114
+ """
115
+ Set the metric bundle or bundles (list or dictionary).
116
+ Reuse the PlotHandler by resetting this reference.
117
+ The metric bundles have to have the same slicer.
118
+ """
119
+ self.m_bundles = []
120
+ # Try to add the metricBundles in filter order.
121
+ if isinstance(m_bundles, dict):
122
+ for m_b in m_bundles.values():
123
+ vals = m_b.file_root.split("_")
124
+ forder = [self.filterorder.get(f, None) for f in vals if len(f) == 1]
125
+ forder = [o for o in forder if o is not None]
126
+ if len(forder) == 0:
127
+ forder = len(self.m_bundles)
128
+ else:
129
+ forder = forder[-1]
130
+ self.m_bundles.insert(forder, m_b)
131
+ self.slicer = self.m_bundles[0].slicer
132
+ else:
133
+ for m_b in m_bundles:
134
+ vals = m_b.file_root.split("_")
135
+ forder = [self.filterorder.get(f, None) for f in vals if len(f) == 1]
136
+ forder = [o for o in forder if o is not None]
137
+ if len(forder) == 0:
138
+ forder = len(self.m_bundles)
139
+ else:
140
+ forder = forder[-1]
141
+ self.m_bundles.insert(forder, m_b)
142
+ self.slicer = self.m_bundles[0].slicer
143
+ for m_b in self.m_bundles:
144
+ if m_b.slicer.slicer_name != self.slicer.slicer_name:
145
+ raise ValueError("MetricBundle items must have the same type of slicer")
146
+ self._combine_metric_names()
147
+ self._combine_run_names()
148
+ self._combine_metadata()
149
+ self._combine_constraints()
150
+ self.set_plot_dicts(reset=True)
151
+
152
+ def set_plot_dicts(self, plot_dicts=None, plot_func=None, reset=False):
153
+ """
154
+ Set or update the plot_dict for the (possibly joint) plots.
155
+
156
+ Resolution is: (from lowest to higher)
157
+ auto-generated items (colors/labels/titles)
158
+ < anything previously set in the plot_handler
159
+ < defaults set by the plotter
160
+ < explicitly set items in the metricBundle plot_dict
161
+ < explicitly set items in the plot_dicts list passed to this method.
162
+ """
163
+ if reset:
164
+ # Have to explicitly set each dictionary to a (separate)
165
+ # blank dictionary.
166
+ self.plot_dicts = [{} for b in self.m_bundles]
167
+
168
+ if isinstance(plot_dicts, dict):
169
+ # We were passed a single dictionary, not a list.
170
+ plot_dicts = [plot_dicts] * len(self.m_bundles)
171
+
172
+ auto_label_list = self._build_legend_labels()
173
+ auto_color_list = self._build_colors()
174
+ auto_cbar = self._build_cbar_format()
175
+ auto_title = self._build_title()
176
+ if plot_func is not None:
177
+ auto_xlabel, auto_ylabel = self._build_x_ylabels(plot_func)
178
+
179
+ # Loop through each bundle and generate a plot_dict for it.
180
+ for i, bundle in enumerate(self.m_bundles):
181
+ # First use the auto-generated values.
182
+ tmp_plot_dict = {}
183
+ tmp_plot_dict["title"] = auto_title
184
+ tmp_plot_dict["label"] = auto_label_list[i]
185
+ tmp_plot_dict["color"] = auto_color_list[i]
186
+ tmp_plot_dict["cbar_format"] = auto_cbar
187
+ # Update that with anything previously set in the plot_handler.
188
+ tmp_plot_dict.update(self.plot_dicts[i])
189
+ # Then override with plot_dict items set explicitly
190
+ # based on the plot type.
191
+ if plot_func is not None:
192
+ tmp_plot_dict["xlabel"] = auto_xlabel
193
+ tmp_plot_dict["ylabel"] = auto_ylabel
194
+ # Replace auto-generated plot dict items with things
195
+ # set by the plotter_defaults, if they are not None.
196
+ plotter_defaults = plot_func.default_plot_dict
197
+ for k, v in plotter_defaults.items():
198
+ if v is not None:
199
+ tmp_plot_dict[k] = v
200
+ # Then add/override based on the bundle plot_dict parameters
201
+ # if they are set.
202
+ tmp_plot_dict.update(bundle.plot_dict)
203
+ # Finally, override with anything set explicitly by the user.
204
+ if plot_dicts is not None:
205
+ tmp_plot_dict.update(plot_dicts[i])
206
+ # And save this new dictionary back in the class.
207
+ self.plot_dicts[i] = tmp_plot_dict
208
+
209
+ # Check that the plot_dicts do not conflict.
210
+ self._check_plot_dicts()
211
+
212
+ def _combine_metric_names(self):
213
+ """
214
+ Combine metric names.
215
+ """
216
+ # Find the unique metric names.
217
+ self.metric_names = set()
218
+ for m_b in self.m_bundles:
219
+ self.metric_names.add(m_b.metric.name)
220
+ # Find a pleasing combination of the metric names.
221
+ order = ["u", "g", "r", "i", "z", "y"]
222
+ if len(self.metric_names) == 1:
223
+ joint_name = " ".join(self.metric_names)
224
+ else:
225
+ # Split each unique name into a list to see
226
+ # if we can merge the names.
227
+ name_lengths = [len(x.split()) for x in self.metric_names]
228
+ name_lists = [x.split() for x in self.metric_names]
229
+ # If the metric names are all the same length, see
230
+ # if we can combine any parts.
231
+ if len(set(name_lengths)) == 1:
232
+ joint_name = []
233
+ for i in range(name_lengths[0]):
234
+ tmp = set([x[i] for x in name_lists])
235
+ # Try to catch special case of filters and
236
+ # put them in order.
237
+ if tmp.intersection(order) == tmp:
238
+ filterlist = ""
239
+ for f in order:
240
+ if f in tmp:
241
+ filterlist += f
242
+ joint_name.append(filterlist)
243
+ else:
244
+ # Otherwise, just join and put into joint_name.
245
+ joint_name.append("".join(tmp))
246
+ joint_name = " ".join(joint_name)
247
+ # If the metric names are not the same length,
248
+ # just join everything.
249
+ else:
250
+ joint_name = " ".join(self.metric_names)
251
+ self.joint_metric_names = joint_name
252
+
253
+ def _combine_run_names(self):
254
+ """
255
+ Combine runNames.
256
+ """
257
+ self.run_names = set()
258
+ for m_b in self.m_bundles:
259
+ self.run_names.add(m_b.run_name)
260
+ self.joint_run_names = " ".join(self.run_names)
261
+
262
+ def _combine_metadata(self):
263
+ """
264
+ Combine info_label.
265
+ """
266
+ info_label = set()
267
+ for m_b in self.m_bundles:
268
+ info_label.add(m_b.info_label)
269
+ self.info_label = info_label
270
+ # Find a pleasing combination of the info_label.
271
+ if len(info_label) == 1:
272
+ self.joint_metadata = " ".join(info_label)
273
+ else:
274
+ order = ["u", "g", "r", "i", "z", "y"]
275
+ # See if there are any subcomponents we can combine,
276
+ # splitting on some values we expect to separate
277
+ # info_label clauses.
278
+ splitmetas = []
279
+ for m in self.info_label:
280
+ # Try to split info_label into separate phrases
281
+ # (filter / proposal / constraint..).
282
+ if " and " in m:
283
+ m = m.split(" and ")
284
+ elif ", " in m:
285
+ m = m.split(", ")
286
+ else:
287
+ m = [
288
+ m,
289
+ ]
290
+ # Strip white spaces from individual elements.
291
+ m = set([im.strip() for im in m])
292
+ splitmetas.append(m)
293
+ # Look for common elements and
294
+ # separate from the general info_label.
295
+ common = set.intersection(*splitmetas)
296
+ diff = [x.difference(common) for x in splitmetas]
297
+ # Now look within the 'diff' elements and
298
+ # see if there are any common words to split off.
299
+ diffsplit = []
300
+ for d in diff:
301
+ if len(d) > 0:
302
+ m = set([x.split() for x in d][0])
303
+ else:
304
+ m = set()
305
+ diffsplit.append(m)
306
+ diffcommon = set.intersection(*diffsplit)
307
+ diffdiff = [x.difference(diffcommon) for x in diffsplit]
308
+ # If the length of any of the 'differences' is 0,
309
+ # then we should stop and not try to subdivide.
310
+ lengths = [len(x) for x in diffdiff]
311
+ if min(lengths) == 0:
312
+ # Sort them in order of length
313
+ # (so it goes 'g', 'g dithered', etc.)
314
+ tmp = []
315
+ for d in diff:
316
+ tmp.append(list(d)[0])
317
+ diff = tmp
318
+ xlengths = [len(x) for x in diff]
319
+ idx = np.argsort(xlengths)
320
+ diffdiff = [diff[i] for i in idx]
321
+ diffcommon = []
322
+ else:
323
+ # diffdiff is the part where we might expect
324
+ # our filter values to appear;
325
+ # try to put this in order.
326
+ diffdiff_ordered = []
327
+ diffdiff_end = []
328
+ for f in order:
329
+ for d in diffdiff:
330
+ if len(d) == 1:
331
+ if list(d)[0] == f:
332
+ diffdiff_ordered.append(d)
333
+ for d in diffdiff:
334
+ if d not in diffdiff_ordered:
335
+ diffdiff_end.append(d)
336
+ diffdiff = diffdiff_ordered + diffdiff_end
337
+ diffdiff = [" ".join(c) for c in diffdiff]
338
+ # And put it all back together.
339
+ combo = (
340
+ ", ".join(["".join(c) for c in diffdiff])
341
+ + " "
342
+ + " ".join(["".join(d) for d in diffcommon])
343
+ + " "
344
+ + " ".join(["".join(e) for e in common])
345
+ )
346
+ self.joint_metadata = combo
347
+
348
+ def _combine_constraints(self):
349
+ """
350
+ Combine the constraints.
351
+ """
352
+ constraints = set()
353
+ for m_b in self.m_bundles:
354
+ if m_b.constraint is not None:
355
+ constraints.add(m_b.constraint)
356
+ self.constraints = "; ".join(constraints)
357
+
358
+ def _build_title(self):
359
+ """
360
+ Build a plot title from the metric names, runNames and info_label.
361
+ """
362
+ # Create a plot title from the unique parts of
363
+ # the metric/run_name/info_label.
364
+ plot_title = ""
365
+ if len(self.run_names) == 1:
366
+ plot_title += list(self.run_names)[0]
367
+ if len(self.info_label) == 1:
368
+ plot_title += " " + list(self.info_label)[0]
369
+ if len(self.metric_names) == 1:
370
+ plot_title += ": " + list(self.metric_names)[0]
371
+ if plot_title == "":
372
+ # If there were more than one of everything above,
373
+ # use joint info_label and metricNames.
374
+ plot_title = self.joint_metadata + " " + self.joint_metric_names
375
+ return plot_title
376
+
377
+ def _build_x_ylabels(self, plot_func, len_max=25):
378
+ """
379
+ Build a plot x and y label.
380
+
381
+ Parameters
382
+ ----------
383
+ len_max : `int`, optional
384
+ If the xlabel starts longer than this, add the units as a newline.
385
+ """
386
+ if plot_func.plot_type == "BinnedData":
387
+ if len(self.m_bundles) == 1:
388
+ m_b = self.m_bundles[0]
389
+ if len(m_b.slicer.slice_col_name) < len_max:
390
+ xlabel = m_b.slicer.slice_col_name + " (" + m_b.slicer.slice_col_units + ")"
391
+ else:
392
+ xlabel = m_b.slicer.slice_col_name + " \n(" + m_b.slicer.slice_col_units + ")"
393
+ ylabel = m_b.metric.name + " (" + m_b.metric.units + ")"
394
+ else:
395
+ xlabel = set()
396
+ for m_b in self.m_bundles:
397
+ xlabel.add(m_b.slicer.slice_col_name)
398
+ xlabel = ", ".join(xlabel)
399
+ ylabel = self.joint_metric_names
400
+ elif plot_func.plot_type == "MetricVsH":
401
+ if len(self.m_bundles) == 1:
402
+ m_b = self.m_bundles[0]
403
+ ylabel = m_b.metric.name + " (" + m_b.metric.units + ")"
404
+ else:
405
+ ylabel = self.joint_metric_names
406
+ xlabel = "H (mag)"
407
+ else:
408
+ if len(self.m_bundles) == 1:
409
+ m_b = self.m_bundles[0]
410
+ xlabel = m_b.metric.name
411
+ if m_b.metric.units is not None:
412
+ if len(m_b.metric.units) > 0:
413
+ if len(xlabel) < len_max:
414
+ xlabel += " (" + m_b.metric.units + ")"
415
+ else:
416
+ xlabel += "\n(" + m_b.metric.units + ")"
417
+ ylabel = None
418
+ else:
419
+ xlabel = self.joint_metric_names
420
+ ylabel = set()
421
+ for m_b in self.m_bundles:
422
+ if "ylabel" in m_b.plot_dict:
423
+ ylabel.add(m_b.plot_dict["ylabel"])
424
+ if len(ylabel) == 1:
425
+ ylabel = list(ylabel)[0]
426
+ else:
427
+ ylabel = None
428
+ return xlabel, ylabel
429
+
430
+ def _build_legend_labels(self):
431
+ """
432
+ Build a set of legend labels,
433
+ using the parts of the run_name/info_label/metricNames that change.
434
+ """
435
+ if len(self.m_bundles) == 1:
436
+ return [None]
437
+ labels = []
438
+ for m_b in self.m_bundles:
439
+ if "label" in m_b.plot_dict:
440
+ label = m_b.plot_dict["label"]
441
+ else:
442
+ label = ""
443
+ if len(self.run_names) > 1:
444
+ label += m_b.run_name
445
+ if len(self.info_label) > 1:
446
+ label += " " + m_b.info_label
447
+ if len(self.metric_names) > 1:
448
+ label += " " + m_b.metric.name
449
+ labels.append(label)
450
+ return labels
451
+
452
+ def _build_colors(self):
453
+ """
454
+ Try to set an appropriate range of colors for the metric Bundles.
455
+ """
456
+ if len(self.m_bundles) == 1:
457
+ if "color" in self.m_bundles[0].plot_dict:
458
+ return [self.m_bundles[0].plot_dict["color"]]
459
+ else:
460
+ return ["b"]
461
+ colors = []
462
+ for m_b in self.m_bundles:
463
+ color = "b"
464
+ if "color" in m_b.plot_dict:
465
+ color = m_b.plot_dict["color"]
466
+ else:
467
+ if m_b.constraint is not None:
468
+ # If the filter is part of the sql constraint, we'll
469
+ # try to use that first.
470
+ if "filter" in m_b.constraint:
471
+ vals = m_b.constraint.split('"')
472
+ for v in vals:
473
+ if len(v) == 1:
474
+ # Guess that this is the filter value
475
+ if v in self.filtercolors:
476
+ color = self.filtercolors[v]
477
+ colors.append(color)
478
+ # If we happened to end up with the same color throughout
479
+ # (say, the metrics were all in the same filter)
480
+ # then go ahead and generate random colors.
481
+ if (len(self.m_bundles) > 1) and (len(np.unique(colors)) == 1):
482
+ colors = [
483
+ np.random.rand(
484
+ 3,
485
+ )
486
+ for m_b in self.m_bundles
487
+ ]
488
+ return colors
489
+
490
+ def _build_cbar_format(self):
491
+ """
492
+ Set the color bar format.
493
+ """
494
+ cbar_format = None
495
+ if len(self.m_bundles) == 1:
496
+ if self.m_bundles[0].metric.metric_dtype == "int":
497
+ cbar_format = "%d"
498
+ else:
499
+ metric_dtypes = set()
500
+ for m_b in self.m_bundles:
501
+ metric_dtypes.add(m_b.metric.metric_dtype)
502
+ if len(metric_dtypes) == 1:
503
+ if list(metric_dtypes)[0] == "int":
504
+ cbar_format = "%d"
505
+ return cbar_format
506
+
507
+ def _build_file_root(self, outfile_suffix=None):
508
+ """
509
+ Build a root filename for plot outputs.
510
+ If there is only one metricBundle,
511
+ this is equal to the metricBundle fileRoot + outfile_suffix.
512
+ For multiple metricBundles,
513
+ this is created from the runNames, info_label and metric names.
514
+
515
+ If you do not wish to use the automatic filenames,
516
+ then you could set 'savefig' to False and
517
+ save the file manually to disk,
518
+ using the plot figure numbers returned by 'plot'.
519
+ """
520
+ if len(self.m_bundles) == 1:
521
+ outfile = self.m_bundles[0].file_root
522
+ else:
523
+ outfile = "_".join([self.joint_run_names, self.joint_metric_names, self.joint_metadata])
524
+ outfile += "_" + self.m_bundles[0].slicer.slicer_name[:4].upper()
525
+ if outfile_suffix is not None:
526
+ outfile += "_" + outfile_suffix
527
+ outfile = utils.name_sanitize(outfile)
528
+ return outfile
529
+
530
+ def _build_display_dict(self):
531
+ """
532
+ Generate a display dictionary.
533
+ This is most useful for when there are many metricBundles
534
+ being combined into a single plot.
535
+ """
536
+ if len(self.m_bundles) == 1:
537
+ return self.m_bundles[0].display_dict
538
+ else:
539
+ display_dict = {}
540
+ group = set()
541
+ subgroup = set()
542
+ order = 0
543
+ for m_b in self.m_bundles:
544
+ group.add(m_b.display_dict["group"])
545
+ subgroup.add(m_b.display_dict["subgroup"])
546
+ if order < m_b.display_dict["order"]:
547
+ order = m_b.display_dict["order"] + 1
548
+ display_dict["order"] = order
549
+ if len(group) > 1:
550
+ display_dict["group"] = "Comparisons"
551
+ else:
552
+ display_dict["group"] = list(group)[0]
553
+ if len(subgroup) > 1:
554
+ display_dict["subgroup"] = "Comparisons"
555
+ else:
556
+ display_dict["subgroup"] = list(subgroup)[0]
557
+
558
+ display_dict["caption"] = (
559
+ "%s metric(s) calculated on a %s grid, for opsim runs %s, for info_label values of %s."
560
+ % (
561
+ self.joint_metric_names,
562
+ self.m_bundles[0].slicer.slicer_name,
563
+ self.joint_run_names,
564
+ self.joint_metadata,
565
+ )
566
+ )
567
+
568
+ return display_dict
569
+
570
+ def _check_plot_dicts(self):
571
+ """
572
+ Check to make sure there are no conflicts in the plot_dicts
573
+ that are being used in the same subplot.
574
+ """
575
+ # Check that the length is OK
576
+ if len(self.plot_dicts) != len(self.m_bundles):
577
+ raise ValueError(
578
+ "plot_dicts (%i) must be same length as mBundles (%i)"
579
+ % (len(self.plot_dicts), len(self.m_bundles))
580
+ )
581
+
582
+ # These are the keys that need to match (or be None)
583
+ keys2_check = ["xlim", "ylim", "color_min", "color_max", "title"]
584
+
585
+ # Identify how many subplots there are.
586
+ # If there are more than one, just don't change anything.
587
+ # This assumes that if there are more than one,
588
+ # the plot_dicts are actually all compatible.
589
+ subplots = set()
590
+ for pd in self.plot_dicts:
591
+ if "subplot" in pd:
592
+ subplots.add(pd["subplot"])
593
+
594
+ # Now check subplots are consistent.
595
+ if len(subplots) <= 1:
596
+ reset_keys = []
597
+ for key in keys2_check:
598
+ values = [pd[key] for pd in self.plot_dicts if key in pd]
599
+ if len(np.unique(values)) > 1:
600
+ # We will reset some of the keys to the default,
601
+ # but for some we should do better.
602
+ if key.endswith("Max"):
603
+ for pd in self.plot_dicts:
604
+ pd[key] = np.max(values)
605
+ elif key.endswith("Min"):
606
+ for pd in self.plot_dicts:
607
+ pd[key] = np.min(values)
608
+ elif key == "title":
609
+ title = self._build_title()
610
+ for pd in self.plot_dicts:
611
+ pd["title"] = title
612
+ else:
613
+ warnings.warn(
614
+ 'Found more than one value to be set for "%s" in the plot_dicts.' % (key)
615
+ + " Will reset to default value. (found values %s)" % values
616
+ )
617
+ reset_keys.append(key)
618
+ # Reset the most of the keys to defaults;
619
+ # this can generally be done safely.
620
+ for key in reset_keys:
621
+ for pd in self.plot_dicts:
622
+ pd[key] = None
623
+
624
+ def plot(
625
+ self,
626
+ plot_func,
627
+ plot_dicts=None,
628
+ display_dict=None,
629
+ outfile_root=None,
630
+ outfile_suffix=None,
631
+ ):
632
+ """
633
+ Create a plot for the active metric bundles (self.set_metric_bundles).
634
+
635
+ Parameters
636
+ ----------
637
+ plot_func : `rubin_sim.plots.BasePlotter`
638
+ The plotter to use to make the figure.
639
+ plot_dicts : `list` of [`dict`], optional
640
+ List of plot_dicts for each metric bundle.
641
+ Can use these to override individual metric bundle colors, etc.
642
+ display_dict : `dict`, optional
643
+ Information to save to resultsDb to accompany the figure on the
644
+ show_maf pages. Generally set automatically. Includes a caption.
645
+ outfile_root : `str`, optional
646
+ Output filename. Generally set automatically, but can be
647
+ overriden (such as when output filenames get too long).
648
+ outfile_suffix : `str`, optional
649
+ A suffix to add to the end of the default output filename.
650
+ Useful when creating a series of plots, such as for a movie.
651
+
652
+ Returns
653
+ -------
654
+ fig : `matplotlib.figure.Figure`
655
+ The plot.
656
+ """
657
+ if not plot_func.object_plotter:
658
+ # Check that metric_values type and plotter are compatible
659
+ # (most are float/float, but some plotters expect object data ..
660
+ # and some only do sometimes).
661
+ for m_b in self.m_bundles:
662
+ if m_b.metric.metric_dtype == "object":
663
+ metric_is_color = m_b.plot_dict.get("metric_is_color", False)
664
+ if not metric_is_color:
665
+ warnings.warn("Cannot plot object metric values with this plotter.")
666
+ return
667
+
668
+ # Update x/y labels using plot_type.
669
+ self.set_plot_dicts(plot_dicts=plot_dicts, plot_func=plot_func, reset=False)
670
+ # Set outfile name.
671
+ if outfile_root is None:
672
+ outfile = self._build_file_root(outfile_suffix)
673
+ else:
674
+ outfile = outfile_root
675
+ plot_type = plot_func.plot_type
676
+ if len(self.m_bundles) > 1:
677
+ plot_type = "Combo" + plot_type
678
+ # Make plot.
679
+ fig = None
680
+ for m_b, plot_dict in zip(self.m_bundles, self.plot_dicts):
681
+ if m_b.metric_values is None:
682
+ # Skip this metricBundle.
683
+ msg = 'MetricBundle (%s) has no "metric_values".' % (m_b.file_root)
684
+ msg += " Either the values have not been calculated or they have been deleted."
685
+ warnings.warn(msg)
686
+ elif np.size(np.where(~m_b.metric_values.mask)) == 0:
687
+ msg = "MetricBundle (%s) has no unmasked metric_values, skipping plots." % (m_b.file_root)
688
+ warnings.warn(msg)
689
+ else:
690
+ fig = plot_func(m_b.metric_values, m_b.slicer, plot_dict, fig=fig)
691
+ # Add a legend if more than one metricValue is being plotted
692
+ # or if legendloc is specified.
693
+ legend_loc = None
694
+ if "legend_loc" in self.plot_dicts[0]:
695
+ legend_loc = self.plot_dicts[0]["legend_loc"]
696
+ if len(self.m_bundles) > 1:
697
+ try:
698
+ legend_loc = self.plot_dicts[0]["legend_loc"]
699
+ except KeyError:
700
+ legend_loc = "upper right"
701
+ if legend_loc is not None:
702
+ # Activate expected figure and write legend.
703
+ plt.figure(fig)
704
+ plt.legend(loc=legend_loc, fancybox=True, fontsize="smaller")
705
+ # Add the super title if provided.
706
+ if "suptitle" in self.plot_dicts[0]:
707
+ plt.suptitle(self.plot_dicts[0]["suptitle"])
708
+ # Save to disk and file info to results_db if desired.
709
+ if self.savefig:
710
+ if display_dict is None:
711
+ display_dict = self._build_display_dict()
712
+ self.save_fig(
713
+ fig,
714
+ outfile,
715
+ plot_type,
716
+ self.joint_metric_names,
717
+ self.slicer.slicer_name,
718
+ self.joint_run_names,
719
+ self.constraints,
720
+ self.joint_metadata,
721
+ display_dict,
722
+ )
723
+ return fig
724
+
725
+ def save_fig(
726
+ self,
727
+ fig,
728
+ outfile_root,
729
+ plot_type,
730
+ metric_name,
731
+ slicer_name,
732
+ run_name,
733
+ constraint,
734
+ info_label,
735
+ display_dict=None,
736
+ ):
737
+ plot_file = outfile_root + "_" + plot_type + "." + self.fig_format
738
+ if fig is None:
739
+ warnings.warn(f"Trying to save figure to {plot_file} but" "figure is None. Skipping.")
740
+ return
741
+ if self.trim_whitespace:
742
+ fig.savefig(
743
+ os.path.join(self.out_dir, plot_file),
744
+ dpi=self.dpi,
745
+ bbox_inches="tight",
746
+ format=self.fig_format,
747
+ )
748
+ else:
749
+ fig.savefig(
750
+ os.path.join(self.out_dir, plot_file),
751
+ dpi=self.dpi,
752
+ format=self.fig_format,
753
+ )
754
+ # Generate a png thumbnail.
755
+ if self.thumbnail:
756
+ thumb_file = "thumb." + outfile_root + "_" + plot_type + ".png"
757
+ fig.savefig(os.path.join(self.out_dir, thumb_file), dpi=72, bbox_inches="tight")
758
+ # Save information about the file to results_db.
759
+ if self.results_db:
760
+ if display_dict is None:
761
+ display_dict = {}
762
+ metric_id = self.results_db.update_metric(
763
+ metric_name, slicer_name, run_name, constraint, info_label, None
764
+ )
765
+ self.results_db.update_display(metric_id=metric_id, display_dict=display_dict, overwrite=False)
766
+ self.results_db.update_plot(metric_id=metric_id, plot_type=plot_type, plot_file=plot_file)