MUSE Pipeline Reference Manual  1.0.2
recipes/muse_lsf.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) 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
20  * 02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 /*---------------------------------------------------------------------------*
28  * Includes *
29  *---------------------------------------------------------------------------*/
30 #include <muse.h>
31 
32 #include "muse_lsf_z.h"
33 
34 /*---------------------------------------------------------------------------*
35  * Functions code *
36  *---------------------------------------------------------------------------*/
37 static cpl_error_code
38 muse_lsf_save_lsf_params(cpl_propertylist *aHeader,
39  const muse_lsf_params **aSlicePars,
40  muse_processing *aProcessing, int aIFU)
41 {
42  cpl_error_code r = CPL_ERROR_NONE;
43  if (aSlicePars != NULL) {
44  cpl_frame *frame = muse_processing_new_frame(aProcessing, aIFU, aHeader,
45  MUSE_TAG_LSF_PROFILE,
46  CPL_FRAME_TYPE_TABLE);
47  if (frame != NULL) {
48  const char *filename = cpl_frame_get_filename(frame);
49  cpl_msg_info(__func__, "Saving LSF profile as %s", filename);
50  char *channel = cpl_sprintf("CHAN%02d", aIFU);
51  cpl_propertylist_update_string(aHeader, "EXTNAME", channel);
52  cpl_free(channel);
53  r = cpl_propertylist_save(aHeader, filename, CPL_IO_CREATE);
54  r = muse_lsf_params_save(aSlicePars, filename);
55  if (r == CPL_ERROR_NONE) {
56  cpl_frameset_insert(aProcessing->outframes, frame);
57  } else {
58  cpl_frame_delete(frame);
59  }
60  }
61  }
62  return r;
63 } /* muse_lsf_save_lsf_params() */
64 
65 static muse_lsf_params *
66 muse_lsf_compute_slice_lsf(cpl_table *aLines, muse_pixtable *aPixtable,
67  muse_lsf_params *aFirstguess, int aMaxIter,
68  const muse_sky_fit_params *aSliceParams)
69 {
70  uint32_t origin = (uint32_t)cpl_table_get_int(aPixtable->table,
71  MUSE_PIXTABLE_ORIGIN, 0, NULL);
72  unsigned short i_ifu = muse_pixtable_origin_get_ifu(origin),
73  i_slice = muse_pixtable_origin_get_slice(origin);
74  cpl_size nrows = muse_pixtable_get_nrow(aPixtable);
75 
76  cpl_msg_info(__func__, "processing slice %hu.%hu with %"CPL_SIZE_FORMAT" rows",
77  i_ifu, i_slice, nrows);
78 
79  cpl_propertylist *order = cpl_propertylist_new();
80  cpl_propertylist_append_bool(order, MUSE_PIXTABLE_LAMBDA, CPL_FALSE);
81  cpl_table_sort(aPixtable->table, order);
82  cpl_propertylist_delete(order);
83 
84  cpl_array *lambda = NULL;
85  if (cpl_table_get_column_type(aPixtable->table, MUSE_PIXTABLE_LAMBDA)
86  == CPL_TYPE_DOUBLE) {
87  lambda = muse_cpltable_extract_column(aPixtable->table,
88  MUSE_PIXTABLE_LAMBDA);
89  } else {
90  cpl_table_cast_column(aPixtable->table, MUSE_PIXTABLE_LAMBDA,
91  "lambda_double", CPL_TYPE_DOUBLE);
92  lambda = muse_cpltable_extract_column(aPixtable->table, "lambda_double");
93  }
94  cpl_array *data = muse_cpltable_extract_column(aPixtable->table,
95  MUSE_PIXTABLE_DATA);
96  cpl_array *stat = muse_cpltable_extract_column(aPixtable->table,
97  MUSE_PIXTABLE_STAT);
98 
99  muse_lsf_params *lsf = muse_lsf_params_fit(lambda, data, stat, aLines,
100  aFirstguess, aMaxIter,
101  aSliceParams);
102  lsf->slice = i_slice;
103  lsf->ifu = i_ifu;
104 
105  if (cpl_table_has_column(aPixtable->table, "lambda_double")) {
106  cpl_table_erase_column(aPixtable->table, "lambda_double");
107  }
108 
109  cpl_array_unwrap(lambda);
110  cpl_array_unwrap(data);
111  cpl_array_unwrap(stat);
112 
113  return lsf;
114 } /* muse_lsf_compute_slice_lsf() */
115 
116 static muse_lsf_params **
117 muse_lsf_compute_lsf(cpl_table *aLines, muse_pixtable *aPixtable,
118  muse_lsf_params **aFirstguess, int aMaxIter,
119  const muse_sky_fit_params *aSliceParams)
120 {
121  muse_pixtable **slice_pixtable = muse_pixtable_extracted_get_slices(aPixtable);
122  int n_slices = muse_pixtable_extracted_get_size(slice_pixtable);
123  int i_slice;
124 
125  muse_lsf_params **lsfParams = cpl_calloc(n_slices+1,
126  sizeof(muse_lsf_params *));
127  #pragma omp parallel for default(none) num_threads(2) /* as req. by Ralf */ \
128  shared(aFirstguess, aLines, aMaxIter, aSliceParams, lsfParams, \
129  n_slices, slice_pixtable)
130  for (i_slice = 0; i_slice < n_slices; i_slice++) {
131  uint32_t origin = (uint32_t)cpl_table_get_int(slice_pixtable[i_slice]->table,
132  MUSE_PIXTABLE_ORIGIN, 0, NULL);
133  unsigned short ifu = muse_pixtable_origin_get_ifu(origin),
134  slice = muse_pixtable_origin_get_slice(origin);
135  muse_lsf_params *firstguess_slice = muse_lsf_params_get(aFirstguess, ifu,
136  slice);
137  cpl_errorstate prestate = cpl_errorstate_get();
138  lsfParams[i_slice] = muse_lsf_compute_slice_lsf(aLines,
139  slice_pixtable[i_slice],
140  firstguess_slice, aMaxIter,
141  aSliceParams);
142  if (!cpl_errorstate_is_equal(prestate)) {
143  cpl_msg_error(__func__, "While processing slice %hu.%hu:", ifu, slice);
144  cpl_errorstate_dump(prestate, CPL_FALSE, NULL);
145  cpl_errorstate_set(prestate);
146  }
147  }
148  muse_pixtable_extracted_delete(slice_pixtable);
149  return lsfParams;
150 } /* muse_lsf_compute_lsf() */
151 
152 /*----------------------------------------------------------------------------*/
161 /*----------------------------------------------------------------------------*/
162 static cpl_error_code
163 muse_lsf_qc(cpl_propertylist *aHeader, const muse_lsf_params **aSlicePars)
164 {
165  cpl_ensure_code(aHeader && aSlicePars, CPL_ERROR_NULL_INPUT);
166 
167 #define LSF_QC_DLBDA 150. /* use steps of 150 Angstrom in wavelength, i.e. *
168  * 30 steps for nominal and 31 for extended mode */
169  /* determine range (and step size) of LSF to compute FWHM for */
170  double lbda1 = muse_pfits_get_mode(aHeader) == MUSE_MODE_WFM_NONAO_X
171  ? 4650. : 4800.,
172  lbda2 = kMuseNominalLambdaMax;
173  int nsteps = (lbda2 - lbda1) / LSF_QC_DLBDA;
174 
175  unsigned char ifu = muse_utils_get_ifu(aHeader);
176  unsigned short nslice = 1;
177  const muse_lsf_params **slice;
178  for (slice = aSlicePars; *slice; slice++, nslice++) {
179  cpl_array *afwhm = cpl_array_new(nsteps + 1, CPL_TYPE_DOUBLE);
180  int idx = 0;
181  double lambda;
182  for (lambda = lbda1; lambda <= lbda2; lambda += LSF_QC_DLBDA, idx++) {
183  double fwhm = muse_lsf_fwhm_lambda(*slice, lambda,
184  1000., 0.01, 1000., NULL);
185  cpl_array_set(afwhm, idx, fwhm);
186  } /* for lambda */
187  char keyword[KEYWORD_LENGTH];
188  snprintf(keyword, KEYWORD_LENGTH, "ESO QC LSF IFU%hhu SLICE%hu FWHM MEAN",
189  ifu, nslice);
190  cpl_propertylist_append_float(aHeader, keyword, cpl_array_get_mean(afwhm));
191  snprintf(keyword, KEYWORD_LENGTH, "ESO QC LSF IFU%hhu SLICE%hu FWHM STDEV",
192  ifu, nslice);
193  cpl_propertylist_append_float(aHeader, keyword, cpl_array_get_stdev(afwhm));
194  snprintf(keyword, KEYWORD_LENGTH, "ESO QC LSF IFU%hhu SLICE%hu FWHM MIN",
195  ifu, nslice);
196  cpl_propertylist_append_float(aHeader, keyword, cpl_array_get_min(afwhm));
197  snprintf(keyword, KEYWORD_LENGTH, "ESO QC LSF IFU%hhu SLICE%hu FWHM MAX",
198  ifu, nslice);
199  cpl_propertylist_append_float(aHeader, keyword, cpl_array_get_max(afwhm));
200  cpl_array_delete(afwhm);
201  } /* for slice */
202  return CPL_ERROR_NONE;
203 } /* muse_lsf_qc_lsf() */
204 
205 /*----------------------------------------------------------------------------*/
212 /*----------------------------------------------------------------------------*/
213 int
214 muse_lsf_compute(muse_processing *aProcessing,
215  muse_lsf_params_t *aParams)
216 {
217  /* load necessary calibrations first */
218  cpl_table *tracetable = muse_table_load(aProcessing, MUSE_TAG_TRACE_TABLE,
219  aParams->nifu);
220  cpl_table *wavecaltable = muse_table_load(aProcessing, MUSE_TAG_WAVECAL_TABLE,
221  aParams->nifu);
222  /* line catalog is needed in any case */
223  cpl_table *linelist = muse_table_load(aProcessing, MUSE_TAG_LINE_CATALOG, 0);
224  cpl_propertylist *linehead = muse_propertylist_load(aProcessing,
225  MUSE_TAG_LINE_CATALOG);
226  int rc = muse_wave_lines_check(linelist, linehead) ? 0 : -1;
227  cpl_propertylist_delete(linehead);
228  if (!tracetable || !wavecaltable || !linelist || rc) {
229  cpl_table_delete(tracetable);
230  cpl_table_delete(wavecaltable);
231  cpl_table_delete(linelist);
232  return rc;
233  }
234 
236  "muse.muse_lsf");
238  aParams->nifu,
239  bpars, NULL);
241  cpl_ensure(images, cpl_error_get_code(), -1);
242 
243  /* do the final (sum!) image combination */
244  char *pname = cpl_sprintf("muse.%s.combine", aProcessing->name);
245  cpl_parameter *param = cpl_parameterlist_find(aProcessing->parameters, pname);
246  char *porig = cpl_strdup(cpl_parameter_get_string(param));
247  cpl_parameter_set_string(param, "sum");
248  cpl_free(pname);
249  muse_combinepar *cpars = muse_combinepar_new(aProcessing->parameters,
250  "muse.muse_lsf");
251  cpl_parameter_set_string(param, porig);
252  cpl_free(porig);
253  muse_image *masterimage = muse_combine_images(cpars, images);
254  muse_combinepar_delete(cpars);
255 
256 
257  /* for the LSF determination, we need a pixel table */
258  muse_pixtable *pixtable = muse_pixtable_create(masterimage, tracetable,
259  wavecaltable, NULL);
260  if (!pixtable) {
261  cpl_error_set_message(__func__, CPL_ERROR_ILLEGAL_INPUT, "pixel table "
262  "creation failed!");
263  muse_image_delete(masterimage);
264  muse_imagelist_delete(images);
265  cpl_table_delete(wavecaltable);
266  cpl_table_delete(tracetable);
267  cpl_table_delete(linelist);
268  return -1;
269  } /* if no pixel table */
270 
271  if (aParams->save_subtracted) {
272  /* Duplicate to have access to original data after fit for residual calc */
273  cpl_table_duplicate_column(pixtable->table, "orig_data", pixtable->table,
274  "data");
275  }
276 
277  /* erase unused (flux == 0.) and non-isolated *
278  * (quality < line_quality, default 3) lines */
279  cpl_table_unselect_all(linelist);
280  cpl_table_or_selected_int(linelist, MUSE_LINE_CATALOG_QUALITY,
281  CPL_LESS_THAN, aParams->line_quality);
282  cpl_table_or_selected_float(linelist, "flux", CPL_NOT_GREATER_THAN, 0.);
283  cpl_table_erase_selected(linelist);
284  /* sort list by decreasing flux and cut it to the 40 brightest lines */
285  cpl_propertylist *flux_sort = cpl_propertylist_new();
286  cpl_propertylist_append_bool(flux_sort, "flux", CPL_TRUE);
287  cpl_table_sort(linelist, flux_sort);
288  cpl_propertylist_delete(flux_sort);
289  cpl_size nrows = 40;
290  if (nrows < cpl_table_get_nrow(linelist)) {
291  cpl_table_erase_window(linelist, nrows, cpl_table_get_nrow(linelist));
292  }
293 
294  muse_sky_fit_params *slice_fit_params = muse_sky_fit_params_new
295  (
296  0, // aParams->slice_fit_offset,
297  0, // aParams->slice_fit_refraction,
298  0, // sensitivity is not used here
299  1, // aParams->slice_fit_slit_width,
300  1, // aParams->slice_fit_bin_width,
301  3, // aParams->slice_fit_lsf_width + 1,
302  1, // aParams->slice_fit_h3 + 1,
303  2, // aParams->slice_fit_h4 + 1,
304  1, // aParams->slice_fit_h5 + 1,
305  2 // aParams->slice_fit_h6 + 1
306  );
307  muse_lsf_params **firstguess = muse_processing_lsf_params_load(aProcessing,
308  aParams->nifu);
309  muse_lsf_params **lsf = muse_lsf_compute_lsf(linelist, pixtable, firstguess,
310  40, slice_fit_params);
311  muse_lsf_params_delete(firstguess);
312  muse_sky_fit_params_delete(slice_fit_params);
313 
314  cpl_propertylist *header = cpl_propertylist_duplicate(pixtable->header);
315  cpl_propertylist_erase_regexp(header, MUSE_HDR_PT_REGEXP"|ESO QC|ESO DRS MUSE",
316  0);
317  muse_lsf_qc(header, (const muse_lsf_params **)lsf);
318  muse_lsf_save_lsf_params(header, (const muse_lsf_params **)lsf,
319  aProcessing, aParams->nifu);
320  cpl_propertylist_delete(header);
322 
323  if (aParams->save_subtracted) {
324  muse_processing_save_table(aProcessing, aParams->nifu, pixtable, NULL,
325  "PIXTABLE_SUBTRACTED", MUSE_TABLE_TYPE_PIXTABLE);
326  }
327 
328  /* clean up */
329  muse_pixtable_delete(pixtable);
330  muse_image_delete(masterimage);
331  muse_imagelist_delete(images);
332  cpl_table_delete(tracetable);
333  cpl_table_delete(wavecaltable);
334  cpl_table_delete(linelist);
335 
336  return rc; /* can only be 0 or -1 */
337 } /* muse_lsf_compute() */
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
void muse_pixtable_extracted_delete(muse_pixtable **aPixtables)
Delete a pixel table array.
unsigned short muse_pixtable_origin_get_slice(uint32_t aOrigin)
Get the slice number from the encoded 32bit origin number.
cpl_size muse_pixtable_extracted_get_size(muse_pixtable **aPixtables)
Get the size of an array of extracted pixel tables.
const char * name
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_imagelist * muse_basicproc_combine_images_lampwise(muse_processing *aProcessing, unsigned char aIFU, muse_basicproc_params *aBPars, cpl_frameset ***aLabeledFrames)
Combine several images into a lampwise image list.
muse_pixtable ** muse_pixtable_extracted_get_slices(muse_pixtable *aPixtable)
Extract one pixel table per IFU and slice.
cpl_size muse_pixtable_get_nrow(const muse_pixtable *aPixtable)
get the number of rows within the pixel table
cpl_error_code muse_lsf_params_save(const muse_lsf_params **aLsf, const char *aFile)
Save slice LSF parameters to the extension "slice" on disk.
muse_lsf_params * muse_lsf_params_fit(const cpl_array *aLambda, cpl_array *aData, const cpl_array *aStat, const cpl_table *aLines, muse_lsf_params *aFirstGuess, int aMaxIter, const muse_sky_fit_params *aFitParams)
Fit all entries of one slice.
Definition: muse_lsf_fit.c:131
muse_lsf_params * muse_lsf_params_get(muse_lsf_params **aParams, int aIFU, int aSlice)
Get the slice LSF parameters for one slice.
int nifu
IFU to handle. If set to 0, all IFUs are processed serially. If set to -1, all IFUs are processed in ...
Definition: muse_lsf_z.h:50
void muse_imagelist_delete(muse_imagelist *aList)
Free the memory of the MUSE image list.
Structure to define which slice parameters are fit.
Definition: muse_sky.h:84
muse_basicproc_params * muse_basicproc_params_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new structure of basic processing parameters.
muse_image * muse_combine_images(muse_combinepar *aCPars, muse_imagelist *aImages)
Combine several images into one.
Definition: muse_combine.c:741
void muse_sky_fit_params_delete(muse_sky_fit_params *)
Delete the fit parameter structure.
Structure definition of MUSE three extension FITS file.
Definition: muse_image.h:40
cpl_table * table
The pixel table.
void muse_basicproc_params_delete(muse_basicproc_params *aBPars)
Free a structure of basic processing parameters.
int save_subtracted
Save the pixel table after the LSF subtraction.
Definition: muse_lsf_z.h:70
void muse_combinepar_delete(muse_combinepar *aCPars)
Clear the combination parameters.
Definition: muse_combine.c:715
double muse_lsf_fwhm_lambda(const muse_lsf_params *aDP, double aLambda, double aFlux, double aSampling, unsigned int aLength, FILE *aOutstream)
Measure the FWHM of an LSF at a given wavelength.
Structure definition of MUSE pixel table.
cpl_frame * muse_processing_new_frame(muse_processing *aProcessing, int aIFU, cpl_propertylist *aHeader, const char *aTag, cpl_frame_type aType)
Create a new frame for a result file.
cpl_array * muse_cpltable_extract_column(cpl_table *aTable, const char *aColumn)
Create an array from a section of a column.
Structure to hold the parameters of the muse_lsf recipe.
Definition: muse_lsf_z.h:48
cpl_frameset * outframes
int line_quality
Minimal quality flag in line catalog for selection.
Definition: muse_lsf_z.h:73
muse_combinepar * muse_combinepar_new(cpl_parameterlist *aParameters, const char *aPrefix)
Create a new set of combination parameters.
Definition: muse_combine.c:672
muse_sky_fit_params * muse_sky_fit_params_new(cpl_boolean, cpl_boolean, cpl_size, cpl_size, cpl_size, cpl_size, cpl_size, cpl_size, cpl_size, cpl_size)
Create a new fit parameter structure.
muse_pixtable * muse_pixtable_create(muse_image *aImage, cpl_table *aTrace, cpl_table *aWave, cpl_table *aGeoTable)
Create the pixel table for one CCD.
unsigned short muse_pixtable_origin_get_ifu(uint32_t aOrigin)
Get the IFU number from the encoded 32bit origin number.
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.
muse_lsf_params ** muse_processing_lsf_params_load(muse_processing *aProcessing, int aIFU)
Load slice LSF parameters.
cpl_propertylist * muse_propertylist_load(muse_processing *aProcessing, const char *aTag)
load a propertylist according to its tag
Definition: muse_utils.c:789
cpl_boolean muse_wave_lines_check(cpl_table *aTable, cpl_propertylist *aHeader)
Check that a LINE_CATALOG has the expected format.
Structure definition of detector (slice) parameters.
Definition: muse_lsf.h:50
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:1097
cpl_parameterlist * parameters
void muse_lsf_params_delete(muse_lsf_params **aParams)
Delete an allocated array of muse_lsf_params structure.
cpl_propertylist * header
The FITS header.