1 // clang-format off
2 /* ----------------------------------------------------------------------
3    LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
4    https://www.lammps.org/, Sandia National Laboratories
5    Steve Plimpton, sjplimp@sandia.gov
6 
7    Copyright (2003) Sandia Corporation.  Under the terms of Contract
8    DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
9    certain rights in this software.  This software is distributed under
10    the GNU General Public License.
11 
12    See the README file in the top-level LAMMPS directory.
13 ------------------------------------------------------------------------- */
14 
15 /* ----------------------------------------------------------------------
16    Contributing author: Trung Dac Nguyen (ndactrung@gmail.com)
17 ------------------------------------------------------------------------- */
18 
19 #include "fix_wall_body_polyhedron.h"
20 #include <cmath>
21 #include <cstring>
22 #include "atom.h"
23 #include "atom_vec_body.h"
24 #include "body_rounded_polyhedron.h"
25 #include "domain.h"
26 #include "update.h"
27 #include "force.h"
28 #include "math_const.h"
29 #include "math_extra.h"
30 #include "memory.h"
31 #include "error.h"
32 
33 using namespace LAMMPS_NS;
34 using namespace FixConst;
35 using namespace MathConst;
36 
37 enum{XPLANE=0,YPLANE=1,ZPLANE=2};    // XYZ PLANE need to be 0,1,2
38 enum{HOOKE,HOOKE_HISTORY};
39 
40 enum {INVALID=0,NONE=1,VERTEX=2};
41 enum {FAR=0,XLO,XHI,YLO,YHI,ZLO,ZHI};
42 
43 //#define _POLYHEDRON_DEBUG
44 #define DELTA 10000
45 #define EPSILON 1e-2
46 #define BIG 1.0e20
47 #define MAX_CONTACTS 4  // maximum number of contacts for 2D models
48 #define EFF_CONTACTS 2  // effective contacts for 2D models
49 
50 /* ---------------------------------------------------------------------- */
51 
FixWallBodyPolyhedron(LAMMPS * lmp,int narg,char ** arg)52 FixWallBodyPolyhedron::FixWallBodyPolyhedron(LAMMPS *lmp, int narg, char **arg) :
53   Fix(lmp, narg, arg)
54 {
55   if (narg < 7) error->all(FLERR,"Illegal fix wall/body/polyhedron command");
56 
57   if (!atom->body_flag)
58     error->all(FLERR,"Fix wall/body/polyhedron requires "
59                "atom style body/rounded/polyhedron");
60 
61   restart_peratom = 1;
62   create_attribute = 1;
63   wallstyle = -1;
64 
65   // wall/particle coefficients
66 
67   kn = utils::numeric(FLERR,arg[3],false,lmp);
68 
69   c_n = utils::numeric(FLERR,arg[4],false,lmp);
70   if (strcmp(arg[5],"NULL") == 0) c_t = 0.5 * c_n;
71   else c_t = utils::numeric(FLERR,arg[5],false,lmp);
72 
73   if (kn < 0.0 || c_n < 0.0 || c_t < 0.0)
74     error->all(FLERR,"Illegal fix wall/body/polyhedron command");
75 
76   // wallstyle args
77 
78   int iarg = 6;
79   if (strcmp(arg[iarg],"xplane") == 0) {
80     if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polyhedron command");
81     wallstyle = XPLANE;
82     if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG;
83     else lo = utils::numeric(FLERR,arg[iarg+1],false,lmp);
84     if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG;
85     else hi = utils::numeric(FLERR,arg[iarg+2],false,lmp);
86     iarg += 3;
87   } else if (strcmp(arg[iarg],"yplane") == 0) {
88     if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polyhedron command");
89     wallstyle = YPLANE;
90     if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG;
91     else lo = utils::numeric(FLERR,arg[iarg+1],false,lmp);
92     if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG;
93     else hi = utils::numeric(FLERR,arg[iarg+2],false,lmp);
94     iarg += 3;
95   } else if (strcmp(arg[iarg],"zplane") == 0) {
96     if (narg < iarg+3) error->all(FLERR,"Illegal fix wall/body/polyhedron command");
97     wallstyle = ZPLANE;
98     if (strcmp(arg[iarg+1],"NULL") == 0) lo = -BIG;
99     else lo = utils::numeric(FLERR,arg[iarg+1],false,lmp);
100     if (strcmp(arg[iarg+2],"NULL") == 0) hi = BIG;
101     else hi = utils::numeric(FLERR,arg[iarg+2],false,lmp);
102     iarg += 3;
103   } else error->all(FLERR,"Unknown wall style {}",arg[iarg]);
104 
105   // check for trailing keyword/values
106 
107   wiggle = 0;
108 
109   while (iarg < narg) {
110     if (strcmp(arg[iarg],"wiggle") == 0) {
111       if (iarg+4 > narg) error->all(FLERR,"Illegal fix wall/body/polyhedron command");
112       if (strcmp(arg[iarg+1],"x") == 0) axis = 0;
113       else if (strcmp(arg[iarg+1],"y") == 0) axis = 1;
114       else if (strcmp(arg[iarg+1],"z") == 0) axis = 2;
115       else error->all(FLERR,"Illegal fix wall/body/polyhedron command");
116       amplitude = utils::numeric(FLERR,arg[iarg+2],false,lmp);
117       period = utils::numeric(FLERR,arg[iarg+3],false,lmp);
118       wiggle = 1;
119       iarg += 4;
120     } else error->all(FLERR,"Illegal fix wall/body/polyhedron command");
121   }
122 
123   if (wallstyle == XPLANE && domain->xperiodic)
124     error->all(FLERR,"Cannot use wall in periodic dimension");
125   if (wallstyle == YPLANE && domain->yperiodic)
126     error->all(FLERR,"Cannot use wall in periodic dimension");
127   if (wallstyle == ZPLANE && domain->zperiodic)
128     error->all(FLERR,"Cannot use wall in periodic dimension");
129 
130   // setup oscillations
131 
132   if (wiggle) omega = 2.0*MY_PI / period;
133 
134   time_origin = update->ntimestep;
135 
136   dmax = nmax = 0;
137   discrete = nullptr;
138   dnum = dfirst = nullptr;
139 
140   edmax = ednummax = 0;
141   edge = nullptr;
142   ednum = edfirst = nullptr;
143 
144   facmax = facnummax = 0;
145   face = nullptr;
146   facnum = facfirst = nullptr;
147 
148   enclosing_radius = nullptr;
149   rounded_radius = nullptr;
150 }
151 
152 /* ---------------------------------------------------------------------- */
153 
~FixWallBodyPolyhedron()154 FixWallBodyPolyhedron::~FixWallBodyPolyhedron()
155 {
156   memory->destroy(discrete);
157   memory->destroy(dnum);
158   memory->destroy(dfirst);
159 
160   memory->destroy(edge);
161   memory->destroy(ednum);
162   memory->destroy(edfirst);
163 
164   memory->destroy(face);
165   memory->destroy(facnum);
166   memory->destroy(facfirst);
167 
168   memory->destroy(enclosing_radius);
169   memory->destroy(rounded_radius);
170 }
171 
172 /* ---------------------------------------------------------------------- */
173 
setmask()174 int FixWallBodyPolyhedron::setmask()
175 {
176   int mask = 0;
177   mask |= POST_FORCE;
178   return mask;
179 }
180 
181 /* ---------------------------------------------------------------------- */
182 
init()183 void FixWallBodyPolyhedron::init()
184 {
185   dt = update->dt;
186 
187   avec = (AtomVecBody *) atom->style_match("body");
188   if (!avec)
189     error->all(FLERR,"Pair body/rounded/polyhedron requires atom style body");
190   if (strcmp(avec->bptr->style,"rounded/polyhedron") != 0)
191     error->all(FLERR,"Pair body/rounded/polyhedron requires "
192                "body style rounded/polyhedron");
193   bptr = (BodyRoundedPolyhedron *) avec->bptr;
194 
195   // set pairstyle from body/polyhedronular pair style
196 
197   if (force->pair_match("body/rounded/polyhedron",1))
198     pairstyle = HOOKE;
199   else error->all(FLERR,"Fix wall/body/polyhedron is incompatible with Pair style");
200 }
201 
202 /* ---------------------------------------------------------------------- */
203 
setup(int vflag)204 void FixWallBodyPolyhedron::setup(int vflag)
205 {
206   if (utils::strmatch(update->integrate_style,"^verlet"))
207     post_force(vflag);
208 }
209 
210 /* ---------------------------------------------------------------------- */
211 
post_force(int)212 void FixWallBodyPolyhedron::post_force(int /*vflag*/)
213 {
214   double vwall[3],dx,dy,dz,del1,del2,rsq,wall_pos;
215   int i,ni,npi,ifirst,nei,iefirst,side;
216   double facc[3];
217 
218   // set position of wall to initial settings and velocity to 0.0
219   // if wiggle, set wall position and velocity accordingly
220 
221   double wlo = lo;
222   double whi = hi;
223   vwall[0] = vwall[1] = vwall[2] = 0.0;
224   if (wiggle) {
225     double arg = omega * (update->ntimestep - time_origin) * dt;
226     if (wallstyle == axis) {
227       wlo = lo + amplitude - amplitude*cos(arg);
228       whi = hi + amplitude - amplitude*cos(arg);
229     }
230     vwall[axis] = amplitude*omega*sin(arg);
231   }
232 
233   // loop over all my atoms
234   // rsq = distance from wall
235   // dx,dy,dz = signed distance from wall
236   // for rotating cylinder, reset vwall based on particle position
237   // skip atom if not close enough to wall
238   //   if wall was set to a null pointer, it's skipped since lo/hi are infinity
239   // compute force and torque on atom if close enough to wall
240   //   via wall potential matched to pair potential
241 
242   double **x = atom->x;
243   double **v = atom->v;
244   double **f = atom->f;
245   int *body = atom->body;
246   double *radius = atom->radius;
247   double **torque = atom->torque;
248   double **angmom = atom->angmom;
249   int *mask = atom->mask;
250   int nlocal = atom->nlocal;
251 
252   // grow the per-atom lists if necessary and initialize
253 
254   if (atom->nmax > nmax) {
255     memory->destroy(dnum);
256     memory->destroy(dfirst);
257     memory->destroy(ednum);
258     memory->destroy(edfirst);
259     memory->destroy(facnum);
260     memory->destroy(facfirst);
261     memory->destroy(enclosing_radius);
262     memory->destroy(rounded_radius);
263     nmax = atom->nmax;
264     memory->create(dnum,nmax,"fix:dnum");
265     memory->create(dfirst,nmax,"fix:dfirst");
266     memory->create(ednum,nmax,"fix:ednum");
267     memory->create(edfirst,nmax,"fix:edfirst");
268     memory->create(facnum,nmax,"fix:facnum");
269     memory->create(facfirst,nmax,"fix:facfirst");
270     memory->create(enclosing_radius,nmax,"fix:enclosing_radius");
271     memory->create(rounded_radius,nmax,"fix:rounded_radius");
272   }
273 
274   ndiscrete = nedge = nface = 0;
275   for (i = 0; i < nlocal; i++)
276     dnum[i] = ednum[i] = facnum[i] = 0;
277 
278   for (i = 0; i < nlocal; i++) {
279     if (mask[i] & groupbit) {
280 
281       if (body[i] < 0) continue;
282 
283       dx = dy = dz = 0.0;
284       side = FAR;
285       if (wallstyle == XPLANE) {
286         del1 = x[i][0] - wlo;
287         del2 = whi - x[i][0];
288         if (del1 < del2) {
289           dx = del1;
290           wall_pos = wlo;
291           side = XLO;
292         } else {
293           dx = -del2;
294           wall_pos = whi;
295           side = XHI;
296         }
297       } else if (wallstyle == YPLANE) {
298         del1 = x[i][1] - wlo;
299         del2 = whi - x[i][1];
300         if (del1 < del2) {
301           dy = del1;
302           wall_pos = wlo;
303           side = YLO;
304         } else {
305           dy = -del2;
306           wall_pos = whi;
307           side = YHI;
308         }
309       } else if (wallstyle == ZPLANE) {
310         del1 = x[i][2] - wlo;
311         del2 = whi - x[i][2];
312         if (del1 < del2) {
313           dy = del1;
314           wall_pos = wlo;
315           side = ZLO;
316         } else {
317           dy = -del2;
318           wall_pos = whi;
319           side = ZHI;
320         }
321       }
322 
323       rsq = dx*dx + dy*dy + dz*dz;
324       if (rsq > radius[i]*radius[i]) continue;
325 
326       if (dnum[i] == 0) body2space(i);
327       npi = dnum[i];
328       ifirst = dfirst[i];
329       nei = ednum[i];
330       iefirst = edfirst[i];
331 
332       if (npi == 1) {
333         sphere_against_wall(i, wall_pos, side, vwall, x, v, f, angmom, torque);
334         continue;
335       }
336 
337       // reset vertex and edge forces
338 
339       for (ni = 0; ni < npi; ni++) {
340         discrete[ifirst+ni][3] = 0;
341         discrete[ifirst+ni][4] = 0;
342         discrete[ifirst+ni][5] = 0;
343         discrete[ifirst+ni][6] = 0;
344       }
345 
346       for (ni = 0; ni < nei; ni++) {
347         edge[iefirst+ni][2] = 0;
348         edge[iefirst+ni][3] = 0;
349         edge[iefirst+ni][4] = 0;
350         edge[iefirst+ni][5] = 0;
351       }
352 
353       int num_contacts;
354       Contact contact_list[MAX_CONTACTS];
355 
356       num_contacts = 0;
357       facc[0] = facc[1] = facc[2] = 0;
358       edge_against_wall(i, wall_pos, side, vwall, x, f, torque,
359                         contact_list, num_contacts, facc);
360 
361     } // group bit
362   }
363 }
364 
365 /* ---------------------------------------------------------------------- */
366 
reset_dt()367 void FixWallBodyPolyhedron::reset_dt()
368 {
369   dt = update->dt;
370 }
371 
372 /* ----------------------------------------------------------------------
373    convert N sub-particles in body I to space frame using current quaternion
374    store sub-particle space-frame displacements from COM in discrete list
375 ------------------------------------------------------------------------- */
376 
body2space(int i)377 void FixWallBodyPolyhedron::body2space(int i)
378 {
379   int ibonus = atom->body[i];
380   AtomVecBody::Bonus *bonus = &avec->bonus[ibonus];
381   int nsub = bptr->nsub(bonus);
382   double *coords = bptr->coords(bonus);
383   int body_num_edges = bptr->nedges(bonus);
384   double* edge_ends = bptr->edges(bonus);
385   int body_num_faces = bptr->nfaces(bonus);
386   double* face_pts = bptr->faces(bonus);
387   double eradius = bptr->enclosing_radius(bonus);
388   double rradius = bptr->rounded_radius(bonus);
389 
390   // get the number of sub-particles (vertices)
391   // and the index of the first vertex of my body in the list
392 
393   dnum[i] = nsub;
394   dfirst[i] = ndiscrete;
395 
396   // grow the vertex list if necessary
397   // the first 3 columns are for coords, the last 3 for forces
398 
399   if (ndiscrete + nsub > dmax) {
400     dmax += DELTA;
401     memory->grow(discrete,dmax,7,"fix:discrete");
402   }
403 
404   double p[3][3];
405   MathExtra::quat_to_mat(bonus->quat,p);
406 
407   for (int m = 0; m < nsub; m++) {
408     MathExtra::matvec(p,&coords[3*m],discrete[ndiscrete]);
409     discrete[ndiscrete][3] = 0;
410     discrete[ndiscrete][4] = 0;
411     discrete[ndiscrete][5] = 0;
412     discrete[ndiscrete][6] = 0;
413     ndiscrete++;
414   }
415 
416   // get the number of edges (vertices)
417   // and the index of the first edge of my body in the list
418 
419   ednum[i] = body_num_edges;
420   edfirst[i] = nedge;
421 
422   // grow the edge list if necessary
423   // the first 2 columns are for vertex indices within body,
424   // the last 3 for forces
425 
426   if (nedge + body_num_edges > edmax) {
427     edmax += DELTA;
428     memory->grow(edge,edmax,6,"fix:edge");
429   }
430 
431   for (int m = 0; m < body_num_edges; m++) {
432     edge[nedge][0] = static_cast<int>(edge_ends[2*m+0]);
433     edge[nedge][1] = static_cast<int>(edge_ends[2*m+1]);
434     edge[nedge][2] = 0;
435     edge[nedge][3] = 0;
436     edge[nedge][4] = 0;
437     edge[nedge][5] = 0;
438     nedge++;
439   }
440 
441   // get the number of faces and the index of the first face
442 
443   facnum[i] = body_num_faces;
444   facfirst[i] = nface;
445 
446   // grow the face list if necessary
447   // the first 3 columns are for vertex indices within body, the last 3 for forces
448 
449   if (nface + body_num_faces > facmax) {
450     facmax += DELTA;
451     memory->grow(face,facmax,6,"pair:face");
452   }
453 
454   for (int m = 0; m < body_num_faces; m++) {
455     face[nface][0] = static_cast<int>(face_pts[3*m+0]);
456     face[nface][1] = static_cast<int>(face_pts[3*m+1]);
457     face[nface][2] = static_cast<int>(face_pts[3*m+2]);
458     face[nface][3] = 0;
459     face[nface][4] = 0;
460     face[nface][5] = 0;
461     nface++;
462   }
463 
464   enclosing_radius[i] = eradius;
465   rounded_radius[i] = rradius;
466 }
467 
468 /* ----------------------------------------------------------------------
469    Determine the interaction mode between a sphere against the wall
470 
471    i = atom i (body i)
472    x      = atoms' coordinates
473    f      = atoms' forces
474    torque = atoms' torques
475 ---------------------------------------------------------------------- */
476 
sphere_against_wall(int i,double wall_pos,int,double * vwall,double ** x,double ** v,double ** f,double ** angmom,double ** torque)477 int FixWallBodyPolyhedron::sphere_against_wall(int i, double wall_pos,
478      int /*side*/, double* vwall, double** x, double** v, double** f,
479      double** angmom, double** torque)
480 {
481   int mode;
482   double rradi,hi[3],d,delx,dely,delz,R,fpair,fx,fy,fz;
483 
484   rradi = rounded_radius[i];
485   mode = NONE;
486 
487   if (wallstyle == XPLANE) {
488     hi[0] = wall_pos;
489     hi[1] = x[i][1];
490     hi[2] = x[i][2];
491   } else if (wallstyle == YPLANE) {
492     hi[0] = x[i][0];
493     hi[1] = wall_pos;
494     hi[2] = x[i][2];
495   } else if (wallstyle == ZPLANE) {
496     hi[0] = x[i][0];
497     hi[1] = x[i][1];
498     hi[2] = wall_pos;
499   }
500 
501   distance(hi, x[i], d);
502 
503   if (d <= rradi) {
504     delx = x[i][0] - hi[0];
505     dely = x[i][1] - hi[1];
506     delz = x[i][2] - hi[2];
507     R = d - rradi;
508 
509     fpair = -kn * R;
510 
511     fx = delx*fpair/d;
512     fy = dely*fpair/d;
513     fz = delz*fpair/d;
514 
515     contact_forces(i, 1.0, x[i], hi, delx, dely, delz,
516                    fx, fy, fz, x, v, angmom, f, torque, vwall);
517     mode = VERTEX;
518   }
519 
520   return mode;
521 }
522 
523 /* ----------------------------------------------------------------------
524    Determine the interaction mode between i's vertices against the wall
525 
526    i = atom i (body i)
527    x      = atoms' coordinates
528    f      = atoms' forces
529    torque = atoms' torques
530    Output:
531      contact_list = list of contacts between i and the wall
532      num_contacts = number of contacts between i's vertices and the wall
533    Return:
534      number of contacts of the edge to the wall (0, 1 or 2)
535 ---------------------------------------------------------------------- */
536 
edge_against_wall(int i,double wall_pos,int side,double * vwall,double ** x,double **,double **,Contact *,int &,double *)537 int FixWallBodyPolyhedron::edge_against_wall(int i, double wall_pos,
538      int side, double* vwall, double** x, double** /*f*/, double** /*torque*/,
539      Contact* /*contact_list*/, int &/*num_contacts*/, double* /*facc*/)
540 {
541   int ni, nei, contact;
542   double rradi;
543 
544   nei = ednum[i];
545   rradi = rounded_radius[i];
546 
547   contact = 0;
548 
549   // loop through body i's edges
550 
551   for (ni = 0; ni < nei; ni++)
552     compute_distance_to_wall(i, ni, x[i], rradi, wall_pos, side, vwall, contact);
553 
554   return contact;
555 }
556 
557 /* -------------------------------------------------------------------------
558   Compute the distance between a vertex to the wall
559   another body
560   Input:
561     x0         = coordinate of the tested vertex
562     rradi      = rounded radius of the vertex
563     wall_pos   = position of the wall
564   Output:
565     d          = Distance from a point x0 to an wall
566     hi         = coordinates of the projection of x0 on the wall
567   contact      = 0 no contact between the queried vertex and the wall
568                  1 contact detected
569   return NONE    if there is no interaction
570          VERTEX  if the tested vertex interacts with the wall
571 ------------------------------------------------------------------------- */
572 
compute_distance_to_wall(int ibody,int edge_index,double * xmi,double rounded_radius_i,double wall_pos,int,double * vwall,int & contact)573 int FixWallBodyPolyhedron::compute_distance_to_wall(int ibody, int edge_index,
574                         double *xmi, double rounded_radius_i, double wall_pos,
575                         int /*side*/, double* vwall, int &contact)
576 {
577   int mode,ifirst,iefirst,npi1,npi2;
578   double d1,d2,xpi1[3],xpi2[3],hi[3];
579   double fx,fy,fz,fpair,delx,dely,delz,R;
580 
581   double** x = atom->x;
582   double** v = atom->v;
583   double** f = atom->f;
584   double** torque = atom->torque;
585   double** angmom = atom->angmom;
586 
587   // two ends of the edge from body i
588 
589   ifirst = dfirst[ibody];
590   iefirst = edfirst[ibody];
591   npi1 = static_cast<int>(edge[iefirst+edge_index][0]);
592   npi2 = static_cast<int>(edge[iefirst+edge_index][1]);
593 
594   xpi1[0] = xmi[0] + discrete[ifirst+npi1][0];
595   xpi1[1] = xmi[1] + discrete[ifirst+npi1][1];
596   xpi1[2] = xmi[2] + discrete[ifirst+npi1][2];
597 
598   xpi2[0] = xmi[0] + discrete[ifirst+npi2][0];
599   xpi2[1] = xmi[1] + discrete[ifirst+npi2][1];
600   xpi2[2] = xmi[2] + discrete[ifirst+npi2][2];
601 
602   // determine the intersection of the edge to the wall
603 
604   mode = NONE;
605   double j_a = 1.0;
606 
607   if (wallstyle == XPLANE) {
608     hi[0] = wall_pos;
609     hi[1] = xpi1[1];
610     hi[2] = xpi1[2];
611   } else if (wallstyle == YPLANE) {
612     hi[0] = xpi1[0];
613     hi[1] = wall_pos;
614     hi[2] = xpi1[2];
615   } else if (wallstyle == ZPLANE) {
616     hi[0] = xpi1[0];
617     hi[1] = xpi1[1];
618     hi[2] = wall_pos;
619   }
620 
621   distance(hi, xpi1, d1);
622 
623   if (d1 <= rounded_radius_i && static_cast<int>(discrete[ifirst+npi1][6]) == 0) {
624     delx = xpi1[0] - hi[0];
625     dely = xpi1[1] - hi[1];
626     delz = xpi1[2] - hi[2];
627     R = d1 - rounded_radius_i;
628 
629     fpair = -kn * R;
630 
631     fx = delx*fpair/d1;
632     fy = dely*fpair/d1;
633     fz = delz*fpair/d1;
634 
635     contact_forces(ibody, j_a, xpi1, hi, delx, dely, delz,
636                    fx, fy, fz, x, v, angmom, f, torque, vwall);
637     discrete[ifirst+npi1][6] = 1;
638     contact++;
639     mode = VERTEX;
640   }
641 
642   if (wallstyle == XPLANE) {
643     hi[0] = wall_pos;
644     hi[1] = xpi2[1];
645     hi[2] = xpi2[2];
646   } else if (wallstyle == YPLANE) {
647     hi[0] = xpi2[0];
648     hi[1] = wall_pos;
649     hi[2] = xpi2[2];
650   } else if (wallstyle == ZPLANE) {
651     hi[0] = xpi2[0];
652     hi[1] = xpi2[1];
653     hi[2] = wall_pos;
654   }
655 
656   distance(hi, xpi2, d2);
657 
658   if (d2 <= rounded_radius_i && static_cast<int>(discrete[ifirst+npi2][6]) == 0) {
659     delx = xpi2[0] - hi[0];
660     dely = xpi2[1] - hi[1];
661     delz = xpi2[2] - hi[2];
662     R = d2 - rounded_radius_i;
663 
664     fpair = -kn * R;
665 
666     fx = delx*fpair/d2;
667     fy = dely*fpair/d2;
668     fz = delz*fpair/d2;
669 
670     contact_forces(ibody, j_a, xpi2, hi, delx, dely, delz,
671                    fx, fy, fz, x, v, angmom, f, torque, vwall);
672     discrete[ifirst+npi2][6] = 1;
673     contact++;
674     mode = VERTEX;
675   }
676 
677   return mode;
678 }
679 
680 /* ----------------------------------------------------------------------
681   Compute contact forces between two bodies
682   modify the force stored at the vertex and edge in contact by j_a
683   sum forces and torque to the corresponding bodies
684   fn = normal friction component
685   ft = tangential friction component (-c_t * v_t)
686 ------------------------------------------------------------------------- */
687 
contact_forces(int ibody,double j_a,double * xi,double *,double delx,double dely,double delz,double fx,double fy,double fz,double ** x,double ** v,double ** angmom,double ** f,double ** torque,double * vwall)688 void FixWallBodyPolyhedron::contact_forces(int ibody,
689   double j_a, double *xi, double * /*xj*/, double delx, double dely, double delz,
690   double fx, double fy, double fz, double** x, double** v, double** angmom,
691   double** f, double** torque, double* vwall)
692 {
693   int ibonus;
694   double fxt,fyt,fzt,rsq,rsqinv;
695   double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
696   double fn[3],ft[3],vi[3];
697   double *quat, *inertia;
698   AtomVecBody::Bonus *bonus;
699 
700   // compute the velocity of the vertex in the space-fixed frame
701 
702   ibonus = atom->body[ibody];
703   bonus = &avec->bonus[ibonus];
704   quat = bonus->quat;
705   inertia = bonus->inertia;
706   total_velocity(xi, x[ibody], v[ibody], angmom[ibody],
707                  inertia, quat, vi);
708 
709   // vector pointing from the contact point on ibody to the wall
710 
711   rsq = delx*delx + dely*dely + delz*delz;
712   rsqinv = 1.0/rsq;
713 
714   // relative translational velocity
715 
716   vr1 = vi[0] - vwall[0];
717   vr2 = vi[1] - vwall[1];
718   vr3 = vi[2] - vwall[2];
719 
720   // normal component
721 
722   vnnr = vr1*delx + vr2*dely + vr3*delz;
723   vn1 = delx*vnnr * rsqinv;
724   vn2 = dely*vnnr * rsqinv;
725   vn3 = delz*vnnr * rsqinv;
726 
727   // tangential component
728 
729   vt1 = vr1 - vn1;
730   vt2 = vr2 - vn2;
731   vt3 = vr3 - vn3;
732 
733   // normal friction term at contact
734 
735   fn[0] = -c_n * vn1;
736   fn[1] = -c_n * vn2;
737   fn[2] = -c_n * vn3;
738 
739   // tangential friction term at contact
740   // excluding the tangential deformation term for now
741 
742   ft[0] = -c_t * vt1;
743   ft[1] = -c_t * vt2;
744   ft[2] = -c_t * vt3;
745 
746   fxt = fx; fyt = fy; fzt = fz;
747   fx = fxt * j_a + fn[0] + ft[0];
748   fy = fyt * j_a + fn[1] + ft[1];
749   fz = fzt * j_a + fn[2] + ft[2];
750 
751   f[ibody][0] += fx;
752   f[ibody][1] += fy;
753   f[ibody][2] += fz;
754   sum_torque(x[ibody], xi, fx, fy, fz, torque[ibody]);
755 
756   #ifdef _POLYHEDRON_DEBUG
757   printf("From contact forces: vertex fx %f fy %f fz %f\n"
758          "      torque body %d: %f %f %f\n"
759          "      torque body %d: %f %f %f\n",
760          fxt, fyt, fzt,
761          atom->tag[ibody],torque[ibody][0],torque[ibody][1],torque[ibody][2],
762          atom->tag[jbody],torque[jbody][0],torque[jbody][1],torque[jbody][2]);
763   #endif
764 }
765 
766 /* ----------------------------------------------------------------------
767   Compute the contact forces between two bodies
768   modify the force stored at the vertex and edge in contact by j_a
769   sum forces and torque to the corresponding bodies
770     fn = normal friction component
771     ft = tangential friction component (-c_t * vrt)
772 ------------------------------------------------------------------------- */
773 
contact_forces(Contact & contact,double j_a,double ** x,double ** v,double ** angmom,double ** f,double ** torque,double * vwall,double * facc)774 void FixWallBodyPolyhedron::contact_forces(Contact& contact, double j_a,
775                       double** x, double** v, double** angmom, double** f,
776                       double** torque, double* vwall, double* facc)
777 {
778   int ibody,ibonus,ifirst,ni;
779   double fx,fy,fz,delx,dely,delz,rsq,rsqinv;
780   double vr1,vr2,vr3,vnnr,vn1,vn2,vn3,vt1,vt2,vt3;
781   double fn[3],ft[3],vi[3];
782   double *quat, *inertia;
783   AtomVecBody::Bonus *bonus;
784 
785   ibody = contact.ibody;
786 
787   // compute the velocity of the vertex in the space-fixed frame
788 
789   ibonus = atom->body[ibody];
790   bonus = &avec->bonus[ibonus];
791   quat = bonus->quat;
792   inertia = bonus->inertia;
793   total_velocity(contact.xv, x[ibody], v[ibody], angmom[ibody],
794                  inertia, quat, vi);
795 
796   // vector pointing from the vertex to the point on the wall
797 
798   delx = contact.xv[0] - contact.xe[0];
799   dely = contact.xv[1] - contact.xe[1];
800   delz = contact.xv[2] - contact.xe[2];
801   rsq = delx*delx + dely*dely + delz*delz;
802   rsqinv = 1.0/rsq;
803 
804   // relative translational velocity
805 
806   vr1 = vi[0] - vwall[0];
807   vr2 = vi[1] - vwall[1];
808   vr3 = vi[2] - vwall[2];
809 
810   // normal component
811 
812   vnnr = vr1*delx + vr2*dely + vr3*delz;
813   vn1 = delx*vnnr * rsqinv;
814   vn2 = dely*vnnr * rsqinv;
815   vn3 = delz*vnnr * rsqinv;
816 
817   // tangential component
818 
819   vt1 = vr1 - vn1;
820   vt2 = vr2 - vn2;
821   vt3 = vr3 - vn3;
822 
823   // normal friction term at contact
824 
825   fn[0] = -c_n * vn1;
826   fn[1] = -c_n * vn2;
827   fn[2] = -c_n * vn3;
828 
829   // tangential friction term at contact
830   // excluding the tangential deformation term for now
831 
832   ft[0] = -c_t * vt1;
833   ft[1] = -c_t * vt2;
834   ft[2] = -c_t * vt3;
835 
836   // only the cohesive force is scaled by j_a
837 
838   ifirst = dfirst[ibody];
839   ni = contact.vertex;
840 
841   fx = discrete[ifirst+ni][3] * j_a + fn[0] + ft[0];
842   fy = discrete[ifirst+ni][4] * j_a + fn[1] + ft[1];
843   fz = discrete[ifirst+ni][5] * j_a + fn[2] + ft[2];
844   f[ibody][0] += fx;
845   f[ibody][1] += fy;
846   f[ibody][2] += fz;
847   sum_torque(x[ibody], contact.xv, fx, fy, fz, torque[ibody]);
848 
849   // accumulate forces to the vertex only
850 
851   facc[0] += fx; facc[1] += fy; facc[2] += fz;
852 
853   #ifdef _POLYHEDRON_DEBUG
854   printf("From contact forces: vertex fx %f fy %f fz %f\n"
855          "      torque body %d: %f %f %f\n",
856          discrete[ifirst+ni][3], discrete[ifirst+ni][4], discrete[ifirst+ni][5],
857          atom->tag[ibody],torque[ibody][0],torque[ibody][1],torque[ibody][2]);
858   #endif
859 }
860 
861 /* ----------------------------------------------------------------------
862   Determine the length of the contact segment, i.e. the separation between
863   2 contacts
864 ------------------------------------------------------------------------- */
865 
contact_separation(const Contact & c1,const Contact & c2)866 double FixWallBodyPolyhedron::contact_separation(const Contact& c1,
867                                               const Contact& c2)
868 {
869   double x1 = c1.xv[0];
870   double y1 = c1.xv[1];
871   double x2 = c1.xe[0];
872   double y2 = c1.xe[1];
873   double x3 = c2.xv[0];
874   double y3 = c2.xv[1];
875 
876   double delta_a = 0.0;
877   if (fabs(x2 - x1) > EPSILON) {
878     double A = (y2 - y1) / (x2 - x1);
879     delta_a = fabs(y1 - A * x1 - y3 + A * x3) / sqrt(1 + A * A);
880   } else {
881     delta_a = fabs(x1 - x3);
882   }
883 
884   return delta_a;
885 }
886 
887 /* ----------------------------------------------------------------------
888   Accumulate torque to body from the force f=(fx,fy,fz) acting at point x
889 ------------------------------------------------------------------------- */
890 
sum_torque(double * xm,double * x,double fx,double fy,double fz,double * torque)891 void FixWallBodyPolyhedron::sum_torque(double* xm, double *x, double fx,
892                                     double fy, double fz, double* torque)
893 {
894   double rx = x[0] - xm[0];
895   double ry = x[1] - xm[1];
896   double rz = x[2] - xm[2];
897   double tx = ry * fz - rz * fy;
898   double ty = rz * fx - rx * fz;
899   double tz = rx * fy - ry * fx;
900   torque[0] += tx;
901   torque[1] += ty;
902   torque[2] += tz;
903 }
904 
905 /* ----------------------------------------------------------------------
906   Calculate the total velocity of a point (vertex, a point on an edge):
907     vi = vcm + omega ^ (p - xcm)
908 ------------------------------------------------------------------------- */
909 
total_velocity(double * p,double * xcm,double * vcm,double * angmom,double * inertia,double * quat,double * vi)910 void FixWallBodyPolyhedron::total_velocity(double* p, double *xcm,
911                               double* vcm, double *angmom, double *inertia,
912                               double *quat, double* vi)
913 {
914   double r[3],omega[3],ex_space[3],ey_space[3],ez_space[3];
915   r[0] = p[0] - xcm[0];
916   r[1] = p[1] - xcm[1];
917   r[2] = p[2] - xcm[2];
918   MathExtra::q_to_exyz(quat,ex_space,ey_space,ez_space);
919   MathExtra::angmom_to_omega(angmom,ex_space,ey_space,ez_space,
920                              inertia,omega);
921   vi[0] = omega[1]*r[2] - omega[2]*r[1] + vcm[0];
922   vi[1] = omega[2]*r[0] - omega[0]*r[2] + vcm[1];
923   vi[2] = omega[0]*r[1] - omega[1]*r[0] + vcm[2];
924 }
925 
926 /* ---------------------------------------------------------------------- */
927 
distance(const double * x2,const double * x1,double & r)928 void FixWallBodyPolyhedron::distance(const double* x2, const double* x1,
929                                   double& r) {
930   r = sqrt((x2[0] - x1[0]) * (x2[0] - x1[0])
931     + (x2[1] - x1[1]) * (x2[1] - x1[1])
932     + (x2[2] - x1[2]) * (x2[2] - x1[2]));
933 }
934