33#include "eris_ifu_error.h"
35#include "eris_utils.h"
36#include "eris_ifu_utils.h"
37#include "eris_pfits.h"
38#include "eris_ifu_combine_static.h"
43static const char eris_ifu_combine_description[] =
"\
44Addapted from sinfo_utl_cube_combine. \n\
45This recipe performs cube combination. The input files are several cubes created \n\
46by eris_ifu_jitter, their associated tags should be OBJECT_CUBE. \n\
47The user must provide offsets in an ASCII file named offset.list located in the \n\
48same dir where the esorex command is given. \n\
49Its content should be two columns with the offsets of the corresponding data \n\
50cubes that can be obtained, for example, with the command: \n\
52 dfits pro/eris_ifu_jitter_obj_cube_00*.fits | fitsort OCS.OFFSET.X OCS.OFFSET.Y > offset.list \n\
54The output is a cube COMBINED_CUBE resulting from the input cubes.\n\
57--compute_mode=MEDIAN should only be used when all data cubes have the same \n\
58exposure time. If these differ MEAN should be used.\n\
59When using MEAN the units of the wavelength axis are ADU/s, when using MEDIAN \n\
60the units of the wavelength axis are ADU. \n\
62----------------------------------------------------------------------------- \n\
64 DO CATG Explanation Required #Frames \n\
65 ------- ----------- -------- ------- \n\
66 OBJECT_CUBE Output cube from eris_ifu_jitter Y [1,N] \n\
69 DO CATG Explanation Product Depth \n\
70 ------- ---------------------------------- ------------- \n\
71 COMBINED_CUBE Combined cube.\n\
72 ----------------------------------------------------------------------------\n\
74 Information on relevant parameters may be found with\n\
75 esorex --params "REC_NAME_COMBINE
"\n\
76 esorex --help "REC_NAME_COMBINE
"\n";
82cpl_recipe_define(eris_ifu_combine, ERIS_BINARY_VERSION,
"Andrea Modigliani, Y. Cao",
83 PACKAGE_BUGREPORT,
"2021",
84 "This recipe combines jittered cubes into a single cube",
85 eris_ifu_combine_description);
87cpl_error_code eris_ifu_combine_check_inputs(cpl_frameset *);
99static cpl_error_code eris_ifu_combine_fill_parameterlist(cpl_parameterlist *pl)
101 cpl_parameter *p = NULL;
102 char *recname_full = NULL;
103 cpl_error_code err = CPL_ERROR_NONE;
105 cpl_ensure_code(pl, CPL_ERROR_NULL_INPUT);
108 recname_full = cpl_sprintf(
"eris.%s", REC_NAME_COMBINE);
111 p = cpl_parameter_new_value(
"eris.eris_ifu_combine.offset_mode",
113 "Offset conventions. If TRUE: applies reference "
114 "offset correction. If FALSE: take user offsets. "
115 "The reference offset is computed as (min_off+max_off)/2",
116 recname_full, CPL_TRUE) ;
117 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"offset_mode") ;
118 cpl_parameterlist_append(pl, p) ;
120 p = cpl_parameter_new_enum(
"eris.eris_ifu_combine.compute_mode",
122 "Coadding compute mode of the combined cube",
123 recname_full,
"MEAN", 2,
125 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"compute_mode");
126 cpl_parameter_disable(p, CPL_PARAMETER_MODE_ENV);
127 cpl_parameterlist_append(pl, p) ;
129 p = cpl_parameter_new_value(
"eris.eris_ifu_combine.name_i",
131 "Input filename. This must be provided when offset_mode=FALSE "
132 "Allows the user to set X and Y cumulative offsets in "
133 "a two column format",
134 recname_full,
"offset.list");
135 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"name_i") ;
136 cpl_parameterlist_append(pl, p) ;
138 p = cpl_parameter_new_value(
"eris.eris_ifu_combine.name_o",
141 recname_full,
"out_coadd_cube.fits");
142 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"name_o") ;
143 cpl_parameterlist_append(pl, p) ;
145 p = cpl_parameter_new_value(
"eris.eris_ifu_combine.ks_clip",
147 "Kappa sigma clipping",
148 recname_full, FALSE);
149 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"ks_clip") ;
150 cpl_parameterlist_append(pl, p) ;
152 p = cpl_parameter_new_value(
"eris.eris_ifu_combine.pclip",
154 "Apply an initial percentile clipping based on"
155 "the absolute deviation from the median if "
158 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"pclip") ;
159 cpl_parameterlist_append(pl, p) ;
161 p = cpl_parameter_new_value(
"eris.eris_ifu_combine.kappa",
163 "Kappa value for sigma clip",
165 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"kappa") ;
166 cpl_parameterlist_append(pl, p) ;
168 p = cpl_parameter_new_value(
"eris.eris_ifu_combine.subtract-background",
170 "Subtract median of the images chanel-by-chanel",
171 recname_full, FALSE);
172 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"subtract-background") ;
173 cpl_parameterlist_append(pl, p) ;
175 p = cpl_parameter_new_value(
"eris.eris_ifu_combine.edge-trim",
177 "Number or pixels to trim for each plane of the "
180 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"edge-trim") ;
181 cpl_parameterlist_append(pl, p);
183 p = cpl_parameter_new_value(
"eris.eris_ifu_combine.weights",
185 "Optional input filename. The user can specify "
186 "his own weighting scheme when compute_mode=MEAN "
187 "A one column format is expected. ",
189 cpl_parameter_set_alias(p, CPL_PARAMETER_MODE_CLI,
"weights") ;
190 cpl_parameterlist_append(pl, p) ;
193 err = cpl_error_get_code();
208 const cpl_parameterlist *parlist)
231 bool subtract_background = CPL_FALSE,
232 offset_mode = CPL_FALSE;
233 const char *name_o = NULL,
238 *compute_mode = NULL;
239 FILE *file_list = NULL;
240 const cpl_parameter *param = NULL;
241 cpl_propertylist *plist = NULL;
242 const cpl_frame *frame = NULL;
243 cpl_imagelist *mergedCubeData = NULL,
244 *mergedCubeError = NULL,
245 *mergedCubeDIT = NULL;
246 cpl_image **mergedImageData = NULL,
247 **mergedImageError = NULL,
248 **mergedImageDIT = NULL;
249 cpl_error_code err = CPL_ERROR_NONE;
251 cpl_ensure_code(parlist != NULL, CPL_ERROR_NULL_INPUT);
252 cpl_ensure_code(frameset != NULL, CPL_ERROR_NULL_INPUT);
258 param = cpl_parameterlist_find_const(parlist,
259 "eris.eris_ifu_combine.ks_clip");
260 ks_clip = cpl_parameter_get_bool(param);
262 param = cpl_parameterlist_find_const(parlist,
263 "eris.eris_ifu_combine.pclip");
264 pclip = cpl_parameter_get_int(param);
266 param = cpl_parameterlist_find_const(parlist,
267 "eris.eris_ifu_combine.subtract-background");
268 subtract_background = cpl_parameter_get_bool(param);
270 param = cpl_parameterlist_find_const(parlist,
271 "eris.eris_ifu_combine.kappa");
272 kappa = cpl_parameter_get_double(param);
274 param = cpl_parameterlist_find_const(parlist,
275 "eris.eris_ifu_combine.name_i");
276 name_i = cpl_parameter_get_string(param);
278 param = cpl_parameterlist_find_const(parlist,
279 "eris.eris_ifu_combine.name_o");
280 name_o = cpl_parameter_get_string(param);
282 param = cpl_parameterlist_find_const(parlist,
283 "eris.eris_ifu_combine.offset_mode");
284 offset_mode = cpl_parameter_get_bool(param);
286 param = cpl_parameterlist_find_const(parlist,
287 "eris.eris_ifu_combine.edge-trim");
288 edge_trim = cpl_parameter_get_int(param);
290 param = cpl_parameterlist_find_const(parlist,
291 "eris.eris_ifu_combine.compute_mode");
292 compute_mode = cpl_parameter_get_string(param);
294 param = cpl_parameterlist_find_const(parlist,
295 "eris.eris_ifu_combine.weights");
296 weights_i = cpl_parameter_get_string(param);
305 eris_ifu_combine_check_inputs(frameset));
309 if (NULL == (file_list = fopen (name_i,
"r" )) ){
311 CPL_ERROR_ILLEGAL_INPUT,
"cannot open %s\n", name_i);
314 while (fscanf(file_list,
"%f %f",&tmpoffx, &tmpoffy) != EOF ){
319 if (cnt != cpl_frameset_get_size(frameset)) {
321 CPL_ERROR_INCOMPATIBLE_INPUT,
"Input offlist is not consistent with frameset.");
326 if (!strcmp(compute_mode,
"MEAN") && strcmp(weights_i,
"")) {
327 if (NULL == (file_list = fopen (weights_i,
"r" )) ){
329 CPL_ERROR_ILLEGAL_INPUT,
"cannot open %s\n", weights_i);
332 while (fscanf(file_list,
"%f", &weights) != EOF ){
337 if (cnt != cpl_frameset_get_size(frameset)) {
339 CPL_ERROR_INCOMPATIBLE_INPUT,
"Input list of weights is not consistent with frameset.");
344 nframes = cpl_frameset_get_size(frameset);
347 exptimes = (
double*) cpl_calloc(nframes,
sizeof(
double)));
349 offsetx = (
float*) cpl_calloc(nframes,
sizeof(
float)));
351 offsety = (
float*) cpl_calloc(nframes,
sizeof(
float)));
355 cpl_msg_info(cpl_func,
"Reading user-specified offset list.") ;
356 file_list = fopen (name_i,
"r" );
359 while (fscanf( file_list,
"%f %f",&tmpoffx,&tmpoffy ) != EOF ) {
360 offsetx[cnt] = tmpoffx;
361 offsety[cnt] = tmpoffy;
366 offsetx[cnt] = offsetx[cnt]-x0;
367 offsety[cnt] = offsety[cnt]-y0;
368 cpl_msg_info(cpl_func,
" User defined offsets RA, DEC in pixels (relative to the first) %.2f, %.2f",
369 offsetx[cnt], offsety[cnt]) ;
374 cpl_msg_info(cpl_func,
"Use CUMOFFS from the header.") ;
382 const cpl_matrix *cd = NULL;
384 for (
int n = 0; n < nframes; n++){
385 frame = cpl_frameset_get_position_const(frameset, n);
386 name = cpl_frame_get_filename(frame);
388 plist = cpl_propertylist_load(name, 0);
389 dra = cpl_propertylist_get_double(plist,
"ESO OCS CUMOFFS RA");
390 ddec = cpl_propertylist_get_double(plist,
"ESO OCS CUMOFFS DEC");
393 plist = cpl_propertylist_load(name, 1);
394 cpl_wcs *tmp_wcs = cpl_wcs_new_from_propertylist(plist);
397 cd = cpl_wcs_get_cd(tmp_wcs);
398 cd1_1 = cpl_matrix_get(cd, 0, 0);
399 cd1_2 = cpl_matrix_get(cd, 1, 0);
400 cd2_1 = cpl_matrix_get(cd, 0, 1);
401 cd2_2 = cpl_matrix_get(cd, 1, 1);
402 cpl_wcs_delete(tmp_wcs);
404 dra = -1 * dra /3600.0;
405 ddec = -1 * ddec/3600.0;
406 offsetx[n] = (float) (cd2_2 * dra - cd1_2 * ddec)/(cd1_1*cd2_2 - cd1_2*cd2_1);
407 offsety[n] = (float) (cd1_1 * ddec - cd2_1 * dra)/(cd1_1*cd2_2 - cd1_2*cd2_1);
413 offsetx[n] = offsetx[n]-x0;
414 offsety[n] = offsety[n]-y0;
416 cpl_msg_info(cpl_func,
" CUMOFFS RA, DEC in pixels (relative to the first): %.2f, %.2f",
417 offsetx[n], offsety[n]) ;
425 files = (
const char**) cpl_calloc(MAX_NAME_SIZE,
sizeof(
const char*)));
427 for (
int n = 0; n < nframes; n++){
428 frame = cpl_frameset_get_position_const(frameset, n);
429 files[n] = cpl_frame_get_filename(frame);
432 plist = cpl_propertylist_load(files[n], 1);
437 if (nx_out < min_size_x) {
440 if (ny_out < min_size_y) {
447 if (!strcmp(compute_mode,
"MEAN") && strcmp(weights_i,
"")) {
448 cpl_msg_info(cpl_func,
"Reading user-specified weights list.") ;
449 file_list = fopen (weights_i,
"r" );
452 while (fscanf(file_list,
"%f", &weights) != EOF ){
453 exptimes[cnt] = weights;
459 cpl_msg_info(cpl_func,
"Min. input cube size x = %d, y = %d", min_size_x, min_size_y);
463 &ref_offx, &ref_offy, &nx_out, &ny_out));
466 cpl_msg_info(cpl_func,
"Number of pixels on edges to be trimmed: %d", edge_trim);
469 if(subtract_background) {
470 cpl_msg_info(cpl_func,
"Subtract spatial median to each cube plane");
474 cpl_msg_info(cpl_func,
"Coadding clipping mode: TRUE");
476 cpl_msg_info(cpl_func,
"Coadding clipping mode: FALSE");
480 cpl_msg_info(cpl_func,
"Coadding compute mode: %s", compute_mode);
486 mergedImageData = cpl_calloc(nz,
sizeof(cpl_image*)));
488 mergedImageError = cpl_calloc(nz,
sizeof(cpl_image*)));
490 mergedImageDIT = cpl_calloc(nz,
sizeof(cpl_image*)));
492 mergedCubeData = cpl_imagelist_new());
494 mergedCubeError = cpl_imagelist_new());
496 mergedCubeDIT = cpl_imagelist_new());
500 return (
int)cpl_error_get_code();
504 cpl_msg_info(cpl_func,
" Processing %d planes...", nz);
505HDRL_OMP(omp parallel
for reduction(max:err))
506 for (
int z = 0; z < nz; z++) {
507 cpl_msg_debug(cpl_func,
" Processing plane [%4.4d] of %d", z, nz);
508 cpl_image **imagesData = cpl_calloc(nframes,
sizeof(cpl_image*)),
509 **imagesError = cpl_calloc(nframes,
sizeof(cpl_image*));
512 imagesData, imagesError,
513 z, edge_trim, subtract_background);
520 &mergedImageError[z],
529 for (
int i = 0; i < nframes; i++) {
533 cpl_free(imagesData);
534 cpl_free(imagesError);
541 if (err != CPL_ERROR_NONE) {
542 cpl_msg_error(__func__,
"Combining jittered images failed!");
543 cpl_error_set(__func__, CPL_ERROR_ILLEGAL_INPUT);
544 return (
int)cpl_error_get_code();
547 for (
int z = 0; z < nz; z++) {
548 if (!mergedImageData[z]) {
549 mergedImageData[z] = cpl_image_new(nx_out, ny_out, CPL_TYPE_DOUBLE);
551 cpl_imagelist_set(mergedCubeData, mergedImageData[z], z);
553 if (!mergedImageError[z]) {
554 mergedImageError[z] = cpl_image_new(nx_out,ny_out,CPL_TYPE_DOUBLE);
556 cpl_imagelist_set(mergedCubeError, mergedImageError[z], z);
558 if (!mergedImageDIT[z]) {
559 mergedImageDIT[z] = cpl_image_new(nx_out,ny_out,CPL_TYPE_DOUBLE);
561 cpl_imagelist_set(mergedCubeDIT, mergedImageDIT[z], z);
565 frame = cpl_frameset_get_position_const(frameset, 1);
566 plist = cpl_propertylist_load(files[0], 0);
567 cpl_propertylist_set_string(plist, CPL_DFS_PRO_CATG, ERIS_IFU_PRO_COMBINE);
568 cpl_dfs_save_propertylist(frameset, NULL, parlist, frameset, frame, REC_NAME_COMBINE,
569 plist, NULL, PACKAGE
"/" PACKAGE_VERSION, name_o);
572 plist = cpl_propertylist_load(files[0], 1);
574 double crpix1 = cpl_propertylist_get_double(plist,
"CRPIX1"),
575 crpix2 = cpl_propertylist_get_double(plist,
"CRPIX2");
577 cpl_propertylist_update_double(plist,
"CRPIX1", crpix1+ref_offx*2-1);
578 cpl_propertylist_update_double(plist,
"CRPIX2", crpix2+ref_offy*2-1);
579 cpl_propertylist_update_string(plist,
"EXTNAME", EXTNAME_DATA);
580 cpl_propertylist_set_comment(plist,
"EXTNAME", EXTNAME_DATA_COMMENT);
581 cpl_propertylist_update_string(plist,
"BUNIT",
"adu/s");
582 cpl_imagelist_save(mergedCubeData, name_o, CPL_TYPE_DOUBLE, plist, CPL_IO_EXTEND);
584 cpl_propertylist_update_string(plist,
"EXTNAME", EXTNAME_ERROR);
585 cpl_propertylist_set_comment(plist,
"EXTNAME", EXTNAME_ERROR_COMMENT);
586 cpl_propertylist_update_string(plist,
"BUNIT",
"adu/s");
587 cpl_imagelist_save(mergedCubeError, name_o, CPL_TYPE_DOUBLE, plist, CPL_IO_EXTEND);
589 cpl_propertylist_update_string(plist,
"EXTNAME", EXTNAME_DQ);
590 cpl_propertylist_set_comment(plist,
"EXTNAME", EXTNAME_DQ_COMMENT);
591 cpl_propertylist_update_string(plist,
"BUNIT",
"sec");
592 cpl_imagelist_save(mergedCubeDIT, name_o, CPL_TYPE_DOUBLE, plist, CPL_IO_EXTEND);
595 for (
int i = 0; i < nz; i++) {
600 cpl_free(mergedImageData);
601 cpl_free(mergedImageError);
602 cpl_free(mergedImageDIT);
608 return (
int)cpl_error_get_code();
616cpl_error_code eris_ifu_combine_check_inputs(cpl_frameset *frameset)
618 const cpl_frame *frame = NULL;
619 const char *tag = NULL;
620 int fs_size = cpl_frameset_get_size(frameset);
624 cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
625 cpl_msg_error(cpl_func,
"Number of frames in SOF less than 2") ;
626 return cpl_error_get_code();
630 for (
int i = 0; i < fs_size; i++){
631 frame = cpl_frameset_get_position_const(frameset, i);
632 tag = cpl_frame_get_tag(frame);
633 if (strcmp(tag, ERIS_IFU_COMBINE_DOCATG)) {
634 cpl_error_set(cpl_func, CPL_ERROR_ILLEGAL_INPUT);
635 cpl_msg_error(cpl_func,
"Wrong frame tag %s; should be %s",
636 tag, ERIS_IFU_COMBINE_DOCATG) ;
637 return cpl_error_get_code();
640 return cpl_error_get_code();
cpl_error_code eris_dfs_set_groups(cpl_frameset *self)
Set the group as RAW or CALIB in a frameset.
void eris_ifu_combine_convert_0_to_NaN_img(cpl_image *img)
eris_ifu_combine_convert_0_to_NaN_img
int eris_ifu_combine_min_cube_size(const cpl_frameset *fs)
eris_ifu_combine_min_cube_size
cpl_error_code eris_ifu_combine_read_image_planes(const cpl_frameset *frameset, cpl_image **imagesData, cpl_image **imagesError, int z, int edge_trim, bool subtract_background)
Read image planes from a frameset.
cpl_error_code eris_ifu_combine_jittered_images(cpl_image **imagesData, cpl_image **imagesError, int nx_out, int ny_out, cpl_image **mergedImageData, cpl_image **mergedImageError, cpl_image **mergedImageDIT, int n_cubes, const float *offsetx, const float *offsety, const double *exptimes, const double kappa, const char *compute_mode, const int pclip)
Combine jittered images into a merged image with optional kappa-sigma clipping.
cpl_error_code eris_ifu_combine_auto_size_cube(const float *offsetx, const float *offsety, const int nframes, float *ref_offx, float *ref_offy, int *size_x, int *size_y)
Computes size of coadded cube.
#define BRK_IF_ERROR(function)
If function is or returns an error != CPL_ERROR_NONE, then the try-block is exited.
#define CHECK_ERROR_STATE(void)
Check the CPL error state, and exit the try-block if not CPL_ERROR_NONE.
#define BRK_WITH_ERROR_MSG(code,...)
Set a new CPL error, and exit the try-block.
#define TRY
Beginning of a TRY-block.
#define CATCH
End of a TRY-block, beginning of a CATCH-block.
#define BRK_IF_NULL(function)
If function is or returns a NULL pointer, then the try-block is exited.
#define CATCH_MSGS()
Displays an error message stack.
cpl_error_code eris_ifu_combine(cubeType obj_type, cpl_frameset *frameset, const cpl_parameterlist *parlist, const char *recipe_name, const char *pipefile_prefix)
Resample and combine multiple IFU cubes into a single cube.
void eris_ifu_free_propertylist(cpl_propertylist **item)
Free memory and set pointer to null.
void eris_ifu_free_string(char **item)
Free memory and set pointer to null.
void eris_ifu_free_image(cpl_image **item)
Free memory and set pointer to null.
double eris_pfits_get_exptime(const char *filename)
find out the character string associated to the EXPTIME keyword
int eris_pfits_get_naxis2(const cpl_propertylist *plist)
find out the character string associated to the NAXIS2 keyword
int eris_pfits_get_naxis1(const cpl_propertylist *plist)
find out the character string associated to the NAXIS1 keyword
cpl_error_code eris_files_dont_exist(cpl_frameset *frameset)
Check if all SOF files exist.