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
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])
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
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