MUSE Pipeline Reference Manual  1.0.2
muse_pixgrid.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 #include <math.h>
31 #include <string.h>
32 
33 #include "muse_pixgrid.h"
34 
35 #include "muse_quality.h"
36 #include "muse_utils.h"
37 #include "muse_wcs.h"
38 
39 /*----------------------------------------------------------------------------*/
47 /*----------------------------------------------------------------------------*/
48 
51 /*---------------------------------------------------------------------------*/
60 /*---------------------------------------------------------------------------*/
61 static muse_pixgrid *
62 muse_pixgrid_new(cpl_size aSizeX, cpl_size aSizeY, cpl_size aSizeZ)
63 {
64  muse_pixgrid *pixels = cpl_calloc(1, sizeof(muse_pixgrid));
65  pixels->size_x = aSizeX;
66  pixels->size_y = aSizeY;
67  pixels->size_z = aSizeZ;
68  cpl_size size = aSizeX * aSizeY * aSizeZ;
69  pixels->pix = cpl_calloc(size, sizeof(cpl_size));
70  return pixels;
71 } /* muse_pixgrid_new() */
72 
73 /*---------------------------------------------------------------------------*/
88 /*---------------------------------------------------------------------------*/
89 static void
90 muse_pixgrid_add(muse_pixgrid *aPixels, cpl_size aIndex, cpl_size aRow)
91 {
92  if (aIndex < 0) {
93  return;
94  }
95  if (aPixels->pix[aIndex] == 0 && aRow > 0) {
96  /* First pixel is stored directly. */
97  aPixels->pix[aIndex] = aRow;
98  } else if (aPixels->pix[aIndex] == 0 && aRow == 0) {
99  /* Special case: we cannot put "0" into the main map. */
100  cpl_size i_ext = aPixels->n_ext++;
101  if (aPixels->n_ext > aPixels->n_alloc) { /* double the number of allocated entries */
102  aPixels->n_alloc = 2 * aPixels->n_ext;
103  aPixels->ext = cpl_realloc(aPixels->ext,
104  aPixels->n_alloc * sizeof(muse_pixels_ext));
105  }
106  aPixels->ext[i_ext].npix = 1;
107  aPixels->ext[i_ext].pix = cpl_malloc(sizeof(cpl_size));
108  aPixels->ext[i_ext].pix[0] = aRow ;
109  aPixels->pix[aIndex] = - (i_ext + 1);
110  } else if (aPixels->pix[aIndex] > 0) {
111  /* When a second pixel is added, put both to the extension map. */
112  cpl_size i_ext = aPixels->n_ext++;
113  if (aPixels->n_ext > aPixels->n_alloc) { /* double the number of allocated entries */
114  aPixels->n_alloc = 2 * aPixels->n_ext;
115  aPixels->ext = cpl_realloc(aPixels->ext,
116  aPixels->n_alloc * sizeof(muse_pixels_ext));
117  }
118  aPixels->ext[i_ext].npix = 2;
119  aPixels->ext[i_ext].pix = cpl_malloc(2 * sizeof(cpl_size));
120  aPixels->ext[i_ext].pix[0] = aPixels->pix[aIndex];
121  aPixels->ext[i_ext].pix[1] = aRow;
122  aPixels->pix[aIndex] = - (i_ext + 1);
123  } else {
124  /* Append additional pixels to the extension map. */
125  cpl_size i_ext = - aPixels->pix[aIndex] - 1;
126  int i_pix = aPixels->ext[i_ext].npix;
127  aPixels->ext[i_ext].npix++;
128  aPixels->ext[i_ext].pix
129  = cpl_realloc(aPixels->ext[i_ext].pix,
130  aPixels->ext[i_ext].npix * sizeof(cpl_size));
131  aPixels->ext[i_ext].pix[i_pix] = aRow;
132  }
133 } /* muse_pixgrid_add() */
134 
135 /*---------------------------------------------------------------------------*/
166 /*---------------------------------------------------------------------------*/
167 muse_pixgrid *
168 muse_pixgrid_create(muse_pixtable *aPixtable, cpl_propertylist *aHeader,
169  cpl_size aXSize, cpl_size aYSize, cpl_size aZSize)
170 {
171  cpl_ensure(aPixtable, CPL_ERROR_NULL_INPUT, NULL);
172  cpl_size nrow = muse_pixtable_get_nrow(aPixtable);
173  if (nrow == 0) {
174  cpl_msg_error(__func__, "Invalid pixel table (no entries?)");
175  cpl_error_set(__func__, CPL_ERROR_NULL_INPUT);
176  return NULL;
177  }
178  cpl_ensure(aXSize > 0 && aYSize > 0 && aZSize > 0, CPL_ERROR_ILLEGAL_INPUT,
179  NULL);
180  muse_pixtable_wcs wcstype = muse_pixtable_wcs_check(aPixtable);
181  cpl_ensure(wcstype == MUSE_PIXTABLE_WCS_CELSPH ||
182  wcstype == MUSE_PIXTABLE_WCS_PIXEL, CPL_ERROR_UNSUPPORTED_MODE,
183  NULL);
184 
185  double crval3 = cpl_propertylist_get_double(aHeader, "CRVAL3"),
186  crpix3 = cpl_propertylist_get_double(aHeader, "CRPIX3"),
187  cd33 = cpl_propertylist_get_double(aHeader, "CD3_3");
188  muse_wcs *wcs = muse_wcs_new(aHeader);
189  wcs->iscelsph = wcstype == MUSE_PIXTABLE_WCS_CELSPH;
190  cpl_boolean loglambda = !strncmp(cpl_propertylist_get_string(aHeader, "CTYPE3"),
191  "AWAV-LOG", 9);
192  /* get all (relevant) table columns for easy pointer access */
193  double ptxoff = 0., ptyoff = 0.;
194  if (wcs->iscelsph) {
195  ptxoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL1");
196  ptyoff = cpl_propertylist_get_double(aPixtable->header, "CRVAL2");
197  }
198  float *xpos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_XPOS),
199  *ypos = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_YPOS),
200  *lbda = cpl_table_get_data_float(aPixtable->table, MUSE_PIXTABLE_LAMBDA);
201  if (!xpos || !ypos || !lbda) {
202  cpl_msg_error(__func__, "Missing pixel table column (%p %p %p): %s",
203  (void *)xpos, (void *)ypos, (void *)lbda,
204  cpl_error_get_message());
205  cpl_error_set(__func__, CPL_ERROR_DATA_NOT_FOUND);
206  return NULL;
207  }
208 #ifdef ESO_ENABLE_DEBUG
209  int debug = 0;
210  if (getenv("MUSE_DEBUG_GRID_CONVERSION")) {
211  debug = atoi(getenv("MUSE_DEBUG_GRID_CONVERSION"));
212  }
213  if (debug) {
214  printf("crpix=%f %f %f, crval=%f %f %f, cd=%e %e %f\n",
215  wcs->crpix1, wcs->crpix2, crpix3, wcs->crval1, wcs->crval2, crval3,
216  wcs->cd11, wcs->cd22, cd33);
217  }
218 #endif
219  if (wcs->iscelsph) {
220  wcs->crval1 /= CPL_MATH_DEG_RAD; /* convert to radians before calling... */
221  wcs->crval2 /= CPL_MATH_DEG_RAD; /* ...muse_wcs_pixel_from_celestial_fast() */
222  }
223  double timeinit = cpl_test_get_walltime(),
224  timeprogress = timeinit,
225  cpuinit = cpl_test_get_cputime();
226  cpl_boolean showprogress = cpl_msg_get_level() == CPL_MSG_DEBUG
227  || cpl_msg_get_log_level() == CPL_MSG_DEBUG;
228  muse_pixgrid *pixgrid = muse_pixgrid_new(aXSize, aYSize, aZSize);
229 
230  /* Loop through the pixel table and write values into the pixel grid. *
231  * This cannot easily be parallelized, as it has to do allocations on *
232  * memory that would be common to all (OpenMP) threads. */
233  cpl_array *asel = cpl_table_where_selected(aPixtable->table);
234  const cpl_size *sel = cpl_array_get_data_cplsize_const(asel);
235  cpl_size isel, nsel = cpl_array_get_size(asel);
236  for (isel = 0 ; isel < nsel; isel++) {
237  if (showprogress && !((isel+1) % 1000000ll)) { /* output before every millionth entry */
238  double timenow = cpl_test_get_walltime();
239  if (timenow - timeprogress > 30.) { /* and more than half a minute passed */
240  timeprogress = timenow;
241  double percent = 100. * (isel + 1.) / nsel,
242  elapsed = timeprogress - timeinit,
243  remaining = (100. - percent) * elapsed / percent;
244  /* overwritable only exists for INFO mode, but we check *
245  * above that we want this only for DEBUG mode output... */
246  cpl_msg_info_overwritable(__func__, "pixel grid creation is %.1f%% "
247  "complete, %gs elapsed, ~%gs remaining",
248  percent, elapsed, remaining);
249  } /* if: 1/2 min passed */
250  } /* if: want debug output */
251  cpl_size n = sel[isel];
252  /* determine the pixel coordinates in the grid (indices, starting at 0) */
253  double xpx, ypx;
254  if (wcs->iscelsph) {
255  muse_wcs_pixel_from_celestial_fast(wcs, (xpos[n] + ptxoff) / CPL_MATH_DEG_RAD,
256  (ypos[n] + ptyoff) / CPL_MATH_DEG_RAD, &xpx, &ypx);
257  } else {
258  muse_wcs_pixel_from_projplane_fast(wcs, xpos[n], ypos[n],
259  &xpx, &ypx);
260  }
261  int x = lround(xpx) - 1,
262  y = lround(ypx) - 1,
263  z = lround((lbda[n] - crval3) / cd33 + crpix3) - 1;
264  if (loglambda) {
265  z = lround(crval3 / cd33 * log(lbda[n] / crval3));
266  }
267  cpl_size idx = muse_pixgrid_get_index(pixgrid, x, y, z, CPL_TRUE);
268 #ifdef ESO_ENABLE_DEBUG
269  if (debug) {
270  printf("%"CPL_SIZE_FORMAT": %f %f %f -> %d %d %d (%"CPL_SIZE_FORMAT")\n",
271  n, xpos[n] + ptxoff, ypos[n] + ptyoff, lbda[n], x, y, z, idx);
272  }
273 #endif
274 
275  /* write the pixel values to the correct place in the grid */
276  muse_pixgrid_add(pixgrid, idx, n);
277  } /* for isel (all selected pixel table rows) */
278  cpl_array_delete(asel);
279  cpl_free(wcs);
280  /* Clean up the possibly too many allocations; this is not strictly *
281  * needed but nice to only consume as much memory as we need. */
282  pixgrid->ext = cpl_realloc(pixgrid->ext,
283  pixgrid->n_ext * sizeof(muse_pixels_ext));
284  pixgrid->n_alloc = pixgrid->n_ext;
285 #ifdef ESO_ENABLE_DEBUG
286  if (debug) {
287  fflush(stdout);
288  }
289 #endif
290 
291  cpl_size idx, npix_sum = 0;
292  for (idx = 0; idx < aXSize * aYSize * aZSize; idx++) {
293  npix_sum += muse_pixgrid_get_count(pixgrid, idx);
294  }
295  double timefini = cpl_test_get_walltime(),
296  cpufini = cpl_test_get_cputime();
297  cpl_msg_debug(__func__, "pixel grid: %dx%dx%d, %"CPL_SIZE_FORMAT" pixels "
298  "total, %"CPL_SIZE_FORMAT" (%.1f%%) in extension map; took %gs "
299  "(wall-clock) and %gs (CPU) to create", (int)pixgrid->size_x,
300  (int)pixgrid->size_y, (int)pixgrid->size_z, npix_sum, pixgrid->n_ext,
301  (double)pixgrid->n_ext / npix_sum * 100., timefini - timeinit,
302  cpufini - cpuinit);
303 
304  return pixgrid;
305 } /* muse_pixgrid_create() */
306 
307 /*---------------------------------------------------------------------------*/
328 /*---------------------------------------------------------------------------*/
329 muse_pixgrid *
330 muse_pixgrid_2d_create(cpl_table *aTable, double aDX,
331  double aZMin, double aZMax, double aDZ, float *aXMin)
332 {
333  cpl_ensure(aTable, CPL_ERROR_NULL_INPUT, NULL);
334  cpl_size nrow = cpl_table_get_nrow(aTable);
335  if (nrow == 0) {
336  cpl_msg_error(__func__, "Invalid pixel table (no entries?)");
337  cpl_error_set(__func__, CPL_ERROR_NULL_INPUT);
338  return NULL;
339  }
340 
341  /* get the (relevant) table columns for easy pointer access */
342  float *xpos = cpl_table_get_data_float(aTable, MUSE_PIXTABLE_XPOS),
343  *lbda = cpl_table_get_data_float(aTable, MUSE_PIXTABLE_LAMBDA);
344  if (!xpos || !lbda) {
345  cpl_msg_error(__func__, "Missing pixel table column (%p %p): %s",
346  (void *)xpos, (void *)lbda, cpl_error_get_message());
347  cpl_error_set(__func__, CPL_ERROR_DATA_NOT_FOUND);
348  return NULL;
349  }
350 
351  /* get the selection for fast access to the relevant rows */
352  cpl_array *selection = cpl_table_where_selected(aTable);
353  cpl_size nsel = cpl_array_get_size(selection);
354  const cpl_size *sel = cpl_array_get_data_cplsize_const(selection);
355 
356  /* search for lowest x value in selected rows */
357  float xlo = FLT_MAX, xhi = -FLT_MAX;
358  cpl_size i;
359  for (i = 0; i < nsel; i++) {
360  if (xpos[sel[i]] > xhi) xhi = xpos[sel[i]];
361  if (xpos[sel[i]] < xlo) xlo = xpos[sel[i]];
362  } /* for i (all selected pixel table rows) */
363  if (aXMin) {
364  *aXMin = xlo;
365  }
366 
367  /* create the empty 2D grid depending on size of input data */
368  cpl_size xsize = ceil((xhi - xlo) / aDX) + 1,
369  zsize = ceil((aZMax - aZMin) / aDZ) + 1;
370  muse_pixgrid *pixgrid = muse_pixgrid_new(xsize, 1, zsize);
371 
372  /* loop through the pixel table and write values into the pixel grid */
373  for (i = 0; i < nsel; i++) {
374  /* determine the pixel coordinates in the grid; offset is min-1 to *
375  * have all these indices start at 0 for easy buffer access */
376  int x = lround((xpos[sel[i]] - xlo) / aDX),
377  z = lround((lbda[sel[i]] - aZMin) / aDZ);
378  cpl_size idx = muse_pixgrid_get_index(pixgrid, x, 0, z, CPL_TRUE);
379  /* write the pixel values to the correct place in the grid */
380  muse_pixgrid_add(pixgrid, idx, sel[i]);
381  } /* for i (all selected pixel table rows) */
382  cpl_array_delete(selection);
383  /* clean up the possibly too many allocations */
384  pixgrid->ext = cpl_realloc(pixgrid->ext,
385  pixgrid->n_ext * sizeof(muse_pixels_ext));
386  pixgrid->n_alloc = pixgrid->n_ext;
387 
388  return pixgrid;
389 } /* muse_pixgrid_2d_create() */
390 
391 /*---------------------------------------------------------------------------*/
396 /*---------------------------------------------------------------------------*/
397 void
399 {
400  if (!aPixels) {
401  return;
402  }
403  cpl_free(aPixels->pix);
404  cpl_size i_ext;
405  for (i_ext = 0; i_ext < aPixels->n_ext; i_ext++) {
406  cpl_free(aPixels->ext[i_ext].pix);
407  }
408  aPixels->n_ext = 0;
409  cpl_free(aPixels->ext);
410  aPixels->n_alloc = 0;
411  cpl_free(aPixels);
412 } /* muse_pixgrid_delete() */
413 
414 
double crpix2
Definition: muse_wcs.h:94
muse_pixtable_wcs
State of the astrometric calibration of a MUSE pixel table.
muse_pixgrid * muse_pixgrid_2d_create(cpl_table *aTable, double aDX, double aZMin, double aZMax, double aDZ, float *aXMin)
Convert selected rows of a pixel table into 2D pixgrid, linking the grid points to entries (=rows) in...
Definition: muse_pixgrid.c:330
A structure containing a spatial two-axis WCS.
Definition: muse_wcs.h:93
double cd22
Definition: muse_wcs.h:96
muse_pixgrid * muse_pixgrid_create(muse_pixtable *aPixtable, cpl_propertylist *aHeader, cpl_size aXSize, cpl_size aYSize, cpl_size aZSize)
Convert selected rows of a pixel table into pixel grid, linking the grid points to entries (=rows) in...
Definition: muse_pixgrid.c:168
cpl_size muse_pixtable_get_nrow(const muse_pixtable *aPixtable)
get the number of rows within the pixel table
static void muse_wcs_pixel_from_projplane_fast(muse_wcs *aWCS, double aX, double aY, double *aXOut, double *aYOut)
Convert from projection plane coordinates to pixel coordinates.
Definition: muse_wcs.h:233
The pixel extension map.
Definition: muse_pixgrid.h:46
The pixel grid.
Definition: muse_pixgrid.h:56
cpl_table * table
The pixel table.
muse_wcs * muse_wcs_new(cpl_propertylist *aHeader)
Create a new WCS structure from a given FITS header.
Definition: muse_wcs.c:1537
Structure definition of MUSE pixel table.
muse_pixtable_wcs muse_pixtable_wcs_check(muse_pixtable *aPixtable)
Check the state of the world coordinate system of a pixel table.
static void muse_wcs_pixel_from_celestial_fast(muse_wcs *aWCS, double aRA, double aDEC, double *aX, double *aY)
Convert from celestial spherical coordinates to pixel coordinates.
Definition: muse_wcs.h:164
void muse_pixgrid_delete(muse_pixgrid *aPixels)
Delete a pixgrid and remove its memory.
Definition: muse_pixgrid.c:398
cpl_boolean iscelsph
Definition: muse_wcs.h:100
double crval2
Definition: muse_wcs.h:95
static cpl_size muse_pixgrid_get_index(muse_pixgrid *aPixels, cpl_size aX, cpl_size aY, cpl_size aZ, cpl_boolean aAllowOutside)
Get the grid index determined from all three coordinates.
Definition: muse_pixgrid.h:89
static cpl_size muse_pixgrid_get_count(muse_pixgrid *aPixels, cpl_size aIndex)
Return the number of rows stored in one pixel.
Definition: muse_pixgrid.h:129
cpl_propertylist * header
The FITS header.