MUSE Pipeline Reference Manual  1.0.2
muse_sky_lsf_fit.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) 2008-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 #include <math.h>
27 #include <string.h>
28 
29 #include "muse_sky.h"
30 #include "muse_instrument.h"
31 #include "muse_lsf.h"
32 #include "muse_optimize.h"
33 
38 /*----------------------------------------------------------------------------*/
55 /*----------------------------------------------------------------------------*/
56 
58 muse_sky_fit_params_new(cpl_boolean aOffset, cpl_boolean aRefraction,
59  cpl_size aSensitivity, cpl_size aSlitWidth,
60  cpl_size aBinWidth, cpl_size aLSFWidth,
61  cpl_size aHermit3, cpl_size aHermit4,
62  cpl_size aHermit5, cpl_size aHermit6)
63 {
64  muse_sky_fit_params *params = cpl_malloc(sizeof(muse_sky_fit_params));
65  params->offset = aOffset;
66  params->refraction = aRefraction;
67  params->sensitivity = aSensitivity;
68  params->slit_width = aSlitWidth;
69  params->bin_width = aBinWidth;
70  params->lsf_width = aLSFWidth;
71  params->hermit[0] = aHermit3;
72  params->hermit[1] = aHermit4;
73  params->hermit[2] = aHermit5;
74  params->hermit[3] = aHermit6;
75 
76  params->n_param = params->offset + params->refraction + params->sensitivity +
77  params->slit_width + params->bin_width + params->lsf_width;
78  cpl_size i;
79  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
80  params->n_param += params->hermit[i];
81  }
82 
83  return params;
84 }
85 
86 /*----------------------------------------------------------------------------*/
91 /*----------------------------------------------------------------------------*/
92 void
94  cpl_free(params);
95 }
96 
97 /*----------------------------------------------------------------------------*/
108 /*----------------------------------------------------------------------------*/
111  const cpl_array *aPar,
112  const muse_sky_fit_params *aFitParams)
113 {
114  cpl_size offset = 0;
115  cpl_size j;
116 
117  cpl_size max_hermit = 0;
118  for (j = 0; j < MAX_HERMIT_ORDER; j++) {
119  if (max_hermit < aFitParams->hermit[j]) {
120  max_hermit = aFitParams->hermit[j];
121  }
122  }
123  muse_lsf_params *lsf
124  = muse_lsf_params_new((aFitParams->sensitivity > 0)?
125  aFitParams->sensitivity:
126  cpl_array_get_size(aTemplate->sensitivity),
127  (aFitParams->lsf_width > 0)?
128  aFitParams->lsf_width:
129  cpl_array_get_size(aTemplate->lsf_width),
130  (max_hermit > 0)?
131  max_hermit:
132  cpl_array_get_size(aTemplate->hermit[0]));
133  cpl_array_set(lsf->sensitivity, 0, 1.0);
134 
135  if (aFitParams->offset > 0) {
136  lsf->offset = cpl_array_get(aPar, offset++, NULL);
137  } else {
138  lsf->offset = aTemplate->offset;
139  }
140 
141  if (aFitParams->refraction > 0) {
142  lsf->refraction = 1.0 + cpl_array_get(aPar, offset++, NULL);
143  } else {
144  lsf->refraction = aTemplate->refraction;
145  }
146 
147  cpl_size n = cpl_array_get_size(lsf->sensitivity);
148  if (aFitParams->sensitivity > 0) {
149  for (j = 0; j < n; j++) {
150  if (j < aFitParams->sensitivity) {
151  cpl_array_set(lsf->sensitivity, j,
152  cpl_array_get(aPar, offset++, NULL));
153  } else {
154  cpl_array_set(lsf->sensitivity, j, 0.0);
155  }
156  }
157  } else {
158  for (j = 0; j < n; j++) {
159  cpl_array_set(lsf->sensitivity, j,
160  cpl_array_get(aTemplate->sensitivity, j, NULL));
161  }
162  }
163 
164  if (aFitParams->slit_width > 0) {
165  lsf->slit_width = cpl_array_get(aPar, offset++, NULL);
166  } else {
167  lsf->slit_width = aTemplate->slit_width;
168  }
169 
170  if (aFitParams->bin_width > 0) {
171  lsf->bin_width = cpl_array_get(aPar, offset++, NULL);
172  } else {
173  lsf->bin_width = aTemplate->bin_width;
174  }
175 
176  n = cpl_array_get_size(lsf->lsf_width);
177  if (aFitParams->lsf_width > 0) {
178  for (j = 0; j < n; j++) {
179  if (j < aFitParams->lsf_width) {
180  cpl_array_set(lsf->lsf_width, j,
181  cpl_array_get(aPar, offset++, NULL));
182  } else {
183  cpl_array_set(lsf->lsf_width, j, 0.0);
184  }
185  }
186  } else {
187  for (j = 0; j < n; j++) {
188  cpl_array_set(lsf->lsf_width, j,
189  cpl_array_get(aTemplate->lsf_width, j, NULL));
190  }
191  }
192 
193  int i;
194  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
195  n = cpl_array_get_size(lsf->hermit[i]);
196  if (aFitParams->hermit[i] > 0) {
197  for (j = 0; j < n; j++) {
198  if (j < aFitParams->hermit[i]) {
199  cpl_array_set(lsf->hermit[i], j,
200  cpl_array_get(aPar, offset++, NULL));
201  } else {
202  cpl_array_set(lsf->hermit[i], j, 0.0);
203  }
204  }
205  } else {
206  for (j = 0; j < n; j++) {
207  cpl_array_set(lsf->hermit[i], j,
208  //cpl_array_get(aTemplate->hermit[i], j, NULL));
209  0.0);
210  }
211  }
212  }
213 
214  if (offset > cpl_array_get_size(aPar)) {
215  cpl_msg_error(__func__,
216  "inconsistent array: size %ld, read with %ld values",
217  (long)cpl_array_get_size(aPar), (long)offset);
219  return NULL;
220  }
221 
222  return lsf;
223 }
224 
225 /*----------------------------------------------------------------------------*/
233 /*----------------------------------------------------------------------------*/
234 cpl_array *
236  const muse_sky_fit_params *aFitParams) {
237  cpl_array *pars = cpl_array_new(aFitParams->n_param, CPL_TYPE_DOUBLE);
238  cpl_size offset = 0;
239 
240  // Relative refraction ratio - 1
241  if (aFitParams->offset > 0) {
242  cpl_array_set(pars, offset++, aLsf->offset);
243  }
244  if (aFitParams->refraction > 0) {
245  cpl_array_set(pars, offset++, aLsf->refraction -1);
246  }
247  // relative sensitivity
248  cpl_size j;
249  cpl_size n = cpl_array_get_size(aLsf->sensitivity);
250  for (j = 0; j < aFitParams->sensitivity; j++) {
251  if (j < n) {
252  cpl_msg_debug(__func__, "S[%li]=%f", (long)j,
253  cpl_array_get(aLsf->sensitivity, j, NULL));
254  cpl_array_set(pars, offset++,
255  cpl_array_get(aLsf->sensitivity, j, NULL));
256  } else {
257  cpl_array_set(pars, offset++, (j == 0)?1.0:0.0);
258  }
259  }
260 
261  if (aFitParams->slit_width > 0) {
262  cpl_array_set(pars, offset++, aLsf->slit_width);
263  }
264 
265  if (aFitParams->bin_width > 0) {
266  cpl_array_set(pars, offset++, aLsf->bin_width);
267  }
268 
269  // LSF width
270  n = cpl_array_get_size(aLsf->lsf_width);
271  for (j = 0; j < aFitParams->lsf_width; j++) {
272  if (j < n) {
273  cpl_array_set(pars, offset++,
274  cpl_array_get(aLsf->lsf_width, j, NULL));
275  } else {
276  cpl_array_set(pars, offset++, (j == 0)?1.0:0.0);
277  }
278  }
279 
280  // Hermitean coefficients
281  cpl_size i;
282  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
283  n = cpl_array_get_size(aLsf->hermit[i]);
284  for (j = 0; j < aFitParams->hermit[i]; j++) {
285  if (j < n) {
286  cpl_array_set(pars, offset++,
287  cpl_array_get(aLsf->hermit[i], j, NULL));
288  } else {
289  cpl_array_set(pars, offset++, 0.0);
290  }
291  }
292  }
293 
294  if (offset > cpl_array_get_size(pars)) {
295  cpl_msg_error(__func__,
296  "inconsistent array: size %ld, filled with %ld values",
297  (long)cpl_array_get_size(pars), (long)offset);
298  }
299  return pars;
300 }
301 
302 /*----------------------------------------------------------------------------*/
312 /*----------------------------------------------------------------------------*/
313 cpl_array *
315  cpl_array *pars = cpl_array_new(aFitParams->n_param, CPL_TYPE_DOUBLE);
316  cpl_size offset = 0;
317 
318  // Wavelength offset
319  if (aFitParams->offset > 0) {
320  cpl_array_set(pars, offset++, 0.0);
321  }
322 
323  // Relative refraction ratio - 1
324  if (aFitParams->refraction > 0) {
325  cpl_array_set(pars, offset++, 0.0);
326  }
327 
328  // Relative sensitivity
329  cpl_size j;
330  for (j = 0; j < aFitParams->sensitivity; j++) {
331  cpl_array_set(pars, offset++, (j == 0)?1.0:0.0);
332  }
333 
334  // Slit width
335  if (aFitParams->slit_width > 0) {
336  cpl_array_set(pars, offset++, kMuseSliceSlitWidthA);
337  }
338 
339  // Bin width
340  if (aFitParams->bin_width > 0) {
341  cpl_array_set(pars, offset++, kMuseSpectralSamplingA);
342  }
343 
344  // LSF width
345  for (j = 0; j < aFitParams->lsf_width; j++) {
346  cpl_array_set(pars, offset++, (j == 0)?0.5:0.0);
347  }
348 
349  // Hermitean coefficients
350  cpl_size i;
351  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
352  for (j = 0; j < aFitParams->hermit[i]; j++) {
353  cpl_array_set(pars, offset++, 0.0);
354  }
355  }
356 
357  if (offset > cpl_array_get_size(pars)) {
358  cpl_msg_error(__func__,
359  "inconsistent array: size %ld, filled with %ld values",
360  (long)cpl_array_get_size(pars), (long)offset);
361  }
362  return pars;
363 }
364 
365 typedef struct {
366  cpl_array *lambda;
367  cpl_array *values;
368  cpl_array *stat;
369  cpl_array *sensitivity;
370  cpl_array *continuum;
371  cpl_table *lines;
372  const muse_sky_fit_params *fit_params;
373  muse_lsf_params *firstGuess;
374 } muse_slice_fit_struct;
375 
376 /*----------------------------------------------------------------------------*/
385 /*----------------------------------------------------------------------------*/
386 static cpl_error_code
387 muse_sky_slice_eval(void *aData, cpl_array *aPar, cpl_array *aRetval) {
388 
389  muse_slice_fit_struct *data = aData;
390  cpl_size size = cpl_array_get_size(aRetval);
391 
392  muse_lsf_params *lsf
393  = muse_sky_slice_apply_lsf_parametrization(data->firstGuess,
394  aPar, data->fit_params);
395 
396  cpl_array *simulated = muse_sky_apply_lsf(data->lambda, data->lines, lsf);
397 
399 
400  cpl_array_subtract(simulated, data->values);
401  cpl_array_fill_window_double(aRetval, 0, size, 0.0);
402  memcpy(cpl_array_get_data_double(aRetval),
403  cpl_array_get_data_double_const(simulated),
404  size * sizeof(double));
405 
406  cpl_array_delete(simulated);
407 
408  return CPL_ERROR_NONE;
409 }
410 
411 /*----------------------------------------------------------------------------*/
425 /*----------------------------------------------------------------------------*/
427 muse_sky_lsf_params_fit(const cpl_array *aLambda, const cpl_array *aData,
428  const cpl_array *aStat, const cpl_table *aLines,
429  const cpl_array *aContinuum,
430  const cpl_array *aSensitivity,
431  muse_lsf_params *aFirstGuess, int aMaxIter,
432  const muse_sky_fit_params *aFitParams) {
433 
434  muse_lsf_params *firstGuess = (aFirstGuess != NULL)?
435  aFirstGuess: muse_lsf_params_new(1, 3, 1);
436  cpl_size reduction = 5; // we use only 20% of all pixels for the fit (speed).
437  cpl_size size = cpl_array_get_size(aLambda)/reduction;
438  cpl_array *lambda = cpl_array_new(size, CPL_TYPE_DOUBLE);
439  cpl_array *data = cpl_array_new(size, CPL_TYPE_DOUBLE);
440  cpl_array *continuum = NULL;
441  if (aContinuum != NULL) {
442  continuum = cpl_array_new(size, CPL_TYPE_DOUBLE);
443  }
444  cpl_array *sensitivity = NULL;
445  if (aSensitivity != NULL) {
446  sensitivity = cpl_array_new(size, CPL_TYPE_DOUBLE);
447  }
448  cpl_size i;
449  for (i = 0; i < size; i++) {
450  cpl_array_set(lambda, i, cpl_array_get(aLambda, reduction * i, NULL));
451  cpl_array_set(data, i, cpl_array_get(aData, reduction * i, NULL));
452  if (aContinuum != NULL) {
453  cpl_array_set(continuum, i, cpl_array_get(aContinuum, reduction * i, NULL));
454  }
455  if (aSensitivity != NULL) {
456  cpl_array_set(sensitivity, i,
457  cpl_array_get(aSensitivity, reduction * i, NULL));
458  }
459  }
460 
461  cpl_array *stat = cpl_array_new(size, CPL_TYPE_DOUBLE);
462  for (i = 0; i < size; i++) {
463  cpl_array_set(stat, i, sqrt(cpl_array_get(aStat, reduction * i, NULL)));
464  }
465 
466  cpl_table *lines = cpl_table_duplicate(aLines);
467  double maxflux = cpl_table_get_column_max(lines, "flux");
468  muse_sky_lines_cut(lines, 3e-3 * maxflux);
469 
470  muse_slice_fit_struct fit_data = {
471  lambda,
472  data,
473  stat,
474  sensitivity,
475  continuum,
476  lines,
477  aFitParams,
478  firstGuess,
479  };
480 
481  cpl_array *pars = (aFirstGuess == NULL)?
482  muse_sky_slice_lsf_firstguess(aFitParams):
483  muse_sky_slice_lsf_set_param(aFirstGuess, aFitParams);
484 
485  cpl_error_code r = CPL_ERROR_NONE;
486  int debug = getenv("MUSE_DEBUG_LSF_FIT")
487  && atoi(getenv("MUSE_DEBUG_LSF_FIT")) > 0;
488  if (aMaxIter > 0) {
490  -1, -1, -1, // default ftol, xtol, gtol
491  aMaxIter, debug
492  };
493 
494  r = muse_cpl_optimize_lvmq(&fit_data, pars, size,
495  muse_sky_slice_eval, &ctrl);
496  }
497 
498  if (r != CPL_ERROR_NONE) { // on error: reset to first guess
499  cpl_array_delete(pars);
500  pars = (aFirstGuess == NULL)?
501  muse_sky_slice_lsf_firstguess(aFitParams):
502  muse_sky_slice_lsf_set_param(aFirstGuess, aFitParams);
503  }
504 
505  muse_lsf_params *lsf
506  = muse_sky_slice_apply_lsf_parametrization(firstGuess, pars, aFitParams);
507 
508  cpl_array_delete(pars);
509  cpl_table_delete(fit_data.lines);
510  cpl_array_delete(fit_data.lambda);
511  cpl_array_delete(fit_data.sensitivity);
512  cpl_array_delete(fit_data.continuum);
513  cpl_array_delete(fit_data.values);
514  cpl_array_delete(fit_data.stat);
515  if (aFirstGuess == NULL) {
516  // remove temporary first guess template
517  muse_lsf_params_delete_one(firstGuess);
518  }
519 
520  return lsf;
521 }
522 
void muse_lsf_params_delete_one(muse_lsf_params *aParams)
Delete an allocated muse_lsf_params structure.
cpl_error_code muse_cpl_optimize_lvmq(void *aData, cpl_array *aPar, int aSize, muse_cpl_evaluate_func *aFunction, muse_cpl_optimize_control_t *aCtrl)
Minimize a function with the Levenberg-Marquardt algorithm.
cpl_array * muse_sky_slice_lsf_firstguess(const muse_sky_fit_params *aFitParams)
Return the parametrization values of the first guess.
Structure to define which slice parameters are fit.
Definition: muse_sky.h:84
void muse_sky_fit_params_delete(muse_sky_fit_params *params)
Delete the fit parameter structure.
double bin_width
Bin width.
Definition: muse_lsf.h:62
muse_lsf_params * muse_sky_lsf_params_fit(const cpl_array *aLambda, const cpl_array *aData, const cpl_array *aStat, const cpl_table *aLines, const cpl_array *aContinuum, const cpl_array *aSensitivity, muse_lsf_params *aFirstGuess, int aMaxIter, const muse_sky_fit_params *aFitParams)
Fit all entries of one slice.
cpl_array * hermit[MAX_HERMIT_ORDER]
coefficients for the damped gauss-hermitean parametrization
Definition: muse_lsf.h:66
muse_lsf_params * muse_lsf_params_new(cpl_size n_sensit, cpl_size n_lsf_width, cpl_size n_hermit)
Create a new lsf_params structure.
Definition: muse/muse_lsf.c:75
cpl_array * sensitivity
Relative detector sensitivity parametrization.
Definition: muse_lsf.h:58
double slit_width
Slit width.
Definition: muse_lsf.h:60
cpl_array * lsf_width
LSF width.
Definition: muse_lsf.h:64
Optimization control parameters.
Definition: muse_optimize.h:32
muse_lsf_params * muse_sky_slice_apply_lsf_parametrization(const muse_lsf_params *template, const cpl_array *aPar, const muse_sky_fit_params *aFitParams)
Convert a parametrization to LSF parameters.
muse_sky_fit_params * muse_sky_fit_params_new(cpl_boolean aOffset, cpl_boolean aRefraction, cpl_size aSensitivity, cpl_size aSlitWidth, cpl_size aBinWidth, cpl_size aLSFWidth, cpl_size aHermit3, cpl_size aHermit4, cpl_size aHermit5, cpl_size aHermit6)
Create a new fit parameter structure.
cpl_error_code muse_sky_lines_cut(cpl_table *, double)
Remove all lines below a certain flux limit.
cpl_array * muse_sky_apply_lsf(const cpl_array *, const cpl_table *, const muse_lsf_params *)
Apply the LSF parameters to a spectrum.
Structure definition of detector (slice) parameters.
Definition: muse_lsf.h:50
cpl_array * muse_sky_slice_lsf_set_param(muse_lsf_params *aLsf, const muse_sky_fit_params *aFitParams)
Return the parametrization values for the specific LSF params.