/* ---------------------------------------------------------------------- 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: Michele Ceriotti (EPFL), Joe Morrone (Stony Brook), Axel Kohylmeyer (Temple U) ------------------------------------------------------------------------- */ #include "fix_gle.h" #include #include #include #include #include "atom.h" #include "force.h" #include "update.h" #include "respa.h" #include "comm.h" #include "random_mars.h" #include "memory.h" #include "error.h" #include "utils.h" using namespace LAMMPS_NS; using namespace FixConst; enum{NOBIAS,BIAS}; enum{CONSTANT,EQUAL,ATOM}; //#define GLE_DEBUG 1 #define MAXLINE 1024 /* syntax for fix_gle: * fix nfix id-group gle ns Tstart Tstop seed amatrix [noneq cmatrix] [every nmts] * * */ /* ---------------------------------------------------------------------- */ namespace GLE { /* GLE Utility functions -- might perhaps use standard LAMMPS routines if available */ #define midx(n,i,j) ((i)*(n)+(j)) //"stabilized" cholesky decomposition. does a LDL^t decomposition, then sets to zero the negative diagonal elements and gets MM^t void StabCholesky(int n, const double* MMt, double* M) { double *L = new double[n*n]; double *D = new double[n]; int i,j,k; for (i=0; i0.0) ? sqrt(D[i]):0.0; } for (i=0; i=0; p--) { MyMult(n, n, n, SM, EM, TMP); for (int i=0; iall(FLERR,"Illegal fix gle command. Expecting: fix " " gle "); restart_peratom = 1; time_integrate = 1; // number of additional momenta ns = force->inumeric(FLERR,arg[3]); ns1sq = (ns+1)*(ns+1); // allocate GLE matrices A = new double[ns1sq]; C = new double[ns1sq]; T = new double[ns1sq]; S = new double[ns1sq]; TT = new double[ns1sq]; ST = new double[ns1sq]; // 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]); // LOADING A matrix FILE *fgle = NULL; char *fname = arg[7]; if (comm->me == 0) { fgle = force->open_potential(fname); if (fgle == NULL) { char str[128]; snprintf(str,128,"Cannot open A-matrix file %s",fname); error->one(FLERR,str); } if (screen) fprintf(screen,"Reading A-matrix from %s\n", fname); if (logfile) fprintf(logfile,"Reading A-matrix from %s\n", fname); } // read each line of the file, skipping blank lines or leading '#' char line[MAXLINE],*ptr; int n,nwords,ndone=0,eof=0; while (1) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fgle); if (ptr == NULL) { eof = 1; fclose(fgle); } else n = strlen(line) + 1; } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) break; MPI_Bcast(&n,1,MPI_INT,0,world); MPI_Bcast(line,n,MPI_CHAR,0,world); // strip comment, skip line if blank if ((ptr = strchr(line,'#'))) *ptr = '\0'; nwords = utils::count_words(line); if (nwords == 0) continue; ptr = strtok(line," \t\n\r\f"); do { A[ndone] = atof(ptr); ptr = strtok(NULL," \t\n\r\f"); ndone++; } while ((ptr != NULL) && (ndone < ns1sq)); } fnoneq=0; gle_every=1; gle_step=0; for (int iarg=8; iargnarg) error->all(FLERR,"Did not specify C matrix for non-equilibrium GLE"); fname = arg[iarg+1]; } else if (strcmp(arg[iarg],"every") == 0) { if (iarg+2>narg) error->all(FLERR, "Did not specify interval for applying the GLE"); gle_every=force->inumeric(FLERR,arg[iarg+1]); } } // set C matrix if (fnoneq == 0) { t_target=t_start; const double kT = t_target * force->boltz / force->mvv2e; memset(C,0,sizeof(double)*ns1sq); for (int i=0; ime == 0) { fgle = force->open_potential(fname); if (fgle == NULL) { char str[128]; snprintf(str,128,"Cannot open C-matrix file %s",fname); error->one(FLERR,str); } if (screen) fprintf(screen,"Reading C-matrix from %s\n", fname); if (logfile) fprintf(logfile,"Reading C-matrix from %s\n", fname); } // read each line of the file, skipping blank lines or leading '#' ndone = eof = 0; const double cfac = force->boltz / force->mvv2e; while (1) { if (comm->me == 0) { ptr = fgets(line,MAXLINE,fgle); if (ptr == NULL) { eof = 1; fclose(fgle); } else n = strlen(line) + 1; } MPI_Bcast(&eof,1,MPI_INT,0,world); if (eof) break; MPI_Bcast(&n,1,MPI_INT,0,world); MPI_Bcast(line,n,MPI_CHAR,0,world); // strip comment, skip line if blank if ((ptr = strchr(line,'#'))) *ptr = '\0'; nwords = utils::count_words(line); if (nwords == 0) continue; ptr = strtok(line," \t\n\r\f"); do { C[ndone] = cfac*atof(ptr); ptr = strtok(NULL," \t\n\r\f"); ndone++; } while ((ptr != NULL) && (ndone < ns1sq)); } } #ifdef GLE_DEBUG printf("A Matrix\n"); GLE::MyPrint(ns+1,A); printf("C Matrix\n"); GLE::MyPrint(ns+1,C); #endif // 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 gle 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,"gle:sqrt_m"); // allocates space for additional degrees of freedom gle_s=NULL; // allocates space for temporaries gle_tmp1=gle_tmp2=NULL; grow_arrays(atom->nmax); init_gles(); // add callbacks to enable restarts atom->add_callback(0); atom->add_callback(1); energy = 0.0; } /* --- Frees up memory used by temporaries and buffers ------------------ */ FixGLE::~FixGLE() { delete random; delete [] A; delete [] C; delete [] S; delete [] T; delete [] TT; delete [] ST; memory->destroy(sqrt_m); memory->destroy(gle_s); memory->destroy(gle_tmp1); memory->destroy(gle_tmp2); } /* ---------------------------------------------------------------------- */ int FixGLE::setmask() { int mask = 0; mask |= INITIAL_INTEGRATE; mask |= FINAL_INTEGRATE; mask |= INITIAL_INTEGRATE_RESPA; mask |= FINAL_INTEGRATE_RESPA; mask |= THERMO_ENERGY; return mask; } /* ------- Initializes one-time quantities for GLE ---------------------- */ void FixGLE::init() { dogle = 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_gle(); } /* ------- Initializes integrator matrices (change with T and dt) ------- */ void FixGLE::init_gle() { // compute Langevin terms double *tmp1 = new double[ns1sq]; double *tmp2 = new double[ns1sq]; for (int i=0; idt*0.5*gle_every; tmp2[i]=S[i]=0.0; } GLE::MatrixExp(ns+1,tmp1,T); GLE::MyMult(ns+1,ns+1,ns+1,T,C,tmp1); GLE::MyTrans(ns+1,T,tmp2); GLE::MyMult(ns+1,ns+1,ns+1,tmp1,tmp2,S); for (int i=0; imask; int nlocal = atom->nlocal; double *rootC = new double[ns1sq]; double *rootCT = new double[ns1sq]; double *newg = new double[3*(ns+1)*nlocal]; double *news = new double[3*(ns+1)*nlocal]; GLE::StabCholesky(ns+1, C, rootC); GLE::MyTrans(ns+1,rootC,rootCT); memset(news,0,sizeof(double)*3*(ns+1)*nlocal); for (int i = 0; i < nlocal*3*(ns+1); ++i) newg[i] = random->gaussian(); GLE::AkMult(nlocal*3,ns+1,ns+1, newg, rootCT, news); int nk=0; // unpacks temporary into gle_s for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { // first loads velocities for (int k = 0; k<3; k++) { for (int j=0; jintegrate_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 FixGLE::gle_integrate() { double **v = atom->v; double *rmass = atom->rmass, smi, ismi; int *type = atom->type; int *mask = atom->mask; int nlocal = atom->nlocal; if (igroup == atom->firstgroup) nlocal = atom->nfirst; #ifdef GLE_DEBUG printf("!MC! GLE THERMO STEP dt=%f\n",update->dt); #endif // loads momentum data (mass-scaled) into the temporary vectors for the propagation int nk=0, ni=0; double deltae=0.0; for (int i = 0; i < nlocal; i++) { if (mask[i] & groupbit) { ni++; if (rmass) { smi = sqrt(rmass[i]); } else { smi=sqrt_m[type[i]]; } for (int k = 0; k<3; k++) { //also zeroes tmp1 gle_tmp1[nk]=0.0; // first loads velocities and accumulates conserved quantity gle_tmp2[nk]=v[i][k]*smi; deltae+= gle_tmp2[nk]*gle_tmp2[nk]; ++nk; // then copies in the additional momenta for (int j=0; jgaussian(); // ... + S \xi GLE::AkMult(ni*3,ns+1,ns+1,gle_tmp2,ST,gle_tmp1,1.0); // 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]=gle_tmp1[nk]*ismi; deltae-= gle_tmp1[nk]*gle_tmp1[nk]; ++nk; // stores the additional momenta in the gle_s buffer for (int j=0; jmvv2e; } void FixGLE::initial_integrate(int /*vflag*/) { double dtfm; // 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; gle_step--; if (dogle && gle_step<1) gle_integrate(); #ifdef GLE_DEBUG printf("!MC! GLE P1 STEP dt=%f\n",dtv); printf("!MC! GLE Q STEP\n"); #endif 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]; } } } void FixGLE::final_integrate() { double dtfm; // update v of atoms in group 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; 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]; } } #ifdef GLE_DEBUG printf("!MC! GLE P2 STEP dt=%f\n",dtv); #endif if (dogle && gle_step<1) { gle_integrate(); gle_step=gle_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 && fnoneq == 0) { // only updates if it is really necessary const double kT = t_target * force->boltz / force->mvv2e; memset(C,0,sizeof(double)*ns1sq); for (int i=0; iftm2v; // innermost level - NVE update of v and x // all other levels - NVE update of v if (ilevel==nlevels_respa-1) gle_integrate(); dogle=0; if (ilevel == 0) initial_integrate(vflag); else { final_integrate();} } void FixGLE::final_integrate_respa(int ilevel, int /*iloop*/) { dtv = step_respa[ilevel]; dtf = 0.5 * step_respa[ilevel] * force->ftm2v; dogle=0; final_integrate(); if (ilevel==nlevels_respa-1) gle_integrate(); } double FixGLE::compute_scalar() { double energy_me = energy; double energy_all; MPI_Allreduce(&energy_me,&energy_all,1,MPI_DOUBLE,MPI_SUM,world); return energy_all; } /* ---------------------------------------------------------------------- extract thermostat properties ------------------------------------------------------------------------- */ void *FixGLE::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 ------------------------------------------------------------------------- */ void FixGLE::reset_target(double t_new) { t_target = t_start = t_stop = t_new; if (fnoneq == 0) { // only updates if it is really necessary for (int i=0; iboltz/force->mvv2e; init_gle(); } else { error->all(FLERR, "Cannot change temperature for a non-equilibrium GLE run"); } } /* ---------------------------------------------------------------------- Called when a change to the timestep is requested mid-run ------------------------------------------------------------------------- */ void FixGLE::reset_dt() { // set the time integration constants dtv = update->dt; dtf = 0.5 * update->dt * (force->ftm2v); init_gle(); } /* ---------------------------------------------------------------------- memory usage of local atom-based arrays ------------------------------------------------------------------------- */ double FixGLE::memory_usage() { double bytes = atom->nmax*(3*ns+2*3*(ns+1))*sizeof(double); return bytes; } /* ---------------------------------------------------------------------- allocate local atom-based arrays ------------------------------------------------------------------------- */ void FixGLE::grow_arrays(int nmax) { memory->grow(gle_s, nmax, 3*ns,"gle:gle_s"); memory->grow(gle_tmp1, nmax*(ns+1)*3,"gle:tmp1"); memory->grow(gle_tmp2, nmax*(ns+1)*3,"gle:tmp2"); //zeroes out temporary buffers for (int i=0; i< nmax*(ns+1)*3; ++i) gle_tmp1[i]=0.0; for (int i=0; i< nmax*(ns+1)*3; ++i) gle_tmp2[i]=0.0; } /* ---------------------------------------------------------------------- copy values within local atom-based arrays ------------------------------------------------------------------------- */ void FixGLE::copy_arrays(int i, int j, int /*delflag*/) { for (int k = 0; k < 3*ns; k++) gle_s[j][k] = gle_s[i][k]; } /* ---------------------------------------------------------------------- Pack extended variables assoc. w/ atom i into buffer for exchange with another processor ------------------------------------------------------------------------- */ int FixGLE::pack_exchange(int i, double *buf) { int m = 0; for (int k = 0; k < 3*ns; k++) buf[m++] = gle_s[i][k]; return m; } /* ---------------------------------------------------------------------- Unpack extended variables from exchange with another processor ------------------------------------------------------------------------- */ int FixGLE::unpack_exchange(int nlocal, double *buf) { int m = 0; for (int k = 0; k < 3*ns; k++) gle_s[nlocal][k] = buf[m++]; return m; } /* ---------------------------------------------------------------------- Pack extended variables assoc. w/ atom i into buffer for writing to a restart file ------------------------------------------------------------------------- */ int FixGLE::pack_restart(int i, double *buf) { int m = 0; // pack buf[0] this way because other fixes unpack it buf[m++] = 3*ns + 1; for (int k = 0; k < 3*ns; k=k+3) { buf[m++] = gle_s[i][k]; buf[m++] = gle_s[i][k+1]; buf[m++] = gle_s[i][k+2]; } return m; } /* ---------------------------------------------------------------------- Unpack extended variables to restart the fix from a restart file ------------------------------------------------------------------------- */ void FixGLE::unpack_restart(int nlocal, int nth) { double **extra = atom->extra; // skip to the nth set of extended variables // unpack the Nth first values this way because other fixes pack them int m = 0; for (int i = 0; i< nth; i++) m += static_cast (extra[nlocal][m]); m++; for (int k = 0; k < 3*ns; k=k+3) { gle_s[nlocal][k] = extra[nlocal][m++]; gle_s[nlocal][k+1] = extra[nlocal][m++]; gle_s[nlocal][k+2] = extra[nlocal][m++]; } } /* ---------------------------------------------------------------------- Returns the number of items in atomic restart data associated with local atom nlocal. Used in determining the total extra data stored by fixes on a given processor. ------------------------------------------------------------------------- */ int FixGLE::size_restart(int /*nlocal*/) { return 3*ns+1; } /* ---------------------------------------------------------------------- Returns the maximum number of items in atomic restart data Called in Modify::restart for peratom restart. ------------------------------------------------------------------------- */ int FixGLE::maxsize_restart() { return 3*ns+1; }