def invert_kernel(ixs, function, dopsf, normalize, context, **kwargs): iix, (data_image, data_visibility) = ixs facet_id = data_image.facet_id ix = None if context == "facets_slice" or context == "facets_wstack": ix = (iix[0], iix[1], iix[2], iix[3], facet_id, iix[5]) elif context in ["facets_timeslice"]: ix = (iix[0], iix[1], iix[2], iix[3], data_visibility.iter_id, iix[5]) else: ix = iix if data_visibility is not None: result = function(data_visibility, data_image, dopsf=dopsf, normalize=normalize, **kwargs) if context == "2d" or context == "facets": result = (ix, sum_invert_results([result])) else: result = (ix, result) result[1][0].facet_id = facet_id else: result = (create_empty_image_like(data_image), 0.0) if context == "2d" or context == "facets": result = (ix, sum_invert_results([result])) else: result = (ix, result) result[1][0].facet_id = facet_id label = "Invert Kernel " print(label + str(result)) return result
def test_continuum_imaging(self): model = create_empty_image_like(self.image) visres, comp, residual = continuum_imaging(self.vis, model, algorithm='msmfsclean') export_image_to_fits( comp, "%s/test_pipelines-continuum-imaging-comp.fits" % (self.dir))
def test_psf_location_2d(self): self.actualSetUp() self.componentvis = create_visibility( self.lowcore, self.times, self.frequency, channel_bandwidth=self.channel_bandwidth, phasecentre=self.phasecentre, weight=1.0, polarisation_frame=self.vis_pol) self.componentvis.data['uvw'][:, 2] = 0.0 self.componentvis.data['vis'] *= 0.0 psf2d = create_empty_image_like(self.model) psf2d, sumwt = invert_2d(self.componentvis, psf2d, dopsf=True, **self.params) export_image_to_fits( psf2d, '%s/test_imaging_functions_invert_psf_location.fits' % self.dir) nchan, npol, ny, nx = psf2d.shape assert numpy.abs(psf2d.data[0, 0, ny // 2, nx // 2] - 1.0) < 2e-3 imagecentre = pixel_to_skycoord(nx // 2 + 1.0, ny // 2 + 1.0, wcs=psf2d.wcs, origin=1) assert imagecentre.separation(self.phasecentre).value < 1e-15, \ "Image phase centre %s not as expected %s" % (imagecentre, self.phasecentre)
def test_invert_2d(self): # Test if the 2D invert works with w set to zero # Set w=0 so that the two-dimensional transform should agree exactly with the model. # Good check on the grid correction in the vis->image direction self.actualSetUp() self.componentvis = create_visibility( self.lowcore, self.times, self.frequency, channel_bandwidth=self.channel_bandwidth, phasecentre=self.phasecentre, weight=1.0, polarisation_frame=self.vis_pol) self.componentvis.data['uvw'][:, 2] = 0.0 self.componentvis.data['vis'] *= 0.0 # Predict the visibility using direct evaluation for comp in self.components: predict_skycomponent_visibility(self.componentvis, comp) dirty2d = create_empty_image_like(self.model) dirty2d, sumwt = invert_2d(self.componentvis, dirty2d, **self.params) export_image_to_fits( dirty2d, '%s/test_imaging_functions_invert_2d_dirty.fits' % self.dir) self._checkcomponents(dirty2d)
def ingest_visibility(self, freq=None, chan_width=None, times=None, reffrequency=None, add_errors=False, block=True): if freq is None: freq = [1e8] if times is None: ntimes = 5 times = numpy.linspace(-numpy.pi / 3.0, numpy.pi / 3.0, ntimes) if chan_width is None: chan_width = [1e6] if reffrequency is None: reffrequency = [1e8] lowcore = create_named_configuration('LOWBD2-CORE') frequency = numpy.array([freq]) channel_bandwidth = numpy.array([chan_width]) phasecentre = SkyCoord(ra=+180.0 * u.deg, dec=-60.0 * u.deg, frame='icrs', equinox='J2000') if block: vt = create_blockvisibility(lowcore, times, frequency, channel_bandwidth=channel_bandwidth, weight=1.0, phasecentre=phasecentre, polarisation_frame=PolarisationFrame("stokesI")) else: vt = create_visibility(lowcore, times, frequency, channel_bandwidth=channel_bandwidth, weight=1.0, phasecentre=phasecentre, polarisation_frame=PolarisationFrame("stokesI")) cellsize = 0.001 model = create_image_from_visibility(vt, npixel=self.npixel, cellsize=cellsize, npol=1, frequency=reffrequency, phasecentre=phasecentre, polarisation_frame=PolarisationFrame("stokesI")) flux = numpy.array([[100.0]]) facets = 4 rpix = model.wcs.wcs.crpix - 1.0 spacing_pixels = self.npixel // facets centers = [-1.5, -0.5, 0.5, 1.5] comps = list() for iy in centers: for ix in centers: p = int(round(rpix[0] + ix * spacing_pixels * numpy.sign(model.wcs.wcs.cdelt[0]))), \ int(round(rpix[1] + iy * spacing_pixels * numpy.sign(model.wcs.wcs.cdelt[1]))) sc = pixel_to_skycoord(p[0], p[1], model.wcs, origin=1) comp = create_skycomponent(flux=flux, frequency=frequency, direction=sc, polarisation_frame=PolarisationFrame("stokesI")) comps.append(comp) if block: predict_skycomponent_blockvisibility(vt, comps) else: predict_skycomponent_visibility(vt, comps) insert_skycomponent(model, comps) self.model = copy_image(model) self.empty_model = create_empty_image_like(model) export_image_to_fits(model, '%s/test_pipeline_bags_model.fits' % self.dir) if add_errors: # These will be the same for all calls numpy.random.seed(180555) gt = create_gaintable_from_blockvisibility(vt) gt = simulate_gaintable(gt, phase_error=1.0, amplitude_error=0.0) vt = apply_gaintable(vt, gt) return vt
def create_illum_vla(model): def disk(a, xx, yy, radius): disk = numpy.zeros_like(a) nx, ny = a.shape rr = numpy.sqrt(xx ** 2 + yy ** 2) for y in range(ny): for x in range(nx): if rr[x, y] < radius / 2: disk[x, y] = 1.0 return disk nchan, npol, ny, nx = model.shape # The beam is assumed to just scale with frequency. beam = create_empty_image_like(model) illum = fft_image(beam) for chan in range(nchan): # The frequency axis is the second to last in the beam frequency = model.wcs.sub(['spectral']).wcs_pix2world([chan], 0)[0] wavelength = const.c.to('m/s').value / frequency scaleuv = numpy.abs(illum.wcs.sub(2).wcs.cdelt[0]) * wavelength xx, yy = numpy.meshgrid(scaleuv * (range(nx) - illum.wcs.sub(2).wcs.crpix[0]), scaleuv * (range(ny) - illum.wcs.sub(2).wcs.crpix[1])) for pol in range(npol): reflector = disk(illum.data[chan, pol, ...], xx, yy, 25.0) blockage = disk(illum.data[chan, pol, ...], xx, yy, 1.67) illum.data[chan, pol, ...] = reflector - blockage return illum
def test_ICAL_bandpass(self): self.setupVis(add_errors=True, block=True, freqwin=32, bandpass=True) model = create_empty_image_like(self.model) comp, residual, restored = ical(self.vis, model, algorithm='msclean', context='wstack', vis_slices=51, scales=[0, 3, 10, 30], threshold=0.01, findpeak='ARL', fractional_threshold=0.01, T_first_selfcal=2, G_first_selfcal=3, B_first_selfcal=4, nmajor=5) export_image_to_fits( comp, "%s/test_pipelines-ical-deconvolved-bandpass.fits" % (self.dir)) export_image_to_fits( residual, "%s/test_pipelines-ical-residual-bandpass.fits" % (self.dir)) export_image_to_fits( restored, "%s/test_pipelines-ical-restored-bandpass.fits" % (self.dir))
def _invert_base(self, invert, fluxthreshold=20.0, positionthreshold=1.0): dirtyFacet = create_empty_image_like(self.model) dirtyFacet, sumwt = invert(self.componentvis, dirtyFacet, **self.params) assert sumwt.all() > 0.0 export_image_to_fits( dirtyFacet, '%s/test_%s_dirty.fits' % (self.dir, invert.__name__)) self._checkcomponents(dirtyFacet, fluxthreshold, positionthreshold)
def test_spectral_line_imaging_no_deconvolution(self): model = create_empty_image_like(self.image) visres, comp, residual = spectral_line_imaging( self.vis, model, continuum_model=model, deconvolve_spectral=False) export_image_to_fits( comp, "%s/test_pipelines-spectral-no-deconvolution-imaging-comp.fits" % (self.dir))
def gather_image_iteration_results(results, template_model): result = create_empty_image_like(template_model) i = 0 for dpatch in image_iter(result, facets=facets, **kwargs): if results[i] is not None: dpatch.data[...] = results[i][0].data[...] i += 1 return result, results[0][1]
def gather_invert_results(results, template_model, facets, **kwargs): # Results contains the images for each facet, after adding across vis_graphs image_results = create_empty_image_like(template_model) image_results = image_gather([result[0] for result in results], image_results, facets=facets) # For the gather, assume all are the same weight sumwt = results[0][1] return image_results, sumwt
def invert_ignore_none(vis, model): if vis is not None: return invert(vis, model, context=context, dopsf=dopsf, normalize=normalize, **kwargs) else: return create_empty_image_like(model), 0.0
def create_low_test_beam(model: Image) -> Image: """Create a test power beam for LOW using an image from OSKAR :param model: Template image :return: Image """ beam = import_image_from_fits(arl_path('data/models/SKA1_LOW_beam.fits')) # Scale the image cellsize to account for the different in frequencies. Eventually we will want to # use a frequency cube log.info("create_low_test_beam: primary beam is defined at %.3f MHz" % (beam.wcs.wcs.crval[2] * 1e-6)) nchan, npol, ny, nx = model.shape # We need to interpolate each frequency channel separately. The beam is assumed to just scale with # frequency. reprojected_beam = create_empty_image_like(model) for chan in range(nchan): model2dwcs = model.wcs.sub(2).deepcopy() model2dshape = [model.shape[2], model.shape[3]] beam2dwcs = beam.wcs.sub(2).deepcopy() # The frequency axis is the second to last in the beam frequency = model.wcs.sub(['spectral']).wcs_pix2world([chan], 0)[0] fscale = beam.wcs.wcs.crval[2] / frequency beam2dwcs.wcs.cdelt = fscale * beam.wcs.sub(2).wcs.cdelt beam2dwcs.wcs.crpix = beam.wcs.sub(2).wcs.crpix beam2dwcs.wcs.crval = model.wcs.sub(2).wcs.crval beam2dwcs.wcs.ctype = model.wcs.sub(2).wcs.ctype model2dwcs.wcs.crpix = [ model.shape[2] // 2 + 1, model.shape[3] // 2 + 1 ] beam2d = create_image_from_array(beam.data[0, 0, :, :], beam2dwcs, model.polarisation_frame) reprojected_beam2d, footprint = reproject_image(beam2d, model2dwcs, shape=model2dshape) assert numpy.max( footprint.data) > 0.0, "No overlap between beam and model" reprojected_beam2d.data *= reprojected_beam2d.data reprojected_beam2d.data[footprint.data <= 0.0] = 0.0 for pol in range(npol): reprojected_beam.data[chan, pol, :, :] = reprojected_beam2d.data[:, :] return reprojected_beam
def test_spectral_line_imaging_with_deconvolution(self): model = create_empty_image_like(self.image) visres, comp, residual = spectral_line_imaging( self.vis, model, continuum_model=self.image, algorithm='hogbom', deconvolve_spectral=True) export_image_to_fits( comp, "%s/test_pipelines-spectral-with-deconvolution-imaging-comp.fits" % (self.dir))
def test_continuum_imaging(self): model = create_empty_image_like(self.image) visres, comp, residual = continuum_imaging(self.vis, model, algorithm='msmfsclean', scales=[0, 3, 10], threshold=0.01, nmoments=2, findpeak='ARL', fractional_threshold=0.01) export_image_to_fits( comp, "%s/test_pipelines-continuum-imaging-comp.fits" % (self.dir))
def gather_image_iteration_results(results, template_model): result = create_empty_image_like(template_model) i = 0 sumwt = numpy.zeros([template_model.nchan, template_model.npol]) for dpatch in image_iter(result, facets=facets, **kwargs): assert i < len( results), "Too few results in gather_image_iteration_results" if results[i] is not None: assert len(results[i]) == 2, results[i] dpatch.data[...] = results[i][0].data[...] sumwt += results[i][1] i += 1 return result, sumwt
def _checkdirty(self, vis, name='test_invert_2d_dirty', fluxthreshold=1.0): # Make the dirty image self.params['imaginary'] = False dirty = create_empty_image_like(self.model) dirty, sumwt = invert_2d(vis=vis, im=dirty, dopsf=False, normalize=True, **self.params) export_image_to_fits(dirty, '%s/%s_dirty.fits' % (self.dir, name)) maxabs = numpy.max(numpy.abs(dirty.data)) assert maxabs < fluxthreshold, "%s, abs max %f exceeds flux threshold" % ( name, maxabs)
def invert_timeslice_single(vis: Visibility, im: Image, dopsf, normalize=True, **kwargs) -> (Image, numpy.ndarray): """Process single time slice Extracted for re-use in parallel version :param vis: Visibility to be inverted :param im: image template (not changed) :param dopsf: Make the psf instead of the dirty image :param normalize: Normalize by the sum of weights (True) """ inchan, inpol, ny, nx = im.shape if not isinstance(vis, Visibility): avis = coalesce_visibility(vis, **kwargs) else: avis = vis log.debug("invert_timeslice: inverting using time slices") avis, p, q = fit_uvwplane(avis, remove=True) workimage, sumwt = invert_2d_base(avis, im, dopsf, normalize=normalize, **kwargs) finalimage = create_empty_image_like(im) # Use griddata to do the conversion. This could be improved. Only cubic is possible in griddata. # The interpolation is ok for invert since the image is smooth. # Calculate nominal and distorted coordinates. The image is in distorted coordinates so we # need to convert back to nominal lnominal, mnominal, ldistorted, mdistorted = lm_distortion( workimage, -p, -q) for chan in range(inchan): for pol in range(inpol): finalimage.data[chan, pol, ...] = \ griddata((mdistorted.flatten(), ldistorted.flatten()), values=workimage.data[chan, pol, ...].flatten(), method='cubic', xi=(mnominal.flatten(), lnominal.flatten()), fill_value=0.0, rescale=True).reshape(finalimage.data[chan, pol, ...].shape) return finalimage, sumwt
def _invert_base(self, context, extra='', fluxthreshold=1.0, positionthreshold=1.0, check_components=True): dirty = create_empty_image_like(self.model) dirty, sumwt = invert_context(self.componentvis, dirty, dopsf=False, context=context, **self.params) export_image_to_fits( dirty, '%s/test_imaging_functions_invert_%s%s_dirty.fits' % (self.dir, context, extra)) if check_components: self._checkcomponents(dirty, fluxthreshold, positionthreshold)
def test_ICAL(self): self.setupVis(add_errors=True, block=True, freqwin=5) model = create_empty_image_like(self.model) visres, comp, residual = ical(self.vis, model, algorithm='msclean', context='wstack', vis_slices=51, scales=[0, 3, 10, 30], threshold=0.01, findpeak='ARL', fractional_threshold=0.01, first_selfcal=2, nmajor=5) export_image_to_fits(comp, "%s/test_pipelines-ical-comp.fits" % (self.dir)) export_image_to_fits( residual, "%s/test_pipelines-ical-residual.fits" % (self.dir))
def _checkdirty(self, vis, context, fluxthreshold=0.3): # Make the dirty image self.params['imaginary'] = False self.params['timeslice'] = 'auto' dirty = create_empty_image_like(self.model) dirty, sumwt = invert_context(vis=vis, im=dirty, dopsf=False, normalize=True, context='2d', **self.params) export_image_to_fits( dirty, '%s/test_imaging_functions_%s_dirty.fits' % (self.dir, context)) maxabs = numpy.max(numpy.abs(dirty.data)) assert maxabs < fluxthreshold, "%s, abs max %f exceeds flux threshold" % ( context, maxabs)
def test_continuum_imaging(self): self.setupVis(add_errors=False, block=True, freqwin=7) model = create_empty_image_like(self.model) comp, residual, restored = continuum_imaging(self.vis, model, algorithm='msmfsclean', context='wstack', vis_slices=51, scales=[0, 3, 10], threshold=0.01, nmoments=2, findpeak='ARL', fractional_threshold=0.01) export_image_to_fits( comp, "%s/test_pipelines-continuum-imaging-comp.fits" % (self.dir)) export_image_to_fits( residual, "%s/test_pipelines-continuum-imaging-residual.fits" % (self.dir))
def test_invert_2d(self): # Test if the 2D invert works with w set to zero # Set w=0 so that the two-dimensional transform should agree exactly with the model. # Good check on the grid correction in the vis->image direction self.actualSetUp() self.componentvis.data['uvw'][:, 2] = 0.0 self.componentvis.data['vis'] *= 0.0 # Predict the visibility using direct evaluation for comp in self.components: predict_skycomponent_visibility(self.componentvis, comp) dirty2d = create_empty_image_like(self.model) dirty2d, sumwt = invert_2d(self.componentvis, dirty2d, **self.params) export_image_to_fits( dirty2d, '%s/test_imaging_functions_invert_2d_dirty.fits' % self.dir) self._checkcomponents(dirty2d)
def invert_with_vis_iterator(vis, im, dopsf=False, normalize=True, vis_iter=vis_slice_iter, invert=invert_2d, **kwargs): """ Invert using a specified iterator and invert This knows about the structure of invert in different execution frameworks but not anything about the actual processing. :param vis: :param im: :param dopsf: Make the psf instead of the dirty image :param normalize: Normalize by the sum of weights (True) :param kwargs: :return: """ resultimage = create_empty_image_like(im) i = 0 for rows in vis_iter(vis, **kwargs): if rows is not None: visslice = create_visibility_from_rows(vis, rows) workimage, sumwt = invert(visslice, im, dopsf, normalize=False, **kwargs) resultimage.data += workimage.data if i == 0: totalwt = sumwt else: totalwt += sumwt i += 1 if normalize: resultimage = normalize_sumwt(resultimage, totalwt) return resultimage, totalwt
def create_pb_vla(model, pointingcentre=None): """ Make an image like model and fill it with an analytical model of the primary beam :param model: :return: """ beam = create_empty_image_like(model) nchan, npol, ny, nx = model.shape if pointingcentre is not None: cx, cy = skycoord_to_pixel(pointingcentre, model.wcs, 0, 'wcs') else: with warnings.catch_warnings(): warnings.simplefilter('ignore') cx, cy = beam.wcs.sub(2).wcs.crpix[0] - 1, beam.wcs.sub( 2).wcs.crpix[1] - 1 for chan in range(nchan): # The frequency axis is the second to last in the beam with warnings.catch_warnings(): warnings.simplefilter('ignore') frequency = model.wcs.sub(['spectral']).wcs_pix2world([chan], 0)[0] wavelength = const.c.to('m/s').value / frequency d2r = numpy.pi / 180.0 scale = d2r * numpy.abs(beam.wcs.sub(2).wcs.cdelt[0]) xx, yy = numpy.meshgrid(scale * (range(nx) - cx), scale * (range(ny) - cy)) # Radius of each cell in radians rr = numpy.sqrt(xx**2 + yy**2) for pol in range(npol): reflector = ft_disk(rr * numpy.pi * 25.0 / wavelength) # blockage = ft_disk(rr * numpy.pi * 1.67 / wavelength) beam.data[chan, pol, ...] = reflector # - blockage beam.data *= beam.data return beam
def test_psf_location_2d(self): self.actualSetUp() psf2d = create_empty_image_like(self.model) psf2d, sumwt = invert_2d(self.componentvis, psf2d, dopsf=True, **self.params) export_image_to_fits( psf2d, '%s/test_imaging_functions_invert_psf_location.fits' % self.dir) nchan, npol, ny, nx = psf2d.shape assert numpy.abs(psf2d.data[0, 0, ny // 2, nx // 2] - 1.0) < 2e-3 imagecentre = pixel_to_skycoord(nx // 2 + 1.0, ny // 2 + 1.0, wcs=psf2d.wcs, origin=1) assert imagecentre.separation(self.phasecentre).value < 1e-15, \ "Image phase centre %s not as expected %s" % (imagecentre, self.phasecentre)
def invert_ignore_none(vis, model): if vis is not None: return invert_context(vis, model, context=context, dopsf=dopsf, normalize=normalize, **kwargs) else: return create_empty_image_like(model), numpy.zeros([model.nchan, model.npol])
def invert_ignore_None(vis, model, *args, **kwargs): if vis is not None: return invert(vis, model, *args, **kwargs) else: return create_empty_image_like(model), 0.0
def test_create_empty_image_like(self): emptyimage = create_empty_image_like(self.m31image) assert emptyimage.shape == self.m31image.shape assert numpy.max(numpy.abs(emptyimage.data)) == 0.0
def invert_function(vis, im: Image, dopsf=False, normalize=True, context='2d', inner=None, **kwargs): """ Invert using algorithm specified by context: * 2d: Two-dimensional transform * wstack: wstacking with either vis_slices or wstack (spacing between w planes) set * wprojection: w projection with wstep (spacing between w places) set, also kernel='wprojection' * timeslice: snapshot imaging with either vis_slices or timeslice set. timeslice='auto' does every time * facets: Faceted imaging with facets facets on each axis * facets_wprojection: facets AND wprojection * facets_wstack: facets AND wstacking * wprojection_wstack: wprojection and wstacking :param vis: :param im: :param dopsf: Make the psf instead of the dirty image (False) :param normalize: Normalize by the sum of weights (True) :param context: Imaging context e.g. '2d', 'timeslice', etc. :param inner: Inner loop 'vis'|'image' :param kwargs: :return: Image, sum of weights """ c = imaging_context(context) vis_iter = c['vis_iterator'] image_iter = c['image_iterator'] invert = c['invert'] if inner is None: inner = c['inner'] if not isinstance(vis, Visibility): svis = coalesce_visibility(vis, **kwargs) else: svis = vis resultimage = create_empty_image_like(im) if inner == 'image': totalwt = None for rows in vis_iter(svis, **kwargs): if numpy.sum(rows): visslice = create_visibility_from_rows(svis, rows) sumwt = 0.0 workimage = create_empty_image_like(im) for dpatch in image_iter(workimage, **kwargs): result, sumwt = invert(visslice, dpatch, dopsf, normalize=False, **kwargs) # Ensure that we fill in the elements of dpatch instead of creating a new numpy arrray dpatch.data[...] = result.data[...] # Assume that sumwt is the same for all patches if totalwt is None: totalwt = sumwt else: totalwt += sumwt resultimage.data += workimage.data else: # We assume that the weight is the same for all image iterations totalwt = None workimage = create_empty_image_like(im) for dpatch in image_iter(workimage, **kwargs): totalwt = None for rows in vis_iter(svis, **kwargs): if numpy.sum(rows): visslice = create_visibility_from_rows(svis, rows) result, sumwt = invert(visslice, dpatch, dopsf, normalize=False, **kwargs) # Ensure that we fill in the elements of dpatch instead of creating a new numpy arrray dpatch.data[...] += result.data[...] if totalwt is None: totalwt = sumwt else: totalwt += sumwt resultimage.data += workimage.data workimage.data[...] = 0.0 assert totalwt is not None, "No valid data found for imaging" if normalize: resultimage = normalize_sumwt(resultimage, totalwt) return resultimage, totalwt