MUSE Pipeline Reference Manual  1.0.2
muse_xcombine.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 02110-1301, USA.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25 
26 /*----------------------------------------------------------------------------*
27  * Includes *
28  *----------------------------------------------------------------------------*/
29 #include <cpl.h>
30 
31 #include "muse_xcombine.h"
32 
33 #include "muse_pfits.h"
34 #include "muse_wcs.h"
35 #include "muse_utils.h"
36 
37 /*----------------------------------------------------------------------------*/
41 /*----------------------------------------------------------------------------*/
42 
45 /*----------------------------------------------------------------------------*/
79 /*----------------------------------------------------------------------------*/
80 cpl_error_code
82 {
83  cpl_ensure_code(aPixtables, CPL_ERROR_NULL_INPUT);
84  unsigned int npt = 0;
85  while (aPixtables[npt++]) ; /* count tables, including first NULL table */
86  cpl_ensure_code(--npt > 1, CPL_ERROR_ILLEGAL_INPUT); /* subtract NULL table */
87  if (aWeighting == MUSE_XCOMBINE_NONE) {
88  cpl_msg_info(__func__, "%d tables, not weighting them", npt);
89  return CPL_ERROR_NONE;
90  }
91  if (aWeighting != MUSE_XCOMBINE_EXPTIME && aWeighting != MUSE_XCOMBINE_FWHM) {
92  cpl_msg_warning(__func__, "Unknown exposure weighting scheme (%d)",
93  aWeighting);
94  return cpl_error_set(__func__, CPL_ERROR_UNSUPPORTED_MODE);
95  }
96 
97  cpl_msg_info(__func__, "%d tables to be weighted using %s", npt,
98  aWeighting == MUSE_XCOMBINE_EXPTIME ? "EXPTIME"
99  : "EXPTIME & FWHM");
100  double exptime0 = muse_pfits_get_exptime(aPixtables[0]->header);
101  if (exptime0 == 0.0) {
102  return cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
103  }
104  double fwhm0 = aWeighting == MUSE_XCOMBINE_FWHM
105  ? (muse_pfits_get_fwhm_start(aPixtables[0]->header)
106  + muse_pfits_get_fwhm_end(aPixtables[0]->header)) / 2.
107  : 1.;
108 
109  /* add and fill the "weight" column in all pixel tables */
110  unsigned int i;
111  for (i = 0; i < npt; i++) {
112  double exptime = muse_pfits_get_exptime(aPixtables[i]->header),
113  weight = exptime / exptime0;
114  if (!cpl_table_has_column(aPixtables[i]->table, MUSE_PIXTABLE_WEIGHT)) {
115  cpl_table_new_column(aPixtables[i]->table, MUSE_PIXTABLE_WEIGHT,
116  CPL_TYPE_FLOAT);
117  }
118  /* modify the "weight" column depending on ambient seeing */
119  if (aWeighting == MUSE_XCOMBINE_FWHM) {
120  cpl_errorstate prestate = cpl_errorstate_get();
121  double fwhm = (muse_pfits_get_fwhm_start(aPixtables[i]->header)
122  + muse_pfits_get_fwhm_end(aPixtables[i]->header)) / 2.;
123  if (fwhm == 0. || !cpl_errorstate_is_equal(prestate)) {
124  cpl_msg_warning(__func__, "No seeing info in table %d. Weighting it "
125  "equal to first table!", i+1);
126  fwhm = fwhm0;
127  }
128  weight *= fwhm0 / fwhm;
129  }
130  cpl_msg_debug(__func__, "Table %d, weight = %f", i+1, weight);
131  cpl_table_fill_column_window_float(aPixtables[i]->table,
132  MUSE_PIXTABLE_WEIGHT,
133  0, muse_pixtable_get_nrow(aPixtables[i]),
134  weight);
135  /* add the status header */
136  cpl_propertylist_update_bool(aPixtables[i]->header, MUSE_HDR_PT_WEIGHTED,
137  CPL_TRUE);
138  cpl_propertylist_set_comment(aPixtables[i]->header, MUSE_HDR_PT_WEIGHTED,
139  MUSE_HDR_PT_WEIGHTED_COMMENT);
140  } /* for i (table index) */
141 
142  return CPL_ERROR_NONE;
143 } /* muse_xcombine_weights() */
144 
145 /*----------------------------------------------------------------------------*/
190 /*----------------------------------------------------------------------------*/
193 {
194  cpl_ensure(aPixtables, CPL_ERROR_NULL_INPUT, NULL);
195  unsigned int npt = 0;
196  while (aPixtables[npt++]) ; /* count tables, including first NULL table */
197  cpl_ensure(--npt > 1, CPL_ERROR_ILLEGAL_INPUT, NULL); /* subtract NULL table */
198  cpl_ensure(muse_pixtable_wcs_check(aPixtables[0]) == MUSE_PIXTABLE_WCS_NATSPH,
199  CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
200  cpl_msg_info(__func__, "%u tables to be combined", npt);
201 
202  /* check for environment variables with exposure offsets (in deg) */
203  cpl_array *dra = NULL,
204  *ddec = NULL;
205  char *raenv = getenv("MUSE_XCOMBINE_RA_OFFSETS"),
206  *decenv = getenv("MUSE_XCOMBINE_DEC_OFFSETS");
207  if (raenv) {
208  dra = muse_cplarray_new_from_delimited_string(raenv, ",");
209  unsigned int nra = cpl_array_get_size(dra);
210  if (nra != npt) {
211  cpl_msg_warning(__func__, "Found %u RA offsets for %u exposures, not "
212  "using them!", nra, npt);
213  cpl_array_delete(dra);
214  dra = NULL;
215  } else {
216  cpl_msg_info(__func__, "Using %u RA offsets", nra);
217  }
218  }
219  if (decenv) {
220  ddec = muse_cplarray_new_from_delimited_string(decenv, ",");
221  unsigned int ndec = cpl_array_get_size(ddec);
222  if (ndec != npt) {
223  cpl_msg_warning(__func__, "Found %u DEC offsets for %u exposures, not "
224  "using them!", ndec, npt);
225  cpl_array_delete(ddec);
226  ddec = NULL;
227  } else {
228  cpl_msg_info(__func__, "Using %u DEC offsets", ndec);
229  }
230  }
231 
232  double timeinit = cpl_test_get_walltime(),
233  cpuinit = cpl_test_get_cputime();
234  muse_utils_memory_dump("muse_xcombine_tables() start");
235  muse_pixtable *pt = aPixtables[0];
236  aPixtables[0] = NULL;
237  /* warn, if the table was not RV corrected */
238  if (!muse_pixtable_is_rvcorr(pt)) {
239  cpl_msg_warning(__func__, "Data of exposure 1 (DATE-OBS=%s) was not radial-"
240  "velocity corrected!", muse_pfits_get_dateobs(pt->header));
241  }
242  /* change exposure number in offset keywords to 1 */
244  /* add exposure range */
245  char keyword[KEYWORD_LENGTH], comment[KEYWORD_LENGTH];
246  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST, 1);
247  cpl_propertylist_append_long_long(pt->header, keyword, 0);
248  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST_COMMENT, 1);
249  cpl_propertylist_set_comment(pt->header, keyword, comment);
250  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST, 1);
251  cpl_propertylist_append_long_long(pt->header, keyword,
252  muse_pixtable_get_nrow(pt) - 1);
253  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST_COMMENT, 1);
254  cpl_propertylist_set_comment(pt->header, keyword, comment);
255 
256  double ra0 = muse_pfits_get_ra(pt->header),
257  dec0 = muse_pfits_get_dec(pt->header);
258  if (dra) {
259  double raoff = atof(cpl_array_get_string(dra, 0));
260  ra0 -= raoff;
261  /* store in the header of the output pixel table */
262  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DRA, 1);
263  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DRA_C, raoff * 3600.);
264  cpl_propertylist_append_double(pt->header, keyword, raoff);
265  cpl_propertylist_set_comment(pt->header, keyword, comment);
266  }
267  if (ddec) {
268  double decoff = atof(cpl_array_get_string(ddec, 0));
269  dec0 -= decoff;
270  /* store in the header of the output pixel table */
271  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DDEC, 1);
272  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DDEC_C, decoff * 3600.);
273  cpl_propertylist_append_double(pt->header, keyword, decoff);
274  cpl_propertylist_set_comment(pt->header, keyword, comment);
275  }
276  if (dra || ddec) {
277  /* store in the header of the output pixel table */
278  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DATEOBS, 1);
279  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DATEOBS_C, 1);
280  cpl_propertylist_append_string(pt->header, keyword,
282  cpl_propertylist_set_comment(pt->header, keyword, comment);
283  }
284  muse_wcs_position_celestial(pt, ra0, dec0);
285 
286  unsigned int i, nskipped = 0;
287  for (i = 1; i < npt; i++) {
288  if (muse_pixtable_wcs_check(aPixtables[i]) != MUSE_PIXTABLE_WCS_NATSPH) {
289  cpl_msg_warning(__func__, "Exposure %d was not projected to native "
290  "spherical coordinates, skipping this one!", i + 1);
291  nskipped++;
292  continue;
293  }
294  if (!muse_pixtable_is_rvcorr(pt)) {
295  cpl_msg_warning(__func__, "Data of exposure %u (DATE-OBS=%s) was not "
296  "radial-velocity corrected!", i+1,
297  muse_pfits_get_dateobs(aPixtables[i]->header));
298  }
299 
300  /* apply spherical coordinate rotation to coordinates of the first exposure */
301  double ra = muse_pfits_get_ra(aPixtables[i]->header),
302  dec = muse_pfits_get_dec(aPixtables[i]->header);
303  if (dra) {
304  double raoff = atof(cpl_array_get_string(dra, i));
305  ra -= raoff;
306  cpl_msg_debug(__func__, "positioning not to RA %f but to %f (dRA = %f "
307  "deg)", ra + raoff, ra, raoff);
308  /* store in the header of the output pixel table */
309  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DRA, i + 1);
310  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DRA_C, raoff * 3600.);
311  cpl_propertylist_append_double(pt->header, keyword, raoff);
312  cpl_propertylist_set_comment(pt->header, keyword, comment);
313  }
314  if (ddec) {
315  double decoff = atof(cpl_array_get_string(ddec, i));
316  dec -= decoff;
317  cpl_msg_debug(__func__, "positioning not to DEC %f but to %f (dDEC = %f "
318  "deg)", dec + decoff, dec, decoff);
319  /* store in the header of the output pixel table */
320  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DDEC, i + 1);
321  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DDEC_C, decoff * 3600.);
322  cpl_propertylist_append_double(pt->header, keyword, decoff);
323  cpl_propertylist_set_comment(pt->header, keyword, comment);
324  }
325  if (dra || ddec) {
326  /* store in the header of the output pixel table */
327  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DATEOBS, i + 1);
328  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_OFFSETi_DATEOBS_C, i + 1);
329  cpl_propertylist_append_string(pt->header, keyword,
330  muse_pfits_get_dateobs(aPixtables[i]->header));
331  cpl_propertylist_set_comment(pt->header, keyword, comment);
332  }
333  muse_wcs_position_celestial(aPixtables[i], ra, dec);
334 
335  /* Shift the x/y coordinates depending on their relative zeropoint! */
336  double raoffset = ra - ra0,
337  decoffset = dec - dec0;
338 #if 0
339  /* this is not switched on, since it actually degrades performance by *
340  * 10% compared to the state before, without this offset correction */
341  float *xpos = cpl_table_get_data_float(aPixtables[i]->table, MUSE_PIXTABLE_XPOS),
342  *ypos = cpl_table_get_data_float(aPixtables[i]->table, MUSE_PIXTABLE_YPOS);
343  cpl_size irow, nrowi = muse_pixtable_get_nrow(aPixtables[i]);
344  #pragma omp parallel for default(none) /* as req. by Ralf */ \
345  shared(decoffset, nrowi, raoffset, xpos, ypos)
346  for (irow = 0; irow < nrowi; irow++) {
347  xpos[irow] += raoffset;
348  ypos[irow] += decoffset;
349  } /* for irow */
350 #else
351  /* using these functions, the speed degradation is not measurable */
352  cpl_table_add_scalar(aPixtables[i]->table, MUSE_PIXTABLE_XPOS, raoffset);
353  cpl_table_add_scalar(aPixtables[i]->table, MUSE_PIXTABLE_YPOS, decoffset);
354 #endif
355 
356  /* compute simple (inaccurate!) offset for information */
357  double avdec = (dec + dec0) / 2.,
358  raoff = (ra - ra0) * cos(avdec * CPL_MATH_RAD_DEG),
359  decoff = dec - dec0;
360  cpl_msg_info(__func__, "Approx. offset of exposure %u: %.3e,%.3e deg", i+1,
361  raoff, decoff);
362 
363  /* append the next pixel table to the end and delete the original */
364  cpl_size nrow = muse_pixtable_get_nrow(pt);
365  cpl_table_insert(pt->table, aPixtables[i]->table, nrow);
366  /* copy the offset headers to the output pixel table before deleting it */
367  muse_pixtable_origin_copy_offsets(pt, aPixtables[i], i + 1);
368  muse_pixtable_delete(aPixtables[i]);
369  aPixtables[i] = NULL;
370 
371  /* add respective exposure range to the header */
372  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST, i + 1);
373  cpl_propertylist_append_long_long(pt->header, keyword, nrow);
374  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST_COMMENT, i + 1);
375  cpl_propertylist_set_comment(pt->header, keyword, comment);
376  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST, i + 1);
377  cpl_propertylist_append_long_long(pt->header, keyword,
378  muse_pixtable_get_nrow(pt) - 1);
379  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST_COMMENT, i + 1);
380  cpl_propertylist_set_comment(pt->header, keyword, comment);
381  } /* for i (pixel tables) */
382  cpl_array_delete(dra);
383  cpl_array_delete(ddec);
385  /* update the merge-related status header */
386  cpl_propertylist_update_int(pt->header, MUSE_HDR_PT_COMBINED, npt - nskipped);
387  cpl_propertylist_set_comment(pt->header, MUSE_HDR_PT_COMBINED,
388  MUSE_HDR_PT_COMBINED_COMMENT);
389  /* debug timing */
390  double timefini = cpl_test_get_walltime(),
391  cpufini = cpl_test_get_cputime();
392  muse_utils_memory_dump("muse_xcombine_tables() end");
393  cpl_msg_debug(__func__, "Combining %u tables took %gs (wall-clock) and %gs "
394  "(CPU)", npt, timefini - timeinit, cpufini - cpuinit);
395  return pt;
396 } /* muse_xcombine_tables() */
397 
#define MUSE_HDR_PT_EXP_FST
FITS header keyword defining the first row index for a given exposure.
Definition: muse_pixtable.h:87
double muse_pfits_get_ra(const cpl_propertylist *aHeaders)
find out the right ascension
Definition: muse_pfits.c:232
cpl_size muse_pixtable_get_nrow(const muse_pixtable *aPixtable)
get the number of rows within the pixel table
#define MUSE_HDR_PT_EXP_LST
FITS header keyword defining the last row index for a given exposure.
Definition: muse_pixtable.h:92
void muse_utils_memory_dump(const char *aMarker)
Display the current memory usage of the given program.
Definition: muse_utils.c:2621
const char * muse_pfits_get_dateobs(const cpl_propertylist *aHeaders)
find out the date of observations
Definition: muse_pfits.c:329
#define MUSE_HDR_PT_COMBINED
cpl_table * table
The pixel table.
cpl_error_code muse_xcombine_weights(muse_pixtable **aPixtables, muse_xcombine_types aWeighting)
compute the weights for combination of two or more exposures
Definition: muse_xcombine.c:81
cpl_error_code muse_pixtable_origin_copy_offsets(muse_pixtable *aOut, muse_pixtable *aFrom, unsigned int aNum)
Copy MUSE_HDR_PT_IFU_SLICE_OFFSET keywords between pixel tables.
cpl_array * muse_cplarray_new_from_delimited_string(const char *aString, const char *aDelim)
Convert a delimited string into an array of strings.
double muse_pfits_get_fwhm_end(const cpl_propertylist *aHeaders)
find out the ambient seeing at end of exposure (in arcsec)
Definition: muse_pfits.c:950
Structure definition of MUSE pixel table.
#define MUSE_HDR_PT_WEIGHTED
muse_pixtable_wcs muse_pixtable_wcs_check(muse_pixtable *aPixtable)
Check the state of the world coordinate system of a pixel table.
double muse_pfits_get_fwhm_start(const cpl_propertylist *aHeaders)
find out the ambient seeing at start of exposure (in arcsec)
Definition: muse_pfits.c:931
double muse_pfits_get_dec(const cpl_propertylist *aHeaders)
find out the declination
Definition: muse_pfits.c:250
muse_pixtable * muse_xcombine_tables(muse_pixtable **aPixtables)
combine the pixel tables of several exposures into one
double muse_pfits_get_exptime(const cpl_propertylist *aHeaders)
find out the exposure time
Definition: muse_pfits.c:347
cpl_error_code muse_wcs_position_celestial(muse_pixtable *aPixtable, double aRA, double aDEC)
Convert native to celestial spherical coordinates in a pixel table.
Definition: muse_wcs.c:1133
void muse_pixtable_delete(muse_pixtable *aPixtable)
Deallocate memory associated to a pixel table object.
muse_xcombine_types
Xposure combination types.
Definition: muse_xcombine.h:44
cpl_error_code muse_pixtable_compute_limits(muse_pixtable *aPixtable)
(Re-)Compute the limits of the coordinate columns of a pixel table.
cpl_propertylist * header
The FITS header.
cpl_boolean muse_pixtable_is_rvcorr(muse_pixtable *aPixtable)
Determine whether the pixel table is radial-velocity corrected.