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,798 @@
1
+ __all__ = (
2
+ "id2intid",
3
+ "intid2id",
4
+ "load_spec_files",
5
+ "BaseSingleInterp",
6
+ "ScatteredStar",
7
+ "LowerAtm",
8
+ "UpperAtm",
9
+ "MergedSpec",
10
+ "Airglow",
11
+ "TwilightInterp",
12
+ "MoonInterp",
13
+ "ZodiacalInterp",
14
+ )
15
+
16
+ import glob
17
+ import os
18
+ import warnings
19
+
20
+ import healpy as hp
21
+ import numpy as np
22
+ from rubin_scheduler.data import get_data_dir
23
+ from scipy.interpolate import InterpolatedUnivariateSpline, interp1d
24
+
25
+ from rubin_sim.phot_utils import Bandpass, Sed
26
+
27
+ from .twilight_func import twilight_func
28
+
29
+ # Make backwards compatible with healpy
30
+ if hasattr(hp, "get_interp_weights"):
31
+ get_neighbours = hp.get_interp_weights
32
+ elif hasattr(hp, "get_neighbours"):
33
+ get_neighbours = hp.get_neighbours
34
+ else:
35
+ print("Could not find appropriate healpy function for get_interp_weight or get_neighbours")
36
+
37
+
38
+ def id2intid(ids):
39
+ """Take an array of ids, and convert them to an integer id.
40
+ Handy if you want to put things into a sparse array.
41
+ """
42
+ uids = np.unique(ids)
43
+ order = np.argsort(ids)
44
+ oids = ids[order]
45
+ uintids = np.arange(np.size(uids), dtype=int)
46
+ left = np.searchsorted(oids, uids)
47
+ right = np.searchsorted(oids, uids, side="right")
48
+ intids = np.empty(ids.size, dtype=int)
49
+ for i in range(np.size(left)):
50
+ intids[left[i] : right[i]] = uintids[i]
51
+ result = intids * 0
52
+ result[order] = intids
53
+ return result, uids, uintids
54
+
55
+
56
+ def intid2id(intids, uintids, uids, dtype=int):
57
+ """convert an int back to an id"""
58
+ ids = np.zeros(np.size(intids))
59
+
60
+ order = np.argsort(intids)
61
+ ointids = intids[order]
62
+ left = np.searchsorted(ointids, uintids, side="left")
63
+ right = np.searchsorted(ointids, uintids, side="right")
64
+ for i, (le, ri) in enumerate(zip(left, right)):
65
+ ids[le:ri] = uids[i]
66
+ result = np.zeros(np.size(intids), dtype=dtype)
67
+ result[order] = ids
68
+
69
+ return result
70
+
71
+
72
+ def load_spec_files(filenames, mags=False):
73
+ """Load up the ESO spectra.
74
+
75
+ The ESO npz files contain the following arrays:
76
+ * filter_wave: The central wavelengths of the pre-computed magnitudes
77
+ * wave: wavelengths for the spectra
78
+ * spec: array of spectra and magnitudes along with the relevant
79
+ variable inputs. For example, airglow has dtype =
80
+ [('airmass', '<f8'),
81
+ ('solar_flux', '<f8'),
82
+ ('spectra','<f8', (17001,)),
83
+ ('mags', '<f8', (6,)]
84
+
85
+ For each unique airmass and solar_flux value, there is a 17001 elements
86
+ spectra and 6 magnitudes.
87
+ """
88
+
89
+ if len(filenames) == 1:
90
+ temp = np.load(filenames[0])
91
+ wave = temp["wave"].copy()
92
+ # Note old camelCase here. Might need to update if sav
93
+ # files regenerated
94
+ filter_wave = temp["filterWave"].copy()
95
+ if mags:
96
+ # don't copy the spectra to save memory space
97
+ dt = np.dtype(
98
+ [
99
+ (key, temp["spec"].dtype[i])
100
+ for i, key in enumerate(temp["spec"].dtype.names)
101
+ if key != "spectra"
102
+ ]
103
+ )
104
+ spec = np.zeros(temp["spec"].size, dtype=dt)
105
+ for key in temp["spec"].dtype.names:
106
+ if key != "spectra":
107
+ spec[key] = temp["spec"][key].copy()
108
+ else:
109
+ spec = temp["spec"].copy()
110
+ else:
111
+ temp = np.load(filenames[0])
112
+ wave = temp["wave"].copy()
113
+ filter_wave = temp["filterWave"].copy()
114
+ if mags:
115
+ # don't copy the spectra to save memory space
116
+ dt = np.dtype(
117
+ [
118
+ (key, temp["spec"].dtype[i])
119
+ for i, key in enumerate(temp["spec"].dtype.names)
120
+ if key != "spectra"
121
+ ]
122
+ )
123
+ spec = np.zeros(temp["spec"].size, dtype=dt)
124
+ for key in temp["spec"].dtype.names:
125
+ if key != "spectra":
126
+ spec[key] = temp["spec"][key].copy()
127
+ else:
128
+ spec = temp["spec"].copy()
129
+ for filename in filenames[1:]:
130
+ temp = np.load(filename)
131
+ if mags:
132
+ # don't copy the spectra to save memory space
133
+ dt = np.dtype(
134
+ [
135
+ (key, temp["spec"].dtype[i])
136
+ for i, key in enumerate(temp["spec"].dtype.names)
137
+ if key != "spectra"
138
+ ]
139
+ )
140
+ tempspec = np.zeros(temp["spec"].size, dtype=dt)
141
+ for key in temp["spec"].dtype.names:
142
+ if key != "spectra":
143
+ tempspec[key] = temp["spec"][key].copy()
144
+ else:
145
+ tempspec = temp["spec"]
146
+ spec = np.append(spec, tempspec)
147
+ return spec, wave, filter_wave
148
+
149
+
150
+ class BaseSingleInterp:
151
+ """Base class for interpolating sky components which only depend
152
+ on airmass.
153
+
154
+ Parameters
155
+ ----------
156
+ comp_name : `str`, optional
157
+ Component name.
158
+ sorted_order : `list` [`str`], optional
159
+ Order of the dimensions in the input .npz files.
160
+ mags : `bool`, optional
161
+ Return magnitudes (only) rather than the full spectrum.
162
+ """
163
+
164
+ def __init__(self, comp_name=None, sorted_order=["airmass", "nightTimes"], mags=False):
165
+ self.mags = mags
166
+
167
+ data_dir = os.path.join(get_data_dir(), "skybrightness", "ESO_Spectra/" + comp_name)
168
+
169
+ filenames = sorted(glob.glob(data_dir + "/*.npz"))
170
+ self.spec, self.wave, self.filter_wave = load_spec_files(filenames, mags=self.mags)
171
+
172
+ # Take the log of the spectra in case we want to interp in log space.
173
+ if not mags:
174
+ self.log_spec = np.zeros(self.spec["spectra"].shape, dtype=float)
175
+ good = np.where(self.spec["spectra"] != 0)
176
+ self.log_spec[good] = np.log10(self.spec["spectra"][good])
177
+ self.spec_size = self.spec["spectra"][0].size
178
+ else:
179
+ self.spec_size = 0
180
+
181
+ # What order are the dimensions sorted by (from how
182
+ # the .npz was packaged)
183
+ self.sorted_order = sorted_order
184
+ self.dim_dict = {}
185
+ self.dim_sizes = {}
186
+ for dt in self.sorted_order:
187
+ self.dim_dict[dt] = np.unique(self.spec[dt])
188
+ self.dim_sizes[dt] = np.size(np.unique(self.spec[dt]))
189
+
190
+ # Set up and save the dict to order the filters once.
191
+ self.filter_name_dict = {"u": 0, "g": 1, "r": 2, "i": 3, "z": 4, "y": 5}
192
+
193
+ def __call__(self, interp_points, filter_names=["u", "g", "r", "i", "z", "y"]):
194
+ """At `interp_points (e.g. airmass), return values."""
195
+ if self.mags:
196
+ return self.interp_mag(interp_points, filter_names=filter_names)
197
+ else:
198
+ return self.interp_spec(interp_points)
199
+
200
+ def _indx_and_weights(self, points, grid):
201
+ """For given 1-D points, find the grid points on
202
+ either side and return the weights assume grid is sorted.
203
+
204
+ Parameters
205
+ ----------
206
+ points : `np.ndarray`, (N,)
207
+ The points on the grid to query.
208
+ grid : `np.ndarray`, (N,)
209
+ The grid on which to locate `points`.
210
+
211
+ Returns
212
+ -------
213
+ indx_r, indx_l : `np.ndarray`, `np.ndarray`
214
+ The grid indexes for each of the 1-d points
215
+ w_r, w_l : `np.ndarray`, `np.ndarray`
216
+ The weights for each of these grid points.
217
+ """
218
+
219
+ order = np.argsort(points)
220
+
221
+ indx_r = np.empty(points.size, dtype=int)
222
+
223
+ indx_r[order] = np.searchsorted(grid, points[order])
224
+ indx_l = indx_r - 1
225
+
226
+ # If points off the grid were requested, just use the edge grid point
227
+ off_grid = np.where(indx_r == grid.size)
228
+ indx_r[off_grid] = grid.size - 1
229
+ full_range = grid[indx_r] - grid[indx_l]
230
+
231
+ w_l = np.zeros(full_range.size, dtype=float)
232
+ w_r = np.ones(full_range.size, dtype=float)
233
+
234
+ good = np.where(full_range != 0)
235
+ w_l[good] = (grid[indx_r][good] - points[good]) / full_range[good]
236
+ w_r[good] = (points[good] - grid[indx_l[good]]) / full_range[good]
237
+
238
+ return indx_r, indx_l, w_r, w_l
239
+
240
+ def _weighting(self, interp_points, values):
241
+ """
242
+ given a list/array of airmass values, return a
243
+ dict with the interpolated spectrum at each airmass
244
+ and the wavelength array.
245
+
246
+ Input interp_points should be sorted
247
+ """
248
+ results = np.zeros((interp_points.size, np.size(values[0])), dtype=float)
249
+
250
+ in_range = np.where(
251
+ (interp_points["airmass"] <= np.max(self.dim_dict["airmass"]))
252
+ & (interp_points["airmass"] >= np.min(self.dim_dict["airmass"]))
253
+ )
254
+ indx_r, indx_l, w_r, w_l = self._indx_and_weights(
255
+ interp_points["airmass"][in_range], self.dim_dict["airmass"]
256
+ )
257
+
258
+ nextra = 1
259
+
260
+ # XXX--should I use the log spectra?
261
+ # Make a check and switch back and forth?
262
+ results[in_range] = (
263
+ w_r[:, np.newaxis] * values[indx_r * nextra] + w_l[:, np.newaxis] * values[indx_l * nextra]
264
+ )
265
+
266
+ return results
267
+
268
+ def interp_spec(self, interp_points):
269
+ result = self._weighting(interp_points, self.log_spec)
270
+ mask = np.where(result == 0.0)
271
+ result = 10.0**result
272
+ result[mask] = 0.0
273
+ return {"spec": result, "wave": self.wave}
274
+
275
+ def interp_mag(self, interp_points, filter_names=["u", "g", "r", "i", "z", "y"]):
276
+ filterindx = [self.filter_name_dict[key] for key in filter_names]
277
+ result = self._weighting(interp_points, self.spec["mags"][:, filterindx])
278
+ mask = np.where(result == 0.0)
279
+ result = 10.0 ** (-0.4 * (result - np.log10(3631.0)))
280
+ result[mask] = 0.0
281
+ return {"spec": result, "wave": self.filter_wave}
282
+
283
+
284
+ class ScatteredStar(BaseSingleInterp):
285
+ """Interpolate the spectra caused by scattered starlight."""
286
+
287
+ def __init__(self, comp_name="ScatteredStarLight", mags=False):
288
+ super(ScatteredStar, self).__init__(comp_name=comp_name, mags=mags)
289
+
290
+
291
+ class LowerAtm(BaseSingleInterp):
292
+ """Interpolate the spectra caused by the lower atmosphere."""
293
+
294
+ def __init__(self, comp_name="LowerAtm", mags=False):
295
+ super(LowerAtm, self).__init__(comp_name=comp_name, mags=mags)
296
+
297
+
298
+ class UpperAtm(BaseSingleInterp):
299
+ """Interpolate the spectra caused by the upper atmosphere."""
300
+
301
+ def __init__(self, comp_name="UpperAtm", mags=False):
302
+ super(UpperAtm, self).__init__(comp_name=comp_name, mags=mags)
303
+
304
+
305
+ class MergedSpec(BaseSingleInterp):
306
+ """Interpolate the combined spectra caused by the sum of the scattered
307
+ starlight, air glow, upper and lower atmosphere.
308
+ """
309
+
310
+ def __init__(self, comp_name="MergedSpec", mags=False):
311
+ super(MergedSpec, self).__init__(comp_name=comp_name, mags=mags)
312
+
313
+
314
+ class Airglow(BaseSingleInterp):
315
+ """Interpolate the spectra caused by airglow."""
316
+
317
+ def __init__(self, comp_name="Airglow", sorted_order=["airmass", "solarFlux"], mags=False):
318
+ super(Airglow, self).__init__(comp_name=comp_name, mags=mags, sorted_order=sorted_order)
319
+ self.n_solar_flux = np.size(self.dim_dict["solarFlux"])
320
+
321
+ def _weighting(self, interp_points, values):
322
+ results = np.zeros((interp_points.size, np.size(values[0])), dtype=float)
323
+ # Only interpolate point that lie in the model grid
324
+ in_range = np.where(
325
+ (interp_points["airmass"] <= np.max(self.dim_dict["airmass"]))
326
+ & (interp_points["airmass"] >= np.min(self.dim_dict["airmass"]))
327
+ & (interp_points["solar_flux"] >= np.min(self.dim_dict["solarFlux"]))
328
+ & (interp_points["solar_flux"] <= np.max(self.dim_dict["solarFlux"]))
329
+ )
330
+ use_points = interp_points[in_range]
331
+ am_right_index, am_left_index, am_right_w, am_left_w = self._indx_and_weights(
332
+ use_points["airmass"], self.dim_dict["airmass"]
333
+ )
334
+
335
+ sf_right_index, sf_left_index, sf_right_w, sf_left_w = self._indx_and_weights(
336
+ use_points["solar_flux"], self.dim_dict["solarFlux"]
337
+ )
338
+
339
+ for am_index, amW in zip([am_right_index, am_left_index], [am_right_w, am_left_w]):
340
+ for sf_index, sfW in zip([sf_right_index, sf_left_index], [sf_right_w, sf_left_w]):
341
+ results[in_range] += (
342
+ amW[:, np.newaxis] * sfW[:, np.newaxis] * values[am_index * self.n_solar_flux + sf_index]
343
+ )
344
+ return results
345
+
346
+
347
+ class TwilightInterp:
348
+ """Use the Solar Spectrum to provide an interpolated spectra or magnitudes
349
+ for the twilight sky.
350
+
351
+ Parameters
352
+ ----------
353
+ mags : `bool`
354
+ If True, only return the LSST filter magnitudes,
355
+ otherwise return the full spectrum
356
+ dark_sky_mags : `dict`
357
+ Dict of the zenith dark sky values to be assumed.
358
+ The twilight fits are done relative to the dark sky level.
359
+ fit_results : `dict`
360
+ Dict of twilight parameters based on twilight_func.
361
+ Keys should be filter names.
362
+ """
363
+
364
+ def __init__(self, mags=False, dark_sky_mags=None, fit_results=None):
365
+ if dark_sky_mags is None:
366
+ dark_sky_mags = {
367
+ "u": 22.8,
368
+ "g": 22.3,
369
+ "r": 21.2,
370
+ "i": 20.3,
371
+ "z": 19.3,
372
+ "y": 18.0,
373
+ "B": 22.35,
374
+ "G": 21.71,
375
+ "R": 21.3,
376
+ }
377
+
378
+ self.mags = mags
379
+
380
+ data_dir = os.path.join(get_data_dir(), "skybrightness")
381
+
382
+ solar_saved = np.load(os.path.join(data_dir, "solarSpec/solarSpec.npz"))
383
+ self.solar_spec = Sed(wavelen=solar_saved["wave"], flambda=solar_saved["spec"])
384
+ solar_saved.close()
385
+
386
+ canon_filters = {}
387
+ fnames = ["blue_canon.csv", "green_canon.csv", "red_canon.csv"]
388
+
389
+ # Filter names, from bluest to reddest.
390
+ self.filter_names = ["B", "G", "R"]
391
+
392
+ # Supress warning that Canon filters are low sampling
393
+ warnings.filterwarnings("ignore", message="There is an area of")
394
+ warnings.filterwarnings("ignore", message="Wavelength sampling of")
395
+ for fname, filter_name in zip(fnames, self.filter_names):
396
+ bpdata = np.genfromtxt(
397
+ os.path.join(data_dir, "Canon/", fname),
398
+ delimiter=", ",
399
+ dtype=list(zip(["wave", "through"], [float] * 2)),
400
+ )
401
+ bp_temp = Bandpass()
402
+ bp_temp.set_bandpass(bpdata["wave"], bpdata["through"])
403
+ bp_temp.resample_bandpass(
404
+ wavelen_min=self.solar_spec.wavelen.min(),
405
+ wavelen_max=self.solar_spec.wavelen.max(),
406
+ wavelen_step=self.solar_spec.wavelen[1] - self.solar_spec.wavelen[0],
407
+ )
408
+ # Force wavelengths to be identical so
409
+ # it doesn't try to resample again later
410
+ bp_temp.wavelen = self.solar_spec.wavelen
411
+ canon_filters[filter_name] = bp_temp
412
+
413
+ # Tack on the LSST filters
414
+ through_path = os.path.join(get_data_dir(), "throughputs", "baseline")
415
+ lsst_keys = ["u", "g", "r", "i", "z", "y"]
416
+ for key in lsst_keys:
417
+ bp = np.loadtxt(
418
+ os.path.join(through_path, "total_" + key + ".dat"),
419
+ dtype=list(zip(["wave", "trans"], [float] * 2)),
420
+ )
421
+ temp_b = Bandpass()
422
+ temp_b.set_bandpass(bp["wave"], bp["trans"])
423
+ canon_filters[key] = temp_b
424
+ self.filter_names.append(key)
425
+
426
+ # MAGIC NUMBERS from fitting the all-sky camera:
427
+ # Code to generate values in
428
+ # sims_skybrightness/examples/fitTwiSlopesSimul.py
429
+ # Which in turn uses twilight maps from
430
+ # sims_skybrightness/examples/buildTwilMaps.py
431
+ # values are of the form:
432
+ # 0: ratio of f^z_12 to f_dark^z
433
+ # 1: slope of curve wrt sun alt
434
+ # 2: airmass term (10^(arg[2]*(X-1)))
435
+ # 3: azimuth term.
436
+ # 4: zenith dark sky flux (erg/s/cm^2)
437
+
438
+ # For z and y, just assuming the shape parameter
439
+ # fits are similar to the other bands.
440
+ # Looks like the diode is not sensitive enough to detect faint sky.
441
+ # Using the Patat et al 2006 I-band values for z and
442
+ # modeified a little for y as a temp fix.
443
+ if fit_results is None:
444
+ self.fit_results = {
445
+ "B": [
446
+ 7.56765633e00,
447
+ 2.29798055e01,
448
+ 2.86879956e-01,
449
+ 3.01162143e-01,
450
+ 2.58462036e-04,
451
+ ],
452
+ "G": [
453
+ 2.38561156e00,
454
+ 2.29310648e01,
455
+ 2.97733083e-01,
456
+ 3.16403197e-01,
457
+ 7.29660095e-04,
458
+ ],
459
+ "R": [
460
+ 1.75498017e00,
461
+ 2.22011802e01,
462
+ 2.98619033e-01,
463
+ 3.28880254e-01,
464
+ 3.24411056e-04,
465
+ ],
466
+ "z": [2.29, 24.08, 0.3, 0.3, -666],
467
+ "y": [2.0, 24.08, 0.3, 0.3, -666],
468
+ }
469
+
470
+ # XXX-completely arbitrary fudge factor to make things
471
+ # brighter in the blue
472
+ # Just copy the blue and say it's brighter.
473
+ self.fit_results["u"] = [
474
+ 16.0,
475
+ 2.29622121e01,
476
+ 2.85862729e-01,
477
+ 2.99902574e-01,
478
+ 2.32325117e-04,
479
+ ]
480
+ else:
481
+ self.fit_results = fit_results
482
+
483
+ # Take out any filters that don't have fit results
484
+ self.filter_names = [key for key in self.filter_names if key in self.fit_results]
485
+
486
+ self.eff_wave = []
487
+ self.solar_mag = []
488
+ for filter_name in self.filter_names:
489
+ self.eff_wave.append(canon_filters[filter_name].calc_eff_wavelen()[0])
490
+ self.solar_mag.append(self.solar_spec.calc_mag(canon_filters[filter_name]))
491
+
492
+ order = np.argsort(self.eff_wave)
493
+ self.filter_names = np.array(self.filter_names)[order]
494
+ self.eff_wave = np.array(self.eff_wave)[order]
495
+ self.solar_mag = np.array(self.solar_mag)[order]
496
+
497
+ # update the fit results to be zeropointed properly
498
+ for key in self.fit_results:
499
+ f0 = 10.0 ** (-0.4 * (dark_sky_mags[key] - np.log10(3631.0)))
500
+ self.fit_results[key][-1] = f0
501
+
502
+ self.solar_wave = self.solar_spec.wavelen
503
+ self.solar_flux = self.solar_spec.flambda
504
+ # This one isn't as bad as the model grids, maybe we could get
505
+ # away with computing the magnitudes in the __call__ each time.
506
+ if mags:
507
+ # Load up the LSST filters and convert the
508
+ # solarSpec.flambda and solarSpec.wavelen to fluxes
509
+ self.lsst_filter_names = ["u", "g", "r", "i", "z", "y"]
510
+ self.lsst_equations = np.zeros(
511
+ (np.size(self.lsst_filter_names), np.size(self.fit_results["B"])),
512
+ dtype=float,
513
+ )
514
+ self.lsst_eff_wave = []
515
+
516
+ fits = np.empty((np.size(self.eff_wave), np.size(self.fit_results["B"])), dtype=float)
517
+ for i, fn in enumerate(self.filter_names):
518
+ fits[i, :] = self.fit_results[fn]
519
+
520
+ through_path = os.path.join(get_data_dir(), "throughputs", "baseline")
521
+ for filtername in self.lsst_filter_names:
522
+ bp = np.loadtxt(
523
+ os.path.join(through_path, "total_" + filtername + ".dat"),
524
+ dtype=list(zip(["wave", "trans"], [float] * 2)),
525
+ )
526
+ temp_b = Bandpass()
527
+ temp_b.set_bandpass(bp["wave"], bp["trans"])
528
+ self.lsst_eff_wave.append(temp_b.calc_eff_wavelen()[0])
529
+ # Loop through the parameters and interpolate to new
530
+ # eff wavelengths
531
+ for i in np.arange(self.lsst_equations[0, :].size):
532
+ interp = InterpolatedUnivariateSpline(self.eff_wave, fits[:, i])
533
+ self.lsst_equations[:, i] = interp(self.lsst_eff_wave)
534
+ # Set the dark sky flux
535
+ for i, filter_name in enumerate(self.lsst_filter_names):
536
+ self.lsst_equations[i, -1] = 10.0 ** (-0.4 * (dark_sky_mags[filter_name] - np.log10(3631.0)))
537
+
538
+ self.filter_name_dict = {"u": 0, "g": 1, "r": 2, "i": 3, "z": 4, "y": 5}
539
+
540
+ def print_fits_used(self):
541
+ """Print out the fit parameters being used"""
542
+ print(
543
+ r"\\tablehead{\colhead{Filter} & \colhead{$r_{12/z}$} & "
544
+ r"\colhead{$a$ (1/radians)} & \colhead{$b$ (1/airmass)} & "
545
+ r"\colhead{$c$ (az term/airmass)} & "
546
+ r"\colhead{$f_z_dark$ (erg/s/cm$^2$)$\\times 10^8$} & "
547
+ r"\colhead{m$_z_dark$}}"
548
+ )
549
+ for key in self.fit_results:
550
+ numbers = ""
551
+ for num in self.fit_results[key]:
552
+ if num > 0.001:
553
+ numbers += " & %.2f" % num
554
+ else:
555
+ numbers += " & %.2f" % (num * 1e8)
556
+ print(
557
+ key,
558
+ numbers,
559
+ " & ",
560
+ "%.2f" % (-2.5 * np.log10(self.fit_results[key][-1]) + np.log10(3631.0)),
561
+ )
562
+
563
+ def __call__(self, intep_points, filter_names=["u", "g", "r", "i", "z", "y"]):
564
+ if self.mags:
565
+ return self.interp_mag(intep_points, filter_names=filter_names)
566
+ else:
567
+ return self.interp_spec(intep_points)
568
+
569
+ def interp_mag(
570
+ self,
571
+ interp_points,
572
+ max_am=3.0,
573
+ limits=(np.radians(15.0), np.radians(-20.0)),
574
+ filter_names=["u", "g", "r", "i", "z", "y"],
575
+ ):
576
+ """
577
+ Parameters
578
+ ----------
579
+ interp_points : `np.ndarray`, (N, 3)
580
+ Interpolation points. Should contain sunAlt, airmass and azRelSun.
581
+ max_am : `float`, optional
582
+ Maximum airmass to calculate twilight sky to.
583
+ limits : `np.ndarray`, (N,), optional
584
+ Sun altitude limits
585
+
586
+ Returns
587
+ -------
588
+ spectra, wavelength : `np.ndarray`, (N, 3), `np.ndarray`, (M,)
589
+
590
+ Note
591
+ ----
592
+ Originally fit the twilight with a cutoff of sun altitude of
593
+ -11 degrees. I think it can be safely extrapolated farther,
594
+ but be warned you may be entering a regime where it breaks down.
595
+ """
596
+ npts = len(filter_names)
597
+ result = np.zeros((np.size(interp_points), npts), dtype=float)
598
+
599
+ out_of_range = np.where(interp_points["sunAlt"] > np.radians(-11))[0]
600
+ if np.size(out_of_range) > 0:
601
+ warnings.warn("Extrapolating twilight beyond a sun altitude of -11 degrees")
602
+
603
+ good = np.where(
604
+ (interp_points["sunAlt"] >= np.min(limits))
605
+ & (interp_points["sunAlt"] <= np.max(limits))
606
+ & (interp_points["airmass"] <= max_am)
607
+ & (interp_points["airmass"] >= 1.0)
608
+ )[0]
609
+
610
+ for i, filterName in enumerate(filter_names):
611
+ out_of_range = np.where(interp_points["sunAlt"] > np.max(limits))[0]
612
+ if np.size(out_of_range) > 0:
613
+ result[:, i] = np.nan
614
+ else:
615
+ result[good, i] = twilight_func(
616
+ interp_points[good], *self.lsst_equations[self.filter_name_dict[filterName], :].tolist()
617
+ )
618
+
619
+ return {"spec": result, "wave": self.lsst_eff_wave}
620
+
621
+ def interp_spec(self, interp_points, max_am=3.0, limits=(np.radians(15.0), np.radians(-20.0))):
622
+ """
623
+ Parameters
624
+ ----------
625
+ interp_points : `np.ndarray`, (N, 3)
626
+ Interpolation points. Should contain sunAlt, airmass and azRelSun.
627
+ max_am : `float`, optional
628
+ Maximum airmass to calculate twilight sky to.
629
+ limits : `np.ndarray`, (N,), optional
630
+ Sun altitude limits
631
+
632
+ Returns
633
+ -------
634
+ spectra, wavelength : `np.ndarray`, (N, 3), `np.ndarray`, (M,)
635
+
636
+ Note
637
+ ----
638
+ Originally fit the twilight with a cutoff of sun altitude of
639
+ -11 degrees. I think it can be safely extrapolated farther,
640
+ but be warned you may be entering a regime where it breaks down.
641
+ """
642
+
643
+ npts = np.size(self.solar_wave)
644
+ result = np.zeros((np.size(interp_points), npts), dtype=float)
645
+
646
+ out_of_range = np.where(interp_points["sunAlt"] > np.radians(-11))[0]
647
+ if np.size(out_of_range) > 0:
648
+ warnings.warn("Extrapolating twilight beyond a sun altitude of -11 degrees")
649
+
650
+ good = np.where(
651
+ (interp_points["sunAlt"] >= np.min(limits))
652
+ & (interp_points["sunAlt"] <= np.max(limits))
653
+ & (interp_points["airmass"] <= max_am)
654
+ & (interp_points["airmass"] >= 1.0)
655
+ )[0]
656
+
657
+ # Compute the expected flux in each of the filters that
658
+ # we have fits for
659
+ fluxes = []
660
+ for filter_name in self.filter_names:
661
+ out_of_range = np.where(interp_points["sunAlt"] > np.max(limits))[0]
662
+ if np.size(out_of_range) > 0:
663
+ fluxes.append(np.nan)
664
+ else:
665
+ fluxes.append(twilight_func(interp_points[good], *self.fit_results[filter_name]))
666
+ fluxes = np.array(fluxes)
667
+
668
+ # ratio of model flux to raw solar flux:
669
+ yvals = fluxes.T / (10.0 ** (-0.4 * (self.solar_mag - np.log10(3631.0))))
670
+
671
+ # Find wavelengths bluer than cutoff
672
+ blue_region = np.where(self.solar_wave < np.min(self.eff_wave))
673
+
674
+ for i, yval in enumerate(yvals):
675
+ interp_f = interp1d(self.eff_wave, yval, bounds_error=False, fill_value=yval[-1])
676
+ ratio = interp_f(self.solar_wave)
677
+ interp_blue = InterpolatedUnivariateSpline(self.eff_wave, yval, k=1)
678
+ ratio[blue_region] = interp_blue(self.solar_wave[blue_region])
679
+ result[good[i]] = self.solar_flux * ratio
680
+
681
+ return {"spec": result, "wave": self.solar_wave}
682
+
683
+
684
+ class MoonInterp(BaseSingleInterp):
685
+ """
686
+ Read in the saved Lunar spectra and interpolate.
687
+ """
688
+
689
+ def __init__(
690
+ self,
691
+ comp_name="Moon",
692
+ sorted_order=["moonSunSep", "moonAltitude", "hpid"],
693
+ mags=False,
694
+ ):
695
+ super(MoonInterp, self).__init__(comp_name=comp_name, sorted_order=sorted_order, mags=mags)
696
+ # Magic number from when the templates were generated
697
+ self.nside = 4
698
+
699
+ def _weighting(self, interp_points, values):
700
+ """
701
+ Weighting for the scattered moonlight.
702
+ """
703
+
704
+ result = np.zeros((interp_points.size, np.size(values[0])), dtype=float)
705
+
706
+ # Check that moonAltitude is in range, otherwise return zero array
707
+ if np.max(interp_points["moonAltitude"]) < np.min(self.dim_dict["moonAltitude"]):
708
+ return result
709
+
710
+ # Find the neighboring healpixels
711
+ hpids, hweights = get_neighbours(
712
+ self.nside, np.pi / 2.0 - interp_points["alt"], interp_points["azRelMoon"]
713
+ )
714
+
715
+ badhp = np.isin(hpids.ravel(), self.dim_dict["hpid"], invert=True).reshape(hpids.shape)
716
+ hweights[badhp] = 0.0
717
+
718
+ norm = np.sum(hweights, axis=0)
719
+ good = np.where(norm != 0.0)[0]
720
+ hweights[:, good] = hweights[:, good] / norm[good]
721
+
722
+ # Find the neighboring moonAltitude points in the grid
723
+ right_m_as, left_m_as, ma_right_w, ma_left_w = self._indx_and_weights(
724
+ interp_points["moonAltitude"], self.dim_dict["moonAltitude"]
725
+ )
726
+
727
+ # Find the neighboring moonSunSep points in the grid
728
+ right_mss, left_mss, mss_right_w, mss_left_w = self._indx_and_weights(
729
+ interp_points["moonSunSep"], self.dim_dict["moonSunSep"]
730
+ )
731
+
732
+ nhpid = self.dim_dict["hpid"].size
733
+ n_ma = self.dim_dict["moonAltitude"].size
734
+ # Convert the hpid to an index.
735
+ tmp = intid2id(hpids.ravel(), self.dim_dict["hpid"], np.arange(self.dim_dict["hpid"].size))
736
+ hpindx = tmp.reshape(hpids.shape)
737
+ # loop though the hweights and the moonAltitude weights
738
+
739
+ for hpid, hweight in zip(hpindx, hweights):
740
+ for maid, maW in zip([right_m_as, left_m_as], [ma_right_w, ma_left_w]):
741
+ for mssid, mssW in zip([right_mss, left_mss], [mss_right_w, mss_left_w]):
742
+ weight = hweight * maW * mssW
743
+ result += weight[:, np.newaxis] * values[mssid * nhpid * n_ma + maid * nhpid + hpid]
744
+
745
+ return result
746
+
747
+
748
+ class ZodiacalInterp(BaseSingleInterp):
749
+ """
750
+ Interpolate the zodiacal light based on the airmass
751
+ and the healpix ID where the healpixels are in ecliptic
752
+ coordinates, with the sun at ecliptic longitude zero
753
+ """
754
+
755
+ def __init__(self, comp_name="Zodiacal", sorted_order=["airmass", "hpid"], mags=False):
756
+ super(ZodiacalInterp, self).__init__(comp_name=comp_name, sorted_order=sorted_order, mags=mags)
757
+ self.nside = hp.npix2nside(
758
+ np.size(np.where(self.spec["airmass"] == np.unique(self.spec["airmass"])[0])[0])
759
+ )
760
+
761
+ def _weighting(self, interp_points, values):
762
+ """
763
+ interp_points is a numpy array where interpolation is desired
764
+ values are the model values.
765
+ """
766
+ result = np.zeros((interp_points.size, np.size(values[0])), dtype=float)
767
+
768
+ in_range = np.where(
769
+ (interp_points["airmass"] <= np.max(self.dim_dict["airmass"]))
770
+ & (interp_points["airmass"] >= np.min(self.dim_dict["airmass"]))
771
+ )
772
+ use_points = interp_points[in_range]
773
+ # Find the neighboring healpixels
774
+ hpids, hweights = get_neighbours(
775
+ self.nside,
776
+ np.pi / 2.0 - use_points["altEclip"],
777
+ use_points["azEclipRelSun"],
778
+ )
779
+
780
+ badhp = np.isin(hpids.ravel(), self.dim_dict["hpid"], invert=True).reshape(hpids.shape)
781
+ hweights[badhp] = 0.0
782
+
783
+ norm = np.sum(hweights, axis=0)
784
+ good = np.where(norm != 0.0)[0]
785
+ hweights[:, good] = hweights[:, good] / norm[good]
786
+
787
+ am_right_index, am_left_index, am_right_w, am_left_w = self._indx_and_weights(
788
+ use_points["airmass"], self.dim_dict["airmass"]
789
+ )
790
+
791
+ nhpid = self.dim_dict["hpid"].size
792
+ # loop though the hweights and the airmass weights
793
+ for hpid, hweight in zip(hpids, hweights):
794
+ for am_index, amW in zip([am_right_index, am_left_index], [am_right_w, am_left_w]):
795
+ weight = hweight * amW
796
+ result[in_range] += weight[:, np.newaxis] * values[am_index * nhpid + hpid]
797
+
798
+ return result