00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #ifdef HAVE_CONFIG_H
00023 #include <config.h>
00024 #endif
00025
00026 #include <math.h>
00027 #include <string.h>
00028
00029 #include "muse_sky.h"
00030 #include "muse_instrument.h"
00031 #include "muse_lsf.h"
00032 #include "muse_optimize.h"
00033 #include "muse_utils.h"
00034 #include "muse_data_format_z.h"
00035
00039
00049
00050 static cpl_array *
00051 muse_sky_lines_firstguess(int ngroups) {
00052 cpl_array *pars = cpl_array_new(ngroups+2, CPL_TYPE_DOUBLE);
00053
00054
00055 cpl_size i;
00056 for (i = 0; i < ngroups; i++) {
00057 cpl_array_set(pars, i, 1e-1);
00058 }
00059 cpl_array_set(pars, ngroups, 0.0);
00060 cpl_array_set(pars, ngroups+1, 0.0);
00061 return pars;
00062 }
00063
00064 typedef struct {
00065 const cpl_array *lambda;
00066 const cpl_array *values;
00067 const cpl_array *weights;
00068 const cpl_table *lines;
00069 const cpl_size ngroups;
00070 const cpl_image *lsfImage;
00071 const muse_wcs *wcs;
00072 } muse_master_fit_struct;
00073
00074
00082
00083 static cpl_array *
00084 simulate_master_sky_parameters(muse_master_fit_struct *aFitData,
00085 const cpl_array *aPar)
00086 {
00087 cpl_table *lines = cpl_table_duplicate(aFitData->lines);
00088 int res = 0;
00089 double fac = cpl_array_get(aPar, cpl_array_get_size(aPar)-2, &res);
00090 double offset = cpl_array_get(aPar, cpl_array_get_size(aPar)-1, &res);
00091 cpl_table_multiply_scalar(lines, "lambda", 1+fac);
00092 cpl_table_add_scalar(lines, "lambda", offset-7000 * fac);
00093 cpl_array *line_strengths = cpl_array_duplicate(aPar);
00094 cpl_array_multiply(line_strengths, aPar);
00095 muse_sky_lines_apply_strength(lines, line_strengths);
00096 cpl_array_delete(line_strengths);
00097
00098 double maxflux = cpl_table_get_column_max(lines, "flux");
00099 muse_sky_lines_cut(lines, 1e-4 * maxflux);
00100
00101 cpl_array *simulated
00102 = muse_sky_lines_spectrum(aFitData->lambda, lines,
00103 aFitData->lsfImage, aFitData->wcs);
00104 cpl_table_delete(lines);
00105
00106 return simulated;
00107 }
00108
00109
00118
00119 static cpl_error_code
00120 muse_sky_master_eval(void *aData, cpl_array *aPar, cpl_array *aRetval)
00121 {
00122 cpl_size size = cpl_array_get_size(aRetval);
00123 muse_master_fit_struct *fitData = aData;
00124 cpl_array *simulated = simulate_master_sky_parameters(fitData, aPar);
00125 cpl_array_subtract(simulated, fitData->values);
00126 cpl_array *dsimulated = muse_cplarray_diff(simulated, 1);
00127
00128 cpl_array_multiply(dsimulated, fitData->weights);
00129
00130 cpl_array_fill_window_double(aRetval, 0, size, 0.0);
00131 memcpy(cpl_array_get_data_double(aRetval),
00132 cpl_array_get_data_double_const(dsimulated),
00133 size * sizeof(double));
00134 cpl_array_delete(simulated);
00135 cpl_array_delete(dsimulated);
00136
00137 return CPL_ERROR_NONE;
00138 }
00139
00140
00153
00154 static cpl_error_code
00155 muse_sky_lines_correct_firstguess(muse_master_fit_struct *aFitData,
00156 cpl_array *aPar)
00157 {
00158
00159 cpl_array *simulated = simulate_master_sky_parameters(aFitData, aPar);
00160
00161 cpl_table *lines = cpl_table_duplicate(aFitData->lines);
00162 cpl_array *line_strengths = cpl_array_duplicate(aPar);
00163 cpl_array_multiply(line_strengths, aPar);
00164 muse_sky_lines_apply_strength(lines, line_strengths);
00165 cpl_array_delete(line_strengths);
00166 cpl_size i_group;
00167 for (i_group = 0; i_group < aFitData->ngroups; i_group++) {
00168
00169 cpl_table_unselect_all(lines);
00170 cpl_table_or_selected_int(lines, "group", CPL_EQUAL_TO, i_group);
00171 cpl_table *gtable = cpl_table_extract_selected(lines);
00172 if (cpl_table_get_nrow(gtable) == 0) {
00173 cpl_table_delete(gtable);
00174 continue;
00175 }
00176 cpl_size row;
00177 cpl_table_get_column_maxpos(gtable, "flux", &row);
00178 double wavelength = cpl_table_get_double(gtable, "lambda", row, NULL);
00179
00180
00181 cpl_size i_lbda1 = muse_cplarray_find_sorted(aFitData->lambda,
00182 wavelength - 3.0);
00183 cpl_size i_lbda2 = muse_cplarray_find_sorted(aFitData->lambda,
00184 wavelength + 3.0);
00185 double y_data = 0;
00186 double y_sim = 0;
00187 double offset = cpl_array_get(aFitData->values, i_lbda1, NULL);
00188 offset += cpl_array_get(aFitData->values, i_lbda2, NULL);
00189 offset /= 2;
00190 cpl_size i_lbda;
00191 for (i_lbda = i_lbda1; i_lbda <= i_lbda2; i_lbda++) {
00192 double wy_data = cpl_array_get(aFitData->values, i_lbda, NULL) - offset;
00193 if (wy_data < 0) wy_data = 0;
00194 double wy_sim = cpl_array_get(simulated, i_lbda, NULL);
00195 y_data += wy_data;
00196 y_sim += wy_sim;
00197 }
00198
00199
00200 if (y_sim > 0) {
00201 double y = cpl_array_get(aPar, i_group, NULL);
00202 cpl_array_set(aPar, i_group, y*sqrt(y_data/y_sim));
00203 }
00204 cpl_table_delete(gtable);
00205 }
00206 cpl_table_delete(lines);
00207 cpl_array_delete(simulated);
00208
00209 return CPL_ERROR_NONE;
00210 }
00211
00212
00225
00226 cpl_error_code
00227 muse_sky_lines_fit(cpl_table *aSpectrum, cpl_table *aLines,
00228 cpl_image *aLsfImage, muse_wcs *aWCS)
00229 {
00230 cpl_ensure_code(aSpectrum, CPL_ERROR_NULL_INPUT);
00231 cpl_ensure_code(aLines, CPL_ERROR_NULL_INPUT);
00232 cpl_size nRows = cpl_table_get_nrow(aSpectrum);
00233 cpl_ensure_code(nRows > 0, CPL_ERROR_DATA_NOT_FOUND);
00234
00235 cpl_array *lambda = muse_cpltable_extract_column(aSpectrum, "lambda");
00236 cpl_array *data = muse_cpltable_extract_column(aSpectrum, "data");
00237 cpl_array *stat = muse_cpltable_extract_column(aSpectrum, "stat");
00238
00239 cpl_array *weights = cpl_array_extract(stat, 0, nRows - 1);
00240 cpl_array *w2 = cpl_array_extract(stat, 1, nRows);
00241 cpl_array_add(weights, w2);
00242 cpl_array_delete(w2);
00243 cpl_array_power(weights, -0.5);
00244
00245
00246 muse_master_fit_struct fit_data = {
00247 lambda,
00248 data,
00249 weights,
00250 aLines,
00251 cpl_table_get_column_max(aLines, "group") + 1,
00252 aLsfImage,
00253 aWCS
00254 };
00255
00256 cpl_array *pars = muse_sky_lines_firstguess(fit_data.ngroups);
00257 muse_sky_lines_correct_firstguess(&fit_data, pars);
00258
00259
00260 int debug = getenv("MUSE_DEBUG_LSF_FIT")
00261 && atoi(getenv("MUSE_DEBUG_LSF_FIT")) > 0;
00262 muse_cpl_optimize_control_t ctrl = {
00263 -1, -1, -1, -1,
00264 debug
00265 };
00266
00267 cpl_msg_info(__func__, "Starting sky line fit");
00268 cpl_error_code r = muse_cpl_optimize_lvmq(&fit_data, pars, nRows-1,
00269 muse_sky_master_eval, &ctrl);
00270 if (r != CPL_ERROR_NONE) {
00271 cpl_msg_error(__func__, "Sky line fit failed with error code %i: %s",
00272 r, cpl_error_get_message());
00273 } else {
00274 int res = 0;
00275 double fac = cpl_array_get(pars, cpl_array_get_size(pars)-2, &res);
00276 double offset = cpl_array_get(pars, cpl_array_get_size(pars)-1, &res);
00277 cpl_table_multiply_scalar(aLines, "lambda", 1+fac);
00278 cpl_table_add_scalar(aLines, "lambda", offset-7000 * fac);
00279 double l_min = cpl_table_get_column_min(aLines, "lambda");
00280 double l_max = cpl_table_get_column_max(aLines, "lambda");
00281 cpl_msg_info(__func__, "Sky line fit finished successfully. "
00282 "Offset %.3f A (at %.0f A) ... %.3f A (at %.0f A)",
00283 offset + (l_min - 7000) *fac, l_min,
00284 offset + (l_max - 7000) * fac, l_max);
00285 }
00286
00287 cpl_array_multiply(pars, pars);
00288 muse_sky_lines_apply_strength(aLines, pars);
00289 cpl_array_delete(pars);
00290
00291 cpl_propertylist *order = cpl_propertylist_new();
00292 cpl_propertylist_append_bool(order, "flux", TRUE);
00293 cpl_table_sort(aLines, order);
00294 cpl_propertylist_delete(order);
00295 cpl_array_unwrap(lambda);
00296 cpl_array_unwrap(data);
00297 cpl_array_unwrap(stat);
00298 cpl_array_delete(weights);
00299
00300 return CPL_ERROR_NONE;
00301 }
00302
00303
00317
00318 cpl_table *
00319 muse_sky_continuum_create(cpl_table *aSpectrum, cpl_table *aLines,
00320 cpl_image *aLsfImage, muse_wcs *aLsfWCS,
00321 double aBinWidth)
00322 {
00323 cpl_ensure(aSpectrum, CPL_ERROR_NULL_INPUT, NULL);
00324 cpl_ensure(aLines, CPL_ERROR_NULL_INPUT, NULL);
00325 cpl_ensure(aLsfImage, CPL_ERROR_NULL_INPUT, NULL);
00326 cpl_ensure(aLsfImage, CPL_ERROR_NULL_INPUT, NULL);
00327
00328 cpl_array *lambda = muse_cpltable_extract_column(aSpectrum, "lambda");
00329 cpl_array *flux = muse_cpltable_extract_column(aSpectrum, "data");
00330 cpl_array *simulated = muse_sky_lines_spectrum(lambda, aLines, aLsfImage, aLsfWCS);
00331 cpl_array_subtract(simulated, flux);
00332 cpl_array_multiply_scalar(simulated, -1.);
00333
00334 double l_min = cpl_array_get_min(lambda);
00335 double l_max = cpl_array_get_max(lambda);
00336 cpl_size n_new = (l_max - l_min) / aBinWidth;
00337 cpl_table *continuum = muse_cpltable_new(muse_fluxspectrum_def, n_new);
00338 cpl_table_fill_column_window(continuum, "flux", 0, n_new, 0.0);
00339 cpl_array *lambda_new = muse_cpltable_extract_column(continuum, "lambda");
00340
00341 cpl_size i;
00342 for (i = 0; i < n_new; i++) {
00343 cpl_table_set(continuum, "lambda", i, l_min + i * aBinWidth);
00344 }
00345 cpl_array *flux_new = muse_cplarray_interpolate_linear(lambda_new,
00346 lambda, simulated);
00347
00348 memcpy(cpl_table_get_data_double(continuum, "flux"),
00349 cpl_array_get_data_double(flux_new),
00350 n_new * sizeof(double));
00351 cpl_array_delete(simulated);
00352 cpl_array_unwrap(lambda);
00353 cpl_array_unwrap(flux);
00354 cpl_array_unwrap(lambda_new);
00355 cpl_array_delete(flux_new);
00356 return continuum;
00357 }
00358
00359
00365
00366 cpl_table *
00367 muse_sky_continuum_load(muse_processing *aProcessing)
00368 {
00369 cpl_ensure(aProcessing, CPL_ERROR_NULL_INPUT, NULL);
00370
00371
00372 cpl_frameset *frames_c = muse_frameset_find(aProcessing->inframes,
00373 MUSE_TAG_SKY_CONT, 0, CPL_FALSE);
00374 if (frames_c == NULL || cpl_frameset_get_size(frames_c) < 1) {
00375 cpl_frameset_delete(frames_c);
00376 cpl_msg_debug(__func__, "No sky continuum found in input frameset!");
00377 return NULL;
00378 }
00379 cpl_frame *frame_c = cpl_frameset_get_position(frames_c, 0);
00380 const char *fn = cpl_frame_get_filename(frame_c);
00381 cpl_table *continuum = muse_cpltable_load(fn, "CONTINUUM",
00382 muse_fluxspectrum_def);
00383
00384 if (continuum == NULL) {
00385 cpl_msg_warning(__func__, "Could not load sky continuum from \"%s\"", fn);
00386 cpl_frameset_delete(frames_c);
00387 return NULL;
00388 }
00389
00390 cpl_msg_info(__func__, "Loaded sky continuum from \"%s\"", fn);
00391 muse_processing_append_used(aProcessing, frame_c, CPL_FRAME_GROUP_CALIB, 1);
00392 cpl_frameset_delete(frames_c);
00393 return continuum;
00394 }
00395