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

169 statements  

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

1import platform 

2import time 

3import numpy 

4import os 

5import shutil 

6import re 

7 

8from casatasks import casalog 

9 

10from casatools import image 

11from casatasks.private.imagerhelpers.imager_deconvolver import PyDeconvolver 

12from casatasks.private.imagerhelpers.input_parameters import ImagerParameters 

13from casatasks.private.imagerhelpers.imager_return_dict import ImagingDict 

14from casatasks.private.parallel.parallel_task_helper import ParallelTaskHelper 

15from .cleanhelper import write_tclean_history, get_func_params 

16from casatools import synthesisimager 

17ia = image( ) 

18 

19try: 

20 from casampi.MPIEnvironment import MPIEnvironment 

21 from casampi import MPIInterface 

22 mpi_available = True 

23except ImportError: 

24 mpi_available = False 

25 

26def check_requiredmask_exists(usemask, mask): 

27 if usemask != "user": # don't use the mask parameter 

28 return 

29 if type(mask) is type([]): # mask is an array of values 

30 return 

31 if mask == "": # mask is an empty string -> no file specified 

32 return 

33 if "[" in mask: # mask is a region string 

34 return 

35 if not os.path.exists(mask): # mask is a filename string <- only option left 

36 raise RuntimeError("Internal Error: 'mask' parameter specified as a filename '"+mask+"', but no such file exists") 

37 

38def check_requiredimgs_exist(imagename, inp): 

39 # get the list of images to check for 

40 reqims = [] 

41 if inp['deconvolver'] == 'mtmfs': 

42 nterms = inp['nterms'] 

43 end = nterms*2-1 

44 for ttn in range(0, nterms*2-1): 

45 reqims.append(imagename + ".psf" + ".tt" + str(ttn)) 

46 for ttn in range(0, nterms): 

47 reqims.append(imagename + ".residual" + ".tt" + str(ttn)) 

48 else: 

49 reqims.append(imagename + ".residual") 

50 reqims.append(imagename + ".psf") 

51 

52 # find images that exist on disk 

53 allfiles = os.listdir(os.path.dirname(os.path.abspath(imagename))) 

54 extims = list(filter(lambda im: os.path.exists(im), reqims)) # TODO replace with allfiles 

55 

56 # verify required images are available 

57 if len(extims) != len(reqims): 

58 diffims = list(filter(lambda im: im not in extims, reqims)) 

59 raise RuntimeError("Internal Error: missing one or more of the required images: " + str(diffims)) 

60 

61 # check for .pb image in the case that nsigma > 0 

62 # see comments on CAS-13144 about casa crashing as to why this check is here 

63 if (inp['nsigma'] > 0): 

64 reqpb = ".pb" if (inp['deconvolver'] != 'mtmfs') else ".pb.tt0" 

65 if (imagename+reqpb not in allfiles): 

66 raise RuntimeError("The parameter nsigma>0 ("+str(inp['nsigma'])+") requires a "+reqpb+" image to be available.") 

67 

68def check_starmodel_model_collisions(startmodel, imagename, deconvolver): 

69 # check for startmodel(s) 

70 startmodels = [] 

71 if type(startmodel) is str: 

72 if len(startmodel) > 0: 

73 startmodels = [startmodel] 

74 else: # if type(startmodel) is list 

75 startmodels = startmodel 

76 

77 # verify the existance of startmodel(s), and map to imagename.model 

78 ttn = 0 

79 sm_modim_map = [] 

80 for sm in startmodels: 

81 sm = os.path.normpath(sm) 

82 smdir = os.path.dirname(sm) 

83 smbase = os.path.basename(sm) 

84 

85 # verify startmodel exists 

86 # Note: this check should be unneccessary (should be done in cpp), but 

87 # current tests indicate that cpp code does not catch this case. 

88 if not os.path.exists(sm): 

89 raise RuntimeError("Internal Error: parameter startmodel set to \"{0}\" but that file does not exist".format(sm)) 

90 

91 # get the path to the destination model 

92 if ".model" in smbase: 

93 ext = re.search(r'(\.model.*)', smbase).group(0) 

94 elif deconvolver == 'mtmfs': 

95 ext = ".model.tt{0}".format(ttn) 

96 ttn += 1 

97 else: 

98 ext = ".model" 

99 modim = os.path.join(smdir, imagename+ext) 

100 sm_modim_map.append([sm, modim]) 

101 

102 # check if both startmodel is set and imagename.model exists 

103 # Note: this check should be unneccessary (should be done in cpp), but 

104 # current tests indicate that cpp code does not catch this case. 

105 if os.path.exists(modim): 

106 raise RuntimeError("Internal Error: imagename.model already exists! Either parameter startmodel must not be set ('') or imagename.model ({0}) must not exist.".format(modim) + 

107 os.linesep+"\tEither unset startmodel or remove {0} to continue".format(modim)) 

108 

109 return sm_modim_map 

110 

111def deconvolve( 

112 ####### Data Selection 

113 imagename,#='', 

114 startmodel,#='', 

115 

116 ####### Deconvolution parameters 

117 deconvolver,#='hogbom', 

118 scales,#=[], 

119 nterms,#=1, 

120 smallscalebias,#=0.0 

121 # TODO in CAS-13570: uncomment once asp is working 

122 # fusedthreshold,#=0.0 

123 # largestscale,#=-1 

124 

125 ### restoration options 

126 restoration,#=True, 

127 restoringbeam,#=[], 

128 

129 ##### Iteration control 

130 niter,#=0, 

131 gain,#=0.1, 

132 threshold,#=0.0,  

133 nsigma,#=0.0 

134 interactive,#=False, 

135 fullsummary,#=False 

136 fastnoise,#=True, 

137 

138 ##### (new) Mask parameters 

139 usemask,#='user', 

140 mask,#='', 

141 pbmask,#='', 

142 

143 ##### automask by multithresh 

144 sidelobethreshold,#=5.0, 

145 noisethreshold,#=3.0, 

146 lownoisethreshold,#=3.0, 

147 negativethreshold,#=0.0, 

148 smoothfactor,#=1.0, 

149 minbeamfrac,#=0.3,  

150 cutthreshold,#=0.01, 

151 growiterations,#=100 

152 dogrowprune,#=True 

153 verbose): #=False 

154 """ 

155 Runs the minor cycle only of tclean. 

156 Most of this code is copied directly from tclean. 

157 """ 

158 

159 cppparallel=False 

160 decon=None 

161 

162 if interactive: 

163 # Check for casaviewer, if it does not exist flag it up front for macOS 

164 # since casaviewer is no longer provided by default with macOS. Returning 

165 # False instead of throwing an exception results in: 

166 # 

167 # RuntimeError: No active exception to reraise 

168 # 

169 # from deconvolve run from casashell. 

170 try: 

171 import casaviewer as __test_casaviewer 

172 except: 

173 if platform.system( ) == "Darwin": 

174 casalog.post( 

175 "casaviewer is no longer available for macOS, for more information see: http://go.nrao.edu/casa-viewer-eol Please restart by setting interactive=F", 

176 "WARN", 

177 "task_deconvolve", 

178 ) 

179 raise RuntimeError( "casaviewer is no longer available for macOS, for more information see: http://go.nrao.edu/casa-viewer-eol" ) 

180 

181 try: 

182 

183 # discard empty start model strings 

184 if type(startmodel) is list: 

185 startmodel = list(filter(lambda v: len(v) > 0, startmodel)) 

186 

187 # clean input 

188 inp=locals().copy() 

189 inp['msname'] = '' # -> no 'vis' parameter for minor cycle only 

190 inp['cycleniter'] = inp['niter'] 

191 inp['loopgain'] = inp.pop('gain') 

192 inp['scalebias'] = inp.pop('smallscalebias') 

193 

194 # TODO in CAS-13570: fix asp description and allow asp value once asp is working 

195 if deconvolver.lower() == "asp": 

196 raise RuntimeError("The "+deconvolver+" deconvolver currently has incorrect end-of-minor-cycle residual calculations and is therefore disabled. Please choose a different deconvolver.") 

197 

198 ##################################################### 

199 #### Construct ImagerParameters 

200 ##################################################### 

201 

202 # Make sure that we have all the necessary images and that the startmodel is valid. 

203 # Note: cpp code should check that .residual and .psf exist, but current tests indicate that it doesn't do that. 

204 check_requiredmask_exists(usemask, mask) 

205 check_requiredimgs_exist(imagename, inp) 

206 check_starmodel_model_collisions(startmodel, imagename, deconvolver) 

207 

208 # make a list of parameters with defaults from tclean 

209 defparm=dict(list(zip(ImagerParameters.__init__.__code__.co_varnames[1:], ImagerParameters.__init__.__defaults__))) 

210 

211 ## assign values to the ones passed to deconvolve and if not defined yet in deconvolve... 

212 ## assign them the default value of the constructor 

213 bparm={k: inp[k] if k in inp else defparm[k] for k in defparm.keys()} 

214 

215 #========================================================= 

216 ####set the children to load c++ libraries and applicator 

217 ### make workers ready for c++ based mpicommands 

218 if deconvolver != 'mtmfs': # mtmfs isn't currently parallelized 

219 ia.open(imagename+'.psf') 

220 isCube=ia.shape()[3] >1 # SynthesisDeconvolver::executeMinorCycle doesn't start mpi without more than one channel 

221 ia.done() 

222 if mpi_available and MPIEnvironment.is_mpi_enabled and isCube: 

223 mint=MPIInterface.MPIInterface() 

224 cl=mint.getCluster() 

225 cl._cluster.pgc("from casatools import synthesisimager", False) 

226 cl._cluster.pgc("si=synthesisimager()", False) 

227 cl._cluster.pgc("si.initmpi()", False) 

228 cppparallel=True 

229 ###ignore chanchunk 

230 bparm['chanchunks']=1 

231 

232 ## create the parameters list help object 

233 paramList=ImagerParameters(**bparm) 

234 

235 # Assign cyclethreshold explicitly to threshold 

236 threshold = threshold if (type(threshold) == str) else (str(threshold*1000)+'mJy') 

237 paramList.setIterPars({'cyclethreshold': threshold, 'cyclethresholdismutable': False}) 

238 

239 ##################################################### 

240 #### Run the minor cycle 

241 ##################################################### 

242 

243 iterrec = False 

244 isit = 0 

245 retrec = {} 

246 

247 ## Setup Imager object 

248 decon = PyDeconvolver(params=paramList) 

249 

250 ################################################# 

251 #### Setup 

252 ################################################# 

253 

254 ## Init minor cycle elements 

255 # print("initializing deconvolver") 

256 t0=time.time(); 

257 decon.initializeDeconvolvers() 

258 ####now is the time to check estimated memory 

259 decon.estimatememory() 

260 ## setup iteration controller 

261 decon.initializeIterationControl() 

262 t1=time.time(); 

263 casalog.post("***Time for initializing deconvolver(s): "+"%.2f"%(t1-t0)+" sec", "INFO3", "task_deconvolve"); 

264 

265 ################################################# 

266 #### Exec 

267 ################################################# 

268 

269 ## Set up the internal state of the iterater and automask 

270 # is this necessary? -> I think so ~bgb200731 

271 isit = decon.hasConverged() 

272 decon.updateMask() 

273 

274 isit = decon.hasConverged() # here in case updateMaskMinor() produces an all-false mask 

275 runmin = not isit ## Are minor cycles going to be run or not ? Will the return dictionary have summaryminor or not ? 

276 ##print ("Runmin? " , runmin) 

277 if not isit: 

278 # print("running minor cycle"); 

279 t0=time.time(); 

280 decon.runMinorCycle() 

281 t1=time.time(); 

282 casalog.post("***Time for minor cycle: "+"%.2f"%(t1-t0)+" sec", "INFO3", "task_deconvolve"); 

283 isit = decon.hasConverged() # get the convergence state, to report back to the calling code 

284 

285 

286 # Residual image needs to be computed for this to work 

287 if niter==0 or runmin==False: 

288 id = ImagingDict() 

289 retrec1 = id.construct_residual_dict(paramList) 

290 

291 ## Get summary from iterbot 

292 #if type(interactive) != bool and niter>0: 

293 # this requrirment should go... 

294 #if niter>0: 

295 retrec=decon.getSummary(fullsummary); 

296 

297 if niter==0 or runmin==False: 

298 retrec['summaryminor'] = retrec1['summaryminor'] #CAS-14184 

299 retrec['stopcode'] = retrec1['stopcode'] 

300 retrec['stopDescription'] = retrec1['stopDescription'] 

301 

302 

303 ################################################# 

304 #### Teardown 

305 ################################################# 

306 

307 ## Get records from iterbot, to be used in the next call to deconvolve 

308 iterrec = decon.getIterRecords() 

309 

310 ## Restore images. 

311 if restoration==True: 

312 t0=time.time(); 

313 decon.restoreImages() 

314 t1=time.time(); 

315 casalog.post("***Time for restoring images: "+"%.2f"%(t1-t0)+" sec", "INFO3", "task_deconvolve"); 

316 

317 ##close tools 

318 decon.deleteTools() 

319 

320 except Exception as e: 

321 casalog.post('Exception from deconvolve : ' + str(e), "SEVERE", "deconvolve") 

322 larg = list(e.args) 

323 larg[0] = 'Exception from deconvolve : ' + str(larg[0]) 

324 e.args = tuple(larg) 

325 raise 

326 

327 finally: 

328 if decon != None: 

329 decon.deleteTools() 

330 if(cppparallel): 

331 ###release workers back to python mpi control 

332 si=synthesisimager() 

333 si.releasempi() 

334 

335 # Write history at the end, when hopefully all temp files are gone from disk, 

336 # so they won't be picked up. They need time to disappear on NFS or slow hw. 

337 # Copied from tclean. 

338 try: 

339 params = get_func_params(deconvolve, locals()) 

340 write_tclean_history(imagename, 'deconvolve', params, casalog) 

341 except Exception as exc: 

342 casalog.post("Error updating history (logtable): {} ".format(exc),'WARN') 

343 

344 return retrec