MUSE Pipeline Reference Manual  2.1.1
muse_lsf_params.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
20  * 02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #define STORE_SLIT_WIDTH
28 #define STORE_BIN_WIDTH
29 
30 /*----------------------------------------------------------------------------*
31  * Includes *
32  *----------------------------------------------------------------------------*/
33 #include <math.h>
34 #include <string.h>
35 #include <fenv.h>
36 
37 #include <cpl.h>
38 
39 #include "muse_lsf_params.h"
40 #include "muse_resampling.h"
41 #include "muse_pfits.h"
42 #include "muse_quality.h"
43 #include "muse_instrument.h"
44 #include "muse_optimize.h"
45 #include "muse_tracing.h"
46 #include "muse_utils.h"
47 
51 /*----------------------------------------------------------------------------*/
61 /*----------------------------------------------------------------------------*/
63 muse_lsf_params_new(cpl_size n_sensit, cpl_size n_lsf_width, cpl_size n_hermit)
64 {
65  muse_lsf_params *res = cpl_calloc(1, sizeof(muse_lsf_params));
66  res->refraction = 1.0;
67  res->offset = 0.0;
68  res->slit_width = kMuseSliceSlitWidthA;
69  res->bin_width = kMuseSpectralSamplingA;
70  res->lambda_ref = 7000;
71  int i;
72  if (n_hermit > 0) {
73  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
74  res->hermit[i] = cpl_array_new(n_hermit, CPL_TYPE_DOUBLE);
75  cpl_array_fill_window_double(res->hermit[i], 0, n_hermit, 0.0);
76  }
77  }
78  res->lsf_width = cpl_array_new(n_lsf_width, CPL_TYPE_DOUBLE);
79  if (n_lsf_width > 0) {
80  cpl_array_fill_window_double(res->lsf_width, 0, n_lsf_width, 0.0);
81  cpl_array_set_double(res->lsf_width, 0, 1.0);
82  }
83  res->sensitivity = cpl_array_new(n_sensit, CPL_TYPE_DOUBLE);
84  if (n_sensit > 0) {
85  cpl_array_fill_window_double(res->sensitivity, 0, n_sensit, 0.0);
86  cpl_array_set_double(res->sensitivity, 0, 1.0);
87  }
88  return res;
89 }
90 
91 /*----------------------------------------------------------------------------*/
97 /*----------------------------------------------------------------------------*/
98 cpl_size
100  if (aParams == NULL) {
101  return 0;
102  }
103  cpl_size i;
104  for (i = 0; *aParams != NULL; i++) {
105  aParams++;
106  }
107  return i;
108 }
109 
110 /*----------------------------------------------------------------------------*/
115 /*----------------------------------------------------------------------------*/
116 void
118  if (aParams != NULL) {
119  cpl_array_delete(aParams->sensitivity);
120  int i;
121  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
122  cpl_array_delete(aParams->hermit[i]);
123  }
124  cpl_array_delete(aParams->lsf_width);
125  cpl_free(aParams);
126  }
127 }
128 
129 /*----------------------------------------------------------------------------*/
134 /*----------------------------------------------------------------------------*/
135 void
137  if (aParams != NULL) {
138  muse_lsf_params **det;
139  for (det = aParams; *det != NULL; det++) {
141  }
142  cpl_free(aParams);
143  }
144 }
145 
146 /*----------------------------------------------------------------------------*/
160 /*----------------------------------------------------------------------------*/
162  {"ifu", CPL_TYPE_INT, NULL, "%i", "IFU number", CPL_TRUE},
163  {"slice", CPL_TYPE_INT, NULL, "%i", "slice number within the IFU", CPL_TRUE},
164  {"sensitivity", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, NULL, "%e",
165  "detector sensitivity, relative to the reference", CPL_TRUE},
166  {"offset", CPL_TYPE_DOUBLE, NULL, "%e", "wavelength calibration offset", CPL_TRUE},
167  {"refraction", CPL_TYPE_DOUBLE, NULL, "%e", "relative refraction index", CPL_TRUE},
168 #ifdef STORE_SLIT_WIDTH
169  {"slit_width", CPL_TYPE_DOUBLE, "Angstrom", "%e", "slit width", CPL_TRUE},
170 #endif
171 #ifdef STORE_BIN_WIDTH
172  {"bin_width", CPL_TYPE_DOUBLE, "Angstrom", "%e", "bin width", CPL_TRUE},
173 #endif
174  {"lsf_width", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, "Angstrom", "%e",
175  " LSF gauss-hermitean width", CPL_TRUE},
176  {"hermit3", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, NULL, "%e",
177  "3rd order hermitean coefficient", CPL_TRUE},
178  {"hermit4", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, NULL, "%e",
179  "4th order hermitean coefficient", CPL_TRUE},
180  {"hermit5", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, NULL, "%e",
181  "5th order hermitean coefficient", CPL_TRUE},
182  {"hermit6", CPL_TYPE_DOUBLE | CPL_TYPE_POINTER, NULL, "%e",
183  "6th order hermitean coefficient", CPL_TRUE},
184  { NULL, 0, NULL, NULL, NULL, CPL_FALSE }
185 };
186 
187 /*----------------------------------------------------------------------------*/
201 /*----------------------------------------------------------------------------*/
202 cpl_error_code
203 muse_lsf_params_save(const muse_lsf_params **aLsfParams, const char *aFile) {
204  cpl_ensure_code(aLsfParams != NULL, CPL_ERROR_NULL_INPUT);
205  cpl_ensure_code(*aLsfParams != NULL, CPL_ERROR_DATA_NOT_FOUND);
206  cpl_ensure_code(aFile != NULL, CPL_ERROR_NULL_INPUT);
207 
208  cpl_size nrows = 0;
209  const muse_lsf_params **det;
210  cpl_size sensitivity_order = 1;
211  cpl_size lsf_order = 1;
212  cpl_size hermit_order[MAX_HERMIT_ORDER];
213  int i;
214  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
215  hermit_order[i] = 1;
216  }
217  for (det = aLsfParams; *det != NULL; det++, nrows++) {
218  sensitivity_order = fmax(sensitivity_order,
219  cpl_array_get_size((*det)->sensitivity));
220  lsf_order = fmax(lsf_order, cpl_array_get_size((*det)->lsf_width));
221  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
222  hermit_order[i] = fmax(hermit_order[i],
223  cpl_array_get_size((*det)->hermit[i]));
224  }
225  }
226 
227  cpl_table *slice_param = cpl_table_new(nrows);
228  cpl_table_new_column(slice_param, "ifu", CPL_TYPE_INT);
229  cpl_table_new_column(slice_param, "slice", CPL_TYPE_INT);
230  cpl_table_new_column_array(slice_param, "sensitivity",
231  cpl_array_get_type(aLsfParams[0]->sensitivity),
232  sensitivity_order);
233  cpl_table_new_column(slice_param, "offset", CPL_TYPE_DOUBLE);
234  cpl_table_new_column(slice_param, "refraction", CPL_TYPE_DOUBLE);
235 #ifdef STORE_SLIT_WIDTH
236  cpl_table_new_column(slice_param, "slit_width", CPL_TYPE_DOUBLE);
237 #endif
238 #ifdef STORE_BIN_WIDTH
239  cpl_table_new_column(slice_param, "bin_width", CPL_TYPE_DOUBLE);
240 #endif
241  cpl_table_new_column_array(slice_param, "lsf_width",
242  cpl_array_get_type(aLsfParams[0]->lsf_width),
243  lsf_order);
244  cpl_table_new_column_array(slice_param, "hermit3",
245  cpl_array_get_type(aLsfParams[0]->hermit[0]),
246  hermit_order[0]);
247  cpl_table_new_column_array(slice_param, "hermit4",
248  cpl_array_get_type(aLsfParams[0]->hermit[1]),
249  hermit_order[1]);
250  cpl_table_new_column_array(slice_param, "hermit5",
251  cpl_array_get_type(aLsfParams[0]->hermit[2]),
252  hermit_order[2]);
253  cpl_table_new_column_array(slice_param, "hermit6",
254  cpl_array_get_type(aLsfParams[0]->hermit[3]),
255  hermit_order[3]);
256 
257  cpl_size iRow = 0;
258  for (det = aLsfParams; *det != NULL; det++, iRow++) {
259  cpl_table_set(slice_param, "ifu", iRow, (*det)->ifu);
260  cpl_table_set(slice_param, "slice", iRow, (*det)->slice);
261  cpl_table_set_array(slice_param, "sensitivity", iRow, (*det)->sensitivity);
262  cpl_table_set(slice_param, "offset", iRow, (*det)->offset);
263  cpl_table_set(slice_param, "refraction", iRow, (*det)->refraction);
264 #ifdef STORE_SLIT_WIDTH
265  cpl_table_set(slice_param, "slit_width", iRow, (*det)->slit_width);
266 #endif
267 #ifdef STORE_BIN_WIDTH
268  cpl_table_set(slice_param, "bin_width", iRow, (*det)->bin_width);
269 #endif
270  cpl_table_set_array(slice_param, "lsf_width", iRow, (*det)->lsf_width);
271  cpl_table_set_array(slice_param, "hermit3", iRow, (*det)->hermit[0]);
272  cpl_table_set_array(slice_param, "hermit4", iRow, (*det)->hermit[1]);
273  cpl_table_set_array(slice_param, "hermit5", iRow, (*det)->hermit[2]);
274  cpl_table_set_array(slice_param, "hermit6", iRow, (*det)->hermit[3]);
275  }
276 
277  int r = muse_cpltable_append_file(slice_param, aFile, "SLICE_PARAM",
278  muse_lsfparams_def);
279  cpl_table_delete(slice_param);
280  return r;
281 }
282 
283 /*----------------------------------------------------------------------------*/
295 /*----------------------------------------------------------------------------*/
297 muse_lsf_params_load(const char *aFile, muse_lsf_params **aParams, int aIFU)
298 {
299  cpl_errorstate prestate = cpl_errorstate_get();
300  cpl_table *lsfTable = muse_cpltable_load(aFile, "SLICE_PARAM",
301  muse_lsfparams_def);
302  if (!lsfTable) {
303  /* try to load from channel extensions */
304  char *extname = cpl_sprintf("CHAN%02d.SLICE_PARAM", aIFU);
305  lsfTable = muse_cpltable_load(aFile, extname, muse_lsfparams_def);
306  cpl_free(extname);
307  if (!lsfTable) {
308  if (aParams == NULL) {
309  /* this may be not a LSF table but a LSF cube? reset the error.. */
310  cpl_errorstate_set(prestate);
311  return NULL;
312  }
313  /* now display both the older and the new error messages */
314  cpl_error_set_message(__func__, cpl_error_get_code(), "Loading LSF data from "
315  "\"%s[SLICE_PARAMS]\" and \"%s[CHAH%02d.SLICE_PARAMS]\" "
316  "failed", aFile, aFile, aIFU);
317  return aParams;
318  } /* if 2nd load failure */
319  } /* if 1st load failure */
320 
321  cpl_size n_rows = cpl_table_get_nrow(lsfTable);
322  cpl_size n_rows_old = muse_lsf_params_get_size(aParams);
323  muse_lsf_params **lsfParams
324  = cpl_realloc(aParams, (n_rows + n_rows_old + 1) * sizeof(muse_lsf_params *));
325  lsfParams[n_rows + n_rows_old] = NULL;
326  cpl_size i_row_new = n_rows_old;
327  cpl_size i_row;
328  for (i_row = 0; i_row < n_rows; i_row++) {
329  int ifu = cpl_table_get(lsfTable, "ifu", i_row, NULL);
330  lsfParams[i_row + n_rows_old] = NULL;
331  if ((aIFU <= 0) || (ifu == aIFU)) {
332  muse_lsf_params *det = muse_lsf_params_new(0,0,0);
333  lsfParams[i_row_new] = det;
334  i_row_new++;
335  det->ifu = ifu;
336  det->slice = cpl_table_get(lsfTable, "slice", i_row, NULL);
337  cpl_array_delete(det->sensitivity);
338  det->sensitivity
339  = muse_cpltable_get_array_copy(lsfTable, "sensitivity",i_row);
340  det->offset = cpl_table_get(lsfTable, "offset", i_row, NULL);
341  det->refraction = cpl_table_get(lsfTable, "refraction", i_row, NULL);
342 #ifdef STORE_SLIT_WIDTH
343  det->slit_width = cpl_table_get(lsfTable, "slit_width", i_row, NULL);
344 #endif
345 #ifdef STORE_BIN_WIDTH
346  det->bin_width = cpl_table_get(lsfTable, "bin_width", i_row, NULL);
347 #endif
348  cpl_array_delete(det->lsf_width);
349  det->lsf_width
350  = muse_cpltable_get_array_copy(lsfTable, "lsf_width", i_row);
351  cpl_array_delete(det->hermit[0]);
352  det->hermit[0]
353  = muse_cpltable_get_array_copy(lsfTable, "hermit3", i_row);
354  cpl_array_delete(det->hermit[1]);
355  det->hermit[1]
356  = muse_cpltable_get_array_copy(lsfTable, "hermit4", i_row);
357  cpl_array_delete(det->hermit[2]);
358  det->hermit[2]
359  = muse_cpltable_get_array_copy(lsfTable, "hermit5", i_row);
360  cpl_array_delete(det->hermit[3]);
361  det->hermit[3]
362  = muse_cpltable_get_array_copy(lsfTable, "hermit6", i_row);
363  }
364  }
365  cpl_table_delete(lsfTable);
366 
367  return lsfParams;
368 }
369 
370 /*----------------------------------------------------------------------------*/
385 /*----------------------------------------------------------------------------*/
388 {
389  cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
390  cpl_frameset *frames = muse_frameset_find(aProcessing->inframes,
391  MUSE_TAG_LSF_PROFILE, aIFU,
392  CPL_FALSE);
393  if (frames == NULL) {
394  return NULL;
395  }
396  /* try standard format first: one file per IFU */
397  cpl_errorstate state = cpl_errorstate_get();
398  cpl_size iframe, nframes = cpl_frameset_get_size(frames);
399  muse_lsf_params **lsfParams = NULL;
400  for (iframe = 0; iframe < nframes; iframe++) {
401  cpl_frame *frame = cpl_frameset_get_position(frames, iframe);
402  lsfParams = muse_lsf_params_load(cpl_frame_get_filename(frame),
403  lsfParams, aIFU);
404  if (lsfParams) {
405  cpl_msg_info(__func__, "Loaded slice LSF params from \"%s\"",
406  cpl_frame_get_filename(frame));
407  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_CALIB, 1);
408  }
409  } /* for iframe (all frames) */
410  char *errmsg = NULL;
411  if (!cpl_errorstate_is_equal(state)) {
412  errmsg = cpl_strdup(cpl_error_get_message());
413  }
414  cpl_errorstate_set(state); /* hide error(s) */
415 
416  /* extra loop to support the merged format */
417  if (!lsfParams && aIFU == 0 && nframes == 1) {
418  cpl_msg_debug(__func__, "No LSF parameters loaded yet, trying merged table "
419  "format.");
420 
421  cpl_frame *frame = cpl_frameset_get_position(frames, 0);
422  const char *fname = cpl_frame_get_filename(frame);
423  state = cpl_errorstate_get();
424  unsigned char ifu;
425  for (ifu = 1; ifu <= kMuseNumIFUs; ifu++) {
426  lsfParams = muse_lsf_params_load(fname, lsfParams, ifu);
427  } /* for ifu */
428  cpl_errorstate_set(state); /* ignore errors */
429  if (lsfParams) {
430  cpl_msg_info(__func__, "Loaded (merged) slice LSF params from \"%s\"", fname);
431  muse_processing_append_used(aProcessing, frame, CPL_FRAME_GROUP_CALIB, 1);
432  }
433  } /* if */
434  cpl_frameset_delete(frames);
435 
436  /* now possible output error message */
437  if (errmsg) {
438  cpl_msg_debug(__func__, "Loading %ss from input frameset did not succeed: "
439  "%s", MUSE_TAG_LSF_PROFILE, errmsg);
440  } /* if still no lsfParams */
441  cpl_free(errmsg);
442  return lsfParams;
443 } /* muse_processing_lsf_params_load() */
444 
445 /*----------------------------------------------------------------------------*/
450 /*----------------------------------------------------------------------------*/
452 muse_lsf_params_get(muse_lsf_params **aParams, int aIFU, int aSlice) {
453  if (aParams == NULL) {
454  return NULL;
455  }
456  int i_det;
457  for (i_det = 0; aParams[i_det] != NULL; i_det++) {
458  if (aParams[i_det]->ifu == aIFU && aParams[i_det]->slice == aSlice) {
459  return aParams[i_det];
460  }
461  }
462  return NULL;
463 }
464 
465 /*----------------------------------------------------------------------------*/
482 /*----------------------------------------------------------------------------*/
483 static cpl_array *
484 muse_lsf_G(cpl_array *aX, cpl_array *aCoeffs) {
485  cpl_ensure(aX != NULL, CPL_ERROR_NULL_INPUT, NULL);
486  cpl_ensure(aCoeffs != NULL, CPL_ERROR_NULL_INPUT, NULL);
487 
488  cpl_array *y = cpl_array_duplicate(aX);
489  cpl_array_multiply(y, y);
490  cpl_array_multiply_scalar(y, -1); // y = - x**2
491 
492  cpl_array *y2 = cpl_array_duplicate(y);
493  muse_cplarray_exp(y2);
494  cpl_array_multiply_scalar(y2, 1.0/60); // y2 = exp(-x**2)/60
495 
496  cpl_array_multiply_scalar(y, 0.5);
497  muse_cplarray_exp(y); // y = exp(-0.5 * x**2)
498 
499  cpl_array *R = cpl_array_duplicate(aX);
500  muse_cplarray_poly1d(R, aCoeffs);
501  cpl_array_multiply(y2, R); // y2 = R * exp(-x**2)/60
502  cpl_array_delete(R);
503  cpl_array_add(y, y2);
504 
505  cpl_array_copy_data_double(y2, cpl_array_get_data_double(aX));
506  cpl_array_multiply_scalar(y2, sqrt(0.5));
507  muse_cplarray_erf(y2);
508  cpl_array_multiply_scalar(y2, sqrt(CPL_MATH_PI / 2));
509  cpl_array_multiply(y2, aX); // y3 = x * sqrt(Pi/2) * erf(x/sqrt(2))
510  cpl_array_add(y, y2);
511  cpl_array_delete(y2);
512 
513  return y;
514 }
515 
516 /*----------------------------------------------------------------------------*/
537 /*----------------------------------------------------------------------------*/
538 cpl_error_code
540  cpl_array *aVal, double aLambda)
541 {
542 
543  cpl_ensure_code(aVal != NULL, CPL_ERROR_NULL_INPUT);
544  cpl_ensure_code(aLsfParams != NULL, CPL_ERROR_NULL_INPUT);
545 
546  double slit_width = aLsfParams->slit_width;
547  double bin_width = aLsfParams->bin_width;
548  double width = muse_cplarray_poly1d_double(aLambda - aLsfParams->lambda_ref,
549  aLsfParams->lsf_width);
550  double h3 = muse_cplarray_poly1d_double(aLambda - aLsfParams->lambda_ref,
551  aLsfParams->hermit[0]);
552  double h4 = muse_cplarray_poly1d_double(aLambda - aLsfParams->lambda_ref,
553  aLsfParams->hermit[1]);
554  double h5 = muse_cplarray_poly1d_double(aLambda - aLsfParams->lambda_ref,
555  aLsfParams->hermit[2]);
556  double h6 = muse_cplarray_poly1d_double(aLambda - aLsfParams->lambda_ref,
557  aLsfParams->hermit[3]);
558 
559  cpl_array *coeff = cpl_array_new(5, CPL_TYPE_DOUBLE);
560  cpl_array_set(coeff, 4, 2 * sqrt(5) * h6);
561  cpl_array_set(coeff, 3, 2 * sqrt(15) * h5);
562  cpl_array_set(coeff, 2, 5 * sqrt(6) * h4 - 6 * sqrt(5) * h6);
563  cpl_array_set(coeff, 1, 10 * sqrt(3) * h3 - 3 * sqrt(15) * h5);
564  cpl_array_set(coeff, 0, -2.5 * sqrt(6) * h4 + 1.5 * sqrt(5) * h6);
565 
566  cpl_array_divide_scalar(aVal, width);
567  bin_width /= 2 * width;
568  slit_width /= 2 * width;
569 
570  cpl_array *x = cpl_array_duplicate(aVal);
571  cpl_array *y;
572  cpl_array *y1;
573  cpl_array_add_scalar(x, slit_width + bin_width);
574  y = muse_lsf_G(x, coeff);
575 
576  cpl_array_copy_data_double(x, cpl_array_get_data_double(aVal));
577  cpl_array_add_scalar(x, slit_width - bin_width);
578  y1 = muse_lsf_G(x, coeff);
579  cpl_array_subtract(y, y1);
580  cpl_array_delete(y1);
581 
582  cpl_array_copy_data_double(x, cpl_array_get_data_double(aVal));
583  cpl_array_add_scalar(x, -slit_width + bin_width);
584  y1 = muse_lsf_G(x, coeff);
585  cpl_array_subtract(y, y1);
586  cpl_array_delete(y1);
587 
588  cpl_array_copy_data_double(x, cpl_array_get_data_double(aVal));
589  cpl_array_add_scalar(x, -slit_width - bin_width);
590  y1 = muse_lsf_G(x, coeff);
591  cpl_array_delete(x);
592  cpl_array_add(y, y1);
593  cpl_array_delete(y1);
594 
595  cpl_array_divide_scalar(y, sqrt(CPL_MATH_PI * 32)
596  * bin_width * slit_width * width * width);
597  cpl_array_multiply_scalar(y, width);
598  cpl_array_copy_data_double(aVal, cpl_array_get_data_double(y));
599  cpl_array_delete(y);
600  cpl_array_delete(coeff);
601 
602  return CPL_ERROR_NONE;
603 }
604 
605 /*----------------------------------------------------------------------------*/
621 /*----------------------------------------------------------------------------*/
622 cpl_array *
623 muse_lsf_params_spectrum(const cpl_array *aLambda, cpl_table *aLines,
624  const muse_lsf_params *aLsfParams)
625 {
626  cpl_size n_lines = cpl_table_get_nrow(aLines);
627  cpl_size i_line;
628  cpl_array *spectrum = cpl_array_new(cpl_array_get_size(aLambda),
629  CPL_TYPE_DOUBLE);
630  cpl_array_fill_window(spectrum, 0, cpl_array_get_size(aLambda), 0.0);
631  int errold = errno;
632  feclearexcept(FE_UNDERFLOW);
633  for (i_line = 0; i_line < n_lines; i_line++) {
634  double l_lambda = cpl_table_get(aLines, "lambda", i_line, NULL);
635  double l_flux = cpl_table_get(aLines, "flux", i_line, NULL);
636  double l_min = -7;
637  double l_max = 7;
638  cpl_size imin = muse_cplarray_find_sorted(aLambda, l_lambda + l_min);
639  cpl_size imax = muse_cplarray_find_sorted(aLambda, l_lambda + l_max);
640  if (imax <= imin) {
641  continue;
642  }
643  cpl_array *l0 = cpl_array_extract(aLambda, imin, imax-imin+1);
644  cpl_array_subtract_scalar(l0, l_lambda);
645  muse_lsf_params_apply(aLsfParams, l0, l_lambda);
646  cpl_array_multiply_scalar(l0, l_flux);
647  muse_cplarray_add_window(spectrum, imin, l0);
648  cpl_array_delete(l0);
649  }
650  if (fetestexcept(FE_UNDERFLOW)) {
651  errno = errold;
652  feclearexcept(FE_UNDERFLOW);
653  }
654  return spectrum;
655 }
656 
657 /*----------------------------------------------------------------------------*/
674 /*----------------------------------------------------------------------------*/
675 
677 muse_lsf_fit_params_new(cpl_boolean aOffset, cpl_boolean aRefraction,
678  cpl_size aSensitivity, cpl_size aSlitWidth,
679  cpl_size aBinWidth, cpl_size aLSFWidth,
680  cpl_size aHermit3, cpl_size aHermit4,
681  cpl_size aHermit5, cpl_size aHermit6)
682 {
683  muse_lsf_fit_params *params = cpl_malloc(sizeof(muse_lsf_fit_params));
684  params->offset = aOffset;
685  params->refraction = aRefraction;
686  params->sensitivity = aSensitivity;
687  params->slit_width = aSlitWidth;
688  params->bin_width = aBinWidth;
689  params->lsf_width = aLSFWidth;
690  params->hermit[0] = aHermit3;
691  params->hermit[1] = aHermit4;
692  params->hermit[2] = aHermit5;
693  params->hermit[3] = aHermit6;
694 
695  params->n_param = params->offset + params->refraction + params->sensitivity +
696  params->slit_width + params->bin_width + params->lsf_width;
697  cpl_size i;
698  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
699  params->n_param += params->hermit[i];
700  }
701 
702  return params;
703 }
704 
705 /*----------------------------------------------------------------------------*/
710 /*----------------------------------------------------------------------------*/
711 void
713  cpl_free(params);
714 }
715 
716 
717 /*----------------------------------------------------------------------------*/
727 /*----------------------------------------------------------------------------*/
728 static cpl_array *
729 muse_lsf_firstguess(const muse_lsf_fit_params *aFitParams) {
730  cpl_array *pars = cpl_array_new(aFitParams->n_param, CPL_TYPE_DOUBLE);
731  cpl_size offset = 0;
732 
733  // Wavelength offset
734  if (aFitParams->offset > 0) {
735  cpl_array_set(pars, offset++, 0.0);
736  }
737 
738  // Relative refraction ratio - 1
739  if (aFitParams->refraction > 0) {
740  cpl_array_set(pars, offset++, 0.0);
741  }
742 
743  // Relative sensitivity
744  cpl_size j;
745  for (j = 0; j < aFitParams->sensitivity; j++) {
746  cpl_array_set(pars, offset++, (j == 0)?1.0:0.0);
747  }
748 
749  // Slit width
750  if (aFitParams->slit_width > 0) {
751  cpl_array_set(pars, offset++, kMuseSliceSlitWidthA);
752  }
753 
754  // Bin width
755  if (aFitParams->bin_width > 0) {
756  cpl_array_set(pars, offset++, kMuseSpectralSamplingA);
757  }
758 
759  // LSF width
760  for (j = 0; j < aFitParams->lsf_width; j++) {
761  cpl_array_set(pars, offset++, (j == 0)?0.5:0.0);
762  }
763 
764  // Hermitean coefficients
765  cpl_size i;
766  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
767  for (j = 0; j < aFitParams->hermit[i]; j++) {
768  cpl_array_set(pars, offset++, 0.0);
769  }
770  }
771 
772  if (offset > cpl_array_get_size(pars)) {
773  cpl_msg_error(__func__,
774  "inconsistent array: size %ld, filled with %ld values",
775  (long)cpl_array_get_size(pars), (long)offset);
776  }
777  return pars;
778 }
779 
780 /*----------------------------------------------------------------------------*/
788 /*----------------------------------------------------------------------------*/
789 static cpl_array *
790 muse_lsf_set_param(muse_lsf_params *aLsfParams,
791  const muse_lsf_fit_params *aFitParams) {
792  cpl_array *pars = cpl_array_new(aFitParams->n_param, CPL_TYPE_DOUBLE);
793  cpl_size offset = 0;
794 
795  // Relative refraction ratio - 1
796  if (aFitParams->offset > 0) {
797  cpl_array_set(pars, offset++, aLsfParams->offset);
798  }
799  if (aFitParams->refraction > 0) {
800  cpl_array_set(pars, offset++, aLsfParams->refraction -1);
801  }
802  // relative sensitivity
803  cpl_size j;
804  cpl_size n = cpl_array_get_size(aLsfParams->sensitivity);
805  for (j = 0; j < aFitParams->sensitivity; j++) {
806  if (j < n) {
807  cpl_msg_debug(__func__, "S[%li]=%f", (long)j,
808  cpl_array_get(aLsfParams->sensitivity, j, NULL));
809  cpl_array_set(pars, offset++,
810  cpl_array_get(aLsfParams->sensitivity, j, NULL));
811  } else {
812  cpl_array_set(pars, offset++, (j == 0)?1.0:0.0);
813  }
814  }
815 
816  if (aFitParams->slit_width > 0) {
817  cpl_array_set(pars, offset++, aLsfParams->slit_width);
818  }
819 
820  if (aFitParams->bin_width > 0) {
821  cpl_array_set(pars, offset++, aLsfParams->bin_width);
822  }
823 
824  // LSF width
825  n = cpl_array_get_size(aLsfParams->lsf_width);
826  for (j = 0; j < aFitParams->lsf_width; j++) {
827  if (j < n) {
828  cpl_array_set(pars, offset++,
829  cpl_array_get(aLsfParams->lsf_width, j, NULL));
830  } else {
831  cpl_array_set(pars, offset++, (j == 0)?1.0:0.0);
832  }
833  }
834 
835  // Hermitean coefficients
836  cpl_size i;
837  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
838  n = cpl_array_get_size(aLsfParams->hermit[i]);
839  for (j = 0; j < aFitParams->hermit[i]; j++) {
840  if (j < n) {
841  cpl_array_set(pars, offset++,
842  cpl_array_get(aLsfParams->hermit[i], j, NULL));
843  } else {
844  cpl_array_set(pars, offset++, 0.0);
845  }
846  }
847  }
848 
849  if (offset > cpl_array_get_size(pars)) {
850  cpl_msg_error(__func__,
851  "inconsistent array: size %ld, filled with %ld values",
852  (long)cpl_array_get_size(pars), (long)offset);
853  }
854  return pars;
855 }
856 
857 /*----------------------------------------------------------------------------*/
868 /*----------------------------------------------------------------------------*/
869 static muse_lsf_params *
870 muse_lsf_apply_parametrization(const muse_lsf_params *aTemplate,
871  const cpl_array *aPar,
872  const muse_lsf_fit_params *aFitParams)
873 {
874  cpl_size offset = 0;
875  cpl_size j;
876 
877  cpl_size max_hermit = 0;
878  for (j = 0; j < MAX_HERMIT_ORDER; j++) {
879  if (max_hermit < aFitParams->hermit[j]) {
880  max_hermit = aFitParams->hermit[j];
881  }
882  }
883  muse_lsf_params *lsf
884  = muse_lsf_params_new((aFitParams->sensitivity > 0)?
885  aFitParams->sensitivity:
886  cpl_array_get_size(aTemplate->sensitivity),
887  (aFitParams->lsf_width > 0)?
888  aFitParams->lsf_width:
889  cpl_array_get_size(aTemplate->lsf_width),
890  (max_hermit > 0)?
891  max_hermit:
892  cpl_array_get_size(aTemplate->hermit[0]));
893  cpl_array_set(lsf->sensitivity, 0, 1.0);
894 
895  if (aFitParams->offset > 0) {
896  lsf->offset = cpl_array_get(aPar, offset++, NULL);
897  } else {
898  lsf->offset = aTemplate->offset;
899  }
900 
901  if (aFitParams->refraction > 0) {
902  lsf->refraction = 1.0 + cpl_array_get(aPar, offset++, NULL);
903  } else {
904  lsf->refraction = aTemplate->refraction;
905  }
906 
907  cpl_size n = cpl_array_get_size(lsf->sensitivity);
908  if (aFitParams->sensitivity > 0) {
909  for (j = 0; j < n; j++) {
910  if (j < aFitParams->sensitivity) {
911  cpl_array_set(lsf->sensitivity, j,
912  cpl_array_get(aPar, offset++, NULL));
913  } else {
914  cpl_array_set(lsf->sensitivity, j, 0.0);
915  }
916  }
917  } else {
918  for (j = 0; j < n; j++) {
919  cpl_array_set(lsf->sensitivity, j,
920  cpl_array_get(aTemplate->sensitivity, j, NULL));
921  }
922  }
923 
924  if (aFitParams->slit_width > 0) {
925  lsf->slit_width = cpl_array_get(aPar, offset++, NULL);
926  } else {
927  lsf->slit_width = aTemplate->slit_width;
928  }
929 
930  if (aFitParams->bin_width > 0) {
931  lsf->bin_width = cpl_array_get(aPar, offset++, NULL);
932  } else {
933  lsf->bin_width = aTemplate->bin_width;
934  }
935 
936  n = cpl_array_get_size(lsf->lsf_width);
937  if (aFitParams->lsf_width > 0) {
938  for (j = 0; j < n; j++) {
939  if (j < aFitParams->lsf_width) {
940  cpl_array_set(lsf->lsf_width, j,
941  cpl_array_get(aPar, offset++, NULL));
942  } else {
943  cpl_array_set(lsf->lsf_width, j, 0.0);
944  }
945  }
946  } else {
947  for (j = 0; j < n; j++) {
948  cpl_array_set(lsf->lsf_width, j,
949  cpl_array_get(aTemplate->lsf_width, j, NULL));
950  }
951  }
952 
953  int i;
954  for (i = 0; i < MAX_HERMIT_ORDER; i++) {
955  n = cpl_array_get_size(lsf->hermit[i]);
956  if (aFitParams->hermit[i] > 0) {
957  for (j = 0; j < n; j++) {
958  if (j < aFitParams->hermit[i]) {
959  cpl_array_set(lsf->hermit[i], j,
960  cpl_array_get(aPar, offset++, NULL));
961  } else {
962  cpl_array_set(lsf->hermit[i], j, 0.0);
963  }
964  }
965  } else {
966  for (j = 0; j < n; j++) {
967  cpl_array_set(lsf->hermit[i], j,
968  //cpl_array_get(aTemplate->hermit[i], j, NULL));
969  0.0);
970  }
971  }
972  }
973 
974  if (offset > cpl_array_get_size(aPar)) {
975  cpl_msg_error(__func__,
976  "inconsistent array: size %ld, read with %ld values",
977  (long)cpl_array_get_size(aPar), (long)offset);
979  return NULL;
980  }
981 
982  return lsf;
983 }
984 
985 
986 /*----------------------------------------------------------------------------*/
987 
988 typedef struct {
989  cpl_array *lambda;
990  cpl_array *values;
991  cpl_array *stat;
992  const cpl_table *lines;
993  const muse_lsf_fit_params *fit_params;
994  muse_lsf_params *firstGuess;
995 } muse_lsf_fit_struct;
996 
997 /*----------------------------------------------------------------------------*/
1006 /*----------------------------------------------------------------------------*/
1007 static cpl_error_code
1008 muse_lsf_eval(void *aData, cpl_array *aPar, cpl_array *aRetval) {
1009 
1010  muse_lsf_fit_struct *data = aData;
1011  cpl_size size = cpl_array_get_size(aRetval);
1012 
1013  muse_lsf_params *lsfParam
1014  = muse_lsf_apply_parametrization(data->firstGuess, aPar, data->fit_params);
1015 
1016  cpl_table *lines = cpl_table_duplicate(data->lines);
1017 
1018  if (!cpl_table_has_column(lines, "flux")) {
1019  cpl_array *linesFlux = cpl_array_extract(aPar, cpl_array_get_size(aPar)
1020  - cpl_table_get_nrow(data->lines),
1021  cpl_table_get_nrow(data->lines));
1022  cpl_table_wrap_double(lines, cpl_array_unwrap(linesFlux), "flux");
1023  }
1024 
1025  cpl_array *simulated
1026  = muse_lsf_params_spectrum(data->lambda, lines, lsfParam);
1027 
1028  cpl_table_delete(lines);
1029 
1030  muse_lsf_params_delete(lsfParam);
1031  cpl_array_subtract(simulated, data->values);
1032  cpl_array_divide(simulated, data->stat);
1033 
1034  cpl_array_fill_window_double(aRetval, 0, size, 0.0);
1035  memcpy(cpl_array_get_data_double(aRetval),
1036  cpl_array_get_data_double_const(simulated),
1037  size * sizeof(double));
1038 
1039  // replace invalid numbers by 0.0
1040  cpl_size i;
1041  double *d = cpl_array_get_data_double(aRetval);
1042  for (i = 0; i < size; i++) {
1043  if (isnan(d[i])) {
1044  d[i] = 0.0;
1045  }
1046  }
1047 
1048  cpl_array_delete(simulated);
1049  return CPL_ERROR_NONE;
1050 }
1051 
1052 /*----------------------------------------------------------------------------*/
1065 /*----------------------------------------------------------------------------*/
1067 muse_lsf_params_fit(muse_pixtable *aPixtable, cpl_table *aLines, int aMaxIter)
1068 {
1069  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, NULL);
1070  uint32_t origin = (uint32_t)cpl_table_get_int(aPixtable->table,
1072  0, NULL);
1073  int i_ifu = muse_pixtable_origin_get_ifu(origin);
1074  int i_slice = muse_pixtable_origin_get_slice(origin);
1075 
1076  cpl_propertylist *order = cpl_propertylist_new();
1077  cpl_propertylist_append_bool(order, MUSE_PIXTABLE_LAMBDA, CPL_FALSE);
1078  cpl_table_sort(aPixtable->table, order);
1079  cpl_propertylist_delete(order);
1080 
1081  // We need a private copy since we will modify the line flux. The fit may
1082  // run in parallel for all slices.
1083  cpl_table *lines = cpl_table_duplicate(aLines);
1084 
1085  //
1086  // Create "lambda", "data", and "stat" fields from the pixel table.
1087  // They do not contain all pixels, but a certain percentage of them.
1088  //
1089  cpl_size reduction = 1; // we use 100% of all pixels for the fit.
1090  cpl_size size = cpl_table_get_nrow(aPixtable->table)/reduction;
1091  cpl_array *lambda = cpl_array_new(size, CPL_TYPE_DOUBLE);
1092  cpl_array *data = cpl_array_new(size, CPL_TYPE_DOUBLE);
1093  cpl_array *stat = cpl_array_new(size, CPL_TYPE_DOUBLE);
1094 
1095  cpl_msg_info(__func__, "processing slice %2i.%02i"
1096  " with %"CPL_SIZE_FORMAT" entries",
1097  i_ifu, i_slice, size);
1098 
1099  cpl_size i;
1100  for (i = 0; i < size; i++) {
1101  int iflg = 0;
1102  cpl_array_set(lambda, i, cpl_table_get(aPixtable->table,
1104  reduction * i, &iflg));
1105  cpl_array_set(data, i, cpl_table_get(aPixtable->table,
1107  reduction * i, &iflg));
1108  cpl_array_set(stat, i, sqrt(cpl_table_get(aPixtable->table,
1110  reduction * i, &iflg)));
1111  }
1112 
1113  muse_lsf_params *firstGuess = muse_lsf_params_new(1, 3, 1);
1114 
1115  int debug = getenv("MUSE_DEBUG_LSF_FIT")
1116  && atoi(getenv("MUSE_DEBUG_LSF_FIT")) > 0;
1118  -1, -1, -1, // default ftol, xtol, gtol
1119  aMaxIter, debug
1120  };
1121 
1122  //
1123  // First minimization step: Fit the lsf width and the fluxes of all lines
1124  //
1125  muse_lsf_fit_params *slice_fit_params0 = muse_lsf_fit_params_new
1126  (
1127  0, // aParams->slice_fit_offset,
1128  0, // aParams->slice_fit_refraction,
1129  0, // sensitivity is not used here
1130  0, // aParams->slice_fit_slit_width,
1131  0, // aParams->slice_fit_bin_width,
1132  3, // aParams->slice_fit_lsf_width + 1,
1133  0, // aParams->slice_fit_h3 + 1,
1134  0, // aParams->slice_fit_h4 + 1,
1135  0, // aParams->slice_fit_h5 + 1,
1136  0 // aParams->slice_fit_h6 + 1
1137  );
1138 
1139  muse_lsf_fit_struct fit_data = {
1140  lambda,
1141  data,
1142  stat,
1143  lines,
1144  slice_fit_params0,
1145  firstGuess
1146  };
1147 
1148  cpl_array *pars = muse_lsf_firstguess(slice_fit_params0);
1149 
1150  // Take the lines fluxes as they come out of the line list as first guess
1151  cpl_array *lf = muse_cpltable_extract_column(lines, "flux");
1152  cpl_array *linesFlux = cpl_array_cast(lf, CPL_TYPE_DOUBLE);
1153  cpl_array_unwrap(lf);
1154  cpl_array_insert(pars, linesFlux, cpl_array_get_size(pars));
1155  cpl_table_erase_column(lines, "flux");
1156 
1157  cpl_error_code r
1158  = muse_cpl_optimize_lvmq(&fit_data, pars, size, muse_lsf_eval, &ctrl);
1159 
1160  if (r != CPL_ERROR_NONE) { // on error: reset to first guess
1161  cpl_array_delete(pars);
1162  pars = muse_lsf_firstguess(slice_fit_params0);
1163  cpl_array_insert(pars, linesFlux, cpl_array_get_size(pars));
1164  }
1165 
1166  //
1167  // Second minimization step: Keep the line fluxes constant and fit
1168  // all shape parameters
1169  //
1170  muse_lsf_fit_params *slice_fit_params = muse_lsf_fit_params_new
1171  (
1172  0, // aParams->slice_fit_offset,
1173  0, // aParams->slice_fit_refraction,
1174  0, // sensitivity is not used here
1175  1, // aParams->slice_fit_slit_width,
1176  1, // aParams->slice_fit_bin_width,
1177  3, // aParams->slice_fit_lsf_width + 1,
1178  1, // aParams->slice_fit_h3 + 1,
1179  2, // aParams->slice_fit_h4 + 1,
1180  1, // aParams->slice_fit_h5 + 1,
1181  2 // aParams->slice_fit_h6 + 1
1182  );
1183 
1184  fit_data.fit_params = slice_fit_params;
1185 
1186  cpl_array_delete(linesFlux);
1187  linesFlux = cpl_array_extract(pars, cpl_array_get_size(pars)
1188  - cpl_table_get_nrow(lines),
1189  cpl_table_get_nrow(lines));
1190  cpl_table_wrap_double(lines, cpl_array_unwrap(linesFlux), "flux");
1191 
1192  fit_data.firstGuess = muse_lsf_apply_parametrization(firstGuess, pars,
1193  slice_fit_params0);
1194  muse_lsf_fit_params_delete(slice_fit_params0);
1195  cpl_array_delete(pars);
1196  pars = muse_lsf_set_param(fit_data.firstGuess, slice_fit_params);
1197 
1198  r = muse_cpl_optimize_lvmq(&fit_data, pars, size, muse_lsf_eval, &ctrl);
1199  if (r != CPL_ERROR_NONE) { // on error: reset to first guess
1200  cpl_array_delete(pars);
1201  pars = muse_lsf_firstguess(slice_fit_params);
1202  }
1203 
1204  muse_lsf_params *lsfParam
1205  = muse_lsf_apply_parametrization(firstGuess, pars, slice_fit_params);
1206  lsfParam->ifu = i_ifu;
1207  lsfParam->slice = i_slice;
1208 
1209  cpl_msg_debug(__func__, "Slice %2i.%02i: Slit width: %f (%s), bin width: %f (%s)",
1210  i_ifu, i_slice,
1211  lsfParam->slit_width, slice_fit_params->slit_width?"fit":"fixed",
1212  lsfParam->bin_width, slice_fit_params->bin_width?"fit":"fixed");
1213 
1214  cpl_array *simulated = muse_lsf_params_spectrum(lambda, lines, lsfParam);
1215  cpl_table_wrap_double(aPixtable->table, cpl_array_unwrap(simulated),
1216  "simulated");
1217  cpl_table_subtract_columns(aPixtable->table, MUSE_PIXTABLE_DATA,
1218  "simulated");
1219  cpl_table_erase_column(aPixtable->table, "simulated");
1220 
1221 
1222  cpl_array_delete(pars);
1223  if (cpl_table_has_column(aPixtable->table, "lambda_double")) {
1224  cpl_table_erase_column(aPixtable->table, "lambda_double");
1225  }
1226  cpl_array_delete(fit_data.lambda);
1227  cpl_array_delete(fit_data.values);
1228  cpl_array_delete(fit_data.stat);
1229  muse_lsf_params_delete(fit_data.firstGuess);
1230  muse_lsf_params_delete(firstGuess);
1231  muse_lsf_fit_params_delete(slice_fit_params);
1232  cpl_table_delete(lines);
1233 
1234  return lsfParam;
1235 }
1236 
1237 /*----------------------------------------------------------------------------*/
1258 /*----------------------------------------------------------------------------*/
1259 double
1260 muse_lsf_fwhm_lambda(const muse_lsf_params *aDP, double aLambda,
1261  double aSampling, unsigned int aLength)
1262 {
1263  cpl_ensure(aDP, CPL_ERROR_NULL_INPUT, 0.);
1264  /* if the lsf_width was not fitted correctly this value would be useless */
1265  cpl_ensure(cpl_array_get(aDP->lsf_width, 0, NULL) != 1 &&
1266  cpl_array_get(aDP->lsf_width, 0, NULL) != 0,
1267  CPL_ERROR_ILLEGAL_INPUT, 0.);
1268 
1269  cpl_table *line = cpl_table_new(1);
1270  cpl_table_new_column(line, "lambda", CPL_TYPE_DOUBLE);
1271  cpl_table_new_column(line, "flux", CPL_TYPE_FLOAT);
1272  cpl_table_set_double(line, "lambda", 0, aLambda);
1273  cpl_table_set_float(line, "flux", 0, 1.0);
1274 
1275  cpl_array *lambda = cpl_array_new(aLength, CPL_TYPE_DOUBLE);
1276  cpl_size i;
1277  for (i = 0; i < aLength; i++) {
1278  cpl_array_set_double(lambda, i, (i + 1. - aLength/2) * aSampling + aLambda);
1279  } /* for i */
1280 
1281  cpl_array *spec = muse_lsf_params_spectrum(lambda, line, aDP);
1282  cpl_size imax;
1283  cpl_array_get_maxpos(spec, &imax);
1284  double max = cpl_array_get_max(spec),
1285  vl = 0., vr = 0.; /* exact values at half maximum */
1286  i = imax;
1287  while (--i >= 0 &&
1288  (vl = cpl_array_get_double(spec, i, NULL)) > max/2.) ;
1289  cpl_size il = i; /* first point with value below half max */
1290  i = imax;
1291  while (++i < aLength &&
1292  (vr = cpl_array_get_double(spec, i, NULL)) > max/2.) ;
1293  cpl_size ir = i;
1294  /* compute the FWHM that we really want; do not use (ir - il + 1) *
1295  * because on average we stepped two half pixels too far... */
1296  double fwhm = (ir - il) * aSampling;
1297  cpl_array_delete(spec);
1298  cpl_array_delete(lambda);
1299  cpl_table_delete(line);
1300  cpl_ensure(il > 0 && ir < aLength, CPL_ERROR_ILLEGAL_OUTPUT, 0.);
1301  return fwhm;
1302 } /* muse_lsf_fwhm_lambda() */
1303 
cpl_array * muse_lsf_params_spectrum(const cpl_array *aLambda, cpl_table *aLines, const muse_lsf_params *aLsfParams)
Create spectrum for a single slice.
unsigned short muse_pixtable_origin_get_slice(uint32_t aOrigin)
Get the slice number from the encoded 32bit origin number.
double muse_lsf_fwhm_lambda(const muse_lsf_params *aDP, double aLambda, double aSampling, unsigned int aLength)
Measure the FWHM of an LSF at a given wavelength.
void muse_lsf_params_delete(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_get(muse_lsf_params **aParams, int aIFU, int aSlice)
Get the slice LSF parameters for one slice.
cpl_error_code muse_cplarray_poly1d(cpl_array *aArray, const cpl_array *aCoeff)
Apply a polynomial to an array.
cpl_table * table
The pixel table.
void muse_lsf_params_delete_all(muse_lsf_params **aParams)
Delete an allocated array of muse_lsf_params structure.
cpl_table * muse_cpltable_load(const char *aFile, const char *aExtension, const muse_cpltable_def aDefinition[])
Load a table from disk (and check against definition).
#define MUSE_PIXTABLE_DATA
Definition: muse_pixtable.h:48
double bin_width
Bin width.
muse_lsf_fit_params * muse_lsf_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_array * hermit[MAX_HERMIT_ORDER]
coefficients for the damped gauss-hermitean parametrization
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.
cpl_array * sensitivity
Relative detector sensitivity parametrization.
cpl_error_code muse_cplarray_exp(cpl_array *aArray)
Compute the exponential function of array elements.
Structure definition of MUSE pixel table.
double slit_width
Slit width.
cpl_array * muse_cpltable_get_array_copy(cpl_table *aTable, const char *aColumn, cpl_size aRow)
Return the copy of an array of a table cell.
cpl_array * lsf_width
LSF width.
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_lsf_params_fit(muse_pixtable *aPixtable, cpl_table *aLines, int aMaxIter)
Fit all entries of one slice.
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.
cpl_error_code muse_cplarray_erf(cpl_array *aArray)
Compute the error function of array elements.
cpl_error_code muse_lsf_params_apply(const muse_lsf_params *aLsfParams, cpl_array *aVal, double aLambda)
Apply the MUSE LSF function to a single line.
cpl_size muse_lsf_params_get_size(muse_lsf_params **aParams)
Count the number of entries in the array.
cpl_error_code muse_cplarray_add_window(cpl_array *aDest, cpl_size aStart, const cpl_array *aArray)
Add the value of an array to a window of a table column.
#define MUSE_PIXTABLE_ORIGIN
Definition: muse_pixtable.h:54
const muse_cpltable_def muse_lsfparams_def[]
Definition of a lsf parameters table.
muse_lsf_params ** muse_lsf_params_load(const char *aFile, muse_lsf_params **aParams, int aIFU)
Load slice LSF parameters from the extension "SLICE_PARAM".
Structure to define which slice parameters are fit.
unsigned short muse_pixtable_origin_get_ifu(uint32_t aOrigin)
Get the IFU number from the encoded 32bit origin number.
cpl_error_code muse_lsf_params_save(const muse_lsf_params **aLsfParams, const char *aFile)
Save slice LSF parameters to the extension "slice" on disk.
double lambda_ref
Reference wavelength for polynomial parametrizations.
#define MUSE_PIXTABLE_STAT
Definition: muse_pixtable.h:50
#define MUSE_PIXTABLE_LAMBDA
Definition: muse_pixtable.h:53
cpl_frameset * inframes
static cpl_array * muse_lsf_G(cpl_array *aX, cpl_array *aCoeffs)
Helper function "G" for integrated damped gauss-hermitean function.
Definition of a cpl table structure.
muse_lsf_params ** muse_processing_lsf_params_load(muse_processing *aProcessing, int aIFU)
Load slice LSF parameters.
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_lsf_fit_params_delete(muse_lsf_fit_params *params)
Delete the fit parameter structure.
Structure definition of detector (slice) parameters.
double muse_cplarray_poly1d_double(double aDouble, const cpl_array *aCoeff)
Apply a polynomial to a double value.
cpl_size muse_cplarray_find_sorted(const cpl_array *aArray, double aValue)
Find a row in an array.
cpl_error_code muse_cpltable_append_file(const cpl_table *aTable, const char *aFile, const char *aExtension, const muse_cpltable_def aDefinition[])
Save a table to disk (into a FITS extension)