34 #include "muse_datacube.h"
36 #include "muse_flux.h"
37 #include "muse_pfits.h"
38 #include "muse_quality.h"
39 #include "muse_utils.h"
72 muse_datacube_collapse_filter_buffer(cpl_table *aFilter,
73 double aCRVAL,
double aCRPIX,
double aCDELT,
74 cpl_boolean aIsLog,
int *aL1,
int *aL2)
76 if (!aFilter || !aL1 || !aL2) {
79 double *fdata = (
double *)cpl_calloc(*aL2,
sizeof(
double));
82 for (l = 0; l < *aL2; l++) {
83 double lambda = (l + 1. - aCRPIX) * aCDELT + aCRVAL;
85 lambda = aCRVAL * exp((l + 1. - aCRPIX) * aCDELT / aCRVAL);
93 while (l < *aL2 && fabs(fdata[l]) < DBL_EPSILON) {
97 while (l > *aL1 && fabs(fdata[l]) < DBL_EPSILON) {
100 cpl_msg_debug(__func__,
"filter wavelength range %.1f..%.1fA (planes %d..%d)",
101 (*aL1 + 1. - aCRPIX) * aCDELT + aCRVAL,
102 (*aL2 + 1. - aCRPIX) * aCDELT + aCRVAL,
133 cpl_ensure(aEuro3D && aEuro3D->
dtable && aEuro3D->
hdata, CPL_ERROR_NULL_INPUT,
140 const char *unitx = cpl_table_get_column_unit(aEuro3D->
dtable,
"XPOS"),
141 *unity = cpl_table_get_column_unit(aEuro3D->
dtable,
"YPOS");
142 cpl_ensure(unitx && unity, CPL_ERROR_DATA_NOT_FOUND, NULL);
144 if (!strncmp(unitx, unity, 4) && !strncmp(unitx,
"deg", 4)) {
148 double xmin = cpl_table_get_column_min(aEuro3D->
dtable,
"XPOS"),
149 xmax = cpl_table_get_column_max(aEuro3D->
dtable,
"XPOS"),
150 ymin = cpl_table_get_column_min(aEuro3D->
dtable,
"YPOS"),
151 ymax = cpl_table_get_column_max(aEuro3D->
dtable,
"YPOS");
152 double x1, x2, y1, y2;
154 wcs->crval1 /= CPL_MATH_DEG_RAD;
155 wcs->
crval2 /= CPL_MATH_DEG_RAD;
157 ymin / CPL_MATH_DEG_RAD, &x1, &y1);
159 ymax / CPL_MATH_DEG_RAD, &x2, &y2);
166 int zmin = cpl_table_get_column_min(aEuro3D->
dtable,
"SPEC_STA"),
167 zmax = cpl_table_get_column_max(aEuro3D->
dtable,
"SPEC_STA"),
168 nx = lround(fabs(x2 - x1)) + 1,
169 ny = lround(fabs(y2 - y1)) + 1,
170 nz = zmax - zmin + 1;
173 cpl_size zmaxpos = -1;
174 cpl_table_get_column_maxpos(aEuro3D->
dtable,
"SPEC_STA", &zmaxpos);
175 const cpl_array *amax = cpl_table_get_array(aEuro3D->
dtable,
"DATA_SPE",
177 int l, nsize = cpl_array_get_size(amax);
178 for (l = nsize - 1; l > 0; l--) {
180 if (cpl_array_is_valid(amax, l) == 1) {
185 int nspec = cpl_table_get_nrow(aEuro3D->
dtable);
186 cpl_msg_debug(__func__,
"Euro3D dimensions: %dx%dx%d (z = %d - %d, valid %d),"
187 " %d spectra", nx, ny, nz, zmax, zmin, l + 1, nspec);
190 double crvals = cpl_propertylist_get_double(aEuro3D->
hdata,
"CRVALS"),
191 cdelts = cpl_propertylist_get_double(aEuro3D->
hdata,
"CDELTS");
192 const char *ctypes = cpl_propertylist_get_string(aEuro3D->
hdata,
"CTYPES");
193 cpl_boolean loglambda = ctypes && !strncmp(ctypes,
"AWAV-LOG", 9);
194 cpl_msg_debug(__func__,
"spectral WCS: %f / %f %s", crvals, cdelts,
195 loglambda ?
"log" :
"linear");
197 double *fdata = muse_datacube_collapse_filter_buffer(aFilter, crvals, zmin,
198 cdelts, loglambda, &l1, &l2);
202 image->
header = cpl_propertylist_duplicate(aEuro3D->
header);
204 image->
data = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
205 float *outdata = cpl_image_get_data_float(image->
data);
206 image->
dq = cpl_image_new(nx, ny, CPL_TYPE_INT);
208 cpl_image_add_scalar(image->
dq, EURO3D_MISSDATA);
209 int *outdq = cpl_image_get_data_int(image->
dq);
213 cpl_boolean usevariance = CPL_FALSE;
214 if (getenv(
"MUSE_COLLAPSE_USE_VARIANCE") &&
215 atoi(getenv(
"MUSE_COLLAPSE_USE_VARIANCE")) > 0) {
216 usevariance = CPL_TRUE;
221 #pragma omp parallel for default(none) private(l) \
222 shared(aEuro3D, fdata, l1, l2, noutside, nspec, nx, ny, outdata, \
223 outdq, usevariance, wcs)
224 for (k = 0; k < nspec; k++) {
226 double xpos = cpl_table_get(aEuro3D->
dtable,
"XPOS", k, &err),
227 ypos = cpl_table_get(aEuro3D->
dtable,
"YPOS", k, &err);
229 cpl_msg_warning(__func__,
"spectrum %d in Euro3D table does not have "
230 "position information!", k + 1);
237 ypos * CPL_MATH_RAD_DEG, &xpx, &ypx);
241 int i = lround(xpx) - 1,
243 if (i >= nx || j >= ny) {
249 int nstart = cpl_table_get_int(aEuro3D->
dtable,
"SPEC_STA", k, &err);
250 const cpl_array *adata = cpl_table_get_array(aEuro3D->
dtable,
"DATA_SPE", k),
251 *adq = cpl_table_get_array(aEuro3D->
dtable,
"QUAL_SPE", k),
254 astat = cpl_table_get_array(aEuro3D->
dtable,
"STAT_SPE", k);
257 double sumdata = 0., sumweight = 0.;
258 for (l = l1; l < l2; l++) {
260 int idx = l - nstart + 1;
263 double fweight = fdata ? fdata[l] : 1.;
264 cpl_errorstate prestate = cpl_errorstate_get();
265 int dq = cpl_array_get_int(adq, idx, &err);
267 cpl_errorstate_set(prestate);
271 double variance = 1.;
273 variance = astat ? cpl_array_get(astat, idx, &err) : 1.;
275 variance *= variance;
279 if (err || !isnormal(variance)) {
283 double data = cpl_array_get(adata, idx, &err);
285 sumdata += data * fweight / variance;
286 sumweight += fweight / variance;
288 if (isnormal(sumweight)) {
289 outdata[i + j*nx] = sumdata / sumweight;
290 outdq[i + j*nx] = EURO3D_GOODPIXEL;
292 outdq[i + j*nx] = EURO3D_MISSDATA;
298 cpl_msg_warning(__func__,
"Skipped %d spaxels, due to their location "
299 "outside the recostructed image!", noutside);
328 cpl_ensure(aCube && aCube->
data && aCube->
header, CPL_ERROR_NULL_INPUT, NULL);
331 int nx = cpl_image_get_size_x(cpl_imagelist_get(aCube->
data, 0)),
332 ny = cpl_image_get_size_y(cpl_imagelist_get(aCube->
data, 0)),
333 nz = cpl_imagelist_get_size(aCube->
data);
335 double crpix3 = cpl_propertylist_get_double(aCube->
header,
"CRPIX3"),
336 crval3 = cpl_propertylist_get_double(aCube->
header,
"CRVAL3"),
337 cd33 = cpl_propertylist_get_double(aCube->
header,
"CD3_3");
338 const char *ctype3 = cpl_propertylist_get_string(aCube->
header,
"CTYPE3");
339 cpl_boolean loglambda = ctype3 && !strncmp(ctype3,
"AWAV-LOG", 9);
341 double *fdata = muse_datacube_collapse_filter_buffer(aFilter, crval3, crpix3,
342 cd33, loglambda, &l1, &l2);
346 image->
header = cpl_propertylist_duplicate(aCube->
header);
347 cpl_propertylist_erase_regexp(image->
header,
"^C...*3$|^CD3_.$", 0);
348 image->
data = cpl_image_new(nx, ny, CPL_TYPE_FLOAT);
349 float *outdata = cpl_image_get_data_float(image->
data);
350 image->
dq = cpl_image_new(nx, ny, CPL_TYPE_INT);
351 int *outdq = cpl_image_get_data_int(image->
dq);
355 cpl_boolean usevariance = CPL_FALSE;
356 if (getenv(
"MUSE_COLLAPSE_USE_VARIANCE") &&
357 atoi(getenv(
"MUSE_COLLAPSE_USE_VARIANCE")) > 0) {
358 usevariance = CPL_TRUE;
363 #pragma omp parallel for default(none) \
364 shared(aCube, nx, ny, l1, l2, fdata, outdata, outdq, usevariance)
365 for (i = 0; i < nx; i++) {
367 for (j = 0; j < ny; j++) {
368 double sumdata = 0., sumweight = 0.;
370 for (l = l1; l < l2; l++) {
372 double fweight = fdata ? fdata[l] : 1.;
373 float *pdata = cpl_image_get_data_float(cpl_imagelist_get(aCube->
data, l)),
376 int *pdq = cpl_image_get_data_int(cpl_imagelist_get(aCube->
dq, l));
377 if (pdq && pdq[i + j*nx]) {
382 double variance = 1.;
384 pstat = cpl_image_get_data_float(cpl_imagelist_get(aCube->
stat, l));
385 variance = pstat ? pstat[i + j*nx] : 1.;
386 if (!isnormal(variance)) {
391 sumdata += pdata[i + j*nx] * fweight / variance;
392 sumweight += fweight / variance;
394 if (isnormal(sumweight)) {
395 outdata[i + j*nx] = sumdata / sumweight;
396 outdq[i + j*nx] = EURO3D_GOODPIXEL;
398 outdq[i + j*nx] = EURO3D_MISSDATA;
421 static cpl_error_code
422 muse_datacube_save_recimages(
const char *aFilename,
muse_imagelist *aImages,
425 cpl_ensure_code(aFilename, CPL_ERROR_NULL_INPUT);
426 cpl_error_code rc = CPL_ERROR_NONE;
427 if (!aImages || !aNames) {
431 for (i = 0; i < nimages; i++) {
435 cpl_propertylist *header = cpl_propertylist_new();
436 char dataext[KEYWORD_LENGTH], obj[KEYWORD_LENGTH],
437 *dqext = NULL, *statext = NULL;
438 snprintf(dataext, KEYWORD_LENGTH,
"%s", cpl_array_get_string(aNames, i));
440 dqext = cpl_sprintf(
"%s_%s", cpl_array_get_string(aNames, i), EXTNAME_DQ);
443 statext = cpl_sprintf(
"%s_%s", cpl_array_get_string(aNames, i), EXTNAME_STAT);
445 snprintf(obj, KEYWORD_LENGTH,
"%s", cpl_array_get_string(aNames, i));
446 cpl_propertylist_append_string(header,
"EXTNAME", dataext);
447 cpl_propertylist_set_comment(header,
"EXTNAME",
"reconstructed image (data values)");
449 cpl_propertylist_update_string(header, MUSE_HDR_FILTER,
450 cpl_array_get_string(aNames, i));
451 cpl_propertylist_copy_property_regexp(header, image->
header,
454 rc = cpl_image_save(image->
data, aFilename, CPL_TYPE_UNSPECIFIED, header,
458 cpl_propertylist_update_string(header,
"EXTNAME", dqext);
459 cpl_propertylist_set_comment(header,
"EXTNAME",
"reconstructed image (bad pixel status values)");
460 snprintf(obj, KEYWORD_LENGTH,
"%s, %s", cpl_array_get_string(aNames, i),
464 rc = cpl_image_save(image->
dq, aFilename, CPL_TYPE_UNSPECIFIED, header,
468 cpl_propertylist_update_string(header,
"EXTNAME", statext);
469 cpl_propertylist_set_comment(header,
"EXTNAME",
"reconstructed image (variance)");
470 snprintf(obj, KEYWORD_LENGTH,
"%s, %s", cpl_array_get_string(aNames, i),
474 rc = cpl_image_save(image->
stat, aFilename, CPL_TYPE_UNSPECIFIED, header,
478 cpl_propertylist_delete(header);
505 cpl_ensure_code(aEuro3D && aFilename, CPL_ERROR_NULL_INPUT);
508 cpl_error_code rc = cpl_table_save(aEuro3D->
dtable, aEuro3D->
header,
509 aEuro3D->
hdata, aFilename, CPL_IO_DEFAULT);
510 if (rc != CPL_ERROR_NONE) {
511 cpl_msg_warning(__func__,
"failed to save data part of the Euro3D table: "
512 "%s", cpl_error_get_message());
515 rc = cpl_table_save(aEuro3D->
gtable, NULL, aEuro3D->
hgroup, aFilename,
517 if (rc != CPL_ERROR_NONE) {
518 cpl_msg_warning(__func__,
"failed to save group part of the Euro3D table: "
519 "%s", cpl_error_get_message());
522 rc = muse_datacube_save_recimages(aFilename, aEuro3D->
recimages,
548 cpl_table_delete(aEuro3D->
dtable);
549 cpl_table_delete(aEuro3D->
gtable);
550 cpl_propertylist_delete(aEuro3D->
header);
551 cpl_propertylist_delete(aEuro3D->
hdata);
552 cpl_propertylist_delete(aEuro3D->
hgroup);
554 cpl_array_delete(aEuro3D->
recnames);
569 static cpl_error_code
572 cpl_ensure_code(aCube, CPL_ERROR_NULL_INPUT);
574 return CPL_ERROR_NONE;
577 for (k = 0; k < nimages; k++) {
583 int *pdq = cpl_image_get_data_int(image->
dq);
584 float *pdata = cpl_image_get_data_float(image->
data),
587 pstat = cpl_image_get_data_float(image->
stat);
589 int i, nx = cpl_image_get_size_x(image->
data),
590 ny = cpl_image_get_size_y(image->
data);
591 for (i = 0; i < nx; i++) {
593 for (j = 0; j < ny; j++) {
594 if (pdq[i + j*nx] == EURO3D_GOODPIXEL) {
598 pdata[i + j*nx] = NAN;
600 pstat[i + j*nx] = NAN;
606 cpl_image_delete(image->
dq);
610 return CPL_ERROR_NONE;
630 cpl_ensure_code(aCube && aCube->
data && aCube->
stat && aCube->
dq,
631 CPL_ERROR_NULL_INPUT);
634 int l, nx = cpl_image_get_size_x(cpl_imagelist_get(aCube->
data, 0)),
635 ny = cpl_image_get_size_y(cpl_imagelist_get(aCube->
data, 0)),
636 nz = cpl_imagelist_get_size(aCube->
data);
637 #pragma omp parallel for default(none) \
638 shared(aCube, nx, ny, nz)
639 for (l = 0; l < nz; l++) {
641 for (i = 0; i < nx; i++) {
643 for (j = 0; j < ny; j++) {
644 float *pdata = cpl_image_get_data_float(cpl_imagelist_get(aCube->
data, l)),
645 *pstat = cpl_image_get_data_float(cpl_imagelist_get(aCube->
stat, l));
646 int *pdq = cpl_image_get_data_int(cpl_imagelist_get(aCube->
dq, l));
647 if (pdq[i + j*nx] == EURO3D_GOODPIXEL) {
651 pdata[i + j*nx] = NAN;
652 pstat[i + j*nx] = NAN;
658 cpl_imagelist_delete(aCube->
dq);
662 muse_datacube_convert_dq_recimages(aCube);
664 return CPL_ERROR_NONE;
700 cpl_ensure_code(aCube && aCube->
header && aFilename, CPL_ERROR_NULL_INPUT);
703 cpl_propertylist *header = cpl_propertylist_new();
705 cpl_propertylist_copy_property_regexp(header, aCube->
header,
707 cpl_error_code rc = cpl_propertylist_save(header, aFilename, CPL_IO_CREATE);
708 cpl_propertylist_delete(header);
711 header = cpl_propertylist_new();
712 cpl_propertylist_append_string(header,
"EXTNAME", EXTNAME_DATA);
713 cpl_propertylist_set_comment(header,
"EXTNAME", EXTNAME_DATA_COMMENT);
716 cpl_propertylist_copy_property_regexp(header, aCube->
header,
717 MUSE_WCS_KEYS
"|^BUNIT", 0);
719 aCube->
dq ?
"DQ" : NULL, aCube->
stat ?
"STAT" : NULL);
720 rc = cpl_imagelist_save(aCube->
data, aFilename, CPL_TYPE_FLOAT, header,
722 cpl_propertylist_delete(header);
724 if (rc == CPL_ERROR_NONE && aCube->
dq) {
725 header = cpl_propertylist_new();
726 cpl_propertylist_append_string(header,
"EXTNAME", EXTNAME_DQ);
727 cpl_propertylist_set_comment(header,
"EXTNAME", EXTNAME_DQ_COMMENT);
730 cpl_propertylist_copy_property_regexp(header, aCube->
header,
733 aCube->
stat ?
"STAT" : NULL);
734 rc = cpl_imagelist_save(aCube->
dq, aFilename, CPL_TYPE_INT, header,
736 cpl_propertylist_delete(header);
739 if (rc == CPL_ERROR_NONE && aCube->
stat) {
740 header = cpl_propertylist_new();
741 cpl_propertylist_append_string(header,
"EXTNAME", EXTNAME_STAT);
742 cpl_propertylist_set_comment(header,
"EXTNAME", EXTNAME_STAT_COMMENT);
744 if (!strncmp(cpl_propertylist_get_string(aCube->
header,
"BUNIT"),
745 kMuseFluxUnitString, strlen(kMuseFluxUnitString) + 1)) {
747 cpl_propertylist_append_string(header,
"BUNIT", kMuseFluxStatString);
751 cpl_propertylist_copy_property_regexp(header, aCube->
header,
755 rc = cpl_imagelist_save(aCube->
stat, aFilename, CPL_TYPE_FLOAT, header,
757 cpl_propertylist_delete(header);
785 cpl_imagelist_delete(aCube->
data);
787 cpl_imagelist_delete(aCube->
dq);
789 cpl_imagelist_delete(aCube->
stat);
793 cpl_propertylist_delete(aCube->
header);
Structure definition of a MUSE datacube.
Structure definition for a collection of muse_images.
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
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.
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_image * muse_image_new(void)
Allocate memory for a new muse_image object.
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_imagelist * stat
the cube containing the data variance