OpenFPM  5.2.0
Project that contain the implementation of distributed structures
main.cpp
1 //
2 // Created by Abhinav Singh @absingh on 26/04/2021
3 //
72 // Include Vector Expression,Vector Expressions for Subset,DCPSE,Odeint header files
74 #include "Operators/Vector/vector_dist_operators.hpp"
75 #include "Vector/vector_dist_subset.hpp"
76 #include "DCPSE/DCPSE_op/DCPSE_op.hpp"
77 #include "OdeIntegrators/OdeIntegrators.hpp"
79 
80 
102 
103 constexpr int x = 0;
104 constexpr int y = 1;
105 
106 double dt;
107 
108 void *PointerDistGlobal, *PointerDistSubset;
109 
114 
144 template<typename DXX,typename DYY>
146 {
147  //Intializing the operators
148  DXX &Dxx;
149  DYY &Dyy;
150  //Constructor
151  RHSFunctor(DXX &Dxx,DYY &Dyy):Dxx(Dxx),Dyy(Dyy)
152  {}
153 
154  void operator()( const state_type_2d_ofp &X , state_type_2d_ofp &dxdt , const double t ) const
155  {
156  //Casting the pointers to OpenFPM vector distributions
157  dist_vector_type &Particles= *(dist_vector_type *) PointerDistGlobal;
158  dist_vector_subset_type &Particles_bulk= *(dist_vector_subset_type *) PointerDistSubset;
159 
160  //Aliasing the properties.
161  auto C = getV<0>(Particles);
162  auto C_bulk = getV<0>(Particles_bulk);
163  auto dC = getV<2>(Particles);
164  auto dC_bulk = getV<2>(Particles_bulk);
165 
166  //Since the state vector does not have enough information to compute derivates, we copy them back to the Particle Distribution.
167  //In this way we can compute all the things that are required to be computed in each stage of the ode integrator.
168  // Note that we are going to use bulk expressions as needed. Whenever we don't want to update the boundaries (Due to the boundary conditions).
169 
170  //These expressions only update the bulk values of C.
171  C_bulk[x]=X.data.get<0>();
172  C_bulk[y]=X.data.get<1>();
173  Particles.ghost_get<0>();
174 
175  // We do the RHS computations for the Laplacian (Updating bulk only).
176  dC_bulk[x] = 0.1*(Dxx(C[x])+Dyy(C[x]));
177  dC_bulk[y] = 0.1*(Dxx(C[y])+Dyy(C[y]));
178 
179  //We copy back to the dxdt state_type for Odeint
180  dxdt.data.get<0>()=dC[x];
181  dxdt.data.get<1>()=dC[y];
182  }
183 };
185 
186 
187 
200 int main(int argc, char* argv[]) {
201  // initialize library
202  openfpm_init(&argc, &argv);
204 
206 
218  const size_t sz[2] = {41, 41};
219  Box<2, double> box({-1, -1}, {1.0, 1.0});
220  size_t bc[2] = {NON_PERIODIC, NON_PERIODIC};
221  double spacing[2];
222  spacing[0] = 2.0 / (sz[0] - 1);
223  spacing[1] = 2.0 / (sz[1] - 1);
224  double rCut = 3.1 * spacing[0];
225  Ghost<2, double> ghost(rCut);
226 
227  dist_vector_type Particles(0, box, bc, ghost);
228  Particles.setPropNames({"Concentration", "Velocity", "TempConcentration"});
229 
230  auto it = Particles.getGridIterator(sz);
231  while (it.isNext()) {
232  Particles.add();
233  auto key = it.get();
234  double x = -1.0 + key.get(0) * spacing[0];
235  Particles.getLastPos()[0] = x;
236  double y = -1.0 + key.get(1) * spacing[1];
237  Particles.getLastPos()[1] = y;
238  //We detect Boundaries. By labelling the subset to be 0 for bulk and 1 for the boundary. Subsets will be constructed later based on the label.
239  if (x != -1.0 && x != 1.0 && y != -1.0 && y != 1) {
240  Particles.getLastSubset(0);
241  } else {
242  Particles.getLastSubset(1);
243  }
244  // Here fill the Initial value of the concentration.
245  if (x == 0.0 && y > -0.5 && y < 0) {
246  Particles.template getLastProp<0>()[0] = 1.0;
247  Particles.template getLastProp<0>()[1] = 0.0;
248  } else if (x == 0.0 && y > 0 && y < 0.5) {
249  Particles.template getLastProp<0>()[0] = 0.0;
250  Particles.template getLastProp<0>()[1] = 1.0;
251  } else {
252  Particles.template getLastProp<0>()[0] = 0.0;
253  Particles.template getLastProp<0>()[1] = 0.0;
254  }
255  ++it;
256  }
257  Particles.map();
258  Particles.ghost_get<0>();
259 
260  //We write the particles to check if the initialization is correct.
261  Particles.write("Init");
263 
276  // Now we initialize the grid with a filled circle. Outside the circle, the value of Phi_0 will be -1, inside +1.
277  //Now we construct the subsets based on the subset number.
278  dist_vector_subset_type Particles_bulk(Particles, 0);
279  dist_vector_subset_type Particles_boundary(Particles, 1);
280 
281  //We cast the global pointers to Particles and Particles_bulk as expected by the RHS functor.
282  PointerDistGlobal = (void *) &Particles;
283  PointerDistSubset = (void *) &Particles_bulk;
285 
299  //We create the DCPSE Based operators for discretization of the operators.
300  auto verletList = Particles.template getVerlet<VL_NON_SYMMETRIC|VL_SKIP_REF_PART>(rCut);
301  Derivative_xx<decltype(verletList)> Dxx(Particles, verletList, 2, rCut);
302  Derivative_yy<decltype(verletList)> Dyy(Particles, verletList, 2, rCut);
303  //We create aliases for referring to property and and positions.
304  auto Pos = getV<POS_PROP>(Particles);
305  auto C = getV<0>(Particles);
306  auto V = getV<1>(Particles);
307  auto dC = getV<2>(Particles);
308 
309  //We create aliases for referring to the subset properties.
310  auto C_bulk = getV<0>(Particles_bulk);
311  auto V_bulk = getV<1>(Particles_bulk);
312  auto dC_bulk = getV<2>(Particles_bulk);
313 
315 
331  //Now we create a odeint stepper object (RK4). Since we are in 2d, we are going to use "state_type_2d_ofp". Which is a structure or state_type compatible with odeint. We further pass all the parameters including "boost::numeric::odeint::vector_space_algebra_ofp",which tell odeint to use openfpm algebra.
332  // The template parameters are: state_type_2d_ofp (state type of X), double (type of the value inside the state), state_type_2d_ofp (state type of DxDt), double (type of the time), boost::numeric::odeint::vector_space_algebra_ofp (our algebra)
333  boost::numeric::odeint::runge_kutta4<state_type_2d_ofp, double, state_type_2d_ofp, double, boost::numeric::odeint::vector_space_algebra_ofp> Odeint_rk4;
334 
335  //The method Odeint_rk4 from Odeint, requires system (a function which computes RHS of the PDE), an instance of the Compute RHS functor. We create the System with the correct types and parameteres for the operators as declared before.
336  RHSFunctor<Derivative_xx<decltype(verletList)>, Derivative_yy<decltype(verletList)>> System(Dxx, Dyy);
337 
338  //Furhter, odeint needs data in a state type "state_type_2d_ofp", we create one and fill in the initial condition.
340  //Since we created a 2d state_type we initialize the two fields in the object data using the method get.
341  X.data.get<x>() = C[x];
342  X.data.get<y>() = C[y];
344 
368  // Now we will create a time loop for controlling the step size ourselves but call odeint to do the 4 stages of RK4.
369  int ctr = 0;
370  double t = 0, tf = 1, dt = 1e-2;
371  while (t < tf) {
372  //computing the velocity at the current position and at time step t (only in the bulk, so boundary remains 0).
373  V_bulk[x] = -Pos[y] * exp(-10.0 * (Pos[x] * Pos[x] + Pos[y] * Pos[y]));
374  V_bulk[y] = Pos[x] * exp(-10.0 * (Pos[x] * Pos[x] + Pos[y] * Pos[y]));
375  //Observing the state
376  Particles.deleteGhost();
377  Particles.write_frame("PDE_Sol", ctr);
378  Particles.ghost_get<0>();
379  //calling rk4 with the function System, the state_type, current time t and the stepsize dt. It computes one step of the RK4.
380  Odeint_rk4.do_step(System, X, t, dt);
381  //Copying back the step, updating only the bulk values.
382  C_bulk[x] = X.data.get<0>();
383  C_bulk[y] = X.data.get<1>();
384  //We advect the particles with the velocity using an Euler step.
385  Pos = Pos + dt * V;
386  //We need to map and get the ghost, as we moved the particles.
387  Particles.map();
388  Particles.ghost_get<0>();
389  //We update the subset and operators as the particles moved.
390  Particles_bulk.update();
391  Particles_boundary.update();
392  Particles.updateVerlet(verletList,rCut);
393  Dxx.update(Particles);
394  Dyy.update(Particles);
395  //Reinitialzing as distributed size can change.
396  X.data.get<0>()=C[x];
397  X.data.get<1>()=C[y];
398  ctr++;
399  t += dt;
400  } //time loop end
401 
402  //Deallocating the operators
403  Dxx.deallocate(Particles);
404  Dyy.deallocate(Particles);
405  openfpm_finalize(); // Finalize openFPM library
406  return 0;
407 } //main end
409 
This class represent an N-dimensional box.
Definition: Box.hpp:60
Definition: Ghost.hpp:40
This class implement the point shape in an N-dimensional space.
Definition: Point.hpp:28
void ghost_get(size_t opt=WITH_POSITION)
It synchronize the properties and position of the ghost particles.
aggregate of properties, from a list of object if create a struct that follow the OPENFPM native stru...
Definition: aggregate.hpp:221
A 2d Odeint and Openfpm compatible structure.