Skip to content

Commit d24012c

Browse files
author
Casey Law
committed
added leanpipedt and whole new build instructions
1 parent c9e08b4 commit d24012c

7 files changed

+2762
-0
lines changed

applycals2.py

+333
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
import numpy as n
2+
import os, glob
3+
import casautil
4+
5+
tb = casautil.tools.table()
6+
7+
class solutions():
8+
""" Container for CASA caltable(s).
9+
Provides tools for applying to data of shape (nints, nbl, nch, npol).
10+
Initialize class based on input file(s) and selection criteria.
11+
Optional flux scale gain file can be given. Should be gain file applied to source with setjy applied.
12+
"""
13+
14+
def __init__(self, gainfile, flagants=True):
15+
""" Initialize with a table of CASA gain solutions. Can later add BP.
16+
"""
17+
18+
self.parsegain(gainfile)
19+
self.flagants = flagants
20+
21+
def parsegain(self, gainfile):
22+
"""Takes .g1 CASA cal table and places values in numpy arrays.
23+
"""
24+
25+
tb.open(gainfile)
26+
mjd = tb.getcol('TIME')/(24*3600) # mjd days, as for telcal
27+
spw = tb.getcol('SPECTRAL_WINDOW_ID')
28+
gain = tb.getcol('CPARAM') # dimensions of (npol, 1?, ntimes*nants)
29+
snr = tb.getcol('SNR')
30+
flagged = tb.getcol('FLAG')
31+
tb.close()
32+
tb.open(gainfile + '/ANTENNA')
33+
antname = tb.getcol('NAME') # ant number in order written to gain file
34+
antnum = n.array([int(antname[i][2:]) for i in range(len(antname))])
35+
tb.close()
36+
37+
# # need to find parent data MS to get some metadata
38+
# mslist = glob.glob(gainfile[:-3] + '*.ms')
39+
# try:
40+
# msfile = mslist[0]
41+
# print 'Found parent data MS %s' % msfile
42+
# except IndexError:
43+
# print 'Could not find parent data MS for metadata...'
44+
45+
# tb.open(msfile + '/ANTENNA')
46+
# antname = tb.getcol('NAME') # one name per ant
47+
# tb.close()
48+
# tb.open(msfile + '/SPECTRAL_WINDOW')
49+
# reffreq = 1e-6*(tb.getcol('REF_FREQUENCY')+tb.getcol('TOTAL_BANDWIDTH')/2) # similar to telcal "skyfreq"
50+
# specname = tb.getcol('NAME')
51+
# tb.close()
52+
# tb.open(msfile + '/SOURCE')
53+
# source = [name for name in tb.getcol('NAME') if 'J' in name][0] # should return single cal name **hack**
54+
# tb.close()
55+
# nsol = len(gain[0,0])
56+
57+
# ifid0R = specname[0][7] + '-' + specname[0][8] # one value
58+
# ifid0L = specname[0][9] + '-' + specname[0][10] # one value
59+
# ifid1R = specname[1][7] + '-' + specname[1][8] # one value
60+
# ifid1L = specname[1][9] + '-' + specname[1][10] # one value
61+
62+
# # paste R,L end to end, so first loop over time, then spw, then pol
63+
# mjd = n.concatenate( (time, time), axis=0)
64+
# ifid = [ifid0R]*(nsol/2) + [ifid1R]*(nsol/2) + [ifid0L]*(nsol/2) + [ifid1L]*(nsol/2) # first quarter is spw0,pol0, then spw1,pol0, ...
65+
# skyfreq = n.concatenate( (reffreq[0]*n.ones(nsol/2), reffreq[1]*n.ones(nsol/2), reffreq[0]*n.ones(nsol/2), reffreq[1]*n.ones(nsol/2)), axis=0)
66+
# gain = n.concatenate( (gain[0,0],gain[1,0]), axis=0)
67+
# amp = n.abs(gain)
68+
# phase = n.degrees(n.angle(gain))
69+
# source = [source]*nsol*2
70+
# flagged = n.concatenate( (flag[0,0],flag[1,0]), axis=0)
71+
72+
nants = len(n.unique(antnum))
73+
nspw = len(n.unique(spw))
74+
self.spwlist = n.unique(spw)
75+
npol = len(gain)
76+
77+
# merge times less than 2s
78+
nsol = 0
79+
newmjd = [n.unique(mjd)[0]]
80+
skip = []
81+
for i in range(1, len(n.unique(mjd))):
82+
if 24*3600*(n.unique(mjd)[i] - n.unique(mjd)[i-1]) < 30.:
83+
skip.append(n.unique(mjd)[i])
84+
continue
85+
else:
86+
newmjd.append(n.unique(mjd)[i])
87+
88+
self.uniquemjd = n.array(newmjd)
89+
nsol = len(self.uniquemjd)
90+
91+
print 'Parsed gain table solutions for %d solutions (skipping %d), %d ants, %d spw, and %d pols' % (nsol, len(skip), nants, nspw, npol)
92+
print 'Unique solution times', self.uniquemjd
93+
94+
self.gain = n.zeros( (nsol, nants, nspw, npol), dtype='complex' )
95+
flags = n.zeros( (nsol, nants, nspw, npol), dtype='complex' )
96+
for sol in range(nsol):
97+
for ant in range(nants):
98+
for spw in range(nspw):
99+
for pol in range(npol):
100+
self.gain[sol, ant, spw, pol] = gain[pol,0,spw*nsol*nants+sol*nants+ant]
101+
flags[sol, ant, spw, pol] = flagged[pol,0,spw*nsol*nants+sol*nants+ant]
102+
self.gain = n.ma.masked_array(self.gain, flags)
103+
104+
# gain = n.concatenate( (n.concatenate( (gain[0,0,:nants*nsol].reshape(nsol,nants,1,1), gain[1,0,:nants*nsol].reshape(nsol,nants,1,1)), axis=3), n.concatenate( (gain[0,0,nants*nsol:].reshape(nsol,nants,1,1), gain[1,0,nants*nsol:].reshape(nsol,nants,1,1)), axis=3)), axis=2)
105+
# flagged = n.concatenate( (n.concatenate( (flagged[0,0,:nants*nsol].reshape(nsol,nants,1,1), flagged[1,0,:nants*nsol].reshape(nsol,nants,1,1)), axis=3), n.concatenate( (flagged[0,0,nants*nsol:].reshape(nsol,nants,1,1), flagged[1,0,nants*nsol:].reshape(nsol,nants,1,1)), axis=3)), axis=2)
106+
# self.gain = n.ma.masked_array(gain, flagged == True)
107+
108+
self.mjd = n.array(mjd); self.antnum = antnum
109+
110+
# make another version of ants array
111+
# self.antnum = n.concatenate( (antnum, antnum), axis=0)
112+
# self.amp = n.array(amp); self.phase = n.array(phase)
113+
# self.antname = n.concatenate( (antname[antnum], antname[antnum]), axis=0)
114+
# self.complete = n.arange(len(self.mjd))
115+
116+
# for consistency with telcal
117+
#self.ifid = n.array(ifid); self.skyfreq = n.array(skyfreq); self.source = n.array(source)
118+
119+
def parsebp(self, bpfile, debug=False):
120+
""" Takes bp CASA cal table and places values in numpy arrays.
121+
Assumes two or fewer spw. :\
122+
Assumes one bp solution per file.
123+
"""
124+
125+
# bandpass. taking liberally from Corder et al's analysisutilities
126+
([polyMode, polyType, nPolyAmp, nPolyPhase, scaleFactor, nRows, nSpws, nUniqueTimesBP, uniqueTimesBP,
127+
nPolarizations, frequencyLimits, increments, frequenciesGHz, polynomialPhase,
128+
polynomialAmplitude, timesBP, antennasBP, cal_desc_idBP, spwBP]) = openBpolyFile(bpfile, debug)
129+
130+
# index iterates over antennas, then times/sources (solution sets). each index has 2x npoly, which are 2 pols
131+
polynomialAmplitude = n.array(polynomialAmplitude); polynomialPhase = n.array(polynomialPhase)
132+
polynomialAmplitude[:,0] = 0.; polynomialAmplitude[:,nPolyAmp] = 0.
133+
polynomialPhase[:,0] = 0.; polynomialPhase[nPolyPhase] = 0.
134+
ampSolR, ampSolL = calcChebyshev(polynomialAmplitude, frequencyLimits, n.array(frequenciesGHz)*1e+9)
135+
phaseSolR, phaseSolL = calcChebyshev(polynomialPhase, frequencyLimits, n.array(frequenciesGHz)*1e+9)
136+
137+
nants = len(n.unique(antennasBP))
138+
self.bptimes = n.array(timesBP)
139+
ptsperspec = 1000
140+
npol = 2
141+
print 'Parsed bp solutions for %d solutions, %d ants, %d spw, and %d pols' % (nUniqueTimesBP, nants, nSpws, nPolarizations)
142+
self.bandpass = n.zeros( (nants, nSpws*ptsperspec, npol), dtype='complex')
143+
for spw in range(nSpws):
144+
ampSolR[spw*nants:(spw+1)*nants] += 1 - ampSolR[spw*nants:(spw+1)*nants].mean() # renormalize mean over ants (per spw) == 1
145+
ampSolL[spw*nants:(spw+1)*nants] += 1 - ampSolL[spw*nants:(spw+1)*nants].mean()
146+
for ant in range(nants):
147+
self.bandpass[ant, spw*ptsperspec:(spw+1)*ptsperspec, 0] = ampSolR[ant+spw*nants] * n.exp(1j*phaseSolR[ant+spw*nants])
148+
self.bandpass[ant, spw*ptsperspec:(spw+1)*ptsperspec, 1] = ampSolL[ant+spw*nants] * n.exp(1j*phaseSolL[ant+spw*nants])
149+
150+
self.bpfreq = n.zeros( (nSpws*ptsperspec) )
151+
for spw in range(nSpws):
152+
self.bpfreq[spw*ptsperspec:(spw+1)*ptsperspec] = 1e9 * frequenciesGHz[nants*spw]
153+
154+
# bpSolR0 = ampSolR[:nants] * n.exp(1j*phaseSolR[:nants])
155+
# bpSolR1 = ampSolR[nants:] * n.exp(1j*phaseSolR[nants:])
156+
# bpSolL0 = ampSolL[:nants] * n.exp(1j*phaseSolL[:nants])
157+
# bpSolL1 = ampSolL[nants:] * n.exp(1j*phaseSolL[nants:])
158+
159+
# structure close to tpipe data structure (nant, freq, pol). note that freq is oversampled to 1000 bins.
160+
# self.bandpass = n.concatenate( (n.concatenate( (bpSolR0[:,:,None], bpSolR1[:,:,None]), axis=1), n.concatenate( (bpSolL0[:,:,None], bpSolL1[:,:,None]), axis=1)), axis=2)
161+
# self.bpfreq = 1e9*n.concatenate( (frequenciesGHz[0], frequenciesGHz[nants]), axis=0) # freq values at bp bins
162+
# print 'Parsed bp table solutions for %d solutions, %d ants, %d spw, and %d pols' % (nUniqueTimesBP, nants, nSpws, nPolarizations)
163+
164+
def setselection(self, time, freqs, spws=[0,1], pols=[0,1], verbose=0):
165+
""" Set select parameter that defines time, spw, and pol solutions to apply.
166+
time defines the time to find solutions near in mjd.
167+
freqs defines frequencies to select bandpass solution
168+
spws is list of min/max indices to be used (e.g., [0,1])
169+
pols is index of polarizations.
170+
pols/spws not yet implemented beyond 2sb, 2pol.
171+
"""
172+
173+
# spw and pols selection not yet implemented beyond 2/2
174+
self.spws = spws
175+
self.pols = pols
176+
177+
# select by smallest time distance for source
178+
mjddist = n.abs(time - self.uniquemjd)
179+
closestgain = n.where(mjddist == mjddist.min())[0][0]
180+
181+
print 'Using gain solution at MJD %.5f, separated by %d min ' % (self.uniquemjd[closestgain], mjddist[closestgain]*24*60)
182+
self.gain = self.gain[closestgain,:,spws[0]:spws[1]+1,pols[0]:pols[1]+1]
183+
184+
if hasattr(self, 'bandpass'):
185+
bins = [n.where(n.min(n.abs(self.bpfreq-selfreq)) == n.abs(self.bpfreq-selfreq))[0][0] for selfreq in freqs]
186+
self.bandpass = self.bandpass[:,bins,pols[0]:pols[1]+1]
187+
self.freqs = freqs
188+
if verbose:
189+
print 'Using solution at BP bins: ', bins
190+
191+
def calc_flag(self, sig=3.0):
192+
""" Calculates antennas to flag, based on bad gain and bp solutions.
193+
"""
194+
195+
if len(self.gain.shape) == 4:
196+
gamp = n.abs(self.gain).mean(axis=0) # mean gain amp for each ant over time
197+
elif len(self.gain.shape) == 3:
198+
gamp = n.abs(self.gain) # gain amp for selected time
199+
200+
# badgain = n.where(gamp < gamp.mean() - sig*gamp.std())
201+
badgain = n.where( (gamp < n.median(gamp) - sig*gamp.std()) | gamp.mask)
202+
print 'Flagging low/bad gains for ant/spw/pol:', self.antnum[badgain[0]], badgain[1], badgain[2]
203+
204+
badants = badgain
205+
return badants
206+
207+
def apply(self, data, blarr):
208+
""" Applies calibration solution to data array. Assumes structure of (nint, nbl, nch, npol).
209+
blarr is array of size 2xnbl that gives pairs of antennas in each baseline (a la tpipe.blarr).
210+
"""
211+
212+
ant1ind = [n.where(ant1 == n.unique(blarr))[0][0] for (ant1,ant2) in blarr]
213+
ant2ind = [n.where(ant2 == n.unique(blarr))[0][0] for (ant1,ant2) in blarr]
214+
215+
# flag bad ants
216+
if self.flagants:
217+
badants = self.calc_flag()
218+
else:
219+
badants = n.array([[]])
220+
221+
# apply gain correction
222+
if hasattr(self, 'bandpass'):
223+
corr = n.ones_like(data)
224+
flag = n.ones_like(data).astype('int')
225+
chans_uncal = range(len(self.freqs))
226+
for spw in range(len(self.gain[0])):
227+
chsize = n.round(self.bpfreq[1]-self.bpfreq[0], 0)
228+
ww = n.where( (self.freqs >= self.bpfreq[spw*1000]) & (self.freqs <= self.bpfreq[(spw+1)*1000-1]+chsize) )[0]
229+
if len(ww) == 0:
230+
print 'Gain solution frequencies not found in data for spw %d.' % (self.spws[spw])
231+
firstch = ww[0]
232+
lastch = ww[-1]+1
233+
for ch in ww:
234+
chans_uncal.remove(ch)
235+
print 'Combining gain sol from spw=%d with BW chans from %d-%d' % (self.spws[spw], firstch, lastch)
236+
for badant in n.transpose(badants):
237+
if badant[1] == spw:
238+
badbl = n.where((badant[0] == n.array(ant1ind)) | (badant[0] == n.array(ant2ind)))[0]
239+
flag[:, badbl, firstch:lastch, badant[2]] = 0
240+
241+
corr1 = self.gain[ant1ind, spw, :][None, :, None, :] * self.bandpass[ant1ind, firstch:lastch, :][None, :, :, :]
242+
corr2 = (self.gain[ant2ind, spw, :][None, :, None, :] * self.bandpass[ant2ind, firstch:lastch, :][None, :, :, :]).conj()
243+
244+
corr[:, :, firstch:lastch, :] = corr1 * corr2
245+
if len(chans_uncal):
246+
print 'Setting data without bp solution to zero for chan range %d-%d.' % (chans_uncal[0], chans_uncal[-1])
247+
flag[:, :, chans_uncal,:] = 0
248+
data[:] *= flag/corr
249+
else:
250+
for spw in range(len(self.gain[0,0])):
251+
pass
252+
253+
def openBpolyFile(caltable, debug=False):
254+
# mytb = au.createCasaTool(tbtool) # from analysisutilities by corder
255+
tb.open(caltable)
256+
desc = tb.getdesc()
257+
if ('POLY_MODE' in desc):
258+
polyMode = tb.getcol('POLY_MODE')
259+
polyType = tb.getcol('POLY_TYPE')
260+
scaleFactor = tb.getcol('SCALE_FACTOR')
261+
antenna1 = tb.getcol('ANTENNA1')
262+
times = tb.getcol('TIME')
263+
cal_desc_id = tb.getcol('CAL_DESC_ID')
264+
nRows = len(polyType)
265+
for pType in polyType:
266+
if (pType != 'CHEBYSHEV'):
267+
print "I do not recognized polynomial type = %s" % (pType)
268+
return
269+
# Here we assume that all spws have been solved with the same mode
270+
uniqueTimesBP = n.unique(tb.getcol('TIME'))
271+
nUniqueTimesBP = len(uniqueTimesBP)
272+
if (nUniqueTimesBP >= 2):
273+
if debug:
274+
print "Multiple BP sols found with times differing by %s seconds. Using first." % (str(uniqueTimesBP-uniqueTimesBP[0]))
275+
nUniqueTimesBP = 1
276+
uniqueTimesBP = uniqueTimesBP[0]
277+
mystring = ''
278+
nPolyAmp = tb.getcol('N_POLY_AMP')
279+
nPolyPhase = tb.getcol('N_POLY_PHASE')
280+
frequencyLimits = tb.getcol('VALID_DOMAIN')
281+
increments = 0.001*(frequencyLimits[1,:]-frequencyLimits[0,:])
282+
frequenciesGHz = []
283+
for i in range(len(frequencyLimits[0])):
284+
freqs = (1e-9)*n.arange(frequencyLimits[0,i],frequencyLimits[1,i],increments[i]) # **for some reason this is nch-1 long?**
285+
frequenciesGHz.append(freqs)
286+
polynomialAmplitude = []
287+
polynomialPhase = []
288+
for i in range(len(polyMode)):
289+
polynomialAmplitude.append([1])
290+
polynomialPhase.append([0])
291+
if (polyMode[i] == 'A&P' or polyMode[i] == 'A'):
292+
polynomialAmplitude[i] = tb.getcell('POLY_COEFF_AMP',i)[0][0][0]
293+
if (polyMode[i] == 'A&P' or polyMode[i] == 'P'):
294+
polynomialPhase[i] = tb.getcell('POLY_COEFF_PHASE',i)[0][0][0]
295+
296+
tb.close()
297+
tb.open(caltable+'/CAL_DESC')
298+
nSpws = len(tb.getcol('NUM_SPW'))
299+
spws = tb.getcol('SPECTRAL_WINDOW_ID')
300+
spwBP = []
301+
for c in cal_desc_id:
302+
spwBP.append(spws[0][c])
303+
tb.close()
304+
nPolarizations = len(polynomialAmplitude[0]) / nPolyAmp[0]
305+
if debug:
306+
mystring += '%.3f, ' % (uniqueTimesBP/(24*3600))
307+
print 'BP solution has unique time(s) %s and %d pols' % (mystring, nPolarizations)
308+
309+
# This value is overridden by the new function doPolarizations in ValueMapping.
310+
# print "Inferring %d polarizations from size of polynomial array" % (nPolarizations)
311+
return([polyMode, polyType, nPolyAmp, nPolyPhase, scaleFactor, nRows, nSpws, nUniqueTimesBP,
312+
uniqueTimesBP, nPolarizations, frequencyLimits, increments, frequenciesGHz,
313+
polynomialPhase, polynomialAmplitude, times, antenna1, cal_desc_id, spwBP])
314+
else:
315+
tb.close()
316+
return([])
317+
# end of openBpolyFile()
318+
319+
def calcChebyshev(coeffs, validDomain, freqs):
320+
"""
321+
Given a set of coefficients,
322+
this method evaluates a Chebyshev approximation.
323+
Used for CASA bandpass reading.
324+
input coeffs and freqs are numpy arrays
325+
"""
326+
327+
domain = (validDomain[1] - validDomain[0])[0]
328+
bins = -1 + 2* n.array([ (freqs[i]-validDomain[0,i])/domain for i in range(len(freqs))])
329+
ncoeffs = len(coeffs[0])/2
330+
rr = n.array([n.polynomial.chebval(bins[i], coeffs[i,:ncoeffs]) for i in range(len(coeffs))])
331+
ll = n.array([n.polynomial.chebval(bins[i], coeffs[i,ncoeffs:]) for i in range(len(coeffs))])
332+
333+
return rr,ll

0 commit comments

Comments
 (0)