30 #include "muse_instrument.h"
32 #include "muse_optimize.h"
33 #include "muse_utils.h"
34 #include "muse_data_format_z.h"
60 if (aMaster != NULL) {
61 cpl_table_delete(aMaster->
lines);
78 cpl_array *line_strength;
83 } muse_sky_master_params;
94 static muse_sky_master_params *
95 muse_sky_master_params_new(cpl_size n_groups, cpl_size n_continuum) {
96 muse_sky_master_params *res = cpl_calloc(1,
sizeof(muse_sky_master_params));
97 res->line_strength = cpl_array_new(n_groups, CPL_TYPE_DOUBLE);
98 cpl_array_fill_window_double(res->line_strength, 0, n_groups, 1.0);
99 res->continuum = cpl_array_new(n_continuum, CPL_TYPE_DOUBLE);
100 cpl_array_fill_window_double(res->continuum, 0, n_continuum, 0.0);
112 muse_sky_master_params_delete(muse_sky_master_params *aParams) {
113 if (aParams != NULL) {
114 cpl_array_delete(aParams->line_strength);
115 cpl_array_delete(aParams->continuum);
134 static muse_sky_master_params *
135 muse_sky_master_apply_sky_parametrization(
const cpl_array *aPar,
138 muse_sky_master_params *p = muse_sky_master_params_new(ngroups, 0);
141 for (i = 0; i < ngroups; i++) {
142 double s = (cpl_array_get(aPar, (*offset)++, NULL));
143 cpl_array_set(p->line_strength, i, s*s);
160 muse_sky_master_sky_firstguess(
int ngroups) {
161 cpl_array *pars = cpl_array_new(0 + ngroups, CPL_TYPE_DOUBLE);
166 for (i = 0; i < ngroups; i++) {
167 cpl_array_set(pars, offset++, 1e-1);
170 if (offset != cpl_array_get_size(pars)) {
171 cpl_msg_error(__func__,
172 "inconsistent array: size %li; filled with %li values",
173 (
long)cpl_array_get_size(pars), (
long)offset);
191 muse_sky_master_apply_lsf_parametrization(
const cpl_array *aPar,
196 lsf->offset = cpl_array_get(aPar, (*offset)++, NULL);
197 lsf->refraction = 1.0 + cpl_array_get(aPar, (*offset)++, NULL);
199 cpl_array_get(aPar, (*offset)++, NULL));
202 cpl_array_get(aPar, (*offset)++, NULL));
204 cpl_array_get(aPar, (*offset)++, NULL));
207 for (i = 0; i < MAX_HERMIT_ORDER; i++) {
208 cpl_array_set(lsf->
hermit[i], 0,
209 cpl_array_get(aPar, (*offset)++, NULL));
225 muse_sky_master_lsf_firstguess(
void) {
226 cpl_array *pars = cpl_array_new(5 + MAX_HERMIT_ORDER, CPL_TYPE_DOUBLE);
230 cpl_array_set(pars, offset++, 0.0);
232 cpl_array_set(pars, offset++, 0.0);
235 cpl_array_set(pars, offset++, 1.0);
237 cpl_array_set(pars, offset++, 0);
238 cpl_array_set(pars, offset++, 0);
242 for (i = 0; i < MAX_HERMIT_ORDER; i++) {
243 cpl_array_set(pars, offset++, 0.0);
245 if (offset != cpl_array_get_size(pars)) {
246 cpl_msg_error(__func__,
247 "inconsistent array: size %ld, filled with %ld values",
248 (
long)cpl_array_get_size(pars), (
long)offset);
265 static muse_sky_master_params *
266 muse_sky_master_apply_parametrization(
const cpl_array *aPar,
int ngroups) {
268 muse_sky_master_params *p = muse_sky_master_apply_sky_parametrization(aPar, &offset,
270 p->lsf = muse_sky_master_apply_lsf_parametrization(aPar, &offset);
272 if (offset != cpl_array_get_size(aPar)) {
273 cpl_msg_error(__func__,
274 "inconsistent array: size %ld, read with %ld values",
275 (
long)cpl_array_get_size(aPar), (
long)offset);
276 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
277 muse_sky_master_params_delete(p);
297 simulate_master_sky_parameters(
const cpl_table *aLines,
299 const cpl_array *aLambda,
300 const cpl_array *aPar) {
301 muse_sky_master_params *p
302 = muse_sky_master_apply_parametrization(aPar, aNgroups);
304 cpl_array *continuum = cpl_array_duplicate(aLambda);
307 cpl_table *lines = cpl_table_duplicate(aLines);
309 double maxflux = cpl_table_get_column_max(lines,
"flux");
313 cpl_array_add(simulated, continuum);
315 cpl_array_delete(continuum);
316 cpl_table_delete(lines);
317 muse_sky_master_params_delete(p);
323 const cpl_array *lambda;
324 const cpl_array *values;
325 const cpl_array *stat;
326 const cpl_table *sky;
327 const cpl_size ngroups;
328 } muse_master_fit_struct;
341 static cpl_error_code
342 muse_sky_master_eval(
void *aData, cpl_array *aPar, cpl_array *aRetval) {
343 cpl_size size = cpl_array_get_size(aRetval);
344 muse_master_fit_struct *data = aData;
346 = simulate_master_sky_parameters(data->sky, data->ngroups,
349 cpl_array_subtract(simulated, data->values);
351 cpl_array_divide(dsimulated, data->stat);
353 cpl_array_fill_window_double(aRetval, 0, size, 0.0);
354 memcpy(cpl_array_get_data_double(aRetval),
355 cpl_array_get_data_double_const(dsimulated),
356 size *
sizeof(
double));
358 cpl_array_delete(simulated);
359 cpl_array_delete(dsimulated);
361 return CPL_ERROR_NONE;
383 static cpl_error_code
384 muse_sky_master_correct_firstguess(
const cpl_table *aLines,
386 const cpl_array *aLambda,
387 const cpl_array *aData,
392 = simulate_master_sky_parameters(aLines, aNgroups, aLambda, aPars);
395 muse_sky_master_params *msp
396 = muse_sky_master_apply_sky_parametrization(aPars, &offset, aNgroups);
397 cpl_table *lines = cpl_table_duplicate(aLines);
399 muse_sky_master_params_delete(msp);
402 for (i_group = 0; i_group < aNgroups; i_group++) {
404 cpl_table_unselect_all(lines);
405 cpl_table_or_selected_int(lines,
"group", CPL_EQUAL_TO, i_group);
406 cpl_table *gtable = cpl_table_extract_selected(lines);
408 cpl_table_get_column_maxpos(gtable,
"flux", &row);
409 double wavelength = cpl_table_get_double(gtable,
"lambda", row, NULL);
410 cpl_table_delete(gtable);
420 for (i_lbda = i_lbda1; i_lbda <= i_lbda2; i_lbda++) {
421 double lbda = cpl_array_get(aLambda, i_lbda, NULL);
422 double wy_data = cpl_array_get(aData, i_lbda, NULL);
423 double wy_sim = cpl_array_get(simulated, i_lbda, NULL);
425 avg_data += wy_data * lbda;
427 avg_sim += wy_sim * lbda;
432 cpl_array_set(aPars, i_group,
433 cpl_array_get(aPars, i_group, NULL)*sqrt(y_data/y_sim));
436 delta += avg_data - avg_sim;
439 cpl_array_set(aPars, aNgroups,
440 cpl_array_get(aPars, aNgroups, NULL) + delta/aNgroups);
441 cpl_table_delete(lines);
442 cpl_array_delete(simulated);
444 return CPL_ERROR_NONE;
465 const cpl_array *aStat,
const cpl_table *aLines)
467 cpl_ensure(aLambda, CPL_ERROR_NULL_INPUT, NULL);
468 cpl_ensure(aData, CPL_ERROR_NULL_INPUT, NULL);
469 cpl_ensure(aStat, CPL_ERROR_NULL_INPUT, NULL);
470 cpl_ensure(aLines, CPL_ERROR_NULL_INPUT, NULL);
472 cpl_size nstat = cpl_array_get_size(aStat);
473 cpl_ensure(nstat > 0, CPL_ERROR_DATA_NOT_FOUND, NULL);
474 cpl_array *stat = cpl_array_extract(aStat, 0, nstat - 1);
475 cpl_array *s2 = cpl_array_extract(aStat, 1, nstat);
476 cpl_array_add(stat, s2);
477 cpl_array_delete(s2);
478 cpl_array_power(stat, 0.5);
480 muse_master_fit_struct data = {
485 cpl_table_get_column_max(aLines,
"group") + 1
488 cpl_array *pars = muse_sky_master_sky_firstguess(data.ngroups);
489 cpl_array *dpars = muse_sky_master_lsf_firstguess();
490 cpl_array_insert(pars, dpars, cpl_array_get_size(pars));
491 cpl_array_delete(dpars);
497 muse_sky_master_correct_firstguess(aLines, data.ngroups, aLambda, aData, pars);
498 muse_sky_master_correct_firstguess(aLines, data.ngroups, aLambda, aData, pars);
499 muse_sky_master_correct_firstguess(aLines, data.ngroups, aLambda, aData, pars);
501 cpl_size size = cpl_array_get_size(aLambda);
503 int debug = getenv(
"MUSE_DEBUG_LSF_FIT")
504 && atoi(getenv(
"MUSE_DEBUG_LSF_FIT")) > 0;
510 cpl_msg_info(__func__,
"Starting master sky fit");
512 muse_sky_master_eval, &ctrl);
513 if (r != CPL_ERROR_NONE) {
514 cpl_msg_error(__func__,
"Master sky fit failed with error code %i: %s",
515 r, cpl_error_get_message());
517 cpl_msg_info(__func__,
"Master sky fit finished successfully.");
520 muse_sky_master_params *p = muse_sky_master_apply_parametrization(pars, data.ngroups);
522 cpl_array_delete(pars);
525 res->
lines = cpl_table_duplicate(aLines);
527 cpl_propertylist *order = cpl_propertylist_new();
529 cpl_propertylist_append_bool(order,
"flux", TRUE);
530 cpl_table_sort(res->
lines, order);
531 cpl_propertylist_delete(order);
534 cpl_msg_info(__func__,
"refraction index=1%s%g, offset=%f Angstrom",
535 p->lsf->refraction < 1?
"-":
"+",
536 fabs(p->lsf->refraction-1), p->lsf->offset);
538 cpl_array_subtract(continuum, aData);
539 cpl_array_multiply_scalar(continuum, -1);
563 cpl_array_get_size(aLambda));
566 cpl_array_delete(continuum);
569 res->
lsf[0] = p->lsf;
572 cpl_array_delete(stat);
573 muse_sky_master_params_delete(p);
588 cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
592 "SKY_CONTINUUM", 0, CPL_FALSE);
593 if (frames_c == NULL || cpl_frameset_get_size(frames_c) < 1) {
594 cpl_frameset_delete(frames_c);
597 cpl_frame *frame_c = cpl_frameset_get_position(frames_c, 0);
598 const char *fn = cpl_frame_get_filename(frame_c);
602 if (continuum == NULL) {
603 cpl_frameset_delete(frames_c);
608 cpl_frameset_delete(frames_c);
627 cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
634 "SKY_LINES", 0, CPL_FALSE);
635 if (cpl_frameset_get_size(frames_l) < 1) {
636 cpl_frameset_delete(frames_l);
640 cpl_frame *frame_l = cpl_frameset_get_position(frames_l, 0);
641 const char *fn = cpl_frame_get_filename(frame_l);
646 "SKY_CONTINUUM", 0, CPL_FALSE);
647 if (cpl_frameset_get_size(frames_c) < 1) {
648 cpl_frameset_delete(frames_c);
649 cpl_frameset_delete(frames_l);
653 cpl_frame *frame_c = cpl_frameset_get_position(frames_c, 0);
654 fn = cpl_frame_get_filename(frame_c);
659 "LSF_TABLE", 0, CPL_FALSE);
660 cpl_size iframe, nframes = cpl_frameset_get_size(frames_d);
661 for (iframe = 0; iframe < nframes; iframe++) {
662 cpl_frame *frame_d = cpl_frameset_get_position(frames_d, iframe);
663 fn = cpl_frame_get_filename(frame_d);
670 cpl_frameset_delete(frames_d);
671 cpl_frameset_delete(frames_c);
672 cpl_frameset_delete(frames_l);
678 for (iframe = 0; iframe < nframes; iframe++) {
679 cpl_frame *frame_d = cpl_frameset_get_position(frames_d, iframe);
681 CPL_FRAME_GROUP_CALIB, 1);
683 cpl_frameset_delete(frames_l);
684 cpl_frameset_delete(frames_c);
685 cpl_frameset_delete(frames_d);
void muse_lsf_params_delete_one(muse_lsf_params *aParams)
Delete an allocated muse_lsf_params structure.
cpl_error_code muse_sky_lines_apply_strength(cpl_table *, const cpl_array *)
Apply the line strengths to the lines.
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.
Structure to hold the MASTER SKY result.
cpl_error_code muse_cplarray_poly1d(cpl_array *aArray, const cpl_array *aCoeff)
Apply a polynomial to an array.
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).
cpl_table * muse_cpltable_new(const muse_cpltable_def *aDef, cpl_size aLength)
Create an empty table according to the specified definition.
cpl_array * hermit[MAX_HERMIT_ORDER]
coefficients for the damped gauss-hermitean parametrization
cpl_table * muse_sky_continuum_load(muse_processing *aProcessing)
Load the SKY_CONTINUUM spectrum.
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_array * lsf_width
LSF width.
cpl_table * lines
Table of Atmospheric emission lines and their intensities.
Optimization control parameters.
const muse_cpltable_def muse_fluxspectrum_def[]
Definition of the flux spectrum table structure.
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.
void muse_sky_master_delete(muse_sky_master *)
Delete a MASTER SKY structure.
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".
muse_sky_master * muse_sky_master_fit(const cpl_array *aLambda, const cpl_array *aData, const cpl_array *aStat, const cpl_table *aLines)
Fit all entries of the pixel table to the master sky.
cpl_error_code muse_cpltable_copy_array(cpl_table *aTable, const char *aColumn, const cpl_array *aArray)
Copy an array into a table.
cpl_array * muse_cplarray_diff(const cpl_array *aArray, int aOffset)
Build the difference of any element and one of the next elements.
cpl_error_code muse_sky_lines_cut(cpl_table *, double)
Remove all lines below a certain flux limit.
muse_sky_master * muse_sky_master_load(muse_processing *aProcessing)
Load SKY_LINES, SKY_CONTINUUM, and LSF_TABLE into a structure.
cpl_array * muse_sky_apply_lsf(const cpl_array *, const cpl_table *, const muse_lsf_params *)
Apply the LSF parameters to a spectrum.
cpl_table * continuum
Continuum flux 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
cpl_frameset * inputFrames
Structure definition of detector (slice) parameters.
cpl_size muse_cplarray_find_sorted(const cpl_array *aArray, double aValue)
Find a row in an array.
muse_lsf_params ** lsf
LSF parameter for the resampled spectrum.
void muse_lsf_params_delete(muse_lsf_params **aParams)
Delete an allocated array of muse_lsf_params structure.
muse_sky_master * muse_sky_master_new(void)
Create a muse_sky_master structure.