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.
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/CHANGELOG.md +24 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/CLAUDE.md +17 -3
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/PKG-INFO +1 -1
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/WhatsNew.md +16 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/andes_riz.py +6 -9
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/andes_yjh_example.py +20 -27
- pyreduce_astro-0.8a2/examples/debug_single_swath.py +139 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/harps_gridsearch.py +1 -1
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/just_one_swath.py +11 -30
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/mosaic_nir.py +6 -10
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/mosaic_preset-slitfunc.py +32 -26
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/mosaic_vis.py +4 -8
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/uves_callfunc.py +6 -6
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyproject.toml +1 -1
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/cli.py +1 -1
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/combine_frames.py +15 -17
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/continuum_normalization.py +24 -24
- pyreduce_astro-0.8a2/pyreduce/echelle.py +18 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/estimate_background_scatter.py +42 -37
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/extract.py +203 -102
- pyreduce_astro-0.8a2/pyreduce/instruments/ANDES_RIZ/__init__.py +27 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/config.yaml +3 -3
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/settings.json +11 -2
- pyreduce_astro-0.8a2/pyreduce/instruments/ANDES_RIZ/wavecal_r0_HDF.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/config.yaml +2 -3
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings.json +1 -1
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings_nir.json +1 -4
- pyreduce_astro-0.8a2/pyreduce/instruments/defaults/atlas/lfc.fits +0 -0
- pyreduce_astro-0.8a2/pyreduce/instruments/defaults/atlas/lfc_list.txt +321 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/models.py +14 -6
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/pipeline.py +50 -112
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/rectify.py +60 -31
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/reduce.py +832 -768
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/slit_curve.py +70 -33
- pyreduce_astro-0.8a2/pyreduce/spectra.py +668 -0
- pyreduce_astro-0.8a2/pyreduce/tools/combine.py +184 -0
- pyreduce_astro-0.8a2/pyreduce/trace.py +1438 -0
- pyreduce_astro-0.8a2/pyreduce/trace_model.py +614 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/util.py +0 -100
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/wavelength_calibration.py +98 -71
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/create_wavelength_guess.py +10 -5
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/plot_swath_debug.py +0 -3
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/uv.lock +1 -1
- pyreduce_astro-0.8a1/examples/debug_single_swath.py +0 -229
- pyreduce_astro-0.8a1/pyreduce/curvature_model.py +0 -194
- pyreduce_astro-0.8a1/pyreduce/echelle.py +0 -413
- pyreduce_astro-0.8a1/pyreduce/instruments/ANDES_RIZ/__init__.py +0 -15
- pyreduce_astro-0.8a1/pyreduce/instruments/ANDES_RIZ/wavecal_r0_HDF.npz +0 -0
- pyreduce_astro-0.8a1/pyreduce/tools/combine.py +0 -118
- pyreduce_astro-0.8a1/pyreduce/trace.py +0 -1904
- pyreduce_astro-0.8a1/trace_spectrum_redesign.md +0 -452
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/.gitattributes +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/.gitignore +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/.pre-commit-config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/.python-version +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/.readthedocs.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/AGENTS.md +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/LICENSE +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/README.md +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/crires_plus_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/custom_instrument_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/harpn_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/harps_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/jwst_miri_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/jwst_niriss_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/lick_apf_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/mcdonald_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/metis_ifu_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/metis_lss_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/micado_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/neid_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/nirspec_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/toes_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/uves_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/examples/xshooter_example.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/hatch_build.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/__main__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/build_extract.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/slit_func_2d_xi_zeta_bd.c +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/slit_func_2d_xi_zeta_bd.h +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/slit_func_bd.c +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clib/slit_func_bd.h +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/clipnflip.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/configuration.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/cwrappers.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/datasets.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/order_centers_r0.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/order_centers_r1.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/order_centers_r2.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/wavecal_r1_HDF.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_RIZ/wavecal_r2_HDF.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/order_centers_h.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/order_centers_j.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/order_centers_y.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/ANDES_YJH/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/mask_det1.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/mask_det2.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/mask_det3.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/wavecal_J1228_det1.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/wavecal_J1228_det2.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/CRIRES_PLUS/wavecal_J1228_det3.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPN/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPN/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPN/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPN/wavecal_harpn_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPN/wavecal_harpn_fibB_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/mask_blue.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/mask_red.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/wavecal_blue_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/wavecal_blue_pol_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/wavecal_red_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/HARPS/wavecal_red_pol_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_MIRI/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_MIRI/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_MIRI/mask_lrs_slitless.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_MIRI/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_NIRISS/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_NIRISS/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_NIRISS/mask_gr700xd.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/JWST_NIRISS/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/LICK_APF/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/LICK_APF/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/LICK_APF/mask_.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/LICK_APF/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MCDONALD/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MCDONALD/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MCDONALD/mask.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MCDONALD/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MCDONALD/wavecal.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_IFU/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_IFU/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_IFU/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_LSS/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_LSS/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_LSS/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_LSS/wavecal_l_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/METIS_LSS/wavecal_m_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MICADO/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MICADO/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MICADO/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MICADO/wavecal_HK_3arcsec_chip5.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/bundle_centers_nir.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/bundle_centers_vis1.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/bundle_centers_vis2.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/bundle_centers_vis3.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/bundle_centers_vis4.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/mask_nir.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/mosaic_fiber_positions.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings_VIS1.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings_VIS2.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings_VIS3.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/MOSAIC/settings_VIS4.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NEID/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NEID/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NEID/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NIRSPEC/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NIRSPEC/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NIRSPEC/mask_nirspec.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NIRSPEC/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NIRSPEC/wavecal_K2.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NTE/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NTE/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/NTE/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_blue.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_blue_binned_2_2.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_middle.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_middle_2x2_split.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_middle_binned_2_2.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_red.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_red_2x2.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_red_2x2_split.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/mask_red_binned_2_2.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_blue_360nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_blue_390nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_blue_437nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_2x2_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_565nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_580nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_600nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_665nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_middle_860nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_red_580nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_red_600nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_red_665nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_red_760nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/UVES/wavecal_red_860nm_2D.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/XSHOOTER/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/XSHOOTER/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/XSHOOTER/mask_nir.fits.gz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/XSHOOTER/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/XSHOOTER/wavecal_nir.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/common.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/atlas/thar.fits +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/atlas/thar_list.txt +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/atlas/une.fits +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/config.yaml +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/schema.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/defaults/settings.json +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/filters.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/instruments/instrument_info.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/pyreduce/tools/__init__.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/andes_hdf_to_wavecal.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/andes_plot_spectra.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/argon.line +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/bpm_creator.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/convert_wavecal.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/download_files.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/ipy_startup.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/make_notebook.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/mosaic_vis_bundle_plot.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/neon.lin +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/neon.line +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/plot_1d_vs_2d_extraction.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/plot_extractmodel_comp.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/pymultispec.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/thar.npz +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/wavecal_creator.py +0 -0
- {pyreduce_astro-0.8a1 → pyreduce_astro-0.8a2}/tools/wavecal_creator_from_existing.py +0 -0
- {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
|
-
├──
|
|
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/
|
|
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,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
|
|
164
|
+
traces = results["trace"] # list[Trace]
|
|
163
165
|
print(f"Traces found: {len(traces)}")
|
|
164
|
-
|
|
165
|
-
|
|
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)
|
|
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
|
-
|
|
62
|
-
print(f" Loaded {len(
|
|
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
|
|
69
|
-
print(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
print(f" Found {len(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
|
86
|
-
print(f"
|
|
87
|
-
|
|
88
|
-
|
|
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.
|
|
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
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
traces = np.
|
|
77
|
-
column_range = np.
|
|
78
|
-
|
|
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(
|
|
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
|
-
|
|
117
|
-
column_range=
|
|
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
|
|
85
|
-
print(f"
|
|
86
|
-
|
|
87
|
-
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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,
|
|
177
|
+
finalize_step.run(continuum, traces, config)
|
|
178
178
|
|
|
179
179
|
print("\nDone!")
|
|
@@ -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/
|
|
250
|
+
wavecal: /data/raw/wavecal/*.fits
|
|
251
251
|
science: /data/raw/science/*.fits
|
|
252
252
|
steps: [bias, flat, trace, wavecal, extract]
|
|
253
253
|
"""
|