def integrate_visibility_by_channel(vis: BlockVisibility) -> BlockVisibility: """ Integrate visibility across channels, returning new visibility :param vis: :return: BlockVisibility """ assert isinstance(vis, Visibility) or isinstance(vis, BlockVisibility), vis vis_shape = list(vis.vis.shape) ntimes, nants, _, nchan, npol = vis_shape vis_shape[-2] = 1 newvis = BlockVisibility( data=None, frequency=numpy.ones([1]) * numpy.average(vis.frequency), channel_bandwidth=numpy.ones([1]) * numpy.sum(vis.channel_bandwidth), phasecentre=vis.phasecentre, configuration=vis.configuration, uvw=vis.uvw, time=vis.time, vis=numpy.zeros(vis_shape, dtype='complex'), weight=numpy.ones(vis_shape, dtype='float'), integration_time=vis.integration_time, polarisation_frame=vis.polarisation_frame) newvis.data['vis'][..., 0, :] = numpy.sum(vis.data['vis'] * vis.data['weight'], axis=-2) newvis.data['weight'][..., 0, :] = numpy.sum(vis.data['weight'], axis=-2) mask = newvis.data['weight'] > 0.0 newvis.data['vis'][ mask] = newvis.data['vis'][mask] / newvis.data['weight'][mask] return newvis
def divide_visibility(vis: BlockVisibility, modelvis: BlockVisibility): """ Divide visibility by model forming visibility for equivalent point source This is a useful intermediate product for calibration. Variation of the visibility in time and frequency due to the model structure is removed and the data can be averaged to a limit determined by the instrumental stability. The weight is adjusted to compensate for the division. Zero divisions are avoided and the corresponding weight set to zero. :param vis: :param modelvis: :return: """ assert isinstance(vis, Visibility) or isinstance(vis, BlockVisibility), vis # Different for scalar and vector/matrix cases isscalar = vis.polarisation_frame.npol == 1 if isscalar: # Scalar case is straightforward x = numpy.zeros_like(vis.vis) xwt = numpy.abs(modelvis.vis)**2 * vis.weight mask = xwt > 0.0 x[mask] = vis.vis[mask] / modelvis.vis[mask] else: nrows, nants, _, nchan, npol = vis.vis.shape nrec = 2 assert nrec * nrec == npol xshape = (nrows, nants, nants, nchan, nrec, nrec) x = numpy.zeros(xshape, dtype='complex') xwt = numpy.zeros(xshape) for row in range(nrows): for ant1 in range(nants): for ant2 in range(ant1 + 1, nants): for chan in range(nchan): ovis = numpy.matrix(vis.vis[row, ant2, ant1, chan].reshape([2, 2])) mvis = numpy.matrix(modelvis.vis[row, ant2, ant1, chan].reshape([2, 2])) wt = numpy.matrix(vis.weight[row, ant2, ant1, chan].reshape([2, 2])) x[row, ant2, ant1, chan] = numpy.matmul(numpy.linalg.inv(mvis), ovis) xwt[row, ant2, ant1, chan] = numpy.dot(mvis, numpy.multiply(wt, mvis.H)).real x = x.reshape((nrows, nants, nants, nchan, nrec * nrec)) xwt = xwt.reshape((nrows, nants, nants, nchan, nrec * nrec)) pointsource_vis = BlockVisibility(data=None, frequency=vis.frequency, channel_bandwidth=vis.channel_bandwidth, phasecentre=vis.phasecentre, configuration=vis.configuration, uvw=vis.uvw, time=vis.time, integration_time=vis.integration_time, vis=x, weight=xwt) return pointsource_vis
def apply_gaintable(vis: BlockVisibility, gt: GainTable, inverse=False, **kwargs) -> BlockVisibility: """Apply a gain table to a block visibility The corrected visibility is:: V_corrected = {g_i * g_j^*}^-1 V_obs If the visibility data are polarised e.g. polarisation_frame("linear") then the inverse operator represents an actual inverse of the gains. :param vis: Visibility to have gains applied :param gt: Gaintable to be applied :param inverse: Apply the inverse (default=False) :return: input vis with gains applied """ assert type( vis) is BlockVisibility, "vis is not a BlockVisibility: %r" % vis assert type(gt) is GainTable, "gt is not a GainTable: %r" % gt assert_vis_gt_compatible(vis, gt) if inverse: log.debug('apply_gaintable: Apply inverse gaintable') else: log.debug('apply_gaintable: Apply gaintable') # 按照time对vis进行切片 for chunk, rows in enumerate(vis_timeslice_iter(vis)): vistime = numpy.average(vis.time[rows]) integration_time = numpy.average(vis.integration_time[rows]) gaintable_rows = abs(gt.time - vistime) < integration_time / 2.0 # Lookup the gain for this set of visibilities gain = gt.data['gain'][gaintable_rows] # The shape of the mueller matrix is ntimes, nant, nchan, nrec, _ = gain.shape original = vis.vis[rows] applied = copy.deepcopy(original) for time in range(ntimes): for a1 in range(vis.nants - 1): for a2 in range(a1 + 1, vis.nants): for chan in range(nchan): mueller = numpy.kron( gain[time, a1, chan, :, :], numpy.conjugate(gain[time, a2, chan, :, :])) if inverse: mueller = numpy.linalg.inv(mueller) applied[time, a2, a1, chan, :] = numpy.matmul( mueller, original[time, a2, a1, chan, :]) vis.data['vis'][rows] = applied return vis
def extract_channel(v, chan): vis_shape = numpy.array(v.data['vis'].shape) vis_shape[3] = 1 vis = BlockVisibility(data=None, frequency=numpy.array([v.frequency[chan]]), channel_bandwidth=numpy.array([v.channel_bandwidth[chan]]), phasecentre=v.phasecentre, configuration=v.configuration, uvw=v.uvw, time=v.time, vis=v.vis[..., chan, :][..., numpy.newaxis, :], weight=v.weight[..., chan, :][..., numpy.newaxis, :], integration_time=v.integration_time, polarisation_frame=v.polarisation_frame) return vis
def visibility_gather_channel(vis_list: List[Visibility], vis: Visibility = None, **kwargs): """ Gather a visibility by channel :param vis_list: :param vis: :param kwargs: :return: """ cols = ['vis', 'weight'] if vis is None: vis_shape = numpy.array(vis_list[0].vis.shape) vis_shape[-2] = len(vis_list) for v in vis_list: assert len(v.frequency) == 1 assert len(v.channel_bandwidth) == 1 vis = BlockVisibility(data=None, frequency=numpy.array([v.frequency[0] for v in vis_list]), channel_bandwidth=numpy.array([v.channel_bandwidth[0] for v in vis_list]), phasecentre=vis_list[0].phasecentre, configuration=vis_list[0].configuration, uvw=vis_list[0].uvw, time=vis_list[0].time, vis=numpy.zeros(vis_shape, dtype=vis_list[0].vis.dtype), weight=numpy.ones(vis_shape, dtype=vis_list[0].weight.dtype), integration_time=vis_list[0].integration_time, polarisation_frame=vis_list[0].polarisation_frame) assert len(vis.frequency) == len(vis_list) for chan, _ in enumerate(vis_list): subvis = vis_list[chan] assert abs(subvis.frequency[0] - vis.frequency[chan]) < 1e-15 for col in cols: vis.data[col][..., chan, :] = subvis.data[col][..., 0, :] vis.frequency[chan] = subvis.frequency[0] nchan = vis.vis.shape[-2] assert nchan == len(vis.frequency) return vis
def apply_gaintable(vis: BlockVisibility, gt: GainTable, inverse=False, **kwargs) -> BlockVisibility: """Apply a gain table to a block visibility The corrected visibility is:: V_corrected = {g_i * g_j^*}^-1 V_obs If the visibility data are polarised e.g. polarisation_frame("linear") then the inverse operator represents an actual inverse of the gains. :param vis: Visibility to have gains applied :param gt: Gaintable to be applied :param inverse: Apply the inverse (default=False) :return: input vis with gains applied """ assert isinstance( vis, BlockVisibility), "vis is not a BlockVisibility: %r" % vis assert isinstance(gt, GainTable), "gt is not a GainTable: %r" % gt assert_vis_gt_compatible(vis, gt) if inverse: log.debug('apply_gaintable: Apply inverse gaintable') else: log.debug('apply_gaintable: Apply gaintable') is_scalar = gt.gain.shape[-2:] == (1, 1) if is_scalar: log.debug('apply_gaintable: scalar gains') for chunk, rows in enumerate(vis_timeslice_iter(vis, **kwargs)): if numpy.sum(rows) > 0: vistime = numpy.average(vis.time[rows]) gaintable_rows = abs(gt.time - vistime) < gt.interval / 2.0 # Lookup the gain for this set of visibilities gain = gt.data['gain'][gaintable_rows] # The shape of the mueller matrix is ntimes, nant, nchan, nrec, _ = gain.shape original = vis.vis[rows] applied = copy.deepcopy(original) for time in range(ntimes): for a1 in range(vis.nants - 1): for a2 in range(a1 + 1, vis.nants): if is_scalar: smueller = gain[time, a1, :, 0] * numpy.conjugate( gain[time, a2, :, 0]) if inverse: if numpy.abs(smueller).all() > 0.0: applied[time, a2, a1, :, 0][..., numpy.newaxis] = original[ time, a2, a1, :, 0][..., numpy.newaxis] / smueller else: applied[time, a2, a1, :, 0][..., numpy.newaxis] = original[ time, a2, a1, :, 0][..., numpy.newaxis] * smueller else: for chan in range(nchan): mueller = numpy.kron( gain[time, a1, chan, :, :], numpy.conjugate(gain[time, a2, chan, :, :])) if inverse: # If the Mueller is singular, ignore it try: mueller = numpy.linalg.inv(mueller) applied[time, a2, a1, chan, :] = numpy.matmul( mueller, original[time, a2, a1, chan, :]) except numpy.linalg.linalg.LinAlgError: applied[time, a2, a1, chan, :] = original[time, a2, a1, chan, :] else: applied[time, a2, a1, chan, :] = numpy.matmul( mueller, original[time, a2, a1, chan, :]) vis.data['vis'][rows] = applied return vis
def create_blockvisibility(config: Configuration, times: numpy.array, frequency: numpy.array, phasecentre: SkyCoord, weight: float = 1.0, polarisation_frame: PolarisationFrame = None, integration_time=1.0, channel_bandwidth=1e6, zerow=False, **kwargs) -> BlockVisibility: """ Create a BlockVisibility from Configuration, hour angles, and direction of source Note that we keep track of the integration time for BDA purposes :param config: Configuration of antennas :param times: hour angles in radians :param frequency: frequencies (Hz] [nchan] :param weight: weight of a single sample :param phasecentre: phasecentre of observation :param channel_bandwidth: channel bandwidths: (Hz] [nchan] :param integration_time: Integration time ('auto' or value in s) :param polarisation_frame: :return: BlockVisibility """ assert phasecentre is not None, "Must specify phase centre" if polarisation_frame is None: polarisation_frame = correlate_polarisation(config.receptor_frame) nch = len(frequency) ants_xyz = config.data['xyz'] nants = len(config.data['names']) ntimes = len(times) npol = polarisation_frame.npol visshape = [ntimes, nants, nants, nch, npol] rvis = numpy.zeros(visshape, dtype='complex') rweight = weight * numpy.ones(visshape) rtimes = numpy.zeros([ntimes]) ruvw = numpy.zeros([ntimes, nants, nants, 3]) # Do each hour angle in turn for iha, ha in enumerate(times): # Calculate the positions of the antennas as seen for this hour angle # and declination ant_pos = xyz_to_uvw(ants_xyz, ha, phasecentre.dec.rad) rtimes[iha] = ha * 43200.0 / numpy.pi # Loop over all pairs of antennas. Note that a2>a1 for a1 in range(nants): for a2 in range(a1 + 1, nants): ruvw[iha, a2, a1, :] = (ant_pos[a2, :] - ant_pos[a1, :]) ruvw[iha, a1, a2, :] = (ant_pos[a1, :] - ant_pos[a2, :]) rintegration_time = numpy.full_like(rtimes, integration_time) rchannel_bandwidth = numpy.full_like(frequency, channel_bandwidth) if zerow: ruvw[..., 2] = 0.0 vis = BlockVisibility(uvw=ruvw, time=rtimes, frequency=frequency, vis=rvis, weight=rweight, integration_time=rintegration_time, channel_bandwidth=rchannel_bandwidth, polarisation_frame=polarisation_frame) vis.phasecentre = phasecentre vis.configuration = config log.info("create_blockvisibility: %s" % (vis_summary(vis))) assert isinstance( vis, BlockVisibility), "vis is not a BlockVisibility: %r" % vis return vis
def divide_visibility(vis: BlockVisibility, modelvis: BlockVisibility): """ Divide visibility by model forming visibility for equivalent point source This is a useful intermediate product for calibration. Variation of the visibility in time and frequency due to the model structure is removed and the data can be averaged to a limit determined by the instrumental stability. The weight is adjusted to compensate for the division. Zero divisions are avoided and the corresponding weight set to zero. :param vis: :param modelvis: :return: """ # Different for scalar and vector/matrix cases # 使用modelvis去除以vis isscalar = vis.polarisation_frame.npol == 1 # 若npol=1 if isscalar: # Scalar case is straightforward x = numpy.zeros_like(vis.vis) xwt = numpy.abs(modelvis.vis)**2 * vis.weight mask = xwt > 0.0 x[mask] = vis.vis[mask] / modelvis.vis[mask] # 否则 else: nrows, nants, _, nchan, npol = vis.vis.shape nrec = 2 assert nrec * nrec == npol xshape = (nrows, nants, nants, nchan, nrec, nrec) x = numpy.zeros(xshape, dtype='complex') xwt = numpy.zeros(xshape) # 该处因为ant2永远大于ant1,所以实际上blockvisibility中vis的所有第1维id小于等于第2维id的值都未被修改 for row in range(nrows): for ant1 in range(nants): for ant2 in range(ant1 + 1, nants): for chan in range(nchan): # print(vis.vis[row, ant2, ant1, chan]) # print(row, ant2, ant1, chan) ovis = numpy.matrix(vis.vis[row, ant2, ant1, chan].reshape([2, 2])) mvis = numpy.matrix(modelvis.vis[row, ant2, ant1, chan].reshape([2, 2])) wt = numpy.matrix(vis.weight[row, ant2, ant1, chan].reshape([2, 2])) x[row, ant2, ant1, chan] = numpy.matmul(numpy.linalg.inv(mvis), ovis) xwt[row, ant2, ant1, chan] = numpy.dot(mvis, numpy.multiply(wt, mvis.H)).real x[abs(x) < 1e-10] = 0.0 # 计算得到新的vis和新的权重, 并且矩阵大部分值为0,因为只计算了ant2 > ant1的部分, 个人认为可以直接使用visibility计算 x = x.reshape((nrows, nants, nants, nchan, nrec * nrec)) xwt = xwt.reshape((nrows, nants, nants, nchan, nrec * nrec)) # 只有vis和weight得到了新的值,其他数据均不变 pointsource_vis = BlockVisibility(data=None, frequency=vis.frequency, channel_bandwidth=vis.channel_bandwidth, phasecentre=vis.phasecentre, configuration=vis.configuration, uvw=vis.uvw, time=vis.time, integration_time=vis.integration_time, vis=x, weight=xwt) return pointsource_vis