Coverage for PyFHD/gridding/visibility_grid.py: 92%
250 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-01 10:58 +0800
« prev ^ index » next coverage.py v7.9.1, created at 2025-07-01 10:58 +0800
1import numpy as np
2from numpy.typing import NDArray
3from PyFHD.gridding.gridding_utils import (
4 interpolate_kernel,
5 baseline_grid_locations,
6 grid_beam_per_baseline,
7 conjugate_mirror,
8)
9from PyFHD.pyfhd_tools.pyfhd_utils import weight_invert, rebin, l_m_n, idl_argunique
10from logging import Logger
11import h5py
14def visibility_grid(
15 visibility: NDArray[np.complex128],
16 vis_weights: NDArray[np.float64],
17 obs: dict,
18 psf: dict | h5py.File,
19 params: dict,
20 polarization: int,
21 pyfhd_config: dict,
22 logger: Logger,
23 uniform_flag: bool = False,
24 no_conjugate: bool = False,
25 model: NDArray[np.complex128] | None = None,
26 fi_use: NDArray[np.integer] | None = None,
27 bi_use: NDArray[np.integer] | None = None,
28 verbose_logging=True,
29) -> dict:
30 """
31 Put visibilities on a discrete, hyperresolved 2D plane in {u,v}-space with the Fourier-transform of the
32 beam sensitivity as the kernel (or spreading function). This can done per frequency to create 3D {u,v,f}
33 cubes that can generate power spectrum statistics, or this can be done once for all frequencies to create
34 a single 2D {u,v} plane for continuum images. These {u,v} planes are the slant-orthographic projection of
35 the sky when Fourier transformed.
37 Gridding is done for calibrated data visibilities, (optionally) simulated model visibilities, weights, and
38 variances. Weights and variances are crucial for propogating uncertainty estimates in the power spectrum
39 space and for properly weighted images. The model is crucial for subtraction.
41 The kernel is a extremely hyperresolved look-up table, which is (optionally) interpolated even further.
42 Since the {u,v} pixels are discrete and the baseline locations are not, the kernel will populate the pixels
43 in a unique way for each individual baseline. This code is optimized to provide the best estimate for each
44 baseline whilst maintaining speed.
46 Parameters
47 ----------
48 visibility : NDArray[np.complex128]
49 Calibrated and flagged data visibilities
50 vis_weights : NDArray[np.float64]
51 Weights (flags) of the visibilities
52 obs : dict
53 Observation metadata dictionary
54 psf : dict | h5py.File
55 Beam metadata dictionary
56 params : dict
57 Visibility metadata dictionary
58 polarization : int
59 Index of the current polarization
60 pyfhd_config : dict
61 PyFHD's configuration dictionary containing all the options for a run
62 logger : Logger
63 PyFHD's logger
64 uniform_flag : bool, optional
65 Grid a number count for contributing baselines per pixel, by default False
66 no_conjugate : bool, optional
67 Do not perform the conjugate mirror to fill half of the {u,v} plane, by default False
68 model : NDArray[np.complex128] | None, optional
69 Simulated model visibilites, by default None
70 fi_use : NDArray[np.integer] | None, optional
71 Frequency index array for gridding, i.e. gridding all frequencies for continuum images, by default None
72 bi_use : NDArray[np.integer] | None, optional
73 Baseline index array for gridding, i.e even vs odd time stamps, by default None
74 verbose_logging : bool, optional
75 If True, will log the gridding process, by default True
76 Set to False if you're doing gridding per frequency (such as when creating HEALPIX files).
78 Returns
79 -------
80 gridding_dict : dict
81 A dictionary with all the gridded {u,v} planes, updated observation metadata dic, and the number of
82 visibilties that where gridded.
84 Raises
85 ------
86 ValueError
87 Raised in the case the model provided was not a NumPy Array when a model is not None
88 """
90 # Get information from the data structures
91 dimension = int(obs["dimension"])
92 elements = int(obs["elements"])
93 interp_flag = pyfhd_config["interpolate_kernel"]
94 n_vis_arr = obs["nf_vis"]
95 if fi_use is None:
96 fi_use = np.nonzero(obs["baseline_info"]["freq_use"])[0]
97 n_f_use = fi_use.size
98 freq_bin_i = obs["baseline_info"]["fbin_i"]
99 freq_bin_i = freq_bin_i[fi_use]
101 # For each unflagged baseline, get the minimum contributing pixel number for gridding
102 # and the 2D derivatives for bilinear interpolation
103 baselines_dict = baseline_grid_locations(
104 obs,
105 psf,
106 params,
107 vis_weights,
108 logger,
109 bi_use=bi_use,
110 fi_use=fi_use,
111 mask_mirror_indices=pyfhd_config["mask_mirror_indices"],
112 interp_flag=interp_flag,
113 )
114 # Extract data from the returned dictionary
115 bin_n = baselines_dict["bin_n"]
116 bin_i = baselines_dict["bin_i"]
117 n_bin_use = baselines_dict["n_bin_use"]
118 ri = baselines_dict["ri"]
119 xmin = baselines_dict["xmin"]
120 ymin = baselines_dict["ymin"]
121 x_offset = baselines_dict["x_offset"]
122 y_offset = baselines_dict["y_offset"]
123 if bi_use is None:
124 bi_use = baselines_dict["bi_use"]
125 if interp_flag:
126 dx0dy0_arr = baselines_dict["dx0dy0_arr"]
127 dx0dy1_arr = baselines_dict["dx0dy1_arr"]
128 dx1dy0_arr = baselines_dict["dx1dy0_arr"]
129 dx1dy1_arr = baselines_dict["dx1dy1_arr"]
131 # Instead of checking the visibilitity pointer we just take the vis_inds_use from visibility
132 rows, cols = np.meshgrid(fi_use, bi_use)
133 vis_arr_use = visibility[rows, cols].T
134 # Model_flag has been removed in favor of just the model taking advantage that the model default is None
135 # If it has been specified at all with anything other than None or False, then it should be a numpy array
136 # if it isn't exit
137 if model is not None:
138 if isinstance(model, np.ndarray): 138 ↛ 142line 138 didn't jump to line 142 because the condition on line 138 was always true
139 model_use = model[rows, cols].T
140 model_return = np.zeros((elements, dimension), dtype=np.complex128)
141 else:
142 raise ValueError(
143 "Your model must be a numpy array when used as an argument"
144 )
146 # Now with the information we need, retrieve more data from the structures
147 frequency_array = obs["baseline_info"]["freq"]
148 frequency_array = frequency_array[fi_use]
149 psf_dim = psf["dim"]
150 psf_resolution = psf["resolution"]
151 group_arr = psf["id"]
152 if isinstance(psf, h5py.File):
153 psf_dim = psf_dim[0]
154 psf_resolution = psf_resolution[0]
155 group_arr = group_arr[:]
156 n_baselines = obs["n_baselines"]
157 n_samples = obs["n_time"]
158 # New group_arr code that is consistent with the FHD version
159 # We go upto n_baselines in the case we have less baselines in the
160 # observation than the transferred in beam. In most cases we're only
161 # using one beam so this is fine, you might have issues if you're using
162 # many beams across the baselines with this code. It passes the tests where
163 # beams have been done per baseline so if you get an error, sorry!
164 group_arr_baselines = np.min([n_baselines, group_arr.shape[-1]])
165 group_arr = group_arr[polarization, freq_bin_i, :group_arr_baselines]
166 # REBIN in IDL when expanding dimensions repeats 2D arrays after expanding
167 group_arr = np.expand_dims(rebin(group_arr, (n_f_use, group_arr_baselines)), axis=0)
168 group_arr = np.repeat(group_arr, n_samples, axis=0)
169 group_arr = np.reshape(group_arr, (n_f_use, n_samples * group_arr_baselines))
170 n_freq_use = frequency_array.size
171 psf_dim2 = 2 * psf_dim
172 psf_dim3 = psf_dim**2
173 bi_use_reduced = bi_use % n_baselines
175 # Flags have been defined in the function definition
176 # Instead of reading the flags and then setting them.
178 if pyfhd_config["beam_per_baseline"]:
179 # Initialization for gridding operation via a low-res beam kernel, calculated per
180 # baseline using offsets from image-space delays
181 uu = params["uu"][bi_use]
182 vv = params["vv"][bi_use]
183 ww = params["ww"][bi_use]
184 x = (np.arange(dimension) - dimension / 2) * obs["kpix"]
185 y = x.copy()
186 psf_intermediate_res = np.min(
187 [np.ceil(np.sqrt(psf_resolution) / 2) * 2, psf_resolution]
188 )
189 psf_image_dim = psf["image_info"]["psf_image_dim"]
190 if isinstance(psf_image_dim, h5py.Dataset): 190 ↛ 191line 190 didn't jump to line 191 because the condition on line 190 was never true
191 psf_image_dim = psf_image_dim[0]
192 image_bot = int(-(psf_dim / 2) * psf_intermediate_res + psf_image_dim / 2)
193 image_top = int(
194 (psf_dim * psf_resolution - 1)
195 - (psf_dim / 2) * psf_intermediate_res
196 + psf_image_dim / 2
197 )
198 l_mode, m_mode, n_tracked = l_m_n(obs, psf)
199 n_tracked = np.zeros_like(n_tracked)
201 # Initialize uv-arrays
202 image_uv = np.zeros((elements, dimension), dtype=np.complex128)
203 weights = np.zeros((elements, dimension), dtype=np.complex128)
204 variance = np.zeros((elements, dimension))
205 uniform_filter = np.zeros((elements, dimension))
207 # If the uniform gridding has been activated we need to activate the uniform filter and switch off mapping if it has been activated
208 if pyfhd_config["grid_uniform"]:
209 uniform_flag = True
211 conj_i = np.where(params["vv"][bi_use] > 0)[0]
212 if conj_i.size > 0: 212 ↛ 222line 212 didn't jump to line 222 because the condition on line 212 was always true
213 if pyfhd_config["beam_per_baseline"]:
214 uu[conj_i] = -uu[conj_i]
215 vv[conj_i] = -vv[conj_i]
216 ww[conj_i] = -ww[conj_i]
217 vis_arr_use[:, conj_i] = np.conj(vis_arr_use[:, conj_i])
218 if model is not None:
219 model_use[:, conj_i] = np.conj(model_use[:, conj_i])
221 # Return if all baselines have been flagged
222 if n_bin_use == 0: 222 ↛ 223line 222 didn't jump to line 223 because the condition on line 222 was never true
223 logger.error("All data has been flagged")
224 return {}
226 n_vis = np.sum(bin_n)
227 for fi in range(n_f_use):
228 n_vis_arr[fi_use[fi]] = np.sum(xmin[fi, :] > 0)
229 obs["nf_vis"] = n_vis_arr
231 init_arr = np.zeros([psf_dim2, psf_dim2], dtype=np.complex128)
233 arr_type = init_arr.dtype
234 if pyfhd_config["grid_spectral"]:
235 # Spectral B and Spectral D shouldn't reference each other just in case
236 spectral_A = np.zeros([elements, dimension], dtype=np.complex128)
237 spectral_B = np.zeros([elements, dimension])
238 spectral_D = np.zeros([elements, dimension])
239 if model is not None: 239 ↛ 240line 239 didn't jump to line 240 because the condition on line 239 was never true
240 spectral_model_A = np.zeros([elements, dimension], dtype=np.complex128)
242 frequency_cache: dict[int, np.ndarray] = {}
244 for bi in range(n_bin_use):
245 # Cycle through sets of visibilities which contribute to the same data/model uv-plane pixels, and perform
246 # the gridding operation per set using each visibilities' hyperresolved kernel
248 # Select the indices of the visibilities which contribute to the same data/model uv-plane pixels
249 inds = ri[ri[bin_i[bi]] : ri[bin_i[bi] + 1]]
250 ind0 = inds[0]
252 # Select the pixel offsets of the hyperresolution uv-kernel of the selected visibilities
253 x_off = x_offset.flat[inds]
254 y_off = y_offset.flat[inds]
256 # Since all selected visibilities have the same minimum x,y pixel they contribute to,
257 # reduce the array
258 xmin_use = xmin.flat[ind0]
259 ymin_use = ymin.flat[ind0]
261 # Find the frequency group per index
262 freq_i = inds % n_freq_use
263 fbin = freq_bin_i[freq_i]
265 # Calculate the number of selected visibilities and their baseline index
266 vis_n = bin_n[bin_i[bi]]
267 baseline_inds = bi_use_reduced[((inds / n_f_use) % n_baselines).astype(int)]
269 if interp_flag:
270 # Calculate the interpolated kernel on the uv-grid given the derivatives to baseline locations
271 # and the hyperresolved pre-calculated beam kernel
273 # Select the 2D derivatives to baseline locations
274 dx1dy1 = dx1dy1_arr.flat[inds]
275 dx1dy0 = dx1dy0_arr.flat[inds]
276 dx0dy1 = dx0dy1_arr.flat[inds]
277 dx0dy0 = dx0dy0_arr.flat[inds]
279 # Select the model/data visibility values of the set, each with a weight of 1
280 if model is not None:
281 model_box = model_use.flat[inds]
282 vis_box = vis_arr_use.flat[inds]
283 psf_weight = np.ones(vis_n)
285 box_matrix = np.zeros((vis_n, psf_dim3), dtype=arr_type)
286 for ii in range(vis_n):
287 if fbin[ii] not in frequency_cache:
288 to_interp = psf["beam_ptr"][polarization, fbin[ii]]
289 frequency_cache[fbin[ii]] = to_interp
290 else:
291 to_interp = frequency_cache[fbin[ii]]
292 # For each visibility, calculate the kernel values on the static uv-grid given the
293 # hyperresolved kernel and an interpolation involving the derivatives
294 box_matrix[ii] = interpolate_kernel(
295 to_interp,
296 x_off[ii],
297 y_off[ii],
298 dx0dy0[ii],
299 dx1dy0[ii],
300 dx0dy1[ii],
301 dx1dy1[ii],
302 )
303 else:
304 # Calculate the beam kernel at each baseline location given the hyperresolved pre-calculated
305 # beam kernel
307 # Calculate a unique index for each kernel location and kernel type in order to reduce
308 # operations if there are repeats
309 group_id = group_arr.flat[inds]
310 group_max = np.max(group_id) + 1
311 xyf_i = (
312 x_off + y_off * psf_resolution + fbin * psf_resolution**2
313 ) * group_max + group_id
315 # Calculate the unique number of kernel locations/types
316 xyf_si = xyf_i.argsort(kind="stable")
317 xyf_i = np.sort(xyf_i)
318 xyf_ui = idl_argunique(xyf_i)
319 n_xyf_bin = xyf_ui.size
321 # There might be a better selection criteria to determine which is most efficient
322 if vis_n > 1.1 * n_xyf_bin and not pyfhd_config["beam_per_baseline"]:
323 # If there are any baselines which use the same beam kernel and the same discretized location
324 # given the hyperresolution, then reduce the number of gridding operations to only
325 # non-repeated baselines
326 inds = inds[xyf_si]
327 inds_use = xyf_si[xyf_ui]
328 freq_i = freq_i[inds_use]
330 x_off = x_off[inds_use]
331 y_off = y_off[inds_use]
332 fbin = fbin[inds_use]
333 baseline_inds = baseline_inds[inds_use]
334 if n_xyf_bin > 1:
335 xyf_ui0 = np.insert(xyf_ui[0 : n_xyf_bin - 1] + 1, 0, 0)
336 else:
337 xyf_ui0 = np.array([0])
338 psf_weight = xyf_ui - xyf_ui0 + 1
340 vis_box1 = vis_arr_use.flat[inds]
341 vis_box = vis_box1.flat[xyf_ui]
342 if model is not None:
343 model_box1 = model_use.flat[inds]
344 model_box = model_box1.flat[xyf_ui]
346 # For the baselines which map to the same pixels and use the same beam,
347 # add the underlying data/model pixels such that the gridding operation
348 # only needs to be performed once for the set
349 if xyf_ui0.size == 1:
350 repeat_i = np.array([0])
351 else:
352 repeat_i = np.where(psf_weight > 1)[0]
353 xyf_ui = xyf_ui[repeat_i]
354 xyf_ui0 = xyf_ui0[repeat_i]
355 for rep_ii in range(repeat_i.size):
356 vis_box[repeat_i[rep_ii]] = np.sum(
357 vis_box1[xyf_ui0[rep_ii] : xyf_ui[rep_ii] + 1]
358 )
359 if model is not None:
360 model_box[repeat_i[rep_ii]] = np.sum(
361 model_box1[xyf_ui0[rep_ii] : xyf_ui[rep_ii] + 1]
362 )
363 vis_n = n_xyf_bin
364 else:
365 # If there are not enough baselines which use the same beam kernel and discretized
366 # location to warrent reduction, then perform the gridding operation per baseline
367 if model is not None:
368 model_box = model_use.flat[inds]
369 vis_box = vis_arr_use.flat[inds]
370 psf_weight = np.ones(vis_n)
371 # IDL had integer / integer i.e. 2015 / 336 == 5, used flooring divider instead in python
372 # ALso do take note that each were very close always within 0.01 of their next number.
373 # e.g. 2015 / 336 = 5.99, should ceiling be be used instead?
374 bt_index = inds // n_freq_use
376 box_matrix = np.zeros((vis_n, psf_dim3), dtype=arr_type)
377 if pyfhd_config["beam_per_baseline"]: 377 ↛ 380line 377 didn't jump to line 380 because the condition on line 377 was never true
378 # Make the beams on the fly with corrective phases given the baseline location for each visibility
379 # to the static uv-grid
380 box_matrix = grid_beam_per_baseline(
381 psf,
382 pyfhd_config,
383 logger,
384 uu,
385 vv,
386 ww,
387 l_mode,
388 m_mode,
389 n_tracked,
390 frequency_array,
391 x,
392 y,
393 xmin_use,
394 ymin_use,
395 freq_i,
396 bt_index,
397 polarization,
398 image_bot,
399 image_top,
400 psf_dim3,
401 box_matrix,
402 vis_n,
403 )
404 else:
405 for ii in range(vis_n):
406 if fbin[ii] not in frequency_cache:
407 box_mat = psf["beam_ptr"][polarization, fbin[ii]]
408 frequency_cache[fbin[ii]] = box_mat
409 else:
410 box_mat = frequency_cache[fbin[ii]]
411 # For each visibility, calculate the kernel values on the static uv-grid given the
412 # hyperresolved kernel
413 box_matrix[ii, :] = box_mat[y_off[ii], x_off[ii]]
415 # Calculate the conjugate transpose (dagger) of the uv-pixels that the current beam kernel contributes to
416 box_matrix_dag = np.conj(box_matrix)
418 if pyfhd_config["grid_spectral"]:
419 term_A_box = np.dot(
420 np.transpose(box_matrix_dag), np.transpose((freq_i * vis_box) / n_vis)
421 )
422 term_B_box = np.dot(
423 np.transpose(box_matrix_dag), np.transpose(freq_i / n_vis)
424 )
425 term_D_box = np.dot(
426 np.transpose(box_matrix_dag), np.transpose(freq_i**2 / n_vis)
427 )
428 spectral_A[
429 ymin_use : ymin_use + psf_dim, xmin_use : xmin_use + psf_dim
430 ].flat += term_A_box
431 spectral_B[
432 ymin_use : ymin_use + psf_dim, xmin_use : xmin_use + psf_dim
433 ].flat += term_B_box.real
434 spectral_D[
435 ymin_use : ymin_use + psf_dim, xmin_use : xmin_use + psf_dim
436 ].flat += term_D_box.real
437 # del(term_A_box, term_B_box, term_D_box)
438 if model is not None: 438 ↛ 439line 438 didn't jump to line 439 because the condition on line 438 was never true
439 term_Am_box = np.dot(
440 np.transpose(box_matrix_dag),
441 np.transpose((freq_i * model_box) / n_vis),
442 )
443 spectral_model_A[
444 ymin_use : ymin_use + psf_dim, xmin_use : xmin_use + psf_dim
445 ] += term_Am_box
447 if model is not None:
448 # If model visibilities are being gridded, calculate the product of the model vis and the beam kernel
449 # for all vis which contribute to the same static uv-pixels, and add to the static uv-plane
451 # Ensure model_box is flat, sometimes odd shapes can come in from metadata
452 model_box = model_box.flatten()
453 box_arr = np.dot(
454 np.transpose(box_matrix_dag), np.transpose(model_box / n_vis)
455 )
456 model_return[
457 ymin_use : ymin_use + psf_dim, xmin_use : xmin_use + psf_dim
458 ].flat += box_arr
460 # Calculate the product of the data vis and the beam kernel
461 # for all vis which contribute to the same static uv-pixels, and add to the static uv-plane
463 # Ensure vis_box is flat, sometimes odd shapes can come in from metadata
464 vis_box = vis_box.flatten()
465 box_arr = np.dot(np.transpose(box_matrix_dag), vis_box / n_vis)
466 image_uv[
467 ymin_use : ymin_use + psf_dim, xmin_use : xmin_use + psf_dim
468 ].flat += box_arr
469 del box_arr
471 if pyfhd_config["grid_weights"]: 471 ↛ 481line 471 didn't jump to line 481 because the condition on line 471 was always true
472 # If weight visibilities are being gridded, calculate the product the weight (1 per vis) and the beam kernel
473 # for all vis which contribute to the same static uv-pixels, and add to the static uv-plane
474 wts_box = np.dot(
475 np.transpose(box_matrix_dag), np.transpose(psf_weight / n_vis)
476 )
477 weights[
478 ymin_use : ymin_use + psf_dim, xmin_use : xmin_use + psf_dim
479 ].flat += wts_box
481 if pyfhd_config["grid_variance"]:
482 # If variance visibilities are being gridded, calculate the product the weight (1 per vis) and the square
483 # of the beam kernel for all vis which contribute to the same static uv-pixels, and add to the static uv-plane
484 var_box = np.dot(
485 np.transpose(np.abs(box_matrix_dag) ** 2),
486 np.transpose(psf_weight / n_vis),
487 )
488 variance[
489 ymin_use : ymin_use + psf_dim, xmin_use : xmin_use + psf_dim
490 ].flat += var_box
492 if uniform_flag:
493 uniform_filter[
494 ymin_use : ymin_use + psf_dim, xmin_use : xmin_use + psf_dim
495 ] += bin_n[bin_i[bi]]
497 if verbose_logging and (
498 (n_bin_use <= 10)
499 or (bi in np.arange(n_bin_use // 10, n_bin_use, n_bin_use // 10))
500 ):
501 logger.info(
502 f"Gridding visibilities for baseline {bi} of {n_bin_use} for polarization {obs['pol_names'][polarization]}"
503 )
505 # Free Up Memory
506 del (
507 vis_arr_use,
508 xmin,
509 ymin,
510 ri,
511 inds,
512 x_offset,
513 y_offset,
514 bin_i,
515 bin_n,
516 frequency_cache,
517 )
519 if model is not None:
520 del model_use
522 # Option to use spectral index information to scale the uv-plane
523 if pyfhd_config["grid_spectral"]:
524 spectral_uv = (spectral_A - n_vis * spectral_B * image_uv) * weight_invert(
525 spectral_D - spectral_B**2
526 )
527 if model is not None: 527 ↛ 528line 527 didn't jump to line 528 because the condition on line 527 was never true
528 spectral_model_uv = (
529 spectral_model_A - n_vis * spectral_B * model_return
530 ) * weight_invert(spectral_D - spectral_B**2)
531 if not no_conjugate: 531 ↛ 539line 531 didn't jump to line 539 because the condition on line 531 was always true
532 spectral_uv = (spectral_uv + conjugate_mirror(spectral_uv)) / 2
533 if model is not None: 533 ↛ 534line 533 didn't jump to line 534 because the condition on line 533 was never true
534 spectral_model_uv = (
535 spectral_model_uv + conjugate_mirror(spectral_model_uv)
536 ) / 2
538 # Option to apply a uniform weighted filter to all uv-planes
539 if pyfhd_config["grid_uniform"]:
540 filter_use = weight_invert(uniform_filter, threshold=1)
541 wts_i = np.nonzero(filter_use)
542 if wts_i[0].size > 0: 542 ↛ 545line 542 didn't jump to line 545 because the condition on line 542 was always true
543 filter_use /= np.mean(filter_use[wts_i])
544 else:
545 filter_use /= np.mean(filter_use)
546 image_uv *= weight_invert(filter_use)
547 if pyfhd_config["grid_weights"]: 547 ↛ 549line 547 didn't jump to line 549 because the condition on line 547 was always true
548 weights *= weight_invert(filter_use)
549 if pyfhd_config["grid_variance"]: 549 ↛ 551line 549 didn't jump to line 551 because the condition on line 549 was always true
550 variance *= weight_invert(filter_use)
551 if model is not None: 551 ↛ 552line 551 didn't jump to line 552 because the condition on line 551 was never true
552 model_return *= weight_invert(filter_use)
554 if not no_conjugate:
555 # The uv-plane is its own conjugate mirror about the x-axis, so fill in the rest of the uv-plane
556 # using simple maths instead of extra gridding
557 image_uv = (image_uv + conjugate_mirror(image_uv)) / 2
558 if pyfhd_config["grid_weights"]: 558 ↛ 560line 558 didn't jump to line 560 because the condition on line 558 was always true
559 weights = (weights + conjugate_mirror(weights)) / 2
560 if pyfhd_config["grid_variance"]:
561 variance = (variance + conjugate_mirror(variance)) / 4
562 if model is not None:
563 model_return = (model_return + conjugate_mirror(model_return)) / 2
564 if uniform_flag:
565 uniform_filter = (uniform_filter + conjugate_mirror(uniform_filter)) / 2
567 # Arrange returns into a dictionary
568 gridding_dict = {
569 "image_uv": image_uv,
570 "weights": weights,
571 "variance": variance,
572 "uniform_filter": uniform_filter,
573 "obs": obs,
574 "n_vis": n_vis,
575 }
577 if pyfhd_config["grid_spectral"]:
578 gridding_dict["spectral_uv"] = spectral_uv
580 if model is not None:
581 gridding_dict["model_return"] = model_return
582 if pyfhd_config["grid_spectral"]: 582 ↛ 583line 582 didn't jump to line 583 because the condition on line 582 was never true
583 gridding_dict["spectral_model_uv"] = spectral_model_uv
585 return gridding_dict