pyreduce-astro 0.8a1__tar.gz → 0.8a2__tar.gz

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 (234) hide show
  1. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/CHANGELOG.md +24 -0
  2. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/CLAUDE.md +17 -3
  3. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/PKG-INFO +1 -1
  4. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/WhatsNew.md +16 -0
  5. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/andes_riz.py +6 -9
  6. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/andes_yjh_example.py +20 -27
  7. pyreduce_astro-0.8a2/examples/debug_single_swath.py +139 -0
  8. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/harps_gridsearch.py +1 -1
  9. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/just_one_swath.py +11 -30
  10. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/mosaic_nir.py +6 -10
  11. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/mosaic_preset-slitfunc.py +32 -26
  12. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/mosaic_vis.py +4 -8
  13. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/uves_callfunc.py +6 -6
  14. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyproject.toml +1 -1
  15. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/cli.py +1 -1
  16. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/combine_frames.py +15 -17
  17. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/continuum_normalization.py +24 -24
  18. pyreduce_astro-0.8a2/pyreduce/echelle.py +18 -0
  19. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/estimate_background_scatter.py +42 -37
  20. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/extract.py +203 -102
  21. pyreduce_astro-0.8a2/pyreduce/instruments/ANDES_RIZ/__init__.py +27 -0
  22. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/config.yaml +3 -3
  23. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/settings.json +11 -2
  24. pyreduce_astro-0.8a2/pyreduce/instruments/ANDES_RIZ/wavecal_r0_HDF.npz +0 -0
  25. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/config.yaml +2 -3
  26. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings.json +1 -1
  27. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings_nir.json +1 -4
  28. pyreduce_astro-0.8a2/pyreduce/instruments/defaults/atlas/lfc.fits +0 -0
  29. pyreduce_astro-0.8a2/pyreduce/instruments/defaults/atlas/lfc_list.txt +321 -0
  30. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/models.py +14 -6
  31. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/pipeline.py +50 -112
  32. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/rectify.py +60 -31
  33. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/reduce.py +832 -768
  34. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/slit_curve.py +70 -33
  35. pyreduce_astro-0.8a2/pyreduce/spectra.py +668 -0
  36. pyreduce_astro-0.8a2/pyreduce/tools/combine.py +184 -0
  37. pyreduce_astro-0.8a2/pyreduce/trace.py +1438 -0
  38. pyreduce_astro-0.8a2/pyreduce/trace_model.py +614 -0
  39. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/util.py +0 -100
  40. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/wavelength_calibration.py +98 -71
  41. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/create_wavelength_guess.py +10 -5
  42. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/plot_swath_debug.py +0 -3
  43. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/uv.lock +1 -1
  44. pyreduce_astro-0.8a1/examples/debug_single_swath.py +0 -229
  45. pyreduce_astro-0.8a1/pyreduce/curvature_model.py +0 -194
  46. pyreduce_astro-0.8a1/pyreduce/echelle.py +0 -413
  47. pyreduce_astro-0.8a1/pyreduce/instruments/ANDES_RIZ/__init__.py +0 -15
  48. pyreduce_astro-0.8a1/pyreduce/instruments/ANDES_RIZ/wavecal_r0_HDF.npz +0 -0
  49. pyreduce_astro-0.8a1/pyreduce/tools/combine.py +0 -118
  50. pyreduce_astro-0.8a1/pyreduce/trace.py +0 -1904
  51. pyreduce_astro-0.8a1/trace_spectrum_redesign.md +0 -452
  52. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/.gitattributes +0 -0
  53. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/.gitignore +0 -0
  54. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/.pre-commit-config.yaml +0 -0
  55. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/.python-version +0 -0
  56. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/.readthedocs.yaml +0 -0
  57. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/AGENTS.md +0 -0
  58. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/LICENSE +0 -0
  59. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/README.md +0 -0
  60. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/crires_plus_example.py +0 -0
  61. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/custom_instrument_example.py +0 -0
  62. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/harpn_example.py +0 -0
  63. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/harps_example.py +0 -0
  64. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/jwst_miri_example.py +0 -0
  65. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/jwst_niriss_example.py +0 -0
  66. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/lick_apf_example.py +0 -0
  67. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/mcdonald_example.py +0 -0
  68. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/metis_ifu_example.py +0 -0
  69. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/metis_lss_example.py +0 -0
  70. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/micado_example.py +0 -0
  71. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/neid_example.py +0 -0
  72. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/nirspec_example.py +0 -0
  73. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/toes_example.py +0 -0
  74. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/uves_example.py +0 -0
  75. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/xshooter_example.py +0 -0
  76. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/hatch_build.py +0 -0
  77. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/__init__.py +0 -0
  78. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/__main__.py +0 -0
  79. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/__init__.py +0 -0
  80. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/build_extract.py +0 -0
  81. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/slit_func_2d_xi_zeta_bd.c +0 -0
  82. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/slit_func_2d_xi_zeta_bd.h +0 -0
  83. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/slit_func_bd.c +0 -0
  84. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/slit_func_bd.h +0 -0
  85. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clipnflip.py +0 -0
  86. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/configuration.py +0 -0
  87. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/cwrappers.py +0 -0
  88. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/datasets.py +0 -0
  89. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/order_centers_r0.yaml +0 -0
  90. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/order_centers_r1.yaml +0 -0
  91. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/order_centers_r2.yaml +0 -0
  92. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/wavecal_r1_HDF.npz +0 -0
  93. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/wavecal_r2_HDF.npz +0 -0
  94. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/__init__.py +0 -0
  95. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/order_centers_h.yaml +0 -0
  96. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/order_centers_j.yaml +0 -0
  97. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/order_centers_y.yaml +0 -0
  98. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/settings.json +0 -0
  99. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/__init__.py +0 -0
  100. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/config.yaml +0 -0
  101. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/mask_det1.fits.gz +0 -0
  102. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/mask_det2.fits.gz +0 -0
  103. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/mask_det3.fits.gz +0 -0
  104. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/settings.json +0 -0
  105. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/wavecal_J1228_det1.npz +0 -0
  106. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/wavecal_J1228_det2.npz +0 -0
  107. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/wavecal_J1228_det3.npz +0 -0
  108. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPN/__init__.py +0 -0
  109. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPN/config.yaml +0 -0
  110. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPN/settings.json +0 -0
  111. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPN/wavecal_harpn_2D.npz +0 -0
  112. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPN/wavecal_harpn_fibB_2D.npz +0 -0
  113. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/__init__.py +0 -0
  114. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/config.yaml +0 -0
  115. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/mask_blue.fits.gz +0 -0
  116. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/mask_red.fits.gz +0 -0
  117. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/settings.json +0 -0
  118. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/wavecal_blue_2D.npz +0 -0
  119. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/wavecal_blue_pol_2D.npz +0 -0
  120. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/wavecal_red_2D.npz +0 -0
  121. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/wavecal_red_pol_2D.npz +0 -0
  122. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_MIRI/__init__.py +0 -0
  123. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_MIRI/config.yaml +0 -0
  124. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_MIRI/mask_lrs_slitless.fits.gz +0 -0
  125. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_MIRI/settings.json +0 -0
  126. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_NIRISS/__init__.py +0 -0
  127. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_NIRISS/config.yaml +0 -0
  128. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_NIRISS/mask_gr700xd.fits.gz +0 -0
  129. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_NIRISS/settings.json +0 -0
  130. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/LICK_APF/__init__.py +0 -0
  131. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/LICK_APF/config.yaml +0 -0
  132. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/LICK_APF/mask_.fits.gz +0 -0
  133. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/LICK_APF/settings.json +0 -0
  134. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MCDONALD/__init__.py +0 -0
  135. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MCDONALD/config.yaml +0 -0
  136. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MCDONALD/mask.fits.gz +0 -0
  137. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MCDONALD/settings.json +0 -0
  138. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MCDONALD/wavecal.npz +0 -0
  139. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_IFU/__init__.py +0 -0
  140. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_IFU/config.yaml +0 -0
  141. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_IFU/settings.json +0 -0
  142. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_LSS/__init__.py +0 -0
  143. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_LSS/config.yaml +0 -0
  144. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_LSS/settings.json +0 -0
  145. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_LSS/wavecal_l_2D.npz +0 -0
  146. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_LSS/wavecal_m_2D.npz +0 -0
  147. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MICADO/__init__.py +0 -0
  148. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MICADO/config.yaml +0 -0
  149. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MICADO/settings.json +0 -0
  150. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MICADO/wavecal_HK_3arcsec_chip5.npz +0 -0
  151. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/__init__.py +0 -0
  152. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/bundle_centers_nir.yaml +0 -0
  153. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/bundle_centers_vis1.yaml +0 -0
  154. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/bundle_centers_vis2.yaml +0 -0
  155. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/bundle_centers_vis3.yaml +0 -0
  156. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/bundle_centers_vis4.yaml +0 -0
  157. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/config.yaml +0 -0
  158. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/mask_nir.fits.gz +0 -0
  159. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/mosaic_fiber_positions.npz +0 -0
  160. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings_VIS1.json +0 -0
  161. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings_VIS2.json +0 -0
  162. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings_VIS3.json +0 -0
  163. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings_VIS4.json +0 -0
  164. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NEID/__init__.py +0 -0
  165. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NEID/config.yaml +0 -0
  166. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NEID/settings.json +0 -0
  167. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NIRSPEC/__init__.py +0 -0
  168. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NIRSPEC/config.yaml +0 -0
  169. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NIRSPEC/mask_nirspec.fits.gz +0 -0
  170. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NIRSPEC/settings.json +0 -0
  171. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NIRSPEC/wavecal_K2.npz +0 -0
  172. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NTE/__init__.py +0 -0
  173. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NTE/config.yaml +0 -0
  174. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NTE/settings.json +0 -0
  175. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/__init__.py +0 -0
  176. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/config.yaml +0 -0
  177. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_blue.fits.gz +0 -0
  178. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_blue_binned_2_2.fits.gz +0 -0
  179. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_middle.fits.gz +0 -0
  180. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_middle_2x2_split.fits.gz +0 -0
  181. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_middle_binned_2_2.fits.gz +0 -0
  182. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_red.fits.gz +0 -0
  183. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_red_2x2.fits.gz +0 -0
  184. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_red_2x2_split.fits.gz +0 -0
  185. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_red_binned_2_2.fits.gz +0 -0
  186. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/settings.json +0 -0
  187. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_blue_360nm_2D.npz +0 -0
  188. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_blue_390nm_2D.npz +0 -0
  189. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_blue_437nm_2D.npz +0 -0
  190. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_2x2_2D.npz +0 -0
  191. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_565nm_2D.npz +0 -0
  192. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_580nm_2D.npz +0 -0
  193. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_600nm_2D.npz +0 -0
  194. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_665nm_2D.npz +0 -0
  195. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_860nm_2D.npz +0 -0
  196. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_red_580nm_2D.npz +0 -0
  197. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_red_600nm_2D.npz +0 -0
  198. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_red_665nm_2D.npz +0 -0
  199. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_red_760nm_2D.npz +0 -0
  200. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_red_860nm_2D.npz +0 -0
  201. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/XSHOOTER/__init__.py +0 -0
  202. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/XSHOOTER/config.yaml +0 -0
  203. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/XSHOOTER/mask_nir.fits.gz +0 -0
  204. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/XSHOOTER/settings.json +0 -0
  205. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/XSHOOTER/wavecal_nir.npz +0 -0
  206. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/__init__.py +0 -0
  207. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/common.py +0 -0
  208. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/atlas/thar.fits +0 -0
  209. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/atlas/thar_list.txt +0 -0
  210. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/atlas/une.fits +0 -0
  211. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/config.yaml +0 -0
  212. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/schema.json +0 -0
  213. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/settings.json +0 -0
  214. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/filters.py +0 -0
  215. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/instrument_info.py +0 -0
  216. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/tools/__init__.py +0 -0
  217. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/andes_hdf_to_wavecal.py +0 -0
  218. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/andes_plot_spectra.py +0 -0
  219. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/argon.line +0 -0
  220. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/bpm_creator.py +0 -0
  221. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/convert_wavecal.py +0 -0
  222. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/download_files.py +0 -0
  223. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/ipy_startup.py +0 -0
  224. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/make_notebook.py +0 -0
  225. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/mosaic_vis_bundle_plot.py +0 -0
  226. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/neon.lin +0 -0
  227. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/neon.line +0 -0
  228. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/plot_1d_vs_2d_extraction.py +0 -0
  229. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/plot_extractmodel_comp.py +0 -0
  230. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/pymultispec.py +0 -0
  231. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/thar.npz +0 -0
  232. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/wavecal_creator.py +0 -0
  233. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/wavecal_creator_from_existing.py +0 -0
  234. {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/xshooter_nir.json +0 -0
@@ -1,6 +1,30 @@
1
1
  # Changelog
2
2
 
3
3
 
4
+ ## [0.8a2] - 2026-02-03
5
+
6
+ ### Added
7
+ - `Trace` dataclass (trace_model.py) for unified trace data model
8
+ - `Spectrum`/`Spectra` classes (spectra.py) replacing legacy Echelle format
9
+ - `Trace.wlen(x)` method to evaluate wavelength polynomial
10
+ - Per-group wavelength calibration support
11
+ - LFC wavecal support for ANDES_RIZ
12
+ - docs/output_formats.md documenting new file formats
13
+
14
+ ### Changed
15
+ - All pipeline steps use `list[Trace]` interface instead of array-based
16
+ - `extract()` takes `list[Trace]`, returns `list[Spectrum]`
17
+ - Renamed `nord`/`iord` to `ntrace`/`idx` for clarity
18
+ - Renamed `Trace.fiber` to `Trace.group`, added `fiber_idx` for per-fiber wavecal
19
+ - File format: traces.npz -> traces.fits (unified FITS format)
20
+ - Wavelength calibration stored in traces.fits instead of separate wavecal.npz
21
+ - Spectra use NaN masking instead of COLUMNS+MASK redundancy
22
+
23
+ ### Removed
24
+ - `curvature_model.py` (curvature now stored in Trace)
25
+ - `echelle.py` functionality (deprecated, use spectra.py)
26
+ - Dead code from util.py
27
+
4
28
  ## [0.8a1] - 2026-02-03
5
29
 
6
30
  ### Added
@@ -31,11 +31,11 @@ pyreduce/
31
31
  ├── reduce.py # Step class implementations
32
32
  ├── configuration.py # Config loading (settings JSON)
33
33
  ├── extract.py # Optimal extraction algorithm
34
- ├── curvature_model.py # SlitCurvature dataclass for curvature data
34
+ ├── trace_model.py # Trace dataclass (geometry, curvature, wavelength)
35
+ ├── spectra.py # Spectrum/Spectra classes for I/O
35
36
  ├── trace.py # Order detection and tracing
36
37
  ├── wavelength_calibration.py # Wavelength solution fitting
37
38
  ├── combine_frames.py # Frame combination/calibration
38
- ├── echelle.py # Echelle spectrum I/O
39
39
  ├── util.py # Utilities, plotting helpers
40
40
  ├── cwrappers.py # CFFI C extension wrappers
41
41
 
@@ -71,6 +71,19 @@ This means:
71
71
  - **Traces** are polynomial functions of x, giving y-position
72
72
  - **`extraction_height`** is the extraction aperture size (fraction of order separation, or pixels if >1.5)
73
73
 
74
+ ## Spectral Order Numbers (Trace.m)
75
+
76
+ Each `Trace` has an `m` attribute representing the physical spectral (diffraction) order number - not a sequential index. Higher order numbers = shorter wavelengths.
77
+
78
+ **Assignment priority:**
79
+ 1. `order_centers_{channel}.yaml` in instrument directory - traces matched by y-position during detection
80
+ 2. `obase` from linelist file (`wavecal_*.npz`) - assigned as `m = obase + trace_index` during wavecal
81
+ 3. Sequential fallback (legacy/MOSAIC mode)
82
+
83
+ **Why it matters:** The 2D wavelength polynomial fits `wavelength = P(x, m)`. Using physical order numbers enables accurate interpolation between orders. `Trace.wlen(x)` evaluates this polynomial at the trace's order number.
84
+
85
+ See `docs/wavecal_linelist.md` for details on wavelength calibration and order numbering.
86
+
74
87
  ## Pipeline Steps
75
88
 
76
89
  The reduction pipeline consists of these steps (in typical order):
@@ -310,7 +323,8 @@ After a fresh clone or `rm -rf .venv`, run `uv sync && uv run reduce-build` to s
310
323
  | `pyreduce/pipeline.py` | Fluent Pipeline API, `from_instrument()` |
311
324
  | `pyreduce/reduce.py` | Step class implementations |
312
325
  | `pyreduce/extract.py` | Optimal extraction algorithm |
313
- | `pyreduce/curvature_model.py` | SlitCurvature dataclass, save/load |
326
+ | `pyreduce/trace_model.py` | Trace dataclass (pos, slit, wave, column_range) |
327
+ | `pyreduce/spectra.py` | Spectrum/Spectra classes for FITS I/O |
314
328
  | `pyreduce/slit_curve.py` | Slit curvature fitting (degree 1-5) |
315
329
  | `pyreduce/wavelength_calibration.py` | Wavelength solution fitting |
316
330
  | `pyreduce/trace.py` | Order detection and tracing |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyreduce-astro
3
- Version: 0.8a1
3
+ Version: 0.8a2
4
4
  Summary: A data reduction package for echelle spectrographs
5
5
  Project-URL: Homepage, https://github.com/ivh/PyReduce
6
6
  Project-URL: Documentation, https://pyreduce-astro.readthedocs.io
@@ -1,3 +1,19 @@
1
+ ## What's New in PyReduce 0.8 ?
2
+
3
+ ### Unified Trace Data Model
4
+
5
+ The `Trace` dataclass now holds all trace-related data in one place: position polynomial, slit curvature, and wavelength calibration. Pipeline steps update traces in-place as they run, eliminating separate `.curve.npz` and `.wavecal.npz` files. All trace data is saved to a single `.traces.fits` file.
6
+
7
+ See [docs/redesign.md](docs/redesign.md) for details.
8
+
9
+ ### New Spectra Format
10
+
11
+ Extracted spectra use a new per-trace FITS format with NaN masking, replacing the legacy Echelle class. Each spectrum row includes order number, group ID, and extraction metadata.
12
+
13
+ See [docs/output_formats.md](docs/output_formats.md) for the file format specification.
14
+
15
+ ---
16
+
1
17
  ## What's New in PyReduce 0.7 ?
2
18
 
3
19
  ### Multi-Fiber Instrument Support
@@ -153,18 +153,15 @@ if __name__ == "__main__":
153
153
 
154
154
  print("\n=== Running pipeline ===")
155
155
  # pipe.trace(trace_files)
156
- pipe.curvature(wavecal_files)
156
+ # pipe.curvature(wavecal_files)
157
+ # Full wavecal: master -> init (MCMC line matching) -> finalize
158
+ pipe.wavelength_calibration([lfc_combined_path])
157
159
  pipe.extract([lfc_combined_path])
158
160
 
159
161
  results = pipe.run()
160
162
 
161
163
  if "trace" in results:
162
- traces, column_range, *_ = results["trace"]
164
+ traces = results["trace"] # list[Trace]
163
165
  print(f"Traces found: {len(traces)}")
164
-
165
- if "trace_groups" in results and results["trace_groups"]:
166
- group_traces, group_cr, group_heights = results["trace_groups"]
167
- print(f"Fiber groups: {list(group_traces.keys())}")
168
- for name, traces_dict in group_traces.items():
169
- if isinstance(traces_dict, dict):
170
- print(f" {name}: orders {list(traces_dict.keys())}")
166
+ for t in traces[:3]:
167
+ print(f" m={t.m}, group={t.group}, columns={t.column_range}")
@@ -2,7 +2,8 @@
2
2
  ANDES_YJH instrument example: Multi-fiber tracing with Pipeline API.
3
3
 
4
4
  Demonstrates tracing fibers illuminated in separate flat field images
5
- (even/odd pattern) using the Pipeline's trace_raw() and organize() methods.
5
+ (even/odd pattern). The Pipeline trace() step handles merging traces
6
+ from multiple files.
6
7
 
7
8
  The fiber config in ANDES_YJH/config.yaml handles:
8
9
  - order_centers_file: assigns traces to spectral orders by y-position (channel-specific)
@@ -58,33 +59,25 @@ LOAD_TRACE = True # Set True to load traces from previous run
58
59
 
59
60
  if LOAD_TRACE:
60
61
  print("\nLoading traces from previous run...")
61
- traces, column_range, heights = pipe._run_step("trace", None, load_only=True)
62
- print(f" Loaded {len(traces)} traces")
63
-
64
- # Re-run organize with current config (picks up any config changes)
65
- print("\nRe-organizing traces with current config...")
66
- pipe.organize(traces, column_range)
62
+ trace_objects = pipe._run_step("trace", None, load_only=True) # list[Trace]
63
+ print(f" Loaded {len(trace_objects)} traces")
67
64
  else:
68
- # Trace each flat independently
69
- print(f"\nTracing even fibers from {os.path.basename(file_even)}...")
70
- traces_even, cr_even, _ = pipe.trace_raw([file_even])
71
- print(f" Found {len(traces_even)} traces")
72
-
73
- print(f"\nTracing odd fibers from {os.path.basename(file_odd)}...")
74
- traces_odd, cr_odd, _ = pipe.trace_raw([file_odd])
75
- print(f" Found {len(traces_odd)} traces")
76
-
77
- # Organize into fiber groups
78
- print("\nOrganizing traces into fiber groups...")
79
- pipe.organize(traces_even, cr_even, traces_odd, cr_odd)
80
-
81
- # Access organized groups
82
- if "trace_groups" in pipe._data and pipe._data["trace_groups"][0]:
83
- group_traces, group_cr, group_heights = pipe._data["trace_groups"]
84
- print("Fiber groups:")
85
- for name, traces_dict in group_traces.items():
86
- n_traces = len(traces_dict)
87
- print(f" {name}: {n_traces} traces")
65
+ # Trace both files together - the pipeline will organize by fiber config
66
+ print(
67
+ f"\nTracing fibers from {os.path.basename(file_even)} and {os.path.basename(file_odd)}..."
68
+ )
69
+ pipe.trace([file_even, file_odd])
70
+ results = pipe.run()
71
+ trace_objects = results["trace"] # list[Trace]
72
+ print(f" Found {len(trace_objects)} traces")
73
+
74
+ # Show trace info
75
+ print("\nTraces:")
76
+ fibers = {t.fiber for t in trace_objects}
77
+ print(f" Fibers: {sorted(fibers)}")
78
+ for fiber in sorted(fibers)[:3]:
79
+ count = sum(1 for t in trace_objects if t.fiber == fiber)
80
+ print(f" {fiber}: {count} traces")
88
81
 
89
82
  # --- Create combined flat for extraction ---
90
83
  print("\nCombining even/odd flats...")
@@ -0,0 +1,139 @@
1
+ """
2
+ Debug script for extracting a single swath from a specific trace.
3
+
4
+ Manually loads traces from FITS, cuts out one swath, runs extraction,
5
+ and saves all intermediate outputs for inspection.
6
+ """
7
+
8
+ import os
9
+ from os.path import join
10
+
11
+ import numpy as np
12
+ from astropy.io import fits
13
+
14
+ from pyreduce.cwrappers import slitfunc_curved
15
+ from pyreduce.extract import fix_parameters, make_bins
16
+ from pyreduce.trace_model import load_traces
17
+ from pyreduce.util import make_index
18
+
19
+ # === Configuration ===
20
+ # Which trace and swath to extract
21
+ TRACE_INDEX = 4 # 5th trace (0-indexed), i.e. 5th fiber bundle center
22
+ SWATH_INDEX = 2 # 3rd swath (0-indexed)
23
+
24
+ # Extraction parameters
25
+ EXTRACTION_HEIGHT = 0.5 # fraction of order spacing
26
+ OSAMPLE = 1
27
+ LAMBDA_SF = 0.1
28
+ LAMBDA_SP = 0
29
+ MAXITER = 20
30
+ GAIN = 1.0
31
+
32
+ # === Data paths ===
33
+ data_dir = os.environ.get("REDUCE_DATA", os.path.expanduser("~/REDUCE_DATA"))
34
+ base_dir = join(data_dir, "MOSAIC", "REF_E2E", "NIR")
35
+ output_dir = join(data_dir, "MOSAIC", "reduced", "NIR")
36
+
37
+ flat_file = join(
38
+ base_dir,
39
+ "E2E_FLAT_DIT_20s_MOSAIC_2Cam_c01",
40
+ "E2E_FLAT_DIT_20s_MOSAIC_2Cam_c01_STATIC_FOCAL_PLANE.fits",
41
+ )
42
+ trace_file = join(output_dir, "MOSAIC_NIR.traces.fits")
43
+
44
+
45
+ def load_image(flat_file):
46
+ """Load the flat field image."""
47
+ with fits.open(flat_file) as hdu:
48
+ img = hdu[0].data.astype(float)
49
+ print(f"Loaded image: {img.shape}")
50
+ return img
51
+
52
+
53
+ def main():
54
+ # Load data
55
+ img = load_image(flat_file)
56
+ nrow, ncol = img.shape
57
+
58
+ # Load traces from FITS file
59
+ trace_list, _ = load_traces(trace_file)
60
+ print(f"Loaded {len(trace_list)} traces from {trace_file}")
61
+
62
+ # Convert to arrays for fix_parameters
63
+ traces = np.array([t.pos for t in trace_list])
64
+ column_range = np.array([t.column_range for t in trace_list])
65
+ ntrace = len(traces)
66
+
67
+ # Get extraction parameters
68
+ xwd, cr = fix_parameters(
69
+ EXTRACTION_HEIGHT, column_range, traces, nrow, ncol, ntrace
70
+ )
71
+ print(f"Extraction heights: {xwd[TRACE_INDEX]}")
72
+ print(f"Column range: {cr[TRACE_INDEX]}")
73
+
74
+ # Select the trace
75
+ trace = traces[TRACE_INDEX]
76
+ xlow, xhigh = xwd[TRACE_INDEX]
77
+ ibeg, iend = cr[TRACE_INDEX]
78
+
79
+ # Calculate trace center positions
80
+ ix = np.arange(ncol)
81
+ ycen = np.polyval(trace, ix)
82
+ ycen_int = ycen.astype(int)
83
+
84
+ # Make swath bins
85
+ bins = make_bins(None, ibeg, iend, ycen) # swath_width=None for default
86
+ print(f"Number of swaths: {len(bins) - 1}")
87
+ print(f"Swath boundaries: {bins}")
88
+
89
+ # Select one swath
90
+ swath_start = bins[SWATH_INDEX]
91
+ swath_end = bins[SWATH_INDEX + 1]
92
+ print(f"Processing swath {SWATH_INDEX}: columns {swath_start} to {swath_end}")
93
+
94
+ # Extract swath region
95
+ index = make_index(ycen_int - xlow, ycen_int + xhigh + 1, swath_start, swath_end)
96
+ swath_img = img[index]
97
+ swath_ycen = ycen[swath_start:swath_end] - ycen_int[swath_start:swath_end]
98
+
99
+ print(f"Swath image shape: {swath_img.shape}")
100
+ print(f"Swath ycen range: [{swath_ycen.min():.3f}, {swath_ycen.max():.3f}]")
101
+
102
+ # Save input for debugging
103
+ np.savetxt("debug_swath_img.txt", swath_img)
104
+ np.savetxt("debug_swath_ycen.txt", swath_ycen)
105
+ print("Saved debug_swath_img.txt and debug_swath_ycen.txt")
106
+
107
+ # Run extraction
108
+ print("\nRunning slitfunc_curved...")
109
+ result = slitfunc_curved(
110
+ swath_img,
111
+ swath_ycen,
112
+ 0, # p0 (tilt)
113
+ 0, # p1 (curvature)
114
+ LAMBDA_SF,
115
+ LAMBDA_SP,
116
+ osample=OSAMPLE,
117
+ maxiter=MAXITER,
118
+ gain=GAIN,
119
+ yrange=(int(xlow), int(xhigh)),
120
+ )
121
+
122
+ spec, slitf, model, unc, mask = result
123
+
124
+ # Save outputs
125
+ np.savetxt("debug_spectrum.txt", spec)
126
+ np.savetxt("debug_slitfunc.txt", slitf)
127
+ np.savetxt("debug_model.txt", model)
128
+ np.savetxt("debug_unc.txt", unc)
129
+ np.savetxt("debug_mask.txt", mask.astype(int))
130
+
131
+ print("\nResults saved:")
132
+ print(f" Spectrum: {len(spec)} points, sum={spec.sum():.1f}")
133
+ print(f" Slit function: {len(slitf)} points")
134
+ print(f" Model: {model.shape}")
135
+ print(f" Masked pixels: {mask.sum()} / {mask.size}")
136
+
137
+
138
+ if __name__ == "__main__":
139
+ main()
@@ -18,7 +18,7 @@ def func_wavecal(deg, thar, instrument, channel, **kwargs):
18
18
  kwargs["degree"] = deg
19
19
 
20
20
  module = WavelengthCalibrationModule(**kwargs)
21
- wave, coef = module.execute(thar, linelist)
21
+ _wave, _coef, _linelist = module.execute(thar, linelist)
22
22
 
23
23
  return module.aic
24
24
 
@@ -1,8 +1,15 @@
1
+ """
2
+ Debug script for extracting a single swath from a specific trace.
3
+
4
+ Demonstrates manual swath extraction using the trace_model interface.
5
+ """
6
+
1
7
  import matplotlib.pyplot as plt
2
8
  import numpy as np
3
9
  from astropy.io import fits
4
10
 
5
11
  from pyreduce import cwrappers, extract
12
+ from pyreduce.trace_model import load_traces
6
13
 
7
14
  input_dir = "../DATA/datasets/UVES/reduced/2010-04-01/middle/"
8
15
  raw_dir = "../DATA/datasets/UVES/raw/"
@@ -11,30 +18,17 @@ hdu = fits.open(input_dir + "uves_middle.flat.fits")
11
18
  img = hdu[0].data
12
19
  nrow, ncol = img.shape
13
20
 
14
- import warnings
15
-
16
- data = np.load(input_dir + "uves_middle.traces.npz", allow_pickle=True)
17
- if "traces" in data:
18
- traces = data["traces"]
19
- elif "orders" in data:
20
- warnings.warn(
21
- "Trace file uses old key 'orders'. Re-run the trace step to update.",
22
- DeprecationWarning,
23
- stacklevel=1,
24
- )
25
- traces = data["orders"]
26
- else:
27
- raise KeyError("Trace file missing 'traces' key")
28
- column_range = data["column_range"]
21
+ # Load traces from FITS file
22
+ traces, _ = load_traces(input_dir + "uves_middle.traces.fits")
23
+ print(f"Loaded {len(traces)} traces")
29
24
 
30
25
  i = 5
31
26
  ix = np.arange(ncol)
32
27
 
33
28
  ylow, yhigh = 50, 50
34
29
  ibeg, iend = 1500, 2000
35
- ycen = np.polyval(traces[i], ix)
30
+ ycen = np.polyval(traces[i].pos, ix)
36
31
  ycen_int = ycen.astype(int)
37
- # yrange = extract.get_y_scale(ycen, [400, 600], [5, 5], nrow)
38
32
 
39
33
  index = extract.make_index(ycen_int - ylow, ycen_int + yhigh, ibeg, iend)
40
34
  swath_img = img[index]
@@ -44,8 +38,6 @@ osample = 10
44
38
  np.savetxt("image.txt", swath_img)
45
39
  np.savetxt("ycen.txt", swath_ycen)
46
40
 
47
- # return sp, sl, model, unc, mask
48
- # data1 = cwrappers.slitfunc(swath_img, swath_ycen, osample=osample)
49
41
  data2 = cwrappers.slitfunc_curved(
50
42
  swath_img, swath_ycen, 0, 0, 0.0, 0.5, osample=osample, yrange=(ylow, yhigh)
51
43
  )
@@ -84,17 +76,6 @@ for i in range(ncol):
84
76
  slitf,
85
77
  )
86
78
 
87
- # plt.subplot(111)
88
- # plt.plot(swath_img[:, i] / np.mean(swath_img[:, i]), label="Image")
89
- # plt.plot(sf[:, i] / np.mean(sf[:, i]), label="Interpolated")
90
- # plt.plot(np.linspace(-1, nrow, nsf), slitf / np.mean(slitf), label="Slitfunction")
91
- # plt.legend()
92
- # plt.show()
93
-
94
- # plt.subplot(122)
95
- # plt.imshow(swath_img / sf, aspect="auto", origin="lower")
96
- # plt.show()
97
-
98
79
  y = swath_img / sf
99
80
  y = y.ravel() * np.mean(spec) / np.mean(y)
100
81
  plt.plot(x, y, ".", label="Observation")
@@ -72,21 +72,17 @@ pipe = Pipeline(
72
72
  # - Groups 630 traces into 90 bundles of 7
73
73
  # - Selects center fiber from each bundle
74
74
  # - Uses grouped traces for curvature and science steps
75
- pipe.trace([flat_file])
75
+ # pipe.trace([flat_file])
76
76
  # pipe.curvature([thar_file])
77
77
  # pipe.flat([flat_file])
78
78
  # pipe.normalize_flat()
79
- # pipe.extract([flat_file])
79
+ pipe.extract([thar_file])
80
80
 
81
81
  print("\n=== Running Pipeline ===")
82
82
  results = pipe.run()
83
83
 
84
84
  print("\n=== Results ===")
85
- traces, column_range = results["trace"]
86
- print(f"Raw traces: {len(traces)}")
87
-
88
- if "trace_groups" in results and results["trace_groups"]:
89
- group_traces, group_cr = results["trace_groups"]
90
- print(
91
- f"Fiber groups: {list(group_traces.keys())[:5]}... ({len(group_traces)} total)"
92
- )
85
+ traces = results["trace"] # list[Trace]
86
+ print(f"Traces: {len(traces)}")
87
+ for t in traces[:3]:
88
+ print(f" m={t.m}, fiber={t.fiber}, columns={t.column_range}")
@@ -13,6 +13,7 @@ import numpy as np
13
13
  from pyreduce import util
14
14
  from pyreduce.configuration import load_config
15
15
  from pyreduce.extract import extract
16
+ from pyreduce.trace_model import load_traces
16
17
 
17
18
  # Parameters
18
19
  instrument_name = "MOSAIC"
@@ -64,18 +65,36 @@ prefix = "mosaic_nir"
64
65
  # Load saved results from previous pipeline run
65
66
  print("\n=== Loading saved results ===")
66
67
 
67
- trace_file = join(output_dir, f"{prefix}.traces.npz")
68
+ trace_file = join(output_dir, f"{prefix}.traces.fits")
68
69
  norm_file = join(output_dir, f"{prefix}.flat_norm.npz")
69
- curve_file = join(output_dir, f"{prefix}.curve.npz")
70
70
 
71
- # Load trace (use grouped traces that norm_flat used)
72
- trace_data = np.load(trace_file)
73
- group_names = trace_data["group_names"]
74
- all_traces = [trace_data[f"group_{name}_traces"] for name in group_names]
75
- all_cr = [trace_data[f"group_{name}_cr"] for name in group_names]
76
- traces = np.vstack(all_traces)
77
- column_range = np.vstack(all_cr)
78
- print(f"Loaded {len(traces)} grouped traces from {trace_file}")
71
+ # Load traces from FITS file (includes curvature if available)
72
+ trace_list, _ = load_traces(trace_file)
73
+ print(f"Loaded {len(trace_list)} traces from {trace_file}")
74
+
75
+ # Convert to arrays for extract()
76
+ traces = np.array([t.pos for t in trace_list])
77
+ column_range = np.array([t.column_range for t in trace_list])
78
+
79
+ # Get curvature from traces (if available)
80
+ # The slit curvature is now stored in each Trace object
81
+ has_curvature = trace_list[0].slit is not None
82
+ if has_curvature:
83
+ # For the extract() function, we need p1, p2 as arrays (ntrace, 2)
84
+ # Extract the linear and quadratic curvature terms
85
+ p1 = np.zeros(len(trace_list))
86
+ p2 = np.zeros(len(trace_list))
87
+ for i, t in enumerate(trace_list):
88
+ if t.slit is not None and t.slit.shape[0] >= 2:
89
+ # slit[1, :] contains coefficients for the y^1 term (linear tilt)
90
+ # Take the constant term (value at x=0) as p1
91
+ p1[i] = t.slit[1, -1] if len(t.slit[1]) > 0 else 0
92
+ if t.slit.shape[0] >= 3:
93
+ p2[i] = t.slit[2, -1] if len(t.slit[2]) > 0 else 0
94
+ print(f"Loaded curvature from traces (p1 range: [{p1.min():.4f}, {p1.max():.4f}])")
95
+ else:
96
+ p1, p2 = None, None
97
+ print("No curvature data in traces")
79
98
 
80
99
  # Load norm_flat (with slitfunc)
81
100
  norm_data = np.load(norm_file, allow_pickle=True)
@@ -84,37 +103,24 @@ slitfunc_meta = norm_data["slitfunc_meta"].item()
84
103
  print(f"Loaded {len(slitfunc_list)} slit functions from {norm_file}")
85
104
  print(f"Slitfunc meta: {slitfunc_meta}")
86
105
 
87
- # Load curvature if available
88
- if os.path.exists(curve_file):
89
- curve_data = np.load(curve_file)
90
- p1, p2 = curve_data["p1"], curve_data["p2"]
91
- print(f"Loaded curvature from {curve_file}")
92
- else:
93
- p1, p2 = None, None
94
- print("No curvature file found, using p1=p2=None")
95
-
96
106
  # Now extract ThAr using preset slit function
97
107
  print("\n=== Extracting ThAr with preset slit function ===")
98
108
 
99
109
  # Load ThAr image
100
110
  thar_img, thar_head = instrument.load_fits(thar_file, channel)
101
111
 
102
- # Use the grouped traces (same as norm_flat)
103
- selected_traces = traces
104
- selected_cr = column_range
105
-
106
112
  # Extraction parameters from slitfunc_meta
107
113
  osample = slitfunc_meta["osample"]
108
114
  extraction_height = slitfunc_meta["extraction_height"]
109
115
 
110
- print(f"Extracting {len(selected_traces)} traces with preset slitfunc")
116
+ print(f"Extracting {len(traces)} traces with preset slitfunc")
111
117
  print(f" osample={osample}, extraction_height={extraction_height}")
112
118
 
113
119
  # Extract using the extract() function with preset_slitfunc
114
120
  spectrum, uncertainties, slitfunc_out, column_range_out = extract(
115
121
  thar_img,
116
- selected_traces,
117
- column_range=selected_cr,
122
+ traces,
123
+ column_range=column_range,
118
124
  extraction_type="optimal",
119
125
  extraction_height=extraction_height,
120
126
  p1=p1,
@@ -81,11 +81,7 @@ print("\n=== Running Pipeline ===")
81
81
  results = pipe.run()
82
82
 
83
83
  print("\n=== Results ===")
84
- traces, column_range = results["trace"]
85
- print(f"Raw traces: {len(traces)}")
86
-
87
- if "trace_groups" in results and results["trace_groups"]:
88
- group_traces, group_cr = results["trace_groups"]
89
- print(
90
- f"Fiber groups: {list(group_traces.keys())[:5]}... ({len(group_traces)} total)"
91
- )
84
+ traces = results["trace"] # list[Trace]
85
+ print(f"Traces: {len(traces)}")
86
+ for t in traces[:3]:
87
+ print(f" m={t.m}, fiber={t.fiber}, columns={t.column_range}")
@@ -148,11 +148,11 @@ wavecal_master = wavecal_master_step.run(
148
148
  wavecal_init_step = WavelengthCalibrationInitialize(
149
149
  *step_args, **step_config("wavecal_init")
150
150
  )
151
- wavecal_init = wavecal_init_step.load(config, wavecal_master)
151
+ wavecal_init = wavecal_init_step.run(wavecal_master)
152
152
 
153
153
  wavecal_step = WavelengthCalibrationFinalize(*step_args, **step_config("wavecal"))
154
154
  wavecal = wavecal_step.run(wavecal_master, wavecal_init)
155
- wave, coef, linelist = wavecal
155
+ # wavecal returns {group: linelist}; wavelengths are stored in traces
156
156
 
157
157
  # Step 8: Science extraction
158
158
  print("\n=== SCIENCE ===")
@@ -166,14 +166,14 @@ science = science_step.run(
166
166
  mask=mask,
167
167
  )
168
168
 
169
- # Step 9: Continuum normalization
169
+ # Step 9: Continuum normalization (gets wavelengths from traces)
170
170
  print("\n=== CONTINUUM ===")
171
171
  continuum_step = ContinuumNormalization(*step_args, **step_config("continuum"))
172
- continuum = continuum_step.run(science, wave, norm_flat)
172
+ continuum = continuum_step.run(science, norm_flat, traces)
173
173
 
174
- # Step 10: Finalize
174
+ # Step 10: Finalize (gets wavelengths from traces)
175
175
  print("\n=== FINALIZE ===")
176
176
  finalize_step = Finalize(*step_args, **step_config("finalize"))
177
- finalize_step.run(continuum, wave, config)
177
+ finalize_step.run(continuum, traces, config)
178
178
 
179
179
  print("\nDone!")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pyreduce-astro"
3
- version = "0.8a1"
3
+ version = "0.8a2"
4
4
  requires-python = ">=3.13"
5
5
  description = "A data reduction package for echelle spectrographs"
6
6
  readme = "README.md"
@@ -247,7 +247,7 @@ def run(config_file: str, steps: str, skip_existing: bool, plot: int):
247
247
  files:
248
248
  bias: /data/raw/bias/*.fits
249
249
  flat: /data/raw/flat/*.fits
250
- wavecal: /data/raw/thar/*.fits
250
+ wavecal: /data/raw/wavecal/*.fits
251
251
  science: /data/raw/science/*.fits
252
252
  steps: [bias, flat, trace, wavecal, extract]
253
253
  """