33# Synthesis imaging functions
44#
55
6+ import functools
67import numpy
78import pylru
9+ import copy
810from astropy import constants as const
911from astropy import units as units
1012from astropy import wcs
1113from astropy .coordinates import CartesianRepresentation
1214
1315from crocodile .simulate import simulate_point , skycoord_to_lmn
14- from crocodile .synthesis import wcacheimg , wcachefwd , wkernaf , doimg , dopredict
16+ from crocodile .synthesis import w_cache_imaging , w_cache_predict , w_kernel , w_conj_kernel_fn , do_imaging , do_predict
1517
1618from arl .data_models import *
1719from arl .image_operations import create_image_from_array
@@ -62,7 +64,7 @@ def create_wcs_from_visibility(vis: Visibility, params={}):
6264 # Beware of python indexing order! wcs and the array have opposite ordering
6365 shape = [len (vis .frequency ), npol , npixel , npixel ]
6466 w = wcs .WCS (naxis = 4 )
65- w .wcs .cdelt = [- cellsize * 180.0 / numpy .pi , cellsize * 180.0 / numpy .pi , 1.0 , channelwidth .value ]
67+ w .wcs .cdelt = [cellsize * 180.0 / numpy .pi , cellsize * 180.0 / numpy .pi , 1.0 , channelwidth .value ]
6668 w .wcs .crpix = [npixel // 2 + 1 , npixel // 2 + 1 , 1.0 , 1.0 ]
6769 w .wcs .ctype = ["RA---SIN" , "DEC--SIN" , 'STOKES' , 'FREQ' ]
6870 w .wcs .crval = [phasecentre .ra .value , phasecentre .dec .value , 1.0 , reffrequency .value ]
@@ -74,6 +76,20 @@ def create_wcs_from_visibility(vis: Visibility, params={}):
7476 return shape , reffrequency , cellsize , w , imagecentre
7577
7678
79+ def w_cache_size (vis :Visibility , wstep , frequency = None ):
80+ """
81+ Determine optimal w-kernel cache size for de/gridding the
82+ given visibilities.
83+ """
84+
85+ if frequency is None :
86+ frequency = max (vis .frequency )
87+
88+ wmax = numpy .max (numpy .abs (vis .w ))
89+ wmax *= float (frequency ) / const .c .value
90+ return int (numpy .ceil (wmax / wstep ))
91+
92+
7793def invert_visibility (vis : Visibility , params = {}):
7894 """Invert to make dirty Image and PSF
7995
@@ -98,11 +114,13 @@ def invert_visibility(vis: Visibility, params={}):
98114 log .debug ("invert_visibility: Gridding by w projection" )
99115
100116 wstep = get_parameter (params , "wstep" , 10000.0 )
101-
102- wcachesize = int (numpy .ceil (numpy .abs (vis .data ['uvw' ][:, 2 ]).max () * reffrequency .value / (const .c .value * wstep )))
117+ wcachesize = w_cache_size (vis , wstep )
103118 log .debug ("invert_visibility: Making w-kernel cache of %d kernels" % wcachesize )
104- wcache = pylru .FunctionCacheManager (lambda iw : wkernaf (N = 256 , theta = theta , w = iw * wstep , s = 15 , Qpx = 4 ), 10000 )
105- imgfn = lambda * x : wcacheimg (* x , wstep = wstep , wcache = wcache )
119+
120+ cache_fn = w_conj_kernel_fn (pylru .FunctionCacheManager (w_kernel , wcachesize ))
121+ imgfn = functools .partial (w_cache_imaging ,
122+ wstep = wstep , kernel_cache = cache_fn ,
123+ NpixFF = 256 , NpixKern = 15 , Qpx = 4 )
106124 else :
107125 raise NotImplementedError ("gridding algorithm %s not supported" % gridding_algorithm )
108126
@@ -123,9 +141,8 @@ def invert_visibility(vis: Visibility, params={}):
123141 for pol in range (npol ):
124142 log .debug ('invert_visibility: Inverting channel %d, polarisation %d' % (channel , pol ))
125143 d [channel , pol , :, :], p [channel , 0 , :, :], pmax = \
126- doimg (theta , 1.0 / cellsize , vis .data ['uvw' ] *
127- (vis .frequency [channel ] / const .c ).value ,
128- vis .data ['vis' ][:, channel , pol ], imgfn = imgfn )
144+ do_imaging (theta , 1.0 / cellsize , vis .uvw_lambda (channel ),
145+ vis .vis [:, channel , pol ], imgfn = imgfn )
129146 assert pmax > 0.0 , ("No data gridded for channel %d" % channel )
130147 else :
131148 raise NotImplementedError ("mode %s not supported" % spectral_mode )
@@ -138,9 +155,8 @@ def invert_visibility(vis: Visibility, params={}):
138155
139156 return dirty , psf , pmax
140157
141-
142158def predict_visibility (vis : Visibility , sm : SkyModel , params = {}) -> Visibility :
143- """Predict the visibility (in place) from a SkyModel including both components and images
159+ """Predict the visibility from a SkyModel including both components and images
144160
145161 :param vis:
146162 :type Visibility: Visibility to be processed
@@ -150,8 +166,10 @@ def predict_visibility(vis: Visibility, sm: SkyModel, params={}) -> Visibility:
150166 """
151167 shape , reffrequency , cellsize , w , imagecentre = create_wcs_from_visibility (vis , params = params )
152168
153- vshape = vis .data ['vis' ].shape
154- vis .data ['vis' ] = numpy .zeros (vshape )
169+ # Create copy of visibilities
170+ vis = copy .copy (vis )
171+ vis .data = copy .copy (vis .data )
172+ vis .data ['vis' ] = numpy .zeros (vis .vis .shape )
155173
156174 spectral_mode = get_parameter (params , 'spectral_mode' , 'channel' )
157175 log .debug ('predict_visibility: spectral mode is %s' % spectral_mode )
@@ -160,75 +178,59 @@ def predict_visibility(vis: Visibility, sm: SkyModel, params={}) -> Visibility:
160178 log .debug ("predict_visibility: Predicting Visibility from sky model images" )
161179
162180 for im in sm .images :
163- wimage = im .wcs
164-
165- ishape = sm .images [0 ].data .shape
166- nchan = ishape [0 ]
167- npol = ishape [1 ]
168- npixel = ishape [3 ]
169- vshape = vis .data ['vis' ].shape
170-
171- assert ishape [0 ] == vshape [1 ], "Image and visibility have different number of polarisations: %d %d" % (
172- ishape [1 ], vshape [2 ])
173- assert ishape [0 ] == len (vis .frequency ), "Image and visibility have different number of channels %d %d" % \
174- (ishape [0 ], len (vis .frequency ))
175- cellsize = abs (wimage .wcs .cdelt [0 ]) * numpy .pi / 180.0
176- theta = npixel * cellsize
181+ assert_same_chan_pol (vis , im )
182+
183+ # Determine image size
184+ cellsize = abs (im .wcs .wcs .cdelt [0 ]) * numpy .pi / 180.0
185+ theta = im .npixel * cellsize
177186 log .debug ("predict_visibility: Image cellsize %f radians" % cellsize )
178187 log .debug ("predict_visibility: Field of view %f radians" % theta )
179188 assert (theta / numpy .sqrt (2 ) < 1.0 ), "Field of view larger than celestial sphere"
180189
190+ # Parameterise imaging
181191 wstep = get_parameter (params , "wstep" , 10000.0 )
182- wcachesize = int (numpy .ceil (numpy .abs (vis .data ['uvw' ][:, 2 ]).max () * reffrequency .value / const .c .value /
183- wstep ))
192+ wcachesize = w_cache_size (vis , wstep )
184193 log .debug ("predict_visibility: Making w-kernel cache of %d kernels" % wcachesize )
185- wcache = pylru .FunctionCacheManager (lambda iw : wkernaf (N = 256 , theta = theta , w = iw * wstep , s = 15 , Qpx = 4 ),
186- 10000 )
187- predfn = lambda * x : wcachefwd (* x , wstep = wstep , wcache = wcache )
194+
195+ cache_fn = w_conj_kernel_fn (pylru .FunctionCacheManager (w_kernel , wcachesize ))
196+ predfn = functools .partial (w_cache_predict ,
197+ wstep = wstep , kernel_cache = cache_fn ,
198+ NpixFF = 256 , NpixKern = 15 , Qpx = 4 )
188199
189200 spectral_mode = get_parameter (params , 'spectral_mode' , 'channel' )
190201 log .debug ('predict_visibility: spectral mode is %s' % spectral_mode )
191202
192203 if spectral_mode == 'channel' :
193- for channel in range (nchan ):
194- uvw = vis .data [ 'uvw' ] * ( vis . frequency [ channel ] / const . c ). value
195- for pol in range (npol ):
204+ for channel in range (im . nchan ):
205+ uvw = vis .uvw_lambda ( channel )
206+ for pol in range (im . npol ):
196207 log .debug ('predict_visibility: Predicting from image channel %d, polarisation %d' % (
197208 channel , pol ))
198- puvw , dv = dopredict ( theta , 1.0 / cellsize , uvw , sm .images [0 ].data [channel , pol , :, :],
199- predfn = predfn )
200- vis .data [ ' vis' ] [:, channel , pol ] = vis . data [ 'vis' ][:, channel , pol ] + dv
209+ img = sm .images [0 ].data [channel , pol , :, :]
210+ dv = do_predict ( theta , 1.0 / cellsize , uvw , img , predfn )
211+ vis .vis [:, channel , pol ] += dv
201212 else :
202213 raise NotImplementedError ("mode %s not supported" % spectral_mode )
203214
204- log .debug ("predict_visibility: Finished predicting Visibility from sky model images" )
215+ log .debug ("predict_visibility: Finished predicting Visibility from sky model images" )
205216
206217 if len (sm .components ):
207218 log .debug ("predict_visibility: Predicting Visibility from sky model components" )
208219
209- for icomp in range (len (sm .components )):
210- comp = sm .components [icomp ]
211- cshape = comp .flux .shape
212- assert len (cshape ) == 2 , "Flux should be two dimensional (pol, freq)"
213- nchan = cshape [0 ]
214- npol = cshape [1 ]
215-
216- vshape = vis .data ['vis' ].shape
217- log .debug ("predict_visibility: visibility shape = %s" % str (vis .data ['vis' ].shape ))
218-
219- assert vshape [1 ] == nchan , "Component %d and visibility %d have different number of channels" % \
220- (cshape [0 ], len (vis .frequency ))
221- assert vshape [2 ] == npol , "Component %d and visibility %d have different number of polarisations" % (
222- npol , vshape [2 ])
223- # dc = comp.direction.represent_as(CartesianRepresentation)
220+ for icomp , comp in enumerate (sm .components ):
221+
222+ log .debug ("predict_visibility: visibility shape = %s" % str (vis .vis .shape ))
223+ assert_same_chan_pol (vis , comp )
224+
224225 l ,m ,n = skycoord_to_lmn (comp .direction , vis .phasecentre )
225226 log .debug ('fourier_transforms.predict_visibility: Cartesian representation of component %d = (%f, %f, %f)'
226227 % (icomp , l ,m ,n ))
228+
227229 if spectral_mode == 'channel' :
228- for channel in range (nchan ):
230+ for channel in range (comp . nchan ):
229231 uvw = vis .uvw_lambda (channel )
230232 phasor = simulate_point (uvw , l , m )
231- for pol in range (npol ):
233+ for pol in range (comp . npol ):
232234 log .debug (
233235 'predict_visibility: Predicting from component %d channel %d, polarisation %d' % (
234236 icomp , channel ,
0 commit comments