def visibility_scatter(vis: Visibility, vis_iter, vis_slices=1) -> List[Visibility]: """Scatter a visibility into a list of subvisibilities If vis_iter is over time then the type of the outvisibilities will be the same as inout If vis_iter is over w then the type of the output visibilities will always be Visibility :param vis: Visibility :param vis_iter: visibility iterator :param vis_slices: Number of slices to be made :return: list of subvisibilitys """ assert vis is not None if vis_slices == 1: return [vis] visibility_list = list() for i, rows in enumerate(vis_iter(vis, vis_slices=vis_slices)): subvis = create_visibility_from_rows(vis, rows) visibility_list.append(subvis) return visibility_list
def test_create_visibility_from_rows_makecopy(self): self.vis = create_visibility(self.lowcore, self.times, self.frequency, phasecentre=self.phasecentre, weight=1.0, channel_bandwidth=self.channel_bandwidth) rows = self.vis.time > 150.0 for makecopy in [True, False]: selected_vis = create_visibility_from_rows(self.vis, rows, makecopy=makecopy) assert selected_vis.nvis == numpy.sum(numpy.array(rows))
def test_coalesce_decoalesce_with_iter(self): for rows in vis_timeslice_iter(self.blockvis): visslice = create_visibility_from_rows(self.blockvis, rows) cvisslice = convert_blockvisibility_to_visibility(visslice) assert numpy.min(cvisslice.frequency) == numpy.min(self.frequency) assert numpy.min(cvisslice.frequency) > 0.0 dvisslice = decoalesce_visibility(cvisslice) assert dvisslice.nvis == visslice.nvis
def test_vis_wslice_iterator(self): self.actualSetUp() nchunks = vis_wslices(self.vis, wslice=10.0) log.debug('Found %d chunks' % (nchunks)) assert nchunks > 1 total_rows = 0 for chunk, rows in enumerate(vis_wslice_iter(self.vis, nchunks)): assert len(rows) visslice = create_visibility_from_rows(self.vis, rows) total_rows += visslice.nvis assert numpy.sum(visslice.nvis) < self.vis.nvis assert total_rows == self.vis.nvis, "Total rows iterated %d, Original rows %d" % (total_rows, self.vis.nvis)
def create_gaintable_from_screen(vis, sc, screen, height=3e5, vis_slices=None, scale=1.0, **kwargs): """ Create gaintables from a screen calculated using ARatmospy Screen axes are ['XX', 'YY', 'TIME', 'FREQ'] :param vis: :param sc: Sky components for which pierce points are needed :param screen: :param height: Height (in m) of screen above telescope e.g. 3e5 :param scale: Multiply the screen by this factor :return: """ assert isinstance(vis, BlockVisibility) station_locations = vis.configuration.xyz # Convert to TEC # phase = image[pixel] * -8.44797245e9 / frequency nant = station_locations.shape[0] t2r = numpy.pi / 43200.0 gaintables = [ create_gaintable_from_blockvisibility(vis, **kwargs) for i in sc ] # The time in the Visibility is hour angle in seconds! for iha, rows in enumerate(vis_timeslice_iter(vis, vis_slices=vis_slices)): v = create_visibility_from_rows(vis, rows) ha = numpy.average(v.time) tec2phase = -8.44797245e9 / numpy.array(vis.frequency) number_bad = 0 number_good = 0 for icomp, comp in enumerate(sc): pp = find_pierce_points(station_locations, (comp.direction.ra.rad + t2r * ha) * u.rad, comp.direction.dec, height=height, phasecentre=vis.phasecentre) scr = numpy.zeros([nant]) for ant in range(nant): pp0 = pp[ant][0:2] worldloc = [pp0[0], pp0[1], ha, 1e8] try: pixloc = screen.wcs.wcs_world2pix([worldloc], 0)[0].astype('int') scr[ant] = scale * screen.data[pixloc[3], pixloc[2], pixloc[1], pixloc[0]] number_good += 1 except: number_bad += 1 scr[ant] = 0.0 scr = scr[:, numpy.newaxis] * tec2phase[numpy.newaxis, :] # axes of gaintable.gain are time, ant, nchan, nrec gaintables[icomp].gain[iha, :, :, :] = numpy.exp( 1j * scr[..., numpy.newaxis, numpy.newaxis]) gaintables[icomp].phasecentre = comp.direction if number_bad > 0: log.warning( "create_gaintable_from_screen: %d pierce points are inside the screen image" % (number_good)) log.warning( "create_gaintable_from_screen: %d pierce points are outside the screen image" % (number_bad)) return gaintables
def solve_gaintable(vis: BlockVisibility, modelvis: BlockVisibility = None, gt=None, phase_only=True, niter=30, tol=1e-8, crosspol=False, normalise_gains=True, **kwargs) -> GainTable: """Solve a gain table by fitting an observed visibility to a model visibility If modelvis is None, a point source model is assumed. :param vis: BlockVisibility containing the observed data_models :param modelvis: BlockVisibility containing the visibility predicted by a model :param gt: Existing gaintable :param phase_only: Solve only for the phases (default=True) :param niter: Number of iterations (default 30) :param tol: Iteration stops when the fractional change in the gain solution is below this tolerance :param crosspol: Do solutions including cross polarisations i.e. XY, YX or RL, LR :return: GainTable containing solution """ assert isinstance(vis, BlockVisibility), vis if modelvis is not None: assert isinstance(modelvis, BlockVisibility), modelvis assert numpy.max(numpy.abs( modelvis.vis)) > 0.0, "Model visibility is zero" if phase_only: log.debug('solve_gaintable: Solving for phase only') else: log.debug('solve_gaintable: Solving for complex gain') if gt is None: log.debug("solve_gaintable: creating new gaintable") gt = create_gaintable_from_blockvisibility(vis, **kwargs) else: log.debug("solve_gaintable: starting from existing gaintable") for row in range(gt.ntimes): vis_rows = numpy.abs(vis.time - gt.time[row]) < gt.interval[row] / 2.0 if numpy.sum(vis_rows) > 0: subvis = create_visibility_from_rows(vis, vis_rows) if modelvis is not None: model_subvis = create_visibility_from_rows(modelvis, vis_rows) pointvis = divide_visibility(subvis, model_subvis) x = numpy.sum(pointvis.vis * pointvis.weight, axis=0) xwt = numpy.sum(pointvis.weight, axis=0) else: x = numpy.sum(subvis.vis * subvis.weight, axis=0) xwt = numpy.sum(subvis.weight, axis=0) mask = numpy.abs(xwt) > 0.0 x_shape = x.shape x[mask] = x[mask] / xwt[mask] x[~mask] = 0.0 x = x.reshape(x_shape) gt = solve_from_X(gt, x, xwt, row, crosspol, niter, phase_only, tol, npol=vis.polarisation_frame.npol) if normalise_gains and not phase_only: gabs = numpy.average(numpy.abs(gt.data['gain'][row])) gt.data['gain'][row] /= gabs assert isinstance(gt, GainTable), "gt is not a GainTable: %r" % gt assert_vis_gt_compatible(vis, gt) return gt
def simulate_gaintable_from_voltage_patterns(vis, sc, vp_list, vp_coeffs, vis_slices=None, order=3, use_radec=False, **kwargs): """ Create gaintables for a set of zernikes :param vis: :param sc: Sky components for which pierce points are needed :param vp: List of Voltage patterns in AZELGEO frame :param vp_coeffs: Fractional contribution [nants, nvp] :param order: order of spline (default is 3) :return: """ ntimes, nant = vis.vis.shape[0:2] vp_coeffs = numpy.array(vp_coeffs) gaintables = [create_gaintable_from_blockvisibility(vis, **kwargs) for i in sc] if not use_radec: assert isinstance(vis, BlockVisibility) assert vis.configuration.mount[0] == 'azel', "Mount %s not supported yet" % vis.configuration.mount[0] # The time in the Visibility is hour angle in seconds! number_bad = 0 number_good = 0 # Cache the splines, one per voltage pattern real_splines = list() imag_splines = list() for ivp, vp in enumerate(vp_list): assert vp.wcs.wcs.ctype[0] == 'AZELGEO long', vp.wcs.wcs.ctype[0] assert vp.wcs.wcs.ctype[1] == 'AZELGEO lati', vp.wcs.wcs.ctype[1] nchan, npol, ny, nx = vp.data.shape real_splines.append(RectBivariateSpline(range(ny), range(nx), vp.data[0, 0, ...].real, kx=order, ky=order)) imag_splines.append(RectBivariateSpline(range(ny), range(nx), vp.data[0, 0, ...].imag, kx=order, ky=order)) latitude = vis.configuration.location.lat.rad r2d = 180.0 / numpy.pi s2r = numpy.pi / 43200.0 # For each hourangle, we need to calculate the location of a component # in AZELGEO. With that we can then look up the relevant gain from the # voltage pattern for iha, rows in enumerate(vis_timeslice_iter(vis, vis_slices=vis_slices)): v = create_visibility_from_rows(vis, rows) ha = numpy.average(v.time) har = s2r * ha # Calculate the az el for this hourangle and the phasecentre declination azimuth_centre, elevation_centre = hadec_to_azel(har, vis.phasecentre.dec.rad, latitude) for icomp, comp in enumerate(sc): antgain = numpy.zeros([nant], dtype='complex') # Calculate the location of the component in AZELGEO, then add the pointing offset # for each antenna hacomp = comp.direction.ra.rad - vis.phasecentre.ra.rad + har deccomp = comp.direction.dec.rad azimuth_comp, elevation_comp = hadec_to_azel(hacomp, deccomp, latitude) for ant in range(nant): for ivp, vp in enumerate(vp_list): nchan, npol, ny, nx = vp.data.shape wcs_azel = vp.wcs.deepcopy() az_comp = azimuth_centre * r2d el_comp = elevation_centre * r2d # We use WCS sensible coordinate handling by labelling the axes misleadingly wcs_azel.wcs.crval[0] = az_comp wcs_azel.wcs.crval[1] = el_comp wcs_azel.wcs.ctype[0] = 'RA---SIN' wcs_azel.wcs.ctype[1] = 'DEC--SIN' worldloc = [azimuth_comp*r2d, elevation_comp*r2d, vp.wcs.wcs.crval[2], vp.wcs.wcs.crval[3]] try: pixloc = wcs_azel.sub(2).wcs_world2pix([worldloc[:2]], 1)[0] assert pixloc[0] > 2 assert pixloc[0] < nx - 3 assert pixloc[1] > 2 assert pixloc[1] < ny - 3 gain = real_splines[ivp].ev(pixloc[1], pixloc[0]) \ + 1j * imag_splines[ivp](pixloc[1], pixloc[0]) antgain[ant] += vp_coeffs[ant, ivp] * gain number_good += 1 except: number_bad += 1 antgain[ant] = 0.0 antgain[ant] = 1.0/antgain[ant] gaintables[icomp].gain[iha, :, :, :] = antgain[:, numpy.newaxis, numpy.newaxis, numpy.newaxis] gaintables[icomp].phasecentre = comp.direction else: assert isinstance(vis, BlockVisibility) number_bad = 0 number_good = 0 # Cache the splines, one per voltage pattern real_splines = list() imag_splines = list() for ivp, vp in enumerate(vp_list): nchan, npol, ny, nx = vp.data.shape real_splines.append(RectBivariateSpline(range(ny), range(nx), vp.data[0, 0, ...].real, kx=order, ky=order)) imag_splines.append(RectBivariateSpline(range(ny), range(nx), vp.data[0, 0, ...].imag, kx=order, ky=order)) for iha, rows in enumerate(vis_timeslice_iter(vis, vis_slices=vis_slices)): # The time in the Visibility is hour angle in seconds! r2d = 180.0 / numpy.pi # For each hourangle, we need to calculate the location of a component # in AZELGEO. With that we can then look up the relevant gain from the # voltage pattern v = create_visibility_from_rows(vis, rows) ha = numpy.average(v.time) for icomp, comp in enumerate(sc): antgain = numpy.zeros([nant], dtype='complex') antwt = numpy.zeros([nant]) ra_comp = comp.direction.ra.rad dec_comp = comp.direction.dec.rad for ant in range(nant): for ivp, vp in enumerate(vp_list): assert vp.wcs.wcs.ctype[0] == 'RA---SIN', vp.wcs.wcs.ctype[0] assert vp.wcs.wcs.ctype[1] == 'DEC--SIN', vp.wcs.wcs.ctype[1] worldloc = [ra_comp * r2d, dec_comp * r2d, vp.wcs.wcs.crval[2], vp.wcs.wcs.crval[3]] nchan, npol, ny, nx = vp.data.shape try: pixloc = vp.wcs.sub(2).wcs_world2pix([worldloc[:2]], 1)[0] assert pixloc[0] > 2 assert pixloc[0] < nx - 3 assert pixloc[1] > 2 assert pixloc[1] < ny - 3 gain = real_splines[ivp].ev(pixloc[1], pixloc[0]) \ + 1j * imag_splines[ivp](pixloc[1], pixloc[0]) antgain[ant] += vp_coeffs[ant, ivp] * gain antwt[ant] = 1.0 number_good += 1 except: number_bad += 1 antgain[ant] = 1e15 antwt[ant] = 0.0 antgain[ant] = 1.0/antgain[ant] gaintables[icomp].gain[iha, :, :, :] = antgain[:, numpy.newaxis, numpy.newaxis, numpy.newaxis] gaintables[icomp].weight[iha, :, :, :] = antwt[:, numpy.newaxis, numpy.newaxis, numpy.newaxis] gaintables[icomp].phasecentre = comp.direction if number_bad > 0: log.warning( "simulate_gaintable_from_voltage_patterns: %d points are inside the voltage pattern image" % (number_good)) log.warning( "simulate_gaintable_from_voltage_patterns: %d points are outside the voltage pattern image" % (number_bad)) return gaintables