Instruction:
Create a Python 3 function to
remove sources which are within nsigma*fwhm/2 pixels of each other, leaving only a single valid source in that region. This algorithm only works for sources which end up sequentially next to each other based on Y position and removes enough duplicates to make the final source list more managable. It sorts the positions by Y value in order to group those at the same positions as much as possible.

Response:
def apply_nsigma_separation(fitind,fluxes,separation,niter=10):
    """
    Remove sources which are within nsigma*fwhm/2 pixels of each other, leaving
    only a single valid source in that region.

    This algorithm only works for sources which end up sequentially next to each other
    based on Y position and removes enough duplicates to make the final source list more
    managable.  It sorts the positions by Y value in order to group those at the
    same positions as much as possible.
    """
    for n in range(niter):
        if len(fitind) < 1:
            break
        fitarr = np.array(fitind,np.float32)
        fluxarr = np.array(fluxes,np.float32)
        inpind = np.argsort(fitarr[:,1])
        npind = fitarr[inpind]
        fluxind = fluxarr[inpind]
        fitind = npind.tolist()
        fluxes = fluxind.tolist()
        dx = npind[1:,0] - npind[:-1,0]
        dy = npind[1:,1] - npind[:-1,1]
        dr = np.sqrt(np.power(dx,2)+np.power(dy,2))
        nsame = np.where(dr <= separation)[0]
        if nsame.shape[0] > 0:
            for ind in nsame[-1::-1]:
                #continue # <- turn off filtering by source separation
                del fitind[ind]
                del fluxes[ind]
        else:
            break
    return fitind,fluxes


Instruction:
Can you tell what is the following Python 3 function doing
def xy_round(data,x0,y0,skymode,ker2d,xsigsq,ysigsq,datamin=None,datamax=None):
    nyk,nxk = ker2d.shape
    if datamin is None:
        datamin = data.min()
    if datamax is None:
        datamax = data.max()

    # call C function for speed now...
    xy_val = cdriz.arrxyround(data,x0,y0,skymode,ker2d,xsigsq,ysigsq,datamin,datamax)
    if xy_val is None:
        x = None
        y = None
        round = None
    else:
        x = xy_val[0]
        y = xy_val[1]
        round = xy_val[2]

    return x,y,round

Response: Compute center of source


Instruction:
Can you generate the documentation for the following Python 3 function
def sharp_round(data, density, kskip, xc, yc, s2m, s4m, nxk, nyk,
                datamin, datamax):
    """
    sharp_round -- Compute first estimate of the roundness and sharpness of the
    detected objects.

    A Python translation of the AP_SHARP_ROUND IRAF/DAOFIND function.
    """

    # Compute the first estimate of roundness:
    sum2 = np.sum(s2m*density)
    sum4 = np.sum(s4m*abs(density))
    if sum2 == 0.0:
        round = 0.0
    elif sum4 <= 0.0: # eps?
        round = None
    else:
        round = 2.0 * sum2 / sum4

    # Eliminate the sharpness test if the central pixel is bad:
    mid_data_pix = data[yc, xc]
    mid_dens_pix = density[yc, xc]
    if mid_data_pix > datamax:
        return True, round, None
    if mid_data_pix < datamin:
        return False, round, None

    ########################
    # Sharpness statistics:

    satur = np.max(kskip*data) > datamax

    # Exclude pixels (create a mask) outside the [datamin, datamax] range:
    uskip = np.where((data >= datamin) & (data <= datamax), 1, 0)
    # Update the mask with the "skipped" values from the convolution kernel:
    uskip *= kskip
    # Also, exclude central pixel:
    uskip[yc, xc] = 0

    npixels = np.sum(uskip)
    if (npixels < 1 or mid_dens_pix <= 0.0):
        return satur, round, None

    sharp = (mid_data_pix - np.sum(uskip*data)/npixels) / mid_dens_pix
    #sharp = (mid_data_pix - np.mean(uskip*data)) / mid_dens_pix

    return satur, round, sharp

Response: Compute the first estimate of the sharpness of the object.


Instruction:
Write a Python 3 script to
return the roundness of the image.

Response:
def roundness(im):
    """
    from astropy.io import fits as pyfits
    data=pyfits.getdata('j94f05bgq_flt.fits',ext=1)
    star0=data[403:412,423:432]
    star=data[396:432,3522:3558]
    In [53]: findobj.roundness(star0)
    Out[53]: 0.99401955054989544
    In [54]: findobj.roundness(star)
    Out[54]: 0.83091919980660645

    """
    perimeter = im.shape[0]*2 +im.shape[1]*2 -4
    area = im.size
    return 4*np.pi*area/perimeter**2


Instruction:
Create a Python 3 function to
compute the immoments of a single image.

Response:
def immoments(im, p,q):
    x = list(range(im.shape[1]))
    y = list(range(im.shape[0]))
    #coord=np.array([x.flatten(),y.flatten()]).T
    """
    moment = 0
    momentx = 0
    for i in x.flatten():
        moment+=momentx
        sumx=0
        for j in y.flatten():
            sumx+=i**0*j**0*star0[i,j]
    """
    moment = np.sum([i**p*j**q*im[i,j] for j in x for i in y], dtype=np.float64)
    return moment


Instruction:
Explain what the following Python 3 code does
def centroid(im):
    # These calls point to Python version of moments function
    m00 = cdriz.arrmoments(im,0,0)
    m10 = cdriz.arrmoments(im, 1,0)
    m01 = cdriz.arrmoments(im,0,1)

    ycen = m10 / m00
    xcen = m01 / m00
    return xcen, ycen

Response: Computes the centroid of an image using the image moments


Instruction:
Write a Python 3 script for
getting the entry in MDRIZTAB where task parameters live.

Response:
def getMdriztabParameters(files):
    """ Gets entry in MDRIZTAB where task parameters live.
        This method returns a record array mapping the selected
        row.
    """

    # Get the MDRIZTAB table file name from the primary header.
    # It is gotten from the first file in the input list. No
    # consistency checks are performed.
    _fileName = files[0]
    _header = fileutil.getHeader(_fileName)
    if 'MDRIZTAB' in _header:
        _tableName = _header['MDRIZTAB']
    else:
        raise KeyError("No MDRIZTAB found in file " + _fileName)

    _tableName = fileutil.osfn(_tableName)

    # Now get the filters from the primary header.
    _filters = fileutil.getFilterNames(_header)

    # Specifically check to see whether the MDRIZTAB file can be found
    mtab_path = os.path.split(_tableName)[0] # protect against no path given for _tableName
    if mtab_path and not os.path.exists(mtab_path): # check path first, if given
        raise IOError("Directory for MDRIZTAB '%s' could not be accessed!"%mtab_path)
    if not os.path.exists(_tableName): # then check for the table itself
        raise IOError("MDRIZTAB table '%s' could not be found!"%_tableName)

    # Open MDRIZTAB file.
    try:
        _mdriztab = fits.open(_tableName, memmap=False)
    except:
        raise IOError("MDRIZTAB table '%s' not valid!" % _tableName)

    # Look for matching rows based on filter name. If no
    # match, pick up rows for the default filter.
    _rows = _getRowsByFilter(_mdriztab, _filters)
    if _rows == []:
        _rows = _getRowsByFilter(_mdriztab, 'ANY')

    # Now look for the row that matches the number of images.
    # The logic below assumes that rows for a given filter
    # are arranged in ascending order of the 'numimage' field.
    _nimages = len(files)
    _row = 0
    for i in _rows:
        _numimages = _mdriztab[1].data.field('numimages')[i]
        if _nimages >= _numimages:
            _row = i
    print('- MDRIZTAB: AstroDrizzle parameters read from row %s.'%(_row+1))

    mpars = _mdriztab[1].data[_row]
    _mdriztab.close()

    interpreted = _interpretMdriztabPars(mpars)

    if "staticfile" in interpreted:
        interpreted.pop("staticfile")

    return interpreted


Instruction:
Write a Python 3 script for
interpreting the parameters in the MDRIZTAB record and update the master parameters list with those values.

Response:
def _interpretMdriztabPars(rec):
    """
    Collect task parameters from the MDRIZTAB record and
    update the master parameters list with those values

    Note that parameters read from the MDRIZTAB record must
    be cleaned up in a similar way that parameters read
    from the user interface are.
    """
    tabdict = {}
    # for each entry in the record...
    for indx in range(len(rec.array.names)):
        # ... get the name, format, and value.
        _name = rec.array.names[indx]
        _format = rec.array.formats[indx]
        _value = rec.field(_name)

        # Translate names from MDRIZTAB columns names to
        # input parameter names found in IRAF par file.
        #
        #if _name.find('final') > -1: _name = 'driz_'+_name
        if _name in ['shiftfile','mdriztab']:
            continue
        drizstep_names = ['driz_sep_','final_']
        if _name in ['refimage','bits']:
            for dnames in drizstep_names:
                tabdict[dnames+_name] = _value
            continue
        if _name in ['driz_sep_bits','final_bits']:
            tabdict[_name] = str(_value)
            continue
        if _name == 'coeffs':
            _val = True
            if _value in ['INDEF',None,"None",'',' ']: _val = False
            tabdict[_name] = _val
            continue

        par_table = {'subsky':'skysub','crbitval':'crbit','readnoise':'rdnoise'}
        if _name in par_table:
            _name = par_table[_name]

        # We do not care about the first two columns at this point
        # as they are only used for selecting the rows
        if _name != 'filter' and _name != 'numimages':
            # start by determining the format type of the parameter
            _fmt = findFormat(_format)

            # Based on format type, apply proper conversion/cleaning
            if (_fmt == 'a') or (_fmt == 'A'):
                _val = cleanBlank(_value)
                if _val is None:
                    _val = ''

            elif (_format == 'i1') or (_format=='1L'):
                _val = toBoolean(_value)

            elif (_format == 'i4') or (_format == '1J'):
                _val = cleanInt(_value)

            elif ('E' in _format) or (_format == 'f4') :
                _val = cleanNaN(_value)

            else:
                print('MDRIZTAB column ',_name,' has unrecognized format',_format)
                raise ValueError
            if _name in ['ra','dec']:
                for dnames in drizstep_names:
                    tabdict[dnames+_name] = _val

            else:
                tabdict[_name] = _val

    return tabdict


Instruction:
How would you code a function in Python 3 to
interpret parameters from TEAL configObj interface as set interactively by the user and build a new WCS instance.

Response:
def run(configObj,wcsmap=None):
    """ Interpret parameters from TEAL/configObj interface as set interactively
        by the user and build the new WCS instance
    """

    distortion_pars = configObj['Distortion Model']

    outwcs = build(configObj['outwcs'], configObj['wcsname'],
            configObj['refimage'], undistort = configObj['undistort'],
            usecoeffs=distortion_pars['applycoeffs'], coeffsfile=distortion_pars['coeffsfile'],
            **configObj['User WCS Parameters'])


Instruction:
Can you write a function in Python 3 where it
creates a new WCS instance from a reference image and a user supplied parameters.

Response:
def build(outname, wcsname, refimage, undistort=False,
          applycoeffs=False, coeffsfile=None, **wcspars):
    """ Core functionality to create a WCS instance from a reference image WCS,
        user supplied parameters or user adjusted reference WCS.
        The distortion information can either be read in as part of the reference
        image WCS or given in 'coeffsfile'.

        Parameters
        ----------
        outname   : string
            filename of output WCS
        wcsname   : string
            WCSNAME ID for generated WCS
        refimage  : string
            filename of image with source WCS used as basis for output WCS
        undistort : bool
            Create an undistorted WCS?
        applycoeffs : bool
            Apply coefficients from refimage to generate undistorted WCS?
        coeffsfile  : string
            If specified, read distortion coeffs from separate file

    """

    # Insure that the User WCS parameters have values for all the parameters,
    # even if that value is 'None'
    user_wcs_pars = convert_user_pars(wcspars)
    userwcs = wcspars['userwcs']

    """
    Use cases to document the logic required to interpret the parameters

    WCS generation based on refimage/userwcs parameters
    -------------------------------------------------------------
    refimage == None, userwcs == False:
        *NO WCS specified*
        => print a WARNING message and return without doing anything
    refimage == None, userwcs == True:
        => Create WCS without a distortion model entirely from user parameters*
    refimage != None, userwcs == False:
        => No user WCS parameters specified
        => Simply use refimage WCS as specified
    refimage != None, userwcs == True:
        => Update refimage WCS with user specified values*

    Apply distortion and generate final headerlet using processed WCS
    -----------------------------------------------------------------
    refimage == None, userwcs == True:
        *Output WCS generated entirely from user supplied parameters*
        Case 1: applycoeffs == False, undistort == True/False (ignored)
            => no distortion model to interpret
            => generate undistorted headerlet with no distortion model
        Case 2: applycoeffs == True/False, undistort == True
            => ignore any user specified distortion model
            => generate undistorted headerlet with no distortion model
        Case 3: applycoeffs == True, undistort == False
            => WCS from scratch combined with distortion model from another image
            => generate headerlet with distortion model

    refimage != None, userwcs == True/False:
        *Output WCS generated from reference image possibly modified by user parameters*
        Case 4: applycoeffs == False, undistort == True
            => If refimage has distortion, remove it
            => generate undistorted headerlet with no distortion model
        Case 5: applycoeffs == False, undistort == False
            => Leave refimage distortion model (if any) unmodified
            => generate a headerlet using same distortion model (if any) as refimage
        Case 6: applycoeffs == True, undistort == False
            => Update refimage with distortion model with user-specified model
            => generate a headerlet with a distortion model
        Case 7: applycoeffs == True, undistort == True
            => ignore user specified distortion model and undistort WCS
            => generate a headerlet without a distortion model
    """
    ### Build WCS from refimage and/or user pars
    if util.is_blank(refimage) and not userwcs:
        print('WARNING: No WCS specified... No WCS created!')
        return
    customwcs = None
    if util.is_blank(refimage) and userwcs:
        # create HSTWCS object from user parameters
        complete_wcs = True
        for key in user_wcs_pars:
            if util.is_blank(user_wcs_pars[key]):
                complete_wcs = False
                break
        if complete_wcs:
            customwcs = wcs_functions.build_hstwcs(user_wcs_pars['crval1'],user_wcs_pars['crval2'],
                user_wcs_pars['crpix1'],user_wcs_pars['crpix2'],
                user_wcs_pars['naxis1'],user_wcs_pars['naxis2'],
                user_wcs_pars['pscale'],user_wcs_pars['orientat'])
        else:
            print('WARNING: Not enough WCS information provided by user!')
            raise ValueError

    if not util.is_blank(refimage):
        refwcs = stwcs.wcsutil.HSTWCS(refimage)
    else:
        refwcs = customwcs

    ### Apply distortion model (if any) to update WCS
    if applycoeffs and not util.is_blank(coeffsfile):
        if not util.is_blank(refimage):
            replace_model(refwcs, coeffsfile)
        else:
            if not undistort:
                add_model(refwcs,coeffsfile)
                # Only working with custom WCS from user, no distortion
                # so apply model to WCS, including modifying the CD matrix
                apply_model(refwcs)

    ### Create undistorted WCS, if requested
    if undistort:
        outwcs = undistortWCS(refwcs)
    else:
        outwcs = refwcs

    if userwcs:
        # replace (some/all?) WCS values from refimage with user WCS values
        # by running 'updatewcs' functions on input WCS
        outwcs = mergewcs(outwcs,customwcs,user_wcs_pars)

    ### Create the final headerlet and write it out, if specified
    if not util.is_blank(refimage):
        template = refimage
    elif not util.is_blank(coeffsfile):
        template = coeffsfile
    else:
        template = None

    # create default WCSNAME if None was given
    wcsname = create_WCSname(wcsname)
    print('Creating final headerlet with name ',wcsname,' using template ',template)
    outhdr = generate_headerlet(outwcs,template,wcsname,outname=outname)

    # synchronize this new WCS with the rest of the chips in the image
    for ext in outhdr:
        if 'extname' in ext.header and ext.header['extname'] == 'SIPWCS':
            ext_wcs = wcsutil.HSTWCS(ext)
            stwcs.updatewcs.makewcs.MakeWCS.updateWCS(ext_wcs,outwcs)

    return outwcs


Instruction:
How would you explain what the following Python 3 function does
def create_WCSname(wcsname):
    if util.is_blank(wcsname):
        ptime = fileutil.getDate()
        wcsname = "User_"+ptime

    return wcsname

Response: Verify that a valid WCSNAME has been provided and create a new one based on current date.


Instruction:
Here you have a function in Python 3, explain what it does
def convert_user_pars(wcspars):
    default_pars = default_user_wcs.copy()
    for kw in user_hstwcs_pars:
        default_pars[user_hstwcs_pars[kw]] = wcspars[kw]
    return default_pars

Response:
Convert the parameters provided by the configObj into the corresponding
CTYPE parameters.


Instruction:
Explain what the following Python 3 code does
def mergewcs(outwcs, customwcs, wcspars):
    # start by working on a copy of the refwcs
    if outwcs.sip is not None:
        wcslin = stwcs.distortion.utils.undistortWCS(outwcs)
        outwcs.wcs.cd = wcslin.wcs.cd
        outwcs.wcs.set()
        outwcs.setOrient()
        outwcs.setPscale()
    else:
        wcslin = outwcs
    if customwcs is None:
        # update valid pars from wcspars
        if wcspars['crval1'] is not None:
            outwcs.wcs.crval = np.array([wcspars['crval1'],wcspars['crval2']])
        if wcspars['crpix1'] is not None:
            outwcs.wcs.crpix = np.array([wcspars['crpix1'],wcspars['crpix2']])
        if wcspars['naxis1'] is not None:
            outwcs.pixel_shape = (wcspars['naxis1'], wcspars['naxis2'])
            outwcs.wcs.crpix = np.array(outwcs.pixel_shape) / 2.0

        pscale = wcspars['pscale']
        orient = wcspars['orientat']
        if pscale is not None or orient is not None:
            if pscale is None: pscale = wcslin.pscale
            if orient is None: orient = wcslin.orientat
            pix_ratio = pscale/wcslin.pscale
            delta_rot = wcslin.orientat - orient
            delta_rot_mat = fileutil.buildRotMatrix(delta_rot)
            outwcs.wcs.cd = np.dot(outwcs.wcs.cd,delta_rot_mat)*pix_ratio
            # apply model to new linear CD matrix
            apply_model(outwcs)
    else:
        # A new fully described WCS was provided in customwcs
        outwcs.wcs.cd = customwcs.wcs.cd
        outwcs.wcs.crval = customwcs.wcs.crval
        outwcs.wcs.crpix = customwcs.wcs.crpix
        outwcs.pixel_shape = customwcs.pixel_shape
    return outwcs

Response: Merge the WCS keywords from user specified values into a full HSTWCS object.


Instruction:
Given the following Python 3 function, write the documentation
def add_model(refwcs, newcoeffs):
    # Update refwcs with distortion model
    for kw in model_attrs:
        if newcoeffs.__dict__[key] is not None:
            refwcs.__dict__[key] = newcoeffs.__dict__[key]

Response: Add distortion model to existing HSTWCS object


Instruction:
How would you code a function in Python 3 to
apply distortion model to WCS including modifying CD with linear distortion terms

Response:
def apply_model(refwcs):
    """ Apply distortion model to WCS, including modifying
        CD with linear distortion terms

    """
    # apply distortion model to CD matrix
    if 'ocx10' in refwcs.__dict__ and refwcs.ocx10 is not None:
        linmat = np.array([[refwcs.ocx11,refwcs.ocx10],[refwcs.ocy11,refwcs.ocy10]])/refwcs.idcscale
        refwcs.wcs.cd = np.dot(refwcs.wcs.cd,linmat)

        refwcs.wcs.set()
        refwcs.setOrient()
        refwcs.setPscale()


Instruction:
Here you have a function in Python 3, explain what it does
def replace_model(refwcs, newcoeffs):
    print('WARNING:')
    print('    Replacing existing distortion model with one')
    print('    not necessarily matched to the observation!')
    # create linear version of WCS to be updated by new model
    wcslin = stwcs.distortion.utils.undistortWCS(refwcs)
    outwcs = refwcs.deepcopy()
    outwcs.wcs.cd = wcslin.wcs.cd
    outwcs.wcs.set()
    outwcs.setOrient()
    outwcs.setPscale()
    # add new model to updated WCS object
    add_model(outwcs,newcoeffs)
    # Update CD matrix with new model
    apply_model(outwcs)

    # replace original input WCS with newly updated WCS
    refwcs = outwcs.deepcopy()

Response: Replace the distortion model in a current WCS with a new model


Instruction:
How would you code a function in Python 3 to
generate an undistorted HSTWCS object from an HSTWCS object with a distortion model

Response:
def undistortWCS(refwcs):
    """ Generate an undistorted HSTWCS from an HSTWCS object with a distortion model
    """
    wcslin = stwcs.distortion.utils.output_wcs([refwcs])

    outwcs = stwcs.wcsutil.HSTWCS()
    outwcs.wcs = wcslin.wcs
    outwcs.wcs.set()
    outwcs.setPscale()
    outwcs.setOrient()
    outwcs.sip = None

    # Update instrument specific keywords
    outwcs.inst_kw = refwcs.inst_kw
    for kw in refwcs.inst_kw:
        outwcs.__dict__[kw] = refwcs.__dict__[kw]
    outwcs.pixel_shape = wcslin.pixel_shape

    return outwcs


Instruction:
Write a Python 3 function that can
generate a headerlet based on the updated HSTWCS object and the template file.

Response:
def generate_headerlet(outwcs,template,wcsname,outname=None):
    """ Create a headerlet based on the updated HSTWCS object

        This function uses 'template' as the basis for the headerlet.
        This file can either be the original wcspars['refimage'] or
        wcspars['coeffsfile'], in this order of preference.

        If 'template' is None, then a simple Headerlet will be
        generated with a single SIPWCS extension and no distortion
    """
    # Create header object from HSTWCS object
    siphdr = True
    if outwcs.sip is None:
        siphdr = False
    outwcs_hdr = outwcs.wcs2header(sip2hdr=siphdr)
    outwcs_hdr['NPIX1'] = outwcs.pixel_shape[0]
    outwcs_hdr['NPIX2'] = outwcs.pixel_shape[1]

    # create headerlet object in memory; either from a file or from scratch
    if template is not None and siphdr:
        print('Creating headerlet from template...')
        fname,extn = fileutil.parseFilename(template)
        extnum = fileutil.parseExtn(extn)
        extname = ('sipwcs',extnum[1])
        hdrlet = headerlet.createHeaderlet(fname,wcsname)
        # update hdrlet with header values from outwcs
        for kw in outwcs_hdr.items():
            hdrlet[extname].header[kw[0]] = kw[1]
        hdrlet[extname].header['WCSNAME'] = wcsname
    else:
        print('Creating headerlet from scratch...')
        hdrlet = fits.HDUList()
        hdrlet.append(fits.PrimaryHDU())
        siphdr = fits.ImageHDU(header=outwcs_hdr)
        siphdr.header['EXTNAME'] = 'SIPWCS'
        siphdr.header['WCSNAME'] = wcsname
        hdrlet.append(siphdr)

    # Write out header to a file as the final product
    if outname is not None:
        if outname.find('_hdr.fits') < 0:
            outname += '_hdr.fits'
        if os.path.exists(outname):
            print('Overwrite existing file "%s"'%outname)
            os.remove(outname)
        hdrlet.writeto(outname)
        print('Wrote out headerlet :',outname)


Instruction:
Here you have a function in Python 3, explain what it does
def qderiv(array): # TAKE THE ABSOLUTE DERIVATIVE OF A NUMARRY OBJECT

    #Create 2 empty arrays in memory of the same dimensions as 'array'
    tmpArray = np.zeros(array.shape,dtype=np.float64)
    outArray = np.zeros(array.shape, dtype=np.float64)

    # Get the length of an array side
    (naxis1,naxis2) = array.shape
    #print "The input image size is (",naxis1,",",naxis2,")."

#Main derivate loop:
    #Shift images +/- 1 in Y.
    for y in range(-1,2,2):
        if y == -1:
            #shift input image 1 pixel right
            tmpArray[0:(naxis1-1),1:(naxis2-1)] = array[0:(naxis1-1),0:(naxis2-2)]
            #print "Y shift = 1"
        else:
            #shift input image 1 pixel left
            tmpArray[0:(naxis1-1),0:(naxis2-2)] = array[0:(naxis1-1),1:(naxis2-1)]
            #print "Y shift = -1"
        #print "call _absoluteSubtract()"
        (tmpArray,outArray) = _absoluteSubtract(array,tmpArray,outArray)

    #Shift images +/- 1 in X.
    for x in range(-1,2,2):
        if x == -1:
            #shift input image 1 pixel right
            tmpArray[1:(naxis1-1),0:(naxis2-1)] = array[0:(naxis1-2),0:(naxis2-1)]
            #print "X shift = 1"
        else:
            #shift input image 1 pixel left
            tmpArray[0:(naxis1-2),0:(naxis2-1)] = array[1:(naxis1-1),0:(naxis2-1)]
            #print "X shift = -1"
        #print "call _absoluteSubtract()"
        (tmpArray,outArray) = _absoluteSubtract(array,tmpArray,outArray)

    return outArray.astype(np.float32)

Response: Take the absolute derivate of an image in memory.


Instruction:
Can you tell what is the following Python 3 function doing
def randomSelectFromCSV(tableName, numEntries, seedValue):

    # Initialize the random number generator
    seed(seedValue)

    # Get the contents of the table
    dataTable = Table.read(tableName, format='ascii.csv')
    numRows   = len(dataTable)

    # Generate a sequence of integers the size of the table, and then
    # obtain a random subset of the sequence with no duplicate selections
    sequence = list(range(numRows))
    subset   = sample(sequence, numEntries)

    # Extract the subset rows...
    outputTable = dataTable[subset]
    #outputTable = dataTable[0:numEntries]

    # Returns the outputTable which is an Astropy Table object
    return(outputTable)

Response: Function to extract random entries from a master CSV file


Instruction:
How would you explain what the following Python 3 function does
def get_hstwcs(filename,hdulist,extnum):
    hdrwcs = wcsutil.HSTWCS(hdulist,ext=extnum)
    hdrwcs.filename = filename
    hdrwcs.expname = hdulist[extnum].header['expname']
    hdrwcs.extver = hdulist[extnum].header['extver']

    return hdrwcs

Response: Return the HSTWCS object for a given chip.


Instruction:
Make a summary of the following Python 3 code
def update_linCD(cdmat, delta_rot=0.0, delta_scale=1.0, cx=[0.0,1.0], cy=[1.0,0.0]):
    rotmat = fileutil.buildRotMatrix(delta_rot)*delta_scale
    new_lincd = np.dot(cdmat,rotmat)

    cxymat = np.array([[cx[1],cx[0]],[cy[1],cy[0]]])
    new_cd = np.dot(new_lincd,cxymat)

    return new_cd

Response:
Modify an existing linear CD matrix with rotation and scale and scale changes
        and return a distorted linear CD matrix.


Instruction:
Can you write a function in Python 3 where it
creates a un - distorted CD matrix from the basic inputs.

Response:
def create_CD(orient, scale, cx=None, cy=None):
    """ Create a (un?)distorted CD matrix from the basic inputs.

    The 'cx' and 'cy' parameters, if given, provide the X and Y coefficients of
    the distortion as returned by reading the IDCTAB.  Only the first 2 elements
    are used and should correspond to the 'OC[X/Y]10' and 'OC[X/Y]11' terms in that
    order as read from the expanded SIP headers.

    The units of 'scale' should be 'arcseconds/pixel' of the reference pixel.
    The value of 'orient' should be the absolute orientation on the sky of the
    reference pixel.

    """
    cxymat = np.array([[cx[1],cx[0]],[cy[1],cy[0]]])
    rotmat = fileutil.buildRotMatrix(orient)*scale/3600.
    new_cd = np.dot(rotmat,cxymat)
    return new_cd


Instruction:
Explain what the following Python 3 code does
def ddtohms(xsky,ysky,verbose=False,precision=6):
    xskyh = xsky /15.
    xskym = (xskyh - np.floor(xskyh)) * 60.
    xskys = (xskym - np.floor(xskym)) * 60.
    yskym = (np.abs(ysky) - np.floor(np.abs(ysky))) * 60.
    yskys = (yskym - np.floor(yskym)) * 60.

    fmt = "%."+repr(precision)+"f"
    if isinstance(xskyh,np.ndarray):
        rah,dech = [],[]
        for i in range(len(xskyh)):
            rastr = repr(int(xskyh[i]))+':'+repr(int(xskym[i]))+':'+fmt%(xskys[i])
            decstr = repr(int(ysky[i]))+':'+repr(int(yskym[i]))+':'+fmt%(yskys[i])
            rah.append(rastr)
            dech.append(decstr)
            if verbose:
                print('RA = ',rastr,', Dec = ',decstr)
    else:
        rastr = repr(int(xskyh))+':'+repr(int(xskym))+':'+fmt%(xskys)
        decstr = repr(int(ysky))+':'+repr(int(yskym))+':'+fmt%(yskys)
        rah = rastr
        dech = decstr
        if verbose:
            print('RA = ',rastr,', Dec = ',decstr)

    return rah,dech

Response: Convert sky position from decimal degrees to HMS format.


Instruction:
Can you generate the documentation for the following Python 3 function
def make_outputwcs(imageObjectList, output, configObj=None, perfect=False):
    if not isinstance(imageObjectList,list):
        imageObjectList = [imageObjectList]

    # Compute default output WCS, replace later if user specifies a refimage
    hstwcs_list = []
    undistort=True
    for img in imageObjectList:
        chip_wcs = copy.deepcopy(img.getKeywordList('wcs'))
        # IF the user turned off use of coeffs (coeffs==False)
        if not configObj['coeffs']:
            for cw in chip_wcs:
                # Turn off distortion model for each input
                cw.sip = None
                cw.cpdis1 = None
                cw.cpdis2 = None
                cw.det2im = None
            undistort=False
        hstwcs_list += chip_wcs
    if not undistort and len(hstwcs_list) == 1:
        default_wcs = hstwcs_list[0].deepcopy()
    else:
        default_wcs = utils.output_wcs(hstwcs_list, undistort=undistort)

    if perfect:
        default_wcs.wcs.cd = make_perfect_cd(default_wcs)

    # Turn WCS instances into WCSObject instances
    outwcs = createWCSObject(output, default_wcs, imageObjectList)

    # Merge in user-specified attributes for the output WCS
    # as recorded in the input configObj object.
    final_pars = DEFAULT_WCS_PARS.copy()

    # More interpretation of the configObj needs to be done here to translate
    # the input parameter names to those understood by 'mergeWCS' as defined
    # by the DEFAULT_WCS_PARS dictionary.
    single_step = configObj[util.getSectionName(configObj, 3)]
    singleParDict = configObj[util.getSectionName(configObj, '3a')].copy()
    if single_step['driz_separate'] and singleParDict['driz_sep_wcs']:
        single_pars = DEFAULT_WCS_PARS.copy()
        del singleParDict['driz_sep_wcs']
        keyname = 'driz_sep_'
        for key in singleParDict:
            k = key[len(keyname):]
            if k != 'refimage':
                single_pars[k] = singleParDict[key]

        # Now, account for any user-specified reference image
        def_wcs = default_wcs.deepcopy()
        if singleParDict[keyname + 'refimage']:
            default_wcs = wcsutil.HSTWCS(singleParDict[keyname + 'refimage'])

        ### Create single_wcs instance based on user parameters
        outwcs.single_wcs = mergeWCS(default_wcs, single_pars)
        # restore global default WCS to original value so single_drizzle WCS does not
        # influence final_drizzle WCS
        default_wcs = def_wcs.deepcopy()

    final_step = configObj[util.getSectionName(configObj,7)]
    finalParDict = configObj[util.getSectionName(configObj,'7a')].copy()
    if final_step['driz_combine'] and finalParDict['final_wcs']:
        del finalParDict['final_wcs']
        keyname = 'final_'
        for key in finalParDict:
            k = key[len(keyname):]
            if k != 'refimage':
                final_pars[k] = finalParDict[key]

        # Now, account for any user-specified reference image
        if finalParDict[keyname + 'refimage']:
            rootname,extnum = fileutil.parseFilename(finalParDict[keyname+'refimage'])
            extnum = util.findWCSExtn(finalParDict[keyname+'refimage'])
            print('Creating OUTPUT WCS from {}[{}]'.format(rootname,extnum))
            default_wcs = wcsutil.HSTWCS('{}[{}]'.format(rootname,extnum))

        ### Create single_wcs instance based on user parameters
        outwcs.final_wcs = mergeWCS(default_wcs, final_pars)
        outwcs.wcs = outwcs.final_wcs.copy()

    # Apply user settings to create custom output_wcs instances
    # for each drizzle step
    updateImageWCS(imageObjectList, outwcs)

    return outwcs

Response: Creates a new output WCS for the input imageList and outputs the output frame.


Instruction:
Write a Python 3 function for
creating a perfect CD matrix from the WCS.

Response:
def make_perfect_cd(wcs):
    """ Create a perfect (square, orthogonal, undistorted) CD matrix from the
        input WCS.
    """
    def_scale = (wcs.pscale) / 3600.
    def_orientat = np.deg2rad(wcs.orientat)
    perfect_cd = def_scale * np.array(
        [[-np.cos(def_orientat),np.sin(def_orientat)],
         [np.sin(def_orientat),np.cos(def_orientat)]]
    )
    return perfect_cd


Instruction:
Explain what the following Python 3 code does
def calcNewEdges(wcs, shape):

    naxis1 = shape[1]
    naxis2 = shape[0]
    # build up arrays for pixel positions for the edges
    # These arrays need to be: array([(x,y),(x1,y1),...])
    numpix = naxis1*2 + naxis2*2
    border = np.zeros(shape=(numpix,2),dtype=np.float64)

    # Now determine the appropriate values for this array
    # We also need to account for any subarray offsets
    xmin = 1.
    xmax = naxis1
    ymin = 1.
    ymax = naxis2

    # Build range of pixel values for each side
    # Add 1 to make them consistent with pixel numbering in IRAF
    # Also include the LTV offsets to represent position in full chip
    #   since the model works relative to full chip positions.
    xside = np.arange(naxis1) + xmin
    yside = np.arange(naxis2) + ymin

    #Now apply them to the array to generate the appropriate tuples
    #bottom
    _range0 = 0
    _range1 = naxis1
    border[_range0:_range1,0] = xside
    border[_range0:_range1,1] = ymin
    #top
    _range0 = _range1
    _range1 = _range0 + naxis1
    border[_range0:_range1,0] = xside
    border[_range0:_range1,1] = ymax
    #left
    _range0 = _range1
    _range1 = _range0 + naxis2
    border[_range0:_range1,0] = xmin
    border[_range0:_range1,1] = yside
    #right
    _range0 = _range1
    _range1 = _range0 + naxis2
    border[_range0:_range1,0] = xmax
    border[_range0:_range1,1] = yside

    edges = wcs.all_pix2world(border[:,0],border[:,1],1)
    return edges

Response: This method calculates the sky coordinates for all the pixels around the image and the edge of the image.


Instruction:
Write a Python 3 function that can
convert a PyWCS WCS object into a WCSObject instance.

Response:
def createWCSObject(output,default_wcs,imageObjectList):
    """Converts a PyWCS WCS object into a WCSObject(baseImageObject) instance."""
    from . import imageObject
    outwcs = imageObject.WCSObject(output)
    outwcs.default_wcs = default_wcs
    outwcs.wcs = default_wcs.copy()
    outwcs.final_wcs = default_wcs.copy()
    outwcs.single_wcs = default_wcs.copy()

    outwcs.updateContextImage(imageObjectList[0].createContext)

    #
    # Add exptime information for use with drizzle
    #
    outwcs._exptime,outwcs._expstart,outwcs._expend = util.compute_texptime(imageObjectList)

    outwcs.nimages = util.countImages(imageObjectList)

    return outwcs


Instruction:
How would you implement a function in Python 3 that
removes all alternate WCS solutions from the header and removes all OPUS WCS Keywords from the header.

Response:
def removeAllAltWCS(hdulist,extlist):
    """
    Removes all alternate WCS solutions from the header
    """
    original_logging_level = log.level
    log.setLevel(logutil.logging.WARNING)

    try:
        hdr = hdulist[extlist[0]].header
        wkeys = altwcs.wcskeys(hdr)
        if ' ' in wkeys:
            wkeys.remove(' ')
        for extn in extlist:
            for wkey in wkeys:
                if wkey == 'O':
                    continue
                altwcs.deleteWCS(hdulist,extn,wkey)

            # Forcibly remove OPUS WCS Keywords, since deleteWCS will not do it
            hwcs = readAltWCS(hdulist,extn,wcskey='O')

            if hwcs is None:
                continue

            for k in hwcs.keys():
                if k not in ['DATE-OBS','MJD-OBS'] and k in hdr:
                    try:
                        del hdr[k]
                    except KeyError:
                        pass
    except:
        raise

    finally:
        log.setLevel(original_logging_level)


Instruction:
Can you write a function in Python 3 where it
restores WCS information to default values and update imageObject accordingly.

Response:
def restoreDefaultWCS(imageObjectList, output_wcs):
    """ Restore WCS information to default values, and update imageObject
        accordingly.
    """
    if not isinstance(imageObjectList,list):
        imageObjectList = [imageObjectList]

    output_wcs.restoreWCS()

    updateImageWCS(imageObjectList, output_wcs)


Instruction:
Can you generate the documentation for the following Python 3 function
def _py2round(x):
    if hasattr(x, '__iter__'):
        rx = np.empty_like(x)
        m = x >= 0.0
        rx[m] = np.floor(x[m] + 0.5)
        m = np.logical_not(m)
        rx[m] = np.ceil(x[m] - 0.5)
        return rx

    else:
        if x >= 0.0:
            return np.floor(x + 0.5)
        else:
            return np.ceil(x - 0.5)

Response: This function returns a rounded up value of the argument similar to Python 2.


Instruction:
Here you have a function in Python 3, explain what it does
def mergeWCS(default_wcs, user_pars):
    #
    # Start by making a copy of the input WCS...
    #
    outwcs = default_wcs.deepcopy()

    # If there are no user set parameters, just return a copy of
    # the original WCS:
    if all([upar is None for upar in user_pars.values()]):
        return outwcs

    if _check_custom_WCS_pars('ra', 'dec', user_pars):
        _crval = (user_pars['ra'], user_pars['dec'])
    else:
        _crval = None

    if ('scale' in user_pars and user_pars['scale'] is not None and
        not _check_close_scale(user_pars['scale'], outwcs.pscale)):
        _scale = user_pars['scale']
        _ratio = outwcs.pscale / _scale
    else:
        _ratio = None
        _scale = None

    if ('rot' not in user_pars) or user_pars['rot'] is None:
        _delta_rot = None
    else:
        _delta_rot = outwcs.orientat - user_pars['rot']
        if _delta_rot == 0.0:
            _delta_rot = None

    if _check_custom_WCS_pars('crpix1', 'crpix2', user_pars):
        _crpix = (user_pars['crpix1'], user_pars['crpix2'])
    else:
        _crpix = None

    shape = None

    if _check_custom_WCS_pars('outnx', 'outny', user_pars):
        shape = (
            int(_py2round(user_pars['outnx'])),
            int(_py2round(user_pars['outny']))
        )

        if shape[0] < 1 or shape[1] < 1:
            raise ValueError("Custom WCS output image size smaller than 1")

        if _crpix is None:
            # make sure new image is centered on the CRPIX of the old WCS:
            _crpix = ((shape[0] + 1.0) / 2.0, (shape[1] + 1.0) / 2.0)

    else:
        naxis1, naxis2 = outwcs.pixel_shape
        if _delta_rot is None:
            # no rotation is involved

            if _ratio is not None:
                # apply scale only:

                # compute output image shape:
                shape = (
                    max(1, int(_py2round(_ratio * naxis1))),
                    max(1, int(_py2round(_ratio * naxis2)))
                )

                # update CRPIX:
                if _crpix is None:
                    _crpix = 1.0 + _ratio * (outwcs.wcs.crpix - 1.0)

        else:
            _corners = np.array(
                [[0.5, 0.5],
                 [naxis1 + 0.5, 0.5],
                 [0.5, naxis2 + 0.5],
                 [naxis1 + 0.5, naxis2 + 0.5]]
            ) - outwcs.wcs.crpix

            if _ratio is not None:
                # scale corners:
                _corners *= _ratio

            # rotate corners and find new image range:
            ((_xmin, _xmax), (_ymin, _ymax)) = util.getRotatedSize(_corners,
                                                                   _delta_rot)

            # compute output image shape:
            # NOTE: _py2round may be replaced with np.ceil
            shape = (
                max(1, int(_py2round(_xmax - _xmin))),
                max(1, int(_py2round(_ymax - _ymin)))
            )

            if _crpix is None:
                # update CRPIX:
                _crpix = (-_xmin + 0.5, -_ymin + 0.5)

    # Set up the new WCS based on values from old one:

    if _ratio is not None:
        # Update plate scale
        outwcs.wcs.cd = outwcs.wcs.cd / _ratio
        outwcs.pscale = _scale

    # update orientation
    if _delta_rot is not None:
        outwcs.wcs.cd = _rotateCD(outwcs.wcs.cd, _delta_rot)
        outwcs.orientat -= _delta_rot

    if shape is not None:
        # update size:
        outwcs.pixel_shape = shape

    # update reference position
    if _crpix is not None:
        outwcs.wcs.crpix = np.array(_crpix, dtype=np.float64)

    if _crval is not None:
        outwcs.wcs.crval = np.array(_crval, dtype=np.float64)

    return outwcs

Response: Returns a copy of the input WCS with the user specified values given as dictionary derived from the input configObj object and the output PyWCS object computed from distortion. output_wcs.


Instruction:
Given the following Python 3 function, write the documentation
def convertWCS(inwcs,drizwcs):
    drizwcs[0] = inwcs.crpix[0]
    drizwcs[1] = inwcs.crval[0]
    drizwcs[2] = inwcs.crpix[1]
    drizwcs[3] = inwcs.crval[1]
    drizwcs[4] = inwcs.cd[0][0]
    drizwcs[5] = inwcs.cd[1][0]
    drizwcs[6] = inwcs.cd[0][1]
    drizwcs[7] = inwcs.cd[1][1]

    return drizwcs

Response: Copy WCSObject WCS into Drizzle compatible array.


Instruction:
Implement a function in Python 3 to
copy output WCS array from Drizzle into WCSObject.

Response:
def updateWCS(drizwcs,inwcs):
    """ Copy output WCS array from Drizzle into WCSObject."""
    crpix = np.array([drizwcs[0],drizwcs[2]], dtype=np.float64)
    crval = np.array([drizwcs[1],drizwcs[3]], dtype=np.float64)
    cd = np.array([[drizwcs[4],drizwcs[6]],[drizwcs[5],drizwcs[7]]], dtype=np.float64)
    inwcs.cd = cd
    inwcs.crval = crval
    inwc.crpix = crpix
    inwcs.pscale = N.sqrt(N.power(inwcs.cd[0][0],2)+N.power(inwcs.cd[1][0],2)) * 3600.
    inwcs.orient = N.arctan2(inwcs.cd[0][1],inwcs.cd[1][1]) * 180./N.pi


Instruction:
Here you have a function in Python 3, explain what it does
def wcsfit(img_wcs, ref_wcs):
    # Define objects that we need to use for the fit...
    #in_refpix = img_geom.model.refpix
    wmap = WCSMap(img_wcs,ref_wcs)
    cx, cy = coeff_converter.sip2idc(img_wcs)
    # Convert the RA/Dec positions back to X/Y in output product image
    #_cpix_xyref = np.zeros((4,2),dtype=np.float64)

    # Start by setting up an array of points +/-0.5 pixels around CRVAL1,2
    # However, we must shift these positions by 1.0pix to match what
    # drizzle will use as its reference position for 'align=center'.
    _cpix = (img_wcs.wcs.crpix[0],img_wcs.wcs.crpix[1])
    _cpix_arr = np.array([_cpix,(_cpix[0],_cpix[1]+1.),
                       (_cpix[0]+1.,_cpix[1]+1.),(_cpix[0]+1.,_cpix[1])], dtype=np.float64)
    # Convert these positions to RA/Dec
    _cpix_rd = wmap.xy2rd(img_wcs,_cpix_arr[:,0],_cpix_arr[:,1])
    #for pix in xrange(len(_cpix_rd[0])):
    _cpix_xref,_cpix_yref = wmap.rd2xy(ref_wcs,_cpix_rd[0],_cpix_rd[1])
    _cpix_xyref = np.zeros((4,2),dtype=np.float64)
    _cpix_xyref[:,0] = _cpix_xref
    _cpix_xyref[:,1] = _cpix_yref

    """
    # needed to handle correctly subarrays and wfpc2 data
    if img_wcs.delta_refx == 0.0 and img_wcs.delta_refy == 0.0:
        offx, offy = (0.0,0.0)
    else:
        offx, offy = (1.0, 1.0)
    """
    offx, offy = (0.0,0.0)

    # Now, apply distortion model to input image XY positions
    #_cpix_xyc = np.zeros((4,2),dtype=np.float64)
    _cpix_xyc = utils.apply_idc(_cpix_arr, cx, cy, img_wcs.wcs.crpix, img_wcs.pscale, order=1)

    # Need to get the XDELTA,YDELTA values included here in order to get this
    # to work with MDTng.
    #if in_refpix:
    #    _cpix_xyc += (in_refpix['XDELTA'], in_refpix['YDELTA'])

    # Perform a fit between:
    #       - undistorted, input positions: _cpix_xyc
    #       - X/Y positions in reference frame: _cpix_xyref
    abxt,cdyt = fitlin(_cpix_xyc,_cpix_xyref)

    # This correction affects the final fit when you are fitting
    # a WCS to itself (no distortion coeffs), so it needs to be
    # taken out in the coeffs file by modifying the zero-point value.
    #  WJH 17-Mar-2005
    abxt[2] -= ref_wcs.wcs.crpix[0] + offx
    cdyt[2] -= ref_wcs.wcs.crpix[1] + offy

    return abxt,cdyt

Response: This function computes a linear fit between 2 WCS objects and returns a new object for the image and ref_wcs.


Instruction:
Write a Python 3 script to
compute the least - squares fit between two arrays.

Response:
def fitlin(imgarr,refarr):
    """ Compute the least-squares fit between two arrays.
        A Python translation of 'FITLIN' from 'drutil.f' (Drizzle V2.9).
    """
    # Initialize variables
    _mat = np.zeros((3,3),dtype=np.float64)
    _xorg = imgarr[0][0]
    _yorg = imgarr[0][1]
    _xoorg = refarr[0][0]
    _yoorg = refarr[0][1]
    _sigxox = 0.
    _sigxoy = 0.
    _sigxo = 0.
    _sigyox = 0.
    _sigyoy = 0.
    _sigyo = 0.

    _npos = len(imgarr)
    # Populate matrices
    for i in range(_npos):
        _mat[0][0] += np.power((imgarr[i][0] - _xorg),2)
        _mat[0][1] += (imgarr[i][0] - _xorg) * (imgarr[i][1] - _yorg)
        _mat[0][2] += (imgarr[i][0] - _xorg)
        _mat[1][1] += np.power((imgarr[i][1] - _yorg),2)
        _mat[1][2] += imgarr[i][1] - _yorg

        _sigxox += (refarr[i][0] - _xoorg)*(imgarr[i][0] - _xorg)
        _sigxoy += (refarr[i][0] - _xoorg)*(imgarr[i][1] - _yorg)
        _sigxo += refarr[i][0] - _xoorg
        _sigyox += (refarr[i][1] - _yoorg)*(imgarr[i][0] -_xorg)
        _sigyoy += (refarr[i][1] - _yoorg)*(imgarr[i][1] - _yorg)
        _sigyo += refarr[i][1] - _yoorg

    _mat[2][2] = _npos
    _mat[1][0] = _mat[0][1]
    _mat[2][0] = _mat[0][2]
    _mat[2][1] = _mat[1][2]

    # Now invert this matrix
    _mat = linalg.inv(_mat)

    _a  = _sigxox*_mat[0][0]+_sigxoy*_mat[0][1]+_sigxo*_mat[0][2]
    _b  = -1*(_sigxox*_mat[1][0]+_sigxoy*_mat[1][1]+_sigxo*_mat[1][2])
    #_x0 = _sigxox*_mat[2][0]+_sigxoy*_mat[2][1]+_sigxo*_mat[2][2]

    _c  = _sigyox*_mat[1][0]+_sigyoy*_mat[1][1]+_sigyo*_mat[1][2]
    _d  = _sigyox*_mat[0][0]+_sigyoy*_mat[0][1]+_sigyo*_mat[0][2]
    #_y0 = _sigyox*_mat[2][0]+_sigyoy*_mat[2][1]+_sigyo*_mat[2][2]

    _xt = _xoorg - _a*_xorg+_b*_yorg
    _yt = _yoorg - _d*_xorg-_c*_yorg

    return [_a,_b,_xt],[_c,_d,_yt]


Instruction:
Create a Python 3 function for
performing a linear, orthogonal fit between matched lists of positions 'xy' (input) and 'uv' (output). Output: (same as for fit_arrays_general)

Response:
def fitlin_rscale(xy,uv,verbose=False):
    """ Performs a linear, orthogonal fit between matched
        lists of positions 'xy' (input) and 'uv' (output).

        Output: (same as for fit_arrays_general)
    """
    mu = uv[:,0].mean()
    mv = uv[:,1].mean()
    mx = xy[:,0].mean()
    my = xy[:,1].mean()

    u = uv[:,0] - mu
    v = uv[:,1] - mv
    x = xy[:,0] - mx
    y = xy[:,1] - my

    Sxx = np.dot(x,x)
    Syy = np.dot(y,y)
    Sux = np.dot(u,x)
    Suy = np.dot(u,y)
    Svx = np.dot(v,x)
    Svy = np.dot(v,y)

    # implement parity check
    if (np.dot(Sux,Svy) > 0):
        p = 1
    else:
        p = -1

    XX = p*Sux + Svy
    YY = Suy - p*Svx

    # derive output values
    theta_deg = np.rad2deg(np.arctan2(YY,XX))% 360.0
    scale = np.sqrt(XX**2 + YY**2) / (Sxx+Syy)
    shift = (mu-mx,mv-my)
    if verbose:
        print('Linear RSCALE fit: rotation = ',theta_deg,'  scale = ',scale,'  offset = ',shift)
    coeffs = scale * fileutil.buildRotMatrix(-theta_deg)

    P = [coeffs[0,0],coeffs[0,1],shift[0]]
    Q = [coeffs[1,1],coeffs[1,0],shift[1]]
    return P,Q


Instruction:
Can you generate the documentation for the following Python 3 function
def fitlin_clipped(xy,uv,verbose=False,mode='rscale',nclip=3,reject=3):
    fitting_funcs = {'rscale':fitlin_rscale,'general':fitlin}
    # Get the fitting function to be used
    fit_func = fitting_funcs[mode.lower()]
    # Perform the initial fit
    P,Q = fit_func(xy,uv)
    xyc = apply_fitlin(xy,P,Q)

    # compute residuals from fit for input positions
    dx = uv[:,0] - xyc[0]
    dy = uv[:,1] - xyc[1]
    fit_rms = [dx.std(),dy.std()]

    if nclip > 0:
        data = xy.copy()
        outdata = uv.copy()

    numclipped = 0
    for i in range(nclip):
        iterclipped = 0
        xyc = apply_fitlin(data,P,Q)

        # compute residuals from fit for input positions
        dx = outdata[:,0] - xyc[0]
        dy = outdata[:,1] - xyc[1]

        # find indices of outliers in x and y
        xout = np.where(np.abs(dx - dx.mean()) > reject*dx.std())
        yout = np.where(np.abs(dy - dy.mean()) > reject*dy.std())
        # concatenate those indices and sort them
        outliers_indx = xout[0].tolist()+yout[0].tolist()
        outliers_indx.sort()
        # define the full range of indices for the data points left
        full_indx = list(range(data.shape[0]))
        # remove all unique indices specified in outliers from full range
        for o in outliers_indx:
            # only remove if it has not been removed already
            # accounts for the same point being an outlier in both x and y
            if full_indx.count(o) > 0:
                full_indx.remove(o)
            iterclipped += 1

        if iterclipped == 0:
            break

        numclipped += iterclipped
        if verbose:
            print('Removed a total of ',numclipped,' points through iteration ',i+1)
        # create clipped data
        data_iter = np.zeros([len(full_indx),2],dtype=data.dtype)
        if verbose:
            print('Iter #',i+1,' data:',data.shape,data_iter.shape,len(full_indx))

        data_iter[:,0] = data[:,0][full_indx]
        data_iter[:,1] = data[:,1][full_indx]
        outdata_iter = np.zeros([len(full_indx),2],dtype=data.dtype)
        outdata_iter[:,0] = outdata[:,0][full_indx]
        outdata_iter[:,1] = outdata[:,1][full_indx]

        # perform the fit again with the clipped data and go to the next iteration
        data = data_iter
        outdata = outdata_iter
        P,Q = fit_func(data,outdata)
        # compute residuals from fit for input positions
        xyc = apply_fitlin(data,P,Q)
        dx = outdata[:,0] - xyc[0]
        dy = outdata[:,1] - xyc[1]
        fit_rms = [dx.std(),dy.std()]

    if verbose:
        print('Fit clipped ',numclipped,' points over ',nclip,' iterations.')
    return P,Q,fit_rms

Response: Fit a clipped version of the fitlin function to the input data and return the result of the fitlin function.


Instruction:
Can you create a Python 3 function that
reads in an alternate primary WCS from a file.

Response:
def readAltWCS(fobj, ext, wcskey=' ', verbose=False):
    """
    Reads in alternate primary WCS from specified extension.

    Parameters
    ----------
    fobj : str, `astropy.io.fits.HDUList`
        fits filename or fits file object
        containing alternate/primary WCS(s) to be converted
    wcskey : str
        [" ",A-Z]
        alternate/primary WCS key that will be replaced by the new key
    ext : int
        fits extension number
    Returns
    -------
    hdr: fits.Header
        header object with ONLY the keywords for specified alternate WCS
    """
    if isinstance(fobj, str):
        fobj = fits.open(fobj, memmap=False)

    hdr = altwcs._getheader(fobj, ext)
    try:
        original_logging_level = log.level
        log.setLevel(logutil.logging.WARNING)
        nwcs = pywcs.WCS(hdr, fobj=fobj, key=wcskey)

    except KeyError:
        if verbose:
            print('readAltWCS: Could not read WCS with key %s' % wcskey)
            print('            Skipping %s[%s]' % (fobj.filename(), str(ext)))
        return None

    finally:
        log.setLevel(original_logging_level) # restore original logging level

    hwcs = nwcs.to_header()

    if nwcs.wcs.has_cd():
        hwcs = altwcs.pc2cd(hwcs, key=wcskey)
    return hwcs


Instruction:
Here you have a function in Python 3, explain what it does
def forward(self,pixx,pixy):
        # This matches WTRAXY results to better than 1e-4 pixels.
        skyx,skyy = self.input.all_pix2world(pixx,pixy,self.origin)
        result= self.output.wcs_world2pix(skyx,skyy,self.origin)
        return result

Response: Transform the input pixx pixy positions in the input frame into the output frame.


Instruction:
Can you tell what is the following Python 3 function doing
def backward(self,pixx,pixy):
        skyx,skyy = self.output.wcs_pix2world(pixx,pixy,self.origin)
        result = self.input.all_world2pix(skyx,skyy,self.origin)
        return result

Response:
Transform pixx pixy positions from the output frame back onto their
            original positions in the input frame.


Instruction:
Write a Python 3 script for
constructing a filename for the given signature.

Response:
def constructFilename(signature):
    """Construct an output filename for the given signature::

         signature=[instr+detector,(nx,ny),detnum]

    The signature is in the image object.
    """
    suffix = buildSignatureKey(signature)
    filename = os.path.join('.', suffix)
    return filename


Instruction:
Explain what the following Python 3 code does
def addMember(self, imagePtr=None):
        numchips=imagePtr._numchips
        log.info("Computing static mask:\n")

        chips = imagePtr.group
        if chips is None:
            chips = imagePtr.getExtensions()

        #for chip in range(1,numchips+1,1):
        for chip in chips:
            chipid=imagePtr.scienceExt + ','+ str(chip)
            chipimage=imagePtr.getData(chipid)
            signature=imagePtr[chipid].signature

            # If this is a new signature, create a new Static Mask file which is empty
            # only create a new mask if one doesn't already exist
            if ((signature not in self.masklist) or (len(self.masklist) == 0)):
                self.masklist[signature] = self._buildMaskArray(signature)
                maskname =  constructFilename(signature)
                self.masknames[signature] = maskname
            else:
                chip_sig = buildSignatureKey(signature)
                for s in self.masknames:
                    if chip_sig in self.masknames[s]:
                        maskname  = self.masknames[s]
                        break
            imagePtr[chipid].outputNames['staticMask'] = maskname

            stats = ImageStats(chipimage,nclip=3,fields='mode')
            mode = stats.mode
            rms  = stats.stddev
            nbins = len(stats.histogram)
            del stats

            log.info('  mode = %9f;   rms = %7f;   static_sig = %0.2f' %
                     (mode, rms, self.static_sig))

            if nbins >= 2: # only combine data from new image if enough data to mask
                sky_rms_diff = mode - (self.static_sig*rms)
                np.bitwise_and(self.masklist[signature],
                               np.logical_not(np.less(chipimage, sky_rms_diff)),
                               self.masklist[signature])
            del chipimage

Response: Adds a member to the static mask list.


Instruction:
How would you explain what the following Python 3 function does
def getMaskArray(self, signature):
        if signature in self.masklist:
            mask =  self.masklist[signature]
        else:
            mask = None
        return mask

Response: Returns the appropriate StaticMask array for the image.


Instruction:
Implement a function in Python 3 to
return the name of the output mask file that contains the given signature.

Response:
def getFilename(self,signature):
        """Returns the name of the output mask file that
        should reside on disk for the given signature. """

        filename=constructFilename(signature)

        if(fileutil.checkFileExists(filename)):
            return filename
        else:
            print("\nmMask file for ", str(signature), " does not exist on disk", file=sys.stderr)
            return None


Instruction:
Write a Python 3 script for
deleting all static mask objects.

Response:
def close(self):
        """ Deletes all static mask objects. """

        for key in self.masklist.keys():
            self.masklist[key] = None
        self.masklist = {}


Instruction:
How would you implement a function in Python 3 that
deletes just the mask that matches the signature given.

Response:
def deleteMask(self,signature):
        """ Delete just the mask that matches the signature given."""
        if signature in self.masklist:
            self.masklist[signature] = None
        else:
            log.warning("No matching mask")


Instruction:
How would you code a function in Python 3 to
save the static mask to a file.

Response:
def saveToFile(self,imageObjectList):
        """ Saves the static mask to a file
            it uses the signatures associated with each
            mask to contruct the filename for the output mask image.
        """
        virtual = imageObjectList[0].inmemory

        for key in self.masklist.keys():
            #check to see if the file already exists on disk
            filename = self.masknames[key]
            #create a new fits image with the mask array and a standard header
            #open a new header and data unit
            newHDU = fits.PrimaryHDU()
            newHDU.data = self.masklist[key]

            if virtual:
                for img in imageObjectList:
                    img.saveVirtualOutputs({filename:newHDU})

            else:
                try:
                    newHDU.writeto(filename, overwrite=True)
                    log.info("Saving static mask to disk: %s" % filename)

                except IOError:
                    log.error("Problem saving static mask file: %s to "
                              "disk!\n" % filename)
                    raise IOError


Instruction:
Can you implement a function in Python 3 that
expands image from original shape to requested shape.

Response:
def expand_image(image, shape):
    """ Expand image from original shape to requested shape. Output shape
    must be an integer multiple of input image shape for each axis. """
    if (shape[0] % image.shape[0]) or (shape[1] % image.shape[1]):
        raise ValueError("Output shape must be an integer multiple of input "
                         "image shape.")
    sx = shape[1] // image.shape[1]
    sy = shape[0] // image.shape[0]

    ox = (sx - 1.0) / (2.0 * sx)
    oy = (sy - 1.0) / (2.0 * sy)

    # generate output coordinates:
    y, x = np.indices(shape, dtype=np.float)
    x = x / sx - ox
    y = y / sy - oy

    # interpolate:
    return bilinear_interp(image, x, y)


Instruction:
How would you code a function in Python 3 to
interpolate input ``data`` at "pixel" coordinates ``x`` and ``y``.

Response:
def bilinear_interp(data, x, y):
    """ Interpolate input ``data`` at "pixel" coordinates ``x`` and ``y``. """
    x = np.asarray(x)
    y = np.asarray(y)
    if x.shape != y.shape:
        raise ValueError("X- and Y-coordinates must have identical shapes.")

    out_shape = x.shape
    out_size = x.size
    x = x.ravel()
    y = y.ravel()

    x0 = np.empty(out_size, dtype=np.int)
    y0 = np.empty(out_size, dtype=np.int)
    np.clip(x, 0, data.shape[1] - 2, out=x0)
    np.clip(y, 0, data.shape[0] - 2, out=y0)
    x1 = x0 + 1
    y1 = y0 + 1

    f00 = data[(y0, x0)]
    f10 = data[(y1, x0)]
    f01 = data[(y0, x1)]
    f11 = data[(y1, x1)]

    w00 = (x1 - x) * (y1 - y)
    w10 = (x1 - x) * (y - y0)
    w01 = (x - x0) * (y1 - y)
    w11 = (x - x0) * (y - y0)

    interp = w00 * f00 + w10 * f10 + w01 * f01 + w11 * f11

    return interp.reshape(out_shape).astype(data.dtype.type)


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _assignSignature(self, chip):
        sci_chip = self._image[self.scienceExt,chip]
        ny=sci_chip._naxis1
        nx=sci_chip._naxis2
        detnum = sci_chip.detnum
        instr=self._instrument

        sig=(instr+self._detector,(nx,ny),int(detnum)) #signature is a tuple
        sci_chip.signature=sig

Response:
Assign a unique signature for the image based
           on the instrument detector chip and size


Instruction:
Given the following Python 3 function, write the documentation
def getReadNoise(self):
        if self.proc_unit == 'native':
            return self._rdnoise / self._gain()
        return self._rdnoise

Response: Method for returning the read noise of a specific detector class.


Instruction:
Given the following Python 3 function, write the documentation
def setInstrumentParameters(self, instrpars):
        pri_header = self._image[0].header

        if self._isNotValid (instrpars['gain'], instrpars['gnkeyword']):
            instrpars['gnkeyword'] = 'ATODGAIN'
        if self._isNotValid (instrpars['rdnoise'], instrpars['rnkeyword']):
            instrpars['rnkeyword'] = 'READNSE'
        if self._isNotValid (instrpars['exptime'], instrpars['expkeyword']):
            instrpars['expkeyword'] = 'EXPTIME'

        for chip in self.returnAllChips(extname=self.scienceExt):

            chip._gain      = self.getInstrParameter(instrpars['gain'], pri_header,
                                                     instrpars['gnkeyword'])
            chip._rdnoise   = self.getInstrParameter(instrpars['rdnoise'], pri_header,
                                                     instrpars['rnkeyword'])
            chip._exptime   = self.getInstrParameter(instrpars['exptime'], chip.header,
                                                     instrpars['expkeyword'])

            if chip._gain is None or chip._rdnoise is None or chip._exptime is None:
                print('ERROR: invalid instrument task parameter')
                raise ValueError

            chip._effGain = chip._gain
            self._assignSignature(chip._chip) #this is used in the static mask

        self.doUnitConversions()

Response: This method is used to set the default values into the parameter dictionary. It is used by the base class to set the default values for the instrument task.


Instruction:
Can you write a function in Python 3 where it
converts the data to electrons.

Response:
def doUnitConversions(self):
        """Convert the data to electrons.

        This converts all science data extensions and saves
        the results back to disk. We need to make sure
        the data inside the chips already in memory is altered as well.

        """
        for det in range(1,self._numchips+1,1):

            chip=self._image[self.scienceExt,det]

            conversionFactor = self.effGain
            chip._gain = self.effGain #1.
            chip.effGain = self.effGain
            chip._conversionFactor = conversionFactor


Instruction:
Implement a Python 3 function for
analyzing the data of the input file and returns a Astropy Table object containing the data that can be processed.

Response:
def analyze_data(inputFileList, **kwargs):
    """
    Determine if images within the dataset can be aligned

    Parameters
    ==========
    inputFileList: list
        List containing FLT and/or FLC filenames for all input images which comprise an associated
        dataset where 'associated dataset' may be a single image, multiple images, an HST association,
        or a number of HST associations

    Returns
    =======
    outputTable: object
        Astropy Table object containing data pertaining to the associated dataset, including
        the doProcess bool.  It is intended this table is updated by subsequent functions for
        bookkeeping purposes.

    Notes
    =====
    The keyword/value pairs below define the "cannot process categories".
    OBSTYPE : is not IMAGING
    MTFLAG : T
    SCAN-TYP : C or D (or !N)
    FILTER : G*, *POL*, *PRISM*
    FILTER1 : G*, *POL*, *PRISM*
    FILTER2 : G*, *POL*, *PRISM*
    APERTURE : *GRISM*, G*-REF, RAMP, *POL*, *PRISM*
    TARGNAME : DARK, TUNGSTEN, BIAS, FLAT, EARTH-CALIB, DEUTERIUM
    EXPTIME : 0
    CHINJECT : is not NONE

    The keyword/value pairs below define the category which the data can be processed, but
    the results may be compromised
    FGSLOCK : FINE/GYRO, FINE/GY, COARSE, GYROS

    FITS Keywords only for WFC3 data: SCAN_TYP, FILTER, and CHINJECT (UVIS)
    FITS Keywords only for ACS data: FILTER1 and FILTER2

    Please be aware of the FITS keyword value NONE vs the Python None.
    FIX: improve robustness when analyzing filter and aperture names, possibly use PHOTMODE instead
    """
    OBSKEY = 'OBSTYPE'
    MTKEY  = 'MTFLAG'
    SCNKEY = 'SCAN_TYP'
    FILKEY = 'FILTER'
    FILKEY1 = 'FILTER1'
    FILKEY2 = 'FILTER2'
    APKEY  = 'APERTURE'
    TARKEY = 'TARGNAME'
    EXPKEY = 'EXPTIME'
    FGSKEY = 'FGSLOCK'
    CHINKEY = 'CHINJECT'

    acsFiltNameList = [FILKEY1, FILKEY2]

    catalog = None     # Astrometric catalog used for alignment
    catalogSources = 0 # Number of astrometric catalog sources determined based upon coordinate overlap with image WCS
    foundSources = 0   # Number of sources detected in images
    matchSources = 0   # Number of sources cross matched between astrometric catalog and detected in image
    offset_x = None
    offset_y = None
    rot = None
    scale = None
    rms_x = -1.0
    rms_y = -1.0
    rms_ra = -1.0
    rms_dec = -1.0
    chisq_x = -1.0
    chisq_y = -1.0
    completed = False # If true, there was no exception and the processing completed all logic
    dateObs = None     # Human readable date
    mjdutc = -1.0      # MJD UTC start of exposure
    fgslock = None
    processMsg = None
    status = 9999
    compromised = 0
    headerletFile = None
    fit_qual = -1

    fit_rms = -1.0
    total_rms = -1.0
    datasetKey = -1.0

    namesArray = ('imageName', 'instrument', 'detector', 'filter', 'aperture', 'obstype',
            'subarray', 'dateObs', 'mjdutc', 'doProcess', 'processMsg', 'catalog', 'foundSources',
            'catalogSources','matchSources',
            'offset_x', 'offset_y', 'rotation','scale',
            'rms_x', 'rms_y', 'rms_ra', 'rms_dec', 'completed',
            'fit_rms', 'total_rms', 'datasetKey', 'status', 'fit_qual', 'headerletFile')
    dataType = ('S20', 'S20', 'S20', 'S20', 'S20', 'S20', 'b', 'S20', 'f8', 'b', 'S30',
            'S20', 'i4', 'i4', 'i4', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8', 'f8',
            'b', 'f8', 'f8', 'i8', 'i4', 'i4', 'S30')

    # Create an astropy table
    outputTable = Table(names=namesArray,dtype=dataType)

    # Loop over the list of images to determine viability for alignment processing
    #
    # Capture the data characteristics before any evaluation so the information is
    # available for the output table regardless of which keyword is used to
    # to determine the data is not viable for alignment.

    for inputFile in inputFileList:

        header_hdu  = 0
        header_data = getheader(inputFile, header_hdu)

        # Keywords to use potentially for downstream analysis
        instrume = (header_data['INSTRUME']).upper()
        detector = (header_data['DETECTOR']).upper()
        subarray = header_data['SUBARRAY']
        dateObs  = header_data['DATE-OBS']
        mjdutc  = header_data['EXPSTART']

        # Obtain keyword values for analysis of viability
        obstype  = (header_data[OBSKEY]).upper()
        mtflag   = (header_data[MTKEY]).upper()
        scan_typ = ''
        if instrume == 'WFC3':
            scan_typ = (header_data[SCNKEY]).upper()

        sfilter = ''
        if instrume == 'WFC3':
            sfilter  = (header_data[FILKEY]).upper()
        # Concatenate the two ACS filter names together with an underscore
        # If the filter name is blank, skip it
        if instrume == 'ACS':
            for filtname in acsFiltNameList:

                # The filter keyword value could be zero or more blank spaces
                # Strip off any leading or trailing blanks
                if len(header_data[filtname].upper().strip()) > 0:

                    # If the current filter variable already has some content,
                    # need to append an underscore before adding more text
                    if len(sfilter) > 0:
                        sfilter += '_'
                    sfilter += header_data[filtname].upper().strip()

        aperture = (header_data[APKEY]).upper()
        targname = (header_data[TARKEY]).upper()
        exptime = header_data[EXPKEY]
        fgslock = (header_data[FGSKEY]).upper()

        chinject = 'NONE'
        if instrume == 'WFC3' and detector == 'UVIS':
            chinject = (header_data[CHINKEY]).upper()

        # Determine if the image has one of these conditions.  The routine
        # will exit processing upon the first satisfied condition.

        noProcKey   = None
        noProcValue = None
        doProcess = True
        # Imaging vs spectroscopic or coronagraphic
        if obstype != 'IMAGING':
            noProcKey   = OBSKEY
            noProcValue = obstype

        # Moving target
        elif mtflag == 'T':
            noProcKey   = MTKEY
            noProcValue = mtflag

        # Bostrophidon without or with dwell (WFC3 only)
        elif any ([scan_typ == 'C', scan_typ == 'D']):
            noProcKey   = SCNKEY
            noProcValue = scan_typ

        # Ramp, polarizer, grism, or prism
        elif any (x in aperture for x in ['RAMP', 'POL', 'GRISM', '-REF', 'PRISM']):
            noProcKey   = APKEY
            noProcValue = aperture

        # Calibration target
        elif any (x in targname for x in ['DARK', 'TUNG', 'BIAS', 'FLAT', 'DEUT', 'EARTH-CAL']):
            noProcKey   = TARKEY
            noProcValue = targname

        # Exposure time of effectively zero
        elif math.isclose(exptime, 0.0, abs_tol=1e-5):
            noProcKey   = EXPKEY
            noProcValue = exptime

        # Commanded FGS lock
        elif any (x in fgslock for x in ['GY', 'COARSE']):
            noProcKey   = FGSKEY
            noProcValue = fgslock

        # Charge injection mode
        elif chinject != 'NONE':
            noProcKey   = CHINKEY
            noProcValue = chinject

        # Filter which does not begin with: 'F'(F###), 'C'(CLEAR), 'N'(N/A), and is not blank
        # The sfilter variable may be the concatenation of two filters (F160_CLEAR)
        elif sfilter and not sfilter.startswith(('F', 'C', 'N')):
            noProcKey   = FILKEY
            noProcValue = sfilter

        elif '_' in sfilter:
            pos = sfilter.index('_')
            pos += 1

            if sfilter[pos] != 'F' and sfilter[pos] != '' and sfilter[pos] != 'C' and sfilter[pos] != 'N':
                noProcKey   = FILKEY
                noProcValue = sfilter

        # If noProcKey is set to a keyword, then this image has been found to not be viable for
        # alignment purposes.
        if (noProcKey is not None):
            if (noProcKey != FGSKEY):
                doProcess = False
                msgType = Messages.NOPROC.value
            else:
                msgType = Messages.WARN.value

            processMsg = noProcKey + '=' + str(noProcValue)

            # Issue message to log file for this data indicating no processing to be done or
            # processing should be allowed, but there may be some issue with the result (e.g.,
            # GYROS mode so some drift)
            generate_msg(inputFile, msgType, noProcKey, noProcValue)

        # Populate a row of the table
        outputTable.add_row([inputFile, instrume, detector, sfilter, aperture, obstype,
                             subarray, dateObs, mjdutc, doProcess, processMsg, catalog,
                             foundSources, catalogSources, matchSources,
                             offset_x, offset_y, rot, scale, rms_x, rms_y,
                             rms_ra, rms_dec, completed, fit_rms, total_rms, datasetKey,
                             status, fit_qual, headerletFile])
        processMsg = None
    #outputTable.pprint(max_width=-1)

    return(outputTable)


Instruction:
Can you generate a brief explanation for the following Python 3 code
def generate_msg(filename, msg, key, value):

    log.info('Dataset ' + filename + ' has (keyword = value) of (' + key + ' = ' + str(value) + ').')
    if msg == Messages.NOPROC.value:
        log.info('Dataset cannot be aligned.')
    else:
        log.info('Dataset can be aligned, but the result may be compromised.')

Response: Generate a message for the output log indicating the file and association is consistent with the alignment of the data.


Instruction:
Can you tell what is the following Python 3 function doing
def drizCR(input=None, configObj=None, editpars=False, **inputDict):
    log.debug(inputDict)
    inputDict["input"] = input
    configObj = util.getDefaultConfigObj(__taskname__, configObj, inputDict,
                                         loadOnly=(not editpars))
    if configObj is None:
        return

    if not editpars:
        run(configObj)

Response: Look for cosmic rays.


Instruction:
Explain what the following Python 3 code does
def _driz_cr(sciImage, virtual_outputs, paramDict):
    grow = paramDict["driz_cr_grow"]
    ctegrow = paramDict["driz_cr_ctegrow"]
    crcorr_list = []
    cr_mask_dict = {}

    for chip in range(1, sciImage._numchips + 1, 1):
        exten = sciImage.scienceExt + ',' + str(chip)
        sci_chip = sciImage[exten]

        if not sci_chip.group_member:
            continue

        blot_image_name = sci_chip.outputNames['blotImage']

        if sciImage.inmemory:
            blot_data = sciImage.virtualOutputs[blot_image_name][0].data
        else:
            if not os.path.isfile(blot_image_name):
                raise IOError("Blotted image not found: {:s}"
                              .format(blot_image_name))

            try:
                blot_data = fits.getdata(blot_image_name, ext=0)
            except IOError:
                print("Problem opening blot images")
                raise
        # Scale blot image, as needed, to match original input data units.
        blot_data *= sci_chip._conversionFactor

        input_image = sciImage.getData(exten)

        # Apply any unit conversions to input image here for comparison
        # with blotted image in units of electrons
        input_image *= sci_chip._conversionFactor

        # make the derivative blot image
        blot_deriv = quickDeriv.qderiv(blot_data)

        # Boolean mask needs to take into account any crbits values
        # specified by the user to be ignored when converting DQ array.
        dq_mask = sciImage.buildMask(chip, paramDict['crbit'])

        # parse out the SNR information
        snr1, snr2 = map(
            float, filter(None, re.split("[,;\s]+", paramDict["driz_cr_snr"]))
        )

        # parse out the scaling information
        mult1, mult2 = map(
            float, filter(
                None, re.split("[,;\s]+", paramDict["driz_cr_scale"])
            )
        )

        gain = sci_chip._effGain
        rn = sci_chip._rdnoise
        backg = sci_chip.subtractedSky * sci_chip._conversionFactor

        # Set scaling factor (used by MultiDrizzle) to 1 since scaling has
        # already been accounted for in blotted image
        # expmult = 1.

        # #################   COMPUTATION PART I    ###################
        # Create a temporary array mask
        t1 = np.absolute(input_image - blot_data)
        # ta = np.sqrt(gain * np.abs((blot_data + backg) * expmult) + rn**2)
        ta = np.sqrt(gain * np.abs(blot_data + backg) + rn**2)
        t2 = (mult1 * blot_deriv + snr1 * ta / gain)  # / expmult
        tmp1 = t1 <= t2

        # Create a convolution kernel that is 3 x 3 of 1's
        kernel = np.ones((3, 3), dtype=np.uint16)
        # Convolve the mask with the kernel
        tmp2 = signal.convolve2d(tmp1, kernel, boundary='symm', mode='same')

        # #################   COMPUTATION PART II    ###################
        # Create the CR Mask
        t2 = (mult2 * blot_deriv + snr2 * ta / gain)  # / expmult
        cr_mask = (t1 <= t2) | (tmp2 >= 9)

        # #################   COMPUTATION PART III    ##################
        # flag additional cte 'radial' and 'tail' pixels surrounding CR pixels
        # as CRs

        # In both the 'radial' and 'length' kernels below, 0->good and 1->bad,
        # so that upon convolving the kernels with cr_mask, the convolution
        # output will have low->bad and high->good from which 2 new arrays are
        # created having 0->bad and 1->good. These 2 new arrays are then
        # 'anded' to create a new cr_mask.

        # make radial convolution kernel and convolve it with original cr_mask
        cr_grow_kernel = np.ones((grow, grow), dtype=np.uint16)
        cr_grow_kernel_conv = signal.convolve2d(
            cr_mask, cr_grow_kernel, boundary='symm', mode='same'
        )

        # make tail convolution kernel and convolve it with original cr_mask
        cr_ctegrow_kernel = np.zeros((2 * ctegrow + 1, 2 * ctegrow + 1))

        # which pixels are masked by tail kernel depends on sign of
        # sci_chip.cte_dir (i.e.,readout direction):
        if sci_chip.cte_dir == 1:
            # 'positive' direction:  HRC: amp C or D; WFC: chip = sci,1; WFPC2
            cr_ctegrow_kernel[0:ctegrow, ctegrow] = 1
        elif sci_chip.cte_dir == -1:
            # 'negative' direction:  HRC: amp A or B; WFC: chip = sci,2
            cr_ctegrow_kernel[ctegrow+1:2*ctegrow+1, ctegrow] = 1

        # do the convolution
        cr_ctegrow_kernel_conv = signal.convolve2d(
            cr_mask, cr_ctegrow_kernel, boundary='symm', mode='same'
        )

        # select high pixels from both convolution outputs;
        # then 'and' them to create new cr_mask
        cr_grow_mask = cr_grow_kernel_conv >= grow**2  # radial
        cr_ctegrow_mask = cr_ctegrow_kernel_conv >= ctegrow  # length
        cr_mask = cr_grow_mask & cr_ctegrow_mask

        # Apply CR mask to the DQ array in place
        dq_mask &= cr_mask

        # Create the corr file
        corrFile = np.where(dq_mask, input_image, blot_data)
        corrFile /= sci_chip._conversionFactor
        corrDQMask = np.where(dq_mask, 0, paramDict['crbit']).astype(np.uint16)

        if paramDict['driz_cr_corr']:
            crcorr_list.append({
                'sciext': fileutil.parseExtn(exten),
                'corrFile': corrFile.copy(),
                'dqext': fileutil.parseExtn(sci_chip.dq_extn),
                'dqMask': corrDQMask
            })

        # Save the cosmic ray mask file to disk
        cr_mask_image = sci_chip.outputNames["crmaskImage"]
        if paramDict['inmemory']:
            print('Creating in-memory(virtual) FITS file...')
            _pf = util.createFile(cr_mask.astype(np.uint8),
                                  outfile=None, header=None)
            cr_mask_dict[cr_mask_image] = _pf
            sciImage.saveVirtualOutputs(cr_mask_dict)

        else:
            # Always write out crmaskimage, as it is required input for
            # the final drizzle step. The final drizzle step combines this
            # image with the DQ information on-the-fly.
            #
            # Remove the existing mask file if it exists
            if os.path.isfile(cr_mask_image):
                os.remove(cr_mask_image)
                print("Removed old cosmic ray mask file: '{:s}'"
                      .format(cr_mask_image))
            print("Creating output: {:s}".format(cr_mask_image))
            util.createFile(cr_mask.astype(np.uint8),
                            outfile=cr_mask_image, header=None)

    if paramDict['driz_cr_corr']:
        createCorrFile(sciImage.outputNames["crcorImage"], crcorr_list,
                       sciImage._filename)

Response: This function is used to generate a Cosmic Ray mask for a given image and virtual_outputs.


Instruction:
Can you create a Python 3 function that
creates a _cor file with the same format as the original input image.

Response:
def createCorrFile(outfile, arrlist, template):
    """
    Create a _cor file with the same format as the original input image.

    The DQ array will be replaced with the mask array used to create the _cor
    file.
    """
    # Remove the existing cor file if it exists
    if os.path.isfile(outfile):
        os.remove(outfile)
        print("Removing old corr file: '{:s}'".format(outfile))

    with fits.open(template, memmap=False) as ftemplate:
        for arr in arrlist:
            ftemplate[arr['sciext']].data = arr['corrFile']
            if arr['dqext'][0] != arr['sciext'][0]:
                ftemplate[arr['dqext']].data = arr['dqMask']
        ftemplate.writeto(outfile)
        print("Created CR corrected file: '{:s}'".format(outfile))


Instruction:
Can you create a Python 3 function that
returns a dictionary of the default parameters for the current system.

Response:
def setDefaults(configObj={}):
    """ Return a dictionary of the default parameters
        which also been updated with the user overrides.
    """
    paramDict = {
        'gain': 7,           # Detector gain, e-/ADU
        'grow': 1,           # Radius around CR pixel to mask [default=1 for
                             #     3x3 for non-NICMOS]
        'ctegrow': 0,        # Length of CTE correction to be applied
        'rn': 5,             # Read noise in electrons
        'snr': '4.0 3.0',    # Signal-to-noise ratio
        'scale': '0.5 0.4',  # scaling factor applied to the derivative
        'backg': 0,          # Background value
        'expkey': 'exptime'  # exposure time keyword
    }

    if len(configObj) > 0:
        for key in configObj:
            paramDict[key] = configObj[key]

    return paramDict


Instruction:
Can you create a Python 3 function that
prints out syntax help for running astrodrizzle.

Response:
def help(file=None):
    """
    Print out syntax help for running ``astrodrizzle``

    Parameters
    ----------
    file : str (Default = None)
        If given, write out help to the filename specified by this parameter
        Any previously existing file with this name will be deleted before
        writing out the help.

    """
    helpstr = getHelpAsString(docstring=True, show_ver=True)
    if file is None:
        print(helpstr)
    else:
        with open(file, mode='w') as f:
            f.write(helpstr)


Instruction:
Explain what the following Python 3 code does
def getHelpAsString(docstring=False, show_ver=True):
    install_dir = os.path.dirname(__file__)
    taskname = util.base_taskname(__taskname__, __package__)
    htmlfile = os.path.join(install_dir, 'htmlhelp', taskname + '.html')
    helpfile = os.path.join(install_dir, taskname + '.help')

    if docstring or (not docstring and not os.path.exists(htmlfile)):
        if show_ver:
            helpString = "\n{:s} Version {:s} updated on {:s}\n\n".format(
                __taskname__, __version__, __version_date__
            )
        else:
            helpString = ''

        if os.path.exists(helpfile):
            helpString += teal.getHelpFileAsString(taskname, __file__)
        elif __doc__ is not None:
                helpString += __doc__ + os.linesep

    else:
        helpString = 'file://' + htmlfile

    return helpString

Response: Return useful help from a file in the script directory called


Instruction:
Can you create a Python 3 function that
prints out a report of how much memory is needed for this run.

Response:
def reportResourceUsage(imageObjectList, outwcs, num_cores,
                        interactive=False):
    """ Provide some information to the user on the estimated resource
    usage (primarily memory) for this run.
    """

    from . import imageObject
    if outwcs is None:
        output_mem = 0
    else:
        if isinstance(outwcs,imageObject.WCSObject):
            owcs = outwcs.final_wcs
        else:
            owcs = outwcs
        output_mem = np.prod(owcs.pixel_shape) * 4 * 3  # bytes used for output arrays
    img1 = imageObjectList[0]
    numchips = 0
    input_mem = 0
    for img in imageObjectList:
        numchips += img._nmembers # account for group parameter set by user

    # if we have the cpus and s/w, ok, but still allow user to set pool size
    pool_size = util.get_pool_size(num_cores, None)
    pool_size = pool_size if (numchips >= pool_size) else numchips

    inimg = 0
    chip_mem = 0
    for img in imageObjectList:
        for chip in range(1,img._numchips+1):
            cmem = img[chip].shape[0]*img[chip].shape[1]*4
            inimg += 1
            if inimg < pool_size:
                input_mem += cmem*2
            if chip_mem == 0:
                chip_mem = cmem
    max_mem = (input_mem + output_mem*pool_size + chip_mem*2)//(1024*1024)

    print('*'*80)
    print('*')
    print('*  Estimated memory usage:  up to %d Mb.'%(max_mem))
    print('*  Output image size:       {:d} X {:d} pixels. '.format(*owcs.pixel_shape))
    print('*  Output image file:       ~ %d Mb. '%(output_mem//(1024*1024)))
    print('*  Cores available:         %d'%(pool_size))
    print('*')
    print('*'*80)

    if interactive:
        print('Continue with processing?')
        while True:
            if sys.version_info[0] >= 3:
                k = input("(y)es or (n)o").strip()[0].lower()
            else:
                k = raw_input("(y)es or (n)o").strip()[0].lower()

            if k not in ['n', 'y']:
                continue

            if k == 'n':
                raise KeyboardInterrupt("Execution aborted")


Instruction:
Can you generate the documentation for the following Python 3 function
def addIVMInputs(imageObjectList,ivmlist):
    if ivmlist is None:
        return

    for img,ivmname in zip(imageObjectList,ivmlist):
        img.updateIVMName(ivmname)

Response: Add IVM filenames provided by user to outputNames dictionary for each input imageObject.


Instruction:
Can you create a Python 3 function that
evaluates the input to determine whether there is 1 or more than 1 valid input file.

Response:
def checkMultipleFiles(input):
    """ Evaluates the input to determine whether there is 1 or more than 1 valid input file.
    """
    f,i,o,a=buildFileList(input)
    return len(f) > 1


Instruction:
Create a Python 3 function to
return a list of imageObject instances 1 for each input image in the list of input filenames.

Response:
def createImageObjectList(files,instrpars,group=None,
                            undistort=True, inmemory=False):
    """ Returns a list of imageObject instances, 1 for each input image in the list of input filenames.
    """
    imageObjList = []
    mtflag = False
    mt_refimg = None
    for img in files:
        image = _getInputImage(img,group=group)
        image.setInstrumentParameters(instrpars)
        image.compute_wcslin(undistort=undistort)
        if 'MTFLAG' in image._image['PRIMARY'].header:
            # check to see whether we are dealing with moving target observations...
            _keyval = image._image['PRIMARY'].header['MTFLAG']
            if not util.is_blank(_keyval):
                if isinstance(_keyval,bool):
                    mtflag = _keyval
                else:
                    if 'T' in _keyval:
                        mtflag = True
                    else:
                        mtflag = False
        else:
            mtflag = False

        if mtflag:
            print("#####\nProcessing Moving Target Observations using reference image as WCS for all inputs!\n#####\n")
            if mt_refimg is None:
                mt_refimg = image
            else:
                image.set_mt_wcs(mt_refimg)
        image.inmemory = inmemory # set flag for inmemory processing
        # Now add (possibly updated) image object to list
        imageObjList.append(image)
    return imageObjList


Instruction:
Explain what the following Python 3 code does
def processFilenames(input=None,output=None,infilesOnly=False):
    ivmlist = None
    oldasndict = None

    if input is None:
        print("No input files provided to processInput")
        raise ValueError

    if not isinstance(input, list) and ('_asn' in input or '_asc' in input):
        # Input is an association table
        # Get the input files, and run makewcs on them
        oldasndict = asnutil.readASNTable(input, prodonly=infilesOnly)

        if not infilesOnly:
            if output in ["",None,"None"]:
                output = oldasndict['output'].lower() # insure output name is lower case

        asnhdr = fits.getheader(input, memmap=False)
        # Only perform duplication check if not already completed...
        dupcheck = asnhdr.get('DUPCHECK',default="PERFORM") == "PERFORM"

        #filelist = [fileutil.buildRootname(fname) for fname in oldasndict['order']]
        filelist = buildASNList(oldasndict['order'],input,check_for_duplicates=dupcheck)

    elif (not isinstance(input, list)) and \
       (input[0] == '@') :
        # input is an @ file
        f = open(input[1:])
        # Read the first line in order to determine whether
        # IVM files have been specified in a second column...
        line = f.readline()
        f.close()
        # Parse the @-file with irafglob to extract the input filename
        filelist = irafglob.irafglob(input, atfile=util.atfile_sci)
        # If there is a second column...
        if len(line.split()) == 2:
            # ...parse out the names of the IVM files as well
            ivmlist = irafglob.irafglob(input, atfile=util.atfile_ivm)
        if output in ['',None,"None"]:
            if len(filelist) == 1:
                output = fileutil.buildNewRootname(filelist[0])
            else:
                output = 'final'
    else:
        #input is a string or a python list
        try:
            filelist, output = parseinput.parseinput(input, outputname=output)
            if output in ['',None,"None"]:
                if len(filelist) == 1:
                    output = fileutil.buildNewRootname(filelist[0])
                else:
                    output = 'final'
            if not isinstance(input, list):
                filelist.sort()
        except IOError: raise

    # sort the list of input files
    # this ensures the list of input files has the same order on all platforms
    # it can have ifferent order because listdir() uses inode order, not unix type order
    #filelist.sort()

    return filelist, output, ivmlist, oldasndict

Response:
Process the input string which contains the input file information and return a filelist and output
AttributeNames


Instruction:
Can you write a function in Python 3 where it
processes input list of filenames and return a list of dictionaries and the full output list of filenames.

Response:
def process_input(input, output=None, ivmlist=None, updatewcs=True,
                  prodonly=False,  wcskey=None, **workinplace):
    """
    Create the full input list of filenames after verifying and converting
    files as needed.
    """

    newfilelist, ivmlist, output, oldasndict, origflist = buildFileListOrig(
            input, output=output, ivmlist=ivmlist, wcskey=wcskey,
            updatewcs=updatewcs, **workinplace)

    if not newfilelist:
        buildEmptyDRZ(input, output)
        return None, None, output

    # run all WCS updating -- Now done in buildFileList
    #pydr_input = _process_input_wcs(newfilelist, wcskey, updatewcs)
    pydr_input = newfilelist

    # AsnTable will handle the case when output==None
    if not oldasndict:# and output is not None:
        oldasndict = asnutil.ASNTable(pydr_input, output=output)
        oldasndict.create()

    asndict = update_member_names(oldasndict, pydr_input)
    asndict['original_file_names'] = origflist

    # Build output filename
    drz_extn = '_drz.fits'
    for img in newfilelist:
        # special case logic to automatically recognize when _flc.fits files
        # are provided as input and produce a _drc.fits file instead
        if '_flc.fits' in img:
            drz_extn = '_drc.fits'
            break

    if output in [None,'']:
        output = fileutil.buildNewRootname(asndict['output'],
                                           extn=drz_extn)
    else:
        if '.fits' in output.lower():
            pass
        elif drz_extn[:4] not in output.lower():
            output = fileutil.buildNewRootname(output, extn=drz_extn)

    log.info('Setting up output name: %s' % output)

    return asndict, ivmlist, output


Instruction:
Create a Python 3 function to
process input files using the WCS key.

Response:
def _process_input_wcs(infiles, wcskey, updatewcs):
    """
    This is a subset of process_input(), for internal use only.  This is the
    portion of input handling which sets/updates WCS data, and is a performance
    hit - a target for parallelization. Returns the expanded list of filenames.
    """

    # Run parseinput though it's likely already been done in processFilenames
    outfiles = parseinput.parseinput(infiles)[0]

    # Disable parallel processing here for now until hardware I/O gets "wider".
    # Since this part is IO bound, parallelizing doesn't help more than a little
    # in most cases, and may actually slow this down on some desktop nodes.
#   cfgval_num_cores = None # get this from paramDict
#   pool_size = util.get_pool_size(cfgval_num_cores, len(outfiles))
    pool_size = 1

    # do the WCS updating
    if wcskey in ['', ' ', 'INDEF', None]:
        if updatewcs:
            log.info('Updating input WCS using "updatewcs"')
    else:
        log.info('Resetting input WCS to be based on WCS key = %s' % wcskey)

    if pool_size > 1:
        log.info('Executing %d parallel workers' % pool_size)
        subprocs = []
        for fname in outfiles:
            p = multiprocessing.Process(target=_process_input_wcs_single,
                name='processInput._process_input_wcs()', # for err msgs
                args=(fname, wcskey, updatewcs) )
            subprocs.append(p)
        mputil.launch_and_wait(subprocs, pool_size) # blocks till all done
    else:
        log.info('Executing serially')
        for fname in outfiles:
            _process_input_wcs_single(fname, wcskey, updatewcs)

    return outfiles


Instruction:
Write a Python 3 script for
processing input WCSs for a single image.

Response:
def _process_input_wcs_single(fname, wcskey, updatewcs):
    """
    See docs for _process_input_wcs.
    This is separated to be spawned in parallel.
    """
    if wcskey in ['', ' ', 'INDEF', None]:
        if updatewcs:
            uw.updatewcs(fname, checkfiles=False)
    else:
        numext = fileutil.countExtn(fname)
        extlist = []
        for extn in range(1, numext + 1):
            extlist.append(('SCI', extn))
        if wcskey in string.ascii_uppercase:
            wkey = wcskey
            wname = ' '
        else:
            wname = wcskey
            wkey = ' '
        altwcs.restoreWCS(fname, extlist, wcskey=wkey, wcsname=wname)
    # make an asn table at the end
    # Make sure there is a WCSCORR table for each input image
    if wcskey not in ['', ' ', 'INDEF', None] or updatewcs:
        wcscorr.init_wcscorr(fname)


Instruction:
Implement a function in Python 3 to
build a file list which has undergone various instrument - specific checks for input to MultiDrizzle including splitting STIS associations.

Response:
def buildFileList(input, output=None, ivmlist=None,
                wcskey=None, updatewcs=True, **workinplace):
    """
    Builds a file list which has undergone various instrument-specific
    checks for input to MultiDrizzle, including splitting STIS associations.
    """
    newfilelist, ivmlist, output, oldasndict, filelist = \
        buildFileListOrig(input=input, output=output, ivmlist=ivmlist,
                    wcskey=wcskey, updatewcs=updatewcs, **workinplace)
    return newfilelist, ivmlist, output, oldasndict