def interpolate_per_source (self,lm_list,dl,dm,grid,vbs,thetaphi=False,rotate=None,masklist=None): # Loops over all source coordinates in the lm tensor, interpolates beams for them, and returns the resulting vellsets. # lm is a list of [[l0,m0],[l1,m1],...] source coordinates # This is the "fail-safe" method, as it interpolates per-source, and therefore allows for the source lm arrays to have different # time/frequency dependencies per source. vellsets = []; for isrc,(l,m) in enumerate(lm_list): # apply pointing offsets, if any if dl is not None: # unite shapes just in case, since l/m and dl/dm may have time/freq axes l,dl,m,dm = unite_multiple_shapes(l,dl,m,dm); # if mask is set, make sure it has the same shape mask = masklist[isrc] if masklist is not None else None; if mask is not None: l,m,mask = unite_multiple_shapes(l,m,mask); grid['l'],grid['m'] = l,m; # loop over all 2x2 matrices (we may have several, they all need to be added) E = [None]*4; for vbmat in vbs: for i,vb in enumerate(vbmat): beam = vb.interpolate(freqaxis=self._freqaxis,lm=self.interpol_lm,thetaphi=thetaphi,rotate=rotate,mask=mask,**grid); if E[i] is None: E[i] = meq.complex_vells(beam.shape); E[i][...] += beam[...]; # make vellsets for ej in E: vellsets.append(meq.vellset(ej)); return vellsets;
def interpolate_per_source(self, lm_list, dl, dm, grid, vbs, thetaphi=False, rotate=None, masklist=None): # Loops over all source coordinates in the lm tensor, interpolates beams for them, and returns the resulting vellsets. # lm is a list of [[l0,m0],[l1,m1],...] source coordinates # This is the "fail-safe" method, as it interpolates per-source, and therefore allows for the source lm arrays to have different # time/frequency dependencies per source. vellsets = [] for isrc, (l, m) in enumerate(lm_list): # apply pointing offsets, if any if dl is not None: # unite shapes just in case, since l/m and dl/dm may have time/freq axes l, dl, m, dm = unite_multiple_shapes(l, dl, m, dm) # if mask is set, make sure it has the same shape mask = masklist[isrc] if masklist is not None else None if mask is not None: l, m, mask = unite_multiple_shapes(l, m, mask) grid['l'], grid['m'] = l, m # loop over all 2x2 matrices (we may have several, they all need to be added) E = [None] * 4 for vbmat in vbs: for i, vb in enumerate(vbmat): beam = vb.interpolate(freqaxis=self._freqaxis, lm=self.interpol_lm, thetaphi=thetaphi, rotate=rotate, mask=mask, **grid) if E[i] is None: E[i] = meq.complex_vells(beam.shape) E[i][...] += beam[...] # make vellsets for ej in E: vellsets.append(meq.vellset(ej)) return vellsets
def get_result(self, request, radec, radec0, dlm=None, azel=None, pa=None): # get list of VoltageBeams vbs = self.init_voltage_beams() # now, figure out the radec and time/freq grid # radec may be a 2/3-vector or an Nx2/3 tensor dims = getattr(radec, 'dims', [len(radec.vellsets)]) if len(dims) == 2 and dims[1] == 2: radec_list = [(radec.vellsets[i].value, radec.vellsets[i + 1].value) for i in range(0, len(radec.vellsets), dims[1])] tensor = True elif len(dims) == 1 and dims[0] == 2: radec_list = [(radec.vellsets[0].value, radec.vellsets[1].value)] tensor = False else: raise TypeError, "expecting a 2-vector or an Nx2 matrix for child 0 (radec)" nsrc = len(radec_list) # radec0 is a 2-vector if len(radec0.vellsets) != 2: raise TypeError, "expecting a 2-vector for child 1 (radec0)" ra0, dec0 = radec0.vellsets[0].value, radec0.vellsets[1].value # get offsets and rotations masklist = rotate = None # pointing offsets are optional -- apply them in here if dlm is not None and len(dlm.vellsets) == 2: dl, dm = dlm.vellsets[0].value, dlm.vellsets[1].value ra0, dl, dec0, dm = unite_multiple_shapes(ra0, dl, dec0, dm) ra0, dec0 = self.lm_to_radec(dl, dm, ra0, dec0) # az/el is optional. If specified, then horizon masking is enabled if azel is not None and len(azel.vellsets) > 1: if len(azel.vellsets) != nsrc * 2: raise TypeError, "expecting a Nx2 matrix for child 3 (azel)" masklist = [ azel.vellsets[i].value < 0 for i in range(1, nsrc * 2, 2) ] # PA is optional. If specified, then this is the rotation angle for interpolation if pa is not None: if len(pa.vellsets) != 1: raise TypeError, "expecting a single value for child 4 (pa)" rotate = pa.vellsets[0].value thetaphi_list = [] # precompute these -- may be functions of time if time-variable pointing is in effect sind0, cosd0 = numpy.sin(dec0), numpy.cos(dec0) # now go over all coordinates for ra, dec in radec_list: # This is based on the formula in Tigger/Coordinates.py, except we flip the sign of phi (by swapping ra0 and ra around), # since rotation is meant to go N through W here dra = numpy.subtract(*unite_shapes(ra, ra0)) cosra, sinra = numpy.cos(dra), numpy.sin(dra) sind0x, sind, cosd0x, cosd, cosra, sinra = unite_multiple_shapes( sind0, numpy.sin(dec), cosd0, numpy.cos(dec), cosra, sinra) theta = numpy.arccos(sind0x * sind + cosd0x * cosd * cosra) phi = numpy.arctan2(-cosd * sinra, -cosd * sind0x * cosra + sind * cosd0x) thetaphi_list.append((theta, phi)) dprint(2, "theta/phi", thetaphi_list) # setup grid dict that will be passed to VoltageBeam.interpolate grid = dict() for axis in 'time', 'freq': values = _cells_grid(radec, axis) if values is None: values = _cells_grid(request, axis) if values is not None: if numpy.isscalar(values) or values.ndim == 0: values = [float(values)] grid[axis] = values # accumulate per-source EJones tensor vellsets = self.interpolate_batch(thetaphi_list, None, None, grid, vbs, thetaphi=True, rotate=rotate, masklist=masklist) # create result object cells = request.cells result = meq.result(vellsets[0], cells=cells) result.vellsets[1:] = vellsets[1:] result.dims = (len(radec_list), 2, 2) if tensor else (2, 2) return result
def interpolate_batch(self, lm_list, dl, dm, grid, vbs, thetaphi=False, rotate=None, masklist=None): # A faster version of interpolate_per_source(), which assumes that all lm's (as well as the masks, if given) # have the same shape, and stacks them into a single array for a single interpolation call. # If there's a shape mismatch, it'll fall back to interpolate_per_source # 'maskarr', if given, should be an list of per-source mask arrays nsrc = len(lm_list) maskcube = None for isrc, (l, m) in enumerate(lm_list): mask = masklist[isrc] if masklist is not None else None # apply pointing offsets, if any if dl is not None: # unite shapes just in case, since l/m and dl/dm may have time/freq axes l, dl, m, dm = unite_multiple_shapes(l, dl, m, dm) l, m = l - dl, m - dm # unite l,m shapes just in case, and transform l, m, mask = unite_multiple_shapes(l, m, mask) if not isrc: lm_shape = l.shape cubeshape = [nsrc] + list(lm_shape) lcube = numpy.zeros(cubeshape, float) mcube = numpy.zeros(cubeshape, float) if masklist is not None: maskcube = numpy.zeros(cubeshape, bool) else: if l.shape != lm_shape: dprint( 1, "l/m shapes unequal at source %d, falling back to per-source interpolation" % isrc) return self.interpolate_per_source(lm_list, dl, dm, grid, vbs, thetaphi=thetaphi, rotate=rotate, masklist=masklist) lcube[isrc, ...] = l mcube[isrc, ...] = m if mask is not None: maskcube[isrc, ...] = mask # if mask.any(): # dprint(2,"source %d has %d slots masked"%(isrc,mask.sum())); # if 'rotate' is specified, it needs to be promoted to the same cube shape, and ravelled if rotate is not None: lcube, mcube, maskcube, rotate = unite_multiple_shapes( lcube, mcube, maskcube, rotate.reshape([1] + list(rotate.shape))) cubeshape = list(lcube.shape) rotate = rotate.ravel() # ok, we've stacked things into lm cubes, interpolate grid['l'], grid['m'] = lcube.ravel(), mcube.ravel() # loop over all 2x2 matrices (we may have several, they all need to be added) E = [None] * 4 for vbmat in vbs: for i, vb in enumerate(vbmat): beam = vb.interpolate(freqaxis=self._freqaxis, extra_axes=1, thetaphi=thetaphi, rotate=rotate, **grid) if E[i] is None: E[i] = beam else: E[i] += beam # The l/m cubes have a shape of [nsrcs,lm_shape]. # These are raveled for interpolation, so the resulting Es have a shape of [nsrcs*num_lm_points,num_freq] # Reshape them properly. Note that there's an extra "source" axis at the front, so the frequency axis # is off by 1. if len(cubeshape) <= self._freqaxis + 1: cubeshape = list(cubeshape) + [1] * (self._freqaxis - len(cubeshape) + 2) cubeshape[self._freqaxis + 1] = len(grid['freq']) E = [ej.reshape(cubeshape) for ej in E] # apply mask cube, if we had one if maskcube is not None: # the E planes now have the same shape as the maskcube, but with an extra frequency axis. Promote the maskcube # accordingly me = unite_multiple_shapes(maskcube, *E) maskcube = me[0] E = me[1:] dprint(2, "maskcube sum", maskcube.sum()) for ej in E: ej[maskcube] = 0 # now tease the E's apart plane by plane # make vellsets vellsets = [] for isrc in range(nsrc): for ej in E: dprint( 2, "source %d has %d null gains" % (isrc, (ej[isrc, ...] == 0).sum())) ejplane = ej[isrc, ...] value = meq.complex_vells(ejplane.shape, ejplane) vellsets.append(meq.vellset(value)) return vellsets
def get_result (self,request,radec,radec0,dlm=None,azel=None,pa=None): # get list of VoltageBeams vbs = self.init_voltage_beams(); # now, figure out the radec and time/freq grid # radec may be a 2/3-vector or an Nx2/3 tensor dims = getattr(radec,'dims',[len(radec.vellsets)]); if len(dims) == 2 and dims[1] == 2: radec_list = [ (radec.vellsets[i].value,radec.vellsets[i+1].value) for i in range(0,len(radec.vellsets),dims[1]) ]; tensor = True; elif len(dims) == 1 and dims[0] == 2: radec_list = [ (radec.vellsets[0].value,radec.vellsets[1].value) ]; tensor = False; else: raise TypeError,"expecting a 2-vector or an Nx2 matrix for child 0 (radec)"; nsrc = len(radec_list); # radec0 is a 2-vector if len(radec0.vellsets) != 2: raise TypeError,"expecting a 2-vector for child 1 (radec0)"; ra0,dec0 = radec0.vellsets[0].value,radec0.vellsets[1].value; # get offsets and rotations masklist = rotate = None; # pointing offsets are optional -- apply them in here if dlm is not None and len(dlm.vellsets) == 2: dl,dm = dlm.vellsets[0].value,dlm.vellsets[1].value; ra0,dl,dec0,dm = unite_multiple_shapes(ra0,dl,dec0,dm); ra0,dec0 = self.lm_to_radec(dl,dm,ra0,dec0); # az/el is optional. If specified, then horizon masking is enabled if azel is not None and len(azel.vellsets) > 1: if len(azel.vellsets) != nsrc*2: raise TypeError,"expecting a Nx2 matrix for child 3 (azel)"; masklist = [ azel.vellsets[i].value<0 for i in range(1,nsrc*2,2) ]; # PA is optional. If specified, then this is the rotation angle for interpolation if pa is not None: if len(pa.vellsets) != 1: raise TypeError,"expecting a single value for child 4 (pa)"; rotate = pa.vellsets[0].value; thetaphi_list = []; # precompute these -- may be functions of time if time-variable pointing is in effect sind0,cosd0 = numpy.sin(dec0),numpy.cos(dec0); # now go over all coordinates for ra,dec in radec_list: # This is based on the formula in Tigger/Coordinates.py, except we flip the sign of phi (by swapping ra0 and ra around), # since rotation is meant to go N through W here dra = numpy.subtract(*unite_shapes(ra,ra0)); cosra,sinra = numpy.cos(dra),numpy.sin(dra); sind0x,sind,cosd0x,cosd,cosra,sinra = unite_multiple_shapes(sind0,numpy.sin(dec),cosd0,numpy.cos(dec),cosra,sinra); theta = numpy.arccos(sind0x*sind + cosd0x*cosd*cosra); phi = numpy.arctan2(-cosd*sinra,-cosd*sind0x*cosra+sind*cosd0x); thetaphi_list.append((theta,phi)); dprint(2,"theta/phi",thetaphi_list); # setup grid dict that will be passed to VoltageBeam.interpolate grid = dict(); for axis in 'time','freq': values = _cells_grid(radec,axis); if values is None: values = _cells_grid(request,axis); if values is not None: if numpy.isscalar(values) or values.ndim == 0: values = [float(values)]; grid[axis] = values; # accumulate per-source EJones tensor vellsets = self.interpolate_batch(thetaphi_list,None,None,grid,vbs,thetaphi=True,rotate=rotate,masklist=masklist); # create result object cells = request.cells; result = meq.result(vellsets[0],cells=cells); result.vellsets[1:] = vellsets[1:]; result.dims = (len(radec_list),2,2) if tensor else (2,2); return result;
def interpolate_batch (self,lm_list,dl,dm,grid,vbs,thetaphi=False,rotate=None,masklist=None): # A faster version of interpolate_per_source(), which assumes that all lm's (as well as the masks, if given) # have the same shape, and stacks them into a single array for a single interpolation call. # If there's a shape mismatch, it'll fall back to interpolate_per_source # 'maskarr', if given, should be an list of per-source mask arrays nsrc = len(lm_list); maskcube = None; for isrc,(l,m) in enumerate(lm_list): mask = masklist[isrc] if masklist is not None else None; # apply pointing offsets, if any if dl is not None: # unite shapes just in case, since l/m and dl/dm may have time/freq axes l,dl,m,dm = unite_multiple_shapes(l,dl,m,dm); l,m = l-dl,m-dm; # unite l,m shapes just in case, and transform l,m,mask = unite_multiple_shapes(l,m,mask); if not isrc: lm_shape = l.shape; cubeshape = [nsrc]+list(lm_shape); lcube = numpy.zeros(cubeshape,float); mcube = numpy.zeros(cubeshape,float); if masklist is not None: maskcube = numpy.zeros(cubeshape,bool); else: if l.shape != lm_shape: dprint(1,"l/m shapes unequal at source %d, falling back to per-source interpolation"%isrc); return self.interpolate_per_source(lm_list,dl,dm,grid,vbs,thetaphi=thetaphi,rotate=rotate,masklist=masklist); lcube[isrc,...] = l; mcube[isrc,...] = m; if mask is not None: maskcube[isrc,...] = mask; # if mask.any(): # dprint(2,"source %d has %d slots masked"%(isrc,mask.sum())); # if 'rotate' is specified, it needs to be promoted to the same cube shape, and ravelled if rotate is not None: lcube,mcube,maskcube,rotate = unite_multiple_shapes(lcube,mcube,maskcube,rotate.reshape([1]+list(rotate.shape))); cubeshape = list(lcube.shape); rotate = rotate.ravel(); # ok, we've stacked things into lm cubes, interpolate grid['l'],grid['m'] = lcube.ravel(),mcube.ravel(); # loop over all 2x2 matrices (we may have several, they all need to be added) E = [None]*4; for vbmat in vbs: for i,vb in enumerate(vbmat): beam = vb.interpolate(freqaxis=self._freqaxis,extra_axes=1,thetaphi=thetaphi,rotate=rotate,**grid); if E[i] is None: E[i] = beam; else: E[i] += beam; # The l/m cubes have a shape of [nsrcs,lm_shape]. # These are raveled for interpolation, so the resulting Es have a shape of [nsrcs*num_lm_points,num_freq] # Reshape them properly. Note that there's an extra "source" axis at the front, so the frequency axis # is off by 1. if len(cubeshape) <= self._freqaxis+1: cubeshape = list(cubeshape) + [1]*(self._freqaxis - len(cubeshape) + 2); cubeshape[self._freqaxis+1] = len(grid['freq']); E = [ ej.reshape(cubeshape) for ej in E ]; # apply mask cube, if we had one if maskcube is not None: # the E planes now have the same shape as the maskcube, but with an extra frequency axis. Promote the maskcube # accordingly me = unite_multiple_shapes(maskcube,*E); maskcube = me[0]; E = me[1:]; dprint(2,"maskcube sum",maskcube.sum()); for ej in E: ej[maskcube] = 0; # now tease the E's apart plane by plane # make vellsets vellsets = []; for isrc in range(nsrc): for ej in E: dprint(2,"source %d has %d null gains"%(isrc,(ej[isrc,...]==0).sum())); ejplane = ej[isrc,...]; value = meq.complex_vells(ejplane.shape,ejplane); vellsets.append(meq.vellset(value)); return vellsets;