def test_convolutional_grid(self): npixel = 256 nvis = 10000 nchan = 1 npol = 4 uvgrid = numpy.zeros([nchan, npol, npixel, npixel], dtype='complex') gcf, kernel = anti_aliasing_calculate((npixel, npixel), 8) # kernel has shape [kernel_oversampling, kernel_oversampling, npixel, npixel] The fractional # part of the coordinate maps onto the first two axes. # Make some uv coordinates with random locations uvcoords = numpy.array( [[random.uniform(-0.25, 0.25), random.uniform(-0.25, 0.25)] for ivis in range(nvis)]) # Make some visibilities, all complex unity vis = numpy.ones([nvis, nchan, npol], dtype='complex') visweights = numpy.ones([nvis, nchan, npol]) kernels = (numpy.zeros([nvis], dtype='int'), [kernel]) frequencymap = numpy.zeros([nvis], dtype='int') # On my laptop the following takes about 34ss for 4e6 * 50 = 2e8 points so about 170 ns per point uvgrid, sumwt = convolutional_grid(kernels, uvgrid, vis, visweights, uvcoords, frequencymap) assert numpy.sum(sumwt) > 0.0 assert uvgrid.shape[0] == nchan assert uvgrid.shape[1] == npol assert uvgrid.shape[2] == npixel assert uvgrid.shape[3] == npixel
def standard_kernel_list(vis, shape, oversampling=8, support=3): """Return a lambda function to calculate the standard visibility kernel :param vis: visibility :param shape: tuple with 2D shape of grid :param oversampling: Oversampling factor :param support: Support of kernel :returns: Function to look up gridding kernel """ return [anti_aliasing_calculate(shape, oversampling, support)[1]]
def standard_kernel_list(vis, shape, oversampling=8, support=3): """Return a generator to calculate the standard visibility kernel :param vis: visibility :param shape: tuple with 2D shape of grid :param oversampling: Oversampling factor :param support: Support of kernel :return: Function to look up gridding kernel """ return numpy.zeros_like(vis.w, dtype='int'), [anti_aliasing_calculate(shape, oversampling, support)[1]]
def get_kernel_list(vis: Visibility, im, **kwargs): """Get the list of kernels, one per visibility """ shape = im.data.shape npixel = shape[3] cellsize = numpy.pi * im.wcs.wcs.cdelt[1] / 180.0 kernelname = get_parameter(kwargs, "kernel", "2d") oversampling = get_parameter(kwargs, "oversampling", 8) padding = get_parameter(kwargs, "padding", 2) gcf, _ = anti_aliasing_calculate((padding * npixel, padding * npixel), oversampling) wabsmax = numpy.max(numpy.abs(vis.w)) if kernelname == 'wprojection' and wabsmax > 0.0: # wprojection needs a lot of commentary! log.debug("get_kernel_list: Using wprojection kernel") # The field of view must be as padded! fov = cellsize * npixel * padding r_f = (cellsize * npixel / 2)**2 / abs(cellsize) log.debug("get_kernel_list: Fresnel number = %f" % (r_f)) delA = get_parameter(kwargs, 'wloss', 0.02) advice = advise_wide_field(vis, delA) wstep = get_parameter(kwargs, "wstep", advice['w_sampling_primary_beam']) log.debug("get_kernel_list: Using w projection with wstep = %f" % (wstep)) # Now calculate the maximum support for the w kernel npixel_kernel = get_parameter( kwargs, "kernelwidth", (2 * int(round(numpy.sin(0.5 * fov) * npixel / 4.0)))) assert npixel_kernel % 2 == 0 log.debug("get_kernel_list: Maximum w kernel full width = %d pixels" % (npixel_kernel)) kernel_list = w_kernel_list(vis, (npixel, npixel), fov, wstep=wstep, npixel_kernel=npixel_kernel, oversampling=oversampling) else: kernelname = '2d' kernel_list = standard_kernel_list( vis, (padding * npixel, padding * npixel), oversampling=8, support=3) return kernelname, gcf, kernel_list
def get_kernel_list(vis: Visibility, im: Image, **kwargs): """Get the list of kernels, one per visibility """ shape = im.data.shape npixel = shape[3] cellsize = numpy.pi * im.wcs.wcs.cdelt[1] / 180.0 kernelname = get_parameter(kwargs, "kernel", "2d") oversampling = get_parameter(kwargs, "oversampling", 8) padding = get_parameter(kwargs, "padding", 2) gcf, _ = anti_aliasing_calculate((padding * npixel, padding * npixel), oversampling) wabsmax = numpy.max(numpy.abs(vis.w)) if kernelname == 'wprojection' and wabsmax > 0.0: # wprojection needs a lot of commentary! log.debug("get_kernel_list: Using wprojection kernel") # The field of view must be as padded! R_F is for reporting only so that # need not be padded. fov = cellsize * npixel * padding r_f = (cellsize * npixel / 2) ** 2 / abs(cellsize) log.debug("get_kernel_list: Fresnel number = %f" % (r_f)) delA = get_parameter(kwargs, 'wloss', 0.02) advice = advise_wide_field(vis, delA) wstep = get_parameter(kwargs, "wstep", advice['w_sampling_primary_beam']) log.debug("get_kernel_list: Using w projection with wstep = %f" % (wstep)) # Now calculate the maximum support for the w kernel kernelwidth = get_parameter(kwargs, "kernelwidth", (2 * int(round(numpy.sin(0.5 * fov) * npixel * wabsmax * cellsize)))) kernelwidth = max(kernelwidth, 8) assert kernelwidth % 2 == 0 log.debug("get_kernel_list: Maximum w kernel full width = %d pixels" % (kernelwidth)) padded_shape=[im.shape[0], im.shape[1], im.shape[2] * padding, im.shape[3] * padding] remove_shift = get_parameter(kwargs, "remove_shift", True) padded_image = pad_image(im, padded_shape) kernel_list = w_kernel_list(vis, padded_image, oversampling=oversampling, wstep=wstep, kernelwidth=kernelwidth, remove_shift=remove_shift) else: kernelname = '2d' kernel_list = standard_kernel_list(vis, (padding * npixel, padding * npixel), oversampling=oversampling) return kernelname, gcf, kernel_list
def test_convolutional_degrid(self): npixel = 256 nvis = 100000 nchan = 1 npol = 4 uvgrid = numpy.ones([nchan, npol, npixel, npixel], dtype='complex') gcf, kernel = anti_aliasing_calculate((npixel, npixel), 8) # kernel has shape [kernel_oversampling, kernel_oversampling, npixel, npixel] The fractional # part of the coordinate maps onto the first two axes. # Make some uv coordinates with random locations uvcoords = numpy.array( [[random.uniform(-0.25, 0.25), random.uniform(-0.25, 0.25)] for ivis in range(nvis)]) vshape = [nvis, npol] kernels = (numpy.zeros([nvis], dtype='int'), [kernel]) frequencymap = numpy.zeros([nvis], dtype='int') vis = convolutional_degrid(kernels, vshape, uvgrid, uvcoords, frequencymap) assert vis.shape[0] == nvis assert vis.shape[1] == npol
def test_anti_aliasing_calculate(self): for shape in [(64, 64), (128, 128), (256, 256)]: _, aaf = anti_aliasing_calculate(shape, 8) self.assertAlmostEqual( numpy.max(aaf[..., aaf.shape[1] // 2, aaf.shape[0] // 2]), 0.18712109669890534)
def w_kernel_list(vis: Visibility, im: Image, oversampling=1, wstep=50.0, kernelwidth=16, **kwargs): """ Calculate w convolution kernels Uses create_w_term_like to calculate the w screen. This is exactly as wstacking does. Returns (indices to the w kernel for each row, kernels) Each kernel has axes [centre_v, centre_u, offset_v, offset_u]. We currently use the same convolution function for all channels and polarisations. Changing that behaviour would require modest changes here and to the gridding/degridding routines. :param vis: visibility :param image: Template image (padding, if any, occurs before this) :param oversampling: Oversampling factor :param wstep: Step in w between cached functions :return: (indices to the w kernel for each row, kernels) """ nchan, npol, ny, nx = im.shape gcf, _ = anti_aliasing_calculate((ny, nx)) assert oversampling % 2 == 0 or oversampling == 1, "oversampling must be unity or even" assert kernelwidth % 2 == 0, "kernelwidth must be even" wmaxabs = numpy.max(numpy.abs(vis.w)) log.debug( "w_kernel_list: Maximum absolute w = %.1f, step is %.1f wavelengths" % (wmaxabs, wstep)) def digitise(w, wstep): return numpy.ceil((w + wmaxabs) / wstep).astype('int') # Find all the unique indices for which we need a kernel nwsteps = digitise(wmaxabs, wstep) + 1 w_list = numpy.linspace(-wmaxabs, +wmaxabs, nwsteps) print('====', nwsteps, wstep, len(w_list)) wtemplate = copy_image(im) wtemplate.data = numpy.zeros(wtemplate.shape, dtype=im.data.dtype) padded_shape = list(wtemplate.shape) padded_shape[3] *= oversampling padded_shape[2] *= oversampling # For all the unique indices, calculate the corresponding w kernel kernels = list() for w in w_list: # Make a w screen wscreen = create_w_term_like(wtemplate, w, vis.phasecentre, **kwargs) wscreen.data /= gcf assert numpy.max(numpy.abs(wscreen.data)) > 0.0, 'w screen is empty' wscreen_padded = pad_image(wscreen, padded_shape) wconv = fft_image(wscreen_padded) wconv.data *= float(oversampling)**2 # For the moment, ignore the polarisation and channel axes kernels.append( convert_image_to_kernel(wconv, oversampling, kernelwidth).data[0, 0, ...]) # Now make a lookup table from row number of vis to the kernel kernel_indices = digitise(vis.w, wstep) assert numpy.max(kernel_indices) < len(kernels), "wabsmax %f wstep %f" % ( wmaxabs, wstep) assert numpy.min(kernel_indices) >= 0, "wabsmax %f wstep %f" % (wmaxabs, wstep) return kernel_indices, kernels