Source code for nifty8.library.nft

# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
# Copyright(C) 2019-2021 Max-Planck-Society
# Copyright(C) 2022 Max-Planck-Society, Philipp Arras
#
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik.

import numpy as np
from scipy.constants import speed_of_light

from ..domain_tuple import DomainTuple
from ..domains.rg_space import RGSpace
from ..domains.unstructured_domain import UnstructuredDomain
from ..ducc_dispatch import nthreads
from ..operators.linear_operator import LinearOperator
from ..sugar import makeDomain, makeField


[docs] class Gridder(LinearOperator): """Compute non-uniform 2D FFT with ducc. Parameters ---------- target : Domain, tuple of domains or DomainTuple. Target domain, must be a single two-dimensional RGSpace. uv : np.ndarray Coordinates of the data-points, shape (n, 2). eps : float Requested precision, defaults to 2e-10. """
[docs] def __init__(self, target, uv, eps=2e-10): self._capability = self.TIMES | self.ADJOINT_TIMES self._target = makeDomain(target) for ii in [0, 1]: if target.shape[ii] % 2 != 0: raise ValueError("even number of pixels is required for gridding operation") if (len(self._target) != 1 or not isinstance(self._target[0], RGSpace) or not len(self._target.shape) == 2): raise ValueError("need target with exactly one 2D RGSpace") if uv.ndim != 2: raise ValueError("uv must be a 2D array") if uv.shape[1] != 2: raise ValueError("second dimension of uv must have length 2") self._domain = DomainTuple.make(UnstructuredDomain((uv.shape[0]))) # wasteful hack to adjust to shape required by ducc0.wgridder self._uvw = np.empty((uv.shape[0], 3), dtype=np.float64) self._uvw[:, 0:2] = uv self._uvw[:, 2] = 0. self._eps = float(eps)
[docs] def apply(self, x, mode): from ducc0.wgridder import dirty2ms, ms2dirty self._check_input(x, mode) freq = np.array([speed_of_light]) x = x.val nxdirty, nydirty = self._target[0].shape dstx, dsty = self._target[0].distances if mode == self.TIMES: res = ms2dirty(self._uvw, freq, x.reshape((-1,1)), None, nxdirty, nydirty, dstx, dsty, 0, 0, self._eps, False, nthreads(), 0) else: res = dirty2ms(self._uvw, freq, x, None, dstx, dsty, 0, 0, self._eps, False, nthreads(), 0) res = res.reshape((-1,)) return makeField(self._tgt(mode), res)
[docs] class Nufft(LinearOperator): """Compute non-uniform 1D, 2D and 3D FFTs. Parameters ---------- target : Domain, tuple of domains or DomainTuple. Target domain, must be an RGSpace with one to three dimensions. pos : np.ndarray Coordinates of the data-points, shape (n, ndim). eps: float Requested precision, defaults to 2e-10. """
[docs] def __init__(self, target, pos, eps=2e-10): try: from ducc0.nufft import nu2u, u2nu except ImportError: raise ImportError("ducc0 needs to be installed for nifty.Nufft()") self._capability = self.TIMES | self.ADJOINT_TIMES self._target = makeDomain(target) if not isinstance(self._target[0], RGSpace): raise TypeError("target needs to be an RGSpace") if len(self._target.shape) > 3: raise ValueError("Only 1D, 2D and 3D FFTs are supported") if pos.ndim != 2: raise TypeError(f"pos needs to be 2d array (got shape {pos.shape})") self._domain = DomainTuple.make(UnstructuredDomain((pos.shape[0]))) dst = np.array(self._target[0].distances) pos = (2*np.pi*pos*dst) % (2*np.pi) self._args = dict(nthreads=1, epsilon=float(eps), coord=pos)
[docs] def apply(self, x, mode): from ducc0.nufft import nu2u, u2nu self._check_input(x, mode) if mode == self.TIMES: res = np.empty(self.target.shape, dtype=x.dtype) nu2u(points=x.val, out=res, forward=False, **self._args) res = res.real else: res = u2nu(grid=x.val.astype('complex128'), forward=True, **self._args) #if res.ndim == 0: #res = np.array([res]) return makeField(self._tgt(mode), res)
[docs] def FinuFFT(*args, **kwargs): from warnings import warn warn("This operator has been renamed to Nufft. " "FinuFFT won't be present in the upcoming release.", DeprecationWarning) return Nufft(*args, **kwargs)