Commit de25e02e authored by incardon's avatar incardon
Browse files

Adding 7_SPH_dlb optimized

parent 84087883
include ../../example.mk
CC=mpic++
LDIR =
OBJ = main.o
%.o: %.cpp
$(CC) -O3 -g -c --std=c++11 -o $@ $< $(INCLUDE_PATH)
sph_dlb: $(OBJ)
$(CC) -o $@ $^ $(CFLAGS) $(LIBS_PATH) $(LIBS)
all: sph_dlb
run: spl_dlb
mpirun -np 2 ./sph_dlb
.PHONY: clean all run
clean:
rm -f *.o *~ core sph_dlb
[pack]
files = main.cpp Makefile
/*!
* \page Vector_7_sph_dlb_opt Vector 7 SPH Dam break simulation with Dynamic load balacing (Optimized version)
*
*
* [TOC]
*
*
* # SPH with Dynamic load Balancing # {#SPH_dlb}
*
* This is just a rework of the SPH Dam break simulation optimized to get better performance we will focus on the
* optimization and differences with the previous example
*
* \see \ref Vector_7_sph_dlb
*
* \htmlonly
* <a href="#" onclick="hide_show('vector-video-3')" >Simulation video 1</a><br>
* <div style="display:none" id="vector-video-3">
* <video id="vid3" width="1200" height="576" controls> <source src="http://openfpm.mpi-cbg.de/web/images/examples/7_SPH_dlb/sph_speed.mp4" type="video/mp4"></video>
* </div>
* <a href="#" onclick="hide_show('vector-video-4')" >Simulation video 2</a><br>
* <div style="display:none" id="vector-video-4">
* <video id="vid4" width="1200" height="576" controls> <source src="http://openfpm.mpi-cbg.de/web/images/examples/7_SPH_dlb/sph_speed2.mp4" type="video/mp4"></video>
* </div>
* <a href="#" onclick="hide_show('vector-video-15')" >Simulation dynamic load balancing video 1</a><br>
* <div style="display:none" id="vector-video-15">
* <video id="vid15" width="1200" height="576" controls> <source src="http://openfpm.mpi-cbg.de/web/images/examples/7_SPH_dlb/sph_dlb.mp4" type="video/mp4"></video>
* </div>
* <a href="#" onclick="hide_show('vector-video-16')" >Simulation dynamic load balancing video 2</a><br>
* <div style="display:none" id="vector-video-16">
* <video id="vid16" width="1200" height="576" controls> <source src="http://openfpm.mpi-cbg.de/web/images/examples/7_SPH_dlb/sph_dlb2.mp4" type="video/mp4"></video>
* </div>
* \endhtmlonly
*
* \htmlonly
* <img src="http://openfpm.mpi-cbg.de/web/images/examples/7_SPH_dlb/dam_break_all.jpg"/>
* \endhtmlonly
*
*
*/
//#define SE_CLASS1
//#define STOP_ON_ERROR
#include "Vector/vector_dist.hpp"
#include <math.h>
#include "Draw/DrawParticles.hpp"
/*!
* \page Vector_7_sph_dlb_opt Vector 7 SPH Dam break simulation with Dynamic load balacing (Optimized version)
*
* ## Using verlet list with skin{#e7_sph_dlb_opt}
*
* The first optimization that we operate is the usage of verlet list. The verlet are reconstructed only when
* the maximum displacement is bigger than the half skin. Because we have to calculate
* the maximum displacement the verlet and euler integration has been modified to do this.
* The function accept a reference to max_disp that is filled with the maximum displacement calculated
* from these functions.
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp verlet_new_arg
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp euler_new_arg
*
*
* The variable is reset inside verlet and euler time integration function
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp reset_max_disp
*
* while iteration across particle the maximum displacement is saved inside the variable
* max_disp
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp calc_max_disp
*
* We also have to be careful that if we are removing particles we have to reconstruct the verlet list,
* so we set it to a really big number
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp big_number_set
*
* Because the maximum displacement has to be calculated across processors, we use the
* function max in Vcluster to calculate the maximum displacement across processors.
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp max_across_proc
*
* We also calculate the skin part and ghost plus skin. Consider also that the ghost
* must be extended to ghost + skin so r_gskin
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp skin_calc
*
* As we explained before, we update the verlet only if particles move more than the skin.
* In case they move more than the skin we do first a map to redistribute the particles and in the
* meanwhile we check if it is a good moment to rebalance. We decided to combine these two steps
* because in case we rebalance we have anyway to reconstruct the Verler-list. Than we calculate
* the pressure for all the particles, refresh the ghost, update the Verlet-list and reset the
* total displacement. In case the the total displacement does not overshoot the skin we just
* calculate the pressure for all the particles and refresh the ghost. We must use the option
* **SKIP_LABELLING**
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp update_verlet
*
* We pass the max_displacement variable to verlet_int and euler_int function. We also add
* the maximum displacement per iteration to the total maximum displacement
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp pass_ver_eu
*
*
*
* ## Symmetric interaction (Crossing scheme){#e7_sph_dlb_sym}
*
* Symmetric interaction give the possibility to reduce the computation by half and speed-up
* your simulation. To do this we have to do some changes into the function calc forces.
* Symmetric interaction require to write on the ghost area. So at the beginning of the function
* we reset the ghost part. In the meanwhile because we have the external force gravity that
* operate per particles, we set this force at the beginning.
*
* \warning The requirement to set per particle external forces outside the particle loop
* come from the symmetric scheme. Suppose to have in pseudocode this
* \code{.unparsed}
1 for each particles p
2 reset the force for p
3 for each neighborhood particle q of p
4 calculate the force p-q
5 add the contribution to p
6 add the contribution to q
* \endcode
* suppose we are on particle p=0 and calculate the force with q=10 we add the
* contribution to p and q. Unfortunately accordingly to this cycle when we reach
* particle q = 10 we reset what we previously calculated. So we have to write
* \code{.unparsed}
*
1 for each particles p
2 reset the force for p
3 for each particles p
4 for each neighborhood particle q of p
5 calculate the force p-q
6 add the contribution to p
7 add the contribution to q
\endcode
*
* With this code we set the per particle external force to gravity and reset the derivative of the
* density for the domain particles
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp reset_particles
*
* With this code we reset the force and derivative of the density of the particles on the ghost part
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp reset_particles2
*
* Small changes must be done to iterate over the neighborhood particles
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp nn_part
*
* skip the self interaction
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp skip_self
*
* This is instead an important change (and honestly it took quite some hour of debuging to
* discover the problem). In case we are on boundary particle (p = boundary particle) and
* calculating an interaction with a particle q = fluid particle we have to remeber that we have
* also to calculate the force for q (not only drho)
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp symmetry
*
* for a fluid particle instead we calculate p-q interaction and we add the contribution
* to p and q. Because we do not integrate over the boundary particles we can also avoid to
* check that q is a boundary particle
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp symmetry2
*
* After the calculation cycle we have to merge the forces and delta density calculated on
* the ghost with the real particles.
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp ghost_put
*
* It is important when we construct our vector of particles to pass the option **BIND_DEC_TO_GHOST**.
* To use symmetric calculation in parallel environment the decomposition must be consistent with the
* cell decomposition of the space.
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp important_option
*
* To construct a Verlet-list using the CRS scheme we use the following function
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp get_verlet_crs
*
* while to update the verlet list we use the following
*
* \snippet Vector/7_SPH_dlb_opt/main.cpp update_verlet_crs
*
* ## Using re-decompose instead of decompose {#e7_sph_dlb_opt_red}
*
* Using redecompose instead of decompose produce less jumping decomposition during the simulation
*
* \htmlonly
* <a href="#" onclick="hide_show('vector-video-1')" >Video 1</a>
* <div style="display:none" id="vector-video-1">
* <video id="vid1" width="1200" height="576" controls> <source src="http://openfpm.mpi-cbg.de/web/images/examples/7_SPH_dlb/sph_dlb.mp4" type="video/mp4"></video>
* <script>video_anim('vid1',100,230)</script>
* </div>
* <a href="#" onclick="hide_show('vector-video-2')" >Video 2</a>
* <div style="display:none" id="vector-video-2">
* <video id="vid2" width="1200" height="576" controls> <source src="http://openfpm.mpi-cbg.de/web/images/examples/7_SPH_dlb/sph_dlb2.mp4" type="video/mp4"></video>
* <script>video_anim('vid2',21,1590)</script>
* </div>
* \endhtmlonly
*
*/
// A constant to indicate boundary particles
#define BOUNDARY 0
// A constant to indicate fluid particles
#define FLUID 1
// initial spacing between particles dp in the formulas
const double dp = 0.0085;
// Maximum height of the fluid water
// is going to be calculated and filled later on
double h_swl = 0.0;
// c_s in the formulas (constant used to calculate the sound speed)
const double coeff_sound = 20.0;
// gamma in the formulas
const double gamma_ = 7.0;
// sqrt(3.0*dp*dp) support of the kernel
const double H = 0.0147224318643;
// Eta in the formulas
const double Eta2 = 0.01 * H*H;
// alpha in the formula
const double visco = 0.1;
// cbar in the formula (calculated later)
double cbar = 0.0;
// Mass of the fluid particles
const double MassFluid = 0.000614125;
// Mass of the boundary particles
const double MassBound = 0.000614125;
// End simulation time
const double t_end = 1.5;
// Gravity acceleration
const double gravity = 9.81;
// Reference densitu 1000Kg/m^3
const double rho_zero = 1000.0;
// Filled later require h_swl, it is b in the formulas
double B = 0.0;
// Constant used to define time integration
const double CFLnumber = 0.2;
// Minimum T
const double DtMin = 0.00001;
// Minimum Rho allowed
const double RhoMin = 700.0;
// Maximum Rho allowed
const double RhoMax = 1300.0;
// Filled in initialization
double max_fluid_height = 0.0;
// Properties
// FLUID or BOUNDARY
const size_t type = 0;
// Density
const int rho = 1;
// Density at step n-1
const int rho_prev = 2;
// Pressure
const int Pressure = 3;
// Delta rho calculated in the force calculation
const int drho = 4;
// calculated force
const int force = 5;
// velocity
const int velocity = 6;
// velocity at previous step
const int velocity_prev = 7;
/*! \cond [sim parameters] \endcond */
/*! \cond [vector_dist_def] \endcond */
// Type of the vector containing particles
typedef vector_dist<3,double,aggregate<size_t,double, double, double, double, double[3], double[3], double[3]> > particles;
// | | | | | | | |
// | | | | | | | |
// type density density Pressure delta force velocity velocity
// at n-1 density at n - 1
struct ModelCustom
{
template<typename Decomposition, typename vector> inline void addComputation(Decomposition & dec, const vector & vd, size_t v, size_t p)
{
if (vd.template getProp<type>(p) == FLUID)
dec.addComputationCost(v,4);
else
dec.addComputationCost(v,3);
}
template<typename Decomposition> inline void applyModel(Decomposition & dec, size_t v)
{
dec.setSubSubDomainComputationCost(v, dec.getSubSubDomainComputationCost(v) * dec.getSubSubDomainComputationCost(v));
}
double distributionTol()
{
return 1.01;
}
};
inline void EqState(particles & vd)
{
auto it = vd.getDomainIterator();
while (it.isNext())
{
auto a = it.get();
double rho_a = vd.template getProp<rho>(a);
double rho_frac = rho_a / rho_zero;
vd.template getProp<Pressure>(a) = B*( rho_frac*rho_frac*rho_frac*rho_frac*rho_frac*rho_frac*rho_frac - 1.0);
++it;
}
}
const double a2 = 1.0/M_PI/H/H/H;
inline double Wab(double r)
{
r /= H;
if (r < 1.0)
return (1.0 - 3.0/2.0*r*r + 3.0/4.0*r*r*r)*a2;
else if (r < 2.0)
return (1.0/4.0*(2.0 - r*r)*(2.0 - r*r)*(2.0 - r*r))*a2;
else
return 0.0;
}
const double c1 = -3.0/M_PI/H/H/H/H;
const double d1 = 9.0/4.0/M_PI/H/H/H/H;
const double c2 = -3.0/4.0/M_PI/H/H/H/H;
const double a2_4 = 0.25*a2;
// Filled later
double W_dap = 0.0;
inline void DWab(Point<3,double> & dx, Point<3,double> & DW, double r, bool print)
{
const double qq=r/H;
if (qq < 1.0)
{
double qq2 = qq * qq;
double fac = (c1*qq + d1*qq2)/r;
DW.get(0) = fac*dx.get(0);
DW.get(1) = fac*dx.get(1);
DW.get(2) = fac*dx.get(2);
}
else if (qq < 2.0)
{
double wqq = (2.0 - qq);
double fac = c2 * wqq * wqq / r;
DW.get(0) = fac * dx.get(0);
DW.get(1) = fac * dx.get(1);
DW.get(2) = fac * dx.get(2);
}
else
{
DW.get(0) = 0.0;
DW.get(1) = 0.0;
DW.get(2) = 0.0;
}
}
inline double Tensile(double r, double rhoa, double rhob, double prs1, double prs2)
{
const double qq=r/H;
//-Cubic Spline kernel
double wab;
if(r>H)
{
double wqq1=2.0f-qq;
double wqq2=wqq1*wqq1;
wab=a2_4*(wqq2*wqq1);
}
else
{
double wqq2=qq*qq;
double wqq3=wqq2*qq;
wab=a2*(1.0f-1.5f*wqq2+0.75f*wqq3);
}
//-Tensile correction.
double fab=wab*W_dap;
fab*=fab; fab*=fab; //fab=fab^4
const double tensilp1=(prs1/(rhoa*rhoa))*(prs1>0? 0.01: -0.2);
const double tensilp2=(prs2/(rhob*rhob))*(prs2>0? 0.01: -0.2);
return (fab*(tensilp1+tensilp2));
}
inline double Pi(const Point<3,double> & dr, double rr2, Point<3,double> & dv, double rhoa, double rhob, double massb, double & visc)
{
const double dot = dr.get(0)*dv.get(0) + dr.get(1)*dv.get(1) + dr.get(2)*dv.get(2);
const double dot_rr2 = dot/(rr2+Eta2);
visc=std::max(dot_rr2,visc);
if(dot < 0)
{
const float amubar=H*dot_rr2;
const float robar=(rhoa+rhob)*0.5f;
const float pi_visc=(-visco*cbar*amubar/robar);
return pi_visc;
}
else
return 0.0;
}
template<typename VerletList> inline double calc_forces(particles & vd, VerletList & NN, double & max_visc)
{
/*! \cond [reset_particles] \endcond */
// Reset the ghost
auto itg = vd.getDomainIterator();
while (itg.isNext())
{
auto p = itg.get();
// Reset force
// Reset the force counter (- gravity on zeta direction)
vd.template getProp<force>(p)[0] = 0.0;
vd.template getProp<force>(p)[1] = 0.0;
vd.template getProp<force>(p)[2] = -gravity;
vd.template getProp<drho>(p) = 0.0;
++itg;
}
/*! \cond [reset_particles] \endcond */
/*! \cond [reset_particles2] \endcond */
auto itg2 = vd.getGhostIterator();
while (itg2.isNext())
{
auto p = itg2.get();
// Reset force
// Reset the force counter (- gravity on zeta direction)
vd.template getProp<force>(p)[0] = 0.0;
vd.template getProp<force>(p)[1] = 0.0;
vd.template getProp<force>(p)[2] = 0.0;
vd.template getProp<drho>(p) = 0.0;
++itg2;
}
/*! \cond [reset_particles2] \endcond */
// Get an iterator over particles
auto part = vd.getParticleIteratorCRS(NN.getInternalCellList());
double visc = 0;
// For each particle ...
while (part.isNext())
{
// ... a
auto a = part.get();
// Get the position xp of the particle
Point<3,double> xa = vd.getPos(a);
// Take the mass of the particle dependently if it is FLUID or BOUNDARY
double massa = (vd.getProp<type>(a) == FLUID)?MassFluid:MassBound;
// Get the density of the of the particle a
double rhoa = vd.getProp<rho>(a);
// Get the pressure of the particle a
double Pa = vd.getProp<Pressure>(a);
// Get the Velocity of the particle a
Point<3,double> va = vd.getProp<velocity>(a);
// We threat FLUID particle differently from BOUNDARY PARTICLES ...
if (vd.getProp<type>(a) != FLUID)
{
// If it is a boundary particle calculate the delta rho based on equation 2
// This require to run across the neighborhoods particles of a
//! \cond [nn_part] \endcond
auto Np = NN.template getNNIterator<NO_CHECK>(a);
//! \cond [nn_part] \endcond
// For each neighborhood particle
while (Np.isNext() == true)
{
// ... q
auto b = Np.get();
// Get the position xp of the particle
Point<3,double> xb = vd.getPos(b);
//! \cond [skip_self] \endcond
// if (p == q) skip this particle
if (a == b) {++Np; continue;};
//! \cond [skip_self] \endcond
// get the mass of the particle
double massb = (vd.getProp<type>(b) == FLUID)?MassFluid:MassBound;
// Get the velocity of the particle b
Point<3,double> vb = vd.getProp<velocity>(b);
// Get the pressure and density of particle b
double Pb = vd.getProp<Pressure>(b);
double rhob = vd.getProp<rho>(b);
// Get the distance between p and q
Point<3,double> dr = xa - xb;
// take the norm of this vector
double r2 = norm2(dr);
// If the particles interact ...
if (r2 < 4.0*H*H)
{
// ... calculate delta rho
double r = sqrt(r2);
Point<3,double> dv = va - vb;
Point<3,double> DW;
DWab(dr,DW,r,false);
const double dot = dr.get(0)*dv.get(0) + dr.get(1)*dv.get(1) + dr.get(2)*dv.get(2);
const double dot_rr2 = dot/(r2+Eta2);
max_visc=std::max(dot_rr2,max_visc);
double scal = (dv.get(0)*DW.get(0)+dv.get(1)*DW.get(1)+dv.get(2)*DW.get(2));
vd.getProp<drho>(a) += massb*scal;
vd.getProp<drho>(b) += massa*scal;
//! \cond [symmetry] \endcond
// If it is a fluid we have to calculate force
if (vd.getProp<type>(b) == FLUID)
{
Point<3,double> v_rel = va - vb;
double factor = - ((vd.getProp<Pressure>(a) + vd.getProp<Pressure>(b)) / (rhoa * rhob) + Tensile(r,rhoa,rhob,Pa,Pb) + Pi(dr,r2,v_rel,rhoa,rhob,massb,visc));
vd.getProp<force>(b)[0] -= massa * factor * DW.get(0);
vd.getProp<force>(b)[1] -= massa * factor * DW.get(1);
vd.getProp<force>(b)[2] -= massa * factor * DW.get(2);