Molecular dynamic with symmetric interactions (Crossing scheme)
In a previous example we show how to build a parallel molecular dynamic simulation in the case of symmetric interaction.
- See also
- Vector 5 molecular dynamic with symmetric Verlet list
Symmetric interaction has one big advantage to cut by half the computation. Unfortunately has also one disadvantage. In the standard way it increase communication overhead. This effect can be seen in a simple way consider the non-symmetric case.
Non-symmetric Symmetric
+-----------------+ +-----------------+ +-----------------+
|XXXXXXXXXXXXXXXXX| |XXXXXXXXXXXXXXXXX| |XXXXXXXXXXXXXXXXX|
|XX+-----------+XX| |XX+-----------+XX| |XX+-----------+XX|
|XX| |XX| |XX| |XX| |XX| |XX|
|XX| |XX| |XX| |XX| |XX| |XX|
|XX| |XX| |XX| |XX| + |XX| |XX|
|XX| |XX| |XX| |XX| |XX| |XX|
|XX| |XX| |XX| |XX| |XX| |XX|
|XX| |XX| |XX| |XX| |XX| |XX|
|XX+-----------+XX| +-----------------+ +-----------------+
|XXXXXXXXXXXXXXXXX|
+-----------------+ ghost_get ghost_put
ghost_get
*
In the non-symmetric case we do a ghost-get communicating the area indicated with "X". After this we are able to calculate the forces inside the box. In the case of normal symmetric like explained in the previous example the first ghost_get communicate the area indicated by X and the ghost_put communicate an equivalent area. This mean that The communication area for the non-symmetric is smaller than the symmetric one. This mean that overall the standard symmetric requires more communication. In order to reduce the communication of the standard symmetric we can use the crossing scheme. The crossing scheme do the following
1 2 3 1 2 3
X 4 X 3 + X 4
If X is the Cell and the numbers are the neighborhood cells, the concept of the crossing scheme is to take the interaction X with "1" and shift by one to the right. This mean that for each cell "X" in our domain we have to do the interaction (X-1),(X-2),(X-3),(3,4). Note that in the case of standard symmetric, all the interactions are in the top,left and right cells of the domain cells. Because of this we can eliminate the bottom part in the ghost_get and in the subsequent ghost_put. In the case of the crossing scheme we have only interaction with the top and right cells, so we do not need the bottom and left part of the ghost.
Symmetric crs
+--------------+ +--------------+
|XXXXXXXXXXXXXX| |XXXXXXXXXXXXXX|
+-----------+XX| +-----------+XX|
| |XX| | |XX|
| |XX| | |XX|
| |XX| + | |XX|
| |XX| | |XX|
| |XX| | |XX|
| |XX| | |XX|
+--------------+ +--------------+
ghost_get ghost_put
In this example we modify the molecular dynamic example to use
this scheme.
Calculate forces
In this function we calculate the forces between particles. It require the vector of particles the Verlet-list and sigma factor for the Lennard-Jhones potential. The function is exactly the same as the normal symmetric
- See also
- Explanation
with the following changes
- We use a different particle iterator compared to the getDomainIterator() this because the the particles to iterate are different from the simple domain particles.
The calculation is the same as the normal symmetric case
template<typename VerletList>
{
auto itg = vd.getDomainAndGhostIterator();
while (itg.isNext())
{
auto p = itg.get();
vd.getProp<force>(p)[0] = 0.0;
vd.getProp<force>(p)[1] = 0.0;
vd.getProp<force>(p)[2] = 0.0;
++itg;
}
auto it2 = vd.getParticleIteratorCRS(NN);
while (it2.isNext())
{
auto p = it2.get();
auto Np = NN.template getNNIterator<NO_CHECK>(p);
while (Np.isNext())
{
auto q = Np.get();
if (q == p) {++Np; continue;};
double rn = norm2(r);
if (rn > r_cut * r_cut) {++Np;continue;}
Point<3,double> f = 24.0*(2.0 *sigma12 / (rn*rn*rn*rn*rn*rn*rn) - sigma6 / (rn*rn*rn*rn)) * r;
vd.template getProp<force>(p)[0] += f.get(0);
vd.template getProp<force>(p)[1] += f.get(1);
vd.template getProp<force>(p)[2] += f.get(2);
vd.template getProp<force>(q)[0] -= f.get(0);
vd.template getProp<force>(q)[1] -= f.get(1);
vd.template getProp<force>(q)[2] -= f.get(2);
++Np;
}
++it2;
}
vd.ghost_put<
add_,force>();
}
This class implement the point shape in an N-dimensional space.
Class for Verlet list implementation.
This structure define the operation add to use with copy general.
aggregate of properties, from a list of object if create a struct that follow the OPENFPM native stru...
Calculate energy
For the energy we use symmetric verlet-list in the same way as we did in the previous example. The changes are the following.
- We use a different particle iterator compared to the getDomainIterator() this because the the particles to iterate are different from the simple domain particles.
- Because we are iterating domain particles and ghost particles. For the Kinetic energy we have to filter-out ghost particles
template<typename VerletList>
{
double E = 0.0;
double rc = r_cut*r_cut;
double shift = 4.0 * ( sigma12 / (rc*rc*rc*rc*rc*rc) - sigma6 / ( rc*rc*rc) );
auto it2 = vd.getDomainIterator();
while (it2.isNext())
{
auto p = it2.get();
auto Np = NN.template getNNIterator<NO_CHECK>(p.getKey());
double Ep = E;
while (Np.isNext())
{
auto q = Np.get();
if (q == p.getKey()) {++Np; continue;};
double rn = norm2(xp - xq);
if (rn >= r_cut*r_cut) {++Np;continue;}
E += 4.0 * ( sigma12 / (rn*rn*rn*rn*rn*rn) - sigma6 / ( rn*rn*rn) ) -
shift;
++Np;
}
E += (vd.template getProp<velocity>(p)[0]*vd.template getProp<velocity>(p)[0] +
vd.template getProp<velocity>(p)[1]*vd.template getProp<velocity>(p)[1] +
vd.template getProp<velocity>(p)[2]*vd.template getProp<velocity>(p)[2]) / 2;
++it2;
}
return E;
}
Simulation
The simulation is equal to the simulation explained in the example molecular dynamic
- See also
- Molecular dynamic with symmetric interactions
With two major differences. First we create a symmetric Verlet-list for crossing scheme instead of a normal one
auto NN = vd.getVerletCrs(r_gskin);;
Second we MUST give the option BIND_DEC_TO_GHOST when we constructing the vector. This option force the vector to decompose the domain in a compatible way for the CRS scheme
Because we will use the crossing scheme and the option BIND_DEC_TO_GHOST we can remove the lower part of the ghost as explained initially in this example
ghost.setLow(0,0.0);
ghost.setLow(1,0.0);
ghost.setLow(2,0.0);
The rest of the code remain unchanged
double dt = 0.00025;
double sigma = 0.1;
double r_cut = 3.0*sigma;
double r_gskin = 1.3*r_cut;
double sigma12 = pow(sigma,12);
double sigma6 = pow(sigma,6);
openfpm_init(&argc,&argv);
size_t sz[3] = {10,10,10};
size_t bc[3]={PERIODIC,PERIODIC,PERIODIC};
ghost.setLow(0,0.0);
ghost.setLow(1,0.0);
ghost.setLow(2,0.0);
size_t k = 0;
size_t start = vd.accum();
auto it = vd.getGridIterator(sz);
while (it.isNext())
{
vd.add();
auto key = it.get();
vd.getLastPos()[0] = key.get(0) * it.getSpacing(0);
vd.getLastPos()[1] = key.get(1) * it.getSpacing(1);
vd.getLastPos()[2] = key.get(2) * it.getSpacing(2);
vd.template getLastProp<velocity>()[0] = 0.0;
vd.template getLastProp<velocity>()[1] = 0.0;
vd.template getLastProp<velocity>()[2] = 0.0;
vd.template getLastProp<force>()[0] = 0.0;
vd.template getLastProp<force>()[1] = 0.0;
vd.template getLastProp<force>()[2] = 0.0;
k++;
++it;
}
vd.map();
vd.ghost_get<>();
auto NN = vd.getVerletCrs(r_gskin);;
calc_forces(vd,NN,sigma12,sigma6,r_cut);
unsigned long int f = 0;
int cnt = 0;
double max_disp = 0.0;
for (size_t i = 0; i < 10000 ; i++)
{
auto it3 = vd.getDomainIterator();
double max_displ = 0.0;
while (it3.isNext())
{
auto p = it3.get();
vd.template getProp<velocity>(p)[0] += 0.5*dt*vd.template getProp<force>(p)[0];
vd.template getProp<velocity>(p)[1] += 0.5*dt*vd.template getProp<force>(p)[1];
vd.template getProp<velocity>(p)[2] += 0.5*dt*vd.template getProp<force>(p)[2];
Point<3,double> disp({vd.template getProp<velocity>(p)[0]*dt,vd.template getProp<velocity>(p)[1]*dt,vd.template getProp<velocity>(p)[2]*dt});
vd.getPos(p)[0] += disp.
get(0);
vd.getPos(p)[1] += disp.get(1);
vd.getPos(p)[2] += disp.get(2);
if (disp.norm() > max_displ)
max_displ = disp.norm();
++it3;
}
if (max_disp < max_displ)
max_disp = max_displ;
if (cnt % 10 == 0)
{
vd.map();
vd.template ghost_get<>();
vd.updateVerlet(NN,r_gskin,VL_CRS_SYMMETRIC);
}
else
{
vd.template ghost_get<>(SKIP_LABELLING);
}
cnt++;
calc_forces(vd,NN,sigma12,sigma6,r_cut);
auto it4 = vd.getDomainIterator();
while (it4.isNext())
{
auto p = it4.get();
vd.template getProp<velocity>(p)[0] += 0.5*dt*vd.template getProp<force>(p)[0];
vd.template getProp<velocity>(p)[1] += 0.5*dt*vd.template getProp<force>(p)[1];
vd.template getProp<velocity>(p)[2] += 0.5*dt*vd.template getProp<force>(p)[2];
++it4;
}
if (i % 100 == 0)
{
vd.deleteGhost();
vd.write("particles_",f);
vd.ghost_get<>();
double energy = calc_energy(vd,NN,sigma12,sigma6,r_cut);
auto & vcl = create_vcluster();
vcl.sum(energy);
vcl.max(max_disp);
vcl.execute();
x.add(i);
y.add({energy});
if (vcl.getProcessUnitID() == 0)
std::cout << "Energy: " << energy << " " << max_disp << " " << std::endl;
max_disp = 0.0;
f++;
}
}
std::cout <<
"Time: " << tsim.
getwct() << std::endl;
This class represent an N-dimensional box.
__device__ __host__ const T & get(unsigned int i) const
Get coordinate.
Implementation of VCluster class.
Implementation of 1-D std::vector like structure.
Class for cpu time benchmarking.
void stop()
Stop the timer.
void start()
Start the timer.
double getwct()
Return the elapsed real time.
Finalize
At the very end of the program we have always to de-initialize the library
Full code
#include "Vector/vector_dist.hpp"
#include "Decomposition/CartDecomposition.hpp"
#include "data_type/aggregate.hpp"
#include "Plot/GoogleChart.hpp"
#include "Plot/util.hpp"
#include "timer.hpp"
constexpr int velocity = 0;
constexpr int force = 1;
template<typename VerletList>
{
auto itg = vd.getDomainAndGhostIterator();
while (itg.isNext())
{
auto p = itg.get();
vd.getProp<force>(p)[0] = 0.0;
vd.getProp<force>(p)[1] = 0.0;
vd.getProp<force>(p)[2] = 0.0;
++itg;
}
auto it2 = vd.getParticleIteratorCRS(NN);
while (it2.isNext())
{
auto p = it2.get();
auto Np = NN.template getNNIterator<NO_CHECK>(p);
while (Np.isNext())
{
auto q = Np.get();
if (q == p) {++Np; continue;};
double rn = norm2(r);
if (rn > r_cut * r_cut) {++Np;continue;}
Point<3,double> f = 24.0*(2.0 *sigma12 / (rn*rn*rn*rn*rn*rn*rn) - sigma6 / (rn*rn*rn*rn)) * r;
vd.template getProp<force>(p)[0] += f.get(0);
vd.template getProp<force>(p)[1] += f.get(1);
vd.template getProp<force>(p)[2] += f.get(2);
vd.template getProp<force>(q)[0] -= f.get(0);
vd.template getProp<force>(q)[1] -= f.get(1);
vd.template getProp<force>(q)[2] -= f.get(2);
++Np;
}
++it2;
}
vd.ghost_put<
add_,force>();
}
template<typename VerletList>
{
double E = 0.0;
double rc = r_cut*r_cut;
double shift = 4.0 * ( sigma12 / (rc*rc*rc*rc*rc*rc) - sigma6 / ( rc*rc*rc) );
auto it2 = vd.getParticleIteratorCRS(NN);
while (it2.isNext())
{
auto p = it2.get();
auto Np = NN.template getNNIterator<NO_CHECK>(p);
double Ep = E;
while (Np.isNext())
{
auto q = Np.get();
if (q == p) {++Np; continue;};
double rn = norm2(xp - xq);
if (rn >= r_cut*r_cut) {++Np;continue;}
E += 4.0 * ( sigma12 / (rn*rn*rn*rn*rn*rn) - sigma6 / ( rn*rn*rn) ) -
shift;
++Np;
}
if (p < vd.size_local())
{
E += (vd.template getProp<velocity>(p)[0]*vd.template getProp<velocity>(p)[0] +
vd.template getProp<velocity>(p)[1]*vd.template getProp<velocity>(p)[1] +
vd.template getProp<velocity>(p)[2]*vd.template getProp<velocity>(p)[2]) / 2;
}
++it2;
}
return E;
}
int main(int argc, char* argv[])
{
double dt = 0.00025;
double sigma = 0.1;
double r_cut = 3.0*sigma;
double r_gskin = 1.3*r_cut;
double sigma12 = pow(sigma,12);
double sigma6 = pow(sigma,6);
openfpm_init(&argc,&argv);
size_t sz[3] = {10,10,10};
size_t bc[3]={PERIODIC,PERIODIC,PERIODIC};
ghost.setLow(0,0.0);
ghost.setLow(1,0.0);
ghost.setLow(2,0.0);
size_t k = 0;
size_t start = vd.accum();
auto it = vd.getGridIterator(sz);
while (it.isNext())
{
vd.add();
auto key = it.get();
vd.getLastPos()[0] = key.get(0) * it.getSpacing(0);
vd.getLastPos()[1] = key.get(1) * it.getSpacing(1);
vd.getLastPos()[2] = key.get(2) * it.getSpacing(2);
vd.template getLastProp<velocity>()[0] = 0.0;
vd.template getLastProp<velocity>()[1] = 0.0;
vd.template getLastProp<velocity>()[2] = 0.0;
vd.template getLastProp<force>()[0] = 0.0;
vd.template getLastProp<force>()[1] = 0.0;
vd.template getLastProp<force>()[2] = 0.0;
k++;
++it;
}
vd.map();
vd.ghost_get<>();
auto NN = vd.getVerletCrs(r_gskin);;
calc_forces(vd,NN,sigma12,sigma6,r_cut);
unsigned long int f = 0;
int cnt = 0;
double max_disp = 0.0;
for (size_t i = 0; i < 10000 ; i++)
{
auto it3 = vd.getDomainIterator();
double max_displ = 0.0;
while (it3.isNext())
{
auto p = it3.get();
vd.template getProp<velocity>(p)[0] += 0.5*dt*vd.template getProp<force>(p)[0];
vd.template getProp<velocity>(p)[1] += 0.5*dt*vd.template getProp<force>(p)[1];
vd.template getProp<velocity>(p)[2] += 0.5*dt*vd.template getProp<force>(p)[2];
Point<3,double> disp({vd.template getProp<velocity>(p)[0]*dt,vd.template getProp<velocity>(p)[1]*dt,vd.template getProp<velocity>(p)[2]*dt});
vd.getPos(p)[0] += disp.
get(0);
vd.getPos(p)[1] += disp.get(1);
vd.getPos(p)[2] += disp.get(2);
if (disp.norm() > max_displ)
max_displ = disp.norm();
++it3;
}
if (max_disp < max_displ)
max_disp = max_displ;
if (cnt % 10 == 0)
{
vd.map();
vd.template ghost_get<>();
vd.updateVerlet(NN,r_gskin,VL_CRS_SYMMETRIC);
}
else
{
vd.template ghost_get<>(SKIP_LABELLING);
}
cnt++;
calc_forces(vd,NN,sigma12,sigma6,r_cut);
auto it4 = vd.getDomainIterator();
while (it4.isNext())
{
auto p = it4.get();
vd.template getProp<velocity>(p)[0] += 0.5*dt*vd.template getProp<force>(p)[0];
vd.template getProp<velocity>(p)[1] += 0.5*dt*vd.template getProp<force>(p)[1];
vd.template getProp<velocity>(p)[2] += 0.5*dt*vd.template getProp<force>(p)[2];
++it4;
}
if (i % 100 == 0)
{
vd.deleteGhost();
vd.write("particles_",f);
vd.ghost_get<>();
double energy = calc_energy(vd,NN,sigma12,sigma6,r_cut);
auto & vcl = create_vcluster();
vcl.sum(energy);
vcl.max(max_disp);
vcl.execute();
x.add(i);
y.add({energy});
if (vcl.getProcessUnitID() == 0)
std::cout << "Energy: " << energy << " " << max_disp << " " << std::endl;
max_disp = 0.0;
f++;
}
}
std::cout <<
"Time: " << tsim.
getwct() << std::endl;
options.
title = std::string(
"Energy with time");
options.
yAxis = std::string(
"Energy");
options.
xAxis = std::string(
"iteration");
cg.
write(
"gc_plot2_out.html");
openfpm_finalize();
}
Small class to produce graph with Google chart in HTML.
void write(std::string file)
It write the graphs on file in html format using Google charts.
void AddLinesGraph(openfpm::vector< X > &x, openfpm::vector< Y > &y, const GCoptions &opt)
Add a simple lines graph.
std::string xAxis
X axis name.
size_t lineWidth
Width of the line.
std::string title
Title of the chart.
std::string yAxis
Y axis name.