// This C++ program has been written to demonstrate the convolutional resampling algorithm used in radio // interferometry. It should compile with: // g++ -O2 -fstrict-aliasing tConvolveBLAS.cc -o tConvolveBLAS // Enabling BLAS support on OS X: // g++ -DUSEBLAS -O2 -fstrict-aliasing -framework vecLib tConvolveBLAS.cc -o tConvolveBLAS // // Strict-aliasing tells the compiler that there are no memory locations accessed through aliases. // // The challenge is to minimize the run time - specifically the time per grid addition. On a MacBookPro // 2GHz Intel Core Duo this is about 6.0ns. // // For further details contact Tim.Cornwell@csiro.au // November 22, 2007 // - Rewritten from tConvolve to use BLAS, and to be much smarter about not using strides in C #include #include #include #include #include #include #ifdef USEBLAS #define CAXPY cblas_caxpy #define CDOTU_SUB cblas_cdotu_sub #ifdef __APPLE_CC__ #ifdef USEMKLLIB #include #else #include #endif #else #include #endif #endif using std::cout; using std::endl; using std::complex; using std::abs; // Typedefs for easy testing // Cost of using double for Coord is low, cost for // double for Real is also low typedef double Coord; typedef float Real; typedef std::complex Value; ///////////////////////////////////////////////////////////////////////////////// // The next two functions are the kernel of the gridding/degridding. // The data are presented as a vector. Offsets for the convolution function // and for the grid location are precalculated so that the kernel does // not need to know anything about world coordinates or the shape of // the convolution function. The ordering of cOffset and iu, iv is // random - some presorting might be advantageous. // // Perform gridding // // data - values to be gridded in a 1D vector // support - Total width of convolution function=2*support+1 // C - convolution function shape: (2*support+1, 2*support+1, *) // cOffset - offset into convolution function per data point // iu, iv - integer locations of grid points // grid - Output grid: shape (gSize, *) // gSize - size of one axis of grid void gridKernel(const std::vector& data, const int support, const std::vector& C, const std::vector& cOffset, const std::vector& iu, const std::vector& iv, std::vector& grid, const int gSize) { int sSize=2*support+1; for (int dind=0; dind& grid, const int gSize, const int support, const std::vector& C, const std::vector& cOffset, const std::vector& iu, const std::vector& iv, std::vector& data) { int sSize=2*support+1; for (int dind=0; dind& w, const std::vector& freq, const Coord cellSize, const Coord baseline, const int wSize, const int gSize, int& support, int& overSample, Coord& wCellSize, std::vector& C) { cout << "Initializing W projection convolution function" << endl; support=static_cast(1.5*sqrt(abs(baseline) *static_cast(cellSize) *freq[0])/cellSize); overSample=8; cout << "Support = " << support << " pixels" << endl; wCellSize=2*baseline*freq[0]/wSize; cout << "W cellsize = " << wCellSize << " wavelengths" << endl; // Convolution function. This should be the convolution of the // w projection kernel (the Fresnel term) with the convolution // function used in the standard case. The latter is needed to // suppress aliasing. In practice, we calculate entire function // by Fourier transformation. Here we take an approximation that // is good enough. int sSize=2*support+1; int cCenter=(sSize-1)/2; C.resize(sSize*sSize*overSample*overSample*wSize); cout << "Size of convolution function = " << sSize*sSize*overSample *overSample*wSize*8/(1024*1024) << " MB" << std::endl; cout << "Shape of convolution function = [" << sSize << ", " << sSize << ", " << overSample << ", " << overSample << ", " << wSize << "]" << std::endl; for (int k=0; k(std::cos(r2/(w*fScale))); } else { C[cind]=static_cast(std::exp(-r2)); } } } } } } // Now normalise the convolution function Real sumC=0.0; for (int i=0; i& u, const std::vector& v, const std::vector& w, const std::vector& freq, const Coord cellSize, const Coord wCellSize, const Coord baseline, const int wSize, const int gSize, const int support, const int overSample, std::vector& cOffset, std::vector& iu, std::vector& iv) { const int nSamples = u.size(); const int nChan = freq.size(); int sSize=2*support+1; // Now calculate the offset for each visibility point cOffset.resize(nSamples*nChan); iu.resize(nSamples*nChan); iv.resize(nSamples*nChan); for (int i=0; i u(nSamples); std::vector v(nSamples); std::vector w(nSamples); std::vector data(nSamples*nChan); std::vector outdata(nSamples*nChan); for (int i=0; i grid(gSize*gSize); grid.assign(grid.size(), Value(0.0)); // Measure frequency in inverse wavelengths std::vector freq(nChan); for (int i=0; i > C; int support, overSample; std::vector cOffset; // Vectors of grid centers std::vector iu; std::vector iv; Coord wCellSize; initC(nSamples, w, freq, cellSize, baseline, wSize, gSize, support, overSample, wCellSize, C); initCOffset(u, v, w, freq, cellSize, wCellSize, baseline, wSize, gSize, support, overSample, cOffset, iu, iv); int sSize=2*support+1; // Now we can do the timing cout << "+++++ Forward processing +++++" << endl; clock_t start, finish; double time; start = clock(); gridKernel(data, support, C, cOffset, iu, iv, grid, gSize); finish = clock(); // Report on timings // Report on timings time = (double(finish)-double(start))/CLOCKS_PER_SEC; cout << " Time " << time << " (s) " << endl; cout << " Time per visibility spectral sample " << 1e6*time/double(data.size()) << " (us) " << endl; cout << " Time per gridding " << 1e9*time/(double(data.size())* double((sSize)*(sSize))) << " (ns) " << endl; cout << "+++++ Reverse processing +++++" << endl; grid.assign(grid.size(), Value(1.0)); start = clock(); degridKernel(grid, gSize, support, C, cOffset, iu, iv, outdata); finish = clock(); // Report on timings time = (double(finish)-double(start))/CLOCKS_PER_SEC; cout << " Time " << time << " (s) " << endl; cout << " Time per visibility spectral sample " << 1e6*time/double(data.size()) << " (us) " << endl; cout << " Time per degridding " << 1e9*time/(double(data.size())* double((sSize)*(sSize))) << " (ns) " << endl; cout << "Done" << endl; return 0; }