Skip to content

Commit c7b7683

Browse files
committed
Change to equivalent point source cal complete
Only the averaged point source equivalent data are sent to solver. gaintables sent back.
1 parent 2bb8a9b commit c7b7683

9 files changed

Lines changed: 190 additions & 152 deletions

File tree

‎arl/calibration/operations.py‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import numpy.linalg
99

10-
from arl.data.data_models import GainTable, BlockVisibility, QA
10+
from arl.data.data_models import GainTable, BlockVisibility, QA, assert_vis_gt_compatible
1111
from arl.data.data_models import ReceptorFrame
1212
from arl.visibility.iterators import vis_timeslice_iter
1313

@@ -61,7 +61,8 @@ def create_gaintable_from_blockvisibility(vis: BlockVisibility, time_width: floa
6161
receptor_frame=receptor_frame)
6262

6363
assert type(gt) is GainTable, "gt is not a GainTable: %r" % gt
64-
64+
assert_vis_gt_compatible(vis, gt)
65+
6566
return gt
6667

6768

@@ -83,6 +84,8 @@ def apply_gaintable(vis: BlockVisibility, gt: GainTable, inverse=False, **kwargs
8384
"""
8485
assert type(vis) is BlockVisibility, "vis is not a BlockVisibility: %r" % vis
8586
assert type(gt) is GainTable, "gt is not a GainTable: %r" % gt
87+
88+
assert_vis_gt_compatible(vis, gt)
8689

8790
if inverse:
8891
log.info('apply_gaintable: Apply inverse gaintable')

‎arl/calibration/solvers.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
import numpy
1818

1919
from arl.calibration.operations import create_gaintable_from_blockvisibility
20-
from arl.data.data_models import GainTable, BlockVisibility
20+
from arl.data.data_models import GainTable, BlockVisibility, assert_vis_gt_compatible
21+
2122
from arl.visibility.iterators import vis_timeslice_iter
2223
from arl.visibility.operations import create_visibility_from_rows, divide_visibility
2324

@@ -66,6 +67,9 @@ def solve_gaintable(vis: BlockVisibility, modelvis: BlockVisibility=None, phase_
6667

6768
assert type(gt) is GainTable, "gt is not a GainTable: %r" % gt
6869

70+
assert_vis_gt_compatible(vis, gt)
71+
72+
6973
return gt
7074

7175
def solve_from_X(gt: GainTable, x: numpy.ndarray, xwt: numpy.ndarray, chunk, crosspol, niter, phase_only, tol, npol) \

‎arl/data/data_models.py‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import logging
2323
import sys
24+
from typing import Union
2425

2526
import numpy
2627
from astropy import units as u
@@ -105,7 +106,7 @@ def mount(self):
105106

106107

107108
class GainTable:
108-
""" Gain table with data: time, antenna, gain[:,chan,pol], weight columns
109+
""" Gain table with data: time, antenna, gain[:, chan, rec, rec], weight columns
109110
110111
The weight is usually that output from gain solvers.
111112
"""
@@ -120,7 +121,7 @@ def __init__(self, data=None, gain: numpy.array = None, time: numpy.array = None
120121
Vobs = g_i g_j^* Vmodel
121122
122123
:param data:
123-
:param gain: [npol, nchan]
124+
:param gain: [:, nchan, nrec, nrec]
124125
:param time:
125126
:param weight:
126127
:param residual:
@@ -172,6 +173,14 @@ def residual(self):
172173
@property
173174
def nants(self):
174175
return self.data['gain'].shape[1]
176+
177+
@property
178+
def nchan(self):
179+
return self.data['gain'].shape[2]
180+
181+
@property
182+
def nrec(self):
183+
return self.receptor_frame.nrec
175184

176185

177186
class Image:
@@ -353,7 +362,7 @@ def __init__(self,
353362
assert len(channel_bandwidth) == nvis
354363
assert len(antenna1) == nvis
355364
assert len(antenna2) == nvis
356-
365+
357366
npol = polarisation_frame.npol
358367
desc = [('uvw', '>f8', (3,)),
359368
('time', '>f8'),
@@ -391,11 +400,11 @@ def size(self):
391400
for col in self.data.dtype.fields.keys():
392401
size += self.data[col].nbytes
393402
return size / 1024.0 / 1024.0 / 1024.0
394-
403+
395404
@property
396405
def nvis(self):
397406
return self.data['vis'].shape[0]
398-
407+
399408
@property
400409
def uvw(self): # In wavelengths in Visibility
401410
return self.data['uvw']
@@ -603,3 +612,14 @@ def assert_same_chan_pol(o1, o2):
603612
assert o1.nchan == o2.nchan, \
604613
"%s and %s have different number of channels: %d != %d" % \
605614
(type(o1).__name__, type(o2).__name__, o1.nchan, o2.nchan)
615+
616+
617+
def assert_vis_gt_compatible(vis: Union[Visibility, BlockVisibility], gt: GainTable):
618+
""" Check if visibility and gaintable are compatible
619+
620+
:param vis:
621+
:param gt:
622+
:return:
623+
"""
624+
assert vis.nchan == gt.nchan
625+
assert vis.npol == gt.nrec * gt.nrec

‎arl/graphs/dask_graphs.py‎

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
from arl.imaging import predict_2d, invert_2d, invert_wstack_single, predict_wstack_single, normalize_sumwt
4545
from arl.visibility.gather_scatter import visibility_scatter_w, visibility_gather_w, \
4646
visibility_scatter_channel, visibility_gather_channel
47-
from arl.visibility.operations import copy_visibility
47+
from arl.visibility.operations import copy_visibility, divide_visibility, integrate_visibility_by_channel
4848

4949

5050
def create_zero_vis_graph_list(vis_graph_list: List[delayed], **kwargs) -> List[delayed]:
@@ -538,3 +538,43 @@ def selfcal_single(vis, model_vis, **kwargs):
538538
else:
539539
return [delayed(selfcal_single, pure=True, nout=1)(vis_graph_list[i], model_vis_graph_list[i], **kwargs)
540540
for i, _ in enumerate(vis_graph_list)]
541+
542+
543+
def create_selfcal_point_graph_list(vis_graph_list: List[delayed], model_graph: delayed,
544+
vis_slices, global_solution=False,
545+
c_predict_vis_graph=create_predict_wstack_graph, **kwargs) -> List[delayed]:
546+
""" Create a set of graphs for (optionally global) selfcalibration of a list of visibilities
547+
548+
The visibilities are gathered to a single visibility data set which is then self-calibrated. The resulting
549+
gaintable is then effectively scattered out.
550+
then scattered.
551+
552+
:param vis_graph_list:
553+
:param model_graph:
554+
:param vis_slices:
555+
:param c_predict_vis_graph: create_predict_wstack_graph
556+
:param kwargs: Parameters for functions in graphs
557+
:return:
558+
"""
559+
560+
def selfcal_single(vis, **kwargs):
561+
gtsol = solve_gaintable(vis, None, **kwargs)
562+
vis = apply_gaintable(vis, gtsol, inverse=True, **kwargs)
563+
return vis
564+
565+
model_vis_graph_list = create_zero_vis_graph_list(vis_graph_list)
566+
model_vis_graph_list = c_predict_vis_graph(model_vis_graph_list, model_graph, vis_slices=vis_slices, **kwargs)
567+
point_vis_graph_list = [delayed(divide_visibility, nout=len(vis_graph_list))(vis_graph_list[i],
568+
model_vis_graph_list[i])
569+
for i, _ in enumerate(vis_graph_list)]
570+
571+
if global_solution:
572+
global_point_vis_graph = delayed(visibility_gather_channel, nout=1)(point_vis_graph_list)
573+
global_point_vis_graph = delayed(integrate_visibility_by_channel, nout=1)(global_point_vis_graph)
574+
gt_graph = delayed(solve_gaintable, pure=True, nout=1)(global_point_vis_graph, **kwargs)
575+
return [delayed(apply_gaintable, nout=len(vis_graph_list))(v, gt_graph, inverse=True, **kwargs)
576+
for v in vis_graph_list]
577+
else:
578+
return [delayed(selfcal_single, pure=True, nout=1)(point_vis_graph_list[i], **kwargs)
579+
for i, _ in enumerate(vis_graph_list)]
580+

‎arl/visibility/operations.py‎

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@
22
33
"""
44

5-
import numpy
6-
7-
from typing import Union
85
import copy
6+
import logging
7+
from typing import Union
98

109
import astropy.constants as constants
10+
import numpy
1111
from astropy.coordinates import SkyCoord
12+
1213
from arl.data.data_models import BlockVisibility, Visibility, Configuration, QA
1314
from arl.data.polarisation import correlate_polarisation, PolarisationFrame
14-
from arl.util.coordinate_support import xyz_to_uvw, uvw_to_xyz, skycoord_to_lmn, simulate_point
1515
from arl.imaging.params import get_frequency_map
16-
17-
import logging
16+
from arl.util.coordinate_support import xyz_to_uvw, uvw_to_xyz, skycoord_to_lmn, simulate_point
1817

1918
log = logging.getLogger(__name__)
2019

@@ -26,7 +25,7 @@ def vis_summary(vis: Union[Visibility, BlockVisibility]):
2625
return "%d rows, %.3f GB" % (vis.nvis, vis.size())
2726

2827

29-
def append_visibility(vis: Union[Visibility, BlockVisibility], othervis: Union[Visibility, BlockVisibility])\
28+
def append_visibility(vis: Union[Visibility, BlockVisibility], othervis: Union[Visibility, BlockVisibility]) \
3029
-> Union[Visibility, BlockVisibility]:
3130
"""Append othervis to vis
3231
@@ -136,7 +135,7 @@ def create_blockvisibility(config: Configuration,
136135
frequency: numpy.array,
137136
phasecentre: SkyCoord,
138137
weight: float,
139-
polarisation_frame: PolarisationFrame=None,
138+
polarisation_frame: PolarisationFrame = None,
140139
integration_time=1.0,
141140
channel_bandwidth=1e6) -> BlockVisibility:
142141
""" Create a BlockVisibility from Configuration, hour angles, and direction of source
@@ -208,7 +207,7 @@ def create_visibility_from_rows(vis: Union[Visibility, BlockVisibility], rows: n
208207
"""
209208

210209
if type(vis) is Visibility:
211-
210+
212211
if makecopy:
213212
newvis = copy_visibility(vis)
214213
newvis.data = copy.deepcopy(vis.data[rows])
@@ -217,7 +216,7 @@ def create_visibility_from_rows(vis: Union[Visibility, BlockVisibility], rows: n
217216
vis.data = copy.deepcopy(vis.data[rows])
218217
return vis
219218
else:
220-
219+
221220
if makecopy:
222221
newvis = copy_visibility(vis)
223222
newvis.data = copy.deepcopy(vis.data[rows])
@@ -290,7 +289,7 @@ def sum_visibility(vis: Visibility, direction: SkyCoord) -> numpy.array:
290289
# TODO: Convert to Visibility or remove?
291290

292291
svis = copy_visibility(vis)
293-
292+
294293
l, m, n = skycoord_to_lmn(direction, svis.phasecentre)
295294
phasor = numpy.conjugate(simulate_point(svis.uvw, l, m))
296295

@@ -316,7 +315,7 @@ def sum_visibility(vis: Visibility, direction: SkyCoord) -> numpy.array:
316315
return flux, weight
317316

318317

319-
def qa_visibility(vis:Union[Visibility, BlockVisibility], context=None) -> QA:
318+
def qa_visibility(vis: Union[Visibility, BlockVisibility], context=None) -> QA:
320319
"""Assess the quality of Visibility
321320
322321
:param context:
@@ -349,7 +348,7 @@ def remove_continuum_blockvisibility(vis: BlockVisibility, degree=1, mask=None,
349348
assert numpy.sum(mask) > 2 * degree, "Insufficient channels for fit"
350349

351350
nchan = len(vis.frequency)
352-
x = (vis.frequency - vis.frequency[nchan//2])/(vis.frequency[0] - vis.frequency[nchan//2])
351+
x = (vis.frequency - vis.frequency[nchan // 2]) / (vis.frequency[0] - vis.frequency[nchan // 2])
353352
for row in range(vis.nvis):
354353
for ant2 in range(vis.nants):
355354
for ant1 in range(vis.nants):
@@ -410,3 +409,33 @@ def divide_visibility(vis: BlockVisibility, modelvis: BlockVisibility):
410409
weight=xwt)
411410
return pointsource_vis
412411

412+
413+
def integrate_visibility_by_channel(vis: BlockVisibility, **kwargs) -> BlockVisibility:
414+
""" Integrate visibility across channels, returning new visibility
415+
416+
:param vis:
417+
:param kwargs:
418+
:return: BlockVisibility
419+
"""
420+
421+
vis_shape = list(vis.vis.shape)
422+
ntimes, nants, _, nchan, npol = vis_shape
423+
vis_shape[-2] = 1
424+
newvis = BlockVisibility(data=None,
425+
frequency=numpy.ones([1]) * numpy.average(vis.frequency),
426+
channel_bandwidth=numpy.ones([1]) * numpy.sum(vis.channel_bandwidth),
427+
phasecentre=vis.phasecentre,
428+
configuration=vis.configuration,
429+
uvw=vis.uvw,
430+
time=vis.time,
431+
vis=numpy.zeros(vis_shape, dtype='complex'),
432+
weight=numpy.ones(vis_shape, dtype='float'),
433+
integration_time=vis.integration_time,
434+
polarisation_frame=vis.polarisation_frame)
435+
436+
newvis.data['vis'][..., 0, :] = numpy.sum(vis.data['vis'] * vis.data['weight'], axis=-2)
437+
newvis.data['weight'][..., 0, :] = numpy.sum(vis.data['weight'], axis=-2)
438+
mask = newvis.data['weight'] > 0.0
439+
newvis.data['vis'][mask] = newvis.data['vis'][mask] / newvis.data['weight'][mask]
440+
441+
return newvis

‎docs/ARL_quickstart.rst‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,12 @@ See the jupyter note books below:
3939
arl/imaging-fits
4040
arl/imaging-wterm
4141
arl/imaging-mfs
42+
arl/imaging-dask
43+
arl/imaging-spectral
44+
arl/calibration-pipelines
4245
arl/rcal
4346
arl/peeling
4447
arl/simple-dask
45-
arl/imaging-dask
46-
arl/imaging-spectral
4748

4849
In addition, there are other notebooks in examples/arl that are not built as part of this documentation. See e.g.
4950
examples/arl/imaging-low.ipynb, examples/arl/imaging-coalesce.ipynb

‎examples/arl/Makefile‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11

22
NBs = imaging.ipynb imaging-wterm.ipynb imaging-fits.ipynb imaging-mfs.ipynb peeling.ipynb rcal.ipynb peeling.ipynb \
3-
imaging-dask.ipynb imaging-spectral.ipynb simple-dask.ipynb
3+
imaging-dask.ipynb imaging-spectral.ipynb simple-dask.ipynb calibration-pipelines.ipynb
44
include ../jupyter.mk

0 commit comments

Comments
 (0)