Commits

Rui Xue authored 5e84fc392dc
PIPE-2366: use the `tsysspwmap` helper function from a copy of `almahelpers.py` in pipeline/extern.
No tags

pipeline/extern/almahelpers.py

Added
1 +#!/usr/bin/env python
2 +#
3 +# almahelpers.py
4 +#
5 +# History:
6 +# v1.0 (scorder, gmoellen, jkern; 2012Apr26) == initial version
7 +# v1.1 (gmoellen; 2013Mar07) Lots of improvements from Eric Villard
8 +# v1.2 (gmoellen; 2013Apr09) Added fixsyscaltimes and calantsub methods
9 +# to handle Tsys irregularities
10 +# v1.3 (dpetry; 2017Sept11) Added genImageName
11 +# v1.4 (gmoellen; 2021Jul08) fixsyscaltimes now returns True if a fix
12 +# was applied, otherwise returns False
13 +#
14 +# This script defines several functions useful for ALMA data processing.
15 +#
16 +# tsysspwmap - generate an "applycal-ready" spwmap for TDM to FDM
17 +# transfer of Tsys
18 +# fixsyscaltimes - repairs the TIME and INTERVAL columns of the MS
19 +# SYSCAL subtable so that gencal properly generates
20 +# the Tsys caltable
21 +# calantsub - provides for substitution of cal solutions by antenna
22 +#
23 +# genImageName - generate canonical image file names
24 +#
25 +# To access these functions, type (at the CASA prompt):
26 +#
27 +# from recipes.almahelpers import *
28 +#
29 +# For more information about each function type
30 +#
31 +# help tsysspwmap
32 +# help fixsyscaltimes
33 +# help calantsub
34 +# help genImageName
35 +#
36 +#
37 +import numpy
38 +from casatools import table as tbtool
39 +from casatools import quanta as qatool
40 +from casatools import msmetadata as msmdtool
41 +from functools import reduce
42 +from casatasks import casalog
43 +import os
44 +import csv # used by editIntentscsv
45 +
46 +class SpwMap:
47 + """
48 + This object is basically set up to hold the information needed
49 + """
50 + def __init__(self,calSpwId):
51 + self.calSpwId = calSpwId
52 + self.validFreqRange = []
53 + self.mapsToSpw = []
54 + self.bbNo = None
55 +
56 +class SpwInfo:
57 + def __init__(self,msfile,spwId) :
58 + self.tb = tbtool()
59 + self.setTableAndSpwId(msfile,spwId)
60 +
61 + def setTableAndSpwId(self,msfile,spwId) :
62 + self.setTable(msfile)
63 + self.setSpwId(spwId)
64 +
65 + def setTable(self,msfile) :
66 + self.tableName = "%s/SPECTRAL_WINDOW" % msfile
67 + self.tb.open(self.tableName)
68 + self.parameters = self.tb.colnames()
69 + self.tb.close()
70 +
71 + def setSpwId(self,spwId) :
72 + self.tb.open(self.tableName)
73 + self.values = {}
74 + for i in self.parameters :
75 + self.values[i] = self.tb.getcell(i,spwId)
76 + self.tb.close()
77 +
78 +def areIdentical(spwInfo1,spwInfo2) :
79 + if len(spwInfo1.parameters) <= len(spwInfo2.parameters) :
80 + minState = spwInfo1 ; maxState = spwInfo2
81 + else :
82 + minState = spwInfo2 ; maxState = spwInfo1
83 + valueCompare = []
84 + for i in minState.parameters :
85 + compare = (minState.values[i] == maxState.values[i])
86 + if numpy.ndarray not in [type(compare)] :
87 + compare = numpy.array(compare)
88 + if compare.all() : valueCompare.append(True)
89 + else : valueCompare.append(False)
90 + if False in valueCompare : return False
91 + else : return True
92 +
93 +def trimSpwmap(spwMap) :
94 + compare = range(len(spwMap))
95 + evenPoint = compare[-1]
96 + for i in compare :
97 + if compare[i:] == spwMap[i:] :
98 + evenPoint = i
99 + break
100 + return spwMap[:i]
101 +
102 +
103 +def tsysspwmap(vis,tsystable,trim=True,relax=False, tsysChanTol=0):
104 + """
105 + Generate default spwmap for ALMA Tsys, including TDM->FDM associations
106 + Input:
107 + vis the target MeasurementSet
108 + tsystable the input Tsys caltable (w/ TDM Tsys measurements)
109 + trim if True (the default), return minimum-length spwmap;
110 + otherwise the spwmap will be exhaustive and include
111 + the high-numbered (and usually irrelevant) wvr
112 + spws
113 + relax (not yet implemented)
114 + Output:
115 + spw list to use in applycal spwmap parameter for the Tsys caltable
116 +
117 + This function takes the Tsys Caltable you wish to apply to a
118 + MeasurementSet and generates a "applycal-ready" spwmap that
119 + provides the appropriate information regarding the transfer
120 + Tsys calibration from TDM spectral windows to FDM spectral
121 + windows. To execute the function:
122 +
123 + tsysmap=tsysspwmap(vis='my.ms',tsystable='mytsys.cal')
124 +
125 + tsysmap can then be supplied to the applycal spwmap parameter
126 + to ensure proper Tsys calibration application.
127 +
128 + """
129 +
130 + localTb = tbtool()
131 + spwMaps = []
132 + # Get the spectral windows with entries in the solution table
133 + localTb.open(tsystable)
134 + measuredTsysSpw = numpy.unique(localTb.getcol("SPECTRAL_WINDOW_ID"))
135 + localTb.close()
136 + # Get the frequency ranges for the allowed
137 + localTb.open("%s/SPECTRAL_WINDOW" % tsystable)
138 + for i in measuredTsysSpw:
139 + spwMap = SpwMap(i)
140 + chanFreqs = localTb.getcell("CHAN_FREQ",i)
141 + chanWidth = abs(chanFreqs[1]-chanFreqs[0])
142 + spwMap.chanWidth = chanWidth
143 + spwMap.validFreqRange = [chanFreqs.min()-0.5*chanWidth,\
144 + chanFreqs.max()+0.5*chanWidth]
145 + spwMaps.append(spwMap)
146 + localTb.close()
147 + # Now loop through the main table's spectral window table
148 + # to map the spectral windows as desired.
149 + localTb.open("%s/SPECTRAL_WINDOW" % vis)
150 + it = localTb.nrows()
151 + localTb.close()
152 + for j in spwMaps :
153 + localTb.open("%s/SPECTRAL_WINDOW" % vis)
154 + j.bbNo = localTb.getcell("BBC_NO",j.calSpwId)
155 + localTb.close()
156 + for i in range(it) :
157 + localTb.open("%s/SPECTRAL_WINDOW" % vis)
158 + chanFreqs = localTb.getcell("CHAN_FREQ",i)
159 + if len(chanFreqs) > 1 :
160 + chanWidth = localTb.getcell("CHAN_WIDTH",i)[0]
161 + freqMin = chanFreqs.min()-0.5*chanWidth
162 + freqMax = chanFreqs.max()+0.5*chanWidth
163 + else :
164 + chanWidth = localTb.getcell("CHAN_WIDTH",i)
165 + freqMin = chanFreqs-0.5*chanWidth
166 + freqMax = chanFreqs+0.5*chanWidth
167 + msSpw = SpwInfo(vis,i)
168 + if j.bbNo == msSpw.values['BBC_NO']:
169 + if freqMin >= j.validFreqRange[0]-tsysChanTol*j.chanWidth and \
170 + freqMax <= j.validFreqRange[1]+tsysChanTol*j.chanWidth :
171 + j.mapsToSpw.append(i)
172 + localTb.close()
173 + applyCalSpwMap = []
174 + spwWithoutMatch = []
175 + localTb.open("%s/SPECTRAL_WINDOW" % vis)
176 + for i in range(it) :
177 + useSpw = None
178 + for j in spwMaps :
179 + if i in j.mapsToSpw :
180 + if useSpw is not None :
181 + if localTb.getcell("BBC_NO") == j.bbNo :
182 + useSpw = j.calSpwId
183 + else :
184 + useSpw = j.calSpwId
185 + if useSpw == None :
186 + useSpw = i
187 + spwWithoutMatch.append(i)
188 + applyCalSpwMap.append(int(useSpw))
189 + if len(spwWithoutMatch) != 0:
190 + casalog.post('Found no match for following spw ids: '+str(spwWithoutMatch))
191 + if trim :
192 + return trimSpwmap(applyCalSpwMap)
193 + else :
194 + return applyCalSpwMap
195 +
196 +
197 +def fixsyscaltimes(vis,newinterval=2.0):
198 + """
199 + Fix TIME,INTERVAL columns in MS SYSCAL subtable
200 + Input:
201 + vis the MS containing the offending SYSCAL subtable
202 + newinterval the interval to use in revised entries
203 +
204 + This function is intended to repair MS SYSCAL tables that suffer from
205 + multiple TIME values (over antennas) per Tsys measurement. The gencal
206 + task (mode='tsys' expects all antennas to share the same TIME value
207 + for each Tsys measurement (and this is usually true). The function
208 + finds those measurements that have multiple TIMEs and replaces them
209 + with a common TIME value which takes the value
210 + mean(oldTIME-INTERVAL/2)+newinterval/2.
211 + Usually (always?), oldTIME-INTERVAL/2 is constant over antennas
212 + and represents the physical timestamp of the Tsys measurment.
213 + If the function finds no pathological timestamps, it does not
214 + revise the table.
215 + """
216 +
217 + import pylab as mypl
218 + import math as mymath
219 + myqa=qatool()
220 + mytb=tbtool()
221 + mytb.open(vis+'/SYSCAL',nomodify=False)
222 +
223 + spws=mypl.unique(mytb.getcol("SPECTRAL_WINDOW_ID"))
224 +
225 + # Nominally, no fix applied
226 + fixapplied=False
227 +
228 + for ispw in spws:
229 + st=mytb.query('SPECTRAL_WINDOW_ID=='+str(ispw),name='byspw')
230 + times=st.getcol('TIME')
231 + interval=st.getcol('INTERVAL')
232 + timestamps=times-interval/2
233 + t0=86400.0*mymath.floor(timestamps[0]/86400.0)
234 +
235 + utimes=mypl.unique(times-t0)
236 + nT=len(utimes)
237 + utimestamps=mypl.unique(mypl.floor(timestamps)-t0)
238 + nTS=len(utimestamps)
239 +
240 + msg='In spw='+str(ispw)+' found '+str(nTS)+' Tsys measurements with '+str(nT)+' TIMEs...'
241 + if nT==nTS:
242 + msg+='OK.'
243 + print(msg)
244 +
245 + else:
246 + # If we reach here, then a fix is being applied, and we'll return True
247 + fixapplied=True
248 +
249 + msg+=' which is too many, so fixing it:'
250 + print(msg)
251 +
252 + for uts in utimestamps:
253 + mask = ((mypl.floor(timestamps))-t0==uts)
254 + uTIMEs=mypl.unique(times[mask])
255 + nTIMEs=len(uTIMEs)
256 + newtime = mypl.mean(times[mask]-interval[mask]/2) + newinterval/2
257 + msg=' Found '+str(nTIMEs)+' TIMEs at timestamp='+str(myqa.time(str(newtime-newinterval/2)+'s',form='ymd')[0])
258 + if nTIMEs>1:
259 + msg+=':'
260 + print(msg)
261 + print(' TIMEs='+str([myqa.time(str(t)+'s',form='ymd')[0] for t in uTIMEs])+' --> '+str(myqa.time(str(newtime)+'s',form='ymd')[0])+' w/ INTERVAL='+str(newinterval))
262 + times[mask]=newtime
263 + interval[mask]=newinterval
264 + st.putcol('TIME',times)
265 + st.putcol('INTERVAL',interval)
266 + else:
267 + msg+='...ok.'
268 + print(msg)
269 + st.close()
270 + mytb.close()
271 +
272 + if fixapplied:
273 + print('Some SYSCAL time corrections have been applied.')
274 + else:
275 + print('All SYSCAL times seem correct; no changes required.')
276 +
277 + # Return if a fix has been applied
278 + return fixapplied
279 +
280 +
281 +def calantsub(incaltable,outcaltable='',
282 + spw='',scan='',
283 + ant='',subant=''):
284 +
285 + """
286 + Substitute cal solutions by antenna
287 + Input:
288 + incaltable Input caltable
289 + outcaltable Output caltable (if '', overwrite result on incaltable)
290 + spw Spectral Window selection (no channel selection permitted)
291 + scan Scan selection
292 + ant Antenna (indices) which need replaced solutions
293 + subant Antenna (indices) with which to replace those in ant
294 +
295 + This function provides a means to replace solutions by antenna,
296 + e.g., to substitute one antenna's Tsys spectra with another.
297 +
298 + The processing can be limited to specific spectral windows and/or
299 + scans. The spw and scan parameters should be specified in the
300 + standard MS selection manner (comma-separated integers in a string),
301 + except no channel selection is supported.
302 +
303 + The ant parameter specifies one or more antenna indices
304 + (comma-separated in a string) for which solutions are to be
305 + replaced. The subant parameter lists the antenna indices
306 + from which the substitute solutions are to be obtained. E.g.,
307 + ant='3,5,7',subant='6,8,10' will cause the solutions from
308 + antenna id 6 to be copied to antenna id 5, id 8 to id 5 and id 10
309 + to id 7. The number of antennas specified in ant and subant
310 + must match.
311 +
312 + """
313 +
314 + import pylab as mypl
315 +
316 + # trap insufficient ant subant specifications
317 + if len(ant)==0 or len(subant)==0:
318 + raise Exception("Must specify at least one ant and subant.")
319 +
320 + antlist=ant.split(',')
321 + sublist=subant.split(',')
322 +
323 + # trap dumb cases
324 + nant=len(antlist)
325 + nsub=len(sublist)
326 + if nant!=nsub:
327 + raise Exception("Must specify equal number of ant and subant.")
328 +
329 + # local tb tool
330 + mytb=tbtool()
331 +
332 + # parse selection
333 + selstr=''
334 + if len(spw)>0:
335 + selstr+='SPECTRAL_WINDOW_ID IN ['+spw+']'
336 + if len(scan)>0:
337 + selstr+=' && '
338 + if len(scan)>0:
339 + selstr+='SCAN_NUMBER IN ['+scan+']'
340 + print("selstr = '"+selstr+"'")
341 +
342 + # verify selection (if any) selects non-zero rows
343 + if len(selstr)>0:
344 + mytb.open(incaltable)
345 + st=mytb.query(query=selstr)
346 + nselrows=st.nrows()
347 + st.close()
348 + mytb.close()
349 + if nselrows==0:
350 + raise Exception('Error: scan and/or spw selection selects no rows!')
351 +
352 + # manage the output table
353 + if outcaltable=='':
354 + outcaltable=incaltable
355 + print("No outcaltable specified; will overwrite incaltable.")
356 + if outcaltable!=incaltable:
357 + os.system('cp -r '+incaltable+' '+outcaltable)
358 +
359 + # open the output table for adjustment
360 + mytb.open(outcaltable,nomodify=False)
361 +
362 + stsel=mytb
363 + if len(selstr)>0:
364 + stsel=mytb.query(query=selstr,name='selected')
365 +
366 + # cols to substitute:
367 + collist=['TIME','INTERVAL','PARAMERR','SNR','FLAG']
368 + cols=mytb.colnames()
369 + if cols.count('CPARAM')>0:
370 + collist.append('CPARAM')
371 + else:
372 + collist.append('FPARAM')
373 +
374 + # scan list
375 + scans=mypl.unique(stsel.getcol('SCAN_NUMBER'))
376 +
377 + print('Found scans = ',scans)
378 +
379 + # do one scan at a time
380 + for scan in scans:
381 + st1=stsel.query(query='SCAN_NUMBER=='+str(scan),name='byscan')
382 + spws=mypl.unique(st1.getcol('SPECTRAL_WINDOW_ID'))
383 +
384 + print('Scan '+str(scan)+' has spws='+str(spws))
385 +
386 + # do one spw at a time
387 + for ispw in spws:
388 + st2=st1.query(query='SPECTRAL_WINDOW_ID=='+str(ispw),name='byspw');
389 +
390 + for ia in range(nant):
391 + stsub=st2.query(query='ANTENNA1=='+sublist[ia],
392 + name='subant')
393 + stant=st2.query(query='ANTENNA1=='+antlist[ia],
394 + name='ant')
395 +
396 +
397 + # go to next ant if nothing to do
398 + if stant.nrows()<1:
399 + continue
400 +
401 + print(' scan='+str(scan)+' spw='+str(ispw)+' ants: '+str(sublist[ia])+'->'+str(antlist[ia]))
402 +
403 + # trap (unlikely?) pathological case
404 + if stsub.nrows()!=stant.nrows():
405 + raise Exception("In spw "+str(ispw)+" antenna ids "+str(antlist[ia])+" and "+str(sublist[ia])+" have a different number of solutions.")
406 +
407 + # substitute values
408 + for col in collist:
409 + stant.putcol(col,stsub.getcol(col))
410 +
411 + stsub.close()
412 + stant.close()
413 + st2.close()
414 + st1.close()
415 + stsel.close()
416 + mytb.close()
417 +
418 +def editIntentscsv(intentcsv, dryrun=False, verbose=False):
419 + """
420 + Reads a list of parameters from a csv text file and runs editIntents on them.
421 + File format:
422 + # any number of comment lines
423 + vis,field,scan,"WVR,BANDPASS"
424 + vis,field,,'FLUX,WVR'
425 + ...
426 +
427 + * field is a required argument, while scan is optional
428 + * scan: can be a single integer, integer list, or comma-delimited string list
429 + multiple scans or intents must be quoted, either single or double quote
430 + -Todd Hunter
431 + """
432 + if (not os.path.exists(intentcsv)):
433 + print("Could not find intentcsv file: ", intentcsv)
434 + return
435 + f = open(intentcsv,'r')
436 + lines = f.readlines()
437 + f.close()
438 + for originalLine in lines:
439 + if (originalLine[0] == '#'): continue
440 + line = originalLine.replace("'",'"').replace(' ','')
441 + if verbose:
442 + print("Parsing reformatted line: ", line[:-1])
443 + if (len(line.split(',')) > 3):
444 + token = []
445 + for i in csv.reader([line]):
446 + token.append(i)
447 + token = token[0]
448 + if (len(token) < 4):
449 + print("Skipping invalid line: ", originalLine)
450 + continue
451 + vis = token[0].strip()
452 + field = token[1].strip()
453 + scan = token[2].strip()
454 + intents = token[3].strip()
455 + if (os.path.exists(vis)):
456 + cmd = "au.editIntents('%s','%s','%s','%s')" % (vis,field,scan,intents)
457 + if dryrun:
458 + print("Would call %s\n" % (cmd))
459 + else:
460 + print("Calling %s" % (cmd))
461 + editIntents(vis,field,scan,intents,verbose=verbose)
462 + else:
463 + print("Could not find requested measurement set: ", vis)
464 +
465 +def editIntents(msName='', field='', scan='', newintents='',
466 + append=False, verbose=True):
467 + """
468 + Change the observation intents for a specified field.
469 + Inputs:
470 + * field: required argument (integer or string ID, or name)
471 + * scan: optional, can be a single integer, integer list, or comma-delimited string list
472 + * append: set to True to add the specified intent to the existing intents
473 + * newintents: enter as a python list, or as single comma-delimited string
474 + * valid intents: the first 12 are simply a shorthand way to specify the latter 12
475 + 'AMPLITUDE', 'ATMOSPHERE', 'BANDPASS', 'CHECK_SOURCE', 'WVR',
476 + 'DELAY', 'FLUX', 'FOCUS', 'PHASE', 'POINTING', 'SIDEBAND_RATIO', 'TARGET',
477 + 'CALIBRATE_AMPLI', 'CALIBRATE_ATMOSPHERE', 'CALIBRATE_BANDPASS',
478 + 'CALIBRATE_DELAY', 'CALIBRATE_FLUX', 'CALIBRATE_FOCUS',
479 + 'CALIBRATE_PHASE', 'CALIBRATE_POINTING', 'CALIBRATE_SIDEBAND_RATIO',
480 + 'OBSERVE_TARGET', 'CALIBRATE_WVR', 'OBSERVE_CHECK_SOURCE'
481 + The modifier #ON_SOURCE will be added to all specified intents.
482 + - T. Hunter
483 + """
484 + validIntents = ['AMPLITUDE','ATMOSPHERE','BANDPASS','CHECK_SOURCE',
485 + 'DELAY','FLUX','FOCUS', 'PHASE','POINTING',
486 + 'SIDEBAND_RATIO', 'TARGET','WVR', 'CALIBRATE_AMPLI',
487 + 'CALIBRATE_ATMOSPHERE', 'CALIBRATE_BANDPASS',
488 + 'CALIBRATE_DELAY', 'CALIBRATE_FLUX', 'CALIBRATE_FOCUS',
489 + 'CALIBRATE_PHASE', 'CALIBRATE_POINTING',
490 + 'CALIBRATE_SIDEBAND_RATIO','OBSERVE_TARGET',
491 + 'CALIBRATE_WVR', 'OBSERVE_CHECK_SOURCE'
492 + ]
493 + if (msName == ''):
494 + print("You must specify a measurement set.")
495 + return
496 + if (field == ''):
497 + print("You must specify a field ID or name.")
498 + return
499 + mytb = tbtool()
500 + mytb.open(msName + '/FIELD')
501 + fieldnames = mytb.getcol('NAME')
502 + if verbose:
503 + print("Found fieldnames = ", fieldnames)
504 + mytb.close()
505 +
506 + mytb.open(msName + '/STATE')
507 + intentcol = mytb.getcol('OBS_MODE')
508 + intentcol = intentcol
509 + mytb.close()
510 +
511 + mytb.open(msName, nomodify=False)
512 + naddedrows = 0
513 + if (type(newintents)==list):
514 + desiredintents = ''
515 + for n in newintents:
516 + desiredintents += n
517 + if (n != newintents[-1]):
518 + desiredintents += ','
519 + else:
520 + desiredintents = newintents
521 + desiredintentsList = desiredintents.split(',')
522 +
523 + for intent in desiredintentsList:
524 + if ((intent in validIntents)==False):
525 + print("Invalid intent = %s. Valid intents = %s" % (intent,validIntents))
526 + mytb.close()
527 + return
528 + foundField = False
529 + if (type(scan) != list):
530 + scan = str(scan)
531 + for id,name in enumerate(fieldnames):
532 + if (name == field or str(id) == str(field)):
533 + foundField = True
534 + if verbose:
535 + print('FIELD_ID %s has name %s' % (id, name))
536 + if scan == '':
537 + s = mytb.query('FIELD_ID==%s' % id)
538 + if verbose:
539 + print("Got %d rows in the ms matching field=%s" % (s.nrows(),id))
540 + else:
541 + if (type(scan) == str):
542 + scans = [int(x) for x in scan.split(',')]
543 + elif (type(scan) != list):
544 + scans = [scan]
545 + else:
546 + scans = scan
547 + if verbose:
548 + print("Querying: 'FIELD_ID==%s AND SCAN_NUMBER in %s'" % (id, str(scans)))
549 + s = mytb.query('FIELD_ID==%s AND SCAN_NUMBER in %s' % (id, str(scans)))
550 + if verbose:
551 + print("Got %d rows in the ms matching field=%s and scan in %s" % (s.nrows(),id,str(scans)))
552 + if (s.nrows() == 0):
553 + mytb.close()
554 + print("Found zero rows for this field (and/or scan). Stopping.")
555 + return
556 + state_ids = s.getcol('STATE_ID')
557 + states = []
558 + for state_id in state_ids:
559 + if state_id not in states:
560 + states.append(state_id)
561 + for ij in range(len(states)):
562 + states[ij] = intentcol[states[ij]]
563 +
564 + print('current intents are:')
565 + for state in states:
566 + print(state)
567 +
568 + if append == False: states = []
569 + for desiredintent in desiredintentsList:
570 + if desiredintent.find('TARGET')>=0:
571 + states.append('OBSERVE_TARGET#ON_SOURCE')
572 + elif desiredintent.find('CHECK_SOURCE')>=0:
573 + states.append('OBSERVE_CHECK_SOURCE#ON_SOURCE')
574 + elif desiredintent.find('BANDPASS')>=0:
575 + states.append('CALIBRATE_BANDPASS#ON_SOURCE')
576 + elif desiredintent.find('PHASE')>=0:
577 + states.append('CALIBRATE_PHASE#ON_SOURCE')
578 + elif desiredintent.find('AMPLI')>=0:
579 + states.append('CALIBRATE_AMPLI#ON_SOURCE')
580 + elif desiredintent.find('FLUX')>=0:
581 + states.append('CALIBRATE_FLUX#ON_SOURCE')
582 + elif desiredintent.find('ATMOSPHERE')>=0:
583 + states.append('CALIBRATE_ATMOSPHERE#ON_SOURCE')
584 + elif desiredintent.find('WVR')>=0:
585 + states.append('CALIBRATE_WVR#ON_SOURCE')
586 + elif desiredintent.find('SIDEBAND_RATIO')>=0:
587 + states.append('CALIBRATE_SIDEBAND_RATIO#ON_SOURCE')
588 + elif desiredintent.find('DELAY')>=0:
589 + states.append('CALIBRATE_DELAY#ON_SOURCE')
590 + elif desiredintent.find('FOCUS')>=0:
591 + states.append('CALIBRATE_FOCUS#ON_SOURCE')
592 + elif desiredintent.find('POINTING')>=0:
593 + states.append('CALIBRATE_POINTING#ON_SOURCE')
594 + else:
595 + print("Unrecognized intent = %s" % desiredintent)
596 + continue
597 + print('setting %s' % (states[-1]))
598 +
599 + if states != []:
600 + state = reduce(lambda x,y: '%s,%s' % (x,y), states)
601 + if state not in intentcol:
602 + if verbose:
603 + print('adding intent to state table')
604 + intentcol = list(intentcol)
605 + intentcol.append(state)
606 + intentcol = numpy.array(intentcol)
607 + state_id = len(intentcol) - 1
608 + naddedrows += 1
609 + if verbose:
610 + print('state_id is', state_id)
611 + state_ids[:] = state_id
612 + else:
613 + if verbose:
614 + print('intent already in state table')
615 + state_id = numpy.arange(len(intentcol))[intentcol==state]
616 + if verbose:
617 + print('state_id is', state_id)
618 + if (type(state_id) == list or type(state_id)==numpy.ndarray):
619 + # ms can have identical combinations of INTENT, so just
620 + # pick the row for the first appearance - T. Hunter
621 + state_ids[:] = state_id[0]
622 + else:
623 + state_ids[:] = state_id
624 + s.putcol('STATE_ID', state_ids)
625 + if (foundField == False):
626 + print("Field not found")
627 + return
628 + mytb.close()
629 +
630 + if verbose:
631 + print('writing new STATE table')
632 + mytb.open(msName + '/STATE', nomodify=False)
633 + if naddedrows > 0:
634 + mytb.addrows(naddedrows)
635 + mytb.putcol('OBS_MODE', intentcol)
636 + mytb.close()
637 +
638 +def genImageName(vis='', spw='', field='', imtype='mfs', targettype='sci', stokes='I', mous='', modtext='manual'):
639 + # DPetry, Sept 2017
640 +
641 + """
642 + Generate image name to be used in clean command
643 + following the ALMA archive conventions (see SCIREQ-110, draft recommendations v4.8).
644 + vis = MS to be imaged (to extract object name, SPW frequencies
645 + spw = ID of SPW to be imaged
646 + If several SPWs are imaged at once, enter list of SPWs.
647 + field = ID of field to be imaged (to extract object name).
648 + in case of mosaic, give any ID of field belonging to the mosaic
649 + imtype = image type, 'mfs' (default) - aggregate bandwidth
650 + 'cont' - mfs with lines excluded
651 + 'cube' - spectrally resolved cube
652 + targettype = 'sci' (default) for science target
653 + 'bp' for bandpass calibrator
654 + 'ph' for phase calibrator
655 + 'chk' for checksource
656 + 'amp' for flux calibrator
657 + stokes = 'I' (default) for Stokes I image
658 + any combination of 'I', 'Q', 'U', 'V', 'P', 'A'
659 + mous = '' (default) normally this is filled by the archive.
660 + But if you know the MOUS UID, enter it in the format with "_"
661 + e.g. uid___A001_X888_X66
662 + modtext = 'manual' (default) - this string can be used to add text,
663 + i.e. a modification, to the standard file name.
664 + It is appended at the end of the name.
665 + """
666 +
667 + themous = ''
668 + theobject = ''
669 + thetargettype = ''
670 + targettypes = ['sci', 'bp', 'ph', 'chk', 'amp']
671 + thespwid = ''
672 + theimtype = ''
673 + imtypes = ['mfs', 'cont', 'cube']
674 + thestokes = ''
675 + stokess = ['I', 'Q', 'U', 'V', 'IQU', 'IQUV', 'P', 'A']
676 + themodtext = ''
677 +
678 + rval = ''
679 +
680 + mymsmd = msmdtool()
681 +
682 + # MOUS
683 + if (mous != ''):
684 + themous = mous+'.'
685 +
686 + # object
687 + try:
688 + mymsmd.open(vis)
689 + except:
690 + mymsmd.done()
691 + raise Exception("ERROR: problem opening vis")
692 +
693 + myfieldnames = mymsmd.fieldnames()
694 + if type(field)==str and (field in myfieldnames):
695 + theobject = field
696 + elif field in range(mymsmd.nfields()):
697 + theobject = myfieldnames[field]
698 + else:
699 + print("ERROR: invalid field: "+field)
700 + print("Valid entries are ", myfieldnames)
701 + print(" or ", range(mymsmd.nfields()))
702 + raise Exception("ERROR: invalid field: "+field)
703 + myspws = mymsmd.spwsforfield(theobject)
704 + mymsmd.close()
705 +
706 + # target type
707 + if targettype in targettypes:
708 + thetargettype = targettype
709 + else:
710 + print("ERROR: invalid targettype ", targettype)
711 + print(" Valid entries are ", targettypes)
712 + raise Exception("ERROR: invalid targettype ", targettype)
713 +
714 + # spw
715 + if type(spw) != type([]):
716 + spw = [spw]
717 + for myspw in spw:
718 + if myspw in myspws:
719 + if thespwid=='':
720 + thespwid = str(myspw)
721 + else:
722 + thespwid = thespwid+'_'+str(myspw)
723 + else:
724 + if not type(myspw) == int:
725 + print("ERROR: invalid spw: "+str(myspw)+". Valid entries are ", myspws)
726 + raise Exception("ERROR: invalid spw: "+str(myspw)+' Data type must be int.')
727 + else:
728 + print("ERROR: invalid spw: "+str(myspw)+". Valid entries are ", myspws)
729 + raise Exception("ERROR: invalid spw: "+str(myspw))
730 +
731 + # imtype
732 + if imtype in imtypes:
733 + theimtype = imtype
734 + else:
735 + print("ERROR: invalid imtype ", imtype)
736 + print(" Valid entries are ", imtypes)
737 + raise Exception( "ERROR: invalid imtype "+str(imtype))
738 +
739 + # stokes
740 + if stokes in stokess:
741 + thestokes = stokes
742 + else:
743 + print("ERROR: invalid stokes ", stokes)
744 + print(" Valid entries are ", stokess)
745 + raise Exception("ERROR: invalid stokes "+str(stokes))
746 +
747 + # modifying text
748 +
749 + if modtext != '':
750 + themodtext = '.'+modtext
751 + if ' ' in themodtext:
752 + raise Exception("ERROR: modtext must not contain spaces")
753 +
754 + rval = themous+theobject+'_'+thetargettype+'.spw'+thespwid+'.'+theimtype+'.'+thestokes+themodtext
755 +
756 + if ' ' in rval:
757 + raise Exception("ERROR: generated name contains spaces: \""+rval+"\"")
758 + if '/' in rval:
759 + raise Exception("ERROR: generated name contains slash: \""+rval+"\"")
760 + if '*' in rval:
761 + raise Exception("ERROR: generated name contains star: \""+rval+"\"")
762 + if '?' in rval:
763 + raise Exception("ERROR: generated name contains question mark: \""+rval+"\"")
764 +
765 + return rval
766 +

Everything looks good. We'll let you know here if there's anything you should know about.

Add shortcut