Commit ce4b57cf authored by Martin Reinecke's avatar Martin Reinecke
Browse files

merge

parents 3502ad2f d02f825f
......@@ -619,6 +619,8 @@ class GridderConfig
if (i2>=nu) i2-=nu;
size_t j2 = nv-ny_dirty/2+j;
if (j2>=nv) j2-=nv;
// FIXME: for some reason g++ warns about double-to-float conversion
// here, even though there is an explicit cast...
dirty(i,j) = tmav(i2,j2)*T(cfu[i]*cfv[j]);
}
}
......@@ -656,6 +658,8 @@ class GridderConfig
if (i2>=nu) i2-=nu;
size_t j2 = nv-ny_dirty/2+j;
if (j2>=nv) j2-=nv;
// FIXME: for some reason g++ warns about double-to-float conversion
// here, even though there is an explicit cast...
grid(i2,j2) = dirty(i,j)*T(cfu[i]*cfv[j]);
}
}
......@@ -676,7 +680,7 @@ class GridderConfig
grid.data(), grid.data(), T(1), nthreads);
}
template<typename T>void getpix(T u_in, T v_in, T &u, T &v, int &iu0, int &iv0) const
void getpix(double u_in, double v_in, double &u, double &v, int &iu0, int &iv0) const
{
u=fmod1(u_in*psx)*nu,
iu0 = int(u-supp*0.5 + 1 + nu) - nu;
......@@ -759,7 +763,7 @@ template<typename T, typename T2=complex<T>> class Helper
vector<T2> rbuf, wbuf;
bool do_w_gridding;
T w0, xdw;
double w0, xdw;
size_t nexp;
size_t nvecs;
vector<Lock> &locks;
......@@ -809,9 +813,9 @@ template<typename T, typename T2=complex<T>> class Helper
T kernel[64] ALIGNED(64);
Helper(const GridderConfig &gconf_, const T2 *grid_r_, T2 *grid_w_,
vector<Lock> &locks_, T w0_=-1, T dw_=-1)
vector<Lock> &locks_, double w0_=-1, double dw_=-1)
: gconf(gconf_), nu(gconf.Nu()), nv(gconf.Nv()), nsafe(gconf.Nsafe()),
supp(gconf.Supp()), beta(gconf.Beta()), grid_r(grid_r_),
supp(gconf.Supp()), beta(T(gconf.Beta())), grid_r(grid_r_),
grid_w(grid_w_), su(2*nsafe+(1<<logsquare)), sv(2*nsafe+(1<<logsquare)),
bu0(-1000000), bv0(-1000000),
rbuf(su*sv*(grid_r!=nullptr),T(0)),
......@@ -829,18 +833,18 @@ template<typename T, typename T2=complex<T>> class Helper
T Wfac() const { return kernel[2*supp]; }
void prep(const UVW &in)
{
T u, v;
gconf.getpix<T>(in.u, in.v, u, v, iu0, iv0);
T xsupp=T(2)/supp;
auto x0 = xsupp*(iu0-u);
auto y0 = xsupp*(iv0-v);
double u, v;
gconf.getpix(in.u, in.v, u, v, iu0, iv0);
double xsupp=2./supp;
double x0 = xsupp*(iu0-u);
double y0 = xsupp*(iv0-v);
for (int i=0; i<supp; ++i)
{
kernel[i ] = x0+i*xsupp;
kernel[i+supp] = y0+i*xsupp;
kernel[i ] = T(x0+i*xsupp);
kernel[i+supp] = T(y0+i*xsupp);
}
if (do_w_gridding)
kernel[2*supp] = min(T(1), xdw*xsupp*abs(w0-T(in.w)));
kernel[2*supp] = min(T(1), T(xdw*xsupp*abs(w0-in.w)));
for (size_t i=nexp; i<nvecs; ++i)
kernel[i]=0;
for (size_t i=0; i<nvecs; ++i)
......@@ -857,6 +861,28 @@ template<typename T, typename T2=complex<T>> class Helper
}
};
template<class T, class Serv> class SubServ
{
private:
const Serv &srv;
const_mav<uint32_t,1> subidx;
public:
SubServ(const Serv &orig, const const_mav<uint32_t,1> &subidx_)
: srv(orig), subidx(subidx_){}
size_t Nvis() const { return subidx.size(); }
const Baselines &getBaselines() const { return srv.getBaselines(); }
UVW getCoord(size_t i) const
{ return srv.getCoord(subidx[i]); }
complex<T> getVis(size_t i) const
{ return srv.getVis(subidx[i]); }
uint32_t getIdx(size_t i) const { return srv.getIdx(subidx[i]); }
void setVis (size_t i, const complex<T> &v) const
{ srv.setVis(subidx[i], v); }
void addVis (size_t i, const complex<T> &v) const
{ srv.addVis(subidx[i], v); }
};
template<class T, class T2> class VisServ
{
private:
......@@ -876,9 +902,8 @@ template<class T, class T2> class VisServ
checkShape(idx.shape(), {nvis});
if (have_wgt) checkShape(wgt.shape(), {nvis});
}
VisServ(const VisServ &orig, const const_mav<uint32_t,1> &/*newidx*/)
: baselines(orig.baselines), idx(orig.idx), vis(orig.vis), wgt(orig.wgt)
{ myfail("must not happen"); }
SubServ<T, VisServ> getSubserv(const const_mav<uint32_t,1> &subidx) const
{ return SubServ<T, VisServ>(*this, subidx); }
size_t Nvis() const { return nvis; }
const Baselines &getBaselines() const { return baselines; }
UVW getCoord(size_t i) const
......@@ -890,7 +915,6 @@ template<class T, class T2> class VisServ
{ vis(i) = have_wgt ? v*wgt(i) : v; }
void addVis (size_t i, const complex<T> &v) const
{ vis(i) += have_wgt ? v*wgt(i) : v; }
bool isMsServ() const { return false; }
};
template<class T, class T2> VisServ<T, T2> makeVisServ
(const Baselines &baselines,
......@@ -916,8 +940,8 @@ template<class T, class T2> class MsServ
checkShape(ms.shape(), {baselines.Nrows(), baselines.Nchannels()});
if (have_wgt) checkShape(wgt.shape(), ms.shape());
}
MsServ(const MsServ &orig, const const_mav<uint32_t,1> &newidx)
: MsServ(orig.baselines, newidx, orig.ms, orig.wgt) {}
SubServ<T, MsServ> getSubserv(const const_mav<uint32_t,1> &subidx) const
{ return SubServ<T, MsServ>(*this, subidx); }
size_t Nvis() const { return nvis; }
const Baselines &getBaselines() const { return baselines; }
UVW getCoord(size_t i) const
......@@ -939,7 +963,6 @@ template<class T, class T2> class MsServ
auto rc = baselines.getRowChan(idx(i));
ms(rc.row, rc.chan) += have_wgt ? v*wgt(rc.row, rc.chan) : v;
}
bool isMsServ() const { return true; }
};
template<class T, class T2> MsServ<T, T2> makeMsServ
(const Baselines &baselines,
......@@ -1215,12 +1238,12 @@ template<typename T> void apply_wcorr(const GridderConfig &gconf,
{
double fy = y0+j*psy;
fy*=fy;
double fct = 0;
T fct = 0;
double tmp = 1.-fx-fy;
if (tmp>=0)
{
auto nm1 = (-fx-fy)/(sqrt(1.-fx-fy)+1.); // accurate form of sqrt(1-x-y)
fct = (nm1<=-1) ? 0. : kernel.corfac(nm1*dw);
fct = T((nm1<=-1) ? 0. : kernel.corfac(nm1*dw));
}
size_t i2 = nx_dirty-i, j2 = ny_dirty-j;
dirty(i,j)*=fct;
......@@ -1315,9 +1338,7 @@ template<typename T, typename Serv> void x2dirty(
wstack_common(gconf, srv, wmin, dw, nplanes, nvis_plane, minplane, verbosity);
dirty.fill(0);
vector<complex<T>> vis_loc_;
vector<uint32_t> idx_loc_;
vector<uint32_t> mapper;
vector<uint32_t> subidx;
tmpStorage<complex<T>,2> grid_({gconf.Nu(),gconf.Nv()});
auto grid=grid_.getMav();
tmpStorage<complex<T>,2> tdirty_(dirty.shape());
......@@ -1331,38 +1352,15 @@ template<typename T, typename Serv> void x2dirty(
double wcur = wmin+iw*dw;
size_t cnt=0;
mapper.resize(nvis_plane[iw]);
subidx.resize(nvis_plane[iw]);
const auto &bla(minplane[iw/supp]);
for (size_t i=0; i<bla.size(); ++i)
if ((int(iw)>=bla[i].minplane) && (int(iw)<bla[i].minplane+int(supp)))
mapper[cnt++] = bla[i].idx;
idx_loc_.resize(nvis_plane[iw]);
if (srv.isMsServ())
{
#pragma omp parallel for num_threads(nthreads)
for (size_t i=0; i<nvis_plane[iw]; ++i)
idx_loc_[i] = srv.getIdx(mapper[i]);
grid.fill(0);
auto idx_loc = const_mav<uint32_t, 1>(idx_loc_.data(), {idx_loc_.size()});
Serv newsrv(srv, idx_loc);
x2grid_c(gconf, newsrv, grid, wcur, dw);
}
else
{
vis_loc_.resize(nvis_plane[iw]);
#pragma omp parallel for num_threads(nthreads)
for (size_t i=0; i<nvis_plane[iw]; ++i)
{
auto ipart = mapper[i];
vis_loc_[i] = srv.getVis(ipart);
idx_loc_[i] = srv.getIdx(ipart);
}
grid.fill(0);
auto vis_loc = const_mav<complex<T>,1>(vis_loc_.data(), {nvis_plane[iw]});
auto idx_loc = const_mav<uint32_t,1>(idx_loc_.data(), {nvis_plane[iw]});
vis2grid_c(srv.getBaselines(), gconf, idx_loc, vis_loc, grid,
nullmav<T,1>(), wcur, dw);
}
subidx[cnt++] = bla[i].idx;
auto subidx2 = const_mav<uint32_t, 1>(subidx.data(), {subidx.size()});
auto subsrv = srv.getSubserv(subidx2);
grid.fill(0);
x2grid_c(gconf, subsrv, grid, wcur, dw);
gconf.grid2dirty_c(cmav(grid), tdirty);
gconf.apply_wscreen(cmav(tdirty), tdirty, wcur, true);
#pragma omp parallel for num_threads(nthreads)
......@@ -1424,9 +1422,7 @@ template<typename T, typename Serv> void dirty2x(
// correct for w gridding
apply_wcorr(gconf, tdirty, ES_Kernel_with_correction(supp, nthreads), dw);
vector<complex<T>> vis_loc_;
vector<uint32_t> idx_loc_;
vector<uint32_t> mapper;
vector<uint32_t> subidx;
tmpStorage<complex<T>,2> grid_({gconf.Nu(),gconf.Nv()});
auto grid=grid_.getMav();
tmpStorage<complex<T>,2> tdirty2_({nx_dirty,ny_dirty});
......@@ -1446,37 +1442,15 @@ template<typename T, typename Serv> void dirty2x(
gconf.apply_wscreen(cmav(tdirty2), tdirty2, wcur, false);
gconf.dirty2grid_c(cmav(tdirty2), grid);
subidx.resize(nvis_plane[iw]);
size_t cnt=0;
mapper.resize(nvis_plane[iw]);
const auto &bla(minplane[iw/supp]);
for (size_t i=0; i<bla.size(); ++i)
if ((int(iw)>=bla[i].minplane) && (int(iw)<bla[i].minplane+int(supp)))
mapper[cnt++] = bla[i].idx;
idx_loc_.resize(nvis_plane[iw]);
if (srv.isMsServ())
{
#pragma omp parallel for num_threads(nthreads)
for (size_t i=0; i<nvis_plane[iw]; ++i)
idx_loc_[i] = srv.getIdx(mapper[i]);
auto idx_loc = const_mav<uint32_t, 1>(idx_loc_.data(), {idx_loc_.size()});
Serv newsrv(srv, idx_loc);
grid2x_c(gconf, cmav(grid), newsrv, wcur, dw);
}
else
{
vis_loc_.resize(nvis_plane[iw]);
auto vis_loc = mav<complex<T>,1>(vis_loc_.data(), {nvis_plane[iw]});
vis_loc.fill(0);
auto idx_loc = mav<uint32_t,1>(idx_loc_.data(), {nvis_plane[iw]});
#pragma omp parallel for num_threads(nthreads)
for (size_t i=0; i<nvis_plane[iw]; ++i)
idx_loc[i] = srv.getIdx(mapper[i]);
grid2vis_c(srv.getBaselines(), gconf, cmav(idx_loc), cmav(grid), vis_loc,
nullmav<T,1>(), wcur, dw);
#pragma omp parallel for num_threads(nthreads)
for (size_t i=0; i<nvis_plane[iw]; ++i)
srv.addVis(mapper[i], vis_loc[i]);
}
subidx[cnt++] = bla[i].idx;
auto subidx2 = const_mav<uint32_t, 1>(subidx.data(), {subidx.size()});
auto subsrv = srv.getSubserv(subidx2);
grid2x_c(gconf, cmav(grid), subsrv, wcur, dw);
}
}
else
......
......@@ -138,9 +138,9 @@ Class storing UVW coordinates and channel information.
Parameters
==========
coord: np.array((nrows, 3), dtype=np.float)
coord: np.array((nrows, 3), dtype=np.float64)
u, v and w coordinates for each row
freq: np.array((nchannels,), dtype=np.float)
freq: np.array((nchannels,), dtype=np.float64)
frequency for each individual channel (in Hz)
)""";
class PyBaselines: public Baselines
......@@ -305,6 +305,8 @@ pixsize_x: float
Pixel size in x direction (radians)
pixsize_y: float
Pixel size in y direction (radians)
nthreads: int
the number of threads to use for all calculations involving this object.
)""";
class PyGridderConfig: public GridderConfig
{
......@@ -714,6 +716,35 @@ template<typename T> pyarr<T> vis2dirty2(const PyBaselines &baselines,
}
return dirty;
}
constexpr auto vis2dirty_DS = R"""(
Converts an array of visibilities to a dirty image.
Parameters
==========
baselines: Baselines
the Baselines object
gconf: GridderConf
the GridderConf object to be used
(used to optimize the ordering of the indices)
idx: np.array((nvis,), dtype=np.uint32)
the indices for the provided visibilities
vis: np.array(nvis,), dtype=np.complex64 or np.complex128)
The input visibilities
Its data type determines the precision in which the calculation is carried
out.
wgt: np.array((nvis,), dtype=float with same precision as `vis`, optional
If present, visibilities are multiplied by the corresponding entries.
do_wstacking: bool
if True, the full improved w-stacking algorithm is carried out, otherwise
the w values are assumed to be zero.
Returns
=======
np.array((nxdirty, nydirty), dtype=float of same precision as `vis`.)
The dirty image
)""";
py::array Pyvis2dirty(const PyBaselines &baselines,
const PyGridderConfig &gconf, const py::array &idx,
const py::array &vis, const py::object &wgt, bool do_wstacking)
......@@ -745,6 +776,34 @@ template<typename T> pyarr<complex<T>> dirty2vis2(const PyBaselines &baselines,
}
return vis;
}
constexpr auto dirty2vis_DS = R"""(
Converts a dirty image into a 1D array of visibilities.
Parameters
==========
baselines: Baselines
the Baselines object
gconf: GridderConf
the GridderConf object to be used
(used to optimize the ordering of the indices)
idx: np.array((nvis,), dtype=np.uint32)
the indices for the visibilities to be computed
dirty: np.array((nxdirty, nydirty), dtype=np.float32 or np.float64)
dirty image
Its data type determines the precision in which the calculation is carried
out.
wgt: np.array((nvis,), same dtype as `dirty`, optional
If present, visibilities are multiplied by the corresponding entries.
do_wstacking: bool
if True, the full improved w-stacking algorithm is carried out, otherwise
the w values are assumed to be zero.
Returns
=======
np.array((nvis,), dtype=complex of same precision as `dirty`.)
The visibility data
)""";
py::array Pydirty2vis(const PyBaselines &baselines,
const PyGridderConfig &gconf, const py::array &idx, const py::array &dirty,
const py::object &wgt, bool do_wstacking)
......@@ -779,6 +838,43 @@ template<typename T> py::array ms2dirty2(const py::array &uvw_,
return dirty;
}
constexpr auto ms2dirty_DS = R"""(
Converts an MS object to dirty image.
Parameters
==========
uvw: np.array((nrows, 3), dtype=np.float64)
UVW coordinates from the measurement set
freq: np.array((nchan,), dtype=np.float64)
channel frequencies
ms: np.array((nrows, nchan,), dtype=np.complex64 or np.complex128)
the input measurement set data.
Its data type determines the precision in which the calculation is carried
out.
wgt: np.array((nrows, nchan), float with same precision as `ms`), optional
If present, its values are multiplied to the output
npix_x, npix_y: int
dimensions of the dirty image
pixsize_x, pixsize_y: float
angular pixel size (in radians) of the dirty image
epsilon: float
accuracy at which the computation should be done. Must be larger than 2e-13.
If `ms` has type np.complex64, it must be larger than 1e-5.
do_wstacking: bool
if True, the full improved w-stacking algorithm is carried out, otherwise
the w values are assumed to be zero.
nthreads: int
number of threads to use for the calculation
verbosity: int
0: no output
1: some output
2: detailed output
Returns
=======
np.array((nxdirty, nydirty), dtype=float of same precision as `ms`)
the dirty image
)""";
py::array Pyms2dirty(const py::array &uvw,
const py::array &freq, const py::array &ms, const py::object &wgt,
size_t npix_x, size_t npix_y, double pixsize_x, double pixsize_y, double epsilon,
......@@ -816,6 +912,41 @@ template<typename T> py::array dirty2ms2(const py::array &uvw_,
return ms;
}
constexpr auto dirty2ms_DS = R"""(
Converts a dirty image to an MS object.
Parameters
==========
uvw: np.array((nrows, 3), dtype=np.float64)
UVW coordinates from the measurement set
freq: np.array((nchan,), dtype=np.float64)
channel frequencies
dirty: np.array((nxdirty, nydirty), dtype=np.float32 or np.float64)
dirty image
Its data type determines the precision in which the calculation is carried
out.
wgt: np.array((nrows, nchan), same dtype as `dirty`), optional
If present, its values are multiplied to the output
pixsize_x, pixsize_y: float
angular pixel size (in radians) of the dirty image
epsilon: float
accuracy at which the computation should be done. Must be larger than 2e-13.
If `dirty` has type np.float32, it must be larger than 1e-5.
do_wstacking: bool
if True, the full improved w-stacking algorithm is carried out, otherwise
the w values are assumed to be zero.
nthreads: int
number of threads to use for the calculation
verbosity: int
0: no output
1: some output
2: detailed output
Returns
=======
np.array((nrows, nchan,), dtype=complex of same precision as `dirty`)
the measurement set data.
)""";
py::array Pydirty2ms(const py::array &uvw,
const py::array &freq, const py::array &dirty, const py::object &wgt,
double pixsize_x, double pixsize_y, double epsilon,
......@@ -909,15 +1040,15 @@ PYBIND11_MODULE(nifty_gridder, m)
"grid"_a, "wgt"_a=None);
m.def("get_correlations", &Pyget_correlations<double>, "baselines"_a, "gconf"_a,
"idx"_a, "du"_a, "dv"_a, "wgt"_a=None);
m.def("vis2dirty",&Pyvis2dirty, "baselines"_a, "gconf"_a,
m.def("vis2dirty",&Pyvis2dirty, vis2dirty_DS, "baselines"_a, "gconf"_a,
"idx"_a, "vis"_a, "wgt"_a=None, "do_wstacking"_a=false);
m.def("dirty2vis",&Pydirty2vis, "baselines"_a, "gconf"_a,
m.def("dirty2vis",&Pydirty2vis, "baselines"_a, dirty2vis_DS, "gconf"_a,
"idx"_a, "dirty"_a, "wgt"_a=None, "do_wstacking"_a=false);
m.def("ms2dirty",&Pyms2dirty,"uvw"_a,"freq"_a,"ms"_a,
"wgt"_a=None,"npix_x"_a,"npix_y"_a,"pixsize_x"_a,"pixsize_y"_a,"epsilon"_a,
m.def("ms2dirty", &Pyms2dirty, ms2dirty_DS, "uvw"_a, "freq"_a, "ms"_a,
"wgt"_a=None, "npix_x"_a, "npix_y"_a, "pixsize_x"_a, "pixsize_y"_a,
"epsilon"_a, "do_wstacking"_a=false, "nthreads"_a=1, "verbosity"_a=0);
m.def("dirty2ms", &Pydirty2ms, dirty2ms_DS, "uvw"_a, "freq"_a, "dirty"_a,
"wgt"_a=None, "pixsize_x"_a, "pixsize_y"_a, "epsilon"_a,
"do_wstacking"_a=false, "nthreads"_a=1, "verbosity"_a=0);
m.def("dirty2ms",&Pydirty2ms,"uvw"_a,"freq"_a,"dirty"_a,
"wgt"_a=None,"pixsize_x"_a,"pixsize_y"_a, "epsilon"_a, "do_wstacking"_a=false, "nthreads"_a=1,
"verbosity"_a=0);
}
......@@ -7,7 +7,6 @@ from numpy.testing import assert_, assert_allclose, assert_array_almost_equal
pmp = pytest.mark.parametrize
def _init_gridder(nxdirty, nydirty, epsilon, nchan, nrow):
pixsize = np.pi/180/60/nxdirty
conf = ng.GridderConfig(nxdirty=nxdirty,
......@@ -45,13 +44,103 @@ def _dft(uvw, vis, conf, apply_w):
def _l2error(a, b):
return np.sqrt(np.sum(np.abs(a-b)**2)/np.sum(np.abs(a)**2))
return np.sqrt(np.sum(np.abs(a-b)**2)/np.maximum(np.sum(np.abs(a)**2),np.sum(np.abs(b)**2)))
def explicit_gridder(uvw, freq, ms, wgt, nxdirty, nydirty, xpixsize, ypixsize, apply_w):
speedoflight = 299792458.
x, y = np.meshgrid(*[-ss/2 + np.arange(ss) for ss in [nxdirty, nydirty]],
indexing='ij')
x *= xpixsize
y *= ypixsize
res = np.zeros((nxdirty, nydirty))
eps = x**2+y**2
if apply_w:
nm1 = -eps/(np.sqrt(1.-eps)+1.)
n = nm1+1
else:
nm1 = 0.
n = 1.
for row in range(ms.shape[0]):
for chan in range(ms.shape[1]):
phase = (freq[chan]/speedoflight *
(x*uvw[row, 0] + y*uvw[row, 1] - uvw[row, 2]*nm1))
if wgt is None:
res += (ms[row, chan]*np.exp(2j*np.pi*phase)).real
else:
res += (ms[row, chan]*wgt[row, chan]*np.exp(2j*np.pi*phase)).real
return res/n
@pmp("nxdirty", (30, 128))
@pmp("nydirty", (128, 250))
@pmp("nrow", (2, 27))
@pmp("nchan", (1, 5))
@pmp("epsilon", (1e-3, 1e-5, 1e-7, 5e-13))
@pmp("singleprec", (True, False))
@pmp("wstacking", (True, False))
@pmp("use_wgt", (True, False))
@pmp("nthreads", (1, 2))
def test_adjointness_ms2dirty(nxdirty, nydirty, nrow, nchan, epsilon, singleprec, wstacking, use_wgt, nthreads):
if singleprec and epsilon < 5e-6:
return
np.random.seed(42)
pixsizex = np.pi/180/60/nxdirty*0.2398
pixsizey = np.pi/180/60/nxdirty
speedoflight, f0 = 3e8, 1e9
freq = f0 + np.arange(nchan)*(f0/nchan)
uvw = (np.random.rand(nrow, 3)-0.5)/(pixsizey*f0/speedoflight)
ms = np.random.rand(nrow, nchan)-0.5 + 1j*(np.random.rand(nrow, nchan)-0.5)
wgt = np.random.rand(nrow, nchan) if use_wgt else None
dirty = np.random.rand(nxdirty, nydirty)-0.5
if singleprec:
ms = ms.astype("c8")
dirty = dirty.astype("f4")
if wgt is not None:
wgt = wgt.astype("f4")
dirty2 = ng.ms2dirty(uvw, freq, ms, wgt, nxdirty, nydirty, pixsizex,
pixsizey, epsilon, wstacking, nthreads, 2).astype("f8")
ms2 = ng.dirty2ms(uvw, freq, dirty, wgt, pixsizex, pixsizey, epsilon,
wstacking, nthreads, 0).astype("c16")
tol = 5e-5 if singleprec else 5e-13
assert_allclose(np.vdot(ms, ms2).real, np.vdot(dirty2, dirty), rtol=tol)
@pmp('nxdirty', [16, 64])
@pmp('nydirty', [64])
@pmp("nrow", (2, 27))
@pmp("nchan", (1, 5))
@pmp("epsilon", (1e-2, 1e-3, 5e-5, 1e-7, 5e-13))
@pmp("singleprec", (True, False))
@pmp("wstacking", (True, False))
@pmp("use_wgt", (False, True))
@pmp("nthreads", (1, 2))
@pmp("fov", (1.,20.))
def test_ms2dirty_against_wdft(nxdirty, nydirty, nrow, nchan, epsilon, singleprec, wstacking, use_wgt, fov, nthreads):
if singleprec and epsilon < 5e-5:
return
np.random.seed(40)
pixsizex = fov*np.pi/180/nxdirty
pixsizey = fov*np.pi/180/nydirty*1.1
speedoflight, f0 = 3e8, 1e9
freq = f0 + np.arange(nchan)*(f0/nchan)
uvw = (np.random.rand(nrow, 3)-0.5)/(pixsizex*f0/speedoflight)
ms = np.random.rand(nrow, nchan)-0.5 + 1j*(np.random.rand(nrow, nchan)-0.5)
wgt = np.random.rand(nrow, nchan) if use_wgt else None
if singleprec:
ms = ms.astype("c8")
if wgt is not None:
wgt = wgt.astype("f4")
dirty = ng.ms2dirty(uvw, freq, ms, wgt, nxdirty, nydirty, pixsizex,
pixsizey, epsilon, wstacking, nthreads, 0).astype("f8")
ref = explicit_gridder(uvw, freq, ms, wgt, nxdirty, nydirty, pixsizex, pixsizey, wstacking)
assert_allclose(_l2error(dirty, ref), 0, atol=epsilon)
@pmp("nxdirty", (128, 300))
@pmp("nydirty", (128, 250))
@pmp("nrow", (1, 10, 10000))
@pmp("nchan", (1, 10, 100))
@pmp("nrow", (1, 100))
@pmp("nchan", (1, 10))
@pmp("epsilon", (1e-2, 1e-7, 2e-13))
def test_adjointness(nxdirty, nydirty, nrow, nchan, epsilon):
np.random.seed(42)
......@@ -68,8 +157,8 @@ def test_adjointness(nxdirty, nydirty, nrow, nchan, epsilon):
@pmp("nxdirty", (128, 300))
@pmp("nydirty", (128, 250))
@pmp("nrow", (1, 10, 10000))
@pmp("nchan", (1, 10, 100))
@pmp("nrow", (1, 127))
@pmp("nchan", (1, 17))
@pmp("epsilon", (1e-2, 1e-7, 2e-13))
def test_adjointness_wgridding(nxdirty, nydirty, nrow, nchan, epsilon):
np.random.seed(42)
......@@ -82,40 +171,10 @@ def test_adjointness_wgridding(nxdirty, nydirty, nrow, nchan, epsilon):
rtol=2e-13)
@pmp("nxdirty", (128, 300))
@pmp("nydirty", (128, 250))
@pmp("nrow", (1, 10, 10000))
@pmp("nchan", (1, 10, 100))
@pmp("epsilon", (1e-2, 1e-3, 5e-5, 1e-6, 1e-7, 5e-13))
@pmp("singleprec", (True, False))
@pmp("wstacking", (True, False))
def test_adjointness_wgridding_highlevel(nxdirty, nydirty, nrow, nchan, epsilon, singleprec, wstacking):
if singleprec and epsilon < 5e-6:
return
np.random.seed(42)
pixsizex = np.pi/180/60/nxdirty*0.2398
pixsizey = np.pi/180/60