pyreduce-astro 0.7a4__cp314-cp314-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyreduce/__init__.py +67 -0
- pyreduce/__main__.py +322 -0
- pyreduce/cli.py +342 -0
- pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp313-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp313-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp314-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_2d.cp314-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_2d.obj +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp313-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp313-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp314-win_amd64.exp +0 -0
- pyreduce/clib/Release/_slitfunc_bd.cp314-win_amd64.lib +0 -0
- pyreduce/clib/Release/_slitfunc_bd.obj +0 -0
- pyreduce/clib/__init__.py +0 -0
- pyreduce/clib/_slitfunc_2d.cp311-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_2d.cp312-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_2d.cp313-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_2d.cp314-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp311-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp312-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp313-win_amd64.pyd +0 -0
- pyreduce/clib/_slitfunc_bd.cp314-win_amd64.pyd +0 -0
- pyreduce/clib/build_extract.py +75 -0
- pyreduce/clib/slit_func_2d_xi_zeta_bd.c +1313 -0
- pyreduce/clib/slit_func_2d_xi_zeta_bd.h +55 -0
- pyreduce/clib/slit_func_bd.c +362 -0
- pyreduce/clib/slit_func_bd.h +17 -0
- pyreduce/clipnflip.py +147 -0
- pyreduce/combine_frames.py +861 -0
- pyreduce/configuration.py +191 -0
- pyreduce/continuum_normalization.py +329 -0
- pyreduce/cwrappers.py +404 -0
- pyreduce/datasets.py +238 -0
- pyreduce/echelle.py +413 -0
- pyreduce/estimate_background_scatter.py +130 -0
- pyreduce/extract.py +1362 -0
- pyreduce/extraction_width.py +77 -0
- pyreduce/instruments/__init__.py +0 -0
- pyreduce/instruments/aj.py +9 -0
- pyreduce/instruments/aj.yaml +51 -0
- pyreduce/instruments/andes.py +102 -0
- pyreduce/instruments/andes.yaml +72 -0
- pyreduce/instruments/common.py +711 -0
- pyreduce/instruments/common.yaml +57 -0
- pyreduce/instruments/crires_plus.py +103 -0
- pyreduce/instruments/crires_plus.yaml +101 -0
- pyreduce/instruments/filters.py +195 -0
- pyreduce/instruments/harpn.py +203 -0
- pyreduce/instruments/harpn.yaml +140 -0
- pyreduce/instruments/harps.py +312 -0
- pyreduce/instruments/harps.yaml +144 -0
- pyreduce/instruments/instrument_info.py +140 -0
- pyreduce/instruments/jwst_miri.py +29 -0
- pyreduce/instruments/jwst_miri.yaml +53 -0
- pyreduce/instruments/jwst_niriss.py +98 -0
- pyreduce/instruments/jwst_niriss.yaml +60 -0
- pyreduce/instruments/lick_apf.py +35 -0
- pyreduce/instruments/lick_apf.yaml +60 -0
- pyreduce/instruments/mcdonald.py +123 -0
- pyreduce/instruments/mcdonald.yaml +56 -0
- pyreduce/instruments/metis_ifu.py +45 -0
- pyreduce/instruments/metis_ifu.yaml +62 -0
- pyreduce/instruments/metis_lss.py +45 -0
- pyreduce/instruments/metis_lss.yaml +62 -0
- pyreduce/instruments/micado.py +45 -0
- pyreduce/instruments/micado.yaml +62 -0
- pyreduce/instruments/models.py +257 -0
- pyreduce/instruments/neid.py +156 -0
- pyreduce/instruments/neid.yaml +61 -0
- pyreduce/instruments/nirspec.py +215 -0
- pyreduce/instruments/nirspec.yaml +63 -0
- pyreduce/instruments/nte.py +42 -0
- pyreduce/instruments/nte.yaml +55 -0
- pyreduce/instruments/uves.py +46 -0
- pyreduce/instruments/uves.yaml +65 -0
- pyreduce/instruments/xshooter.py +39 -0
- pyreduce/instruments/xshooter.yaml +63 -0
- pyreduce/make_shear.py +607 -0
- pyreduce/masks/mask_crires_plus_det1.fits.gz +0 -0
- pyreduce/masks/mask_crires_plus_det2.fits.gz +0 -0
- pyreduce/masks/mask_crires_plus_det3.fits.gz +0 -0
- pyreduce/masks/mask_ctio_chiron.fits.gz +0 -0
- pyreduce/masks/mask_elodie.fits.gz +0 -0
- pyreduce/masks/mask_feros3.fits.gz +0 -0
- pyreduce/masks/mask_flames_giraffe.fits.gz +0 -0
- pyreduce/masks/mask_harps_blue.fits.gz +0 -0
- pyreduce/masks/mask_harps_red.fits.gz +0 -0
- pyreduce/masks/mask_hds_blue.fits.gz +0 -0
- pyreduce/masks/mask_hds_red.fits.gz +0 -0
- pyreduce/masks/mask_het_hrs_2x5.fits.gz +0 -0
- pyreduce/masks/mask_jwst_miri_lrs_slitless.fits.gz +0 -0
- pyreduce/masks/mask_jwst_niriss_gr700xd.fits.gz +0 -0
- pyreduce/masks/mask_lick_apf_.fits.gz +0 -0
- pyreduce/masks/mask_mcdonald.fits.gz +0 -0
- pyreduce/masks/mask_nes.fits.gz +0 -0
- pyreduce/masks/mask_nirspec_nirspec.fits.gz +0 -0
- pyreduce/masks/mask_sarg.fits.gz +0 -0
- pyreduce/masks/mask_sarg_2x2a.fits.gz +0 -0
- pyreduce/masks/mask_sarg_2x2b.fits.gz +0 -0
- pyreduce/masks/mask_subaru_hds_red.fits.gz +0 -0
- pyreduce/masks/mask_uves_blue.fits.gz +0 -0
- pyreduce/masks/mask_uves_blue_binned_2_2.fits.gz +0 -0
- pyreduce/masks/mask_uves_middle.fits.gz +0 -0
- pyreduce/masks/mask_uves_middle_2x2_split.fits.gz +0 -0
- pyreduce/masks/mask_uves_middle_binned_2_2.fits.gz +0 -0
- pyreduce/masks/mask_uves_red.fits.gz +0 -0
- pyreduce/masks/mask_uves_red_2x2.fits.gz +0 -0
- pyreduce/masks/mask_uves_red_2x2_split.fits.gz +0 -0
- pyreduce/masks/mask_uves_red_binned_2_2.fits.gz +0 -0
- pyreduce/masks/mask_xshooter_nir.fits.gz +0 -0
- pyreduce/pipeline.py +619 -0
- pyreduce/rectify.py +138 -0
- pyreduce/reduce.py +2065 -0
- pyreduce/settings/settings_AJ.json +19 -0
- pyreduce/settings/settings_ANDES.json +89 -0
- pyreduce/settings/settings_CRIRES_PLUS.json +89 -0
- pyreduce/settings/settings_HARPN.json +73 -0
- pyreduce/settings/settings_HARPS.json +69 -0
- pyreduce/settings/settings_JWST_MIRI.json +55 -0
- pyreduce/settings/settings_JWST_NIRISS.json +55 -0
- pyreduce/settings/settings_LICK_APF.json +62 -0
- pyreduce/settings/settings_MCDONALD.json +58 -0
- pyreduce/settings/settings_METIS_IFU.json +77 -0
- pyreduce/settings/settings_METIS_LSS.json +77 -0
- pyreduce/settings/settings_MICADO.json +78 -0
- pyreduce/settings/settings_NEID.json +73 -0
- pyreduce/settings/settings_NIRSPEC.json +58 -0
- pyreduce/settings/settings_NTE.json +60 -0
- pyreduce/settings/settings_UVES.json +54 -0
- pyreduce/settings/settings_XSHOOTER.json +78 -0
- pyreduce/settings/settings_pyreduce.json +184 -0
- pyreduce/settings/settings_schema.json +850 -0
- pyreduce/tools/__init__.py +0 -0
- pyreduce/tools/combine.py +117 -0
- pyreduce/trace.py +979 -0
- pyreduce/util.py +1366 -0
- pyreduce/wavecal/MICADO_HK_3arcsec_chip5.npz +0 -0
- pyreduce/wavecal/atlas/thar.fits +4946 -13
- pyreduce/wavecal/atlas/thar_list.txt +4172 -0
- pyreduce/wavecal/atlas/une.fits +0 -0
- pyreduce/wavecal/convert.py +38 -0
- pyreduce/wavecal/crires_plus_J1228_Open_det1.npz +0 -0
- pyreduce/wavecal/crires_plus_J1228_Open_det2.npz +0 -0
- pyreduce/wavecal/crires_plus_J1228_Open_det3.npz +0 -0
- pyreduce/wavecal/harpn_harpn_2D.npz +0 -0
- pyreduce/wavecal/harps_blue_2D.npz +0 -0
- pyreduce/wavecal/harps_blue_pol_2D.npz +0 -0
- pyreduce/wavecal/harps_red_2D.npz +0 -0
- pyreduce/wavecal/harps_red_pol_2D.npz +0 -0
- pyreduce/wavecal/mcdonald.npz +0 -0
- pyreduce/wavecal/metis_lss_l_2D.npz +0 -0
- pyreduce/wavecal/metis_lss_m_2D.npz +0 -0
- pyreduce/wavecal/nirspec_K2.npz +0 -0
- pyreduce/wavecal/uves_blue_360nm_2D.npz +0 -0
- pyreduce/wavecal/uves_blue_390nm_2D.npz +0 -0
- pyreduce/wavecal/uves_blue_437nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_2x2_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_565nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_580nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_600nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_665nm_2D.npz +0 -0
- pyreduce/wavecal/uves_middle_860nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_580nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_600nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_665nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_760nm_2D.npz +0 -0
- pyreduce/wavecal/uves_red_860nm_2D.npz +0 -0
- pyreduce/wavecal/xshooter_nir.npz +0 -0
- pyreduce/wavelength_calibration.py +1871 -0
- pyreduce_astro-0.7a4.dist-info/METADATA +106 -0
- pyreduce_astro-0.7a4.dist-info/RECORD +182 -0
- pyreduce_astro-0.7a4.dist-info/WHEEL +4 -0
- pyreduce_astro-0.7a4.dist-info/entry_points.txt +2 -0
- pyreduce_astro-0.7a4.dist-info/licenses/LICENSE +674 -0
pyreduce/cli.py
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"""Click-based CLI for PyReduce.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
uv run reduce --help
|
|
5
|
+
uv run reduce bias INSTRUMENT --files bias/*.fits --output output/
|
|
6
|
+
uv run reduce run reduction.yaml
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from glob import glob
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
|
|
16
|
+
from . import datasets
|
|
17
|
+
from .instruments.instrument_info import load_instrument
|
|
18
|
+
from .pipeline import Pipeline
|
|
19
|
+
|
|
20
|
+
# Map CLI names to dataset functions
|
|
21
|
+
AVAILABLE_DATASETS = {
|
|
22
|
+
"UVES": datasets.UVES,
|
|
23
|
+
"HARPS": datasets.HARPS,
|
|
24
|
+
"XSHOOTER": datasets.XSHOOTER,
|
|
25
|
+
"NIRSPEC": datasets.KECK_NIRSPEC,
|
|
26
|
+
"JWST_NIRISS": datasets.JWST_NIRISS,
|
|
27
|
+
"JWST_MIRI": datasets.JWST_MIRI,
|
|
28
|
+
"LICK_APF": datasets.LICK_APF,
|
|
29
|
+
"MCDONALD": datasets.MCDONALD,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@click.group()
|
|
34
|
+
@click.version_option(package_name="pyreduce-astro")
|
|
35
|
+
def cli():
|
|
36
|
+
"""PyReduce echelle spectrograph reduction pipeline."""
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@cli.command()
|
|
41
|
+
@click.argument("instrument")
|
|
42
|
+
@click.option(
|
|
43
|
+
"--output",
|
|
44
|
+
"-o",
|
|
45
|
+
default=None,
|
|
46
|
+
help="Output directory (default: $REDUCE_DATA or ~/REDUCE_DATA)",
|
|
47
|
+
)
|
|
48
|
+
def download(instrument: str, output: str | None):
|
|
49
|
+
"""Download example dataset for an instrument.
|
|
50
|
+
|
|
51
|
+
Available instruments: UVES, HARPS, XSHOOTER, NIRSPEC, JWST_NIRISS, JWST_MIRI,
|
|
52
|
+
LICK_APF, MCDONALD
|
|
53
|
+
|
|
54
|
+
Data is saved to $REDUCE_DATA if set, otherwise ~/REDUCE_DATA.
|
|
55
|
+
|
|
56
|
+
\b
|
|
57
|
+
Examples:
|
|
58
|
+
uv run reduce download UVES
|
|
59
|
+
uv run reduce download UVES -o ~/data/
|
|
60
|
+
"""
|
|
61
|
+
instrument_upper = instrument.upper()
|
|
62
|
+
if instrument_upper not in AVAILABLE_DATASETS:
|
|
63
|
+
available = ", ".join(sorted(AVAILABLE_DATASETS.keys()))
|
|
64
|
+
raise click.ClickException(
|
|
65
|
+
f"Unknown instrument '{instrument}'. Available: {available}"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
click.echo(f"Downloading {instrument_upper} example dataset...")
|
|
69
|
+
data_dir = AVAILABLE_DATASETS[instrument_upper](output)
|
|
70
|
+
click.echo(f"Dataset saved to: {data_dir}")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@cli.command("list-datasets")
|
|
74
|
+
def list_datasets():
|
|
75
|
+
"""List available example datasets."""
|
|
76
|
+
click.echo("Available example datasets:")
|
|
77
|
+
click.echo()
|
|
78
|
+
for name in sorted(AVAILABLE_DATASETS.keys()):
|
|
79
|
+
click.echo(f" {name}")
|
|
80
|
+
click.echo()
|
|
81
|
+
click.echo("Download with: uv run reduce download <INSTRUMENT> -o <DIR>")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@cli.command()
|
|
85
|
+
@click.argument("instrument")
|
|
86
|
+
@click.option(
|
|
87
|
+
"--files",
|
|
88
|
+
"-f",
|
|
89
|
+
multiple=True,
|
|
90
|
+
help="Input FITS files (can use glob patterns)",
|
|
91
|
+
)
|
|
92
|
+
@click.option("--output", "-o", default=".", help="Output directory")
|
|
93
|
+
@click.option("--mode", "-m", default="", help="Instrument mode (e.g., RED, BLUE)")
|
|
94
|
+
@click.option(
|
|
95
|
+
"--plot", "-p", default=0, type=int, help="Plot level (0=off, 1=basic, 2=detailed)"
|
|
96
|
+
)
|
|
97
|
+
def bias(instrument: str, files: tuple[str, ...], output: str, mode: str, plot: int):
|
|
98
|
+
"""Create master bias from bias frames."""
|
|
99
|
+
inst = load_instrument(instrument)
|
|
100
|
+
file_list = _expand_globs(files)
|
|
101
|
+
|
|
102
|
+
if not file_list:
|
|
103
|
+
raise click.ClickException("No input files specified. Use --files option.")
|
|
104
|
+
|
|
105
|
+
click.echo(f"Creating master bias from {len(file_list)} files...")
|
|
106
|
+
Pipeline(inst, output, mode=mode, plot=plot).bias(file_list).run()
|
|
107
|
+
click.echo(f"Master bias saved to {output}")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@cli.command()
|
|
111
|
+
@click.argument("instrument")
|
|
112
|
+
@click.option(
|
|
113
|
+
"--files",
|
|
114
|
+
"-f",
|
|
115
|
+
multiple=True,
|
|
116
|
+
help="Input FITS files (can use glob patterns)",
|
|
117
|
+
)
|
|
118
|
+
@click.option("--output", "-o", default=".", help="Output directory")
|
|
119
|
+
@click.option("--mode", "-m", default="", help="Instrument mode")
|
|
120
|
+
@click.option(
|
|
121
|
+
"--plot", "-p", default=0, type=int, help="Plot level (0=off, 1=basic, 2=detailed)"
|
|
122
|
+
)
|
|
123
|
+
def flat(instrument: str, files: tuple[str, ...], output: str, mode: str, plot: int):
|
|
124
|
+
"""Create master flat from flat frames."""
|
|
125
|
+
inst = load_instrument(instrument)
|
|
126
|
+
file_list = _expand_globs(files)
|
|
127
|
+
|
|
128
|
+
if not file_list:
|
|
129
|
+
raise click.ClickException("No input files specified. Use --files option.")
|
|
130
|
+
|
|
131
|
+
click.echo(f"Creating master flat from {len(file_list)} files...")
|
|
132
|
+
Pipeline(inst, output, mode=mode, plot=plot).flat(file_list).run()
|
|
133
|
+
click.echo(f"Master flat saved to {output}")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@cli.command()
|
|
137
|
+
@click.argument("instrument")
|
|
138
|
+
@click.option(
|
|
139
|
+
"--files",
|
|
140
|
+
"-f",
|
|
141
|
+
multiple=True,
|
|
142
|
+
help="Flat files for tracing (optional if flat already exists)",
|
|
143
|
+
)
|
|
144
|
+
@click.option("--output", "-o", default=".", help="Output directory")
|
|
145
|
+
@click.option("--mode", "-m", default="", help="Instrument mode")
|
|
146
|
+
@click.option(
|
|
147
|
+
"--plot", "-p", default=0, type=int, help="Plot level (0=off, 1=basic, 2=detailed)"
|
|
148
|
+
)
|
|
149
|
+
def trace(instrument: str, files: tuple[str, ...], output: str, mode: str, plot: int):
|
|
150
|
+
"""Trace echelle orders on flat field."""
|
|
151
|
+
inst = load_instrument(instrument)
|
|
152
|
+
file_list = _expand_globs(files) if files else None
|
|
153
|
+
|
|
154
|
+
click.echo("Tracing echelle orders...")
|
|
155
|
+
pipe = Pipeline(inst, output, mode=mode, plot=plot)
|
|
156
|
+
if file_list:
|
|
157
|
+
pipe = pipe.flat(file_list)
|
|
158
|
+
pipe.trace_orders(file_list).run()
|
|
159
|
+
click.echo(f"Order trace saved to {output}")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@cli.command()
|
|
163
|
+
@click.argument("instrument")
|
|
164
|
+
@click.option(
|
|
165
|
+
"--files",
|
|
166
|
+
"-f",
|
|
167
|
+
multiple=True,
|
|
168
|
+
required=True,
|
|
169
|
+
help="Wavelength calibration files (ThAr, etc.)",
|
|
170
|
+
)
|
|
171
|
+
@click.option("--output", "-o", default=".", help="Output directory")
|
|
172
|
+
@click.option("--mode", "-m", default="", help="Instrument mode")
|
|
173
|
+
@click.option(
|
|
174
|
+
"--plot", "-p", default=0, type=int, help="Plot level (0=off, 1=basic, 2=detailed)"
|
|
175
|
+
)
|
|
176
|
+
def wavecal(instrument: str, files: tuple[str, ...], output: str, mode: str, plot: int):
|
|
177
|
+
"""Perform wavelength calibration."""
|
|
178
|
+
inst = load_instrument(instrument)
|
|
179
|
+
file_list = _expand_globs(files)
|
|
180
|
+
|
|
181
|
+
if not file_list:
|
|
182
|
+
raise click.ClickException("No input files specified. Use --files option.")
|
|
183
|
+
|
|
184
|
+
click.echo(f"Running wavelength calibration with {len(file_list)} files...")
|
|
185
|
+
Pipeline(inst, output, mode=mode, plot=plot).wavelength_calibration(file_list).run()
|
|
186
|
+
click.echo(f"Wavelength calibration saved to {output}")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@cli.command()
|
|
190
|
+
@click.argument("instrument")
|
|
191
|
+
@click.option(
|
|
192
|
+
"--files",
|
|
193
|
+
"-f",
|
|
194
|
+
multiple=True,
|
|
195
|
+
required=True,
|
|
196
|
+
help="Science observation files",
|
|
197
|
+
)
|
|
198
|
+
@click.option("--output", "-o", default=".", help="Output directory")
|
|
199
|
+
@click.option("--mode", "-m", default="", help="Instrument mode")
|
|
200
|
+
@click.option("--target", "-t", default="", help="Target name")
|
|
201
|
+
@click.option(
|
|
202
|
+
"--plot", "-p", default=0, type=int, help="Plot level (0=off, 1=basic, 2=detailed)"
|
|
203
|
+
)
|
|
204
|
+
def extract(
|
|
205
|
+
instrument: str,
|
|
206
|
+
files: tuple[str, ...],
|
|
207
|
+
output: str,
|
|
208
|
+
mode: str,
|
|
209
|
+
target: str,
|
|
210
|
+
plot: int,
|
|
211
|
+
):
|
|
212
|
+
"""Extract spectra from science frames."""
|
|
213
|
+
inst = load_instrument(instrument)
|
|
214
|
+
file_list = _expand_globs(files)
|
|
215
|
+
|
|
216
|
+
if not file_list:
|
|
217
|
+
raise click.ClickException("No input files specified. Use --files option.")
|
|
218
|
+
|
|
219
|
+
click.echo(f"Extracting spectra from {len(file_list)} files...")
|
|
220
|
+
Pipeline(inst, output, mode=mode, target=target, plot=plot).extract(file_list).run()
|
|
221
|
+
click.echo(f"Extracted spectra saved to {output}")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@cli.command()
|
|
225
|
+
@click.argument("config_file", type=click.Path(exists=True))
|
|
226
|
+
@click.option(
|
|
227
|
+
"--steps",
|
|
228
|
+
"-s",
|
|
229
|
+
default="all",
|
|
230
|
+
help="Steps to run (comma-separated, or 'all')",
|
|
231
|
+
)
|
|
232
|
+
@click.option("--skip-existing", is_flag=True, help="Skip steps with existing output")
|
|
233
|
+
@click.option(
|
|
234
|
+
"--plot", "-p", default=0, type=int, help="Plot level (0=off, 1=basic, 2=detailed)"
|
|
235
|
+
)
|
|
236
|
+
def run(config_file: str, steps: str, skip_existing: bool, plot: int):
|
|
237
|
+
"""Run full reduction pipeline from config file.
|
|
238
|
+
|
|
239
|
+
CONFIG_FILE should be a YAML file with instrument, files, and output settings.
|
|
240
|
+
|
|
241
|
+
Example config.yaml:
|
|
242
|
+
|
|
243
|
+
\b
|
|
244
|
+
instrument: UVES
|
|
245
|
+
output: /data/reduced/
|
|
246
|
+
mode: RED
|
|
247
|
+
files:
|
|
248
|
+
bias: /data/raw/bias/*.fits
|
|
249
|
+
flat: /data/raw/flat/*.fits
|
|
250
|
+
wavecal: /data/raw/thar/*.fits
|
|
251
|
+
science: /data/raw/science/*.fits
|
|
252
|
+
steps: [bias, flat, trace, wavecal, extract]
|
|
253
|
+
"""
|
|
254
|
+
import yaml
|
|
255
|
+
|
|
256
|
+
with open(config_file) as f:
|
|
257
|
+
config = yaml.safe_load(f)
|
|
258
|
+
|
|
259
|
+
instrument_name = config.get("instrument")
|
|
260
|
+
if not instrument_name:
|
|
261
|
+
raise click.ClickException("Config file must specify 'instrument'")
|
|
262
|
+
|
|
263
|
+
inst = load_instrument(instrument_name)
|
|
264
|
+
output = config.get("output", ".")
|
|
265
|
+
mode = config.get("mode", "")
|
|
266
|
+
target = config.get("target", "")
|
|
267
|
+
files = config.get("files", {})
|
|
268
|
+
config_steps = config.get("steps", [])
|
|
269
|
+
|
|
270
|
+
# Parse steps
|
|
271
|
+
if steps != "all":
|
|
272
|
+
config_steps = [s.strip() for s in steps.split(",")]
|
|
273
|
+
elif not config_steps:
|
|
274
|
+
config_steps = ["bias", "flat", "trace", "wavecal", "extract"]
|
|
275
|
+
|
|
276
|
+
click.echo(f"Running pipeline for {instrument_name}")
|
|
277
|
+
click.echo(f"Steps: {', '.join(config_steps)}")
|
|
278
|
+
click.echo(f"Output: {output}")
|
|
279
|
+
|
|
280
|
+
pipe = Pipeline(inst, output, mode=mode, target=target, plot=plot)
|
|
281
|
+
|
|
282
|
+
# Add steps based on config
|
|
283
|
+
if "bias" in config_steps and files.get("bias"):
|
|
284
|
+
pipe = pipe.bias(_expand_globs(files["bias"]))
|
|
285
|
+
|
|
286
|
+
if "flat" in config_steps and files.get("flat"):
|
|
287
|
+
pipe = pipe.flat(_expand_globs(files["flat"]))
|
|
288
|
+
|
|
289
|
+
if "trace" in config_steps:
|
|
290
|
+
trace_files = files.get("orders") or files.get("flat")
|
|
291
|
+
pipe = pipe.trace_orders(_expand_globs(trace_files) if trace_files else None)
|
|
292
|
+
|
|
293
|
+
if "scatter" in config_steps:
|
|
294
|
+
pipe = pipe.scatter()
|
|
295
|
+
|
|
296
|
+
if "norm_flat" in config_steps:
|
|
297
|
+
pipe = pipe.normalize_flat()
|
|
298
|
+
|
|
299
|
+
if "wavecal" in config_steps and files.get("wavecal"):
|
|
300
|
+
pipe = pipe.wavelength_calibration(_expand_globs(files["wavecal"]))
|
|
301
|
+
|
|
302
|
+
if "curvature" in config_steps:
|
|
303
|
+
curv_files = files.get("curvature") or files.get("wavecal")
|
|
304
|
+
pipe = pipe.curvature(_expand_globs(curv_files) if curv_files else None)
|
|
305
|
+
|
|
306
|
+
if "extract" in config_steps and files.get("science"):
|
|
307
|
+
pipe = pipe.extract(_expand_globs(files["science"]))
|
|
308
|
+
|
|
309
|
+
if "continuum" in config_steps:
|
|
310
|
+
pipe = pipe.continuum()
|
|
311
|
+
|
|
312
|
+
if "finalize" in config_steps:
|
|
313
|
+
pipe = pipe.finalize()
|
|
314
|
+
|
|
315
|
+
pipe.run(skip_existing=skip_existing)
|
|
316
|
+
click.echo("Pipeline complete!")
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
def _expand_globs(patterns) -> list[str]:
|
|
320
|
+
"""Expand glob patterns to file list."""
|
|
321
|
+
if isinstance(patterns, str):
|
|
322
|
+
patterns = [patterns]
|
|
323
|
+
|
|
324
|
+
files = []
|
|
325
|
+
for pattern in patterns:
|
|
326
|
+
expanded = glob(pattern)
|
|
327
|
+
if expanded:
|
|
328
|
+
files.extend(expanded)
|
|
329
|
+
else:
|
|
330
|
+
# If no glob match, treat as literal path
|
|
331
|
+
if Path(pattern).exists():
|
|
332
|
+
files.append(pattern)
|
|
333
|
+
return sorted(set(files))
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
def main():
|
|
337
|
+
"""Entry point for the CLI."""
|
|
338
|
+
cli()
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
if __name__ == "__main__":
|
|
342
|
+
main()
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Builds the C library that contains the extraction algorithm
|
|
3
|
+
|
|
4
|
+
This module prepares and builds the C libary containing the curved
|
|
5
|
+
(and vertical) extraction algorithm using CFFI.
|
|
6
|
+
It also prepares the ffibuilder objects for setup.py,
|
|
7
|
+
so that the library is compiled on installation.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
The user can also call the Module as a script to compile the
|
|
11
|
+
C libraries again.
|
|
12
|
+
|
|
13
|
+
Attributes
|
|
14
|
+
----------
|
|
15
|
+
ffi_builder_vertical : FFI
|
|
16
|
+
CFFI Builder for the vertical extraction algorithm
|
|
17
|
+
ffi_builder_curved : FFI
|
|
18
|
+
CFFI Builder for the curved extraction algorithm
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
|
|
24
|
+
from cffi import FFI
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
CWD = os.path.dirname(__file__)
|
|
30
|
+
CWD = os.path.abspath(CWD)
|
|
31
|
+
release_path = os.path.join(CWD, "Release")
|
|
32
|
+
|
|
33
|
+
print("Include dir: ", CWD)
|
|
34
|
+
print("Release dir: ", release_path)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
ffibuilder_vertical = FFI()
|
|
38
|
+
with open(os.path.join(CWD, "slit_func_bd.h")) as f:
|
|
39
|
+
ffibuilder_vertical.cdef(f.read())
|
|
40
|
+
with open(os.path.join(CWD, "slit_func_bd.c")) as f:
|
|
41
|
+
ffibuilder_vertical.set_source(
|
|
42
|
+
"_slitfunc_bd",
|
|
43
|
+
f.read(),
|
|
44
|
+
include_dirs=[CWD, release_path],
|
|
45
|
+
depends=["slit_func_bd.h"],
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
ffibuilder_curved = FFI()
|
|
49
|
+
with open(os.path.join(CWD, "slit_func_2d_xi_zeta_bd.h")) as f:
|
|
50
|
+
ffibuilder_curved.cdef(f.read())
|
|
51
|
+
with open(os.path.join(CWD, "slit_func_2d_xi_zeta_bd.c")) as f:
|
|
52
|
+
ffibuilder_curved.set_source(
|
|
53
|
+
"_slitfunc_2d",
|
|
54
|
+
f.read(),
|
|
55
|
+
include_dirs=[CWD, release_path],
|
|
56
|
+
depends=["slit_func_2d_xi_zeta_bd.h"],
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def build():
|
|
61
|
+
"""Builds the C slitfunc library"""
|
|
62
|
+
logger.info("Building required C libraries, this might take a few seconds")
|
|
63
|
+
|
|
64
|
+
old_cwd = os.getcwd()
|
|
65
|
+
path = os.path.abspath(CWD)
|
|
66
|
+
os.chdir(path)
|
|
67
|
+
|
|
68
|
+
ffibuilder_vertical.compile(verbose=True, debug=False)
|
|
69
|
+
ffibuilder_curved.compile(verbose=True, debug=False)
|
|
70
|
+
|
|
71
|
+
os.chdir(old_cwd)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
if __name__ == "__main__": # pragma: no cover
|
|
75
|
+
build()
|