30 #include "muse_instrument.h"
32 #include "muse_optimize.h"
33 #include "muse_utils.h"
34 #include "muse_data_format_z.h"
59 if (aMaster != NULL) {
60 cpl_table_delete(aMaster->
lines);
77 cpl_array *line_strength;
82 } muse_sky_master_params;
93 static muse_sky_master_params *
94 muse_sky_master_params_new(cpl_size n_groups, cpl_size n_continuum) {
95 muse_sky_master_params *res = cpl_calloc(1,
sizeof(muse_sky_master_params));
96 res->line_strength = cpl_array_new(n_groups, CPL_TYPE_DOUBLE);
97 cpl_array_fill_window_double(res->line_strength, 0, n_groups, 1.0);
98 res->continuum = cpl_array_new(n_continuum, CPL_TYPE_DOUBLE);
99 cpl_array_fill_window_double(res->continuum, 0, n_continuum, 0.0);
111 muse_sky_master_params_delete(muse_sky_master_params *aParams) {
112 if (aParams != NULL) {
113 cpl_array_delete(aParams->line_strength);
114 cpl_array_delete(aParams->continuum);
133 static muse_sky_master_params *
134 muse_sky_master_apply_sky_parametrization(
const cpl_array *aPar,
137 muse_sky_master_params *p = muse_sky_master_params_new(ngroups, 0);
140 for (i = 0; i < ngroups; i++) {
141 double s = (cpl_array_get(aPar, (*offset)++, NULL));
142 cpl_array_set(p->line_strength, i, s*s);
159 muse_sky_master_sky_firstguess(
int ngroups) {
160 cpl_array *pars = cpl_array_new(0 + ngroups, CPL_TYPE_DOUBLE);
165 for (i = 0; i < ngroups; i++) {
166 cpl_array_set(pars, offset++, 1e-1);
169 if (offset != cpl_array_get_size(pars)) {
170 cpl_msg_error(__func__,
171 "inconsistent array: size %li; filled with %li values",
172 (
long)cpl_array_get_size(pars), (
long)offset);
190 muse_sky_master_apply_lsf_parametrization(
const cpl_array *aPar,
195 lsf->offset = cpl_array_get(aPar, (*offset)++, NULL);
196 lsf->refraction = 1.0 + cpl_array_get(aPar, (*offset)++, NULL);
198 cpl_array_get(aPar, (*offset)++, NULL));
201 cpl_array_get(aPar, (*offset)++, NULL));
203 cpl_array_get(aPar, (*offset)++, NULL));
206 for (i = 0; i < MAX_HERMIT_ORDER; i++) {
207 cpl_array_set(lsf->
hermit[i], 0,
208 cpl_array_get(aPar, (*offset)++, NULL));
224 muse_sky_master_lsf_firstguess(
void) {
225 cpl_array *pars = cpl_array_new(5 + MAX_HERMIT_ORDER, CPL_TYPE_DOUBLE);
229 cpl_array_set(pars, offset++, 0.0);
231 cpl_array_set(pars, offset++, 0.0);
234 cpl_array_set(pars, offset++, 1.0);
236 cpl_array_set(pars, offset++, 0);
237 cpl_array_set(pars, offset++, 0);
241 for (i = 0; i < MAX_HERMIT_ORDER; i++) {
242 cpl_array_set(pars, offset++, 0.0);
244 if (offset != cpl_array_get_size(pars)) {
245 cpl_msg_error(__func__,
246 "inconsistent array: size %ld, filled with %ld values",
247 (
long)cpl_array_get_size(pars), (
long)offset);
264 static muse_sky_master_params *
265 muse_sky_master_apply_parametrization(
const cpl_array *aPar,
int ngroups) {
267 muse_sky_master_params *p = muse_sky_master_apply_sky_parametrization(aPar, &offset,
269 p->lsf = muse_sky_master_apply_lsf_parametrization(aPar, &offset);
271 if (offset != cpl_array_get_size(aPar)) {
272 cpl_msg_error(__func__,
273 "inconsistent array: size %ld, read with %ld values",
274 (
long)cpl_array_get_size(aPar), (
long)offset);
275 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
276 muse_sky_master_params_delete(p);
296 simulate_master_sky_parameters(
const cpl_table *aLines,
298 const cpl_array *aLambda,
299 const cpl_array *aPar) {
300 muse_sky_master_params *p
301 = muse_sky_master_apply_parametrization(aPar, aNgroups);
303 cpl_array *continuum = cpl_array_duplicate(aLambda);
306 cpl_table *lines = cpl_table_duplicate(aLines);
308 double maxflux = cpl_table_get_column_max(lines,
"flux");
312 cpl_array_add(simulated, continuum);
314 cpl_array_delete(continuum);
315 cpl_table_delete(lines);
316 muse_sky_master_params_delete(p);
322 const cpl_array *lambda;
323 const cpl_array *values;
324 const cpl_array *stat;
325 const cpl_table *sky;
326 const cpl_size ngroups;
327 } muse_master_fit_struct;
340 static cpl_error_code
341 muse_sky_master_eval(
void *aData, cpl_array *aPar, cpl_array *aRetval) {
342 cpl_size size = cpl_array_get_size(aRetval);
343 muse_master_fit_struct *data = aData;
345 = simulate_master_sky_parameters(data->sky, data->ngroups,
348 cpl_array_subtract(simulated, data->values);
350 cpl_array_divide(dsimulated, data->stat);
352 cpl_array_fill_window_double(aRetval, 0, size, 0.0);
353 memcpy(cpl_array_get_data_double(aRetval),
354 cpl_array_get_data_double_const(dsimulated),
355 size *
sizeof(
double));
357 cpl_array_delete(simulated);
358 cpl_array_delete(dsimulated);
360 return CPL_ERROR_NONE;
382 static cpl_error_code
383 muse_sky_master_correct_firstguess(
const cpl_table *aLines,
385 const cpl_array *aLambda,
386 const cpl_array *aData,
391 = simulate_master_sky_parameters(aLines, aNgroups, aLambda, aPars);
394 muse_sky_master_params *msp
395 = muse_sky_master_apply_sky_parametrization(aPars, &offset, aNgroups);
396 cpl_table *lines = cpl_table_duplicate(aLines);
398 muse_sky_master_params_delete(msp);
401 for (i_group = 0; i_group < aNgroups; i_group++) {
403 cpl_table_unselect_all(lines);
404 cpl_table_or_selected_int(lines,
"group", CPL_EQUAL_TO, i_group);
405 cpl_table *gtable = cpl_table_extract_selected(lines);
407 cpl_table_get_column_maxpos(gtable,
"flux", &row);
408 double wavelength = cpl_table_get_double(gtable,
"lambda", row, NULL);
409 cpl_table_delete(gtable);
419 for (i_lbda = i_lbda1; i_lbda <= i_lbda2; i_lbda++) {
420 double lbda = cpl_array_get(aLambda, i_lbda, NULL);
421 double wy_data = cpl_array_get(aData, i_lbda, NULL);
422 double wy_sim = cpl_array_get(simulated, i_lbda, NULL);
424 avg_data += wy_data * lbda;
426 avg_sim += wy_sim * lbda;
431 cpl_array_set(aPars, i_group,
432 cpl_array_get(aPars, i_group, NULL)*sqrt(y_data/y_sim));
435 delta += avg_data - avg_sim;
438 cpl_array_set(aPars, aNgroups,
439 cpl_array_get(aPars, aNgroups, NULL) + delta/aNgroups);
440 cpl_table_delete(lines);
441 cpl_array_delete(simulated);
443 return CPL_ERROR_NONE;
464 const cpl_array *aStat,
const cpl_table *aLines)
466 cpl_ensure(aLambda, CPL_ERROR_NULL_INPUT, NULL);
467 cpl_ensure(aData, CPL_ERROR_NULL_INPUT, NULL);
468 cpl_ensure(aStat, CPL_ERROR_NULL_INPUT, NULL);
469 cpl_ensure(aLines, CPL_ERROR_NULL_INPUT, NULL);
471 cpl_size nstat = cpl_array_get_size(aStat);
472 cpl_ensure(nstat > 0, CPL_ERROR_DATA_NOT_FOUND, NULL);
473 cpl_array *stat = cpl_array_extract(aStat, 0, nstat - 1);
474 cpl_array *s2 = cpl_array_extract(aStat, 1, nstat);
475 cpl_array_add(stat, s2);
476 cpl_array_delete(s2);
477 cpl_array_power(stat, 0.5);
479 muse_master_fit_struct data = {
484 cpl_table_get_column_max(aLines,
"group") + 1
487 cpl_array *pars = muse_sky_master_sky_firstguess(data.ngroups);
488 cpl_array *dpars = muse_sky_master_lsf_firstguess();
489 cpl_array_insert(pars, dpars, cpl_array_get_size(pars));
490 cpl_array_delete(dpars);
496 muse_sky_master_correct_firstguess(aLines, data.ngroups, aLambda, aData, pars);
497 muse_sky_master_correct_firstguess(aLines, data.ngroups, aLambda, aData, pars);
498 muse_sky_master_correct_firstguess(aLines, data.ngroups, aLambda, aData, pars);
500 cpl_size size = cpl_array_get_size(aLambda);
502 int debug = getenv(
"MUSE_DEBUG_LSF_FIT")
503 && atoi(getenv(
"MUSE_DEBUG_LSF_FIT")) > 0;
509 cpl_msg_info(__func__,
"Starting master sky fit");
511 muse_sky_master_eval, &ctrl);
512 if (r != CPL_ERROR_NONE) {
513 cpl_msg_error(__func__,
"Master sky fit failed with error code %i: %s",
514 r, cpl_error_get_message());
516 cpl_msg_info(__func__,
"Master sky fit finished successfully.");
519 muse_sky_master_params *p = muse_sky_master_apply_parametrization(pars, data.ngroups);
521 cpl_array_delete(pars);
524 res->
lines = cpl_table_duplicate(aLines);
526 cpl_propertylist *order = cpl_propertylist_new();
528 cpl_propertylist_append_bool(order,
"flux", TRUE);
529 cpl_table_sort(res->
lines, order);
530 cpl_propertylist_delete(order);
533 cpl_msg_info(__func__,
"refraction index=1%s%g, offset=%f Angstrom",
534 p->lsf->refraction < 1?
"-":
"+",
535 fabs(p->lsf->refraction-1), p->lsf->offset);
537 cpl_array_subtract(continuum, aData);
538 cpl_array_multiply_scalar(continuum, -1);
562 cpl_array_get_size(aLambda));
565 cpl_array_delete(continuum);
568 res->
lsf[0] = p->lsf;
571 cpl_array_delete(stat);
572 muse_sky_master_params_delete(p);
587 cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
591 "SKY_CONTINUUM", 0, CPL_FALSE);
592 if (frames_c == NULL || cpl_frameset_get_size(frames_c) < 1) {
593 cpl_frameset_delete(frames_c);
596 cpl_frame *frame_c = cpl_frameset_get_position(frames_c, 0);
597 const char *fn = cpl_frame_get_filename(frame_c);
601 if (continuum == NULL) {
602 cpl_frameset_delete(frames_c);
607 cpl_frameset_delete(frames_c);
626 cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
633 "SKY_LINES", 0, CPL_FALSE);
634 if (cpl_frameset_get_size(frames_l) < 1) {
635 cpl_frameset_delete(frames_l);
639 cpl_frame *frame_l = cpl_frameset_get_position(frames_l, 0);
640 const char *fn = cpl_frame_get_filename(frame_l);
645 "SKY_CONTINUUM", 0, CPL_FALSE);
646 if (cpl_frameset_get_size(frames_c) < 1) {
647 cpl_frameset_delete(frames_c);
648 cpl_frameset_delete(frames_l);
652 cpl_frame *frame_c = cpl_frameset_get_position(frames_c, 0);
653 fn = cpl_frame_get_filename(frame_c);
658 MUSE_TAG_LSF_PROFILE, 0, CPL_FALSE);
659 cpl_size iframe, nframes = cpl_frameset_get_size(frames_d);
660 for (iframe = 0; iframe < nframes; iframe++) {
661 cpl_frame *frame_d = cpl_frameset_get_position(frames_d, iframe);
662 fn = cpl_frame_get_filename(frame_d);
669 cpl_frameset_delete(frames_d);
670 cpl_frameset_delete(frames_c);
671 cpl_frameset_delete(frames_l);
677 for (iframe = 0; iframe < nframes; iframe++) {
678 cpl_frame *frame_d = cpl_frameset_get_position(frames_d, iframe);
680 CPL_FRAME_GROUP_CALIB, 1);
682 cpl_frameset_delete(frames_l);
683 cpl_frameset_delete(frames_c);
684 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_PROFILE 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
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.