#include "fix_tfmc.h"
#include <mpi.h>
#include <cstring>
#include <cmath>
#include <cfloat>
#include "atom.h"
#include "force.h"
#include "group.h"
#include "random_mars.h"
#include "comm.h"
#include "domain.h"
#include "memory.h"
#include "modify.h"
#include "error.h"
using namespace LAMMPS_NS;
using namespace FixConst;
FixTFMC::FixTFMC(LAMMPS *lmp, int narg, char **arg) :
Fix(lmp, narg, arg),
xd(NULL), rotflag(0), random_num(NULL)
{
if (narg < 6) error->all(FLERR,"Illegal fix tfmc command");
time_integrate = 1;
d_max = force->numeric(FLERR,arg[3]);
T_set = force->numeric(FLERR,arg[4]);
seed = force->inumeric(FLERR,arg[5]);
if (d_max <= 0) error->all(FLERR,"Fix tfmc displacement length must be > 0");
if (T_set <= 0) error->all(FLERR,"Fix tfmc temperature must be > 0");
if (seed <= 0) error->all(FLERR,"Illegal fix tfmc random seed");
comflag = 0;
rotflag = 0;
int iarg = 6;
while (iarg < narg) {
if (strcmp(arg[iarg],"com") == 0) {
if (iarg+4 > narg) error->all(FLERR,"Illegal fix tfmc command");
comflag = 1;
xflag = force->inumeric(FLERR,arg[iarg+1]);
yflag = force->inumeric(FLERR,arg[iarg+2]);
zflag = force->inumeric(FLERR,arg[iarg+3]);
iarg += 4;
} else if (strcmp(arg[iarg],"rot") == 0) {
if (iarg+1 > narg) error->all(FLERR,"Illegal fix tfmc command");
rotflag = 1;
iarg += 1;
} else error->all(FLERR,"Illegal fix tfmc command");
}
if (comflag)
if (xflag < 0 || xflag > 1 || yflag < 0 || yflag > 1 ||
zflag < 0 || zflag > 1)
error->all(FLERR,"Illegal fix tfmc command");
if (xflag + yflag + zflag == 0)
comflag = 0;
if (rotflag) {
xd = NULL;
nmax = -1;
}
random_num = new RanMars(lmp,seed + comm->me);
}
FixTFMC::~FixTFMC()
{
delete random_num;
if (rotflag) {
memory->destroy(xd);
xd = NULL;
nmax = -1;
}
}
int FixTFMC::setmask()
{
int mask = 0;
mask |= INITIAL_INTEGRATE;
return mask;
}
void FixTFMC::init()
{
int has_shake = 0;
for (int i = 0; i < modify->nfix; i++)
if (strcmp(modify->fix[i]->style,"shake") == 0) ++has_shake;
if (has_shake > 0)
error->all(FLERR,"Fix tfmc is not compatible with fix shake");
double *rmass = atom->rmass;
double *mass = atom->mass;
int *type = atom->type;
int *mask = atom->mask;
int nlocal = atom->nlocal;
if (igroup == atom->firstgroup) nlocal = atom->nfirst;
double mass_min_local = DBL_MAX;
if (rmass) {
for (int i = 0; i < nlocal; i++)
if (mask[i] & groupbit) {
if (mass_min_local > rmass[i]) mass_min_local = rmass[i];
}
} else {
for (int i = 0; i < nlocal; i++)
if (mask[i] & groupbit) {
if (mass_min_local > mass[type[i]]) mass_min_local = mass[type[i]];
}
}
MPI_Allreduce(&mass_min_local,&mass_min,1,MPI_DOUBLE,MPI_MIN,world);
}
void FixTFMC::initial_integrate(int )
{
double boltz = force->boltz;
double **x = atom->x;
double **f = atom->f;
double *rmass = atom->rmass;
double *mass = atom->mass;
double massone;
double masstotal;
double xcm_d[3], xcm_dall[3];
double d_i, xi;
double gamma, gamma_exp, gamma_expi;
double P_acc, P_ran;
int *type = atom->type;
int *mask = atom->mask;
int nlocal = atom->nlocal;
if (igroup == atom->firstgroup) nlocal = atom->nfirst;
if (comflag) {
xcm_d[0] = 0.0;
xcm_d[1] = 0.0;
xcm_d[2] = 0.0;
}
if (rotflag && nmax < nlocal) {
nmax = nlocal + 1;
memory->destroy(xd);
memory->create(xd,nmax,3,"tfmc:xd");
}
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) {
if (rmass) massone = rmass[i];
else massone = mass[type[i]];
d_i = d_max * pow(mass_min/massone, 0.25);
for (int j = 0; j < 3; j++) {
P_acc = 0.0;
P_ran = 1.0;
gamma = f[i][j] * d_i / (2.0*boltz*T_set);
gamma_exp = exp(gamma);
gamma_expi = 1.0/gamma_exp;
while (P_acc < P_ran) {
xi = 2.0*random_num->uniform() - 1.0;
P_ran = random_num->uniform();
if (xi < 0) {
P_acc = exp(2.0*xi*gamma) * gamma_exp - gamma_expi;
P_acc = P_acc / (gamma_exp - gamma_expi);
} else if (xi > 0) {
P_acc = gamma_exp - exp(2.0*xi*gamma) * gamma_expi;
P_acc = P_acc / (gamma_exp - gamma_expi);
} else {
P_acc = 1.0;
}
}
x[i][j] += xi * d_i;
if (comflag) xcm_d[j] += xi * d_i * massone;
if (rotflag) xd[i][j] = xi * d_i;
}
}
}
if (comflag || rotflag) masstotal = group->mass(igroup);
if (comflag == 1 && group->count(igroup) != 0) {
MPI_Allreduce(xcm_d,xcm_dall,3,MPI_DOUBLE,MPI_SUM,world);
if (masstotal > 0.0) {
xcm_dall[0] /= masstotal;
xcm_dall[1] /= masstotal;
xcm_dall[2] /= masstotal;
} else xcm_dall[0] = xcm_dall[1] = xcm_dall[2] = 0.0;
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) {
if (xflag) x[i][0] -= xcm_dall[0];
if (yflag) x[i][1] -= xcm_dall[1];
if (zflag) x[i][2] -= xcm_dall[2];
}
}
}
if (rotflag == 1 && group->count(igroup) != 0) {
double dx, dy, dz;
double unwrap[3];
double cm[3], angmom[3], inertia[3][3], omega[3];
tagint *image = atom->image;
group->xcm(igroup,masstotal,cm);
double p[3];
p[0] = p[1] = p[2] = 0.0;
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) {
domain->unmap(x[i],image[i],unwrap);
dx = unwrap[0] - cm[0];
dy = unwrap[1] - cm[1];
dz = unwrap[2] - cm[2];
if (rmass) massone = rmass[i];
else massone = mass[type[i]];
p[0] += massone * (dy*xd[i][2] - dz*xd[i][1]);
p[1] += massone * (dz*xd[i][0] - dx*xd[i][2]);
p[2] += massone * (dx*xd[i][1] - dy*xd[i][0]);
}
}
MPI_Allreduce(p,angmom,3,MPI_DOUBLE,MPI_SUM,world);
group->inertia(igroup,cm,inertia);
group->omega(angmom,inertia,omega);
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) {
domain->unmap(x[i],image[i],unwrap);
dx = unwrap[0] - cm[0];
dy = unwrap[1] - cm[1];
dz = unwrap[2] - cm[2];
x[i][0] -= omega[1]*dz - omega[2]*dy;
x[i][1] -= omega[2]*dx - omega[0]*dz;
x[i][2] -= omega[0]*dy - omega[1]*dx;
}
}
}
}