/* * Copyright (C) 2020 Max-Planck-Society * Author: Martin Reinecke */ #include #include #include #include #include "mr_util/math/constants.h" #include "mr_util/math/gl_integrator.h" #include "mr_util/math/es_kernel.h" #include "mr_util/infra/mav.h" #include "mr_util/sharp/sharp.h" #include "mr_util/sharp/sharp_almhelpers.h" #include "mr_util/sharp/sharp_geomhelpers.h" #include "alm.h" #include "mr_util/math/fft.h" #include "mr_util/bindings/pybind_utils.h" using namespace std; using namespace mr; namespace py = pybind11; namespace { template class Interpolator { protected: size_t lmax, kmax, nphi0, ntheta0, nphi, ntheta; double ofactor; size_t supp; ES_Kernel kernel; mav cube; // the data cube (theta, phi, 2*mbeam+1[, IQU]) void correct(mav &arr) { mav tmp({nphi,nphi}); auto tmp0=tmp.template subarray<2>({0,0},{nphi0, nphi0}); fmav ftmp0(tmp0); for (size_t i=0; i=nphi0) j2-=nphi0; tmp0.v(i2,j) = arr(i,j2); } // FFT to frequency domain on minimal grid r2r_fftpack(ftmp0,ftmp0,{0,1},true,true,1./(nphi0*nphi0),0); auto fct = kernel.correction_factors(nphi, nphi0, 0); for (size_t i=0; i({0,0},{nphi, nphi0}); fmav ftmp1(tmp1); // zero-padded FFT in theta direction r2r_fftpack(ftmp1,ftmp1,{0},false,false,1.,0); auto tmp2=tmp.template subarray<2>({0,0},{ntheta, nphi}); fmav ftmp2(tmp2); fmav farr(arr); // zero-padded FFT in phi direction r2r_fftpack(ftmp2,farr,{1},false,false,1.,0); } public: Interpolator(const Alm> &slmT, const Alm> &blmT, double epsilon) : lmax(slmT.Lmax()), kmax(blmT.Mmax()), nphi0(2*good_size_real(lmax+1)), ntheta0(nphi0/2+1), nphi(2*good_size_real(2*lmax+1)), ntheta(nphi/2+1), ofactor(double(nphi)/(2*lmax+1)), supp(ES_Kernel::get_supp(epsilon, ofactor)), kernel(supp, ofactor, 1), cube({ntheta+2*supp, nphi+2*supp, 2*kmax+1}) { MR_assert((supp<=ntheta) && (supp<=nphi), "support too large!"); MR_assert(slmT.Mmax()==lmax, "Sky lmax must be equal to Sky mmax"); MR_assert(blmT.Lmax()==lmax, "Sky and beam lmax must be equal"); Alm> a1(lmax, lmax), a2(lmax,lmax); auto ginfo = sharp_make_cc_geom_info(ntheta0,nphi0,0,cube.stride(1),cube.stride(0)); auto ainfo = sharp_make_triangular_alm_info(lmax,lmax,1); vectorlnorm(lmax+1); for (size_t i=0; i<=lmax; ++i) lnorm[i]=sqrt(4*pi/(2*i+1.)); for (size_t k=0; k<=kmax; ++k) { double spinsign = (k==0) ? 1. : -1.; for (size_t m=0; m<=lmax; ++m) { T mfac=T((m&1) ? -1.:1.); for (size_t l=m; l<=lmax; ++l) { if (l v1=slmT(l,m)*blmT(l,k), v2=conj(slmT(l,m))*blmT(l,k)*mfac; a1(l,m) = (v1+conj(v2)*mfac)*T(0.5*spinsign*lnorm[l]); if (k>0) { complex tmp = (v1-conj(v2)*mfac)*T(-spinsign*0.5*lnorm[l]); a2(l,m) = complex(-tmp.imag(), tmp.real()); } } } } size_t kidx1 = (k==0) ? 0 : 2*k-1, kidx2 = (k==0) ? 0 : 2*k; auto quadrant=k%4; if (quadrant&1) swap(kidx1, kidx2); auto m1 = cube.template subarray<2>({supp,supp,kidx1},{ntheta,nphi,0}); auto m2 = cube.template subarray<2>({supp,supp,kidx2},{ntheta,nphi,0}); if (k==0) sharp_alm2map(a1.Alms().data(), m1.vdata(), *ginfo, *ainfo, 0, nullptr, nullptr); else sharp_alm2map_spin(k, a1.Alms().data(), a2.Alms().data(), m1.vdata(), m2.vdata(), *ginfo, *ainfo, 0, nullptr, nullptr); correct(m1); if (k!=0) correct(m2); if ((quadrant==1)||(quadrant==2)) m1.apply([](T &v){v=-v;}); if ((k>0) &&((quadrant==0)||(quadrant==1))) m2.apply([](T &v){v=-v;}); } // fill border regions for (size_t i=0; i=nphi) j2-=nphi; cube.v(supp-1-i,j2+supp,k) = cube(supp+1+i,j+supp,k); cube.v(supp+ntheta+i,j2+supp,k) = cube(supp+ntheta-2-i, j+supp,k); } for (size_t i=0; i &ptg, mav &res) const { MR_assert(ptg.shape(0)==res.shape(0), "dimension mismatch"); MR_assert(ptg.shape(1)==3, "second dimension must have length 3"); vector wt(supp), wp(supp); vector psiarr(2*kmax+1); double delta = 2./supp; double xdtheta = (ntheta-1)/pi, xdphi = nphi/(2*pi); for (size_t i=0; i class PyInterpolator: public Interpolator { public: PyInterpolator(const py::array &slmT, const py::array &blmT, int64_t lmax, int64_t kmax, double epsilon) : Interpolator(Alm>(to_mav,1>(slmT), lmax, lmax), Alm>(to_mav,1>(blmT), lmax, kmax), epsilon) {} using Interpolator::interpolx; py::array interpol(const py::array &ptg) { auto ptg2 = to_mav(ptg); auto res = make_Pyarr({ptg2.shape(0)}); auto res2 = to_mav(res,true); interpolx(ptg2, res2); return res; } }; } // unnamed namespace PYBIND11_MODULE(interpol_ng, m) { using namespace pybind11::literals; py::class_> (m, "PyInterpolator") .def(py::init(), "sky"_a, "beam"_a, "lmax"_a, "kmax"_a, "epsilon"_a) .def ("interpol", &PyInterpolator::interpol, "ptg"_a); }