Giter VIP home page Giter VIP logo

bbfmm3d's Introduction

BBFMM3D

1. INTRODUCTION

BBFMM3D is an open source package of the Black-box Fast Multipole Method in 3 dimensions.
The Black-box Fast Multipole Method is an O(N) fast multipole method, which is a technique to calculate sums of the form

where is kernel function, are observation points, are locations of sources, and are charges at corresponding locations. BBFMM3D provides an O(N) solution to matrix-vector products of the type Ax. In that case the relation between A and K is:

This implementation of the FMM differs from other methods by the fact that it is applicable to all smooth kernels K. [Give examples of RBF kernels, 1/r, log r, Stokes, etc.].

The approximation scheme used in the FMM relies on Chebyshev interplation to construct low-rank approximations for well-separated clusters. In addition the use of Singular Value Decomposition ensures that the computational cost is minimal. In particular the rank is optimally chosen for a given error.

Please cite the following paper if you use this code:

Fong, William, and Eric Darve. "The black-box fast multipole methodshod." Journal of Computational Physics 228, no. 23 (2009): 8712-8725. You can see details here.

2. DIRECTORIES AND FILES

./examples/		:	Example input C++ codes; Needed to read input from user or from input file.  
./src/			:	Source code in C++  
./include/		:	Relevant header files  
./exec/			:	Executables for BBFMM3D  
./input/		:	The input file.  
./README.md		:	This file  
./License.md	:	License file  
./Makefile		:	Makefile

3. TUTORIAL

3.1 To Get Started

To check whether things are set up correctly, you can perform the following: Go to the directory where Makefile is in, then key in the following three commands in the terminal:

	make binary_file_mykernel
	cd exec/
	./binary_file_mykernel

3.2 Basic usage

3.2.1 BBFMM3D with standard kernel

The basic usage of BBFMM3D with standard kernel is as follows:

#include"bbfmm3d.hpp"  
...
{
double L;       // Length of simulation cell (assumed to be a cube)
int n;          // Number of Chebyshev nodes per dimension
doft dof;
int Ns;         // Number of sources in simulation cell
int Nf;         // Number of field points in simulation cell
int m;
int level;		// The number of levels in the hierarchy tree
int use_chebyshev // label of whether the computation will use chebyshev interpolation formula or uniform interpolation formula 
double eps;
vector3 *source = new vector3[Ns];    // Position array for the source points
vector3 *field = new vector3[Nf];     // Position array for the field points
double *q =  new double[Ns*dof.s*m];  // Source array
double *stress      =  new double[Nf*dof.f*m];// Field array (BBFMM calculation)
…
kernel_LaplacianForce Atree(L,level, n, eps, use_chebyshev);
Atree.buildFMMTree();  // Build the fmm tree;

/* The following can be repeated with different field, source, and q */

H2_3D_Compute<kernel_LaplacianForce> compute(&Atree, field, source, Ns, Nf, q,m, stress);
...
}

This example first build a FMM tree with these two lines:

kernel_LaplacianForce Atree(L,level, n,  eps, use_chebyshev);
Atree.buildFMMTree();  

where kernel_LaplacianForce is a class of fmm tree using LaplacianForce kernel, the constructor takes 5 arguments:

  • L(double):
    Length of simulation cell (assumed to be a cube).

  • level(int):
    The number of levels in the hierarchy tree

  • n(int):
    Number of Chebyshev nodes per dimension.

  • eps(double):
    Target accuracy; this is used to determine which singular values are kept after the SVD. cutoff.s and cutoff.f are computed using this epsilon.

  • use_chebyshev(int):
    Label to indicate whether using chebyshev interpolation formula or uniform interpolation formula.
    use_chebyshev = 1: chebyshev interplation formula;
    use_chebyshev = 0: uniform interpolation formula(where FFT is used, now it just supports homogenous kernel, non-homogeneous will be added)

Once the tree is created, you can repeat matrix-vector product with different field, source and q(charge).(see 3.2.4) The code shows an example using LapacianForce kernel:

H2_3D_Compute<kernel_LaplacianForce> compute(&Atree, field, source, Ns, Nf, q,m, stress);

The template class H2_3D_Compute is for computing matrix-vector product after the FMM tree is built. The constructor takes 8 arguments:

  • &Atree(T*):
    A pointer to the FMM tree we just built. Here T is typename, FMM tree built with different kernels have different typenames.
  • field(vector3*):
    A pointer to position array of field points. vector3 is a struct storing x, y and z coordinates of a 3D point.
  • source(vector3*):
    A pointer to position array of source points.
  • Ns(int):
    Number of sources in simulation cell.
  • Nf(int):
    Number of fields in simulation cell
  • q(double*):
    A pointer to the charges.
  • m(int):
    Number of sets of charges.
  • stress(double*):
    A pointer to the result, and the result is stored column-wise in stress.

#####3.2.2 Options of provided kernels

Below are the details of the kernel functions K we have provided:
( For all the kernel functions, we denote r to be Euclidean distance between x and y. )

Options of kernels:

  • LOGARITHM kernel:
    usage: kernel_Logarithm
    kernel function:

  • ONEOVERR2 kernel:
    usage: kernel_OneOverR2
    kernel function:

  • GAUSSIAN kernel:
    usage: kernel_Gaussian
    kernel function:

  • QUADRIC kernel:
    usage: kernel_Quadric
    kernel function:

  • INVERSEQUADRIC kernel:
    usage: kernel_InverseQuadric
    kernel function:

  • THINPLATESPLINE kernel:
    usage: kernel_ThinPlateSpline
    kernel function:

  • LAPLACIAN kernel:
    usage: kernel_Lapacian
    kernel function:

  • ONEOVERR4 kernel:
    usage: kernel_OneOverR4
    kernel function:

  • LAPLACIANFORCE kernel:
    usage: kernel_LaplacianForce

  • STOKES kernel (tensor kernel)
    usage: kernel_Stokes
    kernel function:
    i.e. where delta is an indicator function. vector r is the difference of x and y, see http://en.wikipedia.org/wiki/Stokes_flow#By_Green.27s_function:_the_Stokeslet for details.

If you want to define your own kernel, please see 3.2.3.

3.2.3 BBFMM3D with user defined kernel

The basic usage is almost the same as 3.2.1 except that you have to define your own routine of computing kernel. One example code is as follows:

#include "bbfmm3d.hpp"

class myKernel: public H2_3D_Tree {
public:
myKernel(double L, int level, int n, double epsilon, int use_chebyshev):H2_3D_Tree(L,level,n, epsilon, use_chebyshev){};
virtual void setHomogen(string& kernelType,doft* dof) {
    homogen = -1;
    symmetry = 1;
    kernelType = "myKernel";
    dof->f = 1;
    dof->s = 1;
}
virtual void EvaluateKernel(vector3 fieldpos, vector3 sourcepos,
                            double *K, doft *dof) {
    vector3 diff;
    double rinv;
    
    diff.x = sourcepos.x - fieldpos.x;
    diff.y = sourcepos.y - fieldpos.y;
    diff.z = sourcepos.z - fieldpos.z;
    *K = 1. / sqrt(diff.x*diff.x + diff.y*diff.y + diff.z*diff.z);
}
};



...
{
…
myKernel Atree(L,level, n, eps, use_chebyshev);
Atree.buildFMMTree();  // Build the fmm tree;

/* The following can be repeated with different field, source, and q */

H2_3D_Compute<myKernel> compute(&Atree, field, source, Ns, Nf, q, m, stress);
...
}
  • dof(doft*):
    A pointer to degree of freedom, which is the sizes of the small tensor matrix(tensor kernel). The FMM can also handle case of tensor kernel(not only scalar kernel). This struct stores information about the size of tensor kernel.

You can define your own kernel inside EvaluateKernel(vector3 fieldpos, vector3 sourcepos, double *K, doft *dof), it takes field point, source point and degree of freedom(see 3.2.1) as input, and pass the kernel value to K.

You also need to define information about kernel inside setHomogen(string& kernelType,doft* dof)

  • homogen: The homogeneous property of kernel.(The cost and memory requirements of the pre-computation step can be reduced for the case of homogeneous kernels.)

    • homogen = 0: if the kernel funtion is not homogeneous.
    • homogen = m: if the kernel funtion is homogeneous of degree m, i.e.
    • symmetry:
      The symmetry property of the kernel.
    • symmetry = 0: no symmetric property;
    • symmetry = 1: symmetric kernel; [K(x,y) = K(y,x)]
    • symmetry = -1: anti-symmetric kernel.[K(x,y) = - K(y,x)]
  • kernelType:
    The name of the kernel defined by user. The purpose of this is to label different files generated in the pre-computation step ( see 3.3 )

3.2.4 Usage of multiple sources with same kernel

If you want to compute with different sources (source points, field points, charges) but with same kernel and number of Chebyshev nodes, you can do it in one file: e.g.

class myKernel: public H2_3D_Tree {
...
}
…

{
…
/* Build FMM tree */
myKernel Atree(L,level, n, eps, use_chebyshev);
Atree.buildFMMTree(); 

/* The following can be repeated with different field, source, and q */

H2_3D_Compute<myKernel> compute1(&Atree, field1, source1, Ns1, Nf1, q1,m1, stress1);
H2_3D_Compute<myKernel> compute2(&Atree, field2, source2, Ns2, Nf2, q2,m2, stress2);
…
}

The basic usage is already domonstrated in 3.2.1 and 3.2.3. Once you have built the FMM tree, you can use different sources to compute the matrix-vector multiplication without rebuilding the tree. You can choose kernel type from standard kernels given by us ( see 3.2.2 ), or you can define your own kernel ( see 3.2.3 )

3.3 Pre-computation

The power of this package is in the pre-computing part, which is much more computationally expensive than computing part. This package takes advantage of the fact that for a given kernel and number of chebyshev nodes, the precomputing part is the same, so for a fixed kernel and number of chebyshev nodes, it generates 3 files storing information of FMM tree in the folder /output. Everytime when we use the same kernel type and number of chebyshev nodes, we can directly read from the files, which would save a lot of time.

Note: it is true that sometimes with the pre-computation step, the code will be slower than direct calculation. But if the file already exists, then when doing more computations it will be faster than direct calculation. If you are using your own kernel, make sure to either change the kernelType or delete the existed file if you changed your kernel.

3.4 Test Interplation Error

To give the user an idea of how large the interplation error is, we have provided a routine of computing the interplation error. If you want to test the interplation error between a cluster A (of size length) and a cluster B, where B is in the interplation list of A, you can do the following:

kernel_LaplacianForce testTree(&dof,1/pow(2,2),2, n, eps, use_chebyshev);
double errtest = testInterplationErr(&testTree, 100, 100);

The first line constructs a FMM tree, you can see 3.2.1 for more details of the constructor. Here we more explanations of the second and third arguments:

  • second argument:
    if you want to test the error with cluster of size length, then the second argument should be set to 4 x length.
  • third argument:
    this argument should be set to 2.

The second line is the routine to compute interplation error.

  • &testTree (T* ):
    A pointer to the tree you just created.
  • 100 (int):
    Number of source points in the cluster.
  • 100 (int):
    Number of field points in the cluster.

4. ROUTINES FOR INPUTING AND OUTPUTING DATA

We have provided several routines for reading data from binary file, and writing data into binary file.

4.1 Reading meta data from text file

void read_Metadata(const string& filenameMetadata,double& L, int& n, doft& dof, int& Ns, int& Nf, int& m, int& level);

The first argument, filenameMetadata is the filename for your meta data. L stores the length of simulation cell (assumed to be a cube); n stores the number of chebyshev nodes per dimension; dof stores (???); Ns stores the number of source points; Nf stores the number of field points; m stores the number of sets of charges; level stores the number of levels in the hierarchy tree;

File format:

L, n, dof.s, dof.f, Ns, Nf, m, level

For example:

1,4,9,6,800,500,1,2

4.2 Reading from binary file

void read_Sources(const string& filenameField, vector3 *field, const int& Nf, const string& filenameSource, vector3 *source, const int& Ns, const string& filenameCharge, double *q, const int& m, const doft& dof);

The first argument filenameField, the forth argument filenameSource and the seventh argument filenameCharge are binary file names for field positions, source positions and charges respectively. Nf, Ns, m and dof are passed to this funtion. Nf is the number of field points, Ns is the number of source points, m is the number of sets of charges, and dof is the degree of freedom. The data of field is stored in field, the data of source is stored in source, and the data of charges is stored in q column-wise.

File format:

  1. Binary file for source:

    source positons are stored column-wise:

     sourcepos0.x
     sourcepos1.x
     …
     sourcepos0.y
     sourcepos1.y
     …
     sourcepos0.z
     sourcepos1.z
     ...
    
  2. Binary file for field:

    field positions are stored column-wise:

     fieldpos0.x
     fieldpos1.x
     …
     fieldpos0.y
     fieldpos1.y
     …
     fieldpos0.z
     fieldpos1.z
     ...
    
  3. Binary file for charges:
    charges are stored column-wise:

     first set of charges
     second set of charges
     ...
    

4.3 Writing into binary file

void write_Into_Binary_File(const string& filename, double* outdata, int numOfElems);  

This first argument is the filename for your output data. The second argument is a pointer to the output data, and the last argument is the number of elements in the array of your output data.

5. EXAMPLES

We have provided several examples for BBFMM3D. Go to examples/, read through the files both must be self explanatory for the most part. You can use our examples with your own input. ####5.1 Example file for generating binary/text files. Both gen_binary_file.m and test.cpp shows how our test binary files are generated. ####5.2 Making changes to the examples for your own application

  1. If you want to generate input through your own routine, and use the standard kernels:

    Go to /examples/, open "get_input_through_routine_standard_kernel.cpp".

    • To generate input through routine:

      Change SetMetaData() and SetSources()

    • To use standard kernels:

      Choose the kernel type in main(), options of kernels are in 3.2.2

  2. If you want to generate input through your own routine, and use your own kernel:

    Go to /examples/, open "get_input_through_routine_myKernel.cpp".

    • To define your own kernel:

      Modify class myKernel.

    • To generate your input:

      The same step as described in 1.

  3. If you want to read input from binary file, and use standard kernel:

    Go to /examples/, open "binary_file_standard_kernel.cpp".

    • To change input filename:

      string filenameField = "./../input/field_test.bin";
      string filenameSource = "./../input/source_test.bin"; string filenameCharge = "./../input/charge_test.bin";
      string filenameMetadata = "../input/metadata.txt";
      change them into your input filenames.

    • To use standard kernels:

      The same step as described in 1. ;

  4. If you want to read input from binary file, and use your own kernel:

    Go to /examples/, open "binary_file_mykernel.cpp".

    • To change the input filename:

      The same step as described in 5.

    • To define your own kernel:

      Modify class myKernel.

When using our examples, make sure that the input file format are the same as described in 4.

5.2 Run examples

Here we give an example:
If you want to use "binary_file_standard_kernel.cpp"

  1. As stated earlier to run the code, go to the appropriate directory and key in the following:

     make binary_file_standard_kernel
    
  2. Make sure you have changed or cleaned the .o files from previous compilation. To clean the irrelevant files, key in:

     make clean
    
  3. To tar the file, key in

     make tar
    
  4. Read through the makefile for other options.

To run other .cpp files:

  1. get_input_through_routine_myKernel.cpp
    key in:

     make get_input_through_routine_myKernel   
    
  2. get_input_through_routine_standard_kernel.cpp
    key in:

     make get_input_through_routine_standard_kernel
    
  3. binary_file_mykernel.cpp
    key in:

     make binary_file_mykernel
    
  4. binary_file_standard_kernel.cpp
    key in:

     make binary_file_standard_kernel
    

bbfmm3d's People

Contributors

ruoxi-wang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

bbfmm3d's Issues

Questions about BBFMM3D

When the number of source points and the number of field points are the same, why does the increase of time not conform to O (N)? What is the parameter L related to? Will it affect the time? In this case, L takes 3200

Get started tutorial failed

Hi, I was following the get started tutorial but it failed very early. I found two problems:

  1. I was not able to compile the code in the repository with "make binary_file_mykernel".
    I think that the line 74 of "BBFMM3D/include/compute.hpp" should be commented.

  2. If I comment the line given above then I'm able to compile the code but when I tried to run the executable "BBFMM3D/exec/binary_file_mykernel" the code crash with

Pre-Compute files do not exit. Creating now ...
Segmentation fault (core dumped)

I have not tried anything else as I wanted to run the basic tutorial first.
Thanks

Trying to understand how it works but can't.

I've been trying to figure out how I can access the output of the program. The README file says that the stress.bin file contains the output but when I open that in MATLAB (I don't know how else to open it) I only see two columns of data which could probably mean that only two components, of whatever it is the matrix vector product is calculating, are present. So, it would be nice if someone could please help me understand how this works: If I were to calculate an induced potential using a particular kernel that I would be defining, in 3D, how do I access the three components of the induced vector. Where are they stored? And would I have to change any part of the library to perform my calculations or does this library calculate the matrix vector product on the definition of a kernel by itself? I tried reading the paper 'The black-box fast multipole method' by Dave et al but couldn't understand it at all. Also, I know only the basics of C++ (I try to use it like the way I use MATLAB. Don't know anything about defing classes and such. My apologies.) So please help me understand using this library in the most easiest way possible, step-by-step. It doesn't matter even if there are a 100 steps that I need to follow, please help.

DirectCalc3D vs MATLAB

Hello Ruoxi Wang,

I was comparing the values of the results, when using 1/r kernel, obtained from direct method in MATLAB (my program) and from DirectCalc3D( ) in BBFMM3D. I find that the results are not the same. Are they supposed to be different? Also, can you please help me know where exactly is DirectCalc3D defined?

Kind regards
vktsn

The values of kernel K in BBFM3D differs from that in MATLAB

Hello Ruoxi Wang,

I've been trying to use the BBFMM3D code in C++ and I have been trying to validate it using a simple code written in MATLAB to calculate the same 1./r kernel. At line number 679 of compute.hpp I have written a cout statement to be able to see what the values are in the kernel (cout << *Kij << endl;). It is oberved that the values of K from the BBFMM3D code are completely different from the one obtained in MATLAB. However, the final result of K*q is approximately correct. I would just like to know if I'm accessing Kij in the cout statement correctly or if the actual value of K is calculated somewhere else in the code. Can you please help me understand this?

Many thanks,
vktsn0303

FMM computation doesn't work when looped. (Maybe I'm wrong here)

Hello Ruoxi Wang,

In the binary_file_mykernel.cpp program I need to loop the statement
H2_3D_Compute<myKernel> compute(&Atree, field, source, Ns, Nf, q,m, stress);
using a for loop. When I do it I get an error that says: 'Segementation fault (core dumped)' . The error occurs even when the following is used:

for (int i=0; i<10; i++)
{
H2_3D_Compute<myKernel> compute(&Atree, field, source, Ns, Nf, q,m, stress);
}

Is this supposed to throw an error or is it just an error in the program?

Thanks
vktsn0303

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.