/*                                                                              *
 *   This file is part of the ESO UVES Pipeline                                 *
 *   Copyright (C) 2004,2005 European Southern Observatory                      *
 *                                                                              *
 *   This library is free software; you can redistribute it and/or modify       *
 *   it under the terms of the GNU General Public License as published by       *
 *   the Free Software Foundation; either version 2 of the License, or          *
 *   (at your option) any later version.                                        *
 *                                                                              *
 *   This program is distributed in the hope that it will be useful,            *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
 *   GNU General Public License for more details.                               *
 *                                                                              *
 *   You should have received a copy of the GNU General Public License          *
 *   along with this program; if not, write to the Free Software                *
 *   Foundation, 51 Franklin St, Fifth Floor, Boston, MA  02111-1307  USA       *
 *                                                                              */

/*
 * $Author: amodigli $
 * $Date: 2013-08-08 13:36:46 $
 * $Revision: 1.70 $
 * $Name: not supported by cvs2svn $
 *
 */

#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

/*----------------------------------------------------------------------------*/
/**
 * @addtogroup uves_scired
 *
 */
/*----------------------------------------------------------------------------*/
/**@{*/


/*-----------------------------------------------------------------------------
                                Includes
 -----------------------------------------------------------------------------*/
#include <uves_reduce_scired.h>

#include <uves_reduce.h>
#include <uves_reduce_utils.h>
#include <uves_corrbadpix.h>

#include <uves_chip.h>
#include <uves_plot.h>
#include <uves_dfs.h>
#include <uves_pfits.h>
#include <uves_parameters.h>
#include <uves_msg.h>
#include <uves_utils.h>
#include <uves_utils_wrappers.h>
#include <uves_qclog.h>
#include <uves_error.h>
#include <uves_merge.h>
#include <uves.h>
#include <uves_dump.h>

#include <cpl.h>
#include <string.h>
/*-----------------------------------------------------------------------------
                            Functions prototypes
 -----------------------------------------------------------------------------*/

static void
scired_qclog(const cpl_table* info_tbl, 
         const uves_propertylist *raw_header,
         const cpl_image *raw_image,
         double slit,
         cpl_table* qclog);

static void
tflat_qclog(const cpl_image* ima,
        const uves_propertylist *raw_header,
        cpl_table* qclog);


/*-----------------------------------------------------------------------------
                            Implementation
 -----------------------------------------------------------------------------*/
const char * const uves_scired_desc_short = "Reduces a science frame";
const char * const uves_scired_desc =
"This recipe reduces a science frame (SCIENCE_BLUE or SCIENCE_RED, or\n"
"UVES_SCI_POINT_BLUE or UVES_SCI_POINT_RED, or \n"
"UVES_SCI_EXTND_BLUE or UVES_SCI_EXTND_RED or \n"
"UVES_SCI_SLICER_BLUE or UVES_SCI_SLICER_RED) using "
"a combination (depending on recipe parameters and provided input frames) of "
"the steps:\n"
" - bias subtraction,\n"
" - dark subtraction,\n"
" - background subtraction,\n"
" - extraction/cosmic ray removal,\n"
" - flat field correction,\n"
" - wavelength rebinning,\n"
" - sky subtraction,\n"
" - order merging,\n"
" - response correction (if response curve is provided).\n"
"\n"
"Additional input for this recipe are: \n"
"order table(s) for each chip, ORDER_TABLE_xxxx (where xxxx=BLUE, REDL, REDU),\n"
"line table(s) for each chip, LINE_TABLE_xxxx, a master bias frame,\n"
"MASTER_BIAS_xxxx, a master flat, MASTER_FLAT_xxxx, \n"
"optionally an instrument  response table, INSTR_RESPONSE_xxx\n"
"optionally a table describing the atmospheric extintion,\n"
"EXTCOEFF_TABLE. \n"
"For each chip (xxxx = BLUE, REDL, REDU) the recipe produces a combination of "
"the products:\n"
" 'RED_SCIENCE_xxxx'        Reduced science spectrum\n"
" 'MERGED_SCIENCE_xxxx'     Merged spectrum, no sky subtraction\n"
" 'WCALIB_SCIENCE_xxxx'     Extracted, wavelength calibrated frame in\n"
"                           (wavelength, order) space\n"
" 'WCALIB_FF_SCIENCE_xxxx'  Extracted, flat-fielded, wave.cal. frame in\n"
"                           (wavelength, order) space\n"
"                           (Only if flatfielding done)\n"
" 'WCALIB_FLAT_OBJ_xxxx'    Extracted, wavelength calibrated flat field\n"
"                           in (wavelength, order) space\n"
"                           (Only if flatfielding done)\n"
" 'ERRORBAR_SCIENCE_xxxx'   Error bars of 'RED_SCIENCE_xxxx'\n"
" 'VARIANCE_SCIENCE_xxxx'   Variance of extracted, flatfielded object in\n"
"                           (pixel, order) space\n"
" 'ORDER_TRACE_xxxx'        Table describing the spatial profile\n"
" 'FLUXCAL_SCIENCE_xxxx'    Flux-calibrated science spectrum\n"
" 'FLUXCAL_ERROR_xxxx'      Error bars of 'FLUXCAL_SCIENCE_xxxx'\n"
" 'BKG_SCI_xxxx'            The subtracted background image\n"
" 'CRMASK_xxxx'             List of cosmic ray hits\n"
" 'MERGED_SKY_xxxx'         The merged sky spectrum\n"
" 'EXT_2D_SCIENCE_xxxx'     The 2d extracted spectrum\n"
" 'FF2D_SCIENCE_xxxx'       The 2d extracted, flat-fielded spectrum\n"
" 'WCAL2D_SCIENCE_xxxx'     The 2d extracted, flat-fielded, wave.cal. spectrum\n"
" 'MER2D_SCIENCE_xxxx'      The 2d reduced, flux-calibrated (if possible) \n"
"                           science spectrum\n";





static uves_propertylist*
uves_paste_wave_accuracy(const uves_propertylist* header_from)
{
  uves_propertylist* header_add=NULL;
  double waverms=0;
  double wavenlin=0;
  double waveerr=0;
  double wavesys=0;
  const char* key_comm=NULL;
  char key_name_i[40];
  char key_name_o[40];
  uves_msg("paste wave accuracy");
  header_add=uves_propertylist_new();

  sprintf(key_name_o,"CUNIT1");
  key_comm="Wavelength units";
  uves_propertylist_append_c_string(header_add,key_name_o,"Angstrom",key_comm);

  sprintf(key_name_i,"ESO QC LINE RESIDRMS WLU");
  sprintf(key_name_o,"LAMRMS");
  key_comm="RMS of wavelength solution [CUNIT1]";

  if(uves_propertylist_has(header_from,key_name_i)) {
    waverms=uves_propertylist_get_double(header_from,key_name_i);
    uves_propertylist_append_c_double(header_add,key_name_o,waverms,key_comm);

  }


  sprintf(key_name_i,"ESO QC NLINSOL");
  sprintf(key_name_o,"LAMNLIN");
  key_comm="No. of lines used in wavelength solution";

  if(uves_propertylist_has(header_from,key_name_i)) {
    wavenlin=uves_propertylist_get_int(header_from,key_name_i);
    uves_propertylist_append_c_int(header_add,key_name_o,wavenlin,key_comm);

  }


  sprintf(key_name_i,"ESO QC LINE WAVEERR");
  sprintf(key_name_o,"CRDER1");
  key_comm="Wavelength uncertainty [CUNIT1]";
  if(uves_propertylist_has(header_from,key_name_i)) {
    waveerr=uves_propertylist_get_double(header_from,key_name_i);
    uves_propertylist_append_c_double(header_add,key_name_o,waveerr,key_comm);

  }


  sprintf(key_name_i,"ESO QC LINE SYSERR");
  sprintf(key_name_o,"CSYER1");
  key_comm="Typical systematic wavelength error [CUNIT1]";
  if(uves_propertylist_has(header_from,key_name_i)) {
    wavesys=uves_propertylist_get_double(header_from,key_name_i);
    uves_propertylist_append_c_double(header_add,key_name_o,wavesys,key_comm);

  }

  return header_add;

}




/*----------------------------------------------------------------------------*/
/**
  @brief    Setup the recipe options    
  @param    parameters        the parameterlist to fill
  @param    recipe_id         name of calling recipe
  @return   0 if everything is ok
 */
/*----------------------------------------------------------------------------*/
int uves_scired_define_parameters_body(cpl_parameterlist *parameters,
                       const char *recipe_id)
{

    /*****************
     *    General    *
     *****************/
    if (uves_define_global_parameters(parameters) != CPL_ERROR_NONE)
        {
            return -1;
        }

    /**************************************
     *  detector's trap correction        *
     **************************************/
 
    if (uves_corr_traps_define_parameters(parameters,recipe_id) 
        != CPL_ERROR_NONE)
        {
            return -1;
        }

    /*******************
     *  Reduce.        *
     ******************/
    if (uves_propagate_parameters_step(UVES_REDUCE_ID, parameters,
                                       recipe_id, NULL) != 0)
        {
            return -1;
        }
    
    return (cpl_error_get_code() != CPL_ERROR_NONE);
}


const char*
uves_get_pro_catg_special(bool extract_is_2d, merge_method m_method) {
   const char* result=NULL;
   if(extract_is_2d && m_method == MERGE_NOAPPEND) {
      result="";
   } else if (!extract_is_2d && 
              m_method == MERGE_NOAPPEND) {
      result="_NONMERGED";
   } else {
      result="";
   }

   return result;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Change tag of RAW frames to NONE
  @param    frames         frame set to change
  @return   NULL terminated array of pointers to the frames that were changed
 */
/*----------------------------------------------------------------------------*/
static cpl_frame **
set_all_raw_none(cpl_frameset *frames)
{
    cpl_frame **result = NULL;
    cpl_frame *f;
    int i;

    result = cpl_calloc( cpl_frameset_get_size(frames) + 1,
                         sizeof(*result) );
    
    for (f = cpl_frameset_get_first(frames), i = 0;
         f != NULL;
         f = cpl_frameset_get_next(frames))
        {
            if (cpl_frame_get_group(f) == CPL_FRAME_GROUP_RAW)
                {
                    /* Change + remember this frame */
                    cpl_frame_set_group(f, CPL_FRAME_GROUP_NONE);
                    result[i] = f;
                    i++;
                }
        }

    /* 'result' is now a NULL-terminated array of the frames that were changed */

    return result;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    Reduce one chip of a UVES science frame
  @param    raw_image         The raw image
  @param    raw_header        FITS header of raw image
  @param    rotated_header    Header describing the geometry of the raw image after
                              rotation and removal of pre- and overscan areas
  @param    master_bias       The master bias image for this chip, or NULL
  @param    master_dark       The master bias image for this chip, or NULL
  @param    mdark_header      FITS header of master dark frame
  @param    master_flat       The master flat image for this chip, or NULL
  @param    mflat_header      FITS header of master flat frame
  @param    mdark_header      FITS header of master dark frame
  @param    ordertable        Order table describing the order locations on the raw image
  @param    order_locations   The polynomial describing the order positions
  @param    linetable         Length 3 array of linetables for sky, object, sky.
  @param    linetable_header  Length 3 array of linetable headers for sky, object, sky.
  @param    response_curve    The response curve (image of height 1) used for flux correction
                              If non-NULL this is used for flux correction
  @param    master_response   response curve as a table. If response_curve is NULL and this
                              is non-NULL, it is used for flux correction
  @param    response_curve_header  Header describing the geometry of the @em response_curve.
  @param    atm_extinction    Table of atmospheric extinction coefficients. 
  @param    dispersion_relation  Length 3 array of dispersion relations for sky, object, sky.
  @param    chip              CCD chip
  @param    debug_mode             If set to true, intermediate results are saved to the 
                              current directory
  @param    parameters        The recipe parameter list containing parameters
                              for background subtraction, flat-fielding, extraction, rebinning
  @param    recipe_id         name of calling recipe
  @param    x2d               (output) In 2d extraction mode, the extracted spectrum
  @param    x2d_header        (output) In 2d extraction mode, header of extracted spectrum
  @param    fx2d              (output) In 2d extraction mode, the flat-fielded, 
                              extracted spectrum
  @param    background        (output) The background that was subtracted from the raw image
  @param    flatfielded_variance (output) Variance of extracted, flat-fielded spectrum
  @param    flatfielded_variance_header (output) Header of @em flatfielded_variance
  @param    resampled_science (output) The extracted, rebinned spectrum. If flat-field method
                              is 'extract', this spectrum was @em not flat-fielded.
  @param    resampled_mf      (output) As @em resampled_science, but for the master flat-field
  @param    rebinned_science  (output) The extracted, flat-fielded, rebinned spectrum
  @param    rebinned_header   (output) Header of @em rebinned_science
  @param    merged_sky        (output) The extracted, rebinned, merged sky spectrum
  @param    merged_science    (output) The extracted, rebinned, merged object spectrum
  @param    merged_header     (output) Header of @em merged_science, also applies to @em
                              @em reduced_science and @em reduced_science_error
  @param    reduced_science   (output) The extracted, flat-fielded, rebinned, merged,
                              sky-subtracted spectrum
  @param    reduced_science_error  (output) The error (1 sigma) of @em reduced_science
  @param    cosmic_mask       (output) In optimal extraction mode, the list of hot pixels
  @param    fluxcal_science   (output) As @em reduced_science but also response corrected
                              (if response curve was provided)
  @param    fluxcal_error     (output) Error (1 sigma) of @em fluxcal_science
                              (if response curve was provided)
  @param    fluxcal_header    (output) Header describing the geometry of @em fluxcal_science
                              and @em fluxcal_error
  @param    info_tbl          (output) optimal extraction QC parameters
  @param    extraction_slit   (output) extraction slit length used
  @param    order_trace       (output) In optimal extraction mode, table describing the
                              measured spatial profile
                               
                                    
  @return   CPL_ERROR_NONE iff okay

  This function does a science reduction (see @c uves_reduce) and, if a response curve
  if provided, corrects for the instrument response (by moving the science spectrum
  to the top of the atmosphere (see @em uves_normalize_spectrum) and multiplying be
  the response function).

 */
/*----------------------------------------------------------------------------*/

static cpl_error_code
uves_scired_process_chip(const cpl_image *raw_image, 
                         const uves_propertylist *raw_header,
                         const uves_propertylist *rotated_header,
                         const cpl_image *master_bias,
                         const cpl_image *master_dark, 
                         const uves_propertylist *mdark_header,
                         const cpl_image *master_flat, 
                         const uves_propertylist *mflat_header,
                         const cpl_table *ordertable, 
                         const polynomial *order_locations,
                         const cpl_table *linetable[3], 
                         const uves_propertylist *linetable_header[3], 
                         const polynomial *dispersion_relation[3],
                         const cpl_image *response_curve, 
             const cpl_table *master_response,
                         const uves_propertylist *response_curve_header,
                         const cpl_table *atm_extinction,
                         enum uves_chip chip,
                         /* General */
                         bool   debug_mode,
                         /* Backsub */
                         /* Flat fielding */
                         /* Extraction */
                         /* Rebinning  */
                         const cpl_parameterlist *parameters,
             const char *recipe_id,
                         /* Output     */
                         cpl_image **x2d, uves_propertylist **x2d_header,
                         cpl_image **fx2d,
                         cpl_image **background,
                         cpl_image **flatfielded_variance,
                         uves_propertylist **flatfielded_variance_header,
                         cpl_image **resampled_science,
                         cpl_image **resampled_mf,
                         cpl_image **rebinned_science, 
                         uves_propertylist **rebinned_header,
                         cpl_image **merged_sky,
                         cpl_image **merged_science, 
                         uves_propertylist **merged_header,
                         cpl_image **reduced_science,
                         cpl_image **reduced_science_error,
                         cpl_table **cosmic_mask,
                         cpl_image **wave_map,
                         cpl_image **fluxcal_science,
                         cpl_image **fluxcal_error,
                         uves_propertylist **fluxcal_header,
                         cpl_table **info_tbl,
             double *extraction_slit,
                         cpl_table **order_trace)
{
    cpl_image *rebinned_noise = NULL;
    cpl_image *merged_noise   = NULL;

    cpl_image *reduced_rebinned = NULL;
    cpl_image *reduced_rebinned_noise = NULL;

    cpl_table *response_table = NULL;

    /* Do the science reduction. Produces wave.cal. spectra. */
    uves_msg("Reducing science object");

    check( uves_reduce(raw_image, 
                       raw_header, 
                       rotated_header,
                       master_bias,
                       master_dark, 
                       mdark_header,
                       master_flat, 
                       mflat_header, 
                       ordertable, 
                       order_locations,
                       linetable, 
                       linetable_header, 
                       dispersion_relation,
                       chip,
                       debug_mode, 
                       parameters, 
                       recipe_id,
                       "",
                       /* Output */
                       x2d, 
                       x2d_header, 
                       fx2d,
                      cosmic_mask,
                       wave_map,
                        background,
                       flatfielded_variance, 
                       flatfielded_variance_header,
                       resampled_science,
                       resampled_mf,
                       merged_sky,
                       rebinned_science, 
                       &rebinned_noise, 
                       rebinned_header,
                       merged_science, 
                       &merged_noise, 
                       merged_header,
                       &reduced_rebinned, 
                       &reduced_rebinned_noise,
                       reduced_science, 
                       reduced_science_error,
                       info_tbl,
               extraction_slit,
                       order_trace),
           "Could not reduce frame");

    /* Plot middle row */
    check( uves_plot_image_rows(*reduced_science, 
                1 + cpl_image_get_size_y(*reduced_science)/2,
                1 + cpl_image_get_size_y(*reduced_science)/2, 1,
                "Wavelength (arbitrary units)", "Relative flux",
                "Reduced science spectrum"),
       "Plotting failed");
    

    /*
     * Flux calibrate the reduced spectrum
     * (which is an image of height 1, or more if extract.method=2d)
     */

    if (response_curve != NULL || master_response != NULL)
        {
            double lambda_start;
            double dlambda;
            int bin;

            /* Number of spatial traces (> 1 for 2d extraction) */
            int n_traces = cpl_image_get_size_y(*reduced_science);
            
            uves_msg("Flux calibrating spectrum");

            /* We cannot be certain that the formats (wavelength start, bin width)
               of the science spectrum and the response curve are identical.
               Therefore we interpolate the response curve at the wavelengths 
               defined by the bins of the science spectrum. */
                


            /* If the response curve is an image, convert to table.
           This is needed for the interpolation */
        if (response_curve != NULL) {
                response_table = cpl_table_new(cpl_image_get_size_x(response_curve));
                cpl_table_new_column(response_table, "LAMBDA", CPL_TYPE_DOUBLE);
                cpl_table_new_column(response_table, "FLUX_CONV", CPL_TYPE_DOUBLE);
                
                check( lambda_start = uves_pfits_get_crval1(response_curve_header),
                       "Error reading response curve start wavelength from header");
                
                check( dlambda = uves_pfits_get_cdelt1(response_curve_header),
                       "Error reading bin width from header");
                
                for (bin = 1; bin <= cpl_image_get_size_x(response_curve); bin++) {
                    double lambda;
                    double response;
                    int pis_rejected;
                    
                    lambda = lambda_start + (bin-1) * dlambda;
                    
                    check( response = cpl_image_get(response_curve, bin, 1, &pis_rejected),
                           "Error reading response curve bin = %d", bin);
                    
                    check((cpl_table_set_double(response_table, "LAMBDA", bin - 1, lambda),
                           cpl_table_set_double(response_table, "FLUX_CONV", bin - 1, response)),
                          "Error updating response table at row %d", bin - 1);
                }
            } 
        else {
        response_table = cpl_table_duplicate( master_response );
        } /* Response table created */        
            
            /*
             * Correct for exposure time, gain, binning, atm. ext.
             */
            
            check( *fluxcal_science = uves_normalize_spectrum(*reduced_science,
                                                              *reduced_science_error,
                                                              *merged_header,
                                                              raw_header,
                                                              n_traces,
                                                              chip,
                                                              atm_extinction,
                                                              true,    /* Divide by binning? */
                                                              fluxcal_error), 
                   "Error normalizing reduced spectrum");
            
            /* 
             * Flux calibrate reduced spectrum 
             *  flux := flux * response
             */
            uves_msg("Multiplying by response function");
            {
                int nbins   = cpl_image_get_size_x(*fluxcal_science);
                int ntraces = cpl_image_get_size_y(*fluxcal_science);
                double *fluxcal_science_data  = cpl_image_get_data_double(*fluxcal_science);
                double *fluxcal_science_noise = cpl_image_get_data_double(*fluxcal_error);
                
                check( lambda_start = uves_pfits_get_crval1(*merged_header),
                       "Error reading start wavelength from reduced science header");
                
                check( dlambda = uves_pfits_get_cdelt1(*merged_header),
                       "Error reading bin width from header");
                
                for (bin = 1; bin <= nbins; bin++)
                    {
                        double lambda;
                        double response;
                        int trace;       /* Spatial traces (for 2d extracted spectra) */
                        int istart = 0;

                        lambda = lambda_start + (bin-1) * dlambda;
                        
                        check( response = 
                               uves_spline_hermite_table(lambda, response_table, 
                           "LAMBDA", "FLUX_CONV", &istart),
                               "Error interpolating response curve at lambda = %f wlu", lambda);
                        
                        for (trace = 1; trace <= ntraces; trace++)
                            {
                /* Don't check for bad pixels here, also correct those.
                 * The fluxcal image has the same bad pixels as the reduced_science
                 * image */

                fluxcal_science_data [(bin-1) + (trace-1)*nbins] *= response;
                fluxcal_science_noise[(bin-1) + (trace-1)*nbins] *= response;

                /* Do not propagate the error of the response 
                   curve which is negligibly small (and unknown at this point!).
                */
                }
            }
                
                /* Plot middle row */
                check( uves_plot_image_rows(*fluxcal_science, 
                                            1 + cpl_image_get_size_y(*fluxcal_science)/2,
                                            1 + cpl_image_get_size_y(*fluxcal_science)/2, 1,
                                            "Wavelength (arbitrary units)", 
                                            "Flux (10^-16 erg/cm^2/A/sec)", 
                                            "Flux calibrated science spectrum"),
                       "Plotting failed");
                
                check( *fluxcal_header = uves_initialize_image_header("AWAV", " ", 
                                                                      "Angstrom", NULL,
                                                                      "10^-16 erg/cm^2/A/sec",
                                                                      1,
                                                                      lambda_start, 1.0,
                                                                      1.0, 1.0,
                                                                      dlambda, 1.0),
                       "Error initializing flux calibrated spectrum header");
            } /* Done multiplying by response curve */
        }
    else
        {
            uves_msg("Skipping absolute flux calibration");
        }
    
  cleanup:
    uves_free_image(&rebinned_noise);
    uves_free_image(&merged_noise);
    uves_free_image(&reduced_rebinned_noise);
    uves_free_image(&reduced_rebinned);
    uves_free_table(&response_table);

    if (cpl_error_get_code() != CPL_ERROR_NONE)
        {
        }

    return cpl_error_get_code();
}


/*----------------------------------------------------------------------------*/
/**
  @brief    Get the command line options and execute the data reduction
  @param    parameters  the parameters list
  @param    frames      the frames list
  @param    recipe_id   the recipe name (will be written to FITS headers)
  @param    starttime   time when calling recipe started
  @return   CPL_ERROR_NONE if everything is ok
 */
/*----------------------------------------------------------------------------*/
void uves_reduce_scired(cpl_frameset *frames, const cpl_parameterlist *parameters, 
            const char *recipe_id, const char *starttime)
{
    /* Recipe parameters */
    bool debug_mode;
    bool CLEAN_TRAPS;
    bool extract_is_2d = false;     /* Are we doing a 2d reduction? */

    /* Input, raw */
    cpl_image        *raw_image[2]      = {NULL, NULL};
    uves_propertylist *raw_header[2]     = {NULL, NULL};
    uves_propertylist *rotated_header[2] = {NULL, NULL};

    /* Input, calib */
    cpl_image        *master_bias        = NULL;
    uves_propertylist *master_bias_header = NULL;

    cpl_image        *master_flat        = NULL;
    uves_propertylist *master_flat_header = NULL;

    cpl_image        *master_dark        = NULL;
    uves_propertylist *master_dark_header = NULL;

    cpl_table        *ordertable       = NULL;
    uves_propertylist *ordertable_header= NULL;
    polynomial       *order_locations  = NULL;
    cpl_table        *traces           = NULL;

    /* Line tables for sky, object, sky (UVES specific) */
    const cpl_table        *linetable[3]           = {NULL, NULL, NULL};
    const uves_propertylist *linetable_header[3]    = {NULL, NULL, NULL};
    const polynomial       *dispersion_relation[3] = {NULL, NULL, NULL};

    cpl_image        *response_curve        = NULL;
    uves_propertylist *response_curve_header = NULL;
    cpl_table        *master_response       = NULL;

    cpl_table        *atm_extinction        = NULL;

    /* Output */
    /*  BKG_SCI           */
    cpl_image        *background            = NULL;

    /*  VARIANCE_SCIENCE                 */  
    cpl_image        *flatfielded_variance  = NULL;
    uves_propertylist *flatfielded_variance_header = NULL;

    /*  WCALIB_SCIENCE    */
    cpl_image        *resampled_science     = NULL;     /* extracted -> rebinned */
    /*  WCALIB_FLAT_OBJ   */
    cpl_image        *resampled_mf          = NULL;
    /*  WCALIB_FF_SCIENCE */
    cpl_image        *rebinned_science      = NULL;     /* extracted -> ff -> rebinned */

    uves_propertylist *rebinned_header       = NULL;

    /*  MERGED_SKY        */
    cpl_image        *merged_sky            = NULL;

    /*  MERGED_SCIENCE / MER2D_SCIENCE   */
    /*  RED_SCIENCE                      */
    /*  ERRORBAR_SCIENCE                 */
    cpl_image        *merged_science        = NULL;
    uves_propertylist *merged_header         = NULL;
    cpl_image        *reduced_science       = NULL;
    cpl_image        *reduced_science_error = NULL;
  

    /*  FLUXCAL_SCIENCE / FLUXCAL_ERROR */
    cpl_image        *fluxcal_science       = NULL;
    cpl_image        *fluxcal_error         = NULL;
    uves_propertylist *fluxcal_header       = NULL;

    /*  ORDER_TRACE       */
    cpl_table         *order_trace        = NULL;
    uves_propertylist *order_trace_header = NULL;


    /* EXT_2D_SCIENCE */
    cpl_image        *x2d        = NULL;
    uves_propertylist *x2d_header = NULL;
    /* FF2D_SCIENCE */
    cpl_image        *fx2d       = NULL;

    /*  CRMASK            */
    cpl_table        *cosmic_mask           = NULL;
    uves_propertylist *cosmic_mask_header    = NULL;

    /* QC */
    cpl_table* qclog[2] = {NULL, NULL};
    cpl_table *qclog_tflat = NULL;

    /* Local variables */
    const char *raw_filename = "";
    const char *atm_ext_filename = "";
    const char *sci_type = "";
    cpl_frame **raw_frames = NULL;  /* Array of cpl_frame pointers */
    char *product_tag = NULL;
    char *product_filename = NULL;
    char *context = NULL;
    double extraction_slit;

    bool blue  = false;
    enum uves_chip chip;
    int binx = 0;
    int biny = 0;
    
    cpl_table* info_tbl = NULL;
    const char* PROCESS_CHIP=NULL;
    bool red_ccd_is_new=0;
    merge_method m_method;
    const char* catg_is_noappend=NULL;
    cpl_image* wave_map=NULL;
    uves_propertylist* wave_map_header=NULL;
    uves_propertylist* wave_acc_header=NULL;

    /* Read recipe parameters */
    {
        const char *ex_method = "";

        /* General */
        check( uves_get_parameter(parameters, NULL, "uves", "debug", CPL_TYPE_BOOL, &debug_mode),
               "Could not read parameter");



        check( uves_get_parameter(parameters, NULL, "uves", "debug", CPL_TYPE_BOOL, &debug_mode),
               "Could not read parameter");



    check( uves_get_parameter(parameters, NULL, "uves", "process_chip", CPL_TYPE_STRING, &PROCESS_CHIP),
               "Could not read parameter");
    uves_string_toupper((char*)PROCESS_CHIP);


    check( uves_get_parameter(parameters,NULL,recipe_id, "clean_traps", CPL_TYPE_BOOL, &CLEAN_TRAPS),
               "Could not read parameter");


        
        /* Reduction method */
    context = uves_sprintf("%s.%s.%s", recipe_id, UVES_REDUCE_ID, UVES_EXTRACT_ID);

        check( uves_get_parameter(parameters, NULL, 
                                  context, "method",
                                  CPL_TYPE_STRING, &ex_method),
               "Could not read parameter");

        extract_is_2d = (strcmp(ex_method, "2d") == 0); 
    
    /* Load raw image and header, and identify input frame as red or blue */
    check( uves_load_science(frames, &raw_filename, raw_image, raw_header, rotated_header,
                 &blue, &sci_type),
           "Error loading raw frame");
    
    if ((strcmp(sci_type, "SCI_SLICER") == 0 ||
         strcmp(sci_type, "SCI_EXTND" ) == 0) &&
        strcmp(ex_method, "optimal") == 0)
        {
        uves_msg_warning("Going to optimally extract an extended object (%s). "
                 "This may not work because the sky cannot be "
                 "reliably determined",
                 sci_type);
        }
    }
    
    /* Load atmospheric extinction table if present */
    if (cpl_frameset_find(frames, UVES_EXTCOEFF_TABLE) != NULL)
        {
            check( uves_load_atmo_ext(frames, &atm_ext_filename, &atm_extinction),
                   "Error loading atm. extinction coefficients");
            
            uves_msg_low("Using atmospheric extinction table in '%s'", atm_ext_filename);
        }
    else
        {
            uves_msg_low("No atmospheric extinction table. Flux calibration not done");
        }

    check( m_method = uves_get_merge_method(parameters, recipe_id, "reduce"),
           "Could not get merging method");
    
    /* Adjust parameters according to binning 
     * (note that x- and y-directions are swapped later by uves_crop_and_rotate()) 
     */
    check (binx = uves_pfits_get_binx(raw_header[0]), 
           "Could not read x binning factor from input header");
    check (biny = uves_pfits_get_biny(raw_header[0]), 
           "Could not read y binning factor from input header");

    check_nomsg(red_ccd_is_new=uves_ccd_is_new(raw_header[0]));
    /* Loop over one or two chips, over traces and
       over extraction windows */
    for (chip = uves_chip_get_first(blue);
         chip != UVES_CHIP_INVALID; 
         chip = uves_chip_get_next(chip))
        {



      if(strcmp(PROCESS_CHIP,"REDU") == 0) {
	chip = uves_chip_get_next(chip);
      }

            cpl_frame *mflat_frame = NULL;
            const char *ordertable_filename = "";
            const char *linetable_filename = "";
            const char *master_bias_filename = "";
            const char *master_dark_filename = "";
            const char *master_flat_filename = "";
            const char *response_curve_filename = "";
            const char *chip_name = "";
            /* const char *drs_filename        = "";    not used */
            /* Do this to skip REDL chip: chip = uves_chip_get_next(chip); */
            int raw_index = uves_chip_get_index(chip);
            int tracerow;                      /* Index of table row */
            
            uves_msg("Processing %s chip in '%s'",
                     uves_chip_tostring_upper(chip), raw_filename);
            
            check_nomsg( chip_name = uves_pfits_get_chipid(raw_header[raw_index], chip));

            uves_msg_debug("Binning = %dx%d", binx, biny);
            
            
            /* Load master bias, set pointer to NULL if not present */
            uves_free_image(&master_bias);
            uves_free_propertylist(&master_bias_header);
            if (cpl_frameset_find(frames, UVES_MASTER_BIAS(chip)) != NULL)
                {
                    uves_free_image(&master_bias);
                    uves_free_propertylist(&master_bias_header);

                    check( uves_load_mbias(frames, chip_name, &master_bias_filename,
                                           &master_bias, &master_bias_header,
                                           chip),
                           "Error loading master bias");
                    
                    uves_msg_low("Using master bias in '%s'", master_bias_filename);
                }
            else
                {
                    uves_msg_low("No master bias in SOF. Bias subtraction not done");
                }

            /* Load master dark, set pointer to NULL if not present */
            uves_free_image(&master_dark);
            uves_free_propertylist(&master_dark_header);
            if (cpl_frameset_find(frames, UVES_MASTER_DARK(chip))  != NULL ||
                cpl_frameset_find(frames, UVES_MASTER_PDARK(chip)) != NULL)

                {
                    check( uves_load_mdark(frames, chip_name, &master_dark_filename,
                                           &master_dark, &master_dark_header, chip),
                           "Error loading master dark");
                    
                    uves_msg_low("Using master dark in '%s'", master_dark_filename);
                }
            else
                {
                    uves_msg_low("No master dark in SOF. Dark subtraction not done");
                }
            
            /* Load master flat */
            uves_free_image(&master_flat);
            uves_free_propertylist(&master_flat_header);
            check( uves_load_mflat(frames, chip_name, &master_flat_filename, &master_flat, 
                                   &master_flat_header, chip, &mflat_frame),
                   "Error loading master flat");
            
            uves_msg_low("Using master flat in '%s'", master_flat_filename);
            
            
            /* Load the order table for this chip */
            uves_free_table       (&ordertable);
            uves_free_propertylist(&ordertable_header);
            uves_polynomial_delete(&order_locations);
            uves_free_table       (&traces);
            
            check( uves_load_ordertable(frames, 
                    false,  /* FLAMES? */
                    chip_name, 
                                        &ordertable_filename, 
                                        &ordertable, 
                                        &ordertable_header,
                                        NULL, 
                                        &order_locations, 
                                        &traces,
                    NULL, NULL,
                                       NULL, NULL, /* fibre_pos,fibre_mask */
                                        chip, false),
                   "Could not load order table");
            uves_msg_low("Using order table in '%s'", ordertable_filename);

            /* Load response curve, if present. 
               Only if atm. extinction table was present. */
            if (atm_extinction != NULL)
                {
                    if (cpl_frameset_find(frames, UVES_INSTR_RESPONSE(chip)) != NULL ||
            cpl_frameset_find(frames, UVES_MASTER_RESPONSE(chip)) != NULL)
                        {
                            uves_free_image(&response_curve);
                            uves_free_table(&master_response);
                            uves_free_propertylist(&response_curve_header);
                            check( uves_load_response_curve(frames, chip_name,
                                                            &response_curve_filename,
                                                            &response_curve, 
                                &master_response,
                                                            &response_curve_header,
                                                            chip),
                                   "Error loading response curve");
                            
                uves_msg_low("Using %sresponse curve in '%s'", 
                     master_response != NULL ? "master " : "",
                     response_curve_filename);
                        }
                    else
                        {
                            uves_msg_low("No response curve in SOF. "
                                         "Flux calibration not done");
                        }
                }
            else
                {
                    uves_msg_debug("There is no atmospheric extinction table. "
                                   "Do not look for response curve");
                }
            
            /* Loop over all traces (1 trace for UVES) */
            for(tracerow = 0; tracerow < cpl_table_get_nrow(traces); tracerow++)
                {
                    double trace_offset;
                    int trace_number;
                    int trace_enabled;
                    int badpixels_cleaned;
                    trace_offset  = cpl_table_get_double(traces, "Offset"    , tracerow, NULL);
                    trace_number  = cpl_table_get_int   (traces, "TraceID"   , tracerow, NULL);
                    trace_enabled = cpl_table_get_int   (traces, "Tracemask" , tracerow, NULL);
                    
                    if (trace_enabled != 0)
                        {
                            int window;          /* window number */
                            
                            if (cpl_table_get_nrow(traces) > 1) {
                                uves_msg("Processing trace %d", trace_number);
                            }
                            
                            /* This is UVES specific. Load linetable for the 
                               two sky windows (number 1, 3) and for the object
                               window (number 2) */
                            
                            for (window = 1; window <= 3; window ++) {
                                uves_free_table_const ( &(linetable[window-1]) );
                                uves_free_propertylist_const( &(linetable_header[window-1]) );
                                uves_polynomial_delete_const( &(dispersion_relation[window-1]) );
                                check( uves_load_linetable_const(frames,
                               false,  /* FLAMES? */
                               chip_name,
                                                           order_locations,
                                                           cpl_table_get_column_min(
                                                               ordertable, "Order"),
                                                           cpl_table_get_column_max(
                                                               ordertable, "Order"),
                                                           &linetable_filename,
                                                           &(linetable          [window-1]),
                                                           &(linetable_header   [window-1]),
                                                           &(dispersion_relation[window-1]),
                                                           NULL,
                                                           chip,
                                                           trace_number,
                                                           window),
                                       "Could not load line table, window #%d", window);
                                
                                uves_msg_low("Using line table(s) in '%s'", linetable_filename);

                            }
                            uves_propertylist* plist=uves_propertylist_load(linetable_filename,0);
			    uves_free_propertylist(&wave_acc_header);
			    wave_acc_header=uves_paste_wave_accuracy(plist);
                            uves_free_propertylist(&plist);
                            /* end, UVES specific */
                            
                            /* Do the science reduction + flux calibration */
                            uves_free_image(&x2d);
                            uves_free_image(&fx2d);
                            uves_free_propertylist(&x2d_header);
                            uves_free_image(&background);
                            uves_free_image(&flatfielded_variance);
                            uves_free_propertylist(&flatfielded_variance_header);
                            uves_free_image(&resampled_science);
                            uves_free_image(&resampled_mf);
                            uves_free_image(&rebinned_science);
                            uves_free_propertylist(&rebinned_header);
                            uves_free_image(&merged_sky);
                            uves_free_image(&merged_science);
                            uves_free_propertylist(&merged_header);
                            uves_free_image(&reduced_science);
                            uves_free_image(&reduced_science_error);
                            uves_free_table(&cosmic_mask);
                            uves_free_image(&fluxcal_science);
                            uves_free_image(&fluxcal_error);
                            uves_free_propertylist(&fluxcal_header);
                            uves_free_table(&info_tbl);
                            uves_free_table(&order_trace);                            

                            if(CLEAN_TRAPS) {

                            check( badpixels_cleaned = 
                                   uves_correct_badpix_all(raw_image[raw_index],
                                                           raw_header[raw_index], 
                                                           chip, binx, biny, 
                                                           false,red_ccd_is_new),
                                   "Error replacing bad pixels");

                            uves_msg("%d bad pixels replaced", 
                                     badpixels_cleaned);
                            }
  
                            check( uves_scired_process_chip(
                                       raw_image[raw_index], 
                                       raw_header[raw_index],           /* Raw         */
                                       rotated_header[raw_index],
                                       master_bias,           /* Calibration */
                                       master_dark, 
                                       master_dark_header,
                                       master_flat, 
                                       master_flat_header,
                                       ordertable, 
                                       order_locations,
                                       linetable, 
                                       linetable_header, 
                                       dispersion_relation,
                                       response_curve, 
                       master_response,
                                       response_curve_header,
                                       atm_extinction,
                                       chip,
                                       debug_mode, 
                                       parameters,
                       recipe_id,
                                       &x2d, 
                                       &x2d_header, 
                                       &fx2d,            /* Products      */
                                       &background,
                                       &flatfielded_variance,
                                       &flatfielded_variance_header,
                                       &resampled_science,
                                       &resampled_mf,
                                       &rebinned_science, 
                                       &rebinned_header,
                                       &merged_sky,
                                       &merged_science, 
                                       &merged_header,
                                       &reduced_science,
                                       &reduced_science_error,
                                       &cosmic_mask,
				       &wave_map,
                                       &fluxcal_science,
                                       &fluxcal_error,
                                       &fluxcal_header,
                                       &info_tbl,
                       &extraction_slit,
                                       &order_trace),
                                   "Science reduction failed");
                            
                            if (!extract_is_2d)
                                {
                                    uves_qclog_delete(&qclog[0]);
                                    qclog[0] = uves_qclog_init(raw_header[raw_index], chip);
                                    
                                    check( scired_qclog(info_tbl, 
                            raw_header[raw_index],
                            raw_image[raw_index],
                            extraction_slit,
                            qclog[0]),
                                           "Could not compute QC parameters");
                                }
                            
                            uves_msg("Saving products...");
                            /* It is important to save products in the correct
                             * order, because users want to identify products depending on
                             * their number rather than the PRO-CATG (which would perhaps
                             * make more sense).
                             */
    
                            /* Save RED_SCIENCE / RED2D_SCIENCE = 
                               (reduced_science, merged_header) */
                            cpl_free(product_filename);
                            check( product_filename = 
				   (extract_is_2d) ? 
                                   uves_scired_red_2d_science_filename(chip) : 
                                   ((m_method == MERGE_NOAPPEND) ? 
                                   uves_scired_red_noappend_science_filename(chip): uves_scired_red_science_filename(chip)),
                                   "Error getting filename");   

                            cpl_free(product_tag);
                            catg_is_noappend=uves_get_pro_catg_special(extract_is_2d,m_method);
                            product_tag = uves_sprintf(
                               "RED%s%s_%s_%s",
                                catg_is_noappend,
                (extract_is_2d) ? "_2D" : "",
                sci_type, uves_chip_tostring_upper(chip));
                            
				    uves_propertylist_append(merged_header,wave_acc_header);

                            check( uves_frameset_insert(frames,
                                                        reduced_science,
                                                        CPL_FRAME_GROUP_PRODUCT,
                                                        CPL_FRAME_TYPE_IMAGE,
                                                        CPL_FRAME_LEVEL_FINAL,
                                                        product_filename,
                                                        product_tag,
                                                        raw_header[raw_index],
                                                        merged_header,
                                                        NULL,
                                                        parameters,
                                                        recipe_id,
                                                        PACKAGE "/" PACKAGE_VERSION,
                                                        qclog,
                                                        starttime,
                            false,    /* Do not create QC log */
                                                        UVES_ALL_STATS),
                                   "Could not add reduced science spectrum '%s' (%s) to frameset",
                                   product_filename, product_tag);
                            
                            uves_msg("Reduced science spectrum '%s' (%s) added to frameset",
                                     product_filename, product_tag);
                            
                            if (extract_is_2d)
                                {
                                    /* Save EXT_2D_SCIENCE_xxxx = (x2d, x2d_header) */
                                    cpl_free(product_filename);
                                    check( product_filename = uves_scired_ext2d_filename(chip), 
                                           "Error getting filename");
                                    
                                    cpl_free(product_tag);
                                    product_tag = 
                                        uves_sprintf("EXT_2D_%s_%s", sci_type,
                             uves_chip_tostring_upper(chip));
                                    


                                    check( uves_frameset_insert(frames,
                                                                x2d,
                                                                CPL_FRAME_GROUP_PRODUCT,
                                                                CPL_FRAME_TYPE_IMAGE,
                                                                CPL_FRAME_LEVEL_FINAL,
                                                                product_filename,
                                                                product_tag,
                                                                raw_header[raw_index],
                                                                x2d_header,
                                                                NULL,
                                                                parameters,
                                                                recipe_id,
                                                                PACKAGE "/" PACKAGE_VERSION, 
                                                                qclog,
                                                                starttime, false, 
                                                                UVES_ALL_STATS),
                                           "Could not add 2d extracted "
                                           "spectrum '%s' (%s) to frameset",
                                           product_filename, product_tag);
                                    
                                    uves_msg("2d extracted spectrum '%s' (%s) added to frameset",
                                             product_filename, product_tag);

                                }
                            
                            /* Save MERGED_SCIENCE / MER2D_SCIENCE = 
                               (merged_science, merged_header) */
                            cpl_free(product_filename);
                            check( product_filename = (extract_is_2d) ? 
                                 uves_scired_merged_2d_science_filename(chip) :
                                 uves_scired_merged_science_filename(chip),
                                   "Error getting filename");
                            cpl_free(product_tag);
                            product_tag = uves_sprintf(
                "%s_%s_%s",
                (extract_is_2d) ? "MER_2D" : "MERGED",
                                sci_type, uves_chip_tostring_upper(chip));

			    uves_propertylist_append(merged_header,wave_acc_header);

                            check( uves_frameset_insert(frames,
                                                        merged_science,
                                                        CPL_FRAME_GROUP_PRODUCT,
                                                        CPL_FRAME_TYPE_IMAGE,
                                                        CPL_FRAME_LEVEL_FINAL,
                                                        product_filename,
                                                        product_tag,
                                                        raw_header[raw_index],
                                                        merged_header,
                                                        NULL,
                                                        parameters,
                                                        recipe_id,
                                                        PACKAGE "/" PACKAGE_VERSION,
                                                        qclog,
                                                        starttime, false, 
                                                        UVES_ALL_STATS),
                                   "Could not add merged science spectrum '%s' (%s) to frameset",
                                   product_filename, product_tag);
                            
                            uves_msg("Merged science spectrum '%s' (%s) added to frameset", 
                                     product_filename, product_tag);
                            
                            if (!extract_is_2d)
                                {
                                    /* Save WCALIB_SCIENCE = (resampled_science, rebinned_header) 
                                     * If ff_method = extract, this product was not flat-fielded
                                     */
                                    cpl_free(product_filename);
                                    check( product_filename = (extract_is_2d) ?
                                           uves_scired_resampled_2d_filename(chip) :
                                           uves_scired_resampled_filename(chip), 
                                           "Error getting filename");   

                                    cpl_free(product_tag);
                                    product_tag = 
                                        uves_sprintf("WCALIB_%s_%s", sci_type,
                             uves_chip_tostring_upper(chip));
                            
			    uves_propertylist_append(rebinned_header,wave_acc_header);

                                    check( uves_frameset_insert(frames,
                                                                resampled_science,
                                                                CPL_FRAME_GROUP_PRODUCT,
                                                                CPL_FRAME_TYPE_IMAGE,
                                                                CPL_FRAME_LEVEL_FINAL,
                                                                product_filename,
                                                                product_tag,
                                                                raw_header[raw_index],
                                                                rebinned_header,
                                                                NULL,
                                                                parameters,
                                                                recipe_id,
                                                                PACKAGE "/" PACKAGE_VERSION,
                                                                qclog,
                                                                starttime, false, 
                                                                UVES_ALL_STATS),
                                           "Could not add wavelength calibrated science "
                                           "spectrum '%s' (%s) to frameset", product_filename, 
                                           product_tag);
                                    
                                    uves_msg("Wavelength calibrated science spectrum '%s' "
                                             "(%s) added to frameset", product_filename, 
                                             product_tag);


                                    cpl_free(product_filename);
                              
                                    check( product_filename = 
                                           uves_order_extract_qc_standard_filename(chip),
                                           "Error getting filename");   


                                    check( uves_frameset_insert(frames,
                                                                info_tbl,
                                                                CPL_FRAME_GROUP_PRODUCT,
                                                                CPL_FRAME_TYPE_TABLE,
                                                                CPL_FRAME_LEVEL_INTERMEDIATE,
                                                                product_filename,
                                                                UVES_ORDER_EXTRACT_QC(chip),
                                                                raw_header[raw_index],
                                                                rotated_header[raw_index],
                                                                NULL,
                                                                parameters,
                                                                recipe_id,
                                                                PACKAGE "/" PACKAGE_VERSION,
                                                                NULL,
                                                                starttime, true, 
                                                                0),
                                           "Could not add extraction quality table %s (%s)"
                                           , product_filename, 
                                           UVES_ORDER_EXTRACT_QC(chip));

                                    uves_msg("Extraction quality table '%s' "
                                             "(%s) added to frameset", product_filename, 
                                             UVES_ORDER_EXTRACT_QC(chip));


                                } /* if not 2d extracted */
                            

                            {
                                const char *ff = "";
                                
                                /* Read uves_scired.reduce.ffmethd */
                cpl_free(context);
                context = uves_sprintf("%s.%s", recipe_id, UVES_REDUCE_ID);
                                check( uves_get_parameter(parameters, NULL, 
                                                          context,
                                                          "ffmethod",
                                                          CPL_TYPE_STRING, &ff),
                                       "Could not read parameter");
                                
                                /* If flat-fielding was done */
                                if (strcmp(ff, "no") != 0)
                                    {
                                        /* Save WCALIB_FF_SCIENCE / WCAL2D_SCIENCE =
                                           (rebinned_science, rebinned_header) */
                    cpl_table *qc_tabs[] = {NULL, NULL, NULL};

                    /* QC consists of usual science QC and
                       optionally TFLAT QC
                    */
                    
                    if ( strcmp(recipe_id, make_str(UVES_TFLAT_ID)) == 0 )
                        {
                        uves_qclog_delete(&qclog_tflat);
                        qclog_tflat = 
                            uves_qclog_init(raw_header[raw_index], chip);
                        
                        check( tflat_qclog(resampled_science,
                                   raw_header[raw_index],
                                   qclog_tflat),
                               "Could not compute QC parameters");

                        qc_tabs[0] = qclog_tflat;
                        qc_tabs[1] = qclog[0];
                        }
                    else
                        {
                        qc_tabs[0] = qclog[0];
                        qc_tabs[1] = NULL;
                        }
                    
                                        cpl_free(product_filename);
                                        check( product_filename = 
                                               (extract_is_2d) ? 
                                               uves_scired_rebinned_2d_filename(chip) :
                                               uves_scired_rebinned_filename(chip), 
                                               "Error getting filename");

                                        cpl_free(product_tag);
                                        product_tag = uves_sprintf(
                        "%s_%s_%s",
                                            (extract_is_2d) ? "WCAL_2D" : "WCALIB_FF",
                                            sci_type, uves_chip_tostring_upper(chip));
                                        
			    uves_propertylist_append(rebinned_header,wave_acc_header);

                                        check( uves_frameset_insert(frames,
                                                                    rebinned_science,
                                                                    CPL_FRAME_GROUP_PRODUCT,
                                                                    CPL_FRAME_TYPE_IMAGE,
                                                                    CPL_FRAME_LEVEL_FINAL,
                                                                    product_filename,
                                                                    product_tag,
                                                                    raw_header[raw_index],
                                                                    rebinned_header,
                                                                    NULL,
                                                                    parameters,
                                                                    recipe_id,
                                                                    PACKAGE "/" 
                                                                    PACKAGE_VERSION,
                                                                    qc_tabs,
                                                                    starttime, true,
                                                                    UVES_ALL_STATS),
                                               "Could not add wavelength calibrated flat-fielded "
                                               "science spectrum '%s' (%s) to frameset", 
                                               product_filename, product_tag);
                                    
                                        uves_msg("Wavelength calibrated flat-fielded science "
                                                 "spectrum '%s' (%s) added to frameset",
                                                 product_filename, product_tag);
                                        
                                        if (!extract_is_2d)
                                            {
                                                /* Save WCALIB_FLAT_OBJ_xxxx =
                                                   (resampled_mf, rebinned_header) */
                                                cpl_free(product_filename);
                                                check( product_filename = 
                                                       uves_scired_resampledmf_filename(chip),
                                                       "Error getting filename");
                                                
                                                cpl_free(product_tag);
                                                product_tag = 
                                                    uves_sprintf(
                                                        "WCALIB_FLAT_OBJ_%s",
                                                        uves_chip_tostring_upper(chip));
                                                /* Independent of sci_type */


                                                /* !!!Exception!!!
                                                 *
                                                 * For this reduced master flat frame we
                                                 * want to propagate the keywords *not*
                                                 * from the first raw input frame but
                                                 * from the master flat field itself.
                                                 *
                                                 * For that to work we temporarily set
                                                 * 
                                                 * all raw frames    := NONE
                                                 * master.flat frame := RAW
                                                 *
                                                 * This will make cpl_dfs_setup_product_header()
                                                 * find the proper "raw" frame (i.e. the mf)
                                                 * Also the required 'raw_header' must be
                                                 * that of the master flat frame, not science.
                                                 * After propagating keywords, we change back
                                                 * to normal:
                                                 *
                                                 * all raw frames    := RAW
                                                 * master.flat frame := CALIB
                                                 *
                                                 * (Since there could be more than 1 raw frame,
                                                 *  simply changing the first raw frame would
                                                 *  not work)
                                                 */                             
                                                
                                                cpl_free(raw_frames);
                                                check_nomsg( raw_frames =
                                                             set_all_raw_none(frames) );

                                                cpl_frame_set_group(mflat_frame, 
                                                                    CPL_FRAME_GROUP_RAW);
                                         
			    uves_propertylist_append(rebinned_header,wave_acc_header);

                                                check( uves_frameset_insert(
                                                           frames,
                                                           resampled_mf,
                                                           CPL_FRAME_GROUP_PRODUCT,
                                                           CPL_FRAME_TYPE_IMAGE,
                                                           CPL_FRAME_LEVEL_FINAL,
                                                           product_filename,
                                                           product_tag,
                                                           master_flat_header,  /* Note! */
                                                           rebinned_header,
                                                           NULL,
                                                           parameters,
                                                           recipe_id,
                                                           PACKAGE "/" 
                                                           PACKAGE_VERSION,
                                                           NULL, /* No QC: qclog */
                                                           starttime, false, 
                                                           CPL_STATS_MIN | CPL_STATS_MAX),
                                                       "Could not add wavelength calibrated "
                                                       "flat-field '%s' (%s) to frameset", 
                                                       product_filename, product_tag);
                                                
                                                uves_msg("Wavelength calibrated flat-field "
                                                         "spectrum '%s' (%s) added to frameset", 
                                                         product_filename, product_tag);

                                                /* Change frames groups back to normal */
                                                {
                                                    int i;
                                                    for (i = 0;
                                                         raw_frames[i] != NULL;
                                                         i++)
                                                        {
                                                            cpl_frame_set_group(
                                                                raw_frames[i], 
                                                                CPL_FRAME_GROUP_RAW);
                                                        }
                                                }
                                                cpl_frame_set_group(mflat_frame, 
                                                                    CPL_FRAME_GROUP_CALIB);
                                            }

                                        if (extract_is_2d)
                                            {
                                                /* Save FF2D_SCIENCE_xxxx = (fx2d, x2d_header) */
                                                cpl_free(product_filename);
                                                check( product_filename = 
                                                       uves_scired_ff2d_filename(chip), 
                                                       "Error getting filename");

                                                cpl_free(product_tag);
                                                product_tag = 
                                                    uves_sprintf(
                            "FF_2D_%s_%s", sci_type,
                                                        uves_chip_tostring_upper(chip));
                                         

                                                check( uves_frameset_insert(
                                                           frames,
                                                           fx2d,
                                                           CPL_FRAME_GROUP_PRODUCT,
                                                           CPL_FRAME_TYPE_IMAGE,
                                                           CPL_FRAME_LEVEL_FINAL,
                                                           product_filename,
                                                           product_tag,
                                                           raw_header[raw_index],
                                                           x2d_header,
                                                           NULL,
                                                           parameters,
                                                           recipe_id,
                                                           PACKAGE "/" 
                                                           PACKAGE_VERSION,
                                                           qclog,
                                                           starttime, false, 
                                                           UVES_ALL_STATS),
                                                       "Could not add 2d extracted, flat-fielded "
                                                       "spectrum '%s' (%s) to frameset", 
                                                       product_filename, product_tag);
                                                
                                                uves_msg("2d extracted, flat-fielded spectrum "
                                                         "'%s' (%s) added to frameset", 
                                                         product_filename, product_tag);

                                            }
                                        
                                    }/* If flat-fielding != no */

                                    check( uves_pfits_set_bunit(merged_header, "REL. FLUX."),
                                           "Error writing error spectrum header");
                                    
                                    /* Save ERRORBAR_SCIENCE_xxxx =
                                       (reduced_science_error, merged_header) */
                                    cpl_free(product_filename);

                                   check( product_filename = 
				   (extract_is_2d) ? 
                                   uves_scired_red_2d_error_filename(chip) : 
                                   ((m_method == MERGE_NOAPPEND) ? 
                                    uves_scired_red_noappend_error_filename(chip): uves_scired_red_error_filename(chip)),
                                   "Error getting filename");   


                                    cpl_free(product_tag);
                                    catg_is_noappend=uves_get_pro_catg_special(extract_is_2d,m_method);
                                    product_tag = uves_sprintf("%s%s_%s_%s",
                                                               (extract_is_2d) ? "ERR_2D" : "ERRORBAR",catg_is_noappend,
                                            sci_type, uves_chip_tostring_upper(chip));

/*
                                    product_tag = uves_sprintf(
                                       "%s%s_%s_%s",
                                       (m_method == MERGE_NOAPPEND) ? "ERRORBAR_NONMERGED" : "ERRORBAR",
                                       (extract_is_2d) ? "_2D" : "",
                                       sci_type, uves_chip_tostring_upper(chip));

*/
			    uves_propertylist_append(merged_header,wave_acc_header);

                                    check( uves_frameset_insert(
                                               frames,
                                               reduced_science_error,
                                               CPL_FRAME_GROUP_PRODUCT,
                                               CPL_FRAME_TYPE_IMAGE,
                                               CPL_FRAME_LEVEL_FINAL,
                                               product_filename,
                                               product_tag,
                                               raw_header[raw_index],
                                               merged_header,
                                               NULL,
                                               parameters,
                                               recipe_id,
                                               PACKAGE "/" PACKAGE_VERSION,
                                               qclog,
                                               starttime, false, 
                                               CPL_STATS_MIN | CPL_STATS_MAX),
                                           "Could not add error bars '%s' (%s) to frameset", 
                                           product_filename, product_tag);
                                    
                                    uves_msg("Science spectrum error '%s' (%s) "
                                             "added to frameset", 
                                             product_filename, product_tag);
                                    

                            if (!extract_is_2d)
                                {


                                    /* Save VARIANCE_SCIENCE_xxxx = 
                                       (flatfielded_variance, flatfielded_variance_header) */
                                    cpl_free(product_filename);
                                    check( product_filename = 
                                           uves_scired_ff_variance_filename(chip),
                                           "Error getting filename");
                                    
                                    cpl_free(product_tag);
                                    product_tag = 
                                        uves_sprintf("VARIANCE_%s_%s", sci_type,
                             uves_chip_tostring_upper(chip));
                                    

			

                                    check( uves_frameset_insert(frames,
                                                                flatfielded_variance,
                                                                CPL_FRAME_GROUP_PRODUCT,
                                                                CPL_FRAME_TYPE_IMAGE,
                                                                CPL_FRAME_LEVEL_FINAL,
                                                                product_filename,
                                                                product_tag,
                                                                raw_header[raw_index],
                                                                flatfielded_variance_header,
                                                                NULL,
                                                                parameters,
                                                                recipe_id,
                                                                PACKAGE "/" PACKAGE_VERSION,
                                                                qclog,
                                                                starttime, false, 
                                                                CPL_STATS_MIN | CPL_STATS_MAX),
                                           "Could not add flat-fielded spectrum variance "
                                           "'%s' (%s) to frameset", 
                                           product_filename, product_tag);
                                    
                                    uves_msg("Flat-fielded spectrum variance '%s' (%s) "
                                             "added to frameset", 
                                             product_filename, product_tag);
                                    
                                } /* if not 2d extraction */
                            }

                            if (!extract_is_2d)
                                {
                                    /* Save BKG_SCI_xxxx = (background, rotated_header) */
                                    cpl_free(product_filename);
                                    check( product_filename = 
                                           uves_scired_background_filename(chip),
                                           "Error getting filename");   

                                    cpl_free(product_tag);
                                    product_tag = 
                                        uves_sprintf("BKG_SCI_%s",
                             uves_chip_tostring_upper(chip));
                                    

                                    check( uves_frameset_insert(frames,
                                                                background,
                                                                CPL_FRAME_GROUP_PRODUCT,
                                                                CPL_FRAME_TYPE_IMAGE,
                                                                CPL_FRAME_LEVEL_FINAL,
                                                                product_filename,
                                                                product_tag,
                                                                raw_header[raw_index],
                                                                rotated_header[raw_index],
                                                                NULL,
                                                                parameters,
                                                                recipe_id,
                                                                PACKAGE "/" PACKAGE_VERSION,
                                                                NULL, /* QC */
                                                                starttime, false, 
                                                                CPL_STATS_MIN | CPL_STATS_MAX),
                                           "Could not add background image '%s' (%s) "
                                           "to frameset", product_filename, product_tag);
                                    
                                    uves_msg("Background image '%s' (%s) added to frameset", 
                                             product_filename, product_tag);
                                    
                                    /* If optimal extraction, also save
                                       cosmic_mask, order_trace */
                                    if (order_trace != NULL)
                                        {
                                            /* Save ORDER_TRACE_xxxx  */
                                            uves_free_propertylist(&order_trace_header);
                                            order_trace_header = uves_propertylist_new();


                                            /* !WARNING!: Duplicate code follows, be careful if/when
                                               changing. These parameters should be calculated
                                               the same way as in uves_qclog_add_sci().

                                               The MIDAS pipeline wrote these parameters only
                                               in this product, and for backwards compatibility
                                               do the same here.
                                            */

                                            uves_propertylist_update_int(
                                                order_trace_header, "ESO QC OPTEX NORD",
                                                uves_round_double(
                                                    cpl_table_get_column_max(ordertable, "Order")-
                                                    cpl_table_get_column_min(ordertable, "Order")+1));

                                            uves_propertylist_update_int(
                                                order_trace_header, "ESO QC OPTEX XSIZE",
                                                cpl_image_get_size_x(raw_image[raw_index]));

                                            uves_propertylist_update_int(
                                                order_trace_header, "ESO QC OPTEX YSIZE",
                                                uves_round_double(extraction_slit));


                                            cpl_free(product_filename);
                                            check( product_filename =
                                                   uves_scired_ordertrace_filename(chip),
                                                   "Error getting filename");

                                            cpl_free(product_tag);
                                            product_tag =
                                                uves_sprintf("ORDER_TRACE_%s",
                                                             uves_chip_tostring_upper(chip));
                                         

                                            check( uves_frameset_insert(frames,
                                                                        order_trace,
                                                                        CPL_FRAME_GROUP_PRODUCT,
                                                                        CPL_FRAME_TYPE_TABLE,
                                                                        CPL_FRAME_LEVEL_FINAL,
                                                                        product_filename,
                                                                        product_tag,
                                                                        raw_header[raw_index],
                                                                        order_trace_header,
                                                                        NULL,
                                                                        parameters,
                                                                        recipe_id,
                                                                        PACKAGE "/"
                                                                        PACKAGE_VERSION,
                                                                        qclog,
                                                                        starttime, false,
                                                                        0),
                                                   "Could not add sky spectrum '%s' (%s) "
                                                   "to frameset",
                                                   product_filename, product_tag);

                                            uves_msg("Order trace table '%s' (%s) "
                                                     "added to frameset",
                                                     product_filename, product_tag);
                                        }



                                    if (cosmic_mask != NULL)
                                        {
                                            /* Save CRMASK_xxxx  */
                                            uves_free_propertylist(&cosmic_mask_header);
                                            cosmic_mask_header = uves_propertylist_new();
                                            
                                            cpl_free(product_filename);
                                            check( product_filename = 
                                                   uves_scired_crmask_filename(chip), 
                                                   "Error getting filename");

                                            cpl_free(product_tag);
                                            product_tag = 
                                                uves_sprintf("CRMASK_%s",
                                 uves_chip_tostring_upper(chip));
                                         

                                            check( uves_frameset_insert(frames,
                                                                        cosmic_mask,
                                                                        CPL_FRAME_GROUP_PRODUCT,
                                                                        CPL_FRAME_TYPE_TABLE,
                                                                        CPL_FRAME_LEVEL_FINAL,
                                                                        product_filename,
                                                                        product_tag,
                                                                        raw_header[raw_index],
                                                                        cosmic_mask_header,
                                                                        NULL,
                                                                        parameters,
                                                                        recipe_id,
                                                                        PACKAGE "/" 
                                                                        PACKAGE_VERSION,
                                                                        NULL, /* qc */
                                                                        starttime, false,
                                                                        0),
                                                   "Could not add cosmic ray table "
                                                   "'%s' (%s) to frameset", 
                                                   product_filename, product_tag);
                                            
                                            uves_msg("Cosmic ray table '%s' (%s) "
                                                     "added to frameset",
                                                     product_filename, product_tag);
                                        }








                                    if (wave_map != NULL)
                                        {
                                            /* Save WAVE_MAP_xxxx  */
                                            uves_free_propertylist(&wave_map_header);
                                            wave_map_header = uves_propertylist_new();
                                            
                                            cpl_free(product_filename);
                                            check( product_filename = 
                                                   uves_scired_wmap_filename(chip), 
                                                   "Error getting filename");

                                            cpl_free(product_tag);
                                            product_tag = 
                                                uves_sprintf("WAVE_MAP_%s",
							     uves_chip_tostring_upper(chip));
                                            uves_pfits_set_ctype1(wave_map_header,"PIXEL");
                                            uves_pfits_set_ctype2(wave_map_header,"PIXEL");
                                            check( uves_frameset_insert(frames,
                                                                        wave_map,
                                                                        CPL_FRAME_GROUP_PRODUCT,
                                                                        CPL_FRAME_TYPE_IMAGE,
                                                                        CPL_FRAME_LEVEL_FINAL,
                                                                        product_filename,
                                                                        product_tag,
                                                                        raw_header[raw_index],
                                                                        wave_map_header,
                                                                        NULL,
                                                                        parameters,
                                                                        recipe_id,
                                                                        PACKAGE "/" 
                                                                        PACKAGE_VERSION,
                                                                        NULL, /* qc */
                                                                        starttime, false,
                                                                        0),
                                                   "Could not add wave map "
                                                   "'%s' (%s) to frameset", 
                                                   product_filename, product_tag);
                                            
                                            uves_msg("Wave map '%s' (%s) "
                                                     "added to frameset",
                                                     product_filename, product_tag);
                                        } else {
				      uves_msg("no wave map!!!!!!!!!");
				    }
                            uves_free_image(&wave_map);


                                    if (merged_sky != NULL)     
                                        /* In slicer mode / 2d mode, no sky 
                                           spectrum is extracted */
                                        {
                                            /* Save MERGED_SKY_xxxx = 
                                               (merged_sky, merged_header) */
                                            cpl_free(product_filename);
                                            check( product_filename = 
                                                   uves_scired_merged_sky_filename(chip),
                                                   "Error getting filename");
                                            
                                            cpl_free(product_tag);
                                            product_tag = 
                                                uves_sprintf("MERGED_SKY_%s",
                                 uves_chip_tostring_upper(chip));

				    uves_propertylist_append(merged_header,wave_acc_header);


                                            check( uves_frameset_insert(
                               frames,
                               merged_sky,
                               CPL_FRAME_GROUP_PRODUCT,
                               CPL_FRAME_TYPE_IMAGE,
                               CPL_FRAME_LEVEL_FINAL,
                               product_filename,
                               product_tag,
                               raw_header[raw_index],
                               merged_header,
                               NULL,
                               parameters,
                               recipe_id,
                               PACKAGE "/" 
                               PACKAGE_VERSION,
                               NULL, /* QC */
                               starttime, false, 
                               CPL_STATS_MIN | CPL_STATS_MAX),
                                                   "Could not add sky spectrum "
                                                   "'%s' (%s) to frameset", 
                                                   product_filename, product_tag);
                                            
                                            uves_msg("Sky spectrum '%s' (%s) added to frameset", 
                                                     product_filename, product_tag);
                                        }
                                    else
                                        {
                                            uves_msg_low("No sky spectrum to save");
                                        }
                                    
                                }/* if extract is 2d */

                            if (fluxcal_science != NULL)
                                {
                                    /* Save FLUXCAL_SCIENCE = 
                                       (fluxcal_science, fluxcal_header) */
                                    cpl_free(product_filename);

                                   check( product_filename = 
				   (extract_is_2d) ? 
                                   uves_scired_fluxcal_science_2d_filename(chip) : 
                                   ((m_method == MERGE_NOAPPEND) ? 
                                    uves_scired_fluxcal_science_noappend_filename(chip): uves_scired_fluxcal_science_filename(chip)),
                                   "Error getting filename");   


                                    cpl_free(product_tag);



                                    catg_is_noappend=uves_get_pro_catg_special(extract_is_2d,m_method);
                                    product_tag = uves_sprintf("FLUXCAL%s%s_%s_%s",
                                                               (extract_is_2d) ? "_2D" : "",catg_is_noappend,
                                            sci_type, uves_chip_tostring_upper(chip));


                                    /* Always _SCIENCE_, independent of sci_type */

				    uves_propertylist_append(fluxcal_header,wave_acc_header);
                                   
                                    check( uves_frameset_insert(frames,
                                                                fluxcal_science,
                                                                CPL_FRAME_GROUP_PRODUCT,
                                                                CPL_FRAME_TYPE_IMAGE,
                                                                CPL_FRAME_LEVEL_FINAL,
                                                                product_filename,
                                                                product_tag,
                                                                raw_header[raw_index],
                                                                fluxcal_header,
                                                                NULL,
                                                                parameters,
                                                                recipe_id,
                                                                PACKAGE "/"
                                                                PACKAGE_VERSION,
                                                                qclog,
                                                                starttime, false,
                                                                CPL_STATS_MIN | CPL_STATS_MAX),
                                           "Could not add flux-calibrated science "
                                           "spectrum '%s' (%s) to frameset", 
                                           product_filename, product_tag);

                                    uves_msg("Flux-calibrated science spectrum "
                                             "'%s' (%s) added to frameset", 
                                             product_filename, product_tag);

                                    /* Save FLUXCAL_ERRORBAR = (fluxcal_error, fluxcal_header) */
                                    check( uves_pfits_set_bunit(fluxcal_header, 
                                                                "10^-16 erg/cm^2/A/sec"),
                                           "Error writing error spectrum header");

                                    cpl_free(product_filename);

                                   check( product_filename = 
				   (extract_is_2d) ? 
                                   uves_scired_fluxcal_error_2d_filename(chip) : 
                                   ((m_method == MERGE_NOAPPEND) ? 
                                    uves_scired_fluxcal_error_noappend_filename(chip): uves_scired_fluxcal_error_filename(chip)),
                                   "Error getting filename");   


                                    cpl_free(product_tag);


                                    catg_is_noappend=uves_get_pro_catg_special(extract_is_2d,m_method);
                                    product_tag = uves_sprintf("FLUXCAL_ERRORBAR%s%s_%s_%s",
                                                               (extract_is_2d) ? "_2D" : "",catg_is_noappend,
                                            sci_type, uves_chip_tostring_upper(chip));

				    uves_propertylist_append(fluxcal_header,wave_acc_header);

                                    check( uves_frameset_insert(frames,
                                                                fluxcal_error,
                                                                CPL_FRAME_GROUP_PRODUCT,
                                                                CPL_FRAME_TYPE_IMAGE,
                                                                CPL_FRAME_LEVEL_FINAL,
                                                                product_filename,
                                                                product_tag,
                                                                raw_header[raw_index],
                                                                fluxcal_header,
                                                                NULL,
                                                                parameters,
                                                                recipe_id,
                                                                PACKAGE "/" 
                                                                PACKAGE_VERSION,
                                                                qclog,
                                                                starttime, false,
                                                                CPL_STATS_MIN | CPL_STATS_MAX),
                                           "Could not add flux-calibrated science "
                                           "spectrum error '%s' (%s) to frameset",
                                           product_filename, product_tag);
                                    
                                    uves_msg("Flux-calibrated science spectrum error "
                                             "'%s' (%s) added to frameset", 
                                             product_filename, product_tag);
                                    
                                } /* If flux calibration done */
                            
                        }/* if trace is enabled */
                    else
                        {
                            uves_msg("Skipping trace number %d", trace_number);
                        }
                

                }/* for each trace */


      if(strcmp(PROCESS_CHIP,"REDL") == 0) {
	chip = uves_chip_get_next(chip);
      }

        
        }/* For each chip */
       
  cleanup:   
    /* Input */
    uves_free_table(&info_tbl);
    uves_free_image(&raw_image[0]);
    uves_free_image(&raw_image[1]);
    uves_free_propertylist(&raw_header[0]);
    uves_free_propertylist(&raw_header[1]);
    uves_free_propertylist(&rotated_header[0]);
    uves_free_propertylist(&rotated_header[1]);
    uves_free_propertylist(&wave_map_header);
    uves_free_propertylist(&wave_acc_header);
    
    /* Input, calib */
    uves_free_image(&master_bias);
    uves_free_propertylist(&master_bias_header);
    
    uves_free_image(&master_dark);
    uves_free_propertylist(&master_dark_header);

    uves_free_image(&master_flat);
    uves_free_propertylist(&master_flat_header);
    
    uves_free_table(&ordertable);
    uves_free_propertylist(&ordertable_header);
    uves_polynomial_delete(&order_locations);
    uves_free_table(&traces);
    
    uves_free_table_const( &(linetable[0]) );
    uves_free_table_const( &(linetable[1]) );
    uves_free_table_const( &(linetable[2]) );
    uves_free_propertylist_const( &(linetable_header[0]) );
    uves_free_propertylist_const( &(linetable_header[1]) );
    uves_free_propertylist_const( &(linetable_header[2]) );
    uves_polynomial_delete_const( &(dispersion_relation[0]) );
    uves_polynomial_delete_const( &(dispersion_relation[1]) );
    uves_polynomial_delete_const( &(dispersion_relation[2]) );

    uves_free_image(&response_curve);
    uves_free_propertylist(&response_curve_header);
    uves_free_table(&master_response);
    
    uves_free_table(&atm_extinction);

    /* Output */
    uves_qclog_delete(&qclog[0]);
    uves_qclog_delete(&qclog_tflat);
    uves_free_image(&background);
    uves_free_image(&flatfielded_variance);
    uves_free_propertylist(&flatfielded_variance_header);
    uves_free_image(&rebinned_science);
    uves_free_propertylist(&rebinned_header);
    uves_free_image(&resampled_science);
    uves_free_image(&resampled_mf);
    uves_free_image(&merged_sky);

    uves_free_image(&merged_science);
    uves_free_propertylist(&merged_header);
    uves_free_image(&reduced_science);
    uves_free_image(&reduced_science_error);
    uves_free_image(&fluxcal_science);
    uves_free_image(&fluxcal_error);
    uves_free_propertylist(&fluxcal_header);
    uves_free_table(&cosmic_mask);
    uves_free_propertylist(&cosmic_mask_header);

    uves_free_table(&order_trace);
    uves_free_propertylist(&order_trace_header);

    uves_free_image(&x2d);
    uves_free_image(&fx2d);
    uves_free_propertylist(&x2d_header);
    
    cpl_free(raw_frames);
    cpl_free(product_filename);
    cpl_free(context);
    cpl_free(product_tag);
    
    return;
}

/*----------------------------------------------------------------------------*/
/**
  @brief    compute science QC
  @param    info_tbl     parameters from optimal extraction
  @param    raw_header   input frame FITS header
  @param    raw_image    input image
  @param    slit         extraction slit length
  @param    qclog        QC parameters are written here
 */
/*----------------------------------------------------------------------------*/
static void
scired_qclog(const cpl_table* info_tbl, 
         const uves_propertylist *raw_header,
         const cpl_image *raw_image,
         double slit,
         cpl_table* qclog)
{
  /* This test does not exist as an official QC-TEST in the MIDAS pipeline. But
     the QC parameters are written to the product header */

  check_nomsg(uves_qclog_add_string(qclog,                
                                    "QC TEST1 ID",
                                    "Science-Reduction-Test-Results",
                                    "Name of QC test",
                                    "%s"));
  
  check_nomsg( uves_qclog_add_sci(qclog,
                  raw_header, 
                  raw_image,
                  slit,
                  info_tbl) );

  cleanup:
  return;

}

/*----------------------------------------------------------------------------*/
/**
  @brief    compute tflat QC
  @param    ima          resampled science in wavelength, order space
  @param    raw_header   raw frame header
  @param    qclog        QC parameters are written here
 */
/*----------------------------------------------------------------------------*/
static void
tflat_qclog(const cpl_image* ima,
        const uves_propertylist *raw_header,
        cpl_table* qclog)
{
  char key_name[80];
  cpl_image *window = NULL;

  double exptime;
  int nx;
  int ny;
  int i;
  
  check_nomsg(uves_qclog_add_string(qclog,
                    "QC TEST1 ID",
                    "TFLAT-QC",
                    "Name of QC test",
                    "%s"));
  

  check_nomsg(uves_qclog_add_string(qclog,
                    uves_remove_string_prefix(UVES_INSMODE, "ESO "),
                    uves_pfits_get_insmode(raw_header),
                    "Instrument mode used.",
                    "%s"));
  
  check_nomsg(uves_qclog_add_string(qclog,
                    uves_remove_string_prefix(UVES_INSPATH, "ESO "),
                    uves_pfits_get_inspath(raw_header),
                    "Optical path used.",
                    "%s"));

    check_nomsg(uves_qclog_add_string(qclog,
                    uves_remove_string_prefix(UVES_SLIT1NAME, "ESO "),
                    uves_pfits_get_slit1_name(raw_header),
                    "Slit common name.",
                    "%s"));

  check( exptime = uves_pfits_get_exptime(raw_header),
     "Error reading exposure time");
  
  nx = cpl_image_get_size_x(ima);
  ny = cpl_image_get_size_y(ima);

  for (i = 1; i <= ny; i++) 
      /* Always count order numbers from 1, like MIDAS */
      {
      int size = 100;
      int xlo = uves_max_int(1 , (nx+1)/2 - size);
      int xhi = uves_min_int(nx, (nx+1)/2 + size);
          
      double min, max, avg, rms, med;

      uves_free_image(&window);
      window = cpl_image_extract(ima, xlo, i, xhi, i);
      assure_mem( window );

      if (cpl_image_count_rejected(window) >= cpl_image_get_size_x(window) - 2)
          {
          min = max = avg = rms = med = 0;
          }
      else
          {
          min = cpl_image_get_min   (window) / exptime;
          max = cpl_image_get_max   (window) / exptime;
          avg = cpl_image_get_mean  (window) / exptime;
          rms = cpl_image_get_stdev (window) / exptime;
          med = cpl_image_get_median(window) / exptime;
          }
                       
      sprintf(key_name, "QC ORD%d DATAMIN", i);
      check_nomsg(uves_qclog_add_double(qclog,
                        key_name,
                        min,
                        "extracted order datamin",
                        "%f"));
                  
      sprintf(key_name, "QC ORD%d DATAMAX", i);
      check_nomsg(uves_qclog_add_double(qclog,
                        key_name,
                        max,
                        "extracted order datamax",
                        "%f"));
                  
      sprintf(key_name, "QC ORD%d DATAAVG", i);
      check_nomsg(uves_qclog_add_double(qclog,
                        key_name,
                        avg,
                        "extracted order datamean",
                        "%f"));
                  
      sprintf(key_name, "QC ORD%d DATARMS", i);
      check_nomsg(uves_qclog_add_double(qclog,
                        key_name,
                        rms,
                        "extracted order datarms",
                        "%f"));

      sprintf(key_name, "QC ORD%d DATAMED", i);
      check_nomsg(uves_qclog_add_double(qclog,
                        key_name,
                        med,
                        "extracted order datamed",
                        "%f"));
      }
  
  cleanup:
  uves_free_image(&window);
  return;

}

/**@}*/
