diff --git a/src/fix_temp_stochastic.cpp b/src/fix_temp_stochastic.cpp new file mode 100644 index 000000000..5e4a23f1b --- /dev/null +++ b/src/fix_temp_stochastic.cpp @@ -0,0 +1,344 @@ +/* ---------------------------------------------------------------------- + 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. +------------------------------------------------------------------------- */ + +#include "string.h" +#include "stdlib.h" +#include "math.h" +#include "fix_temp_stochastic.h" +#include "atom.h" +#include "force.h" +#include "comm.h" +#include "group.h" +#include "update.h" +#include "modify.h" +#include "compute.h" +#include "error.h" +#include "random_mars.h" +#include "velocity.h" +#include "input.h" +#include "variable.h" + +using namespace LAMMPS_NS; +using namespace FixConst; + +enum{NOBIAS,BIAS}; +enum{CONSTANT,EQUAL,ATOM}; + +/* ---------------------------------------------------------------------- */ + +FixTempStochastic::FixTempStochastic(LAMMPS *lmp, int narg, char **arg) : + Fix(lmp, narg, arg) +{ + if (narg != 7) error->all(FLERR,"Illegal fix temp/stochastic command"); + + // Stochastic thermostat should be applied every step + + nevery = 1; + scalar_flag = 1; + global_freq = nevery; + extscalar = 1; + + tstr = NULL; + if (strstr(arg[3],"v_") == arg[3]) { + int n = strlen(&arg[3][2]) + 1; + tstr = new char[n]; + strcpy(tstr,&arg[3][2]); + } else { + t_start = force->numeric(FLERR,arg[3]); + t_target = t_start; + tstyle = CONSTANT; + } + + t_stop = force->numeric(FLERR,arg[4]); + t_period = force->numeric(FLERR,arg[5]); + int seed = force->inumeric(FLERR,arg[6]); + + // error checks + + if (t_period <= 0.0) error->all(FLERR,"Fix temp/stochastic period must be > 0.0"); + if (seed <= 0) error->all(FLERR,"Illegal fix temp/stochastic command"); + + // initialize Marsaglia RNG with processor-unique seed + + random = new RanMars(lmp,seed + comm->me); + + // create a new compute temp style + // id = fix-ID + temp, compute group = fix group + + int n = strlen(id) + 6; + id_temp = new char[n]; + strcpy(id_temp,id); + strcat(id_temp,"_temp"); + + char **newarg = new char*[3]; + newarg[0] = id_temp; + newarg[1] = group->names[igroup]; + newarg[2] = (char *) "temp"; + modify->add_compute(3,newarg); + delete [] newarg; + tflag = 1; + + energy = 0; +} + +/* ---------------------------------------------------------------------- */ + +FixTempStochastic::~FixTempStochastic() +{ + // delete temperature if fix created it + + if (tflag) modify->delete_compute(id_temp); + delete random; + delete [] tstr; + delete [] id_temp; +} + +/* ---------------------------------------------------------------------- */ + +int FixTempStochastic::setmask() +{ + int mask = 0; + mask |= END_OF_STEP; + mask |= THERMO_ENERGY; + return mask; +} + +/* ---------------------------------------------------------------------- */ + +void FixTempStochastic::init() +{ + int icompute = modify->find_compute(id_temp); + if (icompute < 0) + error->all(FLERR,"Temperature ID for fix temp/stochastic does not exist"); + temperature = modify->compute[icompute]; + + if (temperature->tempbias) which = BIAS; + else which = NOBIAS; + + if (tstr) { + tvar = input->variable->find(tstr); + if (tvar < 0) + error->all(FLERR,"Variable name for fix temp/stochastic does not exist"); + if (input->variable->equalstyle(tvar)) tstyle = EQUAL; + else if (input->variable->atomstyle(tvar)) tstyle = ATOM; + else error->all(FLERR,"Variable for fix temp/stochastic is invalid style"); + } + + +} + +/* ---------------------------------------------------------------------- */ + +void FixTempStochastic::end_of_step() +{ + t_current = temperature->compute_scalar(); + if (t_current == 0.0) + error->all(FLERR,"Computed temperature for fix temp/stochastic cannot be 0.0"); + + double **v = atom->v; + double *mass = atom->mass; + double *rmass = atom->rmass; + int *type = atom->type; + int *mask = atom->mask; + int nlocal = atom->nlocal; + + double tfactor = force->mvv2e / (3 * nlocal * force->boltz); + double efactor = 0.5 * force->boltz * 3 * nlocal; + + double t = 0.0; + + if (rmass) { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) + t += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * rmass[i]; + } else { + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) + t += (v[i][0]*v[i][0] + v[i][1]*v[i][1] + v[i][2]*v[i][2]) * + mass[type[i]]; + } + + t *= tfactor; + + if (t == 0.0) + error->all(FLERR,"Computed temperature for fix temp/stochastic cannot be 0.0"); + + double delta = update->ntimestep - update->beginstep; + delta /= update->endstep - update->beginstep; + t_target = t_start + delta * (t_stop-t_start); + + double ekin0 = efactor * t; + double kbt = 0.5 * force->boltz * t_target; + + double lambda = resamplekin(kbt, ekin0, 3*nlocal, t_period/update->dt); + + if (which == NOBIAS) { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + v[i][0] *= lambda; + v[i][1] *= lambda; + v[i][2] *= lambda; + } + } + } else { + for (int i = 0; i < nlocal; i++) { + if (mask[i] & groupbit) { + temperature->remove_bias(i,v[i]); + v[i][0] *= lambda; + v[i][1] *= lambda; + v[i][2] *= lambda; + temperature->restore_bias(i,v[i]); + } + } + } + + if (group->count(igroup) == 0) + error->all(FLERR,"Cannot zero momentum of 0 atoms"); + + // compute velocity of center-of-mass of group + + double masstotal = group->mass(igroup); + double vcm[3]; + group->vcm(igroup,masstotal,vcm); + + // adjust velocities by vcm to zero linear momentum + + for (int i = 0; i < nlocal; i++) + if (mask[i] & groupbit) { + v[i][0] -= vcm[0]; + v[i][1] -= vcm[1]; + v[i][2] -= vcm[2]; + } + + double t_current = temperature->compute_scalar(); + energy += t_current * efactor; + +} + +/* ---------------------------------------------------------------------- */ + +int FixTempStochastic::modify_param(int narg, char **arg) +{ + if (strcmp(arg[0],"temp") == 0) { + if (narg < 2) error->all(FLERR,"Illegal fix_modify command"); + if (tflag) { + modify->delete_compute(id_temp); + tflag = 0; + } + delete [] id_temp; + int n = strlen(arg[1]) + 1; + id_temp = new char[n]; + strcpy(id_temp,arg[1]); + + int icompute = modify->find_compute(id_temp); + if (icompute < 0) error->all(FLERR,"Could not find fix_modify temperature ID"); + temperature = modify->compute[icompute]; + + if (temperature->tempflag == 0) + error->all(FLERR,"Fix_modify temperature ID does not compute temperature"); + if (temperature->igroup != igroup && comm->me == 0) + error->warning(FLERR,"Group for fix_modify temp != fix group"); + return 2; + } + return 0; +} + +/* ---------------------------------------------------------------------- */ + +void FixTempStochastic::reset_target(double t_new) +{ + t_start = t_stop = t_new; +} + +/* ---------------------------------------------------------------------- */ + +double FixTempStochastic::compute_scalar() +{ + return energy; +} + +////////////////////////////////////////////////////////////////////////// + +double FixTempStochastic::resamplekin(double kbt, double ekin0, int ndeg, double taut){ +/* + ndeg: number of degrees of freedom of the atoms to be thermalized + taut: relaxation time of the thermostat, in units of 'how often this routine is called' +*/ + double c1, c2, r1, r2, fscale2; + + if(taut>0.1){ + c1=exp(-1.0/taut); + } else{ + c1=0.0; + } + c2 = (1.0-c1)*kbt/ekin0; + r1 = random->gaussian(); + r2 = resamplekin_sumnoises(ndeg-1); + fscale2 = c1 + c2*(r1*r1+r2) + 2.0*r1*sqrt(c1*c2); + return sqrt(fscale2); + +} + +double FixTempStochastic::resamplekin_sumnoises(int nn){ +/* + returns the sum of n independent gaussian noises squared + (i.e. equivalent to summing the square of the return values of nn calls to random->gaussian) +*/ + double rr; + + if(nn==0) { + return 0.0; + } else if(nn==1) { + rr=random->gaussian(); + return rr*rr; + } else if(nn%2==0) { + return 2.0*gamdev(nn/2); + } else { + rr=random->gaussian(); + return 2.0*gamdev((nn-1)/2) + rr*rr; + } +} + +double FixTempStochastic::gamdev(const int ia) +{ + int j; + double am,e,s,v1,v2,x,y; + int iff; + + if (ia < 1) {}; // FATAL ERROR + if (ia < 6) { + x=1.0; + for (j=1;j<=ia;j++) x *= random->uniform(); + x = -log(x); + } else { + restart: + do { + do { + do { + v1=random->uniform(); + v2=2.0*random->uniform()-1.0; + } while (v1*v1+v2*v2 > 1.0); + y=v2/v1; + am=ia-1; + s=sqrt(2.0*am+1.0); + x=s*y+am; + } while (x <= 0.0); + if (am*log(x/am)-s*y < -700 || v1<0.00001) { + goto restart; + } + e=(1.0+y*y)*exp(am*log(x/am)-s*y); + } while (random->uniform() > e); + } + return x; +} + diff --git a/src/fix_temp_stochastic.h b/src/fix_temp_stochastic.h new file mode 100644 index 000000000..d5743c765 --- /dev/null +++ b/src/fix_temp_stochastic.h @@ -0,0 +1,102 @@ +/* ---------------------------------------------------------------------- + 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. +------------------------------------------------------------------------- */ + +#ifdef FIX_CLASS + +FixStyle(temp/stochastic,FixTempStochastic) + +#else + +#ifndef LMP_FIX_TEMP_STOCHASTIC_H +#define LMP_FIX_TEMP_STOCHASTIC_H + +#include "fix.h" +#include <cmath> + +namespace LAMMPS_NS { + +class FixTempStochastic : public Fix { + public: + FixTempStochastic(class LAMMPS *, int, char **); + ~FixTempStochastic(); + int setmask(); + void init(); + void end_of_step(); + int modify_param(int, char **); + void reset_target(double); + double compute_scalar(); + + private: + int which; +// double t_start,t_stop,t_period; + double energy; + + char *id_temp; + class Compute *temperature; + int tflag; + + double resamplekin_sumnoises(int nn); + + double ran1(); + double gasdev(); + double gamdev(const int ia); + + double resamplekin(double kbt, double ekin0, int ndeg, double taut); + + protected: + class RanMars *random; + double t_start,t_stop,t_period,t_target,t_current; + int tstyle,tvar; + char *tstr; +}; + +} + +#endif +#endif + +/* ERROR/WARNING messages: + +E: Illegal ... command + +Self-explanatory. Check the input script syntax and compare to the +documentation for the command. You can use -echo screen as a +command-line option when running LAMMPS to see the offending line. + +E: Fix temp/stochastic period must be > 0.0 + +Self-explanatory. + +E: Temperature ID for fix temp/stochastic does not exist + +Self-explanatory. + +E: Computed temperature for fix temp/stochastic cannot be 0.0 + +Self-explanatory. + +E: Could not find fix_modify temperature ID + +The compute ID for computing temperature does not exist. + +E: Fix_modify temperature ID does not compute temperature + +The compute ID assigned to the fix must compute temperature. + +W: Group for fix_modify temp != fix group + +The fix_modify command is specifying a temperature computation that +computes a temperature on a different group of atoms than the fix +itself operates on. This is probably not what you want to do. + +*/