NDDEM
DEMND.h
Go to the documentation of this file.
1 #include <cstdlib>
2 #include <cmath>
3 #include <cstdio>
4 #include <vector>
5 #include <ctime>
6 #include <cstring>
7 
8 #include <random>
9 #ifndef NO_OPENMP
10  #include <omp.h>
11 #endif
12 
13 #include "cereal/types/vector.hpp"
14 #include "cereal/types/chrono.hpp"
16 #include "cereal/types/utility.hpp"
17 #include "cereal/types/string.hpp"
18 #include "cereal/types/map.hpp"
19 #include "cereal/types/list.hpp"
21 #include "cereal/archives/xml.hpp"
22 
23 //#include "callgrind.h"
24 //#include <ittnotify.h>
25 
26 #include "Typedefs.h"
27 #include "Parameters.h"
28 #include "Contacts.h"
29 #include "ContactList.h"
30 #include "Multiproc.h"
31 #include "Cells.h"
32 #include "Octree.h"
33 #include "Boundaries.h"
34 #ifdef EMSCRIPTEN
35  #include <emscripten.h>
36  #include <emscripten/bind.h>
37 
38  #ifndef JS_CONVERT_ARRAYS
39  #define JS_CONVERT_ARRAYS
40  using namespace emscripten;
41  using Vector2Djs = emscripten::val ;
42  using Vector1Djs = emscripten::val ;
43 
44  template <typename T>
45  emscripten::val to_js_array(const std::vector<std::vector<T>>& data) {
46  using namespace emscripten;
47  val outer = val::array();
48  for (size_t i = 0; i < data.size(); ++i) {
49  val inner = val::array();
50  for (size_t j = 0; j < data[i].size(); ++j) {
51  inner.set(j, data[i][j]);
52  }
53  outer.set(i, inner);
54  }
55  return outer;
56  }
57  template <typename T>
58  emscripten::val to_js_array(const std::vector<T>& data) {
59  using namespace emscripten;
60  val outer = val::array();
61  for (size_t i = 0; i < data.size(); ++i) {
62  outer.set(i, data[i]);
63  }
64  return outer;
65  }
66 
67  std::vector<double> from_js_array(emscripten::val jsArray) {
68  std::vector<double> vec;
69  size_t length = jsArray["length"].as<unsigned>();
70  vec.reserve(length);
71  for (unsigned i = 0; i < length; ++i) {
72  vec.push_back(jsArray[i].as<double>());
73  }
74  return vec;
75 }
76  #endif
77 #else
78  using Vector2Djs = std::vector<std::vector<double>> ;
79  using Vector1Djs = std::vector<double> ;
80  template <typename T> std::vector<std::vector<T>> to_js_array(std::vector<std::vector<T>>& data) {return data ; }
81  template <typename T> std::vector<T> to_js_array(std::vector<T>& data) {return data ; }
82  template <typename T> std::vector<T> from_js_array(std::vector<T>& data) {return data ; }
83 #endif
84 
111 extern vector <std::pair<ExportType,ExportData>> * toclean ;
112 extern XMLWriter * xmlout ;
113 
114 
115 template <int d>
116 class Simulation {
117 public:
119  int N ;
120  std::vector < std::vector <double> > X ;
121  std::vector < std::vector <double> > V ;
122  std::vector < std::vector <double> > A ;
123  std::vector < std::vector <double> > Omega ;
124  std::vector < std::vector <double> > F ;
125  std::vector < std::vector <double> > FOld ;
126  std::vector < std::vector <double> > Torque ;
127  std::vector < std::vector <double> > TorqueOld ;
128  std::vector < double > Vmag ;
129  std::vector < double > OmegaMag ;
130  std::vector < double > Z ;
131  std::vector < std::vector <double> > Fcorr ;
132  std::vector < std::vector <double> > TorqueCorr ;
133 
134  std::vector < std::optional<int> > RigidBodyId ;
135 
136  std::vector <SpecificAction<d>> ExternalAction ;
137 
138  std::vector <uint32_t> PBCFlags ;
139  std::vector < std::vector <double> > WallForce ;
140  std::vector<std::vector<double>> empty_array ;
141  std::vector < std::vector <double> > ParticleForce ;
142 
143  vector <uint32_t> Ghost ;
144  vector <uint32_t> Ghost_dir ;
146 
147  int numthread=1 ;
151 
152  double t ; int ti ;
153  double dt ;
154  clock_t tnow, tprevious ;
155 
156  //-----------------------------------------
157  void save_restart(const std::string &filename) {
158  std::ofstream os(filename, std::ios::binary);
159  cereal::BinaryOutputArchive archive(os);
160  //std::ofstream os(filename);
161  //cereal::XMLOutputArchive archive(os);
162  archive(*this);
163  }
164  void load_restart(const std::string &filename) {
165  std::ifstream os(filename, std::ios::binary);
166  std::cout << "FILEOPEN:" << filename << " " << os.is_open() << "\n" ; fflush(stdout) ;
167  cereal::BinaryInputArchive archive(os);
168  //std::ifstream os(filename);
169  //cereal::XMLInputArchive archive(os);
170  archive(*this);
171 
172  #ifndef NO_OPENMP
173  const char* env_p = std::getenv("OMP_NUM_THREADS") ;
174  if (env_p!=nullptr) numthread = atoi (env_p) ;
175  omp_set_num_threads(numthread) ;
176  #endif
177 
178  MP.C.resize(MP.P, Contacts<d>(P)) ; // Empty default contacts are not saved. Need to rebuild them.
179 
180  // Iterators are invalidated when saving, need to rebuild them
182  {
183  MP.CLp_it.init(P.N, MP.P) ;
184  MP.CLp_it.rebuild(MP.CLp) ;
185  }
186  }
187 
188  template <class Archive>
189  void serialize( Archive & ar )
190  {
191  ar(N,
192  CEREAL_NVP(X), V, A, Omega, F, FOld, Torque, TorqueOld, Vmag, OmegaMag, Z, Fcorr, TorqueCorr, RigidBodyId,
193  PBCFlags, WallForce, empty_array, ParticleForce, Ghost, Ghost_dir, Atmp,
194  numthread, t, ti, dt, tnow, tprevious,
195  P, MP, cells, ExternalAction, octree
196  );
197  }
198  //-----------------------------------------
199 
200  //==========================================================
201  Simulation(int NN) {
202  P = Parameters<d>(NN) ;
204  if (!Tools<d>::check_initialised(d)) printf("ERR: Something terribly wrong happened\n") ;
205  assert(d<(static_cast<int>(sizeof(int))*8-1)) ; //TODO
206  // Array initialisations
207  N=P.N ;
208  X.resize(N, std::vector <double> (d, 0)) ;
209  // X[0][0] = 123;
210  V.resize(N, std::vector <double> (d, 0)) ;
211  A.resize(N, std::vector <double> (d*d, 0)) ; for (int i=0 ; i<N ; i++) A[i]=Tools<d>::Eye ;
212  Omega.resize(N, std::vector <double> (d*(d-1)/2, 0)) ; //Rotational velocity matrix (antisymetrical)
213  F.resize(N, std::vector <double> (d, 0)) ;
214  FOld.resize(N, std::vector <double> (d, 0)) ;
215  Torque.resize(N, std::vector <double> (d*(d-1)/2, 0)) ;
216  TorqueOld.resize(N, std::vector <double> (d*(d-1)/2, 0)) ;
217 
218  Vmag.resize (N,0) ;
219  OmegaMag.resize (N,0) ;
220  Z.resize (N,0) ;
221  Fcorr.resize (N, std::vector <double> (d, 0)) ;
222  TorqueCorr.resize (N, std::vector <double> (d*(d-1)/2, 0)) ;
223  RigidBodyId.resize(N,{}) ;
224 
225  PBCFlags.resize (N, 0) ;
226  WallForce.resize (2*d, std::vector <double> (d,0)) ;
227  empty_array.resize(1, std::vector <double> (1, 0)) ;
228 
229  Ghost.resize (N, 0) ;
230  Ghost_dir.resize (N, 0) ;
231 
232  Atmp.resize (d*d, 0) ;
233 
234  // Initial state setup
235  P.set_boundaries() ;
236  }
237  //-------------------------------------------------------------------------
240  void init_from_file (char filename[])
241  {
242  P.load_datafile (filename, X, V, Omega) ;
243  }
244  //-------------------------------------------------------------------------
249  void finalise_init () {
250  //if (strcmp(argv[3], "default"))
251  // P.load_datafile (argv[3], X, V, Omega) ;
252  toclean = &(P.dumps) ;
253  xmlout = P.xmlout ;
254 
255  P.RigidBodies.allocate (RigidBodyId) ;
256 
257  #ifndef NO_OPENMP
258  const char* env_p = std::getenv("OMP_NUM_THREADS") ;
259  if (env_p!=nullptr) numthread = atoi (env_p) ;
260  omp_set_num_threads(numthread) ;
261  #endif
262 
263  MP.initialise(N, numthread, P) ;
264 
266  {
267  printf("INITIALISE CELLS\n") ; fflush(stdout) ;
268  auto rmax = std::max_element(P.r.begin(), P.r.end()) ;
269  printf("%g ", *rmax) ;
270  cells.init_cells(P.Boundaries, P.cellsize==-1?(*rmax*2.1):P.cellsize) ;
271  MP.splitcells(cells.cells.size()) ;
272  MP.CLp_it.init(N, numthread) ;
273  }
275  {
276  printf("INITIALISE OCTREE\n") ; fflush(stdout) ;
277  octree.init_cells(P.Boundaries, P.r) ;
278  MP.splitcells(octree.cells_to_split()) ;
279  MP.CLp_it.init(N, numthread) ;
280  }
281 
282  dt=P.dt ;
283  t=0 ; ti=0 ;
284  tprevious=clock() ;
285  printf("[INFO] Orientation tracking is %s\n", P.orientationtracking?"True":"False") ;
286  }
287  //-------------------------------------------------------------------
292  void interpret_command (string in)
293  {
294  P.interpret_command(in, X,V,Omega) ;
295  }
296  //--------------------------------------------------------------------
301  {
302  int nsteps = P.T/dt ;
303  step_forward(nsteps) ;
304  }
305  //----
310  void step_forward (int nt)
311  {
312  for (int ntt=0 ; ntt<nt ; ntt++, t+=dt, ti++)
313  {
314  //if (ntt==990) {CALLGRIND_START_INSTRUMENTATION ;}
315 
316  // printf("UP TO TIME: %g with timestep: %g\n", t, dt);
317  // printf("%g %g %g\n", X[0][0],X[0][1],X[0][2]);
318  bool isdumptime = (ntt==nt-1) || (ti % P.tdump==0) ;
319  //P.display_info(ti, V, Omega, F, Torque, 0, 0) ;
320  if (ti%P.tinfo==0)
321  {
322  tnow = clock();
323  printf("\r%10g | %5.2g%% | %d iterations in %10gs | %5d | finish in %10gs",t, t/P.T*100, P.tinfo,
324  double(tnow - tprevious) / CLOCKS_PER_SEC, ti, ((P.T-t)/(P.tinfo*dt))*(double(tnow - tprevious) / CLOCKS_PER_SEC)) ;
325  //fprintf(logfile, "%d %10g %lu %lu\n", ti, double(tnow - tprevious) / CLOCKS_PER_SEC, MP.CLp[0].v.size(), MP.CLw[0].v.size()) ;
326  fflush(stdout) ;
327  tprevious=tnow ;
328  }
329 
330  //----- Velocity Verlet step 1 : compute the new positions
331  #pragma omp parallel for default(none) shared (N) shared(X) shared(P) shared(V) shared(FOld) shared(Omega) shared(PBCFlags) shared(dt) shared(Ghost) shared(Ghost_dir) shared(A)
332  for (int i=0 ; i<N ; i++)
333  {
334  double disp, totdisp=0 ;
335  for (int dd=0 ; dd<d ; dd++)
336  {
337  disp = V[i][dd]*dt + FOld[i][dd] * (dt * dt / P.m[i] /2.) ;
338  X[i][dd] += disp ;
339  totdisp += disp*disp ;
340  }
341  //displacement[i] += sqrt(totdisp) ;
342  //if (displacement[i] > maxdisp[0]) {maxdisp[1]=maxdisp[0] ; maxdisp[0]=displacement[i] ; } // ERROR RACE CONDITION ON MAXDISP
343 
344  // Simpler version to make A evolve (Euler, doesn't need to be accurate actually, A is never used for the dynamics), and Gram-Shmidt orthonormalising after ...
345  if (P.orientationtracking)
346  {
347  v1d tmpO (d*d,0), tmpterm1 (d*d,0) ;
348  Tools<d>::skewexpand(tmpO, Omega[i]) ;
349  Tools<d>::matmult(tmpterm1, tmpO, A[i]) ;
350  for (int dd=0 ; dd<d*d ; dd++)
351  A[i][dd] -= tmpterm1[dd] * dt ;
353  }
354 
355  // Boundary conditions ...
356  P.perform_PBC(X[i], PBCFlags[i]) ;
357  P.perform_PBCLE(X[i], V[i], PBCFlags[i]) ;
358 
359  // Find ghosts
360  Ghost[i]=0 ; Ghost_dir[i]=0 ;
361  uint32_t mask=1 ;
362 
363  for (size_t j=0 ; j<P.Boundaries.size() ; j++, mask<<=1)
364  {
365  if (!P.Boundaries[j].is_periodic()) continue ;
366  if (X[i][j] <= P.Boundaries[j].xmin + P.r[i]) {Ghost[i] |= mask ; }
367  else if (X[i][j] >= P.Boundaries[j].xmax - P.r[i]) {Ghost[i] |= mask ; Ghost_dir[i] |= mask ;}
368  }
369 
370  if (P.Boundaries[0].Type == WallType::PBC_LE && (Ghost[i]&1)) // We need to consider the case where we have a ghost through the LE_PBC
371  {
372  mask = (1<<30) ; // WARNING dim 30 will be used for LEPBC!!
373  double tmpyloc = X[i][1] + (Ghost_dir[i]&1?-1:1)*P.Boundaries[0].displacement ;
374  if (tmpyloc > P.Boundaries[1].xmax) tmpyloc -= P.Boundaries[1].delta ;
375  if (tmpyloc < P.Boundaries[1].xmin) tmpyloc += P.Boundaries[1].delta ;
376  if (tmpyloc <= P.Boundaries[1].xmin + P.r[i]) {Ghost[i] |= mask ; }
377  else if (tmpyloc >= P.Boundaries[1].xmax - P.r[i]) {Ghost[i] |= mask ; Ghost_dir[i] |= mask ;}
378 
379  }
380 
381  if (P.forceinsphere)
382  P.perform_forceinsphere(X[i]) ;
383 
384  //Nghosts=Ghosts.size() ;
385  } // END PARALLEL SECTION
386 
387  P.perform_MOVINGWALL() ;
388  P.perform_PBCLE_move() ;
389  std::invoke (P.update_gravity, &P, dt) ;
392 
393  // ----- Contact detection ------
394  double LE_displacement ;
395  if (P.Boundaries[0].Type == WallType::PBC_LE)
396  LE_displacement = P.Boundaries[0].displacement ;
397  else
398  LE_displacement = 0 ;
399 
400  for (int i=0 ; i<MP.P ; i++)
401  MP.CLp_it.it_ends[i] = MP.CLp[i].v.end() ;
402 
403  #pragma omp parallel default(none) shared(MP) shared(P) shared(N) shared(X) shared(Ghost) shared(Ghost_dir) shared(RigidBodyId) shared (stdout) shared(cells) shared(LE_displacement)
404  {
405  #ifdef NO_OPENMP
406  int ID = 0 ;
407  #else
408  int ID = omp_get_thread_num();
409  double timebeg = omp_get_wtime();
410  #endif
411  cpm<d> tmpcpm(0,0,0,0,nullptr) ;
412  cp<d> tmpcp(0,0,0,nullptr) ; double sum=0 ;
413 
414  ContactList<d> & CLw = MP.CLw[ID] ; ContactListMesh<d> & CLm = MP.CLm[ID] ;
415  CLw.reset(); CLm.reset() ;
416 
418  {
419  cells.contacts(ID, {MP.sharecell[ID], MP.sharecell[ID+1]}, MP.CLp_it, MP.CLp_new[ID], X, P.r, LE_displacement) ;
420  }
422  {
423  octree.contacts(ID, {MP.sharecell[ID], MP.sharecell[ID+1]}, MP.CLp_it, MP.CLp_new[ID], X, P.r, LE_displacement) ;
424  }
425  else
426  {
427  ContactList<d> & CLp = MP.CLp[ID] ;
428  CLp.reset() ;
429 
430  for (int i=MP.share[ID] ; i<MP.share[ID+1] ; i++)
431  {
432  // Contact detection between particles
433  tmpcp.setinfo(CLp.default_action());
434  tmpcp.i=i ;
435  for (int j=i+1 ; j<N ; j++) // Regular particles
436  {
437 
438  if (RigidBodyId[i].has_value() && RigidBodyId[j].has_value() && RigidBodyId[i]==RigidBodyId[j]) continue ;
439  if (Ghost[j] | Ghost[i])
440  {
441  tmpcp.j=j ; tmpcp.ghostdir=Ghost_dir[j] | (Ghost[i]&(~Ghost_dir[i])) ;
442  bitdim tmpghost = Ghost[j] | Ghost[i] ;
443  if (P.Boundaries[0].Type == WallType::PBC_LE && (Ghost[i]&1))
444  {
445  double tmpyloc = X[j][1] + (tmpcp.ghostdir&1?-1:1)*P.Boundaries[0].displacement ;
446  if (tmpyloc > P.Boundaries[1].xmax) tmpyloc -= P.Boundaries[1].delta ;
447  if (tmpyloc < P.Boundaries[1].xmin) tmpyloc += P.Boundaries[1].delta ;
448  if (tmpyloc <= P.Boundaries[1].xmin + P.r[i]) {tmpghost |= (1<<30) ; }
449  else if (tmpyloc >= P.Boundaries[1].xmax - P.r[i]) {tmpghost |= (1<<30) ; tmpcp.ghostdir |= (1<<30) ;}
450  }
451 
452  //CLp.check_ghost (Ghost[j], P, X[i], X[j], tmpcp) ;
453  (CLp.*CLp.check_ghost) (tmpghost, P, X[i], X[j], P.r[i], P.r[j], tmpcp, 0,0,0) ;
454  }
455  else
456  {
457  sum=0 ;
458  for (int k=0 ; sum<(P.r[i]+P.r[j])*(P.r[i]+P.r[j]) && k<d ; k++) sum+= (X[i][k]-X[j][k])*(X[i][k]-X[j][k]) ;
459  if (sum<(P.r[i]+P.r[j])*(P.r[i]+P.r[j]))
460  {
461  tmpcp.j=j ; tmpcp.contactlength=sqrt(sum) ; tmpcp.ghost=0 ; tmpcp.ghostdir=0 ;
462  CLp.insert(tmpcp) ;
463  }
464  }
465  }
466  }
467  CLp.finalise() ;
468  }
469 
470  for (int i=MP.share[ID] ; i<MP.share[ID+1] ; i++)
471  {
472  // Contact detection between particles and walls
473  tmpcp.setinfo(CLw.default_action());
474  tmpcp.i=i ; tmpcp.ghost=0 ;
475  for (size_t j=0 ; j<P.Boundaries.size() ; j++) // Wall contacts
476  {
477  if (P.Boundaries[j].Type==WallType::PBC) continue ;
478 
479  if (P.Boundaries[j].Type==WallType::WALL || P.Boundaries[j].Type==WallType::MOVINGWALL)
480  {
481  tmpcp.contactlength=fabs(X[i][j]-P.Boundaries[j].xmin) ;
482  if (tmpcp.contactlength<P.r[i])
483  {
484  tmpcp.j=(2*j+0);
485  CLw.insert(tmpcp) ;
486  }
487 
488  tmpcp.contactlength=fabs(X[i][j]-P.Boundaries[j].xmax) ;
489  if (tmpcp.contactlength<P.r[i])
490  {
491  tmpcp.j=(2*j+1);
492  CLw.insert(tmpcp) ;
493  }
494  }
495  else if (P.Boundaries[j].Type==WallType::SPHERE ||
496  P.Boundaries[j].Type==WallType::HEMISPHERE ||
498  {
499  tmpcp.contactlength=0 ;
500  for (int dd=0 ; dd<d ; dd++)
501  tmpcp.contactlength += (P.Boundaries[j].center[dd] - X[i][dd])*(P.Boundaries[j].center[dd] - X[i][dd]) ;
502 
503  if (tmpcp.contactlength < (P.Boundaries[j].radius + P.r[i])*(P.Boundaries[j].radius + P.r[i]) &&
504  tmpcp.contactlength > (P.Boundaries[j].radius - P.r[i])*(P.Boundaries[j].radius - P.r[i]))
505  {
506  tmpcp.contactlength = sqrt(tmpcp.contactlength) - P.Boundaries[j].radius ;
507  if (tmpcp.contactlength < 0) tmpcp.j=2*j;
508  else tmpcp.j=2*j+1 ;
509  tmpcp.contactlength = fabs(tmpcp.contactlength) ;
510  CLw.insert(tmpcp) ;
511  }
512  }
513  else if (P.Boundaries[j].Type==WallType::AXIALCYLINDER)
514  {
515  tmpcp.contactlength=0 ;
516 
517  for (int dd=0 ; dd<d ; dd++)
518  {
519  if (dd == P.Boundaries[j].axis) continue ;
520  tmpcp.contactlength += (P.Boundaries[j].center[dd] - X[i][dd])*(P.Boundaries[j].center[dd] - X[i][dd]) ;
521  }
522  if (tmpcp.contactlength < (P.Boundaries[j].radius + P.r[i])*(P.Boundaries[j].radius + P.r[i]) &&
523  tmpcp.contactlength > (P.Boundaries[j].radius - P.r[i])*(P.Boundaries[j].radius - P.r[i]))
524  {
525  tmpcp.contactlength = sqrt(tmpcp.contactlength) - P.Boundaries[j].radius ;
526  if (tmpcp.contactlength < 0) tmpcp.j=2*j;
527  else tmpcp.j=2*j+1 ;
528  tmpcp.contactlength = fabs(tmpcp.contactlength) ;
529  CLw.insert(tmpcp) ;
530  }
531  }
532  else if (P.Boundaries[j].Type==WallType::ELLIPSE)
533  {
534  static_assert((sizeof(double)==8)) ;
535 
536  double tparam ;
537  std::tie(tparam, tmpcp.contactlength) = Tools_2D::contact_ellipse_disk (X[i], P.Boundaries[j].semiaxisx, P.Boundaries[j].semiaxisy, P.Boundaries[j].centerx, P.Boundaries[j].centery, P.graddesc_gamma, P.graddesc_tol) ;
538  if (tmpcp.contactlength<P.r[i])
539  {
540  tmpcp.j=2*j ;
541  #pragma GCC diagnostic push
542  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
543  tmpcp.ghost = *(reinterpret_cast<uint32_t*>(&tparam)) ;
544  tmpcp.ghostdir=*(reinterpret_cast<uint64_t*>(&tparam))>>32 ; //ugly bit punning, I'm so sorry...
545  #pragma GCC diagnostic pop
546  CLw.insert(tmpcp) ;
547  }
548  }
549  }
550 
551  // Contact detection between particles and meshes
552  tmpcpm.setinfo(CLm.default_action()) ;
553  tmpcpm.i=i ;
554  for (size_t j=0 ; j<P.Meshes.size() ; j++) //TODO Meshes incompatible with PBC
555  {
556  bool a= CLm.check_mesh_dst_contact(P.Meshes[j], X[i], P.r[i], tmpcpm) ;
557  //printf("%d %g %g |", a, tmpcpm.contactlength, P.skin) ;
558  if (a && tmpcpm.contactlength < P.r[i])
559  {
560  CLm.insert(tmpcpm) ;
561  }
562  }
563  }
564  CLw.finalise() ;
565  CLm.finalise() ;
566  #ifndef NO_OPENMP
567  MP.timing_contacts[ID] += omp_get_wtime()-timebeg;
568  #endif
569  } //END PARALLEL SECTION
570 
572  {
573  MP.merge_newcontacts() ;
574  for (int i=0 ; i<N ; i++)
575  MP.CLp_it.it_array_beg[i] = MP.CLp_it.null_list.v.begin() ;
576  }
577 
578  //-------------------------------------------------------------------------------
579  // Force and torque computation
580  Tools<d>::setzero(F) ; Tools<d>::setzero(Fcorr) ; Tools<d>::setzero(TorqueCorr) ;
581  Tools<d>::setgravity(F, P.g, P.m); // Actually set gravity is effectively also doing the setzero
582  Tools<d>::setzero(Torque);
583 
584  for (auto it = ExternalAction.begin() ; it<ExternalAction.end() ; /*KEEP EMPTY*/ )
585  {
586  Tools<d>::vAddFew(F[it->id], it->Fn, it->Ft, Fcorr[it->id]) ;
587  Tools<d>::vAddOne(Torque[it->id], it->Torquei, TorqueCorr[it->id]) ;
588 
589  it->duration -- ;
590  if (it->duration<=0)
591  ExternalAction.erase(it) ;
592  else
593  it++ ;
594  }
595 
596  //Particle - particle contacts
597  #pragma omp parallel default(none) shared(MP) shared(P) shared(X) shared(V) shared(Omega) shared(F) shared(Fcorr) shared(TorqueCorr) shared(Torque) shared(stdout) shared(isdumptime)
598  {
599  #ifdef NO_OPENMP
600  int ID = 0 ;
601  #else
602  int ID = omp_get_thread_num();
603  double timebeg = omp_get_wtime();
604  #endif
605  ContactList<d> & CLp = MP.CLp[ID] ; ContactList<d> & CLw = MP.CLw[ID] ; ContactListMesh<d> & CLm = MP.CLm[ID] ;
606  Contacts<d> & C =MP.C[ID] ;
607  v1d tmpcn (d,0) ; v1d tmpvel (d,0) ;
608 
609  // Particle-particle contacts
610  //printf("====%d %g=====\n", ti, P.Boundaries[0].displacement) ;
611  int curi = -1 ;
612  for (auto it = CLp.v.begin() ; it!=CLp.v.end() ; it++)
613  {
614  // We need to do some contact handling first. This should technically only be done for non-NAIVE contact detection, but it shouldn't hurt the NAIVE, and is a very mild slow down.
615  while (it != CLp.v.end() && !it->persisting)
616  it = CLp.v.erase(it) ;
617  if (it == CLp.v.end())
618  break ;
619 
620  if ( it->i != curi )
621  {
622  MP.CLp_it.it_array_beg[it->i] = it ;
623  curi = it->i ;
624  }
625 
626  // Proceed if the contact needs to be considered
627  if (it->ghost==0)
628  {
629  C.particle_particle(X[it->i], V[it->i], Omega[it->i], P.r[it->i], P.m[it->i],
630  X[it->j], V[it->j], Omega[it->j], P.r[it->j], P.m[it->j], *it, isdumptime) ;
631  }
632  else
633  {
634  // printf("%d\n", it->ghost) ; fflush(stdout) ;
635  (C.*C.particle_ghost) (X[it->i], V[it->i], Omega[it->i], P.r[it->i], P.m[it->i],
636  X[it->j], V[it->j], Omega[it->j], P.r[it->j], P.m[it->j], *it, isdumptime);//, logghosts) ;
637  }
638  if (isdumptime) it->saveinfo(C.Act) ;
639 
640  Tools<d>::vAddFew(F[it->i], C.Act.Fn, C.Act.Ft, Fcorr[it->i]) ;
641  Tools<d>::vAddOne(Torque[it->i], C.Act.Torquei, TorqueCorr[it->i]) ;
642 
643  if (MP.ismine(ID,it->j))
644  {
645  Tools<d>::vSubFew(F[it->j], C.Act.Fn, C.Act.Ft, Fcorr[it->j]) ;
646  Tools<d>::vAddOne(Torque[it->j], C.Act.Torquej, TorqueCorr[it->j]) ;
647  }
648  else
649  MP.delaying(ID, it->j, C.Act) ;
650 
651  it->persisting = false ;
652 
653  //Tools<d>::vAdd(F[it->i], Act.Fn, Act.Ft) ; Tools<d>::vSub(F[it->j], Act.Fn, Act.Ft) ; //F[it->i] += (Act.Fn + Act.Ft) ; F[it->j] -= (Act.Fn + Act.Ft) ;
654  //Torque[it->i] += Act.Torquei ; Torque[it->j] += Act.Torquej ;
655  }
656  //if (curi!= -1)
657  // MP.CLp_it.it_array_end[curi] = CLp.v.end() ;
658 
659  // Particle wall contacts
660  for (auto it = CLw.v.begin() ; it!=CLw.v.end() ; it++)
661  {
662  if (P.Boundaries[it->j/2].Type == WallType::SPHERE ||
663  P.Boundaries[it->j/2].Type == WallType::HEMISPHERE)
664  {
665  if ( P.Boundaries[it->j/2].Type == WallType::HEMISPHERE &&
666  X[it->i][P.Boundaries[it->j/2].axis]>P.Boundaries[it->j/2].center[P.Boundaries[it->j/2].axis]+P.r[it->i]) continue ;
667  for (int dd = 0 ; dd<d ; dd++)
668  tmpcn[dd] = (X[it->i][dd]-P.Boundaries[it->j/2].center[dd])*((it->j%2==0)?-1:1) ;
669  tmpcn/=Tools<d>::norm(tmpcn) ;
670  C.particle_wall( V[it->i],Omega[it->i],P.r[it->i], P.m[it->i], tmpcn, *it) ;
671  }
672  else if (P.Boundaries[it->j/2].Type == WallType::AXIALCYLINDER)
673  {
674  tmpcn[P.Boundaries[it->j/2].axis]= 0 ;
675  for (int dd = 0 ; dd<d ; dd++)
676  {
677  if (dd == P.Boundaries[it->j/2].axis) continue ;
678  tmpcn[dd] = (X[it->i][dd]-P.Boundaries[it->j/2].center[dd])*((it->j%2==0)?-1:1) ;
679  }
680  tmpcn/=Tools<d>::norm(tmpcn) ;
681  C.particle_wall( V[it->i],Omega[it->i],P.r[it->i], P.m[it->i], tmpcn, *it) ;
682  }
683  else if (P.Boundaries[it->j/2].Type == WallType::ROTATINGSPHERE)
684  {
685  for (int dd = 0 ; dd<d ; dd++)
686  tmpcn[dd] = (X[it->i][dd]-P.Boundaries[it->j/2].center[dd])*((it->j%2==0)?-1:1) ;
687  tmpcn/=Tools<d>::norm(tmpcn) ;
688  Tools<d>::surfacevelocity(tmpvel, X[it->i]+tmpcn*(-P.r[it->i]), &(P.Boundaries[it->j/2].center[0]) , nullptr, &(P.Boundaries[it->j/2].omega[0])) ;
689  //printf("%g | %g %g | %g %g | %g %g\n", P.Boundaries[it->j/2][4+2], (X[it->i]+tmpcn*(-P.r[it->i]))[0], (X[it->i]+tmpcn*(-P.r[it->i]))[1], P.Boundaries[it->j/2][4], P.Boundaries[it->j/2][5], tmpvel[0], tmpvel[1]) ; fflush(stdout) ;
690  C.particle_movingwall(V[it->i],Omega[it->i],P.r[it->i], P.m[it->i], tmpcn, tmpvel, *it) ;
691  }
692  else if (P.Boundaries[it->j/2].Type == WallType::ELLIPSE)
693  {
694  assert((d==2)) ;
695  std::vector<double> tmpcn(2) ;
696  #pragma GCC diagnostic push
697  #pragma GCC diagnostic ignored "-Wstrict-aliasing"
698  uint64_t c=((uint64_t)(it->ghostdir) <<32 | (uint64_t)(it->ghost)) ;
699  double tparam = *reinterpret_cast<double*>(&c) ;
700  #pragma GCC diagnostic pop
701  tmpcn[0]=X[it->i][0]-(P.Boundaries[it->j/2].centerx+P.Boundaries[it->j/2].semiaxisx*cos(tparam)) ;
702  tmpcn[1]=X[it->i][1]-(P.Boundaries[it->j/2].centery+P.Boundaries[it->j/2].semiaxisy*sin(tparam)) ;
703  tmpcn/=sqrt(tmpcn[0]*tmpcn[0]+tmpcn[1]*tmpcn[1]);
704  C.particle_wall( V[it->i],Omega[it->i],P.r[it->i], P.m[it->i], tmpcn, *it) ;
705  }
706  else
707  {
708 
709  Tools<d>::unitvec(tmpcn, it->j/2) ;
710  tmpcn=tmpcn*(-((it->j%2==0)?-1:1)) ; // l give the orientation (+1 or -1)
711  C.particle_wall( V[it->i],Omega[it->i],P.r[it->i], P.m[it->i], tmpcn, *it) ;
712  }
713 
714  //Tools<d>::vAdd(F[it->i], Act.Fn, Act.Ft) ; // F[it->i] += (Act.Fn+Act.Ft) ;
715  //Torque[it->i] += Act.Torquei ;
716 
717  Tools<d>::vAddFew(F[it->i], C.Act.Fn, C.Act.Ft, Fcorr[it->i]) ;
718  Tools<d>::vAddOne(Torque[it->i], C.Act.Torquei, TorqueCorr[it->i]) ;
719 
720  if ( P.wallforcecompute || ( P.wallforcerequested && !P.wallforcecomputed ) ) MP.delayingwall(ID, it->j, C.Act) ;
721  }
722 
723  // Particle mesh contacts
724  for (auto it = CLm.v.begin() ; it != CLm.v.end() ; it++)
725  {
726  //printf("@ %g | %g %g %g | %d \n", it->contactlength, it->contactpoint[0], it->contactpoint[1], it->contactpoint[2], it->submeshid) ; fflush(stdout) ;
727  C.particle_mesh ( X[it->i], V[it->i], Omega[it->i], P.r[it->i], P.m[it->i], *it) ;
728 
729  Tools<d>::vAddFew(F[it->i], C.Act.Fn, C.Act.Ft, Fcorr[it->i]) ;
730  Tools<d>::vAddOne(Torque[it->i], C.Act.Torquei, TorqueCorr[it->i]) ;
731  }
732 
733  #ifndef NO_OPENMP
734  MP.timing_forces[ID] += omp_get_wtime()-timebeg;
735  #endif
736  } //END PARALLEL PART
737  //ParticleForce = calculateParticleForce() ;
738 
739  // Finish by sequencially adding the grains that were not owned by the parallel proc when computed
740  for (int i=0 ; i<MP.P ; i++)
741  {
742  for (uint j=0 ; j<MP.delayed_size[i] ; j++)
743  {
744  Tools<d>::vSubFew(F[MP.delayedj[i][j]], MP.delayed[i][j].Fn, MP.delayed[i][j].Ft, Fcorr[MP.delayedj[i][j]]) ;
745  Tools<d>::vAddOne(Torque[MP.delayedj[i][j]], MP.delayed[i][j].Torquej, TorqueCorr[MP.delayedj[i][j]]) ;
746  }
747  }
748 
749  MP.delayed_clean() ;
750 
751  // Benchmark::stop_clock("Forces");
752  //---------- Velocity Verlet step 3 : compute the new velocities
753  // Benchmark::start_clock("Verlet last");
754 
755  P.RigidBodies.process_forces(V, F, Torque, P.m, P.g) ;
756 
757 
758  #pragma omp parallel for default(none) shared(N) shared(P) shared(V) shared(Omega) shared(F) shared(FOld) shared(Torque) shared(TorqueOld) shared(dt)
759  for (int i=0 ; i<N ; i++)
760  {
761  //printf("%10g %10g %10g\n%10g %10g %10g\n%10g %10g %10g\n\n", A[0][0], A[0][1], A[0][2], A[0][3], A[0][4], A[0][5], A[0][6], A[0][7], A[0][8]) ;
762  if (P.Frozen[i]) {Tools<d>::setzero(TorqueOld[i]) ; Tools<d>::setzero(F[i]) ; Tools<d>::setzero(FOld[i]) ; /*Tools<d>::setzero(V[i]) ; */ Tools<d>::setzero(Omega[i]) ; }
763 
764  Tools<d>::vAddScaled(V[i], dt/2./P.m[i], F[i], FOld[i]) ; //V[i] += (F[i] + FOld[i])*(dt/2./P.m[i]) ;
765  Tools<d>::vAddScaled(Omega[i], dt/2./P.I[i], Torque[i], TorqueOld[i]) ; // Omega[i] += (Torque[i]+TorqueOld[i])*(dt/2./P.I[i]) ;
766  if ( !P.Frozen[i] && P.damping > 0.0 ) {
767  Tools<d>::vSubScaled(Omega[i], P.damping, Omega[i]) ; // BENJY - add damping to Omega
768  Tools<d>::vSubScaled(V[i], P.damping, V[i]) ; // BENJY - add damping to velocity
769  }
770 
771  FOld[i]=F[i] ;
772  TorqueOld[i]=Torque[i] ;
773  } // END OF PARALLEL SECTION
774 
775  // Benchmark::stop_clock("Verlet last");
776 
777 
778  // Check events
779  P.check_events(t, X,V,Omega) ;
780 
782  {
783  P.wallforcecomputed = true;
784  Tools<d>::setzero(WallForce) ;
785  for (int i=0 ; i<MP.P ; i++)
786  for (uint j=0 ; j<MP.delayedwall_size[i] ; j++)
787  Tools<d>::vSubFew(WallForce[MP.delayedwallj[i][j]], MP.delayedwall[i][j].Fn, MP.delayedwall[i][j].Ft) ;
788  }
789 
790  // Output something at some point I guess
791  if (ti % P.tdump==0)
792  {
793  Tools<d>::setzero(Z) ; for (auto &v: MP.CLp) v.coordinance(Z) ;
794  P.dumphandling (ti, t, X, V, Vmag, A, Omega, OmegaMag, PBCFlags, Z, MP) ;
795  std::fill(PBCFlags.begin(), PBCFlags.end(), 0);
796 
797  if (P.wallforcecompute)
798  {
799  char path[5000] ; sprintf(path, "%s/LogWallForce-%05d.txt", P.Directory.c_str(), ti) ;
800  Tools<d>::setzero(WallForce) ;
801 
802  for (int i=0 ; i<MP.P ; i++)
803  for (uint j=0 ; j<MP.delayedwall_size[i] ; j++)
804  Tools<d>::vSubFew(WallForce[MP.delayedwallj[i][j]], MP.delayedwall[i][j].Fn, MP.delayedwall[i][j].Ft) ;
805 
806  Tools<d>::savetxt(path, WallForce, ( char const *)("Force on the various walls")) ;
807  }
808 
809  //fprintf(stderr, "%g %g\n", X[0][0], X[0][1]) ;
810  }
811  if (P.n_restart >= 0 && ti % P.n_restart == 0 && ti != 0)
812  {
813  if (P.restart_flag & 1)
814  save_restart(P.Directory + "/" + P.restart_filename+"-"+std::to_string(ti)) ;
815  else
816  save_restart(P.Directory + "/" + P.restart_filename+"-"+((ti/P.n_restart)%2==0?"even":"odd")) ;
817  }
818 
820 
821 
822  // Load balancing on the procs as needed
823  #ifndef NO_OPENMP
824  MP.num_time++ ;
825  if (MP.num_time>50)
826  {
828  // Cleaning the load balancing
829  MP.num_time = 0 ;
830  MP.timing_contacts = vector<double>(MP.P,0) ;
831  MP.timing_forces = vector<double>(MP.P,0) ;
832  }
833  #endif
834 
835  //save_restart(*this, "restart_archive.bin") ;
836  }
837  }
838 
839  //-------------------------
843  void finalise ()
844  {
845  P.finalise() ;
846  printf("This is the end ...\n") ;
847  //fclose(logfile) ;
848  }
849 
850  //-------------------------------------------------------------------
852  Vector2Djs getX() { return to_js_array(X); }
853 
855  Vector1Djs getRadii() { return to_js_array(P.r); }
856 
858  void setRadius(int id, double radius) { P.r[id] = radius; } // NOTE: NOT UPDATING THE MASS!!! THIS IS SUCH A BAD IDEA
859 
861  void setMass(int id, double mass) { P.m[id] = mass; }
862 
865 
867  Vector1Djs getRotationRate() { Tools<d>::norm(OmegaMag, Omega) ; return to_js_array(OmegaMag); }
868 
871  {
872  auto [_, res] = MP.contacts2array (ExportData::IDS | ExportData::FN, X, P) ;
873  return to_js_array(res);
874  }
875 
878  {
879  auto [_, res] = MP.contacts2array (static_cast<ExportData>(flags), X, P) ;
880  return to_js_array(res);
881  }
882 
883 
884 
885  // /** \brief DEPRECATED Expose the array of contact forces and velocities. \ingroup API */
886  /*std::vector<std::vector<double>> calculateParticleForce()
887  {
888  std::vector<std::vector<double>> res ;
889  std::vector<double> tmpfrc ;
890  tmpfrc.resize(2+4*d) ;
891  for (size_t i=0 ; i<MP.CLp.size() ; i++)
892  {
893  for (auto it = MP.CLp[i].v.begin() ; it!=MP.CLp[i].v.end() ; it++)
894  {
895  if (it->infos == nullptr) continue ;
896  tmpfrc[0]=it->i ;
897  tmpfrc[1]=it->j ;
898  for (int j=0 ; j<d ; j++) tmpfrc[2+j]=it->infos->Fn[j] ;
899  for (int j=0 ; j<d ; j++) tmpfrc[2+d+j]=it->infos->Ft[j] ;
900  for (int j=0 ; j<d ; j++) tmpfrc[2+2*d+j]=it->infos->vn[j] ;
901  for (int j=0 ; j<d ; j++) tmpfrc[2+3*d+j]=it->infos->vt[j] ;
902  res.push_back(tmpfrc) ;
903  }
904  }
905 
906  return res;
907  }*/
908 
910  //void setX(std::vector < std::vector <double> > X_) { X = X_; }
911 
913  void setVelocity(int id, Vector1Djs vel) {
914  auto vel2 = from_js_array(vel) ;
915  for (int i=0 ; i<d ; i++) {
916  V[id][i] = vel2[i];
917  }
918  }
919 
922  auto omega2 = from_js_array(omega) ;
923  for (int i=0; i<(d*(d-1)/2); i++) {
924  Omega[id][i] = omega2[i];
925  }
926  }
927 
928 
930  void fixParticle(int a, Vector1Djs loc) {
931  auto loc2 = from_js_array (loc) ;
932  for (int i=0 ; i<d ; i++) {
933  X[a][i] = loc2[i];
934  V[a][i] = 0;
935  }
936  for (int i=0; i<(d*(d-1)/2); i++) {
937  Omega[a][i] = 0;
938  }
939  }
940 
942  void setFrozen(int a) {
943  P.Frozen[a] = true;
944  }
945 
947  double getTime() { return t; }
948 
950  double getGravityAngle() { return P.gravityrotateangle; }
951 
952 
955 
957  Vector1Djs getBoundary(int a) { return to_js_array(P.Boundaries[a].as_vector()); }
958  void setBoundary(int a, Vector1Djs loc) {
959  auto loc2 = from_js_array(loc) ;
960  P.Boundaries[a].xmin = loc2[0]; // low value
961  P.Boundaries[a].xmax = loc2[1]; // high value
962  P.Boundaries[a].delta = loc2[1] - loc2[0]; // length
963  if ( P.Boundaries[a].Type == WallType::PBC_LE ) {
964  P.Boundaries[a].vel = loc2[2];
965  }
966  }
967 
970  // std::cout<<P.wallforcerequested<<" "<<P.wallforcecomputed<<std::endl;
971  P.wallforcerequested = true;
972  if ( P.wallforcecomputed ) {
973  P.wallforcerequested = false;
974  P.wallforcecomputed = false;
975  return to_js_array(WallForce);
976  }
977  else {
978  return to_js_array(empty_array) ;
979  }
980  }
981 
983  void setExternalForce (int id, int duration, Vector1Djs force)
984  {
985  auto force2 = from_js_array(force) ;
986 
987  printf("\nSetting the force: %g %g %g %g\n", force2[0],force2[1],force2[2],force2[3]);
988  ExternalAction.resize(ExternalAction.size()+1) ;
989  ExternalAction[ExternalAction.size()-1].id = id ;
990  ExternalAction[ExternalAction.size()-1].duration = duration ;
991  ExternalAction[ExternalAction.size()-1].set(force2, v1d(d,0), v1d(d*(d-1)/2,0), v1d(d*(d-1)/2,0)) ;
992  }
993 
994  void randomDrop()
995  {
996  unsigned long int seed = 5489UL ;
997  boost::random::mt19937 rng(seed);
998  boost::random::uniform_01<boost::mt19937> rand(rng) ;
999  for (int i=0 ; i<N ; i++)
1000  {
1001  for(int dd=0 ; dd < d ; dd++)
1002  {
1003  if (P.Boundaries[dd].Type==WallType::PBC)
1004  X[i][dd] = rand()*P.Boundaries[dd].delta + P.Boundaries[dd].xmin ;
1005  else
1006  X[i][dd] = rand()*(P.Boundaries[dd].delta-2*P.r[i]) + P.Boundaries[dd].xmin + P.r[i] ;
1007  }
1008  }
1009  }
1010 
1011 } ;
1012 
1013 #ifdef EMSCRIPTEN
1014 #ifdef USEBINDINGS
1015 #include "emscripten_specific.h"
1016 #endif
1017 #endif
1018 
std::vector< T > from_js_array(std::vector< T > &data)
Definition: DEMND.h:82
std::vector< std::vector< double > > Vector2Djs
Definition: DEMND.h:78
std::vector< double > Vector1Djs
Definition: DEMND.h:79
std::vector< std::vector< T > > to_js_array(std::vector< std::vector< T >> &data)
Definition: DEMND.h:80
NLOHMANN_BASIC_JSON_TPL_DECLARATION std::string to_string(const NLOHMANN_BASIC_JSON_TPL &j)
user-defined to_string function for JSON values
Definition: json.hpp:25318
boost::random::mt19937 rng(static_cast< unsigned int >(std::time(nullptr)))
Binary input and output archives.
#define CEREAL_NVP(T)
Creates a name value pair for the variable T with the same name as the variable.
Definition: cereal.hpp:72
Support for types found in <chrono>
All the cells making the space, with related function for creating the cell array,...
Definition: Cells.h:44
int contacts(int ID, std::pair< int, int > bounds, CLp_it_t< d > &CLp_it, ContactList< d > &CLnew, std::vector< std::vector< double >> const &X, std::vector< double > const &r, double LE_displacement)
Definition: Cells.h:482
int allocate_to_cells(std::vector< std::vector< double >> &X)
Definition: Cells.h:432
std::vector< Cell > cells
Definition: Cells.h:141
int init_cells(std::vector< Boundary< d >> &boundaries, double ds)
Definition: Cells.h:236
Handles lists of contacts with meshes.
Definition: ContactList.h:298
bool check_mesh_dst_contact(Mesh< d > &mesh, cv1d &Xo, double r, cpm< d > &c)
Definition: ContactList.h:310
Action< d > * default_action()
Easy allocation of a default contact to initialise new contacts.
Definition: ContactList.h:301
list< cpm< d > > v
Definition: ContactList.h:300
void reset()
Go to the contact list beginning.
Definition: ContactList.h:302
void finalise()
Definition: ContactList.h:303
Handles lists of contacts.
Definition: ContactList.h:208
bool(ContactList::* check_ghost)(bitdim, const Parameters< d > &, cv1d &, cv1d &, double, double, cp< d > &, int startd, double partialsum, bitdim mask)
Definition: ContactList.h:270
list< cp< d > > v
Contains the list of contact.
Definition: ContactList.h:277
void finalise()
Go to the end of the contact list, erasing any remaining contact which opened.
Definition: ContactList.h:213
Action< d > * default_action()
Easy allocation of a default contact to initialise new contacts.
Definition: ContactList.h:272
void reset()
Go to the contact list beginning.
Definition: ContactList.h:211
Calculate contact forces.
Definition: Contacts.h:23
void(Contacts::* particle_ghost)(cv1d &Xi, cv1d &Vi, cv1d &Omegai, double ri, double mi, cv1d &Xj, cv1d &Vj, cv1d &Omegaj, double rj, double mj, cp< d > &Contact, bool isdumptime)
Function pointer to the function to calculate the ghost-to-particle contact forces.
Definition: Contacts.h:40
Action< d > Act
Resulting Action.
Definition: Contacts.h:129
Manual multiprocessor handling for OpenMP, for max efficiency & avoiding bugs & race conditions,...
Definition: Multiproc.h:60
vector< uint > delayed_size
Max length of the delayed vector for each thread. Can grow as needed on call to delaying()
Definition: Multiproc.h:285
vector< double > timing_contacts
Definition: Multiproc.h:278
int P
Number of threads.
Definition: Multiproc.h:291
vector< Contacts< d > > C
Dummy Contacts for independent calculation per processor.
Definition: Multiproc.h:275
vector< uint > delayedwall_size
Max length of the delayed wall vector for each thread. Can grow as needed on call to delaying()
Definition: Multiproc.h:289
vector< vector< Action< d > > > delayedwall
Records the delayed Action.
Definition: Multiproc.h:287
vector< ContactListMesh< d > > CLm
ContactList particle-mesh for each processor.
Definition: Multiproc.h:274
vector< vector< Action< d > > > delayed
Records the delayed Action.
Definition: Multiproc.h:283
vector< ContactList< d > > CLp
ContactList particle-particle for each processor.
Definition: Multiproc.h:271
void merge_newcontacts()
Definition: Multiproc.h:94
vector< int > share
Particle share between threads. A thread ID own particles with index between share[ID] and share[ID+1...
Definition: Multiproc.h:276
vector< ContactList< d > > CLw
ContactList particle-wall for each processor.
Definition: Multiproc.h:273
CLp_it_t< d > CLp_it
Iterator list for fast access to particle contacts.
Definition: Multiproc.h:280
bool ismine(int ID, int j)
Check if a particle j belongs to the thread ID.
Definition: Multiproc.h:87
int num_time
Number of sample of time spent. Resets when load_balance() is called.
Definition: Multiproc.h:279
vector< int > sharecell
Cell share between threads (for contact detection based on cells). A thread ID own cells with index b...
Definition: Multiproc.h:277
vector< double > timing_forces
Used to record the time spent by each thread.
Definition: Multiproc.h:278
vector< vector< int > > delayedj
Records the j id of the particle in the associated delayed action.
Definition: Multiproc.h:284
vector< ContactList< d > > CLp_new
New particle-particle contacts for each processor.
Definition: Multiproc.h:272
vector< vector< int > > delayedwallj
Records the j id of the wall in the associated delayed action.
Definition: Multiproc.h:288
void initialise(int NN, int PP, Parameters< d > &Param)
Definition: Multiproc.h:63
Definition: Octree.h:11
int init_cells(std::vector< Boundary< d >> &boundaries, std::vector< double > &r)
Definition: Octree.h:13
int contacts(int ID, std::pair< int, int > bounds, CLp_it_t< d > &CLp_it, ContactList< d > &CLnew, std::vector< std::vector< double >> const &X, std::vector< double > const &r, double LE_displacement)
Definition: Octree.h:78
int cells_to_split()
Definition: Octree.h:42
int allocate_to_cells(std::vector< std::vector< double >> &X)
Definition: Octree.h:58
Generic class to handle the simulation set up.
Definition: Parameters.h:128
ContactStrategies contact_strategy
Strategy for the contact detection.
Definition: Parameters.h:188
vector< double > g
Gravity vector.
Definition: Parameters.h:195
int n_restart
Restart writing frequency.
Definition: Parameters.h:210
bool orientationtracking
Track orientation?
Definition: Parameters.h:199
void load_datafile(char path[], v2d &X, v2d &V, v2d &Omega)
Load and parse input script.
Definition: Parameters.h:507
XMLWriter * xmlout
Definition: Parameters.h:371
void(Parameters::* update_gravity)(double time)
Definition: Parameters.h:265
bool perform_forceinsphere(v1d &X)
Definition: Parameters.h:231
void perform_PBCLE_move()
Definition: Parameters.h:448
double T
Run until this time (note it is a floating point).
Definition: Parameters.h:175
bool forceinsphere
Definition: Parameters.h:185
bool wallforcecompute
Compute for on the wall?
Definition: Parameters.h:200
vector< double > r
Particle radii.
Definition: Parameters.h:192
int N
Number of particles.
Definition: Parameters.h:172
RigidBodies_< d > RigidBodies
Handle all the rigid bodies.
Definition: Parameters.h:208
void perform_PBCLE(v1d &X, v1d &V, uint32_t &PBCFlag)
Definition: Parameters.h:425
int tinfo
Show detail information on scren every this many timesteps.
Definition: Parameters.h:174
bool wallforcerequested
Compute for on the wall?
Definition: Parameters.h:201
double graddesc_gamma
Decay rate parameters for the gradient descent algorithm.
Definition: Parameters.h:203
void finalise()
Close opened dump files.
Definition: Parameters.h:1306
string Directory
Saving directory.
Definition: Parameters.h:198
int dumphandling(int ti, double t, v2d &X, v2d &V, v1d &Vmag, v2d &A, v2d &Omega, v1d &OmegaMag, vector< uint32_t > &PBCFlags, v1d &Z, Multiproc< d > &MP)
Dump writing functions.
Definition: Parameters.h:1316
int set_boundaries()
Set default boundaries.
Definition: Parameters.h:398
double damping
Definition: Parameters.h:184
vector< bool > Frozen
Frozen atom if true.
Definition: Parameters.h:196
double dt
timestep
Definition: Parameters.h:176
void perform_PBC(v1d &X, uint32_t &PBCFlags)
Bring particle back in the simulation box if the grains cross the boundaries.
Definition: Parameters.h:412
vector< Mesh< d > > Meshes
Handle all meshes.
Definition: Parameters.h:209
std::string restart_filename
Restart filename.
Definition: Parameters.h:211
vector< double > m
Particle mass.
Definition: Parameters.h:193
double cellsize
Size of cells for contact detection.
Definition: Parameters.h:186
vector< Boundary< d > > Boundaries
List of boundaries. Second dimension is {min, max, length, type}.
Definition: Parameters.h:197
void check_events(float time, v2d &X, v2d &V, v2d &Omega)
Verify if an event triggers at the current time time.
Definition: Parameters.h:531
bool wallforcecomputed
Compute for on the wall?
Definition: Parameters.h:202
void interpret_command(istream &in, v2d &X, v2d &V, v2d &Omega)
Parse input script commands.
Definition: Parameters.h:561
double graddesc_tol
Tolerance for the gradient descent algorithm.
Definition: Parameters.h:204
vector< double > I
Particule moment of inertia.
Definition: Parameters.h:194
vector< std::pair< ExportType, ExportData > > dumps
Vector linking dump file and data dumped.
Definition: Parameters.h:189
void perform_MOVINGWALL()
Move the boundary wall if moving.
Definition: Parameters.h:459
double gravityrotateangle
Definition: Parameters.h:207
int tdump
Write dump file every this many timesteps.
Definition: Parameters.h:173
unsigned char restart_flag
Definition: Parameters.h:212
Definition: DEMND.h:116
vector< uint32_t > Ghost_dir
Definition: DEMND.h:144
std::vector< std::vector< double > > FOld
Definition: DEMND.h:125
int N
Definition: DEMND.h:119
std::vector< double > Vmag
Definition: DEMND.h:128
v1d Atmp
Definition: DEMND.h:145
std::vector< std::vector< double > > Fcorr
Definition: DEMND.h:131
Cells< d > cells
Definition: DEMND.h:149
std::vector< std::vector< double > > V
Definition: DEMND.h:121
std::vector< std::vector< double > > A
Definition: DEMND.h:122
void serialize(Archive &ar)
Definition: DEMND.h:189
double dt
Definition: DEMND.h:153
Multiproc< d > MP
Definition: DEMND.h:148
std::vector< std::vector< double > > Torque
Definition: DEMND.h:126
std::vector< std::vector< double > > ParticleForce
Definition: DEMND.h:141
std::vector< uint32_t > PBCFlags
Definition: DEMND.h:138
void randomDrop()
Definition: DEMND.h:994
void init_from_file(char filename[])
Initialise the simulation from a file.
Definition: DEMND.h:240
std::vector< double > Z
Definition: DEMND.h:130
std::vector< SpecificAction< d > > ExternalAction
Definition: DEMND.h:136
std::vector< std::optional< int > > RigidBodyId
Definition: DEMND.h:134
Octree< d > octree
Definition: DEMND.h:150
Simulation(int NN)
Definition: DEMND.h:201
double t
Definition: DEMND.h:152
vector< uint32_t > Ghost
Definition: DEMND.h:143
void save_restart(const std::string &filename)
Definition: DEMND.h:157
std::vector< std::vector< double > > empty_array
Definition: DEMND.h:140
std::vector< std::vector< double > > TorqueOld
Definition: DEMND.h:127
clock_t tnow
Definition: DEMND.h:154
std::vector< std::vector< double > > TorqueCorr
Definition: DEMND.h:132
std::vector< std::vector< double > > X
Definition: DEMND.h:120
Parameters< d > P
Definition: DEMND.h:118
std::vector< std::vector< double > > Omega
Definition: DEMND.h:123
void setBoundary(int a, Vector1Djs loc)
Definition: DEMND.h:958
std::vector< std::vector< double > > WallForce
Definition: DEMND.h:139
std::vector< double > OmegaMag
Definition: DEMND.h:129
void step_forward_all()
Run the whole simulation as defined in the input script.
Definition: DEMND.h:300
std::vector< std::vector< double > > F
Definition: DEMND.h:124
void load_restart(const std::string &filename)
Definition: DEMND.h:164
static std::tuple< double, double > contact_ellipse_disk(std::vector< double > &X, double a, double b, double cx, double cy, double gamma=0.1, double tol=1e-5)
Definition: Tools.h:62
Static class to handle multi-dimensional mathematics, and more. It gets specialised for speed with te...
Definition: Tools.h:101
static void vAddScaled(v1d &res, double v, cv1d &a, cv1d &b)
Addition of 3 vectors in-place.
Definition: Tools.h:183
static void vAddFew(v1d &res, cv1d &a, cv1d &b)
Definition: Tools.h:182
static void vAddOne(v1d &res, cv1d &a, v1d &Corr)
Addition of 2 vectors in-place with error correction (Kahan summation algorithm)
Definition: Tools.h:217
static void vSubFew(v1d &res, cv1d &a, cv1d &b)
Subtraction of 2 vectors .
Definition: Tools.h:185
static void vSubScaled(v1d &res, double v, cv1d &a)
Subtraction of a scaled vector .
Definition: Tools.h:186
Definition: Xml.h:28
An input archive designed to load data saved using BinaryOutputArchive.
Definition: binary.hpp:89
An output archive designed to save data in a compact binary representation.
Definition: binary.hpp:52
Contact properties class.
Definition: ContactList.h:69
double contactlength
Length of the contact.
Definition: ContactList.h:115
int j
Index of second contacting particle or wall. If this is a wall contact, j=2*walldimension + (0 or 1 i...
Definition: ContactList.h:114
int i
Index of contacting particle.
Definition: ContactList.h:113
void setinfo(Action< d > *a)
Set information for contact force.
Definition: ContactList.h:92
uint32_t ghost
Contain ghost information about ghost contact, cf detailed description.
Definition: ContactList.h:116
uint32_t ghostdir
Contain ghost information about ghost direction, cf detailed description.
Definition: ContactList.h:117
Contact properties for mesh contacts (mainly), including contact point location, specialising cp.
Definition: ContactList.h:177
void setRadius(int id, double radius)
Set the radius of a specific particle.
Definition: DEMND.h:858
void setFrozen(int a)
Freeze a single particle.
Definition: DEMND.h:942
Vector1Djs getBoundary(int a)
Expose the array of boundaries.
Definition: DEMND.h:957
Vector2Djs getWallForce()
Expose the array of wall forces.
Definition: DEMND.h:969
Vector1Djs getRadii()
Expose the array of radii.
Definition: DEMND.h:855
void interpret_command(string in)
Interpret individual script command from string.
Definition: DEMND.h:292
void step_forward(int nt)
Advance the simulation for nt steps (actual duration nt*dt).
Definition: DEMND.h:310
Vector2Djs getContactForce()
DEPRECATED: Use getContactInfo with the appropriate flags instead. Expose the array of particle id an...
Definition: DEMND.h:870
void setMass(int id, double mass)
Set the mass of a specific particle.
Definition: DEMND.h:861
Vector1Djs getRotationRate()
Expose the array of orientation rate.
Definition: DEMND.h:867
void setExternalForce(int id, int duration, Vector1Djs force)
Set an additional external force on a particle for a certain duration.
Definition: DEMND.h:983
Vector2Djs getOrientation()
Expose the array of orientation.
Definition: DEMND.h:954
void finalise()
Settles the simulation, closing open files etc.
Definition: DEMND.h:843
Vector2Djs getX()
Expose the array of locations.
Definition: DEMND.h:852
Vector2Djs getVelocity()
Expose the array of velocities.
Definition: DEMND.h:864
void setAngularVelocity(int id, Vector1Djs omega)
Set the angular velocity of a single particle.
Definition: DEMND.h:921
void setVelocity(int id, Vector1Djs vel)
Set the array of locations.
Definition: DEMND.h:913
double getGravityAngle()
Expose the current gravity angle.
Definition: DEMND.h:950
Vector2Djs getContactInfos(int flags)
Expose the array of contact information.
Definition: DEMND.h:877
void finalise_init()
Tell NDDEM that the simulations are now initialised and we can start running.
Definition: DEMND.h:249
double getTime()
Expose the current time.
Definition: DEMND.h:947
void fixParticle(int a, Vector1Djs loc)
Set a single particle location, velocity, and angular velocity.
Definition: DEMND.h:930
void delaying(int ID, int j, Action< d > &act)
Record the action to be added later to the relevent atom in sequencial settings, avoid potential race...
Definition: Multiproc.h:385
static void orthonormalise(v1d &A)
Orthonormalise A using the Gram-Shmidt process, in place.
Definition: Tools.h:741
static void setzero(v2d &a)
Set a matrix to zero in-place.
Definition: Tools.h:117
static void unitvec(vector< double > &v, int n)
Construct a unit vector in place.
Definition: Tools.h:735
void particle_particle(cv1d &Xi, cv1d &Vi, cv1d &Omegai, double ri, double mi, cv1d &Xj, cv1d &Vj, cv1d &Omegaj, double rj, double mj, cp< d > &Contact, bool isdumptime)
Force & torque between 2 particles.
Definition: Contacts.h:171
ExportData
Definition: Parameters.h:46
@ vel
Definition: Typedefs.h:19
@ omega
Definition: Typedefs.h:19
@ radius
Definition: Typedefs.h:19
@ mass
Definition: Typedefs.h:19
void particle_mesh(cv1d &Xi, cv1d &Vi, cv1d &Omegai, double ri, double mi, cpm< d > &Contact)
Force & torque between particle and mesh.
Definition: Contacts.h:401
void particle_movingwall(cv1d &Vi, cv1d &Omegai, double ri, double mi, cv1d &cn, cv1d &Vj, cp< d > &Contact)
Force & torque between a particle and a moving wall. Vj is the velocity of the wall at the contact po...
Definition: Contacts.h:331
static int savetxt(char path[], const v2d &table, char const header[])
Definition: Tools.h:349
void load_balance(ContactStrategies contactstrategy)
Modify the atom share between threads to achieve better load balance between the threads based on the...
Definition: Multiproc.h:437
unsigned int uint
Definition: Typedefs.h:8
void delayedwall_clean()
Clean the record of the force on the wal.
Definition: Multiproc.h:428
auto contacts2array(ExportData exp, cv2d &X, Parameters< d > &P)
pack the contact data in a 2d array
Definition: Multiproc.h:568
int insert(const cpm< d > &a)
Insert a contact, maintaining sorting with increasing i, and removing missing contacts on traversal.
Definition: ContactList.h:451
void delayed_clean()
Clean the record list.
Definition: Multiproc.h:420
int insert(const cp< d > &a)
Insert a contact, maintaining sorting with increasing i, and removing missing contacts on traversal.
Definition: ContactList.h:430
static v1d matmult(cv1d &A, cv1d &B)
Multiply 2 matrix together.
Definition: Tools.h:680
unsigned int bitdim
Definition: Typedefs.h:17
static v1d skewexpand(cv1d &A)
Return the skew symetrix matrix M stored on d(d-1)/2 component as a full flattened matrix with d^2 co...
Definition: Tools.h:660
vector< std::pair< ExportType, ExportData > > * toclean
Definition: DEMND.cpp:21
void particle_wall(cv1d &Vi, cv1d &Omegai, double ri, double mi, cv1d &cn, cp< d > &Contact)
Force & torque between a particle and a wall.
Definition: Contacts.h:261
void delayingwall(int ID, int j, Action< d > &act)
Record the action on the wall. Only usefull if the force on the wall needs to be calculated.
Definition: Multiproc.h:402
XMLWriter * xmlout
Definition: DEMND.cpp:22
static void setgravity(v2d &a, v1d &g, v1d &m)
Set the gravity. .
Definition: Tools.h:154
void splitcells(int C)
Definition: Multiproc.h:376
vector< double > v1d
Definition: Typedefs.h:9
static void surfacevelocity(v1d &res, cv1d &p, double *com, double *vel_com, double *omega_com)
Definition: Tools.h:131
static void initialise()
Initialise the member variables, in particular the variables to handle skew-symmetric flattened matri...
Definition: Tools.h:318
static double norm(const vector< double > &a)
Norm of a vector.
Definition: Tools.h:119
@ CELLS
Definition: Typedefs.h:22
@ OCTREE
Definition: Typedefs.h:22
@ ROTATINGSPHERE
uint d
int N
Support for types found in <list>
Support for types found in <map>
Support for std::optional.
const GenericPointer< typename T::ValueType > T2 T::AllocatorType & a
Definition: pointer.h:1181
unsigned int uint32_t
Definition: stdint.h:126
unsigned __int64 uint64_t
Definition: stdint.h:136
Support for types found in <string>
Support for types found in <utility>
Support for types found in <vector>
XML input and output archives.