MUSE Pipeline Reference Manual  2.1.1
muse_scibasic.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set sw=2 sts=2 et cin: */
3 /*
4  * This file is part of the MUSE Instrument Pipeline
5  * Copyright (C) 2005-2014 European Southern Observatory
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 /*----------------------------------------------------------------------------*
27  * Includes *
28  *----------------------------------------------------------------------------*/
29 #include <stdio.h>
30 #include <float.h>
31 #include <math.h>
32 #include <string.h>
33 #include <cpl.h>
34 
35 #include <muse.h>
36 #include "muse_scibasic_z.h"
37 
38 /*----------------------------------------------------------------------------*
39  * Functions code *
40  *----------------------------------------------------------------------------*/
41 
42 /*----------------------------------------------------------------------------*/
60 /*----------------------------------------------------------------------------*/
61 static int
62 muse_scibasic_per_exposure(muse_processing *aProcessing,
63  muse_scibasic_params_t *aParams,
64  cpl_table *aTrace, cpl_table *aWave, cpl_table *aGeo,
65  muse_image *aImage, cpl_array *aSkyLines,
66  muse_image *aFlat, cpl_table *aAttached,
67  muse_datacube **aTwilights)
68 {
69  cpl_ensure(aImage && aTrace && aWave, CPL_ERROR_NULL_INPUT, -1);
70 
71  /* add saturated number of pixels as QC early to save it in all outputs */
72  muse_basicproc_qc_saturated(aImage, QC_SCIBASIC_PREFIX);
73 
74  /* get the input tag of this file from the header *
75  * (before saving deletes it) */
76  char *intag = cpl_strdup(cpl_propertylist_get_string(aImage->header,
77  MUSE_HDR_TMP_INTAG));
78  if (aParams->saveimage) {
79  char *tag = cpl_sprintf("%s_RED", intag);
80  muse_processing_save_image(aProcessing, aParams->nifu, aImage, tag);
81  cpl_free(tag);
82  }
83 
84  /* create and save the output pixel table in any case */
85  muse_pixtable *pixtable = muse_pixtable_create(aImage, aTrace, aWave, aGeo);
86  if (!pixtable) {
87  cpl_msg_error(__func__, "Pixel table was not created for IFU %d: %s",
88  aParams->nifu, cpl_error_get_message());
89  cpl_free(intag);
90  return -1;
91  }
92  muse_pixtable_append_ff(pixtable, aFlat, aTrace, aWave, kMuseSpectralSamplingA);
93  if (aSkyLines) {
94  cpl_array *rejpars = muse_cplarray_new_from_delimited_string(aParams->skyreject, ",");
95  int nrej = cpl_array_get_size(rejpars);
96  float lsigma = 15.,
97  hsigma = 15.;
98  unsigned char niter = 1;
99  if (nrej > 0 && cpl_array_get_string(rejpars, 0)) {
100  /* first number: high sigma limit */
101  hsigma = atof(cpl_array_get_string(rejpars, 0));
102  }
103  if (nrej > 1 && cpl_array_get_string(rejpars, 1)) {
104  /* second number: low sigma limit */
105  lsigma = atof(cpl_array_get_string(rejpars, 1));
106  }
107  if (nrej > 2 && cpl_array_get_string(rejpars, 2)) {
108  /* second number: low sigma limit */
109  niter = atoi(cpl_array_get_string(rejpars, 2));
110  }
111  cpl_array_delete(rejpars);
112  muse_basicproc_shift_pixtable(pixtable, aSkyLines, aParams->skyhalfwidth,
113  aParams->skybinsize, lsigma, hsigma, niter);
114  }
115 
116  /* Trim the pixel table to a useful wavelength range before saving it. *
117  * Use slightly larger ranges to not cut too much, and so leave the *
118  * first plane of the output datacube hopefully mostly intact. */
119  const double llimN = kMuseUsefulNLambdaMin - FLT_EPSILON,
120  llimE = kMuseUsefulELambdaMin - FLT_EPSILON,
121  llimA = kMuseUsefulAOLambdaMin - FLT_EPSILON,
122  hlim = kMuseUsefulLambdaMax + FLT_EPSILON;
123  muse_ins_mode mode = muse_pfits_get_mode(pixtable->header);
124  if (aParams->crop) {
125  if ((mode == MUSE_MODE_WFM_AO_N) || (mode == MUSE_MODE_NFM_AO_N)) {
126  cpl_msg_info(__func__, "Nominal AO mode: cropping the pixel table of IFU "
127  "%d to %.1f...%.1f Angstrom", aParams->nifu, llimA, hlim);
128  muse_pixtable_restrict_wavelength(pixtable, llimA, hlim);
129  } else if (mode == MUSE_MODE_WFM_NONAO_N) {
130  cpl_msg_info(__func__, "Nominal mode: cropping the pixel table of IFU %d"
131  " to %.1f...%.1f Angstrom", aParams->nifu, llimN, hlim);
132  muse_pixtable_restrict_wavelength(pixtable, llimN, hlim);
133  } else { /* extended mode */
134  cpl_msg_info(__func__, "Extended mode: cropping the pixel table of IFU %d"
135  " to %.1f...%.1f Angstrom", aParams->nifu, llimE, hlim);
136  muse_pixtable_restrict_wavelength(pixtable, llimE, hlim);
137  }
138  } /* if crop */
139  if (mode > MUSE_MODE_WFM_NONAO_N) {
140  muse_basicproc_mask_notch_filter(pixtable, aParams->nifu);
141  } /* if AO mode */
142 
143  if (aAttached) { /* test before, to not throw an error */
144  muse_basicproc_apply_illum(pixtable, aAttached);
145  } /* if attached flat given */
146 
147  /* twilight correction using one or more twilight cubes */
148  int icor;
149  for (icor = 0; aTwilights && aTwilights[icor]; icor++) {
150  cpl_msg_info(__func__, "Starting twilight correction %d in IFU %d",
151  icor + 1, aParams->nifu);
152  muse_basicproc_apply_twilight(pixtable, aTwilights[icor]);
153  } /* for twilight correction cube not NULL */
154 
155  if (aParams->resample) {
156  /* to visualize the current state better than the pixtable *
157  * can do, resample the data onto an image */
158  muse_image *image = muse_resampling_image(pixtable,
160  1., aParams->dlambda);
161  char *tag = cpl_sprintf("%s_RESAMPLED", intag);
162  /* don't want to save QC headers with the samples table: */
163  cpl_propertylist_erase_regexp(image->header, QC_SCIBASIC_PREFIX, 0);
164  muse_processing_save_image(aProcessing, aParams->nifu, image, tag);
165  cpl_free(tag);
166  muse_image_delete(image);
167  } /* if resample */
168 
169  /* construct output tag and finally save */
170  char *outtag = cpl_sprintf("PIXTABLE_%s", intag);
171  muse_processing_save_table(aProcessing, aParams->nifu, pixtable, NULL, outtag,
173  cpl_free(outtag);
174  cpl_free(intag);
175  muse_pixtable_delete(pixtable);
176 
177  return 0;
178 } /* muse_scibasic_per_exposure() */
179 
180 /*----------------------------------------------------------------------------*/
187 /*----------------------------------------------------------------------------*/
188 int
189 muse_scibasic_compute(muse_processing *aProcessing,
190  muse_scibasic_params_t *aParams)
191 {
192  muse_imagelist *images = NULL;
193  muse_image *masterflat = NULL;
194  if (muse_processing_check_intags(aProcessing, "REDUCED", 8)) {
195  cpl_msg_warning(__func__, "Found REDUCED files on input, ignoring all "
196  "others inputs!");
197  images = muse_basicproc_load_reduced(aProcessing, aParams->nifu);
198  cpl_size i;
199  cpl_size n = muse_imagelist_get_size(images);
200  for (i = 0; i < n; i++) {
201  muse_image *img = muse_imagelist_get(images, i);
202  cpl_propertylist_update_string(img->header, MUSE_HDR_TMP_INTAG,
203  cpl_array_get_string(aProcessing->intags, 0));
204  }
205  } else {
207  "muse.muse_scibasic");
208  bpars->keepflat = CPL_TRUE;
209  images = muse_basicproc_load(aProcessing, aParams->nifu, bpars);
210  masterflat = bpars->flatimage;
211  bpars->flatimage = NULL; /* so that it doesn't get deallocated */
213  }
214  if (!images) {
215  muse_image_delete(masterflat);
216  cpl_error_set(__func__, cpl_error_get_code());
217  return -1;
218  }
219 
220  cpl_table *tracetable = muse_processing_load_ctable(aProcessing, MUSE_TAG_TRACE_TABLE,
221  aParams->nifu);
222  cpl_table *wavecaltable = muse_processing_load_ctable(aProcessing, MUSE_TAG_WAVECAL_TABLE,
223  aParams->nifu);
224  cpl_table *geotable = muse_processing_load_ctable(aProcessing, MUSE_TAG_GEOMETRY_TABLE, 0);
225  if (!tracetable || !wavecaltable || !geotable) {
226  cpl_msg_error(__func__, "Calibration could not be loaded for IFU %d:%s%s%s",
227  aParams->nifu, !tracetable ? " "MUSE_TAG_TRACE_TABLE : "",
228  !wavecaltable ? " "MUSE_TAG_WAVECAL_TABLE : "",
229  !geotable ? " "MUSE_TAG_GEOMETRY_TABLE : "");
230  /* try to clean up in case some files were successfully loaded */
231  muse_imagelist_delete(images);
232  cpl_table_delete(tracetable);
233  cpl_table_delete(wavecaltable);
234  cpl_table_delete(geotable);
235  return -1;
236  }
237 
238  /* load twilight cube */
239  cpl_frameset *fset = muse_frameset_find(aProcessing->inframes,
240  MUSE_TAG_TWILIGHT_CUBE, 0, 0);
241  int i, ntwilight = cpl_frameset_get_size(fset);
242  muse_datacube **twilights = cpl_calloc(ntwilight + 1, sizeof(muse_datacube *));
243  for (i = 0; i < ntwilight; i++) {
244  cpl_frame *ftwilight = cpl_frameset_get_position(fset, i);
245  const char *fn = cpl_frame_get_filename(ftwilight);
246  twilights[i] = muse_datacube_load(fn);
247  if (!twilights[i]) {
248  cpl_msg_warning(__func__, "Could not load %s from \"%s\"",
249  MUSE_TAG_TWILIGHT_CUBE, fn);
250  break; /* makes no sense to continue */
251  }
252  const char *catg = muse_pfits_get_pro_catg(twilights[i]->header);
253  if (catg && strncmp(MUSE_TAG_TWILIGHT_CUBE, catg,
254  strlen(MUSE_TAG_TWILIGHT_CUBE) + 1)) {
255  cpl_msg_warning(__func__, "Supposed %s (\"%s\") has wrong PRO.CATG: %s",
256  MUSE_TAG_TWILIGHT_CUBE, fn, catg);
257  }
258  muse_processing_append_used(aProcessing, ftwilight, CPL_FRAME_GROUP_CALIB, 1);
259  } /* for i (all twilight cubes in frameset) */
260  cpl_frameset_delete(fset);
261 
262  cpl_array *lines = muse_cplarray_new_from_delimited_string(aParams->skylines, ","),
263  *skylines = muse_cplarray_string_to_double(lines);
264  cpl_array_delete(lines);
265  int rc = 0;
266  muse_combinepar *cpars = muse_combinepar_new(aProcessing->parameters,
267  "muse.muse_scibasic");
268  if (cpars->combine == MUSE_COMBINE_NONE) { /* no combination, the default */
269  /* search for attached/illumination flat-field in input list, *
270  * prepare them and remove all from the list */
271  cpl_table *tattached = muse_basicproc_get_illum(images, tracetable,
272  wavecaltable, geotable);
273  unsigned int k, nimages = muse_imagelist_get_size(images);
274  for (k = 0; k < nimages && !rc; k++) {
275  muse_image *image = muse_imagelist_get(images, k);
276  rc = muse_scibasic_per_exposure(aProcessing, aParams, tracetable,
277  wavecaltable, geotable, image, skylines,
278  masterflat, tattached, twilights);
279  } /* for k (all science images) */
280  cpl_table_delete(tattached);
281  } else { /* some combination is supposed to happen */
282  int ntags = cpl_array_get_size(aProcessing->intags);
283  if (ntags > 1) {
284  cpl_msg_warning(__func__, "Combining images of %d different tags, but "
285  "will use %s for output!", ntags,
286  cpl_array_get_string(aProcessing->intags, 0));
287  } else {
288  cpl_msg_debug(__func__, "Combining images with %d tag", ntags);
289  }
290  muse_image *image = muse_combine_images(cpars, images);
291  /* re-add the INTAG header that was removed by muse_combine_images() */
292  cpl_propertylist_update_string(image->header, MUSE_HDR_TMP_INTAG,
293  cpl_array_get_string(aProcessing->intags, 0));
294  rc = muse_scibasic_per_exposure(aProcessing, aParams, tracetable,
295  wavecaltable, geotable, image, skylines,
296  masterflat, NULL, twilights);
297  muse_image_delete(image);
298  } /* else: combined processing */
299  muse_image_delete(masterflat);
300  cpl_array_delete(skylines);
301  muse_combinepar_delete(cpars);
302 
303  /* clean up */
304  muse_imagelist_delete(images);
305  cpl_table_delete(tracetable);
306  cpl_table_delete(wavecaltable);
307  cpl_table_delete(geotable);
308  for (i = 0; twilights[i]; i++) {
309  muse_datacube_delete(twilights[i]);
310  }
311  cpl_free(twilights);
312 
313  return rc; /* can only be 0 or -1 */
314 } /* muse_scibasic_compute() */
muse_imagelist * muse_basicproc_load(muse_processing *aProcessing, unsigned char aIFU, muse_basicproc_params *aBPars)
Load the raw input files from disk and do basic processing.
Structure definition of a MUSE datacube.
Definition: muse_datacube.h:48
muse_image * muse_resampling_image(muse_pixtable *aPixtable, muse_resampling_type aMethod, double aDX, double aDLambda)
Resample a pixel table onto a two-dimensional regular grid.
const char * skyreject
Sigma clipping parameters for the intermediate spectrum to do the Gaussian fit to each sky emission l...
Structure definition for a collection of muse_images.
void muse_image_delete(muse_image *aImage)
Deallocate memory associated to a muse_image object.
Definition: muse_image.c:85
muse_datacube * muse_datacube_load(const char *aFilename)
Load header, DATA and optionally STAT and DQ extensions as well as the reconstructed images of a MUSE...
cpl_array * muse_cplarray_string_to_double(const cpl_array *aArray)
Convert a string array into an array of type double.
void muse_datacube_delete(muse_datacube *aCube)
Deallocate memory associated to a muse_datacube object.
void muse_imagelist_delete(muse_imagelist *aList)
Free the memory of the MUSE image list.
muse_imagelist * muse_basicproc_load_reduced(muse_processing *aProcessing, unsigned char aIFU)
Load reduced input files from disk.
muse_basicproc_params * muse_basicproc_params_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new structure of basic processing parameters.
muse_image * flatimage
double dlambda
Wavelength step (in Angstrom per pixel) to use for resampling.
muse_image * muse_combine_images(muse_combinepar *aCPars, muse_imagelist *aImages)
Combine several images into one.
Definition: muse_combine.c:741
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:40
void muse_basicproc_params_delete(muse_basicproc_params *aBPars)
Free a structure of basic processing parameters.
cpl_propertylist * header
the FITS header
Definition: muse_image.h:72
cpl_boolean muse_processing_check_intags(muse_processing *aProcessing, const char *aTag, int aNChars)
Check that a tag is part of the input tags of a processing structure.
unsigned int muse_imagelist_get_size(muse_imagelist *aList)
Return the number of stored images.
void muse_combinepar_delete(muse_combinepar *aCPars)
Clear the combination parameters.
Definition: muse_combine.c:715
cpl_error_code muse_pixtable_append_ff(muse_pixtable *aPixtable, muse_image *aFF, cpl_table *aTrace, cpl_table *aWave, float aSampling)
Create flat-field spectrum and append to pixel table.
cpl_error_code muse_pixtable_restrict_wavelength(muse_pixtable *aPixtable, double aLow, double aHigh)
Restrict a pixel table to a certain wavelength range.
int saveimage
Save the pre-processed CCD-based image of each input exposure before it is transformed into a pixel t...
cpl_array * muse_cplarray_new_from_delimited_string(const char *aString, const char *aDelim)
Convert a delimited string into an array of strings.
Structure definition of MUSE pixel table.
cpl_error_code muse_basicproc_shift_pixtable(muse_pixtable *aPt, cpl_array *aLines, double aHalfWidth, double aBinWidth, float aLo, float aHi, unsigned char aIter)
Compute wavelength corrections for science data based on reference sky lines.
muse_image * muse_imagelist_get(muse_imagelist *aList, unsigned int aIdx)
Get the muse_image of given list index.
cpl_error_code muse_basicproc_apply_twilight(muse_pixtable *aPT, muse_datacube *aTwilight)
Apply an attached flat-field to a pixel table.
int resample
Resample the input science data into 2D spectral images using all supplied calibrations for a visual ...
muse_combinepar * muse_combinepar_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new set of combination parameters.
Definition: muse_combine.c:672
int nifu
IFU to handle. If set to 0, all IFUs are processed serially. If set to -1, all IFUs are processed in ...
double skyhalfwidth
Half-width of the extraction box (in Angstrom) around each sky emission line.
void muse_processing_append_used(muse_processing *aProcessing, cpl_frame *aFrame, cpl_frame_group aGroup, int aDuplicate)
Add a frame to the set of used frames.
int crop
Automatically crop the output pixel tables in wavelength depending on the expected useful wavelength ...
muse_pixtable * muse_pixtable_create(muse_image *aImage, cpl_table *aTrace, cpl_table *aWave, cpl_table *aGeoTable)
Create the pixel table for one CCD.
cpl_table * muse_basicproc_get_illum(muse_imagelist *aImages, cpl_table *aTrace, cpl_table *aWave, cpl_table *aGeo)
Get an illum/attached flat-field from an imagelist and prepare it for use.
int muse_processing_save_image(muse_processing *aProcessing, int aIFU, muse_image *aImage, const char *aTag)
Save a computed MUSE image to disk.
cpl_table * muse_processing_load_ctable(muse_processing *aProcessing, const char *aTag, unsigned char aIFU)
Load a CPL table according to its tag and IFU/channel number.
Structure to hold the parameters of the muse_scibasic recipe.
double skybinsize
Size of the bins (in Angstrom per pixel) for the intermediate spectrum to do the Gaussian fit to each...
cpl_array * intags
cpl_frameset * inframes
cpl_error_code muse_basicproc_mask_notch_filter(muse_pixtable *aPT, unsigned char aIFU)
Mask the range of the NaD notch filter in the given pixel table.
cpl_error_code muse_processing_save_table(muse_processing *aProcessing, int aIFU, void *aTable, cpl_propertylist *aHeader, const char *aTag, muse_table_type aType)
Save a computed table to disk.
Structure of basic processing parameters.
const char * muse_pfits_get_pro_catg(const cpl_propertylist *aHeaders)
find out the PRO category
Definition: muse_pfits.c:159
const char * skylines
List of wavelengths of sky emission lines (in Angstrom) to use as reference for wavelength offset cor...
cpl_error_code muse_basicproc_apply_illum(muse_pixtable *aPT, cpl_table *aAttached)
Apply an illum/attached flat-field to a pixel table.
cpl_frameset * muse_frameset_find(const cpl_frameset *aFrames, const char *aTag, unsigned char aIFU, cpl_boolean aInvert)
return frameset containing data from an IFU/channel with a certain tag
Definition: muse_utils.c:160
void muse_pixtable_delete(muse_pixtable *aPixtable)
Deallocate memory associated to a pixel table object.
muse_ins_mode muse_pfits_get_mode(const cpl_propertylist *aHeaders)
find out the observation mode
Definition: muse_pfits.c:1352
cpl_parameterlist * parameters
cpl_propertylist * header
The FITS header.
cpl_error_code muse_basicproc_qc_saturated(muse_image *aImage, const char *aPrefix)
Add QC parameter about saturated pixels to a muse_image.