MUSE Pipeline Reference Manual  0.18.1
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  *
5  * This file is part of the MUSE Instrument Pipeline
6  * Copyright (C) 2005-2011 European Southern Observatory
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 /*----------------------------------------------------------------------------*
28  * Includes *
29  *----------------------------------------------------------------------------*/
30 #include <cpl.h>
31 
32 #include "muse_xcombine.h"
33 
34 #include "muse_pfits.h"
35 #include "muse_wcs.h"
36 #include "muse_utils.h"
37 
38 /*----------------------------------------------------------------------------*/
42 /*----------------------------------------------------------------------------*/
43 
46 /*----------------------------------------------------------------------------*/
80 /*----------------------------------------------------------------------------*/
81 cpl_error_code
83 {
84  cpl_ensure_code(aPixtables, CPL_ERROR_NULL_INPUT);
85  unsigned int npt = 0;
86  while (aPixtables[npt++]) ; /* count tables, including first NULL table */
87  cpl_ensure_code(--npt > 1, CPL_ERROR_ILLEGAL_INPUT); /* subtract NULL table */
88  if (aWeighting != MUSE_XCOMBINE_EXPTIME && aWeighting != MUSE_XCOMBINE_FWHM) {
89  cpl_msg_warning(__func__, "Unknown exposure weighting scheme (%d)",
90  aWeighting);
91  return cpl_error_set(__func__, CPL_ERROR_UNSUPPORTED_MODE);
92  }
93 
94  cpl_msg_info(__func__, "%d tables to be weighted using %s", npt,
95  aWeighting == MUSE_XCOMBINE_EXPTIME ? "EXPTIME"
96  : "EXPTIME & FWHM");
97  double exptime0 = muse_pfits_get_exptime(aPixtables[0]->header);
98  if (exptime0 == 0.0) {
99  return cpl_error_set(__func__, CPL_ERROR_INCOMPATIBLE_INPUT);
100  }
101  double fwhm0 = aWeighting == MUSE_XCOMBINE_FWHM
102  ? (muse_pfits_get_fwhm_start(aPixtables[0]->header)
103  + muse_pfits_get_fwhm_end(aPixtables[0]->header)) / 2.
104  : 1.;
105 
106  /* add and fill the "weight" column in all pixel tables */
107  unsigned int i;
108  for (i = 0; i < npt; i++) {
109  double exptime = muse_pfits_get_exptime(aPixtables[i]->header),
110  weight = exptime / exptime0;
111  if (!cpl_table_has_column(aPixtables[i]->table, MUSE_PIXTABLE_WEIGHT)) {
112  cpl_table_new_column(aPixtables[i]->table, MUSE_PIXTABLE_WEIGHT,
113  CPL_TYPE_FLOAT);
114  }
115  /* modify the "weight" column depending on ambient seeing */
116  if (aWeighting == MUSE_XCOMBINE_FWHM) {
117  cpl_errorstate prestate = cpl_errorstate_get();
118  double fwhm = (muse_pfits_get_fwhm_start(aPixtables[i]->header)
119  + muse_pfits_get_fwhm_end(aPixtables[i]->header)) / 2.;
120  if (fwhm == 0. || !cpl_errorstate_is_equal(prestate)) {
121  cpl_msg_warning(__func__, "No seeing info in table %d. Weighting it "
122  "equal to first table!", i+1);
123  fwhm = fwhm0;
124  }
125  weight *= fwhm0 / fwhm;
126  }
127  cpl_msg_debug(__func__, "Table %d, weight = %f", i+1, weight);
128  cpl_table_fill_column_window_float(aPixtables[i]->table,
129  MUSE_PIXTABLE_WEIGHT,
130  0, muse_pixtable_get_nrow(aPixtables[i]),
131  weight);
132  /* add the status header */
133  cpl_propertylist_update_bool(aPixtables[i]->header, MUSE_HDR_PT_WEIGHTED,
134  CPL_TRUE);
135  cpl_propertylist_set_comment(aPixtables[i]->header, MUSE_HDR_PT_WEIGHTED,
136  MUSE_HDR_PT_WEIGHTED_COMMENT);
137  } /* for i (table index) */
138 
139  return CPL_ERROR_NONE;
140 } /* muse_xcombine_weights() */
141 
142 /*----------------------------------------------------------------------------*/
182 /*----------------------------------------------------------------------------*/
185 {
186  cpl_ensure(aPixtables, CPL_ERROR_NULL_INPUT, NULL);
187  unsigned int npt = 0;
188  while (aPixtables[npt++]) ; /* count tables, including first NULL table */
189  cpl_ensure(--npt > 1, CPL_ERROR_ILLEGAL_INPUT, NULL); /* subtract NULL table */
190  cpl_ensure(muse_pixtable_wcs_check(aPixtables[0]) == MUSE_PIXTABLE_WCS_NATSPH,
191  CPL_ERROR_INCOMPATIBLE_INPUT, NULL);
192  cpl_msg_info(__func__, "%u tables to be combined", npt);
193 
194  /* check for environment variables with exposure offsets (in deg) */
195  cpl_array *dra = NULL,
196  *ddec = NULL;
197  char *raenv = getenv("MUSE_XCOMBINE_RA_OFFSETS"),
198  *decenv = getenv("MUSE_XCOMBINE_DEC_OFFSETS");
199  if (raenv) {
200  dra = muse_cplarray_new_from_delimited_string(raenv, ",");
201  unsigned int nra = cpl_array_get_size(dra);
202  if (nra != npt) {
203  cpl_msg_warning(__func__, "Found %u RA offsets for %u exposures, not "
204  "using them!", nra, npt);
205  cpl_array_delete(dra);
206  dra = NULL;
207  } else {
208  cpl_msg_info(__func__, "Using %u RA offsets", nra);
209  }
210  }
211  if (decenv) {
212  ddec = muse_cplarray_new_from_delimited_string(decenv, ",");
213  unsigned int ndec = cpl_array_get_size(ddec);
214  if (ndec != npt) {
215  cpl_msg_warning(__func__, "Found %u DEC offsets for %u exposures, not "
216  "using them!", ndec, npt);
217  cpl_array_delete(ddec);
218  ddec = NULL;
219  } else {
220  cpl_msg_info(__func__, "Using %u DEC offsets", ndec);
221  }
222  }
223 
224  double timeinit = cpl_test_get_walltime(),
225  cpuinit = cpl_test_get_cputime();
226  muse_utils_memory_dump("muse_xcombine_tables() start");
227  muse_pixtable *pt = aPixtables[0];
228  aPixtables[0] = NULL;
229  /* change exposure number in offset keywords to 1 */
231  /* add exposure range */
232  char keyword[KEYWORD_LENGTH], comment[KEYWORD_LENGTH];
233  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST, 1);
234  cpl_propertylist_append_long_long(pt->header, keyword, 0);
235  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST_COMMENT, 1);
236  cpl_propertylist_set_comment(pt->header, keyword, comment);
237  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST, 1);
238  cpl_propertylist_append_long_long(pt->header, keyword,
239  muse_pixtable_get_nrow(pt) - 1);
240  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST_COMMENT, 1);
241  cpl_propertylist_set_comment(pt->header, keyword, comment);
242 
243  double ra0 = muse_pfits_get_ra(pt->header),
244  dec0 = muse_pfits_get_dec(pt->header);
245  if (dra) {
246  ra0 -= atof(cpl_array_get_string(dra, 0));
247  }
248  if (ddec) {
249  dec0 -= atof(cpl_array_get_string(ddec, 0));
250  }
251  muse_wcs_position_celestial(pt, ra0, dec0);
252 
253  unsigned int i, nskipped = 0;
254  for (i = 1; i < npt; i++) {
255  if (muse_pixtable_wcs_check(aPixtables[i]) != MUSE_PIXTABLE_WCS_NATSPH) {
256  cpl_msg_warning(__func__, "Exposure %d was not projected to native "
257  "spherical coordinates, skipping this one!", i + 1);
258  nskipped++;
259  continue;
260  }
261 
262  /* apply spherical coordinate rotation to coordinates of the first exposure */
263  double ra = muse_pfits_get_ra(aPixtables[i]->header),
264  dec = muse_pfits_get_dec(aPixtables[i]->header);
265  if (dra) {
266  ra -= atof(cpl_array_get_string(dra, i));
267  cpl_msg_debug(__func__, "positioning not to RA %f but to %f",
268  ra + atof(cpl_array_get_string(dra, i)), ra);
269  }
270  if (ddec) {
271  dec -= atof(cpl_array_get_string(ddec, i));
272  cpl_msg_debug(__func__, "positioning not to DEC %f but to %f",
273  dec + atof(cpl_array_get_string(ddec, i)), dec);
274  }
275  muse_wcs_position_celestial(aPixtables[i], ra, dec);
276 
277  /* Shift the x/y coordinates depending on their relative zeropoint! */
278  double raoffset = ra - ra0,
279  decoffset = dec - dec0;
280 #if 0
281  /* this is not switched on, since it actually degrades performance by *
282  * 10% compared to the state before, without this offset correction */
283  float *xpos = cpl_table_get_data_float(aPixtables[i]->table, MUSE_PIXTABLE_XPOS),
284  *ypos = cpl_table_get_data_float(aPixtables[i]->table, MUSE_PIXTABLE_YPOS);
285  cpl_size irow, nrowi = muse_pixtable_get_nrow(aPixtables[i]);
286  #pragma omp parallel for default(none) /* as req. by Ralf */ \
287  shared(decoffset, nrowi, raoffset, xpos, ypos)
288  for (irow = 0; irow < nrowi; irow++) {
289  xpos[irow] += raoffset;
290  ypos[irow] += decoffset;
291  } /* for irow */
292 #else
293  /* using these functions, the speed degradation is not measurable */
294  cpl_table_add_scalar(aPixtables[i]->table, MUSE_PIXTABLE_XPOS, raoffset);
295  cpl_table_add_scalar(aPixtables[i]->table, MUSE_PIXTABLE_YPOS, decoffset);
296 #endif
297 
298  /* compute simple (inaccurate!) offset for information */
299  double avdec = (dec + dec0) / 2.,
300  raoff = (ra - ra0) * cos(avdec * CPL_MATH_RAD_DEG),
301  decoff = dec - dec0;
302  cpl_msg_info(__func__, "Approx. offset of exposure %u: %.3e,%.3e deg", i+1,
303  raoff, decoff);
304 
305  /* append the next pixel table to the end and delete the original */
306  cpl_size nrow = muse_pixtable_get_nrow(pt);
307  cpl_table_insert(pt->table, aPixtables[i]->table, nrow);
308  /* copy the offset headers to the output pixel table before deleting it */
309  muse_pixtable_origin_copy_offsets(pt, aPixtables[i], i + 1);
310  muse_pixtable_delete(aPixtables[i]);
311  aPixtables[i] = NULL;
312 
313  /* add respective exposure range to the header */
314  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST, i + 1);
315  cpl_propertylist_append_long_long(pt->header, keyword, nrow);
316  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_FST_COMMENT, i + 1);
317  cpl_propertylist_set_comment(pt->header, keyword, comment);
318  snprintf(keyword, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST, i + 1);
319  cpl_propertylist_append_long_long(pt->header, keyword,
320  muse_pixtable_get_nrow(pt) - 1);
321  snprintf(comment, KEYWORD_LENGTH, MUSE_HDR_PT_EXP_LST_COMMENT, i + 1);
322  cpl_propertylist_set_comment(pt->header, keyword, comment);
323  } /* for i (pixel tables) */
324  cpl_array_delete(dra);
325  cpl_array_delete(ddec);
327  /* update the merge-related status header */
328  cpl_propertylist_update_int(pt->header, MUSE_HDR_PT_COMBINED, npt - nskipped);
329  cpl_propertylist_set_comment(pt->header, MUSE_HDR_PT_COMBINED,
330  MUSE_HDR_PT_COMBINED_COMMENT);
331  /* debug timing */
332  double timefini = cpl_test_get_walltime(),
333  cpufini = cpl_test_get_cputime();
334  muse_utils_memory_dump("muse_xcombine_tables() end");
335  cpl_msg_debug(__func__, "Combining %u tables took %gs (wall-clock) and %gs "
336  "(CPU)", npt, timefini - timeinit, cpufini - cpuinit);
337  return pt;
338 } /* muse_xcombine_tables() */
339 
#define MUSE_HDR_PT_EXP_FST
FITS header keyword defining the first row index for a given exposure.
Definition: muse_pixtable.h:85
cpl_size muse_pixtable_get_nrow(muse_pixtable *aPixtable)
get the number of rows within the pixel table
double muse_pfits_get_ra(const cpl_propertylist *aHeaders)
find out the right ascension
Definition: muse_pfits.c:206
#define MUSE_HDR_PT_EXP_LST
FITS header keyword defining the last row index for a given exposure.
Definition: muse_pixtable.h:90
void muse_utils_memory_dump(const char *aMarker)
Display the current memory usage of the given program.
Definition: muse_utils.c:2272
#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:82
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:858
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:840
double muse_pfits_get_dec(const cpl_propertylist *aHeaders)
find out the declination
Definition: muse_pfits.c:224
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:278
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:1132
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:45
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.