cubevis 0.5.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cubevis/LICENSE.rst +500 -0
- cubevis/__icons__/20px/fast-backward.svg +13 -0
- cubevis/__icons__/20px/fast-forward.svg +13 -0
- cubevis/__icons__/20px/step-backward.svg +12 -0
- cubevis/__icons__/20px/step-forward.svg +12 -0
- cubevis/__icons__/add-chan.png +0 -0
- cubevis/__icons__/add-chan.svg +84 -0
- cubevis/__icons__/add-cube.png +0 -0
- cubevis/__icons__/add-cube.svg +186 -0
- cubevis/__icons__/drag.png +0 -0
- cubevis/__icons__/drag.svg +109 -0
- cubevis/__icons__/mask-selected.png +0 -0
- cubevis/__icons__/mask.png +0 -0
- cubevis/__icons__/mask.svg +1 -0
- cubevis/__icons__/new-layer-sm-selected.png +0 -0
- cubevis/__icons__/new-layer-sm-selected.svg +88 -0
- cubevis/__icons__/new-layer-sm.png +0 -0
- cubevis/__icons__/new-layer-sm.svg +15 -0
- cubevis/__icons__/reset.png +0 -0
- cubevis/__icons__/reset.svg +11 -0
- cubevis/__icons__/sub-chan.png +0 -0
- cubevis/__icons__/sub-chan.svg +71 -0
- cubevis/__icons__/sub-cube.png +0 -0
- cubevis/__icons__/sub-cube.svg +95 -0
- cubevis/__icons__/zoom-to-fit.png +0 -0
- cubevis/__icons__/zoom-to-fit.svg +21 -0
- cubevis/__init__.py +58 -0
- cubevis/__js__/bokeh-3.6.1.min.js +728 -0
- cubevis/__js__/bokeh-tables-3.6.1.min.js +119 -0
- cubevis/__js__/bokeh-widgets-3.6.1.min.js +141 -0
- cubevis/__js__/casalib.min.js +1 -0
- cubevis/__js__/cubevisjs.min.js +62 -0
- cubevis/__version__.py +1 -0
- cubevis/apps/__init__.py +44 -0
- cubevis/apps/_createmask.py +461 -0
- cubevis/apps/_createregion.py +513 -0
- cubevis/apps/_interactiveclean.py +3260 -0
- cubevis/apps/_interactiveclean_wrappers.py +130 -0
- cubevis/apps/_ms_raster.py +815 -0
- cubevis/apps/_plotants.py +286 -0
- cubevis/apps/_plotbandpass.py +7 -0
- cubevis/bokeh/__init__.py +29 -0
- cubevis/bokeh/annotations/__init__.py +1 -0
- cubevis/bokeh/annotations/_ev_poly_annotation.py +6 -0
- cubevis/bokeh/components/__init__.py +28 -0
- cubevis/bokeh/format/__init__.py +31 -0
- cubevis/bokeh/format/_time_ticks.py +44 -0
- cubevis/bokeh/format/_wcs_ticks.py +45 -0
- cubevis/bokeh/models/__init__.py +4 -0
- cubevis/bokeh/models/_edit_span.py +7 -0
- cubevis/bokeh/models/_ev_text_input.py +6 -0
- cubevis/bokeh/models/_tip.py +37 -0
- cubevis/bokeh/models/_tip_button.py +50 -0
- cubevis/bokeh/sources/__init__.py +35 -0
- cubevis/bokeh/sources/_data_pipe.py +258 -0
- cubevis/bokeh/sources/_image_data_source.py +83 -0
- cubevis/bokeh/sources/_image_pipe.py +581 -0
- cubevis/bokeh/sources/_spectra_data_source.py +55 -0
- cubevis/bokeh/sources/_updatable_data_source.py +189 -0
- cubevis/bokeh/state/__init__.py +34 -0
- cubevis/bokeh/state/_initialize.py +164 -0
- cubevis/bokeh/state/_javascript.py +53 -0
- cubevis/bokeh/state/_palette.py +58 -0
- cubevis/bokeh/state/_session.py +44 -0
- cubevis/bokeh/state/js/bokeh-2.4.1.min.js +596 -0
- cubevis/bokeh/state/js/bokeh-gl-2.4.1.min.js +74 -0
- cubevis/bokeh/state/js/bokeh-tables-2.4.1.min.js +132 -0
- cubevis/bokeh/state/js/bokeh-widgets-2.4.1.min.js +118 -0
- cubevis/bokeh/state/js/casaguijs-v0.0.4.0-b2.4.min.js +49 -0
- cubevis/bokeh/state/js/casaguijs-v0.0.5.0-b2.4.min.js +49 -0
- cubevis/bokeh/state/js/casaguijs-v0.0.6.0-b2.4.min.js +49 -0
- cubevis/bokeh/state/js/casalib-v0.0.1.min.js +1 -0
- cubevis/bokeh/tools/__init__.py +31 -0
- cubevis/bokeh/tools/_cbreset_tool.py +52 -0
- cubevis/bokeh/tools/_drag_tool.py +61 -0
- cubevis/bokeh/utils/__init__.py +35 -0
- cubevis/bokeh/utils/_axes_labels.py +94 -0
- cubevis/bokeh/utils/_svg_icon.py +136 -0
- cubevis/data/__init__.py +1 -0
- cubevis/data/casaimage/__init__.py +114 -0
- cubevis/data/measurement_set/__init__.py +7 -0
- cubevis/data/measurement_set/_ms_data.py +178 -0
- cubevis/data/measurement_set/processing_set/__init__.py +30 -0
- cubevis/data/measurement_set/processing_set/_ps_concat.py +98 -0
- cubevis/data/measurement_set/processing_set/_ps_coords.py +78 -0
- cubevis/data/measurement_set/processing_set/_ps_data.py +213 -0
- cubevis/data/measurement_set/processing_set/_ps_io.py +55 -0
- cubevis/data/measurement_set/processing_set/_ps_raster_data.py +154 -0
- cubevis/data/measurement_set/processing_set/_ps_select.py +91 -0
- cubevis/data/measurement_set/processing_set/_ps_stats.py +218 -0
- cubevis/data/measurement_set/processing_set/_xds_data.py +149 -0
- cubevis/plot/__init__.py +1 -0
- cubevis/plot/ms_plot/__init__.py +29 -0
- cubevis/plot/ms_plot/_ms_plot.py +242 -0
- cubevis/plot/ms_plot/_ms_plot_constants.py +22 -0
- cubevis/plot/ms_plot/_ms_plot_selectors.py +348 -0
- cubevis/plot/ms_plot/_raster_plot.py +292 -0
- cubevis/plot/ms_plot/_raster_plot_inputs.py +116 -0
- cubevis/plot/ms_plot/_xds_plot_axes.py +110 -0
- cubevis/private/__java__/xml-casa-assembly-1.86.jar +0 -0
- cubevis/private/_gclean.py +798 -0
- cubevis/private/casashell/createmask.py +332 -0
- cubevis/private/casashell/iclean.py +4432 -0
- cubevis/private/casatasks/__init__.py +140 -0
- cubevis/private/casatasks/createmask.py +86 -0
- cubevis/private/casatasks/createregion.py +83 -0
- cubevis/private/casatasks/iclean.py +1831 -0
- cubevis/readme.rst +16 -0
- cubevis/remote/__init__.py +10 -0
- cubevis/remote/_gclean.py +61 -0
- cubevis/remote/_local.py +287 -0
- cubevis/remote/_remote_kernel.py +80 -0
- cubevis/toolbox/__init__.py +32 -0
- cubevis/toolbox/_app_context.py +74 -0
- cubevis/toolbox/_cube.py +3457 -0
- cubevis/toolbox/_region_list.py +197 -0
- cubevis/utils/_ResourceManager.py +86 -0
- cubevis/utils/__init__.py +620 -0
- cubevis/utils/_contextmgrchain.py +84 -0
- cubevis/utils/_conversion.py +93 -0
- cubevis/utils/_copydoc.py +55 -0
- cubevis/utils/_docenum.py +25 -0
- cubevis/utils/_import_protected_module.py +35 -0
- cubevis/utils/_logging.py +85 -0
- cubevis/utils/_pkgs.py +77 -0
- cubevis/utils/_regions.py +40 -0
- cubevis/utils/_static.py +66 -0
- cubevis/utils/_tiles.py +167 -0
- cubevis-0.5.2.dist-info/METADATA +151 -0
- cubevis-0.5.2.dist-info/RECORD +132 -0
- cubevis-0.5.2.dist-info/WHEEL +4 -0
- cubevis-0.5.2.dist-info/licenses/LICENSE +504 -0
|
@@ -0,0 +1,798 @@
|
|
|
1
|
+
########################################################################3
|
|
2
|
+
# _gclean.py
|
|
3
|
+
#
|
|
4
|
+
# Copyright (C) 2021,2022,2023
|
|
5
|
+
# Associated Universities, Inc. Washington DC, USA.
|
|
6
|
+
#
|
|
7
|
+
# This script is free software; you can redistribute it and/or modify it
|
|
8
|
+
# under the terms of the GNU Library General Public License as published by
|
|
9
|
+
# the Free Software Foundation; either version 2 of the License, or (at your
|
|
10
|
+
# option) any later version.
|
|
11
|
+
#
|
|
12
|
+
# This library is distributed in the hope that it will be useful, but WITHOUT
|
|
13
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
14
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
|
|
15
|
+
# License for more details.
|
|
16
|
+
#
|
|
17
|
+
# You should have received a copy of the GNU Library General Public License
|
|
18
|
+
# along with this library; if not, write to the Free Software Foundation,
|
|
19
|
+
# Inc., 675 Massachusetts Ave, Cambridge, MA 02139, USA.
|
|
20
|
+
#
|
|
21
|
+
# Correspondence concerning AIPS++ should be adressed as follows:
|
|
22
|
+
# Internet email: casa-feedback@nrao.edu.
|
|
23
|
+
# Postal address: AIPS++ Project Office
|
|
24
|
+
# National Radio Astronomy Observatory
|
|
25
|
+
# 520 Edgemont Road
|
|
26
|
+
# Charlottesville, VA 22903-2475 USA
|
|
27
|
+
#
|
|
28
|
+
import os
|
|
29
|
+
import asyncio
|
|
30
|
+
from functools import reduce
|
|
31
|
+
import copy
|
|
32
|
+
import numpy as np
|
|
33
|
+
import shutil
|
|
34
|
+
import time
|
|
35
|
+
import subprocess
|
|
36
|
+
|
|
37
|
+
from casatasks.private.imagerhelpers.imager_return_dict import ImagingDict
|
|
38
|
+
from casatasks import deconvolve, tclean, imstat
|
|
39
|
+
from casatasks import casalog
|
|
40
|
+
|
|
41
|
+
###
|
|
42
|
+
### import check versions
|
|
43
|
+
###
|
|
44
|
+
_GCV001 = True
|
|
45
|
+
_GCV002 = True
|
|
46
|
+
_GCV003 = True
|
|
47
|
+
_GCV004 = True
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# from casatasks.private.imagerhelpers._gclean import gclean
|
|
51
|
+
class gclean:
|
|
52
|
+
'''gclean(...) creates a stream of convergence records which indicate
|
|
53
|
+
the convergence quaility of the tclean process. The initial record
|
|
54
|
+
describes the initial dirty image.
|
|
55
|
+
It is designed for use with the interactive clean GUI, but it could
|
|
56
|
+
be used independently. It can be used as a regular generator:
|
|
57
|
+
for rec in gclean( vis='refim_point_withline.ms', imagename='test', imsize=512, cell='12.0arcsec',
|
|
58
|
+
specmode='cube', interpolation='nearest', nchan=5, start='1.0GHz', width='0.2GHz',
|
|
59
|
+
pblimit=-1e-05, deconvolver='hogbom', niter=500, cyclefactor=3, scales=[0, 3, 10] ):
|
|
60
|
+
# use rec to decide when to stop, for example to check stopcode or peak residual:
|
|
61
|
+
# if (rec[0] > 1) or (min(rec[1][0][0]['peakRes']) < 0.001):
|
|
62
|
+
# break
|
|
63
|
+
print(rec)
|
|
64
|
+
or as an async generator:
|
|
65
|
+
async for rec in gclean( vis='refim_point_withline.ms', imagename='test', imsize=512, cell='12.0arcsec',
|
|
66
|
+
specmode='cube', interpolation='nearest', nchan=5, start='1.0GHz', width='0.2GHz',
|
|
67
|
+
pblimit=-1e-05, deconvolver='hogbom', niter=500, cyclefactor=3, scales=[0, 3, 10] ):
|
|
68
|
+
# use rec to decide when to stop
|
|
69
|
+
print(rec)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
See also: __next__(...) for a description of the returned rec
|
|
73
|
+
|
|
74
|
+
TODO: do we need to preserve any hidden state between tclean calls for the iterbotsink and/or synthesisimager tools?
|
|
75
|
+
'''
|
|
76
|
+
|
|
77
|
+
def _tclean( self, *args, **kwargs ):
|
|
78
|
+
""" Calls tclean records the arguments in the local history of tclean calls.
|
|
79
|
+
|
|
80
|
+
The full tclean history for this instance can be retrieved via the cmds() method."""
|
|
81
|
+
arg_s = ', '.join( map( lambda a: self._history_filter(len(self._exe_cmds), None, repr(a)), args ) )
|
|
82
|
+
kw_s = ', '.join( map( lambda kv: self._history_filter(len(self._exe_cmds), kv[0], "%s=%s" % (kv[0],repr(kv[1]))), kwargs.items()) )
|
|
83
|
+
if len(arg_s) > 0 and len(kw_s) > 0:
|
|
84
|
+
parameters = arg_s + ", " + kw_s
|
|
85
|
+
else:
|
|
86
|
+
parameters = arg_s + kw_s
|
|
87
|
+
self._exe_cmds.append( "tclean( %s )" % parameters )
|
|
88
|
+
self._exe_cmds_per_iter[-1] += 1
|
|
89
|
+
return tclean( *args, **kwargs )
|
|
90
|
+
|
|
91
|
+
def _deconvolve( self, *args, **kwargs ):
|
|
92
|
+
""" Calls deconvolve records the arguments in the local history of deconvolve calls.
|
|
93
|
+
|
|
94
|
+
The full deconvolve history for this instance can be retrieved via the cmds() method."""
|
|
95
|
+
arg_s = ', '.join( map( lambda a: self._history_filter(len(self._exe_cmds), None, repr(a)), args ) )
|
|
96
|
+
kw_s = ', '.join( map( lambda kv: self._history_filter(len(self._exe_cmds), kv[0], "%s=%s" % (kv[0],repr(kv[1]))), kwargs.items()) )
|
|
97
|
+
if len(arg_s) > 0 and len(kw_s) > 0:
|
|
98
|
+
parameters = arg_s + ", " + kw_s
|
|
99
|
+
else:
|
|
100
|
+
parameters = arg_s + kw_s
|
|
101
|
+
self._exe_cmds.append( "deconvolve( %s )" % parameters )
|
|
102
|
+
self._exe_cmds_per_iter[-1] += 1
|
|
103
|
+
return deconvolve( *args, **kwargs )
|
|
104
|
+
|
|
105
|
+
def _remove_tree( self, directory ):
|
|
106
|
+
if os.path.isdir(directory):
|
|
107
|
+
shutil.rmtree(directory)
|
|
108
|
+
self._exe_cmds.append( f'''shutil.rmtree( {repr(directory)} )''' )
|
|
109
|
+
self._exe_cmds_per_iter[-1] += 1
|
|
110
|
+
|
|
111
|
+
def cmds( self, history=False ):
|
|
112
|
+
""" Returns the history of all tclean calls for this instance. If ``history``
|
|
113
|
+
is set to True then the full history will be returned, otherwise the commands
|
|
114
|
+
executed for generating the latest result are returned.
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
if history:
|
|
118
|
+
return self._exe_cmds
|
|
119
|
+
else:
|
|
120
|
+
if self._exe_cmds_per_iter[-1] > 0:
|
|
121
|
+
# Return the last N commands
|
|
122
|
+
return self._exe_cmds[-self._exe_cmds_per_iter[-1]:]
|
|
123
|
+
else:
|
|
124
|
+
# If convergence is hit, no commands were run so return nothing
|
|
125
|
+
return []
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def update( self, msg ):
|
|
129
|
+
""" Interactive clean parameters update.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
msg: dict with possible keys 'niter', 'cycleniter', 'nmajor', 'threshold', 'cyclefactor'
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
stopcode : Stop code in case of error (-1 on error, 0 if no error), int
|
|
136
|
+
stopdesc : Exception error message, str
|
|
137
|
+
"""
|
|
138
|
+
if 'niter' in msg:
|
|
139
|
+
try:
|
|
140
|
+
self._niter = int(msg['niter'])
|
|
141
|
+
if self._niter < -1:
|
|
142
|
+
return -1, f"niter must be >= -1"
|
|
143
|
+
except ValueError as err:
|
|
144
|
+
return -1, "niter must be an integer"
|
|
145
|
+
|
|
146
|
+
if 'cycleniter' in msg:
|
|
147
|
+
try:
|
|
148
|
+
self._cycleniter = int(msg['cycleniter'])
|
|
149
|
+
if self._cycleniter < -1:
|
|
150
|
+
return -1, f"cycleniter must be >= -1"
|
|
151
|
+
except ValueError:
|
|
152
|
+
return -1, "cycleniter must be an integer"
|
|
153
|
+
|
|
154
|
+
if 'nmajor' in msg:
|
|
155
|
+
try:
|
|
156
|
+
self._nmajor = int(msg['nmajor'])
|
|
157
|
+
if self._nmajor < -1:
|
|
158
|
+
return -1, f"nmajor must be >= -1"
|
|
159
|
+
except ValueError as e:
|
|
160
|
+
return -1, "nmajor must be an integer"
|
|
161
|
+
|
|
162
|
+
if 'threshold' in msg:
|
|
163
|
+
try:
|
|
164
|
+
self._threshold = float(msg['threshold'])
|
|
165
|
+
if self._threshold < 0:
|
|
166
|
+
return -1, f"threshold must be >= 0"
|
|
167
|
+
except ValueError:
|
|
168
|
+
if isinstance(msg['threshold'], str) and "jy" in msg['threshold'].lower():
|
|
169
|
+
self._threshold_to_float(msg['threshold']) # Convert str to float
|
|
170
|
+
else:
|
|
171
|
+
return -1, f"threshold must be a number, or a number with units (Jy/mJy/uJy)"
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
if 'nsigma' in msg:
|
|
175
|
+
try:
|
|
176
|
+
self._nsigma = float(msg['nsigma'])
|
|
177
|
+
if self._nsigma < 0:
|
|
178
|
+
return -1, f"nsigma must be >= 0"
|
|
179
|
+
except ValueError:
|
|
180
|
+
return -1, "nsigma must be a number"
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
if 'gain' in msg:
|
|
184
|
+
try:
|
|
185
|
+
self._gain = float(msg['gain'])
|
|
186
|
+
if self._gain <= 0:
|
|
187
|
+
return -1, f"gain must be > 0"
|
|
188
|
+
except ValueError:
|
|
189
|
+
return -1, "gain must be a number"
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
if 'cyclefactor' in msg:
|
|
193
|
+
try:
|
|
194
|
+
self._cyclefactor = float(msg['cyclefactor'])
|
|
195
|
+
if self._cyclefactor <= 0:
|
|
196
|
+
return -1, f"cyclefactor must be > 0"
|
|
197
|
+
except ValueError:
|
|
198
|
+
return -1, "cyclefactor must be a number"
|
|
199
|
+
|
|
200
|
+
### Automasking parameters
|
|
201
|
+
|
|
202
|
+
if 'dogrowprune' in msg:
|
|
203
|
+
try:
|
|
204
|
+
self._dogrowprune = bool(msg['dogrowprune'])
|
|
205
|
+
except ValueError:
|
|
206
|
+
return -1, "dogrowprune must be a boolean"
|
|
207
|
+
|
|
208
|
+
if 'noisethreshold' in msg:
|
|
209
|
+
try:
|
|
210
|
+
self._noisethreshold = float(msg['noisethreshold'])
|
|
211
|
+
if self._noisethreshold < 0:
|
|
212
|
+
return -1, f"noisethreshold must be >= 0"
|
|
213
|
+
except ValueError:
|
|
214
|
+
return -1, "noisethreshold must be a number"
|
|
215
|
+
|
|
216
|
+
if 'sidelobethreshold' in msg:
|
|
217
|
+
try:
|
|
218
|
+
self._sidelobethreshold = float(msg['sidelobethreshold'])
|
|
219
|
+
if self._sidelobethreshold < 0:
|
|
220
|
+
return -1, f"sidelobethreshold must be >= 0"
|
|
221
|
+
except ValueError:
|
|
222
|
+
return -1, "sidelobethreshold must be a number"
|
|
223
|
+
|
|
224
|
+
if 'lownoisethreshold' in msg:
|
|
225
|
+
try:
|
|
226
|
+
self._lownoisethreshold = float(msg['lownoisethreshold'])
|
|
227
|
+
if self._lownoisethreshold < 0:
|
|
228
|
+
return -1, f"lownoisethreshold must be >= 0"
|
|
229
|
+
except ValueError:
|
|
230
|
+
return -1, "lownoisethreshold must be a number"
|
|
231
|
+
|
|
232
|
+
if 'minbeamfrac' in msg:
|
|
233
|
+
try:
|
|
234
|
+
self._minbeamfrac = float(msg['minbeamfrac'])
|
|
235
|
+
if self._minbeamfrac < 0:
|
|
236
|
+
return -1, f"minbeamfrac must be >= 0"
|
|
237
|
+
except ValueError:
|
|
238
|
+
return -1, "minbeamfrac must be a number"
|
|
239
|
+
|
|
240
|
+
if 'negativethreshold' in msg:
|
|
241
|
+
try:
|
|
242
|
+
self._negativethreshold = float(msg['negativethreshold'])
|
|
243
|
+
if self._negativethreshold < 0:
|
|
244
|
+
return -1, f"negativethreshold must be >= 0"
|
|
245
|
+
except ValueError:
|
|
246
|
+
return -1, "negativethreshold must be a number"
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
return 0, ""
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _threshold_to_float(self, msg=None):
|
|
255
|
+
# Convert threshold from string to float if necessary
|
|
256
|
+
if msg is not None:
|
|
257
|
+
if isinstance(msg, str):
|
|
258
|
+
if "mJy" in msg:
|
|
259
|
+
self._threshold = float(msg.replace("mJy", "")) / 1e3
|
|
260
|
+
elif "uJy" in msg:
|
|
261
|
+
self._threshold = float(msg.replace("uJy", "")) / 1e6
|
|
262
|
+
elif "Jy" in msg:
|
|
263
|
+
self._threshold = float(msg.replace("Jy", ""))
|
|
264
|
+
else:
|
|
265
|
+
if isinstance(self._threshold, str):
|
|
266
|
+
if "mJy" in self._threshold:
|
|
267
|
+
self._threshold = float(self._threshold.replace("mJy", "")) / 1e3
|
|
268
|
+
elif "uJy" in self._threshold:
|
|
269
|
+
self._threshold = float(self._threshold.replace("uJy", "")) / 1e6
|
|
270
|
+
elif "Jy" in self._threshold:
|
|
271
|
+
self._threshold = float(self._threshold.replace("Jy", ""))
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def __init__(self, vis, imagename, selectdata=True, field='', spw='', timerange='', uvrange='', antenna='', scan='', observation='', intent='', datacolumn='corrected', imsize=[100], cell=[ ],
|
|
275
|
+
phasecenter='', projection='SIN', stokes='I', startmodel='', specmode='cube', reffreq='', nchan=-1, start='', width='', outframe='LSRK', veltype='radio', restfreq='',
|
|
276
|
+
interpolation='linear', perchanweightdensity=True, gridder='standard', facets=int(1), psfphasecenter='', wprojplanes=int(1), mosweight=True, aterm=True, psterm=False, wbawp=True,
|
|
277
|
+
conjbeams=False, usepointing=False, cfcache = '', pointingoffsetsigdev=[ ], vptable='', computepastep=float(360.0), rotatepastep=float(360.0), pblimit=0.2, normtype='flatnoise',
|
|
278
|
+
deconvolver='hogbom', smallscalebias=0.0, fusedthreshold=0, largestscale=-1, niter=0, threshold='0.1Jy', nsigma=0.0, cycleniter=-1, nmajor=1, cyclefactor=1.0, minpsffraction=0.05,
|
|
279
|
+
maxpsffraction=0.8, scales=[], restoringbeam='', pbcor=False, outlierfile='', nterms=int(2), weighting='natural', robust=float(0.5), noise='0.0Jy', uvtaper=[], npixels=0,
|
|
280
|
+
gain=float(0.1), pbmask=0.2, sidelobethreshold=3.0, noisethreshold=5.0, lownoisethreshold=1.5, negativethreshold=0.0, smoothfactor=float(1.0), minbeamfrac=0.3, cutthreshold=0.01,
|
|
281
|
+
growiterations=75, dogrowprune=True, minpercentchange=-1.0, verbose=False, fastnoise=True, savemodel='none', usemask='user', mask='', restoration=True, restart=True, calcres=True,
|
|
282
|
+
calcpsf=True, psfcutoff=float(0.35), parallel=False, history_filter=lambda index, arg, history_value: history_value ):
|
|
283
|
+
|
|
284
|
+
self._vis = vis
|
|
285
|
+
self._imagename = imagename
|
|
286
|
+
self._selectdata = selectdata
|
|
287
|
+
self._imsize = imsize
|
|
288
|
+
self._cell = cell
|
|
289
|
+
self._phasecenter = phasecenter
|
|
290
|
+
self._projection = projection
|
|
291
|
+
self._stokes = stokes
|
|
292
|
+
self._startmodel = startmodel
|
|
293
|
+
self._specmode = specmode
|
|
294
|
+
self._reffreq = reffreq
|
|
295
|
+
self._nchan = nchan
|
|
296
|
+
self._start = start
|
|
297
|
+
self._width = width
|
|
298
|
+
self._outframe = outframe
|
|
299
|
+
self._veltype = veltype
|
|
300
|
+
self._restfreq = restfreq
|
|
301
|
+
self._interpolation = interpolation
|
|
302
|
+
self._perchanweightdensity = perchanweightdensity
|
|
303
|
+
self._gridder = gridder
|
|
304
|
+
self._facets = facets
|
|
305
|
+
self._psfphasecenter = psfphasecenter
|
|
306
|
+
self._wprojplanes = wprojplanes
|
|
307
|
+
self._mosweight = mosweight
|
|
308
|
+
self._aterm = aterm
|
|
309
|
+
self._psterm = psterm
|
|
310
|
+
self._wbawp = wbawp
|
|
311
|
+
self._conjbeams = conjbeams
|
|
312
|
+
self._usepointing = usepointing
|
|
313
|
+
self._cfcache = cfcache
|
|
314
|
+
self._pointingoffsetsigdev = pointingoffsetsigdev
|
|
315
|
+
self._vpable = vptable
|
|
316
|
+
self._computepastep = computepastep
|
|
317
|
+
self._rotatepastep = rotatepastep
|
|
318
|
+
self._pblimit = pblimit
|
|
319
|
+
self._normtype = normtype
|
|
320
|
+
self._deconvolver = deconvolver
|
|
321
|
+
self._smallscalebias = smallscalebias
|
|
322
|
+
self._fusedthreshold = fusedthreshold
|
|
323
|
+
self._largestscale = largestscale
|
|
324
|
+
self._niter = niter
|
|
325
|
+
self._threshold = threshold
|
|
326
|
+
self._cycleniter = cycleniter
|
|
327
|
+
self._minpsffraction = minpsffraction
|
|
328
|
+
self._maxpsffraction = maxpsffraction
|
|
329
|
+
self._nsigma = nsigma
|
|
330
|
+
self._nmajor = nmajor
|
|
331
|
+
self._cyclefactor = cyclefactor
|
|
332
|
+
self._scales = scales
|
|
333
|
+
self._restoringbeam = restoringbeam
|
|
334
|
+
self._pbcor = pbcor
|
|
335
|
+
#self._outlierfile = outlierfile
|
|
336
|
+
self._nterms = nterms
|
|
337
|
+
self._exe_cmds = [ ]
|
|
338
|
+
self._exe_cmds_per_iter = [ ]
|
|
339
|
+
self._history_filter = history_filter
|
|
340
|
+
self._finalized = False
|
|
341
|
+
self._field = field
|
|
342
|
+
self._spw = spw
|
|
343
|
+
self._timerange = timerange
|
|
344
|
+
self._uvrange = uvrange
|
|
345
|
+
self._antenna = antenna
|
|
346
|
+
self._scan = scan
|
|
347
|
+
self._observation = observation
|
|
348
|
+
self._intent = intent
|
|
349
|
+
self._datacolumn = datacolumn
|
|
350
|
+
self._weighting = weighting
|
|
351
|
+
self._robust = robust
|
|
352
|
+
self._noise = noise
|
|
353
|
+
self._uvtaper = uvtaper
|
|
354
|
+
self._npixels = npixels
|
|
355
|
+
self._gain = gain
|
|
356
|
+
self._pbmask = pbmask
|
|
357
|
+
self._sidelobethreshold = sidelobethreshold
|
|
358
|
+
self._noisethreshold = noisethreshold
|
|
359
|
+
self._lownoisethreshold = lownoisethreshold
|
|
360
|
+
self._negativethreshold = negativethreshold
|
|
361
|
+
self._smoothfactor = smoothfactor
|
|
362
|
+
self._minbeamfrac = minbeamfrac
|
|
363
|
+
self._cutthreshold = cutthreshold
|
|
364
|
+
self._growiterations = growiterations
|
|
365
|
+
self._dogrowprune = dogrowprune
|
|
366
|
+
self._minpercentchange = minpercentchange
|
|
367
|
+
self._verbose = verbose
|
|
368
|
+
self._fastnoise = fastnoise
|
|
369
|
+
self._savemodel = savemodel
|
|
370
|
+
self._parallel = parallel
|
|
371
|
+
self._usemask = usemask
|
|
372
|
+
self._mask = mask
|
|
373
|
+
self._restoration = restoration
|
|
374
|
+
self._restart = restart
|
|
375
|
+
self._calcres = calcres
|
|
376
|
+
self._calcpsf = calcpsf
|
|
377
|
+
self._psfcutoff = psfcutoff
|
|
378
|
+
self.global_imdict = ImagingDict()
|
|
379
|
+
self.current_imdict = ImagingDict()
|
|
380
|
+
self._major_done = 0
|
|
381
|
+
self.hasit = False # Convergence flag
|
|
382
|
+
self._has_restored = False
|
|
383
|
+
self.stopdescription = '' # Convergence flag
|
|
384
|
+
self._initial_mask_exists = False
|
|
385
|
+
self._convergence_result = (None,None,None,None,None,{ 'chan': None, 'major': None })
|
|
386
|
+
# ^^^^ ^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----->>> convergence info
|
|
387
|
+
# | | | | +----->>> Number of global iterations remaining for current run (niterleft)
|
|
388
|
+
# | | | +---------->>> Number of major cycles remaining for current run (nmajorleft)
|
|
389
|
+
# | | +---------------->>> major cycles done for current run (nmajordone)
|
|
390
|
+
# | +------------------>>> tclean stopcode
|
|
391
|
+
# +----------------------->>> tclean stopdescription
|
|
392
|
+
|
|
393
|
+
# Convert threshold from string to float, interpreting units.
|
|
394
|
+
# XXX : We should ideally use quantities, but we are trying to
|
|
395
|
+
# stick to "public API" funtions inside _gclean
|
|
396
|
+
self._threshold_to_float()
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def __add_per_major_items( self, tclean_ret, major_ret, chan_ret ):
|
|
400
|
+
'''Add meta-data about the whole major cycle, including 'cyclethreshold'
|
|
401
|
+
'''
|
|
402
|
+
|
|
403
|
+
if 'cyclethreshold' in tclean_ret:
|
|
404
|
+
|
|
405
|
+
rdict = dict( major=dict( cyclethreshold=[tclean_ret['cyclethreshold']] if major_ret is None else (major_ret['cyclethreshold'] + [tclean_ret['cyclethreshold']]) ),
|
|
406
|
+
chan=chan_ret )
|
|
407
|
+
else:
|
|
408
|
+
rdict = dict( major=dict( cyclethreshold=major_ret['cyclethreshold'].append(tclean_ret['cyclethreshold']) ),
|
|
409
|
+
chan=chan_ret )
|
|
410
|
+
|
|
411
|
+
return rdict
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _calc_deconv_controls(self, imdict, niterleft=0, threshold=0, cycleniter=-1):
|
|
415
|
+
"""
|
|
416
|
+
Calculate cycleniter and cyclethreshold for deconvolution.
|
|
417
|
+
"""
|
|
418
|
+
|
|
419
|
+
use_cycleniter = niterleft #niter - imdict.returndict['iterdone']
|
|
420
|
+
|
|
421
|
+
if cycleniter > -1 : # User is forcing this number
|
|
422
|
+
use_cycleniter = min(cycleniter, use_cycleniter)
|
|
423
|
+
|
|
424
|
+
psffrac = imdict.returndict['maxpsfsidelobe'] * self._cyclefactor
|
|
425
|
+
psffrac = max(psffrac, self._minpsffraction)
|
|
426
|
+
psffrac = min(psffrac, self._maxpsffraction)
|
|
427
|
+
|
|
428
|
+
# TODO : This assumes the default field (i.e., field=0);
|
|
429
|
+
# This won't work for multiple fields.
|
|
430
|
+
cyclethreshold = psffrac * imdict.get_peakres()
|
|
431
|
+
cyclethreshold = max(cyclethreshold, threshold)
|
|
432
|
+
|
|
433
|
+
return int(use_cycleniter), cyclethreshold
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def __update_convergence(self):
|
|
437
|
+
"""
|
|
438
|
+
Accumulates the per-channel/stokes summaryminor keys across all major cycle calls so far.
|
|
439
|
+
|
|
440
|
+
The "iterDone" key will be replaced with "iterations", and for the "iterations" key,
|
|
441
|
+
the value in the returned cummulative record will be a rolling sum of iterations done
|
|
442
|
+
for tclean calls so far, one value per minor cycle.
|
|
443
|
+
For example, if there have been two clean calls, and in the first call channel 0 had
|
|
444
|
+
[1] iteration in 1 minor cycle, and for the second call channel 0 had [6, 10, 9, 1]
|
|
445
|
+
iterations in 4 minor cycles), then the resultant "iterations" key for channel 0 would be:
|
|
446
|
+
[1, 7, 17, 26, 27]
|
|
447
|
+
"""
|
|
448
|
+
|
|
449
|
+
keys = ['modelFlux', 'iterDone', 'peakRes', 'stopCode', 'cycleThresh']
|
|
450
|
+
|
|
451
|
+
# Grab tuples of keys of interest
|
|
452
|
+
outrec = {}
|
|
453
|
+
for nn in range(self.global_imdict.nchan):
|
|
454
|
+
outrec[nn] = {}
|
|
455
|
+
for ss in range(self.global_imdict.nstokes):
|
|
456
|
+
outrec[nn][ss] = {}
|
|
457
|
+
for key in keys:
|
|
458
|
+
# Replace iterDone with iterations
|
|
459
|
+
if key == 'iterDone':
|
|
460
|
+
# Maintain cumulative sum of iterations per entry
|
|
461
|
+
outrec[nn][ss]['iterations'] = np.cumsum(self.global_imdict.get_key(key, stokes=ss, chan=nn))
|
|
462
|
+
# Replace iterDone with iterations
|
|
463
|
+
#outrec[nn][ss]['iterations'] = self.global_imdict.get_key(key, stokes=ss, chan=nn)
|
|
464
|
+
else:
|
|
465
|
+
outrec[nn][ss][key] = self.global_imdict.get_key(key, stokes=ss, chan=nn)
|
|
466
|
+
|
|
467
|
+
return outrec
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def _check_initial_mask(self):
|
|
471
|
+
"""
|
|
472
|
+
Check if a mask from a previous run exists on disk or not.
|
|
473
|
+
"""
|
|
474
|
+
|
|
475
|
+
if self._usemask == 'user' and self._mask == '':
|
|
476
|
+
maskname = self._imagename + '.mask'
|
|
477
|
+
|
|
478
|
+
if os.path.exists(maskname):
|
|
479
|
+
self._initial_mask_exists = True
|
|
480
|
+
else:
|
|
481
|
+
self._initial_mask_exists = False
|
|
482
|
+
|
|
483
|
+
def _fix_initial_mask(self):
|
|
484
|
+
"""
|
|
485
|
+
If on start up, no user mask is provided, then flip the initial mask to
|
|
486
|
+
be all zeros for interactive use.
|
|
487
|
+
"""
|
|
488
|
+
|
|
489
|
+
from casatools import image
|
|
490
|
+
ia = image()
|
|
491
|
+
|
|
492
|
+
if self._usemask == 'user' and self._mask == '':
|
|
493
|
+
maskname = self._imagename + '.mask'
|
|
494
|
+
|
|
495
|
+
# This means the mask was newly created by deconvolve, so flip it
|
|
496
|
+
if os.path.exists(maskname) and self._initial_mask_exists is False:
|
|
497
|
+
ia.open(maskname)
|
|
498
|
+
ia.set(0.0)
|
|
499
|
+
ia.close()
|
|
500
|
+
|
|
501
|
+
def _update_peakres(self):
|
|
502
|
+
if self._deconvolver == 'mtmfs':
|
|
503
|
+
residname = self._imagename + '.residual.tt0'
|
|
504
|
+
else:
|
|
505
|
+
residname = self._imagename + '.residual'
|
|
506
|
+
|
|
507
|
+
maskname = self._imagename + '.mask'
|
|
508
|
+
if not os.path.exists(maskname):
|
|
509
|
+
maskname = ''
|
|
510
|
+
|
|
511
|
+
peakres = imstat(imagename=residname, mask=f'''"{maskname}"''')['max']
|
|
512
|
+
if len(maskname) > 0:
|
|
513
|
+
masksum = imstat(imagename=maskname)['sum']
|
|
514
|
+
else:
|
|
515
|
+
masksum = []
|
|
516
|
+
|
|
517
|
+
if len(peakres) > 0:
|
|
518
|
+
peakres = peakres[0]
|
|
519
|
+
else:
|
|
520
|
+
peakres = None
|
|
521
|
+
|
|
522
|
+
if len(masksum) > 0:
|
|
523
|
+
masksum = masksum[0]
|
|
524
|
+
else:
|
|
525
|
+
masksum = None
|
|
526
|
+
|
|
527
|
+
return peakres, masksum
|
|
528
|
+
|
|
529
|
+
def __next__( self ):
|
|
530
|
+
""" Runs tclean and returns the (stopcode, convergence result) when executed with the python builtin next() function.
|
|
531
|
+
|
|
532
|
+
The returned convergence result is a nested dictionary:
|
|
533
|
+
{
|
|
534
|
+
channel id: {
|
|
535
|
+
stokes id: {
|
|
536
|
+
summary key: [values, one per minor cycle]
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
See also: gclean.__update_convergence(...)
|
|
542
|
+
"""
|
|
543
|
+
|
|
544
|
+
tclean_ret = {}
|
|
545
|
+
deconv_ret = {}
|
|
546
|
+
|
|
547
|
+
# vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv------------>>> is done to produce an initial dirty image
|
|
548
|
+
if self._niter < 1 and self._convergence_result[2] is not None:
|
|
549
|
+
self._convergence_result = ( f'nothing to run, niter == {self._niter}',
|
|
550
|
+
self._convergence_result[1],
|
|
551
|
+
self._major_done,
|
|
552
|
+
self._nmajor,
|
|
553
|
+
self._niter,
|
|
554
|
+
self._convergence_result[5] )
|
|
555
|
+
return self._convergence_result
|
|
556
|
+
else:
|
|
557
|
+
### CALL SEQUENCE:
|
|
558
|
+
### tclean(niter=0),deconvolve(niter=0),tclean(niter=100),deconvolve(niter=0),tclean(niter=100),tclean(niter=0,restoration=True)
|
|
559
|
+
self._exe_cmds_per_iter.append(0)
|
|
560
|
+
try:
|
|
561
|
+
if self._convergence_result[1] is None:
|
|
562
|
+
# initial call to tclean(...) creates the initial dirty image with niter=0
|
|
563
|
+
|
|
564
|
+
# If calcres and calcpsf are False, no need to run initial tclean - assume image products already on disk.
|
|
565
|
+
if not (self._calcres == False and self._calcpsf == False):
|
|
566
|
+
casalog.post('Running initial major cycle to create first residual image.', 'INFO')
|
|
567
|
+
print('Running initial major cycle to create first residual image.')
|
|
568
|
+
tclean_ret = self._tclean( vis=self._vis, mask=self._mask, imagename=self._imagename, imsize=self._imsize, cell=self._cell, selectdata=self._selectdata, phasecenter=self._phasecenter, stokes=self._stokes,
|
|
569
|
+
startmodel=self._startmodel, specmode=self._specmode, projection=self._projection, reffreq=self._reffreq, gridder=self._gridder, wprojplanes=self._wprojplanes, facets=self._facets,
|
|
570
|
+
mosweight=self._mosweight, psfphasecenter = self._psfphasecenter, aterm=self._aterm, psterm=self._psterm, wbawp=self._wbawp, conjbeams=self._conjbeams, cfcache=self._cfcache,
|
|
571
|
+
usepointing=self._usepointing, computepastep=self._computepastep, rotatepastep=self._rotatepastep, normtype=self._normtype, fusedthreshold=self._fusedthreshold,
|
|
572
|
+
largestscale=self._largestscale, noise = self._noise, uvtaper=self._uvtaper, psfcutoff=self._psfcutoff, interpolation=self._interpolation,
|
|
573
|
+
perchanweightdensity=self._perchanweightdensity, nchan=self._nchan, start=self._start, width=self._width, veltype=self._veltype, restfreq=self._restfreq, outframe=self._outframe,
|
|
574
|
+
pointingoffsetsigdev=self._pointingoffsetsigdev, pblimit=self._pblimit, deconvolver=self._deconvolver, smallscalebias=self._smallscalebias, cyclefactor=self._cyclefactor,
|
|
575
|
+
scales=self._scales, restoringbeam=self._restoringbeam, pbcor=self._pbcor, nterms=self._nterms, field=self._field, spw=self._spw, timerange=self._timerange, uvrange=self._uvrange,
|
|
576
|
+
antenna=self._antenna, scan=self._scan, observation=self._observation, intent=self._intent, datacolumn=self._datacolumn, weighting=self._weighting, robust=self._robust,
|
|
577
|
+
npixels=self._npixels, interactive=False, niter=0, gain=self._gain, calcres=self._calcres, calcpsf=self._calcpsf, restoration=False, parallel=self._parallel, fullsummary=True)
|
|
578
|
+
# outlierfile = self._outlierfile,
|
|
579
|
+
|
|
580
|
+
# Check if a mask from a previous run exists on disk
|
|
581
|
+
self._check_initial_mask()
|
|
582
|
+
|
|
583
|
+
deconv_ret = self._deconvolve(imagename=self._imagename, startmodel=self._startmodel,
|
|
584
|
+
deconvolver=self._deconvolver, scales=self._scales, nterms=self._nterms,
|
|
585
|
+
smallscalebias=self._smallscalebias, restoration=False, restoringbeam=self._restoringbeam,
|
|
586
|
+
niter = 0, gain=self._gain, threshold=self._threshold, nsigma=self._nsigma,
|
|
587
|
+
interactive = False, fullsummary=True, fastnoise=self._fastnoise, usemask=self._usemask,
|
|
588
|
+
mask = self._mask, pbmask=self._pbmask, sidelobethreshold=self._sidelobethreshold,
|
|
589
|
+
noisethreshold=self._noisethreshold, lownoisethreshold=self._lownoisethreshold,
|
|
590
|
+
negativethreshold=self._negativethreshold, smoothfactor=self._smoothfactor,
|
|
591
|
+
minbeamfrac=self._minbeamfrac, cutthreshold=self._cutthreshold,
|
|
592
|
+
growiterations=self._growiterations, dogrowprune=self._dogrowprune,
|
|
593
|
+
verbose=self._verbose)
|
|
594
|
+
|
|
595
|
+
# If no mask from a previous run exists, over-write the ones with zeros for the default mask
|
|
596
|
+
self._fix_initial_mask()
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
if len(tclean_ret) > 0 and len(deconv_ret) > 0:
|
|
600
|
+
self.current_imdict.returndict = self.current_imdict.merge(tclean_ret, deconv_ret)
|
|
601
|
+
elif len(tclean_ret) > 0 and len(deconv_ret) == 0:
|
|
602
|
+
self.current_imdict.returndict = copy.deepcopy(tclean_ret)
|
|
603
|
+
elif len(tclean_ret) == 0 and len(deconv_ret) > 0:
|
|
604
|
+
self.current_imdict.returndict = copy.deepcopy(deconv_ret)
|
|
605
|
+
else: # Both dicts are empty, this should never happen
|
|
606
|
+
raise ValueError("Both tclean and deconvolve return dicts are empty. This should never happen.")
|
|
607
|
+
|
|
608
|
+
self.global_imdict.returndict = self.current_imdict.returndict
|
|
609
|
+
|
|
610
|
+
## Initial call where niterleft and nmajorleft are same as original input values.
|
|
611
|
+
self.hasit, self.stopdescription = self.global_imdict.has_converged(self._niter, self._threshold, self._nmajor)
|
|
612
|
+
|
|
613
|
+
self.current_imdict.returndict['stopcode'] = self.hasit
|
|
614
|
+
self.current_imdict.returndict['stopDescription'] = self.stopdescription
|
|
615
|
+
self._major_done = 0
|
|
616
|
+
else:
|
|
617
|
+
# Reset convergence every time, since we return control to the GUI after a single major cycle
|
|
618
|
+
self.current_imdict.returndict['iterdone'] = 0.
|
|
619
|
+
|
|
620
|
+
# Mask can be updated here...
|
|
621
|
+
# Check for mask update - peakres + masksum
|
|
622
|
+
_peakres, _masksum = self._update_peakres()
|
|
623
|
+
|
|
624
|
+
self.hasit, self.stopdescription = self.global_imdict.has_converged(self._niter, self._threshold, self._nmajor, masksum=_masksum, peakres=_peakres)
|
|
625
|
+
|
|
626
|
+
#self.global_imdict.returndict['stopcode'] = self.hasit
|
|
627
|
+
#self.global_imdict.returndict['stopDescription'] = self.stopdescription
|
|
628
|
+
|
|
629
|
+
#self.current_imdict.returndict['stopcode'] = self.hasit
|
|
630
|
+
#self.current_imdict.returndict['stopDescription'] = self.stopdescription
|
|
631
|
+
|
|
632
|
+
# Has not, i.e., not converged
|
|
633
|
+
if self.hasit ==0 :
|
|
634
|
+
use_cycleniter, cyclethreshold = self._calc_deconv_controls(self.current_imdict, self._niter, self._threshold, self._cycleniter)
|
|
635
|
+
|
|
636
|
+
# Run the minor cycle
|
|
637
|
+
deconv_ret = self._deconvolve(imagename=self._imagename, startmodel=self._startmodel,
|
|
638
|
+
deconvolver=self._deconvolver, restoration=False,
|
|
639
|
+
threshold=cyclethreshold, niter=use_cycleniter, gain=self._gain, fullsummary=True)
|
|
640
|
+
|
|
641
|
+
# Run the major cycle
|
|
642
|
+
tclean_ret = self._tclean( vis=self._vis, imagename=self._imagename, imsize=self._imsize, cell=self._cell,
|
|
643
|
+
phasecenter=self._phasecenter, stokes=self._stokes, specmode=self._specmode, reffreq=self._reffreq,
|
|
644
|
+
gridder=self._gridder, wprojplanes=self._wprojplanes, mosweight=self._mosweight, psterm=self._psterm,
|
|
645
|
+
wbawp=self._wbawp, conjbeams=self._conjbeams, usepointing=self._usepointing, interpolation=self._interpolation,
|
|
646
|
+
perchanweightdensity=self._perchanweightdensity, nchan=self._nchan, start=self._start,
|
|
647
|
+
width=self._width, veltype=self._veltype, restfreq=self._restfreq, outframe=self._outframe,
|
|
648
|
+
pointingoffsetsigdev=self._pointingoffsetsigdev, pblimit=self._pblimit, deconvolver=self._deconvolver,
|
|
649
|
+
smallscalebias=self._smallscalebias, cyclefactor=self._cyclefactor, scales=self._scales,
|
|
650
|
+
restoringbeam=self._restoringbeam, pbcor=self._pbcor, nterms=self._nterms, field=self._field,
|
|
651
|
+
spw=self._spw, timerange=self._timerange, uvrange=self._uvrange, antenna=self._antenna,
|
|
652
|
+
scan=self._scan, observation=self._observation, intent=self._intent, datacolumn=self._datacolumn,
|
|
653
|
+
weighting=self._weighting, robust=self._robust, npixels=self._npixels, interactive=False,
|
|
654
|
+
niter=0, restart=True, calcpsf=False, calcres=True, restoration=False, threshold=self._threshold,
|
|
655
|
+
nsigma=self._nsigma, cycleniter=self._cycleniter, nmajor=1, gain=self._gain,
|
|
656
|
+
sidelobethreshold=self._sidelobethreshold, noisethreshold=self._noisethreshold,
|
|
657
|
+
lownoisethreshold=self._lownoisethreshold, negativethreshold=self._negativethreshold,
|
|
658
|
+
minbeamfrac=self._minbeamfrac, growiterations=self._growiterations, dogrowprune=self._dogrowprune,
|
|
659
|
+
minpercentchange=self._minpercentchange, fastnoise=self._fastnoise, savemodel=self._savemodel,
|
|
660
|
+
maxpsffraction=self._maxpsffraction,
|
|
661
|
+
minpsffraction=self._minpsffraction, parallel=self._parallel, fullsummary=True )
|
|
662
|
+
|
|
663
|
+
# Replace return dict with new return dict
|
|
664
|
+
# The order of the dicts into merge is important.
|
|
665
|
+
self.current_imdict.returndict = self.current_imdict.merge(tclean_ret, deconv_ret)
|
|
666
|
+
|
|
667
|
+
# Append new return dict to global return dict
|
|
668
|
+
self.global_imdict.returndict = self.global_imdict.concat(self.global_imdict.returndict, self.current_imdict.returndict)
|
|
669
|
+
self._major_done = self.current_imdict.returndict['nmajordone']
|
|
670
|
+
|
|
671
|
+
## Decrement count for the major cycle just done...
|
|
672
|
+
self.__decrement_counts()
|
|
673
|
+
|
|
674
|
+
cycleniterleft = self._cycleniter - self.current_imdict.returndict['iterdone']
|
|
675
|
+
|
|
676
|
+
# Use global imdict for convergence check
|
|
677
|
+
if deconv_ret['stopcode'] == 7: ## Tell the convergence checker that the mask is zero and iterations were skipped
|
|
678
|
+
self.hasit, self.stopdescription = self.global_imdict.has_converged(self._niter, self._threshold, self._nmajor, masksum=0)
|
|
679
|
+
else:
|
|
680
|
+
self.hasit, self.stopdescription = self.global_imdict.has_converged(self._niter, self._threshold, self._nmajor, cycleniter=cycleniterleft)
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
self.global_imdict.returndict['stopcode'] = self.hasit
|
|
684
|
+
self.global_imdict.returndict['stopDescription'] = self.stopdescription
|
|
685
|
+
|
|
686
|
+
if not self.hasit and self._usemask == 'auto-multithresh':
|
|
687
|
+
# If we haven't converged, run deconvolve to update the mask
|
|
688
|
+
# Note : This is only necessary if using auto-multithresh. If using the interactive viewer to draw, the mask gets updated as the regions
|
|
689
|
+
# are added or removed in the interactive viewer.
|
|
690
|
+
self._deconvolve(imagename=self._imagename, startmodel=self._startmodel, deconvolver=self._deconvolver, restoration=False, threshold=self._threshold, niter=0,
|
|
691
|
+
nsigma=self._nsigma, fullsummary=True, fastnoise=self._fastnoise, usemask=self._usemask, mask='', pbmask=self._pbmask,
|
|
692
|
+
sidelobethreshold=self._sidelobethreshold, noisethreshold=self._noisethreshold, lownoisethreshold=self._lownoisethreshold,
|
|
693
|
+
negativethreshold=self._negativethreshold, smoothfactor=self._smoothfactor, minbeamfrac=self._minbeamfrac, cutthreshold=self._cutthreshold,
|
|
694
|
+
growiterations=self._growiterations, dogrowprune=self._dogrowprune, verbose=self._verbose)
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
if len(self.global_imdict.returndict) > 0 and 'summaryminor' in self.global_imdict.returndict and sum(map(len,self.global_imdict.returndict['summaryminor'].values())) > 0:
|
|
698
|
+
self._convergence_result = ( self.global_imdict.returndict['stopDescription'] if 'stopDescription' in self.global_imdict.returndict else '',
|
|
699
|
+
self.global_imdict.returndict['stopcode'] if 'stopcode' in self.global_imdict.returndict else 0,
|
|
700
|
+
self._major_done,
|
|
701
|
+
self._nmajor,
|
|
702
|
+
self._niter,
|
|
703
|
+
self.__add_per_major_items( self.global_imdict.returndict,
|
|
704
|
+
self._convergence_result[5]['major'],
|
|
705
|
+
self.__update_convergence()))
|
|
706
|
+
else:
|
|
707
|
+
self._convergence_result = ( f'tclean returned an empty result',
|
|
708
|
+
self._convergence_result[1],
|
|
709
|
+
self._major_done,
|
|
710
|
+
self._nmajor,
|
|
711
|
+
self._niter,
|
|
712
|
+
self._convergence_result[5] )
|
|
713
|
+
except Exception as e:
|
|
714
|
+
self._convergence_result = ( str(e),
|
|
715
|
+
-1,
|
|
716
|
+
self._major_done,
|
|
717
|
+
self._nmajor,
|
|
718
|
+
self._niter,
|
|
719
|
+
self._convergence_result[5] )
|
|
720
|
+
return self._convergence_result
|
|
721
|
+
|
|
722
|
+
return self._convergence_result
|
|
723
|
+
|
|
724
|
+
def __decrement_counts( self ):
|
|
725
|
+
## Update niterleft and nmajorleft now.
|
|
726
|
+
if self.hasit == 0: ##If not yet converged.
|
|
727
|
+
if self._nmajor != -1: ## If -1, don't touch it.
|
|
728
|
+
self._nmajor = self._nmajor - 1
|
|
729
|
+
if self._nmajor<0: ## Force a floor
|
|
730
|
+
self._nmajor=0
|
|
731
|
+
self._niter = self._niter - self.current_imdict.get_key('iterdone')
|
|
732
|
+
if self._niter<0: ## This can happen when we're counting niter across channels in a single minor cycle set, and it crosses the total.
|
|
733
|
+
self._niter=0 ## Force a floor
|
|
734
|
+
else:
|
|
735
|
+
return ##If convergence has been reached, don't try to decrement further.
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
def __reflect_stop( self ):
|
|
739
|
+
## if python wasn't hacky, you would be able to try/except/raise in lambda
|
|
740
|
+
time.sleep(1)
|
|
741
|
+
try:
|
|
742
|
+
return self.__next__( )
|
|
743
|
+
except StopIteration:
|
|
744
|
+
raise StopAsyncIteration
|
|
745
|
+
|
|
746
|
+
async def __anext__( self ):
|
|
747
|
+
### asyncio.run cannot be used here because this is called
|
|
748
|
+
### within an asyncio loop...
|
|
749
|
+
loop = asyncio.get_event_loop( )
|
|
750
|
+
result = await loop.run_in_executor( None, self.__reflect_stop )
|
|
751
|
+
return result
|
|
752
|
+
|
|
753
|
+
def __iter__( self ):
|
|
754
|
+
return self
|
|
755
|
+
|
|
756
|
+
def __aiter__( self ):
|
|
757
|
+
return self
|
|
758
|
+
|
|
759
|
+
def __split_filename( self, path ):
|
|
760
|
+
return os.path.splitext(os.path.basename(path))
|
|
761
|
+
|
|
762
|
+
def __default_mask_name( self ):
|
|
763
|
+
imgparts = self.__split_filename( self._imagename )
|
|
764
|
+
return f'{imgparts[0]}.mask'
|
|
765
|
+
|
|
766
|
+
def __del__(self):
|
|
767
|
+
if not self._has_restored:
|
|
768
|
+
self.restore()
|
|
769
|
+
|
|
770
|
+
def mask(self):
|
|
771
|
+
#return self.__default_mask_name() if self._mask == '' else self._mask
|
|
772
|
+
return f'{self._imagename}.mask' if self._mask == '' else self._mask
|
|
773
|
+
|
|
774
|
+
def reset(self):
|
|
775
|
+
#if not self._finalized:
|
|
776
|
+
# raise RuntimeError('attempt to reset a gclean run that has not been finalized')
|
|
777
|
+
self._finalized = False
|
|
778
|
+
self._convergence_result = ( None,
|
|
779
|
+
self._convergence_result[1],
|
|
780
|
+
self._major_done,
|
|
781
|
+
self._nmajor,
|
|
782
|
+
self._niter,
|
|
783
|
+
self._convergence_result[5] )
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
def restore(self):
|
|
787
|
+
""" Restores the final image, and returns a path to the restored image. """
|
|
788
|
+
deconv_ret = self._deconvolve(imagename=self._imagename,
|
|
789
|
+
deconvolver=self._deconvolver,
|
|
790
|
+
restoration=True, niter=0,
|
|
791
|
+
fullsummary=True)
|
|
792
|
+
|
|
793
|
+
self._has_restored = True
|
|
794
|
+
|
|
795
|
+
return { "image": f"{self._imagename}.image" }
|
|
796
|
+
|
|
797
|
+
def has_next(self):
|
|
798
|
+
return not self._finalized
|