Skip to content

Commit e674f4d

Browse files
committed
add ability to make delta functions when gridding
1 parent 792a1f9 commit e674f4d

File tree

2 files changed

+60
-21
lines changed

2 files changed

+60
-21
lines changed

src/py21cmwedge/uvgridder.py

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ def uvw_to_dict(self):
248248
else:
249249
self.uvbins[uv] = [uv]
250250

251-
def uv_weights(self, u, v):
251+
def uv_weights(self, u, v, spatial_function="triangle"):
252252
"""Compute weights for arbitrary baseline on a gridded UV plane.
253253
254254
uv must be in units of pixels.
@@ -259,19 +259,32 @@ def uv_weights(self, u, v):
259259
# weights = np.exp( - abs(uv - grid)/(np.diff(grid)[0]))
260260
_range = np.arange(self.uv_size) - (self.uv_size - 1) / 2.0
261261
_range *= self.uv_delta
262-
x, y = np.meshgrid(_range, _range)
263-
x.shape += (1,)
264-
y.shape += (1,)
265-
x = u - x
266-
y = v - y
267-
dists = np.linalg.norm([x, y], axis=0)
268-
weights = 1.0 - dists / self.wavelength_scale
269-
weights = np.ma.masked_less_equal(weights, 0).filled(0)
270-
weights /= np.sum(weights, axis=(0, 1))
271-
weights = np.transpose(weights, [2, 0, 1])
262+
match spatial_function.casefold():
263+
case "triangle":
264+
x, y = np.meshgrid(_range, _range)
265+
x.shape += (1,)
266+
y.shape += (1,)
267+
x = u - x
268+
y = v - y
269+
dists = np.linalg.norm([x, y], axis=0)
270+
weights = 1.0 - dists / self.wavelength_scale
271+
weights = np.ma.masked_less_equal(weights, 0).filled(0)
272+
weights /= np.sum(weights, axis=(0, 1))
273+
weights = np.transpose(weights, [2, 0, 1])
274+
case "nearest":
275+
u_index = np.argmin(np.abs(u - _range))
276+
v_index = np.argmin(np.abs(v - _range))
277+
print(f"{u_index=:}, {v_index=:}")
278+
weights = np.zeros((1, self.uv_size, self.uv_size), dtype=complex)
279+
weights[0, u_index, v_index] = 1.0
280+
case _:
281+
raise ValueError(
282+
f"Unknown value for 'spatial_function': {spatial_function}"
283+
)
284+
272285
return weights
273286

274-
def __sum_uv__(self, uv_key):
287+
def __sum_uv__(self, uv_key, spatial_function="triangle"):
275288
"""Convert uvbin dictionary to a UV-plane."""
276289
uvbin = self.uvbins[uv_key]
277290
nbls = len(uvbin)
@@ -280,10 +293,10 @@ def __sum_uv__(self, uv_key):
280293
v /= self.wavelength
281294
_beam = np.zeros((self.freqs.size, self.uv_size, self.uv_size), dtype=complex)
282295
# Create interpolation weights based on grid size and sampling
283-
_beam += self.uv_weights(u, v)
296+
_beam += self.uv_weights(u, v, spatial_function=spatial_function)
284297
self.uvf_cube += nbls * _beam
285298

286-
def grid_uvw(self):
299+
def grid_uvw(self, convolve_beam=True, spatial_function="triangle"):
287300
"""Create UV coverage from object data."""
288301
self.uv_size = (
289302
int(np.round(self.bl_len_max / self.wavelength / self.uv_delta).max() * 1.1)
@@ -294,27 +307,40 @@ def grid_uvw(self):
294307
(self.freqs.size, self.uv_size, self.uv_size), dtype=complex
295308
)
296309
for uv_key in self.uvbins.keys():
297-
self.__sum_uv__(uv_key)
310+
self.__sum_uv__(uv_key, spatial_function=spatial_function)
298311
beam_array = self.get_uv_beam()
299312
# if only one beam was given, use that beam for all freqs
300313
if np.shape(beam_array)[0] < self.freqs.size:
301314
beam_array = np.tile(beam_array[0], (self.freqs.size, 1, 1))
302-
for _fq in range(self.freqs.size):
303-
beam = beam_array[_fq]
304-
self.uvf_cube[_fq] = fftconvolve(self.uvf_cube[_fq], beam, mode="same")
305315

306-
def calc_all(self, refresh_all=True):
316+
if convolve_beam:
317+
for _fq in range(self.freqs.size):
318+
beam = beam_array[_fq]
319+
self.uvf_cube[_fq] = fftconvolve(self.uvf_cube[_fq], beam, mode="same")
320+
321+
def calc_all(
322+
self, refresh_all=True, convolve_beam=True, spatial_function="triangle"
323+
):
307324
"""Calculate all necessary info.
308325
309326
Perform All calculations:
310327
Convert uvw_array to dict (uvw_to_dict())
311328
Grid uvw to plane (grid_uvw())
312-
refresh_all : boolean, if true, recalculate the uvbins
329+
330+
Parameters
331+
-----------
332+
refresh_all : boolean,
333+
if true, recalculate the uvbins
334+
convolve_beam: bool
335+
when set to true, perform an FFT convolution with the supplied beam
336+
spatial_function: string
337+
must be one of ["nearest", "triangle"]. Nearest modes performs delta function like assignment into a uv-bin
338+
triangle performs simple distance based weighting of uv-bins based on self.wavelength_scale slope
313339
"""
314340
if refresh_all:
315341
self.uvbins = {}
316342
self.uvf_cube = None
317343
if self.n_obs > 1:
318344
self.simulate_observation()
319345
self.uvw_to_dict()
320-
self.grid_uvw()
346+
self.grid_uvw(convolve_beam=convolve_beam, spatial_function=spatial_function)

tests/test_uvgridder.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,3 +311,16 @@ def test_grid_uv():
311311
test_obj.uvw_to_dict()
312312
test_obj.grid_uvw()
313313
assert isinstance(test_obj.uvf_cube.flatten()[0], complex)
314+
315+
316+
def test_grid_uv_deltas():
317+
"""Test grid_uv sets up complex type."""
318+
test_obj = UVGridder()
319+
test_obj.uv_delta = 0.5
320+
test_obj.set_freqs(150e6)
321+
test_uvw = np.zeros((3, 10)) + np.array([[14.6], [0], [0]])
322+
test_obj.set_uvw_array(test_uvw)
323+
test_obj.uvw_to_dict()
324+
test_obj.grid_uvw(convolve_beam=False, spatial_function="nearest")
325+
326+
np.testing.assert_allclose(test_obj.uvf_cube[0, 33, 18], 10)

0 commit comments

Comments
 (0)