def display(self, rc):
        # Instantiate the log
        log = gemLog.getGeminiLog(logType=rc["logType"],
                                  logLevel=rc["logLevel"])
        
        # Log the standard "starting primitive" debug message
        log.debug(gt.log_message("primitive", "display", "starting"))
        
        # Get parameters from RC
        threshold = rc["threshold"]
        remove_bias = rc["remove_bias"]

        # Get inputs
        adinput = rc.get_inputs_as_astrodata()
        orig_input = adinput
        deepcopied = False

        # Threshold and bias parameters only make sense for SCI extension;
        # turn it off for others
        extname = rc["extname"]
        if extname!="SCI":
            threshold = None
            remove_bias = False
        elif threshold=="None":
            threshold = None
        elif threshold=="auto":
            dqext = np.array([ad["DQ"] for ad in adinput])
            mosaic = np.array([((ad.phu_get_key_value(
                                        self.timestamp_keys["mosaicDetectors"])
                                 is not None)
                                or
                                (ad.phu_get_key_value(
                                        self.timestamp_keys["tileArrays"])
                                 is not None))
                               for ad in adinput])
            if not np.all(dqext):
                if not np.any(mosaic):
                    # This is the first possible modification to the data;
                    # always deepcopy before proceeding
                    adinput = [deepcopy(ad) for ad in orig_input]
                    deepcopied = True

                    adinput = sdz.add_dq(adinput, bpm=None,
                                         copy_input=False, index=rc["index"])
                    if not isinstance(adinput,list):
                        adinput = [adinput]
                else:
                    log.warning("Cannot add DQ plane to mosaicked data; " \
                                "no threshold mask will be displayed")
                    threshold=None

        # Check whether approximate bias level should be removed
        if remove_bias:
            # Copy the original input if necessary, before
            # modifying it
            if not deepcopied:
                adinput = [deepcopy(ad) for ad in orig_input]
                deepcopied = True

            new_adinput = []
            for ad in adinput:
                # Check whether data has been bias- or dark-subtracted
                biasim = ad.phu_get_key_value("BIASIM")
                darkim = ad.phu_get_key_value("DARKIM")

                # Check whether data has been overscan-subtracted
                overscan = np.array([ext.get_key_value("OVERSCAN") 
                                     for ext in ad["SCI"]])
                if np.any(overscan) or biasim or darkim:
                    log.fullinfo("Bias level has already been removed "
                                 "from data; no approximate correction "
                                 "will be performed")
                else:
                    # Get the bias level
                    bias_level = gdc.get_bias_level(adinput=ad)
                    if bias_level is not None:
                        # Subtract the bias level from each science extension
                        log.stdinfo("Subtracting approximate bias level "
                                     "from %s for display" % ad.filename)
                        log.fullinfo("Bias levels used: %s" % str(bias_level))
                        ad = ad.sub(bias_level)
                    else:
                        log.warning("Bias level not found for %s; "
                                    "approximate bias will not be removed" % 
                                    ad.filename)

                new_adinput.append(ad)
            adinput = new_adinput

        # Check whether data needs to be tiled before displaying
        # Otherwise, flatten all desired extensions into a single list
        tile = rc["tile"]
        if tile:
            next = np.array([ad.count_exts(extname) for ad in adinput])
            if np.any(next>1):
                log.fullinfo("Tiling extensions together before displaying")
                if not deepcopied:
                    adinput = [deepcopy(ad) for ad in orig_input]
                    deepcopied = True

                adinput = gm.tile_arrays(adinput, tile_all=True,
                                         copy_input=False, index=rc["index"])
                if not isinstance(adinput,list):
                    adinput = [adinput]
        else:
            extinput = []
            for ad in adinput:
                exts = ad[extname]
                if exts is None:
                    continue
                for ext in exts:
                    if extname=="SCI" and threshold=="auto":
                        dqext = ad["DQ",ext.extver()]
                        if dqext is not None:
                            ext.append(dqext)
                    extinput.append(ext)
            adinput = extinput

        # Get overlays from RC if available (eg. IQ overlays made by measureIQ)
        # then clear them out so they don't persist to the next display call
        overlay_dict = gt.make_dict(key_list=adinput, value_list=rc["overlay"])
        rc["overlay"] = None

        # Set the starting frame
        frame = rc["frame"]
        if frame is None:
            frame = 1

        # Initialize the local version of numdisplay
        # (overrides the display function to allow for quick overlays)
        lnd = _localNumDisplay()

        # Loop over each input AstroData object in the input list
        if len(adinput)<1:
            log.warning("No extensions to display with extname %s" % extname)
        for ad in adinput:
              
            if frame>16:
                log.warning("Too many images; only the first 16 are displayed.")
                break

            # Check for more than one extension
            ndispext = ad.count_exts(extname)
            if ndispext==0:
                log.warning("No extensions to display in "\
                                "%s with extname %s" %
                            (ad.filename,extname))
                continue
            elif ndispext>1:
                raise Errors.InputError("Found %i extensions for "\
                                        "%s[%s]; exactly 1 is required" %
                                        (ndispext,ad.filename,extname))

            dispext = ad[extname]
            
            # Squeeze the data to get rid of any empty dimensions
            # (eg. in raw F2 data)
            data = np.squeeze(dispext.data)

            # Check for 1-D data (ie. extracted spectra)
            if len(data.shape)==1:
                
                # Use splot to display instead of numdisplay
                log.fullinfo("Calling IRAF task splot to display data")
                splot_task = eti.sploteti.SplotETI(rc,ad)
                splot_task.run()
                continue

            # Make threshold mask if desired
            masks = []
            mask_colors = []
            if threshold is not None:
            
                if threshold!="auto":
                    # Make threshold mask from user supplied value;
                    # Assume units match units of data
                    threshold = float(threshold)
                    satmask = np.where(data>threshold)
                else:
                    # Make the mask from the nonlinear and 
                    # saturation bits in the DQ plane
                    dqext = ad["DQ",dispext.extver()]
                    if dqext is None:
                        log.warning("No DQ plane found; cannot make threshold "\
                                    "mask")
                        satmask = None
                    else:
                        dqdata = np.squeeze(dqext.data)
                        satmask = np.where(np.logical_or(dqdata & 2,
                                                         dqdata & 4))
                if satmask is not None:
                    masks.append(satmask)
                    mask_colors.append(204)

            overlay = overlay_dict[ad]
            if overlay is not None:
                masks.append(overlay)
                mask_colors.append(206)

                # ds9 color codes: should make this into a dictionary
                # and allow user to specify
                #
                # red = 204
                # green = 205
                # blue = 206
                # yellow = 207
                # light blue = 208
                # magenta = 209
                # orange = 210
                # dark pink = 211
                # bright orange = 212
                # light yellow = 213
                # pink = 214
                # blue-green = 215
                # pink = 216
                # peach = 217


            # Define the display name
            if tile and extname=="SCI":
                name = ad.filename
            elif tile:
                # numdisplay/ds9 doesn't seem to like square brackets
                # or spaces in the name, so use parentheses for extension
                name = "%s(%s)" % (ad.filename,extname)
            else:
                name = "%s(%s,%d)" % (ad.filename,extname,dispext.extver())

            # Display the data
            try:
                lnd.display(data,name=name,
                            frame=frame,zscale=rc["zscale"],quiet=True,
                            masks=masks, mask_colors=mask_colors)
            except IOError:
                log.warning("DS9 not found; cannot display input")

            frame+=1
        
            # Print some statistics for flats
            if "GMOS_IMAGE_FLAT" in ad.types and extname=="SCI":
                scidata = ad["SCI"].data
                dqext = ad["DQ"]
                if dqext is not None:
                    dqdata = dqext.data
                    good_data = scidata[dqdata==0]
                else:
                    good_data = scidata

                log.stdinfo("Twilight flat counts for %s:" % ad.filename)
                log.stdinfo("    Mean value:   %.0f" % np.mean(good_data, dtype=np.float64))
                log.stdinfo("    Median value: %.0f" % np.median(good_data))

        rc.report_output(orig_input)
        yield rc
    def attachWavelengthSolution(self,rc):

        # Instantiate the log
        log = logutils.get_logger(__name__)
        
        # Define the keyword to be used for the time stamp
        timestamp_key = self.timestamp_keys["attachWavelengthSolution"]

        # Log the standard "starting primitive" debug message
        log.debug(gt.log_message("primitive", "attachWavelengthSolution",
                                 "starting"))

        # Initialize the list of output AstroData objects
        adoutput_list = []

        # Check for a user-supplied arc
        adinput = rc.get_inputs_as_astrodata()
        arc_param = rc["arc"]
        arc_dict = None
        if arc_param is not None:
            # The user supplied an input to the arc parameter
            if not isinstance(arc_param, list):
                arc_list = [arc_param]
            else:
                arc_list = arc_param

            # Convert filenames to AD instances if necessary
            tmp_list = []
            for arc in arc_list:
                if type(arc) is not AstroData:
                    arc = AstroData(arc)
                tmp_list.append(arc)
            arc_list = tmp_list
            
            arc_dict = gt.make_dict(key_list=adinput, value_list=arc_list)

        for ad in adinput:
            if arc_dict is not None:
                arc = arc_dict[ad]
            else:
                arc = rc.get_cal(ad, "processed_arc")
            
                # Take care of the case where there was no arc 
                if arc is None:
                    log.warning("Could not find an appropriate arc for %s" \
                                % (ad.filename))
                    adoutput_list.append(ad)
                    continue
                else:
                    arc = AstroData(arc)

            wavecal = arc["WAVECAL"]
            if wavecal is not None:
                # Remove old versions
                if ad["WAVECAL"] is not None:
                    for wc in ad["WAVECAL"]:
                        ad.remove((wc.extname(),wc.extver()))
                # Append new solution
                ad.append(wavecal)

                # Add the appropriate time stamps to the PHU
                gt.mark_history(adinput=ad, keyword=timestamp_key)

                # Change the filename
                ad.filename = gt.filename_updater(adinput=ad, 
                                                  suffix=rc["suffix"], 
                                                  strip=True)
                adoutput_list.append(ad)
            else:
                log.warning("No wavelength solution found for %s" % ad.filename)
                adoutput_list.append(ad)

        # Report the list of output AstroData objects to the reduction
        # context
        rc.report_output(adoutput_list)
      
        yield rc
    def subtractDark(self, rc):
        """
        This primitive will subtract each SCI extension of the inputs by those
        of the corresponding dark. If the inputs contain VAR or DQ frames,
        those will also be updated accordingly due to the subtraction on the 
        data.
        """
        # Instantiate the log
        log = logutils.get_logger(__name__)
        
        # Log the standard "starting primitive" debug message
        log.debug(gt.log_message("primitive", "subtractDark", "starting"))
        
        # Define the keyword to be used for the time stamp for this primitive
        timestamp_key = self.timestamp_keys["subtractDark"]
        
        # Initialize the list of output AstroData objects
        adoutput_list = []
        
        # Check for a user-supplied dark
        adinput = rc.get_inputs_as_astrodata()
        dark_param = rc["dark"]
        dark_dict = None
        if dark_param is not None:
            # The user supplied an input to the dark parameter
            if not isinstance(dark_param, list):
                dark_list = [dark_param]
            else:
                dark_list = dark_param

            # Convert filenames to AD instances if necessary
            tmp_list = []
            for dark in dark_list:
                if type(dark) is not AstroData:
                    dark = AstroData(dark)
                tmp_list.append(dark)
            dark_list = tmp_list
            
            dark_dict = gt.make_dict(key_list=adinput, value_list=dark_list)

        # Loop over each input AstroData object in the input list
        for ad in adinput:
            
            # Check whether the subtractDark primitive has been run previously
            if ad.phu_get_key_value(timestamp_key):
                log.warning("No changes will be made to %s, since it has " \
                            "already been processed by subtractDark" \
                            % (ad.filename))
                # Append the input AstroData object to the list of output
                # AstroData objects without further processing
                adoutput_list.append(ad)
                continue
            
            # Retrieve the appropriate dark
            if dark_dict is not None:
                dark = dark_dict[ad]
            else:
                dark = rc.get_cal(ad, "processed_dark")

                # If there is no appropriate dark, there is no need to
                # subtract the dark
                if dark is None:
                    log.warning("No changes will be made to %s, since no " \
                                "appropriate dark could be retrieved" \
                                % (ad.filename))
                    # Append the input AstroData object to the list of output
                    # AstroData objects without further processing
                    adoutput_list.append(ad)
                    continue
                else:
                    dark = AstroData(dark)

            # Subtract the dark from the input AstroData object
            log.fullinfo("Subtracting the dark (%s) from the input " \
                         "AstroData object %s" \
                         % (dark.filename, ad.filename))
            ad = ad.sub(dark)
            
            # Record the dark file used
            ad.phu_set_key_value("DARKIM", 
                                 os.path.basename(dark.filename),
                                 comment=self.keyword_comments["DARKIM"])

            # Add the appropriate time stamps to the PHU
            gt.mark_history(adinput=ad, keyword=timestamp_key)

            # Change the filename
            ad.filename = gt.filename_updater(adinput=ad, suffix=rc["suffix"], 
                                              strip=True)
            
            # Append the output AstroData object to the list
            # of output AstroData objects
            adoutput_list.append(ad)
        
        # Report the list of output AstroData objects to the reduction
        # context
        rc.report_output(adoutput_list)
        
        yield rc
    def divideByFlat(self, rc):
        """
        This primitive will divide each SCI extension of the inputs by those
        of the corresponding flat. If the inputs contain VAR or DQ frames,
        those will also be updated accordingly due to the division on the data.
        """
        # Instantiate the log
        log = logutils.get_logger(__name__)
        
        # Log the standard "starting primitive" debug message
        log.debug(gt.log_message("primitive", "divideByFlat", "starting"))
        
        # Define the keyword to be used for the time stamp for this primitive
        timestamp_key = self.timestamp_keys["divideByFlat"]

        # Initialize the list of output AstroData objects
        adoutput_list = []
        
        # Check for a user-supplied flat
        adinput = rc.get_inputs_as_astrodata()
        flat_param = rc["flat"]
        flat_dict = None
        if flat_param is not None:
            # The user supplied an input to the flat parameter
            if not isinstance(flat_param, list):
                flat_list = [flat_param]
            else:
                flat_list = flat_param

            # Convert filenames to AD instances if necessary
            tmp_list = []
            for flat in flat_list:
                if type(flat) is not AstroData:
                    flat = AstroData(flat)
                tmp_list.append(flat)
            flat_list = tmp_list
            
            flat_dict = gt.make_dict(key_list=adinput, value_list=flat_list)

        # Loop over each input AstroData object in the input list
        for ad in adinput:
            
            # Check whether the divideByFlat primitive has been run previously
            if ad.phu_get_key_value(timestamp_key):
                log.warning("No changes will be made to %s, since it has " \
                            "already been processed by divideByFlat" \
                            % (ad.filename))
                # Append the input AstroData object to the list of output
                # AstroData objects without further processing
                adoutput_list.append(ad)
                continue
            
            # Retrieve the appropriate flat
            if flat_dict is not None:
                flat = flat_dict[ad]
            else:
                flat = rc.get_cal(ad, "processed_flat")
            
                # If there is no appropriate flat, there is no need to divide by
                # the flat in QA context; in SQ context, raise an error
                if flat is None:
                    if "qa" in rc.context:
                        log.warning("No changes will be made to %s, since no " \
                                    "appropriate flat could be retrieved" \
                                    % (ad.filename))
                        # Append the input AstroData object to the list of output
                        # AstroData objects without further processing
                        adoutput_list.append(ad)
                        continue
                    else:
                        raise Errors.PrimitiveError("No processed flat found "\
                                                    "for %s" % ad.filename)
                else:
                    flat = AstroData(flat)
            
            # Check the inputs have matching filters, binning, and SCI shapes.
            try:
                gt.check_inputs_match(ad1=ad, ad2=flat) 
            except Errors.ToolboxError:
                # If not, try to clip the flat frame to the size
                # of the science data
                # For a GMOS example, this allows a full frame flat to
                # be used for a CCD2-only science frame. 
                flat = gt.clip_auxiliary_data(
                    adinput=ad,aux=flat,aux_type="cal")[0]

                # Check again, but allow it to fail if they still don't match
                gt.check_inputs_match(ad1=ad, ad2=flat)


            # Divide the adinput by the flat
            log.fullinfo("Dividing the input AstroData object (%s) " \
                         "by this flat:\n%s" % (ad.filename,
                                                flat.filename))
            ad = ad.div(flat)
                        
            # Record the flat file used
            ad.phu_set_key_value("FLATIM", 
                                 os.path.basename(flat.filename),
                                 comment=self.keyword_comments["FLATIM"])

            
            # Add the appropriate time stamps to the PHU
            gt.mark_history(adinput=ad, keyword=timestamp_key)

            # Change the filename
            ad.filename = gt.filename_updater(adinput=ad, suffix=rc["suffix"], 
                                              strip=True)
            
            # Append the output AstroData object to the list 
            # of output AstroData objects
            adoutput_list.append(ad)
        
        # Report the list of output AstroData objects to the reduction
        # context
        rc.report_output(adoutput_list)
        
        yield rc
    def subtractFringe(self, rc):
        
        # Instantiate the log
        log = gemLog.getGeminiLog(logType=rc["logType"],
                                  logLevel=rc["logLevel"])
        
        # Log the standard "starting primitive" debug message
        log.debug(gt.log_message("primitive", "subtractFringe",
                                 "starting"))
        
        # Define the keyword to be used for the time stamp for this primitive
        timestamp_key = self.timestamp_keys["subtractFringe"]

        # Initialize the list of output AstroData objects
        adoutput_list = []
        
        # Check for a user-supplied fringe
        adinput = rc.get_inputs_as_astrodata()
        fringe_param = rc["fringe"]
        fringe_dict = None
        if fringe_param is not None:
            # The user supplied an input to the fringe parameter
            if not isinstance(fringe_param, list):
                fringe_list = [fringe_param]
            else:
                fringe_list = fringe_param

            # Convert filenames to AD instances if necessary
            tmp_list = []
            for fringe in fringe_list:
                if type(fringe) is not AstroData:
                    fringe = AstroData(fringe)
                tmp_list.append(fringe)
            fringe_list = tmp_list
            
            fringe_dict = gt.make_dict(key_list=adinput, value_list=fringe_list)
        

        # Loop over each input AstroData object in the input list
        for ad in adinput:
            
            # Check whether the subtractFringe primitive has been run
            # previously
            if ad.phu_get_key_value(timestamp_key):
                log.warning("No changes will be made to %s, since it has " \
                            "already been processed by subtractFringe" \
                            % (ad.filename))
                # Append the input AstroData object to the list of output
                # AstroData objects without further processing
                adoutput_list.append(ad)
                continue
            
            # Retrieve the appropriate fringe
            if fringe_dict is not None:
                fringe = fringe_dict[ad]
            else:
                fringe = rc.get_cal(ad, "processed_fringe")
            
                # Take care of the case where there was no fringe 
                if fringe is None:
                    log.warning("Could not find an appropriate fringe for %s" \
                                % (ad.filename))
                    # Append the input to the output without further processing
                    adoutput_list.append(ad)
                    continue
                else:
                    fringe = AstroData(fringe)

            # Check the inputs have matching filters, binning and SCI shapes.
            try:
                gt.check_inputs_match(ad1=ad, ad2=fringe)
            except Errors.ToolboxError:
                # If not, try to clip the fringe frame to the size of the
                # science data
                # For a GMOS example, this allows a full frame fringe to
                # be used for a CCD2-only science frame. 
                fringe = gt.clip_auxiliary_data(
                    adinput=ad, aux=fringe, aux_type="cal")[0]

                # Check again, but allow it to fail if they still don't match
                gt.check_inputs_match(ad1=ad, ad2=fringe)


            # Subtract the fringe from the science
            ad = ad.sub(fringe)
            
            # Record the fringe file used
            ad.phu_set_key_value("FRINGEIM", 
                                 os.path.basename(fringe.filename),
                                 comment=self.keyword_comments["FRINGEIM"])

            # Add the appropriate time stamps to the PHU
            gt.mark_history(adinput=ad, keyword=timestamp_key)

            # Change the filename
            ad.filename = gt.filename_updater(adinput=ad, suffix=rc["suffix"], 
                                              strip=True)
            
            # Append the output AstroData object to the list 
            # of output AstroData objects
            adoutput_list.append(ad)
            
        # Report the list of output AstroData objects to the reduction context
        rc.report_output(adoutput_list)
        yield rc
    def scaleFringeToScience(self, rc):
        """
        This primitive will scale the fringes to their matching science data
        The fringes should be in the stream this primitive is called on,
        and the reference science frames should be loaded into the RC,
        as, eg. rc["science"] = adinput.
        
        There are two ways to find the value to scale fringes by:
        1. If stats_scale is set to True, the equation:
        (letting science data = b (or B), and fringe = a (or A))
    
        arrayB = where({where[SCIb < (SCIb.median+2.5*SCIb.std)]} 
                          > [SCIb.median-3*SCIb.std])
        scale = arrayB.std / SCIa.std
    
        The section of the SCI arrays to use for calculating these statistics
        is the CCD2 SCI data excluding the outer 5% pixels on all 4 sides.
        Future enhancement: allow user to choose section
    
        2. If stats_scale=False, then scale will be calculated using:
        exposure time of science / exposure time of fringe

        :param stats_scale: Use statistics to calculate the scale values,
                            rather than exposure time
        :type stats_scale: Python boolean (True/False)
        """
        
        # Instantiate the log
        log = gemLog.getGeminiLog(logType=rc["logType"],
                                  logLevel=rc["logLevel"])
        
        # Log the standard "starting primitive" debug message
        log.debug(gt.log_message("primitive", "scaleFringeToScience",
                                 "starting"))
        
        # Define the keyword to be used for the time stamp for this primitive
        timestamp_key = self.timestamp_keys["scaleFringeToScience"]

        # Check for user-supplied science frames
        fringe = rc.get_inputs_as_astrodata()
        science_param = rc["science"]
        fringe_dict = None
        if science_param is not None:
            # The user supplied an input to the science parameter
            if not isinstance(science_param, list):
                science_list = [science_param]
            else:
                science_list = science_param

            # If there is one fringe and multiple science frames,
            # the fringe must be deepcopied to allow it to be
            # scaled separately for each frame
            if len(fringe)==1 and len(science_list)>1:
                fringe = [deepcopy(fringe[0]) for img in science_list]

            # Convert filenames to AD instances if necessary
            tmp_list = []
            for science in science_list:
                if type(science) is not AstroData:
                    science = AstroData(science)
                tmp_list.append(science)
            science_list = tmp_list
            
            fringe_dict = gt.make_dict(key_list=science_list, 
                                       value_list=fringe)
            fringe_output = []
        else:
            log.warning("No science frames specified; no scaling will be done")
            science_list = []
            fringe_output = fringe

        # Loop over each AstroData object in the science list
        for ad in science_list:
            
            # Retrieve the appropriate fringe
            fringe = fringe_dict[ad]

            # Check the inputs have matching filters, binning and SCI shapes.
            try:
                gt.check_inputs_match(ad1=ad, ad2=fringe)
            except Errors.ToolboxError:
                # If not, try to clip the fringe frame to the size of the
                # science data
                # For a GMOS example, this allows a full frame fringe to
                # be used for a CCD2-only science frame. 
                fringe = gt.clip_auxiliary_data(
                    adinput=ad, aux=fringe, aux_type="cal")[0]

                # Check again, but allow it to fail if they still don't match
                gt.check_inputs_match(ad1=ad, ad2=fringe)

            # Check whether statistics should be used
            stats_scale = rc["stats_scale"]

            # Calculate the scale value
            scale = 1.0
            if not stats_scale:
                # Use the exposure times to calculate the scale
                log.fullinfo("Using exposure times to calculate the scaling"+
                             " factor")
                try:
                    scale = ad.exposure_time() / fringe.exposure_time()
                except:
                    raise Errors.InputError("Could not get exposure times " +
                                            "for %s, %s. Try stats_scale=True" %
                                            (ad.filename,fringe.filename))
            else:

                # Use statistics to calculate the scaling factor
                log.fullinfo("Using statistics to calculate the " +
                             "scaling factor")

                # Deepcopy the input so it can be manipulated without
                # affecting the original
                statsad = deepcopy(ad)
                statsfringe = deepcopy(fringe)

                # Trim off any overscan region still present
                statsad,statsfringe = gt.trim_to_data_section([statsad,
                                                               statsfringe])

                # Check the number of science extensions; if more than
                # one, use CCD2 data only
                nsciext = statsad.count_exts("SCI")
                if nsciext>1:

                    # Get the CCD numbers and ordering information
                    # corresponding to each extension
                    log.fullinfo("Trimming data to data section to remove "\
                                 "overscan region")
                    sci_info,frng_info = gt.array_information([statsad,
                                                               statsfringe])

                    # Pull out CCD2 data
                    scidata = []
                    frngdata = []
                    dqdata = []
                    for i in range(nsciext):

                        # Get the next extension in physical order
                        sciext = statsad["SCI",sci_info["amps_order"][i]]
                        frngext = statsfringe["SCI",frng_info["amps_order"][i]]

                        # Check to see if it is on CCD2; if so, keep it
                        if sci_info[
                            "array_number"][("SCI",sciext.extver())]==2:

                            scidata.append(sciext.data)

                            dqext = statsad["DQ",sci_info["amps_order"][i]]
                            maskext = statsad["OBJMASK",
                                              sci_info["amps_order"][i]]
                            if dqext is not None and maskext is not None:
                                dqdata.append(dqext.data | maskext.data)
                            elif dqext is not None:
                                dqdata.append(dqext.data)
                            elif maskext is not None:
                                dqdata.append(maskext.data)

                        if frng_info[
                            "array_number"][("SCI",frngext.extver())]==2:
                            frngdata.append(frngext.data)
                        
                    # Stack data if necessary
                    if len(scidata)>1:
                        scidata = np.hstack(scidata)
                        frngdata = np.hstack(frngdata)
                    else:
                        scidata = scidata[0]
                        frngdata = frngdata[0]
                    if len(dqdata)>0:
                        if len(dqdata)>1:
                            dqdata = np.hstack(dqdata)
                        else:
                            dqdata = dqdata[0]
                    else:
                        dqdata = None
                else:
                    scidata = statsad["SCI"].data
                    frngdata = statsfringe["SCI"].data

                    dqext = statsad["DQ"]
                    maskext = statsad["OBJMASK"]
                    if dqext is not None and maskext is not None:
                        dqdata = dqext.data | maskext.data
                    elif dqext is not None:
                        dqdata = dqext.data
                    elif maskext is not None:
                        dqdata = maskext.data
                    else:
                        dqdata = None

                if dqdata is not None:
                    # Replace any DQ-flagged data with the median value
                    smed = np.median(scidata[dqdata==0])
                    scidata = np.where(dqdata!=0,smed,scidata)

                # Calculate the maximum and minimum in a box centered on 
                # each data point.  The local depth of the fringe is
                # max - min.  The overall fringe strength is the median
                # of the local fringe depths.

                # Width of the box is binning and
                # filter dependent, determined by experimentation
                # Results don't seem to depend heavily on the box size
                if ad.filter_name(pretty=True).as_pytype=="i":
                    size = 20
                else:
                    size = 40
                size /= ad.detector_x_bin().as_pytype()
                
                # Use ndimage maximum_filter and minimum_filter to
                # get the local maxima and minima
                import scipy.ndimage as ndimage
                sci_max = ndimage.filters.maximum_filter(scidata,size)
                sci_min = ndimage.filters.minimum_filter(scidata,size)


                # Take off 5% of the width as a border
                xborder = int(0.05 * scidata.shape[1])
                yborder = int(0.05 * scidata.shape[0])
                if xborder<20:
                    xborder = 20
                if yborder<20:
                    yborder = 20
                sci_max = sci_max[yborder:-yborder,xborder:-xborder]
                sci_min = sci_min[yborder:-yborder,xborder:-xborder]

                # Take the median difference
                sci_df = np.median(sci_max - sci_min)

                # Do the same for the fringe
                frn_max = ndimage.filters.maximum_filter(frngdata,size)
                frn_min = ndimage.filters.minimum_filter(frngdata,size)
                frn_max = frn_max[yborder:-yborder,xborder:-xborder]
                frn_min = frn_min[yborder:-yborder,xborder:-xborder]
                frn_df = np.median(frn_max - frn_min)

                # Scale factor
                # This tends to overestimate the factor, but it is
                # at least in the right ballpark, unlike the estimation
                # used in girmfringe (masked_sci.std/fringe.std)
                scale = sci_df / frn_df

            log.fullinfo("Scale factor found = "+str(scale))
                
            # Use mult from the arith toolbox to perform the scaling of 
            # the fringe frame
            scaled_fringe = fringe.mult(scale)
            
            # Add the appropriate time stamps to the PHU
            gt.mark_history(adinput=scaled_fringe, keyword=timestamp_key)

            # Change the filename
            scaled_fringe.filename = gt.filename_updater(
                adinput=ad, suffix=rc["suffix"], strip=True)
            
            fringe_output.append(scaled_fringe)
            
        # Report the list of output AstroData objects to the reduction context
        rc.report_output(fringe_output)
        yield rc
Esempio n. 7
0
    def attachWavelengthSolution(self, rc):

        # Instantiate the log
        log = logutils.get_logger(__name__)

        # Define the keyword to be used for the time stamp
        timestamp_key = self.timestamp_keys["attachWavelengthSolution"]

        # Log the standard "starting primitive" debug message
        log.debug(
            gt.log_message("primitive", "attachWavelengthSolution",
                           "starting"))

        # Initialize the list of output AstroData objects
        adoutput_list = []

        # Check for a user-supplied arc
        adinput = rc.get_inputs_as_astrodata()
        arc_param = rc["arc"]
        arc_dict = None
        if arc_param is not None:
            # The user supplied an input to the arc parameter
            if not isinstance(arc_param, list):
                arc_list = [arc_param]
            else:
                arc_list = arc_param

            # Convert filenames to AD instances if necessary
            tmp_list = []
            for arc in arc_list:
                if type(arc) is not AstroData:
                    arc = AstroData(arc)
                tmp_list.append(arc)
            arc_list = tmp_list

            arc_dict = gt.make_dict(key_list=adinput, value_list=arc_list)

        for ad in adinput:
            if arc_dict is not None:
                arc = arc_dict[ad]
            else:
                arc = rc.get_cal(ad, "processed_arc")

                # Take care of the case where there was no arc
                if arc is None:
                    log.warning("Could not find an appropriate arc for %s" \
                                % (ad.filename))
                    adoutput_list.append(ad)
                    continue
                else:
                    arc = AstroData(arc)

            wavecal = arc["WAVECAL"]
            if wavecal is not None:
                # Remove old versions
                if ad["WAVECAL"] is not None:
                    for wc in ad["WAVECAL"]:
                        ad.remove((wc.extname(), wc.extver()))
                # Append new solution
                ad.append(wavecal)

                # Add the appropriate time stamps to the PHU
                gt.mark_history(adinput=ad,
                                primname=self.myself(),
                                keyword=timestamp_key)

                # Change the filename
                ad.filename = gt.filename_updater(adinput=ad,
                                                  suffix=rc["suffix"],
                                                  strip=True)
                adoutput_list.append(ad)
            else:
                log.warning("No wavelength solution found for %s" %
                            ad.filename)
                adoutput_list.append(ad)

        # Report the list of output AstroData objects to the reduction
        # context
        rc.report_output(adoutput_list)

        yield rc