#ifndef FCHEBSYMM2LHANDLER_HPP #define FCHEBSYMM2LHANDLER_HPP #include #include "../../Utils/FBlas.hpp" #include "./FChebTensor.hpp" #include "./FChebSymmetries.hpp" #include "./FChebM2LHandler.hpp" /** * @author Matthias Messner (matthias.matthias@inria.fr) * Please read the license */ /*! If \a FACASVD is defined ACA+SVD will be used instead of only SVD. */ #define FACASVD /*! The fully pivoted adaptive cross approximation (fACA) compresses a far-field interaction as \f$K\sim UV^\top\f$. The fACA requires all entries to be computed beforehand, then the compression follows in \f$\mathcal{O}(2\ell^3k)\f$ operations based on the required accuracy \f$\varepsilon\f$. @param[in] K far-field to be approximated @param[in] eps prescribed accuracy @param[out] U matrix containing \a k column vectors @param[out] V matrix containing \a k row vectors @param[out] k final low-rank depends on prescribed accuracy \a eps */ template static void fACA(FReal *const K, const double eps, FReal* &U, FReal* &V, unsigned int &k) { static const int nnodes = ORDER*ORDER*ORDER; // control vectors (true if not used, false if used) bool r[nnodes], c[nnodes]; for (int i=0; i eps*eps * norm2K); //////////////////////////////////////////////// } /*! Precomputes the 16 far-field interactions (due to symmetries in their arrangement all 316 far-field interactions can be represented by permutations of the 16 we compute in this function). Depending on whether FACASVD is defined or not, either ACA+SVD or only SVD is used to compress them. */ template static void precompute(const MatrixKernelClass *const MatrixKernel, const FReal CellWidth, const double Epsilon, FReal* K[343], int LowRank[343]) { std::cout << "\nComputing 16 far-field interactions for cells of width w = " << CellWidth << std::endl; static const unsigned int nnodes = ORDER*ORDER*ORDER; // interpolation points of source (Y) and target (X) cell FPoint X[nnodes], Y[nnodes]; // set roots of target cell (X) FChebTensor::setRoots(FPoint(0.,0.,0.), CellWidth, X); // temporary matrix FReal* U = new FReal [nnodes*nnodes]; // needed for the SVD unsigned int INFO; const unsigned int LWORK = 2 * (3*nnodes + nnodes); FReal *const WORK = new FReal [LWORK]; FReal *const VT = new FReal [nnodes*nnodes]; FReal *const S = new FReal [nnodes]; unsigned int counter = 0; for (int i=2; i<=3; ++i) { for (int j=0; j<=i; ++j) { for (int k=0; k<=j; ++k) { // assemble matrix const FPoint cy(CellWidth*FReal(i), CellWidth*FReal(j), CellWidth*FReal(k)); FChebTensor::setRoots(cy, CellWidth, Y); for (unsigned int n=0; nevaluate(X[m], Y[n]); // applying weights //////////////////////////////////////// FReal weights[nnodes]; FChebTensor::setRootOfWeights(weights); for (unsigned int n=0; n(U, Epsilon, UU, VV, rank); // QR decomposition FReal* phi = new FReal [rank*rank]; { // QR of U and V FReal* tauU = new FReal [rank]; INFO = FBlas::geqrf(nnodes, rank, UU, tauU, LWORK, WORK); assert(INFO==0); FReal* tauV = new FReal [rank]; INFO = FBlas::geqrf(nnodes, rank, VV, tauV, LWORK, WORK); assert(INFO==0); // phi = Ru Rv' FReal* rU = new FReal [2 * rank*rank]; FReal* rV = rU + rank*rank; FBlas::setzero(2 * rank*rank, rU); for (unsigned int l=0; l V and then Qu V FReal *const V = new FReal [aca_rank * rank]; for (unsigned int r=0; r(S, Epsilon); // store const unsigned int idx = (i+3)*7*7 + (j+3)*7 + (k+3); assert(K[idx]==NULL); K[idx] = new FReal [2*rank*nnodes]; LowRank[idx] = rank; for (unsigned int r=0; r(&idx), sizeof(int)); // 2) write low rank (int) int rank = LowRank[idx]; stream.write(reinterpret_cast(&rank), sizeof(int)); // 3) write U and V (both: rank*nnodes * FReal) FReal *const U = K[idx]; FReal *const V = K[idx] + rank*nnodes; stream.write(reinterpret_cast(U), sizeof(FReal)*rank*nnodes); stream.write(reinterpret_cast(V), sizeof(FReal)*rank*nnodes); } } else throw std::runtime_error("File could not be opened to write"); stream.close(); // write info std::cout << "Compressed M2L operators stored in binary file " << filename << " in " << time.tacAndElapsed() << "sec." << std::endl; // free memory ///////////////////// for (unsigned int t=0; t<343; ++t) if (K[t]!=NULL) delete [] K[t]; } /** * Reads the 16 compressed M2L kernels from the binary files and writes them * in K and the respective low-rank in LowRank. */ template void ReadFromBinaryFile(const FReal Epsilon, FReal* K[343], int LowRank[343]) { // compile time constants const unsigned int nnodes = ORDER*ORDER*ORDER; // find filename const char precision = (typeid(FReal)==typeid(double) ? 'd' : 'f'); std::stringstream sstream; sstream << "sym2l_" << precision << "_o" << ORDER << "_e" << Epsilon << ".bin"; const std::string filename(sstream.str()); // read binary file std::ifstream istream(filename.c_str(), std::ios::in | std::ios::binary | std::ios::ate); const std::ifstream::pos_type size = istream.tellg(); if (size<=0) throw std::runtime_error("The requested binary file does not yet exist. Exit."); if (istream.good()) { istream.seekg(0); // 1) read index (int) int _idx; istream.read(reinterpret_cast(&_idx), sizeof(int)); // loop to find 16 compressed m2l operators for (int idx=0; idx<343; ++idx) { K[idx] = NULL; LowRank[idx] = 0; // if it exists if (idx == _idx) { // 2) read low rank (int) int rank; istream.read(reinterpret_cast(&rank), sizeof(int)); LowRank[idx] = rank; // 3) read U and V (both: rank*nnodes * FReal) K[idx] = new FReal [2*rank*nnodes]; FReal *const U = K[idx]; FReal *const V = K[idx] + rank*nnodes; istream.read(reinterpret_cast(U), sizeof(FReal)*rank*nnodes); istream.read(reinterpret_cast(V), sizeof(FReal)*rank*nnodes); // 1) read next index istream.read(reinterpret_cast(&_idx), sizeof(int)); } } } else throw std::runtime_error("File could not be opened to read"); istream.close(); } #endif