├── tests ├── Makefile.inc ├── ncrit_search.cpp ├── scaling.cpp ├── correctness.cpp ├── dual_correctness.cpp ├── Math.hpp ├── Makefile ├── single_level.cpp ├── single_level_stresslet.cpp ├── multi_level.cpp └── multi_level_stresslet.cpp ├── Makefile.inc ├── include ├── timing.hpp ├── executor │ ├── ExecutorBase.hpp │ ├── EvaluatorBase.hpp │ ├── INITL.hpp │ ├── INITM.hpp │ ├── EvalDownward.hpp │ ├── L2L.hpp │ ├── M2L.hpp │ ├── M2M.hpp │ ├── EvalUpward.hpp │ ├── L2P.hpp │ ├── M2P.hpp │ ├── P2M.hpp │ ├── EvalLocal.hpp │ ├── EvalInteraction.hpp │ ├── EvalDiagonalSparse.hpp │ ├── P2P.hpp │ ├── make_executor.hpp │ ├── EvalP2P.hpp │ ├── EvalInteractionQueue.hpp │ ├── EvalLocalSparse.hpp │ ├── EvalInteractionLazy_bkp.hpp │ ├── ExecutorSingleTree.hpp │ └── ExecutorDualTree.hpp ├── Matvec.hpp ├── Mat3.hpp ├── FMMOptions.hpp ├── Logger.hpp ├── SparseMatrix.hpp ├── FMM_plan.hpp ├── tree │ └── BoundingBox.hpp └── KernelTraits.hpp ├── examples ├── BEM │ ├── timing.hpp │ ├── AnalyticalIntegral.hpp │ ├── MeshIO.hpp │ ├── ReadData.hpp │ ├── DirectMatvec.hpp │ ├── SolverOptions.hpp │ ├── Preconditioner.hpp │ ├── BioMeshReader.hpp │ ├── BlockDiagonalPC.hpp │ ├── BEMConfig.hpp │ ├── BlockDiagonalPC_Stokes.hpp │ ├── fmgmres.hpp │ ├── VertFaceReader.hpp │ ├── LocalPC_Stokes.hpp │ ├── LocalPC.hpp │ ├── MshReader.hpp │ ├── BLAS.hpp │ ├── SemiAnalytical.hpp │ └── Triangulation.hpp ├── Makefile └── YukawaBEM.cpp ├── boost_version.cpp ├── .gitignore ├── README.md ├── test_vec.cpp ├── test_tree.cpp ├── test_direct.cpp ├── Makefile ├── kernel └── UnitKernel.hpp ├── serialrun_stresslet.cpp └── serialrun.cpp /tests/Makefile.inc: -------------------------------------------------------------------------------- 1 | # This FMM library 2 | 3 | FMMW_DIR = .. 4 | 5 | # External libraries 6 | 7 | BOOST_DIR = /usr/include 8 | FFTW_DIR = /usr/include 9 | GSL_DIR = /usr/include 10 | -------------------------------------------------------------------------------- /Makefile.inc: -------------------------------------------------------------------------------- 1 | # This FMM library 2 | 3 | FMMW_DIR = . 4 | 5 | 6 | # External libraries 7 | 8 | BOOST_DIR = /usr/local/include 9 | FFTW_DIR = /usr/include 10 | GSL_DIR = /usr/include 11 | -------------------------------------------------------------------------------- /include/timing.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | double get_time() { 6 | struct timeval tv; 7 | gettimeofday(&tv,NULL); 8 | return (double)(tv.tv_sec + 1e-6*tv.tv_usec); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /examples/BEM/timing.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | double get_time() { 6 | struct timeval tv; 7 | gettimeofday(&tv,NULL); 8 | return (double)(tv.tv_sec + 1e-6*tv.tv_usec); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /examples/BEM/AnalyticalIntegral.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Main class to hold shared things for (semi-) analytical integration routines 5 | */ 6 | 7 | namespace AnalyticalIntegral 8 | { 9 | // type of equation 10 | typedef enum { LAPLACE, YUKAWA, HELMHOLTZ, STOKES } equation; 11 | 12 | }; // end namespace AnalyticalIntegral 13 | -------------------------------------------------------------------------------- /boost_version.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | int main() { 5 | std::cout << "Using Boost " 6 | << BOOST_VERSION / 100000 << "." // major version 7 | << BOOST_VERSION / 100 % 1000 << "." // minior version 8 | << BOOST_VERSION % 100 // patch level 9 | << std::endl; 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # make and build files 3 | *.o 4 | .deps 5 | *.swp 6 | examples/LaplaceBEM 7 | examples/sphericalBEM 8 | examples/StokesBEM 9 | examples/YukawaBEM 10 | examples/*.sh 11 | examples/result 12 | serialrun 13 | tests/correctness 14 | tests/single_level 15 | tests/scaling 16 | tests/multi_level 17 | 18 | *.face 19 | *.vert 20 | *.charge 21 | 22 | # results from autorun 23 | examples/Laplace_result 24 | examples/Stokes_sphere_result 25 | examples/Stokes_rbc_result 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | fmm-bem-relaxed 2 | ======= 3 | Research code implementing a fast-multipole boundary element method (BEM), in multi-threaded C++, used for the results in the paper: 4 | 5 | *Inexact Krylov iterations and relaxation strategies with fast-multipole boundary element method* 6 | 7 | Preprint on [arXiv:1506.05957](http://arxiv.org/abs/1506.05957) 8 | 9 | Manuscript source files at [https://github.com/barbagroup/inexact-gmres](https://github.com/barbagroup/inexact-gmres) 10 | -------------------------------------------------------------------------------- /test_vec.cpp: -------------------------------------------------------------------------------- 1 | #include "Vec.hpp" 2 | 3 | #include 4 | 5 | int main() { 6 | Vec<3,double> v1 = Vec<3,double>(0.,1.,2); 7 | Vec<3,double> v2 = Vec<3,double>(3,4,5); 8 | 9 | Vec<3,double> v3 = v1 * (v2 + v2); 10 | 11 | std::cout << v3 << std::endl; 12 | std::cout << norm(v3) << std::endl; 13 | std::cout << norm_2(v3) << std::endl; 14 | 15 | Vec<4, double> v = Vec<4,double>(1, 2.1f, 3.14, 2u); 16 | 17 | std::cout << v << std::endl; 18 | 19 | Vec<3,double> v0; 20 | 21 | std::cout << v0 << std::endl; 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /examples/BEM/MeshIO.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * General purpose mesh reading utilities 5 | */ 6 | 7 | namespace MeshIO 8 | { 9 | 10 | // split a string by white space 11 | void split(const std::string& str, std::vector& v) 12 | { 13 | std::stringstream ss(str); 14 | ss >> std::noskipws; 15 | 16 | std::string field; 17 | char ws_delim; 18 | 19 | while (1) { 20 | if (ss >> field) 21 | v.push_back(field); 22 | else if (ss.eof()) 23 | break; 24 | else 25 | v.push_back(std::string()); 26 | ss.clear(); 27 | ss >> ws_delim; 28 | } 29 | } 30 | 31 | }; // end namespace MeshIO 32 | -------------------------------------------------------------------------------- /include/executor/ExecutorBase.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /** @class ExecutorBase 6 | * @brief Abstract Executor class 7 | */ 8 | template 9 | struct ExecutorBase { 10 | //! Kernel type 11 | typedef Kernel kernel_type; 12 | //! Kernel charge type 13 | typedef typename kernel_type::charge_type charge_type; 14 | //! Kernel result type 15 | typedef typename kernel_type::result_type result_type; 16 | 17 | /** Virtual Destructor */ 18 | virtual ~ExecutorBase() {}; 19 | 20 | // TODO: improve? 21 | /** Virtual execute */ 22 | virtual void execute(const std::vector& charges, 23 | std::vector& results) = 0; 24 | }; 25 | -------------------------------------------------------------------------------- /include/executor/EvaluatorBase.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | template 6 | struct EvaluatorBase { 7 | typedef Context context_type; 8 | 9 | virtual ~EvaluatorBase() {}; 10 | virtual void execute(context_type&) const = 0; 11 | }; 12 | 13 | 14 | 15 | template 16 | class EvaluatorCollection : public EvaluatorBase 17 | { 18 | typedef Context context_type; 19 | 20 | //! Evaluator algorithms to apply 21 | std::vector*> evals_; 22 | 23 | public: 24 | 25 | virtual ~EvaluatorCollection() { 26 | for (auto eval : evals_) 27 | delete eval; 28 | } 29 | 30 | void insert(EvaluatorBase* eval) { 31 | if (eval) 32 | evals_.push_back(eval); 33 | } 34 | 35 | void execute(context_type& context) const { 36 | for (auto eval : evals_) 37 | eval->execute(context); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /include/Matvec.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * matvec for ublas, where: 5 | * matrix elements are Mat3 6 | * vector elements are Vec<3,double> 7 | */ 8 | 9 | #include 10 | #include 11 | #include "Mat3.hpp" 12 | #include "Vec.hpp" 13 | 14 | template 15 | ResultVector Matvec(const Matrix& A, const Vector& x) 16 | { 17 | ResultVector r(x.size(),typename ResultVector::value_type(0)); 18 | 19 | // get internal details from A 20 | auto& offsets = A.index1_data(); 21 | auto& indices = A.index2_data(); 22 | auto& values = A.value_data(); 23 | 24 | // loop over rows 25 | for (unsigned i=0; i 4 | #include 5 | 6 | namespace DataReaders { 7 | 8 | template 9 | void readVertices(const char *fname, std::vector& vertices) 10 | { 11 | std::ifstream file(fname); 12 | std::string str; 13 | 14 | while(getline(file, str)) { 15 | // split the string into x y z 16 | std::istringstream s(str); 17 | double x, y, z; 18 | s >> x >> y >> z; 19 | 20 | // create and append point_type(x,y,z) to array 21 | 22 | } 23 | 24 | file.close(); 25 | } 26 | 27 | void readTriangle(const char *fname) 28 | { 29 | std::ifstream file(fname); 30 | std::string str; 31 | 32 | while (getline(file, str)) { 33 | // split the string into vertices 34 | std::istringstream s(str); 35 | 36 | unsigned v1, v2, v3; 37 | s >> v1 >> v3 >> v2; 38 | 39 | // create and append data structure 40 | } 41 | 42 | file.close(); 43 | } 44 | 45 | }; // end namespace DataReaders 46 | -------------------------------------------------------------------------------- /include/executor/INITL.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** @file INITL.hpp 3 | * @brief Dispatch methods for the initializing a local expansion 4 | * 5 | */ 6 | 7 | #include "KernelTraits.hpp" 8 | #include 9 | 10 | struct INITL 11 | { 12 | /** If no init_local dispatcher matches */ 13 | template 14 | inline static void eval(Args...) { 15 | // Do nothing 16 | } 17 | 18 | template 19 | inline static 20 | typename std::enable_if::has_init_local>::type 21 | eval(const Kernel& K, 22 | typename Kernel::local_type& L, 23 | const typename Kernel::point_type& extents, 24 | unsigned level) { 25 | K.init_local(L, extents, level); 26 | } 27 | 28 | template 29 | inline static void eval(const Kernel& K, 30 | Context& bc, 31 | const typename Context::box_type& b) 32 | { 33 | #ifdef DEBUG 34 | printf("initL: %d\n", b.index()); 35 | #endif 36 | 37 | INITL::eval(K, bc.local_expansion(b), b.extents(), b.level()); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /include/executor/INITM.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** @file INITM.hpp 3 | * @brief Dispatch methods for the initializing a multipole expansion 4 | * 5 | */ 6 | 7 | #include "KernelTraits.hpp" 8 | #include 9 | 10 | struct INITM 11 | { 12 | /** If no init_multipole dispatcher matches */ 13 | template 14 | inline static void eval(Args...) { 15 | // Do nothing 16 | } 17 | 18 | template 19 | inline static 20 | typename std::enable_if::has_init_multipole>::type 21 | eval(const Kernel& K, 22 | typename Kernel::multipole_type& M, 23 | const typename Kernel::point_type& extents, 24 | unsigned level) { 25 | K.init_multipole(M, extents, level); 26 | } 27 | 28 | template 29 | inline static void eval(const Kernel& K, 30 | Context& bc, 31 | const typename Context::box_type& b) 32 | { 33 | #ifdef DEBUG 34 | printf("initM: %d\n", b.index()); 35 | #endif 36 | 37 | INITM::eval(K, bc.multipole_expansion(b), b.extents(), b.level()); 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /examples/BEM/DirectMatvec.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Simple wrapper class to use direct evaluation in new GMRES implementation 5 | */ 6 | 7 | #include "Direct.hpp" 8 | 9 | template 10 | class DirectMV 11 | { 12 | public: 13 | typedef typename Kernel::charge_type charge_type; 14 | typedef typename Kernel::source_type source_type; 15 | typedef typename Kernel::source_type target_type; 16 | typedef typename Kernel::result_type result_type; 17 | 18 | private: 19 | const Kernel& K; 20 | // references to sources, targets & results 21 | const std::vector& sources; 22 | const std::vector& targets; 23 | 24 | public: 25 | 26 | DirectMV(const Kernel& k, const std::vector& s, const std::vector& t) 27 | : K(k), sources(s), targets(t) {}; 28 | 29 | void set_p(int p) 30 | { 31 | (void) p; 32 | } 33 | 34 | std::vector execute(std::vector& charges, unsigned dummy) 35 | { 36 | (void) dummy; 37 | std::vector results(targets.size(),0); 38 | Direct::matvec(K, sources.begin(), sources.end(), charges.begin(), 39 | targets.begin(), targets.end(), results.begin()); 40 | return results; 41 | } 42 | 43 | }; 44 | -------------------------------------------------------------------------------- /include/executor/EvalDownward.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EvaluatorBase.hpp" 4 | 5 | #include "L2L.hpp" 6 | #include "L2P.hpp" 7 | 8 | template 9 | struct EvalDownward : public EvaluatorBase 10 | { 11 | void execute(Context& bc) const { 12 | auto& tree = bc.target_tree(); 13 | 14 | // For the highest level down to the lowest level 15 | for (unsigned l = 1; l < tree.levels(); ++l) { 16 | // For all boxes at this level 17 | auto b_end = tree.box_end(l); 18 | for (auto bit = tree.box_begin(l); bit != b_end; ++bit) { 19 | auto box = *bit; 20 | 21 | // Initialize box data 22 | if (box.is_leaf()) { 23 | // If leaf, make L2P calls 24 | L2P::eval(bc.kernel(), bc, box); 25 | } else { 26 | // If not leaf, then for all the children L2L 27 | auto c_end = box.child_end(); 28 | for (auto cit = box.child_begin(); cit != c_end; ++cit) 29 | L2L::eval(bc.kernel(), bc, box, *cit); 30 | } 31 | } 32 | } 33 | } 34 | }; 35 | 36 | 37 | template 38 | EvaluatorBase* make_downward(Context&, Options& opts) { 39 | if (opts.evaluator == FMMOptions::FMM) 40 | return new EvalDownward(); 41 | return nullptr; 42 | } 43 | -------------------------------------------------------------------------------- /examples/BEM/SolverOptions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Central place to store Solver config options 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | struct SolverOptions 12 | { 13 | double residual; 14 | int max_iters, restart; 15 | unsigned max_p, p_min; 16 | bool variable_p; 17 | 18 | enum relaxation_type { SIMONCINI, BOURAS }; 19 | 20 | relaxation_type relax_type; 21 | 22 | SolverOptions(double r, int m_iters, unsigned p) : residual(r), max_iters(m_iters), restart(50), max_p(p), variable_p(false), relax_type(BOURAS) {}; 23 | SolverOptions() : residual(1e-5), max_iters(500), restart(500), max_p(16), p_min(5), variable_p(true), relax_type(BOURAS) {}; 24 | 25 | unsigned predict_p(double eps) const { 26 | // if no relaxation, return default p 27 | if (!this->variable_p) return max_p; 28 | // relaxation from Bouras & Fraysse 29 | if (this->relax_type == BOURAS) { 30 | double alpha = 1. / std::min(eps, 1.); 31 | double nu = std::min(alpha*this->residual,1.); 32 | // predict p for Spherical Laplace kernel -- abstract out 33 | return std::min((unsigned)ceil(-log2(nu)),max_p); 34 | } else if (this->relax_type == SIMONCINI) { 35 | return std::min((unsigned)ceil(-log2(eps)),max_p); 36 | } 37 | return max_p; 38 | } 39 | }; 40 | 41 | -------------------------------------------------------------------------------- /examples/BEM/Preconditioner.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** Base and derived precondtioners for BEM problems */ 4 | 5 | namespace Preconditioners { 6 | 7 | /** Identity preconditioner -- no change */ 8 | class Identity 9 | { 10 | public: 11 | template 12 | void operator()(const VecType& x, VecType& y) const { 13 | auto yit = y.begin(); 14 | for (auto it=x.begin(); it!=x.end(); ++it, ++yit) *yit = *it; 15 | }; 16 | }; 17 | 18 | /** Diagonal PC -- divide by Panel self-interactions */ 19 | template 20 | class Diagonal 21 | { 22 | public: 23 | template 24 | Diagonal(Kernel& K, SourceIter SourceBegin, SourceIter SourceEnd) { 25 | Reciprocals.resize(SourceEnd-SourceBegin); 26 | auto Rit = Reciprocals.begin(); 27 | for ( ; SourceBegin!=SourceEnd; ++SourceBegin, ++Rit) { 28 | // generate the reciprocal of the self-interaction 29 | *Rit = 1./K(*SourceBegin, *SourceBegin); 30 | } 31 | } 32 | 33 | template 34 | void operator()(const VecType& x, VecType& y) const { 35 | auto yit = y.begin(); 36 | auto Rit = Reciprocals.begin(); 37 | for (auto it=x.begin(); it!=x.end(); ++it, ++yit, ++Rit) *yit = (*Rit) * (*it); 38 | } 39 | private: 40 | std::vector Reciprocals; 41 | }; 42 | 43 | }; // end namespace Preconditioners 44 | -------------------------------------------------------------------------------- /examples/BEM/BioMeshReader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | /** Class to read a mesh from following files: 9 | * .vert | x y z 10 | * .face | v1 v3 v2 11 | * into panels */ 12 | template 13 | class BioMeshReader 14 | { 15 | public: 16 | static void ReadMesh(const char *vert_file, const char *face_file, std::vector& panels) { 17 | 18 | // temp storage for vertices 19 | std::vector vertices; 20 | 21 | std::ifstream vert(vert_file); 22 | std::ifstream face(face_file); 23 | 24 | std::string line; 25 | // read vertices 26 | while (getline(vert,line)) { 27 | std::istringstream i(line); 28 | 29 | double x, y, z; 30 | i >> x >> y >> z; 31 | 32 | // create panel -- for now just print 33 | vertices.push_back(PointType(x,y,z)); 34 | } 35 | vert.close(); 36 | 37 | while (getline(face,line)) { 38 | std::istringstream i(line); 39 | 40 | int v1, v2, v3; 41 | i >> v1 >> v3 >> v2; 42 | 43 | // deal with 0 vs. 1-indexing 44 | v1--; v2--; v3--; 45 | 46 | // append this panel 47 | panels.push_back(PanelType(vertices[v1], vertices[v2], vertices[v3])); 48 | } 49 | 50 | face.close(); 51 | } 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /include/executor/L2L.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** @file L2L.hpp 3 | * @brief Dispatch methods for the L2L stage 4 | * 5 | */ 6 | 7 | #include "KernelTraits.hpp" 8 | #include 9 | 10 | struct L2L 11 | { 12 | /** If no other L2L dispatcher matches */ 13 | template 14 | inline static void eval(Args...) { 15 | std::cerr << "Kernel does not have a correct L2L!\n"; 16 | exit(1); 17 | } 18 | 19 | template 20 | inline static 21 | typename std::enable_if::has_L2L>::type 22 | eval(const Kernel& K, 23 | const typename Kernel::local_type& source, 24 | typename Kernel::local_type& target, 25 | const typename Kernel::point_type& translation) { 26 | K.L2L(source, target, translation); 27 | } 28 | 29 | public: 30 | 31 | template 32 | inline static void eval(const Kernel& K, 33 | Context& bc, 34 | const typename Context::box_type& source, 35 | const typename Context::box_type& target) 36 | { 37 | #ifdef DEBUG 38 | printf("M2M: %d to %d\n", source.index(), target.index()); 39 | #endif 40 | 41 | typename Kernel::point_type r = bc.center(target) - bc.center(source); 42 | L2L::eval(K, 43 | bc.local_expansion(source), 44 | bc.local_expansion(target), 45 | r); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /include/executor/M2L.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** @file M2L.hpp 3 | * @brief Dispatch methods for the M2L stage 4 | * 5 | */ 6 | 7 | #include "KernelTraits.hpp" 8 | #include 9 | 10 | class M2L 11 | { 12 | /** If no other M2L dispatcher matches */ 13 | template 14 | inline static void eval(Args...) { 15 | std::cerr << "Kernel does not have a correct M2L!\n"; 16 | exit(1); 17 | } 18 | 19 | template 20 | inline static 21 | typename std::enable_if::has_M2L>::type 22 | eval(const Kernel& K, 23 | const typename Kernel::multipole_type& source, 24 | typename Kernel::local_type& target, 25 | const typename Kernel::point_type& translation) { 26 | K.M2L(source, target, translation); 27 | } 28 | 29 | public: 30 | 31 | template 32 | inline static void eval(const Kernel& K, 33 | Context& bc, 34 | const typename Context::box_type& source, 35 | const typename Context::box_type& target) 36 | { 37 | #ifdef DEBUG 38 | printf("M2L: %d to %d\n", source.index(), target.index()); 39 | #endif 40 | 41 | typename Kernel::point_type r = bc.center(target) - bc.center(source); 42 | M2L::eval(K, 43 | bc.multipole_expansion(source), 44 | bc.local_expansion(target), 45 | r); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /include/executor/M2M.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** @file M2M.hpp 3 | * @brief Dispatch methods for the M2M stage 4 | * 5 | */ 6 | 7 | #include "KernelTraits.hpp" 8 | #include 9 | 10 | class M2M 11 | { 12 | /** If no other M2M dispatcher matches */ 13 | template 14 | inline static void eval(Args...) { 15 | std::cerr << "Kernel does not have a correct M2M!\n"; 16 | exit(1); 17 | } 18 | 19 | template 20 | inline static 21 | typename std::enable_if::has_M2M>::type 22 | eval(const Kernel& K, 23 | const typename Kernel::multipole_type& source, 24 | typename Kernel::multipole_type& target, 25 | const typename Kernel::point_type& translation) { 26 | K.M2M(source, target, translation); 27 | } 28 | 29 | public: 30 | 31 | template 32 | inline static void eval(const Kernel& K, 33 | Context& bc, 34 | const typename Context::box_type& source, 35 | const typename Context::box_type& target) 36 | { 37 | #ifdef DEBUG 38 | printf("M2M: %d to %d\n", source.index(), target.index()); 39 | #endif 40 | 41 | typename Kernel::point_type r = bc.center(target) - bc.center(source); 42 | M2M::eval(K, 43 | bc.multipole_expansion(source), 44 | bc.multipole_expansion(target), 45 | r); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /include/executor/EvalUpward.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EvaluatorBase.hpp" 4 | 5 | #include "INITM.hpp" 6 | #include "INITL.hpp" 7 | 8 | #include "P2M.hpp" 9 | #include "M2M.hpp" 10 | 11 | template 12 | struct EvalUpward : public EvaluatorBase 13 | { 14 | void execute(Context& bc) const { 15 | auto& tree = bc.source_tree(); 16 | 17 | // For the lowest level up to the highest level 18 | for (unsigned l = tree.levels()-1; l != 0; --l) { 19 | // For all boxes at this level 20 | auto b_end = tree.box_end(l); 21 | for (auto bit = tree.box_begin(l); bit != b_end; ++bit) { 22 | auto box = *bit; 23 | 24 | // TODO: initialize on-demand? 25 | INITM::eval(bc.kernel(), bc, box); 26 | if (INITIALIZE_LOCAL) INITL::eval(bc.kernel(), bc, box); 27 | 28 | if (box.is_leaf()) { 29 | // If leaf, make P2M calls 30 | P2M::eval(bc.kernel(), bc, box); 31 | } else { 32 | // If not leaf, then for all the children M2M 33 | auto c_end = box.child_end(); 34 | for (auto cit = box.child_begin(); cit != c_end; ++cit) 35 | M2M::eval(bc.kernel(), bc, *cit, box); 36 | } 37 | } 38 | } 39 | } 40 | }; 41 | 42 | template 43 | EvaluatorBase* make_upward(Context&, Options& opts) { 44 | if (opts.evaluator == FMMOptions::TREECODE) 45 | return new EvalUpward(); 46 | if (opts.evaluator == FMMOptions::FMM) 47 | return new EvalUpward(); 48 | return nullptr; 49 | } 50 | -------------------------------------------------------------------------------- /test_tree.cpp: -------------------------------------------------------------------------------- 1 | #include "include/tree/Octree.hpp" 2 | #include "include/Vec.hpp" 3 | 4 | #include 5 | #include 6 | 7 | // Random number in [0,1) 8 | inline double drand() { 9 | return ::drand48(); 10 | } 11 | 12 | template 13 | void print_box(const Box& b, std::string padding = std::string()) { 14 | std::cout << padding << "Box " << b.index() 15 | << " (Level " << b.level() << ", Parent " << b.parent().index() << "): " 16 | << b.morton_index() << " " << b.center() << "\n"; 17 | 18 | padding.append(2,' '); 19 | for (auto ci = b.body_begin(); ci != b.body_end(); ++ci) 20 | std::cout << padding << "Point " << ci->index() << ": " << ci->morton_index() << "\t" << ci->point() << "\n"; 21 | if (!b.is_leaf()) { 22 | for (auto ci = b.child_begin(); ci != b.child_end(); ++ci) 23 | print_box(*ci, padding); 24 | } 25 | } 26 | 27 | 28 | 29 | int main(int argc, char** argv) 30 | { 31 | if (argc < 2) { 32 | std::cerr << "Usage: test_tree NUM_POINTS\n"; 33 | exit(1); 34 | } 35 | 36 | int N = atoi(argv[1]); 37 | 38 | typedef Vec<3,double[3]> point_type; 39 | 40 | Octree otree(BoundingBox(point_type(0), point_type(1))); 41 | 42 | std::vector points; 43 | for (int k = 0; k < N; ++k) { 44 | point_type p; 45 | p[0] = drand(); 46 | p[1] = drand(); 47 | p[2] = drand(); 48 | points.push_back(p); 49 | } 50 | 51 | otree.construct_tree(points.begin(), points.end()); 52 | 53 | std::cout << otree << "\n"; 54 | 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /examples/BEM/BlockDiagonalPC.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "FMM_plan.hpp" 4 | #include "Preconditioner.hpp" 5 | #include "GMRES.hpp" 6 | 7 | 8 | 9 | namespace Preconditioners { 10 | 11 | /** 12 | * Preconditioner solving diagonal inner near-field problem 13 | */ 14 | 15 | template 16 | class BlockDiagonal 17 | { 18 | typedef typename Plan::charge_type input_type; 19 | typedef typename Plan::result_type output_type; 20 | typedef std::vector vector_type; 21 | private: 22 | Plan plan; 23 | SolverOptions options; 24 | GMRESContext context; 25 | 26 | public: 27 | //! Call inner solver from preconditioner interface 28 | template 29 | void operator()(VecType x, VecType& y) { 30 | // y = x; 31 | // printf("Calling Preconditioners::LocalInnerSolver\n"); 32 | std::fill(y.begin(),y.end(),typename VecType::value_type(0.)); 33 | GMRES(plan, y, x, options); 34 | } 35 | 36 | // construct from sources & targets 37 | template 38 | BlockDiagonal(Kernel k, SourceVector& sources) 39 | : plan(k,sources,BlockDiagonal::local_options()), context(sources.size(), 50) { 40 | 41 | options.residual= 1e-1; 42 | options.variable_p = false; 43 | options.max_iters = 1; 44 | 45 | context.output = false; 46 | } 47 | 48 | static FMMOptions& local_options() 49 | { 50 | FMMOptions* opts = new FMMOptions; 51 | opts->local_evaluation = false; 52 | opts->lazy_evaluation = false; 53 | opts->set_mac_theta(0.5); 54 | opts->sparse_local = true; 55 | opts->block_diagonal = true; 56 | 57 | return *opts; 58 | } 59 | 60 | }; 61 | 62 | }; // end namespace Preconditioners 63 | -------------------------------------------------------------------------------- /examples/BEM/BEMConfig.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "GaussQuadrature.hpp" 4 | 5 | /** Singleton pattern to hold BEM config options 6 | * see: http://stackoverflow.com/questions/2496918/singleton-pattern-in-c */ 7 | class BEMConfig { 8 | 9 | public: 10 | static void Init(); 11 | static BEMConfig *Instance(); 12 | static void Destroy(); 13 | 14 | void setK(unsigned k) { 15 | K = k; 16 | } 17 | 18 | unsigned getK() { 19 | return K; 20 | } 21 | 22 | std::vector>& GaussPoints() { 23 | return GQ.points(K); 24 | } 25 | std::vector& GaussWeights() { 26 | return GQ.weights(K); 27 | } 28 | 29 | std::vector>& GaussPoints(int k) { 30 | return GQ.points(k); 31 | } 32 | std::vector& GaussWeights(int k) { 33 | return GQ.weights(k); 34 | } 35 | 36 | private: 37 | static BEMConfig* MyInstance(BEMConfig* pConfig); 38 | BEMConfig() { }; // static variables 39 | ~BEMConfig() {}; 40 | BEMConfig(const BEMConfig& config); 41 | BEMConfig operator=(const BEMConfig& config); 42 | 43 | // actual data 44 | unsigned K; 45 | GaussQuadrature GQ; 46 | }; 47 | 48 | inline void BEMConfig::Init() 49 | { 50 | BEMConfig* ptr = new BEMConfig(); 51 | MyInstance(ptr); 52 | atexit(&Destroy); 53 | } 54 | 55 | inline BEMConfig* BEMConfig::Instance() 56 | { 57 | return BEMConfig::MyInstance(NULL); 58 | } 59 | 60 | inline BEMConfig* BEMConfig::MyInstance(BEMConfig* ptr) 61 | { 62 | static BEMConfig* myInstance = NULL; 63 | if (ptr) myInstance = ptr; 64 | return myInstance; 65 | } 66 | 67 | inline void BEMConfig::Destroy() 68 | { 69 | BEMConfig* pConfig = MyInstance(NULL); 70 | if (pConfig) { 71 | delete pConfig; 72 | pConfig = 0; 73 | } 74 | } 75 | 76 | -------------------------------------------------------------------------------- /tests/ncrit_search.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | inline double drand() 7 | { 8 | return ::drand48(); 9 | } 10 | 11 | int main() 12 | { 13 | typedef LaplaceSpherical kernel_type; 14 | kernel_type K(5); 15 | typedef kernel_type::point_type point_type; 16 | typedef kernel_type::charge_type charge_type; 17 | typedef kernel_type::result_type result_type; 18 | 19 | FMMOptions opts; 20 | opts.set_mac_theta(.5); 21 | opts.set_max_per_box(125); // optimal ncrit 22 | 23 | std::vector> times; 24 | double tic, toc; 25 | 26 | 27 | int numBodies = 1000000; 28 | // initialize points 29 | std::vector points(numBodies); 30 | for (int k=0; k charges(numBodies); 36 | for (int k=0; k plan = FMM_plan(K, points, opts); 47 | // execute FMM 48 | // run 3 times and make an average 49 | int nt = 3; // number of identical runs for timing 50 | std::vector timings(nt); 51 | std::vector result(numBodies); 52 | for (int i=0; i 16 | class BlockDiagonal 17 | { 18 | typedef typename Plan::charge_type input_type; 19 | typedef typename Plan::result_type output_type; 20 | typedef std::vector vector_type; 21 | private: 22 | Plan plan; 23 | SolverOptions options; 24 | GMRESContext context; 25 | Preconditioners::Identity M; 26 | 27 | public: 28 | //! Call inner solver from preconditioner interface 29 | template 30 | void operator()(VecType x, VecType& y) { 31 | // y = x; 32 | // printf("Calling Preconditioners::LocalInnerSolver\n"); 33 | std::fill(y.begin(),y.end(),typename VecType::value_type(0.)); 34 | GMRES(plan, y, x, options, M, context); 35 | context.reset(); 36 | } 37 | 38 | // construct from sources & targets 39 | template 40 | BlockDiagonal(Kernel k, SourceVector& sources) 41 | : plan(k,sources,BlockDiagonal::local_options()), context(sources.size(), 50), M() { 42 | 43 | options.residual= 1e-1; 44 | options.variable_p = false; 45 | options.max_iters = 1; 46 | 47 | context.output = false; 48 | } 49 | 50 | static FMMOptions& local_options() 51 | { 52 | FMMOptions* opts = new FMMOptions; 53 | opts->local_evaluation = false; 54 | opts->lazy_evaluation = false; 55 | opts->set_mac_theta(0.5); 56 | opts->sparse_local = true; 57 | opts->block_diagonal = true; 58 | opts->set_max_per_box(100); 59 | 60 | return *opts; 61 | } 62 | 63 | }; 64 | 65 | }; // end namespace Preconditioners 66 | -------------------------------------------------------------------------------- /examples/BEM/fmgmres.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Preconditioner.hpp" 4 | #include "GMRES.hpp" 5 | 6 | namespace Preconditioners { 7 | 8 | template 9 | class FMGMRES 10 | { 11 | // typedefs 12 | typedef typename FMM_plan::charge_type input_type; 13 | typedef typename FMM_plan::result_type output_type; 14 | typedef std::vector vector_type; 15 | 16 | private: 17 | FMM_plan& plan; 18 | vector_type& rhs; 19 | const SolverOptions& options; 20 | Preconditioner& preconditioner; 21 | GMRESContext context; 22 | 23 | /* interface to upload / download input / output vectors 24 | template 25 | void vector_upload(const VecType& in, vector_type& out) { 26 | auto out_it = out.begin(); 27 | for (auto& it : in) { 28 | out_it++ = static_cast(it); 29 | } 30 | } 31 | template 32 | void vector_download(const vector_type& in, VecType& out) { 33 | auto out_it = out.begin(); 34 | for (auto& it : in) { 35 | out_it++ = static_cast(it); 36 | } 37 | } 38 | */ 39 | 40 | public: 41 | //! Call inner iteration from preconditioner interface 42 | // input / output vectors not necessarily same precision as input (worry about this later) 43 | // different precisions will require a conversion step (easy) 44 | template 45 | void operator()(VecType x, VecType& y) { 46 | GMRES(plan, y, x, options, preconditioner, context); 47 | } 48 | 49 | //! Need plan, RHS vector, Options & preconditioner (optional) 50 | template 51 | FMGMRES(FMM_plan& Plan, Vector& RHS, SolverOptions& opts, Preconditioner& P) 52 | : plan(Plan), rhs(RHS), options(opts), preconditioner(P), context(RHS.size(),opts.restart) {}; 53 | //! Constructor using default Preconditioner (by delagation) 54 | template 55 | FMGMRES(FMM_plan& Plan, Vector& RHS, SolverOptions& opts) 56 | : FMGMRES(Plan, RHS, opts, Preconditioners::Identity()) {}; 57 | 58 | }; 59 | 60 | }; // end namespace preconditioners 61 | -------------------------------------------------------------------------------- /examples/BEM/VertFaceReader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Read in a mesh defined as .vert and .face 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "MeshIO.hpp" 13 | 14 | namespace MeshIO 15 | { 16 | 17 | template 18 | void ReadVertFace(const char *vert_file, const char *face_file, std::vector& elements) 19 | { 20 | std::ifstream vert(vert_file); 21 | std::ifstream face(face_file); 22 | 23 | std::string str; 24 | 25 | // mesh info 26 | int num_vertices = 0, num_faces = 0; 27 | std::vector vertices; 28 | 29 | // get first line of vertices -- # vertices 30 | std::getline(vert, str); 31 | num_vertices = atoi(str.c_str()); 32 | vertices.resize(num_vertices); 33 | printf("# vertices: %d\n",num_vertices); 34 | 35 | std::vector split_str; 36 | // read all vertices 37 | for (int i=0; i 9 | 10 | struct L2P 11 | { 12 | /** If no other L2P dispatcher matches */ 13 | template 14 | inline static void eval(Args...) { 15 | std::cerr << "Kernel does not have a correct L2P!\n"; 16 | exit(1); 17 | } 18 | 19 | /** L2P evaluation. 20 | * The Kernel provides a vector L2P accumulator. 21 | */ 22 | template 23 | inline static 24 | typename std::enable_if::has_vector_L2P>::type 25 | eval(const Kernel& K, 26 | const typename Kernel::local_type& L, 27 | const typename Kernel::point_type& center, 28 | TargetIter t_begin, TargetIter t_end, ResultIter r_begin) { 29 | K.L2P(L, center, t_begin, t_end, r_begin); 30 | } 31 | 32 | /** L2P evaluation. 33 | * The Kernel provides a scalar L2P accumulator. Use it for each target point. 34 | */ 35 | template 36 | inline static 37 | typename std::enable_if::has_L2P & 38 | !ExpansionTraits::has_vector_L2P>::type 39 | eval(const Kernel& K, 40 | const typename Kernel::local_type& L, 41 | const typename Kernel::point_type& center, 42 | TargetIter t_begin, TargetIter t_end, ResultIter r_begin) { 43 | for ( ; t_begin != t_end; ++t_begin, ++r_begin) 44 | K.L2P(L, center, *t_begin, *r_begin); 45 | } 46 | 47 | public: 48 | 49 | /** Unwrap the data from BoxContext and dispatch to the L2P evaluator 50 | */ 51 | template 52 | inline static void eval(const Kernel& K, 53 | Context& bc, 54 | const typename Context::box_type& box) 55 | { 56 | #ifdef DEBUG 57 | printf("L2P: %d\n", box.index()); 58 | #endif 59 | 60 | L2P::eval(K, 61 | bc.local_expansion(box), bc.center(box), 62 | bc.target_begin(box), bc.target_end(box), bc.result_begin(box)); 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /include/executor/M2P.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** @file M2P.hpp 3 | * @brief Dispatch methods for the M2P stage 4 | * 5 | */ 6 | 7 | #include "KernelTraits.hpp" 8 | 9 | class M2P 10 | { 11 | /** If no other M2P dispatcher matches */ 12 | template 13 | inline static void eval(Args...) { 14 | std::cerr << "Kernel does not have a correct M2P!\n"; 15 | exit(1); 16 | } 17 | 18 | /** M2P evaluation. 19 | * The Kernel provides a vector M2P accumulator. 20 | */ 21 | template 22 | inline static 23 | typename std::enable_if::has_vector_M2P>::type 24 | eval(const Kernel& K, 25 | const typename Kernel::multipole_type& M, 26 | const typename Kernel::point_type& center, 27 | TargetIter t_begin, TargetIter t_end, 28 | ResultIter r_begin) { 29 | K.M2P(M, center, t_begin, t_end, r_begin); 30 | } 31 | 32 | /** M2P evaluation. 33 | * The Kernel provides a scalar M2P accumulator. Use it for each target point. 34 | */ 35 | template 36 | inline static 37 | typename std::enable_if::has_M2P & 38 | !ExpansionTraits::has_vector_M2P>::type 39 | eval(const Kernel& K, 40 | const typename Kernel::multipole_type& M, 41 | const typename Kernel::point_type& center, 42 | TargetIter t_begin, TargetIter t_end, 43 | ResultIter r_begin) { 44 | for ( ; t_begin != t_end; ++t_begin, ++r_begin) 45 | K.M2P(M, center, *t_begin, *r_begin); 46 | } 47 | 48 | public: 49 | 50 | /** Unwrap the data from BoxContext and dispatch to the M2P evaluator 51 | */ 52 | template 53 | inline static void eval(const Kernel& K, 54 | Context& bc, 55 | const typename Context::box_type& source, 56 | const typename Context::box_type& target) { 57 | #ifdef DEBUG 58 | printf("M2P: %d to %d\n", source.index(), target.index()); 59 | #endif 60 | 61 | M2P::eval(K, 62 | bc.multipole_expansion(source), bc.center(source), 63 | bc.target_begin(target), bc.target_end(target), 64 | bc.result_begin(target)); 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /examples/BEM/LocalPC_Stokes.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "FMM_plan.hpp" 4 | #include "Preconditioner.hpp" 5 | #include "GMRES_Stokes.hpp" 6 | 7 | FMMOptions& local_options() 8 | { 9 | FMMOptions* opts = new FMMOptions; 10 | opts->local_evaluation = true; 11 | opts->lazy_evaluation = false; 12 | opts->set_mac_theta(0.5); 13 | opts->sparse_local = true; 14 | opts->set_max_per_box(100); 15 | 16 | return *opts; 17 | } 18 | 19 | 20 | namespace Preconditioners { 21 | 22 | /** 23 | * Preconditioner solving inner near-field problem 24 | */ 25 | 26 | template 27 | class LocalInnerSolver 28 | { 29 | typedef typename Plan::charge_type input_type; 30 | typedef typename Plan::result_type output_type; 31 | typedef std::vector vector_type; 32 | private: 33 | Plan plan; 34 | SolverOptions options; 35 | GMRESContext context; 36 | Preconditioners::Identity M; 37 | 38 | public: 39 | //! Call inner solver from preconditioner interface 40 | template 41 | void operator()(VecType x, VecType& y) { 42 | // y = x; 43 | // printf("Calling Preconditioners::LocalInnerSolver\n"); 44 | std::fill(y.begin(),y.end(),typename VecType::value_type(0.)); 45 | 46 | GMRES(plan, y, x, options, M, context); 47 | context.reset(); 48 | } 49 | 50 | // construct from sources & targets 51 | template 52 | LocalInnerSolver(Kernel k, SourceVector& sources, ResultVector& RHS) 53 | : plan(k,sources,local_options()), context(RHS.size(), 50), M() { 54 | //: plan(k,sources,local_options()), rhs(RHS), context(RHS.size(), 50) { 55 | 56 | options.residual= 1e-1; 57 | options.variable_p = false; 58 | options.max_iters = 1; 59 | 60 | context.output = false; 61 | } 62 | 63 | /* 64 | 65 | // need plan, RHS, options & preconditioner 66 | template 67 | LocalInnerSolver(Plan& p, Vector& RHS, SolverOptions& opts, Preconditioner& P) 68 | : plan(p), rhs(RHS), options(opts), preconditioner(P), context(RHS.size(), opts.restart) {}; 69 | 70 | // constructor using default preconditioner 71 | template 72 | LocalInnerSolver(Plan& p, Vector& RHS, SolverOptions& opts) 73 | : LocalInnerSolver(p, RHS, opts, Preconditioners::Identity()) {}; 74 | */ 75 | }; 76 | 77 | }; // end namespace Preconditioners 78 | -------------------------------------------------------------------------------- /include/executor/P2M.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** @file P2M.hpp 3 | * @brief Dispatch methods for the P2M stage 4 | * 5 | */ 6 | 7 | #include "KernelTraits.hpp" 8 | #include 9 | 10 | class P2M 11 | { 12 | /** If no other P2M dispatcher matches */ 13 | template 14 | inline static void eval(Args...) { 15 | std::cerr << "Kernel does not have a correct P2M!\n"; 16 | exit(1); 17 | } 18 | 19 | /** P2M evaluation. 20 | * The Kernel provides a vector P2M accumulator. 21 | */ 22 | template 23 | inline static 24 | typename std::enable_if::has_vector_P2M>::type 25 | eval(const Kernel& K, 26 | SourceIter s_begin, SourceIter s_end, 27 | ChargeIter c_begin, 28 | const typename Kernel::point_type& center, 29 | typename Kernel::multipole_type& M) { 30 | K.P2M(s_begin, s_end, c_begin, center, M); 31 | } 32 | 33 | /** P2M evaluation. 34 | * The Kernel provides a scalar P2M accumulator. Use it for each source point. 35 | */ 36 | template 37 | inline static 38 | typename std::enable_if::has_P2M & 39 | !ExpansionTraits::has_vector_P2M>::type 40 | eval(const Kernel& K, 41 | SourceIter s_begin, SourceIter s_end, 42 | ChargeIter c_begin, 43 | const typename Kernel::point_type& center, 44 | typename Kernel::multipole_type& M) { 45 | for ( ; s_begin != s_end; ++s_begin, ++c_begin) 46 | K.P2M(*s_begin, *c_begin, center, M); 47 | } 48 | 49 | public: 50 | 51 | /** Unwrap the data from the BoxContext and dispatch to P2M evaluator 52 | */ 53 | template 54 | inline static void eval(const Kernel& K, 55 | Context& bc, 56 | const typename Context::box_type& box) 57 | { 58 | #ifdef DEBUG 59 | printf("P2M: %d\n", box.index()); 60 | printf(" Bodies %d-%d\n", box.body_begin()->index(), (--box.body_end())->index()); 61 | printf(" Bodies %d-%d\n", box.body_begin()->number(), (--box.body_end())->number()); 62 | #endif 63 | 64 | P2M::eval(K, 65 | bc.source_begin(box), bc.source_end(box), bc.charge_begin(box), 66 | bc.center(box), bc.multipole_expansion(box)); 67 | } 68 | }; 69 | -------------------------------------------------------------------------------- /examples/BEM/LocalPC.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "FMM_plan.hpp" 4 | #include "Preconditioner.hpp" 5 | #include "GMRES.hpp" 6 | 7 | FMMOptions& local_options() 8 | { 9 | FMMOptions* opts = new FMMOptions; 10 | opts->local_evaluation = true; 11 | opts->lazy_evaluation = false; 12 | opts->set_mac_theta(0.5); 13 | opts->sparse_local = true; 14 | 15 | return *opts; 16 | } 17 | 18 | 19 | namespace Preconditioners { 20 | 21 | /** 22 | * Preconditioner solving inner near-field problem 23 | */ 24 | 25 | template 26 | class LocalInnerSolver 27 | { 28 | typedef typename Plan::charge_type input_type; 29 | typedef typename Plan::result_type output_type; 30 | typedef std::vector vector_type; 31 | private: 32 | Plan plan; 33 | vector_type& rhs; 34 | SolverOptions options; 35 | Preconditioner preconditioner; 36 | GMRESContext context; 37 | 38 | public: 39 | //! Call inner solver from preconditioner interface 40 | template 41 | void operator()(VecType x, VecType& y) { 42 | // y = x; 43 | // printf("Calling Preconditioners::LocalInnerSolver\n"); 44 | std::fill(y.begin(),y.end(),typename VecType::value_type(0.)); 45 | GMRES(plan, y, x, options); // , preconditioner); 46 | } 47 | 48 | // construct from sources & targets 49 | template 50 | LocalInnerSolver(Kernel k, SourceVector& sources, ResultVector& RHS) 51 | : plan(k,sources,local_options()), rhs(RHS), preconditioner(k,sources.begin(),sources.end()), context(RHS.size(), 50) { 52 | //: plan(k,sources,local_options()), rhs(RHS), context(RHS.size(), 50) { 53 | 54 | options.residual= 1e-1; 55 | options.variable_p = false; 56 | options.max_iters = 1; 57 | 58 | context.output = false; 59 | } 60 | 61 | /* 62 | 63 | // need plan, RHS, options & preconditioner 64 | template 65 | LocalInnerSolver(Plan& p, Vector& RHS, SolverOptions& opts, Preconditioner& P) 66 | : plan(p), rhs(RHS), options(opts), preconditioner(P), context(RHS.size(), opts.restart) {}; 67 | 68 | // constructor using default preconditioner 69 | template 70 | LocalInnerSolver(Plan& p, Vector& RHS, SolverOptions& opts) 71 | : LocalInnerSolver(p, RHS, opts, Preconditioners::Identity()) {}; 72 | */ 73 | }; 74 | 75 | }; // end namespace Preconditioners 76 | -------------------------------------------------------------------------------- /tests/scaling.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | inline double drand() 7 | { 8 | return ::drand48(); 9 | } 10 | 11 | int main() 12 | { 13 | typedef LaplaceSpherical kernel_type; 14 | kernel_type K(5); 15 | typedef kernel_type::point_type point_type; 16 | typedef kernel_type::charge_type charge_type; 17 | typedef kernel_type::result_type result_type; 18 | 19 | FMMOptions opts; 20 | opts.set_mac_theta(.5); 21 | opts.set_max_per_box(125); // optimal ncrit 22 | 23 | std::vector> times; 24 | double tic, toc; 25 | 26 | 27 | int numBodies = 10000; 28 | // initialize points 29 | std::vector points(numBodies); 30 | for (int k=0; k charges(numBodies); 36 | for (int k=0; k plan = FMM_plan(K, points, opts); 42 | // execute FMM 43 | // run 3 times and make an average 44 | int nt = 3; // number of identical runs for timing 45 | std::vector timings(nt); 46 | std::vector result(numBodies); 47 | for (int i=0; i exact(numBodies); 58 | // compute using direct summation 59 | tic = get_time(); 60 | Direct::matvec(K, points, charges, exact); 61 | toc = get_time(); 62 | 63 | std::cout << "direct summation execution time: " << toc-tic << std::endl; 64 | 65 | // calculate l2 norm of relative error of the Laplace force 66 | double e1 = 0; // numerator 67 | double e2 = 0; // denominator 68 | for (int k=0; k points(numBodies); 49 | for (int k = 0; k < numBodies; ++k) 50 | points[k] = source_type(drand(), drand(), drand()); 51 | 52 | std::vector charges(numBodies); 53 | for (int k = 0; k < numBodies; ++k) 54 | charges[k] = drand(); 55 | 56 | // Build the FMM 57 | //fmm_plan plan = fmm_plan(K, bodies, opts); 58 | FMM_plan plan = FMM_plan(K, points, opts); 59 | 60 | // Execute the FMM 61 | //fmm_execute(plan, charges, target_points); 62 | std::vector result = plan.execute(charges); 63 | 64 | // Check the result 65 | if (checkErrors) { 66 | std::vector exact(numBodies); 67 | 68 | // Compute the result with a direct matrix-vector multiplication 69 | Direct::matvec(K, points, charges, exact); 70 | 71 | int wrong_results = 0; 72 | for (unsigned k = 0; k < result.size(); ++k) { 73 | printf("[%03d] exact: %lg, FMM: %lg\n", k, exact[k], result[k]); 74 | 75 | if ((exact[k] - result[k]) / exact[k] > 1e-13) 76 | ++wrong_results; 77 | } 78 | printf("Wrong counts: %d\n", wrong_results); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /include/Mat3.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Simple 3x3 matrix class for analytical / semi-analytical integrals 5 | */ 6 | 7 | template 8 | struct Mat3 { 9 | // 3 rows, 3 cols, 9 values 10 | T vals_[9]; 11 | 12 | Mat3() { for (unsigned i=0; i<9; i++) vals_[i] = 0.; }; 13 | template 14 | Mat3(IterType start, IterType end) { 15 | assert(end-start == 9); // ensure correct # values 16 | unsigned i=0; 17 | for ( ; start!=end; ++start, ++i) vals_[i] = *start; 18 | }; 19 | // copy constructor 20 | Mat3(const Mat3& M) { 21 | for (unsigned i=0; i<9; i++) vals_[i] = M.vals_[i]; 22 | } 23 | // initialise with a uniform value 24 | Mat3(double v) { 25 | for (unsigned i=0; i<9; i++) vals_[i] = v; 26 | } 27 | 28 | // return negated matrix 29 | Mat3 operator-() const { 30 | Mat3 temp; 31 | for (unsigned i=0; i<9; i++) temp.vals_[i] = -vals_[i]; 32 | return temp; 33 | }; 34 | // accessors 35 | const T& operator()(unsigned i, unsigned j) const { 36 | return vals_[i*3+j]; 37 | }; 38 | T& operator()(unsigned i, unsigned j) { 39 | return vals_[i*3+j]; 40 | }; 41 | // M = M2 42 | Mat3& operator=(const Mat3& M) { 43 | if (this == &M) return *this; 44 | for (unsigned i=0; i<9; i++) this->vals_[i] = M.vals_[i]; 45 | return *this; 46 | } 47 | // M += M2 48 | Mat3& operator+=(const Mat3& M) { 49 | for (unsigned i=0; i<9; i++) this->vals_[i] += M.vals_[i]; 50 | 51 | return *this; 52 | } 53 | // M = M1 + M2 54 | Mat3 operator+(const Mat3& M) { 55 | Mat3 temp(*this); 56 | 57 | return (temp += M); 58 | } 59 | Vec<3,T> operator*(const Vec<3,T>& x) const { 60 | Vec<3,T> result; 61 | result[0] = vals_[0]*x[0]+vals_[1]*x[1]+vals_[2]*x[2]; 62 | result[1] = vals_[3]*x[0]+vals_[4]*x[1]+vals_[5]*x[2]; 63 | result[2] = vals_[6]*x[0]+vals_[7]*x[1]+vals_[8]*x[2]; 64 | return result; 65 | } 66 | Mat3 operator*(const double x) const { 67 | Mat3 result; 68 | 69 | for (unsigned i=0; i<9; i++) result.vals_[i] = x*vals_[i]; 70 | 71 | return result; 72 | } 73 | // matvec 74 | Vec<3,T> multiply(const Vec<3,T>& x) const { 75 | Vec<3,T> result; 76 | result[0] = vals_[0]*x[0]+vals_[1]*x[1]+vals_[2]*x[2]; 77 | result[1] = vals_[3]*x[0]+vals_[4]*x[1]+vals_[5]*x[2]; 78 | result[2] = vals_[6]*x[0]+vals_[7]*x[1]+vals_[8]*x[2]; 79 | return result; 80 | } 81 | Mat3 multiply(const double x) const { 82 | Mat3 M(*this); 83 | 84 | for (unsigned i=0; i<9; i++) M.vals_[i]*=x; 85 | return M; 86 | } 87 | }; 88 | -------------------------------------------------------------------------------- /test_direct.cpp: -------------------------------------------------------------------------------- 1 | #include "include/Direct.hpp" 2 | #include "include/Vec.hpp" 3 | 4 | #include 5 | #include 6 | 7 | struct TempKernel { 8 | //! Multipole expansion type 9 | typedef unsigned multipole_type; 10 | //! Local expansion type 11 | typedef unsigned local_type; 12 | 13 | //! The dimension of the Kernel 14 | static constexpr unsigned dimension = 3; 15 | //! Point type 16 | // typedef vec point_type; 17 | typedef Vec point_type; 18 | //! Charge type 19 | typedef double charge_type; 20 | //! The return type of a kernel evaluation 21 | typedef unsigned kernel_value_type; 22 | //! The product of the kernel_value_type and the charge_type 23 | typedef double result_type; 24 | 25 | kernel_value_type operator()(const point_type& t, 26 | const point_type& s) const { 27 | (void) t; 28 | (void) s; 29 | 30 | std::cout << "In op()\n"; 31 | 32 | return kernel_value_type(1); 33 | } 34 | 35 | void P2P(int a) const { std::cout << "In P2P(int)\n"; } 36 | 37 | kernel_value_type transpose(const kernel_value_type& kst) const { 38 | std::cout << "In Transpose\n"; 39 | return kernel_value_type(1); 40 | } 41 | 42 | #if 1 43 | template 44 | void P2P(PointIter s_begin, PointIter s_end, ChargeIter c_begin, 45 | PointIter t_begin, PointIter t_end, ResultIter r_begin) const { 46 | (void) s_begin; 47 | (void) s_end; 48 | (void) c_begin; 49 | (void) t_begin; 50 | (void) t_end; 51 | (void) r_begin; 52 | 53 | std::cout << "In P2P\n"; 54 | } 55 | #endif 56 | }; 57 | 58 | 59 | 60 | 61 | int main() { 62 | typedef TempKernel kernel_type; 63 | typedef kernel_type::point_type point_type; 64 | typedef kernel_type::charge_type charge_type; 65 | typedef kernel_type::result_type result_type; 66 | typedef kernel_type::multipole_type multipole_type; 67 | typedef kernel_type::local_type local_type; 68 | 69 | kernel_type K; 70 | 71 | // init source 72 | std::vector points(1); 73 | points[0] = point_type(0,0,0); 74 | 75 | // init charge 76 | std::vector charges(1); 77 | charges[0] = 1.; 78 | 79 | // init target 80 | std::vector target(1); 81 | target[0] = point_type(1,1,1); 82 | 83 | // init results vectors for exact 84 | std::vector exact(target.size()); 85 | 86 | // test direct 87 | Direct::matvec(K, points, charges, exact); 88 | 89 | std::cout << exact[0] << "\n"; 90 | } 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /tests/dual_correctness.cpp: -------------------------------------------------------------------------------- 1 | /** @file correctness.cpp 2 | * @brief Test the tree and tree traversal by running an instance 3 | * of the UnitKernel with random points and charges 4 | */ 5 | 6 | #include "FMM_plan.hpp" 7 | #include "UnitKernel.hpp" 8 | 9 | // Random number in [0,1) 10 | inline double drand() { 11 | return ::drand48(); 12 | } 13 | 14 | // Random number in [A,B) 15 | inline double drand(double A, double B) { 16 | return (B-A) * drand() + A; 17 | } 18 | 19 | int main(int argc, char **argv) 20 | { 21 | int numBodies = 1000; 22 | bool checkErrors = true; 23 | 24 | // Parse custom command line args 25 | for (int i = 1; i < argc; ++i) { 26 | if (strcmp(argv[i],"-N") == 0) { 27 | i++; 28 | numBodies = atoi(argv[i]); 29 | } else if (strcmp(argv[i],"-nocheck") == 0) { 30 | checkErrors = false; 31 | } 32 | } 33 | 34 | // Init the FMM Kernel and options 35 | FMMOptions opts = get_options(argc, argv); 36 | typedef UnitKernel kernel_type; 37 | kernel_type K; 38 | 39 | typedef kernel_type::point_type point_type; 40 | typedef kernel_type::source_type source_type; 41 | typedef kernel_type::target_type target_type; 42 | typedef kernel_type::charge_type charge_type; 43 | typedef kernel_type::result_type result_type; 44 | 45 | // Init sources 46 | std::vector sources(numBodies); 47 | for (int k = 0; k < numBodies; ++k) 48 | sources[k] = source_type(drand(), drand(), drand()); 49 | // Init targets 50 | std::vector targets(numBodies); 51 | for (int k = 0; k < numBodies; ++k) 52 | targets[k] = target_type(drand(), drand(), drand()); 53 | // Init charges 54 | std::vector charges(numBodies); 55 | for (int k = 0; k < numBodies; ++k) 56 | charges[k] = drand(); 57 | 58 | // Build the FMM 59 | //fmm_plan plan = fmm_plan(K, bodies, opts); 60 | FMM_plan plan = FMM_plan(K, sources, targets, opts); 61 | 62 | // Execute the FMM 63 | //fmm_execute(plan, charges, target_points); 64 | std::vector result = plan.execute(charges); 65 | 66 | // Check the result 67 | if (checkErrors) { 68 | std::vector exact(numBodies); 69 | 70 | // Compute the result with a direct matrix-vector multiplication 71 | Direct::matvec(K, sources, charges, targets, exact); 72 | 73 | int wrong_results = 0; 74 | for (unsigned k = 0; k < result.size(); ++k) { 75 | printf("[%03d] exact: %lg, FMM: %lg\n", k, exact[k], result[k]); 76 | 77 | if ((exact[k] - result[k]) / exact[k] > 1e-13) 78 | ++wrong_results; 79 | } 80 | printf("Wrong counts: %d\n", wrong_results); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # 'make' build executable file 3 | # 'make clean' removes all .o and executable files 4 | # 5 | -include Makefile.inc 6 | 7 | # dependency directory 8 | DEPSDIR := $(shell mkdir -p .deps; echo .deps) 9 | 10 | # get the shell name to determine the OS 11 | UNAME := $(shell uname) 12 | 13 | # define the C compiler to use 14 | CC := gcc 15 | ifeq ($(UNAME), Linux) 16 | CXX := g++-4.7 -std=gnu++0x 17 | endif 18 | ifeq ($(UNAME), Darwin) 19 | CXX := $(shell for i in 4.7 4.6 4.5; do if g++-mp-$$i -v >/dev/null 2>&1; then echo g++-mp-$$i; exit; fi; done; echo false) -std=gnu++0x 20 | OBJC := gcc 21 | endif 22 | LINK := $(CXX) 23 | 24 | # define any compile-time flags 25 | CFLAGS := -fopenmp -funroll-loops -O3 -W -Wall -Wextra 26 | ifeq ($(DEBUG),1) 27 | CFLAGS += -g -fno-inline 28 | endif 29 | ifeq ($(PROFILE),1) 30 | CFLAGS += -g -pg 31 | endif 32 | DEPCFLAGS = -MD -MF $(DEPSDIR)/$*.d -MP 33 | 34 | # Other in-code flags 35 | CFLAGS += 36 | 37 | # define any directories containing header files other than /usr/include 38 | # include directories like -Ipath/to/files 39 | INCLUDES = -I. -I$(FMMW_DIR)/include -I$(FMMW_DIR)/kernel 40 | # External libraries defined in Makefile.inc 41 | INCLUDES += -I$(GSL_DIR) -I$(FFTW_DIR) -I$(BOOST_DIR) 42 | 43 | # define any libraries to link into executable 44 | # To link in libraries (libXXX.so or libXXX.a) use -lXXX options 45 | LIBS += 46 | 47 | ################## 48 | # The following part of the makefile is generic; it can be used to 49 | # build any executable just by changing the definitions above and by 50 | # deleting dependencies appended to the file from 'make depend' 51 | ################## 52 | 53 | # 'make' - default rule 54 | all: serialrun 55 | 56 | serialrun: serialrun.o 57 | $(LINK) $(CFLAGS) $(LDFLAGS) -o $@ $^ 58 | 59 | serialrun_stresslet: serialrun_stresslet.o 60 | $(LINK) $(CFLAGS) $(LDFLAGS) -o $@ $^ 61 | 62 | test_tree: test_tree.o 63 | $(LINK) $(CFLAGS) $(LDFLAGS) -o $@ $^ 64 | 65 | test_vec: test_vec.o 66 | $(LINK) $(CFLAGS) $(LDFLAGS) -o $@ $^ 67 | 68 | 69 | # suffix replacement rule for building .o's from .cpp's 70 | # $<: the name of the prereq of the rule (a .cpp file) 71 | # $@: the name of the target of the rule (a .o file) 72 | .cpp.o: 73 | $(CXX) $(CFLAGS) $(DEPCFLAGS) $(INCLUDES) -c -o $@ $< 74 | 75 | # 'make clean' - deletes all .o and temp files, exec, and dependency file 76 | clean: 77 | -$(RM) *.o *~ */*~ 78 | -$(RM) serialrun test_tree serialrun_stresslet 79 | $(RM) -r $(DEPSDIR) 80 | 81 | DEPFILES := $(wildcard $(DEPSDIR)/*.d) $(wildcard $(DEPSDIR)/*/*.d) 82 | ifneq ($(DEPFILES),) 83 | -include $(DEPFILES) 84 | endif 85 | 86 | # define rules that do not actually generate the corresponding file 87 | .PHONY: clean all 88 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # 'make' build executable file 3 | # 'make clean' removes all .o and executable files 4 | # 5 | 6 | # dependency directory 7 | DEPSDIR := $(shell mkdir -p .deps; echo .deps) 8 | 9 | # get the shell name to determine the OS 10 | UNAME := $(shell uname) 11 | 12 | # define the C compiler to use 13 | CC := gcc 14 | ifeq ($(UNAME), Linux) 15 | CXX := g++-4.7 -std=gnu++0x 16 | endif 17 | ifeq ($(UNAME), Darwin) 18 | CXX := $(shell for i in 4.7 4.6 4.5; do if g++-mp-$$i -v >/dev/null 2>&1; then echo g++-mp-$$i; exit; fi; done; echo false) -std=gnu++0x 19 | OBJC := gcc 20 | endif 21 | LINK := $(CXX) 22 | 23 | DEBUG = 1 24 | 25 | # define any compile-time flags 26 | CFLAGS := -fopenmp -funroll-loops -fforce-addr -O3 -W -Wall -Wextra #-ffast-math 27 | ifeq ($(DEBUG),1) 28 | CFLAGS += -g -fno-inline 29 | endif 30 | ifeq ($(PROFILE),1) 31 | CFLAGS += -g -pg 32 | endif 33 | DEPCFLAGS = -MD -MF $(DEPSDIR)/$*.d -MP 34 | 35 | # Other in-code flags 36 | CFLAGS += 37 | 38 | # define any directories containing header files other than /usr/include 39 | # include directories like -Ipath/to/files 40 | INCLUDES = -I. -I../include -I../include/tree -I../include/executor -I../kernel -I./BEM 41 | 42 | # define any libraries to link into executable 43 | # To link in libraries (libXXX.so or libXXX.a) use -lXXX options 44 | LIBS += 45 | 46 | ################## 47 | # The following part of the makefile is generic; it can be used to 48 | # build any executable just by changing the definitions above and by 49 | # deleting dependencies appended to the file from 'make depend' 50 | ################## 51 | 52 | # 'make' - default rule 53 | all: LaplaceBEM StokesBEM 54 | 55 | LaplaceBEM: LaplaceBEM.o 56 | $(LINK) $(CFLAGS) $(LDFLAGS) -o $@ $^ 57 | 58 | LaplaceBEM_time: LaplaceBEM_time.o 59 | $(LINK) $(CFLAGS) $(LDFLAGS) -o $@ $^ 60 | 61 | StokesBEM: StokesBEM.o 62 | $(LINK) $(CFLAGS) $(LDFLAGS) -o $@ $^ 63 | 64 | reader_test: reader_test.o 65 | $(LINK) $(CFLAGS) $(LDFLAGS) -o $@ $^ -lGL -lGLU -lglut 66 | 67 | # suffix replacement rule for building .o's from .cpp's 68 | # $<: the name of the prereq of the rule (a .cpp file) 69 | # $@: the name of the target of the rule (a .o file) 70 | .cpp.o: 71 | $(CXX) $(CFLAGS) $(DEPCFLAGS) $(INCLUDES) -c -o $@ $< 72 | 73 | # 'make clean' - deletes all .o and temp files, exec, and dependency file 74 | clean: 75 | -$(RM) *.o *~ */*~ 76 | -$(RM) LaplaceBEM YukawaBEM StokesBEM 77 | -$(RM) *.charge *.face *.vert 78 | $(RM) -r $(DEPSDIR) 79 | 80 | DEPFILES := $(wildcard $(DEPSDIR)/*.d) $(wildcard $(DEPSDIR)/*/*.d) 81 | ifneq ($(DEPFILES),) 82 | -include $(DEPFILES) 83 | endif 84 | 85 | # define rules that do not actually generate the corresponding file 86 | .PHONY: clean all 87 | -------------------------------------------------------------------------------- /include/executor/EvalLocal.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EvaluatorBase.hpp" 4 | 5 | #include "P2P.hpp" 6 | 7 | #include 8 | 9 | /** 10 | * Only evaluate local (direct) portion of the tree 11 | */ 12 | template 13 | class EvalLocal: public EvaluatorBase 14 | { 15 | //! type of box 16 | typedef typename Context::box_type box_type; 17 | //! Pair of boxees 18 | typedef std::pair box_pair; 19 | 20 | public: 21 | void execute(Context& bc) const { 22 | // Queue based tree traversal for P2P, M2P, and/or M2L operations 23 | std::deque pairQ; 24 | pairQ.push_back(box_pair(bc.source_tree().root(), 25 | bc.target_tree().root())); 26 | 27 | while (!pairQ.empty()) { 28 | auto b1 = pairQ.front().first; 29 | auto b2 = pairQ.front().second; 30 | pairQ.pop_front(); 31 | 32 | if (b1.is_leaf()) { 33 | if (b2.is_leaf()) { 34 | // Both are leaves, P2P 35 | P2P::eval(bc.kernel(), bc, b1, b2, P2P::ONE_SIDED()); 36 | } else { 37 | // b2 is not a leaf, Split the second box into children and interact 38 | auto c_end = b2.child_end(); 39 | for (auto cit = b2.child_begin(); cit != c_end; ++cit) 40 | interact(bc, b1, *cit, pairQ); 41 | } 42 | } else if (b2.is_leaf()) { 43 | // b1 is not a leaf, Split the first box into children and interact 44 | auto c_end = b1.child_end(); 45 | for (auto cit = b1.child_begin(); cit != c_end; ++cit) 46 | interact(bc, *cit, b2, pairQ); 47 | } else { 48 | // Neither are leaves, Split the larger into children and interact 49 | if (b1.side_length() > b2.side_length()) { // TODO: optimize? 50 | // Split the first box into children and interact 51 | auto c_end = b1.child_end(); 52 | for (auto cit = b1.child_begin(); cit != c_end; ++cit) 53 | interact(bc, *cit, b2, pairQ); 54 | } else { 55 | // Split the second box into children and interact 56 | auto c_end = b2.child_end(); 57 | for (auto cit = b2.child_begin(); cit != c_end; ++cit) 58 | interact(bc, b1, *cit, pairQ); 59 | } 60 | } 61 | } 62 | } 63 | 64 | template 65 | void interact(Context& bc, 66 | const BOX& b1, const BOX& b2, 67 | Q& pairQ) const { 68 | // ignore accepted multipoles 69 | if (!bc.accept_multipole(b1, b2)) { 70 | pairQ.push_back(box_pair(b1,b2)); 71 | } 72 | } 73 | }; 74 | 75 | 76 | template 77 | EvaluatorBase* make_local_eval(Context&, Options& opts) { 78 | (void) opts; 79 | return new EvalLocal(); 80 | } 81 | -------------------------------------------------------------------------------- /tests/Math.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** Compute the L1 error of v1 and v2 4 | * E = 1/N * sum_k |v1[k] - v2[k]| 5 | * 6 | * @pre The class Vec needs .size() and .op[] 7 | */ 8 | template 9 | double l1_error(const Vec& v1, const Vec& v2) { 10 | assert(v1.size() == v2.size()); 11 | unsigned N = v1.size(); 12 | double e = 0; 13 | for (unsigned k = 0; k < N; ++k) 14 | e += fabs(v1[k] - v2[k]); 15 | return e / N; 16 | } 17 | 18 | 19 | /** Compute the L1 relative error of v with respect to ve 20 | * E = 1/N * sum_k |ve[k] - v[k]| / |ve[k]| 21 | * 22 | * @pre The class Vec needs .size() and .op[] 23 | */ 24 | template 25 | double l1_rel_error(const Vec& v, const Vec& ve) { 26 | assert(v.size() == ve.size()); 27 | unsigned N = v.size(); 28 | double e = 0; 29 | double ev = 0; 30 | for (unsigned k = 0; k < N; ++k) { 31 | e += fabs(ve[k] - v[k]); 32 | ev += fabs(ve[k]); 33 | } 34 | return e / (N*ev); 35 | } 36 | 37 | /** Compute the L2 error of v1 and v2 38 | * E = sqrt( sum_k (ve[k] - v[k])^2 ) 39 | * 40 | * @pre The class Vec needs .size() and .op[] 41 | */ 42 | template 43 | double l2_error(const Vec& v1, const Vec& v2) { 44 | assert(v1.size() == v2.size()); 45 | unsigned N = v1.size(); 46 | double e2 = 0; 47 | for (unsigned k = 0; k < N; ++k) { 48 | double e = v1[k] - v2[k]; 49 | e2 += e * e; 50 | } 51 | return sqrt(e2); 52 | } 53 | 54 | 55 | /** Compute the L2 relative error of v with respect to ve 56 | * E = sqrt( sum_k (ve[k] - v[k])^2 / ve[k]^2 ) 57 | * 58 | * @pre The class Vec needs .size() and .op[] 59 | */ 60 | template 61 | double l2_rel_error(const Vec& v, const Vec& ve) { 62 | assert(v.size() == ve.size()); 63 | unsigned N = v.size(); 64 | double e2 = 0; 65 | double ve2 = 0; 66 | for (unsigned k = 0; k < N; ++k) { 67 | double e = ve[k] - v[k]; 68 | e2 += e * e; 69 | ve2 += ve[k] * ve[k]; 70 | } 71 | return sqrt(e2 / ve2); 72 | } 73 | 74 | /** Compute the max error of v1 and v2 75 | * E = max_k |ve[k] - v[k]| 76 | * 77 | * @pre The class Vec needs .size() and .op[] 78 | */ 79 | template 80 | double max_error(const Vec& v1, const Vec& v2) { 81 | assert(v1.size() == v2.size()); 82 | unsigned N = v1.size(); 83 | double e = 0; 84 | for (unsigned k = 0; k < N; ++k) 85 | e = max(e, fabs(v1[k] - v2[k])); 86 | return e; 87 | } 88 | 89 | 90 | /** Compute the max relative error of v with respect to ve 91 | * E = max_k |ve[k] - v[k]| / |ve[k]| 92 | * 93 | * @pre The class Vec needs .size() and .op[] 94 | */ 95 | template 96 | double max_rel_error(const Vec& v, const Vec& ve) { 97 | assert(v.size() == ve.size()); 98 | unsigned N = v.size(); 99 | double e = 0; 100 | for (unsigned k = 0; k < N; ++k) 101 | e = max(e, fabs(ve[k] - v[k]) / fabs(ve[k])); 102 | return e; 103 | } 104 | -------------------------------------------------------------------------------- /examples/BEM/MshReader.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Read in a gmsh .msh format and output as vector of triangles 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "MeshIO.hpp" 13 | 14 | namespace MeshIO 15 | { 16 | 17 | 18 | template 19 | void readMsh(const char *fname, std::vector& elements) 20 | { 21 | std::ifstream mesh(fname); 22 | 23 | // read header line 24 | std::string str; 25 | // information about the mesh 26 | int num_nodes = 0, num_elements = 0; 27 | std::vector nodes; 28 | 29 | bool in_header = true; 30 | while (in_header) { 31 | std::getline(mesh, str); 32 | if (str == "$Nodes") in_header = false; 33 | } 34 | 35 | // this is # of nodes 36 | std::getline(mesh,str); 37 | num_nodes = atoi(str.c_str()); 38 | nodes.resize(num_nodes); 39 | printf("num_nodes: %d\n",num_nodes); 40 | 41 | std::vector split_str; 42 | // now read all num_nodes nodes into an array 43 | for (int i=0; i 26 | bool operator()(const BOX& b1, const BOX& b2) const { 27 | double r0_normSq = normSq(b1.center() - b2.center()); 28 | double rhs = (b1.radius() + b2.radius()) / theta_; 29 | return r0_normSq > rhs*rhs; 30 | } 31 | }; 32 | 33 | // TODO: Generalize type? 34 | DefaultMAC MAC_; 35 | unsigned NCRIT_; 36 | 37 | // DEBUGGING FLAGS 38 | bool printTree; 39 | 40 | FMMOptions() 41 | : lazy_evaluation(true), 42 | local_evaluation(false), 43 | sparse_local(false), 44 | block_diagonal(false), 45 | evaluator(FMM), 46 | MAC_(DefaultMAC(0.5)), 47 | NCRIT_(64), 48 | printTree(false) { 49 | }; 50 | 51 | void set_mac_theta(double theta) { 52 | MAC_ = DefaultMAC(theta); 53 | } 54 | 55 | DefaultMAC MAC() { 56 | return MAC_; 57 | } 58 | 59 | void set_max_per_box(unsigned ncrit) { 60 | NCRIT_ = ncrit; 61 | } 62 | 63 | unsigned max_per_box() const { 64 | return NCRIT_; 65 | } 66 | 67 | void print_tree(bool v) { printTree = v; } 68 | bool print_tree() const { return printTree; } 69 | }; 70 | 71 | 72 | #include 73 | #include 74 | #include 75 | 76 | /** Get the FMMOptions from command line arguments 77 | */ 78 | FMMOptions get_options(int argc, char** argv) { 79 | FMMOptions opts = FMMOptions(); 80 | 81 | // parse command line args 82 | for (int i = 1; i < argc; ++i) { 83 | if (strcmp(argv[i],"-theta") == 0) { 84 | i++; 85 | opts.set_mac_theta((double)atof(argv[i])); 86 | } else if (strcmp(argv[i],"-eval") == 0) { 87 | i++; 88 | if (strcmp(argv[i],"FMM") == 0) { 89 | opts.evaluator = FMMOptions::FMM; 90 | } else if (strcmp(argv[i],"TREE") == 0) { 91 | opts.evaluator = FMMOptions::TREECODE; 92 | } else { 93 | printf("[W]: Unknown evaluator type: \"%s\"\n",argv[i]); 94 | } 95 | } else if (strcmp(argv[i],"-lazy_eval") == 0) { 96 | opts.lazy_evaluation = true; 97 | } else if (strcmp(argv[i],"-ncrit") == 0) { 98 | i++; 99 | opts.set_max_per_box((unsigned)atoi(argv[i])); 100 | } else if (strcmp(argv[i],"-printtree") == 0) { 101 | opts.print_tree(true); 102 | } 103 | } 104 | 105 | return opts; 106 | } 107 | -------------------------------------------------------------------------------- /include/executor/EvalInteraction.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EvaluatorBase.hpp" 4 | 5 | #include "M2L.hpp" 6 | #include "M2P.hpp" 7 | #include "P2P.hpp" 8 | 9 | #include 10 | 11 | template 12 | class EvalInteraction : public EvaluatorBase 13 | { 14 | //! type of box 15 | typedef typename Context::box_type box_type; 16 | //! Pair of boxees 17 | typedef std::pair box_pair; 18 | 19 | public: 20 | void execute(Context& bc) const { 21 | // Queue based tree traversal for P2P, M2P, and/or M2L operations 22 | std::deque pairQ; 23 | pairQ.push_back(box_pair(bc.source_tree().root(), 24 | bc.target_tree().root())); 25 | 26 | while (!pairQ.empty()) { 27 | auto b1 = pairQ.front().first; 28 | auto b2 = pairQ.front().second; 29 | pairQ.pop_front(); 30 | 31 | if (b1.is_leaf()) { 32 | if (b2.is_leaf()) { 33 | // Both are leaves, P2P 34 | P2P::eval(bc.kernel(), bc, b1, b2, P2P::ONE_SIDED()); 35 | } else { 36 | // b2 is not a leaf, Split the second box into children and interact 37 | auto c_end = b2.child_end(); 38 | for (auto cit = b2.child_begin(); cit != c_end; ++cit) 39 | interact(bc, b1, *cit, pairQ); 40 | } 41 | } else if (b2.is_leaf()) { 42 | // b1 is not a leaf, Split the first box into children and interact 43 | auto c_end = b1.child_end(); 44 | for (auto cit = b1.child_begin(); cit != c_end; ++cit) 45 | interact(bc, *cit, b2, pairQ); 46 | } else { 47 | // Neither are leaves, Split the larger into children and interact 48 | if (b1.side_length() > b2.side_length()) { // TODO: optimize? 49 | // Split the first box into children and interact 50 | auto c_end = b1.child_end(); 51 | for (auto cit = b1.child_begin(); cit != c_end; ++cit) 52 | interact(bc, *cit, b2, pairQ); 53 | } else { 54 | // Split the second box into children and interact 55 | auto c_end = b2.child_end(); 56 | for (auto cit = b2.child_begin(); cit != c_end; ++cit) 57 | interact(bc, b1, *cit, pairQ); 58 | } 59 | } 60 | } 61 | } 62 | 63 | template 64 | void interact(Context& bc, 65 | const BOX& b1, const BOX& b2, 66 | Q& pairQ) const { 67 | if (bc.accept_multipole(b1, b2)) { 68 | // These boxes satisfy the multipole acceptance criteria 69 | if (IS_FMM) 70 | M2L::eval(bc.kernel(), bc, b1, b2); 71 | else 72 | M2P::eval(bc.kernel(), bc, b1, b2); 73 | } else { 74 | pairQ.push_back(box_pair(b1,b2)); 75 | } 76 | } 77 | }; 78 | 79 | 80 | 81 | 82 | template 83 | EvaluatorBase* make_interact(Context&, Options& opts) { 84 | if (opts.evaluator == FMMOptions::FMM) 85 | return new EvalInteraction(); 86 | if (opts.evaluator == FMMOptions::TREECODE) 87 | return new EvalInteraction(); 88 | return nullptr; 89 | } 90 | -------------------------------------------------------------------------------- /include/Logger.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | 9 | #include 10 | 11 | /** Clock class, useful when timing code. 12 | */ 13 | struct Clock { 14 | /** Construct a Clock and start timing. */ 15 | Clock() { 16 | start(); 17 | } 18 | /** Start the clock. */ 19 | inline void start() { 20 | time_ = now(); 21 | } 22 | /** Return the seconds elapsed since the last start. */ 23 | inline double elapsed() const { 24 | timeval tv = now(); 25 | timersub(&tv, &time_, &tv); 26 | return seconds(tv); 27 | } 28 | /** Return the seconds difference between the Clocks */ 29 | inline double operator-(const Clock& clock) const { 30 | timeval tv; 31 | timersub(&time_, &clock.time_, &tv); 32 | return seconds(tv); 33 | } 34 | private: 35 | timeval time_; 36 | inline static timeval now() { 37 | timeval tv; 38 | gettimeofday(&tv, nullptr); 39 | return tv; 40 | } 41 | inline static double seconds(const timeval& tv) { 42 | return tv.tv_sec + 1e-6 * tv.tv_usec; 43 | } 44 | }; 45 | 46 | 47 | 48 | //! Timer and Trace logger 49 | class Logger { 50 | 51 | struct EventData { 52 | Clock start_time; 53 | double total_time; 54 | int hit; 55 | EventData() 56 | : start_time(), total_time(0), hit(0) { 57 | } 58 | void start() { 59 | start_time.start(); 60 | } 61 | double log(const Clock& end_time) { 62 | double elapsed = (end_time - start_time); 63 | total_time += elapsed; 64 | hit += 1; 65 | return elapsed; 66 | } 67 | friend std::ostream& operator<<(std::ostream& s, const EventData& e) { 68 | return s << e.hit << " (calls) * " << e.total_time/e.hit << " (sec/call) = " 69 | << e.total_time << " (secs)"; 70 | } 71 | }; 72 | 73 | std::map data_; 74 | 75 | public: 76 | 77 | //! Start a clock for an event 78 | inline void start(const std::string& event) { 79 | data_[event].start(); 80 | } 81 | 82 | //! Return the elasped time for given event 83 | double stop(const std::string& event, bool print_event = false) { 84 | Clock end_time; // Stop the clock 85 | double elapsed = data_[event].log(end_time); 86 | 87 | if (print_event) 88 | std::cout << data_[event] << std::endl; 89 | 90 | return elapsed; 91 | } 92 | 93 | //! Erase entry in timer 94 | inline void clear(const std::string& event) { 95 | data_.erase(event); 96 | } 97 | 98 | //! Erase all events in timer 99 | inline void clear() { 100 | data_.clear(); 101 | } 102 | 103 | // Get an event's data? 104 | //PublicEventData operator[](const std::string& event) { ... } 105 | 106 | //! Print all events and timing to an ostream 107 | friend std::ostream& operator<<(std::ostream& s, const Logger& log) { 108 | for (auto it : log.data_) 109 | s << std::setw(20) << std::left << it.first << " : " 110 | << it.second << std::endl; 111 | return s; 112 | } 113 | }; 114 | -------------------------------------------------------------------------------- /include/executor/EvalDiagonalSparse.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EvaluatorBase.hpp" 4 | #include "EvalP2P.hpp" 5 | #include "Matvec.hpp" 6 | 7 | #include 8 | 9 | /** Only evaluate local (direct) portion of the tree 10 | */ 11 | template 12 | class EvalDiagonalSparse 13 | : public EvaluatorBase { 14 | //! Type of box 15 | typedef typename Context::box_type box_type; 16 | //! Pair of boxees 17 | typedef std::pair box_pair; 18 | //! kernel type 19 | typedef typename Context::kernel_type kernel_type; 20 | //! kernel value type 21 | typedef typename kernel_type::kernel_value_type kernel_value_type; 22 | //! kernel charge type 23 | typedef typename kernel_type::charge_type charge_type; 24 | //! kernel result type 25 | typedef typename kernel_type::result_type result_type; 26 | 27 | //! matrix pair 28 | typedef std::pair matrix_pair; 29 | //! sparse matrix 30 | ublas::compressed_matrix A; 31 | 32 | public: 33 | // constructor -- create matrix 34 | EvalDiagonalSparse(Context& bc) { 35 | // Local P2P evaluator to construct the interaction matrix 36 | P2P_Lazy p2p_lazy(bc); 37 | 38 | // get the source root 39 | auto& tree = bc.source_tree(); 40 | 41 | // loop through all leaf boxes, pushing back self-interactions 42 | for (auto bi = tree.box_begin(); bi!=tree.box_end(); ++bi) 43 | { 44 | if (bi->is_leaf()) { 45 | p2p_lazy.insert(*bi,*bi); 46 | } 47 | } 48 | 49 | A = p2p_lazy.to_matrix(); 50 | } // end constructor 51 | 52 | void execute(Context& bc) const { 53 | auto root = bc.source_tree().root(); 54 | typedef typename Context::charge_type charge_type; 55 | ublas::vector charges(bc.source_tree().bodies()); 56 | std::copy(bc.charge_begin(root), bc.charge_end(root), charges.begin()); 57 | 58 | // call the matvec 59 | typedef typename Context::result_type result_type; 60 | // ublas::vector results = ublas::prod(A, charges); 61 | // ublas::vector results = Matvec(A, charges); 62 | ublas::vector results = Matvec, 63 | ublas::vector, 64 | ublas::vector>(A,charges); 65 | 66 | // copy results back into iterator 67 | std::transform(results.begin(), results.end(), 68 | bc.result_begin(root), bc.result_begin(root), 69 | std::plus()); 70 | } 71 | 72 | template 73 | void interact(Context& bc, 74 | const BOX& b1, const BOX& b2, 75 | Q& pairQ) const { 76 | // ignore accepted multipoles 77 | if (!bc.accept_multipole(b1, b2)) { 78 | pairQ.push_back(box_pair(b1,b2)); 79 | } 80 | } 81 | }; 82 | 83 | 84 | template 85 | EvaluatorBase* make_sparse_diagonal_eval(Context& bc, Options& opts) { 86 | (void) opts; 87 | return new EvalDiagonalSparse(bc); 88 | } 89 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # 'make' build executable file 3 | # 'make clean' removes all .o and executable files 4 | # 5 | include Makefile.inc 6 | 7 | # dependency directory 8 | DEPSDIR := $(shell mkdir -p .deps; echo .deps) 9 | 10 | # get the shell name to determine the OS 11 | UNAME := $(shell uname) 12 | 13 | # define the C compiler to use 14 | CC := gcc 15 | ifeq ($(UNAME), Linux) 16 | CXX := g++-4.7 -std=gnu++0x 17 | endif 18 | ifeq ($(UNAME), Darwin) 19 | CXX := $(shell for i in 4.7 4.6 4.5; do if g++-mp-$$i -v >/dev/null 2>&1; then echo g++-mp-$$i; exit; fi; done; echo false) -std=gnu++0x 20 | OBJC := gcc 21 | endif 22 | LINK := $(CXX) 23 | 24 | # define any compile-time flags 25 | CFLAGS := -fopenmp -funroll-loops -O3 -W -Wall -Wextra 26 | ifeq ($(DEBUG),1) 27 | CFLAGS += -g -fno-inline -DDEBUG 28 | endif 29 | ifeq ($(PROFILE),1) 30 | CFLAGS += -g -pg 31 | endif 32 | DEPCFLAGS = -MD -MF $(DEPSDIR)/$*.d -MP 33 | 34 | # Other in-code flags 35 | CFLAGS += 36 | 37 | # define any directories containing header files other than /usr/include 38 | # include directories like -Ipath/to/files 39 | INCLUDES = -I. -I$(FMMW_DIR)/include -I$(FMMW_DIR)/kernel 40 | 41 | # define any libraries to link into executable 42 | # To link in libraries (libXXX.so or libXXX.a) use -lXXX options 43 | LIBS += -lm 44 | 45 | ################## 46 | # The following part of the makefile is generic; it can be used to 47 | # build any executable just by changing the definitions above and by 48 | # deleting dependencies appended to the file from 'make depend' 49 | ################## 50 | 51 | # 'make' - default rule 52 | #EXECS += multi_level 53 | EXECS += ncrit_search 54 | EXECS += scaling 55 | #EXECS += correctness 56 | #EXECS += dual_correctness 57 | #EXECS += single_level 58 | #EXECS += single_level_stresslet 59 | #EXECS += multi_level_stresslet 60 | 61 | all: $(EXECS) 62 | 63 | 64 | multi_level: multi_level.o 65 | $(LINK) $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ $^ 66 | 67 | scaling: scaling.o 68 | $(LINK) $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ $^ 69 | 70 | correctness: correctness.o 71 | $(LINK) $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ $^ 72 | 73 | ncrit_search: ncrit_search.o 74 | $(LINK) $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ $^ 75 | 76 | dual_correctness: dual_correctness.o 77 | $(LINK) $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ $^ 78 | 79 | single_level: single_level.o 80 | $(LINK) $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ $^ 81 | 82 | single_level_stresslet: single_level_stresslet.o 83 | $(LINK) $(CFLAGS) $(LDFLAGS) -o $@ $^ 84 | 85 | multi_level_stresslet: multi_level_stresslet.o 86 | $(LINK) $(CFLAGS) $(LDFLAGS) -o $@ $^ 87 | 88 | 89 | # suffix replacement rule for building .o's from .cpp's 90 | # $<: the name of the prereq of the rule (a .cpp file) 91 | # $@: the name of the target of the rule (a .o file) 92 | %.o: %.cpp 93 | $(CXX) $(CFLAGS) $(DEPCFLAGS) $(INCLUDES) -c -o $@ $< 94 | 95 | # 'make clean' - deletes all .o and temp files, exec, and dependency file 96 | clean: 97 | -$(RM) *.o *~ */*~ 98 | -$(RM) $(EXECS) 99 | $(RM) -r $(DEPSDIR) 100 | 101 | DEPFILES := $(wildcard $(DEPSDIR)/*.d) $(wildcard $(DEPSDIR)/*/*.d) 102 | ifneq ($(DEPFILES),) 103 | -include $(DEPFILES) 104 | endif 105 | 106 | # define rules that do not actually generate the corresponding file 107 | .PHONY: clean all 108 | -------------------------------------------------------------------------------- /tests/single_level.cpp: -------------------------------------------------------------------------------- 1 | #include "executor/INITM.hpp" 2 | #include "executor/INITL.hpp" 3 | 4 | #include "LaplaceCartesian.hpp" 5 | #include "LaplaceSpherical.hpp" 6 | 7 | #include "StokesSpherical.hpp" 8 | 9 | #include "Math.hpp" 10 | 11 | #include 12 | 13 | template 14 | void single_level_test(const Kernel& K) 15 | { 16 | typedef Kernel kernel_type; 17 | typedef typename kernel_type::point_type point_type; 18 | typedef typename kernel_type::source_type source_type; 19 | typedef typename kernel_type::target_type target_type; 20 | typedef typename kernel_type::charge_type charge_type; 21 | typedef typename kernel_type::result_type result_type; 22 | typedef typename kernel_type::multipole_type multipole_type; 23 | typedef typename kernel_type::local_type local_type; 24 | 25 | // init source 26 | std::vector s(1); 27 | s[0] = source_type(0,0,0); 28 | 29 | // init charge 30 | std::vector c(1); 31 | c[0] = charge_type(1,2,3); 32 | 33 | // init target 34 | std::vector t(1); 35 | t[0] = target_type(0.9,0,0); 36 | // target_type t = target_type(0.9,0,0); // 1,1); 37 | 38 | // init results vectors for exact, FMM 39 | std::vector rexact(1); 40 | // result_type rexact; 41 | result_type rm2p; 42 | result_type rfmm; 43 | 44 | // test direct 45 | // rexact = K(t,s) * c; 46 | K.P2P(s.begin(),s.end(),c.begin(),t.begin(),t.end(),rexact.begin()); 47 | // rexact = result_type(rexact[0],0.,0.,0.); 48 | 49 | // setup intial multipole expansion 50 | multipole_type M; 51 | point_type M_center(0.125,0,0); // 0.125,0.125); 52 | INITM::eval(K, M, M_center, 1u); 53 | K.P2M(s[0], c[0], M_center, M); 54 | 55 | // test M2P 56 | K.M2P(M, M_center, t[0], rm2p); 57 | 58 | // test M2L, L2P 59 | local_type L; 60 | point_type L_center(0.875,0,0); // 0.875,0.875); 61 | auto d = L_center - M_center; 62 | printf("DIST: (%lg, %lg, %lg) : %lg\n",d[0],d[1],d[2],norm(d)); 63 | // L_center = point_type(t); 64 | INITL::eval(K, L, L_center, 1u); 65 | K.M2L(M, L, L_center - M_center); 66 | K.L2P(L, L_center, t[0], rfmm); 67 | 68 | // check errors 69 | std::cout << "rexact = " << rexact[0] << std::endl; 70 | std::cout << "rm2p = " << rm2p << std::endl; 71 | std::cout << "rfmm = " << rfmm << std::endl; 72 | 73 | /* 74 | std::cout << "M2P L1 rel error: " 75 | << std::scientific << l1_rel_error(rm2p, rexact) << std::endl; 76 | std::cout << "M2P L2 error: " 77 | << std::scientific << l2_error(rm2p, rexact) << std::endl; 78 | std::cout << "M2P L2 rel error: " 79 | << std::scientific << l2_rel_error(rm2p, rexact) << std::endl; 80 | 81 | std::cout << "FMM L1 rel error: " 82 | << std::scientific << l1_rel_error(rfmm, rexact) << std::endl; 83 | std::cout << "FMM L2 error: " 84 | << std::scientific << l2_error(rfmm, rexact) << std::endl; 85 | std::cout << "FMM L2 rel error: " 86 | << std::scientific << l2_rel_error(rfmm, rexact) << std::endl; 87 | */ 88 | } 89 | 90 | int main(int argc, char** argv) 91 | { 92 | (void) argc; 93 | (void) argv; 94 | 95 | //LaplaceCartesian<5> K; 96 | // LaplaceSpherical K(5); 97 | StokesSpherical K(5); 98 | 99 | single_level_test(K); 100 | } 101 | 102 | -------------------------------------------------------------------------------- /include/executor/P2P.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** @file P2P.hpp 3 | * @brief Dispatch methods for the P2P stage 4 | * 5 | */ 6 | 7 | #include "Direct.hpp" 8 | 9 | struct P2P 10 | { 11 | ////////////////////////////////////// 12 | /////// Static Dispatchers /////////// 13 | ////////////////////////////////////// 14 | 15 | struct ONE_SIDED {}; 16 | struct TWO_SIDED {}; 17 | 18 | /** One sided P2P 19 | */ 20 | template 21 | inline static void eval(const Kernel& K, 22 | Context& bc, 23 | const typename Context::box_type& source, 24 | const typename Context::box_type& target, 25 | const ONE_SIDED&) 26 | { 27 | #ifdef DEBUG 28 | printf("P2P: %d to %d\n", source.index(), target.index()); 29 | #endif 30 | 31 | Direct::matvec(K, 32 | bc.source_begin(source), bc.source_end(source), 33 | bc.charge_begin(source), 34 | bc.target_begin(target), bc.target_end(target), 35 | bc.result_begin(target)); 36 | } 37 | 38 | /** Two sided P2P 39 | */ 40 | template 41 | inline static void eval(const Kernel& K, 42 | Context& bc, 43 | const typename Context::box_type& source, 44 | const typename Context::box_type& target, 45 | const TWO_SIDED&) 46 | { 47 | #ifdef DEBUG 48 | printf("P2P: %d to %d\n", source.index(), target.index()); 49 | printf("P2P: %d to %d\n", target.index(), source.index()); 50 | #endif 51 | 52 | Direct::matvec(K, 53 | bc.source_begin(source), bc.source_end(source), 54 | bc.charge_begin(source), bc.result_begin(source), 55 | bc.target_begin(target), bc.target_end(target), 56 | bc.charge_begin(target), bc.result_begin(target)); 57 | } 58 | 59 | /** Two sided, self P2P 60 | */ 61 | template 62 | inline static void eval(const Kernel& K, 63 | Context& bc, 64 | const typename Context::box_type& source) 65 | { 66 | #ifdef DEBUG 67 | printf("P2P: %d to %d\n", source.index(), source.index()); 68 | #endif 69 | 70 | Direct::matvec(K, 71 | bc.source_begin(source), bc.source_end(source), 72 | bc.charge_begin(source), bc.result_begin(source), 73 | bc.target_begin(source), bc.target_end(source), 74 | bc.charge_begin(source), bc.result_begin(source)); 75 | } 76 | }; 77 | 78 | 79 | 80 | struct P2P_Batch { 81 | /** Construct a P2P context on the bodies within a single box */ 82 | template 83 | P2P_Batch(Context& bc, 84 | const typename Context::box_type& b) { 85 | // Do nothing 86 | } 87 | 88 | /** Construct a P2P context on the sources and targets within two boxes */ 89 | template 90 | P2P_Batch(Context& bc, 91 | const typename Context::box_type& source, 92 | const typename Context::box_type& target) { 93 | // Do nothing 94 | } 95 | 96 | inline void compute() { 97 | // Do nothing 98 | } 99 | }; 100 | -------------------------------------------------------------------------------- /tests/single_level_stresslet.cpp: -------------------------------------------------------------------------------- 1 | #include "INITM.hpp" 2 | #include "INITL.hpp" 3 | 4 | #define STRESSLET 5 | #include "StokesSpherical.hpp" 6 | 7 | #include "Math.hpp" 8 | 9 | #include 10 | 11 | template 12 | void single_level_test(const Kernel& K) 13 | { 14 | typedef Kernel kernel_type; 15 | typedef typename kernel_type::point_type point_type; 16 | typedef typename kernel_type::source_type source_type; 17 | typedef typename kernel_type::target_type target_type; 18 | typedef typename kernel_type::charge_type charge_type; 19 | typedef typename kernel_type::result_type result_type; 20 | typedef typename kernel_type::multipole_type multipole_type; 21 | typedef typename kernel_type::local_type local_type; 22 | 23 | // init source 24 | std::vector s(1); 25 | s[0] = source_type(0,0,0); 26 | 27 | // init charge 28 | std::vector c(1); 29 | // c[0] = charge_type(1,2,3,1,0,0); 30 | c[0][0] = 1; 31 | c[0][1] = 2; 32 | c[0][2] = 1; 33 | c[0][3] = 1; 34 | c[0][4] = 0; 35 | c[0][5] = 1; 36 | 37 | // init target 38 | std::vector t(1); 39 | t[0] = target_type(0.9,0.9,0.9); 40 | // target_type t = target_type(0.9,0,0); // 1,1); 41 | 42 | // init results vectors for exact, FMM 43 | std::vector rexact(1); 44 | // result_type rexact; 45 | result_type rm2p; 46 | result_type rfmm; 47 | 48 | // test direct 49 | // rexact = K(t,s) * c; 50 | K.P2P(s.begin(),s.end(),c.begin(),t.begin(),t.end(),rexact.begin()); 51 | // rexact = result_type(rexact[0],0.,0.,0.); 52 | 53 | // setup intial multipole expansion 54 | multipole_type M; 55 | point_type M_center(0.125,0,0); // 0.125,0.125); 56 | INITM::eval(K, M, M_center, 1u); 57 | K.P2M(s[0], c[0], M_center, M); 58 | 59 | 60 | // test M2P 61 | K.M2P(M, M_center, t[0], rm2p); 62 | 63 | // test M2L, L2P 64 | local_type L; 65 | point_type L_center(0.875,0.875,0.875); 66 | auto d = L_center - M_center; 67 | printf("DIST: (%lg, %lg, %lg) : %lg\n",d[0],d[1],d[2],norm(d)); 68 | // L_center = point_type(t); 69 | INITL::eval(K, L, L_center, 1u); 70 | K.M2L(M, L, L_center - M_center); 71 | K.L2P(L, L_center, t[0], rfmm); 72 | 73 | // check errors 74 | std::cout << "rexact = " << rexact[0] << std::endl; 75 | std::cout << "rm2p = " << 1./6*rm2p << std::endl; 76 | std::cout << "rfmm = " << 1./6*rfmm << std::endl; 77 | 78 | /* 79 | std::cout << "M2P L1 rel error: " 80 | << std::scientific << l1_rel_error(rm2p, rexact) << std::endl; 81 | std::cout << "M2P L2 error: " 82 | << std::scientific << l2_error(rm2p, rexact) << std::endl; 83 | std::cout << "M2P L2 rel error: " 84 | << std::scientific << l2_rel_error(rm2p, rexact) << std::endl; 85 | 86 | std::cout << "FMM L1 rel error: " 87 | << std::scientific << l1_rel_error(rfmm, rexact) << std::endl; 88 | std::cout << "FMM L2 error: " 89 | << std::scientific << l2_error(rfmm, rexact) << std::endl; 90 | std::cout << "FMM L2 rel error: " 91 | << std::scientific << l2_rel_error(rfmm, rexact) << std::endl; 92 | */ 93 | } 94 | 95 | int main(int argc, char** argv) 96 | { 97 | (void) argc; 98 | (void) argv; 99 | 100 | //LaplaceCartesian<5> K; 101 | // LaplaceSpherical K(5); 102 | StokesSpherical K(5); 103 | 104 | single_level_test(K); 105 | } 106 | 107 | -------------------------------------------------------------------------------- /include/executor/make_executor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ExecutorBase.hpp" 4 | 5 | #include "ExecutorSingleTree.hpp" 6 | #include "ExecutorDualTree.hpp" 7 | 8 | #include "EvalUpward.hpp" 9 | #include "EvalInteraction.hpp" 10 | #include "EvalDownward.hpp" 11 | 12 | #include "EvalLocal.hpp" 13 | #include "EvalLocalSparse.hpp" 14 | #include "EvalDiagonalSparse.hpp" 15 | 16 | #include "EvalInteractionQueue.hpp" 17 | #include "EvalInteractionLazy.hpp" 18 | #include "EvalInteractionLazySparse.hpp" 19 | 20 | #include "tree/Octree.hpp" 21 | 22 | 23 | 24 | template 25 | void make_evaluators(Executor& executor, Options& opts) 26 | { 27 | if (opts.lazy_evaluation) { 28 | if (opts.sparse_local) { 29 | // sparse local evaluation 30 | auto lazy_eval = make_lazy_sparse_eval(executor, opts); 31 | executor.insert(lazy_eval); 32 | } else { 33 | // Custom lazy evaluator 34 | auto lazy_eval = make_lazy_eval(executor, opts); 35 | executor.insert(lazy_eval); 36 | } 37 | } else if (opts.local_evaluation) { 38 | // only evaluate local field for preconditioner 39 | if (opts.sparse_local) { 40 | auto sparse_eval = make_sparse_local_eval(executor, opts); 41 | executor.insert(sparse_eval); 42 | } 43 | else { 44 | auto local_eval = make_local_eval(executor, opts); 45 | executor.insert(local_eval); 46 | } 47 | } else if (opts.block_diagonal) { 48 | auto block_diagonal_eval = make_sparse_diagonal_eval(executor, opts); 49 | executor.insert(block_diagonal_eval); 50 | } else { 51 | // Standard evaluators 52 | auto upward = make_upward(executor, opts); 53 | executor.insert(upward); 54 | auto inter = make_interact(executor, opts); 55 | executor.insert(inter); 56 | auto downward = make_downward(executor, opts); 57 | executor.insert(downward); 58 | } 59 | } 60 | 61 | /** Single tree executor construction 62 | */ 63 | template 66 | // ExecutorBase* make_executor(const Kernel& K, 67 | ExecutorSingleTree>* make_executor(const Kernel& K, 68 | SourceIter first, SourceIter last, 69 | Options& opts) { 70 | typedef Octree Tree; 71 | 72 | auto executor = make_executor(K, 73 | first, last, 74 | opts); 75 | make_evaluators(*executor, opts); 76 | 77 | return executor; 78 | } 79 | 80 | 81 | /** Dual tree executor construction 82 | */ 83 | template 86 | ExecutorBase* make_executor(const Kernel& K, 87 | SourceIter sfirst, SourceIter slast, 88 | TargetIter tfirst, TargetIter tlast, 89 | Options& opts) { 90 | typedef Octree Tree; 91 | 92 | auto executor = make_executor(K, 93 | sfirst, slast, 94 | tfirst, tlast, 95 | opts); 96 | make_evaluators(*executor, opts); 97 | 98 | return executor; 99 | } 100 | -------------------------------------------------------------------------------- /include/executor/EvalP2P.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define BOOST_UBLAS_NDEBUG 4 | #include 5 | namespace ublas = boost::numeric::ublas; 6 | 7 | #include 8 | 9 | #include "P2P.hpp" 10 | 11 | /** A lazy P2P evaluator which saves a list of pairs of boxes 12 | * That are sent to the P2P dispatcher on demand. 13 | */ 14 | template 15 | class P2P_Lazy 16 | : public EvaluatorBase { 17 | // The context of this evaluator 18 | Context& bc; 19 | 20 | typedef typename Context::kernel_type kernel_type; 21 | //! Kernel value type 22 | typedef typename Context::kernel_value_type kernel_value_type; 23 | 24 | //! Type of box 25 | typedef typename Context::box_type box_type; 26 | //! Box list for P2P interactions TODO: could further compress these... 27 | typedef std::pair box_pair; 28 | std::vector p2p_list; 29 | 30 | public: 31 | P2P_Lazy(Context& _bc) 32 | : bc(_bc) { 33 | } 34 | 35 | /** Insert a source-target box interaction to the interaction list */ 36 | void insert(const box_type& box1, const box_type& box2) { 37 | p2p_list.push_back(std::make_pair(box1,box2)); 38 | } 39 | 40 | /** Compute all interations in the interaction list */ 41 | void execute(Context& bc) const { 42 | for (const box_pair& b2b : p2p_list) 43 | P2P::eval(bc.kernel(), bc, b2b.first, b2b.second, P2P::ONE_SIDED()); 44 | } 45 | 46 | /** Convert the interaction list to an interaction matrix */ 47 | ublas::compressed_matrix to_matrix() { 48 | auto first_source = bc.source_begin(bc.source_tree().root()); 49 | auto first_target = bc.target_begin(bc.target_tree().root()); 50 | 51 | // Interaction list for each target body 52 | unsigned rows = bc.target_tree().bodies(); 53 | unsigned cols = 0; 54 | unsigned nnz = 0; 55 | std::vector> csr(rows); 56 | 57 | for (const box_pair& b2b : p2p_list) { 58 | const box_type& box1 = b2b.first; 59 | const box_type& box2 = b2b.second; 60 | 61 | auto source_end = bc.source_end(box1); 62 | auto target_end = bc.target_end(box2); 63 | for (auto t = bc.target_begin(box2); t != target_end; ++t) { 64 | // Row 65 | unsigned i = t - first_target; 66 | std::vector& csri = csr[i]; 67 | 68 | for (auto s = bc.source_begin(box1); s != source_end; ++s) { 69 | // Column 70 | unsigned j = s - first_source; 71 | 72 | //assert(std::find(csri.begin(), csri.end(), j) == csri.end()); 73 | csri.push_back(j); 74 | ++nnz; 75 | cols = std::max(cols, j); 76 | } 77 | } 78 | } 79 | ++cols; 80 | 81 | // The precomputed interaction matrix 82 | ublas::compressed_matrix m(rows, cols, nnz); 83 | 84 | typedef typename kernel_type::source_type source_type; 85 | typedef typename kernel_type::target_type target_type; 86 | for (unsigned i = 0; i < csr.size(); ++i) { 87 | // Insert elements to compressed_matrix in-order for efficiency 88 | std::sort(csr[i].begin(), csr[i].end()); 89 | const target_type& target = first_target[i]; 90 | 91 | for (unsigned j : csr[i]) { 92 | const source_type& source = first_source[j]; 93 | m.push_back(i, j, bc.kernel()(target, source)); 94 | } 95 | } 96 | 97 | return m; 98 | } 99 | }; 100 | -------------------------------------------------------------------------------- /include/SparseMatrix.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Simple sparse matrix class (CSR) 5 | */ 6 | 7 | #include 8 | 9 | template 10 | struct SparseMatrix 11 | { 12 | typedef T value_type; 13 | typedef I index_type; 14 | // data 15 | I rows, cols, nnz; 16 | std::vector offsets, indices; 17 | std::vector vals; 18 | 19 | // default constructor 20 | SparseMatrix() : rows(0), cols(0), nnz(0), offsets(0), indices(0), vals(0) {}; 21 | // empty matrix 22 | SparseMatrix(int r, int c, int nz) 23 | : rows(r), cols(c), nnz(nz), offsets(r+1), indices(nz), vals(nz) {}; 24 | 25 | // matvec 26 | template 27 | std::vector dot(VecType& x) const 28 | { 29 | // init return vector 30 | std::vector y(x.size(),T(0)); 31 | 32 | // matvec 33 | I jj, j; 34 | T yy; 35 | #pragma omp parallel for private(j,jj,yy) 36 | for (I i=0; i 52 | std::vector dot(VecType& x, double droptol) const 53 | { 54 | // init return vector 55 | std::vector y(x.size(),T(0)); 56 | 57 | // matvec 58 | I j, jj; 59 | T yy, v; 60 | #pragma omp parallel for private(j,jj,yy,v) 61 | for (I i=0; i= droptol)*v; 69 | } 70 | // write out 71 | y[i] = yy; 72 | } 73 | return y; 74 | } 75 | // assignment operator 76 | SparseMatrix& operator=(const SparseMatrix& m) { 77 | this->rows = m.rows; 78 | this->cols = m.cols; 79 | this->nnz = m.nnz; 80 | 81 | this->offsets.resize(m.rows+1); 82 | this->indices.resize(m.nnz); 83 | this->vals.resize(m.nnz); 84 | this->offsets = m.offsets; 85 | this->indices = m.indices; 86 | this->vals = m.vals; 87 | 88 | return *this; 89 | } 90 | 91 | void resize(int r, int c, int nz) { 92 | rows = r; 93 | cols = c; 94 | nnz = nz; 95 | offsets.resize(r+1); 96 | indices.resize(nnz); 97 | vals.resize(nnz); 98 | } 99 | // copy constructor 100 | SparseMatrix(const SparseMatrix& m) : rows(m.rows), cols(m.cols), nnz(m.nnz), offsets(m.offsets), indices(m.nnz), vals(m.vals) {}; 101 | 102 | // destructor 103 | ~SparseMatrix() { 104 | offsets.resize(0); 105 | indices.resize(0); 106 | vals.resize(0); 107 | } 108 | 109 | // return storage size in bytes 110 | auto storage_size() -> decltype(sizeof(T) + sizeof(I)) 111 | { 112 | auto index_storage = (rows+1 + nnz + 3)*sizeof(I); 113 | auto value_storage = nnz * sizeof(T); 114 | return index_storage + value_storage; 115 | } 116 | }; 117 | 118 | template 119 | std::vector matvec(const Matrix& A, const Vector& x) 120 | { 121 | return A.dot(x); 122 | } 123 | 124 | template 125 | std::vector matvec(const Matrix& A, const Vector& x, const double droptol) 126 | { 127 | return A.dot(x,droptol); 128 | } 129 | 130 | -------------------------------------------------------------------------------- /tests/multi_level.cpp: -------------------------------------------------------------------------------- 1 | #include "executor/INITM.hpp" 2 | #include "executor/INITL.hpp" 3 | 4 | #include "LaplaceCartesian.hpp" 5 | #include "LaplaceSpherical.hpp" 6 | #include "YukawaCartesian.hpp" 7 | #include "StokesSpherical.hpp" 8 | 9 | #include "Math.hpp" 10 | 11 | #include 12 | 13 | // #define TREECODE_ONLY 14 | 15 | template 16 | void two_level_test(const Kernel& K) 17 | { 18 | typedef Kernel kernel_type; 19 | typedef typename kernel_type::point_type point_type; 20 | typedef typename kernel_type::source_type source_type; 21 | typedef typename kernel_type::target_type target_type; 22 | typedef typename kernel_type::charge_type charge_type; 23 | typedef typename kernel_type::result_type result_type; 24 | typedef typename kernel_type::multipole_type multipole_type; 25 | typedef typename kernel_type::local_type local_type; 26 | 27 | // init source 28 | std::vector s(1); 29 | s[0] = source_type(0,0,0); 30 | 31 | // init charge 32 | std::vector c(1); 33 | c[0] = charge_type(1,2,3); 34 | 35 | // init target 36 | std::vector t(1); 37 | t[0] = target_type(0.98,0.98,0.98); 38 | 39 | // init results vectors for exact, FMM 40 | std::vector rexact(1); 41 | result_type rm2p; 42 | result_type rfmm; 43 | 44 | // test direct 45 | K.P2P(s.begin(),s.end(),c.begin(),t.begin(),t.end(),rexact.begin()); 46 | 47 | // setup intial multipole expansion 48 | multipole_type M; 49 | const point_type M_center(0.05, 0.05, 0.05); 50 | INITM::eval(K, M, M_center, 2u); 51 | K.P2M(s[0], c[0], M_center, M); 52 | 53 | // perform M2M 54 | multipole_type M2; 55 | const point_type M2_center(0.1, 0.1, 0.1); 56 | INITM::eval(K, M2, M2_center, 1u); 57 | K.M2M(M, M2, M2_center - M_center); 58 | //K.P2M(s,c,M2_center,M2); 59 | 60 | // test M2P 61 | K.M2P(M2, M2_center, t[0], rm2p); 62 | 63 | // test M2L, L2P 64 | #ifndef TREECODE_ONLY 65 | local_type L2; 66 | point_type L2_center(0.9, 0.9, 0.9); 67 | INITL::eval(K, L2, L2_center, 1u); 68 | K.M2L(M2, L2, L2_center - M2_center); 69 | 70 | // test L2L 71 | local_type L; 72 | point_type L_center(0.95, 0.95, 0.95); 73 | INITL::eval(K, L, L_center, 2u); 74 | K.L2L(L2, L, L_center - L2_center); 75 | 76 | // test L2P 77 | K.L2P(L2, L2_center, t[0], rfmm); 78 | #endif 79 | 80 | // check errors 81 | std::cout << "rexact = " << rexact[0] << std::endl; 82 | std::cout << "rm2p = " << rm2p << std::endl; 83 | std::cout << "rfmm = " << rfmm << std::endl; 84 | 85 | /* 86 | std::cout << "M2P L1 rel error: " 87 | << std::scientific << l1_rel_error(rm2p, rexact) << std::endl; 88 | std::cout << "M2P L2 error: " 89 | << std::scientific << l2_error(rm2p, rexact) << std::endl; 90 | std::cout << "M2P L2 rel error: " 91 | << std::scientific << l2_rel_error(rm2p, rexact) << std::endl; 92 | #ifndef TREECODE_ONLY 93 | std::cout << "FMM L1 rel error: " 94 | << std::scientific << l1_rel_error(rfmm, rexact) << std::endl; 95 | std::cout << "FMM L2 error: " 96 | << std::scientific << l2_error(rfmm, rexact) << std::endl; 97 | std::cout << "FMM L2 rel error: " 98 | << std::scientific << l2_rel_error(rfmm, rexact) << std::endl; 99 | #endif 100 | */ 101 | } 102 | 103 | 104 | int main(int argc, char **argv) 105 | { 106 | (void) argc; 107 | (void) argv; 108 | 109 | //LaplaceCartesian<5> K; 110 | //LaplaceSpherical K(10); 111 | //YukawaCartesian K(10, 0.1); 112 | StokesSpherical K(5); 113 | 114 | two_level_test(K); 115 | } 116 | 117 | -------------------------------------------------------------------------------- /tests/multi_level_stresslet.cpp: -------------------------------------------------------------------------------- 1 | #include "INITM.hpp" 2 | #include "INITL.hpp" 3 | 4 | #define STRESSLET 5 | #include "StokesSpherical.hpp" 6 | 7 | #include "Math.hpp" 8 | 9 | #include 10 | 11 | // #define TREECODE_ONLY 12 | 13 | template 14 | void two_level_test(const Kernel& K) 15 | { 16 | typedef Kernel kernel_type; 17 | typedef typename kernel_type::point_type point_type; 18 | typedef typename kernel_type::source_type source_type; 19 | typedef typename kernel_type::target_type target_type; 20 | typedef typename kernel_type::charge_type charge_type; 21 | typedef typename kernel_type::result_type result_type; 22 | typedef typename kernel_type::multipole_type multipole_type; 23 | typedef typename kernel_type::local_type local_type; 24 | 25 | // init source 26 | std::vector s(1); 27 | s[0] = source_type(0,0,0); 28 | 29 | // init charge 30 | std::vector c(1); 31 | c[0][0] = 1.; 32 | c[0][1] = 2.; 33 | c[0][2] = 3.; 34 | c[0][3] = 1.; 35 | c[0][4] = 0.; 36 | c[0][5] = 1.; 37 | 38 | // init target 39 | std::vector t(1); 40 | t[0] = target_type(0.98,0.98,0.98); 41 | 42 | // init results vectors for exact, FMM 43 | std::vector rexact(1); 44 | result_type rm2p; 45 | result_type rfmm; 46 | 47 | // test direct 48 | K.P2P(s.begin(),s.end(),c.begin(),t.begin(),t.end(),rexact.begin()); 49 | 50 | // setup intial multipole expansion 51 | multipole_type M; 52 | const point_type M_center(0.05, 0.05, 0.05); 53 | INITM::eval(K, M, M_center, 2u); 54 | K.P2M(s[0], c[0], M_center, M); 55 | 56 | // perform M2M 57 | multipole_type M2; 58 | const point_type M2_center(0.1, 0.1, 0.1); 59 | INITM::eval(K, M2, M2_center, 1u); 60 | K.M2M(M, M2, M2_center - M_center); 61 | //K.P2M(s,c,M2_center,M2); 62 | 63 | // test M2P 64 | K.M2P(M2, M2_center, t[0], rm2p); 65 | 66 | // test M2L, L2P 67 | #ifndef TREECODE_ONLY 68 | local_type L2; 69 | point_type L2_center(0.9, 0.9, 0.9); 70 | INITL::eval(K, L2, L2_center, 1u); 71 | K.M2L(M2, L2, L2_center - M2_center); 72 | 73 | // test L2L 74 | local_type L; 75 | point_type L_center(0.95, 0.95, 0.95); 76 | INITL::eval(K, L, L_center, 2u); 77 | K.L2L(L2, L, L_center - L2_center); 78 | 79 | // test L2P 80 | K.L2P(L2, L2_center, t[0], rfmm); 81 | #endif 82 | 83 | // check errors 84 | std::cout << "rexact = " << rexact[0] << std::endl; 85 | std::cout << "rm2p = " << 1./6*rm2p << std::endl; 86 | std::cout << "rfmm = " << 1./6*rfmm << std::endl; 87 | 88 | /* 89 | std::cout << "M2P L1 rel error: " 90 | << std::scientific << l1_rel_error(rm2p, rexact) << std::endl; 91 | std::cout << "M2P L2 error: " 92 | << std::scientific << l2_error(rm2p, rexact) << std::endl; 93 | std::cout << "M2P L2 rel error: " 94 | << std::scientific << l2_rel_error(rm2p, rexact) << std::endl; 95 | #ifndef TREECODE_ONLY 96 | std::cout << "FMM L1 rel error: " 97 | << std::scientific << l1_rel_error(rfmm, rexact) << std::endl; 98 | std::cout << "FMM L2 error: " 99 | << std::scientific << l2_error(rfmm, rexact) << std::endl; 100 | std::cout << "FMM L2 rel error: " 101 | << std::scientific << l2_rel_error(rfmm, rexact) << std::endl; 102 | #endif 103 | */ 104 | } 105 | 106 | 107 | int main(int argc, char **argv) 108 | { 109 | (void) argc; 110 | (void) argv; 111 | 112 | //LaplaceCartesian<5> K; 113 | //LaplaceSpherical K(10); 114 | //YukawaCartesian K(10, 0.1); 115 | StokesSpherical K(5); 116 | 117 | two_level_test(K); 118 | } 119 | 120 | -------------------------------------------------------------------------------- /include/FMM_plan.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // FMM includes 4 | #include "Vec.hpp" 5 | 6 | #include "FMMOptions.hpp" 7 | #include "KernelTraits.hpp" 8 | #include "Logger.hpp" 9 | 10 | #include "executor/make_executor.hpp" 11 | 12 | //! global logging 13 | Logger Log; 14 | 15 | template 16 | class FMM_plan 17 | { 18 | public: 19 | typedef Kernel kernel_type; 20 | 21 | typedef typename kernel_type::point_type point_type; 22 | typedef typename kernel_type::source_type source_type; 23 | typedef typename kernel_type::target_type target_type; 24 | // TODO: Better point support? 25 | // Want all derived classes to use the following fmmplan::point_type wrapper? 26 | //typedef typename Vec point_type; 27 | typedef typename kernel_type::charge_type charge_type; 28 | typedef typename kernel_type::result_type result_type; 29 | // executor type 30 | typedef ExecutorSingleTree> executor_type; 31 | 32 | // CONSTRUCTOR 33 | 34 | FMM_plan(const kernel_type& k, 35 | const std::vector& source, 36 | FMMOptions& opts) 37 | : K(k), opts_(opts) { 38 | check_kernel(); 39 | 40 | executor_ = make_executor(K, 41 | source.begin(), source.end(), 42 | opts_); 43 | } 44 | 45 | FMM_plan(const Kernel& k, 46 | const std::vector& source, 47 | const std::vector& target, 48 | FMMOptions& opts) 49 | : K(k), opts_(opts) { 50 | check_kernel(); 51 | 52 | executor_ = make_executor(K, 53 | source.begin(), source.end(), 54 | target.begin(), target.end(), 55 | opts_); 56 | } 57 | 58 | // DESTRUCTOR 59 | 60 | ~FMM_plan() { 61 | delete executor_; 62 | } 63 | 64 | // MODIFIER 65 | 66 | kernel_type& kernel() { 67 | return K; 68 | } 69 | const kernel_type& kernel() const { 70 | return K; 71 | } 72 | 73 | // EXECUTE 74 | 75 | std::vector execute(const std::vector& charges) 76 | { 77 | // Assert that source == target in FMMOptions 78 | 79 | if (!executor_) { 80 | printf("[E]: Executor not initialised -- returning..\n"); 81 | return std::vector(0); 82 | } 83 | 84 | // XXX: results.size == charges.size()? 85 | std::vector results(charges.size()); 86 | executor_->execute(charges, results); 87 | 88 | // TODO: don't return this, provide accessor 89 | return results; 90 | } 91 | 92 | /** Access to the Options this plan is operating with 93 | */ 94 | FMMOptions& options() { 95 | return opts_; // XXX: Need to update the plan with any new settings... 96 | } 97 | 98 | /** Access to iterators 99 | */ 100 | typedef typename executor_type::body_source_iterator body_source_iterator; 101 | body_source_iterator source_begin() { 102 | return executor_->source_begin(executor_->source_tree().root()); 103 | } 104 | 105 | body_source_iterator source_end() { 106 | return executor_->source_end(executor_->source_tree().root()); 107 | } 108 | 109 | private: 110 | // ExecutorBase* executor_; 111 | executor_type *executor_; 112 | kernel_type K; 113 | FMMOptions opts_; 114 | 115 | void check_kernel() { 116 | if (opts_.evaluator == FMMOptions::FMM && 117 | !ExpansionTraits::is_valid_fmm) { 118 | std::cerr << ExpansionTraits(); 119 | std::cerr << "[W] Cannot use Kernel for FMM!\n"; 120 | } 121 | 122 | if (opts_.evaluator == FMMOptions::TREECODE && 123 | !ExpansionTraits::is_valid_treecode) { 124 | std::cerr << ExpansionTraits(); 125 | std::cerr << "[W] Cannot use Kernel for treecode!\n"; 126 | } 127 | } 128 | }; 129 | -------------------------------------------------------------------------------- /include/executor/EvalInteractionQueue.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EvaluatorBase.hpp" 4 | 5 | #include "M2L.hpp" 6 | #include "M2P.hpp" 7 | #include "P2P.hpp" 8 | 9 | #include 10 | 11 | template 12 | class EvalInteractionQueue : public EvaluatorBase 13 | { 14 | //! type of box 15 | typedef typename Context::box_type box_type; 16 | //! Pair of boxes 17 | typedef std::pair box_pair; 18 | //! List for P2P interactions 19 | mutable std::vector P2P_list; 20 | //! List for Long-range (M2P / M2L) interactions 21 | mutable std::vector LR_list; 22 | 23 | public: 24 | 25 | EvalInteractionQueue(Context& bc) { 26 | std::deque pairQ; 27 | pairQ.push_back(box_pair(bc.source_tree().root(), 28 | bc.target_tree().root())); 29 | 30 | while (!pairQ.empty()) { 31 | auto b1 = pairQ.front().first; 32 | auto b2 = pairQ.front().second; 33 | pairQ.pop_front(); 34 | 35 | if (b1.is_leaf()) { 36 | if (b2.is_leaf()) { 37 | // Both are leaves, P2P 38 | P2P::eval(bc.kernel(), bc, b1, b2, P2P::ONE_SIDED()); 39 | } else { 40 | // b2 is not a leaf, Split the second box into children and interact 41 | auto c_end = b2.child_end(); 42 | for (auto cit = b2.child_begin(); cit != c_end; ++cit) 43 | interact(bc, b1, *cit, pairQ); 44 | } 45 | } else if (b2.is_leaf()) { 46 | // b1 is not a leaf, Split the first box into children and interact 47 | auto c_end = b1.child_end(); 48 | for (auto cit = b1.child_begin(); cit != c_end; ++cit) 49 | interact(bc, *cit, b2, pairQ); 50 | } else { 51 | // Neither are leaves, Split the larger into children and interact 52 | if (b1.side_length() > b2.side_length()) { // TODO: optimize? 53 | // Split the first box into children and interact 54 | auto c_end = b1.child_end(); 55 | for (auto cit = b1.child_begin(); cit != c_end; ++cit) 56 | interact(bc, *cit, b2, pairQ); 57 | } else { 58 | // Split the second box into children and interact 59 | auto c_end = b2.child_end(); 60 | for (auto cit = b2.child_begin(); cit != c_end; ++cit) 61 | interact(bc, b1, *cit, pairQ); 62 | } 63 | } 64 | } 65 | } 66 | 67 | void execute(Context& bc) const { 68 | // now evaluate lists 69 | eval_LR_list(bc); 70 | eval_P2P_list(bc); 71 | } 72 | 73 | private: 74 | 75 | void eval_LR_list(Context& bc) const { 76 | for (auto it=LR_list.begin(); it!=LR_list.end(); ++it) { 77 | // evaluate this pair using M2L / M2P 78 | if (IS_FMM) 79 | M2L::eval(bc.kernel(), bc, it->first, it->second); 80 | else 81 | M2P::eval(bc.kernel(), bc, it->first, it->second); 82 | } 83 | } 84 | 85 | void eval_P2P_list(Context& bc) const { 86 | for (auto it=P2P_list.begin(); it!=P2P_list.end(); ++it) { 87 | // evaluate this pair using P2P 88 | P2P::eval(bc.kernel(), bc, it->first, it->second, P2P::ONE_SIDED()); 89 | } 90 | } 91 | 92 | template 93 | void interact(Context& bc, const BOX& b1, const BOX& b2, Q& pairQ) const { 94 | if (bc.accept_multipole(b1, b2)) { 95 | // These boxes satisfy the multipole acceptance criteria 96 | LR_list.push_back(box_pair(b1,b2)); 97 | } else { 98 | pairQ.push_back(box_pair(b1,b2)); 99 | } 100 | } 101 | }; 102 | 103 | 104 | template 105 | EvaluatorBase* make_interact_queue(Context&, Options& opts) { 106 | if (opts.evaluator == FMMOptions::FMM) 107 | return new EvalInteractionQueue(); 108 | if (opts.evaluator == FMMOptions::TREECODE) 109 | return new EvalInteractionQueue(); 110 | return nullptr; 111 | } 112 | -------------------------------------------------------------------------------- /examples/BEM/BLAS.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * BLAS and custom matrix operations 5 | */ 6 | 7 | /** Very basic matrix type */ 8 | template 9 | class Matrix 10 | { 11 | private: 12 | int rows_, cols_; 13 | // 1D storage, column-major 14 | std::vector vals_; 15 | // T *vals_; 16 | 17 | public: 18 | Matrix() : rows_(0), cols_(0), vals_(0) {}; 19 | Matrix(int r, int c) : rows_(r), cols_(c) { 20 | // vals_.resize(rows_*cols_); 21 | vals_ = std::vector(rows_*cols_); 22 | // vals_ = new T[rows_*cols_]; 23 | }; 24 | Matrix(const Matrix& M) : rows_(M.rows_), cols_(M.cols_), vals_(M.vals_) {}; 25 | 26 | //~Matrix() { delete [] vals_; }; 27 | 28 | int rows() { return rows_; }; 29 | int cols() { return cols_; }; 30 | 31 | std::vector column(int c) { 32 | std::vector r(rows_,0); 33 | for (int i=0; i(&vals_[c*rows_],&vals_[c*(rows_+1)]); 38 | } 39 | const T& operator()(int i, int j) const { 40 | return vals_[j*rows_+i]; 41 | } 42 | T& operator()(int i, int j) { 43 | return vals_[j*rows_+i]; 44 | } 45 | // modify parts of the matrix 46 | void set_column(int col, std::vector v) { 47 | for (unsigned i=0; i 52 | template 53 | void set_column(int col, std::vector> v) { 54 | for (unsigned i=0; ioperator()(i,j)); 64 | } 65 | printf("\n"); 66 | } 67 | } 68 | }; 69 | 70 | namespace blas { 71 | 72 | template 73 | T nrm2(std::vector& vec) { 74 | T res = T(0); 75 | for (auto it=vec.begin(); it!=vec.end(); ++it) res += (*it) * (*it); 76 | return std::sqrt(res); 77 | } 78 | 79 | template 80 | T nrm2(std::vector>& vec) { 81 | T res = T(0.); 82 | 83 | for (auto it=vec.begin(); it!=vec.end(); ++it) { 84 | for (int j=0; j 92 | void scal(std::vector& vec, T2 s) 93 | { 94 | for (auto it=vec.begin(); it!=vec.end(); ++it) 95 | *it *= s; 96 | } 97 | 98 | template 99 | T dotc(std::vector& x, std::vector& y) { 100 | T ret = T(0); 101 | auto yit = y.begin(); 102 | for (auto it=x.begin(); it!=x.end(); ++it, ++yit) 103 | ret += (*it) * (*yit); 104 | return ret; 105 | }; 106 | 107 | template 108 | void matvec(Matrix& A, std::vector& x, std::vector& r) { 109 | r = std::vector(x.size(),0); 110 | unsigned j; 111 | #pragma omp parallel for private(j) 112 | for (unsigned i=0; i 121 | void axpy(std::vector x, std::vector& y, T a) { 122 | for (unsigned i=0; i 128 | void axpy(std::vector> x, std::vector>& y, T a) { 129 | for (unsigned i=0; i 135 | void axpy(std::vector> x, std::vector& y, T a) { 136 | for (unsigned i=0; i 146 | typename Iter::value_type norm(Iter start, Iter end) 147 | { 148 | typename Iter::value_type sum = 0; 149 | 150 | for ( ; start != end; ++start) sum += (*start) * (*start); 151 | 152 | return std::sqrt(sum); 153 | } 154 | 155 | -------------------------------------------------------------------------------- /include/executor/EvalLocalSparse.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EvaluatorBase.hpp" 4 | #include "EvalP2P.hpp" 5 | #include "Matvec.hpp" 6 | 7 | #include 8 | 9 | /** Only evaluate local (direct) portion of the tree 10 | */ 11 | template 12 | class EvalLocalSparse 13 | : public EvaluatorBase { 14 | //! Type of box 15 | typedef typename Context::box_type box_type; 16 | //! Pair of boxees 17 | typedef std::pair box_pair; 18 | //! kernel type 19 | typedef typename Context::kernel_type kernel_type; 20 | //! kernel value type 21 | typedef typename kernel_type::kernel_value_type kernel_value_type; 22 | //! kernel charge type 23 | typedef typename kernel_type::charge_type charge_type; 24 | //! kernel result type 25 | typedef typename kernel_type::result_type result_type; 26 | 27 | //! matrix pair 28 | typedef std::pair matrix_pair; 29 | //! sparse matrix 30 | ublas::compressed_matrix A; 31 | 32 | public: 33 | // constructor -- create matrix 34 | EvalLocalSparse(Context& bc) { 35 | // Local P2P evaluator to construct the interaction matrix 36 | P2P_Lazy p2p_lazy(bc); 37 | 38 | // Queue based tree traversal for P2P, M2P, and/or M2L operations 39 | std::deque pairQ; 40 | pairQ.push_back(box_pair(bc.source_tree().root(), 41 | bc.target_tree().root())); 42 | 43 | while (!pairQ.empty()) { 44 | auto b1 = pairQ.front().first; 45 | auto b2 = pairQ.front().second; 46 | pairQ.pop_front(); 47 | 48 | if (b1.is_leaf()) { 49 | if (b2.is_leaf()) { 50 | // Both are leaves, P2P 51 | p2p_lazy.insert(b1, b2); 52 | } else { 53 | // b2 is not a leaf, Split the second box into children and interact 54 | auto c_end = b2.child_end(); 55 | for (auto cit = b2.child_begin(); cit != c_end; ++cit) 56 | interact(bc, b1, *cit, pairQ); 57 | } 58 | } else if (b2.is_leaf()) { 59 | // b1 is not a leaf, Split the first box into children and interact 60 | auto c_end = b1.child_end(); 61 | for (auto cit = b1.child_begin(); cit != c_end; ++cit) 62 | interact(bc, *cit, b2, pairQ); 63 | } else { 64 | // Neither are leaves, Split the larger into children and interact 65 | if (b1.side_length() > b2.side_length()) { // TODO: optimize? 66 | // Split the first box into children and interact 67 | auto c_end = b1.child_end(); 68 | for (auto cit = b1.child_begin(); cit != c_end; ++cit) 69 | interact(bc, *cit, b2, pairQ); 70 | } else { 71 | // Split the second box into children and interact 72 | auto c_end = b2.child_end(); 73 | for (auto cit = b2.child_begin(); cit != c_end; ++cit) 74 | interact(bc, b1, *cit, pairQ); 75 | } 76 | } 77 | } 78 | 79 | A = p2p_lazy.to_matrix(); 80 | } // end constructor 81 | 82 | void execute(Context& bc) const { 83 | // printf("EvalLocalSparse::execute(Context&)\n"); 84 | // do shit here 85 | auto root = bc.source_tree().root(); 86 | 87 | //double tic, toc; 88 | //static double t_charge = 0., t_result = 0.; 89 | 90 | //tic = get_time(); 91 | typedef typename Context::charge_type charge_type; 92 | ublas::vector charges(bc.source_tree().bodies()); 93 | std::copy(bc.charge_begin(root), bc.charge_end(root), charges.begin()); 94 | //toc = get_time(); 95 | //t_charge += toc-tic; 96 | 97 | // call the matvec 98 | typedef typename Context::result_type result_type; 99 | // ublas::vector results = ublas::prod(A, charges); 100 | // ublas::vector results = Matvec(A, charges); 101 | ublas::vector results = Matvec, 102 | ublas::vector, 103 | ublas::vector>(A,charges); 104 | 105 | // copy results back into iterator 106 | //tic = get_time(); 107 | std::transform(results.begin(), results.end(), 108 | bc.result_begin(root), bc.result_begin(root), 109 | std::plus()); 110 | //toc = get_time(); 111 | //t_result += toc-tic; 112 | 113 | //printf("charge copy: %.3es, result copy: %.3es\n",t_charge, t_result); 114 | } 115 | 116 | template 117 | void interact(Context& bc, 118 | const BOX& b1, const BOX& b2, 119 | Q& pairQ) const { 120 | // ignore accepted multipoles 121 | if (!bc.accept_multipole(b1, b2)) { 122 | pairQ.push_back(box_pair(b1,b2)); 123 | } 124 | } 125 | }; 126 | 127 | 128 | template 129 | EvaluatorBase* make_sparse_local_eval(Context& bc, Options& opts) { 130 | (void) opts; 131 | return new EvalLocalSparse(bc); 132 | } 133 | -------------------------------------------------------------------------------- /kernel/UnitKernel.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /** @file UnitKernel.hpp 3 | * @brief Implements the unit kernel defined by 4 | * K(t,s) = 1 if t != s 5 | * K(t,s) = 0 if t == s 6 | */ 7 | 8 | #include "Vec.hpp" 9 | 10 | class UnitKernel 11 | { 12 | public: 13 | //! The dimension of the Kernel 14 | static constexpr unsigned dimension = 3; 15 | //! Point type 16 | typedef Vec point_type; 17 | //! Source type 18 | typedef point_type source_type; 19 | //! Target type 20 | typedef point_type target_type; 21 | //! Charge type 22 | typedef double charge_type; 23 | //! The return type of a kernel evaluation 24 | typedef unsigned kernel_value_type; 25 | //! The product of the kernel_value_type and the charge_type 26 | typedef double result_type; 27 | 28 | //! Multipole expansion type 29 | typedef double multipole_type; 30 | //! Local expansion type 31 | typedef double local_type; 32 | 33 | /** Initialize a multipole expansion with the size of a box at this level */ 34 | void init_multipole(multipole_type& M, const point_type&, unsigned) const { 35 | M = 0; 36 | } 37 | /** Initialize a local expansion with the size of a box at this level */ 38 | void init_local(local_type& L, const point_type&, unsigned) const { 39 | L = 0; 40 | } 41 | 42 | /** Kernel evaluation 43 | * K(t,s) 44 | * 45 | * @param[in] t,s The target and source points to evaluate the kernel 46 | */ 47 | kernel_value_type operator()(const point_type& t, 48 | const point_type& s) const { 49 | return t == s ? kernel_value_type(0) : kernel_value_type(1); 50 | } 51 | 52 | /** Kernel P2M operation 53 | * M += Op(s) * c where M is the multipole and s is the source 54 | * 55 | * @param[in] source The point source 56 | * @param[in] charge The source's corresponding charge 57 | * @param[in] center The center of the box containing the multipole expansion 58 | * @param[in,out] M The multipole expansion to accumulate into 59 | */ 60 | void P2M(const point_type&, const charge_type& charge, 61 | const point_type&, multipole_type& M) const { 62 | M += charge; 63 | } 64 | 65 | /** Kernel M2M operator 66 | * M_t += Op(M_s) where M_t is the target and M_s is the source 67 | * 68 | * @param[in] source The multipole source at the child level 69 | * @param[in,out] target The multipole target to accumulate into 70 | * @param[in] translation The vector from source to target 71 | * @pre source includes the influence of all points within its box 72 | */ 73 | void M2M(const multipole_type& source, 74 | multipole_type& target, 75 | const point_type&) const { 76 | target += source; 77 | } 78 | 79 | /** Kernel M2L operation 80 | * L += Op(M) 81 | * 82 | * @param[in] source The multpole expansion source 83 | * @param[in,out] target The local expansion target 84 | * @param[in] translation The vector from source to target 85 | * @pre translation obeys the multipole-acceptance criteria 86 | * @pre source includes the influence of all points within its box 87 | */ 88 | void M2L(const multipole_type& source, 89 | local_type& target, 90 | const point_type&) const { 91 | target += source; 92 | } 93 | 94 | /** Kernel M2P operation 95 | * r += Op(M) where M is the multipole and r is the result 96 | * 97 | * @param[in] M The multpole expansion 98 | * @param[in] center The center of the box with the multipole expansion 99 | * @param[in] target The target point position 100 | * @param[in,out] result The target's corresponding result to accumulate into 101 | * @pre M includes the influence of all points within its box 102 | */ 103 | void M2P(const multipole_type& M, const point_type&, 104 | const point_type&, result_type& result) const { 105 | result += M; 106 | } 107 | 108 | /** Kernel L2L operator 109 | * L_t += Op(L_s) where L_t is the target and L_s is the source 110 | * 111 | * @param[in] source The local source at the parent level 112 | * @param[in,out] target The local target to accumulate into 113 | * @param[in] translation The vector from source to target 114 | * @pre source includes the influence of all points outside its box 115 | */ 116 | void L2L(const local_type& source, 117 | local_type& target, 118 | const point_type&) const { 119 | target += source; 120 | } 121 | 122 | /** Kernel L2P operation 123 | * r += Op(L) where L is the local expansion and r is the result 124 | * 125 | * @param[in] L The local expansion 126 | * @param[in] center The center of the box with the local expansion 127 | * @param[in] t_begin,t_end Iterator pair to the target points 128 | * @param[in] r_begin Iterator to the result accumulator 129 | * @pre L includes the influence of all points outside its box 130 | */ 131 | void L2P(const local_type& L, const point_type&, 132 | const point_type&, result_type& result) const { 133 | result += L; 134 | } 135 | }; 136 | -------------------------------------------------------------------------------- /examples/BEM/SemiAnalytical.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** holder class for semi-analytical integral */ 4 | 5 | #include "AnalyticalIntegral.hpp" 6 | #include "Vec.hpp" 7 | #include "Mat3.hpp" 8 | 9 | namespace AnalyticalIntegral 10 | { 11 | 12 | template 13 | void lineInt(ResultType& G, ResultType& dGdn, T z, T x, T v1, T v2, KappaType kappa=0.) 14 | { 15 | auto theta1 = atan2(v1,x); 16 | auto theta2 = atan2(v2,x); 17 | auto dtheta = theta2 - theta1; 18 | auto thetam = (theta2 + theta1)/2; 19 | 20 | 21 | T absZ = fabs(z), signZ; 22 | if (absZ<1e-10) signZ = 0; 23 | else signZ = z/absZ; 24 | 25 | // Loop over gauss points 26 | ResultType expKr, expKz = exp(-kappa*absZ); 27 | T thetak, Rtheta, R; 28 | 29 | // define gauss points -- hardcode for now 30 | const int n_gauss = 5; 31 | double xk[5] = { -9.06179846e-01, -5.38469310e-01, 1.78162900e-17, 9.06179846e-01,5.38469310e-01 }; 32 | double wk[5] = { 0.23692689, 0.47862867, 0.56888889, 0.23692689, 0.47862867 }; 33 | //double xk[7] = { -9.49107912e-01, -7.41531186e-01, -4.05845151e-01, -5.34227877e-17, 9.49107912e-01, 7.41531186e-01, 4.05845151e-01 }; 34 | //double wk[7] = { 0.12948497, 0.27970539, 0.38183005, 0.41795918, 0.12948497, 0.27970539, 0.38183005 }; 35 | for (int i=0; i1e-10) 43 | { 44 | G += ResultType(0.); 45 | dGdn += ResultType(0.); 46 | } 47 | // devolve to Laplace 48 | else 49 | { 50 | G += wk[i]*(R-absZ) * dtheta/2; 51 | dGdn += wk[i]*(z/R - signZ) * dtheta/2; 52 | } 53 | } else if (E == YUKAWA) { 54 | if (kappa>1e-10) 55 | { 56 | G += -wk[i]*(expKr - expKz)/kappa * dtheta/2; 57 | dGdn += wk[i]*(z/R*expKr - expKz*signZ) * dtheta/2; 58 | } 59 | // devolve to Laplace 60 | else 61 | { 62 | G += wk[i]*(R-absZ) * dtheta/2; 63 | dGdn += wk[i]*(z/R - signZ) * dtheta/2; 64 | } 65 | } else { 66 | // E == LAPLACE 67 | G += wk[i]*(R-absZ) * dtheta/2; 68 | dGdn += wk[i]*(z/R - signZ) * dtheta/2; 69 | } 70 | } 71 | } 72 | 73 | template 74 | Vec<3,T> cross(const Vec<3,T>& u, const Vec<3,T>& v) { 75 | return Vec<3,T>(u[1]*v[2] - u[2]*v[1], 76 | u[2]*v[0] - u[0]*v[2], 77 | u[0]*v[1] - u[1]*v[0]); 78 | } 79 | 80 | template 81 | void intSide(ResultType& G, ResultType& dGdn, Vec<3,T>& v1, Vec<3,T>& v2, T p, T Kappa) 82 | { 83 | typedef Vec<3,T> vec3; 84 | typedef Mat3 mat3; 85 | 86 | vec3 v21 = v2 - v1; 87 | 88 | auto L21 = norm(v21); 89 | vec3 v21u = v21/L21; 90 | 91 | vec3 unit(0,0,1); 92 | vec3 orthog = cross(unit, v21u); 93 | 94 | auto alpha = dot(v21,v1)/(L21*L21); 95 | 96 | vec3 rOrthog = -alpha*v21 + v1; 97 | 98 | mat3 rotateToVertLine; 99 | 100 | for(int i=0; i<3; i++) 101 | { 102 | //rotateToVertLine(0,i) = orthog[i]; 103 | //rotateToVertLine(1,i) = v21u[i]; 104 | //rotateToVertLine(2,i) = unit[i]; 105 | rotateToVertLine(i,0) = orthog[i]; 106 | rotateToVertLine(i,1) = v21u[i]; 107 | rotateToVertLine(i,2) = unit[i]; 108 | } 109 | 110 | vec3 v1new = rotateToVertLine.multiply(v1); 111 | 112 | if (v1new[0]<0) 113 | { 114 | v21u = -v21u; 115 | orthog = -orthog; 116 | rotateToVertLine = -rotateToVertLine; 117 | rotateToVertLine(2,2) = 1.; 118 | v1new = rotateToVertLine.multiply(v1); 119 | } 120 | 121 | vec3 v2new = rotateToVertLine.multiply(v2); 122 | vec3 rOrthognew = rotateToVertLine.multiply(rOrthog); 123 | auto x = v1new[0]; 124 | 125 | if ((v1new[1]>0 && v2new[1]<0) || (v1new[1]<0 && v2new[1]>0)) 126 | { 127 | T G1 = 0., dG1dn = 0.; 128 | T G2 = 0., dG2dn = 0.; 129 | 130 | lineInt(G1,dG1dn, p, x, 0, v1new[1], Kappa); 131 | lineInt(G2,dG2dn, p, x, v2new[1], 0, Kappa); 132 | 133 | G += G1 + G2; 134 | dGdn += dG1dn + dG2dn; 135 | } 136 | else 137 | { 138 | T G1 = 0., dG1dn = 0.; 139 | lineInt(G1,dG1dn, p, x, v1new[1], v2new[1], Kappa); 140 | 141 | G -= G1; 142 | dGdn -= dG1dn; 143 | } 144 | 145 | } 146 | 147 | template 148 | void SemiAnalytical(ResultType& G, ResultType& dGdn, Vec<3,T> y0, Vec<3,T> y1, Vec<3,T> y2, Vec<3,T> x, bool same, T Kappa=0) 149 | { 150 | typedef Vec<3,T> vec3; 151 | typedef Mat3 mat3; 152 | 153 | // Put first panel at origin 154 | vec3 x_panel = x-y0; 155 | vec3 y0_panel; 156 | vec3 y1_panel = y1-y0; 157 | vec3 y2_panel = y2-y0; 158 | vec3 X = y1_panel; 159 | 160 | // Find panel coordinate system X: 0->1 161 | vec3 Z = cross(y1_panel, y2_panel); 162 | auto Xnorm = norm(X); 163 | auto Znorm = norm(Z); 164 | X /= Xnorm; 165 | Z /= Znorm; 166 | 167 | vec3 Y = cross(Z,X); 168 | 169 | // Rotate the coordinate system to match panel plane 170 | mat3 rot_matrix; 171 | for (auto i=0u; i< 3; i++) { 172 | rot_matrix(0,i) = X[i]; 173 | rot_matrix(1,i) = Y[i]; 174 | rot_matrix(2,i) = Z[i]; 175 | } 176 | 177 | vec3 panel0_plane = rot_matrix.multiply(y0_panel); 178 | vec3 panel1_plane = rot_matrix.multiply(y1_panel); 179 | vec3 panel2_plane = rot_matrix.multiply(y2_panel); 180 | vec3 x_plane = rot_matrix.multiply(x_panel); 181 | 182 | // Shift origin so it matches collocation point 183 | vec3 panel0_final = panel0_plane - x_plane; 184 | vec3 panel1_final = panel1_plane - x_plane; 185 | vec3 panel2_final = panel2_plane - x_plane; 186 | 187 | // adjust final value 188 | panel0_final[2] = panel0_plane[2]; 189 | panel1_final[2] = panel1_plane[2]; 190 | panel2_final[2] = panel2_plane[2]; 191 | 192 | // Loop over sides 193 | intSide(G, dGdn, panel0_final, panel1_final, x_plane[2], Kappa); // Side 0 194 | intSide(G, dGdn, panel1_final, panel2_final, x_plane[2], Kappa); // Side 1 195 | intSide(G, dGdn, panel2_final, panel0_final, x_plane[2], Kappa); // Side 2 196 | 197 | if (same) 198 | { 199 | if (E == YUKAWA) dGdn = -2*M_PI; 200 | if (E == LAPLACE) dGdn = 2*M_PI; 201 | } 202 | // printf("G: %.4lg, dGdn: %.4lg\n",G,dGdn); 203 | } 204 | 205 | }; // end namespace AnalyticalIntegral 206 | -------------------------------------------------------------------------------- /examples/YukawaBEM.cpp: -------------------------------------------------------------------------------- 1 | /** @file serialBEM.cpp 2 | * @brief Testing and debugging script for FMM-BEM applications 3 | */ 4 | 5 | 6 | #include "FMM_plan.hpp" 7 | #include "YukawaCartesianBEM.hpp" 8 | #include "Triangulation.hpp" 9 | #include "gmres.hpp" 10 | 11 | #include 12 | 13 | double get_time() 14 | { 15 | struct timeval tv; 16 | gettimeofday(&tv, NULL); 17 | return (double)(tv.tv_sec + 1e-6*tv.tv_usec); 18 | } 19 | 20 | struct ProblemOptions 21 | { 22 | typedef enum { PHI_SET, DPHIDN_SET } BoundaryCondition; 23 | double kappa_ = 0.; 24 | BoundaryCondition bc_ = PHI_SET; 25 | double value_ = 1.; 26 | int recursions = 4; 27 | 28 | ProblemOptions() : kappa_(0.125), bc_(PHI_SET), value_(1.) {}; 29 | ProblemOptions(double kappa) : kappa_(kappa), bc_(PHI_SET), value_(1.) {}; 30 | ProblemOptions(double kappa, double value) : kappa_(kappa), bc_(PHI_SET), value_(value) {}; 31 | ProblemOptions(double kappa, BoundaryCondition bc, double value) : kappa_(kappa), bc_(bc), value_(value) {}; 32 | 33 | double getKappa() { return kappa_; }; 34 | double getValue() { return value_; }; 35 | BoundaryCondition getBC() { return bc_; }; 36 | }; 37 | 38 | void printHelpAndExit() 39 | { 40 | printf("serialBEM : FMM-BEM for Potential problems\n"); 41 | printf("\nUsage: ./serialBEM \n\n"); 42 | printf("Options:\n"); 43 | printf("-kappa : Set shielding constant for the Yukawa potential\n"); 44 | printf("-theta : Set MAC theta for treecode evaluators\n"); 45 | printf("-p : Number of terms in the Multipole / Local expansions\n"); 46 | printf("-k {1,3,4,7} : Number of Gauss integration points used per panel\n"); 47 | printf("-lazy_eval : enable 'lazy' evaluator\n"); 48 | printf("-ncrit : Maximum # of particles per Octree box\n"); 49 | printf("-recursions : number of recursive subdivisions to create a sphere - # panels = 2*4^recursions\n"); 50 | printf("-help : print this message\n"); 51 | std::exit(0); 52 | } 53 | 54 | template 55 | void initialiseSphere(std::vector& panels, 56 | std::vector& charges, 57 | unsigned recursions = 4) 58 | { 59 | (void) charges; 60 | create_unit_sphere(panels, recursions); 61 | } 62 | 63 | int main(int argc, char **argv) 64 | { 65 | int numPanels= 1000, recursions = 4, p = 5, k = 3; 66 | double kappa = 0.; 67 | FMMOptions opts; 68 | opts.set_mac_theta(0.5); // Multipole acceptance criteria 69 | opts.set_max_per_box(10); 70 | SolverOptions solver_options; 71 | bool second_kind = false; 72 | 73 | // parse command line args 74 | // check if no arguments given 75 | if (argc == 1) printHelpAndExit(); 76 | for (int i = 1; i < argc; ++i) { 77 | if (strcmp(argv[i],"-kappa") == 0 ) { 78 | i++; 79 | kappa = atof(argv[i]); 80 | } else if (strcmp(argv[i],"-theta") == 0) { 81 | i++; 82 | opts.set_mac_theta((double)atof(argv[i])); 83 | } else if (strcmp(argv[i],"-eval") == 0) { 84 | i++; 85 | if (strcmp(argv[i],"FMM") == 0) { 86 | opts.evaluator = FMMOptions::FMM; 87 | } else if (strcmp(argv[i],"TREE") == 0) { 88 | opts.evaluator = FMMOptions::TREECODE; 89 | } else { 90 | printf("[W]: Unknown evaluator type: \"%s\"\n",argv[i]); 91 | } 92 | } else if (strcmp(argv[i],"-p") == 0) { 93 | i++; 94 | p = atoi(argv[i]); 95 | } else if (strcmp(argv[i],"-k") == 0) { 96 | i++; 97 | k = atoi(argv[i]); 98 | } else if (strcmp(argv[i],"-lazy_eval") == 0) { 99 | opts.lazy_evaluation = true; 100 | } else if (strcmp(argv[i],"-ncrit") == 0) { 101 | i++; 102 | opts.set_max_per_box((unsigned)atoi(argv[i])); 103 | } else if (strcmp(argv[i],"-recursions") == 0) { 104 | i++; 105 | recursions = atoi(argv[i]); 106 | } else if (strcmp(argv[i],"-second_kind") == 0) { 107 | second_kind = true; 108 | } else if (strcmp(argv[i],"-fixed_p") == 0) { 109 | solver_options.variable_p = false; 110 | } else if (strcmp(argv[i],"-help") == 0) { 111 | printHelpAndExit(); 112 | } else { 113 | printf("[W]: Unknown command line arg: \"%s\"\n",argv[i]); 114 | printHelpAndExit(); 115 | } 116 | } 117 | 118 | double tic, toc; 119 | tic = get_time(); 120 | // Init the FMM Kernel 121 | typedef YukawaCartesianBEM kernel_type; 122 | kernel_type K(p, kappa, k); 123 | 124 | // useful typedefs 125 | typedef kernel_type::point_type point_type; 126 | typedef kernel_type::source_type source_type; 127 | typedef kernel_type::target_type target_type; 128 | typedef kernel_type::charge_type charge_type; 129 | typedef kernel_type::result_type result_type; 130 | 131 | // Init points and charges 132 | std::vector panels(numPanels); 133 | std::vector charges(numPanels); 134 | initialiseSphere(panels, charges, recursions); //, ProblemOptions()); 135 | 136 | // run case solving for Phi (instead of dPhi/dn) // Second-kind equation 137 | if (second_kind) 138 | for (auto& it : panels) it.switch_BC(); 139 | 140 | // set constant Phi || dPhi/dn for each panel 141 | charges.resize(panels.size()); 142 | charges = std::vector(panels.size(),1.); 143 | 144 | // Build the FMM structure 145 | FMM_plan plan = FMM_plan(K, panels, opts); 146 | 147 | // generate the RHS and initial condition 148 | std::vector x(panels.size(),0.); 149 | 150 | // generate RHS using temporary FMM plan 151 | std::vector b(panels.size(),0.); 152 | { 153 | for (auto& it : panels) it.switch_BC(); 154 | FMM_plan rhs_plan = FMM_plan(K,panels,opts); 155 | b = rhs_plan.execute(charges,p); 156 | for (auto& it : panels) it.switch_BC(); 157 | } 158 | 159 | toc = get_time(); 160 | double setup_time = toc-tic; 161 | 162 | // Solve the system using GMRES 163 | // generate the Preconditioner 164 | tic = get_time(); 165 | Preconditioners::Diagonal M(K,panels.begin(),panels.end()); 166 | fmm_gmres(plan, x, b, solver_options, M); 167 | // direct_gmres(K, panels, x, b, SolverOptions()); 168 | toc = get_time(); 169 | double solve_time = toc-tic; 170 | 171 | printf("\nTIMING:\n"); 172 | printf("\tsetup : %.4es\n",setup_time); 173 | printf("\tsolve : %.4es\n",solve_time); 174 | 175 | // check errors -- analytical solution for dPhi/dn = 1. 176 | double e = 0.; 177 | double e2 = 0.; 178 | double an = 1.; 179 | for (auto xi : x) { e += (xi-an)*(xi-an); e2 += an*an; } 180 | 181 | printf("error: %.3e\n",sqrt(e/e2)); 182 | } 183 | 184 | -------------------------------------------------------------------------------- /serialrun_stresslet.cpp: -------------------------------------------------------------------------------- 1 | /** @file serialun.cpp 2 | * @brief Testing and debugging script 3 | */ 4 | 5 | //#define DEBUG 6 | 7 | #include 8 | #define STRESSLET 9 | #include "StokesSpherical.hpp" 10 | 11 | #include 12 | 13 | // modify error checking for counting kernel 14 | // TODO: Do this much better... 15 | //#define SKELETON_KERNEL 16 | //#define UNIT_KERNEL 17 | //#define SPH_KERNEL 18 | //#define CART_KERNEL 19 | //#define YUKAWA_KERNEL 20 | //#define YUKAWA_SPH 21 | #define STOKES_SPH 22 | 23 | // Random number in [0,1) 24 | inline double drand() { 25 | return ::drand48(); 26 | } 27 | 28 | // Random number in [A,B) 29 | inline double drand(double A, double B) { 30 | return A + (B-A) * drand(); 31 | } 32 | 33 | int main(int argc, char **argv) 34 | { 35 | int numBodies = 1000, p=5; 36 | bool checkErrors = true; 37 | double beta = 0.125; 38 | 39 | FMMOptions opts = get_options(argc, argv); 40 | 41 | // parse custom command line args 42 | for (int i = 1; i < argc; ++i) { 43 | if (strcmp(argv[i],"-N") == 0) { 44 | i++; 45 | numBodies = atoi(argv[i]); 46 | } else if (strcmp(argv[i],"-p") == 0) { 47 | i++; 48 | p = atoi(argv[i]); 49 | } else if (strcmp(argv[i],"-beta") == 0) { 50 | i++; 51 | beta = atof(argv[i]); 52 | } else if (strcmp(argv[i],"-nocheck") == 0) { 53 | checkErrors = false; 54 | } 55 | } 56 | 57 | // Init the FMM Kernel 58 | #ifdef SKELETON_KERNEL 59 | typedef KernelSkeleton kernel_type; 60 | kernel_type K; 61 | #endif 62 | #ifdef SPH_KERNEL 63 | typedef LaplaceSpherical kernel_type; 64 | kernel_type K(p); 65 | #endif 66 | #ifdef CART_KERNEL 67 | typedef LaplaceCartesian<5> kernel_type; 68 | kernel_type K; 69 | #endif 70 | #ifdef YUKAWA_KERNEL 71 | typedef YukawaCartesian kernel_type; 72 | kernel_type K(p,beta); 73 | #endif 74 | #ifdef YUKAWA_SPH 75 | typedef YukawaSpherical kernel_type; 76 | kernel_type K(p,beta); 77 | #endif 78 | #ifdef UNIT_KERNEL 79 | typedef UnitKernel kernel_type; 80 | kernel_type K; 81 | #endif 82 | #ifdef STOKES_SPH 83 | typedef StokesSpherical kernel_type; 84 | kernel_type K(p); 85 | #endif 86 | // if not using a Yukawa kernel, quiet warnings 87 | #if !defined(YUKAWA_KERNEL) && !defined(YUKAWA_SPH) 88 | (void) beta; 89 | #endif 90 | 91 | typedef kernel_type::point_type point_type; 92 | typedef kernel_type::source_type source_type; 93 | typedef kernel_type::target_type target_type; 94 | typedef kernel_type::charge_type charge_type; 95 | typedef kernel_type::result_type result_type; 96 | 97 | 98 | // Init points and charges 99 | std::vector points(numBodies); 100 | for (int k = 0; k < numBodies; ++k) 101 | points[k] = source_type(drand(), drand(), drand()); 102 | //std::vector target_points = points; 103 | 104 | std::vector charges(numBodies); 105 | for (int k = 0; k < numBodies; ++k) { 106 | #if defined(STOKES_SPH) 107 | #if defined(STRESSLET) 108 | charges[k][0] = drand(); 109 | charges[k][1] = drand(); 110 | charges[k][2] = drand(); 111 | charges[k][3] = 1.; 112 | charges[k][4] = 0.; 113 | charges[k][5] = 0.; 114 | #else 115 | charges[k] = charge_type(1,1,1); // charge_type(drand(),drand(),drand()); 116 | #endif 117 | #else 118 | charges[k] = drand(); 119 | #endif 120 | } 121 | 122 | // Build the FMM 123 | //fmm_plan plan = fmm_plan(K, bodies, opts); 124 | FMM_plan plan = FMM_plan(K, points, opts); 125 | 126 | // Execute the FMM 127 | //fmm_execute(plan, charges, target_points); 128 | std::vector result = plan.execute(charges); 129 | 130 | // Check the result 131 | // TODO: More elegant 132 | if (checkErrors) { 133 | // choose a number of samples to use 134 | int numSamples = std::min(numBodies, 1000); 135 | std::vector sample_map(numSamples); 136 | std::vector sample_targets(numSamples); 137 | std::vector exact(numSamples); 138 | 139 | // sample the space (needs to be done better) 140 | for (int i=0; i> 15 % numBodies; 142 | sample_map[i] = sample; 143 | sample_targets[i] = points[sample]; 144 | } 145 | 146 | // Compute the result with a direct matrix-vector multiplication 147 | Direct::matvec(K, points.begin(), points.end(), charges.begin(), 148 | sample_targets.begin(), sample_targets.end(), exact.begin()); 149 | 150 | #if defined(SPH_KERNEL) || defined(CART_KERNEL) || defined(YUKAWA_KERNEL) 151 | result_type rdiff, rnorm; 152 | for (unsigned k = 0; k < exact.size(); ++k) { 153 | auto exact_val = exact[k]; 154 | auto fmm_val = result[sample_map[k]]; 155 | // printf("[%03d] exact: %lg, FMM: %lg\n", k, exact_val[0], fmm_val[0]); 156 | 157 | rdiff += (fmm_val - exact_val) * (fmm_val - exact_val); 158 | rnorm += exact_val * exact_val; 159 | } 160 | 161 | printf("Error (pot) : %.4e\n", sqrt(rdiff[0] / rnorm[0])); 162 | printf("Error (acc) : %.4e\n", sqrt((rdiff[1]+rdiff[2]+rdiff[3]) / 163 | (rnorm[1]+rnorm[2]+rnorm[3]))); 164 | #endif 165 | #if defined(YUKAWA_SPH) 166 | result_type rdiff = 0., rnorm = 0.; 167 | for (unsigned k = 0; k < exact.size(); ++k) { 168 | // printf("[%03d] exact: %lg, FMM: %lg\n", k, exact[k], result[k]); 169 | auto exact_val = exact[k]; 170 | auto fmm_val = result[sample_map[k]]; 171 | 172 | rdiff = (fmm_val - exact_val) * (fmm_val - exact_val); 173 | rnorm = exact_val * exact_val; 174 | } 175 | 176 | printf("Error (pot) : %.4e\n", sqrt(rdiff / rnorm)); 177 | #endif 178 | #if defined(STOKES_SPH) 179 | result_type rdiff = result_type(0.), rnorm = result_type(0.); 180 | for (unsigned k = 0; k < exact.size(); ++k) { 181 | auto exact_val = exact[k]; 182 | auto fmm_val = result[sample_map[k]]; 183 | // std::cout << exact_val << " : " << fmm_val << std::endl; 184 | 185 | for (unsigned i=0; i < 3; i++) { 186 | rdiff[i] += (fmm_val[i]-exact_val[i])*(fmm_val[i]-exact_val[i]); 187 | rnorm[i] += exact_val[i]*exact_val[i]; 188 | } 189 | } 190 | auto div = rdiff/rnorm; 191 | printf("Error (u) : %.4e, (v) : %.4e, (w) : %.4e\n",sqrt(div[0]),sqrt(div[1]),sqrt(div[2])); 192 | #endif 193 | #ifdef UNIT_KERNEL 194 | int wrong_results = 0; 195 | for (unsigned k = 0; k < exact.size(); ++k) { 196 | auto exact_val = exact[k]; 197 | auto fmm_val = result[sample_map[k]]; 198 | 199 | // printf("[%03d] exact: %lg, FMM: %lg\n", k, exact[k], result[k]); 200 | 201 | if ((exact_val - fmm_val) / exact_val > 1e-13) 202 | ++wrong_results; 203 | } 204 | printf("Wrong counts: %d\n", wrong_results); 205 | #endif 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /serialrun.cpp: -------------------------------------------------------------------------------- 1 | /** @file serialun.cpp 2 | * @brief Testing and debugging script 3 | */ 4 | 5 | //#define DEBUG 6 | 7 | #include 8 | //#include 9 | #include 10 | //#include 11 | //#include 12 | //#include 13 | #include 14 | //#include 15 | #include 16 | #include "StokesSpherical.hpp" 17 | 18 | #include 19 | 20 | // modify error checking for counting kernel 21 | // TODO: Do this much better... 22 | //#define SKELETON_KERNEL 23 | //#define UNIT_KERNEL 24 | // #define SPH_KERNEL 25 | //#define CART_KERNEL 26 | //#define YUKAWA_KERNEL 27 | #define YUKAWA_SPH 28 | //#define STOKES_SPH 29 | 30 | // Random number in [0,1) 31 | inline double drand() { 32 | return ::drand48(); 33 | } 34 | 35 | // Random number in [A,B) 36 | inline double drand(double A, double B) { 37 | return A + (B-A) * drand(); 38 | } 39 | 40 | int main(int argc, char **argv) 41 | { 42 | //std::vector args(argv+1, argv+argc); 43 | //std::cout << args[0] << '\n' << args[1] << '\n' << args[2] << std::endl; 44 | 45 | int numBodies = 1000, p=5; 46 | bool checkErrors = true; 47 | double beta = 0.125; 48 | 49 | FMMOptions opts = get_options(argc, argv); 50 | 51 | //opts.local_evaluation = true; 52 | //opts.sparse_local = true; 53 | 54 | // parse custom command line args 55 | for (int i = 1; i < argc; ++i) { 56 | if (strcmp(argv[i],"-N") == 0) { 57 | i++; 58 | numBodies = atoi(argv[i]); 59 | } else if (strcmp(argv[i],"-p") == 0) { 60 | i++; 61 | p = atoi(argv[i]); 62 | } else if (strcmp(argv[i],"-beta") == 0) { 63 | i++; 64 | beta = atof(argv[i]); 65 | } else if (strcmp(argv[i],"-nocheck") == 0) { 66 | checkErrors = false; 67 | } 68 | } 69 | 70 | // Init the FMM Kernel 71 | #ifdef SKELETON_KERNEL 72 | typedef KernelSkeleton kernel_type; 73 | kernel_type K; 74 | #endif 75 | #ifdef SPH_KERNEL 76 | typedef LaplaceSpherical kernel_type; 77 | kernel_type K(p); 78 | #endif 79 | #ifdef CART_KERNEL 80 | typedef LaplaceCartesian<5> kernel_type; 81 | kernel_type K; 82 | #endif 83 | #ifdef YUKAWA_KERNEL 84 | typedef YukawaCartesian kernel_type; 85 | kernel_type K(p,beta); 86 | #endif 87 | #ifdef YUKAWA_SPH 88 | typedef YukawaSpherical kernel_type; 89 | kernel_type K(p,beta); 90 | #endif 91 | #ifdef UNIT_KERNEL 92 | typedef UnitKernel kernel_type; 93 | kernel_type K; 94 | #endif 95 | #ifdef STOKES_SPH 96 | typedef StokesSpherical kernel_type; 97 | kernel_type K(p); 98 | #endif 99 | // if not using a Yukawa kernel, quiet warnings 100 | #if !defined(YUKAWA_KERNEL) && !defined(YUKAWA_SPH) 101 | (void) beta; 102 | #endif 103 | 104 | typedef kernel_type::point_type point_type; 105 | typedef kernel_type::source_type source_type; 106 | typedef kernel_type::target_type target_type; 107 | typedef kernel_type::charge_type charge_type; 108 | typedef kernel_type::result_type result_type; 109 | 110 | 111 | // Init points and charges 112 | std::vector points(numBodies); 113 | for (int k = 0; k < numBodies; ++k) 114 | points[k] = source_type(drand(), drand(), drand()); 115 | //std::vector target_points = points; 116 | 117 | std::vector charges(numBodies); 118 | for (int k = 0; k < numBodies; ++k) { 119 | #if defined(STOKES_SPH) 120 | charges[k] = charge_type(1,1,1); // charge_type(drand(),drand(),drand()); 121 | #else 122 | charges[k] = drand(); 123 | #endif 124 | } 125 | 126 | // Build the FMM 127 | //fmm_plan plan = fmm_plan(K, bodies, opts); 128 | FMM_plan plan = FMM_plan(K, points, opts); 129 | 130 | // Execute the FMM 131 | //fmm_execute(plan, charges, target_points); 132 | std::vector result = plan.execute(charges); 133 | 134 | // Check the result 135 | // TODO: More elegant 136 | if (checkErrors) { 137 | // choose a number of samples to use 138 | int numSamples = std::min(numBodies, 1000); 139 | std::vector sample_map(numSamples); 140 | std::vector sample_targets(numSamples); 141 | std::vector exact(numSamples); 142 | 143 | // sample the space (needs to be done better) 144 | for (int i=0; i> 15 % numBodies; 146 | sample_map[i] = sample; 147 | sample_targets[i] = points[sample]; 148 | } 149 | 150 | // Compute the result with a direct matrix-vector multiplication 151 | Direct::matvec(K, points.begin(), points.end(), charges.begin(), 152 | sample_targets.begin(), sample_targets.end(), exact.begin()); 153 | 154 | #if defined(SPH_KERNEL) || defined(CART_KERNEL) || defined(YUKAWA_KERNEL) 155 | result_type rdiff, rnorm; 156 | for (unsigned k = 0; k < exact.size(); ++k) { 157 | auto exact_val = exact[k]; 158 | auto fmm_val = result[sample_map[k]]; 159 | // printf("[%03d] exact: %lg, FMM: %lg\n", k, exact_val[0], fmm_val[0]); 160 | 161 | rdiff += (fmm_val - exact_val) * (fmm_val - exact_val); 162 | rnorm += exact_val * exact_val; 163 | } 164 | 165 | printf("Error (pot) : %.4e\n", sqrt(rdiff[0] / rnorm[0])); 166 | printf("Error (acc) : %.4e\n", sqrt((rdiff[1]+rdiff[2]+rdiff[3]) / 167 | (rnorm[1]+rnorm[2]+rnorm[3]))); 168 | #endif 169 | #if defined(YUKAWA_SPH) 170 | result_type rdiff = 0., rnorm = 0.; 171 | for (unsigned k = 0; k < exact.size(); ++k) { 172 | // printf("[%03d] exact: %lg, FMM: %lg\n", k, exact[k], result[k]); 173 | auto exact_val = exact[k]; 174 | auto fmm_val = result[sample_map[k]]; 175 | 176 | rdiff = (fmm_val - exact_val) * (fmm_val - exact_val); 177 | rnorm = exact_val * exact_val; 178 | } 179 | 180 | printf("Error (pot) : %.4e\n", sqrt(rdiff / rnorm)); 181 | #endif 182 | #if defined(STOKES_SPH) 183 | result_type rdiff = result_type(0.), rnorm = result_type(0.); 184 | for (unsigned k = 0; k < exact.size(); ++k) { 185 | auto exact_val = exact[k]; 186 | auto fmm_val = result[sample_map[k]]; 187 | 188 | for (unsigned i=0; i < 3; i++) { 189 | rdiff[i] += (fmm_val[i]-exact_val[i])*(fmm_val[i]-exact_val[i]); 190 | rnorm[i] += exact_val[i]*exact_val[i]; 191 | } 192 | } 193 | auto div = rdiff/rnorm; 194 | printf("Error (u) : %.4e, (v) : %.4e, (w) : %.4e\n",sqrt(div[0]),sqrt(div[1]),sqrt(div[2])); 195 | #endif 196 | #ifdef UNIT_KERNEL 197 | int wrong_results = 0; 198 | for (unsigned k = 0; k < exact.size(); ++k) { 199 | auto exact_val = exact[k]; 200 | auto fmm_val = result[sample_map[k]]; 201 | 202 | // printf("[%03d] exact: %lg, FMM: %lg\n", k, exact[k], result[k]); 203 | 204 | if ((exact_val - fmm_val) / exact_val > 1e-13) 205 | ++wrong_results; 206 | } 207 | printf("Wrong counts: %d\n", wrong_results); 208 | #endif 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /include/tree/BoundingBox.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /** @file BoundingBox.hpp 8 | * @brief Define the BoundingBox class for ND bounding boxes. */ 9 | 10 | /** @class BoundingBox 11 | * @brief Class representing ND bounding boxes. 12 | * 13 | * A BoundingBox is a ND volume. Its fundamental operations are contains(), 14 | * which tests whether a point is in the volume, and operator+=(), which 15 | * extends the volume as necessary to ensure that the volume contains a point. 16 | * 17 | * BoundingBoxes are implemented as boxes -- ND rectangular cuboids -- whose 18 | * sides are aligned with the principal axes. 19 | */ 20 | 21 | // TODO: Optimize for cubes? 22 | template 23 | class BoundingBox { 24 | public: 25 | typedef Point point_type; 26 | 27 | /** Construct an empty bounding box. */ 28 | BoundingBox() 29 | : empty_(true) { 30 | } 31 | /** Construct the minimal bounding box containing @a p. 32 | * @post contains(@a p) && min() == @a p && max() == @a p */ 33 | explicit BoundingBox(const point_type& p) 34 | : empty_(false), min_(p), max_(p) { 35 | } 36 | /** Construct the minimal bounding box containing a given sphere. 37 | * @param[in] center center of the sphere 38 | * @param[in] radius radius of the sphere */ 39 | BoundingBox(const point_type& center, double radius) 40 | : empty_(false) { 41 | radius = fabs(radius); 42 | min_ = center - radius; 43 | max_ = center + radius; 44 | } 45 | /** Construct the minimal bounding box containing @a p1 and @a p2. 46 | * @post contains(@a p1) && contains(@a p2) */ 47 | BoundingBox(const point_type& p1, const point_type& p2) 48 | : empty_(false), min_(p1), max_(p1) { 49 | *this |= p2; 50 | } 51 | /** Construct a bounding box containing the points in [first, last). */ 52 | template 53 | BoundingBox(IT first, IT last) 54 | : empty_(true) { 55 | insert(first, last); 56 | } 57 | 58 | /** Test if the bounding box is empty (contains no points). */ 59 | bool empty() const { 60 | return empty_; 61 | } 62 | /** Test if the bounding box is nonempty. 63 | * 64 | * This function lets you write code such as "if (b) { ... }" or 65 | * "if (box1 & box2) std::cout << "box1 and box2 intersect\n". */ 66 | operator const void*() const { 67 | return empty_ ? 0 : this; 68 | } 69 | 70 | /** Return the minimum corner of the bounding box. 71 | * @post empty() || contains(min()) 72 | * 73 | * The minimum corner has minimum x, y, and z coordinates of any corner. 74 | * An empty box has min() == point_type(). */ 75 | const point_type& min() const { 76 | return min_; 77 | } 78 | /** Return the maximum corner of the bounding box. 79 | * @post empty() || contains(max()) */ 80 | const point_type& max() const { 81 | return max_; 82 | } 83 | /** Return the dimensions of the bounding box. 84 | * @return max() - min() 85 | */ 86 | point_type dimensions() const { 87 | return max_ - min_; 88 | } 89 | /** Return the center of the bounding box. */ 90 | point_type center() const { 91 | return (min_ + max_) / 2; 92 | } 93 | 94 | /** Test if point @a p is in the bounding box. */ 95 | bool contains(const point_type& p) const { 96 | if (empty()) 97 | return false; 98 | for (unsigned i=0; i!=point_type::dimension; ++i) 99 | if (p[i] < min_[i] || p[i] > max_[i]) 100 | return false; 101 | return true; 102 | } 103 | /** Test if @a box is entirely within this bounding box. 104 | * 105 | * Returns false if @a box.empty(). */ 106 | bool contains(const BoundingBox& box) const { 107 | return !box.empty() && contains(box.min()) && contains(box.max()); 108 | } 109 | /** Test if @a box intersects this bounding box. */ 110 | bool intersects(const BoundingBox& box) const { 111 | if (empty() || box.empty()) 112 | return false; 113 | for (unsigned i=0; i!=point_type::dimension; ++i) 114 | if (box.min_[i] > max_[i] || box.max_[i] < min_[i]) 115 | return false; 116 | return true; 117 | } 118 | 119 | /** Extend the bounding box to contain @a p. 120 | * @post contains(@a p) is true 121 | * @post if old contains(@a x) was true, then new contains(@a x) is true */ 122 | BoundingBox& operator|=(const point_type& p) { 123 | if (empty_) { 124 | empty_ = false; 125 | min_ = max_ = p; 126 | } else { 127 | for (unsigned i = 0; i != point_type::dimension; ++i) { 128 | min_[i] = std::min(min_[i], p[i]); 129 | max_[i] = std::max(max_[i], p[i]); 130 | } 131 | } 132 | return *this; 133 | } 134 | /** Extend the bounding box to contain @a box. 135 | * @post contains(@a box) is true 136 | * @post if old contains(@a x) was true, then new contains(@a x) is true */ 137 | BoundingBox& operator|=(const BoundingBox& box) { 138 | if (!box.empty()) 139 | (*this |= box.min()) |= box.max(); 140 | return *this; 141 | } 142 | /** Extend the bounding box to contain the points in [first, last). */ 143 | template 144 | BoundingBox& insert(IT first, IT last) { 145 | while (first != last) { 146 | *this |= *first; 147 | ++first; 148 | } 149 | return *this; 150 | } 151 | 152 | /** Intersect this bounding box with @a box. */ 153 | BoundingBox& operator&=(const BoundingBox& box) { 154 | if (empty() || box.empty()) 155 | return clear(); 156 | for (unsigned i = 0; i != point_type::dimension; ++i) { 157 | if (min_[i] > box.max_[i] || max_[i] < box.min_[i]) 158 | return clear(); 159 | if (min_[i] < box.min_[i]) 160 | min_[i] = box.min_[i]; 161 | if (max_[i] > box.max_[i]) 162 | max_[i] = box.max_[i]; 163 | } 164 | return *this; 165 | } 166 | 167 | /** Clear the bounding box. 168 | * @post empty() */ 169 | BoundingBox& clear() { 170 | empty_ = true; 171 | min_ = max_ = point_type(); 172 | return *this; 173 | } 174 | 175 | /** Write a BoundingBox to an output stream. 176 | * 177 | * An empty BoundingBox is written as "[]". A nonempty BoundingBox is 178 | * written as "[min:max] (dim)". 179 | */ 180 | inline friend std::ostream& operator<<(std::ostream& s, 181 | const BoundingBox& box) { 182 | if (box.empty()) 183 | return (s << '[' << ']'); 184 | else 185 | return (s << '[' << box.min() << ':' << box.max() << "] (" 186 | << box.dimensions() << ')'); 187 | } 188 | 189 | private: 190 | bool empty_; 191 | point_type min_; 192 | point_type max_; 193 | }; 194 | 195 | 196 | 197 | /** Return a bounding box that contains @a box and @a p. */ 198 | template 199 | BoundingBox

operator|(BoundingBox

box, const P& p) { 200 | return box |= p; 201 | } 202 | /** Return the union of @a box1 and @a box2. */ 203 | template 204 | BoundingBox

operator|(BoundingBox

box1, const BoundingBox

& box2) { 205 | return box1 |= box2; 206 | } 207 | /** Return a bounding box that contains @a p1 and @a p2. */ 208 | template 209 | BoundingBox

operator|(const P& p1, const P& p2) { 210 | return BoundingBox

(p1, p2); 211 | } 212 | /** Return the intersection of @a box1 and @a box2. */ 213 | template 214 | BoundingBox

operator&(BoundingBox

box1, const BoundingBox

& box2) { 215 | return box1 &= box2; 216 | } 217 | -------------------------------------------------------------------------------- /include/executor/EvalInteractionLazy_bkp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "EvaluatorBase.hpp" 4 | 5 | #include "INITM.hpp" 6 | #include "INITL.hpp" 7 | #include "P2M.hpp" 8 | #include "M2M.hpp" 9 | #include "M2L.hpp" 10 | #include "M2P.hpp" 11 | #include "P2P.hpp" 12 | #include "L2P.hpp" 13 | #include "L2L.hpp" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | template 21 | class EvalInteractionLazy : public EvaluatorBase 22 | { 23 | //! Type of box 24 | typedef typename Context::box_type box_type; 25 | //! Pair of boxes 26 | typedef std::pair box_pair; 27 | //! List for P2P interactions TODO: could further compress these... 28 | std::vector P2P_list; 29 | //! List for Long-range (M2P / M2L) interactions 30 | std::vector LR_list; 31 | 32 | //! List to recursively compute multipoles 33 | std::vector multipole_cover; 34 | //! List to recursively initialize locals and perform downward pass 35 | std::vector local_cover; 36 | 37 | 38 | template 39 | void interact(Context& bc, 40 | const box_type& b1, const box_type& b2, 41 | Q& pairQ) { 42 | if (bc.accept_multipole(b1, b2)) 43 | // These boxes satisfy the multipole acceptance criteria 44 | LR_list.push_back(box_pair(b1,b2)); 45 | else 46 | pairQ.push_back(box_pair(b1,b2)); 47 | } 48 | 49 | public: 50 | 51 | /** Constructor 52 | * Precompute the interaction lists, P2P_list and LR_list 53 | */ 54 | EvalInteractionLazy(Context& bc) { 55 | // Queue based tree traversal for P2P, M2P, and/or M2L operations 56 | std::deque pairQ; 57 | pairQ.push_back(box_pair(bc.source_tree().root(), 58 | bc.target_tree().root())); 59 | 60 | while (!pairQ.empty()) { 61 | auto b1 = pairQ.front().first; 62 | auto b2 = pairQ.front().second; 63 | pairQ.pop_front(); 64 | 65 | char code = (b1.is_leaf() << 1) | (b2.is_leaf() << 0); 66 | switch (code) { 67 | case 3: { // Both boxes are leaves 68 | P2P_list.push_back(std::make_pair(b2,b1)); 69 | } break; 70 | 71 | case 2: { // Box 1 is a leaf 72 | // Split box 2 into children and interact 73 | auto c_end = b2.child_end(); 74 | for (auto cit = b2.child_begin(); cit != c_end; ++cit) 75 | interact(bc, b1, *cit, pairQ); 76 | } break; 77 | 78 | case 1: { // Box 2 is a leaf 79 | // Split box 1 into children and interact 80 | auto c_end = b1.child_end(); 81 | for (auto cit = b1.child_begin(); cit != c_end; ++cit) 82 | interact(bc, *cit, b2, pairQ); 83 | } break; 84 | 85 | case 0: { // Neither box is leaf 86 | // Split the larger of the two into children and interact 87 | if (b1.side_length() > b2.side_length()) { 88 | // Split box 1 into children and interact 89 | auto c_end = b1.child_end(); 90 | for (auto cit = b1.child_begin(); cit != c_end; ++cit) 91 | interact(bc, *cit, b2, pairQ); 92 | } else { 93 | // Split box 2 into children and interact 94 | auto c_end = b2.child_end(); 95 | for (auto cit = b2.child_begin(); cit != c_end; ++cit) 96 | interact(bc, b1, *cit, pairQ); 97 | } 98 | } break; 99 | } 100 | } 101 | 102 | // P2P and LR list are created. 103 | 104 | // Construct a covering set of multipole boxes 105 | 106 | } 107 | 108 | /** Execute this evaluator by applying the operators to the interaction lists 109 | */ 110 | void execute(Context& bc) const { 111 | // Evaluate queued P2P interactions 112 | eval_P2P_list(bc); 113 | // Evaluate queued long-range interactions 114 | eval_LR_list(bc); 115 | } 116 | 117 | private: 118 | 119 | /** Recursively resolve all needed multipole expansions */ 120 | void resolve_multipole(Context& bc, const box_type& b) const 121 | { 122 | // Early exit if already initialised 123 | if (initialised_M.count(b.index())) return; 124 | 125 | // setup memory for the expansion 126 | INITM::eval(bc.kernel(), bc, b); 127 | 128 | if (b.is_leaf()) { 129 | // eval P2M 130 | // P2M::eval(bc.kernel(), bc, b); 131 | P2M_list.push_back(b); 132 | } else { 133 | // recursively call resolve_multipole on children 134 | for (auto it=b.child_begin(); it!=b.child_end(); ++it) { 135 | // resolve the lower multipole 136 | resolve_multipole(bc, *it); 137 | // now invoke M2M to get child multipoles 138 | M2M_list.push_back(std::make_pair(*it,b)); 139 | } 140 | } 141 | // set this box as initialised 142 | initialised_M.insert(b.index()); 143 | } 144 | 145 | /** Downward pass of L2L & L2P 146 | * We can always assume that the called Local expansion has been initialised 147 | */ 148 | void propagate_local(Context& bc, const box_type& b) const 149 | { 150 | if (b.is_leaf()) { 151 | // call L2P 152 | if (!L2P_set.count(b.index())) { 153 | L2P_list.push_back(b); 154 | L2P_set.insert(b.index()); 155 | } 156 | } else { 157 | // loop over children and propagate 158 | for (auto cit=b.child_begin(); cit!=b.child_end(); ++cit) { 159 | if (!initialised_L.count(cit->index())) { 160 | // initialised the expansion if necessary 161 | INITL::eval(bc.kernel(), bc, *cit); 162 | initialised_L.insert(cit->index()); 163 | } 164 | 165 | // call L2L on parent -> child 166 | L2L_list.push_back(std::make_pair(b,*cit)); 167 | // now recurse down the tree 168 | propagate_local(bc, *cit); 169 | } 170 | } 171 | } 172 | 173 | /** Evaluate long-range interactions 174 | * Generate all necessary multipoles */ 175 | // void eval_LR_list(Context& bc) const 176 | void resolve_LR_interactions(Context& bc) const 177 | { 178 | for (auto it=LR_list.begin(); it!=LR_list.end(); ++it) { 179 | // resolve all needed multipole expansions from lower levels of the tree 180 | resolve_multipole(bc, it->first); 181 | 182 | // If needed, initialise Local expansion for target box 183 | if (IS_FMM) { 184 | if (!initialised_L.count(it->second.index())) { 185 | // initialise Local expansion at target box if necessary 186 | INITL::eval(bc.kernel(), bc, it->second); 187 | initialised_L.insert(it->second.index()); 188 | } 189 | } 190 | } 191 | } 192 | 193 | void eval_L_list(Context& bc) const 194 | { 195 | for (auto it=L_list.begin(); it!=L_list.end(); ++it) { 196 | // propagate this local expansion down the tree 197 | propagate_local(bc, *it); 198 | } 199 | } 200 | 201 | void eval_P2P_list(Context& bc) const { 202 | for (box_pair b2b : P2P_list) 203 | P2P::eval(bc.kernel(), bc, b2b.first, b2b.second, P2P::ONE_SIDED()); 204 | } 205 | 206 | void init_multipole(const box_type& b) const { 207 | if (M_init[b.index()]) return; 208 | 209 | // Initialize this multipole 210 | INITM::eval(bc.kernel(), bc, b); 211 | 212 | if (b.is_leaf()) { 213 | P2M::eval(bc.kernel(), bc, b); 214 | } else { 215 | // Initialize and accumulate the children 216 | auto c_end = b.child_end(); 217 | for (auto ci = b.child_begin(); ci != c_end; ++ci) { 218 | init_multipole(*ci); 219 | M2M::eval(bc.kernel(), bc, *ci, b); 220 | } 221 | } 222 | 223 | M_init[b.index()] = true; 224 | } 225 | 226 | void eval_LR_list(Context& bc) const { 227 | for (auto b2b = LR_list) { 228 | init_multipole(b2b.first); 229 | if (IS_FMM) 230 | M2L::eval(bc.kernel(), bc, b2b.first, b2b.second); 231 | else 232 | M2P::eval(bc.kernel(), bc, b2b.first, b2b.second); 233 | } 234 | } 235 | }; 236 | 237 | 238 | template 239 | EvaluatorBase* make_lazy_eval(Context& c, Options& opts) { 240 | if (opts.evaluator == FMMOptions::FMM) 241 | return new EvalInteractionLazy(c); 242 | if (opts.evaluator == FMMOptions::TREECODE) 243 | return new EvalInteractionLazy(c); 244 | return nullptr; 245 | } 246 | -------------------------------------------------------------------------------- /include/KernelTraits.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | // Automatically derive !=, <=, >, and >= from a class's == and < 7 | #include 8 | using namespace std::rel_ops; 9 | 10 | #define SFINAE_TEMPLATE(NAME, OP) \ 11 | template \ 12 | struct NAME { \ 13 | template struct SFINAE {}; \ 14 | template static std::true_type sfinae(SFINAE*); \ 15 | template static std::false_type sfinae(...); \ 16 | static constexpr bool value = decltype(sfinae(0))::value; \ 17 | } 18 | 19 | 20 | template 21 | struct KernelTraits { 22 | typedef KernelTraits self_type; 23 | 24 | typedef Kernel kernel_type; 25 | 26 | typedef typename kernel_type::source_type source_type; 27 | typedef typename kernel_type::target_type target_type; 28 | typedef typename kernel_type::charge_type charge_type; 29 | typedef typename kernel_type::kernel_value_type kernel_value_type; 30 | typedef typename kernel_type::result_type result_type; 31 | 32 | // A dummy iterator adaptor to check for vectorized methods 33 | template 34 | struct dummy_iterator { 35 | typedef T value_type; 36 | typedef T& reference; 37 | typedef T* pointer; 38 | typedef std::ptrdiff_t difference_type; 39 | typedef std::forward_iterator_tag iterator_category; 40 | 41 | dummy_iterator() {} 42 | dummy_iterator(const dummy_iterator&) {} 43 | dummy_iterator& operator=(const dummy_iterator&) { return *this; } 44 | 45 | bool operator==(const dummy_iterator&) const { return true; } 46 | dummy_iterator& operator++() { return *this; } 47 | value_type& operator*() { return value; } 48 | const value_type& operator*() const { return value; } 49 | pointer operator->() { return nullptr; } 50 | value_type value; 51 | }; 52 | 53 | typedef dummy_iterator source_iterator; 54 | typedef dummy_iterator charge_iterator; 55 | typedef dummy_iterator target_iterator; 56 | typedef dummy_iterator result_iterator; 57 | 58 | // Kernel Evaluations and P2P 59 | SFINAE_TEMPLATE(HasEvalOp,operator()); 60 | static constexpr bool has_eval_op = 61 | HasEvalOp::value; 63 | SFINAE_TEMPLATE(HasTranspose,transpose); 64 | static constexpr bool has_transpose = 65 | HasTranspose::value; 67 | SFINAE_TEMPLATE(HasP2P,P2P); 68 | static constexpr bool has_vector_P2P_symm = 69 | HasP2P::value; 73 | static constexpr bool has_vector_P2P_asymm = 74 | HasP2P::value; 77 | 78 | static constexpr bool is_valid_kernel = has_eval_op || has_vector_P2P_asymm; 79 | 80 | friend std::ostream& operator<<(std::ostream& s, const self_type& traits) { 81 | s << "has_eval_op: " << traits.has_eval_op << std::endl; 82 | s << "has_transpose: " << traits.has_transpose << std::endl; 83 | s << "has_vector_P2P_symm: " << traits.has_vector_P2P_symm << std::endl; 84 | s << "has_vector_P2P_asymm: " << traits.has_vector_P2P_asymm << std::endl; 85 | return s; 86 | } 87 | }; 88 | 89 | 90 | 91 | 92 | template 93 | struct ExpansionTraits : public KernelTraits 94 | { 95 | typedef ExpansionTraits self_type; 96 | typedef KernelTraits super_type; 97 | 98 | typedef typename super_type::source_type source_type; 99 | typedef typename super_type::target_type target_type; 100 | typedef typename super_type::charge_type charge_type; 101 | typedef typename super_type::kernel_value_type kernel_value_type; 102 | typedef typename super_type::result_type result_type; 103 | 104 | typedef typename super_type::source_iterator source_iterator; 105 | typedef typename super_type::charge_iterator charge_iterator; 106 | typedef typename super_type::target_iterator target_iterator; 107 | typedef typename super_type::result_iterator result_iterator; 108 | 109 | 110 | static constexpr unsigned dimension = Kernel::dimension; 111 | typedef typename Kernel::point_type point_type; 112 | 113 | // TODO: Check that dimension and point_type make sense 114 | 115 | typedef typename Kernel::multipole_type multipole_type; 116 | typedef typename Kernel::local_type local_type; 117 | 118 | // Initializers 119 | SFINAE_TEMPLATE(HasInitMultipole,init_multipole); 120 | static constexpr bool has_init_multipole = 121 | HasInitMultipole::value; 123 | SFINAE_TEMPLATE(HasInitLocal,init_local); 124 | static constexpr bool has_init_local = 125 | HasInitLocal::value; 127 | 128 | // Kernel Evaluations and P2P 129 | static constexpr bool has_eval_op = super_type::has_eval_op; 130 | static constexpr bool has_transpose = super_type::has_transpose; 131 | static constexpr bool has_vector_P2P_symm = super_type::has_vector_P2P_symm; 132 | static constexpr bool has_vector_P2P_asymm = super_type::has_vector_P2P_asymm; 133 | 134 | // P2M 135 | SFINAE_TEMPLATE(HasP2M,P2M); 136 | static constexpr bool has_P2M = 137 | HasP2M::value; 140 | static constexpr bool has_vector_P2M = 141 | HasP2M::value; 144 | 145 | // M2M 146 | SFINAE_TEMPLATE(HasM2M,M2M); 147 | static constexpr bool has_M2M = 148 | HasM2M::value; 150 | 151 | // M2P 152 | SFINAE_TEMPLATE(HasM2P,M2P); 153 | static constexpr bool has_M2P = 154 | HasM2P::value; 157 | static constexpr bool has_vector_M2P = 158 | HasM2P::value; 161 | 162 | // M2L 163 | SFINAE_TEMPLATE(HasM2L,M2L); 164 | static constexpr bool has_M2L = 165 | HasM2L::value; 167 | 168 | // L2L 169 | SFINAE_TEMPLATE(HasL2L,L2L); 170 | static constexpr bool has_L2L = 171 | HasL2L::value; 173 | 174 | // L2P 175 | SFINAE_TEMPLATE(HasL2P,L2P); 176 | static constexpr bool has_L2P = 177 | HasL2P::value; 180 | static constexpr bool has_vector_L2P = 181 | HasL2P::value; 184 | 185 | static constexpr bool is_valid_treecode = 186 | (has_eval_op || has_vector_P2P_asymm) && 187 | (has_P2M || has_vector_P2M) && 188 | (has_M2M) && 189 | (has_M2P || has_vector_M2P); 190 | 191 | static constexpr bool is_valid_fmm = (has_eval_op || has_vector_P2P_asymm) && 192 | (has_P2M || has_vector_P2M) && 193 | (has_M2M) && 194 | (has_M2L) && 195 | (has_L2L) && 196 | (has_L2P || has_vector_L2P); 197 | 198 | friend std::ostream& operator<<(std::ostream& s, const self_type& traits) { 199 | s << static_cast(traits); 200 | s << "has_init_multipole: " << traits.has_init_multipole << std::endl; 201 | s << "has_init_local: " << traits.has_init_local << std::endl; 202 | s << "has_vector_P2M: " << traits.has_vector_P2M << std::endl; 203 | s << "has_P2M: " << traits.has_P2M << std::endl; 204 | s << "has_vector_P2M: " << traits.has_vector_P2M << std::endl; 205 | s << "has_M2M: " << traits.has_M2M << std::endl; 206 | s << "has_M2P: " << traits.has_M2P << std::endl; 207 | s << "has_vector_M2P: " << traits.has_vector_M2P << std::endl; 208 | s << "has_M2L: " << traits.has_M2L << std::endl; 209 | s << "has_L2L: " << traits.has_L2L << std::endl; 210 | s << "has_L2P: " << traits.has_L2P << std::endl; 211 | s << "has_vector_L2P: " << traits.has_vector_L2P << std::endl; 212 | return s; 213 | } 214 | }; 215 | 216 | #undef SFINAE_TEMPLATE 217 | -------------------------------------------------------------------------------- /include/executor/ExecutorSingleTree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ExecutorBase.hpp" 4 | #include "EvaluatorBase.hpp" 5 | 6 | //#include "tree/TreeContext.hpp" 7 | #include "executor/INITM.hpp" 8 | #include "executor/INITL.hpp" 9 | 10 | #include "INITM.hpp" 11 | #include "INITL.hpp" 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | using boost::transform_iterator; 18 | using boost::make_transform_iterator; 19 | 20 | /** @class Executor 21 | * @brief A very general Executor class. This provides a context to any tree 22 | * that provides the following interface: 23 | * Tree 24 | * Tree(Iter, Iter, Options) // Tree constructor 25 | * int boxes() const // The number of boxes 26 | * Tree::box_type 27 | * int index() const // Each box has a unique index 28 | * point_type center() const // Each box has a center point 29 | * body_iterator body_begin() const 30 | * body_iterator body_end() const // Iterator pair to the box's bodies 31 | * Tree::body_iterator 32 | * Tree::body_type 33 | * int index() const // Each body has a unique index 34 | * int number() const // Original index of this body TODO TEMP 35 | * This class assumes nothing else about the tree. 36 | */ 37 | template 38 | class ExecutorSingleTree : public ExecutorBase 39 | { 40 | public: 41 | //! This type 42 | typedef ExecutorSingleTree self_type; 43 | 44 | //! Tree type 45 | typedef Tree tree_type; 46 | //! Source tree type 47 | typedef tree_type source_tree_type; 48 | //! Target tree type 49 | typedef tree_type target_tree_type; 50 | //! Tree box type 51 | typedef typename tree_type::box_type box_type; 52 | //! Tree box iterator 53 | typedef typename tree_type::box_iterator box_iterator; 54 | //! Tree body type 55 | typedef typename tree_type::body_type body_type; 56 | //! Tree body iterator 57 | typedef typename tree_type::body_iterator body_iterator; 58 | 59 | //! Kernel type 60 | typedef Kernel kernel_type; 61 | //! Kernel value type 62 | typedef typename Kernel::kernel_value_type kernel_value_type; 63 | //! Kernel charge type 64 | typedef typename kernel_type::multipole_type multipole_type; 65 | //! Kernel result type 66 | typedef typename kernel_type::local_type local_type; 67 | //! Kernel point type 68 | typedef typename kernel_type::point_type point_type; 69 | //! Kernel source type 70 | typedef typename kernel_type::source_type source_type; 71 | //! Kernel target type 72 | typedef typename kernel_type::target_type target_type; 73 | //! Kernel charge type 74 | typedef typename kernel_type::charge_type charge_type; 75 | //! Kernel result type 76 | typedef typename kernel_type::result_type result_type; 77 | 78 | protected: 79 | // Transform a body to a value 80 | template 81 | struct BodyTransformer { 82 | typedef typename Indexable::reference result_type; 83 | typedef body_type argument_type; 84 | BodyTransformer(const Indexable& value) : value_(value) {} 85 | result_type operator()(const body_type& body) const { 86 | return value_[body.number()]; // TODO: TEMP to avoid permutation 87 | } 88 | private: 89 | Indexable value_; 90 | }; 91 | 92 | template 93 | BodyTransformer body_transformer(Indexable v) const { 94 | return BodyTransformer(v); 95 | } 96 | 97 | template 98 | using body_transform = transform_iterator, 99 | body_iterator>; 100 | 101 | //! Reference to the Kernel 102 | const kernel_type& K_; 103 | 104 | //! The tree of sources 105 | tree_type source_tree_; 106 | //! Multipole acceptance 107 | std::function acceptMultipole; 108 | 109 | //! Multipole expansions corresponding to Box indices in Tree 110 | typedef std::vector multipole_container; 111 | multipole_container M_; 112 | //! Local expansions corresponding to Box indices in Tree 113 | typedef std::vector local_container; 114 | local_container L_; 115 | //! The sources associated with bodies in the source_tree (aliased as targets) 116 | typedef std::vector source_container; 117 | // typedef typename source_container::const_iterator source_iterator; 118 | typedef typename source_container::iterator source_iterator; 119 | source_container sources; 120 | source_iterator s_; 121 | 122 | //! Iterator to the start of the charge vector 123 | typedef std::vector charge_container; 124 | typedef typename charge_container::const_iterator charge_iterator; 125 | charge_iterator c_; 126 | //! Iterator to the start of the result vector 127 | typedef std::vector result_container; 128 | typedef typename result_container::iterator result_iterator; 129 | result_iterator r_; 130 | 131 | //! Evaluator algorithms to apply 132 | EvaluatorCollection evals_; 133 | 134 | public: 135 | //! Constructor 136 | template 137 | ExecutorSingleTree(const kernel_type& K, 138 | SourceIter first, SourceIter last, 139 | Options& opts) 140 | : K_(K), 141 | source_tree_(first, last, opts), 142 | acceptMultipole(opts.MAC()), 143 | M_(source_tree_.boxes()), 144 | L_((opts.evaluator == FMMOptions::TREECODE ? 0 : source_tree_.boxes())), 145 | sources(first, last) { 146 | s_ = sources.begin(); 147 | } 148 | 149 | void insert(EvaluatorBase* eval) { 150 | evals_.insert(eval); 151 | } 152 | 153 | virtual void execute(const std::vector& charges, 154 | std::vector& results) { 155 | s_ = sources.begin(); 156 | c_ = charges.begin(); 157 | r_ = results.begin(); 158 | 159 | evals_.execute(*this); 160 | } 161 | 162 | bool accept_multipole(const box_type& source, const box_type& target) const { 163 | return acceptMultipole(source, target); 164 | } 165 | 166 | const kernel_type& kernel() const { 167 | return K_; 168 | } 169 | 170 | tree_type& source_tree() { 171 | return source_tree_; 172 | } 173 | tree_type& target_tree() { 174 | return source_tree_; 175 | } 176 | 177 | // Accessors to make this Executor into a BoxContext 178 | inline multipole_type& multipole_expansion(const box_type& box) { 179 | return M_[box.index()]; 180 | } 181 | inline const multipole_type& multipole_expansion(const box_type& box) const { 182 | return M_[box.index()]; 183 | } 184 | inline local_type& local_expansion(const box_type& box) { 185 | return L_[box.index()]; 186 | } 187 | inline const local_type& local_expansion(const box_type& box) const { 188 | return L_[box.index()]; 189 | } 190 | 191 | inline point_type center(const box_type& b) const { 192 | return b.center(); 193 | } 194 | 195 | typedef body_transform body_source_iterator; 196 | inline body_source_iterator source_begin(const box_type& b) const { 197 | return make_transform_iterator(b.body_begin(), body_transformer(s_)); 198 | } 199 | inline body_source_iterator source_end(const box_type& b) const { 200 | return make_transform_iterator(b.body_end(), body_transformer(s_)); 201 | } 202 | 203 | typedef body_transform body_charge_iterator; 204 | inline body_charge_iterator charge_begin(const box_type& b) const { 205 | return make_transform_iterator(b.body_begin(), body_transformer(c_)); 206 | } 207 | inline body_charge_iterator charge_end(const box_type& b) const { 208 | return make_transform_iterator(b.body_end(), body_transformer(c_)); 209 | } 210 | 211 | // Single tree targets are the same as the sources 212 | inline body_source_iterator target_begin(const box_type& b) const { 213 | return source_begin(b); 214 | } 215 | inline body_source_iterator target_end(const box_type& b) const { 216 | return source_end(b); 217 | } 218 | 219 | typedef body_transform body_result_iterator; 220 | inline body_result_iterator result_begin(const box_type& b) { 221 | return make_transform_iterator(b.body_begin(), body_transformer(r_)); 222 | } 223 | inline body_result_iterator result_end(const box_type& b) { 224 | return make_transform_iterator(b.body_end(), body_transformer(r_)); 225 | } 226 | }; 227 | 228 | 229 | template 230 | ExecutorSingleTree* make_executor(const Kernel& K, 231 | SourceIter first, SourceIter last, 232 | Options& opts) { 233 | return new ExecutorSingleTree(K, first, last, opts); 234 | } 235 | 236 | // TODO 237 | /* 238 | // This assumes the the body indices are consecutive within a box 239 | // The assumption is true for Octree, 240 | // but should be left for an "optimized" Evaluator that 241 | // explicitely makes this assumption 242 | inline charge_iterator charge_begin(const box_type& b) const { 243 | return charges_begin + b.body_begin()->index(); 244 | } 245 | inline charge_iterator charge_end(const box_type& b) const { 246 | return charges_begin + b.body_end()->index(); 247 | } 248 | */ 249 | /* 250 | // This assumes the the body indices are consecutive within a box 251 | // The assumption is true for Octree, 252 | // but should be left for an "optimized" Evaluator that 253 | // explicitely makes this assumption 254 | inline result_iterator result_begin(const box_type& b) { 255 | return results_begin + b.body_begin()->index(); 256 | } 257 | inline result_iterator result_end(const box_type& b) { 258 | return results_begin + b.body_end()->index(); 259 | } 260 | */ 261 | -------------------------------------------------------------------------------- /examples/BEM/Triangulation.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * holder for triangulation routines 5 | */ 6 | 7 | #include 8 | #include "Vec.hpp" 9 | #include 10 | #include 11 | 12 | namespace Triangulation { 13 | 14 | struct triangle 15 | { 16 | typedef struct triangle triangle_type; 17 | typedef Vec<3,double> vertex_type; 18 | vertex_type v0_; 19 | vertex_type v1_; 20 | vertex_type v2_; 21 | 22 | triangle() {}; 23 | triangle(vertex_type v0, vertex_type v1, vertex_type v2) 24 | : v0_(v0), v1_(v1), v2_(v2) {} 25 | // copy constructor 26 | triangle(const triangle& t) 27 | : v0_(t.v0_), v1_(t.v1_), v2_(t.v2_) {} 28 | // assignment 29 | triangle& operator=(const triangle& t) { 30 | v0_ = t.v0_; 31 | v1_ = t.v1_; 32 | v2_ = t.v2_; 33 | return *this; 34 | } 35 | /** subdivides into 4 triangles */ 36 | std::vector split() { 37 | std::vector r(4); 38 | 39 | vertex_type a = (v0_+v2_)*0.5; 40 | vertex_type b = (v0_+v1_)*0.5; 41 | vertex_type c = (v1_+v2_)*0.5; 42 | 43 | a /= norm(a); 44 | b /= norm(b); 45 | c /= norm(c); 46 | 47 | // assemble the 4 new triangles 48 | r[0] = triangle(v0_,b,a); 49 | r[1] = triangle(b,v1_,c); 50 | r[2] = triangle(a,b,c); 51 | r[3] = triangle(a,c,v2_); 52 | 53 | return r; 54 | } 55 | }; 56 | 57 | std::vector octahedron_vertices = {1.,0.,0.,-1.,0.,0.,0.,1.,0.,0.,-1.,0.,0.,0.,1.,0.,0.,-1.}; 58 | std::vector octahedron_triangles = {0,4,2,2,4,1,1,4,3,3,4,0,0,2,5,2,1,5,1,3,5,3,0,5}; 59 | 60 | template 61 | void init_octahedron_vertices(std::vector& vertices) 62 | { 63 | for (unsigned i=0; i<6; i++) { 64 | vertices[i] = vertex_type(octahedron_vertices[i*3 + 0], 65 | octahedron_vertices[i*3 + 1], 66 | octahedron_vertices[i*3 + 2]); 67 | } 68 | } 69 | 70 | void init_octahedron_triangles(std::vector& triangles) 71 | { 72 | // get the initial vertices 73 | std::vector initial_vertices(6); 74 | init_octahedron_vertices(initial_vertices); 75 | 76 | // construct the triangles 77 | for (unsigned i=0; i<8; i++) { 78 | triangles[i] = triangle(initial_vertices[octahedron_triangles[i*3 + 0]], 79 | initial_vertices[octahedron_triangles[i*3 + 1]], 80 | initial_vertices[octahedron_triangles[i*3 + 2]]); 81 | } 82 | } 83 | 84 | void divide_all(std::vector& triangles) 85 | { 86 | // take a copy of the triangles array & size 87 | auto triangles_copy = triangles; 88 | auto num_initial_triangles = triangles_copy.size(); 89 | 90 | // resize output 91 | triangles.resize(num_initial_triangles*4); 92 | 93 | // loop over all triangles, splitting 94 | unsigned pos = 0; 95 | for (auto it=triangles_copy.begin(); it!=triangles_copy.end(); ++it, pos+=4) { 96 | auto split = it->split(); 97 | 98 | for (unsigned i=0; i<4; i++) { 99 | triangles[pos + i] = split[i]; 100 | } 101 | } 102 | } 103 | 104 | template 105 | void UnitSphere(std::vector& panels, unsigned recursions = 2) 106 | { 107 | std::vector triangles(8); 108 | init_octahedron_triangles(triangles); 109 | 110 | for (unsigned i=0; iv0_,it->v1_,it->v2_); 121 | } 122 | 123 | // save the triangulation to test.vert, test.face 124 | std::ofstream face("test.face"); 125 | std::ofstream vert("test.vert"); 126 | 127 | int vnum = 1; 128 | for (auto it=triangles.begin(); it!=triangles.end();++it) { 129 | vert << it->v0_[0] << " " << it->v0_[1] << " " << it->v0_[2] << std::endl; 130 | vert << it->v1_[0] << " " << it->v1_[1] << " " << it->v1_[2] << std::endl; 131 | vert << it->v2_[0] << " " << it->v2_[1] << " " << it->v2_[2] << std::endl; 132 | face << vnum << ' ' << vnum+1 << ' ' << vnum+2 << std::endl; 133 | vnum+=3; 134 | } 135 | } 136 | 137 | template 138 | int sgn(T val) 139 | { 140 | return (T(0) < val) - (val < T(0)); 141 | } 142 | 143 | Mat3 RotationMatrix(double alpha, double beta, double gamma) 144 | { 145 | double ca = cos(alpha), cb = cos(beta), cg = cos(gamma); 146 | double sa = sin(alpha), sb = sin(beta), sg = sin(gamma); 147 | 148 | Mat3 M; 149 | 150 | M(0,0) = cb*cg; 151 | M(0,1) = -cb*sg; 152 | M(0,2) = sb; 153 | 154 | M(1,0) = ca*sg + cg*sa*sb; 155 | M(1,1) = ca*cg - sa*sb*sg; 156 | M(1,2) = -cb*sa; 157 | 158 | M(2,0) = sa*sg - ca*cg*sb; 159 | M(2,1) = cg*sa + ca*sb*sg; 160 | M(2,2) = ca*cb; 161 | 162 | return M; 163 | } 164 | 165 | template 166 | void shift(VertexType& v, double xs, double ys, double zs) 167 | { 168 | v[0] += xs; 169 | v[1] += ys; 170 | v[2] += zs; 171 | } 172 | 173 | // rotate a point using a rotation matrix 174 | template 175 | void rotate(VertexType& v, MatrixType& m) 176 | { 177 | VertexType r(0.); 178 | 179 | r = m*v; 180 | v = r; 181 | } 182 | 183 | template 184 | void ConvertRedBloodCellTriangle(VertexType& v) 185 | { 186 | // constants: 187 | double r = 3.91, C0 = 0.81, C2 = 7.83, C4 = -4.39; 188 | 189 | double x = v[0]*r, y = v[1]*r; 190 | double rho = std::sqrt(x*x + y*y); 191 | double ratio = rho / r; 192 | 193 | // new z co-ordinate 194 | double z = std::sqrt(1-ratio*ratio + 1e-12)*(C0 + C2*ratio*ratio + C4*ratio*ratio*ratio*ratio)*0.5*sgn(v[2]); 195 | 196 | if (isnan(z)) { 197 | printf("[E]: z is NaN\n"); 198 | printf("x: %g, y: %g, rho: %g, ratio: %g\n",x,y,rho,ratio); 199 | double temp = 1-ratio*ratio; 200 | printf("temp: %g, z: %g\n",temp,z); 201 | std::exit(0); 202 | } 203 | v[0] = x; v[1] = y; v[2] = z; 204 | 205 | } 206 | 207 | /** 208 | * Convert a unit sphere into an ethrocyte (RBC) 209 | */ 210 | template 211 | void RedBloodCell(std::vector& panels, unsigned recursions = 2, Mat3 rotation = Mat3(0.), double *s = nullptr) 212 | { 213 | std::vector triangles(8); 214 | init_octahedron_triangles(triangles); 215 | 216 | for (unsigned i=0; iv0_); 224 | rotate(it->v0_, rotation); 225 | ConvertRedBloodCellTriangle(it->v1_); 226 | rotate(it->v1_, rotation); 227 | ConvertRedBloodCellTriangle(it->v2_); 228 | rotate(it->v2_, rotation); 229 | shift(it->v0_, s[0], s[1], s[2]); 230 | shift(it->v1_, s[0], s[1], s[2]); 231 | shift(it->v2_, s[0], s[1], s[2]); 232 | } 233 | 234 | printf("RBC: initialised %d triangles\n",(int)triangles.size()); 235 | // now triangles contains the full triangulation -- convert to panel 236 | panels.resize(triangles.size()); 237 | 238 | auto pit = panels.begin(); 239 | for (auto it=triangles.begin(); it!=triangles.end(); ++it, ++pit) { 240 | *pit = PanelType(it->v0_,it->v1_,it->v2_); 241 | } 242 | 243 | // save the triangulation to test.vert, test.face 244 | std::ofstream face("rbc.face"); 245 | std::ofstream vert("rbc.vert"); 246 | 247 | int vnum = 1; 248 | for (auto it=triangles.begin(); it!=triangles.end();++it) { 249 | vert << it->v0_[0] << " " << it->v0_[1] << " " << it->v0_[2] << std::endl; 250 | vert << it->v1_[0] << " " << it->v1_[1] << " " << it->v1_[2] << std::endl; 251 | vert << it->v2_[0] << " " << it->v2_[1] << " " << it->v2_[2] << std::endl; 252 | face << vnum << ' ' << vnum+1 << ' ' << vnum+2 << std::endl; 253 | vnum+=3; 254 | } 255 | } 256 | 257 | /** 258 | * Create multiple red blood cells, with random orientations 259 | */ 260 | template 261 | void MultipleRedBloodCell(std::vector& panels, unsigned recursions = 2, unsigned cells = 1) 262 | { 263 | unsigned panels_per_cell = 8 * (unsigned)pow(4,recursions-1); 264 | unsigned total_panels = panels_per_cell * cells; 265 | panels.resize(total_panels); 266 | 267 | // temp vector to hold a single cell 268 | std::vector temp; 269 | 270 | unsigned offset = 0; 271 | double sx = 0., sy = 0., sz = 0.; 272 | for (unsigned i=0; i0) { 277 | sy += 2*3.91 + 3.91*::drand48(); 278 | sx = i*::drand48() + 2.83*::drand48(); 279 | sz = ((i%2==0)? 1 : -1)*10*::drand48(); 280 | } 281 | 282 | // double sy = 3.91*::drand48() + 2*i*3.91, sx = i*::drand48() + ::drand48(), sz = ((i%2==0)? 1 : -1)*4*::drand48(); 283 | double s[] = {sx, sy, sz}; 284 | // generate the RBC into temp vector 285 | RedBloodCell(temp, recursions, M, s); 286 | 287 | // shift all panels 288 | printf("shifting: %g, %g, %g\n",sx,sy,sz); 289 | /* 290 | for (auto it=temp.begin(); it!=temp.end(); ++it) { 291 | shift(it->vertices[0], sx, sy, sz); 292 | shift(it->vertices[1], sx, sy, sz); 293 | shift(it->vertices[2], sx, sy, sz); 294 | } 295 | */ 296 | // copy temp vector into main panels vector 297 | for (unsigned j=0; jvertices; 313 | 314 | // output vertices 315 | vert << v[0][0] << " " << v[0][1] << " " << v[0][2] << std::endl; 316 | vert << v[1][0] << " " << v[1][1] << " " << v[1][2] << std::endl; 317 | vert << v[2][0] << " " << v[2][1] << " " << v[2][2] << std::endl; 318 | face << vnum << " " << vnum+1 << " " << vnum+2 << std::endl; 319 | vnum += 3; 320 | } 321 | } 322 | 323 | }; // end namespace Triangulation 324 | -------------------------------------------------------------------------------- /include/executor/ExecutorDualTree.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "ExecutorBase.hpp" 4 | #include "EvaluatorBase.hpp" 5 | 6 | //#include "tree/TreeContext.hpp" 7 | #include "executor/INITM.hpp" 8 | #include "executor/INITL.hpp" 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | using boost::transform_iterator; 15 | using boost::make_transform_iterator; 16 | 17 | /** @class Executor 18 | * @brief A very general Executor class. This provides a context to any tree 19 | * that provides the following interface: 20 | * Tree 21 | * Tree(Iter, Iter, Options) // Tree constructor 22 | * int boxes() const // The number of boxes 23 | * Tree::box_type 24 | * int index() const // Each box has a unique index 25 | * point_type center() const // Each box has a center point 26 | * body_iterator body_begin() const 27 | * body_iterator body_end() const // Iterator pair to the box's bodies 28 | * Tree::body_iterator 29 | * Tree::body_type 30 | * int index() const // Each body has a unique index 31 | * int number() const // Original index of this body TODO TEMP 32 | * This class assumes nothing else about the tree. 33 | */ 34 | template 35 | class ExecutorDualTree : public ExecutorBase 36 | { 37 | public: 38 | //! This type 39 | typedef ExecutorDualTree self_type; 40 | 41 | //! Tree type 42 | typedef Tree tree_type; 43 | //! Source tree type 44 | typedef tree_type source_tree_type; 45 | //! Target tree type 46 | typedef tree_type target_tree_type; 47 | //! Tree box type 48 | typedef typename tree_type::box_type box_type; 49 | //! Tree box iterator 50 | typedef typename tree_type::box_iterator box_iterator; 51 | //! Tree body type 52 | typedef typename tree_type::body_type body_type; 53 | //! Tree body iterator 54 | typedef typename tree_type::body_iterator body_iterator; 55 | 56 | //! Kernel type 57 | typedef Kernel kernel_type; 58 | //! Kernel value type 59 | typedef typename Kernel::kernel_value_type kernel_value_type; 60 | //! Kernel charge type 61 | typedef typename kernel_type::multipole_type multipole_type; 62 | //! Kernel result type 63 | typedef typename kernel_type::local_type local_type; 64 | //! Kernel point type 65 | typedef typename kernel_type::point_type point_type; 66 | //! Kernel source type 67 | typedef typename kernel_type::source_type source_type; 68 | //! Kernel target type 69 | typedef typename kernel_type::target_type target_type; 70 | //! Kernel charge type 71 | typedef typename kernel_type::charge_type charge_type; 72 | //! Kernel result type 73 | typedef typename kernel_type::result_type result_type; 74 | 75 | protected: 76 | // Transform a body to a value 77 | template 78 | struct BodyTransformer { 79 | typedef typename Indexable::reference result_type; 80 | typedef body_type argument_type; 81 | BodyTransformer(const Indexable& value) : value_(value) {} 82 | result_type operator()(const body_type& body) const { 83 | return value_[body.number()]; // TODO: TEMP to avoid permutation 84 | } 85 | private: 86 | Indexable value_; 87 | }; 88 | 89 | template 90 | BodyTransformer body_transformer(Indexable v) const { 91 | return BodyTransformer(v); 92 | } 93 | 94 | template 95 | using body_transform = transform_iterator, 96 | body_iterator>; 97 | 98 | //! Reference to the Kernel 99 | const kernel_type& K_; 100 | 101 | //! The tree of sources 102 | tree_type source_tree_; 103 | //! The tree of targets 104 | tree_type target_tree_; 105 | //! Multipole acceptance 106 | std::function acceptMultipole; 107 | 108 | //! Multipole expansions corresponding to Box indices in Tree 109 | typedef std::vector multipole_container; 110 | multipole_container M_; 111 | //! Local expansions corresponding to Box indices in Tree 112 | typedef std::vector local_container; 113 | local_container L_; 114 | 115 | //! The sources associated with bodies in the source_tree 116 | typedef const std::vector source_container; 117 | typedef typename source_container::const_iterator source_iterator; 118 | source_container sources; 119 | source_iterator s_; 120 | //! The targets associated with bodies in the target_tree 121 | typedef const std::vector target_container; 122 | typedef typename target_container::const_iterator target_iterator; 123 | target_container targets; 124 | target_iterator t_; 125 | 126 | //! Iterator to the start of the charge vector 127 | typedef std::vector charge_container; 128 | typedef typename charge_container::const_iterator charge_iterator; 129 | charge_iterator c_; 130 | //! Iterator to the start of the result vector 131 | typedef std::vector result_container; 132 | typedef typename result_container::iterator result_iterator; 133 | result_iterator r_; 134 | 135 | //! Evaluator algorithms to apply 136 | EvaluatorCollection evals_; 137 | 138 | public: 139 | //! Constructor 140 | template 141 | ExecutorDualTree(const kernel_type& K, 142 | SourceIter sfirst, SourceIter slast, 143 | TargetIter tfirst, TargetIter tlast, 144 | Options& opts) 145 | : K_(K), 146 | source_tree_(sfirst, slast, opts), 147 | target_tree_(tfirst, tlast, opts), 148 | acceptMultipole(opts.MAC()), 149 | M_(source_tree_.boxes()), 150 | L_((opts.evaluator == FMMOptions::TREECODE ? 0 : target_tree_.boxes())), 151 | sources(sfirst, slast), 152 | targets(tfirst, tlast) { 153 | s_ = sources.begin(); 154 | t_ = targets.begin(); 155 | } 156 | 157 | void insert(EvaluatorBase* eval) { 158 | evals_.insert(eval); 159 | } 160 | 161 | virtual void execute(const std::vector& charges, 162 | std::vector& results) { 163 | s_ = sources.begin(); 164 | c_ = charges.begin(); 165 | t_ = targets.begin(); 166 | r_ = results.begin(); 167 | 168 | evals_.execute(*this); 169 | } 170 | 171 | bool accept_multipole(const box_type& source, const box_type& target) const { 172 | return acceptMultipole(source, target); 173 | } 174 | 175 | const kernel_type& kernel() const { 176 | return K_; 177 | } 178 | 179 | tree_type& source_tree() { 180 | return source_tree_; 181 | } 182 | tree_type& target_tree() { 183 | return target_tree_; 184 | } 185 | 186 | // Accessors to make this Executor into a BoxContext 187 | inline multipole_type& multipole_expansion(const box_type& box) { 188 | return M_[box.index()]; 189 | } 190 | inline const multipole_type& multipole_expansion(const box_type& box) const { 191 | return M_[box.index()]; 192 | } 193 | inline local_type& local_expansion(const box_type& box) { 194 | return L_[box.index()]; 195 | } 196 | inline const local_type& local_expansion(const box_type& box) const { 197 | return L_[box.index()]; 198 | } 199 | 200 | inline point_type center(const box_type& b) const { 201 | return b.center(); 202 | } 203 | 204 | typedef body_transform body_source_iterator; 205 | inline body_source_iterator source_begin(const box_type& b) const { 206 | return make_transform_iterator(b.body_begin(), body_transformer(s_)); 207 | } 208 | inline body_source_iterator source_end(const box_type& b) const { 209 | return make_transform_iterator(b.body_end(), body_transformer(s_)); 210 | } 211 | 212 | typedef body_transform body_charge_iterator; 213 | inline body_charge_iterator charge_begin(const box_type& b) const { 214 | return make_transform_iterator(b.body_begin(), body_transformer(c_)); 215 | } 216 | inline body_charge_iterator charge_end(const box_type& b) const { 217 | return make_transform_iterator(b.body_end(), body_transformer(c_)); 218 | } 219 | 220 | typedef body_transform body_target_iterator; 221 | inline body_target_iterator target_begin(const box_type& b) const { 222 | return make_transform_iterator(b.body_begin(), body_transformer(t_)); 223 | } 224 | inline body_target_iterator target_end(const box_type& b) const { 225 | return make_transform_iterator(b.body_end(), body_transformer(t_)); 226 | } 227 | 228 | typedef body_transform body_result_iterator; 229 | inline body_result_iterator result_begin(const box_type& b) { 230 | return make_transform_iterator(b.body_begin(), body_transformer(r_)); 231 | } 232 | inline body_result_iterator result_end(const box_type& b) { 233 | return make_transform_iterator(b.body_end(), body_transformer(r_)); 234 | } 235 | }; 236 | 237 | 238 | template 241 | ExecutorDualTree* make_executor(const Kernel& K, 242 | SourceIter sfirst, SourceIter slast, 243 | TargetIter tfirst, TargetIter tlast, 244 | Options& opts) { 245 | return new ExecutorDualTree(K, 246 | sfirst, slast, 247 | tfirst, tlast, 248 | opts); 249 | } 250 | 251 | // TODO 252 | /* 253 | // This assumes the the body indices are consecutive within a box 254 | // The assumption is true for Octree, 255 | // but should be left for an "optimized" Evaluator that 256 | // explicitely makes this assumption 257 | inline charge_iterator charge_begin(const box_type& b) const { 258 | return charges_begin + b.body_begin()->index(); 259 | } 260 | inline charge_iterator charge_end(const box_type& b) const { 261 | return charges_begin + b.body_end()->index(); 262 | } 263 | */ 264 | /* 265 | // This assumes the the body indices are consecutive within a box 266 | // The assumption is true for Octree, 267 | // but should be left for an "optimized" Evaluator that 268 | // explicitely makes this assumption 269 | inline result_iterator result_begin(const box_type& b) { 270 | return results_begin + b.body_begin()->index(); 271 | } 272 | inline result_iterator result_end(const box_type& b) { 273 | return results_begin + b.body_end()->index(); 274 | } 275 | */ 276 | --------------------------------------------------------------------------------