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.
Files changed (182) hide show
  1. pyreduce/__init__.py +67 -0
  2. pyreduce/__main__.py +322 -0
  3. pyreduce/cli.py +342 -0
  4. pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.exp +0 -0
  5. pyreduce/clib/Release/_slitfunc_2d.cp311-win_amd64.lib +0 -0
  6. pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.exp +0 -0
  7. pyreduce/clib/Release/_slitfunc_2d.cp312-win_amd64.lib +0 -0
  8. pyreduce/clib/Release/_slitfunc_2d.cp313-win_amd64.exp +0 -0
  9. pyreduce/clib/Release/_slitfunc_2d.cp313-win_amd64.lib +0 -0
  10. pyreduce/clib/Release/_slitfunc_2d.cp314-win_amd64.exp +0 -0
  11. pyreduce/clib/Release/_slitfunc_2d.cp314-win_amd64.lib +0 -0
  12. pyreduce/clib/Release/_slitfunc_2d.obj +0 -0
  13. pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.exp +0 -0
  14. pyreduce/clib/Release/_slitfunc_bd.cp311-win_amd64.lib +0 -0
  15. pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.exp +0 -0
  16. pyreduce/clib/Release/_slitfunc_bd.cp312-win_amd64.lib +0 -0
  17. pyreduce/clib/Release/_slitfunc_bd.cp313-win_amd64.exp +0 -0
  18. pyreduce/clib/Release/_slitfunc_bd.cp313-win_amd64.lib +0 -0
  19. pyreduce/clib/Release/_slitfunc_bd.cp314-win_amd64.exp +0 -0
  20. pyreduce/clib/Release/_slitfunc_bd.cp314-win_amd64.lib +0 -0
  21. pyreduce/clib/Release/_slitfunc_bd.obj +0 -0
  22. pyreduce/clib/__init__.py +0 -0
  23. pyreduce/clib/_slitfunc_2d.cp311-win_amd64.pyd +0 -0
  24. pyreduce/clib/_slitfunc_2d.cp312-win_amd64.pyd +0 -0
  25. pyreduce/clib/_slitfunc_2d.cp313-win_amd64.pyd +0 -0
  26. pyreduce/clib/_slitfunc_2d.cp314-win_amd64.pyd +0 -0
  27. pyreduce/clib/_slitfunc_bd.cp311-win_amd64.pyd +0 -0
  28. pyreduce/clib/_slitfunc_bd.cp312-win_amd64.pyd +0 -0
  29. pyreduce/clib/_slitfunc_bd.cp313-win_amd64.pyd +0 -0
  30. pyreduce/clib/_slitfunc_bd.cp314-win_amd64.pyd +0 -0
  31. pyreduce/clib/build_extract.py +75 -0
  32. pyreduce/clib/slit_func_2d_xi_zeta_bd.c +1313 -0
  33. pyreduce/clib/slit_func_2d_xi_zeta_bd.h +55 -0
  34. pyreduce/clib/slit_func_bd.c +362 -0
  35. pyreduce/clib/slit_func_bd.h +17 -0
  36. pyreduce/clipnflip.py +147 -0
  37. pyreduce/combine_frames.py +861 -0
  38. pyreduce/configuration.py +191 -0
  39. pyreduce/continuum_normalization.py +329 -0
  40. pyreduce/cwrappers.py +404 -0
  41. pyreduce/datasets.py +238 -0
  42. pyreduce/echelle.py +413 -0
  43. pyreduce/estimate_background_scatter.py +130 -0
  44. pyreduce/extract.py +1362 -0
  45. pyreduce/extraction_width.py +77 -0
  46. pyreduce/instruments/__init__.py +0 -0
  47. pyreduce/instruments/aj.py +9 -0
  48. pyreduce/instruments/aj.yaml +51 -0
  49. pyreduce/instruments/andes.py +102 -0
  50. pyreduce/instruments/andes.yaml +72 -0
  51. pyreduce/instruments/common.py +711 -0
  52. pyreduce/instruments/common.yaml +57 -0
  53. pyreduce/instruments/crires_plus.py +103 -0
  54. pyreduce/instruments/crires_plus.yaml +101 -0
  55. pyreduce/instruments/filters.py +195 -0
  56. pyreduce/instruments/harpn.py +203 -0
  57. pyreduce/instruments/harpn.yaml +140 -0
  58. pyreduce/instruments/harps.py +312 -0
  59. pyreduce/instruments/harps.yaml +144 -0
  60. pyreduce/instruments/instrument_info.py +140 -0
  61. pyreduce/instruments/jwst_miri.py +29 -0
  62. pyreduce/instruments/jwst_miri.yaml +53 -0
  63. pyreduce/instruments/jwst_niriss.py +98 -0
  64. pyreduce/instruments/jwst_niriss.yaml +60 -0
  65. pyreduce/instruments/lick_apf.py +35 -0
  66. pyreduce/instruments/lick_apf.yaml +60 -0
  67. pyreduce/instruments/mcdonald.py +123 -0
  68. pyreduce/instruments/mcdonald.yaml +56 -0
  69. pyreduce/instruments/metis_ifu.py +45 -0
  70. pyreduce/instruments/metis_ifu.yaml +62 -0
  71. pyreduce/instruments/metis_lss.py +45 -0
  72. pyreduce/instruments/metis_lss.yaml +62 -0
  73. pyreduce/instruments/micado.py +45 -0
  74. pyreduce/instruments/micado.yaml +62 -0
  75. pyreduce/instruments/models.py +257 -0
  76. pyreduce/instruments/neid.py +156 -0
  77. pyreduce/instruments/neid.yaml +61 -0
  78. pyreduce/instruments/nirspec.py +215 -0
  79. pyreduce/instruments/nirspec.yaml +63 -0
  80. pyreduce/instruments/nte.py +42 -0
  81. pyreduce/instruments/nte.yaml +55 -0
  82. pyreduce/instruments/uves.py +46 -0
  83. pyreduce/instruments/uves.yaml +65 -0
  84. pyreduce/instruments/xshooter.py +39 -0
  85. pyreduce/instruments/xshooter.yaml +63 -0
  86. pyreduce/make_shear.py +607 -0
  87. pyreduce/masks/mask_crires_plus_det1.fits.gz +0 -0
  88. pyreduce/masks/mask_crires_plus_det2.fits.gz +0 -0
  89. pyreduce/masks/mask_crires_plus_det3.fits.gz +0 -0
  90. pyreduce/masks/mask_ctio_chiron.fits.gz +0 -0
  91. pyreduce/masks/mask_elodie.fits.gz +0 -0
  92. pyreduce/masks/mask_feros3.fits.gz +0 -0
  93. pyreduce/masks/mask_flames_giraffe.fits.gz +0 -0
  94. pyreduce/masks/mask_harps_blue.fits.gz +0 -0
  95. pyreduce/masks/mask_harps_red.fits.gz +0 -0
  96. pyreduce/masks/mask_hds_blue.fits.gz +0 -0
  97. pyreduce/masks/mask_hds_red.fits.gz +0 -0
  98. pyreduce/masks/mask_het_hrs_2x5.fits.gz +0 -0
  99. pyreduce/masks/mask_jwst_miri_lrs_slitless.fits.gz +0 -0
  100. pyreduce/masks/mask_jwst_niriss_gr700xd.fits.gz +0 -0
  101. pyreduce/masks/mask_lick_apf_.fits.gz +0 -0
  102. pyreduce/masks/mask_mcdonald.fits.gz +0 -0
  103. pyreduce/masks/mask_nes.fits.gz +0 -0
  104. pyreduce/masks/mask_nirspec_nirspec.fits.gz +0 -0
  105. pyreduce/masks/mask_sarg.fits.gz +0 -0
  106. pyreduce/masks/mask_sarg_2x2a.fits.gz +0 -0
  107. pyreduce/masks/mask_sarg_2x2b.fits.gz +0 -0
  108. pyreduce/masks/mask_subaru_hds_red.fits.gz +0 -0
  109. pyreduce/masks/mask_uves_blue.fits.gz +0 -0
  110. pyreduce/masks/mask_uves_blue_binned_2_2.fits.gz +0 -0
  111. pyreduce/masks/mask_uves_middle.fits.gz +0 -0
  112. pyreduce/masks/mask_uves_middle_2x2_split.fits.gz +0 -0
  113. pyreduce/masks/mask_uves_middle_binned_2_2.fits.gz +0 -0
  114. pyreduce/masks/mask_uves_red.fits.gz +0 -0
  115. pyreduce/masks/mask_uves_red_2x2.fits.gz +0 -0
  116. pyreduce/masks/mask_uves_red_2x2_split.fits.gz +0 -0
  117. pyreduce/masks/mask_uves_red_binned_2_2.fits.gz +0 -0
  118. pyreduce/masks/mask_xshooter_nir.fits.gz +0 -0
  119. pyreduce/pipeline.py +619 -0
  120. pyreduce/rectify.py +138 -0
  121. pyreduce/reduce.py +2065 -0
  122. pyreduce/settings/settings_AJ.json +19 -0
  123. pyreduce/settings/settings_ANDES.json +89 -0
  124. pyreduce/settings/settings_CRIRES_PLUS.json +89 -0
  125. pyreduce/settings/settings_HARPN.json +73 -0
  126. pyreduce/settings/settings_HARPS.json +69 -0
  127. pyreduce/settings/settings_JWST_MIRI.json +55 -0
  128. pyreduce/settings/settings_JWST_NIRISS.json +55 -0
  129. pyreduce/settings/settings_LICK_APF.json +62 -0
  130. pyreduce/settings/settings_MCDONALD.json +58 -0
  131. pyreduce/settings/settings_METIS_IFU.json +77 -0
  132. pyreduce/settings/settings_METIS_LSS.json +77 -0
  133. pyreduce/settings/settings_MICADO.json +78 -0
  134. pyreduce/settings/settings_NEID.json +73 -0
  135. pyreduce/settings/settings_NIRSPEC.json +58 -0
  136. pyreduce/settings/settings_NTE.json +60 -0
  137. pyreduce/settings/settings_UVES.json +54 -0
  138. pyreduce/settings/settings_XSHOOTER.json +78 -0
  139. pyreduce/settings/settings_pyreduce.json +184 -0
  140. pyreduce/settings/settings_schema.json +850 -0
  141. pyreduce/tools/__init__.py +0 -0
  142. pyreduce/tools/combine.py +117 -0
  143. pyreduce/trace.py +979 -0
  144. pyreduce/util.py +1366 -0
  145. pyreduce/wavecal/MICADO_HK_3arcsec_chip5.npz +0 -0
  146. pyreduce/wavecal/atlas/thar.fits +4946 -13
  147. pyreduce/wavecal/atlas/thar_list.txt +4172 -0
  148. pyreduce/wavecal/atlas/une.fits +0 -0
  149. pyreduce/wavecal/convert.py +38 -0
  150. pyreduce/wavecal/crires_plus_J1228_Open_det1.npz +0 -0
  151. pyreduce/wavecal/crires_plus_J1228_Open_det2.npz +0 -0
  152. pyreduce/wavecal/crires_plus_J1228_Open_det3.npz +0 -0
  153. pyreduce/wavecal/harpn_harpn_2D.npz +0 -0
  154. pyreduce/wavecal/harps_blue_2D.npz +0 -0
  155. pyreduce/wavecal/harps_blue_pol_2D.npz +0 -0
  156. pyreduce/wavecal/harps_red_2D.npz +0 -0
  157. pyreduce/wavecal/harps_red_pol_2D.npz +0 -0
  158. pyreduce/wavecal/mcdonald.npz +0 -0
  159. pyreduce/wavecal/metis_lss_l_2D.npz +0 -0
  160. pyreduce/wavecal/metis_lss_m_2D.npz +0 -0
  161. pyreduce/wavecal/nirspec_K2.npz +0 -0
  162. pyreduce/wavecal/uves_blue_360nm_2D.npz +0 -0
  163. pyreduce/wavecal/uves_blue_390nm_2D.npz +0 -0
  164. pyreduce/wavecal/uves_blue_437nm_2D.npz +0 -0
  165. pyreduce/wavecal/uves_middle_2x2_2D.npz +0 -0
  166. pyreduce/wavecal/uves_middle_565nm_2D.npz +0 -0
  167. pyreduce/wavecal/uves_middle_580nm_2D.npz +0 -0
  168. pyreduce/wavecal/uves_middle_600nm_2D.npz +0 -0
  169. pyreduce/wavecal/uves_middle_665nm_2D.npz +0 -0
  170. pyreduce/wavecal/uves_middle_860nm_2D.npz +0 -0
  171. pyreduce/wavecal/uves_red_580nm_2D.npz +0 -0
  172. pyreduce/wavecal/uves_red_600nm_2D.npz +0 -0
  173. pyreduce/wavecal/uves_red_665nm_2D.npz +0 -0
  174. pyreduce/wavecal/uves_red_760nm_2D.npz +0 -0
  175. pyreduce/wavecal/uves_red_860nm_2D.npz +0 -0
  176. pyreduce/wavecal/xshooter_nir.npz +0 -0
  177. pyreduce/wavelength_calibration.py +1871 -0
  178. pyreduce_astro-0.7a4.dist-info/METADATA +106 -0
  179. pyreduce_astro-0.7a4.dist-info/RECORD +182 -0
  180. pyreduce_astro-0.7a4.dist-info/WHEEL +4 -0
  181. pyreduce_astro-0.7a4.dist-info/entry_points.txt +2 -0
  182. 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
File without changes
@@ -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()