1 // clang-format off
2 /* ----------------------------------------------------------------------
3 LAMMPS - Large-scale Atomic/Molecular Massively Parallel Simulator
4 https://www.lammps.org/, Sandia National Laboratories
5 Steve Plimpton, sjplimp@sandia.gov
6
7 Copyright (2003) Sandia Corporation. Under the terms of Contract
8 DE-AC04-94AL85000 with Sandia Corporation, the U.S. Government retains
9 certain rights in this software. This software is distributed under
10 the GNU General Public License.
11
12 See the README file in the top-level LAMMPS directory.
13 ------------------------------------------------------------------------- */
14
15 #include "fix_langevin_drude.h"
16 #include "fix_drude.h"
17
18 #include "atom.h"
19 #include "comm.h"
20 #include "compute.h"
21 #include "domain.h"
22 #include "error.h"
23 #include "force.h"
24 #include "input.h"
25 #include "modify.h"
26 #include "random_mars.h"
27 #include "update.h"
28 #include "variable.h"
29
30 #include <cstring>
31 #include <cmath>
32
33 using namespace LAMMPS_NS;
34 using namespace FixConst;
35
36 enum{NOBIAS,BIAS};
37 enum{CONSTANT,EQUAL};
38
39 /* ---------------------------------------------------------------------- */
40
FixLangevinDrude(LAMMPS * lmp,int narg,char ** arg)41 FixLangevinDrude::FixLangevinDrude(LAMMPS *lmp, int narg, char **arg) :
42 Fix(lmp, narg, arg)
43 {
44 if (narg < 9) error->all(FLERR,"Illegal fix langevin/drude command");
45 // TODO add option for tally
46
47 // Langevin thermostat should be applied every step
48 nevery = 1;
49 global_freq = nevery;
50 comm_reverse = 3;
51
52 // core temperature
53 tstr_core = nullptr;
54 if (utils::strmatch(arg[3],"^v_")) {
55 tstr_core = utils::strdup(arg[3]+2);
56 tstyle_core = EQUAL;
57 } else {
58 t_start_core = utils::numeric(FLERR,arg[3],false,lmp);
59 t_target_core = t_start_core;
60 tstyle_core = CONSTANT;
61 }
62 t_period_core = utils::numeric(FLERR,arg[4],false,lmp);
63 int seed_core = utils::inumeric(FLERR,arg[5],false,lmp);
64
65 // drude temperature
66 tstr_drude = nullptr;
67 if (strstr(arg[7],"v_") == arg[6]) {
68 tstr_drude = utils::strdup(arg[6]+2);
69 tstyle_drude = EQUAL;
70 } else {
71 t_start_drude = utils::numeric(FLERR,arg[6],false,lmp);
72 t_target_drude = t_start_drude;
73 tstyle_drude = CONSTANT;
74 }
75 t_period_drude = utils::numeric(FLERR,arg[7],false,lmp);
76 int seed_drude = utils::inumeric(FLERR,arg[8],false,lmp);
77
78 // error checks
79 if (t_period_core <= 0.0)
80 error->all(FLERR,"Fix langevin/drude period must be > 0.0");
81 if (seed_core <= 0) error->all(FLERR,"Illegal langevin/drude seed");
82 if (t_period_drude <= 0.0)
83 error->all(FLERR,"Fix langevin/drude period must be > 0.0");
84 if (seed_drude <= 0) error->all(FLERR,"Illegal langevin/drude seed");
85
86 random_core = new RanMars(lmp,seed_core);
87 random_drude = new RanMars(lmp,seed_drude);
88
89 int iarg = 9;
90 zero = 0;
91 while (iarg < narg) {
92 if (strcmp(arg[iarg],"zero") == 0) {
93 if (iarg+2 > narg) error->all(FLERR,"Illegal fix langevin/drude command");
94 if (strcmp(arg[iarg+1],"no") == 0) zero = 0;
95 else if (strcmp(arg[iarg+1],"yes") == 0) zero = 1;
96 else error->all(FLERR,"Illegal fix langevin/drude command");
97 iarg += 2;
98 } else error->all(FLERR,"Illegal fix langevin/drude command");
99 }
100
101 tflag = 0; // no external compute/temp is specified yet (for bias)
102 energy = 0.;
103 fix_drude = nullptr;
104 temperature = nullptr;
105 id_temp = nullptr;
106 }
107
108 /* ---------------------------------------------------------------------- */
109
~FixLangevinDrude()110 FixLangevinDrude::~FixLangevinDrude()
111 {
112 delete random_core;
113 delete [] tstr_core;
114 delete random_drude;
115 delete [] tstr_drude;
116 }
117
118 /* ---------------------------------------------------------------------- */
119
setmask()120 int FixLangevinDrude::setmask()
121 {
122 int mask = 0;
123 mask |= POST_FORCE;
124 return mask;
125 }
126
127 /* ---------------------------------------------------------------------- */
128
init()129 void FixLangevinDrude::init()
130 {
131 // check variable-style target core temperature
132 if (tstr_core) {
133 tvar_core = input->variable->find(tstr_core);
134 if (tvar_core < 0)
135 error->all(FLERR,"Variable name for fix langevin/drude does not exist");
136 if (input->variable->equalstyle(tvar_core)) tstyle_core = EQUAL;
137 else error->all(FLERR,"Variable for fix langevin/drude is invalid style");
138 }
139
140 // check variable-style target drude temperature
141 if (tstr_drude) {
142 tvar_drude = input->variable->find(tstr_drude);
143 if (tvar_drude < 0)
144 error->all(FLERR,"Variable name for fix langevin/drude does not exist");
145 if (input->variable->equalstyle(tvar_drude)) tstyle_drude = EQUAL;
146 else error->all(FLERR,"Variable for fix langevin/drude is invalid style");
147 }
148
149 int ifix;
150 for (ifix = 0; ifix < modify->nfix; ifix++)
151 if (strcmp(modify->fix[ifix]->style,"drude") == 0) break;
152 if (ifix == modify->nfix) error->all(FLERR, "fix langevin/drude requires fix drude");
153 fix_drude = (FixDrude *) modify->fix[ifix];
154 }
155
156 /* ---------------------------------------------------------------------- */
157
setup(int)158 void FixLangevinDrude::setup(int /*vflag*/)
159 {
160 if (!utils::strmatch(update->integrate_style,"^verlet"))
161 error->all(FLERR,"RESPA style not compatible with fix langevin/drude");
162 if (!comm->ghost_velocity)
163 error->all(FLERR,"fix langevin/drude requires ghost velocities. Use comm_modify vel yes");
164
165 if (zero) {
166 int *mask = atom->mask;
167 int nlocal = atom->nlocal;
168 int *drudetype = fix_drude->drudetype;
169 int *type = atom->type;
170 bigint ncore_loc = 0;
171 for (int i=0; i<nlocal; i++)
172 if (mask[i] & groupbit && drudetype[type[i]] != DRUDE_TYPE)
173 ncore_loc++;
174 MPI_Allreduce(&ncore_loc, &ncore, 1, MPI_LMP_BIGINT, MPI_SUM, world);
175 }
176 }
177
178 /* ---------------------------------------------------------------------- */
179
modify_param(int narg,char ** arg)180 int FixLangevinDrude::modify_param(int narg, char **arg)
181 {
182 if (strcmp(arg[0],"temp") == 0) {
183 if (narg < 2) error->all(FLERR,"Illegal fix_modify command");
184 delete [] id_temp;
185 id_temp = utils::strdup(arg[1]);
186
187 int icompute = modify->find_compute(id_temp);
188 if (icompute < 0)
189 error->all(FLERR,"Could not find fix_modify temperature ID");
190 temperature = modify->compute[icompute];
191
192 if (temperature->tempflag == 0)
193 error->all(FLERR,
194 "Fix_modify temperature ID does not compute temperature");
195 if (temperature->igroup != igroup && comm->me == 0)
196 error->warning(FLERR,"Group for fix_modify temp != fix group");
197 return 2;
198 }
199 return 0;
200 }
201
202 /* ---------------------------------------------------------------------- */
203
post_force(int)204 void FixLangevinDrude::post_force(int /*vflag*/)
205 {
206 // Thermalize by adding the langevin force if thermalize=true.
207 // Each core-Drude pair is thermalized only once: where the core is local.
208
209 double **v = atom->v, **f = atom->f;
210 int *mask = atom->mask;
211 int nlocal = atom->nlocal, nall = atom->nlocal + atom->nghost;
212 int *type = atom->type;
213 double *mass = atom->mass;
214 double *rmass = atom->rmass;
215 double ftm2v = force->ftm2v, mvv2e = force->mvv2e;
216 double kb = force->boltz, dt = update->dt;
217
218 int *drudetype = fix_drude->drudetype;
219 tagint *drudeid = fix_drude->drudeid;
220 double vdrude[3], vcore[3]; // velocities in reduced representation
221 double fdrude[3], fcore[3]; // forces in reduced representation
222 double Ccore, Cdrude, Gcore, Gdrude;
223 double fcoresum[3], fcoreloc[3];
224 int dim = domain->dimension;
225
226 // Compute target core temperature
227 if (tstyle_core == CONSTANT)
228 t_target_core = t_start_core; // + delta * (t_stop-t_start_core);
229 else {
230 modify->clearstep_compute();
231 t_target_core = input->variable->compute_equal(tvar_core);
232 if (t_target_core < 0.0)
233 error->one(FLERR, "Fix langevin/drude variable returned "
234 "negative core temperature");
235 modify->addstep_compute(update->ntimestep + nevery);
236 }
237
238 // Compute target drude temperature
239 if (tstyle_drude == CONSTANT)
240 t_target_drude = t_start_drude; // + delta * (t_stop-t_start_core);
241 else {
242 modify->clearstep_compute();
243 t_target_drude = input->variable->compute_equal(tvar_drude);
244 if (t_target_drude < 0.0)
245 error->one(FLERR, "Fix langevin/drude variable returned "
246 "negative drude temperature");
247 modify->addstep_compute(update->ntimestep + nevery);
248 }
249
250 // Clear ghost forces
251 // They have already been communicated if needed
252 for (int i = nlocal; i < nall; i++) {
253 for (int k = 0; k < dim; k++)
254 f[i][k] = 0.;
255 }
256 if (zero) for (int k=0; k<dim; k++) fcoreloc[k] = 0.;
257
258 // NB : the masses are the real masses, not the reduced ones
259 for (int i = 0; i < nlocal; i++) {
260 if (mask[i] & groupbit) { // only the cores need to be in the group
261 if (drudetype[type[i]] == NOPOL_TYPE) { // Non-polarizable atom
262 double mi;
263 if (rmass)
264 mi = rmass[i];
265 else
266 mi = mass[type[i]];
267 Gcore = mi / t_period_core / ftm2v;
268 Ccore = sqrt(2.0 * Gcore * kb * t_target_core / dt / ftm2v / mvv2e);
269 if (temperature) temperature->remove_bias(i, v[i]);
270 for (int k = 0; k < dim; k++) {
271 fcore[k] = Ccore * random_core->gaussian() - Gcore * v[i][k];
272 if (zero) fcoreloc[k] += fcore[k];
273 f[i][k] += fcore[k];
274 }
275 if (temperature) temperature->restore_bias(i, v[i]);
276 } else {
277 if (drudetype[type[i]] == DRUDE_TYPE) continue; // do with the core
278
279 int j = atom->map(drudeid[i]);
280 double mi, mj, mtot, mu; // i is core, j is drude
281 if (rmass) {
282 mi = rmass[i];
283 mj = rmass[j];
284 } else {
285 mi = mass[type[i]];
286 mj = mass[type[j]];
287 }
288 mtot = mi + mj;
289 mu = mi * mj / mtot;
290 mi /= mtot;
291 mj /= mtot;
292
293 Gcore = mtot / t_period_core / ftm2v;
294 Gdrude = mu / t_period_drude / ftm2v;
295 Ccore = sqrt(2.0 * Gcore * kb * t_target_core / dt / ftm2v / mvv2e);
296 Cdrude = sqrt(2.0 * Gdrude * kb * t_target_drude / dt / ftm2v / mvv2e);
297
298 if (temperature) {
299 temperature->remove_bias(i, v[i]);
300 temperature->remove_bias(j, v[j]);
301 }
302 for (int k=0; k<dim; k++) {
303 // TODO check whether a fix_modify temp can subtract a bias velocity
304 vcore[k] = mi * v[i][k] + mj * v[j][k];
305 vdrude[k] = v[j][k] - v[i][k];
306
307 fcore[k] = Ccore * random_core->gaussian() - Gcore * vcore[k];
308 fdrude[k] = Cdrude * random_drude->gaussian() - Gdrude * vdrude[k];
309
310 if (zero) fcoreloc[k] += fcore[k];
311
312 f[i][k] += mi * fcore[k] - fdrude[k];
313 f[j][k] += mj * fcore[k] + fdrude[k];
314
315 // TODO tally energy if asked
316 }
317 if (temperature) {
318 temperature->restore_bias(i, v[i]);
319 temperature->restore_bias(j, v[j]);
320 }
321 }
322 }
323 }
324
325 if (zero) { // Remove the drift
326 MPI_Allreduce(fcoreloc, fcoresum, dim, MPI_DOUBLE, MPI_SUM, world);
327 for (int k=0; k<dim; k++) fcoresum[k] /= ncore;
328 for (int i=0; i<nlocal; i++) {
329 if (mask[i] & groupbit) { // only the cores need to be in the group
330 if (drudetype[type[i]] == NOPOL_TYPE) {
331 for (int k=0; k<dim; k++) f[i][k] -= fcoresum[k];
332 } else {
333 if (drudetype[type[i]] == DRUDE_TYPE) continue; // do with the core
334 int j = atom->map(drudeid[i]);
335 double mi, mj, mtot; // i is core, j is drude
336 if (rmass) {
337 mi = rmass[i];
338 mj = rmass[j];
339 } else {
340 mi = mass[type[i]];
341 mj = mass[type[j]];
342 }
343 mtot = mi + mj;
344 mi /= mtot;
345 mj /= mtot;
346 for (int k=0; k<dim; k++) {
347 f[i][k] -= mi * fcoresum[k];
348 f[j][k] -= mj * fcoresum[k];
349 }
350 }
351 }
352 }
353 }
354
355 // Reverse communication of the forces on ghost Drude particles
356 comm->reverse_comm();
357 }
358
359 /* ---------------------------------------------------------------------- */
360
reset_target(double t_new)361 void FixLangevinDrude::reset_target(double t_new)
362 {
363 t_target_core = t_start_core = t_new;
364 }
365
366 /* ----------------------------------------------------------------------
367 extract thermostat properties
368 ------------------------------------------------------------------------- */
369
extract(const char * str,int & dim)370 void *FixLangevinDrude::extract(const char *str, int &dim)
371 {
372 dim = 0;
373 if (strcmp(str,"t_target_core") == 0) {
374 return &t_target_core;
375 } else if (strcmp(str,"t_target_drude") == 0) {
376 return &t_target_drude;
377 } else error->all(FLERR, "Illegal extract string in fix langevin/drude");
378 return nullptr;
379 }
380
381 /* ---------------------------------------------------------------------- */
pack_reverse_comm(int n,int first,double * buf)382 int FixLangevinDrude::pack_reverse_comm(int n, int first, double *buf)
383 {
384 int i,m,last;
385 double ** f = atom->f;
386
387 m = 0;
388 last = first + n;
389 for (i = first; i < last; i++) {
390 buf[m++] = f[i][0];
391 buf[m++] = f[i][1];
392 buf[m++] = f[i][2];
393 }
394 return m;
395 }
396
397 /* ---------------------------------------------------------------------- */
398
unpack_reverse_comm(int n,int * list,double * buf)399 void FixLangevinDrude::unpack_reverse_comm(int n, int *list, double *buf)
400 {
401 int i,j,m;
402 double ** f = atom->f;
403
404 m = 0;
405 for (i = 0; i < n; i++) {
406 j = list[i];
407 f[j][0] += buf[m++];
408 f[j][1] += buf[m++];
409 f[j][2] += buf[m++];
410 }
411 }
412
413