diff --git a/gridder_cxx.h b/gridder_cxx.h index 6ddec362398c2660436af5782efdf328da2a50b6..8c23d529ad6de5b428d665b716cc0d52d4e18487 100644 --- a/gridder_cxx.h +++ b/gridder_cxx.h @@ -31,35 +31,32 @@ #include "pocketfft_hdronly.h" #include <array> -template<typename T, size_t ndim, bool c_contiguous> class mav +namespace gridder { + +using namespace std; + +template<typename T, size_t ndim> class mav { static_assert((ndim>0) && (ndim<3), "only supports 1D and 2D arrays"); private: T *d; - std::array<size_t, ndim> shp; - std::array<ptrdiff_t, ndim> str; + array<size_t, ndim> shp; + array<ptrdiff_t, ndim> str; public: - mav(T *d_, const std::array<size_t,ndim> &shp_, - const std::array<ptrdiff_t,ndim> &str_) - : d(d_), shp(shp_), str(str_) - { static_assert(!c_contiguous, "this type does not accept strides"); } - mav(T *d_, const std::array<size_t,ndim> &shp_) + mav(T *d_, const array<size_t,ndim> &shp_, + const array<ptrdiff_t,ndim> &str_) + : d(d_), shp(shp_), str(str_) {} + mav(T *d_, const array<size_t,ndim> &shp_) : d(d_), shp(shp_) { - static_assert(c_contiguous, "this type requires strides"); str[ndim-1]=1; for (size_t d=2; d<=ndim; ++d) str[ndim-d] = str[ndim-d+1]*shp[ndim-d+1]; } - operator mav<const T, ndim, true>() const - { - static_assert(c_contiguous); - return mav<const T, ndim, true>(d,shp); - } - operator mav<const T, ndim, false>() const - { return mav<const T, ndim, false>(d,shp, str); } + operator mav<const T, ndim>() const + { return mav<const T, ndim>(d,shp,str); } T &operator[](size_t i) { return operator()(i); } const T &operator[](size_t i) const @@ -67,25 +64,25 @@ template<typename T, size_t ndim, bool c_contiguous> class mav T &operator()(size_t i) { static_assert(ndim==1, "ndim must be 1"); - return c_contiguous ? d[i] : d[str[0]*i]; + return d[str[0]*i]; } const T &operator()(size_t i) const { static_assert(ndim==1, "ndim must be 1"); - return c_contiguous ? d[i] : d[str[0]*i]; + return d[str[0]*i]; } T &operator()(size_t i, size_t j) { static_assert(ndim==2, "ndim must be 2"); - return c_contiguous ? d[str[0]*i + j] : d[str[0]*i + str[1]*j]; + return d[str[0]*i + str[1]*j]; } const T &operator()(size_t i, size_t j) const { static_assert(ndim==2, "ndim must be 2"); - return c_contiguous ? d[str[0]*i + j] : d[str[0]*i + str[1]*j]; + return d[str[0]*i + str[1]*j]; } size_t shape(size_t i) const { return shp[i]; } - const std::array<size_t,ndim> &shape() const { return shp; } + const array<size_t,ndim> &shape() const { return shp; } size_t size() const { size_t res=1; @@ -94,20 +91,11 @@ template<typename T, size_t ndim, bool c_contiguous> class mav } size_t stride(size_t i) const { return str[i]; } T *data() - { -// static_assert(c_contiguous, "type is not C contiguous"); - return d; - } + { return d; } const T *data() const - { -// static_assert(c_contiguous, "type is not C contiguous"); - return d; - } + { return d; } }; -template<typename T, size_t ndim> using cmav = mav<T,ndim,true>; -template<typename T, size_t ndim> using smav = mav<T,ndim,false>; - // // basic utilities // @@ -115,11 +103,11 @@ template<typename T, size_t ndim> using smav = mav<T,ndim,false>; void myassert(bool cond, const char *msg) { if (cond) return; - throw std::runtime_error(msg); + throw runtime_error(msg); } template<size_t ndim> void checkShape - (const std::array<size_t, ndim> &shp1, const std::array<size_t, ndim> &shp2) + (const array<size_t, ndim> &shp1, const array<size_t, ndim> &shp2) { for (size_t i=0; i<ndim; ++i) myassert(shp1[i]==shp2[i], "shape mismatch"); @@ -138,27 +126,12 @@ template<typename T> inline T fmodulo (T v1, T v2) static size_t nthreads = 1; -constexpr auto set_nthreads_DS = R"""( -Specifies the number of threads to be used by the module - -Parameters -========== -nthreads: int - the number of threads to be used. Must be >=1. -)"""; void set_nthreads(size_t nthreads_) { myassert(nthreads_>=1, "nthreads must be >= 1"); nthreads = nthreads_; } -constexpr auto get_nthreads_DS = R"""( -Returns the number of threads used by the module - -Returns -======= -int : the number of threads used by the module -)"""; size_t get_nthreads() { return nthreads; } @@ -169,7 +142,7 @@ size_t get_nthreads() static inline double one_minus_x2 (double x) { return (fabs(x)>0.1) ? (1.+x)*(1.-x) : 1.-x*x; } -void legendre_prep(int n, std::vector<double> &x, std::vector<double> &w) +void legendre_prep(int n, vector<double> &x, vector<double> &w) { constexpr double pi = 3.141592653589793238462643383279502884197; constexpr double eps = 3e-14; @@ -229,7 +202,7 @@ void legendre_prep(int n, std::vector<double> &x, std::vector<double> &w) size_t get_supp(double epsilon) { - static const std::vector<double> maxmaperr { 1e8, 0.32, 0.021, 6.2e-4, + static const vector<double> maxmaperr { 1e8, 0.32, 0.021, 6.2e-4, 1.08e-5, 1.25e-7, 8.25e-10, 5.70e-12, 1.22e-13, 2.48e-15, 4.82e-17, 6.74e-19, 5.41e-21, 4.41e-23, 7.88e-25, 3.9e-26 }; @@ -237,70 +210,7 @@ size_t get_supp(double epsilon) for (size_t i=1; i<maxmaperr.size(); ++i) if (epssq>maxmaperr[i]) return i; - throw std::runtime_error("requested epsilon too small - minimum is 2e-13"); - } - -template<typename T> void complex2hartley - (const smav<const std::complex<T>, 2> &grid, smav<T,2> &grid2) - { - myassert(grid.shape()==grid2.shape(), "shape mismatch"); - size_t nu=grid.shape(0), nv=grid.shape(1); - -#pragma omp parallel for num_threads(nthreads) - for (size_t u=0; u<nu; ++u) - { - size_t xu = (u==0) ? 0 : nu-u; - for (size_t v=0; v<nv; ++v) - { - size_t xv = (v==0) ? 0 : nv-v; - grid2(u,v) += T(0.5)*(grid( u, v).real()+grid( u, v).imag()+ - grid(xu,xv).real()-grid(xu,xv).imag()); - } - } - } - -template<typename T> void hartley2complex - (const smav<const T,2> &grid, smav<std::complex<T>,2> &grid2) - { - myassert(grid.shape()==grid2.shape(), "shape mismatch"); - size_t nu=grid.shape(0), nv=grid.shape(1); - -#pragma omp parallel for num_threads(nthreads) - for (size_t u=0; u<nu; ++u) - { - size_t xu = (u==0) ? 0 : nu-u; - for (size_t v=0; v<nv; ++v) - { - size_t xv = (v==0) ? 0 : nv-v; - T v1 = T(0.5)*grid( u, v); - T v2 = T(0.5)*grid(xu,xv); - grid2(u,v) = std::complex<T>(v1+v2, v1-v2); - } - } - } - -template<typename T> void hartley2_2D(const smav<const T,2> &in, smav<T,2> &out) - { - myassert(in.shape()==out.shape(), "shape mismatch"); - size_t nu=in.shape(0), nv=in.shape(1), sz=sizeof(T); - pocketfft::stride_t str{ptrdiff_t(sz*nv), ptrdiff_t(sz)}; - auto d_i = in.data(); - auto ptmp = out.data(); - pocketfft::r2r_separable_hartley({nu, nv}, str, str, {0,1}, d_i, ptmp, T(1), - nthreads); -#pragma omp parallel for num_threads(nthreads) - for(size_t i=1; i<(nu+1)/2; ++i) - for(size_t j=1; j<(nv+1)/2; ++j) - { - T a = ptmp[i*nv+j]; - T b = ptmp[(nu-i)*nv+j]; - T c = ptmp[i*nv+nv-j]; - T d = ptmp[(nu-i)*nv+nv-j]; - ptmp[i*nv+j] = T(0.5)*(a+b+c-d); - ptmp[(nu-i)*nv+j] = T(0.5)*(a+b+d-c); - ptmp[i*nv+nv-j] = T(0.5)*(a+c+d-b); - ptmp[(nu-i)*nv+nv-j] = T(0.5)*(b+c+d-a); - } + throw runtime_error("requested epsilon too small - minimum is 2e-13"); } template<typename T> class EC_Kernel @@ -322,7 +232,7 @@ template<typename T> class EC_Kernel_with_correction: public EC_Kernel<T> protected: static constexpr T pi = T(3.141592653589793238462643383279502884197L); int p; - std::vector<T> x, wgt, psi; + vector<T> x, wgt, psi; using EC_Kernel<T>::supp; public: @@ -347,10 +257,10 @@ template<typename T> class EC_Kernel_with_correction: public EC_Kernel<T> }; /* Compute correction factors for the ES gridding kernel This implementation follows eqs. (3.8) to (3.10) of Barnett et al. 2018 */ -std::vector<double> correction_factors (size_t n, size_t nval, size_t supp) +vector<double> correction_factors (size_t n, size_t nval, size_t supp) { EC_Kernel_with_correction<double> kernel(supp); - std::vector<double> res(nval); + vector<double> res(nval); double xn = 1./n; #pragma omp parallel for schedule(static) num_threads(nthreads) for (size_t k=0; k<nval; ++k) @@ -370,12 +280,12 @@ template<typename T> struct UVW template<typename T> class Baselines { protected: - std::vector<UVW<T>> coord; - std::vector<T> f_over_c; + vector<UVW<T>> coord; + vector<T> f_over_c; size_t nrows, nchan; public: - Baselines(const smav<const T,2> &coord_, const smav<const T,1> &freq) + Baselines(const mav<const T,2> &coord_, const mav<const T,1> &freq) { constexpr double speedOfLight = 299792458.; myassert(coord_.shape(1)==3, "dimension mismatch"); @@ -401,7 +311,7 @@ template<typename T> class Baselines size_t Nrows() const { return nrows; } size_t Nchannels() const { return nchan; } - void effectiveUVW(const smav<const uint32_t,1> &idx, smav<T,2> &res) const + void effectiveUVW(const mav<const uint32_t,1> &idx, mav<T,2> &res) const { size_t nvis = idx.shape(0); myassert(res.shape(0)==nvis, "shape mismatch"); @@ -415,8 +325,8 @@ template<typename T> class Baselines } } - template<typename T2> void ms2vis(const smav<const T2,2> &ms, - const smav<const uint32_t,1> &idx, smav<T2,1> &vis) const + template<typename T2> void ms2vis(const mav<const T2,2> &ms, + const mav<const uint32_t,1> &idx, mav<T2,1> &vis) const { myassert(ms.shape(0)==nrows, "shape mismatch"); myassert(ms.shape(1)==nchan, "shape mismatch"); @@ -432,8 +342,8 @@ template<typename T> class Baselines } } - template<typename T2> void vis2ms(const smav<const T2,1> &vis, - const smav<const uint32_t,1> &idx, smav<T2,2> &ms) const + template<typename T2> void vis2ms(const mav<const T2,1> &vis, + const mav<const uint32_t,1> &idx, mav<T2,2> &ms) const { size_t nvis = vis.shape(0); myassert(idx.shape(0)==nvis, "shape mismatch"); @@ -457,11 +367,10 @@ template<typename T> class GridderConfig double eps, psx, psy; size_t supp, nsafe, nu, nv; T beta; - std::vector<T> cfu, cfv; + vector<T> cfu, cfv; - std::complex<T> wscreen(double x, double y, double w, bool adjoint) const + complex<T> wscreen(double x, double y, double w, bool adjoint) const { - using namespace std; constexpr double pi = 3.141592653589793238462643383279502884197; #if 1 double eps = sqrt(x+y); @@ -482,7 +391,7 @@ template<typename T> class GridderConfig : nx_dirty(nxdirty), ny_dirty(nydirty), eps(epsilon), psx(pixsize_x), psy(pixsize_y), supp(get_supp(epsilon)), nsafe((supp+1)/2), - nu(std::max(2*nsafe,2*nx_dirty)), nv(std::max(2*nsafe,2*ny_dirty)), + nu(max(2*nsafe,2*nx_dirty)), nv(max(2*nsafe,2*ny_dirty)), beta(2.3*supp), cfu(nx_dirty), cfv(ny_dirty) { @@ -514,78 +423,30 @@ template<typename T> class GridderConfig size_t Nsafe() const { return nsafe; } T Beta() const { return beta; } - void grid2dirty(const smav<const T,2> &grid, smav<T,2> &grid2) const - { - checkShape(grid.shape(), {nu,nv}); - std::vector<T> tmpdat(nu*nv); - auto tmp=smav<T,2>(tmpdat.data(),{nu,nv},{nv,1}); - hartley2_2D<T>(grid, tmp); - checkShape(grid2.shape(), {nx_dirty, ny_dirty}); - for (size_t i=0; i<nx_dirty; ++i) - for (size_t j=0; j<ny_dirty; ++j) - { - size_t i2 = nu-nx_dirty/2+i; - if (i2>=nu) i2-=nu; - size_t j2 = nv-ny_dirty/2+j; - if (j2>=nv) j2-=nv; - grid2(i,j) = tmp(i2,j2)*cfu[i]*cfv[j]; - } - } -#if 0 - pyarr_c<T> apply_taper(const pyarr_c<T> &img, bool divide) const - { - checkArray(img, "img", {nx_dirty, ny_dirty}); - auto pin = img.data(); - auto res = makeArray<T>({nx_dirty, ny_dirty}); - auto pout = res.mutable_data(); + void apply_taper(const mav<const T,2> &img, mav<T,2> &img2, bool divide) const { - py::gil_scoped_release release; + checkShape(img.shape(), {nx_dirty, ny_dirty}); + checkShape(img2.shape(), {nx_dirty, ny_dirty}); if (divide) for (size_t i=0; i<nx_dirty; ++i) for (size_t j=0; j<ny_dirty; ++j) - pout[ny_dirty*i + j] = pin[ny_dirty*i + j]/(cfu[i]*cfv[j]); + img2(i,j) = img(i,j)/(cfu[i]*cfv[j]); else for (size_t i=0; i<nx_dirty; ++i) for (size_t j=0; j<ny_dirty; ++j) - pout[ny_dirty*i + j] = pin[ny_dirty*i + j]*cfu[i]*cfv[j]; - } - return res; - } - pyarr_c<complex<T>> grid2dirty_c(const pyarr_c<complex<T>> &grid) const - { - checkArray(grid, "grid", {nu, nv}); - auto tmp = makeArray<complex<T>>({nu, nv}); - auto ptmp = tmp.mutable_data(); - pocketfft::c2c({nu,nv},{grid.strides(0),grid.strides(1)}, - {tmp.strides(0), tmp.strides(1)}, {0,1}, pocketfft::BACKWARD, - grid.data(), tmp.mutable_data(), T(1), nthreads); - auto res = makeArray<complex<T>>({nx_dirty, ny_dirty}); - auto pout = res.mutable_data(); - { - py::gil_scoped_release release; - for (size_t i=0; i<nx_dirty; ++i) - for (size_t j=0; j<ny_dirty; ++j) - { - size_t i2 = nu-nx_dirty/2+i; - if (i2>=nu) i2-=nu; - size_t j2 = nv-ny_dirty/2+j; - if (j2>=nv) j2-=nv; - pout[ny_dirty*i + j] = ptmp[nv*i2+j2]*cfu[i]*cfv[j]; - } - } - return res; + img2(i,j) = img(i,j)*cfu[i]*cfv[j]; } - pyarr_c<T> dirty2grid(const pyarr_c<T> &dirty) const - { - checkArray(dirty, "dirty", {nx_dirty, ny_dirty}); - auto pdirty = dirty.data(); - auto tmp = makeArray<T>({nu, nv}); - auto ptmp = tmp.mutable_data(); + void grid2dirty_c(const mav<const complex<T>,2> &grid, mav<complex<T>,2> &dirty) const { - py::gil_scoped_release release; - for (size_t i=0; i<nu*nv; ++i) - ptmp[i] = 0.; + checkShape(grid.shape(), {nu,nv}); + checkShape(dirty.shape(), {nx_dirty,ny_dirty}); + vector<complex<T>> tmpdat(nu*nv); + auto tmp=mav<complex<T>,2>(tmpdat.data(),{nu,nv},{nv,1}); + constexpr auto sc = sizeof(complex<T>); + pocketfft::c2c({nu,nv},{grid.stride(0)*sc,grid.stride(1)*sc}, + {tmp.stride(0)*sc, tmp.stride(1)*sc}, {0,1}, pocketfft::BACKWARD, + grid.data(), tmp.data(), T(1), nthreads); for (size_t i=0; i<nx_dirty; ++i) for (size_t j=0; j<ny_dirty; ++j) { @@ -593,12 +454,10 @@ template<typename T> class GridderConfig if (i2>=nu) i2-=nu; size_t j2 = nv-ny_dirty/2+j; if (j2>=nv) j2-=nv; - ptmp[nv*i2+j2] = pdirty[ny_dirty*i + j]*cfu[i]*cfv[j]; + dirty(i,j) = tmp(i2,j2)*cfu[i]*cfv[j]; } } - hartley2_2D<T>(tmp, tmp); - return tmp; - } +#if 0 pyarr_c<complex<T>> dirty2grid_c(const pyarr_c<complex<T>> &dirty) const { checkArray(dirty, "dirty", {nx_dirty, ny_dirty}); @@ -677,7 +536,7 @@ template<typename T> class GridderConfig constexpr int logsquare=4; -template<typename T, typename T2=std::complex<T>> class Helper +template<typename T, typename T2=complex<T>> class Helper { private: const GridderConfig<T> &gconf; @@ -689,7 +548,7 @@ template<typename T, typename T2=std::complex<T>> class Helper int iu0, iv0; // start index of the current visibility int bu0, bv0; // start index of the current buffer - std::vector<T2> rbuf, wbuf; + vector<T2> rbuf, wbuf; void dump() const { @@ -731,7 +590,7 @@ template<typename T, typename T2=std::complex<T>> class Helper public: const T2 *p0r; T2 *p0w; - std::vector<T> kernel; + vector<T> kernel; Helper(const GridderConfig<T> &gconf_, const T2 *grid_r_, T2 *grid_w_) : gconf(gconf_), nu(gconf.Nu()), nv(gconf.Nv()), nsafe(gconf.Nsafe()), @@ -775,4 +634,6 @@ template<typename T, typename T2=std::complex<T>> class Helper } }; +} // namespace gridder + #endif diff --git a/nifty_gridder.cc b/nifty_gridder.cc index fccb64f2b3cf19c5aa7082e3072b3e3f7c6af274..5afa07207aaf45cbcccb630dc82d09889f0205d3 100644 --- a/nifty_gridder.cc +++ b/nifty_gridder.cc @@ -25,6 +25,7 @@ #include "gridder_cxx.h" using namespace std; +using namespace gridder; namespace py = pybind11; @@ -39,15 +40,27 @@ auto None = py::none(); // // Start of real gridder functionality // +constexpr auto set_nthreads_DS = R"""( +Specifies the number of threads to be used by the module + +Parameters +========== +nthreads: int + the number of threads to be used. Must be >=1. +)"""; +constexpr auto get_nthreads_DS = R"""( +Returns the number of threads used by the module + +Returns +======= +int : the number of threads used by the module +)"""; template<typename T> using pyarr = py::array_t<T>; -// The "_c" suffix here stands for "C memory order, contiguous" -template<typename T> - using pyarr_c = py::array_t<T, py::array::c_style | py::array::forcecast>; -template<typename T> pyarr_c<T> makeArray(const vector<size_t> &shape) - { return pyarr_c<T>(shape); } +template<typename T> pyarr<T> makeArray(const vector<size_t> &shape) + { return pyarr<T>(shape); } void checkArray(const py::array &arr, const char *aname, const vector<size_t> &shape) @@ -94,7 +107,7 @@ template<typename T> pyarr<T> providePotentialArray(const py::object &in, return tmp_; } -template<typename T> pyarr_c<T> provideCArray(py::object &in, +template<typename T> pyarr<T> provideCArray(py::object &in, const vector<size_t> &shape) { if (in.is_none()) @@ -106,19 +119,12 @@ template<typename T> pyarr_c<T> provideCArray(py::object &in, tmp[i] = T(0); return tmp_; } - auto tmp_ = in.cast<pyarr_c<T>>(); + auto tmp_ = in.cast<pyarr<T>>(); checkArray(tmp_, "temporary", shape); return tmp_; } -template<size_t ndim, typename T> cmav<T,ndim> make_cmav(pyarr_c<T> &in) - { - myassert(ndim==in.ndim(), "dimension mismatch"); - array<size_t,ndim> dims; - for (size_t i=0; i<ndim; ++i) dims[i]=in.shape(i); - return cmav<T, ndim>(in.mutable_data(),dims); - } -template<size_t ndim, typename T> smav<T,ndim> make_smav(pyarr<T> &in) +template<size_t ndim, typename T> mav<T,ndim> make_mav(pyarr<T> &in) { myassert(ndim==in.ndim(), "dimension mismatch"); array<size_t,ndim> dims; @@ -129,9 +135,9 @@ template<size_t ndim, typename T> smav<T,ndim> make_smav(pyarr<T> &in) str[i]=in.strides(i)/sizeof(T); myassert(str[i]*ptrdiff_t(sizeof(T))==in.strides(i), "weird strides"); } - return smav<T, ndim>(in.mutable_data(),dims,str); + return mav<T, ndim>(in.mutable_data(),dims,str); } -template<size_t ndim, typename T> smav<T,ndim> make_smav(pyarr_c<T> &in) +template<size_t ndim, typename T> mav<const T,ndim> make_const_mav(const pyarr<T> &in) { myassert(ndim==in.ndim(), "dimension mismatch"); array<size_t,ndim> dims; @@ -142,77 +148,7 @@ template<size_t ndim, typename T> smav<T,ndim> make_smav(pyarr_c<T> &in) str[i]=in.strides(i)/sizeof(T); myassert(str[i]*ptrdiff_t(sizeof(T))==in.strides(i), "weird strides"); } - return smav<T, ndim>(in.mutable_data(),dims,str); - } -template<size_t ndim, typename T> cmav<const T,ndim> make_const_cmav(const pyarr_c<T> &in) - { - myassert(ndim==in.ndim(), "dimension mismatch"); - array<size_t,ndim> dims; - for (size_t i=0; i<ndim; ++i) dims[i]=in.shape(i); - return cmav<const T, ndim>(in.data(),dims); - } -template<size_t ndim, typename T> smav<const T,ndim> make_const_smav(const pyarr<T> &in) - { - myassert(ndim==in.ndim(), "dimension mismatch"); - array<size_t,ndim> dims; - array<ptrdiff_t,ndim> str; - for (size_t i=0; i<ndim; ++i) - { - dims[i]=in.shape(i); - str[i]=in.strides(i)/sizeof(T); - myassert(str[i]*ptrdiff_t(sizeof(T))==in.strides(i), "weird strides"); - } - return smav<const T, ndim>(in.data(),dims,str); - } -template<size_t ndim, typename T> smav<const T,ndim> make_const_smav(const pyarr_c<T> &in) - { - myassert(ndim==in.ndim(), "dimension mismatch"); - array<size_t,ndim> dims; - array<ptrdiff_t,ndim> str; - for (size_t i=0; i<ndim; ++i) - { - dims[i]=in.shape(i); - str[i]=in.strides(i)/sizeof(T); - myassert(str[i]*ptrdiff_t(sizeof(T))==in.strides(i), "weird strides"); - } - return smav<const T, ndim>(in.data(),dims,str); - } -template<typename T> pyarr_c<T> complex2hartley - (const pyarr_c<complex<T>> &grid_, py::object &grid_in) - { - auto grid = make_const_smav<2>(grid_); - size_t nu=grid.shape(0), nv=grid.shape(1); - - auto res = provideCArray<T>(grid_in, {nu, nv}); - auto grid2 = make_smav<2>(res); - { - py::gil_scoped_release release; - complex2hartley(grid, grid2); - } - return res; - } - -template<typename T> pyarr_c<complex<T>> hartley2complex - (const pyarr_c<T> &grid_) - { - auto grid = make_const_smav<2>(grid_); - size_t nu=grid.shape(0), nv=grid.shape(1); - - auto res = makeArray<complex<T>>({nu, nv}); - auto grid2 = make_smav<2>(res); - { - py::gil_scoped_release release; - hartley2complex(grid, grid2); - } - return res; - } - -template<typename T> void hartley2_2D(const pyarr_c<T> &in, pyarr_c<T> &out) - { - auto grid = make_const_smav<2>(in); - auto grid2 = make_smav<2>(out); - py::gil_scoped_release release; - hartley2_2D(grid, grid2); + return mav<const T, ndim>(in.data(),dims,str); } constexpr auto PyBaselines_DS = R"""( @@ -236,7 +172,7 @@ template<typename T> class PyBaselines: public Baselines<T> public: using Baselines<T>::Baselines; PyBaselines(const pyarr<T> &coord, const pyarr<T> &freq) - : Baselines<T>(make_const_smav<2>(coord), make_const_smav<1>(freq)) + : Baselines<T>(make_const_mav<2>(coord), make_const_mav<1>(freq)) {} using Baselines<T>::effectiveCoord; @@ -260,12 +196,12 @@ template<typename T> class PyBaselines: public Baselines<T> )"""; // using Baselines<T>::effectiveUVW; - pyarr_c<T> effectiveuvw(const pyarr<uint32_t> &idx_) const + pyarr<T> effectiveuvw(const pyarr<uint32_t> &idx_) const { size_t nvis = size_t(idx_.shape(0)); - auto idx=make_const_smav<1>(idx_); + auto idx=make_const_mav<1>(idx_); auto res_=makeArray<T>({nvis, 3}); - auto res=make_smav<2>(res_); + auto res=make_mav<2>(res_); { py::gil_scoped_release release; Baselines<T>::effectiveUVW(idx,res); @@ -273,14 +209,14 @@ template<typename T> class PyBaselines: public Baselines<T> return res_; } - template<typename T2> pyarr_c<T2> ms2vis(const pyarr<T2> &ms_, - const pyarr_c<uint32_t> &idx_) const + template<typename T2> pyarr<T2> ms2vis(const pyarr<T2> &ms_, + const pyarr<uint32_t> &idx_) const { - auto idx=make_const_smav<1>(idx_); + auto idx=make_const_mav<1>(idx_); size_t nvis = size_t(idx.shape(0)); - auto ms = make_const_smav<2>(ms_); + auto ms = make_const_mav<2>(ms_); auto res=makeArray<T2>({nvis}); - auto vis = make_smav<1>(res); + auto vis = make_mav<1>(res); { py::gil_scoped_release release; Baselines<T>::ms2vis(ms, idx, vis); @@ -305,13 +241,13 @@ template<typename T> class PyBaselines: public Baselines<T> np.array((nrows, nchannels), dtype=np.complex) the measurement set's visibility data (0 where not covered by idx) )"""; - template<typename T2> pyarr_c<T2> vis2ms(const pyarr<T2> &vis_, + template<typename T2> pyarr<T2> vis2ms(const pyarr<T2> &vis_, const pyarr<uint32_t> &idx_, py::object &ms_in) const { - auto vis=make_const_smav<1>(vis_); - auto idx=make_const_smav<1>(idx_); + auto vis=make_const_mav<1>(vis_); + auto idx=make_const_mav<1>(idx_); auto res = provideArray<T2>(ms_in, {nrows, nchan}); - auto ms = make_smav<2>(res); + auto ms = make_mav<2>(res); { py::gil_scoped_release release; Baselines<T>::vis2ms(vis, idx, ms); @@ -320,34 +256,6 @@ template<typename T> class PyBaselines: public Baselines<T> } }; -constexpr auto grid2dirty_DS = R"""( -Converts from UV grid to dirty image (FFT, cropping, correction) - -Parameters -========== -grid: np.array((nu, nv), dtype=np.float64) - gridded UV data - -Returns -======= -nd.array((nxdirty, nydirty), dtype=np.float64) - the dirty image -)"""; - -constexpr auto dirty2grid_DS = R"""( -Converts from a dirty image to a UV grid (correction, padding, FFT) - -Parameters -========== -dirty: nd.array((nxdirty, nydirty), dtype=np.float64) - the dirty image - -Returns -======= -np.array((nu, nv), dtype=np.float64) - gridded UV data -)"""; - constexpr auto apply_taper_DS = R"""( Applies the taper (or its inverse) to an image @@ -432,86 +340,30 @@ template<typename T> class PyGridderConfig: public GridderConfig<T> using GridderConfig<T>::Nsafe; using GridderConfig<T>::Beta; - pyarr_c<T> grid2dirty(const pyarr_c<T> &grid) const + pyarr<T> apply_taper(const pyarr<T> &img, bool divide) const { - checkArray(grid, "grid", {nu, nv}); auto res = makeArray<T>({nx_dirty, ny_dirty}); - auto grid2=make_const_smav<2>(grid); - auto res2=make_smav<2>(res); + auto img2 = make_const_mav<2>(img); + auto res2 = make_mav<2>(res); { py::gil_scoped_release release; - GridderConfig<T>::grid2dirty(grid2,res2); + GridderConfig<T>::apply_taper(img2, res2, divide); } return res; } - pyarr_c<T> apply_taper(const pyarr_c<T> &img, bool divide) const + pyarr<complex<T>> grid2dirty_c(const pyarr<complex<T>> &grid) const { - checkArray(img, "img", {nx_dirty, ny_dirty}); - auto pin = img.data(); - auto res = makeArray<T>({nx_dirty, ny_dirty}); - auto pout = res.mutable_data(); - { - py::gil_scoped_release release; - if (divide) - for (size_t i=0; i<nx_dirty; ++i) - for (size_t j=0; j<ny_dirty; ++j) - pout[ny_dirty*i + j] = pin[ny_dirty*i + j]/(cfu[i]*cfv[j]); - else - for (size_t i=0; i<nx_dirty; ++i) - for (size_t j=0; j<ny_dirty; ++j) - pout[ny_dirty*i + j] = pin[ny_dirty*i + j]*cfu[i]*cfv[j]; - } - return res; - } - pyarr_c<complex<T>> grid2dirty_c(const pyarr_c<complex<T>> &grid) const - { - checkArray(grid, "grid", {nu, nv}); - auto tmp = makeArray<complex<T>>({nu, nv}); - auto ptmp = tmp.mutable_data(); - pocketfft::c2c({nu,nv},{grid.strides(0),grid.strides(1)}, - {tmp.strides(0), tmp.strides(1)}, {0,1}, pocketfft::BACKWARD, - grid.data(), tmp.mutable_data(), T(1), nthreads); auto res = makeArray<complex<T>>({nx_dirty, ny_dirty}); - auto pout = res.mutable_data(); + auto grid2=make_const_mav<2>(grid); + auto res2=make_mav<2>(res); { py::gil_scoped_release release; - for (size_t i=0; i<nx_dirty; ++i) - for (size_t j=0; j<ny_dirty; ++j) - { - size_t i2 = nu-nx_dirty/2+i; - if (i2>=nu) i2-=nu; - size_t j2 = nv-ny_dirty/2+j; - if (j2>=nv) j2-=nv; - pout[ny_dirty*i + j] = ptmp[nv*i2+j2]*cfu[i]*cfv[j]; - } + GridderConfig<T>::grid2dirty_c(grid2,res2); } return res; } - pyarr_c<T> dirty2grid(const pyarr_c<T> &dirty) const - { - checkArray(dirty, "dirty", {nx_dirty, ny_dirty}); - auto pdirty = dirty.data(); - auto tmp = makeArray<T>({nu, nv}); - auto ptmp = tmp.mutable_data(); - { - py::gil_scoped_release release; - for (size_t i=0; i<nu*nv; ++i) - ptmp[i] = 0.; - for (size_t i=0; i<nx_dirty; ++i) - for (size_t j=0; j<ny_dirty; ++j) - { - size_t i2 = nu-nx_dirty/2+i; - if (i2>=nu) i2-=nu; - size_t j2 = nv-ny_dirty/2+j; - if (j2>=nv) j2-=nv; - ptmp[nv*i2+j2] = pdirty[ny_dirty*i + j]*cfu[i]*cfv[j]; - } - } - hartley2_2D<T>(tmp, tmp); - return tmp; - } - pyarr_c<complex<T>> dirty2grid_c(const pyarr_c<complex<T>> &dirty) const + pyarr<complex<T>> dirty2grid_c(const pyarr<complex<T>> &dirty) const { checkArray(dirty, "dirty", {nx_dirty, ny_dirty}); auto pdirty = dirty.data(); @@ -536,7 +388,7 @@ template<typename T> class PyGridderConfig: public GridderConfig<T> } return tmp; } - pyarr_c<complex<T>> apply_wscreen(const pyarr_c<complex<T>> &dirty_, double w, bool adjoint) const + pyarr<complex<T>> apply_wscreen(const pyarr<complex<T>> &dirty_, double w, bool adjoint) const { checkArray(dirty_, "dirty", {nx_dirty, ny_dirty}); auto dirty = dirty_.data(); @@ -600,7 +452,7 @@ Returns np.array((nu,nv), dtype=np.complex128): the gridded visibilities )"""; -template<typename T> pyarr_c<complex<T>> vis2grid_c( +template<typename T> pyarr<complex<T>> vis2grid_c( const PyBaselines<T> &baselines, const PyGridderConfig<T> &gconf, const pyarr<uint32_t> &idx_, const pyarr<complex<T>> &vis_, py::object &grid_in, const py::object &wgt_) @@ -677,10 +529,6 @@ Returns np.array((nu,nv), dtype=np.float64): the gridded visibilities (made real by making use of Hermitian symmetry) )"""; -template<typename T> pyarr_c<T> vis2grid(const PyBaselines<T> &baselines, - const PyGridderConfig<T> &gconf, const pyarr<uint32_t> &idx_, - const pyarr<complex<T>> &vis_, py::object &grid_in, const py::object &wgt_) - { return complex2hartley(vis2grid_c(baselines, gconf, idx_, vis_, None, wgt_), grid_in); } constexpr auto ms2grid_c_DS = R"""( Grids measurement set data onto a UV grid @@ -706,7 +554,7 @@ Returns np.array((nu,nv), dtype=np.complex128): the gridded visibilities )"""; -template<typename T> pyarr_c<complex<T>> ms2grid_c( +template<typename T> pyarr<complex<T>> ms2grid_c( const PyBaselines<T> &baselines, const PyGridderConfig<T> &gconf, const pyarr<uint32_t> &idx_, const pyarr<complex<T>> &ms_, py::object &grid_in, const py::object &wgt_) @@ -764,14 +612,9 @@ template<typename T> pyarr_c<complex<T>> ms2grid_c( return res; } -template<typename T> pyarr_c<T> ms2grid(const PyBaselines<T> &baselines, - const PyGridderConfig<T> &gconf, const pyarr<uint32_t> &idx_, - const pyarr<complex<T>> &ms_, py::object &grid_in, const py::object &wgt_) - { return complex2hartley(ms2grid_c(baselines, gconf, idx_, ms_, None, wgt_), grid_in); } - -template<typename T> pyarr_c<complex<T>> grid2vis_c( +template<typename T> pyarr<complex<T>> grid2vis_c( const PyBaselines<T> &baselines, const PyGridderConfig<T> &gconf, - const pyarr<uint32_t> &idx_, const pyarr_c<complex<T>> &grid_, + const pyarr<uint32_t> &idx_, const pyarr<complex<T>> &grid_, const py::object &wgt_) { size_t nu=gconf.Nu(), nv=gconf.Nv(); @@ -845,14 +688,9 @@ Returns np.array((nvis,), dtype=np.complex) The degridded visibility data )"""; -template<typename T> pyarr_c<complex<T>> grid2vis(const PyBaselines<T> &baselines, - const PyGridderConfig<T> &gconf, const pyarr<uint32_t> &idx_, - const pyarr_c<T> &grid_, const py::object &wgt_) - { return grid2vis_c(baselines, gconf, idx_, hartley2complex(grid_), wgt_); } - -template<typename T> pyarr_c<complex<T>> grid2ms_c(const PyBaselines<T> &baselines, +template<typename T> pyarr<complex<T>> grid2ms_c(const PyBaselines<T> &baselines, const PyGridderConfig<T> &gconf, const pyarr<uint32_t> &idx_, - const pyarr_c<complex<T>> &grid_, py::object &ms_in, const py::object &wgt_) + const pyarr<complex<T>> &grid_, py::object &ms_in, const py::object &wgt_) { auto nrows = baselines.Nrows(); auto nchan = baselines.Nchannels(); @@ -910,14 +748,9 @@ template<typename T> pyarr_c<complex<T>> grid2ms_c(const PyBaselines<T> &baselin return res; } -template<typename T> pyarr_c<complex<T>> grid2ms(const PyBaselines<T> &baselines, - const PyGridderConfig<T> &gconf, const pyarr<uint32_t> &idx_, - const pyarr_c<T> &grid_, py::object &ms_in, const py::object &wgt_) - { return grid2ms_c(baselines, gconf, idx_, hartley2complex(grid_), ms_in, wgt_); } - -template<typename T> pyarr_c<complex<T>> apply_holo( +template<typename T> pyarr<complex<T>> apply_holo( const PyBaselines<T> &baselines, const PyGridderConfig<T> &gconf, - const pyarr<uint32_t> &idx_, const pyarr_c<complex<T>> &grid_, + const pyarr<uint32_t> &idx_, const pyarr<complex<T>> &grid_, const py::object &wgt_) { size_t nu=gconf.Nu(), nv=gconf.Nv(); @@ -984,7 +817,7 @@ template<typename T> pyarr_c<complex<T>> apply_holo( return res; } -template<typename T> pyarr_c<T> get_correlations( +template<typename T> pyarr<T> get_correlations( const PyBaselines<T> &baselines, const PyGridderConfig<T> &gconf, const pyarr<uint32_t> &idx_, int du, int dv, const py::object &wgt_) { @@ -1077,8 +910,8 @@ np.array((nvis,), dtype=np.uint32) the compressed indices for all entries which match the selected criteria and are not flagged. )"""; -template<typename T> pyarr_c<uint32_t> getIndices(const PyBaselines<T> &baselines, - const PyGridderConfig<T> &gconf, const pyarr_c<bool> &flags_, int chbegin, +template<typename T> pyarr<uint32_t> getIndices(const PyBaselines<T> &baselines, + const PyGridderConfig<T> &gconf, const pyarr<bool> &flags_, int chbegin, int chend, T wmin, T wmax) { size_t nrow=baselines.Nrows(), @@ -1134,7 +967,7 @@ template<typename T> pyarr_c<uint32_t> getIndices(const PyBaselines<T> &baseline return res; } -template<typename T> pyarr_c<complex<T>> vis2dirty_wstack(const PyBaselines<T> &baselines, +template<typename T> pyarr<complex<T>> vis2dirty_wstack(const PyBaselines<T> &baselines, const PyGridderConfig<T> &gconf, const pyarr<uint32_t> &idx_, const pyarr<complex<T>> &vis_) { @@ -1258,7 +1091,7 @@ PYBIND11_MODULE(nifty_gridder, m) using namespace pybind11::literals; py::class_<PyBaselines<double>> (m, "Baselines", PyBaselines_DS) - .def(py::init<const pyarr_c<double> &, const pyarr_c<double> &>(), + .def(py::init<const pyarr<double> &, const pyarr<double> &>(), "coord"_a, "freq"_a) .def ("Nrows",&PyBaselines<double>::Nrows) .def ("Nchannels",&PyBaselines<double>::Nchannels) @@ -1280,11 +1113,7 @@ PYBIND11_MODULE(nifty_gridder, m) .def("Supp", &PyGridderConfig<double>::Supp) .def("apply_taper", &PyGridderConfig<double>::apply_taper, apply_taper_DS, "img"_a, "divide"_a=false) - .def("grid2dirty", &PyGridderConfig<double>::grid2dirty, - grid2dirty_DS, "grid"_a) .def("grid2dirty_c", &PyGridderConfig<double>::grid2dirty_c, "grid"_a) - .def("dirty2grid", &PyGridderConfig<double>::dirty2grid, - dirty2grid_DS, "dirty"_a) .def("dirty2grid_c", &PyGridderConfig<double>::dirty2grid_c, "dirty"_a) .def("apply_wscreen", &PyGridderConfig<double>::apply_wscreen, apply_wscreen_DS, "dirty"_a, "w"_a, "adjoint"_a) @@ -1310,14 +1139,6 @@ PYBIND11_MODULE(nifty_gridder, m) m.def("getIndices", getIndices<double>, getIndices_DS, "baselines"_a, "gconf"_a, "flags"_a, "chbegin"_a=-1, "chend"_a=-1, "wmin"_a=-1e30, "wmax"_a=1e30); - m.def("vis2grid",&vis2grid<double>, vis2grid_DS, "baselines"_a, "gconf"_a, - "idx"_a, "vis"_a, "grid_in"_a=None, "wgt"_a=None); - m.def("ms2grid",&ms2grid<double>, "baselines"_a, "gconf"_a, "idx"_a, "ms"_a, - "grid_in"_a=None, "wgt"_a=None); - m.def("grid2vis",&grid2vis<double>, grid2vis_DS, "baselines"_a, "gconf"_a, - "idx"_a, "grid"_a, "wgt"_a=None); - m.def("grid2ms",&grid2ms<double>, "baselines"_a, "gconf"_a, "idx"_a, - "grid"_a, "ms_in"_a=None, "wgt"_a=None); m.def("vis2grid_c",&vis2grid_c<double>, vis2grid_c_DS, "baselines"_a, "gconf"_a, "idx"_a, "vis"_a, "grid_in"_a=None, "wgt"_a=None); m.def("ms2grid_c",&ms2grid_c<double>, ms2grid_c_DS, "baselines"_a, "gconf"_a, diff --git a/test.py b/test.py index 801d363202a31f8714b3021acce097890a6dfcab..bac5fb4f20b576cb5ff708e524462997cc31c5db 100644 --- a/test.py +++ b/test.py @@ -12,7 +12,7 @@ def _init_gridder(nxdirty, nydirty, epsilon, nchan, nrow): nydirty=nydirty, epsilon=epsilon, pixsize_x=pixsize, - pixsize_y=pixsize) + pixsize_y=0.5*pixsize) speedoflight, f0 = 3e8, 1e9 freq = f0 + np.arange(nchan)*(f0/nchan) uvw = (np.random.rand(nrow, 3)-0.5)/(pixsize*f0/speedoflight) @@ -45,11 +45,11 @@ def test_adjointness(nxdirty, nydirty, nrow, nchan, epsilon): bl, gconf, idx = _init_gridder(nxdirty, nydirty, epsilon, nchan, nrow) ms = np.random.rand(nrow, nchan)-0.5 + 1j*(np.random.rand(nrow, nchan)-0.5) vis = bl.ms2vis(ms, idx) - grid = ng.vis2grid(bl, gconf, idx, vis) - dirty = gconf.grid2dirty(grid) + grid = ng.vis2grid_c(bl, gconf, idx, vis) + dirty = gconf.grid2dirty_c(grid) dirty2 = np.random.rand(*dirty.shape)-0.5 - ms2 = bl.vis2ms(ng.grid2vis(bl, gconf, idx, gconf.dirty2grid(dirty2)), idx) - assert_allclose(np.vdot(ms, ms2).real, np.vdot(dirty, dirty2)) + ms2 = bl.vis2ms(ng.grid2vis_c(bl, gconf, idx, gconf.dirty2grid_c(dirty2)), idx) + assert_allclose(np.vdot(ms, ms2).real, np.vdot(dirty.real, dirty2.real)) @pmp("nxdirty", (128,)) @@ -78,38 +78,6 @@ def test_hoisted_grid_allocation(nxdirty, nydirty, nrow, nchan, epsilon): ms = np.random.rand(nrow, nchan)-0.5 + 1j*(np.random.rand(nrow, nchan)-0.5) vis = baselines.ms2vis(ms, idx) - # ----------------------------- - # Test real grid case (ms2grid) - # ----------------------------- - real_grid = np.zeros((gconf.Nu(), gconf.Nv()), dtype=np.float64) - grid = ng.ms2grid(baselines, gconf, idx, ms, grid_in=None) - grid2 = ng.ms2grid(baselines, gconf, idx, ms, grid_in=real_grid) - - assert_array_almost_equal(grid, grid2) - assert grid.dtype == real_grid.dtype == grid2.dtype - assert id(grid2) == id(real_grid) # Same grid object - - real_grid = np.zeros((gconf.Nu(), gconf.Nv()), dtype=np.float64) - grid = ng.ms2grid(baselines, gconf, idx, ms, grid_in=None, wgt=weights) - grid2 = ng.ms2grid(baselines, gconf, idx, ms, grid_in=real_grid, - wgt=weights) - - assert_array_almost_equal(grid, grid2) - assert grid.dtype == real_grid.dtype == grid2.dtype - assert id(grid2) == id(real_grid) # Same grid object - - # ------------------------------ - # Test real grid case (vis2grid) - # ------------------------------ - real_grid = np.zeros((gconf.Nu(), gconf.Nv()), dtype=np.float64) - grid = ng.vis2grid(baselines, gconf, idx, vis, grid_in=None) - grid2 = ng.vis2grid(baselines, gconf, idx, vis, grid_in=real_grid) - - # Almost same result - assert_array_almost_equal(grid, grid2) - assert grid.dtype == real_grid.dtype == grid2.dtype - assert id(grid2) == id(real_grid) # Same grid object - # ---------------------------------- # Test complex grid case (ms2grid_c) # ---------------------------------- @@ -248,7 +216,7 @@ def test_against_dft(nxdirty, nydirty, epsilon, nchan, nrow): ms = np.random.rand(nrow, nchan)-0.5 + 1j*(np.random.rand(nrow, nchan)-0.5) vis = bl.ms2vis(ms, idx) - res0 = conf.grid2dirty(ng.vis2grid(bl, conf, idx, vis)) + res0 = conf.grid2dirty_c(ng.vis2grid_c(bl, conf, idx, vis)) x, y = np.meshgrid(*[-ss/2 + np.arange(ss) for ss in [nxdirty, nydirty]], indexing='ij') @@ -258,7 +226,7 @@ def test_against_dft(nxdirty, nydirty, epsilon, nchan, nrow): uvw = bl.effectiveuvw(idx) for ii in idx: phase = x*uvw[ii, 0] + y*uvw[ii, 1] - res1 += (vis[ii]*np.exp(2j*np.pi*phase)).real + res1 += (vis[ii]*np.exp(2j*np.pi*phase)) assert_(_l2error(res0, res1) < epsilon) @@ -275,7 +243,7 @@ def test_against_wdft(nxdirty, nydirty, nchan, nrow, fov): nydirty=nydirty, epsilon=epsilon, pixsize_x=pixsize, - pixsize_y=pixsize) + pixsize_y=0.5*pixsize) speedoflight, f0 = 3e8, 1e9 freq = f0 + np.arange(nchan)*(f0/nchan) uvw = (np.random.rand(nrow, 3)-0.5)/(pixsize*f0/speedoflight) @@ -314,4 +282,56 @@ def test_against_wdft(nxdirty, nydirty, nchan, nrow, fov): phase = x*uvw[ii, 0] + y*uvw[ii, 1] + uvw[ii, 2]*nm1 res1 += (vis[ii]*np.exp(2j*np.pi*phase)).real res1 /= n + print(_l2error(res0, res1)) + assert_(_l2error(res0, res1) < 1e-4) + +@pmp('nxdirty', [16, 64]) +@pmp('nydirty', [64]) +@pmp("nrow", (10, 100, 1000)) +@pmp("nchan", (1,)) +@pmp("fov", (50,)) +def test_against_wdft_exact(nxdirty, nydirty, nchan, nrow, fov): + epsilon = 1e-10 + np.random.seed(40) + pixsize = fov*np.pi/180/nxdirty + conf = ng.GridderConfig(nxdirty=nxdirty, + nydirty=nydirty, + epsilon=epsilon, + pixsize_x=pixsize, + pixsize_y=0.2*pixsize) + speedoflight, f0 = 3e8, 1e9 + freq = f0 + np.arange(nchan)*(f0/nchan) + uvw = (np.random.rand(nrow, 3)-0.5)/(pixsize*f0/speedoflight) + # use exactly one random w + uvw[:,2] = uvw[0,2] + bl = ng.Baselines(coord=uvw, freq=freq) + flags = np.zeros((nrow, nchan), dtype=np.bool) + idx = ng.getIndices(bl, conf, flags) + ms = np.random.rand(nrow, nchan)-0.5 + 1j*(np.random.rand(nrow, nchan)-0.5) + vis = bl.ms2vis(ms, idx) + uvw = bl.effectiveuvw(idx) + + mi, ma = np.min(uvw[:, 2]), np.max(uvw[:, 2]) + nplanes = 1 + w = mi + iind = ng.getIndices(bl, conf, flags) + dd = conf.grid2dirty_c(ng.vis2grid_c(bl, conf, iind, bl.ms2vis(ms, iind))) + res0 = conf.apply_wscreen(dd, w, adjoint=False).real + + # Compute dft with w term + x, y = np.meshgrid(*[-ss/2 + np.arange(ss) for ss in [nxdirty, nydirty]], + indexing='ij') + x *= conf.Pixsize_x() + y *= conf.Pixsize_y() + res1 = np.zeros_like(res0) + + eps = np.sqrt(x**2+y**2) + s = np.sin(eps) + nm1 = -s*s/(1+np.cos(eps)) + n = nm1+1 + for ii in idx: + phase = x*uvw[ii, 0] + y*uvw[ii, 1] + uvw[ii, 2]*nm1 + res1 += (vis[ii]*np.exp(2j*np.pi*phase)).real + res1 /= n + print(_l2error(res0, res1)) assert_(_l2error(res0, res1) < 1e-4)