コード例 #1
0
ファイル: acssum.c プロジェクト: jhunkeler/hstcal
int AcsSum (char *input, char *output, char *mtype, int printtime, int verbose)
{

    extern int status;

    IRAFPointer tpin;

    AcsSumInfo acs;
    IODescPtr im;        /* descriptor for input image */
    Hdr phdr;        /* primary header for input image */
    int nimgs;
    int i;
    char acs_input[CHAR_FNAME_LENGTH];

    int          FileExists (char *);
    void         TimeStamp (char *, char *);
    void         PrBegin (char *);
    void         PrEnd (char *);
    void         PrFileName (char *, char *);
    void         PrHdrInfo (char *, char *, char *, char *);
    int          MkName (char *, char *, char *, char *, char *, int);
    void         WhichError (int);
    void         InitSumTrl (char *input, char *output);
    void         FindAsnRoot (char *, char *);
    int          mkNewSpt (char *, char *, char *);

    /* Determine input and output trailer files, then initialize
        output file by combining inputs into output file */
    InitSumTrl (input, output);

    PrBegin ("ACSSUM");
    nimgs = 0;

    if (printtime)
        TimeStamp ("ACSSUM started", "");

    /* open the input file template */
    tpin = c_imtopen (input);

    nimgs = c_imtlen(tpin);

    /* Initialize structure containing acssum information. */
    AcsInit (&acs,nimgs);

    /* Copy command-line arguments into acs. */
    for (i = 0; i < nimgs; i++) {
        c_imtgetim (tpin, acs.input[i], CHAR_FNAME_LENGTH);
        PrFileName ("input", acs.input[i]);
    }

    /* close file template */
    c_imtclose (tpin);

    strcpy (acs.output, output);
    acs.printtime = printtime;
    acs.verbose = verbose;

    PrFileName ("output", acs.output);
    FindAsnRoot (output, acs.rootname);

    initHdr (&phdr);

    /* Check whether the output file already exists. */
    if (FileExists (acs.output)) {
        FreeAcsInput (acs.input, nimgs);
        return (status);
    }
    strcpy(acs_input,acs.input[0]);

    /* Open input image in order to read its primary header. */
    im = openInputImage (acs_input, "", 0);

    if (hstio_err()) {
        FreeAcsInput (acs.input, nimgs);
        return (status = OPEN_FAILED);
    }

    getHeader (im, &phdr);        /* get primary header */
    if (hstio_err()) {
        FreeAcsInput (acs.input, nimgs);
        return (status = OPEN_FAILED);
    }
    closeImage (im);

    /* Get keyword values from primary header. */
    if (GetSumKeyInfo (&acs, &phdr)) {
        FreeAcsInput (acs.input, nimgs);
        return (status);
    }
    freeHdr (&phdr);

    /* Print information about this image. */
    PrHdrInfo (acs.aperture, acs.filter1, acs.filter2, acs.det);

    if (acs.printtime)
        TimeStamp ("Begin processing", acs.rootname);

    /* Sum all imsets. */
    if (SumGrps (&acs,mtype)){
        FreeAcsInput (acs.input, nimgs);
        return (status);
    }

    /* create new SPT file for output product */
    if (mkNewSpt (input, mtype, output)) {
        return(status);
    }

    /* Done... */
    trlmessage ("\n");
    PrEnd ("ACSSUM");

    if (acs.printtime)
        TimeStamp ("ACSSUM completed", acs.rootname);

    /* Write out temp trailer file to final file */
    WriteTrlFile ();

    FreeAcsInput (acs.input, nimgs);
    return (status);
}
コード例 #2
0
ファイル: calacs.c プロジェクト: jhunkeler/hstcal
int CalAcsRun (char *input, int printtime, int save_tmp, int verbose, int debug, const unsigned nThreads, const unsigned cteAlgorithmGen, const char * pcteTabNameFromCmd) {

    /* arguments:
       char *input     i: name of the FITS file/table to be processed
       int printtime   i: true --> print time stamps at intermediate steps
       int *save_tmp   i: true --> save temporary files
       int verbose     i: true --> print info during processing
       int debug       i: true --> print debugging info during processing
       int onecpu      i: true --> turn off use of OpenMP during processing
    */

    extern int status;

    ACSInfo acshdr;        /* calibration switches, etc */
    AsnInfo    asn;        /* association table data    */

    char *acsdth_input;    /* Input list for ACSDTH */

    int prod;

    void PrBegin (char *);
    void PrEnd (char *);
    void PrFileName (char *, char *);
    void TimeStamp (char *, char *);
    /* Association table routines */
    void initAsnInfo (AsnInfo *);
    void freeAsnInfo (AsnInfo *);
    int LoadAsn (AsnInfo *);
    int ProcessACSCCD (AsnInfo *, ACSInfo *, int *, int, const unsigned nThreads, const unsigned cteAlgorithmGen, const char * pcteTabNameFromCmd);
    int ProcessMAMA (AsnInfo *, ACSInfo *, int);
    int AcsDth (char *, char *, int, int, int);
    char *BuildDthInput (AsnInfo *, int);
    void InitDthTrl (char *, char *);

    /* Post error handler */
    push_hstioerr (errchk);

    PrBegin ("CALACS");    /* *** CALACS -- Version ... *** */

    if (printtime)
    TimeStamp ("CALACS started", "");

    /* Determine if input is a single file or an association
       table, then populate ASN structure with appropriate
       information to control processing.
    */
    initAsnInfo(&asn);

    if (debug) {
        trlmessage ("Initialized Association data ... ");
    }

    /* Copy Input filename to ASN structure    */
    strcpy (asn.input, input);

    /* Print image name. */
    trlmessage ("\n");

    PrFileName ("input", asn.input);

    /* Set verbose flag... */
    asn.verbose = verbose;
    asn.debug = debug;

    /* LoadAsn will determine whether input is a single file or an
       Association table.  If a single image, it will look in that images
       association table to see what products are associated with it, and
       process them accordingly.  If it is just a single image as its
       own output, it will proceed as a 1 element table.
       Based on routines from n_getAsnTable() and n_setup() in CALNICB
    */
    if(LoadAsn(&asn)) {
        freeAsnInfo (&asn);
        return (status);
    }

    /* Check to see that detector is known, as it could come through with a
       value of 0 or UNKNOWN_DETECTOR.
       WJH  2Mar99
    */
    if (asn.detector == UNKNOWN_DETECTOR || asn.detector == 0) {
        trlwarn ("Unknown detector type for observations.");
        freeAsnInfo(&asn);
        return (status = NOTHING_TO_DO);
    }

    if (asn.verbose) {
        sprintf (MsgText,"CALACS: Detector %s, type %d ",asn.instr, asn.detector);
        trlmessage (MsgText);
    }

    /* Determine what detector we are working with... */
    if (asn.detector != MAMA_DETECTOR ) {  /* Process CCD data ... */
        if (asn.verbose) {
            trlmessage ("CALACS: processing a CCD product");
        }
        if (ProcessACSCCD(&asn, &acshdr, &save_tmp, printtime, nThreads, cteAlgorithmGen, pcteTabNameFromCmd)) {
            if (status == NOTHING_TO_DO) {
                trlwarn ("No processing desired for CCD data.");
            } else {
                trlerror ("Couldn't process CCD data");
            }

            freeAsnInfo(&asn);
            return (status);
        }
        trlmessage("Finished CCD processing...");
    } else {  /* Process MAMA observations here */

        trlmessage("Starting to process MAMA data now...");

        if (ProcessMAMA(&asn, &acshdr, printtime)) {
            if (status == NOTHING_TO_DO){
                trlwarn ("No processing desired for MAMA data.");
            } else{
                trlerror ("Couldn't process MAMA data");
            }
            freeAsnInfo(&asn);
            return (status);
        }
        trlmessage("Finished MAMA processing...");
    }


    /* Add DTH processing here... */
    /* For each DTH product... */
    if (asn.process == FULL){
        if (asn.verbose) {
            trlmessage ("CALACS: Building DTH products");
        }
        acsdth_input = NULL;
        for (prod = 0; prod < asn.numprod; prod++) {
            /* Create empty DTH product, header only */
            /* This uses only one sub-product for the header template,
               but later versions should use a function similar to
               BuildSumInput to create list of subproducts as inputs...
            */
            acsdth_input = BuildDthInput (&asn, prod);

            /* We always want to create a final concatenated trailer file for
               the entire association whether there is a product or not. So, we
               set up the trailer file based on the association file name itself.
            */

            /* If desired, we could optionally use the full _drz.tra filename
               as the trailer filename, based on the output dither product name.
            if (strcmp(asn.product[prod].prodname,"") != 0) {
                InitDthTrl (acsdth_input, asn.product[prod].prodname);
            } else { */

            InitDthTrl(acsdth_input, asn.rootname);

            /* End brace for optional dither product name assignment...
            } */

            /* Check if we have a PROD-DTH specified...*/
            if (strcmp(asn.product[prod].prodname, "") != 0) {

                if ((asn.dthcorr == PERFORM || asn.dthcorr == DUMMY)) {
                    if (AcsDth (acsdth_input, asn.product[prod].prodname, asn.dthcorr, printtime, asn.verbose) )
                        return (status);

                    /* Pass posid of 0 to indicate a PRODUCT is to be updated */
                    updateAsnTable(&asn, prod, NOPOSID);
                }
            } else {
                trlwarn ("No DTH product name specified. No product created.");
                /*status = ACS_OK; */
            }
        }
        free (acsdth_input);
    }
    if (asn.verbose) {
        trlmessage ("CALACS: Finished processing product ");
    }

    freeAsnInfo(&asn);

    trlmessage ("\n");
    PrEnd ("CALACS");        /* *** CALACS complete *** */

    if (printtime)
    TimeStamp ("CALACS completed", acshdr.rootname);

    /* Return the final value of status, which should be ACS_OK if
       all went well or some error condition otherwise. */
    return (status);
}
コード例 #3
0
ファイル: calstis11.c プロジェクト: brechmos-stsci/hstcal
int CalStis11 (char *inwav, char *insci, char *output,
		int printtime, int verbose) {

	int status;

	StisInfo11 wavecal, scidata;	/* calibration switches, etc. */

	IODescPtr imWav;	/* descriptor for input wavecal */
	IODescPtr imSci;	/* descriptor for input science file */
	Hdr phdrWav;		/* primary header for input wavecal */
	Hdr phdrSci;		/* primary header for input science file */
	int subscicorr;		/* PERFORM if CCD and sclamp is HITM1 or 2 */

	int GetKeyInfo11 (StisInfo11 *, Hdr *);
	int SubSci (StisInfo11 *, StisInfo11 *);

	PrBegin (11);

	if (printtime)
	    TimeStamp ("CALSTIS-11 started", "");

	/* Initialize structure containing calstis information. */
	StisInit11 (&wavecal, &scidata);

	/* Copy command-line arguments into wavecal & scidata. */
	strcpy (wavecal.input, inwav);
	strcpy (scidata.input, insci);
	strcpy (wavecal.output, output);
	wavecal.printtime = printtime;
	scidata.printtime = printtime;
	wavecal.verbose = verbose;
	scidata.verbose = verbose;

	PrFileName ("wavecal", wavecal.input);
	PrFileName ("science", scidata.input);
	PrFileName ("output", wavecal.output);

	initHdr (&phdrWav);
	initHdr (&phdrSci);

	/* Check whether the output file already exists. */
	if ((status = FileExists (wavecal.output)))
	    return (status);

	/* Read primary header of input wavecal. */
	imWav = openInputImage (wavecal.input, "", 0);
	if (hstio_err())
	    return (OPEN_FAILED);
	getHeader (imWav, &phdrWav);
	if (hstio_err())
	    return (OPEN_FAILED);
	closeImage (imWav);

	/* Get keyword values from wavecal primary header. */
	if ((status = GetKeyInfo11 (&wavecal, &phdrWav)))
	    return (status);

	freeHdr (&phdrWav);

	/* Print information about the input wavecal. */
	PrHdrInfo (wavecal.obsmode, wavecal.aperture,
		wavecal.opt_elem, wavecal.det);

	/* Do we need to subtract the science image from the wavecal? */
	subscicorr = PERFORM;			/* initial value */
	if (wavecal.detector != CCD_DETECTOR) {
	    subscicorr = OMIT;
	    printf ("Warning  Detector is %s\n", wavecal.det);
	}
	if (strcmp (wavecal.sclamp, "HITM1") != 0 &&
		   strcmp (wavecal.sclamp, "HITM2") != 0) {
	    subscicorr = OMIT;
	    printf ("Warning  Wavecal SCLAMP is `%s', not HITM1 or HITM2\n",
			wavecal.sclamp);
	}
	if (wavecal.texpstrt >= EXT_SHUTTER_CLOSED) {
	    subscicorr = OMIT;
	    printf (
	"Warning  TEXPSTRT=%.2f implies external shutter is closed.\n",
		wavecal.texpstrt);
	}

	if (subscicorr != PERFORM) {
	    printf (
	"Warning  Science data will not be subtracted from wavecal.\n");
	    return (NOTHING_TO_DO);
	}

	/* Read primary header of input science file. */
	imSci = openInputImage (scidata.input, "", 0);
	if (hstio_err())
	    return (OPEN_FAILED);
	getHeader (imSci, &phdrSci);
	if (hstio_err())
	    return (OPEN_FAILED);
	closeImage (imSci);

	if (wavecal.printtime)
	    TimeStamp ("Begin processing", wavecal.rootname);

	/* Get keyword values from science file primary header. */
	if ((status = GetKeyInfo11 (&scidata, &phdrSci)))
	    return (status);

	freeHdr (&phdrSci);

	/* Detector, central wavelength, grating, and aperture must be
	   the same in the wavecal and science file.
	*/
	if (wavecal.detector != scidata.detector ||
	    wavecal.cenwave != scidata.cenwave ||
	    strcmp (wavecal.opt_elem, scidata.opt_elem) != 0 ||
	    strcmp (wavecal.aperture, scidata.aperture) != 0) {

	    printf ("Warning  Wavecal and science file do not match; \\\n");
	    printf ("Warning  the science file will not be subtracted.\n");
	    return (NOTHING_TO_DO);
	}

	/* Subtract the science image from the wavecal. */
	if ((status = SubSci (&wavecal, &scidata)))
	    return (status);

	printf ("\n");
	PrEnd (11);

	if (wavecal.printtime)
	    TimeStamp ("CALSTIS-11 completed", wavecal.rootname);

	return (0);
}
コード例 #4
0
ファイル: wf3cte.c プロジェクト: sosey/hstcal
int WF3cte (char *input, char *output, CCD_Switch *cte_sw,
        RefFileInfo *refnames, int printtime, int verbose, int onecpu) {

/*
input: filename
output: filename
cte_sw: the calibration flags
refnames: the names of the calibration reference files
onecpu: use parallel processing?

The following are new primary header keywords which will be added to the data
so that they can be updated by the code. They are also specified in the PCTETAB
reference file.

These are taken from the PCTETAB
CTE_NAME - name of cte algorithm
CTE_VER - version number of cte algorithm
CTEDATE0 - date of wfc3/uvis installation in HST, in MJD
CTEDATE1 - reference date of CTE model pinning, in MJD

PCTETLEN - max length of CTE trail
PCTERNOI - readnoise amplitude for clipping
PCTESMIT - number of iterations used in CTE forward modeling
PCTESHFT - number of iterations used in the parallel transfer
PCTENSMD - readnoise mitigation algorithm
PCTETRSH - over-subtraction threshold
PCTEFRAC - cte scaling frac calculated from expstart
PCTERNOI - the readnoise clipping level to use      

#These are taken from getreffiles.c
DRKCFILE is a new dark reference file used only in the CTE branch *_DRC.fits
BIACFILE is a new super-bias reference file used only in the CTE branch *_BIC.fits
PCTETAB is a new reference file FITS table which will contain the software parameter switches for the CTE correction *_CTE.fit

This is the main workhorse function for removing the CTE from WFC3 UVIS images  

Unfortunately this happens before anything else in wfc3, so there's a lot of reading files
at the beginning in order to populate needed information. The rest of the pipeline works
on one chip at a time and the structures are all defined to support that. None of these 
structures are defined until the code enters the single chip loops. This differs from the
CTE correction in ACS which occurs later in the process after basic structures are defined.

*/

    extern int status;

    WF3Info wf3; /*structure with calibration switches and reference files for passing*/
    Hdr phdr; /*primary header for input image, all output information saved here*/
        
    CTEParams cte_pars; /*STRUCTURE HOLDING THE MODEL PARAMETERS*/
    SingleGroup cd; /*SCI 1*/
    SingleGroup ab; /*SCI 2*/
    SingleGroup raz; /* THE LARGE FORMAT COMBINATION OF CDAB*/
    SingleGroup rsz; /* LARGE FORMAT READNOISE CORRECTED IMAGE */
    SingleGroup rsc; /* CTE CORRECTED*/
    SingleGroup rzc; /* FINAL CTE CORRECTED IMAGE */
    SingleGroup chg; /* THE CHANGE DUE TO CTE  */
    SingleGroup raw; /* THE RAW IMAGE IN RAZ FORMAT */
    
    int i,j; /*loop vars*/
    int max_threads=1;
    clock_t begin;
    double  time_spent;
    float hardset=0.0;

    begin = (double)clock();
        
    Bool subarray; /* to verify that no subarray is being used, it's not implemented yet*/

    /*CONTAIN PARALLEL PROCESSING TO A SINGLE THREAD AS USER OPTION*/
#   ifdef _OPENMP
    trlmessage("Using parallel processing provided by OpenMP inside CTE routine");
    if (onecpu){
        omp_set_dynamic(0);
        max_threads=1;
        sprintf(MsgText,"onecpu == TRUE, Using only %i threads/cpu", max_threads);
    } else {
        omp_set_dynamic(0);
        max_threads = omp_get_num_procs(); /*be nice, use 1 less than avail?*/
        sprintf(MsgText,"Setting max threads to %i of %i cpus",max_threads, omp_get_num_procs()); 
    }
    omp_set_num_threads(max_threads);
    trlmessage(MsgText);
#   endif
   

    /* COPY COMMAND-LINE ARGUMENTS INTO WF3. */ 
    WF3Init (&wf3);
    strcpy (wf3.input, input);
    strcpy (wf3.output, output);

    PrBegin ("WFC3CTE");
    if (wf3.printtime)
        TimeStamp("WFC3CTE Started: ",wf3.rootname);

    /* CHECK WHETHER THE OUTPUT FILE ALREADY EXISTS. */
    if (FileExists (wf3.output)){
        WhichError(status);
        return (ERROR_RETURN);
    }

    wf3.pctecorr = cte_sw->pctecorr;
    wf3.darkcorr = cte_sw->darkcorr;
    wf3.biascorr = cte_sw->biascorr;
    wf3.blevcorr = cte_sw->blevcorr;
    wf3.printtime = printtime;
    wf3.verbose = verbose;
    wf3.refnames = refnames;

    PrFileName ("input", wf3.input);
    PrFileName ("output", wf3.output);

    if (wf3.biascorr == COMPLETE){
        trlmessage("BIASCORR complete for input image, CTE can't be performed");
        return(ERROR_RETURN);
    }
    if (wf3.darkcorr == COMPLETE){
        trlmessage("DARKCORR complete for input image, CTE can't be performed");
        return(ERROR_RETURN);
    }
    if (wf3.blevcorr == COMPLETE){
        trlmessage("BLEVCORR complete for input image, CTE can't be performed");
        return(ERROR_RETURN);
    }


    /* DETERMINE THE NAMES OF THE TRAILER FILES BASED ON THE INPUT
       AND OUTPUT FILE NAMES, THEN INITIALIZE THE TRAILER FILE BUFFER
       WITH THOSE NAMES.
       */
    if (initCTETrl (input, output))
        return (status);
    

    /* OPEN INPUT IMAGE IN ORDER TO READ ITS PRIMARY HEADER. */
    if (LoadHdr (wf3.input, &phdr) ){
        WhichError(status);
        return (ERROR_RETURN);
    }

    /* GET KEYWORD VALUES FROM PRIMARY HEADER. */
    if (GetKeys (&wf3, &phdr)) {
        freeHdr (&phdr);
        return (status);
    }

    if (GetCTEFlags (&wf3, &phdr)) {
        freeHdr(&phdr);
        return (status);
    }

    /* OPEN THE INPUT IMAGES AND GET THE  SCIENCE EXTENSIONS  */
    initSingleGroup (&cd);
    getSingleGroup (wf3.input, 1, &cd);
    
    if (hstio_err())
        return (status = OPEN_FAILED);

    /*** MAKE SURE THIS IS NOT A SUBARRAY ***/
    if (GetKeyBool (cd.globalhdr, "SUBARRAY", NO_DEFAULT, 0, &subarray))
        return (status=KEYWORD_MISSING);

    if (subarray) {
        sprintf(MsgText,"**SUBARRAY FOUND!; SUBARRAY images are not yet supported for CTE**");
        trlmessage(MsgText);
        status=ERROR_RETURN;
        return(status);
    }   
    
    initSingleGroup (&ab);
    getSingleGroup (wf3.input, 2, &ab);
    if (hstio_err())
        return (status = OPEN_FAILED);

    if (GetKeyBool (ab.globalhdr, "SUBARRAY", NO_DEFAULT, 0, &subarray))
        return (status=KEYWORD_MISSING);

    if (subarray) {
        sprintf(MsgText,"SUBARRAY FOUND; **SUBARRAY images are not yet supported for CTE**");
        trlmessage(MsgText);
        status=ERROR_RETURN;
        return(status);
    }

    /*READ IN THE CTE PARAMETER TABLE*/
    initCTEParams(&cte_pars);
    if (GetCTEPars (wf3.pctetab.name,&cte_pars))
        return (status);

    if (verbose){
        PrRefInfo ("pctetab", wf3.pctetab.name, wf3.pctetab.pedigree,
                wf3.pctetab.descrip, wf3.pctetab.descrip2);
    }
    
    
    /*SAVE THE PCTETABLE INFORMATION TO THE HEADER OF THE SCIENCE IMAGE
      AFTER CHECKING TO SEE IF THE USER HAS SPECIFIED ANY CHANGES TO THE
      CTE CODE VARIABLES.
    */
    if (CompareCTEParams(&cd, &cte_pars)){
        return (status);
    }    


    /*SET UP THE ARRAYS WHICH WILL BE PASSED AROUND*/  
    initSingleGroup(&raz);
    allocSingleGroup(&raz, RAZ_COLS, RAZ_ROWS);

    initSingleGroup(&rsz);
    allocSingleGroup(&rsz, RAZ_COLS, RAZ_ROWS);

    initSingleGroup(&rsc);
    allocSingleGroup(&rsc, RAZ_COLS, RAZ_ROWS);

    initSingleGroup(&rzc);
    allocSingleGroup(&rzc, RAZ_COLS, RAZ_ROWS);
    
    initSingleGroup(&raw);
    allocSingleGroup(&raw, RAZ_COLS, RAZ_ROWS);
    
    initSingleGroup(&chg);    
    allocSingleGroup(&chg, RAZ_COLS, RAZ_ROWS);

    for (i=0;i<RAZ_COLS;i++){ 
        for(j=0;j<RAZ_ROWS;j++){ 
            Pix(raw.sci.data,i,j)=hardset;
            Pix(rsz.sci.data,i,j)=hardset;
            Pix(raz.sci.data,i,j)=hardset;
            Pix(rsc.sci.data,i,j)=hardset;
            Pix(rzc.sci.data,i,j)=hardset;
            Pix(chg.sci.data,i,j)=hardset;
        }
    }            


    /* SAVE A COPY OF THE RAW IMAGE FOR LATER */
    makesciRAZ(&cd,&ab,&raw);

    /***SUBTRACT THE CTE BIAS FROM BOTH CHIPS IN PLACE***/
    if (doCteBias(&wf3,&cd)){
        freeSingleGroup(&cd);
        return(status);
    }

    
    if (doCteBias(&wf3,&ab)){
        freeSingleGroup(&ab);
        return(status);
    }
      

    /*CONVERT TO RAZ FORMAT AND CORRECT FOR GAIN*/
    if (raw2raz(&wf3, &cd, &ab, &raz))
        return (status);

    /***CALCULATE THE SMOOTH READNOISE IMAGE***/
    trlmessage("CTE: Calculating smooth readnoise image");
    
 
    /***CREATE THE NOISE MITIGATION MODEL ***/
    if (cte_pars.noise_mit == 0) {
        if (raz2rsz(&wf3, &raz, &rsz, cte_pars.rn_amp, max_threads))
            return (status);
    } else {
        trlmessage("Only noise model 0 implemented!");
        return (status=ERROR_RETURN);
    }

    /***CONVERT THE READNOISE SMNOOTHED IMAGE TO RSC IMAGE
        THIS IS WHERE THE CTE GETS CALCULATED         ***/
    if (rsz2rsc(&wf3, &rsz, &rsc, &cte_pars))
        return (status);
       
    /*** SAVE USEFULL HEADER INFORMATION ***/
    if (cteHistory (&wf3, cd.globalhdr))
        return (status);
    
    /*** CREATE THE FINAL CTE CORRECTED IMAGE, PUT IT BACK INTO ORIGNAL RAW FORMAT***/
    for (i=0;i<RAZ_COLS;i++){
        for(j=0; j<RAZ_ROWS; j++){
            Pix(chg.sci.data,i,j) = (Pix(rsc.sci.data,i,j) - Pix(rsz.sci.data,i,j))/wf3.ccdgain;
            Pix(rzc.sci.data,i,j) =  Pix(raw.sci.data,i,j) + Pix(chg.sci.data,i,j);
        }
    }
            
    /*BACK TO NORMAL FORMATTING*/
    undosciRAZ(&cd,&ab,&rzc);
    
    /*UPDATE THE OUTPUT HEADER ONE FINAL TIME*/
    PutKeyDbl(cd.globalhdr, "PCTEFRAC", cte_pars.scale_frac,"CTE scaling fraction based on expstart");
    trlmessage("PCTEFRAC saved to header");
    
    /*SAVE THE NEW RAW FILE WITH UPDATED SCIENCE ARRAYS AND PRIMARY HEADER TO RAC*/
    putSingleGroup(output,cd.group_num, &cd,0);
    putSingleGroup(output,ab.group_num, &ab,0);


    /** CLEAN UP **/    
    freeSingleGroup(&rzc);
    freeSingleGroup(&rsc);
    freeSingleGroup(&chg);
    freeSingleGroup(&raz);
    freeSingleGroup(&rsz);
    freeSingleGroup(&raw);
    
    time_spent = ((double) clock()- begin +0.0) / CLOCKS_PER_SEC;
    if (verbose){
        sprintf(MsgText,"CTE run time: %.2f(s) with %i procs/threads\n",time_spent,max_threads);
        trlmessage(MsgText);
    }

    PrSwitch("pctecorr", COMPLETE);
    if(wf3.printtime)
        TimeStamp("PCTECORR Finished",wf3.rootname);

    return (status);
}