308 lines
9.5 KiB
C++
308 lines
9.5 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 author: Craig Tenney (UND) added support
|
|
for swapping atoms of different masses
|
|
------------------------------------------------------------------------- */
|
|
|
|
#include "math.h"
|
|
#include "mpi.h"
|
|
#include "string.h"
|
|
#include "stdlib.h"
|
|
#include "fix_viscosity.h"
|
|
#include "atom.h"
|
|
#include "domain.h"
|
|
#include "modify.h"
|
|
#include "error.h"
|
|
|
|
using namespace LAMMPS_NS;
|
|
using namespace FixConst;
|
|
|
|
// needs to be big, but not so big that lose precision when subtract velocity
|
|
|
|
#define BIG 1.0e10
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
FixViscosity::FixViscosity(LAMMPS *lmp, int narg, char **arg) :
|
|
Fix(lmp, narg, arg)
|
|
{
|
|
if (narg < 7) error->all(FLERR,"Illegal fix viscosity command");
|
|
|
|
MPI_Comm_rank(world,&me);
|
|
|
|
nevery = atoi(arg[3]);
|
|
if (nevery <= 0) error->all(FLERR,"Illegal fix viscosity command");
|
|
|
|
scalar_flag = 1;
|
|
global_freq = nevery;
|
|
extscalar = 0;
|
|
|
|
if (strcmp(arg[4],"x") == 0) vdim = 0;
|
|
else if (strcmp(arg[4],"y") == 0) vdim = 1;
|
|
else if (strcmp(arg[4],"z") == 0) vdim = 2;
|
|
else error->all(FLERR,"Illegal fix viscosity command");
|
|
|
|
if (strcmp(arg[5],"x") == 0) pdim = 0;
|
|
else if (strcmp(arg[5],"y") == 0) pdim = 1;
|
|
else if (strcmp(arg[5],"z") == 0) pdim = 2;
|
|
else error->all(FLERR,"Illegal fix viscosity command");
|
|
|
|
nbin = atoi(arg[6]);
|
|
if (nbin % 2 || nbin <= 2) error->all(FLERR,"Illegal fix viscosity command");
|
|
|
|
// optional keywords
|
|
|
|
nswap = 1;
|
|
vtarget = BIG;
|
|
|
|
int iarg = 7;
|
|
while (iarg < narg) {
|
|
if (strcmp(arg[iarg],"swap") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix viscosity command");
|
|
nswap = atoi(arg[iarg+1]);
|
|
if (nswap <= 0) error->all(FLERR,"Fix viscosity swap value must be positive");
|
|
iarg += 2;
|
|
} else if (strcmp(arg[iarg],"vtarget") == 0) {
|
|
if (iarg+2 > narg) error->all(FLERR,"Illegal fix viscosity command");
|
|
if (strcmp(arg[iarg+1],"INF") == 0) vtarget = BIG;
|
|
else vtarget = atof(arg[iarg+1]);
|
|
if (vtarget <= 0.0)
|
|
error->all(FLERR,"Fix viscosity vtarget value must be positive");
|
|
iarg += 2;
|
|
} else error->all(FLERR,"Illegal fix viscosity command");
|
|
}
|
|
|
|
// initialize array sizes to nswap+1 so have space to shift values down
|
|
|
|
pos_index = new int[nswap+1];
|
|
neg_index = new int[nswap+1];
|
|
pos_delta = new double[nswap+1];
|
|
neg_delta = new double[nswap+1];
|
|
|
|
p_exchange = 0.0;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
FixViscosity::~FixViscosity()
|
|
{
|
|
delete [] pos_index;
|
|
delete [] neg_index;
|
|
delete [] pos_delta;
|
|
delete [] neg_delta;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
int FixViscosity::setmask()
|
|
{
|
|
int mask = 0;
|
|
mask |= END_OF_STEP;
|
|
return mask;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void FixViscosity::init()
|
|
{
|
|
// warn if any fix ave/spatial comes after this fix
|
|
// can cause glitch in averaging since ave will happen after swap
|
|
|
|
int foundme = 0;
|
|
for (int i = 0; i < modify->nfix; i++) {
|
|
if (modify->fix[i] == this) foundme = 1;
|
|
if (foundme && strcmp(modify->fix[i]->style,"ave/spatial") == 0 && me == 0)
|
|
error->warning(FLERR,"Fix viscosity comes before fix ave/spatial");
|
|
}
|
|
|
|
// set bounds of 2 slabs in pdim
|
|
// only necessary for static box, else re-computed in end_of_step()
|
|
// lo bin is always bottom bin
|
|
// hi bin is just above half height
|
|
|
|
if (domain->box_change == 0) {
|
|
prd = domain->prd[pdim];
|
|
boxlo = domain->boxlo[pdim];
|
|
boxhi = domain->boxhi[pdim];
|
|
double binsize = (boxhi-boxlo) / nbin;
|
|
slablo_lo = boxlo;
|
|
slablo_hi = boxlo + binsize;
|
|
slabhi_lo = boxlo + (nbin/2)*binsize;
|
|
slabhi_hi = boxlo + (nbin/2+1)*binsize;
|
|
}
|
|
|
|
periodicity = domain->periodicity[pdim];
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
void FixViscosity::end_of_step()
|
|
{
|
|
int i,m,insert;
|
|
double coord,delta;
|
|
MPI_Status status;
|
|
struct {
|
|
double value;
|
|
int proc;
|
|
} mine[2],all[2];
|
|
|
|
// if box changes, recompute bounds of 2 slabs in pdim
|
|
|
|
if (domain->box_change) {
|
|
prd = domain->prd[pdim];
|
|
boxlo = domain->boxlo[pdim];
|
|
boxhi = domain->boxhi[pdim];
|
|
double binsize = (boxhi-boxlo) / nbin;
|
|
slablo_lo = boxlo;
|
|
slablo_hi = boxlo + binsize;
|
|
slabhi_lo = boxlo + (nbin/2)*binsize;
|
|
slabhi_hi = boxlo + (nbin/2+1)*binsize;
|
|
}
|
|
|
|
// make 2 lists of up to nswap atoms with velocity closest to +/- vtarget
|
|
// lists are sorted by closeness to vtarget
|
|
// only consider atoms in the bottom/middle slabs
|
|
// map atoms back into periodic box if necessary
|
|
// insert = location in list to insert new atom
|
|
|
|
double **x = atom->x;
|
|
double **v = atom->v;
|
|
int *type = atom->type;
|
|
int *mask = atom->mask;
|
|
int nlocal = atom->nlocal;
|
|
|
|
npositive = nnegative = 0;
|
|
|
|
for (i = 0; i < nlocal; i++)
|
|
if (mask[i] & groupbit) {
|
|
coord = x[i][pdim];
|
|
if (coord < boxlo && periodicity) coord += prd;
|
|
else if (coord >= boxhi && periodicity) coord -= prd;
|
|
|
|
if (coord >= slablo_lo && coord < slablo_hi) {
|
|
if (v[i][vdim] < 0.0) continue;
|
|
delta = fabs(v[i][vdim] - vtarget);
|
|
if (npositive < nswap || delta < pos_delta[nswap-1]) {
|
|
for (insert = npositive-1; insert >= 0; insert--)
|
|
if (delta > pos_delta[insert]) break;
|
|
insert++;
|
|
for (m = npositive-1; m >= insert; m--) {
|
|
pos_delta[m+1] = pos_delta[m];
|
|
pos_index[m+1] = pos_index[m];
|
|
}
|
|
pos_delta[insert] = delta;
|
|
pos_index[insert] = i;
|
|
if (npositive < nswap) npositive++;
|
|
}
|
|
}
|
|
|
|
if (coord >= slabhi_lo && coord < slabhi_hi) {
|
|
if (v[i][vdim] > 0.0) continue;
|
|
delta = fabs(v[i][vdim] + vtarget);
|
|
if (nnegative < nswap || delta < neg_delta[nswap-1]) {
|
|
for (insert = nnegative-1; insert >= 0; insert--)
|
|
if (delta > neg_delta[insert]) break;
|
|
insert++;
|
|
for (m = nnegative-1; m >= insert; m--) {
|
|
neg_delta[m+1] = neg_delta[m];
|
|
neg_index[m+1] = neg_index[m];
|
|
}
|
|
neg_delta[insert] = delta;
|
|
neg_index[insert] = i;
|
|
if (nnegative < nswap) nnegative++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// loop over nswap pairs
|
|
// find 2 global atoms with smallest delta in bottom/top slabs
|
|
// BIG values are for procs with no atom to contribute
|
|
// MINLOC also communicates which procs own them
|
|
// exchange momenta between the 2 particles
|
|
// if I own both particles just swap, else point2point comm of vel,mass
|
|
|
|
double *mass = atom->mass;
|
|
double *rmass = atom->rmass;
|
|
|
|
int ipos,ineg;
|
|
double sbuf[2],rbuf[2],vcm;
|
|
|
|
double pswap = 0.0;
|
|
mine[0].proc = mine[1].proc = me;
|
|
int ipositive = 0;
|
|
int inegative = 0;
|
|
|
|
for (m = 0; m < nswap; m++) {
|
|
if (ipositive < npositive) mine[0].value = pos_delta[ipositive];
|
|
else mine[0].value = BIG;
|
|
if (inegative < nnegative) mine[1].value = neg_delta[inegative];
|
|
else mine[1].value = BIG;
|
|
|
|
MPI_Allreduce(mine,all,2,MPI_DOUBLE_INT,MPI_MINLOC,world);
|
|
|
|
if (all[0].value == BIG || all[1].value == BIG) continue;
|
|
|
|
if (me == all[0].proc && me == all[1].proc) {
|
|
ipos = pos_index[ipositive++];
|
|
ineg = neg_index[inegative++];
|
|
rbuf[0] = v[ipos][vdim];
|
|
if (rmass) rbuf[1] = rmass[ipos];
|
|
else rbuf[1] = mass[type[ipos]];
|
|
sbuf[0] = v[ineg][vdim];
|
|
if (rmass) sbuf[1] = rmass[ineg];
|
|
else sbuf[1] = mass[type[ineg]];
|
|
vcm = (sbuf[1]*sbuf[0] + rbuf[1]*rbuf[0]) / (sbuf[1] + rbuf[1]);
|
|
v[ineg][vdim] = 2.0 * vcm - sbuf[0];
|
|
v[ipos][vdim] = 2.0 * vcm - rbuf[0];
|
|
pswap += rbuf[1] * (vcm - rbuf[0]) - sbuf[1] * (vcm - sbuf[0]);
|
|
|
|
} else if (me == all[0].proc) {
|
|
ipos = pos_index[ipositive++];
|
|
sbuf[0] = v[ipos][vdim];
|
|
if (rmass) sbuf[1] = rmass[ipos];
|
|
else sbuf[1] = mass[type[ipos]];
|
|
MPI_Sendrecv(sbuf,2,MPI_DOUBLE,all[1].proc,0,
|
|
rbuf,2,MPI_DOUBLE,all[1].proc,0,world,&status);
|
|
vcm = (sbuf[1]*sbuf[0] + rbuf[1]*rbuf[0]) / (sbuf[1] + rbuf[1]);
|
|
v[ipos][vdim] = 2.0 * vcm - sbuf[0];
|
|
pswap += sbuf[1] * (vcm - sbuf[0]);
|
|
|
|
} else if (me == all[1].proc) {
|
|
ineg = neg_index[inegative++];
|
|
sbuf[0] = v[ineg][vdim];
|
|
if (rmass) sbuf[1] = rmass[ineg];
|
|
else sbuf[1] = mass[type[ineg]];
|
|
MPI_Sendrecv(sbuf,2,MPI_DOUBLE,all[0].proc,0,
|
|
rbuf,2,MPI_DOUBLE,all[0].proc,0,world,&status);
|
|
vcm = (sbuf[1]*sbuf[0] + rbuf[1]*rbuf[0]) / (sbuf[1] + rbuf[1]);
|
|
v[ineg][vdim] = 2.0 * vcm - sbuf[0];
|
|
pswap -= sbuf[1] * (vcm - sbuf[0]);
|
|
}
|
|
}
|
|
|
|
// tally momentum exchange from all swaps
|
|
|
|
double pswap_all;
|
|
MPI_Allreduce(&pswap,&pswap_all,1,MPI_DOUBLE,MPI_SUM,world);
|
|
p_exchange += pswap_all;
|
|
}
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
double FixViscosity::compute_scalar()
|
|
{
|
|
return p_exchange;
|
|
}
|