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,689 @@
1
+ __all__ = ("ChebyFits",)
2
+
3
+ import os
4
+ import warnings
5
+
6
+ import numpy as np
7
+
8
+ from .chebyshev_utils import chebfit, make_cheb_matrix, make_cheb_matrix_only_x
9
+ from .ooephemerides import PyOrbEphemerides, get_oorb_data_dir
10
+ from .orbits import Orbits
11
+
12
+
13
+ def three_sixty_to_neg(ra):
14
+ """Wrap discontiguous RA values into more-contiguous results."""
15
+ if (ra.min() < 100) and (ra.max() > 270):
16
+ ra = np.where(ra > 270, ra - 360, ra)
17
+ return ra
18
+
19
+
20
+ class ChebyFits:
21
+ """Generates chebyshev coefficients for a provided set of orbits.
22
+
23
+ Calculates true ephemerides using PyEphemerides, then fits these
24
+ positions with a constrained Chebyshev Polynomial, using the routines
25
+ in chebyshevUtils.py.
26
+
27
+ Parameters
28
+ ----------
29
+ orbits_obj : `rubin_sim.moving_objects.Orbits`
30
+ The orbits for which to fit chebyshev polynomial coefficients.
31
+ t_start : `float`
32
+ The starting point in time to fit coefficients. MJD.
33
+ t_span : `float`
34
+ The time span (starting at t_start) over which to fit coefficients
35
+ (Days).
36
+ time_scale : `str`, optional
37
+ One of {'TAI', 'UTC', 'TT'}
38
+ The timescale of the MJD time, t_start, and the time_scale
39
+ that should be used with the chebyshev coefficients.
40
+ obsCode : `int`, optional
41
+ The observatory code of the location for which to generate
42
+ ephemerides. Default I11 (Cerro Pachon).
43
+ sky_tolerance : `float`, optional
44
+ The desired tolerance in mas between ephemerides calculated by
45
+ OpenOrb and fitted values.
46
+ Default 2.5 mas.
47
+ nCoeff_position : `int`, optional
48
+ The number of Chebyshev coefficients to fit for the RA/Dec positions.
49
+ Default 14.
50
+ nCoeff_vmag : `int`, optional
51
+ The number of Chebyshev coefficients to fit for the V magnitude values.
52
+ Default 9.
53
+ nCoeff_delta : `int`, optional
54
+ The number of Chebyshev coefficients to fit for the distance
55
+ between Earth/Object. Default 5.
56
+ nCoeff_elongation : `int`, optional
57
+ The number of Chebyshev coefficients to fit for the solar
58
+ elongation. Default 5.
59
+ ngran : `int`, optional
60
+ The number of ephemeris points within each Chebyshev
61
+ polynomial segment. Default 64.
62
+ eph_file : `str`, optional
63
+ The path to the JPL ephemeris file to use.
64
+ Default is '$OORB_DATA/de405.dat'.
65
+ n_decimal : `int`, optional
66
+ The number of decimal places to allow in the segment length
67
+ (and thus the times of the endpoints) can be limited to
68
+ n_decimal places. Default 10.
69
+ For LSST SIMS moving object database, this should be 13 decimal
70
+ places for NEOs and 0 for all others.
71
+
72
+ Notes
73
+ -----
74
+ Many chebyshev polynomials are used to fit one moving object over
75
+ a given timeperiod; typically, the length of each segment is typically
76
+ about 2 days for MBAs. The start and end of each segment must match
77
+ exactly, and the entire segments must fit into the total timespan an
78
+ integer number of times. This is accomplished by setting n_decimal to
79
+ the number of decimal places desired in the 'time' value.
80
+ For faster moving objects, this number needs be greater to allow for
81
+ smaller subdivisions.
82
+ It's tempting to allow flexibility to the point of not
83
+ enforcing this non-overlap; however, then the resulting ephemeris
84
+ may have multiple values depending on which polynomial segment was
85
+ used to calculate the ephemeris.
86
+
87
+ The length of each chebyshev polynomial is related to the number of
88
+ ephemeris positions used to fit that polynomial by ngran:
89
+ length = timestep * ngran
90
+ The length of each polynomial is adjusted so that the residuals in
91
+ RA/Dec position are less than sky_tolerance - default = 2.5mas.
92
+ The polynomial length (and the resulting residuals) is affected
93
+ by ngran (i.e. timestep).
94
+
95
+ Default values are based on Yusra AlSayaad's work.
96
+ """
97
+
98
+ def __init__(
99
+ self,
100
+ orbits_obj,
101
+ t_start,
102
+ t_span,
103
+ time_scale="TAI",
104
+ obscode="I11",
105
+ sky_tolerance=2.5,
106
+ n_coeff_position=14,
107
+ n_coeff_vmag=9,
108
+ n_coeff_delta=5,
109
+ n_coeff_elongation=6,
110
+ ngran=64,
111
+ eph_file=None,
112
+ n_decimal=10,
113
+ ):
114
+ # Set up PyOrbEphemerides.
115
+ if eph_file is None:
116
+ self.eph_file = os.path.join(get_oorb_data_dir(), "de405.dat")
117
+ else:
118
+ self.eph_file = eph_file
119
+ self.pyephems = PyOrbEphemerides(self.eph_file)
120
+ # And then set orbits.
121
+ self._set_orbits(orbits_obj)
122
+ # Save input parameters.
123
+ # We have to play some games with the start and end times,
124
+ # using Decimal, in order to get the subdivision and times to
125
+ # match exactly, up to n_decimal places.
126
+ self.n_decimal = int(n_decimal)
127
+ self.t_start = round(t_start, self.n_decimal)
128
+ self.t_span = round(t_span, self.n_decimal)
129
+ self.t_end = round(self.t_start + self.t_span, self.n_decimal)
130
+ if time_scale.upper() == "TAI":
131
+ self.time_scale = "TAI"
132
+ elif time_scale.upper() == "UTC":
133
+ self.time_scale = "UTC"
134
+ elif time_scale.upper() == "TT":
135
+ self.time_scale = "TT"
136
+ else:
137
+ raise ValueError("Do not understand time_scale; use TAI, UTC or TT.")
138
+ self.obscode = obscode
139
+ self.sky_tolerance = sky_tolerance
140
+ self.n_coeff = {}
141
+ self.n_coeff["position"] = int(n_coeff_position)
142
+ self.n_coeff["geo_dist"] = int(n_coeff_delta)
143
+ self.n_coeff["vmag"] = int(n_coeff_vmag)
144
+ self.n_coeff["elongation"] = int(n_coeff_elongation)
145
+ self.ngran = int(ngran)
146
+ # Precompute multipliers (we only do this once).
147
+ self._precompute_multipliers()
148
+ # Initialize attributes to save the coefficients and residuals.
149
+ self.coeffs = {
150
+ "obj_id": [],
151
+ "t_start": [],
152
+ "t_end": [],
153
+ "ra": [],
154
+ "dec": [],
155
+ "geo_dist": [],
156
+ "vmag": [],
157
+ "elongation": [],
158
+ }
159
+ self.resids = {
160
+ "obj_id": [],
161
+ "t_start": [],
162
+ "t_end": [],
163
+ "pos": [],
164
+ "geo_dist": [],
165
+ "vmag": [],
166
+ "elongation": [],
167
+ }
168
+ self.failed = []
169
+
170
+ def _set_orbits(self, orbits_obj):
171
+ """Set the orbits, to be used to generate ephemerides.
172
+
173
+ Parameters
174
+ ----------
175
+ orbits_obj : `rubin_sim.moving_objects.Orbits`
176
+ The orbits to use to generate ephemerides.
177
+ """
178
+ if not isinstance(orbits_obj, Orbits):
179
+ raise ValueError("Need to provide an Orbits object.")
180
+ self.orbits_obj = orbits_obj
181
+ self.pyephems.set_orbits(self.orbits_obj)
182
+
183
+ def _precompute_multipliers(self):
184
+ """Calculate multipliers for Chebyshev fitting.
185
+
186
+ Calculate these once, rather than for each segment.
187
+ """
188
+ # The nPoints are predetermined here, based on Yusra's earlier work.
189
+ # The weight is based on Newhall, X. X. 1989, Celestial Mechanics,
190
+ # 45, p. 305-310
191
+ self.multipliers = {}
192
+ self.multipliers["position"] = make_cheb_matrix(self.ngran + 1, self.n_coeff["position"], weight=0.16)
193
+ self.multipliers["vmag"] = make_cheb_matrix_only_x(self.ngran + 1, self.n_coeff["vmag"])
194
+ self.multipliers["geo_dist"] = make_cheb_matrix_only_x(self.ngran + 1, self.n_coeff["geo_dist"])
195
+ self.multipliers["elongation"] = make_cheb_matrix_only_x(self.ngran + 1, self.n_coeff["elongation"])
196
+
197
+ def _length_to_timestep(self, length):
198
+ """Convert chebyshev polynomial segment lengths to the
199
+ corresponding timestep over the segment.
200
+
201
+ Parameters
202
+ ----------
203
+ length : `float`
204
+ The chebyshev polynomial segment length (nominally, days).
205
+
206
+ Returns
207
+ -------
208
+ timestep : `float`
209
+ The corresponding timestep, = length/ngran (nominally, days).
210
+ """
211
+ return length / self.ngran
212
+
213
+ def make_all_times(self):
214
+ """Using t_start and t_end, generate a numpy array containing
215
+ times spaced at timestep = self.length/self.ngran.
216
+ The expected use for this time array would be to generate
217
+ ephemerides at each timestep.
218
+
219
+ Returns
220
+ -------
221
+ times : `np.ndarray`
222
+ Numpy array of times.
223
+ """
224
+ try:
225
+ self.length
226
+ except AttributeError:
227
+ raise AttributeError("Need to set self.timestep first, using calcSegmentLength.")
228
+ timestep = self._length_to_timestep(self.length)
229
+ times = np.arange(self.t_start, self.t_end + timestep / 2, timestep)
230
+ return times
231
+
232
+ def generate_ephemerides(self, times, by_object=True):
233
+ """Generate ephemerides using OpenOrb for all orbits.
234
+
235
+ Parameters
236
+ ----------
237
+ times : `np.ndarray`
238
+ The times to use for ephemeris generation.
239
+ """
240
+ return self.pyephems.generate_ephemerides(
241
+ times,
242
+ obscode=self.obscode,
243
+ eph_mode="N",
244
+ eph_type="basic",
245
+ time_scale=self.time_scale,
246
+ by_object=by_object,
247
+ )
248
+
249
+ def _round_length(self, length):
250
+ """Modify length, to fit in an 'integer multiple' within the
251
+ t_start/t_end, and to have the desired number of decimal values.
252
+
253
+ Parameters
254
+ ----------
255
+ length : `float`
256
+ The input length value to be rounded.
257
+
258
+ Returns
259
+ -------
260
+ length : `float`
261
+ The rounded length value.
262
+ """
263
+ length = round(length, self.n_decimal)
264
+ length_in = length
265
+ # Make length an integer value within the time interval,
266
+ # to last decimal place accuracy.
267
+ counter = 0
268
+ prev_int_factor = 0
269
+ num_tolerance = 10.0 ** (-1 * (self.n_decimal - 1))
270
+ while ((self.t_span % length) > num_tolerance) and (length > 0) and (counter < 20):
271
+ int_factor = int(self.t_span / length) + 1 # round up / ceiling
272
+ if int_factor == prev_int_factor:
273
+ int_factor = prev_int_factor + 1
274
+ prev_int_factor = int_factor
275
+ length = round(self.t_span / int_factor, self.n_decimal)
276
+ counter += 1
277
+ if (self.t_span % length) > num_tolerance or (length <= 0):
278
+ # Add this entire segment into the failed list.
279
+ for obj_id in self.orbits_obj.orbits["obj_id"].as_matrix():
280
+ self.failed.append((obj_id, self.t_start, self.t_end))
281
+ raise ValueError(
282
+ "Could not find a suitable length for the timespan (start %f, span %f), "
283
+ "starting with length %s, ending with length value %f"
284
+ % (self.t_start, self.t_span, str(length_in), length)
285
+ )
286
+ return length
287
+
288
+ def _test_residuals(self, length, cutoff=99):
289
+ """Calculate the position residual, for a test case.
290
+ Convenience function to make calcSegmentLength easier to read.
291
+ """
292
+ # The pos_resid used will be the 'cutoff' percentile of all
293
+ # max residuals per object.
294
+ max_pos_resids = np.zeros(len(self.orbits_obj), float)
295
+ timestep = self._length_to_timestep(length)
296
+ # Test for one segment near the start (would do at midpoint,
297
+ # but for long timespans this is not efficient ..
298
+ # a point near the start should be fine).
299
+ times = np.arange(self.t_start, self.t_start + length + timestep / 2, timestep)
300
+ # We must regenerate ephemerides here, because the timestep is
301
+ # different each time.
302
+ ephs = self.generate_ephemerides(times, by_object=True)
303
+ # Look for the coefficients and residuals.
304
+ for i, e in enumerate(ephs):
305
+ coeff_ra, coeff_dec, max_pos_resids[i] = self._get_coeffs_position(e)
306
+ # Find a representative value and return.
307
+ pos_resid = np.percentile(max_pos_resids, cutoff)
308
+ ratio = pos_resid / self.sky_tolerance
309
+ return pos_resid, ratio
310
+
311
+ def calc_segment_length(self, length=None):
312
+ """Set the typical initial ephemeris timestep and segment length
313
+ for all objects between t_start/t_end.
314
+
315
+ Sets self.length.
316
+
317
+ The segment length will fit into the time period between
318
+ t_start/t_end an approximately integer multiple of times,
319
+ and will only have a given number of decimal places.
320
+
321
+ Parameters
322
+ ----------
323
+ length : `float`, optional
324
+ If specified, this value for the length is used,
325
+ instead of calculating it here.
326
+ """
327
+ # If length is specified, use it and do nothing else.
328
+ if length is not None:
329
+ length = self._round_length(length)
330
+ pos_resid, ratio = self._test_residuals(length)
331
+ if pos_resid > self.sky_tolerance:
332
+ warnings.warn(
333
+ "Will set length and timestep, but this value of length "
334
+ "produces residuals (%f) > skyTolerance (%f)." % (pos_resid, self.sky_tolerance)
335
+ )
336
+ self.length = length
337
+ return
338
+ # Otherwise, calculate an appropriate length and timestep.
339
+ # Give a guess at a very approximate segment length,
340
+ # given the skyTolerance,
341
+ # purposefully trying to overestimate this value.
342
+ # The actual behavior of the residuals is not linear with
343
+ # segment length.
344
+ # There is a linear increase at low residuals
345
+ # < ~2 mas / segment length < 2 days
346
+ # Then at around 2 days the residuals blow up,
347
+ # increasing rapidly to about 5000 mas
348
+ # (depending on orbit .. TNOs, for example, increase but
349
+ # only to about 300 mas, when the residuals resume ~linear growth
350
+ # out to 70 day segments if ngran=128)
351
+ # Make an arbitrary cap on segment length at 60 days,
352
+ # (25000 mas) ~.5 arcminute accuracy.
353
+ max_length = 60
354
+ max_iterations = 50
355
+ if self.sky_tolerance < 5:
356
+ # This is the cap of the low-linearity regime,
357
+ # looping below will refine this value.
358
+ length = 2.0
359
+ elif self.sky_tolerance >= 5000:
360
+ # Make a very rough guess.
361
+ length = np.round((5000.0 / 20.0) * (self.sky_tolerance - 5000.0)) + 5.0
362
+ length = np.min([max_length, int(length * 10) / 10.0])
363
+ else:
364
+ # Try to pick a length in the middle of the fast increase.
365
+ length = 4.0
366
+ # Tidy up some characteristics of "length":
367
+ # make it fit an integer number of times into overall timespan.
368
+ # and use a given number of decimal places
369
+ # (easier for database storage).
370
+ length = self._round_length(length)
371
+ # Check the resulting residuals.
372
+ pos_resid, ratio = self._test_residuals(length)
373
+ counter = 0
374
+ # Now should be relatively close.
375
+ # Start to zero in using slope around the value.ngran
376
+ while pos_resid > self.sky_tolerance and counter <= max_iterations and length > 0:
377
+ length = length / 2
378
+ length = self._round_length(length)
379
+ pos_resid, ratio = self._test_residuals(length)
380
+ counter += 1
381
+ if counter > max_iterations or length <= 0:
382
+ # Add this entire segment into the failed list.
383
+ for obj_id in self.orbits_obj.orbits["obj_id"].as_matrix():
384
+ self.failed.append((obj_id, self.t_start, self.t_end))
385
+ error_message = "Could not find good segment length to meet skyTolerance %f" % (
386
+ self.sky_tolerance
387
+ )
388
+ error_message += " milliarcseconds within %d iterations. " % (max_iterations)
389
+ error_message += "Final residual was %f milli-arcseconds." % (pos_resid)
390
+ raise ValueError(error_message)
391
+ else:
392
+ self.length = length
393
+
394
+ def _get_coeffs_position(self, ephs):
395
+ """Calculate coefficients for the ra/dec values of a
396
+ single objects ephemerides.
397
+
398
+ Parameters
399
+ ----------
400
+ times : `np.ndarray`
401
+ The times of the ephemerides.
402
+ ephs : `np.ndarray`
403
+ The structured array returned by PyOrbEphemerides
404
+ holding ephemeris values, for one object.
405
+
406
+ Returns
407
+ -------
408
+ coeff_ra, coeff_dec, max_pos_resid : `np.ndarray`, `np.ndarray`,
409
+ `np.ndarray`
410
+ The ra coefficients, dec coefficients, and the positional error
411
+ residuals between fit and ephemeris values, in mas.
412
+ """
413
+ dradt_coord = ephs["dradt"] / np.cos(np.radians(ephs["dec"]))
414
+ coeff_ra, resid_ra, rms_ra_resid, max_ra_resid = chebfit(
415
+ ephs["time"],
416
+ three_sixty_to_neg(ephs["ra"]),
417
+ dxdt=dradt_coord,
418
+ x_multiplier=self.multipliers["position"][0],
419
+ dx_multiplier=self.multipliers["position"][1],
420
+ n_poly=self.n_coeff["position"],
421
+ )
422
+ coeff_dec, resid_dec, rms_dec_resid, max_dec_resid = chebfit(
423
+ ephs["time"],
424
+ ephs["dec"],
425
+ dxdt=ephs["ddecdt"],
426
+ x_multiplier=self.multipliers["position"][0],
427
+ dx_multiplier=self.multipliers["position"][1],
428
+ n_poly=self.n_coeff["position"],
429
+ )
430
+ max_pos_resid = np.max(np.sqrt(resid_dec**2 + (resid_ra * np.cos(np.radians(ephs["dec"]))) ** 2))
431
+ # Convert position residuals to mas.
432
+ max_pos_resid *= 3600.0 * 1000.0
433
+ return coeff_ra, coeff_dec, max_pos_resid
434
+
435
+ def _get_coeffs_other(self, ephs):
436
+ """Calculate coefficients for the ra/dec values of a
437
+ single objects ephemerides.
438
+
439
+ Parameters
440
+ ----------
441
+ ephs : `np.ndarray`
442
+ The structured array returned by PyOrbEphemerides
443
+ holding ephemeris values, for one object.
444
+
445
+ Returns
446
+ -------
447
+ coeffs, max_resids : `dict` of `float`
448
+ Dictionary containing the coefficients for each of 'geo_dist',
449
+ 'vmag', 'elongation', and another dictionary containing the
450
+ max residual values for each of 'geo_dist', 'vmag', 'elongation'.
451
+ """
452
+ coeffs = {}
453
+ max_resids = {}
454
+ for key, ephValue in zip(("geo_dist", "vmag", "elongation"), ("geo_dist", "magV", "solarelon")):
455
+ coeffs[key], resid, rms, max_resids[key] = chebfit(
456
+ ephs["time"],
457
+ ephs[ephValue],
458
+ dxdt=None,
459
+ x_multiplier=self.multipliers[key],
460
+ dx_multiplier=None,
461
+ n_poly=self.n_coeff[key],
462
+ )
463
+ return coeffs, max_resids
464
+
465
+ def calc_segments(self):
466
+ """Run the calculation of all segments over the entire time span."""
467
+ # First calculate ephemerides for all objects, over entire time span.
468
+ # For some objects, we will end up recalculating the ephemeride values,
469
+ # but most should be fine.
470
+ times = self.make_all_times()
471
+ ephs = self.generate_ephemerides(times)
472
+ eps = self._length_to_timestep(self.length) / 4.0
473
+ # Loop through each object to generate coefficients.
474
+ for orbit_obj, e in zip(self.orbits_obj, ephs):
475
+ t_segment_start = self.t_start
476
+ # Cycle through all segments until we reach the end of the
477
+ # period we're fitting.
478
+ while t_segment_start < (self.t_end - eps):
479
+ # Identify the subset of times and ephemerides
480
+ # which are relevant for this segment
481
+ # (at the default segment size).
482
+ t_segment_end = round(t_segment_start + self.length, self.n_decimal)
483
+ subset = np.where((times >= t_segment_start) & (times < t_segment_end + eps))
484
+ self.calc_one_segment(orbit_obj, e[subset])
485
+ t_segment_start = t_segment_end
486
+
487
+ def calc_one_segment(self, orbit_obj, ephs):
488
+ """Calculate the coefficients for a single Chebyshev segment,
489
+ for a single object.
490
+
491
+ Calculates the coefficients and residuals, and saves this
492
+ information to self.coeffs, self.resids, and
493
+ (if there are problems), self.failed.
494
+
495
+ Parameters
496
+ ----------
497
+ orbit_obj : `rubin_sim.moving_objects.Orbits`
498
+ The single Orbits object we're fitting at the moment.
499
+ ephs : `np.ndarray`
500
+ The ephemerides we're fitting at the moment
501
+ (for the single object / single segment).
502
+ """
503
+ obj_id = orbit_obj.orbits.obj_id.iloc[0]
504
+ t_segment_start = ephs["time"][0]
505
+ t_segment_end = ephs["time"][-1]
506
+ coeff_ra, coeff_dec, max_pos_resid = self._get_coeffs_position(ephs)
507
+ if max_pos_resid > self.sky_tolerance:
508
+ self._subdivide_segment(orbit_obj, ephs)
509
+ else:
510
+ coeffs, max_resids = self._get_coeffs_other(ephs)
511
+ fit_failed = False
512
+ for k in max_resids:
513
+ if np.isnan(max_resids[k]):
514
+ fit_failed = True
515
+ if fit_failed:
516
+ warnings.warn(
517
+ "Fit failed for orbit_obj %s for times between %f and %f"
518
+ % (obj_id, t_segment_start, t_segment_end)
519
+ )
520
+ self.failed.append((orbit_obj.orbits["obj_id"], t_segment_start, t_segment_end))
521
+ else:
522
+ # Consolidate items into the tracked coefficient values.
523
+ self.coeffs["obj_id"].append(obj_id)
524
+ self.coeffs["t_start"].append(t_segment_start)
525
+ self.coeffs["t_end"].append(t_segment_end)
526
+ self.coeffs["ra"].append(coeff_ra)
527
+ self.coeffs["dec"].append(coeff_dec)
528
+ self.coeffs["geo_dist"].append(coeffs["geo_dist"])
529
+ self.coeffs["vmag"].append(coeffs["vmag"])
530
+ self.coeffs["elongation"].append(coeffs["elongation"])
531
+ # Consolidate items into the tracked residual values.
532
+ self.resids["obj_id"].append(obj_id)
533
+ self.resids["t_start"].append(t_segment_start)
534
+ self.resids["t_end"].append(t_segment_end)
535
+ self.resids["pos"].append(max_pos_resid)
536
+ self.resids["geo_dist"].append(max_resids["geo_dist"])
537
+ self.resids["vmag"].append(max_resids["geo_dist"])
538
+ self.resids["elongation"].append(max_resids["elongation"])
539
+
540
+ def _subdivide_segment(self, orbit_obj, ephs):
541
+ """Subdivide a segment, then calculate the segment coefficients.
542
+
543
+ Parameters
544
+ ----------
545
+ orbit_obj : `rubin_sim.moving_objects.Orbits`
546
+ The single Orbits object we're fitting at the moment.
547
+ ephs : `np.ndarray`
548
+ The ephemerides we're fitting at the moment
549
+ (for the single object / single segment).
550
+ """
551
+ new_cheby = ChebyFits(
552
+ orbit_obj,
553
+ ephs["time"][0],
554
+ (ephs["time"][-1] - ephs["time"][0]),
555
+ time_scale=self.time_scale,
556
+ obscode=self.obscode,
557
+ sky_tolerance=self.sky_tolerance,
558
+ n_coeff_position=self.n_coeff["position"],
559
+ n_coeff_vmag=self.n_coeff["vmag"],
560
+ n_coeff_delta=self.n_coeff["geo_dist"],
561
+ n_coeff_elongation=self.n_coeff["elongation"],
562
+ ngran=self.ngran,
563
+ eph_file=self.eph_file,
564
+ n_decimal=self.n_decimal,
565
+ )
566
+ try:
567
+ new_cheby.calc_segment_length()
568
+ except ValueError as ve:
569
+ # Could not find a good segment length.
570
+ warningmessage = "Objid %s, segment %f to %f " % (
571
+ orbit_obj.orbits.obj_id.iloc[0],
572
+ ephs["time"][0],
573
+ ephs["time"][-1],
574
+ )
575
+ warningmessage += " - error: %s" % (ve)
576
+ warnings.warn(warningmessage)
577
+ self.failed += new_cheby.failed
578
+ return
579
+ new_cheby.calc_segments()
580
+ # Add subdivided segment values into tracked values here.
581
+ for k in self.coeffs:
582
+ self.coeffs[k] += new_cheby.coeffs[k]
583
+ for k in self.resids:
584
+ self.resids[k] += new_cheby.resids[k]
585
+ self.failed += new_cheby.failed
586
+
587
+ def write(self, coeff_file, resid_file, failed_file, append=False):
588
+ """Write coefficients, residuals and failed fits to disk.
589
+
590
+ Parameters
591
+ ----------
592
+ coeff_file : `str`
593
+ The filename for the coefficient values.
594
+ resid_file : `str`
595
+ The filename for the residual values.
596
+ failed_file : `str`
597
+ The filename to write the failed fit information
598
+ (if failed objects exist).
599
+ append : `bool`, optional
600
+ Flag to append (or overwrite) the output files.
601
+ """
602
+
603
+ warnings.warn(
604
+ "Writing cheby fit values may have cross-platform issues. Consider passing values directly"
605
+ )
606
+
607
+ if append:
608
+ open_mode = "aw"
609
+ else:
610
+ open_mode = "w"
611
+ # Write a header to the coefficients file, if writing to a new file:
612
+ if (not append) or (not os.path.isfile(coeff_file)):
613
+ header = "obj_id t_start t_end "
614
+ header += " ".join(["ra_%d" % x for x in range(self.n_coeff["position"])]) + " "
615
+ header += " ".join(["dec_%d" % x for x in range(self.n_coeff["position"])]) + " "
616
+ header += " ".join(["geo_dist_%d" % x for x in range(self.n_coeff["geo_dist"])]) + " "
617
+ header += " ".join(["vmag_%d" % x for x in range(self.n_coeff["vmag"])]) + " "
618
+ header += " ".join(["elongation_%d" % x for x in range(self.n_coeff["elongation"])])
619
+ else:
620
+ header = None
621
+ if (not append) or (not os.path.isfile(resid_file)):
622
+ resid_header = "obj_id segNum t_start t_end length pos geo_dist vmag elong"
623
+ else:
624
+ resid_header = None
625
+ timeformat = "%." + "%s" % self.n_decimal + "f"
626
+ with open(coeff_file, open_mode) as f:
627
+ if header is not None:
628
+ print(header, file=f)
629
+ for i, (obj_id, t_start, t_end, cRa, cDec, cDelta, cVmag, cE) in enumerate(
630
+ zip(
631
+ self.coeffs["obj_id"],
632
+ self.coeffs["t_start"],
633
+ self.coeffs["t_end"],
634
+ self.coeffs["ra"],
635
+ self.coeffs["dec"],
636
+ self.coeffs["geo_dist"],
637
+ self.coeffs["vmag"],
638
+ self.coeffs["elongation"],
639
+ )
640
+ ):
641
+ print(
642
+ "%s %s %s %s %s %s %s %s"
643
+ % (
644
+ obj_id,
645
+ timeformat % t_start,
646
+ timeformat % t_end,
647
+ " ".join("%.14e" % j for j in cRa),
648
+ " ".join("%.14e" % j for j in cDec),
649
+ " ".join("%.7e" % j for j in cDelta),
650
+ " ".join("%.7e" % j for j in cVmag),
651
+ " ".join("%.7e" % j for j in cE),
652
+ ),
653
+ file=f,
654
+ )
655
+
656
+ with open(resid_file, open_mode) as f:
657
+ if resid_header is not None:
658
+ print(resid_header, file=f)
659
+ for i, (obj_id, t_start, t_end, rPos, rDelta, rVmag, rE) in enumerate(
660
+ zip(
661
+ self.resids["obj_id"],
662
+ self.resids["t_start"],
663
+ self.resids["t_end"],
664
+ self.resids["pos"],
665
+ self.resids["geo_dist"],
666
+ self.resids["vmag"],
667
+ self.resids["elongation"],
668
+ )
669
+ ):
670
+ print(
671
+ "%s %i %.14f %.14f %.14f %.14e %.14e %.14e %.14e"
672
+ % (
673
+ obj_id,
674
+ i + 1,
675
+ t_start,
676
+ t_end,
677
+ (t_end - t_start),
678
+ rPos,
679
+ rDelta,
680
+ rVmag,
681
+ rE,
682
+ ),
683
+ file=f,
684
+ )
685
+
686
+ if len(self.failed) > 0:
687
+ with open(failed_file, open_mode) as f:
688
+ for i, failed in enumerate(self.failed):
689
+ print(" ".join([str(x) for x in failed]), file=f)