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