33 #include "muse_datacube.h"
35 #include "muse_flux.h"
36 #include "muse_pfits.h"
37 #include "muse_quality.h"
38 #include "muse_utils.h"
71 muse_datacube_collapse_filter_buffer(cpl_table *aFilter,
72 double aCRVAL,
double aCRPIX,
double aCDELT,
73 cpl_boolean aIsLog,
int *aL1,
int *aL2)
75 if (!aFilter || !aL1 || !aL2) {
78 double *fdata = (
double *)cpl_calloc(*aL2,
sizeof(
double));
81 for (l = 0; l < *aL2; l++) {
82 double lambda = (l + 1. - aCRPIX) * aCDELT + aCRVAL;
84 lambda = aCRVAL * exp((l + 1. - aCRPIX) * aCDELT / aCRVAL);
92 while (l < *aL2 && fabs(fdata[l]) < DBL_EPSILON) {
96 while (l > *aL1 && fabs(fdata[l]) < DBL_EPSILON) {
99 cpl_msg_debug(__func__,
"filter wavelength range %.1f..%.1fA (planes %d..%d)",
100 (*aL1 + 1. - aCRPIX) * aCDELT + aCRVAL,
101 (*aL2 + 1. - aCRPIX) * aCDELT + aCRVAL,
132 cpl_ensure(aEuro3D && aEuro3D->
dtable && aEuro3D->
hdata, CPL_ERROR_NULL_INPUT,
139 const char *unitx = cpl_table_get_column_unit(aEuro3D->
dtable,
"XPOS"),
140 *unity = cpl_table_get_column_unit(aEuro3D->
dtable,
"YPOS");
141 cpl_ensure(unitx && unity, CPL_ERROR_DATA_NOT_FOUND, NULL);
143 if (!strncmp(unitx, unity, 4) && !strncmp(unitx,
"deg", 4)) {
147 double xmin = cpl_table_get_column_min(aEuro3D->
dtable,
"XPOS"),
148 xmax = cpl_table_get_column_max(aEuro3D->
dtable,
"XPOS"),
149 ymin = cpl_table_get_column_min(aEuro3D->
dtable,
"YPOS"),
150 ymax = cpl_table_get_column_max(aEuro3D->
dtable,
"YPOS");
151 double x1, x2, y1, y2;
153 wcs->crval1 /= CPL_MATH_DEG_RAD;
154 wcs->
crval2 /= CPL_MATH_DEG_RAD;
156 ymin / CPL_MATH_DEG_RAD, &x1, &y1);
158 ymax / CPL_MATH_DEG_RAD, &x2, &y2);
165 int zmin = cpl_table_get_column_min(aEuro3D->
dtable,
"SPEC_STA"),
166 zmax = cpl_table_get_column_max(aEuro3D->
dtable,
"SPEC_STA"),
167 nx = lround(fabs(x2 - x1)) + 1,
168 ny = lround(fabs(y2 - y1)) + 1,
169 nz = zmax - zmin + 1;
172 cpl_size zmaxpos = -1;
173 cpl_table_get_column_maxpos(aEuro3D->
dtable,
"SPEC_STA", &zmaxpos);
174 const cpl_array *amax = cpl_table_get_array(aEuro3D->
dtable,
"DATA_SPE",
176 int l, nsize = cpl_array_get_size(amax);
177 for (l = nsize - 1; l > 0; l--) {
179 if (cpl_array_is_valid(amax, l) == 1) {
184 int nspec = cpl_table_get_nrow(aEuro3D->
dtable);
185 cpl_msg_debug(__func__,
"Euro3D dimensions: %dx%dx%d (z = %d - %d, valid %d),"
186 " %d spectra", nx, ny, nz, zmax, zmin, l + 1, nspec);
189 double crvals = cpl_propertylist_get_double(aEuro3D->
hdata,
"CRVALS"),
190 cdelts = cpl_propertylist_get_double(aEuro3D->
hdata,
"CDELTS");
191 const char *ctypes = cpl_propertylist_get_string(aEuro3D->
hdata,
"CTYPES");
192 cpl_boolean loglambda = ctypes && !strncmp(ctypes,
"AWAV-LOG", 9);
193 cpl_msg_debug(__func__,
"spectral WCS: %f / %f %s", crvals, cdelts,
194 loglambda ?
"log" :
"linear");
196 double *fdata = muse_datacube_collapse_filter_buffer(aFilter, crvals, zmin,
197 cdelts, loglambda, &l1, &l2);
201 image->
header = cpl_propertylist_duplicate(aEuro3D->
header);
203 image->
data = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
204 float *outdata = cpl_image_get_data_float(image->
data);
205 image->
dq = cpl_image_new(nx, ny, CPL_TYPE_INT);
207 cpl_image_add_scalar(image->
dq, EURO3D_MISSDATA);
208 int *outdq = cpl_image_get_data_int(image->
dq);
212 cpl_boolean usevariance = CPL_FALSE;
213 if (getenv(
"MUSE_COLLAPSE_USE_VARIANCE") &&
214 atoi(getenv(
"MUSE_COLLAPSE_USE_VARIANCE")) > 0) {
215 usevariance = CPL_TRUE;
220 #pragma omp parallel for default(none) private(l) \
221 shared(aEuro3D, fdata, l1, l2, noutside, nspec, nx, ny, outdata, \
222 outdq, usevariance, wcs)
223 for (k = 0; k < nspec; k++) {
225 double xpos = cpl_table_get(aEuro3D->
dtable,
"XPOS", k, &err),
226 ypos = cpl_table_get(aEuro3D->
dtable,
"YPOS", k, &err);
228 cpl_msg_warning(__func__,
"spectrum %d in Euro3D table does not have "
229 "position information!", k + 1);
236 ypos * CPL_MATH_RAD_DEG, &xpx, &ypx);
240 int i = lround(xpx) - 1,
242 if (i >= nx || j >= ny) {
248 int nstart = cpl_table_get_int(aEuro3D->
dtable,
"SPEC_STA", k, &err);
249 const cpl_array *adata = cpl_table_get_array(aEuro3D->
dtable,
"DATA_SPE", k),
250 *adq = cpl_table_get_array(aEuro3D->
dtable,
"QUAL_SPE", k),
253 astat = cpl_table_get_array(aEuro3D->
dtable,
"STAT_SPE", k);
256 double sumdata = 0., sumweight = 0.;
257 for (l = l1; l < l2; l++) {
259 int idx = l - nstart + 1;
262 double fweight = fdata ? fdata[l] : 1.;
263 cpl_errorstate prestate = cpl_errorstate_get();
264 int dq = cpl_array_get_int(adq, idx, &err);
266 cpl_errorstate_set(prestate);
270 double variance = 1.;
272 variance = astat ? cpl_array_get(astat, idx, &err) : 1.;
274 variance *= variance;
278 if (err || !isnormal(variance)) {
282 double data = cpl_array_get(adata, idx, &err);
284 sumdata += data * fweight / variance;
285 sumweight += fweight / variance;
287 if (isnormal(sumweight)) {
288 outdata[i + j*nx] = sumdata / sumweight;
289 outdq[i + j*nx] = EURO3D_GOODPIXEL;
291 outdq[i + j*nx] = EURO3D_MISSDATA;
297 cpl_msg_warning(__func__,
"Skipped %d spaxels, due to their location "
298 "outside the recostructed image!", noutside);
327 cpl_ensure(aCube && aCube->
data && aCube->
header, CPL_ERROR_NULL_INPUT, NULL);
330 int nx = cpl_image_get_size_x(cpl_imagelist_get(aCube->
data, 0)),
331 ny = cpl_image_get_size_y(cpl_imagelist_get(aCube->
data, 0)),
332 nz = cpl_imagelist_get_size(aCube->
data);
334 double crpix3 = cpl_propertylist_get_double(aCube->
header,
"CRPIX3"),
335 crval3 = cpl_propertylist_get_double(aCube->
header,
"CRVAL3"),
336 cd33 = cpl_propertylist_get_double(aCube->
header,
"CD3_3");
337 const char *ctype3 = cpl_propertylist_get_string(aCube->
header,
"CTYPE3");
338 cpl_boolean loglambda = ctype3 && !strncmp(ctype3,
"AWAV-LOG", 9);
340 double *fdata = muse_datacube_collapse_filter_buffer(aFilter, crval3, crpix3,
341 cd33, loglambda, &l1, &l2);
345 image->
header = cpl_propertylist_duplicate(aCube->
header);
346 cpl_propertylist_erase_regexp(image->
header,
"^C...*3$|^CD3_.$", 0);
347 image->
data = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
348 float *outdata = cpl_image_get_data_float(image->
data);
349 image->
dq = cpl_image_new(nx, ny, CPL_TYPE_INT);
350 int *outdq = cpl_image_get_data_int(image->
dq);
354 cpl_boolean usevariance = CPL_FALSE;
355 if (getenv(
"MUSE_COLLAPSE_USE_VARIANCE") &&
356 atoi(getenv(
"MUSE_COLLAPSE_USE_VARIANCE")) > 0) {
357 usevariance = CPL_TRUE;
362 #pragma omp parallel for default(none) \
363 shared(aCube, nx, ny, l1, l2, fdata, outdata, outdq, usevariance)
364 for (i = 0; i < nx; i++) {
366 for (j = 0; j < ny; j++) {
367 double sumdata = 0., sumweight = 0.;
369 for (l = l1; l < l2; l++) {
371 double fweight = fdata ? fdata[l] : 1.;
372 float *pdata = cpl_image_get_data_float(cpl_imagelist_get(aCube->
data, l)),
374 if (!isfinite(pdata[i + j*nx])) {
378 int *pdq = cpl_image_get_data_int(cpl_imagelist_get(aCube->
dq, l));
379 if (pdq && pdq[i + j*nx]) {
384 double variance = 1.;
386 pstat = cpl_image_get_data_float(cpl_imagelist_get(aCube->
stat, l));
387 variance = pstat ? pstat[i + j*nx] : 1.;
388 if (!isnormal(variance)) {
393 sumdata += pdata[i + j*nx] * fweight / variance;
394 sumweight += fweight / variance;
396 if (isnormal(sumweight)) {
397 outdata[i + j*nx] = sumdata / sumweight;
398 outdq[i + j*nx] = EURO3D_GOODPIXEL;
400 outdq[i + j*nx] = EURO3D_MISSDATA;
426 cpl_ensure_code(aFilename, CPL_ERROR_NULL_INPUT);
427 cpl_error_code rc = CPL_ERROR_NONE;
428 if (!aImages || !aNames) {
432 for (i = 0; i < nimages; i++) {
436 cpl_propertylist *header = cpl_propertylist_new();
437 char dataext[KEYWORD_LENGTH], obj[KEYWORD_LENGTH],
438 *dqext = NULL, *statext = NULL;
439 snprintf(dataext, KEYWORD_LENGTH,
"%s", cpl_array_get_string(aNames, i));
441 dqext = cpl_sprintf(
"%s_%s", cpl_array_get_string(aNames, i), EXTNAME_DQ);
444 statext = cpl_sprintf(
"%s_%s", cpl_array_get_string(aNames, i), EXTNAME_STAT);
446 snprintf(obj, KEYWORD_LENGTH,
"%s", cpl_array_get_string(aNames, i));
447 cpl_propertylist_append_string(header,
"EXTNAME", dataext);
448 cpl_propertylist_set_comment(header,
"EXTNAME",
"reconstructed image (data values)");
450 cpl_propertylist_update_string(header, MUSE_HDR_FILTER,
451 cpl_array_get_string(aNames, i));
452 cpl_propertylist_copy_property_regexp(header, image->
header,
455 rc = cpl_image_save(image->
data, aFilename, CPL_TYPE_UNSPECIFIED, header,
459 cpl_propertylist_update_string(header,
"EXTNAME", dqext);
460 cpl_propertylist_set_comment(header,
"EXTNAME",
"reconstructed image (bad pixel status values)");
461 snprintf(obj, KEYWORD_LENGTH,
"%s, %s", cpl_array_get_string(aNames, i),
465 rc = cpl_image_save(image->
dq, aFilename, CPL_TYPE_UNSPECIFIED, header,
469 cpl_propertylist_update_string(header,
"EXTNAME", statext);
470 cpl_propertylist_set_comment(header,
"EXTNAME",
"reconstructed image (variance)");
471 snprintf(obj, KEYWORD_LENGTH,
"%s, %s", cpl_array_get_string(aNames, i),
475 rc = cpl_image_save(image->
stat, aFilename, CPL_TYPE_UNSPECIFIED, header,
479 cpl_propertylist_delete(header);
506 cpl_ensure_code(aEuro3D && aFilename, CPL_ERROR_NULL_INPUT);
509 cpl_error_code rc = cpl_table_save(aEuro3D->
dtable, aEuro3D->
header,
510 aEuro3D->
hdata, aFilename, CPL_IO_DEFAULT);
511 if (rc != CPL_ERROR_NONE) {
512 cpl_msg_warning(__func__,
"failed to save data part of the Euro3D table: "
513 "%s", cpl_error_get_message());
516 rc = cpl_table_save(aEuro3D->
gtable, NULL, aEuro3D->
hgroup, aFilename,
518 if (rc != CPL_ERROR_NONE) {
519 cpl_msg_warning(__func__,
"failed to save group part of the Euro3D table: "
520 "%s", cpl_error_get_message());
549 cpl_table_delete(aEuro3D->
dtable);
550 cpl_table_delete(aEuro3D->
gtable);
551 cpl_propertylist_delete(aEuro3D->
header);
552 cpl_propertylist_delete(aEuro3D->
hdata);
553 cpl_propertylist_delete(aEuro3D->
hgroup);
555 cpl_array_delete(aEuro3D->
recnames);
570 static cpl_error_code
573 cpl_ensure_code(aCube, CPL_ERROR_NULL_INPUT);
575 return CPL_ERROR_NONE;
578 for (k = 0; k < nimages; k++) {
586 return CPL_ERROR_NONE;
606 cpl_ensure_code(aCube && aCube->
data && aCube->
stat && aCube->
dq,
607 CPL_ERROR_NULL_INPUT);
610 int l, nx = cpl_image_get_size_x(cpl_imagelist_get(aCube->
data, 0)),
611 ny = cpl_image_get_size_y(cpl_imagelist_get(aCube->
data, 0)),
612 nz = cpl_imagelist_get_size(aCube->
data);
613 #pragma omp parallel for default(none) \
614 shared(aCube, nx, ny, nz)
615 for (l = 0; l < nz; l++) {
617 for (i = 0; i < nx; i++) {
619 for (j = 0; j < ny; j++) {
620 float *pdata = cpl_image_get_data_float(cpl_imagelist_get(aCube->
data, l)),
621 *pstat = cpl_image_get_data_float(cpl_imagelist_get(aCube->
stat, l));
622 int *pdq = cpl_image_get_data_int(cpl_imagelist_get(aCube->
dq, l));
623 if (pdq[i + j*nx] == EURO3D_GOODPIXEL) {
627 pdata[i + j*nx] = NAN;
628 pstat[i + j*nx] = NAN;
634 cpl_imagelist_delete(aCube->
dq);
638 muse_datacube_convert_dq_recimages(aCube);
640 return CPL_ERROR_NONE;
676 cpl_ensure_code(aCube && aCube->
header && aFilename, CPL_ERROR_NULL_INPUT);
679 cpl_propertylist *header = cpl_propertylist_new();
681 cpl_propertylist_copy_property_regexp(header, aCube->
header,
683 cpl_error_code rc = cpl_propertylist_save(header, aFilename, CPL_IO_CREATE);
684 cpl_propertylist_delete(header);
687 header = cpl_propertylist_new();
688 cpl_propertylist_append_string(header,
"EXTNAME", EXTNAME_DATA);
689 cpl_propertylist_set_comment(header,
"EXTNAME", EXTNAME_DATA_COMMENT);
692 cpl_propertylist_copy_property_regexp(header, aCube->
header,
693 MUSE_WCS_KEYS
"|^BUNIT", 0);
695 aCube->
dq ?
"DQ" : NULL, aCube->
stat ?
"STAT" : NULL);
696 rc = cpl_imagelist_save(aCube->
data, aFilename, CPL_TYPE_FLOAT, header,
698 cpl_propertylist_delete(header);
700 if (rc == CPL_ERROR_NONE && aCube->
dq) {
701 header = cpl_propertylist_new();
702 cpl_propertylist_append_string(header,
"EXTNAME", EXTNAME_DQ);
703 cpl_propertylist_set_comment(header,
"EXTNAME", EXTNAME_DQ_COMMENT);
706 cpl_propertylist_copy_property_regexp(header, aCube->
header,
709 aCube->
stat ?
"STAT" : NULL);
710 rc = cpl_imagelist_save(aCube->
dq, aFilename, CPL_TYPE_INT, header,
712 cpl_propertylist_delete(header);
715 if (rc == CPL_ERROR_NONE && aCube->
stat) {
716 header = cpl_propertylist_new();
717 cpl_propertylist_append_string(header,
"EXTNAME", EXTNAME_STAT);
718 cpl_propertylist_set_comment(header,
"EXTNAME", EXTNAME_STAT_COMMENT);
720 if (!strncmp(cpl_propertylist_get_string(aCube->
header,
"BUNIT"),
721 kMuseFluxUnitString, strlen(kMuseFluxUnitString) + 1)) {
723 cpl_propertylist_append_string(header,
"BUNIT", kMuseFluxStatString);
727 cpl_propertylist_copy_property_regexp(header, aCube->
header,
731 rc = cpl_imagelist_save(aCube->
stat, aFilename, CPL_TYPE_FLOAT, header,
733 cpl_propertylist_delete(header);
749 static cpl_propertylist *
750 muse_datacube_load_header(
const char *aFilename)
752 cpl_ensure(aFilename, CPL_ERROR_NULL_INPUT, NULL);
753 int extdata = cpl_fits_find_extension(aFilename,
"DATA");
754 cpl_ensure(extdata >= 0, CPL_ERROR_ILLEGAL_INPUT, NULL);
755 cpl_ensure(extdata > 0, CPL_ERROR_DATA_NOT_FOUND, NULL);
757 cpl_propertylist *header = cpl_propertylist_load(aFilename, 0);
758 cpl_propertylist *hdata = cpl_propertylist_load(aFilename, extdata);
759 cpl_propertylist_copy_property_regexp(header, hdata,
760 MUSE_WCS_KEYS
"|BUNIT", 0);
761 cpl_propertylist_delete(hdata);
790 cpl_ensure(aFilename, CPL_ERROR_NULL_INPUT, NULL);
793 cpl_errorstate state = cpl_errorstate_get();
794 cube->
header = muse_datacube_load_header(aFilename);
795 if (!cpl_errorstate_is_equal(state) || !cube->
header) {
796 cpl_msg_error(__func__,
"Loading cube-like headers from \"%s\" failed!",
801 int ext = cpl_fits_find_extension(aFilename,
"DATA");
802 cube->
data = cpl_imagelist_load(aFilename, CPL_TYPE_UNSPECIFIED, ext);
804 ext = cpl_fits_find_extension(aFilename,
"DQ");
806 cube->
stat = cpl_imagelist_load(aFilename, CPL_TYPE_UNSPECIFIED, ext);
808 ext = cpl_fits_find_extension(aFilename,
"STAT");
810 cube->
stat = cpl_imagelist_load(aFilename, CPL_TYPE_UNSPECIFIED, ext);
812 int next = cpl_fits_count_extensions(aFilename);
813 while (++ext <= next) {
816 image->
header = cpl_propertylist_load(aFilename, ext);
817 image->
data = cpl_image_load(aFilename, CPL_TYPE_UNSPECIFIED, 0, ext);
821 cube->
recnames = cpl_array_new(1, CPL_TYPE_STRING);
826 cpl_array_set_string(cube->
recnames, cpl_array_get_size(cube->
recnames) - 1,
859 cpl_imagelist_delete(aCube->
data);
861 cpl_imagelist_delete(aCube->
dq);
863 cpl_imagelist_delete(aCube->
stat);
867 cpl_propertylist_delete(aCube->
header);
Structure definition of a MUSE datacube.
Structure definition for a collection of muse_images.
const char * muse_pfits_get_extname(const cpl_propertylist *aHeaders)
find out the extension name
A structure containing a spatial two-axis WCS.
cpl_error_code muse_euro3dcube_save(muse_euro3dcube *aEuro3D, const char *aFilename)
Save a Euro3D cube object to a file.
cpl_image * data
the data extension
muse_datacube * muse_datacube_load(const char *aFilename)
Load header, DATA and optionally STAT and DQ extensions as well as the reconstructed images of a MUSE...
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.
cpl_image * stat
the statistics extension
void muse_datacube_delete(muse_datacube *aCube)
Deallocate memory associated to a muse_datacube object.
void muse_imagelist_delete(muse_imagelist *aList)
Free the memory of the MUSE image list.
cpl_propertylist * hgroup
the group FITS header
cpl_array * recnames
the reconstructed image filter names
muse_image * muse_datacube_collapse(muse_datacube *aCube, cpl_table *aFilter)
Integrate a FITS NAXIS=3 datacube along the wavelength direction.
muse_imagelist * recimages
the reconstructed image data
cpl_propertylist * header
the primary FITS header
Structure definition of MUSE three extension FITS file.
cpl_array * recnames
the reconstructed image filter names
cpl_propertylist * header
the FITS header
cpl_error_code muse_utils_set_hduclass(cpl_propertylist *aHeader, const char *aClass2, const char *aExtData, const char *aExtDQ, const char *aExtStat)
Set HDU headers for the ESO FITS data format.
cpl_error_code muse_datacube_save(muse_datacube *aCube, const char *aFilename)
Save the three cube extensions and the FITS headers of a MUSE datacube to a file. ...
unsigned int muse_imagelist_get_size(muse_imagelist *aList)
Return the number of stored images.
cpl_image * dq
the data quality extension
double muse_flux_response_interpolate(const cpl_table *aResponse, double aLambda, double *aError, muse_flux_interpolation_type aType)
Compute linearly interpolated response of some kind at given wavelength.
muse_wcs * muse_wcs_new(cpl_propertylist *aHeader)
Create a new WCS structure from a given FITS header.
cpl_error_code muse_datacube_convert_dq(muse_datacube *aCube)
Convert the DQ extension of a datacube to NANs in DATA and STAT.
muse_image * muse_imagelist_get(muse_imagelist *aList, unsigned int aIdx)
Get the muse_image of given list index.
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.
cpl_error_code muse_datacube_save_recimages(const char *aFilename, muse_imagelist *aImages, cpl_array *aNames)
Save reconstructed images of a cube in extra extensions.
void muse_euro3dcube_delete(muse_euro3dcube *aEuro3D)
Deallocate memory associated to a muse_euro3dcube object.
cpl_imagelist * data
the cube containing the actual data values
Structure definition of a Euro3D datacube.
cpl_imagelist * dq
the optional cube containing the bad pixel status
cpl_table * dtable
the table containing the actual Euro3D data
cpl_propertylist * hdata
the data FITS header
cpl_table * gtable
the table containing the Euro3D groups
cpl_propertylist * header
the FITS header
muse_image * muse_euro3dcube_collapse(muse_euro3dcube *aEuro3D, cpl_table *aFilter)
Integrate a Euro3D datacube along the wavelength direction.
muse_imagelist * muse_imagelist_new(void)
Create a new (empty) MUSE image list.
muse_image * muse_image_new(void)
Allocate memory for a new muse_image object.
cpl_error_code muse_imagelist_set(muse_imagelist *aList, muse_image *aImage, unsigned int aIdx)
Set the muse_image of given list index.
muse_imagelist * recimages
the reconstructed image data
cpl_error_code muse_utils_copy_modified_header(cpl_propertylist *aH1, cpl_propertylist *aH2, const char *aKeyword, const char *aString)
Copy a modified header keyword from one header to another.
cpl_error_code muse_image_dq_to_nan(muse_image *aImage)
Convert pixels flagged in the DQ extension to NANs in DATA (and STAT, if present).
cpl_imagelist * stat
the cube containing the data variance