MUSE Pipeline Reference Manual  1.0.2
muse_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 cpl_array *
40 cpl_array *
42  const muse_sky_fit_params *aFitParams);
45  const cpl_array *aPar,
46  const muse_sky_fit_params *aFitParams);
47 
48 typedef struct {
49  cpl_array *lambda;
50  cpl_array *values;
51  cpl_array *stat;
52  cpl_array *linesLambda;
53  cpl_array *linesFlux;
54  const muse_sky_fit_params *fit_params;
55  muse_lsf_params *firstGuess;
56 } muse_lsf_fit_struct;
57 
58 /*----------------------------------------------------------------------------*/
67 /*----------------------------------------------------------------------------*/
68 static cpl_error_code
69 muse_lsf_slice_eval(void *aData, cpl_array *aPar, cpl_array *aRetval) {
70 
71  muse_lsf_fit_struct *data = aData;
72  cpl_size size = cpl_array_get_size(aRetval);
73 
74  muse_lsf_params *lsfParam
76  aPar, data->fit_params);
77  cpl_array *linesFlux = data->linesFlux;
78  if (data->linesFlux == NULL) {
79  linesFlux = cpl_array_extract(aPar, cpl_array_get_size(aPar)
80  - cpl_array_get_size(data->linesLambda),
81  cpl_array_get_size(data->linesLambda));
82  }
83 
84  cpl_array *simulated
85  = muse_lsf_spectrum_get_lines(data->lambda, data->linesLambda,
86  linesFlux, lsfParam);
87 
88  if (data->linesFlux == NULL) {
89  cpl_array_delete(linesFlux);
90  }
91 
93  cpl_array_subtract(simulated, data->values);
94  cpl_array_divide(simulated, data->stat);
95 
96  cpl_array_fill_window_double(aRetval, 0, size, 0.0);
97  memcpy(cpl_array_get_data_double(aRetval),
98  cpl_array_get_data_double_const(simulated),
99  size * sizeof(double));
100 
101  // replace invalid numbers by 0.0
102  cpl_size i;
103  double *d = cpl_array_get_data_double(aRetval);
104  for (i = 0; i < size; i++) {
105  if (isnan(d[i])) {
106  d[i] = 0.0;
107  }
108  }
109 
110  cpl_array_delete(simulated);
111  return CPL_ERROR_NONE;
112 }
113 
114 /*----------------------------------------------------------------------------*/
129 /*----------------------------------------------------------------------------*/
131 muse_lsf_params_fit(const cpl_array *aLambda, cpl_array *aData,
132  const cpl_array *aStat,
133  const cpl_table *aLines,
134  muse_lsf_params *aFirstGuess,
135  int aMaxIter,
136  const muse_sky_fit_params *aFitParams) {
137 
138  muse_lsf_params *firstGuess = (aFirstGuess != NULL)?
139  aFirstGuess: muse_lsf_params_new(1, 3, 1);
140 
141  int debug = getenv("MUSE_DEBUG_LSF_FIT")
142  && atoi(getenv("MUSE_DEBUG_LSF_FIT")) > 0;
144  -1, -1, -1, // default ftol, xtol, gtol
145  aMaxIter, debug
146  };
147 
148  //
149  // Create "lambda", "data", and "stat" fields from the pixel table.
150  // They do not contain all pixels, but a certain percentage of them.
151  //
152  cpl_size reduction = 2; // we use only 50% of all pixels for the fit (speed).
153  cpl_size size = cpl_array_get_size(aLambda)/reduction;
154  cpl_array *lambda = cpl_array_new(size, CPL_TYPE_DOUBLE);
155  cpl_array *data = cpl_array_new(size, CPL_TYPE_DOUBLE);
156  cpl_array *stat = cpl_array_new(size, CPL_TYPE_DOUBLE);
157 
158  cpl_size i;
159  for (i = 0; i < size; i++) {
160  cpl_array_set(lambda, i, cpl_array_get(aLambda, reduction * i, NULL));
161  cpl_array_set(data, i, cpl_array_get(aData, reduction * i, NULL));
162  cpl_array_set(stat, i, sqrt(cpl_array_get(aStat, reduction * i, NULL)));
163  }
164 
165  cpl_array *linesLambda = muse_cpltable_extract_column((cpl_table *)aLines,
166  "lambda");
167 
168  //
169  // First minimization step: Fit the lsf width and the fluxes of all lines
170  //
171  muse_sky_fit_params *slice_fit_params0 = muse_sky_fit_params_new
172  (
173  0, // aParams->slice_fit_offset,
174  0, // aParams->slice_fit_refraction,
175  0, // sensitivity is not used here
176  0, // aParams->slice_fit_slit_width,
177  0, // aParams->slice_fit_bin_width,
178  aFitParams->lsf_width, // aParams->slice_fit_lsf_width + 1,
179  0, // aParams->slice_fit_h3 + 1,
180  0, // aParams->slice_fit_h4 + 1,
181  0, // aParams->slice_fit_h5 + 1,
182  0 // aParams->slice_fit_h6 + 1
183  );
184 
185  muse_lsf_fit_struct fit_data = {
186  lambda,
187  data,
188  stat,
189  linesLambda,
190  NULL, // linesFlux
191  slice_fit_params0,
192  firstGuess,
193  };
194 
195  cpl_array *pars = (aFirstGuess == NULL)?
196  muse_sky_slice_lsf_firstguess(slice_fit_params0):
197  muse_sky_slice_lsf_set_param(aFirstGuess, slice_fit_params0);
198 
199  // Take the lines fluxes as they come out of the line list as first guess
200  cpl_array *lf = muse_cpltable_extract_column((cpl_table *)aLines, "flux");
201  cpl_array *linesFlux = cpl_array_cast(lf, CPL_TYPE_DOUBLE);
202  cpl_array_unwrap(lf);
203  cpl_array_insert(pars, linesFlux, cpl_array_get_size(pars));
204 
205  cpl_error_code r
206  = muse_cpl_optimize_lvmq(&fit_data, pars, size,
207  muse_lsf_slice_eval, &ctrl);
208 
209  if (r != CPL_ERROR_NONE) { // on error: reset to first guess
210  cpl_array_delete(pars);
211  pars = (aFirstGuess == NULL)?
212  muse_sky_slice_lsf_firstguess(aFitParams):
213  muse_sky_slice_lsf_set_param(aFirstGuess, aFitParams);
214  cpl_array_insert(pars, linesFlux, cpl_array_get_size(pars));
215  }
216 
217  //
218  // Second minimization step: Keep the line fluxes constant and fit
219  // all shape parameters
220  //
221  fit_data.fit_params = aFitParams;
222  cpl_array_delete(linesFlux);
223  linesFlux = cpl_array_extract(pars, cpl_array_get_size(pars)
224  - cpl_array_get_size(linesLambda),
225  cpl_array_get_size(linesLambda));
226  fit_data.linesFlux = linesFlux;
227  fit_data.firstGuess
229  pars, slice_fit_params0);
230  muse_sky_fit_params_delete(slice_fit_params0);
231  cpl_array_delete(pars);
232  pars = muse_sky_slice_lsf_set_param(fit_data.firstGuess, aFitParams);
233 
234  r = muse_cpl_optimize_lvmq(&fit_data, pars, size, muse_lsf_slice_eval, &ctrl);
235  if (r != CPL_ERROR_NONE) { // on error: reset to first guess
236  cpl_array_delete(pars);
237  pars = (aFirstGuess == NULL)?
238  muse_sky_slice_lsf_firstguess(aFitParams):
239  muse_sky_slice_lsf_set_param(aFirstGuess, aFitParams);
240  cpl_array_insert(pars, linesFlux, cpl_array_get_size(pars));
241  }
242 
243  muse_lsf_params *lsfParam
245  pars, aFitParams);
246  cpl_msg_debug(__func__, "Slit width: %f (%s), bin width: %f (%s)",
247  lsfParam->slit_width, aFitParams->slit_width?"fit":"fixed",
248  lsfParam->bin_width, aFitParams->bin_width?"fit":"fixed");
249 
250  cpl_array *simulated = muse_lsf_spectrum_get_lines(aLambda, linesLambda,
251  linesFlux, lsfParam);
252  cpl_array_delete(linesFlux);
253  cpl_array_subtract(aData, simulated);
254  cpl_array_delete(simulated);
255 
256  cpl_array_delete(pars);
257  cpl_array_unwrap(fit_data.linesLambda);
258  cpl_array_delete(fit_data.lambda);
259  cpl_array_delete(fit_data.values);
260  cpl_array_delete(fit_data.stat);
261  muse_lsf_params_delete_one(fit_data.firstGuess);
262  if (aFirstGuess == NULL) {
263  // remove temporary first guess template
264  muse_lsf_params_delete_one(firstGuess);
265  }
266 
267  return lsfParam;
268 }
269 
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.
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
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 *)
Delete the fit parameter structure.
cpl_array * muse_lsf_spectrum_get_lines(const cpl_array *aLambda, const cpl_array *aLinesLambda, const cpl_array *aLinesFlux, const muse_lsf_params *aLsf)
Set the lines spectrum from the lines table and the detector LSF.
double bin_width
Bin width.
Definition: muse_lsf.h:62
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
double slit_width
Slit width.
Definition: muse_lsf.h:60
cpl_array * muse_cpltable_extract_column(cpl_table *aTable, const char *aColumn)
Create an array from a section of a column.
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, cpl_boolean, cpl_size, cpl_size, cpl_size, cpl_size, cpl_size, cpl_size, cpl_size, cpl_size)
Create a new fit parameter structure.
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.