Exemple #1
0
    def blot_images(self):
        """ Core blotting routine

        This is the main routine for blotting the median sky image back to
        the detector space and creating a blotting image for each input model
        1. Loop over every data model to be blotted and find ra,dec,wavelength
           for every pixel in a valid slice on the detector.
        2. Using WCS of input image convert the ra and dec of input model
           to then tangent plane values: xi,eta.
        3. For the median sky cube convert the ra and dec of each x,y in this
           cube to xi,eta using the wcs of the imput image.
        4. a. Loop over  every input_model valid IFU slice pixel and find the
            median image pixels that fall within the ROI of the center of
            pixel.The ROI parameters are the same as those used to construct
            the median sky cube and are stored in the meta data of the median
            cube.
            b. After all the overlapping median pixels have been found find the
            weighted flux using these overlapping pixels. The weighting is
            based on the distance between the detector pixel and the median
            flux pixel in tangent plane plane. Additional weighting parameters
            are read in from the median cube meta data.
            c. The blotted flux  = the weighted flux determined in
            step b and stored in blot.data
        """
        t0 = time.time()
        blot_models = datamodels.ModelContainer()
        lower_limit = 0.01
        instrument_info = instrument_defaults.InstrumentInfo()

        for model in self.input_models:
            blot = model.copy()
            blot.err = None
            blot.dq = None

            filename = model.meta.filename
            indx = filename.rfind('.fits')

            blot_flux = np.zeros(model.shape, dtype=np.float32)
            # ________________________________________________________________________________
            # From the x,y pixel for detector. For MIRI we only work on one channel at a time

            if self.instrument == 'MIRI':
                # only one channel is blotted at a time
                this_par1 = self.channel
                ch_name = '_ch' + this_par1
                blot.meta.filename = filename[:indx] + ch_name + '_blot.fits'

                # get the detector values for this model
                xstart, xend = instrument_info.GetMIRISliceEndPts(this_par1)
                ydet, xdet = np.mgrid[:1024, :1032]

                # mask out the side channel we aren not working on
                pixel_mask = np.full(model.shape, False, dtype=bool)
                pixel_mask[:, xstart:xend] = True
                ra_det, dec_det, lam_det = model.meta.wcs(xdet, ydet)

            elif self.instrument == 'NIRSPEC':
                blot.meta.filename = filename[:indx] + '_blot.fits'
                # initialize the ra,dec, and wavelength arrays
                # we will loop over slices and fill in values
                # the flag_det will be set when a slice pixel is filled in
                #   at the end we will use this flag to pull out valid data
                ra_det = np.zeros((2048, 2048))
                dec_det = np.zeros((2048, 2048))
                lam_det = np.zeros((2048, 2048))
                flag_det = np.zeros((2048, 2048))

                # for NIRSPEC each file has 30 slices
                # wcs information access seperately for each slice

                nslices = 30
                log.info(
                    'Looping over 30 slices on NIRSPEC detector, this takes a little while'
                )
                for ii in range(nslices):
                    slice_wcs = nirspec.nrs_wcs_set_input(model, ii)
                    x, y = wcstools.grid_from_bounding_box(
                        slice_wcs.bounding_box)
                    ra, dec, lam = slice_wcs(x, y)

                    # the slices are curved on detector so a rectangular region
                    # returns NaNs
                    valid = ~np.isnan(lam)
                    ra = ra[valid]
                    dec = dec[valid]
                    lam = lam[valid]
                    x = x[valid]
                    y = y[valid]

                    xind = _toindex(x)
                    yind = _toindex(y)
                    xind = np.ndarray.flatten(xind)
                    yind = np.ndarray.flatten(yind)
                    ra = np.ndarray.flatten(ra)
                    dec = np.ndarray.flatten(dec)
                    lam = np.ndarray.flatten(lam)
                    ra_det[yind, xind] = ra
                    dec_det[yind, xind] = dec
                    lam_det[yind, xind] = lam
                    flag_det[yind, xind] = 1

# Done looping over slices
            log.info('Blotting back %s', model.meta.filename)

            if self.instrument == 'MIRI':
                valid3 = np.isfinite(lam_det)
                good_data1 = valid3 & pixel_mask
                good_data = np.where(good_data1)
            elif self.instrument == 'NIRSPEC':
                good_data = np.where(flag_det == 1)

            y, x = good_data
            ra_blot = ra_det[good_data]
            dec_blot = dec_det[good_data]
            wave_blot = lam_det[good_data]
            crval1 = model.meta.wcsinfo.crval1
            crval2 = model.meta.wcsinfo.crval2

            # x,y detector pixels --> xi, eta
            xi_blot, eta_blot = coord.radec2std(crval1, crval2, ra_blot,
                                                dec_blot)

            # cube spaxel ra,dec values --> xi, eta
            xi_cube, eta_cube = coord.radec2std(crval1, crval2, self.cube_ra,
                                                self.cube_dec)
            nplane = self.naxis1 * self.naxis2
            self.xi_centers = np.reshape(xi_cube[0, :, :], nplane)
            self.eta_centers = np.reshape(eta_cube[0, :, :], nplane)

            num = ra_blot.size
            # ______________________________________________________________________________
            # For every detector pixel find the overlapping median cube spaxels.
            # A median spaxel that falls withing the ROI of the center of the detector
            # pixel in the tangent plane is flagged as an overlapping pixel.

            for ipt in range(0, num - 1):
                # xx,yy are the index value of the orginal detector frame -
                # blot image
                yy = y[ipt]
                xx = x[ipt]
                # find the cube values that fall withing ROI of detector xx,yy
                xdistance = (xi_blot[ipt] - self.xi_centers)
                ydistance = (eta_blot[ipt] - self.eta_centers)
                radius = np.sqrt(xdistance * xdistance + ydistance * ydistance)
                # indexr holds the index of the sky median spaxels that fall
                # within the spatial  ROI of xx,yy location
                indexr = np.where(radius <= self.rois)
                # indexz holds the index of the sky median spaxels that fall
                # withing the spectral ROI of wave length assocation with xx,yy
                indexz = np.where(
                    abs(self.lam_centers - wave_blot[ipt]) <= self.roiw)
                # Pull out the Cube spaxels falling with ROI regions

                wave_found = self.lam_centers[indexz]
                xi_found = self.xi_centers[indexr]
                eta_found = self.eta_centers[indexr]
                # ______________________________________________________________________________
                # form the arrays to be used calculated the weighting
                d1 = np.array(xi_found - xi_blot[ipt]) / self.cdelt1
                d2 = np.array(eta_found - eta_blot[ipt]) / self.cdelt2
                d3 = np.array(wave_found - wave_blot[ipt]) / self.cdelt3

                dxy = d1 * d1 + d2 * d2
                dxy_matrix = np.tile(dxy[np.newaxis].T, [1, d3.shape[0]])
                d3_matrix = np.tile(d3 * d3, [dxy_matrix.shape[0], 1])

                wdistance = dxy_matrix + d3_matrix
                weight_distance = np.power(np.sqrt(wdistance),
                                           self.weight_power)
                weight_distance[weight_distance < lower_limit] = lower_limit
                weight_distance = 1.0 / weight_distance

                # determine the spaxel xx_cube,yy_cube values of these spaxels
                # in the ROI so they can be used to pull out the flux of the
                # median sky cube.
                yy_cube = (indexr[0] / self.naxis1).astype(np.int)
                xx_cube = indexr[0] - yy_cube * self.naxis1
                scf = np.array([
                    self.cube_flux[zz, yy_cube[ir], xx_cube[ir]]
                    for ir, rr in enumerate(indexr[0]) for zz in indexz[0]
                ])
                scf = np.reshape(scf, weight_distance.shape)
                blot_flux[yy, xx] = np.sum(weight_distance * scf)
                blot_weight = np.sum(weight_distance)

                # check for blot_weight !=0
                if blot_weight == 0:
                    blot_flux[yy, xx] = 0
                else:
                    blot_flux[yy, xx] = blot_flux[yy, xx] / blot_weight
# ________________________________________________________________________________
            blot.data = blot_flux
            blot_models.append(blot)
        t1 = time.time()
        log.info("Time Blot images = %.1f.s" % (t1 - t0, ))
        return blot_models
Exemple #2
0
def match_det2cube(instrument, x, y, sliceno, input_model, transform,
                   spaxel_flux, spaxel_weight, spaxel_iflux, spaxel_var,
                   acoord, zcoord, crval_along, crval3, cdelt_along, cdelt3,
                   naxis1, naxis2):
    """ Match detector pixels to output plane in local IFU coordinate system

    This routine assumes a 1-1 mapping in across slice to slice no.
    This routine assumes the output coordinate systems is local IFU plane.
    The user can not change scaling in across slice dimension
    Map the corners of the x,y detector values to a cube defined by local IFU plane.
    In the along slice, lambda plane find the % area of the detector pixel
    which it overlaps with in the cube. For each spaxel record the detector
    pixels that overlap with it - store flux,  % overlap, beta_distance.

    Parameters
    ----------
    x : numpy.ndarray
       x values of pixels in slice
    y : numpy.ndarray
       y values of pixels in slice
    sliceno : int
      slice number
    input_model : datamodel
      input slope model or file
    transform : transform
      wcs transform to transform x,y to alpha,beta, lambda
    spaxel : list
      list of spaxels holding information on each cube pixel.

    Returns
    -------
    spaxel filled in with needed information on overlapping detector pixels
    """

    x = _toindex(x)
    y = _toindex(y)

    pixel_dq = input_model.dq[y, x]

    all_flags = (dqflags.pixel['DO_NOT_USE'] + dqflags.pixel['NON_SCIENCE'])
    # find the location of all the values to reject in cube building
    good_data = np.where((np.bitwise_and(pixel_dq, all_flags) == 0))

    # good data holds the location of pixels we want to map to cube
    x = x[good_data]
    y = y[good_data]

    coord1, coord2, lam = transform(x, y)
    valid = ~np.isnan(coord2)
    x = x[valid]
    y = y[valid]

    yy_bot = y
    yy_top = y + 1
    xx_left = x
    xx_right = x + 1
    # along slice dimension is second coordinate returned from transform
    if instrument == 'NIRSPEC':
        b1, a1, lam1 = transform(xx_left, yy_bot)
        b2, a2, lam2 = transform(xx_right, yy_bot)
        b3, a3, lam3 = transform(xx_right, yy_top)
        b4, a4, lam4 = transform(xx_left, yy_top)
        # check if units are in microns or meters, if meters convert to microns
        # only need to check one of the wavelenghts
        lmax = np.nanmax(lam1.flatten())
        if lmax < 0.0001:
            lam1 = lam1 * 1.0e6
            lam2 = lam2 * 1.0e6
            lam3 = lam3 * 1.0e6
            lam4 = lam4 * 1.0e6

    if instrument == 'MIRI':
        a1, b1, lam1 = transform(xx_left, yy_bot)
        a2, b2, lam2 = transform(xx_right, yy_bot)
        a3, b3, lam3 = transform(xx_right, yy_top)
        a4, b4, lam4 = transform(xx_left, yy_top)

    # corners are returned Nanned if outside range of slice
    # fixed the nanned corners when a1,lam1 is valid but
    # adding 1 to x,y pushes data outside BB valid region

    # index_bad2 = np.isnan(a2)
    # index_bad3 = np.isnan(a3)
    # index_bad4 = np.isnan(a4)

    # on the edge out of bounds
    index_good2 = ~np.isnan(a2)
    index_good3 = ~np.isnan(a3)
    index_good4 = ~np.isnan(a4)

    # we need the cases of only all corners valid numbers
    good = np.where(index_good2 & index_good3 & index_good4)

    a1 = a1[good]
    a2 = a2[good]
    a3 = a3[good]
    a4 = a4[good]
    lam1 = lam1[good]
    lam2 = lam2[good]
    lam3 = lam3[good]
    lam4 = lam4[good]
    x = x[good]
    y = y[good]
    # approximate what out of bound value should be
    # w12 = np.mean(lam1[index_good2] - lam2[index_good2])
    # w13 = np.mean(lam1[index_good3] - lam3[index_good3])
    # w14 = np.mean(lam1[index_good4] - lam4[index_good4])

    # a12 = np.mean(a1[index_good2] - a2[index_good2])
    # a13 = np.mean(a1[index_good3] - a3[index_good3])
    # a14 = np.mean(a1[index_good4] - a4[index_good4])

    # a2[index_bad2] = a1[index_bad2] - a12
    # a3[index_bad3] = a1[index_bad3] - a13
    # a4[index_bad4] = a1[index_bad4] - a14

    # lam2[index_bad2] = lam1[index_bad2] - w12
    # lam3[index_bad3] = lam1[index_bad3] - w13
    # lam4[index_bad4] = lam1[index_bad4] - w14

    # center of first pixel, x,y = 1 for Adrian's equations
    # but we want the pixel corners, x,y values passed into this
    # routine to start at 0
    pixel_flux = input_model.data[y, x]
    pixel_err = input_model.err[y, x]

    nzc = len(zcoord)
    nac = len(acoord)
    # 1-1 mapping in across slice direction (x for NIRSPEC, y for MIRI)
    if instrument == 'NIRSPEC':
        xx = sliceno

    if instrument == 'MIRI':
        yy = sliceno

    # Loop over all pixels in slice
    nn = len(x)
    for ipixel in range(0, nn - 1):
        # detector pixel -> 4 corners
        # In along slice,wave space
        # along slice: a

        along_corner = []
        wave_corner = []

        along_corner.append(a1[ipixel])
        along_corner.append(a2[ipixel])
        along_corner.append(a3[ipixel])
        along_corner.append(a4[ipixel])

        wave_corner.append(lam1[ipixel])
        wave_corner.append(lam2[ipixel])
        wave_corner.append(lam3[ipixel])
        wave_corner.append(lam4[ipixel])
        # ________________________________________________________________________________
        # Now it does not matter the WCS method used
        along_min = min(along_corner)
        along_max = max(along_corner)
        wave_min = min(wave_corner)
        wave_max = max(wave_corner)

        Area = find_area_quad(along_min, wave_min, along_corner, wave_corner)

        # estimate where the pixel overlaps in the cube
        # find the min and max values in the cube xcoord,ycoord and zcoord
        # along_min = -2.0
        # along_max = -1.86
        MinA = (along_min - crval_along) / cdelt_along
        MaxA = (along_max - crval_along) / cdelt_along
        ia1 = max(0, int(MinA))
        # ia2 = int(math.ceil(MaxA))
        ia2 = int(MaxA)
        if ia2 >= nac:
            ia2 = nac - 1

        MinW = (wave_min - crval3) / cdelt3
        MaxW = (wave_max - crval3) / cdelt3
        iz1 = int(math.trunc(MinW))
        iz2 = int(math.ceil(MaxW))
        if iz2 >= nzc:
            iz2 = nzc - 1

        # loop over possible overlapping cube pixels
        # noverlap = 0
        nplane = naxis1 * naxis2

        for zz in range(iz1, iz2 + 1):
            zcenter = zcoord[zz]
            istart = zz * nplane

            for aa in range(ia1, ia2 + 1):
                if instrument == 'NIRSPEC':
                    cube_index = istart + aa * naxis1 + xx  # xx = slice #

                if instrument == 'MIRI':
                    cube_index = istart + yy * naxis1 + aa  # xx = slice #

                acenter = acoord[aa]
                area_overlap = sh_find_overlap(acenter, zcenter, cdelt_along,
                                               cdelt3, along_corner,
                                               wave_corner)
                if area_overlap > 0.0:
                    AreaRatio = area_overlap / Area

                    spaxel_flux[cube_index] = spaxel_flux[cube_index] + \
                        (AreaRatio * pixel_flux[ipixel])
                    spaxel_weight[cube_index] = spaxel_weight[cube_index] + \
                        AreaRatio
                    spaxel_iflux[cube_index] = spaxel_iflux[cube_index] + 1
                    spaxel_var[cube_index] = spaxel_var[cube_index] + \
                        (AreaRatio * pixel_err[ipixel]) * (AreaRatio * pixel_err[ipixel])
Exemple #3
0
def match_det2cube(instrument, x, y, sliceno, input_model, transform, acoord,
                   zcoord, crval_along, crval3, cdelt_along, cdelt3, naxis1,
                   naxis2):
    """ Match detector pixels to output plane in local IFU coordinate system

    This routine assumes a 1-1 mapping in across slice to slice no.
    This routine assumes the output coordinate systems is local IFU plane.
    The user can not change scaling in across slice dimension
    Map the corners of the x,y detector values to a cube defined by local IFU plane.
    In the along slice, lambda plane find the % area of the detector pixel
    which it overlaps with in the cube. For each spaxel record the detector
    pixels that overlap with it - store flux,  % overlap, beta_distance.

    Parameters
    ----------
    x : numpy.ndarray
       x values of pixels in slice
    y : numpy.ndarray
       y values of pixels in slice
    sliceno : int
      slice number
    input_model : datamodel
      input slope model or file
    transform : transform
      wcs transform to transform x,y to alpha,beta, lambda
    spaxel : list
      list of spaxels holding information on each cube pixel.

    Returns
    -------
    spaxel filled in with needed information on overlapping detector pixels
    """

    x = _toindex(x)
    y = _toindex(y)
    pixel_dq = input_model.dq[y, x]

    all_flags = (dqflags.pixel['DO_NOT_USE'] + dqflags.pixel['NON_SCIENCE'])
    # find the location of all the values to reject in cube building
    good_data = np.where((np.bitwise_and(pixel_dq, all_flags) == 0))

    # good data holds the location of pixels we want to map to cube
    x = x[good_data]
    y = y[good_data]
    coord1, coord2, lam = transform(x, y)
    valid = ~np.isnan(coord2)
    x = x[valid]
    y = y[valid]

    yy_bot = y
    yy_top = y + 1
    xx_left = x
    xx_right = x + 1
    # along slice dimension is second coordinate returned from transform
    if instrument == 'NIRSPEC':
        b1, a1, lam1 = transform(xx_left, yy_bot)
        b2, a2, lam2 = transform(xx_right, yy_bot)
        b3, a3, lam3 = transform(xx_right, yy_top)
        b4, a4, lam4 = transform(xx_left, yy_top)
        # check if units are in microns or meters, if meters convert to microns
        # only need to check one of the wavelengths
        lmax = np.nanmax(lam1.flatten())
        if lmax < 0.0001:
            lam1 = lam1 * 1.0e6
            lam2 = lam2 * 1.0e6
            lam3 = lam3 * 1.0e6
            lam4 = lam4 * 1.0e6

    if instrument == 'MIRI':
        a1, b1, lam1 = transform(xx_left, yy_bot)
        a2, b2, lam2 = transform(xx_right, yy_bot)
        a3, b3, lam3 = transform(xx_right, yy_top)
        a4, b4, lam4 = transform(xx_left, yy_top)

    # corners are returned Nanned if outside range of slice
    # fixed the nanned corners when a1,lam1 is valid but
    # adding 1 to x,y pushes data outside BB valid region

    # on the edge out of bounds
    index_good2 = ~np.isnan(a2)
    index_good3 = ~np.isnan(a3)
    index_good4 = ~np.isnan(a4)

    # we need the cases of only all corners valid numbers
    good = np.where(index_good2 & index_good3 & index_good4)
    a1 = a1[good]
    a2 = a2[good]
    a3 = a3[good]
    a4 = a4[good]
    lam1 = lam1[good]
    lam2 = lam2[good]
    lam3 = lam3[good]
    lam4 = lam4[good]
    x = x[good]
    y = y[good]

    # center of first pixel, x,y = 1 for Adrian's equations
    # but we want the pixel corners, x,y values passed into this
    # routine to start at 0
    pixel_flux = input_model.data[y, x]
    pixel_err = input_model.err[y, x]

    # 1-1 mapping in across slice direction (x for NIRSPEC, y for MIRI)
    if instrument == 'NIRSPEC':
        ss = sliceno
        instrument_no = 1
    if instrument == 'MIRI':
        ss = sliceno
        instrument_no = 0
    result = cube_wrapper_internal(instrument_no, naxis1, naxis2, crval_along,
                                   cdelt_along, crval3, cdelt3, a1, a2, a3, a4,
                                   lam1, lam2, lam3, lam4, acoord, zcoord, ss,
                                   pixel_flux, pixel_err)

    return result
Exemple #4
0
    def blot_images(self):
        """
        Short Summary
        ------------
        Core blotting module
        Initialize blot_model = input_model
        1. Loop over every data model to be blotted and find ra,dec,wavelength
           for every slice pixel.
        2. Using WCS of input image convert ra,dec of input model to tangent
           plane values: xi,eta
        3. For the median sky cube convert the ra,dec of each x,y in this cube
           to xi,eta using the wcs of the imput image
        4. a. Loop over  every input_model valid IFU slice pixel and find the
            median image pixels that fall within the ROI of the center of pixel.
            The ROI parameters are the same as those used to construct the
            median sky cube and are stored in the meta data of the Median Cube.
            b. After all the overlapping median pixels have been found find the
            weighted flux using this overlapping pixels. The weighting is based
            on the distance between the detector pixel and the median flux pixel
            in tangent plane plane. Additional weighting parameters are read in
            from the median cube meta data.
            c. The blotted flux (blot.data) = the weighted flux determined in
            step b.

        """
        t0 = time.time()
        blot_models = datamodels.ModelContainer()
        lower_limit = 0.01
        instrument_info = instrument_defaults.InstrumentInfo()

        for model in self.input_models:
            blot = model.copy()
            blot.err = None
            blot.dq = None

            filename = model.meta.filename
            indx = filename.rfind('.fits')

            blot_flux = np.zeros(model.shape, dtype=np.float32)
#________________________________________________________________________________
# From the x,y pixel for detector. For MIRI we only work on one channel at a time

            if self.instrument == 'MIRI':
                this_par1 = self.channel # only one channel is blotted at a time
                ch_name = '_ch' + this_par1
                blot.meta.filename = filename[:indx] + ch_name + '_blot.fits'

                # get the detector values for this model
                xstart, xend = instrument_info.GetMIRISliceEndPts(this_par1)
                ydet, xdet = np.mgrid[:1024, :1032]

                #mask out the side channel we aren not working on
                pixel_mask = np.full(model.shape, False, dtype=bool)
                pixel_mask[:, xstart:xend] = True
                ra_det, dec_det, lam_det = model.meta.wcs(xdet, ydet)

            elif self.instrument == 'NIRSPEC':
                blot.meta.filename = filename[:indx] + '_blot.fits'
                # initialize the ra,dec, and wavelength arrays
                # we will loop over slices and fill in values
                # the flag_det will be set when a slice pixel is filled in
                #   at the end we will use this flag to pull out valid data
                ra_det = np.zeros((2048, 2048))
                dec_det = np.zeros((2048, 2048))
                lam_det = np.zeros((2048, 2048))
                flag_det = np.zeros((2048, 2048))

                # for NIRSPEC each file has 30 slices
                # wcs information access seperately for each slice

                nslices = 30
                log.info('Looping over 30 slices on NIRSPEC detector, this takes a little while')
                for ii in range(nslices):
                    slice_wcs = nirspec.nrs_wcs_set_input(model, ii)
                    x, y = wcstools.grid_from_bounding_box(slice_wcs.bounding_box)
                    ra, dec, lam = slice_wcs(x, y)

                    # the slices are curved on detector so a rectangular region
                    # returns NaNs
                    valid = ~np.isnan(lam)
                    ra = ra[valid]
                    dec = dec[valid]
                    lam = lam[valid]
                    x = x[valid]
                    y = y[valid]

                    xind = _toindex(x)
                    yind = _toindex(y)
                    xind = np.ndarray.flatten(xind)
                    yind = np.ndarray.flatten(yind)
                    ra = np.ndarray.flatten(ra)
                    dec = np.ndarray.flatten(dec)
                    lam = np.ndarray.flatten(lam)
                    ra_det[yind, xind] = ra
                    dec_det[yind, xind] = dec
                    lam_det[yind, xind] = lam
                    flag_det[yind, xind] = 1

# Done looping over slices
            log.info('Blotting back %s', model.meta.filename)

            if self.instrument == 'MIRI':
                valid3 = np.isfinite(lam_det)
                good_data1 = valid3 & pixel_mask
                good_data = np.where(good_data1)
            elif self.instrument == 'NIRSPEC':
                good_data = np.where(flag_det == 1)

            y, x = good_data
            ra_blot = ra_det[good_data]
            dec_blot = dec_det[good_data]
            wave_blot = lam_det[good_data]
            crval1 = model.meta.wcsinfo.crval1
            crval2 = model.meta.wcsinfo.crval2

            # x,y detector pixels --> xi, eta
            xi_blot, eta_blot = coord.radec2std(crval1, crval2,
                                               ra_blot, dec_blot)

            # cube spaxel ra,dec values --> xi, eta
            xi_cube, eta_cube = coord.radec2std(crval1, crval2,
                                               self.cube_ra, self.cube_dec)
            nplane = self.naxis1 * self.naxis2
            self.xi_centers = np.reshape(xi_cube[0, :, :], nplane)
            self.eta_centers = np.reshape(eta_cube[0, :, :], nplane)

            num = ra_blot.size
#________________________________________________________________________________
# For every detector pixel find the overlapping median cube spaxels.
# A median spaxel that falls withing the ROI of the center of the detector pixel
# in the tangent plane is flagged as an overlapping pixel

            for ipt in range(0, num - 1):
                # xx,yy are the index value of the orginal detector frame -
                # blot image
                yy = y[ipt]
                xx = x[ipt]
                # find the cube values that fall withing ROI of detector xx,yy
                xdistance = (xi_blot[ipt] - self.xi_centers)
                ydistance = (eta_blot[ipt] - self.eta_centers)
                radius = np.sqrt(xdistance * xdistance + ydistance * ydistance)
                # indexr holds the index of the sky median spaxels that fall within
                # the spatial  ROI of xx,yy location
                indexr = np.where(radius <= self.rois)
                #indexz holds the index of the sky median spaxels that fall within
                # the spectral ROI of wave length assocation with xx,yy
                indexz = np.where(abs(self.lam_centers - wave_blot[ipt]) <= self.roiw)
                # Pull out the Cube spaxels falling with ROI regions

                wave_found = self.lam_centers[indexz]
                xi_found = self.xi_centers[indexr]
                eta_found = self.eta_centers[indexr]
#________________________________________________________________________________
                # form the arrays to be used calculated the weighting
                d1 = np.array(xi_found - xi_blot[ipt]) / self.cdelt1
                d2 = np.array(eta_found - eta_blot[ipt]) / self.cdelt2
                d3 = np.array(wave_found - wave_blot[ipt]) / self.cdelt3

                dxy = d1 * d1 + d2 * d2
                dxy_matrix = np.tile(dxy[np.newaxis].T, [1, d3.shape[0]])
                d3_matrix = np.tile(d3 * d3, [dxy_matrix.shape[0], 1])

                wdistance = dxy_matrix + d3_matrix
                weight_distance = np.power(np.sqrt(wdistance), self.weight_power)
                weight_distance[weight_distance < lower_limit] = lower_limit
                weight_distance = 1.0 / weight_distance

                # determine the spaxel xx_cube,yy_cube values of these spaxels in
                # the ROI so they can be used to pull out the flux of the median
                # sky cube.
                yy_cube = (indexr[0] / self.naxis1).astype(np.int)
                xx_cube = indexr[0] - yy_cube * self.naxis1
                scf = np.array([self.cube_flux[zz, yy_cube[ir], xx_cube[ir]]
                                for ir, rr in enumerate(indexr[0]) for zz in indexz[0]])
                scf = np.reshape(scf, weight_distance.shape)
                blot_flux[yy, xx] = np.sum(weight_distance * scf)
                blot_weight = np.sum(weight_distance)

                # check for blot_weight !=0
                if blot_weight == 0:
                    blot_flux[yy, xx] = 0
                else:
                    blot_flux[yy, xx] = blot_flux[yy, xx] / blot_weight
#________________________________________________________________________________
            blot.data = blot_flux
            blot_models.append(blot)
        t1 = time.time()
        log.info("Time Blot images = %.1f.s" % (t1 - t0,))
        return blot_models