MUSE Pipeline Reference Manual  1.0.2
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 /*----------------------------------------------------------------------------*/
59 /*----------------------------------------------------------------------------*/
60 static int
61 muse_scibasic_per_exposure(muse_processing *aProcessing,
62  muse_scibasic_params_t *aParams,
63  cpl_table *aTrace, cpl_table *aWave, cpl_table *aGeo,
64  muse_image *aImage, cpl_array *aSkyLines,
65  cpl_table *aAttached, muse_datacube **aTwilights)
66 {
67  cpl_ensure(aImage && aTrace && aWave, CPL_ERROR_NULL_INPUT, -1);
68  /* immediately return, if the given image is an ILLUM flat */
69  char *intag = cpl_strdup(cpl_propertylist_get_string(aImage->header,
70  MUSE_HDR_TMP_INTAG));
71  if (intag && !strncmp(intag, MUSE_TAG_ILLUM, strlen(MUSE_TAG_ILLUM) + 1)) {
72  cpl_free(intag);
73  return 0; /* is the attached flat */
74  } /* if */
75 
76  /* add saturated number of pixels as QC early to save it in all outputs */
77  muse_basicproc_qc_saturated(aImage, QC_SCIBASIC_PREFIX);
78 
79  /* get the input tag of this file from the header *
80  * (before saving deletes it) */
81  if (aParams->saveimage) {
82  char *tag = cpl_sprintf("%s_RED", intag);
83  muse_processing_save_image(aProcessing, aParams->nifu, aImage, tag);
84  cpl_free(tag);
85  }
86 
87  /* create and save the output pixel table in any case */
88  muse_pixtable *pixtable = muse_pixtable_create(aImage, aTrace, aWave, aGeo);
89  if (!pixtable) {
90  cpl_msg_error(__func__, "Pixel table was not created for IFU %d: %s",
91  aParams->nifu, cpl_error_get_message());
92  cpl_free(intag);
93  return -1;
94  }
95  if (aSkyLines) {
96  muse_basicproc_shift_pixtable(pixtable, aSkyLines, aParams->skyhalfwidth,
97  aParams->skybinsize);
98  }
99 
100  if (aParams->resample) {
101  /* to visualize the current state better than the pixtable *
102  * can do, resample the data onto an image */
103  muse_image *image = muse_resampling_image(pixtable,
105  1., aParams->dlambda);
106  char *tag = cpl_sprintf("%s_RESAMPLED", intag);
107  /* don't want to save QC headers with the samples table: */
108  cpl_propertylist_erase_regexp(image->header, QC_SCIBASIC_PREFIX, 0);
109  muse_processing_save_image(aProcessing, aParams->nifu, image, tag);
110  cpl_free(tag);
111  muse_image_delete(image);
112  }
113 
114  /* Trim the pixel table to a useful wavelength range before saving it. *
115  * Use slightly larger ranges to not cut too much, and so leave the *
116  * first plane of the output datacube hopefully mostly intact. */
117  const double llimN = 4750. - FLT_EPSILON, hlimN = 9350. + FLT_EPSILON,
118  llimE = 4600. - FLT_EPSILON, hlimE = 9350. + FLT_EPSILON;
119  if (aParams->crop) {
120  if (muse_pfits_get_mode(pixtable->header) > MUSE_MODE_WFM_NONAO_X) {
121  cpl_msg_info(__func__, "Nominal mode: cropping the pixel table of IFU %d"
122  " to %.1f...%.1f Angstrom", aParams->nifu, llimN, hlimN);
123  muse_pixtable_restrict_wavelength(pixtable, llimN, hlimN);
124  } else { /* extended mode */
125  cpl_msg_info(__func__, "Extended mode: cropping the pixel table of IFU %d"
126  " to %.1f...%.1f Angstrom", aParams->nifu, llimE, hlimE);
127  muse_pixtable_restrict_wavelength(pixtable, llimE, hlimE);
128  }
129  } /* if crop */
130 
131  if (aAttached) {
132  muse_basicproc_apply_illum(pixtable, aAttached);
133  } /* if attached flat given */
134 
135  /* twilight correction using one or more twilight cubes */
136  int icor;
137  for (icor = 0; aTwilights && aTwilights[icor]; icor++) {
138  cpl_msg_info(__func__, "Starting twilight correction %d in IFU %d",
139  icor + 1, aParams->nifu);
140  muse_basicproc_apply_twilight(pixtable, aTwilights[icor]);
141  } /* for twilight correction cube not NULL */
142 
143  /* construct output tag and finally save */
144  char *outtag = cpl_sprintf("PIXTABLE_%s", intag);
145  muse_processing_save_table(aProcessing, aParams->nifu, pixtable, NULL, outtag,
147  cpl_free(outtag);
148  cpl_free(intag);
149  muse_pixtable_delete(pixtable);
150 
151  return 0;
152 } /* muse_scibasic_per_exposure() */
153 
154 /*----------------------------------------------------------------------------*/
161 /*----------------------------------------------------------------------------*/
162 int
163 muse_scibasic_compute(muse_processing *aProcessing,
164  muse_scibasic_params_t *aParams)
165 {
166  muse_imagelist *images = NULL;
167  if (muse_processing_check_intags(aProcessing, "REDUCED", 8)) {
168  cpl_msg_warning(__func__, "Found REDUCED files on input, ignoring all "
169  "others inputs!");
170  images = muse_basicproc_load_reduced(aProcessing, aParams->nifu);
171  } else {
173  "muse.muse_scibasic");
174  images = muse_basicproc_load(aProcessing, aParams->nifu, bpars);
176  }
177  cpl_ensure(images, cpl_error_get_code(), -1);
178 
179  cpl_table *tracetable = muse_table_load(aProcessing, MUSE_TAG_TRACE_TABLE,
180  aParams->nifu);
181  cpl_table *wavecaltable = muse_table_load(aProcessing, MUSE_TAG_WAVECAL_TABLE,
182  aParams->nifu);
183  cpl_table *geotable = muse_table_load(aProcessing, MUSE_TAG_GEOMETRY_TABLE, 0);
184  if (!tracetable || !wavecaltable || !geotable) {
185  cpl_msg_error(__func__, "Calibration could not be loaded for IFU %d:%s%s%s",
186  aParams->nifu, !tracetable ? " "MUSE_TAG_TRACE_TABLE : "",
187  !wavecaltable ? " "MUSE_TAG_WAVECAL_TABLE : "",
188  !geotable ? " "MUSE_TAG_GEOMETRY_TABLE : "");
189  /* try to clean up in case some files were successfully loaded */
190  muse_imagelist_delete(images);
191  cpl_table_delete(tracetable);
192  cpl_table_delete(wavecaltable);
193  cpl_table_delete(geotable);
194  return -1;
195  }
196 
197  /* load twilight cube */
198  cpl_frameset *fset = muse_frameset_find(aProcessing->inframes,
199  MUSE_TAG_TWILIGHT_CUBE, 0, 0);
200  int i, ntwilight = cpl_frameset_get_size(fset);
201  muse_datacube **twilights = cpl_calloc(ntwilight + 1, sizeof(muse_datacube *));
202  for (i = 0; i < ntwilight; i++) {
203  cpl_frame *ftwilight = cpl_frameset_get_position(fset, i);
204  const char *fn = cpl_frame_get_filename(ftwilight);
205  twilights[i] = muse_datacube_load(fn);
206  if (!twilights[i]) {
207  cpl_msg_warning(__func__, "Could not load %s from \"%s\"",
208  MUSE_TAG_TWILIGHT_CUBE, fn);
209  break; /* makes no sense to continue */
210  } else {
211  muse_processing_append_used(aProcessing, ftwilight, CPL_FRAME_GROUP_CALIB, 1);
212  } /* else */
213  } /* for i (all twilight cubes in frameset) */
214  cpl_frameset_delete(fset);
215 
216  cpl_array *lines = muse_cplarray_new_from_delimited_string(aParams->skylines, ","),
217  *skylines = muse_cplarray_string_to_double(lines);
218  cpl_array_delete(lines);
219  int rc = 0;
220  muse_combinepar *cpars = muse_combinepar_new(aProcessing->parameters,
221  "muse.muse_scibasic");
222  if (cpars->combine == MUSE_COMBINE_NONE) { /* no combination, the default */
223  /* search for attached/illumination flat-field in input list */
224  cpl_table *tattached = NULL;
225  unsigned int k, nimages = muse_imagelist_get_size(images);
226  for (k = 0; k < nimages; k++) {
227  cpl_boolean isillum = CPL_FALSE;
228  muse_image *image = muse_imagelist_get(images, k);
229  const char *tag = cpl_propertylist_get_string(image->header,
230  MUSE_HDR_TMP_INTAG);
231  if (tag && !strncmp(tag, MUSE_TAG_ILLUM, strlen(MUSE_TAG_ILLUM) + 1)) {
232  isillum = CPL_TRUE; /* is the attached FLAT,ILLUM */
233  /* also verify the template ID */
234  if (cpl_propertylist_has(image->header, "ESO TPL ID")) {
235  const char *tplid = cpl_propertylist_get_string(image->header,
236  "ESO TPL ID"),
237  *fn = cpl_propertylist_get_string(image->header,
238  MUSE_HDR_TMP_FN),
239  *tplatt = "MUSE_wfm_cal_specflatatt",
240  *tplill = "MUSE_wfm_cal_illum";
241  if (strncmp(tplid, tplatt, strlen(tplatt) + 1) &&
242  strncmp(tplid, tplill, strlen(tplill) + 1)) {
243  cpl_msg_warning(__func__, "%s input (\"%s\") was taken with neither"
244  " %s nor %s template, but %s!", MUSE_TAG_ILLUM, fn,
245  tplatt, tplill, tplid);
246  } else {
247  cpl_msg_debug(__func__, "%s input (\"%s\") was taken with template "
248  "%s", MUSE_TAG_ILLUM, fn, tplid);
249  } /* else */
250  } /* if TPL.ID present */
251  } /* if */
252  unsigned char ifu = muse_utils_get_ifu(image->header);
253  if (isillum) {
254  if (tattached) {
255  cpl_msg_warning(__func__, "Image %u of %u of IFU %hhu is illum flat, "
256  "but not the first; not using it!", k + 1, nimages, ifu);
257  continue;
258  }
259  cpl_msg_debug(__func__, "Image %u of %u of IFU %hhu is illum flat.",
260  k + 1, nimages, ifu);
261  muse_pixtable *pt = muse_pixtable_create(image, tracetable, wavecaltable,
262  geotable);
263  tattached = muse_basicproc_prepare_illum(pt);
265  } else {
266  cpl_msg_debug(__func__, "Image %u of %u of IFU %hhu is science image.",
267  k + 1, nimages, ifu);
268  }
269  } /* for k */
270  for (k = 0; k < nimages && !rc; k++) {
271  muse_image *image = muse_imagelist_get(images, k);
272  /* if this is an attached flat, the following function will ignore it */
273  rc = muse_scibasic_per_exposure(aProcessing, aParams, tracetable,
274  wavecaltable, geotable, image, skylines,
275  tattached, twilights);
276  } /* for k (all science images) */
277  cpl_table_delete(tattached);
278  } else { /* some combination is supposed to happen */
279  int ntags = cpl_array_get_size(aProcessing->intags);
280  if (ntags > 1) {
281  cpl_msg_warning(__func__, "Combining images of %d different tags, but "
282  "will use %s for output!", ntags,
283  cpl_array_get_string(aProcessing->intags, 0));
284  } else {
285  cpl_msg_debug(__func__, "Combining images with %d tag", ntags);
286  }
287  muse_image *image = muse_combine_images(cpars, images);
288  /* re-add the INTAG header that was removed by muse_combine_images() */
289  cpl_propertylist_update_string(image->header, MUSE_HDR_TMP_INTAG,
290  cpl_array_get_string(aProcessing->intags, 0));
291  rc = muse_scibasic_per_exposure(aProcessing, aParams, tracetable,
292  wavecaltable, geotable, image, skylines,
293  NULL, twilights);
294  muse_image_delete(image);
295  } /* else: combined processing */
296  cpl_array_delete(skylines);
297  muse_combinepar_delete(cpars);
298 
299  /* clean up */
300  muse_imagelist_delete(images);
301  cpl_table_delete(tracetable);
302  cpl_table_delete(wavecaltable);
303  cpl_table_delete(geotable);
304  for (i = 0; twilights[i]; i++) {
305  muse_datacube_delete(twilights[i]);
306  }
307  cpl_free(twilights);
308 
309  return rc; /* can only be 0 or -1 */
310 } /* 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:47
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.
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
unsigned char muse_utils_get_ifu(const cpl_propertylist *aHeaders)
Find out the IFU/channel from which this header originated.
Definition: muse_utils.c:99
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.
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_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.
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 tracing and wavelength calibration solu...
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_error_code muse_basicproc_shift_pixtable(muse_pixtable *aPt, cpl_array *aLines, double aHalfWidth, double aBinWidth)
Compute wavelength corrections for science data based on reference sky lines.
int muse_processing_save_image(muse_processing *aProcessing, int aIFU, muse_image *aImage, const char *aTag)
Save a computed MUSE image to disk.
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_table * muse_table_load(muse_processing *aProcessing, const char *aTag, unsigned char aIFU)
load a table according to its tag and IFU/channel number
Definition: muse_utils.c:721
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 * 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:158
void muse_pixtable_delete(muse_pixtable *aPixtable)
Deallocate memory associated to a pixel table object.
cpl_table * muse_basicproc_prepare_illum(muse_pixtable *aPT)
Apply an illum/attached flat-field to a pixel table.
muse_ins_mode muse_pfits_get_mode(const cpl_propertylist *aHeaders)
find out the observation mode
Definition: muse_pfits.c:1097
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.