4095 lines
135 KiB
C++
4095 lines
135 KiB
C++
/* ----------------------------------------------------------------------
|
|
LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
|
|
http://lammps.sandia.gov, Sandia National Laboratories
|
|
Steve Plimpton, sjplimp@sandia.gov
|
|
|
|
Copyright (2003) Sandia Corporation. Under the terms of Contract
|
|
DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
|
|
certain rights in this software. This software is distributed under
|
|
the GNU General Public License.
|
|
|
|
See the README file in the top-level LAMMPS directory.
|
|
------------------------------------------------------------------------- */
|
|
|
|
/* ----------------------------------------------------------------------
|
|
Contributing authors: Jeremy Lechman (SNL), Pieter in 't Veld (BASF)
|
|
------------------------------------------------------------------------- */
|
|
|
|
#include <cmath>
|
|
#include <cstring>
|
|
#include <cstdlib>
|
|
#include "fix_srd.h"
|
|
#include "math_extra.h"
|
|
#include "atom.h"
|
|
#include "atom_vec_ellipsoid.h"
|
|
#include "atom_vec_line.h"
|
|
#include "atom_vec_tri.h"
|
|
#include "group.h"
|
|
#include "update.h"
|
|
#include "force.h"
|
|
#include "pair.h"
|
|
#include "domain.h"
|
|
#include "neighbor.h"
|
|
#include "comm.h"
|
|
#include "modify.h"
|
|
#include "fix_deform.h"
|
|
#include "fix_wall_srd.h"
|
|
#include "random_mars.h"
|
|
#include "random_park.h"
|
|
#include "math_const.h"
|
|
#include "citeme.h"
|
|
#include "memory.h"
|
|
#include "error.h"
|
|
|
|
using namespace LAMMPS_NS;
|
|
using namespace FixConst;
|
|
using namespace MathConst;
|
|
|
|
enum{SLIP,NOSLIP};
|
|
enum{SPHERE,ELLIPSOID,LINE,TRIANGLE,WALL};
|
|
enum{INSIDE_ERROR,INSIDE_WARN,INSIDE_IGNORE};
|
|
enum{BIG_MOVE,SRD_MOVE,SRD_ROTATE};
|
|
enum{CUBIC_ERROR,CUBIC_WARN};
|
|
enum{SHIFT_NO,SHIFT_YES,SHIFT_POSSIBLE};
|
|
|
|
#define EINERTIA 0.2 // moment of inertia prefactor for ellipsoid
|
|
|
|
#define ATOMPERBIN 30
|
|
#define BIG 1.0e20
|
|
#define VBINSIZE 5
|
|
#define TOLERANCE 0.00001
|
|
#define MAXITER 20
|
|
|
|
static const char cite_fix_srd[] =
|
|
"fix srd command:\n\n"
|
|
"@Article{Petersen10,\n"
|
|
" author = {M. K. Petersen, J. B. Lechman, S. J. Plimpton, G. S. Grest, P. J. in 't Veld, P. R. Schunk},\n"
|
|
" title = {Mesoscale Hydrodynamics via Stochastic Rotation Dynamics: Comparison with Lennard-Jones Fluid},"
|
|
" journal = {J.~Chem.~Phys.},\n"
|
|
" year = 2010,\n"
|
|
" volume = 132,\n"
|
|
" pages = {174106}\n"
|
|
"}\n\n";
|
|
|
|
//#define SRD_DEBUG 1
|
|
//#define SRD_DEBUG_ATOMID 58
|
|
//#define SRD_DEBUG_TIMESTEP 449
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
FixSRD::FixSRD(LAMMPS *lmp, int narg, char **arg) : Fix(lmp, narg, arg),
|
|
wallfix(NULL), wallwhich(NULL), xwall(NULL), xwallhold(NULL),
|
|
vwall(NULL), fwall(NULL), avec_ellipsoid(NULL), avec_line(NULL),
|
|
avec_tri(NULL), random(NULL), randomshift(NULL), flocal(NULL),
|
|
tlocal(NULL), biglist(NULL), binhead(NULL), binnext(NULL), sbuf1(NULL),
|
|
sbuf2(NULL), rbuf1(NULL), rbuf2(NULL), nbinbig(NULL), binbig(NULL),
|
|
binsrd(NULL), stencil(NULL)
|
|
{
|
|
if (lmp->citeme) lmp->citeme->add(cite_fix_srd);
|
|
|
|
if (narg < 8) error->all(FLERR,"Illegal fix srd command");
|
|
|
|
restart_pbc = 1;
|
|
vector_flag = 1;
|
|
size_vector = 12;
|
|
global_freq = 1;
|
|
extvector = 0;
|
|
|
|
nevery = force->inumeric(FLERR,arg[3]);
|
|
|
|
bigexist = 1;
|
|
if (strcmp(arg[4],"NULL") == 0) bigexist = 0;
|
|
else biggroup = group->find(arg[4]);
|
|
|
|
temperature_srd = force->numeric(FLERR,arg[5]);
|
|
gridsrd = force->numeric(FLERR,arg[6]);
|
|
int seed = force->inumeric(FLERR,arg[7]);
|
|
|
|
// parse options
|
|
|
|
lamdaflag = 0;
|
|
collidestyle = NOSLIP;
|
|
overlap = 0;
|
|
insideflag = INSIDE_ERROR;
|
|
exactflag = 1;
|
|
radfactor = 1.0;
|
|
maxbounceallow = 0;
|
|
gridsearch = gridsrd;
|
|
cubicflag = CUBIC_ERROR;
|
|
cubictol = 0.01;
|
|
shiftuser = SHIFT_NO;
|
|
shiftseed = 0;
|
|
tstat = 0;
|
|
rescale_rotate = rescale_collide = 1;
|
|
|
|
int iarg = 8;
|
|
while (iarg < narg) {
|
|
if (strcmp(arg[iarg],"lamda") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
lamda = force->numeric(FLERR,arg[iarg+1]);
|
|
lamdaflag = 1;
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"collision") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
if (strcmp(arg[iarg+1],"slip") == 0) collidestyle = SLIP;
|
|
else if (strcmp(arg[iarg+1],"noslip") == 0) collidestyle = NOSLIP;
|
|
else error->all(FLERR,"Illegal fix srd command");
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"overlap") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
if (strcmp(arg[iarg+1],"yes") == 0) overlap = 1;
|
|
else if (strcmp(arg[iarg+1],"no") == 0) overlap = 0;
|
|
else error->all(FLERR,"Illegal fix srd command");
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"inside") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
if (strcmp(arg[iarg+1],"error") == 0) insideflag = INSIDE_ERROR;
|
|
else if (strcmp(arg[iarg+1],"warn") == 0) insideflag = INSIDE_WARN;
|
|
else if (strcmp(arg[iarg+1],"ignore") == 0) insideflag = INSIDE_IGNORE;
|
|
else error->all(FLERR,"Illegal fix srd command");
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"exact") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
if (strcmp(arg[iarg+1],"yes") == 0) exactflag = 1;
|
|
else if (strcmp(arg[iarg+1],"no") == 0) exactflag = 0;
|
|
else error->all(FLERR,"Illegal fix srd command");
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"radius") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
radfactor = force->numeric(FLERR,arg[iarg+1]);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"bounce") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
maxbounceallow = force->inumeric(FLERR,arg[iarg+1]);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"search") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
gridsearch = force->numeric(FLERR,arg[iarg+1]);
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"cubic") == 0) {
|
|
if (iarg+3 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
if (strcmp(arg[iarg+1],"error") == 0) cubicflag = CUBIC_ERROR;
|
|
else if (strcmp(arg[iarg+1],"warn") == 0) cubicflag = CUBIC_WARN;
|
|
else error->all(FLERR,"Illegal fix srd command");
|
|
cubictol = force->numeric(FLERR,arg[iarg+2]);
|
|
iarg += 3;
|
|
} else if (strcmp(arg[iarg],"shift") == 0) {
|
|
if (iarg+3 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
else if (strcmp(arg[iarg+1],"no") == 0) shiftuser = SHIFT_NO;
|
|
else if (strcmp(arg[iarg+1],"yes") == 0) shiftuser = SHIFT_YES;
|
|
else if (strcmp(arg[iarg+1],"possible") == 0) shiftuser = SHIFT_POSSIBLE;
|
|
else error->all(FLERR,"Illegal fix srd command");
|
|
shiftseed = force->inumeric(FLERR,arg[iarg+2]);
|
|
iarg += 3;
|
|
} else if (strcmp(arg[iarg],"tstat") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
if (strcmp(arg[iarg+1],"no") == 0) tstat = 0;
|
|
else if (strcmp(arg[iarg+1],"yes") == 0) tstat = 1;
|
|
else error->all(FLERR,"Illegal fix srd command");
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"rescale") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix srd command");
|
|
if (strcmp(arg[iarg+1],"no") == 0)
|
|
rescale_rotate = rescale_collide = 0;
|
|
else if (strcmp(arg[iarg+1],"yes") == 0)
|
|
rescale_rotate = rescale_collide = 1;
|
|
else if (strcmp(arg[iarg+1],"rotate") == 0) {
|
|
rescale_rotate = 1;
|
|
rescale_collide = 0;
|
|
} else if (strcmp(arg[iarg+1],"collide") == 0) {
|
|
rescale_rotate = 0;
|
|
rescale_collide = 1;
|
|
}
|
|
else error->all(FLERR,"Illegal fix srd command");
|
|
iarg += 2;
|
|
} else error->all(FLERR,"Illegal fix srd command");
|
|
}
|
|
|
|
// error check
|
|
|
|
if (nevery <= 0) error->all(FLERR,"Illegal fix srd command");
|
|
if (bigexist && biggroup < 0)
|
|
error->all(FLERR,"Could not find fix srd group ID");
|
|
if (gridsrd <= 0.0) error->all(FLERR,"Illegal fix srd command");
|
|
if (temperature_srd <= 0.0) error->all(FLERR,"Illegal fix srd command");
|
|
if (seed <= 0) error->all(FLERR,"Illegal fix srd command");
|
|
if (radfactor <= 0.0) error->all(FLERR,"Illegal fix srd command");
|
|
if (maxbounceallow < 0) error->all(FLERR,"Illegal fix srd command");
|
|
if (lamdaflag && lamda <= 0.0) error->all(FLERR,"Illegal fix srd command");
|
|
if (gridsearch <= 0.0) error->all(FLERR,"Illegal fix srd command");
|
|
if (cubictol < 0.0 || cubictol > 1.0)
|
|
error->all(FLERR,"Illegal fix srd command");
|
|
if ((shiftuser == SHIFT_YES || shiftuser == SHIFT_POSSIBLE) &&
|
|
shiftseed <= 0) error->all(FLERR,"Illegal fix srd command");
|
|
|
|
// initialize Marsaglia RNG with processor-unique seed
|
|
|
|
me = comm->me;
|
|
nprocs = comm->nprocs;
|
|
|
|
random = new RanMars(lmp,seed + me);
|
|
|
|
// if requested, initialize shift RNG, same on every proc
|
|
|
|
if (shiftuser == SHIFT_YES || shiftuser == SHIFT_POSSIBLE)
|
|
randomshift = new RanPark(lmp,shiftseed);
|
|
else randomshift = NULL;
|
|
|
|
// initialize data structs and flags
|
|
|
|
if (bigexist) biggroupbit = group->bitmask[biggroup];
|
|
else biggroupbit = 0;
|
|
|
|
nmax = 0;
|
|
binhead = NULL;
|
|
maxbin1 = 0;
|
|
binnext = NULL;
|
|
maxbuf = 0;
|
|
sbuf1 = sbuf2 = rbuf1 = rbuf2 = NULL;
|
|
|
|
shifts[0].maxvbin = shifts[1].maxvbin = 0;
|
|
shifts[0].vbin = shifts[1].vbin = NULL;
|
|
|
|
shifts[0].maxbinsq = shifts[1].maxbinsq = 0;
|
|
for (int ishift = 0; ishift < 2; ishift++)
|
|
for (int iswap = 0; iswap < 6; iswap++)
|
|
shifts[ishift].bcomm[iswap].sendlist =
|
|
shifts[ishift].bcomm[iswap].recvlist = NULL;
|
|
|
|
maxbin2 = 0;
|
|
nbinbig = NULL;
|
|
binbig = NULL;
|
|
binsrd = NULL;
|
|
|
|
nstencil = maxstencil = 0;
|
|
stencil = NULL;
|
|
|
|
maxbig = 0;
|
|
biglist = NULL;
|
|
|
|
stats_flag = 1;
|
|
for (int i = 0; i < size_vector; i++) stats_all[i] = 0.0;
|
|
|
|
initflag = 0;
|
|
|
|
srd_bin_temp = 0.0;
|
|
srd_bin_count = 0;
|
|
|
|
// atom style pointers to particles that store bonus info
|
|
|
|
avec_ellipsoid = (AtomVecEllipsoid *) atom->style_match("ellipsoid");
|
|
avec_line = (AtomVecLine *) atom->style_match("line");
|
|
avec_tri = (AtomVecTri *) atom->style_match("tri");
|
|
|
|
// fix parameters
|
|
|
|
if (collidestyle == SLIP) comm_reverse = 3;
|
|
else comm_reverse = 6;
|
|
force_reneighbor = 1;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
FixSRD::~FixSRD()
|
|
{
|
|
delete random;
|
|
delete randomshift;
|
|
|
|
memory->destroy(binhead);
|
|
memory->destroy(binnext);
|
|
memory->destroy(sbuf1);
|
|
memory->destroy(sbuf2);
|
|
memory->destroy(rbuf1);
|
|
memory->destroy(rbuf2);
|
|
|
|
memory->sfree(shifts[0].vbin);
|
|
memory->sfree(shifts[1].vbin);
|
|
for (int ishift = 0; ishift < 2; ishift++)
|
|
for (int iswap = 0; iswap < 6; iswap++) {
|
|
memory->destroy(shifts[ishift].bcomm[iswap].sendlist);
|
|
memory->destroy(shifts[ishift].bcomm[iswap].recvlist);
|
|
}
|
|
|
|
memory->destroy(nbinbig);
|
|
memory->destroy(binbig);
|
|
memory->destroy(binsrd);
|
|
memory->destroy(stencil);
|
|
memory->sfree(biglist);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
int FixSRD::setmask()
|
|
{
|
|
int mask = 0;
|
|
mask |= PRE_NEIGHBOR;
|
|
mask |= POST_FORCE;
|
|
return mask;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void FixSRD::init()
|
|
{
|
|
// error checks
|
|
|
|
if (force->newton_pair == 0)
|
|
error->all(FLERR,"Fix srd requires newton pair on");
|
|
if (bigexist && comm->ghost_velocity == 0)
|
|
error->all(FLERR,"Fix srd requires ghost atoms store velocity");
|
|
if (bigexist && collidestyle == NOSLIP && !atom->torque_flag)
|
|
error->all(FLERR,"Fix srd no-slip requires atom attribute torque");
|
|
if (initflag && update->dt != dt_big)
|
|
error->all(FLERR,"Cannot change timestep once fix srd is setup");
|
|
if (comm->style != 0)
|
|
error->universe_all(FLERR,"Fix srd can only currently be used with "
|
|
"comm_style brick");
|
|
|
|
// orthogonal vs triclinic simulation box
|
|
// could be static or shearing box
|
|
|
|
triclinic = domain->triclinic;
|
|
|
|
// wallexist = 1 if SRD wall(s) are defined
|
|
|
|
wallexist = 0;
|
|
for (int m = 0; m < modify->nfix; m++) {
|
|
if (strcmp(modify->fix[m]->style,"wall/srd") == 0) {
|
|
if (wallexist) error->all(FLERR,"Cannot use fix wall/srd more than once");
|
|
wallexist = 1;
|
|
wallfix = (FixWallSRD *) modify->fix[m];
|
|
nwall = wallfix->nwall;
|
|
wallvarflag = wallfix->varflag;
|
|
wallwhich = wallfix->wallwhich;
|
|
xwall = wallfix->xwall;
|
|
xwallhold = wallfix->xwallhold;
|
|
vwall = wallfix->vwall;
|
|
fwall = wallfix->fwall;
|
|
walltrigger = 0.5 * neighbor->skin;
|
|
if (wallfix->overlap && overlap == 0 && me == 0)
|
|
error->warning(FLERR,
|
|
"Fix SRD walls overlap but fix srd overlap not set");
|
|
}
|
|
}
|
|
|
|
// set change_flags if box size or shape changes
|
|
|
|
change_size = change_shape = deformflag = 0;
|
|
if (domain->nonperiodic == 2) change_size = 1;
|
|
for (int i = 0; i < modify->nfix; i++) {
|
|
if (modify->fix[i]->box_change_size) change_size = 1;
|
|
if (modify->fix[i]->box_change_shape) change_shape = 1;
|
|
if (strcmp(modify->fix[i]->style,"deform") == 0) {
|
|
deformflag = 1;
|
|
FixDeform *deform = (FixDeform *) modify->fix[i];
|
|
if (deform->box_change_shape && deform->remapflag != Domain::V_REMAP)
|
|
error->all(FLERR,"Using fix srd with inconsistent "
|
|
"fix deform remap option");
|
|
}
|
|
}
|
|
|
|
if (deformflag && tstat == 0 && me == 0)
|
|
error->warning(FLERR,
|
|
"Using fix srd with box deformation but no SRD thermostat");
|
|
|
|
// parameterize based on current box volume
|
|
|
|
dimension = domain->dimension;
|
|
parameterize();
|
|
|
|
// limit initial SRD velocities if necessary
|
|
|
|
double **v = atom->v;
|
|
int *mask = atom->mask;
|
|
int nlocal = atom->nlocal;
|
|
|
|
double vsq;
|
|
nrescale = 0;
|
|
|
|
for (int i = 0; i < nlocal; i++)
|
|
if (mask[i] & groupbit) {
|
|
vsq = v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2];
|
|
if (vsq > vmaxsq) {
|
|
nrescale++;
|
|
MathExtra::scale3(vmax/sqrt(vsq),v[i]);
|
|
}
|
|
}
|
|
|
|
int all;
|
|
MPI_Allreduce(&nrescale,&all,1,MPI_INT,MPI_SUM,world);
|
|
if (me == 0) {
|
|
if (screen)
|
|
fprintf(screen," # of rescaled SRD velocities = %d\n",all);
|
|
if (logfile)
|
|
fprintf(logfile," # of rescaled SRD velocities = %d\n",all);
|
|
}
|
|
|
|
velocity_stats(igroup);
|
|
if (bigexist) velocity_stats(biggroup);
|
|
|
|
// zero per-run stats
|
|
|
|
nrescale = 0;
|
|
bouncemaxnum = 0;
|
|
bouncemax = 0;
|
|
reneighcount = 0;
|
|
initflag = 1;
|
|
|
|
next_reneighbor = -1;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void FixSRD::setup(int vflag)
|
|
{
|
|
setup_bounds();
|
|
|
|
if (dist_srd_reneigh < nevery*dt_big*vmax && me == 0)
|
|
error->warning(FLERR,
|
|
"Fix srd SRD moves may trigger frequent reneighboring");
|
|
|
|
// setup search bins and search stencil based on these distances
|
|
|
|
if (bigexist || wallexist) {
|
|
setup_search_bins();
|
|
setup_search_stencil();
|
|
} else nbins2 = 0;
|
|
|
|
// perform first bining of SRD and big particles and walls
|
|
// set reneighflag to turn off SRD rotation
|
|
// don't do SRD rotation in setup, only during timestepping
|
|
|
|
reneighflag = BIG_MOVE;
|
|
pre_neighbor();
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
assign SRD particles to bins
|
|
assign big particles to all bins they overlap
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::pre_neighbor()
|
|
{
|
|
int i,j,m,ix,iy,iz,jx,jy,jz,ibin,jbin,lo,hi;
|
|
double rsq,cutbinsq;
|
|
|
|
// grow SRD per-atom bin arrays if necessary
|
|
|
|
if (atom->nmax > nmax) {
|
|
nmax = atom->nmax;
|
|
memory->destroy(binsrd);
|
|
memory->destroy(binnext);
|
|
memory->create(binsrd,nmax,"fix/srd:binsrd");
|
|
memory->create(binnext,nmax,"fix/srd:binnext");
|
|
}
|
|
|
|
// setup and grow BIG info list if necessary
|
|
// set index ptrs to BIG particles and to WALLS
|
|
// big_static() adds static properties to info list
|
|
|
|
if (bigexist || wallexist) {
|
|
if (bigexist) {
|
|
if (biggroup == atom->firstgroup) nbig = atom->nfirst + atom->nghost;
|
|
else {
|
|
int *mask = atom->mask;
|
|
int nlocal = atom->nlocal;
|
|
nbig = atom->nghost;
|
|
for (i = 0; i < nlocal; i++)
|
|
if (mask[i] & biggroupbit) nbig++;
|
|
}
|
|
} else nbig = 0;
|
|
|
|
int ninfo = nbig;
|
|
if (wallexist) ninfo += nwall;
|
|
|
|
if (ninfo > maxbig) {
|
|
maxbig = ninfo;
|
|
memory->destroy(biglist);
|
|
biglist = (Big *) memory->smalloc(maxbig*sizeof(Big),"fix/srd:biglist");
|
|
}
|
|
|
|
if (bigexist) {
|
|
int *mask = atom->mask;
|
|
int nlocal = atom->nlocal;
|
|
if (biggroup == atom->firstgroup) nlocal = atom->nfirst;
|
|
nbig = 0;
|
|
for (i = 0; i < nlocal; i++)
|
|
if (mask[i] & biggroupbit) biglist[nbig++].index = i;
|
|
int nall = atom->nlocal + atom->nghost;
|
|
for (i = atom->nlocal; i < nall; i++)
|
|
if (mask[i] & biggroupbit) biglist[nbig++].index = i;
|
|
big_static();
|
|
}
|
|
|
|
if (wallexist) {
|
|
for (m = 0; m < nwall; m++) {
|
|
biglist[nbig+m].index = m;
|
|
biglist[nbig+m].type = WALL;
|
|
}
|
|
wallfix->wall_params(1);
|
|
}
|
|
}
|
|
|
|
// if simulation box size changes, reset velocity bins
|
|
// if big particles exist, reset search bins if box size or shape changes,
|
|
// b/c finite-size particles will overlap different bins as the box tilts
|
|
|
|
if (change_size) setup_bounds();
|
|
if (change_size) setup_velocity_bins();
|
|
if ((change_size || change_shape) && (bigexist || wallexist)) {
|
|
setup_search_bins();
|
|
setup_search_stencil();
|
|
}
|
|
|
|
// map each owned & ghost big particle to search bins it overlaps
|
|
// zero out bin counters for big particles
|
|
// if firstgroup is defined, only loop over first and ghost particles
|
|
// for each big particle: loop over stencil to find overlap bins
|
|
|
|
int *mask = atom->mask;
|
|
double **x = atom->x;
|
|
int nlocal = atom->nlocal;
|
|
int nall = nlocal + atom->nghost;
|
|
int nfirst = nlocal;
|
|
if (bigexist && biggroup == atom->firstgroup) nfirst = atom->nfirst;
|
|
|
|
if (bigexist || wallexist)
|
|
for (i = 0; i < nbins2; i++)
|
|
nbinbig[i] = 0;
|
|
|
|
if (bigexist) {
|
|
i = nbig = 0;
|
|
while (i < nall) {
|
|
if (mask[i] & biggroupbit) {
|
|
ix = static_cast<int> ((x[i][0]-xblo2)*bininv2x);
|
|
iy = static_cast<int> ((x[i][1]-yblo2)*bininv2y);
|
|
iz = static_cast<int> ((x[i][2]-zblo2)*bininv2z);
|
|
ibin = iz*nbin2y*nbin2x + iy*nbin2x + ix;
|
|
|
|
if (ix < 0 || ix >= nbin2x || iy < 0 || iy >= nbin2y ||
|
|
iz < 0 || iz >= nbin2z)
|
|
error->one(FLERR,"Fix SRD: bad search bin assignment");
|
|
|
|
cutbinsq = biglist[nbig].cutbinsq;
|
|
for (j = 0; j < nstencil; j++) {
|
|
jx = ix + stencil[j][0];
|
|
jy = iy + stencil[j][1];
|
|
jz = iz + stencil[j][2];
|
|
|
|
if (jx < 0 || jx >= nbin2x || jy < 0 || jy >= nbin2y ||
|
|
jz < 0 || jz >= nbin2z) {
|
|
if (screen) {
|
|
fprintf(screen,"Big particle " TAGINT_FORMAT " %d %g %g %g\n",
|
|
atom->tag[i],i,x[i][0],x[i][1],x[i][2]);
|
|
fprintf(screen,"Bin indices: %d %d %d, %d %d %d, %d %d %d\n",
|
|
ix,iy,iz,jx,jy,jz,nbin2x,nbin2y,nbin2z);
|
|
}
|
|
error->one(FLERR,"Fix SRD: bad stencil bin for big particle");
|
|
}
|
|
rsq = point_bin_distance(x[i],jx,jy,jz);
|
|
if (rsq < cutbinsq) {
|
|
jbin = ibin + stencil[j][3];
|
|
if (nbinbig[jbin] == ATOMPERBIN)
|
|
error->one(FLERR,"Fix SRD: too many big particles in bin");
|
|
binbig[jbin][nbinbig[jbin]++] = nbig;
|
|
}
|
|
}
|
|
nbig++;
|
|
}
|
|
|
|
i++;
|
|
if (i == nfirst) i = nlocal;
|
|
}
|
|
}
|
|
|
|
// map each wall to search bins it covers, up to non-periodic boundary
|
|
// if wall moves, add walltrigger to its position
|
|
// this insures it is added to all search bins it may move into
|
|
// may not overlap any of my search bins
|
|
|
|
if (wallexist) {
|
|
double delta = 0.0;
|
|
if (wallvarflag) delta = walltrigger;
|
|
|
|
for (m = 0; m < nwall; m++) {
|
|
int dim = wallwhich[m] / 2;
|
|
int side = wallwhich[m] % 2;
|
|
|
|
if (dim == 0) {
|
|
if (side == 0) {
|
|
hi = static_cast<int> ((xwall[m]+delta-xblo2)*bininv2x);
|
|
if (hi < 0) continue;
|
|
if (hi >= nbin2x) error->all(FLERR,
|
|
"Fix SRD: bad search bin assignment");
|
|
lo = 0;
|
|
} else {
|
|
lo = static_cast<int> ((xwall[m]-delta-xblo2)*bininv2x);
|
|
if (lo >= nbin2x) continue;
|
|
if (lo < 0) error->all(FLERR,"Fix SRD: bad search bin assignment");
|
|
hi = nbin2x-1;
|
|
}
|
|
|
|
for (ix = lo; ix <= hi; ix++)
|
|
for (iy = 0; iy < nbin2y; iy++)
|
|
for (iz = 0; iz < nbin2z; iz++) {
|
|
ibin = iz*nbin2y*nbin2x + iy*nbin2x + ix;
|
|
if (nbinbig[ibin] == ATOMPERBIN)
|
|
error->all(FLERR,"Fix SRD: too many walls in bin");
|
|
binbig[ibin][nbinbig[ibin]++] = nbig+m;
|
|
}
|
|
|
|
} else if (dim == 1) {
|
|
if (side == 0) {
|
|
hi = static_cast<int> ((xwall[m]+delta-yblo2)*bininv2y);
|
|
if (hi < 0) continue;
|
|
if (hi >= nbin2y) error->all(FLERR,
|
|
"Fix SRD: bad search bin assignment");
|
|
lo = 0;
|
|
} else {
|
|
lo = static_cast<int> ((xwall[m]-delta-yblo2)*bininv2y);
|
|
if (lo >= nbin2y) continue;
|
|
if (lo < 0) error->all(FLERR,"Fix SRD: bad search bin assignment");
|
|
hi = nbin2y-1;
|
|
}
|
|
|
|
for (iy = lo; iy <= hi; iy++)
|
|
for (ix = 0; ix < nbin2x; ix++)
|
|
for (iz = 0; iz < nbin2z; iz++) {
|
|
ibin = iz*nbin2y*nbin2x + iy*nbin2x + ix;
|
|
if (nbinbig[ibin] == ATOMPERBIN)
|
|
error->all(FLERR,"Fix SRD: too many walls in bin");
|
|
binbig[ibin][nbinbig[ibin]++] = nbig+m;
|
|
}
|
|
|
|
} else if (dim == 2) {
|
|
if (side == 0) {
|
|
hi = static_cast<int> ((xwall[m]+delta-zblo2)*bininv2z);
|
|
if (hi < 0) continue;
|
|
if (hi >= nbin2z) error->all(FLERR,
|
|
"Fix SRD: bad search bin assignment");
|
|
lo = 0;
|
|
} else {
|
|
lo = static_cast<int> ((xwall[m]-delta-zblo2)*bininv2z);
|
|
if (lo >= nbin2z) continue;
|
|
if (lo < 0) error->all(FLERR,"Fix SRD: bad search bin assignment");
|
|
hi = nbin2z-1;
|
|
}
|
|
|
|
for (iz = lo; iz < hi; iz++)
|
|
for (ix = 0; ix < nbin2x; ix++)
|
|
for (iy = 0; iy < nbin2y; iy++) {
|
|
ibin = iz*nbin2y*nbin2x + iy*nbin2x + ix;
|
|
if (nbinbig[ibin] == ATOMPERBIN)
|
|
error->all(FLERR,"Fix SRD: too many walls in bin");
|
|
binbig[ibin][nbinbig[ibin]++] = nbig+m;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// rotate SRD velocities on SRD timestep
|
|
// done now since all SRDs are currently inside my sub-domain
|
|
|
|
if (reneighflag == SRD_ROTATE) reset_velocities();
|
|
|
|
// log stats if reneighboring occurred b/c SRDs moved too far
|
|
|
|
if (reneighflag == SRD_MOVE) reneighcount++;
|
|
reneighflag = BIG_MOVE;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
advect SRD particles and detect collisions between SRD and BIG particles
|
|
when collision occurs, change x,v of SRD, force,torque of BIG particle
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::post_force(int vflag)
|
|
{
|
|
int i,m,ix,iy,iz;
|
|
|
|
// zero per-timestep stats
|
|
|
|
stats_flag = 0;
|
|
ncheck = ncollide = nbounce = ninside = 0;
|
|
|
|
// zero ghost forces & torques on BIG particles
|
|
|
|
double **f = atom->f;
|
|
double **torque = atom->torque;
|
|
int nlocal = atom->nlocal;
|
|
int nall = nlocal + atom->nghost;
|
|
if (bigexist == 0) nall = 0;
|
|
|
|
for (i = nlocal; i < nall; i++)
|
|
f[i][0] = f[i][1] = f[i][2] = 0.0;
|
|
|
|
if (collidestyle == NOSLIP)
|
|
for (i = nlocal; i < nall; i++)
|
|
torque[i][0] = torque[i][1] = torque[i][2] = 0.0;
|
|
|
|
// advect SRD particles
|
|
// assign to search bins if big particles or walls exist
|
|
|
|
int *mask = atom->mask;
|
|
double **x = atom->x;
|
|
double **v = atom->v;
|
|
|
|
if (bigexist || wallexist) {
|
|
for (i = 0; i < nlocal; i++)
|
|
if (mask[i] & groupbit) {
|
|
x[i][0] += dt_big*v[i][0];
|
|
x[i][1] += dt_big*v[i][1];
|
|
x[i][2] += dt_big*v[i][2];
|
|
|
|
ix = static_cast<int> ((x[i][0]-xblo2)*bininv2x);
|
|
iy = static_cast<int> ((x[i][1]-yblo2)*bininv2y);
|
|
iz = static_cast<int> ((x[i][2]-zblo2)*bininv2z);
|
|
binsrd[i] = iz*nbin2y*nbin2x + iy*nbin2x + ix;
|
|
|
|
if (ix < 0 || ix >= nbin2x || iy < 0 || iy >= nbin2y ||
|
|
iz < 0 || iz >= nbin2z) {
|
|
if (screen) {
|
|
fprintf(screen,"SRD particle " TAGINT_FORMAT
|
|
" on step " BIGINT_FORMAT "\n",
|
|
atom->tag[i],update->ntimestep);
|
|
fprintf(screen,"v = %g %g %g\n",v[i][0],v[i][1],v[i][2]);
|
|
fprintf(screen,"x = %g %g %g\n",x[i][0],x[i][1],x[i][2]);
|
|
fprintf(screen,"ix,iy,iz nx,ny,nz = %d %d %d %d %d %d\n",
|
|
ix,iy,iz,nbin2x,nbin2y,nbin2z);
|
|
}
|
|
error->one(FLERR,"Fix SRD: bad bin assignment for SRD advection");
|
|
}
|
|
}
|
|
|
|
} else {
|
|
for (i = 0; i < nlocal; i++)
|
|
if (mask[i] & groupbit) {
|
|
x[i][0] += dt_big*v[i][0];
|
|
x[i][1] += dt_big*v[i][1];
|
|
x[i][2] += dt_big*v[i][2];
|
|
}
|
|
}
|
|
|
|
// detect collision of SRDs with BIG particles or walls
|
|
|
|
if (bigexist || wallexist) {
|
|
if (bigexist) big_dynamic();
|
|
if (wallexist) wallfix->wall_params(0);
|
|
if (overlap) collisions_multi();
|
|
else collisions_single();
|
|
}
|
|
|
|
// reverse communicate forces & torques on BIG particles
|
|
|
|
if (bigexist) {
|
|
flocal = f;
|
|
tlocal = torque;
|
|
comm->reverse_comm_fix(this);
|
|
}
|
|
|
|
// if any SRD particle has moved too far, trigger reneigh on next step
|
|
// for triclinic, perform check in lamda units
|
|
|
|
int flag = 0;
|
|
|
|
if (triclinic) domain->x2lamda(nlocal);
|
|
for (i = 0; i < nlocal; i++)
|
|
if (mask[i] & groupbit) {
|
|
if (x[i][0] < srdlo_reneigh[0] || x[i][0] > srdhi_reneigh[0] ||
|
|
x[i][1] < srdlo_reneigh[1] || x[i][1] > srdhi_reneigh[1] ||
|
|
x[i][2] < srdlo_reneigh[2] || x[i][2] > srdhi_reneigh[2]) flag = 1;
|
|
}
|
|
if (triclinic) domain->lamda2x(nlocal);
|
|
|
|
int flagall;
|
|
MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_SUM,world);
|
|
if (flagall) {
|
|
next_reneighbor = update->ntimestep + 1;
|
|
reneighflag = SRD_MOVE;
|
|
}
|
|
|
|
// if wall has moved too far, trigger reneigh on next step
|
|
// analogous to neighbor check for big particle moving 1/2 of skin distance
|
|
|
|
if (wallexist) {
|
|
for (m = 0; m < nwall; m++)
|
|
if (fabs(xwall[m]-xwallhold[m]) > walltrigger)
|
|
next_reneighbor = update->ntimestep + 1;
|
|
}
|
|
|
|
// if next timestep is SRD timestep, trigger reneigh
|
|
|
|
if ((update->ntimestep+1) % nevery == 0) {
|
|
next_reneighbor = update->ntimestep + 1;
|
|
reneighflag = SRD_ROTATE;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
reset SRD velocities
|
|
may perform random shifting by up to 1/2 bin in each dimension
|
|
called at pre-neighbor stage when all SRDs are now inside my sub-domain
|
|
if tstat, then thermostat SRD particles as well, including streaming effects
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::reset_velocities()
|
|
{
|
|
int i,j,n,ix,iy,iz,ibin,axis,sign,irandom;
|
|
double u[3],vsum[3];
|
|
double vsq,tbin,scale;
|
|
double *vave,*xlamda;
|
|
double vstream[3];
|
|
|
|
// if requested, perform a dynamic shift of bin positions
|
|
|
|
if (shiftflag) {
|
|
double *boxlo;
|
|
if (triclinic == 0) boxlo = domain->boxlo;
|
|
else boxlo = domain->boxlo_lamda;
|
|
shifts[1].corner[0] = boxlo[0] - binsize1x*randomshift->uniform();
|
|
shifts[1].corner[1] = boxlo[1] - binsize1y*randomshift->uniform();
|
|
if (dimension == 3)
|
|
shifts[1].corner[2] = boxlo[2] - binsize1z*randomshift->uniform();
|
|
else shifts[1].corner[2] = boxlo[2];
|
|
setup_velocity_shift(1,1);
|
|
}
|
|
|
|
double *corner = shifts[shiftflag].corner;
|
|
int *binlo = shifts[shiftflag].binlo;
|
|
int *binhi = shifts[shiftflag].binhi;
|
|
int nbins = shifts[shiftflag].nbins;
|
|
int nbinx = shifts[shiftflag].nbinx;
|
|
int nbiny = shifts[shiftflag].nbiny;
|
|
BinAve *vbin = shifts[shiftflag].vbin;
|
|
|
|
// binhead = 1st SRD particle in each bin
|
|
// binnext = index of next particle in bin
|
|
// bin assignment is done in lamda units for triclinic
|
|
|
|
int *mask = atom->mask;
|
|
double **x = atom->x;
|
|
double **v = atom->v;
|
|
int nlocal = atom->nlocal;
|
|
|
|
if (triclinic) domain->x2lamda(nlocal);
|
|
|
|
for (i = 0; i < nbins; i++) binhead[i] = -1;
|
|
|
|
for (i = 0; i < nlocal; i++)
|
|
if (mask[i] & groupbit) {
|
|
ix = static_cast<int> ((x[i][0]-corner[0])*bininv1x);
|
|
ix = MAX(ix,binlo[0]);
|
|
ix = MIN(ix,binhi[0]);
|
|
iy = static_cast<int> ((x[i][1]-corner[1])*bininv1y);
|
|
iy = MAX(iy,binlo[1]);
|
|
iy = MIN(iy,binhi[1]);
|
|
iz = static_cast<int> ((x[i][2]-corner[2])*bininv1z);
|
|
iz = MAX(iz,binlo[2]);
|
|
iz = MIN(iz,binhi[2]);
|
|
|
|
ibin = (iz-binlo[2])*nbiny*nbinx + (iy-binlo[1])*nbinx + (ix-binlo[0]);
|
|
binnext[i] = binhead[ibin];
|
|
binhead[ibin] = i;
|
|
}
|
|
|
|
if (triclinic) domain->lamda2x(nlocal);
|
|
|
|
// for each bin I have particles contributing to:
|
|
// compute summed v of particles in that bin
|
|
// if I own the bin, set its random value, else set to 0.0
|
|
|
|
for (i = 0; i < nbins; i++) {
|
|
n = 0;
|
|
vsum[0] = vsum[1] = vsum[2] = 0.0;
|
|
for (j = binhead[i]; j >= 0; j = binnext[j]) {
|
|
vsum[0] += v[j][0];
|
|
vsum[1] += v[j][1];
|
|
vsum[2] += v[j][2];
|
|
n++;
|
|
}
|
|
|
|
vbin[i].vsum[0] = vsum[0];
|
|
vbin[i].vsum[1] = vsum[1];
|
|
vbin[i].vsum[2] = vsum[2];
|
|
vbin[i].n = n;
|
|
if (vbin[i].owner) vbin[i].random = random->uniform();
|
|
else vbin[i].random = 0.0;
|
|
}
|
|
|
|
// communicate bin info for bins which more than 1 proc contribute to
|
|
|
|
if (shifts[shiftflag].commflag) vbin_comm(shiftflag);
|
|
|
|
// for each bin I have particles contributing to:
|
|
// compute vave over particles in bin
|
|
// thermal velocity of each particle = v - vave
|
|
// rotate thermal vel of each particle around one of 6 random axes
|
|
// add vave back to each particle
|
|
// thermostat if requested:
|
|
// if no deformation, rescale thermal vel to temperature
|
|
// if deformation, rescale thermal vel and change vave to vstream
|
|
// these are settings for extra dof_temp, dof_tstat to subtract
|
|
// (not sure why these settings work best)
|
|
// no deformation, no tstat: dof_temp = 1
|
|
// yes deformation, no tstat: doesn't matter, system will not equilibrate
|
|
// no deformation, yes tstat: dof_temp = dof_tstat = 1
|
|
// yes deformation, yes tstat: dof_temp = dof_tstat = 0
|
|
// accumulate final T_srd for each bin I own
|
|
|
|
double tfactor = force->mvv2e * mass_srd / (dimension * force->boltz);
|
|
int dof_temp = 1;
|
|
int dof_tstat;
|
|
if (tstat) {
|
|
if (deformflag) dof_tstat = dof_temp = 0;
|
|
else dof_tstat = 1;
|
|
}
|
|
|
|
srd_bin_temp = 0.0;
|
|
srd_bin_count = 0;
|
|
|
|
if (dimension == 2) axis = 2;
|
|
double *h_rate = domain->h_rate;
|
|
double *h_ratelo = domain->h_ratelo;
|
|
|
|
for (i = 0; i < nbins; i++) {
|
|
vbin[i].value[0] = 0.0;
|
|
n = vbin[i].n;
|
|
if (n == 0) continue;
|
|
vave = vbin[i].vsum;
|
|
vave[0] /= n;
|
|
vave[1] /= n;
|
|
vave[2] /= n;
|
|
|
|
irandom = static_cast<int> (6.0*vbin[i].random);
|
|
sign = irandom % 2;
|
|
if (dimension == 3) axis = irandom / 2;
|
|
|
|
vsq = 0.0;
|
|
for (j = binhead[i]; j >= 0; j = binnext[j]) {
|
|
if (axis == 0) {
|
|
u[0] = v[j][0]-vave[0];
|
|
u[1] = sign ? v[j][2]-vave[2] : vave[2]-v[j][2];
|
|
u[2] = sign ? vave[1]-v[j][1] : v[j][1]-vave[1];
|
|
} else if (axis == 1) {
|
|
u[1] = v[j][1]-vave[1];
|
|
u[0] = sign ? v[j][2]-vave[2] : vave[2]-v[j][2];
|
|
u[2] = sign ? vave[0]-v[j][0] : v[j][0]-vave[0];
|
|
} else {
|
|
u[2] = v[j][2]-vave[2];
|
|
u[1] = sign ? v[j][0]-vave[0] : vave[0]-v[j][0];
|
|
u[0] = sign ? vave[1]-v[j][1] : v[j][1]-vave[1];
|
|
}
|
|
vsq += u[0]*u[0] + u[1]*u[1] + u[2]*u[2];
|
|
v[j][0] = u[0] + vave[0];
|
|
v[j][1] = u[1] + vave[1];
|
|
v[j][2] = u[2] + vave[2];
|
|
}
|
|
|
|
if (n > 1) vbin[i].value[0] = vsq;
|
|
}
|
|
|
|
if (shifts[shiftflag].commflag) xbin_comm(shiftflag,1);
|
|
|
|
if (tstat) {
|
|
for (i = 0; i < nbins; i++){
|
|
n = vbin[i].n;
|
|
if (n <= 1) continue;
|
|
|
|
// vsum is already average velocity
|
|
|
|
vave = vbin[i].vsum;
|
|
|
|
if (deformflag) {
|
|
xlamda = vbin[i].xctr;
|
|
vstream[0] = h_rate[0]*xlamda[0] + h_rate[5]*xlamda[1] +
|
|
h_rate[4]*xlamda[2] + h_ratelo[0];
|
|
vstream[1] = h_rate[1]*xlamda[1] + h_rate[3]*xlamda[2] + h_ratelo[1];
|
|
vstream[2] = h_rate[2]*xlamda[2] + h_ratelo[2];
|
|
} else {
|
|
vstream[0] = vave[0];
|
|
vstream[1] = vave[1];
|
|
vstream[2] = vave[2];
|
|
}
|
|
|
|
// tbin = thermal temperature of particles in bin
|
|
// scale = scale factor for thermal velocity
|
|
|
|
tbin = vbin[i].value[0]/(n-dof_tstat) * tfactor;
|
|
scale = sqrt(temperature_srd/tbin);
|
|
vsq = 0.0;
|
|
for (j = binhead[i]; j >= 0; j = binnext[j]) {
|
|
u[0] = (v[j][0] - vave[0]) * scale;
|
|
u[1] = (v[j][1] - vave[1]) * scale;
|
|
u[2] = (v[j][2] - vave[2]) * scale;
|
|
vsq += u[0]*u[0] + u[1]*u[1] + u[2]*u[2];
|
|
v[j][0] = u[0] + vstream[0];
|
|
v[j][1] = u[1] + vstream[1];
|
|
v[j][2] = u[2] + vstream[2];
|
|
}
|
|
vbin[i].value[0] = vsq;
|
|
}
|
|
|
|
if (shifts[shiftflag].commflag) xbin_comm(shiftflag,1);
|
|
}
|
|
|
|
for (i = 0; i < nbins; i++){
|
|
if (vbin[i].owner) {
|
|
if (vbin[i].n > 1) {
|
|
srd_bin_temp += vbin[i].value[0]/(vbin[i].n-dof_temp);
|
|
srd_bin_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
srd_bin_temp *= tfactor;
|
|
|
|
// rescale any too-large velocities
|
|
|
|
if (rescale_rotate) {
|
|
for (i = 0; i < nlocal; i++)
|
|
if (mask[i] & groupbit) {
|
|
vsq = v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2];
|
|
if (vsq > vmaxsq) {
|
|
nrescale++;
|
|
MathExtra::scale3(vmax/sqrt(vsq),v[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
communicate summed particle info for bins that overlap 1 or more procs
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::vbin_comm(int ishift)
|
|
{
|
|
BinComm *bcomm1,*bcomm2;
|
|
MPI_Request request1,request2;
|
|
|
|
// send/recv bins in both directions in each dimension
|
|
// don't send if nsend = 0
|
|
// due to static bins aliging with proc boundary
|
|
// due to dynamic bins across non-periodic global boundary
|
|
// copy to self if sendproc = me
|
|
// MPI send to another proc if sendproc != me
|
|
// don't recv if nrecv = 0
|
|
// copy from self if recvproc = me
|
|
// MPI recv from another proc if recvproc != me
|
|
|
|
BinAve *vbin = shifts[ishift].vbin;
|
|
int *procgrid = comm->procgrid;
|
|
|
|
int iswap = 0;
|
|
for (int idim = 0; idim < dimension; idim++) {
|
|
bcomm1 = &shifts[ishift].bcomm[iswap++];
|
|
bcomm2 = &shifts[ishift].bcomm[iswap++];
|
|
|
|
if (procgrid[idim] == 1) {
|
|
if (bcomm1->nsend)
|
|
vbin_pack(vbin,bcomm1->nsend,bcomm1->sendlist,sbuf1);
|
|
if (bcomm2->nsend)
|
|
vbin_pack(vbin,bcomm2->nsend,bcomm2->sendlist,sbuf2);
|
|
if (bcomm1->nrecv)
|
|
vbin_unpack(sbuf1,vbin,bcomm1->nrecv,bcomm1->recvlist);
|
|
if (bcomm2->nrecv)
|
|
vbin_unpack(sbuf2,vbin,bcomm2->nrecv,bcomm2->recvlist);
|
|
|
|
} else {
|
|
if (bcomm1->nrecv)
|
|
MPI_Irecv(rbuf1,bcomm1->nrecv*VBINSIZE,MPI_DOUBLE,bcomm1->recvproc,0,
|
|
world,&request1);
|
|
if (bcomm2->nrecv)
|
|
MPI_Irecv(rbuf2,bcomm2->nrecv*VBINSIZE,MPI_DOUBLE,bcomm2->recvproc,0,
|
|
world,&request2);
|
|
if (bcomm1->nsend) {
|
|
vbin_pack(vbin,bcomm1->nsend,bcomm1->sendlist,sbuf1);
|
|
MPI_Send(sbuf1,bcomm1->nsend*VBINSIZE,MPI_DOUBLE,
|
|
bcomm1->sendproc,0,world);
|
|
}
|
|
if (bcomm2->nsend) {
|
|
vbin_pack(vbin,bcomm2->nsend,bcomm2->sendlist,sbuf2);
|
|
MPI_Send(sbuf2,bcomm2->nsend*VBINSIZE,MPI_DOUBLE,
|
|
bcomm2->sendproc,0,world);
|
|
}
|
|
if (bcomm1->nrecv) {
|
|
MPI_Wait(&request1,MPI_STATUS_IGNORE);
|
|
vbin_unpack(rbuf1,vbin,bcomm1->nrecv,bcomm1->recvlist);
|
|
}
|
|
if (bcomm2->nrecv) {
|
|
MPI_Wait(&request2,MPI_STATUS_IGNORE);
|
|
vbin_unpack(rbuf2,vbin,bcomm2->nrecv,bcomm2->recvlist);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
pack velocity bin data into a message buffer for sending
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::vbin_pack(BinAve *vbin, int n, int *list, double *buf)
|
|
{
|
|
int j;
|
|
int m = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
j = list[i];
|
|
buf[m++] = vbin[j].n;
|
|
buf[m++] = vbin[j].vsum[0];
|
|
buf[m++] = vbin[j].vsum[1];
|
|
buf[m++] = vbin[j].vsum[2];
|
|
buf[m++] = vbin[j].random;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
unpack velocity bin data from a message buffer and sum values to my bins
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::vbin_unpack(double *buf, BinAve *vbin, int n, int *list)
|
|
{
|
|
int j;
|
|
int m = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
j = list[i];
|
|
vbin[j].n += static_cast<int> (buf[m++]);
|
|
vbin[j].vsum[0] += buf[m++];
|
|
vbin[j].vsum[1] += buf[m++];
|
|
vbin[j].vsum[2] += buf[m++];
|
|
vbin[j].random += buf[m++];
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
communicate summed particle vsq info for bins that overlap 1 or more procs
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::xbin_comm(int ishift, int nval)
|
|
{
|
|
BinComm *bcomm1,*bcomm2;
|
|
MPI_Request request1,request2;
|
|
|
|
// send/recv bins in both directions in each dimension
|
|
// don't send if nsend = 0
|
|
// due to static bins aliging with proc boundary
|
|
// due to dynamic bins across non-periodic global boundary
|
|
// copy to self if sendproc = me
|
|
// MPI send to another proc if sendproc != me
|
|
// don't recv if nrecv = 0
|
|
// copy from self if recvproc = me
|
|
// MPI recv from another proc if recvproc != me
|
|
|
|
BinAve *vbin = shifts[ishift].vbin;
|
|
int *procgrid = comm->procgrid;
|
|
|
|
int iswap = 0;
|
|
for (int idim = 0; idim < dimension; idim++) {
|
|
bcomm1 = &shifts[ishift].bcomm[iswap++];
|
|
bcomm2 = &shifts[ishift].bcomm[iswap++];
|
|
|
|
if (procgrid[idim] == 1) {
|
|
if (bcomm1->nsend)
|
|
xbin_pack(vbin,bcomm1->nsend,bcomm1->sendlist,sbuf1,nval);
|
|
if (bcomm2->nsend)
|
|
xbin_pack(vbin,bcomm2->nsend,bcomm2->sendlist,sbuf2,nval);
|
|
if (bcomm1->nrecv)
|
|
xbin_unpack(sbuf1,vbin,bcomm1->nrecv,bcomm1->recvlist,nval);
|
|
if (bcomm2->nrecv)
|
|
xbin_unpack(sbuf2,vbin,bcomm2->nrecv,bcomm2->recvlist,nval);
|
|
|
|
} else {
|
|
if (bcomm1->nrecv)
|
|
MPI_Irecv(rbuf1,bcomm1->nrecv*nval,MPI_DOUBLE,bcomm1->recvproc,0,
|
|
world,&request1);
|
|
if (bcomm2->nrecv)
|
|
MPI_Irecv(rbuf2,bcomm2->nrecv*nval,MPI_DOUBLE,bcomm2->recvproc,0,
|
|
world,&request2);
|
|
if (bcomm1->nsend) {
|
|
xbin_pack(vbin,bcomm1->nsend,bcomm1->sendlist,sbuf1,nval);
|
|
MPI_Send(sbuf1,bcomm1->nsend*nval,MPI_DOUBLE,
|
|
bcomm1->sendproc,0,world);
|
|
}
|
|
if (bcomm2->nsend) {
|
|
xbin_pack(vbin,bcomm2->nsend,bcomm2->sendlist,sbuf2,nval);
|
|
MPI_Send(sbuf2,bcomm2->nsend*nval,MPI_DOUBLE,
|
|
bcomm2->sendproc,0,world);
|
|
}
|
|
if (bcomm1->nrecv) {
|
|
MPI_Wait(&request1,MPI_STATUS_IGNORE);
|
|
xbin_unpack(rbuf1,vbin,bcomm1->nrecv,bcomm1->recvlist,nval);
|
|
}
|
|
if (bcomm2->nrecv) {
|
|
MPI_Wait(&request2,MPI_STATUS_IGNORE);
|
|
xbin_unpack(rbuf2,vbin,bcomm2->nrecv,bcomm2->recvlist,nval);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
pack velocity bin data into a message buffer for sending
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::xbin_pack(BinAve *vbin, int n, int *list, double *buf, int nval)
|
|
{
|
|
int j, k;
|
|
int m = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
j = list[i];
|
|
for (k = 0; k < nval; k++)
|
|
buf[m++] = vbin[j].value[k];
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
unpack velocity bin data from a message buffer and sum values to my bins
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::xbin_unpack(double *buf, BinAve *vbin, int n, int *list, int nval)
|
|
{
|
|
int j, k;
|
|
int m = 0;
|
|
for (int i = 0; i < n; i++) {
|
|
j = list[i];
|
|
for (k = 0; k < nval; k++)
|
|
vbin[j].value[k] += buf[m++];
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
detect all collisions between SRD and BIG particles or WALLS
|
|
assume SRD can be inside at most one BIG particle or WALL at a time
|
|
unoverlap SRDs for each collision
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::collisions_single()
|
|
{
|
|
int i,j,k,m,type,nbig,ibin,ibounce,inside,collide_flag;
|
|
double dt,t_remain;
|
|
double norm[3],xscoll[3],xbcoll[3],vsnew[3];
|
|
Big *big;
|
|
|
|
// outer loop over SRD particles
|
|
// inner loop over BIG particles or WALLS that overlap SRD particle bin
|
|
// if overlap between SRD and BIG particle or wall:
|
|
// for exact, compute collision pt in time
|
|
// for inexact, push SRD to surf of BIG particle or WALL
|
|
// update x,v of SRD and f,torque on BIG particle
|
|
// re-bin SRD particle after collision
|
|
// iterate until the SRD particle has no overlaps with BIG particles or WALLS
|
|
|
|
double **x = atom->x;
|
|
double **v = atom->v;
|
|
double **f = atom->f;
|
|
double **torque = atom->torque;
|
|
int *mask = atom->mask;
|
|
int nlocal = atom->nlocal;
|
|
|
|
for (i = 0; i < nlocal; i++) {
|
|
if (!(mask[i] & groupbit)) continue;
|
|
|
|
ibin = binsrd[i];
|
|
if (nbinbig[ibin] == 0) continue;
|
|
|
|
ibounce = 0;
|
|
collide_flag = 1;
|
|
dt = dt_big;
|
|
|
|
while (collide_flag) {
|
|
nbig = nbinbig[ibin];
|
|
if (ibounce == 0) ncheck += nbig;
|
|
|
|
collide_flag = 0;
|
|
for (m = 0; m < nbig; m++) {
|
|
k = binbig[ibin][m];
|
|
big = &biglist[k];
|
|
j = big->index;
|
|
type = big->type;
|
|
|
|
if (type == SPHERE) inside = inside_sphere(x[i],x[j],big);
|
|
else if (type == ELLIPSOID) inside = inside_ellipsoid(x[i],x[j],big);
|
|
else inside = inside_wall(x[i],j);
|
|
|
|
if (inside) {
|
|
if (exactflag) {
|
|
if (type == SPHERE)
|
|
t_remain = collision_sphere_exact(x[i],x[j],v[i],v[j],big,
|
|
xscoll,xbcoll,norm);
|
|
else if (type == ELLIPSOID)
|
|
t_remain = collision_ellipsoid_exact(x[i],x[j],v[i],v[j],big,
|
|
xscoll,xbcoll,norm);
|
|
else
|
|
t_remain = collision_wall_exact(x[i],j,v[i],xscoll,xbcoll,norm);
|
|
|
|
} else {
|
|
t_remain = 0.5*dt;
|
|
if (type == SPHERE)
|
|
collision_sphere_inexact(x[i],x[j],big,xscoll,xbcoll,norm);
|
|
else if (type == ELLIPSOID)
|
|
collision_ellipsoid_inexact(x[i],x[j],big,xscoll,xbcoll,norm);
|
|
else
|
|
collision_wall_inexact(x[i],j,xscoll,xbcoll,norm);
|
|
}
|
|
|
|
#ifdef SRD_DEBUG
|
|
if (update->ntimestep == SRD_DEBUG_TIMESTEP &&
|
|
atom->tag[i] == SRD_DEBUG_ATOMID)
|
|
print_collision(i,j,ibounce,t_remain,dt,xscoll,xbcoll,norm,type);
|
|
#endif
|
|
|
|
if (t_remain > dt) {
|
|
ninside++;
|
|
if (insideflag == INSIDE_ERROR || insideflag == INSIDE_WARN) {
|
|
char str[128];
|
|
if (type != WALL) {
|
|
sprintf(str,
|
|
"SRD particle " TAGINT_FORMAT " started "
|
|
"inside big particle " TAGINT_FORMAT
|
|
" on step " BIGINT_FORMAT " bounce %d",
|
|
atom->tag[i],atom->tag[j],update->ntimestep,ibounce+1);
|
|
if (insideflag == INSIDE_ERROR) error->one(FLERR,str);
|
|
error->warning(FLERR,str);
|
|
} else{
|
|
sprintf(str,
|
|
"SRD particle " TAGINT_FORMAT " started "
|
|
"inside wall %d on step " BIGINT_FORMAT " bounce %d",
|
|
atom->tag[i],j,update->ntimestep,ibounce+1);
|
|
if (insideflag == INSIDE_ERROR) error->one(FLERR,str);
|
|
error->warning(FLERR,str);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (collidestyle == SLIP) {
|
|
if (type != WALL) slip(v[i],v[j],x[j],big,xscoll,norm,vsnew);
|
|
else slip_wall(v[i],j,norm,vsnew);
|
|
} else {
|
|
if (type != WALL) noslip(v[i],v[j],x[j],big,-1, xscoll,norm,vsnew);
|
|
else noslip(v[i],NULL,x[j],big,j,xscoll,norm,vsnew);
|
|
}
|
|
|
|
if (dimension == 2) vsnew[2] = 0.0;
|
|
|
|
// check on rescaling of vsnew
|
|
|
|
if (rescale_collide) {
|
|
double vsq = vsnew[0]*vsnew[0] + vsnew[1]*vsnew[1] +
|
|
vsnew[2]*vsnew[2];
|
|
if (vsq > vmaxsq) {
|
|
nrescale++;
|
|
MathExtra::scale3(vmax/sqrt(vsq),vsnew);
|
|
}
|
|
}
|
|
|
|
// update BIG particle and WALL and SRD
|
|
// BIG particle is not torqued if sphere and SLIP collision
|
|
|
|
if (collidestyle == SLIP && type == SPHERE)
|
|
force_torque(v[i],vsnew,xscoll,xbcoll,f[j],NULL);
|
|
else if (type != WALL)
|
|
force_torque(v[i],vsnew,xscoll,xbcoll,f[j],torque[j]);
|
|
else if (type == WALL)
|
|
force_wall(v[i],vsnew,j);
|
|
|
|
ibin = binsrd[i] = update_srd(i,t_remain,xscoll,vsnew,x[i],v[i]);
|
|
|
|
if (ibounce == 0) ncollide++;
|
|
ibounce++;
|
|
if (ibounce < maxbounceallow || maxbounceallow == 0)
|
|
collide_flag = 1;
|
|
dt = t_remain;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
nbounce += ibounce;
|
|
if (maxbounceallow && ibounce >= maxbounceallow) bouncemaxnum++;
|
|
if (ibounce > bouncemax) bouncemax = ibounce;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
detect all collisions between SRD and big particles
|
|
an SRD can be inside more than one big particle at a time
|
|
requires finding which big particle SRD collided with first
|
|
unoverlap SRDs for each collision
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::collisions_multi()
|
|
{
|
|
int i,j,k,m,type,nbig,ibin,ibounce,inside,jfirst,typefirst,jlast;
|
|
double dt,t_remain,t_first;
|
|
double norm[3],xscoll[3],xbcoll[3],vsnew[3];
|
|
double normfirst[3],xscollfirst[3],xbcollfirst[3];
|
|
Big *big;
|
|
|
|
// outer loop over SRD particles
|
|
// inner loop over BIG particles or WALLS that overlap SRD particle bin
|
|
// loop over all BIG and WALLS to find which one SRD collided with first
|
|
// if overlap between SRD and BIG particle or wall:
|
|
// compute collision pt in time
|
|
// update x,v of SRD and f,torque on BIG particle
|
|
// re-bin SRD particle after collision
|
|
// iterate until the SRD particle has no overlaps with BIG particles or WALLS
|
|
|
|
double **x = atom->x;
|
|
double **v = atom->v;
|
|
double **f = atom->f;
|
|
double **torque = atom->torque;
|
|
int *mask = atom->mask;
|
|
int nlocal = atom->nlocal;
|
|
|
|
for (i = 0; i < nlocal; i++) {
|
|
if (!(mask[i] & groupbit)) continue;
|
|
|
|
ibin = binsrd[i];
|
|
if (nbinbig[ibin] == 0) continue;
|
|
|
|
ibounce = 0;
|
|
jlast = -1;
|
|
dt = dt_big;
|
|
|
|
while (1) {
|
|
nbig = nbinbig[ibin];
|
|
if (ibounce == 0) ncheck += nbig;
|
|
|
|
t_first = 0.0;
|
|
for (m = 0; m < nbig; m++) {
|
|
k = binbig[ibin][m];
|
|
big = &biglist[k];
|
|
j = big->index;
|
|
if (j == jlast) continue;
|
|
type = big->type;
|
|
|
|
if (type == SPHERE)
|
|
inside = inside_sphere(x[i],x[j],big);
|
|
else if (type == ELLIPSOID)
|
|
inside = inside_ellipsoid(x[i],x[j],big);
|
|
else if (type == LINE)
|
|
inside = inside_line(x[i],x[j],v[i],v[j],big,dt);
|
|
else if (type == TRIANGLE)
|
|
inside = inside_tri(x[i],x[j],v[i],v[j],big,dt);
|
|
else
|
|
inside = inside_wall(x[i],j);
|
|
|
|
if (inside) {
|
|
if (type == SPHERE)
|
|
t_remain = collision_sphere_exact(x[i],x[j],v[i],v[j],big,
|
|
xscoll,xbcoll,norm);
|
|
else if (type == ELLIPSOID)
|
|
t_remain = collision_ellipsoid_exact(x[i],x[j],v[i],v[j],big,
|
|
xscoll,xbcoll,norm);
|
|
else if (type == LINE)
|
|
t_remain = collision_line_exact(x[i],x[j],v[i],v[j],big,dt,
|
|
xscoll,xbcoll,norm);
|
|
else if (type == TRIANGLE)
|
|
t_remain = collision_tri_exact(x[i],x[j],v[i],v[j],big,dt,
|
|
xscoll,xbcoll,norm);
|
|
else
|
|
t_remain = collision_wall_exact(x[i],j,v[i],xscoll,xbcoll,norm);
|
|
|
|
#ifdef SRD_DEBUG
|
|
if (update->ntimestep == SRD_DEBUG_TIMESTEP &&
|
|
atom->tag[i] == SRD_DEBUG_ATOMID)
|
|
print_collision(i,j,ibounce,t_remain,dt,xscoll,xbcoll,norm,type);
|
|
#endif
|
|
|
|
if (t_remain > dt || t_remain < 0.0) {
|
|
ninside++;
|
|
if (insideflag == INSIDE_ERROR || insideflag == INSIDE_WARN) {
|
|
char str[128];
|
|
if (type != WALL) {
|
|
sprintf(str,
|
|
"SRD particle " TAGINT_FORMAT " started "
|
|
"inside big particle " TAGINT_FORMAT
|
|
" on step " BIGINT_FORMAT " bounce %d",
|
|
atom->tag[i],atom->tag[j],update->ntimestep,ibounce+1);
|
|
if (insideflag == INSIDE_ERROR) error->one(FLERR,str);
|
|
error->warning(FLERR,str);
|
|
} else{
|
|
sprintf(str,
|
|
"SRD particle " TAGINT_FORMAT " started "
|
|
"inside wall %d on step " BIGINT_FORMAT " bounce %d",
|
|
atom->tag[i],j,update->ntimestep,ibounce+1);
|
|
if (insideflag == INSIDE_ERROR) error->one(FLERR,str);
|
|
error->warning(FLERR,str);
|
|
}
|
|
}
|
|
t_first = 0.0;
|
|
break;
|
|
}
|
|
|
|
if (t_remain > t_first) {
|
|
t_first = t_remain;
|
|
jfirst = j;
|
|
typefirst = type;
|
|
xscollfirst[0] = xscoll[0];
|
|
xscollfirst[1] = xscoll[1];
|
|
xscollfirst[2] = xscoll[2];
|
|
xbcollfirst[0] = xbcoll[0];
|
|
xbcollfirst[1] = xbcoll[1];
|
|
xbcollfirst[2] = xbcoll[2];
|
|
normfirst[0] = norm[0];
|
|
normfirst[1] = norm[1];
|
|
normfirst[2] = norm[2];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (t_first == 0.0) break;
|
|
j = jlast = jfirst;
|
|
type = typefirst;
|
|
xscoll[0] = xscollfirst[0];
|
|
xscoll[1] = xscollfirst[1];
|
|
xscoll[2] = xscollfirst[2];
|
|
xbcoll[0] = xbcollfirst[0];
|
|
xbcoll[1] = xbcollfirst[1];
|
|
xbcoll[2] = xbcollfirst[2];
|
|
norm[0] = normfirst[0];
|
|
norm[1] = normfirst[1];
|
|
norm[2] = normfirst[2];
|
|
|
|
if (collidestyle == SLIP) {
|
|
if (type != WALL) slip(v[i],v[j],x[j],big,xscoll,norm,vsnew);
|
|
else slip_wall(v[i],j,norm,vsnew);
|
|
} else {
|
|
if (type != WALL) noslip(v[i],v[j],x[j],big,-1,xscoll,norm,vsnew);
|
|
else noslip(v[i],NULL,x[j],big,j,xscoll,norm,vsnew);
|
|
}
|
|
|
|
if (dimension == 2) vsnew[2] = 0.0;
|
|
|
|
// check on rescaling of vsnew
|
|
|
|
if (rescale_collide) {
|
|
double vsq = vsnew[0]*vsnew[0] + vsnew[1]*vsnew[1] + vsnew[2]*vsnew[2];
|
|
if (vsq > vmaxsq) {
|
|
nrescale++;
|
|
MathExtra::scale3(vmax/sqrt(vsq),vsnew);
|
|
}
|
|
}
|
|
|
|
// update BIG particle and WALL and SRD
|
|
// BIG particle is not torqued if sphere and SLIP collision
|
|
|
|
if (collidestyle == SLIP && type == SPHERE)
|
|
force_torque(v[i],vsnew,xscoll,xbcoll,f[j],NULL);
|
|
else if (type != WALL)
|
|
force_torque(v[i],vsnew,xscoll,xbcoll,f[j],torque[j]);
|
|
else if (type == WALL)
|
|
force_wall(v[i],vsnew,j);
|
|
|
|
ibin = binsrd[i] = update_srd(i,t_first,xscoll,vsnew,x[i],v[i]);
|
|
|
|
if (ibounce == 0) ncollide++;
|
|
ibounce++;
|
|
if (ibounce == maxbounceallow) break;
|
|
dt = t_first;
|
|
}
|
|
|
|
nbounce += ibounce;
|
|
if (maxbounceallow && ibounce >= maxbounceallow) bouncemaxnum++;
|
|
if (ibounce > bouncemax) bouncemax = ibounce;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
check if SRD particle S is inside spherical big particle B
|
|
------------------------------------------------------------------------- */
|
|
|
|
int FixSRD::inside_sphere(double *xs, double *xb, Big *big)
|
|
{
|
|
double dx,dy,dz;
|
|
|
|
dx = xs[0] - xb[0];
|
|
dy = xs[1] - xb[1];
|
|
dz = xs[2] - xb[2];
|
|
|
|
if (dx*dx + dy*dy + dz*dz <= big->radsq) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
check if SRD particle S is inside ellipsoidal big particle B
|
|
------------------------------------------------------------------------- */
|
|
|
|
int FixSRD::inside_ellipsoid(double *xs, double *xb, Big *big)
|
|
{
|
|
double x,y,z;
|
|
|
|
double *ex = big->ex;
|
|
double *ey = big->ey;
|
|
double *ez = big->ez;
|
|
|
|
double xs_xb[3];
|
|
xs_xb[0] = xs[0] - xb[0];
|
|
xs_xb[1] = xs[1] - xb[1];
|
|
xs_xb[2] = xs[2] - xb[2];
|
|
|
|
x = xs_xb[0]*ex[0] + xs_xb[1]*ex[1] + xs_xb[2]*ex[2];
|
|
y = xs_xb[0]*ey[0] + xs_xb[1]*ey[1] + xs_xb[2]*ey[2];
|
|
z = xs_xb[0]*ez[0] + xs_xb[1]*ez[1] + xs_xb[2]*ez[2];
|
|
|
|
if (x*x*big->aradsqinv + y*y*big->bradsqinv + z*z*big->cradsqinv <= 1.0)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
check if SRD particle S is inside line big particle B
|
|
collision only possible if:
|
|
S starts on positive side of infinite line,
|
|
which means it will collide with outside of rigid body made of lines
|
|
since line segments have outward normals,
|
|
when vector from first to last point is crossed with +z axis
|
|
S ends on negative side of infinite line
|
|
unlike most other inside() routines, then calculate exact collision:
|
|
solve for collision pt along infinite line
|
|
collision if pt is within endpoints of B
|
|
------------------------------------------------------------------------- */
|
|
|
|
int FixSRD::inside_line(double *xs, double *xb, double *vs, double *vb,
|
|
Big *big, double dt_step)
|
|
{
|
|
double pmc0[2],pmc1[2],n0[2],n1[2];
|
|
|
|
// 1 and 2 = start and end of timestep
|
|
// pmc = P - C, where P = position of S, C = position of B
|
|
// n = normal to line = [-sin(theta),cos(theta)], theta = orientation of B
|
|
// (P-C) dot N = side of line that S is on
|
|
// side0 = -1,0,1 for which side of line B that S is on at start of step
|
|
// side1 = -1,0,1 for which side of line B that S is on at end of step
|
|
|
|
xs1[0] = xs[0];
|
|
xs1[1] = xs[1];
|
|
xb1[0] = xb[0];
|
|
xb1[1] = xb[1];
|
|
|
|
xs0[0] = xs1[0] - dt_step*vs[0];
|
|
xs0[1] = xs1[1] - dt_step*vs[1];
|
|
xb0[0] = xb1[0] - dt_step*vb[0];
|
|
xb0[1] = xb1[1] - dt_step*vb[1];
|
|
|
|
theta1 = big->theta;
|
|
theta0 = theta1 - dt_step*big->omega[2];
|
|
|
|
pmc0[0] = xs0[0] - xb0[0];
|
|
pmc0[1] = xs0[1] - xb0[1];
|
|
n0[0] = sin(theta0);
|
|
n0[1] = -cos(theta0);
|
|
|
|
pmc1[0] = xs1[0] - xb1[0];
|
|
pmc1[1] = xs1[1] - xb1[1];
|
|
n1[0] = sin(theta1);
|
|
n1[1] = -cos(theta1);
|
|
|
|
double side0 = pmc0[0]*n0[0] + pmc0[1]*n0[1];
|
|
double side1 = pmc1[0]*n1[0] + pmc1[1]*n1[1];
|
|
|
|
if (side0 <= 0.0 || side1 >= 0.0) return 0;
|
|
|
|
// solve for time t (0 to 1) at which moving particle
|
|
// crosses infinite moving/rotating line
|
|
|
|
// Newton-Raphson solve of full non-linear parametric equation
|
|
|
|
tfraction = newton_raphson(0.0,1.0);
|
|
|
|
// quadratic equation solve of approximate parametric equation
|
|
|
|
/*
|
|
n1_n0[0] = n1[0]-n0[0]; n1_n0[1] = n1[1]-n0[1];
|
|
pmc1_pmc0[0] = pmc1[0]-pmc0[0]; pmc1_pmc0[1] = pmc1[1]-pmc0[1];
|
|
|
|
double a = pmc1_pmc0[0]*n1_n0[0] + pmc1_pmc0[1]*n1_n0[1];
|
|
double b = pmc1_pmc0[0]*n0[0] + pmc1_pmc0[1]*n0[1] +
|
|
n1_n0[0]*pmc0[0] + n1_n0[1]*pmc0[1];
|
|
double c = pmc0[0]*n0[0] + pmc0[1]*n0[1];
|
|
|
|
if (a == 0.0) {
|
|
double dot0 = pmc0[0]*n0[0] + pmc0[1]*n0[1];
|
|
double dot1 = pmc1[0]*n0[0] + pmc1[1]*n0[1];
|
|
double root = -dot0 / (dot1 - dot0);
|
|
//printf("Linear root: %g %g\n",root,tfraction);
|
|
tfraction = root;
|
|
|
|
} else {
|
|
|
|
double term = sqrt(b*b - 4.0*a*c);
|
|
double root1 = (-b + term) / (2.0*a);
|
|
double root2 = (-b - term) / (2.0*a);
|
|
|
|
//printf("ABC vecs: %g %g: %g %g\n",
|
|
// pmc1_pmc0[0],pmc1_pmc0[1],n1_n0[0],n1_n0[1]);
|
|
//printf("ABC vecs: %g %g: %g %g: %g %g %g\n",
|
|
// n0[0],n0[1],n1[0],n1[1],theta0,theta1,big->omega[2]);
|
|
//printf("ABC root: %g %g %g: %g %g %g\n",a,b,c,root1,root2,tfraction);
|
|
|
|
if (0.0 <= root1 && root1 <= 1.0) tfraction = root1;
|
|
else if (0.0 <= root2 && root2 <= 1.0) tfraction = root2;
|
|
else error->one(FLERR,"Bad quadratic solve for particle/line collision");
|
|
}
|
|
*/
|
|
|
|
// check if collision pt is within line segment at collision time
|
|
|
|
xsc[0] = xs0[0] + tfraction*(xs1[0]-xs0[0]);
|
|
xsc[1] = xs0[1] + tfraction*(xs1[1]-xs0[1]);
|
|
xbc[0] = xb0[0] + tfraction*(xb1[0]-xb0[0]);
|
|
xbc[1] = xb0[1] + tfraction*(xb1[1]-xb0[1]);
|
|
double delx = xsc[0] - xbc[0];
|
|
double dely = xsc[1] - xbc[1];
|
|
double rsq = delx*delx + dely*dely;
|
|
if (rsq > 0.25*big->length*big->length) return 0;
|
|
|
|
//nbc[0] = n0[0] + tfraction*(n1[0]-n0[0]);
|
|
//nbc[1] = n0[1] + tfraction*(n1[1]-n0[1]);
|
|
|
|
nbc[0] = sin(theta0 + tfraction*(theta1-theta0));
|
|
nbc[1] = -cos(theta0 + tfraction*(theta1-theta0));
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
check if SRD particle S is inside triangle big particle B
|
|
collision only possible if:
|
|
S starts on positive side of triangle plane,
|
|
which means it will collide with outside of rigid body made of tris
|
|
since triangles have outward normals,
|
|
S ends on negative side of triangle plane
|
|
unlike most other inside() routines, then calculate exact collision:
|
|
solve for collision pt on triangle plane
|
|
collision if pt is inside triangle B
|
|
------------------------------------------------------------------------- */
|
|
|
|
int FixSRD::inside_tri(double *xs, double *xb, double *vs, double *vb,
|
|
Big *big, double dt_step)
|
|
{
|
|
double pmc0[3],pmc1[3],n0[3];
|
|
double n1_n0[3],pmc1_pmc0[3];
|
|
double excoll[3],eycoll[3],ezcoll[3];
|
|
double dc1[3],dc2[3],dc3[3];
|
|
double c1[3],c2[3],c3[3];
|
|
double c2mc1[3],c3mc2[3],c1mc3[3];
|
|
double pvec[3],xproduct[3];
|
|
|
|
// 1 and 2 = start and end of timestep
|
|
// pmc = P - C, where P = position of S, C = position of B
|
|
// n = normal to triangle
|
|
// (P-C) dot N = side of tri that S is on
|
|
// side0 = -1,0,1 for which side of tri B that S is on at start of step
|
|
// side1 = -1,0,1 for which side of tri B that S is on at end of step
|
|
|
|
double *omega = big->omega;
|
|
double *n1 = big->norm;
|
|
|
|
n0[0] = n1[0] - dt_step*(omega[1]*n1[2] - omega[2]*n1[1]);
|
|
n0[1] = n1[1] - dt_step*(omega[2]*n1[0] - omega[0]*n1[2]);
|
|
n0[2] = n1[2] - dt_step*(omega[0]*n1[1] - omega[1]*n1[0]);
|
|
|
|
pmc0[0] = xs[0] - dt_step*vs[0] - xb[0] + dt_step*vb[0];
|
|
pmc0[1] = xs[1] - dt_step*vs[1] - xb[1] + dt_step*vb[1];
|
|
pmc0[2] = xs[2] - dt_step*vs[2] - xb[2] + dt_step*vb[2];
|
|
pmc1[0] = xs[0] - xb[0];
|
|
pmc1[1] = xs[1] - xb[1];
|
|
pmc1[2] = xs[2] - xb[2];
|
|
|
|
double side0 = MathExtra::dot3(pmc0,n0);
|
|
double side1 = MathExtra::dot3(pmc1,n1);
|
|
|
|
if (side0 <= 0.0 || side1 >= 0.0) return 0;
|
|
|
|
// solve for time t (0 to 1) at which moving particle
|
|
// crosses moving/rotating tri
|
|
// quadratic equation solve of approximate parametric equation
|
|
|
|
n1_n0[0] = n1[0]-n0[0];
|
|
n1_n0[1] = n1[1]-n0[1];
|
|
n1_n0[2] = n1[2]-n0[2];
|
|
pmc1_pmc0[0] = pmc1[0]-pmc0[0];
|
|
pmc1_pmc0[1] = pmc1[1]-pmc0[1];
|
|
pmc1_pmc0[2] = pmc1[2]-pmc0[2];
|
|
|
|
double a = MathExtra::dot3(pmc1_pmc0,n1_n0);
|
|
double b = MathExtra::dot3(pmc1_pmc0,n0) + MathExtra::dot3(n1_n0,pmc0);
|
|
double c = MathExtra::dot3(pmc0,n0);
|
|
|
|
if (a == 0.0) {
|
|
double dot0 = MathExtra::dot3(pmc0,n0);
|
|
double dot1 = MathExtra::dot3(pmc1,n0);
|
|
double root = -dot0 / (dot1 - dot0);
|
|
tfraction = root;
|
|
} else {
|
|
double term = sqrt(b*b - 4.0*a*c);
|
|
double root1 = (-b + term) / (2.0*a);
|
|
double root2 = (-b - term) / (2.0*a);
|
|
if (0.0 <= root1 && root1 <= 1.0) tfraction = root1;
|
|
else if (0.0 <= root2 && root2 <= 1.0) tfraction = root2;
|
|
else error->one(FLERR,"Bad quadratic solve for particle/tri collision");
|
|
}
|
|
|
|
// calculate position/orientation of S and B at collision time
|
|
// dt = time previous to now at which collision occurs
|
|
// point = S position in plane of triangle at collision time
|
|
// Excoll,Eycoll,Ezcoll = orientation of tri at collision time
|
|
// c1,c2,c3 = corner points of triangle at collision time
|
|
// nbc = normal to plane of triangle at collision time
|
|
|
|
AtomVecTri::Bonus *tbonus;
|
|
tbonus = avec_tri->bonus;
|
|
|
|
double *ex = big->ex;
|
|
double *ey = big->ey;
|
|
double *ez = big->ez;
|
|
int index = atom->tri[big->index];
|
|
double *c1body = tbonus[index].c1;
|
|
double *c2body = tbonus[index].c2;
|
|
double *c3body = tbonus[index].c3;
|
|
|
|
double dt = (1.0-tfraction)*dt_step;
|
|
|
|
xsc[0] = xs[0] - dt*vs[0];
|
|
xsc[1] = xs[1] - dt*vs[1];
|
|
xsc[2] = xs[2] - dt*vs[2];
|
|
xbc[0] = xb[0] - dt*vb[0];
|
|
xbc[1] = xb[1] - dt*vb[1];
|
|
xbc[2] = xb[2] - dt*vb[2];
|
|
|
|
excoll[0] = ex[0] - dt*(omega[1]*ex[2] - omega[2]*ex[1]);
|
|
excoll[1] = ex[1] - dt*(omega[2]*ex[0] - omega[0]*ex[2]);
|
|
excoll[2] = ex[2] - dt*(omega[0]*ex[1] - omega[1]*ex[0]);
|
|
|
|
eycoll[0] = ey[0] - dt*(omega[1]*ey[2] - omega[2]*ey[1]);
|
|
eycoll[1] = ey[1] - dt*(omega[2]*ey[0] - omega[0]*ey[2]);
|
|
eycoll[2] = ey[2] - dt*(omega[0]*ey[1] - omega[1]*ey[0]);
|
|
|
|
ezcoll[0] = ez[0] - dt*(omega[1]*ez[2] - omega[2]*ez[1]);
|
|
ezcoll[1] = ez[1] - dt*(omega[2]*ez[0] - omega[0]*ez[2]);
|
|
ezcoll[2] = ez[2] - dt*(omega[0]*ez[1] - omega[1]*ez[0]);
|
|
|
|
MathExtra::matvec(excoll,eycoll,ezcoll,c1body,dc1);
|
|
MathExtra::matvec(excoll,eycoll,ezcoll,c2body,dc2);
|
|
MathExtra::matvec(excoll,eycoll,ezcoll,c3body,dc3);
|
|
|
|
MathExtra::add3(xbc,dc1,c1);
|
|
MathExtra::add3(xbc,dc2,c2);
|
|
MathExtra::add3(xbc,dc3,c3);
|
|
|
|
MathExtra::sub3(c2,c1,c2mc1);
|
|
MathExtra::sub3(c3,c2,c3mc2);
|
|
MathExtra::sub3(c1,c3,c1mc3);
|
|
|
|
MathExtra::cross3(c2mc1,c3mc2,nbc);
|
|
MathExtra::norm3(nbc);
|
|
|
|
// check if collision pt is within triangle
|
|
// pvec = vector from tri vertex to intersection point
|
|
// xproduct = cross product of edge vec with pvec
|
|
// if dot product of xproduct with nbc < 0.0 for any of 3 edges,
|
|
// intersection point is outside tri
|
|
|
|
MathExtra::sub3(xsc,c1,pvec);
|
|
MathExtra::cross3(c2mc1,pvec,xproduct);
|
|
if (MathExtra::dot3(xproduct,nbc) < 0.0) return 0;
|
|
|
|
MathExtra::sub3(xsc,c2,pvec);
|
|
MathExtra::cross3(c3mc2,pvec,xproduct);
|
|
if (MathExtra::dot3(xproduct,nbc) < 0.0) return 0;
|
|
|
|
MathExtra::sub3(xsc,c3,pvec);
|
|
MathExtra::cross3(c1mc3,pvec,xproduct);
|
|
if (MathExtra::dot3(xproduct,nbc) < 0.0) return 0;
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
check if SRD particle S is inside wall IWALL
|
|
------------------------------------------------------------------------- */
|
|
|
|
int FixSRD::inside_wall(double *xs, int iwall)
|
|
{
|
|
int dim = wallwhich[iwall] / 2;
|
|
int side = wallwhich[iwall] % 2;
|
|
|
|
if (side == 0 && xs[dim] < xwall[iwall]) return 1;
|
|
if (side && xs[dim] > xwall[iwall]) return 1;
|
|
return 0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
collision of SRD particle S with surface of spherical big particle B
|
|
exact because compute time of collision
|
|
dt = time previous to now at which collision occurs
|
|
xscoll = collision pt = position of SRD at time of collision
|
|
xbcoll = position of big particle at time of collision
|
|
norm = surface normal of collision pt at time of collision
|
|
------------------------------------------------------------------------- */
|
|
|
|
double FixSRD::collision_sphere_exact(double *xs, double *xb,
|
|
double *vs, double *vb, Big *big,
|
|
double *xscoll, double *xbcoll,
|
|
double *norm)
|
|
{
|
|
double vs_dot_vs,vb_dot_vb,vs_dot_vb;
|
|
double vs_dot_xb,vb_dot_xs,vs_dot_xs,vb_dot_xb;
|
|
double xs_dot_xs,xb_dot_xb,xs_dot_xb;
|
|
double a,b,c,scale;
|
|
|
|
vs_dot_vs = vs[0]*vs[0] + vs[1]*vs[1] + vs[2]*vs[2];
|
|
vb_dot_vb = vb[0]*vb[0] + vb[1]*vb[1] + vb[2]*vb[2];
|
|
vs_dot_vb = vs[0]*vb[0] + vs[1]*vb[1] + vs[2]*vb[2];
|
|
|
|
vs_dot_xb = vs[0]*xb[0] + vs[1]*xb[1] + vs[2]*xb[2];
|
|
vb_dot_xs = vb[0]*xs[0] + vb[1]*xs[1] + vb[2]*xs[2];
|
|
vs_dot_xs = vs[0]*xs[0] + vs[1]*xs[1] + vs[2]*xs[2];
|
|
vb_dot_xb = vb[0]*xb[0] + vb[1]*xb[1] + vb[2]*xb[2];
|
|
|
|
xs_dot_xs = xs[0]*xs[0] + xs[1]*xs[1] + xs[2]*xs[2];
|
|
xb_dot_xb = xb[0]*xb[0] + xb[1]*xb[1] + xb[2]*xb[2];
|
|
xs_dot_xb = xs[0]*xb[0] + xs[1]*xb[1] + xs[2]*xb[2];
|
|
|
|
a = vs_dot_vs + vb_dot_vb - 2.0*vs_dot_vb;
|
|
b = 2.0 * (vs_dot_xb + vb_dot_xs - vs_dot_xs - vb_dot_xb);
|
|
c = xs_dot_xs + xb_dot_xb - 2.0*xs_dot_xb - big->radsq;
|
|
|
|
double dt = (-b + sqrt(b*b - 4.0*a*c)) / (2.0*a);
|
|
|
|
xscoll[0] = xs[0] - dt*vs[0];
|
|
xscoll[1] = xs[1] - dt*vs[1];
|
|
xscoll[2] = xs[2] - dt*vs[2];
|
|
|
|
xbcoll[0] = xb[0] - dt*vb[0];
|
|
xbcoll[1] = xb[1] - dt*vb[1];
|
|
xbcoll[2] = xb[2] - dt*vb[2];
|
|
|
|
norm[0] = xscoll[0] - xbcoll[0];
|
|
norm[1] = xscoll[1] - xbcoll[1];
|
|
norm[2] = xscoll[2] - xbcoll[2];
|
|
scale = 1.0/sqrt(norm[0]*norm[0] + norm[1]*norm[1] + norm[2]*norm[2]);
|
|
norm[0] *= scale;
|
|
norm[1] *= scale;
|
|
norm[2] *= scale;
|
|
|
|
return dt;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
collision of SRD particle S with surface of spherical big particle B
|
|
inexact because just push SRD to surface of big particle at end of step
|
|
time of collision = end of step
|
|
xscoll = collision pt = position of SRD at time of collision
|
|
xbcoll = xb = position of big particle at time of collision
|
|
norm = surface normal of collision pt at time of collision
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::collision_sphere_inexact(double *xs, double *xb,
|
|
Big *big,
|
|
double *xscoll, double *xbcoll,
|
|
double *norm)
|
|
{
|
|
double scale;
|
|
|
|
norm[0] = xs[0] - xb[0];
|
|
norm[1] = xs[1] - xb[1];
|
|
norm[2] = xs[2] - xb[2];
|
|
scale = 1.0/sqrt(norm[0]*norm[0] + norm[1]*norm[1] + norm[2]*norm[2]);
|
|
norm[0] *= scale;
|
|
norm[1] *= scale;
|
|
norm[2] *= scale;
|
|
|
|
xscoll[0] = xb[0] + big->radius*norm[0];
|
|
xscoll[1] = xb[1] + big->radius*norm[1];
|
|
xscoll[2] = xb[2] + big->radius*norm[2];
|
|
|
|
xbcoll[0] = xb[0];
|
|
xbcoll[1] = xb[1];
|
|
xbcoll[2] = xb[2];
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
collision of SRD particle S with surface of ellipsoidal big particle B
|
|
exact because compute time of collision
|
|
dt = time previous to now at which collision occurs
|
|
xscoll = collision pt = position of SRD at time of collision
|
|
xbcoll = position of big particle at time of collision
|
|
norm = surface normal of collision pt at time of collision
|
|
------------------------------------------------------------------------- */
|
|
|
|
double FixSRD::collision_ellipsoid_exact(double *xs, double *xb,
|
|
double *vs, double *vb, Big *big,
|
|
double *xscoll, double *xbcoll,
|
|
double *norm)
|
|
{
|
|
double vs_vb[3],xs_xb[3],omega_ex[3],omega_ey[3],omega_ez[3];
|
|
double excoll[3],eycoll[3],ezcoll[3],delta[3],xbody[3],nbody[3];
|
|
double ax,bx,cx,ay,by,cy,az,bz,cz;
|
|
double a,b,c;
|
|
|
|
double *omega = big->omega;
|
|
double *ex = big->ex;
|
|
double *ey = big->ey;
|
|
double *ez = big->ez;
|
|
|
|
vs_vb[0] = vs[0]-vb[0]; vs_vb[1] = vs[1]-vb[1]; vs_vb[2] = vs[2]-vb[2];
|
|
xs_xb[0] = xs[0]-xb[0]; xs_xb[1] = xs[1]-xb[1]; xs_xb[2] = xs[2]-xb[2];
|
|
|
|
MathExtra::cross3(omega,ex,omega_ex);
|
|
MathExtra::cross3(omega,ey,omega_ey);
|
|
MathExtra::cross3(omega,ez,omega_ez);
|
|
|
|
ax = vs_vb[0]*omega_ex[0] + vs_vb[1]*omega_ex[1] + vs_vb[2]*omega_ex[2];
|
|
bx = -(vs_vb[0]*ex[0] + vs_vb[1]*ex[1] + vs_vb[2]*ex[2]);
|
|
bx -= xs_xb[0]*omega_ex[0] + xs_xb[1]*omega_ex[1] + xs_xb[2]*omega_ex[2];
|
|
cx = xs_xb[0]*ex[0] + xs_xb[1]*ex[1] + xs_xb[2]*ex[2];
|
|
|
|
ay = vs_vb[0]*omega_ey[0] + vs_vb[1]*omega_ey[1] + vs_vb[2]*omega_ey[2];
|
|
by = -(vs_vb[0]*ey[0] + vs_vb[1]*ey[1] + vs_vb[2]*ey[2]);
|
|
by -= xs_xb[0]*omega_ey[0] + xs_xb[1]*omega_ey[1] + xs_xb[2]*omega_ey[2];
|
|
cy = xs_xb[0]*ey[0] + xs_xb[1]*ey[1] + xs_xb[2]*ey[2];
|
|
|
|
az = vs_vb[0]*omega_ez[0] + vs_vb[1]*omega_ez[1] + vs_vb[2]*omega_ez[2];
|
|
bz = -(vs_vb[0]*ez[0] + vs_vb[1]*ez[1] + vs_vb[2]*ez[2]);
|
|
bz -= xs_xb[0]*omega_ez[0] + xs_xb[1]*omega_ez[1] + xs_xb[2]*omega_ez[2];
|
|
cz = xs_xb[0]*ez[0] + xs_xb[1]*ez[1] + xs_xb[2]*ez[2];
|
|
|
|
a = (bx*bx + 2.0*ax*cx)*big->aradsqinv +
|
|
(by*by + 2.0*ay*cy)*big->bradsqinv +
|
|
(bz*bz + 2.0*az*cz)*big->cradsqinv;
|
|
b = 2.0 * (bx*cx*big->aradsqinv + by*cy*big->bradsqinv +
|
|
bz*cz*big->cradsqinv);
|
|
c = cx*cx*big->aradsqinv + cy*cy*big->bradsqinv +
|
|
cz*cz*big->cradsqinv - 1.0;
|
|
|
|
double dt = (-b + sqrt(b*b - 4.0*a*c)) / (2.0*a);
|
|
|
|
xscoll[0] = xs[0] - dt*vs[0];
|
|
xscoll[1] = xs[1] - dt*vs[1];
|
|
xscoll[2] = xs[2] - dt*vs[2];
|
|
|
|
xbcoll[0] = xb[0] - dt*vb[0];
|
|
xbcoll[1] = xb[1] - dt*vb[1];
|
|
xbcoll[2] = xb[2] - dt*vb[2];
|
|
|
|
// calculate normal to ellipsoid at collision pt
|
|
// Excoll,Eycoll,Ezcoll = orientation of ellipsoid at collision time
|
|
// nbody = normal in body frame of ellipsoid (Excoll,Eycoll,Ezcoll)
|
|
// norm = normal in space frame
|
|
// only worry about normalizing final norm vector
|
|
|
|
excoll[0] = ex[0] - dt*(omega[1]*ex[2] - omega[2]*ex[1]);
|
|
excoll[1] = ex[1] - dt*(omega[2]*ex[0] - omega[0]*ex[2]);
|
|
excoll[2] = ex[2] - dt*(omega[0]*ex[1] - omega[1]*ex[0]);
|
|
|
|
eycoll[0] = ey[0] - dt*(omega[1]*ey[2] - omega[2]*ey[1]);
|
|
eycoll[1] = ey[1] - dt*(omega[2]*ey[0] - omega[0]*ey[2]);
|
|
eycoll[2] = ey[2] - dt*(omega[0]*ey[1] - omega[1]*ey[0]);
|
|
|
|
ezcoll[0] = ez[0] - dt*(omega[1]*ez[2] - omega[2]*ez[1]);
|
|
ezcoll[1] = ez[1] - dt*(omega[2]*ez[0] - omega[0]*ez[2]);
|
|
ezcoll[2] = ez[2] - dt*(omega[0]*ez[1] - omega[1]*ez[0]);
|
|
|
|
MathExtra::sub3(xscoll,xbcoll,delta);
|
|
MathExtra::transpose_matvec(excoll,eycoll,ezcoll,delta,xbody);
|
|
|
|
nbody[0] = xbody[0]*big->aradsqinv;
|
|
nbody[1] = xbody[1]*big->bradsqinv;
|
|
nbody[2] = xbody[2]*big->cradsqinv;
|
|
|
|
MathExtra::matvec(excoll,eycoll,ezcoll,nbody,norm);
|
|
MathExtra::norm3(norm);
|
|
|
|
return dt;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
collision of SRD particle S with surface of ellipsoidal big particle B
|
|
inexact because just push SRD to surface of big particle at end of step
|
|
time of collision = end of step
|
|
xscoll = collision pt = position of SRD at time of collision
|
|
xbcoll = xb = position of big particle at time of collision
|
|
norm = surface normal of collision pt at time of collision
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::collision_ellipsoid_inexact(double *xs, double *xb,
|
|
Big *big,
|
|
double *xscoll, double *xbcoll,
|
|
double *norm)
|
|
{
|
|
double xs_xb[3],delta[3],xbody[3],nbody[3];
|
|
|
|
double *ex = big->ex;
|
|
double *ey = big->ey;
|
|
double *ez = big->ez;
|
|
|
|
MathExtra::sub3(xs,xb,xs_xb);
|
|
double x = MathExtra::dot3(xs_xb,ex);
|
|
double y = MathExtra::dot3(xs_xb,ey);
|
|
double z = MathExtra::dot3(xs_xb,ez);
|
|
|
|
double scale = 1.0/sqrt(x*x*big->aradsqinv + y*y*big->bradsqinv +
|
|
z*z*big->cradsqinv);
|
|
x *= scale;
|
|
y *= scale;
|
|
z *= scale;
|
|
|
|
xscoll[0] = x*ex[0] + y*ey[0] + z*ez[0] + xb[0];
|
|
xscoll[1] = x*ex[1] + y*ey[1] + z*ez[1] + xb[1];
|
|
xscoll[2] = x*ex[2] + y*ey[2] + z*ez[2] + xb[2];
|
|
|
|
xbcoll[0] = xb[0];
|
|
xbcoll[1] = xb[1];
|
|
xbcoll[2] = xb[2];
|
|
|
|
// calculate normal to ellipsoid at collision pt
|
|
// nbody = normal in body frame of ellipsoid
|
|
// norm = normal in space frame
|
|
// only worry about normalizing final norm vector
|
|
|
|
MathExtra::sub3(xscoll,xbcoll,delta);
|
|
MathExtra::transpose_matvec(ex,ey,ez,delta,xbody);
|
|
|
|
nbody[0] = xbody[0]*big->aradsqinv;
|
|
nbody[1] = xbody[1]*big->bradsqinv;
|
|
nbody[2] = xbody[2]*big->cradsqinv;
|
|
|
|
MathExtra::matvec(ex,ey,ez,nbody,norm);
|
|
MathExtra::norm3(norm);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
collision of SRD particle S with surface of line big particle B
|
|
exact because compute time of collision
|
|
dt = time previous to now at which collision occurs
|
|
xscoll = collision pt = position of SRD at time of collision
|
|
xbcoll = position of big particle at time of collision
|
|
norm = surface normal of collision pt at time of collision
|
|
------------------------------------------------------------------------- */
|
|
|
|
double FixSRD::collision_line_exact(double *xs, double *xb,
|
|
double *vs, double *vb, Big *big,
|
|
double dt_step,
|
|
double *xscoll, double *xbcoll,
|
|
double *norm)
|
|
{
|
|
xscoll[0] = xsc[0];
|
|
xscoll[1] = xsc[1];
|
|
xscoll[2] = 0.0;
|
|
xbcoll[0] = xbc[0];
|
|
xbcoll[1] = xbc[1];
|
|
xbcoll[2] = 0.0;
|
|
|
|
norm[0] = nbc[0];
|
|
norm[1] = nbc[1];
|
|
norm[2] = 0.0;
|
|
|
|
return (1.0-tfraction)*dt_step;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
collision of SRD particle S with surface of triangle big particle B
|
|
exact because compute time of collision
|
|
dt = time previous to now at which collision occurs
|
|
xscoll = collision pt = position of SRD at time of collision
|
|
xbcoll = position of big particle at time of collision
|
|
norm = surface normal of collision pt at time of collision
|
|
------------------------------------------------------------------------- */
|
|
|
|
double FixSRD::collision_tri_exact(double *xs, double *xb,
|
|
double *vs, double *vb, Big *big,
|
|
double dt_step,
|
|
double *xscoll, double *xbcoll,
|
|
double *norm)
|
|
{
|
|
xscoll[0] = xsc[0];
|
|
xscoll[1] = xsc[1];
|
|
xscoll[2] = xsc[2];
|
|
xbcoll[0] = xbc[0];
|
|
xbcoll[1] = xbc[1];
|
|
xbcoll[2] = xbc[2];
|
|
|
|
norm[0] = nbc[0];
|
|
norm[1] = nbc[1];
|
|
norm[2] = nbc[2];
|
|
|
|
return (1.0-tfraction)*dt_step;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
collision of SRD particle S with wall IWALL
|
|
exact because compute time of collision
|
|
dt = time previous to now at which collision occurs
|
|
xscoll = collision pt = position of SRD at time of collision
|
|
xbcoll = position of wall at time of collision
|
|
norm = surface normal of collision pt at time of collision
|
|
------------------------------------------------------------------------- */
|
|
|
|
double FixSRD::collision_wall_exact(double *xs, int iwall, double *vs,
|
|
double *xscoll, double *xbcoll,
|
|
double *norm)
|
|
{
|
|
int dim = wallwhich[iwall] / 2;
|
|
|
|
double dt = (xs[dim] - xwall[iwall]) / (vs[dim] - vwall[iwall]);
|
|
xscoll[0] = xs[0] - dt*vs[0];
|
|
xscoll[1] = xs[1] - dt*vs[1];
|
|
xscoll[2] = xs[2] - dt*vs[2];
|
|
|
|
xbcoll[0] = xbcoll[1] = xbcoll[2] = 0.0;
|
|
xbcoll[dim] = xwall[iwall] - dt*vwall[iwall];
|
|
|
|
int side = wallwhich[iwall] % 2;
|
|
norm[0] = norm[1] = norm[2] = 0.0;
|
|
if (side == 0) norm[dim] = 1.0;
|
|
else norm[dim] = -1.0;
|
|
|
|
return dt;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
collision of SRD particle S with wall IWALL
|
|
inexact because just push SRD to surface of wall at end of step
|
|
time of collision = end of step
|
|
xscoll = collision pt = position of SRD at time of collision
|
|
xbcoll = position of wall at time of collision
|
|
norm = surface normal of collision pt at time of collision
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::collision_wall_inexact(double *xs, int iwall, double *xscoll,
|
|
double *xbcoll, double *norm)
|
|
{
|
|
int dim = wallwhich[iwall] / 2;
|
|
|
|
xscoll[0] = xs[0];
|
|
xscoll[1] = xs[1];
|
|
xscoll[2] = xs[2];
|
|
xscoll[dim] = xwall[iwall];
|
|
|
|
xbcoll[0] = xbcoll[1] = xbcoll[2] = 0.0;
|
|
xbcoll[dim] = xwall[iwall];
|
|
|
|
int side = wallwhich[iwall] % 2;
|
|
norm[0] = norm[1] = norm[2] = 0.0;
|
|
if (side == 0) norm[dim] = 1.0;
|
|
else norm[dim] = -1.0;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
SLIP collision with BIG particle with omega
|
|
vs = velocity of SRD, vb = velocity of BIG
|
|
xb = position of BIG, omega = rotation of BIG
|
|
xsurf = collision pt on surf of BIG
|
|
norm = unit normal from surface of BIG at collision pt
|
|
v of BIG particle in direction of surf normal is added to v of SRD
|
|
includes component due to rotation of BIG
|
|
return vsnew of SRD
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::slip(double *vs, double *vb, double *xb, Big *big,
|
|
double *xsurf, double *norm, double *vsnew)
|
|
{
|
|
double r1,r2,vnmag,vs_dot_n,vsurf_dot_n;
|
|
double tangent[3],vsurf[3];
|
|
double *omega = big->omega;
|
|
|
|
while (1) {
|
|
r1 = sigma * random->gaussian();
|
|
r2 = sigma * random->gaussian();
|
|
vnmag = sqrt(r1*r1 + r2*r2);
|
|
if (vnmag*vnmag <= vmaxsq) break;
|
|
}
|
|
|
|
vs_dot_n = vs[0]*norm[0] + vs[1]*norm[1] + vs[2]*norm[2];
|
|
|
|
tangent[0] = vs[0] - vs_dot_n*norm[0];
|
|
tangent[1] = vs[1] - vs_dot_n*norm[1];
|
|
tangent[2] = vs[2] - vs_dot_n*norm[2];
|
|
|
|
// vsurf = velocity of collision pt = translation/rotation of BIG particle
|
|
// NOTE: for sphere could just use vsurf = vb, since w x (xsurf-xb)
|
|
// is orthogonal to norm and thus doesn't contribute to vsurf_dot_n
|
|
|
|
vsurf[0] = vb[0] + omega[1]*(xsurf[2]-xb[2]) - omega[2]*(xsurf[1]-xb[1]);
|
|
vsurf[1] = vb[1] + omega[2]*(xsurf[0]-xb[0]) - omega[0]*(xsurf[2]-xb[2]);
|
|
vsurf[2] = vb[2] + omega[0]*(xsurf[1]-xb[1]) - omega[1]*(xsurf[0]-xb[0]);
|
|
|
|
vsurf_dot_n = vsurf[0]*norm[0] + vsurf[1]*norm[1] + vsurf[2]*norm[2];
|
|
|
|
vsnew[0] = (vnmag+vsurf_dot_n)*norm[0] + tangent[0];
|
|
vsnew[1] = (vnmag+vsurf_dot_n)*norm[1] + tangent[1];
|
|
vsnew[2] = (vnmag+vsurf_dot_n)*norm[2] + tangent[2];
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
SLIP collision with wall IWALL
|
|
vs = velocity of SRD
|
|
norm = unit normal from WALL at collision pt
|
|
v of WALL in direction of surf normal is added to v of SRD
|
|
return vsnew of SRD
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::slip_wall(double *vs, int iwall, double *norm, double *vsnew)
|
|
{
|
|
double vs_dot_n,scale,r1,r2,vnmag,vtmag1,vtmag2;
|
|
double tangent1[3],tangent2[3];
|
|
|
|
vs_dot_n = vs[0]*norm[0] + vs[1]*norm[1] + vs[2]*norm[2];
|
|
|
|
tangent1[0] = vs[0] - vs_dot_n*norm[0];
|
|
tangent1[1] = vs[1] - vs_dot_n*norm[1];
|
|
tangent1[2] = vs[2] - vs_dot_n*norm[2];
|
|
scale = 1.0/sqrt(tangent1[0]*tangent1[0] + tangent1[1]*tangent1[1] +
|
|
tangent1[2]*tangent1[2]);
|
|
tangent1[0] *= scale;
|
|
tangent1[1] *= scale;
|
|
tangent1[2] *= scale;
|
|
|
|
tangent2[0] = norm[1]*tangent1[2] - norm[2]*tangent1[1];
|
|
tangent2[1] = norm[2]*tangent1[0] - norm[0]*tangent1[2];
|
|
tangent2[2] = norm[0]*tangent1[1] - norm[1]*tangent1[0];
|
|
|
|
while (1) {
|
|
r1 = sigma * random->gaussian();
|
|
r2 = sigma * random->gaussian();
|
|
vnmag = sqrt(r1*r1 + r2*r2);
|
|
vtmag1 = sigma * random->gaussian();
|
|
vtmag2 = sigma * random->gaussian();
|
|
if (vnmag*vnmag + vtmag1*vtmag1 + vtmag2*vtmag2 <= vmaxsq) break;
|
|
}
|
|
|
|
vsnew[0] = vnmag*norm[0] + vtmag1*tangent1[0] + vtmag2*tangent2[0];
|
|
vsnew[1] = vnmag*norm[1] + vtmag1*tangent1[1] + vtmag2*tangent2[1];
|
|
vsnew[2] = vnmag*norm[2] + vtmag1*tangent1[2] + vtmag2*tangent2[2];
|
|
|
|
// add in velocity of collision pt = velocity of wall
|
|
|
|
int dim = wallwhich[iwall] / 2;
|
|
vsnew[dim] += vwall[iwall];
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
NO-SLIP collision with BIG particle including WALL
|
|
vs = velocity of SRD, vb = velocity of BIG
|
|
xb = position of BIG, omega = rotation of BIG
|
|
xsurf = collision pt on surf of BIG
|
|
norm = unit normal from surface of BIG at collision pt
|
|
v of collision pt is added to v of SRD
|
|
includes component due to rotation of BIG
|
|
return vsnew of SRD
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::noslip(double *vs, double *vb, double *xb, Big *big, int iwall,
|
|
double *xsurf, double *norm, double *vsnew)
|
|
{
|
|
double vs_dot_n,scale,r1,r2,vnmag,vtmag1,vtmag2;
|
|
double tangent1[3],tangent2[3];
|
|
|
|
vs_dot_n = vs[0]*norm[0] + vs[1]*norm[1] + vs[2]*norm[2];
|
|
|
|
tangent1[0] = vs[0] - vs_dot_n*norm[0];
|
|
tangent1[1] = vs[1] - vs_dot_n*norm[1];
|
|
tangent1[2] = vs[2] - vs_dot_n*norm[2];
|
|
scale = 1.0/sqrt(tangent1[0]*tangent1[0] + tangent1[1]*tangent1[1] +
|
|
tangent1[2]*tangent1[2]);
|
|
tangent1[0] *= scale;
|
|
tangent1[1] *= scale;
|
|
tangent1[2] *= scale;
|
|
|
|
tangent2[0] = norm[1]*tangent1[2] - norm[2]*tangent1[1];
|
|
tangent2[1] = norm[2]*tangent1[0] - norm[0]*tangent1[2];
|
|
tangent2[2] = norm[0]*tangent1[1] - norm[1]*tangent1[0];
|
|
|
|
while (1) {
|
|
r1 = sigma * random->gaussian();
|
|
r2 = sigma * random->gaussian();
|
|
vnmag = sqrt(r1*r1 + r2*r2);
|
|
vtmag1 = sigma * random->gaussian();
|
|
vtmag2 = sigma * random->gaussian();
|
|
if (vnmag*vnmag + vtmag1*vtmag1 + vtmag2*vtmag2 <= vmaxsq) break;
|
|
}
|
|
|
|
vsnew[0] = vnmag*norm[0] + vtmag1*tangent1[0] + vtmag2*tangent2[0];
|
|
vsnew[1] = vnmag*norm[1] + vtmag1*tangent1[1] + vtmag2*tangent2[1];
|
|
vsnew[2] = vnmag*norm[2] + vtmag1*tangent1[2] + vtmag2*tangent2[2];
|
|
|
|
// add in velocity of collision pt
|
|
// for WALL: velocity of wall in one dim
|
|
// else: translation/rotation of BIG particle
|
|
|
|
if (big->type == WALL) {
|
|
int dim = wallwhich[iwall] / 2;
|
|
vsnew[dim] += vwall[iwall];
|
|
|
|
} else {
|
|
double *omega = big->omega;
|
|
vsnew[0] += vb[0] + omega[1]*(xsurf[2]-xb[2]) - omega[2]*(xsurf[1]-xb[1]);
|
|
vsnew[1] += vb[1] + omega[2]*(xsurf[0]-xb[0]) - omega[0]*(xsurf[2]-xb[2]);
|
|
vsnew[2] += vb[2] + omega[0]*(xsurf[1]-xb[1]) - omega[1]*(xsurf[0]-xb[0]);
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
impart force and torque to BIG particle
|
|
force on BIG particle = -dp/dt of SRD particle
|
|
torque on BIG particle = r cross (-dp/dt)
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::force_torque(double *vsold, double *vsnew,
|
|
double *xs, double *xb,
|
|
double *fb, double *tb)
|
|
{
|
|
double dpdt[3],xs_xb[3];
|
|
|
|
double factor = mass_srd / dt_big / force->ftm2v;
|
|
dpdt[0] = factor * (vsnew[0] - vsold[0]);
|
|
dpdt[1] = factor * (vsnew[1] - vsold[1]);
|
|
dpdt[2] = factor * (vsnew[2] - vsold[2]);
|
|
|
|
fb[0] -= dpdt[0];
|
|
fb[1] -= dpdt[1];
|
|
fb[2] -= dpdt[2];
|
|
|
|
// no torque if SLIP collision and BIG is a sphere
|
|
|
|
if (tb) {
|
|
xs_xb[0] = xs[0]-xb[0];
|
|
xs_xb[1] = xs[1]-xb[1];
|
|
xs_xb[2] = xs[2]-xb[2];
|
|
tb[0] -= xs_xb[1]*dpdt[2] - xs_xb[2]*dpdt[1];
|
|
tb[1] -= xs_xb[2]*dpdt[0] - xs_xb[0]*dpdt[2];
|
|
tb[2] -= xs_xb[0]*dpdt[1] - xs_xb[1]*dpdt[0];
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
impart force to WALL
|
|
force on WALL = -dp/dt of SRD particle
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::force_wall(double *vsold, double *vsnew, int iwall)
|
|
|
|
{
|
|
double dpdt[3];
|
|
|
|
double factor = mass_srd / dt_big / force->ftm2v;
|
|
dpdt[0] = factor * (vsnew[0] - vsold[0]);
|
|
dpdt[1] = factor * (vsnew[1] - vsold[1]);
|
|
dpdt[2] = factor * (vsnew[2] - vsold[2]);
|
|
|
|
fwall[iwall][0] -= dpdt[0];
|
|
fwall[iwall][1] -= dpdt[1];
|
|
fwall[iwall][2] -= dpdt[2];
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
update SRD particle position & velocity & search bin assignment
|
|
check if SRD moved outside of valid region
|
|
if so, may overlap off-processor BIG particle
|
|
------------------------------------------------------------------------- */
|
|
|
|
int FixSRD::update_srd(int i, double dt, double *xscoll, double *vsnew,
|
|
double *xs, double *vs)
|
|
{
|
|
int ix,iy,iz;
|
|
|
|
vs[0] = vsnew[0];
|
|
vs[1] = vsnew[1];
|
|
vs[2] = vsnew[2];
|
|
|
|
xs[0] = xscoll[0] + dt*vsnew[0];
|
|
xs[1] = xscoll[1] + dt*vsnew[1];
|
|
xs[2] = xscoll[2] + dt*vsnew[2];
|
|
|
|
if (triclinic) domain->x2lamda(xs,xs);
|
|
|
|
if (xs[0] < srdlo[0] || xs[0] > srdhi[0] ||
|
|
xs[1] < srdlo[1] || xs[1] > srdhi[1] ||
|
|
xs[2] < srdlo[2] || xs[2] > srdhi[2]) {
|
|
if (screen) {
|
|
error->warning(FLERR,"Fix srd particle moved outside valid domain");
|
|
fprintf(screen," particle " TAGINT_FORMAT
|
|
" on proc %d at timestep " BIGINT_FORMAT,
|
|
atom->tag[i],me,update->ntimestep);
|
|
fprintf(screen," xnew %g %g %g\n",xs[0],xs[1],xs[2]);
|
|
fprintf(screen," srdlo/hi x %g %g\n",srdlo[0],srdhi[0]);
|
|
fprintf(screen," srdlo/hi y %g %g\n",srdlo[1],srdhi[1]);
|
|
fprintf(screen," srdlo/hi z %g %g\n",srdlo[2],srdhi[2]);
|
|
}
|
|
}
|
|
|
|
if (triclinic) domain->lamda2x(xs,xs);
|
|
|
|
ix = static_cast<int> ((xs[0]-xblo2)*bininv2x);
|
|
iy = static_cast<int> ((xs[1]-yblo2)*bininv2y);
|
|
iz = static_cast<int> ((xs[2]-zblo2)*bininv2z);
|
|
return iz*nbin2y*nbin2x + iy*nbin2x + ix;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
setup all SRD parameters with big particles
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::parameterize()
|
|
{
|
|
// timesteps
|
|
|
|
dt_big = update->dt;
|
|
dt_srd = nevery * update->dt;
|
|
|
|
// maxbigdiam,minbigdiam = max/min diameter of any big particle
|
|
// big particle must either have radius > 0 or shape > 0 defined
|
|
// apply radfactor at end
|
|
|
|
AtomVecEllipsoid::Bonus *ebonus;
|
|
if (avec_ellipsoid) ebonus = avec_ellipsoid->bonus;
|
|
AtomVecLine::Bonus *lbonus;
|
|
if (avec_line) lbonus = avec_line->bonus;
|
|
AtomVecTri::Bonus *tbonus;
|
|
if (avec_tri) tbonus = avec_tri->bonus;
|
|
double *radius = atom->radius;
|
|
int *ellipsoid = atom->ellipsoid;
|
|
int *line = atom->line;
|
|
int *tri = atom->tri;
|
|
int *mask = atom->mask;
|
|
int nlocal = atom->nlocal;
|
|
|
|
int any_ellipsoids = 0;
|
|
int any_lines = 0;
|
|
int any_tris = 0;
|
|
maxbigdiam = 0.0;
|
|
minbigdiam = BIG;
|
|
|
|
for (int i = 0; i < nlocal; i++)
|
|
if (mask[i] & biggroupbit) {
|
|
if (radius && radius[i] > 0.0) {
|
|
maxbigdiam = MAX(maxbigdiam,2.0*radius[i]);
|
|
minbigdiam = MIN(minbigdiam,2.0*radius[i]);
|
|
} else if (ellipsoid && ellipsoid[i] >= 0) {
|
|
any_ellipsoids = 1;
|
|
double *shape = ebonus[ellipsoid[i]].shape;
|
|
maxbigdiam = MAX(maxbigdiam,2.0*shape[0]);
|
|
maxbigdiam = MAX(maxbigdiam,2.0*shape[1]);
|
|
maxbigdiam = MAX(maxbigdiam,2.0*shape[2]);
|
|
minbigdiam = MIN(minbigdiam,2.0*shape[0]);
|
|
minbigdiam = MIN(minbigdiam,2.0*shape[1]);
|
|
minbigdiam = MIN(minbigdiam,2.0*shape[2]);
|
|
} else if (line && line[i] >= 0) {
|
|
any_lines = 1;
|
|
double length = lbonus[line[i]].length;
|
|
maxbigdiam = MAX(maxbigdiam,length);
|
|
minbigdiam = MIN(minbigdiam,length);
|
|
} else if (tri && tri[i] >= 0) {
|
|
any_tris = 1;
|
|
double length1 = MathExtra::len3(tbonus[tri[i]].c1);
|
|
double length2 = MathExtra::len3(tbonus[tri[i]].c2);
|
|
double length3 = MathExtra::len3(tbonus[tri[i]].c3);
|
|
double length = MAX(length1,length2);
|
|
length = MAX(length,length3);
|
|
maxbigdiam = MAX(maxbigdiam,length);
|
|
minbigdiam = MIN(minbigdiam,length);
|
|
} else
|
|
error->one(FLERR,"Big particle in fix srd cannot be point particle");
|
|
}
|
|
|
|
double tmp = maxbigdiam;
|
|
MPI_Allreduce(&tmp,&maxbigdiam,1,MPI_DOUBLE,MPI_MAX,world);
|
|
tmp = minbigdiam;
|
|
MPI_Allreduce(&tmp,&minbigdiam,1,MPI_DOUBLE,MPI_MIN,world);
|
|
|
|
maxbigdiam *= radfactor;
|
|
minbigdiam *= radfactor;
|
|
|
|
int itmp = any_ellipsoids;
|
|
MPI_Allreduce(&itmp,&any_ellipsoids,1,MPI_INT,MPI_MAX,world);
|
|
itmp = any_lines;
|
|
MPI_Allreduce(&itmp,&any_lines,1,MPI_INT,MPI_MAX,world);
|
|
itmp = any_tris;
|
|
MPI_Allreduce(&itmp,&any_tris,1,MPI_INT,MPI_MAX,world);
|
|
|
|
if (any_lines && overlap == 0)
|
|
error->all(FLERR,"Cannot use lines with fix srd unless overlap is set");
|
|
if (any_tris && overlap == 0)
|
|
error->all(FLERR,"Cannot use tris with fix srd unless overlap is set");
|
|
|
|
// big particles are only torqued if ellipsoids/lines/tris or NOSLIP
|
|
|
|
if (any_ellipsoids == 0 && any_lines == 0 && any_tris == 0 &&
|
|
collidestyle == SLIP) torqueflag = 0;
|
|
else torqueflag = 1;
|
|
|
|
// mass of SRD particles, require monodispersity
|
|
|
|
double *rmass = atom->rmass;
|
|
double *mass = atom->mass;
|
|
int *type = atom->type;
|
|
|
|
int flag = 0;
|
|
mass_srd = 0.0;
|
|
|
|
for (int i = 0; i < nlocal; i++)
|
|
if (mask[i] & groupbit) {
|
|
if (rmass) {
|
|
if (mass_srd == 0.0) mass_srd = rmass[i];
|
|
else if (rmass[i] != mass_srd) flag = 1;
|
|
} else {
|
|
if (mass_srd == 0.0) mass_srd = mass[type[i]];
|
|
else if (mass[type[i]] != mass_srd) flag = 1;
|
|
}
|
|
}
|
|
|
|
int flagall;
|
|
MPI_Allreduce(&flag,&flagall,1,MPI_INT,MPI_MAX,world);
|
|
if (flagall)
|
|
error->all(FLERR,"Fix srd requires SRD particles all have same mass");
|
|
|
|
// set temperature and lamda of SRD particles from each other
|
|
// lamda = dt_srd * sqrt(boltz * temperature_srd / mass_srd);
|
|
|
|
if (lamdaflag == 0)
|
|
lamda = dt_srd * sqrt(force->boltz*temperature_srd/mass_srd/force->mvv2e);
|
|
else
|
|
temperature_srd = force->mvv2e *
|
|
(lamda/dt_srd)*(lamda/dt_srd) * mass_srd/force->boltz;
|
|
|
|
// vmax = maximum velocity of an SRD particle
|
|
// dmax = maximum distance an SRD can move = 4*lamda = vmax * dt_srd
|
|
|
|
sigma = lamda/dt_srd;
|
|
dmax = 4.0*lamda;
|
|
vmax = dmax/dt_srd;
|
|
vmaxsq = vmax*vmax;
|
|
|
|
// volbig = total volume of all big particles
|
|
// LINE/TRI particles have no volume
|
|
// incorrect if part of rigid particles, so add fudge factor with WIDTH
|
|
// apply radfactor to reduce final volume
|
|
|
|
double volbig = 0.0;
|
|
double WIDTH = 1.0;
|
|
|
|
if (dimension == 3) {
|
|
for (int i = 0; i < nlocal; i++)
|
|
if (mask[i] & biggroupbit) {
|
|
if (radius && radius[i] > 0.0) {
|
|
double r = radfactor * radius[i];
|
|
volbig += 4.0/3.0*MY_PI * r*r*r;;
|
|
} else if (ellipsoid && ellipsoid[i] >= 0) {
|
|
double *shape = ebonus[ellipsoid[i]].shape;
|
|
volbig += 4.0/3.0*MY_PI * shape[0]*shape[1]*shape[2] *
|
|
radfactor*radfactor*radfactor;
|
|
} else if (tri && tri[i] >= 0) {
|
|
double *c1 = tbonus[tri[i]].c1;
|
|
double *c2 = tbonus[tri[i]].c2;
|
|
double *c3 = tbonus[tri[i]].c3;
|
|
double c2mc1[3],c3mc1[3],cross[3];
|
|
MathExtra::sub3(c2,c1,c2mc1);
|
|
MathExtra::sub3(c3,c1,c3mc1);
|
|
MathExtra::cross3(c2mc1,c3mc1,cross);
|
|
volbig += 0.5 * MathExtra::len3(cross);
|
|
}
|
|
}
|
|
} else {
|
|
for (int i = 0; i < nlocal; i++)
|
|
if (mask[i] & biggroupbit) {
|
|
if (radius && radius[i] > 0.0) {
|
|
double r = radfactor * radius[i];
|
|
volbig += MY_PI * r*r;
|
|
} else if (ellipsoid && ellipsoid[i] >= 0) {
|
|
double *shape = ebonus[ellipsoid[i]].shape;
|
|
volbig += MY_PI * shape[0]*shape[1] * radfactor*radfactor;
|
|
} else if (line && line[i] >= 0) {
|
|
double length = lbonus[line[i]].length;
|
|
volbig += length * WIDTH;
|
|
}
|
|
}
|
|
}
|
|
|
|
tmp = volbig;
|
|
MPI_Allreduce(&tmp,&volbig,1,MPI_DOUBLE,MPI_SUM,world);
|
|
|
|
// particle counts
|
|
|
|
bigint mbig = 0;
|
|
if (bigexist) mbig = group->count(biggroup);
|
|
bigint nsrd = group->count(igroup);
|
|
|
|
// mass_big = total mass of all big particles
|
|
|
|
mass_big = 0.0;
|
|
for (int i = 0; i < nlocal; i++)
|
|
if (mask[i] & biggroupbit) {
|
|
if (rmass) mass_big += rmass[i];
|
|
else mass_big += mass[type[i]];
|
|
}
|
|
|
|
tmp = mass_big;
|
|
MPI_Allreduce(&tmp,&mass_big,1,MPI_DOUBLE,MPI_SUM,world);
|
|
|
|
// mass density ratio = big / SRD
|
|
|
|
double density_big = 0.0;
|
|
if (bigexist) density_big = mass_big / volbig;
|
|
|
|
double volsrd,density_srd;
|
|
if (dimension == 3) {
|
|
volsrd = (domain->xprd * domain->yprd * domain->zprd) - volbig;
|
|
density_srd = nsrd * mass_srd /
|
|
(domain->xprd*domain->yprd*domain->zprd - volbig);
|
|
} else {
|
|
volsrd = (domain->xprd * domain->yprd) - volbig;
|
|
density_srd = nsrd * mass_srd /
|
|
(domain->xprd*domain->yprd - volbig);
|
|
}
|
|
|
|
double mdratio = density_big/density_srd;
|
|
|
|
// create grid for binning/rotating SRD particles from gridsrd
|
|
|
|
setup_velocity_bins();
|
|
|
|
// binsize3 = binsize1 in box units (not lamda units for triclinic)
|
|
|
|
double binsize3x = binsize1x;
|
|
double binsize3y = binsize1y;
|
|
double binsize3z = binsize1z;
|
|
if (triclinic) {
|
|
binsize3x = binsize1x * domain->xprd;
|
|
binsize3y = binsize1y * domain->yprd;
|
|
binsize3z = binsize1z * domain->zprd;
|
|
}
|
|
|
|
// srd_per_grid = # of SRD particles per SRD grid cell
|
|
|
|
double ncell;
|
|
if (dimension == 3)
|
|
ncell = volsrd / (binsize3x*binsize3y*binsize3z);
|
|
else
|
|
ncell = volsrd / (binsize3x*binsize3y);
|
|
|
|
srd_per_cell = (double) nsrd / ncell;
|
|
|
|
// kinematic viscosity of SRD fluid
|
|
// output in cm^2/sec units, converted by xxt2kmu
|
|
|
|
double viscosity;
|
|
if (dimension == 3)
|
|
viscosity = gridsrd*gridsrd/(18.0*dt_srd) *
|
|
(1.0-(1.0-exp(-srd_per_cell))/srd_per_cell) +
|
|
(force->boltz*temperature_srd*dt_srd/(4.0*mass_srd*force->mvv2e)) *
|
|
((srd_per_cell+2.0)/(srd_per_cell-1.0));
|
|
else
|
|
viscosity =
|
|
(force->boltz*temperature_srd*dt_srd/(2.0*mass_srd*force->mvv2e)) *
|
|
(srd_per_cell/(srd_per_cell-1.0 + exp(-srd_per_cell)) - 1.0) +
|
|
(gridsrd*gridsrd)/(12.0*dt_srd) *
|
|
((srd_per_cell-1.0 + exp(-srd_per_cell))/srd_per_cell);
|
|
viscosity *= force->xxt2kmu;
|
|
|
|
// print SRD parameters
|
|
|
|
if (me == 0) {
|
|
if (screen) {
|
|
fprintf(screen,"SRD info:\n");
|
|
fprintf(screen,
|
|
" SRD/big particles = " BIGINT_FORMAT " " BIGINT_FORMAT "\n",
|
|
nsrd,mbig);
|
|
fprintf(screen," big particle diameter max/min = %g %g\n",
|
|
maxbigdiam,minbigdiam);
|
|
fprintf(screen," SRD temperature & lamda = %g %g\n",
|
|
temperature_srd,lamda);
|
|
fprintf(screen," SRD max distance & max velocity = %g %g\n",dmax,vmax);
|
|
fprintf(screen," SRD grid counts: %d %d %d\n",nbin1x,nbin1y,nbin1z);
|
|
fprintf(screen," SRD grid size: request, actual (xyz) = %g, %g %g %g\n",
|
|
gridsrd,binsize3x,binsize3y,binsize3z);
|
|
fprintf(screen," SRD per actual grid cell = %g\n",srd_per_cell);
|
|
fprintf(screen," SRD viscosity = %g\n",viscosity);
|
|
fprintf(screen," big/SRD mass density ratio = %g\n",mdratio);
|
|
}
|
|
if (logfile) {
|
|
fprintf(logfile,"SRD info:\n");
|
|
fprintf(logfile,
|
|
" SRD/big particles = " BIGINT_FORMAT " " BIGINT_FORMAT "\n",
|
|
nsrd,mbig);
|
|
fprintf(logfile," big particle diameter max/min = %g %g\n",
|
|
maxbigdiam,minbigdiam);
|
|
fprintf(logfile," SRD temperature & lamda = %g %g\n",
|
|
temperature_srd,lamda);
|
|
fprintf(logfile," SRD max distance & max velocity = %g %g\n",dmax,vmax);
|
|
fprintf(logfile," SRD grid counts: %d %d %d\n",nbin1x,nbin1y,nbin1z);
|
|
fprintf(logfile," SRD grid size: request, actual (xyz) = %g, %g %g %g\n",
|
|
gridsrd,binsize3x,binsize3y,binsize3z);
|
|
fprintf(logfile," SRD per actual grid cell = %g\n",srd_per_cell);
|
|
fprintf(logfile," SRD viscosity = %g\n",viscosity);
|
|
fprintf(logfile," big/SRD mass density ratio = %g\n",mdratio);
|
|
}
|
|
}
|
|
|
|
// error if less than 1 SRD bin per processor in some dim
|
|
|
|
if (nbin1x < comm->procgrid[0] || nbin1y < comm->procgrid[1] ||
|
|
nbin1z < comm->procgrid[2])
|
|
error->all(FLERR,"Fewer SRD bins than processors in some dimension");
|
|
|
|
// check if SRD bins are within tolerance for shape and size
|
|
|
|
int tolflag = 0;
|
|
if (binsize3y/binsize3x > 1.0+cubictol ||
|
|
binsize3x/binsize3y > 1.0+cubictol) tolflag = 1;
|
|
if (dimension == 3) {
|
|
if (binsize3z/binsize3x > 1.0+cubictol ||
|
|
binsize3x/binsize3z > 1.0+cubictol) tolflag = 1;
|
|
}
|
|
|
|
if (tolflag) {
|
|
if (cubicflag == CUBIC_ERROR)
|
|
error->all(FLERR,"SRD bins for fix srd are not cubic enough");
|
|
if (me == 0)
|
|
error->warning(FLERR,"SRD bins for fix srd are not cubic enough");
|
|
}
|
|
|
|
tolflag = 0;
|
|
if (binsize3x/gridsrd > 1.0+cubictol || gridsrd/binsize3x > 1.0+cubictol)
|
|
tolflag = 1;
|
|
if (binsize3y/gridsrd > 1.0+cubictol || gridsrd/binsize3y > 1.0+cubictol)
|
|
tolflag = 1;
|
|
if (dimension == 3) {
|
|
if (binsize3z/gridsrd > 1.0+cubictol || gridsrd/binsize3z > 1.0+cubictol)
|
|
tolflag = 1;
|
|
}
|
|
|
|
if (tolflag) {
|
|
if (cubicflag == CUBIC_ERROR)
|
|
error->all(FLERR,"SRD bin size for fix srd differs from user request");
|
|
if (me == 0)
|
|
error->warning(FLERR,
|
|
"SRD bin size for fix srd differs from user request");
|
|
}
|
|
|
|
// error if lamda < 0.6 of SRD grid size and no shifting allowed
|
|
// turn on shifting in this case if allowed
|
|
|
|
double maxgridsrd = MAX(binsize3x,binsize3y);
|
|
if (dimension == 3) maxgridsrd = MAX(maxgridsrd,binsize3z);
|
|
|
|
shiftflag = 0;
|
|
if (lamda < 0.6*maxgridsrd && shiftuser == SHIFT_NO)
|
|
error->all(FLERR,"Fix srd lamda must be >= 0.6 of SRD grid size");
|
|
else if (lamda < 0.6*maxgridsrd && shiftuser == SHIFT_POSSIBLE) {
|
|
shiftflag = 1;
|
|
if (me == 0)
|
|
error->warning(FLERR,"SRD bin shifting turned on due to small lamda");
|
|
} else if (shiftuser == SHIFT_YES) shiftflag = 1;
|
|
|
|
// warnings
|
|
|
|
if (bigexist && maxgridsrd > 0.25 * minbigdiam && me == 0)
|
|
error->warning(FLERR,"Fix srd grid size > 1/4 of big particle diameter");
|
|
if (viscosity < 0.0 && me == 0)
|
|
error->warning(FLERR,"Fix srd viscosity < 0.0 due to low SRD density");
|
|
if (bigexist && dt_big*vmax > minbigdiam && me == 0)
|
|
error->warning(FLERR,"Fix srd particles may move > big particle diameter");
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
set static parameters of each big particle, owned and ghost
|
|
called each reneighboring
|
|
use radfactor in distance parameters as appropriate
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::big_static()
|
|
{
|
|
int i;
|
|
double rad,arad,brad,crad,length,length1,length2,length3;
|
|
double *shape,*c1,*c2,*c3;
|
|
double c2mc1[3],c3mc1[3];
|
|
|
|
AtomVecEllipsoid::Bonus *ebonus;
|
|
if (avec_ellipsoid) ebonus = avec_ellipsoid->bonus;
|
|
AtomVecLine::Bonus *lbonus;
|
|
if (avec_line) lbonus = avec_line->bonus;
|
|
AtomVecTri::Bonus *tbonus;
|
|
if (avec_tri) tbonus = avec_tri->bonus;
|
|
double *radius = atom->radius;
|
|
int *ellipsoid = atom->ellipsoid;
|
|
int *line = atom->line;
|
|
int *tri = atom->tri;
|
|
|
|
double skinhalf = 0.5 * neighbor->skin;
|
|
|
|
for (int k = 0; k < nbig; k++) {
|
|
i = biglist[k].index;
|
|
|
|
// sphere
|
|
// set radius and radsq and cutoff based on radius
|
|
|
|
if (radius && radius[i] > 0.0) {
|
|
biglist[k].type = SPHERE;
|
|
rad = radfactor*radius[i];
|
|
biglist[k].radius = rad;
|
|
biglist[k].radsq = rad*rad;
|
|
biglist[k].cutbinsq = (rad+skinhalf) * (rad+skinhalf);
|
|
|
|
// ellipsoid
|
|
// set abc radsqinv and cutoff based on max radius
|
|
|
|
} else if (ellipsoid && ellipsoid[i] >= 0) {
|
|
shape = ebonus[ellipsoid[i]].shape;
|
|
biglist[k].type = ELLIPSOID;
|
|
arad = radfactor*shape[0];
|
|
brad = radfactor*shape[1];
|
|
crad = radfactor*shape[2];
|
|
biglist[k].aradsqinv = 1.0/(arad*arad);
|
|
biglist[k].bradsqinv = 1.0/(brad*brad);
|
|
biglist[k].cradsqinv = 1.0/(crad*crad);
|
|
rad = MAX(arad,brad);
|
|
rad = MAX(rad,crad);
|
|
biglist[k].cutbinsq = (rad+skinhalf) * (rad+skinhalf);
|
|
|
|
// line
|
|
// set length and cutoff based on 1/2 length
|
|
|
|
} else if (line && line[i] >= 0) {
|
|
length = lbonus[line[i]].length;
|
|
biglist[k].type = LINE;
|
|
biglist[k].length = length;
|
|
rad = 0.5*length;
|
|
biglist[k].cutbinsq = (rad+skinhalf) * (rad+skinhalf);
|
|
|
|
// tri
|
|
// set normbody based on c1,c2,c3
|
|
// set cutoff based on point furthest from centroid
|
|
|
|
} else if (tri && tri[i] >= 0) {
|
|
biglist[k].type = TRIANGLE;
|
|
c1 = tbonus[tri[i]].c1;
|
|
c2 = tbonus[tri[i]].c2;
|
|
c3 = tbonus[tri[i]].c3;
|
|
MathExtra::sub3(c2,c1,c2mc1);
|
|
MathExtra::sub3(c3,c1,c3mc1);
|
|
MathExtra::cross3(c2mc1,c3mc1,biglist[k].normbody);
|
|
length1 = MathExtra::len3(c1);
|
|
length2 = MathExtra::len3(c2);
|
|
length3 = MathExtra::len3(c3);
|
|
rad = MAX(length1,length2);
|
|
rad = MAX(rad,length3);
|
|
biglist[k].cutbinsq = (rad+skinhalf) * (rad+skinhalf);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
set dynamic parameters of each big particle, owned and ghost
|
|
called each timestep
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::big_dynamic()
|
|
{
|
|
int i;
|
|
double *shape,*quat,*inertia;
|
|
double inertiaone[3];
|
|
|
|
AtomVecEllipsoid::Bonus *ebonus;
|
|
if (avec_ellipsoid) ebonus = avec_ellipsoid->bonus;
|
|
AtomVecLine::Bonus *lbonus;
|
|
if (avec_line) lbonus = avec_line->bonus;
|
|
AtomVecTri::Bonus *tbonus;
|
|
if (avec_tri) tbonus = avec_tri->bonus;
|
|
double **omega = atom->omega;
|
|
double **angmom = atom->angmom;
|
|
double *rmass = atom->rmass;
|
|
int *ellipsoid = atom->ellipsoid;
|
|
int *line = atom->line;
|
|
int *tri = atom->tri;
|
|
|
|
for (int k = 0; k < nbig; k++) {
|
|
i = biglist[k].index;
|
|
|
|
// sphere
|
|
// set omega from atom->omega directly
|
|
|
|
if (biglist[k].type == SPHERE) {
|
|
biglist[k].omega[0] = omega[i][0];
|
|
biglist[k].omega[1] = omega[i][1];
|
|
biglist[k].omega[2] = omega[i][2];
|
|
|
|
// ellipsoid
|
|
// set ex,ey,ez from quaternion
|
|
// set omega from angmom & ex,ey,ez
|
|
|
|
} else if (biglist[k].type == ELLIPSOID) {
|
|
quat = ebonus[ellipsoid[i]].quat;
|
|
MathExtra::q_to_exyz(quat,biglist[k].ex,biglist[k].ey,biglist[k].ez);
|
|
shape = ebonus[ellipsoid[i]].shape;
|
|
inertiaone[0] = EINERTIA*rmass[i] * (shape[1]*shape[1]+shape[2]*shape[2]);
|
|
inertiaone[1] = EINERTIA*rmass[i] * (shape[0]*shape[0]+shape[2]*shape[2]);
|
|
inertiaone[2] = EINERTIA*rmass[i] * (shape[0]*shape[0]+shape[1]*shape[1]);
|
|
MathExtra::angmom_to_omega(angmom[i],
|
|
biglist[k].ex,biglist[k].ey,biglist[k].ez,
|
|
inertiaone,biglist[k].omega);
|
|
|
|
// line
|
|
// set omega from atom->omega directly
|
|
|
|
} else if (biglist[k].type == LINE) {
|
|
biglist[k].theta = lbonus[line[i]].theta;
|
|
biglist[k].omega[0] = omega[i][0];
|
|
biglist[k].omega[1] = omega[i][1];
|
|
biglist[k].omega[2] = omega[i][2];
|
|
|
|
// tri
|
|
// set ex,ey,ez from quaternion
|
|
// set omega from angmom & ex,ey,ez
|
|
// set unit space-frame norm from body-frame norm
|
|
|
|
} else if (biglist[k].type == TRIANGLE) {
|
|
quat = tbonus[tri[i]].quat;
|
|
MathExtra::q_to_exyz(quat,biglist[k].ex,biglist[k].ey,biglist[k].ez);
|
|
inertia = tbonus[tri[i]].inertia;
|
|
MathExtra::angmom_to_omega(angmom[i],
|
|
biglist[k].ex,biglist[k].ey,biglist[k].ez,
|
|
inertia,biglist[k].omega);
|
|
MathExtra::matvec(biglist[k].ex,biglist[k].ey,biglist[k].ez,
|
|
biglist[k].normbody,biglist[k].norm);
|
|
MathExtra::norm3(biglist[k].norm);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
set bounds for big and SRD particle movement
|
|
called at setup() and when box size changes (but not shape)
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::setup_bounds()
|
|
{
|
|
// triclinic scale factors
|
|
// convert a real distance (perpendicular to box face) to a lamda distance
|
|
|
|
double length0,length1,length2;
|
|
if (triclinic) {
|
|
double *h_inv = domain->h_inv;
|
|
length0 = sqrt(h_inv[0]*h_inv[0] + h_inv[5]*h_inv[5] + h_inv[4]*h_inv[4]);
|
|
length1 = sqrt(h_inv[1]*h_inv[1] + h_inv[3]*h_inv[3]);
|
|
length2 = h_inv[2];
|
|
}
|
|
|
|
// collision object = CO = big particle or wall
|
|
// big particles can be owned or ghost or unknown, walls are all owned
|
|
// dist_ghost = distance from sub-domain (SD) that
|
|
// owned/ghost CO may move to before reneigh,
|
|
// used to bound search bins in setup_search_bins()
|
|
// dist_srd = distance from SD at which SRD could collide with unknown CO,
|
|
// used to error check bounds of SRD movement after collisions via srdlo/hi
|
|
// dist_srd_reneigh = distance from SD at which an SRD should trigger
|
|
// a reneigh, b/c next SRD move might overlap with unknown CO,
|
|
// used for SRD triggering of reneighboring via srdlo/hi_reneigh
|
|
// onemove = max distance an SRD can move in one step
|
|
// if big_particles (and possibly walls):
|
|
// dist_ghost = cut + 1/2 skin due to moving away before reneigh
|
|
// dist_srd = cut - 1/2 skin - 1/2 diam due to ghost CO moving towards
|
|
// dist_reneigh = dist_srd - onemove
|
|
// if walls and no big particles:
|
|
// dist_ghost = 0.0, since not used
|
|
// if no big particles or walls:
|
|
// dist_ghost and dist_srd = 0.0, since not used since no search bins
|
|
// dist_srd_reneigh = subsize - onemove =
|
|
// max distance to move without being lost during comm->exchange()
|
|
// subsize = perp distance between sub-domain faces (orthog or triclinic)
|
|
|
|
double cut = MAX(neighbor->cutneighmax,comm->cutghostuser);
|
|
double onemove = dt_big*vmax;
|
|
|
|
if (bigexist) {
|
|
dist_ghost = cut + 0.5*neighbor->skin;
|
|
dist_srd = cut - 0.5*neighbor->skin - 0.5*maxbigdiam;
|
|
dist_srd_reneigh = dist_srd - onemove;
|
|
} else if (wallexist) {
|
|
dist_ghost = 4*onemove;
|
|
dist_srd = 4*onemove;
|
|
dist_srd_reneigh = 4*onemove - onemove;
|
|
} else {
|
|
dist_ghost = dist_srd = 0.0;
|
|
double subsize;
|
|
if (triclinic == 0) {
|
|
subsize = domain->prd[0]/comm->procgrid[0];
|
|
subsize = MIN(subsize,domain->prd[1]/comm->procgrid[1]);
|
|
if (dimension == 3)
|
|
subsize = MIN(subsize,domain->prd[2]/comm->procgrid[2]);
|
|
} else {
|
|
subsize = 1.0/comm->procgrid[0]/length0;
|
|
subsize = MIN(subsize,1.0/comm->procgrid[1]/length1);
|
|
if (dimension == 3)
|
|
subsize = MIN(subsize,1.0/comm->procgrid[2]/length2);
|
|
}
|
|
dist_srd_reneigh = subsize - onemove;
|
|
}
|
|
|
|
// lo/hi = bbox on this proc which SRD particles must stay inside
|
|
// lo/hi reneigh = bbox on this proc outside of which SRDs trigger a reneigh
|
|
// for triclinic, these bbox are in lamda units
|
|
|
|
if (triclinic == 0) {
|
|
srdlo[0] = domain->sublo[0] - dist_srd;
|
|
srdhi[0] = domain->subhi[0] + dist_srd;
|
|
srdlo[1] = domain->sublo[1] - dist_srd;
|
|
srdhi[1] = domain->subhi[1] + dist_srd;
|
|
srdlo[2] = domain->sublo[2] - dist_srd;
|
|
srdhi[2] = domain->subhi[2] + dist_srd;
|
|
|
|
srdlo_reneigh[0] = domain->sublo[0] - dist_srd_reneigh;
|
|
srdhi_reneigh[0] = domain->subhi[0] + dist_srd_reneigh;
|
|
srdlo_reneigh[1] = domain->sublo[1] - dist_srd_reneigh;
|
|
srdhi_reneigh[1] = domain->subhi[1] + dist_srd_reneigh;
|
|
srdlo_reneigh[2] = domain->sublo[2] - dist_srd_reneigh;
|
|
srdhi_reneigh[2] = domain->subhi[2] + dist_srd_reneigh;
|
|
|
|
} else {
|
|
srdlo[0] = domain->sublo_lamda[0] - dist_srd*length0;
|
|
srdhi[0] = domain->subhi_lamda[0] + dist_srd*length0;
|
|
srdlo[1] = domain->sublo_lamda[1] - dist_srd*length1;
|
|
srdhi[1] = domain->subhi_lamda[1] + dist_srd*length1;
|
|
srdlo[2] = domain->sublo_lamda[2] - dist_srd*length2;
|
|
srdhi[2] = domain->subhi_lamda[2] + dist_srd*length2;
|
|
|
|
srdlo_reneigh[0] = domain->sublo_lamda[0] - dist_srd_reneigh*length0;
|
|
srdhi_reneigh[0] = domain->subhi_lamda[0] + dist_srd_reneigh*length0;
|
|
srdlo_reneigh[1] = domain->sublo_lamda[1] - dist_srd_reneigh*length1;
|
|
srdhi_reneigh[1] = domain->subhi_lamda[1] + dist_srd_reneigh*length1;
|
|
srdlo_reneigh[2] = domain->sublo_lamda[2] - dist_srd_reneigh*length2;
|
|
srdhi_reneigh[2] = domain->subhi_lamda[2] + dist_srd_reneigh*length2;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
setup bins used for binning SRD particles for velocity reset
|
|
gridsrd = desired bin size
|
|
also setup bin shifting parameters
|
|
also setup comm of bins that straddle processor boundaries
|
|
called at beginning of each run
|
|
called every reneighbor if box size changes, but not if box shape changes
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::setup_velocity_bins()
|
|
{
|
|
// require integer # of bins across global domain
|
|
|
|
nbin1x = static_cast<int> (domain->xprd/gridsrd + 0.5);
|
|
nbin1y = static_cast<int> (domain->yprd/gridsrd + 0.5);
|
|
nbin1z = static_cast<int> (domain->zprd/gridsrd + 0.5);
|
|
if (dimension == 2) nbin1z = 1;
|
|
|
|
if (nbin1x == 0) nbin1x = 1;
|
|
if (nbin1y == 0) nbin1y = 1;
|
|
if (nbin1z == 0) nbin1z = 1;
|
|
|
|
if (triclinic == 0) {
|
|
binsize1x = domain->xprd / nbin1x;
|
|
binsize1y = domain->yprd / nbin1y;
|
|
binsize1z = domain->zprd / nbin1z;
|
|
bininv1x = 1.0/binsize1x;
|
|
bininv1y = 1.0/binsize1y;
|
|
bininv1z = 1.0/binsize1z;
|
|
} else {
|
|
binsize1x = 1.0 / nbin1x;
|
|
binsize1y = 1.0 / nbin1y;
|
|
binsize1z = 1.0 / nbin1z;
|
|
bininv1x = nbin1x;
|
|
bininv1y = nbin1y;
|
|
bininv1z = nbin1z;
|
|
}
|
|
|
|
nbins1 = nbin1x*nbin1y*nbin1z;
|
|
|
|
// setup two shifts, 0 = no shift, 1 = shift
|
|
// initialize no shift case since static
|
|
// shift case is dynamic, has to be initialized each time shift occurs
|
|
// setup_velocity_shift allocates memory for vbin and sendlist/recvlist
|
|
|
|
double *boxlo;
|
|
if (triclinic == 0) boxlo = domain->boxlo;
|
|
else boxlo = domain->boxlo_lamda;
|
|
|
|
shifts[0].corner[0] = boxlo[0];
|
|
shifts[0].corner[1] = boxlo[1];
|
|
shifts[0].corner[2] = boxlo[2];
|
|
setup_velocity_shift(0,0);
|
|
|
|
shifts[1].corner[0] = boxlo[0];
|
|
shifts[1].corner[1] = boxlo[1];
|
|
shifts[1].corner[2] = boxlo[2];
|
|
setup_velocity_shift(1,0);
|
|
|
|
// allocate binhead based on max # of bins in either shift
|
|
|
|
int max = shifts[0].nbins;
|
|
max = MAX(max,shifts[1].nbins);
|
|
|
|
if (max > maxbin1) {
|
|
memory->destroy(binhead);
|
|
maxbin1 = max;
|
|
memory->create(binhead,max,"fix/srd:binhead");
|
|
}
|
|
|
|
// allocate sbuf,rbuf based on biggest bin message
|
|
|
|
max = 0;
|
|
for (int ishift = 0; ishift < 2; ishift++)
|
|
for (int iswap = 0; iswap < 2*dimension; iswap++) {
|
|
max = MAX(max,shifts[ishift].bcomm[iswap].nsend);
|
|
max = MAX(max,shifts[ishift].bcomm[iswap].nrecv);
|
|
}
|
|
|
|
if (max > maxbuf) {
|
|
memory->destroy(sbuf1);
|
|
memory->destroy(sbuf2);
|
|
memory->destroy(rbuf1);
|
|
memory->destroy(rbuf2);
|
|
maxbuf = max;
|
|
memory->create(sbuf1,max*VBINSIZE,"fix/srd:sbuf");
|
|
memory->create(sbuf2,max*VBINSIZE,"fix/srd:sbuf");
|
|
memory->create(rbuf1,max*VBINSIZE,"fix/srd:rbuf");
|
|
memory->create(rbuf2,max*VBINSIZE,"fix/srd:rbuf");
|
|
}
|
|
|
|
// commflag = 1 if any comm required due to bins overlapping proc boundaries
|
|
|
|
shifts[0].commflag = 0;
|
|
if (nbin1x % comm->procgrid[0]) shifts[0].commflag = 1;
|
|
if (nbin1y % comm->procgrid[1]) shifts[0].commflag = 1;
|
|
if (nbin1z % comm->procgrid[2]) shifts[0].commflag = 1;
|
|
shifts[1].commflag = 1;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
setup velocity shift parameters
|
|
set binlo[]/binhi[] and nbins,nbinx,nbiny,nbinz for this proc
|
|
set bcomm[6] params based on bin overlaps with proc boundaries
|
|
no comm of bins across non-periodic global boundaries
|
|
set vbin owner flags for bins I am owner of
|
|
ishift = 0, dynamic = 0:
|
|
set all settings since are static
|
|
allocate and set bcomm params and vbins
|
|
do not comm bins that align with proc boundaries
|
|
ishift = 1, dynamic = 0:
|
|
set max bounds on bin counts and message sizes
|
|
allocate and set bcomm params and vbins based on max bounds
|
|
other settings will later change dynamically
|
|
ishift = 1, dynamic = 1:
|
|
set actual bin bounds and counts for specific shift
|
|
set bcomm params and vbins (already allocated)
|
|
called by setup_velocity_bins() and reset_velocities()
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::setup_velocity_shift(int ishift, int dynamic)
|
|
{
|
|
int i,j,k,m,id,nsend;
|
|
int *sendlist;
|
|
BinComm *first,*second;
|
|
BinAve *vbin;
|
|
|
|
double *sublo,*subhi;
|
|
if (triclinic == 0) {
|
|
sublo = domain->sublo;
|
|
subhi = domain->subhi;
|
|
} else {
|
|
sublo = domain->sublo_lamda;
|
|
subhi = domain->subhi_lamda;
|
|
}
|
|
|
|
int *binlo = shifts[ishift].binlo;
|
|
int *binhi = shifts[ishift].binhi;
|
|
double *corner = shifts[ishift].corner;
|
|
int *procgrid = comm->procgrid;
|
|
int *myloc = comm->myloc;
|
|
|
|
binlo[0] = static_cast<int> ((sublo[0]-corner[0])*bininv1x);
|
|
binlo[1] = static_cast<int> ((sublo[1]-corner[1])*bininv1y);
|
|
binlo[2] = static_cast<int> ((sublo[2]-corner[2])*bininv1z);
|
|
if (dimension == 2) shifts[ishift].binlo[2] = 0;
|
|
|
|
binhi[0] = static_cast<int> ((subhi[0]-corner[0])*bininv1x);
|
|
binhi[1] = static_cast<int> ((subhi[1]-corner[1])*bininv1y);
|
|
binhi[2] = static_cast<int> ((subhi[2]-corner[2])*bininv1z);
|
|
if (dimension == 2) shifts[ishift].binhi[2] = 0;
|
|
|
|
if (ishift == 0) {
|
|
if (myloc[0]*nbin1x % procgrid[0] == 0)
|
|
binlo[0] = myloc[0]*nbin1x/procgrid[0];
|
|
if (myloc[1]*nbin1y % procgrid[1] == 0)
|
|
binlo[1] = myloc[1]*nbin1y/procgrid[1];
|
|
if (myloc[2]*nbin1z % procgrid[2] == 0)
|
|
binlo[2] = myloc[2]*nbin1z/procgrid[2];
|
|
|
|
if ((myloc[0]+1)*nbin1x % procgrid[0] == 0)
|
|
binhi[0] = (myloc[0]+1)*nbin1x/procgrid[0] - 1;
|
|
if ((myloc[1]+1)*nbin1y % procgrid[1] == 0)
|
|
binhi[1] = (myloc[1]+1)*nbin1y/procgrid[1] - 1;
|
|
if ((myloc[2]+1)*nbin1z % procgrid[2] == 0)
|
|
binhi[2] = (myloc[2]+1)*nbin1z/procgrid[2] - 1;
|
|
}
|
|
|
|
int nbinx = binhi[0] - binlo[0] + 1;
|
|
int nbiny = binhi[1] - binlo[1] + 1;
|
|
int nbinz = binhi[2] - binlo[2] + 1;
|
|
|
|
// allow for one extra bin if shifting will occur
|
|
|
|
if (ishift == 1 && dynamic == 0) {
|
|
nbinx++;
|
|
nbiny++;
|
|
if (dimension == 3) nbinz++;
|
|
}
|
|
|
|
int nbins = nbinx*nbiny*nbinz;
|
|
int nbinxy = nbinx*nbiny;
|
|
int nbinsq = nbinx*nbiny;
|
|
nbinsq = MAX(nbiny*nbinz,nbinsq);
|
|
nbinsq = MAX(nbinx*nbinz,nbinsq);
|
|
|
|
shifts[ishift].nbins = nbins;
|
|
shifts[ishift].nbinx = nbinx;
|
|
shifts[ishift].nbiny = nbiny;
|
|
shifts[ishift].nbinz = nbinz;
|
|
|
|
int reallocflag = 0;
|
|
if (dynamic == 0 && nbinsq > shifts[ishift].maxbinsq) {
|
|
shifts[ishift].maxbinsq = nbinsq;
|
|
reallocflag = 1;
|
|
}
|
|
|
|
// bcomm neighbors
|
|
// first = send in lo direction, recv from hi direction
|
|
// second = send in hi direction, recv from lo direction
|
|
|
|
if (dynamic == 0) {
|
|
shifts[ishift].bcomm[0].sendproc = comm->procneigh[0][0];
|
|
shifts[ishift].bcomm[0].recvproc = comm->procneigh[0][1];
|
|
shifts[ishift].bcomm[1].sendproc = comm->procneigh[0][1];
|
|
shifts[ishift].bcomm[1].recvproc = comm->procneigh[0][0];
|
|
shifts[ishift].bcomm[2].sendproc = comm->procneigh[1][0];
|
|
shifts[ishift].bcomm[2].recvproc = comm->procneigh[1][1];
|
|
shifts[ishift].bcomm[3].sendproc = comm->procneigh[1][1];
|
|
shifts[ishift].bcomm[3].recvproc = comm->procneigh[1][0];
|
|
shifts[ishift].bcomm[4].sendproc = comm->procneigh[2][0];
|
|
shifts[ishift].bcomm[4].recvproc = comm->procneigh[2][1];
|
|
shifts[ishift].bcomm[5].sendproc = comm->procneigh[2][1];
|
|
shifts[ishift].bcomm[5].recvproc = comm->procneigh[2][0];
|
|
}
|
|
|
|
// set nsend,nrecv and sendlist,recvlist for each swap in x,y,z
|
|
// set nsend,nrecv = 0 if static bins align with proc boundary
|
|
// or to prevent dynamic bin swapping across non-periodic global boundary
|
|
// allocate sendlist,recvlist only for dynamic = 0
|
|
|
|
first = &shifts[ishift].bcomm[0];
|
|
second = &shifts[ishift].bcomm[1];
|
|
|
|
first->nsend = first->nrecv = second->nsend = second->nrecv = nbiny*nbinz;
|
|
if (ishift == 0) {
|
|
if (myloc[0]*nbin1x % procgrid[0] == 0)
|
|
first->nsend = second->nrecv = 0;
|
|
if ((myloc[0]+1)*nbin1x % procgrid[0] == 0)
|
|
second->nsend = first->nrecv = 0;
|
|
} else {
|
|
if (domain->xperiodic == 0) {
|
|
if (myloc[0] == 0) first->nsend = second->nrecv = 0;
|
|
if (myloc[0] == procgrid[0]-1) second->nsend = first->nrecv = 0;
|
|
}
|
|
}
|
|
|
|
if (reallocflag) {
|
|
memory->destroy(first->sendlist);
|
|
memory->destroy(first->recvlist);
|
|
memory->destroy(second->sendlist);
|
|
memory->destroy(second->recvlist);
|
|
memory->create(first->sendlist,nbinsq,"fix/srd:sendlist");
|
|
memory->create(first->recvlist,nbinsq,"fix/srd:sendlist");
|
|
memory->create(second->sendlist,nbinsq,"fix/srd:sendlist");
|
|
memory->create(second->recvlist,nbinsq,"fix/srd:sendlist");
|
|
}
|
|
|
|
m = 0;
|
|
i = 0;
|
|
for (j = 0; j < nbiny; j++)
|
|
for (k = 0; k < nbinz; k++) {
|
|
id = k*nbinxy + j*nbinx + i;
|
|
first->sendlist[m] = second->recvlist[m] = id;
|
|
m++;
|
|
}
|
|
m = 0;
|
|
i = nbinx-1;
|
|
for (j = 0; j < nbiny; j++)
|
|
for (k = 0; k < nbinz; k++) {
|
|
id = k*nbinxy + j*nbinx + i;
|
|
second->sendlist[m] = first->recvlist[m] = id;
|
|
m++;
|
|
}
|
|
|
|
first = &shifts[ishift].bcomm[2];
|
|
second = &shifts[ishift].bcomm[3];
|
|
|
|
first->nsend = first->nrecv = second->nsend = second->nrecv = nbinx*nbinz;
|
|
if (ishift == 0) {
|
|
if (myloc[1]*nbin1y % procgrid[1] == 0)
|
|
first->nsend = second->nrecv = 0;
|
|
if ((myloc[1]+1)*nbin1y % procgrid[1] == 0)
|
|
second->nsend = first->nrecv = 0;
|
|
} else {
|
|
if (domain->yperiodic == 0) {
|
|
if (myloc[1] == 0) first->nsend = second->nrecv = 0;
|
|
if (myloc[1] == procgrid[1]-1) second->nsend = first->nrecv = 0;
|
|
}
|
|
}
|
|
|
|
if (reallocflag) {
|
|
memory->destroy(first->sendlist);
|
|
memory->destroy(first->recvlist);
|
|
memory->destroy(second->sendlist);
|
|
memory->destroy(second->recvlist);
|
|
memory->create(first->sendlist,nbinsq,"fix/srd:sendlist");
|
|
memory->create(first->recvlist,nbinsq,"fix/srd:sendlist");
|
|
memory->create(second->sendlist,nbinsq,"fix/srd:sendlist");
|
|
memory->create(second->recvlist,nbinsq,"fix/srd:sendlist");
|
|
}
|
|
|
|
m = 0;
|
|
j = 0;
|
|
for (i = 0; i < nbinx; i++)
|
|
for (k = 0; k < nbinz; k++) {
|
|
id = k*nbinxy + j*nbinx + i;
|
|
first->sendlist[m] = second->recvlist[m] = id;
|
|
m++;
|
|
}
|
|
m = 0;
|
|
j = nbiny-1;
|
|
for (i = 0; i < nbinx; i++)
|
|
for (k = 0; k < nbinz; k++) {
|
|
id = k*nbinxy + j*nbinx + i;
|
|
second->sendlist[m] = first->recvlist[m] = id;
|
|
m++;
|
|
}
|
|
|
|
if (dimension == 3) {
|
|
first = &shifts[ishift].bcomm[4];
|
|
second = &shifts[ishift].bcomm[5];
|
|
|
|
first->nsend = first->nrecv = second->nsend = second->nrecv = nbinx*nbiny;
|
|
if (ishift == 0) {
|
|
if (myloc[2]*nbin1z % procgrid[2] == 0)
|
|
first->nsend = second->nrecv = 0;
|
|
if ((myloc[2]+1)*nbin1z % procgrid[2] == 0)
|
|
second->nsend = first->nrecv = 0;
|
|
} else {
|
|
if (domain->zperiodic == 0) {
|
|
if (myloc[2] == 0) first->nsend = second->nrecv = 0;
|
|
if (myloc[2] == procgrid[2]-1) second->nsend = first->nrecv = 0;
|
|
}
|
|
}
|
|
|
|
if (reallocflag) {
|
|
memory->destroy(first->sendlist);
|
|
memory->destroy(first->recvlist);
|
|
memory->destroy(second->sendlist);
|
|
memory->destroy(second->recvlist);
|
|
memory->create(first->sendlist,nbinx*nbiny,"fix/srd:sendlist");
|
|
memory->create(first->recvlist,nbinx*nbiny,"fix/srd:sendlist");
|
|
memory->create(second->sendlist,nbinx*nbiny,"fix/srd:sendlist");
|
|
memory->create(second->recvlist,nbinx*nbiny,"fix/srd:sendlist");
|
|
}
|
|
|
|
m = 0;
|
|
k = 0;
|
|
for (i = 0; i < nbinx; i++)
|
|
for (j = 0; j < nbiny; j++) {
|
|
id = k*nbinxy + j*nbinx + i;
|
|
first->sendlist[m] = second->recvlist[m] = id;
|
|
m++;
|
|
}
|
|
m = 0;
|
|
k = nbinz-1;
|
|
for (i = 0; i < nbinx; i++)
|
|
for (j = 0; j < nbiny; j++) {
|
|
id = k*nbinxy + j*nbinx + i;
|
|
second->sendlist[m] = first->recvlist[m] = id;
|
|
m++;
|
|
}
|
|
}
|
|
|
|
// allocate vbins, only for dynamic = 0
|
|
|
|
if (dynamic == 0 && nbins > shifts[ishift].maxvbin) {
|
|
memory->destroy(shifts[ishift].vbin);
|
|
shifts[ishift].maxvbin = nbins;
|
|
shifts[ishift].vbin = (BinAve *)
|
|
memory->smalloc(nbins*sizeof(BinAve),"fix/srd:vbin");
|
|
}
|
|
|
|
// for vbins I own, set owner = 1
|
|
// if bin never sent to anyone, I own it
|
|
// if bin sent to lower numbered proc, I do not own it
|
|
// if bin sent to self, I do not own it on even swap (avoids double counting)
|
|
|
|
vbin = shifts[ishift].vbin;
|
|
for (i = 0; i < nbins; i++) vbin[i].owner = 1;
|
|
for (int iswap = 0; iswap < 2*dimension; iswap++) {
|
|
if (shifts[ishift].bcomm[iswap].sendproc > me) continue;
|
|
if (shifts[ishift].bcomm[iswap].sendproc == me && iswap % 2 == 0) continue;
|
|
nsend = shifts[ishift].bcomm[iswap].nsend;
|
|
sendlist = shifts[ishift].bcomm[iswap].sendlist;
|
|
for (m = 0; m < nsend; m++) vbin[sendlist[m]].owner = 0;
|
|
}
|
|
|
|
// if tstat and deformflag:
|
|
// set xctr for all nbins in lamda units so can later compute vstream of bin
|
|
|
|
if (tstat && deformflag) {
|
|
m = 0;
|
|
for (k = 0; k < nbinz; k++)
|
|
for (j = 0; j < nbiny; j++)
|
|
for (i = 0; i < nbinx; i++) {
|
|
vbin[m].xctr[0] = corner[0] + (i+binlo[0]+0.5)/nbin1x;
|
|
vbin[m].xctr[1] = corner[1] + (j+binlo[1]+0.5)/nbin1y;
|
|
vbin[m].xctr[2] = corner[2] + (k+binlo[2]+0.5)/nbin1z;
|
|
m++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
setup bins used for big and SRD particle searching
|
|
gridsearch = desired bin size
|
|
bins are orthogonal whether simulation box is orthogonal or triclinic
|
|
for orthogonal boxes, called at each setup since cutghost may change
|
|
for triclinic boxes, called at every reneigh, since tilt may change
|
|
sets the following:
|
|
nbin2 xyz = # of bins in each dim
|
|
binsize2 and bininv2 xyz = size of bins in each dim
|
|
xyz blo2 = origin of bins
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::setup_search_bins()
|
|
{
|
|
// subboxlo/hi = real space bbox which
|
|
// owned/ghost big particles or walls can be in
|
|
// start with bounding box for my sub-domain, add dist_ghost
|
|
// for triclinic, need to:
|
|
// a) convert dist_ghost to lamda space via length012
|
|
// b) lo/hi = sub-domain big particle bbox in lamda space
|
|
// c) convert lo/hi to real space bounding box via domain->bbox()
|
|
// similar to neighbor::setup_bins() and comm::cutghost[] calculation
|
|
|
|
double subboxlo[3],subboxhi[3];
|
|
|
|
if (triclinic == 0) {
|
|
subboxlo[0] = domain->sublo[0] - dist_ghost;
|
|
subboxlo[1] = domain->sublo[1] - dist_ghost;
|
|
subboxlo[2] = domain->sublo[2] - dist_ghost;
|
|
subboxhi[0] = domain->subhi[0] + dist_ghost;
|
|
subboxhi[1] = domain->subhi[1] + dist_ghost;
|
|
subboxhi[2] = domain->subhi[2] + dist_ghost;
|
|
} else {
|
|
double *h_inv = domain->h_inv;
|
|
double length0,length1,length2;
|
|
length0 = sqrt(h_inv[0]*h_inv[0] + h_inv[5]*h_inv[5] + h_inv[4]*h_inv[4]);
|
|
length1 = sqrt(h_inv[1]*h_inv[1] + h_inv[3]*h_inv[3]);
|
|
length2 = h_inv[2];
|
|
double lo[3],hi[3];
|
|
lo[0] = domain->sublo_lamda[0] - dist_ghost*length0;
|
|
lo[1] = domain->sublo_lamda[1] - dist_ghost*length1;
|
|
lo[2] = domain->sublo_lamda[2] - dist_ghost*length2;
|
|
hi[0] = domain->subhi_lamda[0] + dist_ghost*length0;
|
|
hi[1] = domain->subhi_lamda[1] + dist_ghost*length1;
|
|
hi[2] = domain->subhi_lamda[2] + dist_ghost*length2;
|
|
domain->bbox(lo,hi,subboxlo,subboxhi);
|
|
}
|
|
|
|
// require integer # of bins for that volume
|
|
|
|
nbin2x = static_cast<int> ((subboxhi[0] - subboxlo[0]) / gridsearch);
|
|
nbin2y = static_cast<int> ((subboxhi[1] - subboxlo[1]) / gridsearch);
|
|
nbin2z = static_cast<int> ((subboxhi[2] - subboxlo[2]) / gridsearch);
|
|
if (dimension == 2) nbin2z = 1;
|
|
|
|
if (nbin2x == 0) nbin2x = 1;
|
|
if (nbin2y == 0) nbin2y = 1;
|
|
if (nbin2z == 0) nbin2z = 1;
|
|
|
|
binsize2x = (subboxhi[0] - subboxlo[0]) / nbin2x;
|
|
binsize2y = (subboxhi[1] - subboxlo[1]) / nbin2y;
|
|
binsize2z = (subboxhi[2] - subboxlo[2]) / nbin2z;
|
|
bininv2x = 1.0/binsize2x;
|
|
bininv2y = 1.0/binsize2y;
|
|
bininv2z = 1.0/binsize2z;
|
|
|
|
// add bins on either end due to extent of big particles
|
|
// radmax = max distance from central bin that biggest particle overlaps
|
|
// includes skin movement
|
|
// nx,ny,nz = max # of bins to search away from central bin
|
|
|
|
double radmax = 0.5*maxbigdiam + 0.5*neighbor->skin;
|
|
|
|
int nx = static_cast<int> (radmax/binsize2x) + 1;
|
|
int ny = static_cast<int> (radmax/binsize2y) + 1;
|
|
int nz = static_cast<int> (radmax/binsize2z) + 1;
|
|
if (dimension == 2) nz = 0;
|
|
|
|
nbin2x += 2*nx;
|
|
nbin2y += 2*ny;
|
|
nbin2z += 2*nz;
|
|
|
|
xblo2 = subboxlo[0] - nx*binsize2x;
|
|
yblo2 = subboxlo[1] - ny*binsize2y;
|
|
zblo2 = subboxlo[2] - nz*binsize2z;
|
|
if (dimension == 2) zblo2 = domain->boxlo[2];
|
|
|
|
// allocate bins
|
|
// first deallocate previous bins if necessary
|
|
|
|
nbins2 = nbin2x*nbin2y*nbin2z;
|
|
if (nbins2 > maxbin2) {
|
|
memory->destroy(nbinbig);
|
|
memory->destroy(binbig);
|
|
maxbin2 = nbins2;
|
|
memory->create(nbinbig,nbins2,"fix/srd:nbinbig");
|
|
memory->create(binbig,nbins2,ATOMPERBIN,"fix/srd:binbig");
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute stencil of bin offsets for a big particle overlapping search bins
|
|
------------------------------------------------------------------------- */
|
|
|
|
void FixSRD::setup_search_stencil()
|
|
{
|
|
// radmax = max distance from central bin that any big particle overlaps
|
|
// includes skin movement
|
|
// nx,ny,nz = max # of bins to search away from central bin
|
|
|
|
double radmax = 0.5*maxbigdiam + 0.5*neighbor->skin;
|
|
double radsq = radmax*radmax;
|
|
|
|
int nx = static_cast<int> (radmax/binsize2x) + 1;
|
|
int ny = static_cast<int> (radmax/binsize2y) + 1;
|
|
int nz = static_cast<int> (radmax/binsize2z) + 1;
|
|
if (dimension == 2) nz = 0;
|
|
|
|
// allocate stencil array
|
|
// deallocate previous stencil if necessary
|
|
|
|
int max = (2*nx+1) * (2*ny+1) * (2*nz+1);
|
|
if (max > maxstencil) {
|
|
memory->destroy(stencil);
|
|
maxstencil = max;
|
|
memory->create(stencil,max,4,"fix/srd:stencil");
|
|
}
|
|
|
|
// loop over all bins
|
|
// add bin to stencil:
|
|
// if distance of closest corners of bin and central bin is within cutoff
|
|
|
|
nstencil = 0;
|
|
for (int k = -nz; k <= nz; k++)
|
|
for (int j = -ny; j <= ny; j++)
|
|
for (int i = -nx; i <= nx; i++)
|
|
if (bin_bin_distance(i,j,k) < radsq) {
|
|
stencil[nstencil][0] = i;
|
|
stencil[nstencil][1] = j;
|
|
stencil[nstencil][2] = k;
|
|
stencil[nstencil][3] = k*nbin2y*nbin2x + j*nbin2x + i;
|
|
nstencil++;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute closest squared distance between point x and bin ibin
|
|
------------------------------------------------------------------------- */
|
|
|
|
double FixSRD::point_bin_distance(double *x, int i, int j, int k)
|
|
{
|
|
double delx,dely,delz;
|
|
|
|
double xlo = xblo2 + i*binsize2x;
|
|
double xhi = xlo + binsize2x;
|
|
double ylo = yblo2 + j*binsize2y;
|
|
double yhi = ylo + binsize2y;
|
|
double zlo = zblo2 + k*binsize2z;
|
|
double zhi = zlo + binsize2z;
|
|
|
|
if (x[0] < xlo) delx = xlo - x[0];
|
|
else if (x[0] > xhi) delx = x[0] - xhi;
|
|
else delx = 0.0;
|
|
|
|
if (x[1] < ylo) dely = ylo - x[1];
|
|
else if (x[1] > yhi) dely = x[1] - yhi;
|
|
else dely = 0.0;
|
|
|
|
if (x[2] < zlo) delz = zlo - x[2];
|
|
else if (x[2] > zhi) delz = x[2] - zhi;
|
|
else delz = 0.0;
|
|
|
|
return (delx*delx + dely*dely + delz*delz);
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
compute closest squared distance between 2 bins
|
|
central bin (0,0,0) and bin (i,j,k)
|
|
------------------------------------------------------------------------- */
|
|
|
|
double FixSRD::bin_bin_distance(int i, int j, int k)
|
|
{
|
|
double delx,dely,delz;
|
|
|
|
if (i > 0) delx = (i-1)*binsize2x;
|
|
else if (i == 0) delx = 0.0;
|
|
else delx = (i+1)*binsize2x;
|
|
|
|
if (j > 0) dely = (j-1)*binsize2y;
|
|
else if (j == 0) dely = 0.0;
|
|
else dely = (j+1)*binsize2y;
|
|
|
|
if (k > 0) delz = (k-1)*binsize2z;
|
|
else if (k == 0) delz = 0.0;
|
|
else delz = (k+1)*binsize2z;
|
|
|
|
return (delx*delx + dely*dely + delz*delz);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
int FixSRD::pack_reverse_comm(int n, int first, double *buf)
|
|
{
|
|
int i,m,last;
|
|
|
|
m = 0;
|
|
last = first + n;
|
|
|
|
if (torqueflag) {
|
|
for (i = first; i < last; i++) {
|
|
buf[m++] = flocal[i][0];
|
|
buf[m++] = flocal[i][1];
|
|
buf[m++] = flocal[i][2];
|
|
buf[m++] = tlocal[i][0];
|
|
buf[m++] = tlocal[i][1];
|
|
buf[m++] = tlocal[i][2];
|
|
}
|
|
|
|
} else {
|
|
for (i = first; i < last; i++) {
|
|
buf[m++] = flocal[i][0];
|
|
buf[m++] = flocal[i][1];
|
|
buf[m++] = flocal[i][2];
|
|
}
|
|
}
|
|
|
|
return m;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void FixSRD::unpack_reverse_comm(int n, int *list, double *buf)
|
|
{
|
|
int i,j,m;
|
|
|
|
m = 0;
|
|
|
|
if (torqueflag) {
|
|
for (i = 0; i < n; i++) {
|
|
j = list[i];
|
|
flocal[j][0] += buf[m++];
|
|
flocal[j][1] += buf[m++];
|
|
flocal[j][2] += buf[m++];
|
|
tlocal[j][0] += buf[m++];
|
|
tlocal[j][1] += buf[m++];
|
|
tlocal[j][2] += buf[m++];
|
|
}
|
|
|
|
} else {
|
|
for (i = 0; i < n; i++) {
|
|
j = list[i];
|
|
flocal[j][0] += buf[m++];
|
|
flocal[j][1] += buf[m++];
|
|
flocal[j][2] += buf[m++];
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
SRD stats
|
|
------------------------------------------------------------------------- */
|
|
|
|
double FixSRD::compute_vector(int n)
|
|
{
|
|
// only sum across procs one time
|
|
|
|
if (stats_flag == 0) {
|
|
stats[0] = ncheck;
|
|
stats[1] = ncollide;
|
|
stats[2] = nbounce;
|
|
stats[3] = ninside;
|
|
stats[4] = nrescale;
|
|
stats[5] = nbins2;
|
|
stats[6] = nbins1;
|
|
stats[7] = srd_bin_count;
|
|
stats[8] = srd_bin_temp;
|
|
stats[9] = bouncemaxnum;
|
|
stats[10] = bouncemax;
|
|
stats[11] = reneighcount;
|
|
|
|
MPI_Allreduce(stats,stats_all,10,MPI_DOUBLE,MPI_SUM,world);
|
|
MPI_Allreduce(&stats[10],&stats_all[10],1,MPI_DOUBLE,MPI_MAX,world);
|
|
if (stats_all[7] != 0.0) stats_all[8] /= stats_all[7];
|
|
stats_all[6] /= nprocs;
|
|
|
|
stats_flag = 1;
|
|
}
|
|
|
|
return stats_all[n];
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void FixSRD::velocity_stats(int groupnum)
|
|
{
|
|
int bitmask = group->bitmask[groupnum];
|
|
|
|
double **v = atom->v;
|
|
int *mask = atom->mask;
|
|
int nlocal = atom->nlocal;
|
|
|
|
double vone;
|
|
double vave = 0.0;
|
|
double vmax = 0.0;
|
|
|
|
for (int i = 0; i < nlocal; i++)
|
|
if (mask[i] & bitmask) {
|
|
vone = sqrt(v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]);
|
|
vave += vone;
|
|
if (vone > vmax) vmax = vone;
|
|
}
|
|
|
|
double all;
|
|
MPI_Allreduce(&vave,&all,1,MPI_DOUBLE,MPI_SUM,world);
|
|
double count = group->count(groupnum);
|
|
if (count != 0.0) vave = all/count;
|
|
else vave = 0.0;
|
|
|
|
MPI_Allreduce(&vmax,&all,1,MPI_DOUBLE,MPI_MAX,world);
|
|
vmax = all;
|
|
|
|
if (me == 0) {
|
|
if (screen)
|
|
fprintf(screen," ave/max %s velocity = %g %g\n",
|
|
group->names[groupnum],vave,vmax);
|
|
if (logfile)
|
|
fprintf(logfile," ave/max %s velocity = %g %g\n",
|
|
group->names[groupnum],vave,vmax);
|
|
}
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
double FixSRD::newton_raphson(double t1, double t2)
|
|
{
|
|
double f1,df,tlo,thi;
|
|
lineside(t1,f1,df);
|
|
if (f1 < 0.0) {
|
|
tlo = t1;
|
|
thi = t2;
|
|
} else {
|
|
thi = t1;
|
|
tlo = t2;
|
|
}
|
|
|
|
double f;
|
|
double t = 0.5*(t1+t2);
|
|
double dtold = fabs(t2-t1);
|
|
double dt = dtold;
|
|
lineside(t,f,df);
|
|
|
|
double temp;
|
|
for (int i = 0; i < MAXITER; i++) {
|
|
if ((((t-thi)*df - f)*((t-tlo)*df - f) > 0.0) ||
|
|
(fabs(2.0*f) > fabs(dtold*df))) {
|
|
dtold = dt;
|
|
dt = 0.5 * (thi-tlo);
|
|
t = tlo + dt;
|
|
if (tlo == t) return t;
|
|
} else {
|
|
dtold = dt;
|
|
dt = f / df;
|
|
temp = t;
|
|
t -= dt;
|
|
if (temp == t) return t;
|
|
}
|
|
if (fabs(dt) < TOLERANCE) return t;
|
|
lineside(t,f,df);
|
|
if (f < 0.0) tlo = t;
|
|
else thi = t;
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void FixSRD::lineside(double t, double &f, double &df)
|
|
{
|
|
double p[2],c[2];
|
|
|
|
p[0] = xs0[0] + (xs1[0]-xs0[0])*t;
|
|
p[1] = xs0[1] + (xs1[1]-xs0[1])*t;
|
|
c[0] = xb0[0] + (xb1[0]-xb0[0])*t;
|
|
c[1] = xb0[1] + (xb1[1]-xb0[1])*t;
|
|
double dtheta = theta1 - theta0;
|
|
double theta = theta0 + dtheta*t;
|
|
double cosT = cos(theta);
|
|
double sinT = sin(theta);
|
|
|
|
f = (p[1]-c[1]) * cosT - (p[0]-c[0]) * sinT;
|
|
df = ((xs1[1]-xs0[1]) - (xb1[1]-xb0[1]))*cosT - (p[1]-c[1])*sinT*dtheta -
|
|
((xs1[0]-xs0[0]) - (xb1[0]-xb0[0]))*sinT - (p[0]-c[0])*cosT*dtheta;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void FixSRD::triside(double t, double &f, double &df)
|
|
{
|
|
double p[2],c[2];
|
|
|
|
p[0] = xs0[0] + (xs1[0]-xs0[0])*t;
|
|
p[1] = xs0[1] + (xs1[1]-xs0[1])*t;
|
|
c[0] = xb0[0] + (xb1[0]-xb0[0])*t;
|
|
c[1] = xb0[1] + (xb1[1]-xb0[1])*t;
|
|
double dtheta = theta1 - theta0;
|
|
double theta = theta0 + dtheta*t;
|
|
double cosT = cos(theta);
|
|
double sinT = sin(theta);
|
|
|
|
f = (p[1]-c[1]) * cosT - (p[0]-c[0]) * sinT;
|
|
df = ((xs1[1]-xs0[1]) - (xb1[1]-xb0[1]))*cosT - (p[1]-c[1])*sinT*dtheta -
|
|
((xs1[0]-xs0[0]) - (xb1[0]-xb0[0]))*sinT - (p[0]-c[0])*cosT*dtheta;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
double FixSRD::memory_usage()
|
|
{
|
|
double bytes = 0.0;
|
|
bytes += (shifts[0].nbins + shifts[1].nbins) * sizeof(BinAve);
|
|
bytes += nmax * sizeof(int);
|
|
if (bigexist) {
|
|
bytes += nbins2 * sizeof(int);
|
|
bytes += nbins2*ATOMPERBIN * sizeof(int);
|
|
}
|
|
bytes += nmax * sizeof(int);
|
|
return bytes;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------
|
|
useful debugging functions
|
|
------------------------------------------------------------------------- */
|
|
|
|
double FixSRD::distance(int i, int j)
|
|
{
|
|
double dx = atom->x[i][0] - atom->x[j][0];
|
|
double dy = atom->x[i][1] - atom->x[j][1];
|
|
double dz = atom->x[i][2] - atom->x[j][2];
|
|
return sqrt(dx*dx + dy*dy + dz*dz);
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void FixSRD::print_collision(int i, int j, int ibounce,
|
|
double t_remain, double dt,
|
|
double *xscoll, double *xbcoll, double *norm,
|
|
int type)
|
|
{
|
|
double xsstart[3],xbstart[3];
|
|
double **x = atom->x;
|
|
double **v = atom->v;
|
|
|
|
if (type != WALL) {
|
|
printf("COLLISION between SRD " TAGINT_FORMAT
|
|
" and BIG " TAGINT_FORMAT "\n",atom->tag[i],atom->tag[j]);
|
|
printf(" bounce # = %d\n",ibounce+1);
|
|
printf(" local indices: %d %d\n",i,j);
|
|
printf(" timestep = %g\n",dt);
|
|
printf(" time remaining post-collision = %g\n",t_remain);
|
|
|
|
xsstart[0] = x[i][0] - dt*v[i][0];
|
|
xsstart[1] = x[i][1] - dt*v[i][1];
|
|
xsstart[2] = x[i][2] - dt*v[i][2];
|
|
xbstart[0] = x[j][0] - dt*v[j][0];
|
|
xbstart[1] = x[j][1] - dt*v[j][1];
|
|
xbstart[2] = x[j][2] - dt*v[j][2];
|
|
|
|
printf(" SRD start position = %g %g %g\n",
|
|
xsstart[0],xsstart[1],xsstart[2]);
|
|
printf(" BIG start position = %g %g %g\n",
|
|
xbstart[0],xbstart[1],xbstart[2]);
|
|
printf(" SRD coll position = %g %g %g\n",
|
|
xscoll[0],xscoll[1],xscoll[2]);
|
|
printf(" BIG coll position = %g %g %g\n",
|
|
xbcoll[0],xbcoll[1],xbcoll[2]);
|
|
printf(" SRD end position = %g %g %g\n",x[i][0],x[i][1],x[i][2]);
|
|
printf(" BIG end position = %g %g %g\n",x[j][0],x[j][1],x[j][2]);
|
|
|
|
printf(" SRD vel = %g %g %g\n",v[i][0],v[i][1],v[i][2]);
|
|
printf(" BIG vel = %g %g %g\n",v[j][0],v[j][1],v[j][2]);
|
|
printf(" surf norm = %g %g %g\n",norm[0],norm[1],norm[2]);
|
|
|
|
double rstart = sqrt((xsstart[0]-xbstart[0])*(xsstart[0]-xbstart[0]) +
|
|
(xsstart[1]-xbstart[1])*(xsstart[1]-xbstart[1]) +
|
|
(xsstart[2]-xbstart[2])*(xsstart[2]-xbstart[2]));
|
|
double rcoll = sqrt((xscoll[0]-xbcoll[0])*(xscoll[0]-xbcoll[0]) +
|
|
(xscoll[1]-xbcoll[1])*(xscoll[1]-xbcoll[1]) +
|
|
(xscoll[2]-xbcoll[2])*(xscoll[2]-xbcoll[2]));
|
|
double rend = sqrt((x[i][0]-x[j][0])*(x[i][0]-x[j][0]) +
|
|
(x[i][1]-x[j][1])*(x[i][1]-x[j][1]) +
|
|
(x[i][2]-x[j][2])*(x[i][2]-x[j][2]));
|
|
|
|
printf(" separation at start = %g\n",rstart);
|
|
printf(" separation at coll = %g\n",rcoll);
|
|
printf(" separation at end = %g\n",rend);
|
|
|
|
} else {
|
|
int dim = wallwhich[j] / 2;
|
|
|
|
printf("COLLISION between SRD " TAGINT_FORMAT " and WALL %d\n",
|
|
atom->tag[i],j);
|
|
printf(" bounce # = %d\n",ibounce+1);
|
|
printf(" local indices: %d %d\n",i,j);
|
|
printf(" timestep = %g\n",dt);
|
|
printf(" time remaining post-collision = %g\n",t_remain);
|
|
|
|
xsstart[0] = x[i][0] - dt*v[i][0];
|
|
xsstart[1] = x[i][1] - dt*v[i][1];
|
|
xsstart[2] = x[i][2] - dt*v[i][2];
|
|
xbstart[0] = xbstart[1] = xbstart[2] = 0.0;
|
|
xbstart[dim] = xwall[j] - dt*vwall[j];
|
|
|
|
printf(" SRD start position = %g %g %g\n",
|
|
xsstart[0],xsstart[1],xsstart[2]);
|
|
printf(" WALL start position = %g\n",xbstart[dim]);
|
|
printf(" SRD coll position = %g %g %g\n",
|
|
xscoll[0],xscoll[1],xscoll[2]);
|
|
printf(" WALL coll position = %g\n",xbcoll[dim]);
|
|
printf(" SRD end position = %g %g %g\n",x[i][0],x[i][1],x[i][2]);
|
|
printf(" WALL end position = %g\n",xwall[j]);
|
|
|
|
printf(" SRD vel = %g %g %g\n",v[i][0],v[i][1],v[i][2]);
|
|
printf(" WALL vel = %g\n",vwall[j]);
|
|
printf(" surf norm = %g %g %g\n",norm[0],norm[1],norm[2]);
|
|
|
|
double rstart = xsstart[dim]-xbstart[dim];
|
|
double rcoll = xscoll[dim]-xbcoll[dim];
|
|
double rend = x[dim][0]-xwall[j];
|
|
|
|
printf(" separation at start = %g\n",rstart);
|
|
printf(" separation at coll = %g\n",rcoll);
|
|
printf(" separation at end = %g\n",rend);
|
|
}
|
|
}
|