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,55 @@
1
+ __all__ = ("UseMetric",)
2
+
3
+ import numpy as np
4
+
5
+ from rubin_sim.maf.metrics.base_metric import BaseMetric
6
+
7
+
8
+ class UseMetric(BaseMetric): # pylint: disable=too-few-public-methods
9
+ """Metric to classify visits by type of visits"""
10
+
11
+ def __init__(self, note_col="scheduler_note", **kwargs):
12
+ self.note_col = note_col
13
+ super().__init__(col=[note_col], metric_dtype="object", **kwargs)
14
+
15
+ def run(self, data_slice, slice_point=None): # pylint: disable=invalid-name
16
+ """Run the metric.
17
+
18
+ Parameters
19
+ ----------
20
+ data_slice : `np.ndarray`, (N,)`
21
+ slice_point : `dict`
22
+ Dictionary of slice_point metadata passed to each metric.
23
+ E.g. the ra/dec of the healpix pixel.
24
+
25
+ Returns
26
+ -------
27
+ use_name : `str`
28
+ use at each slice_point.
29
+ """
30
+ use_name = None
31
+ visible_bands = ("u", "g", "r")
32
+ notes = data_slice[self.note_col]
33
+ if len(notes.shape) == 0:
34
+ note = notes
35
+ else:
36
+ note = notes[0]
37
+ assert np.all(notes == note)
38
+
39
+ note_elems = note.replace(":", ", ").split(", ")
40
+ # XXX--survey note strings should not be hard-coded here.
41
+ if note_elems[0] == "greedy":
42
+ use_name = note_elems[0]
43
+ if note_elems[0] == "DD":
44
+ use_name = note_elems[1]
45
+ if (note_elems[0] == "blob") | (note_elems[0] == "blob_twi"):
46
+ use_name = "wide with only IR"
47
+ for band in visible_bands:
48
+ if band in note_elems[1]:
49
+ use_name = "wide with u, g, or r"
50
+
51
+ assert use_name is not None, f"Unrecognized scheduler_note: {note}"
52
+ return use_name
53
+
54
+
55
+ # internal functions & classes
@@ -0,0 +1,190 @@
1
+ __all__ = (
2
+ "HistogramMetric",
3
+ "AccumulateMetric",
4
+ "AccumulateCountMetric",
5
+ "HistogramM5Metric",
6
+ "AccumulateM5Metric",
7
+ "AccumulateUniformityMetric",
8
+ )
9
+
10
+ import numpy as np
11
+ from scipy import stats
12
+
13
+ from .base_metric import BaseMetric
14
+
15
+
16
+ class VectorMetric(BaseMetric):
17
+ """
18
+ Base for metrics that return a vector
19
+ """
20
+
21
+ def __init__(self, bins=None, bin_col="night", col="night", units=None, metric_dtype=float, **kwargs):
22
+ if isinstance(col, str):
23
+ cols = [col, bin_col]
24
+ else:
25
+ cols = list(col) + [bin_col]
26
+
27
+ super(VectorMetric, self).__init__(col=cols, units=units, metric_dtype=metric_dtype, **kwargs)
28
+ self.bins = bins
29
+ self.bin_col = bin_col
30
+ self.shape = np.size(bins) - 1
31
+
32
+
33
+ class HistogramMetric(VectorMetric):
34
+ """
35
+ A wrapper to stats.binned_statistic
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ bins=None,
41
+ bin_col="night",
42
+ col="night",
43
+ units="Count",
44
+ statistic="count",
45
+ metric_dtype=float,
46
+ **kwargs,
47
+ ):
48
+ self.statistic = statistic
49
+ self.col = col
50
+ super(HistogramMetric, self).__init__(
51
+ col=col, bins=bins, bin_col=bin_col, units=units, metric_dtype=metric_dtype, **kwargs
52
+ )
53
+
54
+ def run(self, data_slice, slice_point=None):
55
+ data_slice.sort(order=self.bin_col)
56
+ result, bin_edges, bin_number = stats.binned_statistic(
57
+ data_slice[self.bin_col],
58
+ data_slice[self.col],
59
+ bins=self.bins,
60
+ statistic=self.statistic,
61
+ )
62
+ return result
63
+
64
+
65
+ class AccumulateMetric(VectorMetric):
66
+ """
67
+ Calculate the accumulated stat
68
+ """
69
+
70
+ def __init__(
71
+ self, col="night", bins=None, bin_col="night", function=np.add, metric_dtype=float, **kwargs
72
+ ):
73
+ self.function = function
74
+ super(AccumulateMetric, self).__init__(
75
+ col=col, bin_col=bin_col, bins=bins, metric_dtype=metric_dtype, **kwargs
76
+ )
77
+ self.col = col
78
+
79
+ def run(self, data_slice, slice_point=None):
80
+ data_slice.sort(order=self.bin_col)
81
+
82
+ result = self.function.accumulate(data_slice[self.col])
83
+ indices = np.searchsorted(data_slice[self.bin_col], self.bins[1:], side="right")
84
+ indices[np.where(indices >= np.size(result))] = np.size(result) - 1
85
+ result = result[indices]
86
+ result[np.where(indices == 0)] = self.badval
87
+ return result
88
+
89
+
90
+ class AccumulateCountMetric(AccumulateMetric):
91
+ def run(self, data_slice, slice_point=None):
92
+ data_slice.sort(order=self.bin_col)
93
+ to_count = np.ones(data_slice.size, dtype=int)
94
+ result = self.function.accumulate(to_count)
95
+ indices = np.searchsorted(data_slice[self.bin_col], self.bins[1:], side="right")
96
+ indices[np.where(indices >= np.size(result))] = np.size(result) - 1
97
+ result = result[indices]
98
+ result[np.where(indices == 0)] = self.badval
99
+ return result
100
+
101
+
102
+ class HistogramM5Metric(HistogramMetric):
103
+ """
104
+ Calculate the coadded depth for each bin (e.g., per night).
105
+ """
106
+
107
+ def __init__(
108
+ self,
109
+ bins=None,
110
+ bin_col="night",
111
+ m5_col="fiveSigmaDepth",
112
+ units="mag",
113
+ metric_name="HistogramM5Metric",
114
+ **kwargs,
115
+ ):
116
+ super(HistogramM5Metric, self).__init__(
117
+ col=m5_col, bin_col=bin_col, bins=bins, metric_name=metric_name, units=units, **kwargs
118
+ )
119
+ self.m5_col = m5_col
120
+
121
+ def run(self, data_slice, slice_point=None):
122
+ data_slice.sort(order=self.bin_col)
123
+ flux = 10.0 ** (0.8 * data_slice[self.m5_col])
124
+ result, bin_edges, bin_number = stats.binned_statistic(
125
+ data_slice[self.bin_col], flux, bins=self.bins, statistic="sum"
126
+ )
127
+ no_flux = np.where(result == 0.0)
128
+ result = 1.25 * np.log10(result)
129
+ result[no_flux] = self.badval
130
+ return result
131
+
132
+
133
+ class AccumulateM5Metric(AccumulateMetric):
134
+ def __init__(
135
+ self, bins=None, bin_col="night", m5_col="fiveSigmaDepth", metric_name="AccumulateM5Metric", **kwargs
136
+ ):
137
+ self.m5_col = m5_col
138
+ super(AccumulateM5Metric, self).__init__(
139
+ bins=bins, bin_col=bin_col, col=m5_col, metric_name=metric_name, **kwargs
140
+ )
141
+
142
+ def run(self, data_slice, slice_point=None):
143
+ data_slice.sort(order=self.bin_col)
144
+ flux = 10.0 ** (0.8 * data_slice[self.m5_col])
145
+
146
+ result = np.add.accumulate(flux)
147
+ indices = np.searchsorted(data_slice[self.bin_col], self.bins[1:], side="right")
148
+ indices[np.where(indices >= np.size(result))] = np.size(result) - 1
149
+ result = result[indices]
150
+ result = 1.25 * np.log10(result)
151
+ result[np.where(indices == 0)] = self.badval
152
+ return result
153
+
154
+
155
+ class AccumulateUniformityMetric(AccumulateMetric):
156
+ """
157
+ Make a 2D version of UniformityMetric
158
+ """
159
+
160
+ def __init__(
161
+ self,
162
+ bins=None,
163
+ bin_col="night",
164
+ exp_mjd_col="observationStartMJD",
165
+ metric_name="AccumulateUniformityMetric",
166
+ survey_length=10.0,
167
+ units="Fraction",
168
+ **kwargs,
169
+ ):
170
+ self.exp_mjd_col = exp_mjd_col
171
+ if bins is None:
172
+ bins = np.arange(0, np.ceil(survey_length * 365.25)) - 0.5
173
+ super(AccumulateUniformityMetric, self).__init__(
174
+ bins=bins, bin_col=bin_col, col=exp_mjd_col, metric_name=metric_name, units=units, **kwargs
175
+ )
176
+ self.survey_length = survey_length
177
+
178
+ def run(self, data_slice, slice_point=None):
179
+ data_slice.sort(order=self.bin_col)
180
+ if data_slice.size == 1:
181
+ return np.ones(self.bins.size - 1, dtype=float)
182
+
183
+ visits_per_night, blah = np.histogram(data_slice[self.bin_col], bins=self.bins)
184
+ visits_per_night = np.add.accumulate(visits_per_night)
185
+ expected_per_night = np.arange(0.0, self.bins.size - 1) / (self.bins.size - 2) * data_slice.size
186
+
187
+ d_max = np.abs(visits_per_night - expected_per_night)
188
+ d_max = np.maximum.accumulate(d_max)
189
+ result = d_max / expected_per_night.max()
190
+ return result
@@ -0,0 +1,341 @@
1
+ __all__ = ("VisitGroupsMetric", "PairFractionMetric")
2
+
3
+
4
+ import numpy as np
5
+
6
+ from .base_metric import BaseMetric
7
+
8
+ # Example of more complex metric
9
+ # Takes multiple columns of data (although 'night' could be calculable
10
+ # from 'expmjd')
11
+ # Returns variable length array of data
12
+ # Uses multiple reduce functions
13
+
14
+
15
+ class PairFractionMetric(BaseMetric):
16
+ """What fraction of observations are part of a pair.
17
+
18
+ Note, an observation can be a member of more than one "pair". For example,
19
+ t=[0, 5, 30], all observations would be considered part of a pair because
20
+ they all have an observation within the given window to pair with (the
21
+ observation at t=30 pairs twice).
22
+
23
+ Parameters
24
+ ----------
25
+ min_gap : float, optional
26
+ Minimum time to consider something part of a pair (minutes).
27
+ Default 15.
28
+ max_gap : float, optional
29
+ Maximum time to consider something part of a pair (minutes).
30
+ Default 90.
31
+ """
32
+
33
+ def __init__(
34
+ self, mjd_col="observationStartMJD", metric_name="PairFraction", min_gap=15.0, max_gap=90.0, **kwargs
35
+ ):
36
+ self.mjd_col = mjd_col
37
+ self.min_gap = min_gap / 60.0 / 24.0
38
+ self.max_gap = max_gap / 60.0 / 24.0
39
+ units = ""
40
+ super(PairFractionMetric, self).__init__(
41
+ col=[mjd_col], metric_name=metric_name, units=units, **kwargs
42
+ )
43
+
44
+ def run(self, data_slice, slice_point=None):
45
+ nobs = np.size(data_slice[self.mjd_col])
46
+ times = np.sort(data_slice[self.mjd_col])
47
+
48
+ # Check which ones have a forard match
49
+ t_plus = times + self.max_gap
50
+ t_minus = times + self.min_gap
51
+ ind1 = np.searchsorted(times, t_plus)
52
+ ind2 = np.searchsorted(times, t_minus)
53
+ # If ind1 and ind2 are the same, there is no pairable image for
54
+ # that exposure
55
+ diff1 = ind1 - ind2
56
+
57
+ # Check which have a back match
58
+ t_plus = times - self.max_gap
59
+ t_minus = times - self.min_gap
60
+ ind1 = np.searchsorted(times, t_plus)
61
+ ind2 = np.searchsorted(times, t_minus)
62
+
63
+ diff2 = ind1 - ind2
64
+
65
+ # The exposure has a pair ahead or behind
66
+ is_paired = np.where((diff1 != 0) | (diff2 != 0))[0]
67
+ result = np.size(is_paired) / float(nobs)
68
+ return result
69
+
70
+
71
+ class VisitGroupsMetric(BaseMetric):
72
+ """Count the number of visits per night within delta_t_min and delta_t_max.
73
+
74
+ Parameters
75
+ ----------
76
+ time_col : str, optional
77
+ Column with the time of the visit.
78
+ Default: 'observationStartMJD'
79
+ nights_col : str, optional
80
+ Column with the night of the visit
81
+ Default: 'night'
82
+ delta_t_min : float, min
83
+ Minimum time of window: units are days
84
+ Default: 15.0 / 60.0 / 24.0 (15min in days)
85
+ delta_t_max : float, optional
86
+ Maximum time of window: units are days
87
+ Default: 90.0 / 60.0 / 24.0 (90min in days)
88
+ min_n_visits : int, optional
89
+ Minimum number of visits within a night (with spacing between
90
+ delta_t_min/max from any other visit) required
91
+ Default: 2
92
+ window : int, optional
93
+ Number of nights to consider within a window (for reduce methods)
94
+ Default: 30
95
+ min_n_nights : int, optional
96
+ minimum required number of nights within window to make a full 'group'
97
+ Default: 3
98
+
99
+ """
100
+
101
+ def __init__(
102
+ self,
103
+ time_col="observationStartMJD",
104
+ nights_col="night",
105
+ metric_name="VisitGroups",
106
+ delta_t_min=15.0 / 60.0 / 24.0,
107
+ delta_t_max=90.0 / 60.0 / 24.0,
108
+ min_n_visits=2,
109
+ window=30,
110
+ min_n_nights=3,
111
+ **kwargs,
112
+ ):
113
+ self.times = time_col
114
+ self.nights = nights_col
115
+ eps = 1e-10
116
+ self.delta_tmin = float(delta_t_min) - eps
117
+ self.delta_tmax = float(delta_t_max)
118
+ self.min_n_visits = int(min_n_visits)
119
+ self.window = int(window)
120
+ self.min_n_nights = int(min_n_nights)
121
+ super(VisitGroupsMetric, self).__init__(
122
+ col=[self.times, self.nights], metric_name=metric_name, **kwargs
123
+ )
124
+ self.reduce_order = {
125
+ "Median": 0,
126
+ "NNightsWithNVisits": 1,
127
+ "NVisitsInWindow": 2,
128
+ "NNightsInWindow": 3,
129
+ "NLunations": 4,
130
+ "MaxSeqLunations": 5,
131
+ }
132
+ self.comment = "Evaluation of the number of visits within a night, with separations between "
133
+ self.comment += "tMin %.1f and tMax %.1f minutes." % (
134
+ self.delta_tmin * 24.0 * 60.0,
135
+ self.delta_tmax * 24.0 * 60.0,
136
+ )
137
+ self.comment += "Groups of visits use a minimum number of visits per night of %d, " % (
138
+ self.min_n_visits
139
+ )
140
+ self.comment += "and minimum number of nights of %d." % (self.min_n_nights)
141
+ self.comment += "Two visits within this interval would count as 2. "
142
+ self.comment += "Visits closer than tMin, paired with visits that do fall within tMin/tMax, "
143
+ self.comment += "count as half visits. <br>"
144
+ self.comment += "VisitsGroups_Median calculates the median number of visits between tMin/tMax for "
145
+ self.comment += "all nights. <br>"
146
+ self.comment += "VisitGroups_NNightsWithNNights calculates the number of nights that have at "
147
+ self.comment += "least %d visits. <br>" % (self.min_n_visits)
148
+ self.comment += "VisitGroups_NVisitsInWindow calculates the max number of visits within a window of "
149
+ self.comment += "%d days. <br>" % (self.window)
150
+ self.comment += "VisitGroups_NNightsInWindow calculates the max number of nights that have more "
151
+ self.comment += "than %d visits within %d days. <br>" % (
152
+ self.min_n_visits,
153
+ self.window,
154
+ )
155
+ self.comment += "VisitGroups_NLunations calculates the number of lunations (30 days) that have "
156
+ self.comment += "at least one group of more than %d nights with more than %d visits, within " % (
157
+ self.min_n_nights,
158
+ self.min_n_visits,
159
+ )
160
+ self.comment += "%d days. <br>" % (self.window)
161
+ self.comment += "VisitGroups_MaxSeqLunations calculates the maximum sequential lunations that have "
162
+ self.comment += 'at least one "group". <br>'
163
+
164
+ def run(self, data_slice, slice_point=None):
165
+ """
166
+ Return a dictionary of:
167
+ the number of visits within a night (within delta tmin/tmax of another
168
+ visit), and the nights with visits > minNVisits.
169
+ Count two visits which are within tmin of each other, but which have
170
+ another visit within tmin/tmax interval, as one and a half (instead of
171
+ two).
172
+
173
+ So for example: 4 visits, where 1, 2, 3 were all within deltaTMax of
174
+ each other, and 4 was later but within deltaTmax of visit 3 -- would
175
+ give you 4 visits. If visit 1 and 2 were closer together than
176
+ deltaTmin, the two would be counted as 1.5 visits together (if only 1
177
+ and 2 existed, then there would be 0 visits as none would be within the
178
+ qualifying time interval).
179
+ """
180
+ uniquenights = np.unique(data_slice[self.nights])
181
+ nights = []
182
+ visit_num = []
183
+ # Find the nights with visits within deltaTmin/max of one another and
184
+ # count the number of visits
185
+ for n in uniquenights:
186
+ condition = data_slice[self.nights] == n
187
+ times = np.sort(data_slice[self.times][condition])
188
+ nvisits = 0
189
+ ntooclose = 0
190
+ # Calculate difference between each visit and time of previous
191
+ # visit (tnext- tnow)
192
+ timediff = np.diff(times)
193
+ timegood = np.where(
194
+ (timediff <= self.delta_tmax) & (timediff >= self.delta_tmin),
195
+ True,
196
+ False,
197
+ )
198
+ timetooclose = np.where(timediff < self.delta_tmin, True, False)
199
+ if len(timegood) > 1:
200
+ # Count visits for all but last index in timediff
201
+ for tg1, ttc1, tg2, ttc2 in zip(
202
+ timegood[:-1], timetooclose[:-1], timegood[1:], timetooclose[1:]
203
+ ):
204
+ if tg1:
205
+ nvisits += 1
206
+ if not tg2:
207
+ nvisits += 1
208
+ if ttc1:
209
+ ntooclose += 1
210
+ if not tg2 and not ttc2:
211
+ ntooclose += 1
212
+ # Take care of last timediff
213
+ if timegood[-1]:
214
+ nvisits += 2 # close out visit sequence
215
+ if timetooclose[-1]:
216
+ ntooclose += 1
217
+ if not timegood[-2] and not timetooclose[-2]:
218
+ ntooclose += 1
219
+ else:
220
+ if timegood.size > 0:
221
+ nvisits += 2
222
+ # Count up all visits for night.
223
+ if nvisits > 0:
224
+ nvisits = nvisits + ntooclose / 2.0
225
+ visit_num.append(nvisits)
226
+ nights.append(n)
227
+ # Convert to numpy arrays.
228
+ visit_num = np.array(visit_num)
229
+ nights = np.array(nights)
230
+ metricval = {"visits": visit_num, "nights": nights}
231
+ if len(visit_num) == 0:
232
+ return self.badval
233
+ return metricval
234
+
235
+ def reduce_median(self, metricval):
236
+ """Reduce to median number of visits per night."""
237
+ return np.median(metricval["visits"])
238
+
239
+ def reduce_n_nights_with_n_visits(self, metricval):
240
+ """Reduce to total number of nights with more than 'minNVisits'
241
+ visits."""
242
+ condition = metricval["visits"] >= self.min_n_visits
243
+ return len(metricval["visits"][condition])
244
+
245
+ def _in_window(self, visits, nights, night, window, min_n_visits):
246
+ condition = (nights >= night) & (nights < night + window)
247
+ condition2 = visits[condition] >= min_n_visits
248
+ return visits[condition][condition2], nights[condition][condition2]
249
+
250
+ def reduce_n_visits_in_window(self, metricval):
251
+ """Reduce to max number of total visits on all nights with more than
252
+ minNVisits, within any 'window' (default=30 nights)."""
253
+ maxnvisits = 0
254
+ for n in metricval["nights"]:
255
+ vw, nw = self._in_window(
256
+ metricval["visits"],
257
+ metricval["nights"],
258
+ n,
259
+ self.window,
260
+ self.min_n_visits,
261
+ )
262
+ maxnvisits = max((vw.sum(), maxnvisits))
263
+ return maxnvisits
264
+
265
+ def reduce_n_nights_in_window(self, metricval):
266
+ """Reduce to max number of nights with more than minNVisits, within
267
+ 'window' over all windows."""
268
+ maxnights = 0
269
+ for n in metricval["nights"]:
270
+ vw, nw = self._in_window(
271
+ metricval["visits"],
272
+ metricval["nights"],
273
+ n,
274
+ self.window,
275
+ self.min_n_visits,
276
+ )
277
+ maxnights = max(len(nw), maxnights)
278
+ return maxnights
279
+
280
+ def _in_lunation(self, visits, nights, lunation_start, lunation_length):
281
+ condition = (nights >= lunation_start) & (nights < lunation_start + lunation_length)
282
+ return visits[condition], nights[condition]
283
+
284
+ def reduce_n_lunations(self, metricval):
285
+ """Reduce to number of lunations (unique 30 day windows) that contain
286
+ at least one 'group': a set of more than minNVisits per night, with
287
+ more than minNNights of visits within 'window' time period.
288
+ """
289
+ lunation_length = 30
290
+ lunations = np.arange(
291
+ metricval["nights"][0],
292
+ metricval["nights"][-1] + lunation_length / 2.0,
293
+ lunation_length,
294
+ )
295
+ n_lunations = 0
296
+ for lunation in lunations:
297
+ # Find visits within lunation.
298
+ vl, nl = self._in_lunation(metricval["visits"], metricval["nights"], lunation, lunation_length)
299
+ for n in nl:
300
+ # Find visits which are in groups within the lunation.
301
+ vw, nw = self._in_window(vl, nl, n, self.window, self.min_n_visits)
302
+ if len(nw) >= self.min_n_nights:
303
+ n_lunations += 1
304
+ break
305
+ return n_lunations
306
+
307
+ def reduce_max_seq_lunations(self, metricval):
308
+ """Count the max number of sequential lunations (unique 30 day windows
309
+ that contain at least one 'group': a set of more than minNVisits per
310
+ night, with more than minNNights of visits within 'window' time period.
311
+ """
312
+ lunation_length = 30
313
+ lunations = np.arange(
314
+ metricval["nights"][0],
315
+ metricval["nights"][-1] + lunation_length / 2.0,
316
+ lunation_length,
317
+ )
318
+ max_sequence = 0
319
+ cur_sequence = 0
320
+ for lunation in lunations:
321
+ # Find visits within lunation.
322
+ vl, nl = self._in_lunation(metricval["visits"], metricval["nights"], lunation, lunation_length)
323
+ # If no visits this lunation:
324
+ if len(vl) == 0:
325
+ max_sequence = max(max_sequence, cur_sequence)
326
+ cur_sequence = 0
327
+ # Else, look to see if groups can be made from the visits.
328
+ for n in nl:
329
+ # Find visits which are in groups within the lunation.
330
+ vw, nw = self._in_window(vl, nl, n, self.window, self.min_n_visits)
331
+ # If there was a group within this lunation:
332
+ if len(nw) >= self.min_n_nights:
333
+ cur_sequence += 1
334
+ break
335
+ # Otherwise we're not in a sequence (anymore, or still).
336
+ else:
337
+ max_sequence = max(max_sequence, cur_sequence)
338
+ cur_sequence = 0
339
+ # Pick up last sequence if were in a sequence at last lunation.
340
+ max_sequence = max(max_sequence, cur_sequence)
341
+ return max_sequence