################################################################## # v 1.01 - 2011 Nov 06 ################################################################## # Task to process wideband continuum EVLA datasets # # v 1.01: added parameter "doplot" to control whether plots of data # and calibration products are produced. This is in response to the # current plotcal bug (now ~15 x slower than before) as well as # display-back issues for plotms # # Order of operations: # # -> Import data # -> Check for missing scans # -> Check for zeros; plot # -> Apply on-line flags, zero flagging, shadow flagging # -> Plots of raw data for flux calibrator # -> Calibrate & plot # -> Apply calibration & plot # -> Image # # to-do: # - somehow, sniff for configuration -- if D, need to image # more primary beams, and perhaps time-averaging needs to take this # into account for the calculation of time-average smearing # - fix ":" in HTML files # - calculate dynamic range using peak / rms instead of fit at phase # center # - check time-binning of data to decide on solution lengths # - get flux at peak of image as well as fitting to phase center # - add option of using nterms > 1 # - channel-average data # - import calibrators separately and merge MSs after calibration (?) # - add refant choosing code: be sure it's config-dependent # - make padding of online flags integration-time dependent # - make calibrated channels observation-specific (not hard-coded); # perhaps dependent on detected RMS noise/channel # - add histogram / rms plots import os, time import numpy as np import pylab as pl from taskinit import * from listsdm_cli import listsdm_cli as listsdm from listobs_cli import listobs_cli as listobs from importevla_cli import importevla_cli as importevla from flagmanager_cli import flagmanager_cli as flagmanager from flagdata_cli import flagdata_cli as flagdata from flagcmd_cli import flagcmd_cli as flagcmd from plotms_cli import plotms_cli as plotms from plotcal_cli import plotcal_cli as plotcal from bandpass_cli import bandpass_cli as bandpass from gaincal_cli import gaincal_cli as gaincal from fluxscale_cli import fluxscale_cli as fluxscale from plotants_cli import plotants_cli as plotants from applycal_cli import applycal_cli as applycal from clean_cli import clean_cli as clean import casac from tasks import * from math import pi,floor #===================================================================== # Task inputs def mkpipeline(mode, msname, sdmname, rootname, band, dummy, checkzeros, timeave, flaglist, doplot, calimage, email, webdir, http): version = "v 1.01, 6 November 2011" timeStart = time.time() #==> Check that needed parameters are set: if mode == 'initproc': if rootname == '': casalog.post("ERROR: you must specify a root name for output files", \ priority='ERROR') return if band == '': casalog.post("ERROR: you must specify a band for processing", \ priority='ERROR') return if mode == 'reproc': if msname == '': casalog.post("ERROR: you must specify an MS for reprocessing", \ priority='ERROR') return #==> Remove trailing '/' which will cause problems later on: if mode == 'initproc': sdmname = sdmname.strip('/') if mode == 'reproc': msname = msname.strip('/') #==> Make a time string for later use: timeArr = time.localtime() timeStr = '%4.0f-%02.0f-%02.0f.%02.0f:%02.0f' % (timeArr[0], timeArr[1], timeArr[2], timeArr[3], timeArr[4]) #==> Definitions to use later: if mode == 'initproc': plotDir = rootname + '.mkpipeline' msName = rootname + '.ms' repro = False else: plotDir = msname + '.mkpipeline.' + timeStr msName = msname rootname = msname repro = True #==> Log file handling: initLog = casalog.logfile() if repro: logName = rootname + '.mkpipeline.' + timeStr + '.log' casalog.post("Running mkpipeline " + version) casalog.post("See %s for ongoing log output" % logName) casalog.setlogfile(logName) else: logName = rootname + '.mkpipeline.log' casalog.post("Running mkpipeline " + version) casalog.post("See %s for ongoing log output" % logName) casalog.setlogfile(logName) casalog.origin('mkpipeline') casalog.post("Task inputs:") casalog.post("mode = " + mode) if mode == 'initproc': casalog.post("rootname = " + rootname) casalog.post("sdmname = " + sdmname) casalog.post("band = " + band) casalog.post("dummy = " + str(dummy)) casalog.post("checkzeros = " + str(checkzeros)) casalog.post("timeave = " + str(timeave)) if mode == 'reproc': casalog.post("msname = " + msname) casalog.post("doplot = " + str(doplot)) casalog.post("calimage = " + str(calimage)) casalog.post("email = " + email) casalog.post("webdir = " + webdir) casalog.post("http = " + http) casalog.post(" ") # Reference antenna to use refAnt='ea21' #==> Given band, retrieve other needed information from SDM: if mode == 'initproc': infoDict = getSDMInfo(sdmName=sdmname, band=band, dummy=dummy, \ calImage=calimage) else: infoDict = getMSInfo(msName=msName, calImage=calimage) band = infoDict['band'] if not infoDict: return #==> Make output plot directory & check os.system('mkdir '+plotDir) if not os.path.isdir(plotDir): casalog.post("mkpipeline: Unable to create output plot directory -- ", priority='ERROR') casalog.post("mkpipeline: check permissions.", priority='ERROR') return #==> Import the data if mode == 'initproc': importData(sdmName=sdmname, msName=msName, timeStr=timeStr, baseBand=infoDict['baseBand'], plotDir=plotDir, scanStr=infoDict['scanStr'], checkZeros=checkzeros) bBandOrig = infoDict['baseBand'] # **** Perhaps should put this after the time-average step? #==> Plot the raw data if ((mode == 'initproc') and (doplot)): plotRawData(msName=msName, baseBand=infoDict['baseBand'], plotScan=infoDict['plotScan'], refAnt=refAnt, plotDir=plotDir) #==> Time-average the data, if requested if ((mode == 'initproc') and (timeave)): timeAveDict = timeAverage(msName=msName, band=band, baseBand=infoDict['baseBand'], msRoot=rootname) # Time-averaging was not successful (zeros were produced): if timeAveDict['success'] == 0: return msName = timeAveDict['msAve'] infoDict['baseBand'] = timeAveDict['baseBand'] infoDict['SPWs'] = timeAveDict['SPWs'] ## #== *** This is a hack to be removed later! For now, need to flag ## # subband 0 lower edges or bandpass fails completely. ## if mode == 'initproc': ## for bBand in infoDict['baseBand']: ## testBand = bBand.find('~') ## if (testBand > -1): ## (bLow, bHigh) = str.split(bBand, '~') ## spwStr = bLow+':0~5' ## flagdata(vis=msName, spw=spwStr, flagbackup=False) #==> Apply additional flags, if requested: # *** put in its own subroutine & add timing if flaglist: flagSave = 'before_mf_' + timeStr flagmanager(vis=msName, mode='save', versionname=flagSave) flagcmd(vis=msName, flagmode='cmd', command=flaglist, flagbackup=False) #==> If this is a reprocessing run, clear calibration first: if mode == 'reproc': clearcal(msName) #==> Calibrate the data calTables = calibData(msName=msName, rootName=rootname, fluxFields=infoDict['fluxSrc'], baseBand=infoDict['baseBand'], spwCalib=infoDict['SPWs'], doPlot=doplot, plotDir=plotDir, useGC=infoDict['useGC'], calFields=infoDict['calSrc'], band=band, refAnt=refAnt, repro=repro, timeStr=timeStr, nChan=infoDict['nChan']) #==> Apply the calibration applyCalib(msName=msName, clearPrev=False, rootName=rootname, # calChan='0~63', calTables=calTables, useGC=infoDict['useGC'], calTables=calTables, useGC=infoDict['useGC'], spwCalib=infoDict['SPWs'], plotField=infoDict['calSrc'], timeBin='10s', chanBin='5', doCal=1, timeStr=timeStr, doPlot=1, plotDir=plotDir, targetField=infoDict['targetSrc'], fluxFields=infoDict['fluxSrc']) #==> Image the data mkImages(msName=msName, rootName=rootname, repro=repro, imageFields=infoDict['imageSrc'], timeStr=timeStr, plotDir=plotDir, baseBand=infoDict['baseBand']) timeEnd = time.time() timeTotal = timeEnd - timeStart casalog.origin('mkpipeline') casalog.post("Complete mkpipeline run took %0.2f seconds (%0.2f minutes)" % (timeTotal, timeTotal/60)) casalog.post(" ") #==> Copy the CASA log information to appropriately-named files os.system("grep 'mkpipeline::' " + logName + " >> " + plotDir + "/mkpipeline.log") os.system('mv %s %s/' % (logName, plotDir)) casalog.setlogfile(initLog) #==> Write the summary web page: mkWebPage(msName=msName, plotDir=plotDir, logName=logName, plotScan=infoDict['plotScan'], calFields=infoDict['calSrc'], baseBand=infoDict['baseBand'], checkZeros=checkzeros, bBandOrig=bBandOrig, fluxFields=infoDict['fluxSrc'], repro=repro, doPlot=doplot) #==> Copy plots to web space, if requested: if webdir: os.system('cp -rp ' + plotDir + ' ' + webdir) #==> Send an email notification, if requested: if email: subject = 'mkpipeline complete, plots made: %s' % msName if http: os.system('echo "Finished processing %s to %s; see %s/%s" > tempmail' % (sdmname, msName, http, plotDir+'/index.html')) else: os.system('echo "Finished processing %s to %s; see %s" > tempmail' % (sdmname, msName, plotDir+'/index.html')) os.system('cat tempmail | mail -s "%s" %s' % (subject, email)) os.system('rm tempmail') ######################################################################## # Get information needed for processing from the SDM files def getSDMInfo(sdmName, band, dummy, calImage): # EVLA bands - ultimately, the band names should be in the SDM. # This is not currently the case, so we must make some # assumptions about the frequency ranges, which may overlap. # # Last boolean controls whether or not gain curves should be # used during calibration. bandDict = { 'L': [0.9, 2.0, False], 'S': [2.0, 4.0, False], 'C': [4.0, 8.0, True], 'X': [8.0, 12.0, True], 'Ku': [12.0, 18.0, True], 'K': [18.0, 26.5, True], 'Ka': [26.5, 40.0, True], 'Q': [40.0, 50.0, True] } scanList = listsdm(sdmName) if (dummy): scan1 = scanList.pop(1) scan2 = scanList.pop(2) myFreqRange = bandDict[band] freqMin = myFreqRange[0]*1e9 freqMax = myFreqRange[1]*1e9 scanStr = '' bpassSrc = [] gainSrc = [] targetSrc = [] fluxSrc = [] spwObs = [] nChan = [] for scan in scanList: # Use the midpoint of the AC frequency range to avoid band # edges: testEl = int(len(scanList[scan]['reffreq'][0])/4) if ((scanList[scan]['reffreq'][0][testEl] > freqMin) and (scanList[scan]['reffreq'][0][testEl] < freqMax) and (string.find(scanList[scan]['intent'], 'POINTING') == -1)): scanStr = scanStr + str(scan) + ',' spwObs.extend(scanList[scan]['spws'][0]) nChan.extend(scanList[scan]['nchan'][0]) if (string.find(scanList[scan]['intent'], 'BANDPASS') > -1): # Will plot the last bandpass scan plotScan = str(scan) bpassSrc.append(scanList[scan]['field']) if (string.find(scanList[scan]['intent'], 'PHASE') > -1): gainSrc.append(scanList[scan]['field']) if (string.find(scanList[scan]['intent'], 'TARGET') > -1): targetSrc.append(scanList[scan]['field']) if (string.find(scanList[scan]['intent'], 'AMPLI') > -1): fluxSrc.append(scanList[scan]['field']) if scanStr: # Check that needed sources are present, make unique lists: if not gainSrc: casalog.origin('mkpipeline') casalog.post("ERROR: no gain calibration sources present. This could be due to lack of proper SCAN_INTENT definition.", priority='ERROR') return else: gainSrc = list(np.unique(gainSrc)) if not bpassSrc: casalog.origin('mkpipeline') casalog.post("ERROR: no bandpass calibration sources present. This could be due to lack of proper SCAN_INTENT definition.", priority='ERROR') return else: bpassSrc = list(np.unique(bpassSrc)) if not targetSrc: casalog.origin('mkpipeline') casalog.post("ERROR: no science target scans are present. This could be due to lack of proper SCAN_INTENT definition. Proceeding with only calibration sources", priority='WARNING') else: targetSrc = list(np.unique(targetSrc)) if not fluxSrc: casalog.origin('mkpipeline') casalog.post("ERROR: no flux calibration sources present. Proceeding with calibration; will not perform flux scaling.", priority='WARNING') else: fluxSrc = list(np.unique(fluxSrc)) # Need to get rid of trailing ',' scanStr = str.rstrip(scanStr, ',') spwObs = list(np.unique(spwObs)) # If there are spectral windows with varying numbers of channels, fail: nChan = np.unique(nChan) if nChan.shape[0] > 1: casalog.origin('mkpipeline') casalog.post("ERROR: Spectral windows have varying numbers of channels; cannot proceed.", priority='ERROR') return else: nChan = nChan[0] # Determine baseband SPW sets spwObs.sort() baseband1 = spwObs[0:len(spwObs)/2] baseband2 = spwObs[len(spwObs)/2:len(spwObs)] bb1str = str(min(baseband1))+'~'+str(max(baseband1)) bb2str = str(min(baseband2))+'~'+str(max(baseband2)) # Image calibrators if requested; otherwise, just image the # science target: if calImage: tmp1 = np.append(bpassSrc,gainSrc) tmp2 = np.append(tmp1,targetSrc) imageSrc = list(np.unique(np.append(tmp2,fluxSrc))) else: imageSrc = list(np.unique(targetSrc)) # Make a list of all the calibrators: tmp1 = np.append(bpassSrc,gainSrc) if fluxSrc: calSrc = list(np.unique(np.append(tmp1,fluxSrc))) else: calSrc = list(np.unique(tmp1)) # Post the sources to the logger: casalog.origin('mkpipeline') casalog.post("Bandpass calibrator fields: " + str(bpassSrc)) casalog.post("Gain calibrator fields: " + str(gainSrc)) casalog.post("Flux calibrator fields: " + str(fluxSrc)) casalog.post("All calibrator fields: " + str(calSrc)) casalog.post("Science target fields: " + str(targetSrc)) casalog.post("Fields to image: " + str(imageSrc)) casalog.post(" ") infoDict = { 'SPWs': spwObs, 'nChan': nChan, 'baseBand': [bb1str, bb2str], 'useGC': bandDict[band][2], 'scanStr': scanStr, 'plotScan': plotScan, 'bpassSrc': bpassSrc, 'gainSrc': gainSrc, 'fluxSrc': fluxSrc, 'calSrc': calSrc, 'targetSrc': targetSrc, 'imageSrc': imageSrc } return(infoDict) else: casalog.origin('mkpipeline') casalog.post("There are no scans present for %s-band" % band, priority='ERROR') return ######################################################################## # Get information needed for processing from the MS files. Note that # the MS must have the verbatim xml files from the SDM directory. def getMSInfo(msName, calImage): # EVLA bands - ultimately, the band names should be in the SDM. # This is not currently the case, so we must make some # assumptions about the frequency ranges, which may overlap. # # Last boolean controls whether or not gain curves should be # used during calibration. bandDict = { 'L': [0.9, 2.0, False], 'S': [2.0, 4.0, False], 'C': [4.0, 8.0, True], 'X': [8.0, 12.0, True], 'Ku': [12.0, 18.0, True], 'K': [18.0, 26.5, True], 'Ka': [26.5, 40.0, True], 'Q': [40.0, 50.0, True] } # Return the SPW range: tb.open(msName + '/SPECTRAL_WINDOW') refFreq = tb.getcol('REF_FREQUENCY') nChan = tb.getcol('NUM_CHAN') spwObs = range(0, len(refFreq)) baseband1 = spwObs[0:len(spwObs)/2] baseband2 = spwObs[len(spwObs)/2:len(spwObs)] bb1str = str(min(baseband1))+'~'+str(max(baseband1)) bb2str = str(min(baseband2))+'~'+str(max(baseband2)) # If there are spectral windows with varying numbers of channels, fail: nChan = np.unique(nChan) if nChan.shape[0] > 1: casalog.origin('mkpipeline') casalog.post("ERROR: Spectral windows have varying numbers of channels; cannot proceed.", priority='ERROR') return else: nChan = nChan[0] # Determine the band: freqMinGHz = min(refFreq)/1e9 freqMaxGHz = max(refFreq)/1e9 freqMid = freqMinGHz + (freqMaxGHz - freqMinGHz) / 2 for bandName in bandDict: if (freqMid > bandDict[bandName][0]) and (freqMid < bandDict[bandName][1]): band = bandName break freqMin = min(refFreq) freqMax = max(refFreq) bpassSrc = [] gainSrc = [] targetSrc = [] fluxSrc = [] scanList = listsdm(msName) for scan in scanList: # Use the midpoint of the frequency range to avoid band # edges: testEl = int(len(scanList[scan]['reffreq'][0])/4) if ((scanList[scan]['reffreq'][0][testEl] > freqMin) and (scanList[scan]['reffreq'][0][testEl] < freqMax) and (string.find(scanList[scan]['intent'], 'POINTING') == -1)): if (string.find(scanList[scan]['intent'], 'BANDPASS') > -1): bpassSrc.append(scanList[scan]['field']) if (string.find(scanList[scan]['intent'], 'PHASE') > -1): gainSrc.append(scanList[scan]['field']) if (string.find(scanList[scan]['intent'], 'TARGET') > -1): targetSrc.append(scanList[scan]['field']) if (string.find(scanList[scan]['intent'], 'AMPLI') > -1): fluxSrc.append(scanList[scan]['field']) # Check that needed sources are present, make unique lists: if not gainSrc: casalog.origin('mkpipeline') casalog.post("ERROR: no gain calibration sources present. This could be due to lack of proper SCAN_INTENT definition.", priority='ERROR') return else: gainSrc = list(np.unique(gainSrc)) if not bpassSrc: casalog.origin('mkpipeline') casalog.post("ERROR: no bandpass calibration sources present. This could be due to lack of proper SCAN_INTENT definition.", priority='ERROR') return else: bpassSrc = list(np.unique(bpassSrc)) if not targetSrc: casalog.origin('mkpipeline') casalog.post("ERROR: no science target scans are present. This could be due to lack of proper SCAN_INTENT definition. Proceeding with only calibration sources", priority='WARNING') else: targetSrc = list(np.unique(targetSrc)) if not fluxSrc: casalog.origin('mkpipeline') casalog.post("ERROR: no flux calibration sources present. Proceeding with calibration; will not perform flux scaling.", priority='WARNING') else: fluxSrc = list(np.unique(fluxSrc)) # Image calibrators if requested; otherwise, just image the # science target: if calImage: tmp1 = np.append(bpassSrc,gainSrc) tmp2 = np.append(tmp1,targetSrc) imageSrc = list(np.unique(np.append(tmp2,fluxSrc))) else: imageSrc = list(np.unique(targetSrc)) # Make a list of all the calibrators: tmp1 = np.append(bpassSrc,gainSrc) if fluxSrc: calSrc = list(np.unique(np.append(tmp1,fluxSrc))) else: calSrc = list(np.unique(tmp1)) # Post the sources to the logger: casalog.origin('mkpipeline') casalog.post("Bandpass calibrator fields: " + str(bpassSrc)) casalog.post("Gain calibrator fields: " + str(gainSrc)) casalog.post("Flux calibrator fields: " + str(fluxSrc)) casalog.post("All calibrator fields: " + str(calSrc)) casalog.post("Science target fields: " + str(targetSrc)) casalog.post("Fields to image: " + str(imageSrc)) casalog.post(" ") infoDict = { 'SPWs': spwObs, 'nChan': nChan, 'baseBand': [bb1str, bb2str], 'useGC': bandDict[band][2], 'band': band, 'plotScan': '', 'bpassSrc': bpassSrc, 'gainSrc': gainSrc, 'fluxSrc': fluxSrc, 'calSrc': calSrc, 'targetSrc': targetSrc, 'imageSrc': imageSrc } return(infoDict) ######################################################################## # Import the data def importData(sdmName, timeStr, msName, baseBand, scanStr, plotDir, checkZeros): casalog.origin('mkpipeline') casalog.post("Importing %s to MS format..." % (sdmName)) timeImportStart = time.time() importevla(asdm=sdmName, vis=msName, applyflags=False, \ switchedpower=True, scans=scanStr, flagzero=False, \ shadow=False) # Copy over the XML tables needed for listsdm xmlTab = ['Scan.xml', 'Main.xml', 'ConfigDescription.xml', 'DataDescription.xml', 'SpectralWindow.xml', 'Field.xml', 'Antenna.xml', 'Station.xml', 'ExecBlock.xml'] for xmlFile in xmlTab: os.system('cp ' + sdmName + '/' + xmlFile + ' ' + msName) timeImportEnd = time.time() timeImportTotal = timeImportEnd - timeImportStart casalog.origin('mkpipeline') casalog.post("Importing data took %0.2f seconds (%0.2f minutes)" % (timeImportTotal, timeImportTotal/60)) casalog.post(" ") # Plot antenna distribution and choose a reference antenna ## casalog.origin('mkpipeline') ## casalog.post("Selecting a reference antenna...") ## timeRefAntStart = time.time() pl.clf() plotants(vis=msName, figfile=plotDir+'/antpos.png') ## refAnt = chooseRefAnt(msName) ## timeRefAntEnd = time.time() ## timeRefAntTotal = timeRefAntEnd - timeRefAntStart ## casalog.origin('mkpipeline') ## casalog.post("Selecting a reference antenna took %0.2f seconds (%0.2f minutes)" % (timeRefAntTotal, timeRefAntTotal/60)) ## casalog.post(" ") ## return(refAnt) # Check for and plot zeros, if requested: if checkZeros: casalog.origin('mkpipeline') casalog.post("Checking for zeros...") timeZeroCheckStart = time.time() findZeros(sdmName=sdmName, timeStr=timeStr, msName=msName, plotDir=plotDir) timeZeroCheckEnd = time.time() timeZeroCheckTotal = timeZeroCheckEnd - timeZeroCheckStart casalog.origin('mkpipeline') casalog.post("Checking for and plotting zeros took %0.2f seconds (%0.2f minutes)" % (timeZeroCheckTotal, timeZeroCheckTotal/60)) casalog.post(" ") ## # Flag antenna(s) with known problems ## if antenna: ## flagdata(vis=msName, mode='manualflag', selectdata=True, \ ## antenna=antenna, flagbackup=False) # Check for missing scans # Note that this check is not meaningful if only a subset of the # SDM file was imported casalog.origin('mkpipeline') casalog.post("Checking for missing scans...") checkScans(msName=msName) # Import the online flags, make plots, apply flags, and save a flag # version: casalog.origin('mkpipeline') casalog.post("Importing and applying online flags...") timeOnlineFlagStart = time.time() plotName = plotDir + '/onlineFlags.png' # plot flags flagcmd(vis=msName, flagmode='xml', optype='plot', outfile=plotName) # apply online flags, 0.5 s padding (should really be tint / 2). flagcmd(vis=msName, flagmode='xml', tbuff=0.5, optype='apply', \ flagbackup=False) timeOnlineFlagEnd = time.time() timeOnlineFlagTotal = timeOnlineFlagEnd - timeOnlineFlagStart casalog.origin('mkpipeline') casalog.post("Applying online flags took %0.2f seconds (%0.2f minutes)" % (timeOnlineFlagTotal, timeOnlineFlagTotal/60)) casalog.post(" ") # Apply zero and shadow flags; save flags: casalog.origin('mkpipeline') casalog.post("Applying zero and shadow flags...") timeFlagStart = time.time() flagdata(vis=msName, mode='shadow', flagbackup=False) flagdata(vis=msName, mode='manualflag', clipminmax=[0.0, 1E-10], \ clipoutside=False, clipcolumn='DATA', \ clipexpr=['ABS RR','ABS LL','ABS RL','ABS LR'], \ flagbackup=False) flagmanager(vis=msName, mode='save', \ versionname='flags.shadow.zeros_'+timeStr, \ comment='Flag save after online, shadow, and zero flags applied') timeFlagEnd = time.time() timeFlagTotal = timeFlagEnd - timeFlagStart casalog.origin('mkpipeline') casalog.post("Applying zero and shadow flags took %0.2f seconds (%0.2f minutes)" % (timeFlagTotal, timeFlagTotal/60)) casalog.post(" ") ######################################################################## # Time-average the data def timeAverage(msName, band, baseBand, msRoot): casalog.origin('mkpipeline') casalog.post("Time-averaging data...") timeTimeAveStart = time.time() tb.open(msName+'/ANTENNA') antPos = tb.getcol('POSITION') tb.close() antDist = [] for antenna1 in range(0,antPos.shape[1]): xRef = antPos[0][antenna1] yRef = antPos[1][antenna1] zRef = antPos[2][antenna1] for antenna2 in range(antenna1+1,antPos.shape[1]): x = antPos[0][antenna2] y = antPos[1][antenna2] z = antPos[2][antenna2] antDist.append(np.sqrt((xRef-x)**2.0+(yRef-y)**2.0+(zRef-z)**2.0)) # Maximum distance between antennas (m) maxDist = max(antDist) # Tolerance for time-average smearing at the edge of the image # field (assume this is 1 primary beam), fraction of ideal response: Rtol = 0.01 # Ratio of synthesized beam to primary beam, assuming # # thetaSB(rad) = 1.2 * lambda / Dmax(m) # thetaPB(arcmin) = 45 / nu(GHz) # # where Dmax(m) is the max. distance between antennas calculated # gives thetaSB / thetaPB = 27.5 / Dmax(m): thetaRat = 27.5 / maxDist # From Chapter 18 of Synthesis Imaging in Radio Astronomy II, find # an acceptable average time tau; round down the the nearest # integer number of seconds: tau = int(np.sqrt(1e9 * Rtol) * thetaRat) casalog.post("Assuming a 1% tolerance for time-average smearing,") casalog.post(" can time-average to %i seconds." % tau) casalog.post(" ") # If the observation is high-frequency, don't want to average too # much (consider atmospheric effects); for low-frequency don't # want to average beyond the timescale of possible instrumental # variations. (Could improve this in the future, perhaps, by # using information in the weather table.) bandAve = { 'L': 60, 'S': 60, 'C': 60, 'X': 30, 'Ku': 20, 'K': 10, 'Ka': 5, 'Q': 5 } tauBand = bandAve[band]; casalog.post("Observations were performed in %s-band, so recommended" % band) casalog.post(" maximum time bin is %i seconds." % tauBand) casalog.post(" ") # Use the shorter of these two: tauApply = min(tau, tauBand) tauStr = '%is' % tauApply casalog.post("Averaging data using %i-second bins." % tauApply) # Average the data: msAve = msRoot + '.' + str(tauApply) + 's.ms' # Explicitly set the SPW range to avoid a bug related to # gain curve selection (CAS-2705): spwStr = ','.join(baseBand) split(vis=msName, outputvis=msAve, datacolumn='data', timebin=tauStr, \ keepflags=False, spw=spwStr) # Check that split didn't write all zeros to a column: ms.open(msAve) testRange = ms.range(['u','v','w','sigma','weight']) # Takes a lot of time to check whether the data are zeros. # Just test the uvw, sigma, and weight columns for now. ## dataRange = ms.range(['real']) ms.close dataOK = 1 if (((max(testRange['u']) == 0) and (max(testRange['v']) == 0) and (max(testRange['w']) == 0))): casalog.post("All UVW values in " + msAve + " are zero.", \ priority='ERROR') dataOK = 0 if (max(testRange['sigma']) == 0): casalog.post("All SIGMA values in " + msAve + " are zero.", \ priority='ERROR') dataOK = 0 if (max(testRange['weight']) == 0): casalog.post("All WEIGHT values in " + msAve + " are zero.", \ priority='ERROR') dataOK = 0 if (dataOK == 0): aveDict = { 'success': 0 } return aveDict # Copy over the XML tables needed for listsdm xmlTab = ['Scan.xml', 'Main.xml', 'ConfigDescription.xml', 'DataDescription.xml', 'SpectralWindow.xml', 'Field.xml', 'Antenna.xml', 'Station.xml', 'ExecBlock.xml'] for xmlFile in xmlTab: os.system('cp ' + msName + '/' + xmlFile + ' ' + msAve) # Return a corrected version of the SPW range: tb.open(msAve + '/SPECTRAL_WINDOW') newSPW = tb.getcol('REF_FREQUENCY') spwObs = range(0, len(newSPW)) baseband1 = spwObs[0:len(spwObs)/2] baseband2 = spwObs[len(spwObs)/2:len(spwObs)] bb1str = str(min(baseband1))+'~'+str(max(baseband1)) bb2str = str(min(baseband2))+'~'+str(max(baseband2)) timeTimeAveEnd = time.time() timeTimeAveTotal = timeTimeAveEnd - timeTimeAveStart casalog.origin('mkpipeline') casalog.post("Time-averaging data took %0.2f seconds (%0.2f minutes)" % (timeTimeAveTotal, timeTimeAveTotal/60)) casalog.post(" ") aveDict = { 'msAve': msAve, 'tauApply': tauApply, 'baseBand': [bb1str, bb2str], 'SPWs': spwObs, 'success': 1 } return aveDict ######################################################################## # Plot the raw data def plotRawData(msName, baseBand, plotScan, refAnt, plotDir): casalog.origin('mkpipeline') casalog.post("Plotting uncalibrated phase and amplitude data...") timePlotStart = time.time() # Only plot antennas for which there is data: tb.open(msName + '/ANTENNA') antArr = tb.getcol('NAME') tb.close() for i in range(0, len(baseBand)): spwStr = baseBand[i] html = open(plotDir+'/scan_%s.amp-phase_v_freq.spw%s.html' % (plotScan, spwStr), 'w') htmlHead = ''' Phase/amplitude vs. freq for '''+msName+''', scan '''+plotScan+''', spw '''+spwStr+''' ''' html.write(htmlHead) for j in range(0, len(antArr)): plotMade = False if antArr[j] == refAnt: continue antStr = '%s&%s' % (antArr[j], refAnt) plotNameAmp = plotDir + '/' + antStr + '.spw' + spwStr + '.freq.amp.png' plotNamePh = plotDir + '/' + antStr + '.spw' + spwStr + '.freq.phase.png' htmlNameAmp = antStr + '.spw' + spwStr + '.freq.amp.png' htmlNamePh = antStr + '.spw' + spwStr + '.freq.phase.png' htmlStr = ''' ''' % (antStr, htmlNameAmp, htmlNameAmp, htmlNamePh, htmlNamePh) html.write(htmlStr) # Make amplitude vs. frequency plots: plotms(vis=msName, xaxis='freq', yaxis='amp', selectdata=True, \ ## spw=spwStr, antenna=antStr, avgtime='5s', \ spw=spwStr, antenna=antStr, \ plotfile=plotNameAmp, correlation='RR,LL', scan=plotScan, \ coloraxis='corr') while (plotMade == False): time.sleep(1) print "Testing for plot %s..." % plotNameAmp plotMade = os.path.isfile(plotNameAmp) print "Plot not found." plotMade = False # Make phase vs. frequency plots: plotms(vis=msName, xaxis='freq', yaxis='phase', selectdata=True, \ ## spw=spwStr, antenna=antStr, avgtime='5s', \ spw=spwStr, antenna=antStr, \ plotfile=plotNamePh, correlation='RR,LL', \ scan=plotScan, plotrange=[0,0,-180,180], \ coloraxis='corr') while (plotMade == False): time.sleep(1) print "Testing for plot %s..." % plotNamePh plotMade = os.path.isfile(plotNamePh) print "Plot not found." htmlTail = '''
Amplitude vs. Frequency: Phase vs. Frequency:

%s:


''' html.write(htmlTail) html.close() timePlotEnd = time.time() timePlotTotal = timePlotEnd - timePlotStart casalog.origin('mkpipeline') casalog.post("Plotting uncalibrated data took %0.2f seconds (%0.2f minutes)" % (timePlotTotal, timePlotTotal/60)) casalog.post(" ") ######################################################################## # Calibrate data # # msName = name of ms to be calibrated # fluxFields = list of field IDs for flux calibrator, e.g. [2,3,4] # refAnt = desired reference antenna # baseBand = array of spw strings to plot together (oft. basebands), # e.g. ['2~9', '10~17'] # plotDir = output plotting directory # rootName = root for output filenames # band = observing band (e.g., 'C' or 'Ku') def calibData(msName, fluxFields, calFields, nChan, refAnt, spwCalib, baseBand, useGC, doPlot, plotDir, rootName, band, repro, timeStr): #===================================================================== # Set up useful bits for later # Look for the model image directory casaPath = os.environ.get('CASAPATH').split()[0] modImagePath = casaPath + '/data/nrao/VLA/CalModels/' if not os.path.isdir(modImagePath): casalog.origin('mkpipeline') casalog.post("ERROR: The calibrator model image directory not present at "+modImagePath, priority='ERROR') return # Get antenna information tb.open(msName + '/ANTENNA') antArr = tb.getcol('NAME') tb.close() #===================================================================== # Determine calibration intervals based on observation information # Set up dictionary of phase and amplitude solution intervals, # dependent on observing band. Order is: # bpassPhInt, gainPhInt, gainAmpInt, gainPhaseSmo, gainCurve solIntDict = { 'L': ['5s', '20s', 'inf', 40], 'S': ['5s', '20s', 'inf', 40], 'C': ['5s', '20s', 'inf', 40], 'X': ['5s', '15s', 'inf', 30], 'Ku': ['5s', '10s', 'inf', 20], 'K': ['5s', '10s', 'inf', 20], 'Ka': ['5s', '10s', 'inf', 20], 'Q': ['5s', '10s', 'inf', 20] } # Solution interval and channel range for initial phase solutions: bpassPhInt = solIntDict[band][0] # Use middle 10% of channels for the initial phase solution: phChanNum = int(nChan * 0.1) phChanMin = int((nChan / 2) - (phChanNum / 2)) phChanMax = int((nChan / 2) + (phChanNum / 2) - 1) bpassPhChan = str(phChanMin) + '~' + str(phChanMax) # bpassPhChan = '29~34' # Solution interval and channel range for gain phase solutions, # as well as smoothing time interval: gainPhInt = solIntDict[band][1] gainPhSmo = solIntDict[band][3] # gainChan = '0~63' # Solution interval for gain amplitude solutions: gainAmpInt = solIntDict[band][2] #===================================================================== # Determine needed opacity corrections, per spw, and plot the weather # table: casalog.origin('mkpipeline') casalog.post("Determining opacity values and plotting weather table...") tauMS = plotWX(vis=msName, seasonal_weight=0.5, doPlot=True, plotDir=plotDir) #===================================================================== # setjy for any calibrator fields that are standard flux calibrators: casalog.origin('mkpipeline') casalog.post("Setting flux scale for any standard flux calibrators...") timeSetjyStart = time.time() # Calibrate all spectral windows tb.open(msName+'/SPECTRAL_WINDOW') freqList = tb.getcol('REF_FREQUENCY')/1e9 tb.close() # Get information about the fields tb.open(msName+'/FIELD') srcNamesArr = tb.getcol('NAME') srcIDArrField = tb.getcol('SOURCE_ID') srcPosArr = tb.getcol('PHASE_DIR') tb.close() # Get information about observed spectral windows tb.open(msName+'/SOURCE') srcIDArrSource = tb.getcol('SOURCE_ID') srcSPWArr = tb.getcol('SPECTRAL_WINDOW_ID') tb.close() # EVLA bands - ultimately, these should be in the MS and sniffed out # from there. This is not currently the case, so we must make some # assumptions about the frequency ranges, which may overlap. # # Since there aren't currently Ka images, use K or Q images for this # band instead. # # Since there aren't currently S images, use L or C images for this # band instead. # # Note that this is a rather fragile way of doing things. Need to # change once band indications are available in the MS. bandDict = { 'L': [0.9, 3.0], ## 'S': [2.0, 4.0], 'C': [3.0, 8.0], 'X': [8.0, 12.0], 'U': [12.0, 18.0], 'K': [18.0, 34.0], ## 'Ka': [26.5, 40.0], 'Q': [34.0, 50.0] } # List of positions for flux calibrators: fluxcalDict = { '3C138': [np.array([80.2911858, 16.6394103])*np.pi/180.0, \ np.array(['L','C','X','U','K','Q'])], '3C147': [np.array([85.6511025, 49.8519675])*np.pi/180.0, \ np.array(['L','C','X','U','K','Q'])], '3C286': [np.array([202.7845246, 30.5091444])*np.pi/180.0, \ np.array(['L','C','X','U','K','Q'])], '3C48': [np.array([24.4220712, 33.1597550])*np.pi/180.0, \ np.array(['L','C','X','U','K','Q'])] } # Open the MS for writing im.open(msName, usescratch=True) # Comparing the positions of flux calibrators in the observation to find # model images & run setjy, once for each source: for field in calFields: srcID = srcIDArrField[field] spwArr = srcSPWArr[np.where(srcIDArrSource == srcID)] fluxBandArr = [] # Make a band list that matches the spw list: for spw in spwArr: for keys in bandDict: if (freqList[spw] >= bandDict[keys][0]) and \ (freqList[spw] <= bandDict[keys][1]): fluxBandArr.append(keys) break # RA, Dec of flux calibrator in radians: RA = np.array(srcPosArr[0][0])[field] Dec = np.array(srcPosArr[1][0])[field] srcX = np.cos(RA) * np.cos(Dec) srcY = np.sin(RA) * np.cos(Dec) srcZ = np.sin(Dec) # Check distance from standard calibrators to match source: for keys in fluxcalDict: calRA = fluxcalDict[keys][0][0] calDec = fluxcalDict[keys][0][1] calX = np.cos(calRA) * np.cos(calDec) calY = np.sin(calRA) * np.cos(calDec) calZ = np.sin(calDec) testDist = np.arccos(srcX*calX + srcY*calY + srcZ*calZ) # If the source is less than 60 arcsec away, assume it is # a match and loop over spectral windows to set the flux: if (testDist < 2.9e-4): for i in range(0,len(spwArr)): if ((fluxcalDict[keys][1] == fluxBandArr[i]).any()): refImgBand = fluxcalDict[keys][1][np.where(fluxcalDict[keys][1] == fluxBandArr[i])] modImg = modImagePath + keys + '_' + refImgBand[0] + '.im' im.setjy(field=str(field), spw=str(spwArr[i]), modimage=modImg, standard='Perley-Butler 2010', fluxdensity=-1, scalebychan=True) else: im.setjy(field=str(field), spw=str(spwArr[i]), standard='Perley-Butler 2010', fluxdensity=-1, scalebychan=True) im.close() timeSetjyEnd = time.time() timeSetjyTotal = timeSetjyEnd - timeSetjyStart casalog.origin('mkpipeline') casalog.post("Setting flux scale took %0.2f seconds (%0.2f minutes)" % (timeSetjyTotal, timeSetjyTotal/60)) casalog.post(" ") #===================================================================== # Bandpass calibration: casalog.origin('mkpipeline') casalog.post("Calibrating antenna bandpasses...") timeBandpassStart = time.time() if repro: bpassPh = rootName + '.' + timeStr + '.' + bpassPhInt + '.bcal.phase' bpassTab = rootName + '.' + timeStr + '.bcal' else: bpassPh = rootName + '.' + bpassPhInt + '.bcal.phase' bpassTab = rootName + '.bcal' # Loop through spectral windows to create initial phase solutions for # all bandpass calibration scans: for n in spwCalib: zVal = tauMS[n] bpassSpwStr = '%i:%s' % (n, bpassPhChan) gaincal(vis=msName, caltable=bpassPh, intent='*BANDPASS*', \ selectdata=True, spw=bpassSpwStr, gaintype='G', \ solint=bpassPhInt, calmode='p', refant=refAnt, \ append=True, minsnr=3.0) ## gaincal(vis=msName, caltable=bpassPh, intent='*BANDPASS*', \ ## selectdata=True, spw=bpassSpwStr, gaintype='G', \ ## solint=bpassPhInt, calmode='p', refant=refAnt, \ ## append=True, \ ## minsnr=5.0) # Loop through spectral windows to create bandpass solutions: for i in spwCalib: zVal = tauMS[i] bpassSpwStr = '%i' % i bandpass(vis=msName, caltable=bpassTab, gaintable=[bpassPh], \ spw=bpassSpwStr, solint='inf', refant=refAnt, \ combine='scan', solnorm=False, selectdata=True, \ append=True, gaincurve=useGC, opacity=zVal, \ intent='*BANDPASS*', minsnr=10.0) ## bandpass(vis=msName, caltable=bpassTab, gaintable=[bpassPh], \ ## spw=bpassSpwStr, solint='inf', refant=refAnt, \ ## combine='scan', solnorm=True, selectdata=True, \ ## append=True, gaincurve=True, opacity=zVal, \ ## intent='*BANDPASS*', minsnr=10.0, \ ## bandtype='BPOLY') timeBandpassEnd = time.time() timeBandpassTotal = timeBandpassEnd - timeBandpassStart casalog.origin('mkpipeline') casalog.post("Calibrating antenna bandpasses took %0.2f seconds (%0.2f minutes)" % (timeBandpassTotal, timeBandpassTotal/60)) casalog.post(" ") #===================================================================== # Gain calibration: casalog.origin('mkpipeline') casalog.post("Performing gain calibration...") timeGainStart = time.time() if repro: gainPh = rootName + '.' + timeStr + '.' + gainPhInt + '.gain.phase' gainPhSmooth = rootName + '.' + timeStr + '.' + gainPhInt + '.gain.phase.sm' gainPhAmp = rootName + '.' + timeStr + '.' + gainAmpInt + '.gain.phase.amp' else: gainPh = rootName + '.' + gainPhInt + '.gain.phase' gainPhSmooth = rootName + '.' + gainPhInt + '.gain.phase.sm' gainPhAmp = rootName + '.' + gainAmpInt + '.gain.phase.amp' # Loop through spectral windows to create phase-only solutions: for n in spwCalib: zVal = tauMS[n] gainSpwStr = '%i' % n # gainSpwStr = '%i:%s' % (n, gainChan) ## gaincal(vis=msName, caltable=gainPh, \ ## intent='*BANDPASS*,*PHASE*,*AMPLI*', \ ## selectdata=False, spw=gainSpwStr, interp=['nearest'], \ ## solint=gainPhInt, calmode='p', refant=refAnt, \ ## gaintable=[bpassTab], minsnr=10.0, minblperant=3, \ ## ## append=True, gaincurve=True, opacity=zVal) ## append=True) if fluxFields: gaincal(vis=msName, caltable=gainPh, \ intent='*BANDPASS*,*PHASE*,*AMPLI*', \ selectdata=False, spw=gainSpwStr, interp=['nearest'], \ solint=gainPhInt, calmode='p', refant=refAnt, \ gaintable=[bpassTab], minsnr=10.0, \ ## append=True, gaincurve=True, opacity=zVal) append=True) else: gaincal(vis=msName, caltable=gainPh, \ intent='*BANDPASS*,*PHASE*', \ selectdata=False, spw=gainSpwStr, interp=['nearest'], \ solint=gainPhInt, calmode='p', refant=refAnt, \ gaintable=[bpassTab], minsnr=10.0, \ ## append=True, gaincurve=True, opacity=zVal) append=True) # Smooth this solution: smoothcal(vis=msName, tablein=gainPh, caltable=gainPhSmooth, \ smoothtype='median', smoothtime=gainPhSmo) # Loop through spectral windows to create phase+amplitude solutions: for n in spwCalib: zVal = tauMS[n] gainSpwStr = '%i' % n # gainSpwStr = '%i:%s' % (n, gainChan) ## gaincal(vis=msName, caltable=gainPhAmp, \ ## intent='*BANDPASS*,*PHASE*,*AMPLI*', \ ## selectdata=False, spw=gainSpwStr, interp=['nearest','nearest'], \ ## solint=gainAmpInt, calmode='ap', refant=refAnt, \ ## gaintable=[bpassTab, gainPh], minsnr=10.0, \ ## minblperant=3, append=True, gaincurve=True, opacity=zVal) if fluxFields: gaincal(vis=msName, caltable=gainPhAmp, \ intent='*BANDPASS*,*PHASE*,*AMPLI*', \ selectdata=False, spw=gainSpwStr, interp=['nearest','nearest'], \ solint=gainAmpInt, calmode='ap', refant=refAnt, \ gaintable=[bpassTab, gainPhSmooth], minsnr=10.0, \ append=True, gaincurve=useGC, opacity=zVal) else: gaincal(vis=msName, caltable=gainPhAmp, \ intent='*BANDPASS*,*PHASE*', \ selectdata=False, spw=gainSpwStr, interp=['nearest','nearest'], \ solint=gainAmpInt, calmode='ap', refant=refAnt, \ gaintable=[bpassTab, gainPhSmooth], minsnr=10.0, \ append=True, gaincurve=useGC, opacity=zVal) timeGainEnd = time.time() timeGainTotal = timeGainEnd - timeGainStart casalog.origin('mkpipeline') casalog.post("Performing gain calibration took %0.2f seconds (%0.2f minutes)" % (timeGainTotal, timeGainTotal/60)) casalog.post(" ") #===================================================================== # Flux scaling: if fluxFields: casalog.origin('mkpipeline') casalog.post("Applying flux scale to gain solutions...") timeFluxscaleStart = time.time() fluxStr = '' for s in range(0, len(fluxFields)): fluxStr = str(fluxFields[s]) + ',' + fluxStr fluxStr = str.rstrip(fluxStr,',') # remove trailing comma if repro: fluxTab = rootName + '.' + timeStr + '.' + gainAmpInt + '.fscale.phase.amp' else: fluxTab = rootName + '.' + gainAmpInt + '.fscale.phase.amp' cb.open(msName) # Though we don't currently do anything with it, scaledFlux # is a list containing one entry per source ID / SPW combination # of the returned flux values. scaledFlux = cb.fluxscale(tablein=gainPhAmp, tableout=fluxTab, reference=fluxStr); cb.close(); timeFluxscaleEnd = time.time() timeFluxscaleTotal = timeFluxscaleEnd - timeFluxscaleStart casalog.origin('mkpipeline') casalog.post("Applying flux scale took %0.2f seconds (%0.2f minutes)" % (timeFluxscaleTotal, timeFluxscaleTotal/60)) casalog.post(" ") #===================================================================== # Plot solutions and make web pages if doPlot: casalog.origin('mkpipeline') casalog.post("Making calibration plots...") timeCalplotStart = time.time() # Phase solutions html = open(plotDir+'/bpphase.html', 'w') htmlHead = ''' Initial phase solutions vs. time for '''+msName+''' ''' html.write(htmlHead) for j in range(0, len(antArr)): antStr = '%s' % antArr[j] htmlNamePh = antStr + '.time.phase.png' plotNamePh = plotDir + '/' + htmlNamePh htmlStr = ''' ''' % (antStr, htmlNamePh, htmlNamePh) html.write(htmlStr) # Make phase vs. time plots: plotcal(caltable=bpassPh, xaxis='time', yaxis='phase', \ antenna=antStr, \ showgui=False, figfile=plotNamePh, \ plotrange=[-1,-1,-180,180]) htmlTail = '''

%s:
''' html.write(htmlTail) html.close() # Bandpass solutions, one per baseband per antenna: for i in range(0, len(baseBand)): html = open(plotDir+'/bandpass.spw%s.html' % (baseBand[i]), 'w') htmlHead = ''' Bandpass phase/amplitude vs. freq for '''+msName+''', band '''+baseBand[i]+''' ''' html.write(htmlHead) for j in range(0, len(antArr)): antStr = '%s' % antArr[j] htmlNameAmp = antStr + '.spw' + baseBand[i] + '.freq.amp.png' htmlNamePh = antStr + '.spw' + baseBand[i] + '.freq.phase.png' plotNameAmp = plotDir + '/' + htmlNameAmp plotNamePh = plotDir + '/' + htmlNamePh htmlStr = ''' ''' % (antStr, htmlNameAmp, htmlNameAmp, htmlNamePh, htmlNamePh) html.write(htmlStr) # Make amplitude vs. frequency plots: plotcal(caltable=bpassTab, xaxis='freq', yaxis='amp', \ antenna=antStr, spw=baseBand[i], \ showgui=False, figfile=plotNameAmp) # Make phase vs. frequency plots: plotcal(caltable=bpassTab, xaxis='freq', yaxis='phase', \ antenna=antStr, spw=baseBand[i], \ showgui=False, figfile=plotNamePh, \ plotrange=[-1,-1,-180,180]) htmlTail = '''

%s:


''' html.write(htmlTail) html.close() # Make plots of gain solutions, one per baseband per antenna: for i in range(0, len(baseBand)): html = open(plotDir+'/gain.spw%s.html' % (baseBand[i]), 'w') htmlHead = ''' Gain phase/amplitude vs. freq for '''+msName+''', band '''+baseBand[i]+''' ''' html.write(htmlHead) for j in range(0, len(antArr)): antStr = '%s' % antArr[j] htmlNameAmp = antStr + '.spw' + baseBand[i] + '.time.amp.png' htmlNamePh = antStr + '.spw' + baseBand[i] + '.time.phase.png' plotNameAmp = plotDir + '/' + htmlNameAmp plotNamePh = plotDir + '/' + htmlNamePh htmlStr = ''' ''' % (antStr, htmlNameAmp, htmlNameAmp, htmlNamePh, htmlNamePh) html.write(htmlStr) # Make amplitude vs. frequency plots: plotcal(caltable=gainPhAmp, xaxis='time', yaxis='amp', \ antenna=antStr, spw=baseBand[i], \ showgui=False, figfile=plotNameAmp) # Make phase vs. frequency plots: plotcal(caltable=gainPhSmooth, xaxis='time', yaxis='phase', \ antenna=antStr, spw=baseBand[i], \ showgui=False, figfile=plotNamePh, \ plotrange=[-1,-1,-180,180]) htmlTail = '''

%s:


''' html.write(htmlTail) html.close() # Make flux-scaled amplitude vs. frequency plots, if flux-scaled calibration exists: if fluxFields: for i in range(0, len(baseBand)): html = open(plotDir+'/fscale.gain.spw%s.html' % (baseBand[i]), 'w') htmlHead = ''' Gain phase/flux-scaled amplitude vs. freq for '''+msName+''', band '''+baseBand[i]+''' ''' html.write(htmlHead) for j in range(0, len(antArr)): antStr = '%s' % antArr[j] htmlNameAmp = antStr + '.spw' + baseBand[i] + '.time.amp.fscale.png' htmlNamePh = antStr + '.spw' + baseBand[i] + '.time.phase.png' plotNameAmp = plotDir + '/' + htmlNameAmp htmlStr = ''' ''' % (antStr, htmlNameAmp, htmlNameAmp, htmlNamePh, htmlNamePh) html.write(htmlStr) plotcal(caltable=fluxTab, xaxis='time', yaxis='amp', \ antenna=antStr, spw=baseBand[i], \ showgui=False, figfile=plotNameAmp) htmlTail = '''

%s:


''' html.write(htmlTail) html.close() timeCalplotEnd = time.time() timeCalplotTotal = timeCalplotEnd - timeCalplotStart casalog.origin('mkpipeline') casalog.post("Calibration plotting took %0.2f seconds (%0.2f minutes)" % (timeCalplotTotal, timeCalplotTotal/60)) casalog.post(" ") if not fluxFields: fluxTab = gainPhAmp else: fluxTab = fluxTab calTables = [bpassTab, gainPhSmooth, fluxTab] return calTables ######################################################################## # Apply calibration # # msName = name of file to be calibrated # clearPrev = run clearcal to clear prior calibration? # calChan = channels to calibrate, e.g. '4~58' # plotDir = output/plotting directory # rootName = root name for output filenames # calInterp = list of interpolation methods to use # plotField = list of field(s) to plot, e.g. [1,2] # timeBin = time binning to use for plots, e.g. '5' # chanBin = channel binning to use for plots, e.g. '5' # doCal = apply calibration? # doPlot = make plots? def applyCalib(msName, clearPrev, plotDir, rootName, calTables, plotField, timeBin, chanBin, timeStr, doCal, doPlot, useGC, spwCalib, targetField, fluxFields): #===================================================================== # Clear prior calibration, if requested: if doCal: casalog.origin('mkpipeline') casalog.post("Applying calibration...") timeApplycalStart = time.time() if clearPrev == True: clearcal(vis=msName) #===================================================================== # Determine needed opacity corrections, per spw: tauMS = plotWX(vis=msName, seasonal_weight=0.5, doPlot=False, plotDir='') # Save a version of the flags before applying calibration: flagStr = 'preCalib_'+timeStr flagComm = 'Flag save before calibration on '+timeStr flagmanager(vis=msName, mode='save', versionname=flagStr, \ comment=flagComm) tb.open(msName+'/SPECTRAL_WINDOW') freqList = tb.getcol('REF_FREQUENCY')/1e9 tb.close() # Apply calibration, per spw: for n in spwCalib: # spwStr = str(n)+':'+calChan spwStr = str(n) zVal = tauMS[n] calTableLen = len(calTables) interpArr = ['nearest']*calTableLen if fluxFields: applycal(vis=msName, spw=spwStr, selectdata=False, \ intent='*BANDPASS*,*PHASE*,*AMPLI*', \ interp=interpArr, gaintable=calTables, gaincurve=useGC, \ opacity=zVal, calwt=False, flagbackup=False) else: applycal(vis=msName, spw=spwStr, selectdata=False, \ intent='*BANDPASS*,*PHASE*', \ interp=interpArr, gaintable=calTables, gaincurve=useGC, \ opacity=zVal, calwt=False, flagbackup=False) if targetField: applycal(vis=msName, spw=spwStr, selectdata=False, \ intent='*TARGET*', \ gaintable=calTables, gaincurve=useGC, \ opacity=zVal, calwt=False, flagbackup=False) timeApplycalEnd = time.time() timeApplycalTotal = timeApplycalEnd - timeApplycalStart casalog.origin('mkpipeline') casalog.post("Applying calibration took %0.2f seconds (%0.2f minutes)" % (timeApplycalTotal, timeApplycalTotal/60)) casalog.post(" ") #===================================================================== # Make plots of calibrated data, one per spw of each phase vs. amp and # amp vs. baseline: if doPlot: casalog.origin('mkpipeline') casalog.post("Plotting calibrated data...") timePlotCaldataStart = time.time() # Loop over fields and spws: for i in range(0, len(plotField)): plotStr = str(plotField[i]) html = open(plotDir+'/field_%s.html' % plotStr, 'w') htmlHead = ''' Calibrated phase vs. amp and amp vs. baseline for '''+msName+''', field '''+plotStr+''' ''' html.write(htmlHead) for s in spwCalib: # spwStr = str(s)+':'+calChan spwStr = str(s) plotMade = False htmlAmpPh = 'field_'+plotStr+'.spw'+spwStr+'.amp.phase.png' htmlAmpBl = 'field_'+plotStr+'.spw'+spwStr+'.amp.bl.png' plotAmpPh = plotDir+'/'+htmlAmpPh plotAmpBl = plotDir+'/'+htmlAmpBl htmlStr = ''' ''' % (spwStr, htmlAmpPh, htmlAmpPh, htmlAmpBl, htmlAmpBl) html.write(htmlStr) # Make phase vs. amplitude plots: plotms(vis=msName, xaxis='phase', yaxis='amp', \ selectdata=True, \ field=plotStr, spw=spwStr, avgtime=timeBin, \ avgchannel=chanBin, \ plotfile=plotAmpPh, correlation='RR,LL', \ xdatacolumn='corrected', ydatacolumn='corrected', \ coloraxis='antenna1') while (plotMade == False): time.sleep(1) print "Testing for plot %s..." % plotAmpPh plotMade = os.path.isfile(plotAmpPh) print "Plot not found." plotMade = False # Make amplitude vs. baseline plots: plotms(vis=msName, xaxis='baseline', \ yaxis='amp', selectdata=True, \ field=plotStr, spw=spwStr, avgtime=timeBin, \ avgchannel=chanBin, \ plotfile=plotAmpBl, correlation='RR,LL', \ ydatacolumn='corrected', \ coloraxis='antenna1') while (plotMade == False): time.sleep(1) print "Testing for plot %s..." % plotAmpBl plotMade = os.path.isfile(plotAmpBl) print "Plot not found." htmlTail = '''
Phase vs. Amplitude: Amplitude vs. Baseline:

spw%s:


''' html.write(htmlTail) html.close() timePlotCaldataEnd = time.time() timePlotCaldataTotal = timePlotCaldataEnd - timePlotCaldataStart casalog.origin('mkpipeline') casalog.post("Plotting calibrated data took %0.2f seconds (%0.2f minutes)" % (timePlotCaldataTotal, timePlotCaldataTotal/60)) casalog.post(" ") ######################################################################## # Imaging and measurement of dynamic range -- one image per source name # and spw for which there are valid calibration or target data. def mkImages(msName, imageFields, rootName, plotDir, baseBand, repro, timeStr): casalog.origin('mkpipeline') casalog.post("Imaging calibrated data...") timeImgStart = time.time() # Speed of light, m/s C = 2.998e8 tb.open(msName+'/ANTENNA') antPos = tb.getcol('POSITION') tb.close() tb.open(msName+'/SPECTRAL_WINDOW') freqList = tb.getcol('REF_FREQUENCY')/1e9 tb.close() tb.open(msName+'/FIELD') srcNamesArr = tb.getcol('NAME') tb.close() antDist = [] for antenna1 in range(0,antPos.shape[1]): xRef = antPos[0][antenna1] yRef = antPos[1][antenna1] zRef = antPos[2][antenna1] for antenna2 in range(antenna1+1,antPos.shape[1]): x = antPos[0][antenna2] y = antPos[1][antenna2] z = antPos[2][antenna2] antDist.append(np.sqrt((xRef-x)**2.0+(yRef-y)**2.0+(zRef-z)**2.0)) # Maximum distance between antennas (m) maxDist = max(antDist) # Set up arrays to be used for image statistics allFlux = np.zeros((len(imageFields),len(baseBand)),float) ## allFluxErr = np.zeros((len(imageFields),len(baseBand)),float) allMedianRMS = np.zeros((len(imageFields),len(baseBand)),float) allFreq = np.zeros((len(imageFields),len(baseBand)),float) for j in range(0,len(imageFields)): for s in range(0, len(baseBand)): imgField = imageFields[j] imgSPWstr = baseBand[s] srcName = str(srcNamesArr[imgField]) # chanMax = chanNum[imgSPW] - 4 # srcSPWstr = '%s:3~%d' % (imgSPWstr,chanMax) # Use the center SPW's frequency: testBand = baseBand[s].find('~') if (testBand > -1): (bLow, bHigh) = str.split(baseBand[s], '~') bLow = int(bLow) bHigh = int(bHigh) cntrSPW = int(bLow + (bHigh - bLow + 1)/2) else: cntrSPW = int(baseBand[s]) srcFreq = freqList[cntrSPW] try: # Get source name, spw frequency: if repro: imName = rootName+'.'+timeStr+'.field'+str(imageFields[j])+'.spw'+imgSPWstr else: imName = rootName+'.field'+str(imageFields[j])+'.spw'+imgSPWstr # Determine the cell and image size (note using ref. # frequency that is the lower bound of the spw). # Want image to be 6xprimary beam and pixels to be # 1/5 of the synthesized beam. wavelength = C/(srcFreq*1e9) maxDistL = maxDist/wavelength # Synthesized beam in arcseconds synthBeam=(1.2/maxDistL)*(180/np.pi)*60*60 # Pixel size (arcsec) cellSize = synthBeam/5 # Primary beam in arcseconds primaryBeam = 60.0*45.0/srcFreq # Image the larger of one full primary beam or 120 synthesized beams. # Note that the number of synthesized beams this will include depends on the # configuration: # A-config -> theta(PB) ~ 1200 * theta(SB) # D-config -> theta(PB) ~ 40 * theta(SB) (so we will image 3 primary beams) imSize = np.max([np.int(primaryBeam / cellSize), 640]) # Don't use a mask for cleaning for now # imMask = [ctrPix-(imSize/8),ctrPix-(imSize/8), \ # ctrPix+(imSize/8),ctrPix+(imSize/8)] casalog.origin('mkpipeline') casalog.post("Calculated a synthesized beam size of %.3f arcsec and a primary beam size of %.2f arcmin" % (synthBeam, primaryBeam/60)) casalog.post("Imaging %s in spw%s (%s GHz) using a cell size of %.5f arcsec and" % (srcName,imgSPWstr,str(srcFreq),cellSize)) casalog.post("an image size of %i pixels, corresponding to %.1f primary beam(s)." % (imSize, (imSize / primaryBeam / cellSize))) clean(vis=msName, imagename=imName, field=str(imgField), \ spw=imgSPWstr, imsize=[imSize], imagermode='csclean', \ cell=[str(cellSize)+'arcsec'], calready=True, \ niter=100, psfmode='clark', pbcor=True, minpb=0.05) # Print out the images for summary page outImg = plotDir + '/' + imName + '.jpg' viewer(infile=imName+'.image', zoom=4, gui=False, outfile=outImg) # Sample RMS at eight points around phase center, each of which is # 1/4 PB from center of image and 1/10 PB on a side ctrPix = imSize/2 rmsPix = imSize/4 lenPix = imSize/20 # Offset in X, Y: eightPtX = [-rmsPix, 0, rmsPix, rmsPix, rmsPix, 0, -rmsPix, -rmsPix] eightPtY = [rmsPix, rmsPix, rmsPix, 0, -rmsPix, -rmsPix, -rmsPix, 0] rmsList = [] for i in range(0,8): Xmin = ctrPix + eightPtX[i] - lenPix Xmax = ctrPix + eightPtX[i] + lenPix Ymin = ctrPix + eightPtY[i] - lenPix Ymax = ctrPix + eightPtY[i] + lenPix boxStr = str(Xmin)+','+str(Ymin)+','+str(Xmax)+','+str(Ymax) statArr = imstat(imagename=imName+'.image', \ box=boxStr) rmsList.append(statArr['rms'][0]) allMedianRMS[j,s] = np.median(rmsList) # Find maximum in image, as well as frequency: imMax = imstat(imagename=imName+'.image')['max'][0] srcFreq = imhead(imName+'.image', mode='get', hdkey='crval4')['value'] / 1e9 # Image statistics: peak in image as well as noise # statistics from several different locations. Use # the median value of the latter to determine the # dynamic range of the image. Use a box that is 12 x # synthesized beam on a side: ## boxStr = str(ctrPix - 30) + ',' + str(ctrPix - 30) + ',' + \ ## str(ctrPix + 30) + ',' + str(ctrPix + 30) ## casalog.origin('mkpipeline') ## casalog.post("Fitting gaussian in region %s" % boxStr) ## gaussFit = imfit(imagename=imName+'.image', \ ## box=boxStr) ## srcFlux = gaussFit['results']['component0']['flux']['value'][0] ## srcFluxErr = gaussFit['results']['component0']['flux']['error'][0] ## srcFreq = gaussFit['results']['component0']['spectrum']['frequency']['m0']['value'] # Estimate dynamic range: casalog.origin('mkpipeline') casalog.post("Evaluating image "+imName+".image:") ## casalog.post(" source flux is "+str(srcFlux)+"("+str(srcFluxErr)+") Jy") casalog.post(" maximum value is "+str(imMax)+" Jy / beam") casalog.post(" median RMS is "+str(np.median(rmsList))+" Jy / beam:") ## casalog.post(" measured dynamic range is "+str(srcFlux/np.median(rmsList))) casalog.post(" estimated dynamic range is "+str(imMax/np.median(rmsList))) casalog.post(" ") ## allFlux[j,s] = srcFlux ## allFluxErr[j,s] = srcFluxErr allFlux[j,s] = imMax allFreq[j,s] = srcFreq except Exception, instance: casalog.origin('mkpipeline') casalog.post("*** Error *** "+str(instance),priority='ERROR') timeImgEnd = time.time() timeImgTotal = timeImgEnd - timeImgStart casalog.origin('mkpipeline') casalog.post("Imaging calibrated data took %0.2f seconds (%0.2f minutes)" % (timeImgTotal, timeImgTotal/60)) casalog.post(" ") # Make web page with plots linkHTML = open(plotDir+'/imagePlots.html', 'w') htmlText = ''' Cleaned images for '''+msName+''' Image median RMS values and dynamic ranges:

''' linkHTML.write(htmlText) for j in range(0,len(imageFields)): srcName = str(srcNamesArr[imageFields[j]]) for b in range(0,len(baseBand)): srcSPW = baseBand[b] srcFreq = allFreq[j,b] if (allMedianRMS[j,b] == 0): htmlText = ''' ''' % (srcName,imageFields[j],srcSPW,srcFreq) else: htmlText = ''' ''' % (srcName,imageFields[j],srcSPW,srcFreq,allMedianRMS[j,b]*1e6,allFlux[j,b]*1e3,allFlux[j,b]/allMedianRMS[j,b]) linkHTML.write(htmlText) htmlText = '''
Source (field ID): SPW (frequency): Median image RMS: Source flux: Dynamic range:
%s (%i) spw %s, %.2f GHz INVALID INVALID INVALID
%s (%i) spw %s, %.2f GHz %d uJy / beam %.3f mJy / beam %d

Images:

''' linkHTML.write(htmlText) for j in range(0,len(imageFields)): srcName = str(srcNamesArr[imageFields[j]]) srcID = str(imageFields[j]) for b in range(0,len(baseBand)): srcSPW = baseBand[b] if repro: imName = rootName + '.' + timeStr + '.field' + srcID + '.spw' + srcSPW + '.jpg' else: imName = rootName + '.field' + srcID + '.spw' + srcSPW + '.jpg' htmlText = ' \n' % (srcSPW, srcName, srcID, imName) linkHTML.write(htmlText) htmlText = '''
spw%s, %s (field ID %s):
''' linkHTML.write(htmlText) linkHTML.close() ######################################################################## # Make a summary web page: def mkWebPage(msName, plotDir, plotScan, logName, fluxFields, doPlot, repro, checkZeros, baseBand, bBandOrig, calFields): linkHTML = open(plotDir+'/index.html', 'w') htmlText = ''' mkpipeline: '''+msName+'''

Plots and log files for '''+msName+''':

''' linkHTML.write(htmlText) if not repro: htmlText = '''

Observation metadata:

Raw data:

''' linkHTML.write(htmlText) htmlText = '''

Calibration:

Imaging:

Log files:

'''
    linkHTML.write(htmlText)
    linkHTML.close()

    os.system("cat " + plotDir + "/mkpipeline.log >> " + plotDir + "/index.html")

    linkHTML = open(plotDir+'/index.html', 'a')
    htmlText = '''
''' linkHTML.write(htmlText) linkHTML.close() ######################################################################## # Check for missing scans. def checkScans(msName): tb.open(msName) scanNums = sorted(np.unique(tb.getcol('SCAN_NUMBER'))) tb.close() missingScans = 0 missingScanStr = '' for i in range(max(scanNums)): if scanNums.count(i+1) == 1: pass else: casalog.origin('mkpipeline') casalog.post("WARNING: Scan "+str(i+1)+" is not present",priority='WARNING') missingScans += 1 missingScanStr = missingScanStr+str(i+1)+', ' if (missingScans > 0): casalog.origin('mkpipeline') casalog.post("WARNING: There were "+str(missingScans)+" missing scans in this MS",priority='WARNING') casalog.post(" ") else: casalog.origin('mkpipeline') casalog.post("No missing scans found.") casalog.post(" ") ######################################################################## # Find zeros. For now, use a clip expression that clips *outside* a # particular range. Back up the flags first, then restore when done. def findZeros(sdmName, timeStr, msName, plotDir): zerosave = sdmName + '.' + timeStr + '.flagzero.backup' zerocomment = 'widartest - saved flags before zero flagging' fg.done() fg.open(msName) fg.saveflagversion(versionname=zerosave,comment=zerocomment,merge='replace') fg.done() flagdata(vis=msName, mode='manualflag', \ clipexpr=['ABS RR','ABS LL','ABS RL','ABS LR'], \ clipminmax=[0.0,1e-8], clipoutside=True, flagbackup=False) zeroStats = flagdata(vis=msName, mode='summary', flagbackup=False) # Want to make plots of zeros, # First by spectral window: SPWs = [] zerosSPW = [] nonZerosSPW = [] for spwStr in zeroStats['spw'].keys(): SPWs.append(int(spwStr)) zerosSPW.append(zeroStats['spw'][spwStr]['total'] - zeroStats['spw'][spwStr]['flagged']) nonZerosSPW.append(zeroStats['spw'][spwStr]['flagged']) spwInd = np.argsort(SPWs) zerosSPW = np.array(zerosSPW)[spwInd] nonZerosSPW = np.array(nonZerosSPW)[spwInd] SPWs = np.array(SPWs)[spwInd] # Next by baseline: Baselines = [] zerosBL = [] nonZerosBL = [] sortedKeys = sorted(zeroStats['baseline'].keys()) for baseline in sortedKeys: Baselines.append(baseline) zerosBL.append(zeroStats['baseline'][baseline]['total'] - zeroStats['baseline'][baseline]['flagged']) nonZerosBL.append(zeroStats['baseline'][baseline]['flagged']) # And by antenna: Antennas = [] zerosAnt = [] nonZerosAnt = [] sortedKeys = sorted(zeroStats['antenna'].keys()) for antenna in sortedKeys: Antennas.append(antenna) zerosAnt.append(zeroStats['antenna'][antenna]['total'] - zeroStats['antenna'][antenna]['flagged']) nonZerosAnt.append(zeroStats['antenna'][antenna]['flagged']) # Finally, by scan: Scans = [] zerosScan = [] nonZerosScan = [] for scanStr in zeroStats['scan'].keys(): Scans.append(int(scanStr)) zerosScan.append(zeroStats['scan'][scanStr]['total'] - zeroStats['scan'][scanStr]['flagged']) nonZerosScan.append(zeroStats['scan'][scanStr]['flagged']) scanInd = np.argsort(Scans) zerosScan = np.array(zerosScan)[scanInd] nonZerosScan = np.array(nonZerosScan)[scanInd] Scans = np.array(Scans)[scanInd] # Check that there are zeros present; if not, print a message that # asks if perhaps they were already flagged: if ((np.sum(zerosSPW) == 0) and (np.sum(zerosBL) == 0) and (np.sum(zerosAnt) == 0) and (np.sum(zerosScan) == 0)): casalog.origin('mkpipeline') casalog.post("Did not find any zeros in the data. This could be because they were already flagged.") # Plot everything: pl.clf() pl.subplot(221) pl.subplots_adjust(left=0.1,right=0.97,bottom=0.1,top=0.95,wspace=0.3,hspace=0.2) pl.title('Spectral Window') pl.ylabel('% Zeros') pl.xticks(pl.arange(len(SPWs)),SPWs) zerosPctSPW = 100*np.array(zerosSPW,float)/(np.array(nonZerosSPW,float)+np.array(zerosSPW,float)) pl.plot(SPWs, zerosPctSPW, 'ro') pl.axis([SPWs[0]-0.5, SPWs[len(SPWs)-1]+0.5, -5, 105]) pl.subplot(222) pl.title('Scan') pl.ylabel('% Zeros') zerosPctScan = 100*np.array(zerosScan,float)/(np.array(nonZerosScan,float)+np.array(zerosScan,float)) pl.plot(Scans, zerosPctScan, 'r.') pl.axis([Scans[0]-0.5, Scans[len(Scans)-1]+0.5, -5, 105]) pl.subplot(223) pl.title('Baseline') pl.ylabel('% Zeros') zerosPctBL = 100*np.array(zerosBL,float)/(np.array(nonZerosBL,float)+np.array(zerosBL,float)) pl.plot(zerosPctBL, 'r.') pl.axis([0.5, len(zerosBL)+0.5, -5, 105]) pl.subplot(224) pl.title('Antenna') pl.ylabel('% Zeros') pl.xticks(pl.arange(len(Antennas)),Antennas,rotation=90) zerosPctAnt = 100*np.array(zerosAnt,float)/(np.array(nonZerosAnt,float)+np.array(zerosAnt,float)) pl.plot(zerosPctAnt, 'r.') pl.axis([-0.5, len(zerosAnt), -5, 105]) pl.savefig(plotDir+'/zeros.png', format='png') # Revert to the old flags fg.done() fg.open(msName) fg.restoreflagversion(versionname=zerosave, merge='replace') fg.done() ######################################################################## # Choose a reference antenna def chooseRefAnt(msName): # Get antenna information tb.open(msName+'/ANTENNA') antStation = tb.getcol('STATION') antName = tb.getcol('NAME') antPos = tb.getcol('POSITION') tb.close() # A list of pads, in order of preference (note: may wish to tie this # to the array configuration in the future): bestStationList = ['E01','W02','E02','W03','E03','N02','W01','N01', \ 'N03','E04','W04','N04','E05','W05','N05','E06', \ 'W06','N06','E07','W07','N07','E08','W08','N08', \ 'E09','W09','N09'] bestAntList = [] for i in range(0,len(bestStationList)): if antStation.__contains__(bestStationList[i]): name = antName[np.where(antStation == bestStationList[i])][0] bestAntList.append(name) # Get flagging statistics to make sure antenna isn't "overly" flagged: fg.done() fg.clearflagselection(0) fg.open(msName) fg.setdata() fg.setflagsummary() msFlagStats = fg.run() fg.done() fracFlagAnt = [] fracAntList = [] for i in range(0,len(bestAntList)): flagged = float(msFlagStats['antenna'][bestAntList[i].upper()]['flagged']) total = float(msFlagStats['antenna'][bestAntList[i].upper()]['total']) fracFlagged = float(flagged/total) if (fracFlagged < 1.0): # Antennas which aren't completely flagged fracFlagAnt.append(fracFlagged) fracAntList.append(bestAntList[i]) casalog.origin('mkpipeline') casalog.post("For antenna %s there are %d flagged points, %d total points, and %.2f fraction" % (bestAntList[i],flagged,total,fracFlagged)) # Choose the first antenna with fewer flags than the mean number: flagAntMean = np.mean(fracFlagAnt) refAnt = '' for i in range(0,len(fracAntList)): if (fracFlagAnt[i] < flagAntMean): refAnt = fracAntList[i] break if (refAnt == ''): casalog.origin('mkpipeline') casalog.post("ERROR: Unable to find a reference antenna",priority='ERROR') return flagAntMean, refAnt, fracFlagAnt else: casalog.origin('mkpipeline') casalog.post("Using reference antenna %s" % refAnt) return refant ######################################################################## # Weather table plotting & opacity determination - J. Marvil 3.21.11 # gets and plots data from the weather table of the given MS def plotWX(vis, seasonal_weight, doPlot, plotDir): myMS=vis ##retrieve center frequency for each sub-band tb.open(myMS+'/SPECTRAL_WINDOW') spwFreqs=tb.getcol('REF_FREQUENCY') * 1e-9 tb.close() ##retrieve stuff from weather table tb.open(myMS+'/WEATHER') mytime=tb.getcol('TIME') mytemp=tb.getcol('TEMPERATURE') - 273.15 mydew=tb.getcol('DEW_POINT') - 273.15 mywinds=tb.getcol('WIND_SPEED') mywindd=tb.getcol('WIND_DIRECTION')*(180.0/pi) mypres=tb.getcol('PRESSURE') myhum=tb.getcol('REL_HUMIDITY') tb.close() ##calculate the elevation of the sun sunEL=[] for time in mytime: t1= qa.quantity(time,'s') myday=qa.convert(t1,'d') sunEL1=jm_sunEL(myday) sunEL.append(sunEL1) ##convert time to a string of date/time myTimestr = [] myTimestr2=[] for time in mytime: q1=qa.quantity(time,'s') time1=qa.time(q1,form='ymd') time2=qa.time(q1,form='local') myTimestr.append(time1) myTimestr2.append(time2) ##convert time to a decimal numtime=pl.datestr2num(myTimestr) ##calculate opacity as in EVLA memo 143 thisday= 30*(float(myTimestr[0][5:7])-1)+float(myTimestr[0][8:10]) thisday=thisday + 5 * (thisday / 365.) myTauZ=Tau_K_Calc(mydew,mytemp,thisday) myTauZ1=Tau_K_Calc(mydew,mytemp,thisday, weights=(0,1)) myTauZ2=Tau_K_Calc(mydew,mytemp,thisday, weights=(1,0)) myTauZ_custom=Tau_K_Calc(mydew,mytemp,thisday,weights=(1-seasonal_weight,seasonal_weight)) myPWV = -1.71 + 1.3647*myTauZ myPWV1 = -1.71 + 1.3647*myTauZ1 myPWV2 = -1.71 + 1.3647*myTauZ2 myPWV_custom=-1.71+1.3647*myTauZ_custom tmp = casac.Quantity(270.0,'K') pre = casac.Quantity(790.0,'mbar') alt = casac.Quantity(2125,'m') h0 = casac.Quantity(2.0,'km') wvl = casac.Quantity(-5.6, 'K/km') mxA = casac.Quantity(48,'km') dpr = casac.Quantity(10.0,'mbar') dpm = 1.2 att = 1 nb = 1 fC=casac.Quantity(25.0,'GHz') fW=casac.Quantity(50.,'GHz') fR=casac.Quantity(0.25,'GHz') attool=casac.homefinder.find_home_by_name('atmosphereHome') at=attool.create() hum=20.0 myatm=at.initAtmProfile(alt,tmp,pre,mxA,hum,wvl,dpr,dpm,h0,att) at.initSpectralWindow(nb,fC,fW,fR) sg=at.getSpectralWindow() mysg = sg.value nstep = 20 pwv = [] opac = pl.zeros((len(mysg),nstep)) for i in range(nstep): hum = 20.0*(i+1) myatm = at.initAtmProfile(alt,tmp,pre,mxA,hum,wvl,dpr,dpm,h0,att) w=at.getGroundWH2O() pwv.append(w.value) at.initSpectralWindow(nb,fC,fW,fR) at.setUserWH2O(w) sg=at.getSpectralWindow() mysg = sg.value sdry=at.getDryOpacitySpec() swet=at.getWetOpacitySpec() sd=sdry['dryOpacity'] sw=swet['wetOpacity'].value stot = pl.array(sd)+pl.array(sw) opac[:,i]=stot pwv_coef=pl.zeros((len(mysg),2)) for i in range(len(mysg)): myfit=pl.polyfit(pwv,opac[i,:],1) pwv_coef[i,:]=myfit freqs=pl.array(mysg)/1e9 coef0=pwv_coef[:,1]/1e-3 coef1=pwv_coef[:,0]/1e-3 #interpolate between nearest table entries for each spw center frequency meanTau=[] meanTau1=[] meanTau2=[] meanTau_custom=[] for i in range(len(spwFreqs)): mysearch=(pl.array(freqs)-spwFreqs[i])**2 hits=pl.find(mysearch == min(mysearch)) if len(hits) > 1: hits=hits[0] tau_interp = (pl.array(coef0[hits-2:hits+2])+pl.array(coef1[hits-2:hits+2])*pl.mean(myPWV)) * 1e-1 #taupercent tau_F = pl.interp(spwFreqs[i],freqs[hits-2:hits+2],tau_interp) meanTau.append(pl.mean(tau_F)) tau_interp1 = (pl.array(coef0[hits-2:hits+2])+pl.array(coef1[hits-2:hits+2])*pl.mean(myPWV1)) * 1e-1 #taupercent tau_F1 = pl.interp(spwFreqs[i],freqs[hits-2:hits+2],tau_interp1) meanTau1.append(pl.mean(tau_F1)) tau_interp2 = (pl.array(coef0[hits-2:hits+2])+pl.array(coef1[hits-2:hits+2])*pl.mean(myPWV2)) * 1e-1 #taupercent tau_F2 = pl.interp(spwFreqs[i],freqs[hits-2:hits+2],tau_interp2) meanTau2.append(pl.mean(tau_F2)) tau_interp_c = (pl.array(coef0[hits-2:hits+2])+pl.array(coef1[hits-2:hits+2])*pl.mean(myPWV_custom)) * 1e-1 #taupercent tau_F_c = pl.interp(spwFreqs[i],freqs[hits-2:hits+2],tau_interp_c) meanTau_custom.append(pl.mean(tau_F_c)/100.) tau_allF = (pl.array(coef0) + pl.array(coef1)*pl.mean(myPWV)) * 1e-1 tau_allF1 = (pl.array(coef0) + pl.array(coef1)*pl.mean(myPWV1)) *1e-1 tau_allF2 = (pl.array(coef0) + pl.array(coef1)*pl.mean(myPWV2)) *1e-1 tau_allF_c = (pl.array(coef0) + pl.array(coef1)*pl.mean(myPWV_custom)) *1e-1 ##make the plot if doPlot==False: casalog.origin('mkpipeline') casalog.post('SPW : Frequency (GHz) : Zenith opacity (nepers)') for i in range(len(meanTau_custom)): myStr = str(i).rjust(3) + ' : ' myStr2 = '%.3f'%(spwFreqs[i]) myStr += myStr2.rjust(7) + ' : ' +str(round(meanTau_custom[i], 3)) casalog.post(myStr) casalog.post(" ") return meanTau_custom myColor1='#A6A6A6' myBlueGreyColor='#92B5F2' myColor2='#4D4DFF' myOrangeColor='#FF6600' myYellowColor='#FFCC00' myWeirdColor='#006666' myLightBrown='#996633' myDarkGreay='#333333' pl.rcdefaults() pl.rc('axes',labelsize=12) thisfig=pl.figure(1) thisfig.clf() thisfig.set_size_inches(8.5,10) Xrange=numtime[-1]-numtime[0] Yrange=max(mywinds)-min(mywinds) Xtextoffset=-Xrange*.01 Ytextoffset=-Yrange*.08 Xplotpad=Xrange*.03 Yplotpad=Yrange*.03 sp1=thisfig.add_axes([.13,.8,.8,.15]) pl.ylabel('solar el') nsuns=30 myj=pl.array(pl.linspace(0,len(sunEL)-1,nsuns),dtype='int') for i in myj: if sunEL[i]<0: pl.plot([numtime[i],numtime[i]],[(180/pi)*sunEL[i],(180/pi)*sunEL[i]],'kH') else: pl.plot([numtime[i],numtime[i]],[(180/pi)*sunEL[i],(180/pi)*sunEL[i]],'H',color=myYellowColor) pl.plot([numtime[0],numtime[-1]],[0,0],'-',color='brown') xa=pl.gca(); xa.set_xticklabels('') jm_set_Ylim_ticks(myMin=-90,myMax=90) jm_set_Ylabel_pos(pos=(0,.5)) pl.title('Weather Summary for '+myMS) pl.xlim(numtime[0]-Xplotpad,numtime[-1]+Xplotpad) xa.set_xticks(pl.linspace(min(numtime),max(numtime),3)) sp2=thisfig.add_axes([.13,.65,.8,.15]) pl.ylabel('wind (m/s)') nwind=60 myj=pl.array(pl.linspace(0,len(mywinds)-1,nwind),dtype='int') for i in myj: pl.text(numtime[i]+Xtextoffset,Ytextoffset+mywinds[i],'-->',rotation=mywindd[i], alpha=1,color='purple',fontsize=12) pl.plot(numtime, .3+mywinds,'.', color='black', ms=2, alpha=0) jm_set_Ylabel_pos(pos=(0,.5)) jm_set_Yvar_ticks(5) xa=pl.gca(); xa.set_xticklabels('') pl.xlim(numtime[0]-Xplotpad,numtime[-1]+Xplotpad) pl.ylim(min(mywinds)-Yplotpad,max(mywinds)+Yplotpad) xa.set_xticks(pl.linspace(min(numtime),max(numtime),3)) sp4=thisfig.add_axes([.13,.5,.8,.15]) pl.plot(numtime, mytemp,'-', color=myOrangeColor,lw=2) pl.plot(numtime, mydew,'-', color=myWeirdColor,lw=2) pl.ylabel('temp,dew') jm_set_Ylabel_pos(pos=(0, .5)) xa=pl.gca(); xa.set_xticklabels('') jm_set_Yvar_ticks(5) pl.xlim(numtime[0]-Xplotpad,numtime[-1]+Xplotpad) xa.set_xticks(pl.linspace(min(numtime),max(numtime),3)) sp7=thisfig.add_axes([.13,.35,.8,.15]) pl.ylabel('PWV (mm)') pl.plot(numtime, myPWV, color=myBlueGreyColor,lw=2, label='average') pl.plot(numtime, myPWV1, color=myColor1, lw=2, label='weather station') pl.plot(numtime, myPWV2, color=myColor2, lw=2, label='seasonal') thismin=min([min(myPWV),min(myPWV1),min(myPWV2)]) thismax=max([max(myPWV),max(myPWV1),max(myPWV2)]) pl.ylim(.8*thismin,1.2*thismax) jm_set_Ylabel_pos(pos=(0,.5)) jm_set_Yvar_ticks(5) xa=pl.gca(); xa.set_xticklabels('') pl.xlim(numtime[0]-Xplotpad,numtime[-1]+Xplotpad) middletimei=int(floor(len(myTimestr)/2.)) middletimes=str(myTimestr[middletimei])[11:] endtimes=myTimestr[-1][11:] ax=pl.gca() axt=ax.get_xticks() ax.set_xticks(pl.linspace(min(numtime),max(numtime),3)) ax.set_xticklabels([myTimestr[0],middletimes,endtimes ]) sp8=thisfig.add_axes([.13,.1,.8,.2]) pl.plot(freqs,.01*tau_allF2,'-', color=myColor2, lw=2, label='weather station') pl.plot(freqs,.01*tau_allF1,'-', color=myColor1, lw=2, label='seasonal model') pl.plot(freqs,.01*tau_allF,'-', color=myBlueGreyColor, lw=2,label='average') sp8.legend(loc=2, borderaxespad=0) pl.ylabel('Tau_Z (nepers)') pl.xlabel('Frequency (GHz)') pl.ylim(0,.25) jm_set_Yvar_ticks(6) jm_set_Ylabel_pos(pos=(0,.5)) pl.savefig(plotDir+'/weatherInfo.png',dpi=150) pl.close() casalog.post('wrote weather figure: weatherInfo.png') casalog.post('SPW : Frequency (GHz) : Zenith opacity (nepers)') for i in range(len(meanTau_custom)): myStr = str(i).rjust(3) + ' : ' myStr2 = '%.3f'%(spwFreqs[i]) myStr += myStr2.rjust(7) + ' : ' +str(round(meanTau_custom[i], 3)) casalog.post(myStr) casalog.post(" ") return meanTau_custom ############### ## hides the extreme Y-axis ticks, helps stack plots close together without labels overlaping def jm_clip_Yticks(): xa=pl.gca() nlabels=0 for label in xa.yaxis.get_ticklabels(): nlabels+=1 thislabel=0 if nlabels>3: for label in xa.yaxis.get_ticklabels(): if thislabel==0: label.set_alpha(0) if thislabel==nlabels-1: label.set_alpha(0) thislabel+=1 ############## ## sets the position of the y-axis label to the right side of the plot, can also move up/down def jm_set_Ylabel_pos(pos=(0.5,0.5)): ax=pl.gca(); ax.yaxis.set_label_position('right') ax.yaxis.label.set_rotation(270) ax.yaxis.label.set_position(pos) ############### ## fixed y-ticks for plotting phase, in this case, wind direction def jm_set_Yphase_ticks(myScale=180): myYlocs=pl.linspace(-myScale,myScale,5) myLocator = pl.FixedLocator(myYlocs) ax=pl.gca() ax.yaxis.set_major_locator( myLocator ) pl.ylim([-1.1*myScale,1.1*myScale]) jm_clip_Yticks() ############### ## fixed y-ticks, from zero to myScale, used for data that can't be negative def jm_set_Ygain_ticks(myScale=.2): myYlocs=pl.linspace(0,myScale,5) myLocator = pl.FixedLocator(myYlocs) ax=pl.gca() ax.yaxis.set_major_locator( myLocator ) pl.ylim([-.02*myScale,1.1*myScale]) jm_clip_Yticks() ############### ## fixed y-ticks, from myMin to myMax def jm_set_Ylim_ticks(myMin=-1,myMax=1): myYlocs=pl.linspace(round(myMin,1),round(myMax,1),5) myLocator = pl.FixedLocator(myYlocs) ax=pl.gca() ax.yaxis.set_major_locator( myLocator ) pl.ylim(myMin,myMax) jm_clip_Yticks() ############### ## variable y-ticks, but not more than 1+ this argument def jm_set_Yvar_ticks(myScale=4): xa=pl.gca() xa.yaxis.set_major_locator(pl.MaxNLocator(myScale)) jm_clip_Yticks() ############### ## calculates K-band zenith opacity from temperature and dewpoint def Tau_K_Calc(D,T,day, weights=(.5,.5)): P = pl.exp(1.81+(17.27*D)/(D+237.3)) # water vapor partial pressure h = 324.7*P/(T+273.15) # PWV in mm tau_w = 3.8 + 0.23*h + 0.065*h**2 # tau from weather, in %, at 22GHz if day > 199: day = day - 365. m = day + 165. # modified day of the year tau_d = 22.1 - 0.178*m + 0.00044*m**2 # tau from seaonal model, in % tau_k = weights[0]*tau_w + weights[1]*tau_d # the average, with equal weights (as in the AIPS default) return tau_k ################ ## calculates elevation of the sun def jm_sunEL(mytime): me.doframe(me.observatory('VLA')) me.doframe(me.epoch('utc',mytime)) mysun=me.measure(me.direction('SUN'),'AZELGEO') return mysun['m1']['value']