OpenFPM  5.2.0
Project that contain the implementation of distributed structures
main.cpp
1 #define SE_CLASS1
2 
3 #include "DCPSE/DCPSE_op/DCPSE_op.hpp"
4 #include "Vector/vector_dist.hpp"
6 #include <chrono>
7 #include "ellipsoid_helpfunctions.h"
8 #include "kernel_functions.h"
9 
10 
11 // properties of distributed vector
12 const int sdf = 0; // level-set function
13 const int normal = 1; // surface normal
14 const int curvature = 2;// (mean) curvature
15 const int cp = 3; // closest point on surface
16 const int surf_flag = 4;// surface flag that can help debugging redistancing
17 const int rho = 5; // density
18 const int p = 6; // pressure
19 const int force = 7; // force acting on particle
20 const int vel = 8; // velocity of particle
21 const int vel_prev = 9; // velocity of particle at previous dt
22 const int pos_prev = 10;// position of particle at previous dt
23 
25 // | | | | | | | | | | |
26 // sdf normal curvature closest point surf_flag density pressure force velocity previous velocity previous position
28 
29 const std::string filebasename = "Geometry_2Ddroplet";
30 
31 // simulation parameters:
32 
33 // ellipse parameters
34 const double A = 0.75;
35 const double B = 0.5;
36 // initial interparticle spacing
37 const double dp = 1/32.0;
38 // smoothing length of SPH operators
39 const double H = 3.0*dp;
40 // smoothing length of continuum surface
41 const double H_1D = H;
42 // polytropic exponent of equation of state p(\rho)
43 const double gamma_eos = 7.0;
44 // speed of sound
45 const double c = 100.0;
46 // reference density (of both fluids)
47 const double rho_0 = 1.0;
48 // viscosity (of both fluids)
49 const double eta = 0.5;
50 const double nu = eta*rho_0;
51 // surface tension coefficient
52 const double alpha = 50.0;
53 // time step size
54 const double dt = 2.0*1e-4;
55 // time variable
56 double t;
57 // variable indicating whether we are in a predictor or corrector step
58 int corr;
59 
60 // parameters of the particle closest point redistancing method
61 const double band_width = 15.0*dp;
62 const double rdist_cutoff_factor = 2.8;
63 
64 // dimensions of spatial and temporal domain
65 const double l = 2.44;
66 #ifdef TEST_RUN
67 const double t_end = 0.1;
68 #else
69 const double t_end = 1.0;
70 #endif
71 // total mass in the domain to compute individual particle masses from
72 const double M = l*l*rho_0;
73 // number of particles in total
74 double np;
75 // individual particle masses
76 double m;
77 
78 // initialize kernel functions, both for the SPH operators and the continuum surface
81 
82 // SPH density summation to determine the individual particle densities (and volumes)
83 template <typename CellList> inline void density_summation(particles & vd, CellList & NN)
84 {
85  vd.template ghost_get<rho>();
86  auto part = vd.getDomainIterator();
87  vd.updateCellList(NN);
88  // iterate over particles a (central particle)
89  while (part.isNext())
90  {
91  auto a = part.get();
92  Point<2, double> xa = vd.getPos(a);
93  // intitialize sum that yields 1/(particle volume)
94  double V_inv = 0.0;
95 
96  auto Np = NN.getNNIteratorBox(NN.getCell(vd.getPos(a)));
97  // iterate over particles b (neighboring particles)
98  while (Np.isNext() == true)
99  {
100  auto b = Np.get();
101  Point<2, double> xb = vd.getPos(b);
102  Point<2, double> dr = xa - xb;
103  double r2 = norm2(dr);
104  // if particles interact, compute and add contribution
105  if (r2 <= 4.0*H*H)
106  {
107  double r = std::sqrt(r2);
108  V_inv += kernel2D.Wab(r);
109  }
110  ++Np;
111  }
112  // particle density = (particle mass)/(particle volume)
113  vd.getProp<rho>(a) = m*V_inv;
114  ++part;
115  }
116 }
117 // Equation of state to compute particle pressures from particle densities
118 // Evaluate entire list of particles in one go
119 inline void EqOfState(particles & vd)
120 {
121  auto it = vd.getDomainIterator();
122  // for all particles a
123  while (it.isNext())
124  {
125  auto a = it.get();
126  double dens = vd.getProp<rho>(a);
127  // Equation of state
128  vd.getProp<p>(a) = (c*c*rho_0/gamma_eos)*(std::pow(dens/rho_0, gamma_eos) - 1);
129 
130  ++it;
131  }
132 }
133 // force computation: pressure gradient + viscous forces + surface tension force
134 template<typename CellList> inline void calc_forces(particles & vd, CellList & NN)
135 {
136  vd.template ghost_get<rho, p, vel, normal>();
137  auto part = vd.getDomainIterator();
138 
139  // Update the cell-list
140  vd.updateCellList(NN);
141 
142  double max_p = 0.0;
143  double max_eta = 0.0;
144  double avg_alpha = 0.0;
145  double maxvel = 0.0;
146  int numparticles = 0;
147  int numsurfparticles = 0;
148 
149  // Iterate over all central particles a
150  while (part.isNext())
151  {
152  auto a = part.get();
153 
154  // initialize variables (forces acting on particle a, and contributions due to
155  // pressure gradient and viscous forces
156  vd.getProp<force>(a)[0] = 0.0;
157  vd.getProp<force>(a)[1] = 0.0;
158 
159  Point<2, double> p_force;
160  Point<2, double> eta_force;
161  for(int k;k<2;k++) p_force[k] = 0.0;
162  for(int k;k<2;k++) eta_force[k] = 0.0;
163 
164  // Get the position xp of the particle
165  Point<2, double> xa = vd.getPos(a);
166 
167  // Get the density and volume of the of the particle a
168  double rhoa = vd.getProp<rho>(a);
169  double Va = m/rhoa;
170 
171  // Get the pressure of the particle a
172  double Pa = vd.getProp<p>(a);
173 
174  // Get the Velocity of the particle a
175  Point<2, double> va = vd.getProp<vel>(a);
176 
177  // Get an iterator over the neighborhood particles of p
178  auto Np = NN.getNNIteratorBox(NN.getCell(vd.getPos(a)));
179 
180  // For each neighborhood particle b
181  while (Np.isNext() == true)
182  {
183  auto b = Np.get();
184 
185  // Get the position xp of the particle
186  Point<2, double> xb = vd.getPos(b);
187 
188  // if (p == q) skip this particle
189  if (a.getKey() == b) {++Np; continue;};
190 
191  Point<2, double> vb = vd.getProp<vel>(b);
192  double Pb = vd.getProp<p>(b);
193  double rhob = vd.getProp<rho>(b);
194  double Vb = m/rhob;
195 
196  // Get the distance between p and q
197  Point<2, double> dr = xa - xb;
198  // take the norm of this vector
199  double r2 = norm2(dr);
200 
201  // if they interact
202  if (r2 < 4.0*H*H)
203  {
204  double r = sqrt(r2);
205  // compute the difference in velocities for the viscous forces
206  Point<2, double> v_rel = va - vb;
207  // get derivative kernel values
208  Point<2, double> DW;
209  double dwdrab;
210  kernel2D.DWab(dr,DW,r,false,dwdrab);
211  // compute pressure gradient factor
212  double factor_p = - (Va*Va + Vb*Vb)*(rhoa*Pb + rhob*Pa)/(rhoa + rhob)/m;
213  // compute viscosity factor
214  double factor_visc = eta*(Va*Va + Vb*Vb)*dwdrab/r/m;
215  // add contributions to sum
216  p_force[0] += factor_p * DW.get(0);
217  p_force[1] += factor_p * DW.get(1);
218  eta_force[0] += factor_visc*v_rel[0];
219  eta_force[1] += factor_visc*v_rel[1];
220 
221  }
222 
223  ++Np;
224  }
225  // compute force on particle a as sum of pressure gradient and viscous forces
226  vd.getProp<force>(a)[0] += p_force[0] + eta_force[0];
227  vd.getProp<force>(a)[1] += p_force[1] + eta_force[1];
228 
229  // if particle a is close enough to the surface to experience surface tension force
230  if (std::abs(vd.getProp<sdf>(a)) < (2.0*H_1D))
231  {
232  double stf0;
233  double stf1;
234  // evaluate 1D smoothing function that distributes surface tension effect across the interface
235  double smoothing_factor = kernel1D.Wab(std::abs(vd.getProp<sdf>(a)))/rhoa;
236  // surface tension force points inwards for particles of both phases, so we need to adjust the
237  // direction of one of the surface normals
238  double sign_corr = 1.0;
239  if (return_sign(vd.getProp<sdf>(a) > 0)) sign_corr = -1.0;
240  // continuum surface force acting on particle = smoothing function*surface tension coefficient
241  // *curvature*surface normal component/density (the latter comes from the NS-eq.)
242  stf0 = smoothing_factor*alpha*vd.getProp<curvature>(a)*vd.getProp<normal>(a)[0]*sign_corr;
243  stf1 = smoothing_factor*alpha*vd.getProp<curvature>(a)*vd.getProp<normal>(a)[1]*sign_corr;
244 
245  // add surface tension force to obtain the overall force on particle a
246  vd.getProp<force>(a)[0] += stf0;
247  vd.getProp<force>(a)[1] += stf1;
248 
249  // add curvature of particle to track the average curvature value over time (can be interesting)
250  avg_alpha += vd.getProp<curvature>(a);
251  numsurfparticles++;
252  }
253 
254  // get and process some info to to print during the simulation
255  if (va.norm() > maxvel) maxvel = va.norm();
256  if (p_force.norm() > max_p) max_p = p_force.norm();
257  if (eta_force.norm() > max_eta) max_eta = eta_force.norm();
258  ++numparticles;
259  ++part;
260  }
261  avg_alpha = avg_alpha/numsurfparticles;
262  if ((corr) && (((int) std::round(t/dt)%5) == 0)) std::cout<<"Time step: "<<t<<", Max p force: "<<max_p<<", Max eta force: "<<max_eta<<", Max vel: "<<maxvel<<", Average curvature: "<<avg_alpha<<", number of particles: "<<numparticles<<std::endl;
263  if (numparticles == 0) throw std::invalid_argument("no particles");
264 }
265 // Predictor corrector integrator
266 void pred_corr_int(particles & vd, double dt, int & corr)
267 {
268  // particle iterator
269  auto part = vd.getDomainIterator();
270 
271  // iterate over all particles in the domain and integrate them
272  while (part.isNext())
273  {
274  auto a = part.get();
275 
276  if (!corr)
277  {
278  // store the values at time n:
279  vd.getProp<pos_prev>(a) = vd.getPos(a);
280  vd.getProp<vel_prev>(a) = vd.getProp<vel>(a);
281  // calculate intermediate values
282  Point<2, double> dx = 0.5*dt*vd.getProp<vel>(a);
283  vd.getPos(a)[0] += dx[0];
284  vd.getPos(a)[1] += dx[1];
285  // compute acceleration due to forces that act on particle
286  vd.getProp<vel>(a) = vd.getProp<vel>(a) + 0.5*dt*vd.getProp<force>(a);
287 
288  }
289  else
290  {
291  // correct the accelerations and velocities
292  Point<2, double> x = vd.getProp<pos_prev>(a) + dt*(vd.getProp<vel_prev>(a) + 0.5*dt*vd.getProp<force>(a));
293  vd.getPos(a)[0] = x[0];
294  vd.getPos(a)[1] = x[1];
295  vd.getProp<vel>(a) = vd.getProp<vel_prev>(a) + dt*vd.getProp<force>(a);
296 
297  }
298  ++part;
299  }
300  corr = !corr;
301 
302 }
303 
304 int main(int argc, char* argv[])
305 {
306  np = 0;
307  openfpm_init(&argc, &argv);
308  // initialize the domain
309  Box<2, double> domain({-l/2.0, -l/2.0}, {l/2.0, l/2.0});
310  size_t sz[2] = {(size_t)(l/dp),(size_t)(l/dp)};
311 
312  // periodic boundary conditions in both x- and y-direction
313  size_t bc[2] = {PERIODIC, PERIODIC};
314  // ghost layers required by geometric computing
315  // (finding the closest sample particle for a given particle)
316  Ghost<2, double> g(3.0*band_width);
317 
318  // initialize particle list
319  particles vd(0, domain, bc, g, DEC_GRAN(512));
320 
321  // give properties names for writing
322  openfpm::vector<std::string> names({"sdf","normal","curvature","cp","surf_flag","rho","p","f","vel","vel_prev"});
323  vd.setPropNames(names);
324 
325  auto particle_it = vd.getGridIterator(sz);
326 
327  // fill particle list using a grid iterator
328  while (particle_it.isNext())
329  {
330  double x = -l/2.0 + dp/2.0 + particle_it.get().get(0)*dp;
331  double y = -l/2.0 + dp/2.0 + particle_it.get().get(1)*dp;
332 
333  vd.add();
334  vd.getLastPos()[0] = x;
335  vd.getLastPos()[1] = y;
336  np++;
337 
338  // initialize properties of newly added particle
339  vd.template getLastProp<surf_flag>() = 0;
340 
341  double xcp = 0.0;
342  double ycp = 0.0;
343  // get initial SDF value by using algorithm computing distances to ellipse
344  double dist = DistancePointEllipse(A, B, abs(x), abs(y), xcp, ycp);
345  dist = -1.0*return_sign(1 - sqrt((x/A)*(x/A) + (y/B)*(y/B)))*dist;
346 
347  vd.template getLastProp<sdf>() = dist;
348  vd.template getLastProp<normal>()[0] = 0.0;
349  vd.template getLastProp<normal>()[1] = 0.0;
350  vd.template getLastProp<curvature>() = 0.0;
351  vd.template getLastProp<vel>()[0] = 0.0;
352  vd.template getLastProp<vel>()[1] = 0.0;
353  vd.template getLastProp<vel_prev>()[0] = 0.0;
354  vd.template getLastProp<vel_prev>()[1] = 0.0;
355  vd.template getLastProp<force>()[0] = 0.0;
356  vd.template getLastProp<force>()[1] = 0.0;
357  vd.template getLastProp<rho>() = 0.0;
358  vd.template getLastProp<p>() = 0.0;
359 
360  ++particle_it;
361  }
362 
363  Vcluster<> & v_cl = create_vcluster();
364  // Get the total number of particles
365  size_t tot_part = vd.size_local();
366  v_cl.sum(tot_part);
367  v_cl.execute();
368 
369  // compute individual particle masses
370  m = M/tot_part;
371  // print the CFL condition
372  std::cout<<"dt should be smaller than:\n"<<0.25*H/(c+3.0)<<"\t"<<0.125*rho_0*H*H/eta<<"\t"<<0.25*sqrt(rho_0*H*H*H/(2*M_PI*alpha))<<std::endl;
373 
374  vd.map();
375  vd.ghost_get<rho>();
376  // get the cell list for SPH operators (we use Wendland C2, so radius=2*smoothing length)
377  auto NN = vd.getCellList(2.0*H);
378  // get initial densities/volumes by computing density summation
379  density_summation(vd, NN);
380  // get initial pressures by evaluating the equation of state
381  EqOfState(vd);
382 
383  vd.deleteGhost();
384  // write initial distribution
385  vd.write(filebasename + "_init_before_redistancing");
386 
387  // set redistancing parameters for the initial geometric computing
388  // (to compute surface normals and curvatures)
389  Redist_options rdistoptions;
390  // average interparticle spacing
391  rdistoptions.H = dp;
392  // cutoff factor for regression neighborhood
393  rdistoptions.r_cutoff_factor = rdist_cutoff_factor;
394  // sampling radius of how far away sample particles can be detected
395  rdistoptions.sampling_radius = 0.75*band_width;
396  // tolerance for both the projections onto surface during sample particle generation
397  // and Newton algorithm for solving the constrained optimization problem
398  rdistoptions.tolerance = 1e-12;
399  // flags on which fields to compute
400  rdistoptions.compute_normals = 1;
401  rdistoptions.compute_curvatures = 1;
402  // flag for writing the sdf. Here, we know that the initial SDF is correct, so no need
403  // for writing yet.
404  rdistoptions.write_sdf = 0;
405  // the closest point is not known however, so why not write it
406  rdistoptions.write_cp = 1;
407  // polynomial degree for minter regression
408  rdistoptions.minter_poly_degree = 4;
409  // lp degree of polynomials
410  rdistoptions.minter_lp_degree = 1.0;
411  // compute number of coefficients for internal initialization
412  static constexpr unsigned int num_coeffs = minter_lp_degree_one_num_coeffs(2,4);
413  // initialize the redistancing object
415  // start recording time to measure performance
416  std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
417  // run redistancing
418  pcprdist_init.run_redistancing();
419  // write the results (same as beforehand, but now with surface normals, curvatures and closest points)
420  vd.write(filebasename + "_init");
421 
422  // change the redistancing parameters for repeated redistancing during dynamic simulation
423  // now we also want to write the SDF
424  rdistoptions.write_sdf = 1;
425 
426  // initialize parameters for the time loop
427  size_t write = 0;
428  size_t write_interim = 0;
429  size_t rdist = 0;
430  size_t largebw = 0;
431  size_t it = 0;
432  t = 0.0;
433  corr = 0;
434 
435  // enter main time loop
436  while (t <= t_end)
437  {
438  timer it_time;
439 
440  vd.map();
441  vd.ghost_get<rho>();
442  vd.updateCellList(NN);
443 
444  // Calculate particle densities and from these the pressure
445  density_summation(vd, NN);
446  EqOfState(vd);
447 
448  if (t != 0.0)
449  {
450  // with a frequency of 100, so period of 0.01, redistance with a larger sampling radius to
451  // ensure detection of particles entering the narrow band
452  if (largebw < t*100)
453  {
454  std::cout<<"Increased sampling radius for dt."<<std::endl;
455  rdistoptions.sampling_radius = 3.0*band_width;
456  }
457  // initialize redistancing object with current distribution of SDF values
459  // perform redistancing
460  pcprdist.run_redistancing();
461  // reset the sampling radius back to the original radius if the sampling radius was increased this
462  // time step
463  if (largebw < t*100)
464  {
465  rdistoptions.sampling_radius = 0.75*band_width;
466  largebw++;
467  }
468  rdist++;
469  }
470 
471  // Compute forces acting on particles
472  calc_forces(vd, NN);
473 
474  // Predictor step
475  it++;
476  pred_corr_int(vd, dt, corr);
477 
478  if (corr == 0) t +=dt;
479 
480  if (write_interim < t*10)
481  {
482  vd.save(filebasename + "_interim_save");
483  write_interim++;
484  }
485 
486  // write with a frequency of 100
487  if (write < t*100.0)//(write < t*400)
488  {
489  vd.deleteGhost();
490 
491  vd.write_frame(filebasename, write);
492  write++;
493 
494  if (v_cl.getProcessUnitID() == 0)
495  {std::cout << "TIME: " << t << std::endl;}
496  }
497 
498  }
499  std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
500  std::cout << "Time required for simulation = " << std::chrono::duration_cast<std::chrono::milliseconds>(end - begin).count() << "[ms]" << std::endl;
501  openfpm_finalize();
502 }
This class represent an N-dimensional box.
Definition: Box.hpp:60
Class for FAST cell list implementation.
Definition: CellList.hpp:558
Definition: Ghost.hpp:40
__device__ __host__ const T & get(unsigned int i) const
Get coordinate.
Definition: Point.hpp:172
__device__ __host__ T norm() const
norm of the vector
Definition: Point.hpp:231
void execute()
Execute all the requests.
void sum(T &num)
Sum the numbers across all processors and get the result.
size_t getProcessUnitID()
Get the process unit id.
Implementation of VCluster class.
Definition: VCluster.hpp:59
Class for reinitializing a level-set function into a signed distance function using the Closest-Point...
Definition: particle_cp.hpp:64
Class for cpu time benchmarking.
Definition: timer.hpp:28
Distributed vector.
size_t size_local() const
return the local size of the vector
bool write_frame(std::string out, size_t iteration, int opt=VTK_WRITER)
Output particle position and properties.
auto getProp(vect_dist_key_dx vec_key) -> decltype(vPrp.template get< id >(vec_key.getKey()))
Get the property of an element.
void updateCellList(CellL &cellList, bool no_se3=false)
Update a cell list using the stored particles.
void setPropNames(const openfpm::vector< std::string > &names)
Set the properties names.
grid_dist_id_iterator_dec< Decomposition > getGridIterator(const size_t(&sz)[dim])
auto getPos(vect_dist_key_dx vec_key) -> decltype(vPos.template get< 0 >(vec_key.getKey()))
Get the position of an element.
vector_dist_iterator getDomainIterator() const
Get an iterator that traverse the particles in the domain.
void ghost_get(size_t opt=WITH_POSITION)
It synchronize the properties and position of the ghost particles.
void map(size_t opt=NONE)
It move all the particles that does not belong to the local processor to the respective processor.
bool write(std::string out, int opt=VTK_WRITER)
Output particle position and properties.
void save(const std::string &filename) const
Save the distributed vector on HDF5 file.
void add()
Add local particle.
CellList_type getCellList(St r_cut, size_t opt=CL_NON_SYMMETRIC|CL_LINEAR_CELL_KEYS, bool no_se3=false, float ghostEnlargeFactor=1.013)
Construct a cell list starting from the stored particles.
void deleteGhost()
Delete the particles on the ghost.
auto getLastPos() -> decltype(vPos.template get< 0 >(0))
Get the position of the last element.
KeyT const ValueT ValueT OffsetIteratorT OffsetIteratorT int
[in] The number of segments that comprise the sorting data
Structure to bundle options for redistancing.
Definition: particle_cp.hpp:33
aggregate of properties, from a list of object if create a struct that follow the OPENFPM native stru...
Definition: aggregate.hpp:221
[Definition of the system]