Newer
Older
/* ----------------------------------------------------------------------
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.
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Fast-forward Langevin thermostat, see
M. Hijazi, D. M. Wilkins, M. Ceriotti, J. Chem. Phys. 148, 184109 (2018)
------------------------------------------------------------------------- */
/* ----------------------------------------------------------------------
Contributing authors: Lionel Constantin (EPFL), David M. Wilkins (EPFL),
Michele Ceriotti (EPFL)
------------------------------------------------------------------------- */
#include <mpi.h>
dilkins
committed
#include <cmath>
#include <cstring>
#include <cstdlib>
#include "fix_ffl.h"
#include "math_extra.h"
#include "atom.h"
#include "atom_vec_ellipsoid.h"
#include "force.h"
#include "update.h"
#include "modify.h"
#include "compute.h"
#include "domain.h"
#include "region.h"
#include "respa.h"
#include "comm.h"
#include "input.h"
#include "variable.h"
#include "random_mars.h"
#include "memory.h"
#include "error.h"
#include "group.h"
using namespace LAMMPS_NS;
using namespace FixConst;
enum {NOBIAS,BIAS};
enum {CONSTANT,EQUAL,ATOM};
//#define FFL_DEBUG 1
#define MAXLINE 1024
/* syntax for fix_ffl:
* fix nfix id-group ffl tau Tstart Tstop seed [flip_type]
*
* */
/* ---------------------------------------------------------------------- */
FixFFL::FixFFL(LAMMPS *lmp, int narg, char **arg) :
Fix(lmp, narg, arg) {
if (narg < 7)
error->all(FLERR,"Illegal fix ffl command. Expecting: fix <fix-ID>"
" <group-ID> ffl <tau> <Tstart> <Tstop> <seed> ");
restart_peratom = 1;
time_integrate = 1;
scalar_flag = 1;
//gamma = 1/ time constant(tau)
if (force->numeric(FLERR,arg[3]) <= 0)
error->all(FLERR,"Illegal fix ffl tau value, should be greater than 0");
gamma = 1.0/force->numeric(FLERR,arg[3]);
ffl_every=1;
ffl_step=0;
// start temperature (t ramp)
t_start = force->numeric(FLERR,arg[4]);
// final temperature (t ramp)
t_stop = force->numeric(FLERR,arg[5]);
// PRNG seed
int seed = force->inumeric(FLERR,arg[6]);
// Flip type used, uses rescale if no flip is given
if (narg == 8) {
if (strcmp(arg[7],"no_flip") == 0) {
dilkins
committed
} else {
error->all(FLERR,"Illegal fix ffl flip type, only accepts : rescale - hard - soft - no_flip");
dilkins
committed
}
dilkins
committed
t_target=t_start;
const double kT = t_target * force->boltz / force->mvv2e;
dilkins
committed
// initialize Marsaglia RNG with processor-unique seed
// NB: this means runs will not be the same with different numbers of processors
if (seed <= 0) error->all(FLERR,"Illegal fix ffl command");
random = new RanMars(lmp,seed + comm->me);
// allocate per-type arrays for mass-scaling
sqrt_m=NULL;
memory->grow(sqrt_m, atom->ntypes+1,"ffl:sqrt_m");
// allocates space for temporaries
ffl_tmp1=ffl_tmp2=NULL;
// add callbacks to enable restarts
atom->add_callback(0);
atom->add_callback(1);
}
/* --- Frees up memory used by temporaries and buffers ------------------ */
memory->destroy(sqrt_m);
memory->destroy(ffl_tmp1);
memory->destroy(ffl_tmp2);
}
/* ---------------------------------------------------------------------- */
mask |= INITIAL_INTEGRATE;
mask |= FINAL_INTEGRATE;
mask |= INITIAL_INTEGRATE_RESPA;
mask |= FINAL_INTEGRATE_RESPA;
mask |= THERMO_ENERGY;
}
/* ------- Initializes one-time quantities for FFL ---------------------- */
void FixFFL::init() {
doffl = 1;
dtv = update->dt;
dtf = 0.5 * update->dt * force->ftm2v;
// set force prefactors
if (!atom->rmass) {
for (int i = 1; i <= atom->ntypes; i++) {
sqrt_m[i] = sqrt(atom->mass[i]);
if (strstr(update->integrate_style,"respa")) {
nlevels_respa = ((Respa *) update->integrate)->nlevels;
step_respa = ((Respa *) update->integrate)->step;
}
init_ffl();
}
/* ------- Initializes constants for FFL (change with T and dt) ------- */
void FixFFL::init_ffl() {
const double kT = t_target * force->boltz / force->mvv2e;
c1 = exp ( - gamma * 0.5 * dtv );
c2 = sqrt( (1.0 - c1*c1)* kT ); //without the mass term
}
/* ---------------------------------------------------------------------- */
void FixFFL::setup(int vflag) {
if (strstr(update->integrate_style,"verlet"))
post_force(vflag);
else {
((Respa *) update->integrate)->copy_flevel_f(nlevels_respa-1);
post_force_respa(vflag,nlevels_respa-1,0);
((Respa *) update->integrate)->copy_f_flevel(nlevels_respa-1);
}
void FixFFL::ffl_integrate() {
double **v = atom->v;
double *rmass = atom->rmass, smi, ismi;
double factor;
int *type = atom->type;
int *mask = atom->mask;
int nlocal = atom->nlocal;
if (igroup == atom->firstgroup) nlocal = atom->nfirst;
// loads momentum data (mass-scaled) into the temporary vectors for the propagation
int nk=0;
double deltae=0.0;
for (int i = 0; i < nlocal; i++) {
if (mask[i] & groupbit) {
if (rmass) smi = sqrt(rmass[i]);
else smi = sqrt_m[type[i]];
for (int k = 0; k<3; k++) {
// first loads velocities and accumulates conserved quantity
ffl_tmp2[nk] = v[i][k] * smi;
deltae += ffl_tmp2[nk] * ffl_tmp2[nk];
nk++;
}
//fills up a vector of random numbers
for (int i = 0; i < nk; i++) ffl_tmp1[i] = random->gaussian();
// unloads momentum data (mass-scaled) from the temporary vectors
nk=0;
for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) {
if (rmass) ismi = 1.0 / sqrt(rmass[i]);
else ismi = 1.0/ sqrt_m[type[i]];
for (int k = 0; k<3; k++) {
// fetches new velocities and completes computation of the conserved quantity change
v[i][k]= c1*v[i][k] + c2*ffl_tmp1[nk]*ismi;
deltae-= v[i][k]*v[i][k] /ismi /ismi;
//flips the sign of the momentum (HARD FLIP)
nk=0;
for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) {
factor = sqrt ((v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) /
(ffl_tmp2[nk]*ffl_tmp2[nk] + ffl_tmp2[nk+1]*ffl_tmp2[nk+1]
+ ffl_tmp2[nk+2]*ffl_tmp2[nk+2]));
for (int k = 0; k<3; k++) {
v[i][k]= factor * ffl_tmp2[nk];
nk++;
}
}
}
nk=0;
for (int i = 0; i < nlocal; i++) if (mask[i] & groupbit) {
factor = v[i][0]*ffl_tmp2[nk] + v[i][1]*ffl_tmp2[nk+1] + v[i][2]*ffl_tmp2[nk+2];
if (factor < 0) {
factor = factor / (ffl_tmp2[nk]*ffl_tmp2[nk] + ffl_tmp2[nk+1]*ffl_tmp2[nk+1]
+ ffl_tmp2[nk+2]*ffl_tmp2[nk+2]);
for (int k = 0; k<3; k++) {
v[i][k] -= 2.0 * factor * ffl_tmp2[nk];
nk++;
}
} else {
nk += 3;
}
void FixFFL::initial_integrate(int vflag) {
double dtfm;
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
// update v and x of atoms in group
double **x = atom->x;
double **v = atom->v;
double **f = atom->f;
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;
ffl_step--;
if (doffl && ffl_step<1) ffl_integrate();
if (rmass) {
for (int i = 0; i < nlocal; i++)
if (mask[i] & groupbit) {
dtfm = dtf / rmass[i];
v[i][0] += dtfm * f[i][0];
v[i][1] += dtfm * f[i][1];
v[i][2] += dtfm * f[i][2];
x[i][0] += dtv * v[i][0];
x[i][1] += dtv * v[i][1];
x[i][2] += dtv * v[i][2];
}
} else {
for (int i = 0; i < nlocal; i++)
if (mask[i] & groupbit) {
dtfm = dtf / mass[type[i]];
v[i][0] += dtfm * f[i][0];
v[i][1] += dtfm * f[i][1];
v[i][2] += dtfm * f[i][2];
x[i][0] += dtv * v[i][0];
x[i][1] += dtv * v[i][1];
x[i][2] += dtv * v[i][2];
}
}
double **v = atom->v;
double **f = atom->f;
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;
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
if (rmass) {
for (int i = 0; i < nlocal; i++)
if (mask[i] & groupbit) {
dtfm = dtf / rmass[i];
v[i][0] += dtfm * f[i][0];
v[i][1] += dtfm * f[i][1];
v[i][2] += dtfm * f[i][2];
}
} else {
for (int i = 0; i < nlocal; i++)
if (mask[i] & groupbit) {
dtfm = dtf / mass[type[i]];
v[i][0] += dtfm * f[i][0];
v[i][1] += dtfm * f[i][1];
v[i][2] += dtfm * f[i][2];
}
}
if (doffl && ffl_step<1) {
ffl_integrate();
ffl_step = ffl_every;
}
// Change the temperature for the next step
double delta = update->ntimestep - update->beginstep;
delta /= update->endstep - update->beginstep;
t_target = t_start + delta * (t_stop - t_start);
if (t_stop != t_start) {
// only updates if it is really necessary
init_ffl();
}
}
/* ---------------------------------------------------------------------- */
void FixFFL::initial_integrate_respa(int vflag, int ilevel, int iloop) {
dtv = step_respa[ilevel];
dtf = 0.5 * step_respa[ilevel] * force->ftm2v;
// innermost level - NVE update of v and x
// all other levels - NVE update of v
if (ilevel==nlevels_respa-1) ffl_integrate();
doffl=0;
if (ilevel == 0) initial_integrate(vflag);
else {
final_integrate();
}
void FixFFL::final_integrate_respa(int ilevel, int iloop) {
dtv = step_respa[ilevel];
dtf = 0.5 * step_respa[ilevel] * force->ftm2v;
doffl=0;
final_integrate();
if (ilevel==nlevels_respa-1) ffl_integrate();
double energy_me = energy;
double energy_all;
MPI_Allreduce(&energy_me,&energy_all,1,MPI_DOUBLE,MPI_SUM,world);
}
/* ----------------------------------------------------------------------
extract thermostat properties
------------------------------------------------------------------------- */
void *FixFFL::extract(const char *str, int &dim) {
dim = 0;
if (strcmp(str,"t_target") == 0) {
return &t_target;
}
return NULL;
}
/* ----------------------------------------------------------------------
Called when a change to the target temperature is requested mid-run
------------------------------------------------------------------------- */
}
/* ----------------------------------------------------------------------
Called when a change to the timestep is requested mid-run
------------------------------------------------------------------------- */
void FixFFL::reset_dt() {
// set the time integration constants
dtv = update->dt;
dtf = 0.5 * update->dt * (force->ftm2v);
init_ffl();
}
/* ----------------------------------------------------------------------
memory usage of local atom-based arrays
------------------------------------------------------------------------- */
double FixFFL::memory_usage() {
double bytes = atom->nmax*(3*2)*sizeof(double);
return bytes;
}
/* ----------------------------------------------------------------------
allocate local atom-based arrays
------------------------------------------------------------------------- */
void FixFFL::grow_arrays(int nmax) {
memory->grow(ffl_tmp1, nmax*3,"ffl:tmp1");
memory->grow(ffl_tmp2, nmax*3,"ffl:tmp2");
//zeroes out temporary buffers
for (int i=0; i< nmax*3; ++i) ffl_tmp1[i] = 0.0;
for (int i=0; i< nmax*3; ++i) ffl_tmp2[i] = 0.0;