Coverage for /wheeldirectory/casa-6.7.0-12-py3.10.el8/lib/py/lib/python3.10/site-packages/casatasks/private/sdutil.py: 20%

527 statements  

« prev     ^ index     » next       coverage.py v7.6.4, created at 2024-10-31 19:10 +0000

1import abc 

2import contextlib 

3import functools 

4import inspect 

5import os 

6import re 

7import sys 

8import traceback 

9from types import CodeType 

10 

11import numpy 

12 

13from casatasks import casalog 

14from casatools import calibrater, imager, measures 

15from casatools import ms as mstool 

16from casatools import mstransformer, table 

17 

18from . import flaghelper as fh 

19from casatasks.private.mstools import write_history 

20from casatasks.private.parallel.parallel_data_helper import ParallelDataHelper 

21from casatasks.private.update_spw import update_spwchan 

22 

23class Casalog: 

24 """Easily and consistently log CASA messages. 

25 

26 Motivation: 

27 The origin of a message posted directly with casalog.post 

28 must be specified at each casalog.post call. 

29 

30 Usage Example: 

31 def my_funtion(): 

32 logger = Casalog(origin="my_function") 

33 logger.post("Hello 1") # Logs <TaskName>::my_function::<ProcessorName> Hello 1 

34 logger.post("Hello 2") # Logs <TaskName>::my_function::<ProcessorName> Hello 2 

35 

36 """ 

37 def __init__(self, origin: str): 

38 """Construct an object posting messages having the given origin 

39 """ 

40 self.post = functools.partial(casalog.post, origin=origin) 

41 

42 

43@contextlib.contextmanager 

44def tool_manager(vis, ctor, *args, **kwargs): 

45 # this is the only syntax allowed in CASA6, code in CASA6 should be converted to 

46 # call this method with a tool constructor directly 

47 tool = ctor() 

48 if vis and "open" in dir(tool): 

49 tool.open(vis, *args, **kwargs) 

50 try: 

51 yield tool 

52 finally: 

53 if "close" in dir(tool): 

54 tool.close() 

55 elif "done" in dir(tool): 

56 tool.done() 

57 

58 

59def table_manager(vis, *args, **kwargs): 

60 return tool_manager(vis, table, *args, **kwargs) 

61 

62 

63def calibrater_manager(vis, *args, **kwargs): 

64 return tool_manager(vis, calibrater, *args, **kwargs) 

65 

66 

67def measures_manager(*args, **kwargs): 

68 return tool_manager(None, measures, *args, **kwargs) 

69 

70 

71def mstransformer_manager(*args, **kwargs): 

72 return tool_manager(None, mstransformer, *args, **kwargs) 

73 

74 

75def mstool_manager(vis, *args, **kwargs): 

76 return tool_manager(vis, mstool, *args, **kwargs) 

77 

78 

79def is_ms(filename): 

80 if (os.path.isdir(filename) 

81 and os.path.exists(filename + '/table.info') 

82 and os.path.exists(filename + '/table.dat')): 

83 f = open(filename + '/table.info',encoding=sys.getdefaultencoding( )) 

84 lines = f.readline() 

85 f.close() 

86 if (lines.find('Measurement Set') != -1): 

87 return True 

88 else: 

89 return False 

90 else: 

91 return False 

92 

93 

94@contextlib.contextmanager 

95def table_selector(table, taql, *args, **kwargs): 

96 with table_manager(table, *args, **kwargs) as tb: 

97 tsel = tb.query(taql) 

98 try: 

99 yield tsel 

100 finally: 

101 tsel.close() 

102 

103 

104def sdtask_decorator(func): 

105 """This is a decorator function for sd tasks. 

106 

107 Currently the decorator does: 

108 

109 1) set origin to the logger 

110 2) handle exception 

111 

112 So, you don't need to set origin in the task any more. 

113 Also, you don't need to write anything about error 

114 handling in the task. If you have something to do 

115 at the end of the task execution, those should be written 

116 in finally block in the task class. 

117 """ 

118 @functools.wraps(func) 

119 def wrapper(*args, **kwargs): 

120 # set origin 

121 casalog.origin(func.__name__) 

122 

123 retval = None 

124 # Any errors are handled outside the task. 

125 # however, the implementation below is effectively 

126 # equivalent to handling it inside the task. 

127 try: 

128 # execute task 

129 retval = func(*args, **kwargs) 

130 except Exception as e: 

131 traceback_info = __format_trace(traceback.format_exc()) 

132 casalog.post(traceback_info, 'SEVERE') 

133 casalog.post(str(e), 'ERROR') 

134 raise 

135 return retval 

136 return wrapper 

137 

138 

139def callable_sdtask_decorator(func): 

140 """This is a decorator function for sd tasks. 

141 

142 Currently the decorator does: 

143 

144 1) set the origin of messages logged by the "sub" tasks called by the "super" task to "super" 

145 For example: 

146 super_casatask::casa "Msg from super task" 

147 super_casatask::casa "Msg from sub-task1 called by super task" 

148 super_casatask::casa "Msg from sub-task1 called by super task" 

149 2) handle exception 

150 

151 So, you don't need to set origin in the task any more. 

152 Also, you don't need to write anything about error handling 

153 in the task. If you have something to do at the end of 

154 the task execution, those should be written in finally block 

155 in the task class. 

156 

157 Usage: 

158 

159 @callable_sdtask_decorator 

160 def sometask(..) 

161 pass 

162 

163 def othertask(..) 

164 sometask(*args, **kwargs) # logged "othertask::..." 

165 """ 

166 @functools.wraps(func) 

167 def wrapper(*args, **kwargs): 

168 __set_origin(inspect.stack(), casalog.getOrigin(), func.__name__) 

169 

170 retval = None 

171 # Any errors are handled outside the task. 

172 # however, the implementation below is effectively 

173 # equivalent to handling it inside the task. 

174 try: 

175 # execute task 

176 retval = func(*args, **kwargs) 

177 except Exception as e: 

178 traceback_info = __format_trace(traceback.format_exc()) 

179 casalog.post(traceback_info, 'SEVERE') 

180 casalog.post(str(e), 'ERROR') 

181 raise 

182 return retval 

183 return wrapper 

184 

185 

186def __set_origin(callstack, origin, funcname): 

187 for frame_info in callstack: 

188 if frame_info.function == origin: 

189 casalog.origin(origin) 

190 return 

191 casalog.origin(funcname) 

192 

193 

194def __format_trace(s): 

195 wexists = True 

196 regex = r'.*sdutil\.py.*in wrapper.*' 

197 retval = s 

198 while wexists: 

199 ss = retval.split('\n') 

200 wexists = False 

201 for i in range(len(ss)): 

202 if re.match(regex, ss[i]): 

203 ss = ss[:i] + ss[i + 2:] 

204 wexists = True 

205 break 

206 retval = '\n'.join(ss) 

207 return retval 

208 

209 

210class sdtask_manager(object): 

211 def __init__(self, cls, args): 

212 self.cls = cls 

213 self.args = args 

214 

215 def __enter__(self): 

216 self.obj = self.cls(**self.args) 

217 return self.obj 

218 

219 def __exit__(self, exc_type, exc_value, traceback): 

220 # explicitly call destructure to make sure it is called here 

221 self.obj.__del__() 

222 del self.obj 

223 if exc_type: 

224 return False 

225 else: 

226 return True 

227 

228 

229class sdtask_interface(object): 

230 """The sdtask_interface defines a common interface for sdtask_worker class. 

231 

232 All worker classes can be used as follows: 

233 

234 worker = sdtask_worker(**locals()) 

235 worker.initialize() 

236 worker.execute() 

237 worker.finalize() 

238 del worker 

239 

240 Derived classes must implement the above three methods: initialize(), 

241 execute(), and finalize(). 

242 """ 

243 

244 __metaclass__ = abc.ABCMeta 

245 

246 def __init__(self, **kwargs): 

247 for (k, v) in kwargs.items(): 

248 setattr(self, k, v) 

249 # special treatment for selection parameters 

250 select_params = ['scan', 'pol', 'beam'] 

251 for param in select_params: 

252 if hasattr(self, param): 

253 setattr(self, param + 'no', getattr(self, param)) 

254 # casalog.post( 

255 # "renaming self.%s -> self.%sno='%s'" % (param, param, getattr(self, param))) 

256 delattr(self, param) 

257 

258 def __del__(self): 

259 pass 

260 

261 def __enter__(self): 

262 return self 

263 

264 def __exit__(self, exc_type, exc_val, exc_tb): 

265 # explicitly call destructor to make sure it is called here 

266 self.__del__() 

267 if exc_type: 

268 return False 

269 else: 

270 return True 

271 

272 @abc.abstractmethod 

273 def initialize(self): 

274 raise NotImplementedError('initialize is abstract method') 

275 

276 @abc.abstractmethod 

277 def execute(self): 

278 raise NotImplementedError('execute is abstract method') 

279 

280 @abc.abstractmethod 

281 def finalize(self): 

282 raise NotImplementedError('finalize is abstract method') 

283 

284 

285class sdtask_template_imaging(sdtask_interface): 

286 """Template class for imaging tasks. 

287 

288 The sdtask_template_imaging is a template class for worker 

289 class of imaging related sdtasks. It partially implement initialize() 

290 and finalize() using internal methods that must be implemented 

291 in the derived classes. For initialize(), derived classes 

292 must implement compile(), which sets up imaging parameters. 

293 You can implement paramter_check() to do any task specific parameter 

294 check in initialize(). 

295 For finalize(), derived classes can implement cleanup(). 

296 """ 

297 

298 def __init__(self, **kwargs): 

299 super(sdtask_template_imaging, self).__init__(**kwargs) 

300 self.is_table_opened = False 

301 self.is_imager_opened = False 

302 self.table = table() 

303 self.imager = imager() 

304 # workaround for sdtpimaging 

305 if not hasattr(self, 'infiles') and hasattr(self, 'infile'): 

306 self.infiles = [self.infile] 

307 

308 self.__set_infiles() 

309 self.__set_subtable_name() 

310 

311 def __del__(self, base=sdtask_interface): 

312 # table and imager must be closed when the instance 

313 # is deleted 

314 self.close_table() 

315 self.close_imager() 

316 self.cleanup() 

317 super(sdtask_template_imaging, self).__del__() 

318 

319 def open_table(self, name, nomodify=True): 

320 if self.is_table_opened: 

321 casalog.post('Close table before re-open', priority='WARN') 

322 return 

323 self.table.open(name, nomodify=nomodify) 

324 self.is_table_opened = True 

325 

326 def close_table(self): 

327 if self.is_table_opened: 

328 self.table.close() 

329 self.is_table_opened = False 

330 

331 def open_imager(self, name=''): 

332 if self.is_imager_opened: 

333 casalog.post('Close imager before re-open', priority='WARN') 

334 return 

335 self.imager.open(name) 

336 self.is_imager_opened = True 

337 

338 def close_imager(self): 

339 if self.is_imager_opened: 

340 self.imager.close() 

341 self.is_imager_opened = False 

342 

343 def initialize(self): 

344 # infiles must be MS 

345 for idx in range(len(self.infiles)): 

346 if not is_ms(self.infiles[idx]): 

347 msg = 'input data sets must be in MS format' 

348 raise Exception(msg) 

349 

350 self.parameter_check() 

351 self.compile() 

352 

353 def finalize(self): 

354 pass 

355 

356 def parameter_check(self): 

357 pass 

358 

359 def compile(self): 

360 pass 

361 

362 def cleanup(self): 

363 pass 

364 

365 def __set_subtable_name(self): 

366 self.open_table(self.infiles[0]) 

367 keys = self.table.getkeywords() 

368 self.close_table() 

369 self.field_table = get_subtable_name(keys['FIELD']) 

370 self.spw_table = get_subtable_name(keys['SPECTRAL_WINDOW']) 

371 self.source_table = get_subtable_name(keys['SOURCE']) 

372 self.antenna_table = get_subtable_name(keys['ANTENNA']) 

373 self.polarization_table = get_subtable_name(keys['POLARIZATION']) 

374 self.observation_table = get_subtable_name(keys['OBSERVATION']) 

375 self.pointing_table = get_subtable_name(keys['POINTING']) 

376 self.data_desc_table = get_subtable_name(keys['DATA_DESCRIPTION']) 

377 self.pointing_table = get_subtable_name(keys['POINTING']) 

378 

379 def __set_infiles(self): 

380 if type(self.infiles) == str: 

381 self.infiles = [self.infiles] 

382 

383 

384def __get_abspath(filename): 

385 return os.path.abspath(__expand_path(filename)) 

386 

387 

388def __expand_path(filename): 

389 return os.path.expanduser(os.path.expandvars(filename)) 

390 

391 

392def assert_outfile_canoverwrite_or_nonexistent(outfile=None, outform=None, overwrite=None): 

393 if not overwrite and (outform.upper() != "ASCII"): 

394 filename = __get_abspath(outfile) 

395 if os.path.exists(filename): 

396 mesg = "Output file '%s' exists." % (outfile) 

397 raise Exception(mesg) 

398 

399 

400def convert_antenna_spec_autocorr(antenna): 

401 """Convert antenna (baseline) specification(s) to include autocorr data. 

402 

403 Args: 

404 antenna (str): antenna specification 

405 

406 Returns: 

407 str: tweaked antenna specification 

408 """ 

409 if len(antenna) == 0: 

410 return antenna 

411 elif antenna.find(';') >= 0: 

412 # antenna selection is semi-colon separated list of baseline 

413 # specifications: 'SEL1;SEL2...' 

414 return ';'.join(map(convert_antenna_spec_autocorr, antenna.split(';'))) 

415 elif antenna.find('&') < 0: 

416 # no '&' in the selection string 

417 # -> 'ANT&&&' 

418 return antenna + '&&&' 

419 elif antenna.endswith('&&'): 

420 # 'ANT&&' or 'ANT&&&' 

421 # -> as is 

422 return antenna 

423 elif antenna.endswith('&'): 

424 # 'ANT&' 

425 # -> 'ANT&&&' 

426 return antenna.strip('&') + '&&&' 

427 else: 

428 # 'ANT1&ANT2' or 'ANT1&&ANT2' 

429 # -> 'ANT1&&&;ANT2&&&' 

430 specs = [a for a in antenna.split('&') if len(a) > 0] 

431 return ';'.join(map(convert_antenna_spec_autocorr, specs)) 

432 

433 

434def get_antenna_selection_include_autocorr(msname, antenna): 

435 """Get antenna selection string that includes autocorr data. 

436 

437 Args: 

438 msname (str): name of MS 

439 antenna (str): antenna selection string 

440 

441 Raises: 

442 RuntimeError: failed to handle antenna selection string 

443 

444 Returns: 

445 str: antenna selection string including autocorr data 

446 """ 

447 if len(antenna) == 0: 

448 # if no selection is specified, do nothing 

449 return antenna 

450 

451 # test if given antenna selection is valid and if contains any autocorr data 

452 ms = mstool() 

453 sel = ms.msseltoindex(msname, baseline=antenna) 

454 if any([b[0] == b[1] for b in sel['baselines']]): 

455 antenna_autocorr = antenna 

456 else: 

457 antenna_autocorr = convert_antenna_spec_autocorr(antenna) 

458 casalog.post( 

459 'Tweaked antenna selection to include autocorr data: original "{}" tweaked "{}"'.format( 

460 antenna, antenna_autocorr 

461 ) 

462 ) 

463 # test if tweaked selection is valid 

464 sel = ms.msseltoindex(msname, baseline=antenna_autocorr) 

465 if all([b[0] != b[1] for b in sel['baselines']]): 

466 raise RuntimeError('Cannot handle antenna selection properly. Abort.') 

467 return antenna_autocorr 

468 

469 

470def get_nx_ny(n): 

471 nl = to_list(n, int) 

472 if not nl: # check for numpy int types 

473 nl = to_list(n, numpy.integer) 

474 if len(nl) == 1: 

475 nx = ny = nl[0] 

476 else: 

477 nx = nl[0] 

478 ny = nl[1] 

479 return (nx, ny) 

480 

481 

482def get_cellx_celly(c, unit='arcsec'): 

483 if isinstance(c, str): 

484 cellx = celly = c 

485 elif type(c) in (list, tuple, numpy.ndarray): 

486 if len(c) == 1: 

487 cellx = celly = __to_quantity_string(c[0], unit) 

488 elif len(c) > 1: 

489 cellx = __to_quantity_string(c[0], unit) 

490 celly = __to_quantity_string(c[1], unit) 

491 else: 

492 cellx = celly = '' 

493 else: 

494 cellx = celly = __to_quantity_string(c, unit) 

495 return (cellx, celly) 

496 

497 

498def __to_quantity_string(v, unit='arcsec'): 

499 if isinstance(v, str): 

500 return v 

501 else: 

502 return '%s%s' % (v, unit) 

503 

504 

505def get_subtable_name(v): 

506 return v.replace('Table:', '').strip() 

507 

508 

509def get_spwids(selection, infile=None): 

510 # return a comma-separated string of spw IDs. 

511 # selection should be an output of ms.msseltoindex() 

512 

513 spw_list = selection['spw'] 

514 if len(spw_list) == 0: 

515 if infile is None: 

516 raise Exception("infile is needed when selection['spw'] is empty.") 

517 with table_manager(os.path.join(infile, 'DATA_DESCRIPTION')) as tb: 

518 spw_list = tb.getcol('SPECTRAL_WINDOW_ID') 

519 

520 items = [] 

521 for item in spw_list: 

522 items.append(str(item)) 

523 return ','.join(items) 

524 

525 

526def __is_sequence_or_number(param, ptype=int): 

527 """Return true if input is an array type or a number with a give data type. 

528 

529 Arguments 

530 param : an array or number to test 

531 ptype : the data type that param should be. 

532 """ 

533 if hasattr(param, '__iter__'): 

534 out = True 

535 for p in param: 

536 out &= isinstance(p, ptype) 

537 return out 

538 else: 

539 return isinstance(param, ptype) 

540 

541 

542def to_list(param, ptype=int, convert=False): 

543 """Convert a number, an array type or a string to a list. 

544 

545 The function returns None if input values are not ptype and convert=False. 

546 When convert is True, force converting input values to a list of ptype. 

547 """ 

548 if isinstance(param, ptype): # a string or a number 

549 if ptype is str: 

550 return param.split() 

551 elif convert: 

552 return [ptype(param)] 

553 else: 

554 return [param] 

555 if __is_sequence_or_number(param, ptype): 

556 return list(param) 

557 elif convert: 

558 return [ptype(p) for p in param] 

559 return None 

560 

561 

562def do_mst( 

563 infile, 

564 datacolumn, 

565 field, 

566 spw, 

567 timerange, 

568 scan, 

569 antenna, 

570 timebin, 

571 timespan, 

572 outfile, 

573 intent, 

574 caller: CodeType, 

575 ext_config): 

576 """Call mstransform by the provided procedure. 

577 

578 Followings are parameters of mstransform, but not used by sdtimeaverage, 

579 just only putting default values. 

580 """ 

581 vis = infile # needed for ParallelDataHelper 

582 outputvis = outfile # needed for ParallelDataHelper 

583 separationaxis = "auto" 

584 tileshape = [0] 

585 

586# intent = '' 

587 correlation = '' 

588 array = '' 

589 uvrange = '' 

590 observation = '' 

591 feed = '' 

592 

593 realmodelcol = False 

594 usewtspectrum = False 

595 chanbin = 1 

596 mode = 'channel' 

597 start = 0 

598 width = 1 

599 

600 maxuvwdistance = 0.0 

601 

602 ddistart = -1 

603 reindex = True 

604 _disableparallel = False 

605 _monolithic_processing = False 

606 

607 taqlstr = '' 

608 if ext_config.get('keepflags'): 

609 taqlstr = "NOT (FLAG_ROW OR ALL(FLAG))" 

610 

611 # Initialize the helper class 

612 pdh = ParallelDataHelper(caller.co_name, locals()) 

613 

614 # When dealing with MMS, process in parallel or sequential 

615 # _disableparallel is a hidden parameter. Only for debugging purposes! 

616 if _disableparallel: 

617 pdh.bypassParallelProcessing(1) 

618 else: 

619 pdh.bypassParallelProcessing(0) 

620 

621 # Validate input and output parameters 

622 pdh.setupIO() 

623 

624 # Process the input Multi-MS 

625 if ParallelDataHelper.isMMSAndNotServer(infile) and not _monolithic_processing: 

626 do_createmms, separationaxis, do_return = __process_input_multi_ms(pdh, separationaxis) 

627 if do_return: 

628 return 

629 # Create an output Multi-MS 

630 if do_createmms: 

631 __create_output_multi_ms(pdh, separationaxis) 

632 return 

633 

634 # Create a local copy of the MSTransform tool 

635 with mstransformer_manager() as mtlocal: 

636 # Gather all the parameters in a dictionary. 

637 config = {} 

638 

639 # set config param. 

640 config = pdh.setupParameters( 

641 inputms=infile, 

642 outputms=outfile, 

643 field=field, 

644 spw=spw, 

645 array=array, 

646 scan=scan, 

647 antenna=antenna, 

648 correlation=correlation, 

649 uvrange=uvrange, 

650 timerange=timerange, 

651 intent=intent, 

652 observation=str(observation), 

653 feed=feed, 

654 taql=taqlstr) 

655 

656 # ddistart will be used in the tool when re-indexing the spw table 

657 config['ddistart'] = ddistart 

658 

659 # re-index parameter is used by the pipeline to not re-index any 

660 # sub-table and the associated IDs 

661 config['reindex'] = reindex 

662 

663 config['datacolumn'] = datacolumn 

664 dc = datacolumn.upper() 

665 # Make real a virtual MODEL column in the output MS 

666 if 'MODEL' in dc or dc == 'ALL': 

667 config['realmodelcol'] = realmodelcol 

668 

669 config['usewtspectrum'] = usewtspectrum 

670 

671 if ext_config.get('do_check_tileshape'): 

672 __check_tileshape(tileshape) 

673 

674 config['tileshape'] = tileshape 

675 

676 # set config for Averaging 

677 if ext_config.get('do_timeaverage'): 

678 casalog.post('Parse time averaging parameters') 

679 config['timeaverage'] = True 

680 config['timebin'] = timebin 

681 config['timespan'] = timespan 

682 config['maxuvwdistance'] = maxuvwdistance 

683 

684 # porting from sdpolaverage 

685 __if_do_polaverage(config, ext_config) 

686 

687 # porting from sdpolaverage, but not used 

688 if ext_config.get('parse_chanaverage'): 

689 chanbin = __if_parse_chanaverage(chanbin, config, pdh) 

690 

691 # porting from sdpolaverage, but not used 

692 __if_do_hanning(config, ext_config) 

693 

694 # porting from sdpolaverage, but not used 

695 __if_parse_regridding_parameters(config, ext_config, mode, pdh) 

696 

697 # Configure the tool and all the parameters 

698 casalog.post('%s' % config, 'DEBUG') 

699 mtlocal.config(config) 

700 

701 # Open the MS, select the data and configure the output 

702 mtlocal.open() 

703 

704 # Run the tool 

705 casalog.post('Apply the transformations') 

706 mtlocal.run() 

707 

708 """ 

709 CAS-12721: 

710 Note: Following section were written concerning with CAS-7751 or others. 

711 Program logic is copied and used without change. 

712 """ 

713 # Update the FLAG_CMD sub-table to reflect any spw/channels selection 

714 # If the spw selection is by name or FLAG_CMD contains spw with names, 

715 # skip the updating 

716 

717 if (spw != '' and spw != '*') or ext_config.get('parse_chanaverage'): 

718 __update_flag_cmd(infile, outfile, chanbin, spw) 

719 

720 # END 

721 

722 

723def add_history( 

724 caller, 

725 casalog, 

726 outfile): 

727 """Write history to output MS, not the input ms.""" 

728 mslocal = mstool() 

729 try: 

730 param_names = caller.co_varnames[:caller.co_argcount] 

731 local_vals = locals() 

732 param_vals = [local_vals.get(p, None) for p in param_names] 

733 write_history(mslocal, outfile, 'sdtimeaverage', param_names, 

734 param_vals, casalog) 

735 except Exception as instance: 

736 casalog.post("*** Error \'%s\' updating HISTORY" % (instance), 

737 'WARN') 

738 return False 

739 

740 mslocal = None 

741 

742 return True 

743 

744 

745def __if_parse_regridding_parameters(config, ext_config, mode, pdh): 

746 if ext_config.get('regridms'): 

747 nchan = -1 

748 nspw = 1 

749 interpolation = "linear" 

750 restfreq = "" 

751 outframe = "" 

752 phasecenter = "" 

753 veltype = "radio" 

754 preaverage = False 

755 casalog.post('Parse regridding parameters') 

756 config['regridms'] = True 

757 # Reset the defaults depending on the mode 

758 # Only add non-empty string parameters to config dictionary 

759 start, width = pdh.defaultRegridParams() 

760 config['mode'] = mode 

761 config['nchan'] = nchan 

762 if start != '': 

763 config['start'] = start 

764 if width != '': 

765 config['width'] = width 

766 if nspw > 1: 

767 casalog.post('Separate MS into %s spws' % nspw) 

768 config['nspw'] = nspw 

769 config['interpolation'] = interpolation 

770 if restfreq != '': 

771 config['restfreq'] = restfreq 

772 if outframe != '': 

773 config['outframe'] = outframe 

774 if phasecenter != '': 

775 config['phasecenter'] = phasecenter 

776 config['veltype'] = veltype 

777 config['preaverage'] = preaverage 

778 

779 

780def __if_do_hanning(config, ext_config): 

781 if ext_config.get('hanning'): 

782 casalog.post('Apply Hanning smoothing') 

783 config['hanning'] = True 

784 

785 

786def __if_do_polaverage(config, ext_config): 

787 if ext_config.get('polaverage'): 

788 polaverage_ = ext_config.get('polaverage').strip() 

789 if polaverage_ != '': 

790 config['polaverage'] = True 

791 config['polaveragemode'] = polaverage_ 

792 

793 

794def __if_parse_chanaverage(chanbin, config, pdh): 

795 # Only parse chanaverage if chanbin is valid 

796 if isinstance(chanbin, int) and chanbin <= 1: 

797 raise ValueError('Parameter chanbin must be > 1 to do channel averaging') 

798 

799 # Validate the case of int or list chanbin 

800 if pdh.validateChanBin(): 

801 casalog.post('Parse channel averaging parameters') 

802 config['chanaverage'] = True 

803 

804 # convert numpy types, until CAS-6493 is not fixed 

805 chanbin = fh.evaluateNumpyType(chanbin) 

806 config['chanbin'] = chanbin 

807 return chanbin 

808 

809 

810def __update_flag_cmd(infile, outfile, chanbin, spw): 

811 with table_manager(outfile + '/FLAG_CMD', nomodify=False) as mytb: 

812 mslocal = mstool() 

813 nflgcmds = mytb.nrows() 

814 

815 if nflgcmds > 0: 

816 update_flag_cmd = False 

817 

818 # If spw selection is by name in FLAG_CMD, do not update, CAS-7751 

819 mycmd = mytb.getcell('COMMAND', 0) 

820 cmdlist = mycmd.split() 

821 for cmd in cmdlist: 

822 # Match only spw indices, not names 

823 if cmd.__contains__('spw'): 

824 cmd = cmd.strip('spw=') 

825 spwstr = re.search('^[^a-zA-Z]+$', cmd) 

826 if spwstr is not None and spwstr.string.__len__() > 0: 

827 update_flag_cmd = True 

828 break 

829 

830 if update_flag_cmd: 

831 mademod = False 

832 cmds = mytb.getcol('COMMAND') 

833 widths = {} 

834 if hasattr(chanbin, 'has_key'): 

835 widths = chanbin 

836 else: 

837 if hasattr(chanbin, '__iter__') and len(chanbin) > 1: 

838 for i in range(len(chanbin)): 

839 widths[i] = chanbin[i] 

840 elif chanbin != 1: 

841 numspw = len(mslocal.msseltoindex(vis=infile, 

842 spw='*')['spw']) 

843 if hasattr(chanbin, '__iter__'): 

844 w = chanbin[0] 

845 else: 

846 w = chanbin 

847 for i in range(numspw): 

848 widths[i] = w 

849 for rownum in range(nflgcmds): 

850 # Matches a bare number or a string quoted any way. 

851 spwmatch = re.search(r'spw\s*=\s*(\S+)', cmds[rownum]) 

852 if spwmatch: 

853 sch1 = spwmatch.groups()[0] 

854 sch1 = re.sub(r"[\'\"]", '', sch1) # Dequote 

855 # Provide a default in case the split selection excludes 

856 # cmds[rownum]. update_spwchan() will throw an exception 

857 # in that case. 

858 cmd = '' 

859 try: 

860 sch2 = update_spwchan( 

861 infile, spw, sch1, truncate=True, widths=widths) 

862 if sch2: 

863 repl = '' 

864 if sch2 != '*': 

865 repl = "spw='" + sch2 + "'" 

866 cmd = cmds[rownum].replace( 

867 spwmatch.group(), repl) 

868 # except: # cmd[rownum] no longer applies. 

869 except Exception as e: 

870 casalog.post( 

871 'Error %s updating row %d of FLAG_CMD' % 

872 (e, rownum), 'WARN') 

873 casalog.post('sch1 = ' + sch1, 'DEBUG1') 

874 casalog.post('cmd = ' + cmd, 'DEBUG1') 

875 if cmd != cmds[rownum]: 

876 mademod = True 

877 cmds[rownum] = cmd 

878 if mademod: 

879 casalog.post('Updating FLAG_CMD', 'INFO') 

880 mytb.putcol('COMMAND', cmds) 

881 

882 else: 

883 casalog.post( 

884 'FLAG_CMD table contains spw selection by name. Will not update it!', 'DEBUG') 

885 

886 

887def __check_tileshape(tileshape): 

888 # Add the tile shape parameter 

889 if tileshape.__len__() == 1: 

890 # The only allowed values are 0 or 1 

891 if tileshape[0] != 0 and tileshape[0] != 1: 

892 raise ValueError('When tileshape has one element, it should be either 0 or 1.') 

893 

894 elif tileshape.__len__() != 3: 

895 # The 3 elements are: correlations, channels, rows 

896 raise ValueError('Parameter tileshape must have 1 or 3 elements.') 

897 

898 

899def __process_input_multi_ms(pdh, separationaxis): 

900 """Process MS. 

901 

902 retval{'status': True, 'axis':''} --> can run in parallel 

903 retval{'status': False, 'axis':'value'} --> treat MMS as monolithic MS, 

904 set new axis for output MMS 

905 retval{'status': False, 'axis':''} --> treat MMS as monolithic MS, 

906 create an output MS 

907 """ 

908 retval = pdh.validateInputParams() 

909 

910 # Cannot create an output MMS. 

911 if not retval['status'] and retval['axis'] == '': 

912 casalog.post('Cannot process MMS with the requested transformations', 'WARN') 

913 casalog.post('Use task listpartition to see the contents of the MMS') 

914 casalog.post('Will create an output MS', 'WARN') 

915 createmms = False 

916 return createmms, separationaxis, False 

917 

918 # MMS is processed as monolithic MS. 

919 elif not retval['status'] and retval['axis'] != '': 

920 createmms = True 

921 pdh.override__args('createmms', True) 

922 pdh.override__args('monolithic_processing', True) 

923 separationaxis = retval['axis'] 

924 pdh.override__args('separationaxis', retval['axis']) 

925 casalog.post("Will process the input MMS as a monolithic MS", 'WARN') 

926 casalog.post( 

927 f"Will create an output MMS with separation axis \'{retval['axis']}\'", 

928 priority='WARN') 

929 return createmms, separationaxis, False 

930 

931 # MMS is processed in parallel 

932 else: 

933 createmms = False 

934 pdh.override__args('createmms', False) 

935 pdh.setupCluster('sdpolaverage') 

936 pdh.go() 

937 return createmms, separationaxis, True 

938 

939 

940def __create_output_multi_ms(pdh, separationaxis): 

941 # Check the heuristics of separationaxis and the requested transformations 

942 pval = pdh.validateOutputParams() 

943 if pval == 0: 

944 raise RuntimeError( 

945 'Cannot create MMS using separationaxis=%s with some of the requested transformations.' 

946 % separationaxis 

947 ) 

948 pdh.setupCluster('sdpolaverage') 

949 pdh.go() 

950 _monolithic_processing = False