OpenFPM_pdata  4.1.0
Project that contain the implementation of distributed structures
main_vic_petsc.cpp
1 
105 #include "config.h"
106 
107 #ifdef HAVE_PETSC
108 
109 //#define SE_CLASS1
110 //#define PRINT_STACKTRACE
111 
113 
114 #include "interpolation/interpolation.hpp"
115 #include "Grid/grid_dist_id.hpp"
116 #include "Vector/vector_dist.hpp"
117 #include "Matrix/SparseMatrix.hpp"
118 #include "Vector/Vector.hpp"
119 #include "FiniteDifference/FDScheme.hpp"
120 #include "Solvers/petsc_solver.hpp"
121 #include "interpolation/mp4_kernel.hpp"
122 #include "Solvers/petsc_solver_AMG_report.hpp"
123 
124 constexpr int x = 0;
125 constexpr int y = 1;
126 constexpr int z = 2;
127 
128 // The type of the grids
130 
131 // The type of the particles
133 
134 // radius of the torus
135 float ringr1 = 1.0;
136 // radius of the core of the torus
137 float sigma = 1.0/3.523;
138 // Reynold number (If you want to use 7500.0 you have to use a grid 1600x400x400)
139 //float tgtre = 7500.0;
140 float tgtre = 3000.0;
141 
142 // Kinematic viscosity
143 float nu = 1.0/tgtre;
144 
145 // Time step
146 // float dt = 0.0025 for Re 7500
147 float dt = 0.0125;
148 
149 #ifdef TEST_RUN
150 const unsigned int nsteps = 10;
151 #else
152 const unsigned int nsteps = 10001;
153 #endif
154 
155 // All the properties by index
156 constexpr unsigned int vorticity = 0;
157 constexpr unsigned int velocity = 0;
158 constexpr unsigned int p_vel = 1;
159 constexpr int rhs_part = 2;
160 constexpr unsigned int old_vort = 3;
161 constexpr unsigned int old_pos = 4;
162 
164 
165 template<typename grid> void calc_and_print_max_div_and_int(grid & g_vort)
166 {
168 
169  g_vort.template ghost_get<vorticity>();
170  auto it5 = g_vort.getDomainIterator();
171 
172  double max_vort = 0.0;
173 
174  double int_vort[3] = {0.0,0.0,0.0};
175 
176  while (it5.isNext())
177  {
178  auto key = it5.get();
179 
180  double tmp;
181 
182  tmp = 1.0f/g_vort.spacing(x)/2.0f*(g_vort.template get<vorticity>(key.move(x,1))[x] - g_vort.template get<vorticity>(key.move(x,-1))[x] ) +
183  1.0f/g_vort.spacing(y)/2.0f*(g_vort.template get<vorticity>(key.move(y,1))[y] - g_vort.template get<vorticity>(key.move(y,-1))[y] ) +
184  1.0f/g_vort.spacing(z)/2.0f*(g_vort.template get<vorticity>(key.move(z,1))[z] - g_vort.template get<vorticity>(key.move(z,-1))[z] );
185 
186  int_vort[x] += g_vort.template get<vorticity>(key)[x];
187  int_vort[y] += g_vort.template get<vorticity>(key)[y];
188  int_vort[z] += g_vort.template get<vorticity>(key)[z];
189 
190  if (tmp > max_vort)
191  max_vort = tmp;
192 
193  ++it5;
194  }
195 
196  Vcluster<> & v_cl = create_vcluster();
197  v_cl.max(max_vort);
198  v_cl.sum(int_vort[0]);
199  v_cl.sum(int_vort[1]);
200  v_cl.sum(int_vort[2]);
201  v_cl.execute();
202 
203  if (v_cl.getProcessUnitID() == 0)
204  {std::cout << "Max div for vorticity " << max_vort << " Integral: " << int_vort[0] << " " << int_vort[1] << " " << int_vort[2] << std::endl;}
205 
207 }
208 
232 
234 /*
235  * gr is the grid where we are initializing the vortex ring
236  * domain is the simulation domain
237  *
238  */
239 void init_ring(grid_type & gr, const Box<3,float> & domain)
240 {
241  // To add some noise to the vortex ring we create two random
242  // vector
243  constexpr int nk = 32;
244 
245  float ak[nk];
246  float bk[nk];
247 
248  for (size_t i = 0 ; i < nk ; i++)
249  {
250  ak[i] = rand()/RAND_MAX;
251  bk[i] = rand()/RAND_MAX;
252  }
253 
254  // We calculate the circuitation gamma
255  float gamma = nu * tgtre;
256  float rinv2 = 1.0f/(sigma*sigma);
257  float max_vorticity = gamma*rinv2/M_PI;
258 
259  // We go through the grid to initialize the vortex
260  auto it = gr.getDomainIterator();
261 
262  while (it.isNext())
263  {
264  auto key_d = it.get();
265  auto key = it.getGKey(key_d);
266 
267  float tx = (key.get(x)-2)*gr.spacing(x) + domain.getLow(x);
268  float ty = (key.get(y)-2)*gr.spacing(y) + domain.getLow(y);
269  float tz = (key.get(z)-2)*gr.spacing(z) + domain.getLow(z);
270  float theta1 = atan2((ty-domain.getHigh(1)/2.0f),(tz-domain.getHigh(2)/2.0f));
271 
272 
273  float rad1r = sqrt((ty-domain.getHigh(1)/2.0f)*(ty-domain.getHigh(1)/2.0f) + (tz-domain.getHigh(2)/2.0f)*(tz-domain.getHigh(2)/2.0f)) - ringr1;
274  float rad1t = tx - 1.0f;
275  float rad1sq = rad1r*rad1r + rad1t*rad1t;
276  float radstr = -exp(-rad1sq*rinv2)*rinv2*gamma/M_PI;
277  gr.template get<vorticity>(key_d)[x] = 0.0f;
278  gr.template get<vorticity>(key_d)[y] = -radstr * cos(theta1);
279  gr.template get<vorticity>(key_d)[z] = radstr * sin(theta1);
280 
281  // kill the axis term
282 
283  float rad1r_ = sqrt((ty-domain.getHigh(1)/2.0f)*(ty-domain.getHigh(1)/2.0f) + (tz-domain.getHigh(2)/2.0f)*(tz-domain.getHigh(2)/2.0f)) + ringr1;
284  float rad1sqTILDA = rad1sq*rinv2;
285  radstr = exp(-rad1sq*rinv2)*rinv2*gamma/M_PI;
286  gr.template get<vorticity>(key_d)[x] = 0.0f;
287  gr.template get<vorticity>(key_d)[y] = -radstr * cos(theta1);
288  gr.template get<vorticity>(key_d)[z] = radstr * sin(theta1);
289 
290  ++it;
291  }
292 }
293 
295 
297 
298 // Specification of the poisson equation for the helmotz-hodge projection
299 // 3D (dims = 3). The field is a scalar value (nvar = 1), bournary are periodic
300 // type of the the space is float. The grid type that store \psi
301 // The others indicate which kind of Matrix to use to construct the linear system and
302 // which kind of vector to construct for the right hand side. Here we use a PETSC Sparse Matrix
303 // and PETSC vector. NORMAL_GRID indicate that is a standard grid (non-staggered)
304 struct poisson_nn_helm
305 {
307  static const unsigned int dims = 3;
309  static const unsigned int nvar = 1;
311  static const bool boundary[];
313  typedef float stype;
321  static const int grid_type = NORMAL_GRID;
322 };
323 
325 const bool poisson_nn_helm::boundary[] = {PERIODIC,PERIODIC,PERIODIC};
326 
328 
329 
438 /*
439  * gr vorticity grid where we apply the correction
440  * domain simulation domain
441  *
442  */
443 void helmotz_hodge_projection(grid_type & gr, const Box<3,float> & domain, petsc_solver<double> & solver, petsc_solver<double>::return_type & x_ , bool init)
444 {
446 
448 
449  // Convenient constant
450  constexpr unsigned int phi = 0;
451 
452  // We define a field phi_f
453  typedef Field<phi,poisson_nn_helm> phi_f;
454 
455  // We assemble the poisson equation doing the
456  // poisson of the Laplacian of phi using Laplacian
457  // central scheme (where the both derivative use central scheme, not
458  // forward composed backward like usually)
460 
462 
464 
465  // ghost get
466  gr.template ghost_get<vorticity>();
467 
468  // ghost size of the psi function
469  Ghost<3,long int> g(2);
470 
471  // Here we create a distributed grid to store the result of the helmotz projection
473 
474  // Calculate the divergence of the vortex field
475  auto it = gr.getDomainIterator();
476 
477  while (it.isNext())
478  {
479  auto key = it.get();
480 
481  psi.template get<phi>(key) = 1.0f/gr.spacing(x)/2.0f*(gr.template get<vorticity>(key.move(x,1))[x] - gr.template get<vorticity>(key.move(x,-1))[x] ) +
482  1.0f/gr.spacing(y)/2.0f*(gr.template get<vorticity>(key.move(y,1))[y] - gr.template get<vorticity>(key.move(y,-1))[y] ) +
483  1.0f/gr.spacing(z)/2.0f*(gr.template get<vorticity>(key.move(z,1))[z] - gr.template get<vorticity>(key.move(z,-1))[z] );
484 
485  ++it;
486  }
487 
489 
490 
491  calc_and_print_max_div_and_int(gr);
492 
493 
495 
496  // In order to create a matrix that represent the poisson equation we have to indicate
497  // we have to indicate the maximum extension of the stencil and we we need an extra layer
498  // of points in case we have to impose particular boundary conditions.
499  Ghost<3,long int> stencil_max(2);
500 
501  // Here we define our Finite difference disctetization scheme object
502  FDScheme<poisson_nn_helm> fd(stencil_max, domain, psi);
503 
504  // impose the poisson equation, using right hand side psi on the full domain (psi.getDomainIterator)
505  // the template paramereter instead define which property of phi carry the righ-hand-side
506  // in this case phi has only one property, so the property 0 carry the right hand side
507  fd.template impose_dit<0>(poisson(),psi,psi.getDomainIterator());
508 
510 
512 
513  timer tm_solve;
514  if (init == true)
515  {
516  // Set the conjugate-gradient as method to solve the linear solver
517  solver.setSolver(KSPBCGS);
518 
519  // Set the absolute tolerance to determine that the method is converged
520  solver.setAbsTol(0.0001);
521 
522  // Set the maximum number of iterations
523  solver.setMaxIter(500);
524 
525 #ifdef USE_MULTIGRID
526 
527  solver.setPreconditioner(PCHYPRE_BOOMERAMG);
528  solver.setPreconditionerAMG_nl(6);
529  solver.setPreconditionerAMG_maxit(1);
530  solver.setPreconditionerAMG_relax("SOR/Jacobi");
531  solver.setPreconditionerAMG_cycleType("V",0,4);
533  solver.setPreconditionerAMG_coarsen("HMIS");
534 
535 
536 #endif
537 
538  tm_solve.start();
539 
540  // Give to the solver A and b, return x, the solution
541  solver.solve(fd.getA(),x_,fd.getB());
542 
543  tm_solve.stop();
544  }
545  else
546  {
547  tm_solve.start();
548  solver.solve(x_,fd.getB());
549  tm_solve.stop();
550  }
551 
552  // copy the solution x to the grid psi
553  fd.template copy<phi>(x_,psi);
554 
556 
558 
559  psi.template ghost_get<phi>();
560 
561  // Correct the vorticity to make it divergence free
562 
563  auto it2 = gr.getDomainIterator();
564 
565  while (it2.isNext())
566  {
567  auto key = it2.get();
568 
569  gr.template get<vorticity>(key)[x] -= 1.0f/2.0f/psi.spacing(x)*(psi.template get<phi>(key.move(x,1))-psi.template get<phi>(key.move(x,-1)));
570  gr.template get<vorticity>(key)[y] -= 1.0f/2.0f/psi.spacing(y)*(psi.template get<phi>(key.move(y,1))-psi.template get<phi>(key.move(y,-1)));
571  gr.template get<vorticity>(key)[z] -= 1.0f/2.0f/psi.spacing(z)*(psi.template get<phi>(key.move(z,1))-psi.template get<phi>(key.move(z,-1)));
572 
573  ++it2;
574  }
575 
577 
578  calc_and_print_max_div_and_int(gr);
579 }
580 
581 
597 
599 void remesh(particles_type & vd, grid_type & gr,Box<3,float> & domain)
600 {
601  // Remove all particles because we reinitialize in a grid like position
602  vd.clear();
603 
604  // Get a grid iterator
605  auto git = vd.getGridIterator(gr.getGridInfo().getSize());
606 
607  // For each grid point
608  while (git.isNext())
609  {
610  // Get the grid point in global coordinates (key). p is in local coordinates
611  auto p = git.get();
612  auto key = git.get_dist();
613 
614  // Add the particle
615  vd.add();
616 
617  // Assign the position to the particle in a grid like way
618  vd.getLastPos()[x] = gr.spacing(x)*p.get(x) + domain.getLow(x);
619  vd.getLastPos()[y] = gr.spacing(y)*p.get(y) + domain.getLow(y);
620  vd.getLastPos()[z] = gr.spacing(z)*p.get(z) + domain.getLow(z);
621 
622  // Initialize the vorticity
623  vd.template getLastProp<vorticity>()[x] = gr.template get<vorticity>(key)[x];
624  vd.template getLastProp<vorticity>()[y] = gr.template get<vorticity>(key)[y];
625  vd.template getLastProp<vorticity>()[z] = gr.template get<vorticity>(key)[z];
626 
627  // next grid point
628  ++git;
629  }
630 
631  // redistribute the particles
632  vd.map();
633 }
634 
636 
637 
690 void comp_vel(Box<3,float> & domain, grid_type & g_vort,grid_type & g_vel, petsc_solver<double>::return_type (& phi_s)[3],petsc_solver<double> & solver)
691 {
693 
694  // Convenient constant
695  constexpr unsigned int phi = 0;
696 
697  // We define a field phi_f
698  typedef Field<phi,poisson_nn_helm> phi_f;
699 
700  // We assemble the poisson equation doing the
701  // poisson of the Laplacian of phi using Laplacian
702  // central scheme (where the both derivative use central scheme, not
703  // forward composed backward like usually)
705 
707 
708  // Maximum size of the stencil
709  Ghost<3,long int> g(2);
710 
711  // Here we create a distributed grid to store the result
714 
715  // We calculate and print the maximum divergence of the vorticity
716  // and the integral of it
717  calc_and_print_max_div_and_int(g_vort);
718 
719  // For each component solve a poisson
720  for (size_t i = 0 ; i < 3 ; i++)
721  {
723 
724  // Copy the vorticity component i in gr_ps
725  auto it2 = gr_ps.getDomainIterator();
726 
727  // calculate the velocity from the curl of phi
728  while (it2.isNext())
729  {
730  auto key = it2.get();
731 
732  // copy
733  gr_ps.get<vorticity>(key) = g_vort.template get<vorticity>(key)[i];
734 
735  ++it2;
736  }
737 
738  // Maximum size of the stencil
739  Ghost<3,long int> stencil_max(2);
740 
741  // Finite difference scheme
742  FDScheme<poisson_nn_helm> fd(stencil_max, domain, gr_ps);
743 
744  // impose the poisson equation using gr_ps = vorticity for the right-hand-side (on the full domain)
745  fd.template impose_dit<phi>(poisson(),gr_ps,gr_ps.getDomainIterator());
746 
747  solver.setAbsTol(0.01);
748 
749  // Get the vector that represent the right-hand-side
750  Vector<double,PETSC_BASE> & b = fd.getB();
751 
752  timer tm_solve;
753  tm_solve.start();
754 
755  // Give to the solver A and b in this case we are giving
756  // an intitial guess phi_s calculated in the previous
757  // time step
758  solver.solve(phi_s[i],b);
759 
760  tm_solve.stop();
761 
762  // Calculate the residual
763 
764  solError serr;
765  serr = solver.get_residual_error(phi_s[i],b);
766 
767  Vcluster<> & v_cl = create_vcluster();
768  if (v_cl.getProcessUnitID() == 0)
769  {std::cout << "Solved component " << i << " Error: " << serr.err_inf << std::endl;}
770 
771  // copy the solution to grid
772  fd.template copy<phi>(phi_s[i],gr_ps);
773 
775 
777 
778  auto it3 = gr_ps.getDomainIterator();
779 
780  // calculate the velocity from the curl of phi
781  while (it3.isNext())
782  {
783  auto key = it3.get();
784 
785  phi_v.get<velocity>(key)[i] = gr_ps.get<phi>(key);
786 
787  ++it3;
788  }
789 
791  }
792 
794 
795  phi_v.ghost_get<phi>();
796 
797  float inv_dx = 0.5f / g_vort.spacing(0);
798  float inv_dy = 0.5f / g_vort.spacing(1);
799  float inv_dz = 0.5f / g_vort.spacing(2);
800 
801  auto it3 = phi_v.getDomainIterator();
802 
803  // calculate the velocity from the curl of phi
804  while (it3.isNext())
805  {
806  auto key = it3.get();
807 
808  float phi_xy = (phi_v.get<phi>(key.move(y,1))[x] - phi_v.get<phi>(key.move(y,-1))[x])*inv_dy;
809  float phi_xz = (phi_v.get<phi>(key.move(z,1))[x] - phi_v.get<phi>(key.move(z,-1))[x])*inv_dz;
810  float phi_yx = (phi_v.get<phi>(key.move(x,1))[y] - phi_v.get<phi>(key.move(x,-1))[y])*inv_dx;
811  float phi_yz = (phi_v.get<phi>(key.move(z,1))[y] - phi_v.get<phi>(key.move(z,-1))[y])*inv_dz;
812  float phi_zx = (phi_v.get<phi>(key.move(x,1))[z] - phi_v.get<phi>(key.move(x,-1))[z])*inv_dx;
813  float phi_zy = (phi_v.get<phi>(key.move(y,1))[z] - phi_v.get<phi>(key.move(y,-1))[z])*inv_dy;
814 
815  g_vel.template get<velocity>(key)[x] = phi_zy - phi_yz;
816  g_vel.template get<velocity>(key)[y] = phi_xz - phi_zx;
817  g_vel.template get<velocity>(key)[z] = phi_yx - phi_xy;
818 
819  ++it3;
820  }
821 
823 }
824 
825 
826 template<unsigned int prp> void set_zero(grid_type & gr)
827 {
828  auto it = gr.getDomainGhostIterator();
829 
830  // calculate the velocity from the curl of phi
831  while (it.isNext())
832  {
833  auto key = it.get();
834 
835  gr.template get<prp>(key)[0] = 0.0;
836  gr.template get<prp>(key)[1] = 0.0;
837  gr.template get<prp>(key)[2] = 0.0;
838 
839  ++it;
840  }
841 }
842 
843 
844 template<unsigned int prp> void set_zero(particles_type & vd)
845 {
846  auto it = vd.getDomainIterator();
847 
848  // calculate the velocity from the curl of phi
849  while (it.isNext())
850  {
851  auto key = it.get();
852 
853  vd.template getProp<prp>(key)[0] = 0.0;
854  vd.template getProp<prp>(key)[1] = 0.0;
855  vd.template getProp<prp>(key)[2] = 0.0;
856 
857  ++it;
858  }
859 }
860 
874 
876 // Calculate the right hand side of the vorticity formulation
877 template<typename grid> void calc_rhs(grid & g_vort, grid & g_vel, grid & g_dwp)
878 {
879  // usefull constant
880  constexpr int rhs = 0;
881 
882  // calculate several pre-factors for the stencil finite
883  // difference
884  float fac1 = 2.0f*nu/(g_vort.spacing(0)*g_vort.spacing(0));
885  float fac2 = 2.0f*nu/(g_vort.spacing(1)*g_vort.spacing(1));
886  float fac3 = 2.0f*nu/(g_vort.spacing(2)*g_vort.spacing(2));
887 
888  float fac4 = 0.5f/(g_vort.spacing(0));
889  float fac5 = 0.5f/(g_vort.spacing(1));
890  float fac6 = 0.5f/(g_vort.spacing(2));
891 
892  auto it = g_dwp.getDomainIterator();
893 
894  g_vort.template ghost_get<vorticity>();
895 
896  while (it.isNext())
897  {
898  auto key = it.get();
899 
900  g_dwp.template get<rhs>(key)[x] = fac1*(g_vort.template get<vorticity>(key.move(x,1))[x]+g_vort.template get<vorticity>(key.move(x,-1))[x])+
901  fac2*(g_vort.template get<vorticity>(key.move(y,1))[x]+g_vort.template get<vorticity>(key.move(y,-1))[x])+
902  fac3*(g_vort.template get<vorticity>(key.move(z,1))[x]+g_vort.template get<vorticity>(key.move(z,-1))[x])-
903  2.0f*(fac1+fac2+fac3)*g_vort.template get<vorticity>(key)[x]+
904  fac4*g_vort.template get<vorticity>(key)[x]*
905  (g_vel.template get<velocity>(key.move(x,1))[x] - g_vel.template get<velocity>(key.move(x,-1))[x])+
906  fac5*g_vort.template get<vorticity>(key)[y]*
907  (g_vel.template get<velocity>(key.move(y,1))[x] - g_vel.template get<velocity>(key.move(y,-1))[x])+
908  fac6*g_vort.template get<vorticity>(key)[z]*
909  (g_vel.template get<velocity>(key.move(z,1))[x] - g_vel.template get<velocity>(key.move(z,-1))[x]);
910 
911  g_dwp.template get<rhs>(key)[y] = fac1*(g_vort.template get<vorticity>(key.move(x,1))[y]+g_vort.template get<vorticity>(key.move(x,-1))[y])+
912  fac2*(g_vort.template get<vorticity>(key.move(y,1))[y]+g_vort.template get<vorticity>(key.move(y,-1))[y])+
913  fac3*(g_vort.template get<vorticity>(key.move(z,1))[y]+g_vort.template get<vorticity>(key.move(z,-1))[y])-
914  2.0f*(fac1+fac2+fac3)*g_vort.template get<vorticity>(key)[y]+
915  fac4*g_vort.template get<vorticity>(key)[x]*
916  (g_vel.template get<velocity>(key.move(x,1))[y] - g_vel.template get<velocity>(key.move(x,-1))[y])+
917  fac5*g_vort.template get<vorticity>(key)[y]*
918  (g_vel.template get<velocity>(key.move(y,1))[y] - g_vel.template get<velocity>(key.move(y,-1))[y])+
919  fac6*g_vort.template get<vorticity>(key)[z]*
920  (g_vel.template get<velocity>(key.move(z,1))[y] - g_vel.template get<velocity>(key.move(z,-1))[y]);
921 
922 
923  g_dwp.template get<rhs>(key)[z] = fac1*(g_vort.template get<vorticity>(key.move(x,1))[z]+g_vort.template get<vorticity>(key.move(x,-1))[z])+
924  fac2*(g_vort.template get<vorticity>(key.move(y,1))[z]+g_vort.template get<vorticity>(key.move(y,-1))[z])+
925  fac3*(g_vort.template get<vorticity>(key.move(z,1))[z]+g_vort.template get<vorticity>(key.move(z,-1))[z])-
926  2.0f*(fac1+fac2+fac3)*g_vort.template get<vorticity>(key)[z]+
927  fac4*g_vort.template get<vorticity>(key)[x]*
928  (g_vel.template get<velocity>(key.move(x,1))[z] - g_vel.template get<velocity>(key.move(x,-1))[z])+
929  fac5*g_vort.template get<vorticity>(key)[y]*
930  (g_vel.template get<velocity>(key.move(y,1))[z] - g_vel.template get<velocity>(key.move(y,-1))[z])+
931  fac6*g_vort.template get<vorticity>(key)[z]*
932  (g_vel.template get<velocity>(key.move(z,1))[z] - g_vel.template get<velocity>(key.move(z,-1))[z]);
933 
934  ++it;
935  }
936 }
937 
939 
958 
960 void rk_step1(particles_type & particles)
961 {
962  auto it = particles.getDomainIterator();
963 
964  while (it.isNext())
965  {
966  auto key = it.get();
967 
968  // Save the old vorticity
969  particles.getProp<old_vort>(key)[x] = particles.getProp<vorticity>(key)[x];
970  particles.getProp<old_vort>(key)[y] = particles.getProp<vorticity>(key)[y];
971  particles.getProp<old_vort>(key)[z] = particles.getProp<vorticity>(key)[z];
972 
973  particles.getProp<vorticity>(key)[x] = particles.getProp<vorticity>(key)[x] + 0.5f * dt * particles.getProp<rhs_part>(key)[x];
974  particles.getProp<vorticity>(key)[y] = particles.getProp<vorticity>(key)[y] + 0.5f * dt * particles.getProp<rhs_part>(key)[y];
975  particles.getProp<vorticity>(key)[z] = particles.getProp<vorticity>(key)[z] + 0.5f * dt * particles.getProp<rhs_part>(key)[z];
976 
977  ++it;
978  }
979 
980  auto it2 = particles.getDomainIterator();
981 
982  while (it2.isNext())
983  {
984  auto key = it2.get();
985 
986  // Save the old position
987  particles.getProp<old_pos>(key)[x] = particles.getPos(key)[x];
988  particles.getProp<old_pos>(key)[y] = particles.getPos(key)[y];
989  particles.getProp<old_pos>(key)[z] = particles.getPos(key)[z];
990 
991  particles.getPos(key)[x] = particles.getPos(key)[x] + 0.5f * dt * particles.getProp<p_vel>(key)[x];
992  particles.getPos(key)[y] = particles.getPos(key)[y] + 0.5f * dt * particles.getProp<p_vel>(key)[y];
993  particles.getPos(key)[z] = particles.getPos(key)[z] + 0.5f * dt * particles.getProp<p_vel>(key)[z];
994 
995  ++it2;
996  }
997 
998  particles.map();
999 }
1000 
1002 
1021 
1023 void rk_step2(particles_type & particles)
1024 {
1025  auto it = particles.getDomainIterator();
1026 
1027  while (it.isNext())
1028  {
1029  auto key = it.get();
1030 
1031  particles.getProp<vorticity>(key)[x] = particles.getProp<old_vort>(key)[x] + dt * particles.getProp<rhs_part>(key)[x];
1032  particles.getProp<vorticity>(key)[y] = particles.getProp<old_vort>(key)[y] + dt * particles.getProp<rhs_part>(key)[y];
1033  particles.getProp<vorticity>(key)[z] = particles.getProp<old_vort>(key)[z] + dt * particles.getProp<rhs_part>(key)[z];
1034 
1035  ++it;
1036  }
1037 
1038  auto it2 = particles.getDomainIterator();
1039 
1040  while (it2.isNext())
1041  {
1042  auto key = it2.get();
1043 
1044  particles.getPos(key)[x] = particles.getProp<old_pos>(key)[x] + dt * particles.getProp<p_vel>(key)[x];
1045  particles.getPos(key)[y] = particles.getProp<old_pos>(key)[y] + dt * particles.getProp<p_vel>(key)[y];
1046  particles.getPos(key)[z] = particles.getProp<old_pos>(key)[z] + dt * particles.getProp<p_vel>(key)[z];
1047 
1048  ++it2;
1049  }
1050 
1051  particles.map();
1052 }
1053 
1054 
1055 
1057 
1078 template<typename grid, typename vector> void do_step(vector & particles,
1079  grid & g_vort,
1080  grid & g_vel,
1081  grid & g_dvort,
1082  Box<3,float> & domain,
1084  petsc_solver<double>::return_type (& phi_s)[3],
1085  petsc_solver<double> & solver)
1086 {
1087  constexpr int rhs = 0;
1088 
1090 
1091  set_zero<vorticity>(g_vort);
1092  inte.template p2m<vorticity,vorticity>(particles,g_vort);
1093 
1094  g_vort.template ghost_put<add_,vorticity>();
1095 
1097 
1099 
1100  // Calculate velocity from vorticity
1101  comp_vel(domain,g_vort,g_vel,phi_s,solver);
1102  calc_rhs(g_vort,g_vel,g_dvort);
1103 
1105 
1107 
1108  g_dvort.template ghost_get<rhs>();
1109  g_vel.template ghost_get<velocity>();
1112  inte.template m2p<rhs,rhs_part>(g_dvort,particles);
1113  inte.template m2p<velocity,p_vel>(g_vel,particles);
1114 
1116 }
1117 
1118 template<typename vector, typename grid> void check_point_and_save(vector & particles,
1119  grid & g_vort,
1120  grid & g_vel,
1121  grid & g_dvort,
1122  size_t i)
1123 {
1124  Vcluster<> & v_cl = create_vcluster();
1125 
1126  if (v_cl.getProcessingUnits() < 24)
1127  {
1128  particles.write("part_out_" + std::to_string(i),VTK_WRITER | FORMAT_BINARY);
1129  g_vort.template ghost_get<vorticity>();
1130  g_vel.template ghost_get<velocity>();
1131  g_vel.write_frame("grid_velocity",i, VTK_WRITER | FORMAT_BINARY);
1132  g_vort.write_frame("grid_vorticity",i, VTK_WRITER | FORMAT_BINARY);
1133  }
1134 
1135  // In order to reduce the size of the saved data we apply a threshold.
1136  // We only save particles with vorticity higher than 0.1
1138 
1139  auto it_s = particles.getDomainIterator();
1140 
1141  while (it_s.isNext())
1142  {
1143  auto p = it_s.get();
1144 
1145  float vort_magn = sqrt(particles.template getProp<vorticity>(p)[0] * particles.template getProp<vorticity>(p)[0] +
1146  particles.template getProp<vorticity>(p)[1] * particles.template getProp<vorticity>(p)[1] +
1147  particles.template getProp<vorticity>(p)[2] * particles.template getProp<vorticity>(p)[2]);
1148 
1149  if (vort_magn < 0.1)
1150  {
1151  ++it_s;
1152  continue;
1153  }
1154 
1155  part_save.add();
1156 
1157  part_save.getLastPos()[0] = particles.getPos(p)[0];
1158  part_save.getLastPos()[1] = particles.getPos(p)[1];
1159  part_save.getLastPos()[2] = particles.getPos(p)[2];
1160 
1161  part_save.template getLastProp<0>() = vort_magn;
1162 
1163  ++it_s;
1164  }
1165 
1166  part_save.map();
1167 
1168  // Save and HDF5 file for checkpoint restart
1169  particles.save("check_point");
1170 }
1171 
1190 int main(int argc, char* argv[])
1191 {
1192  // Initialize
1193  openfpm_init(&argc,&argv);
1194  {
1195  // Domain, a rectangle
1196  // For the grid 1600x400x400 use
1197  // Box<3,float> domain({0.0,0.0,0.0},{22.0,5.57,5.57});
1198  Box<3,float> domain({0.0,0.0,0.0},{3.57,3.57,3.57});
1199 
1200  // Ghost (Not important in this case but required)
1201  Ghost<3,long int> g(2);
1202 
1203  // Grid points on x=128 y=64 z=64
1204  // if we use Re = 7500
1205  // long int sz[] = {1600,400,400};
1206  long int sz[] = {96,96,96};
1207  size_t szu[] = {(size_t)sz[0],(size_t)sz[1],(size_t)sz[2]};
1208 
1209  periodicity<3> bc = {{PERIODIC,PERIODIC,PERIODIC}};
1210 
1211  grid_type g_vort(szu,domain,g,bc);
1212  grid_type g_vel(g_vort.getDecomposition(),szu,g);
1213  grid_type g_dvort(g_vort.getDecomposition(),szu,g);
1214  particles_type particles(g_vort.getDecomposition(),0);
1215 
1216  // It store the solution to compute velocity
1217  // It is used as initial guess every time we call the solver
1218  Vector<double,PETSC_BASE> phi_s[3];
1220 
1221  // Parallel object
1222  Vcluster<> & v_cl = create_vcluster();
1223 
1224  // print out the spacing of the grid
1225  if (v_cl.getProcessUnitID() == 0)
1226  {std::cout << "SPACING " << g_vort.spacing(0) << " " << g_vort.spacing(1) << " " << g_vort.spacing(2) << std::endl;}
1227 
1228  // initialize the ring step 1
1229  init_ring(g_vort,domain);
1230 
1231  x_.resize(g_vort.size(),g_vort.getLocalDomainSize());
1232  x_.setZero();
1233 
1234  // Create a PETSC solver to get the solution x
1235  petsc_solver<double> solver;
1236 
1237  // Do the helmotz projection step 2
1238  helmotz_hodge_projection(g_vort,domain,solver,x_,true);
1239 
1240  // initialize the particle on a mesh step 3
1241  remesh(particles,g_vort,domain);
1242 
1243  // Get the total number of particles
1244  size_t tot_part = particles.size_local();
1245  v_cl.sum(tot_part);
1246  v_cl.execute();
1247 
1248  // Now we set the size of phi_s
1249  phi_s[0].resize(g_vort.size(),g_vort.getLocalDomainSize());
1250  phi_s[1].resize(g_vort.size(),g_vort.getLocalDomainSize());
1251  phi_s[2].resize(g_vort.size(),g_vort.getLocalDomainSize());
1252 
1253  // And we initially set it to zero
1254  phi_s[0].setZero();
1255  phi_s[1].setZero();
1256  phi_s[2].setZero();
1257 
1258  // We create the interpolation object outside the loop cycles
1259  // to avoid to recreate it at every cycle. Internally this object
1260  // create some data-structure to optimize the conversion particle
1261  // position to sub-domain. If there are no re-balancing it is safe
1262  // to reuse-it
1264 
1265  // With more than 24 core we rely on the HDF5 checkpoint restart file
1266  if (v_cl.getProcessingUnits() < 24)
1267  {g_vort.write("grid_vorticity_init", VTK_WRITER | FORMAT_BINARY);}
1268 
1269 
1270  // Before entering the loop we check if we want to restart from
1271  // a previous simulation
1272  size_t i = 0;
1273 
1274  // if we have an argument
1275  if (argc > 1)
1276  {
1277  // take that argument
1278  std::string restart(argv[1]);
1279 
1280  // convert it into number
1281  i = std::stoi(restart);
1282 
1283  // load the file
1284  particles.load("check_point_" + std::to_string(i));
1285 
1286  // Print to inform that we are restarting from a
1287  // particular position
1288  if (v_cl.getProcessUnitID() == 0)
1289  {std::cout << "Restarting from " << i << std::endl;}
1290  }
1291 
1292  // Time Integration
1293  for ( ; i < nsteps ; i++)
1294  {
1295  // do step 4-5-6-7
1296  do_step(particles,g_vort,g_vel,g_dvort,domain,inte,phi_s,solver);
1297 
1298  // do step 8
1299  rk_step1(particles);
1300 
1301  // do step 9-10-11-12
1302  do_step(particles,g_vort,g_vel,g_dvort,domain,inte,phi_s,solver);
1303 
1304  // do step 13
1305  rk_step2(particles);
1306 
1307  // so step 14
1308  set_zero<vorticity>(g_vort);
1309  inte.template p2m<vorticity,vorticity>(particles,g_vort);
1310  g_vort.template ghost_put<add_,vorticity>();
1311 
1312  // helmotz-hodge projection
1313  helmotz_hodge_projection(g_vort,domain,solver,x_,false);
1314 
1315  remesh(particles,g_vort,domain);
1316 
1317  // print the step number
1318  if (v_cl.getProcessUnitID() == 0)
1319  {std::cout << "Step " << i << std::endl;}
1320 
1321  // every 100 steps write the output
1322  if (i % 100 == 0) {check_point_and_save(particles,g_vort,g_vel,g_dvort,i);}
1323 
1324  }
1325  }
1326 
1327  openfpm_finalize();
1328 }
1329 
1330 #else
1331 
1332 int main(int argc, char* argv[])
1333 {
1334  return 0;
1335 }
1336 
1337 #endif
void setPreconditioner(PCType type)
Set the preconditioner of the linear solver.
Decomposition & getDecomposition()
Get the decomposition.
size_t getProcessUnitID()
Get the process unit id.
Sparse Matrix implementation stub object when OpenFPM is compiled with no linear algebra support.
Definition: Vector.hpp:39
__device__ __host__ T getLow(int i) const
get the i-coordinate of the low bound interval of the box
Definition: Box.hpp:556
Definition: eq.hpp:82
static const unsigned int dims
Number of dimansions of the problem.
void setMaxIter(PetscInt n)
Set the maximum number of iteration for Krylov solvers.
In case T does not match the PETSC precision compilation create a stub structure.
auto getPos(vect_dist_key_dx vec_key) -> decltype(v_pos.template get< 0 >(vec_key.getKey()))
Get the position of an element.
SparseMatrix< double, int, PETSC_BASE > SparseMatrix_type
Sparse matrix used to sove the linear system (we use PETSC)
Meta-function to return a compatible zero-element.
auto getProp(vect_dist_key_dx vec_key) -> decltype(v_prp.template get< id >(vec_key.getKey()))
Get the property of an element.
It contain statistic of the error of the calculated solution.
auto get(const grid_dist_key_dx< dim, bg_key > &v1) const -> typename std::add_lvalue_reference< decltype(loc_grid.get(v1.getSub()).template get< p >(v1.getKey()))>::type
Get the reference of the selected element.
void setPreconditionerAMG_cycleType(const std::string &cycle_type, int sweep_up=-1, int sweep_dw=-1, int sweep_crs=-1)
It set the type of cycle and optionally the number of sweep.
float stype
type of the spatial coordinates
void setAbsTol(PetscReal abstol_)
Set the absolute tolerance as stop criteria.
St spacing(size_t i) const
Get the spacing of the grid in direction i.
Vector< double, PETSC_BASE > solve(SparseMatrix< double, int, PETSC_BASE > &A, const Vector< double, PETSC_BASE > &b, bool initial_guess=false)
Here we invert the matrix and solve the system.
void save(const std::string &filename) const
Save the distributed vector on HDF5 file.
Definition: Ghost.hpp:39
Vector< double, PETSC_BASE > Vector_type
Vector to solve the system (PETSC)
Implementation of VCluster class.
Definition: VCluster.hpp:58
void setPreconditionerAMG_maxit(int nit)
Set the maximum number of V or W cycle for algebraic-multi-grid.
void execute()
Execute all the requests.
grid_dist_iterator< dim, device_grid, decltype(device_grid::type_of_iterator()), FIXED > getDomainGhostIterator() const
It return an iterator that span the grid domain + ghost part.
auto getLastPos() -> decltype(v_pos.template get< 0 >(0))
Get the position of the last element.
void map(size_t opt=NONE)
It move all the particles that does not belong to the local processor to the respective processor.
Sparse Matrix implementation.
This is a distributed grid.
void resize(size_t row, size_t row_n)
stub resize
Definition: Vector.hpp:97
grid_dist_id_iterator_dec< Decomposition > getGridIterator(const size_t(&sz)[dim])
size_t size_local() const
return the local size of the vector
static const unsigned int nvar
We have only one scalar unknown.
const size_t(& getSize() const)[N]
Return the size of the grid as an array.
Definition: grid_sm.hpp:740
void setPreconditionerAMG_nl(int nl)
Set the number of levels for the algebraic-multigrid preconditioner.
This class is able to do Matrix inversion in parallel with PETSC solvers.
void setSolver(KSPType type)
Set the Petsc solver.
void start()
Start the timer.
Definition: timer.hpp:90
bool write(std::string out, int opt=VTK_WRITER)
Output particle position and properties.
size_t getProcessingUnits()
Get the total number of processors.
Laplacian second order on h (spacing)
Definition: Laplacian.hpp:22
void add()
Add local particle.
grid_dist_iterator< dim, device_grid, decltype(device_grid::type_of_subiterator()), FREE > getDomainIterator() const
It return an iterator that span the full grid domain (each processor span its local domain)
void sum(T &num)
Sum the numbers across all processors and get the result.
grid_dist_id< 3, float, aggregate< float > > b_grid
grid that store \psi
const grid_sm< dim, T > & getGridInfo() const
Get an object containing the grid informations.
Distributed vector.
void setPreconditionerAMG_coarsen(const std::string &type)
Set the coarsening method for the algebraic-multi-grid preconditioner.
void setPreconditionerAMG_relax(const std::string &type, int k=REL_ALL)
Set the relaxation method for the algebraic-multi-grid preconditioner.
void max(T &num)
Get the maximum number across all processors (or reduction with infinity norm)
vect_dist_key_dx get()
Get the actual key.
static const bool boundary[]
specify the boundary conditions
Finite Differences.
Definition: FDScheme.hpp:126
OutputIteratorT OffsetT ReductionOpT OuputT init
< [in] The initial value of the reduction
solError get_residual_error(SparseMatrix< double, int, PETSC_BASE > &A, const Vector< double, PETSC_BASE > &x, const Vector< double, PETSC_BASE > &b)
It return the resiual error.
__device__ __host__ T getHigh(int i) const
get the high interval of the box
Definition: Box.hpp:567
vector_dist_iterator getDomainIterator() const
Get an iterator that traverse the particles in the domain.
void clear()
remove all the elements
Decomposition & getDecomposition()
Get the object that store the information about the decomposition.
Boundary conditions.
Definition: common.hpp:21
void load(const std::string &filename)
Load the distributed vector from an HDF5 file.
Class for cpu time benchmarking.
Definition: timer.hpp:27
void setPreconditionerAMG_coarsenNodalType(int norm)
Set the block coarsening norm type.
grid_key_dx< Decomposition::dims > get()
Get the actual global key of the grid.
Main class for interpolation Particle to mest p2m and Mesh to particle m2p.
void stop()
Stop the timer.
Definition: timer.hpp:119
PetscReal err_inf
infinity norm of the error