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_external.h"
16
17 #include "atom.h"
18 #include "comm.h"
19 #include "error.h"
20 #include "memory.h"
21 #include "update.h"
22
23 #include <cstring>
24
25 using namespace LAMMPS_NS;
26 using namespace FixConst;
27
28 enum{PF_CALLBACK,PF_ARRAY};
29
30 /* ---------------------------------------------------------------------- */
31
FixExternal(LAMMPS * lmp,int narg,char ** arg)32 FixExternal::FixExternal(LAMMPS *lmp, int narg, char **arg) :
33 Fix(lmp, narg, arg),
34 fexternal(nullptr), caller_vector(nullptr)
35 {
36 if (narg < 4) error->all(FLERR,"Illegal fix external command");
37
38 scalar_flag = 1;
39 global_freq = 1;
40 extscalar = 1;
41 energy_global_flag = energy_peratom_flag = 1;
42 virial_global_flag = virial_peratom_flag = 1;
43 thermo_energy = thermo_virial = 1;
44
45 if (strcmp(arg[3],"pf/callback") == 0) {
46 if (narg != 6) error->all(FLERR,"Illegal fix external command");
47 mode = PF_CALLBACK;
48 ncall = utils::inumeric(FLERR,arg[4],false,lmp);
49 napply = utils::inumeric(FLERR,arg[5],false,lmp);
50 if (ncall <= 0 || napply <= 0)
51 error->all(FLERR,"Illegal fix external command");
52 } else if (strcmp(arg[3],"pf/array") == 0) {
53 if (narg != 5) error->all(FLERR,"Illegal fix external command");
54 mode = PF_ARRAY;
55 napply = utils::inumeric(FLERR,arg[4],false,lmp);
56 if (napply <= 0) error->all(FLERR,"Illegal fix external command");
57 } else error->all(FLERR,"Illegal fix external command");
58
59 callback = nullptr;
60
61 // perform initial allocation of atom-based array
62 // register with Atom class
63
64 FixExternal::grow_arrays(atom->nmax);
65 atom->add_callback(Atom::GROW);
66
67 user_energy = 0.0;
68
69 // optional vector of values provided by caller
70 // vector_flag and size_vector are setup via set_vector_length()
71
72 caller_vector = nullptr;
73 }
74
75 /* ---------------------------------------------------------------------- */
76
~FixExternal()77 FixExternal::~FixExternal()
78 {
79 // unregister callbacks to this fix from Atom class
80
81 atom->delete_callback(id,Atom::GROW);
82
83 memory->destroy(fexternal);
84 delete[] caller_vector;
85 }
86
87 /* ---------------------------------------------------------------------- */
88
setmask()89 int FixExternal::setmask()
90 {
91 int mask = 0;
92 if (mode == PF_CALLBACK || mode == PF_ARRAY) {
93 mask |= PRE_REVERSE;
94 mask |= POST_FORCE;
95 mask |= MIN_POST_FORCE;
96 }
97 return mask;
98 }
99
100 /* ---------------------------------------------------------------------- */
101
init()102 void FixExternal::init()
103 {
104 if (mode == PF_CALLBACK && callback == nullptr)
105 error->all(FLERR,"Fix external callback function not set");
106 }
107
108 /* ---------------------------------------------------------------------- */
109
setup(int vflag)110 void FixExternal::setup(int vflag)
111 {
112 post_force(vflag);
113 }
114
115 /* --------------------------------------------------------------------- */
116
setup_pre_reverse(int eflag,int vflag)117 void FixExternal::setup_pre_reverse(int eflag, int vflag)
118 {
119 pre_reverse(eflag,vflag);
120 }
121
122 /* ---------------------------------------------------------------------- */
123
min_setup(int vflag)124 void FixExternal::min_setup(int vflag)
125 {
126 post_force(vflag);
127 }
128
129 /* ----------------------------------------------------------------------
130 store eflag, so can use it in post_force to tally per-atom energies
131 ------------------------------------------------------------------------- */
132
pre_reverse(int eflag,int)133 void FixExternal::pre_reverse(int eflag, int /*vflag*/)
134 {
135 eflag_caller = eflag;
136 }
137
138 /* ---------------------------------------------------------------------- */
139
post_force(int vflag)140 void FixExternal::post_force(int vflag)
141 {
142 bigint ntimestep = update->ntimestep;
143
144 int eflag = eflag_caller;
145 ev_init(eflag,vflag);
146
147 // invoke the callback in driver program
148 // it will fill fexternal with forces
149
150 if (mode == PF_CALLBACK && ntimestep % ncall == 0)
151 (this->callback)(ptr_caller,update->ntimestep,
152 atom->nlocal,atom->tag,atom->x,fexternal);
153
154 // add forces from fexternal to atoms in group
155
156 if (ntimestep % napply == 0) {
157 double **f = atom->f;
158 int *mask = atom->mask;
159 int nlocal = atom->nlocal;
160
161 for (int i = 0; i < nlocal; i++)
162 if (mask[i] & groupbit) {
163 f[i][0] += fexternal[i][0];
164 f[i][1] += fexternal[i][1];
165 f[i][2] += fexternal[i][2];
166 }
167
168 // add contribution to global virial from previously stored value
169
170 if (vflag_global)
171 for (int i = 0; i < 6; ++i)
172 virial[i] = user_virial[i];
173 }
174 }
175
176 /* ---------------------------------------------------------------------- */
177
min_post_force(int vflag)178 void FixExternal::min_post_force(int vflag)
179 {
180 post_force(vflag);
181 }
182
183 // ----------------------------------------------------------------------
184 // "set" methods caller can invoke directly
185 // ----------------------------------------------------------------------
186
187 /* ----------------------------------------------------------------------
188 caller invokes this method to set its contribution to global energy
189 this is the *total* energy across all MPI ranks of the external code
190 and must be set for all MPI ranks.
191 unlike other energy/virial set methods:
192 do not just return if eflag_global is not set
193 b/c input script could access this quantity via compute_scalar()
194 even if eflag is not set on a particular timestep
195 this function is compatible with CALLBACK and ARRAY mode
196 ------------------------------------------------------------------------- */
197
set_energy_global(double caller_energy)198 void FixExternal::set_energy_global(double caller_energy)
199 {
200 user_energy = caller_energy;
201 }
202
203 /* ----------------------------------------------------------------------
204 caller invokes this method to set its contribution to the global virial
205 for all MPI ranks. the virial value is the *total* contribution across
206 all MPI ranks of the external code and thus we need to divide by the
207 number of MPI ranks since the tallying code expects per MPI rank contributions.
208 this function is compatible with PF_CALLBACK and PF_ARRAY mode
209 ------------------------------------------------------------------------- */
210
set_virial_global(double * caller_virial)211 void FixExternal::set_virial_global(double *caller_virial)
212 {
213 const double npscale = 1.0/(double)comm->nprocs;
214 for (int i = 0; i < 6; i++)
215 user_virial[i] = npscale * caller_virial[i];
216 }
217
218 /* ----------------------------------------------------------------------
219 caller invokes this method to set its contribution to peratom energy.
220 this is applied to the *local* atoms only.
221 this function is compatible with PF_CALLBACK mode only since it tallies
222 its energy contributions directly into the accumulator arrays.
223 ------------------------------------------------------------------------- */
224
set_energy_peratom(double * caller_energy)225 void FixExternal::set_energy_peratom(double *caller_energy)
226 {
227 if (!eflag_atom) return;
228 if ((mode == PF_ARRAY) && (comm->me == 0))
229 error->warning(FLERR,"Can only set energy/atom for fix external in pf/callback mode");
230
231 int nlocal = atom->nlocal;
232 for (int i = 0; i < nlocal; i++)
233 eatom[i] = caller_energy[i];
234 }
235
236 /* ----------------------------------------------------------------------
237 caller invokes this method to set its contribution to peratom virial
238 this is applied to the *local* atoms only.
239 this function is compatible with PF_CALLBACK mode only since it tallies
240 its virial contributions directly into the accumulator arrays.
241 ------------------------------------------------------------------------- */
242
set_virial_peratom(double ** caller_virial)243 void FixExternal::set_virial_peratom(double **caller_virial)
244 {
245 int i,j;
246
247 if (!vflag_atom) return;
248 if ((mode == PF_ARRAY) && (comm->me == 0))
249 error->warning(FLERR,"Can only set virial/atom for fix external in pf/callback mode");
250
251 int nlocal = atom->nlocal;
252 for (i = 0; i < nlocal; i++)
253 for (j = 0; j < 6; j++)
254 vatom[i][j] = caller_virial[i][j];
255 }
256
257 /* ----------------------------------------------------------------------
258 caller invokes this method to set length of global vector of values
259 assume all vector values are extensive.
260 ------------------------------------------------------------------------- */
261
set_vector_length(int n)262 void FixExternal::set_vector_length(int n)
263 {
264 delete[] caller_vector;
265
266 vector_flag = 1;
267 size_vector = n;
268 extvector = 1;
269
270 caller_vector = new double[n];
271 }
272
273 /* ----------------------------------------------------------------------
274 caller invokes this method to set value for item at "index" in vector
275 index is 1-based, thus index ranges from 1 to N inclusively.
276 Must be called from all MPI ranks.
277 ------------------------------------------------------------------------- */
278
set_vector(int index,double value)279 void FixExternal::set_vector(int index, double value)
280 {
281 if (index > size_vector)
282 error->all(FLERR,"Invalid set_vector index ({} of {}) in fix external",index,size_vector);
283 caller_vector[index-1] = value;
284 }
285
286 /* ----------------------------------------------------------------------
287 potential energy of added force
288 up to user to set it via set_energy()
289 ------------------------------------------------------------------------- */
290
compute_scalar()291 double FixExternal::compute_scalar()
292 {
293 return user_energy;
294 }
295
296 /* ----------------------------------------------------------------------
297 arbitrary value computed by caller
298 up to user to set it via set_vector()
299 ------------------------------------------------------------------------- */
300
compute_vector(int n)301 double FixExternal::compute_vector(int n)
302 {
303 return caller_vector[n];
304 }
305
306 /* ----------------------------------------------------------------------
307 memory usage of local atom-based array
308 ------------------------------------------------------------------------- */
309
memory_usage()310 double FixExternal::memory_usage()
311 {
312 double bytes = 3*atom->nmax * sizeof(double);
313 bytes += 6*sizeof(double);
314 return bytes;
315 }
316
317 /* ----------------------------------------------------------------------
318 allocate atom-based array
319 ------------------------------------------------------------------------- */
320
grow_arrays(int nmax)321 void FixExternal::grow_arrays(int nmax)
322 {
323 memory->grow(fexternal,nmax,3,"external:fexternal");
324 }
325
326 /* ----------------------------------------------------------------------
327 copy values within local atom-based array
328 ------------------------------------------------------------------------- */
329
copy_arrays(int i,int j,int)330 void FixExternal::copy_arrays(int i, int j, int /*delflag*/)
331 {
332 fexternal[j][0] = fexternal[i][0];
333 fexternal[j][1] = fexternal[i][1];
334 fexternal[j][2] = fexternal[i][2];
335 }
336
337 /* ----------------------------------------------------------------------
338 pack values in local atom-based array for exchange with another proc
339 ------------------------------------------------------------------------- */
340
pack_exchange(int i,double * buf)341 int FixExternal::pack_exchange(int i, double *buf)
342 {
343 buf[0] = fexternal[i][0];
344 buf[1] = fexternal[i][1];
345 buf[2] = fexternal[i][2];
346 return 3;
347 }
348
349 /* ----------------------------------------------------------------------
350 unpack values in local atom-based array from exchange with another proc
351 ------------------------------------------------------------------------- */
352
unpack_exchange(int nlocal,double * buf)353 int FixExternal::unpack_exchange(int nlocal, double *buf)
354 {
355 fexternal[nlocal][0] = buf[0];
356 fexternal[nlocal][1] = buf[1];
357 fexternal[nlocal][2] = buf[2];
358 return 3;
359 }
360
361 /* ----------------------------------------------------------------------
362 external caller sets a callback function to invoke in post_force()
363 ------------------------------------------------------------------------- */
364
set_callback(FnPtr caller_callback,void * caller_ptr)365 void FixExternal::set_callback(FnPtr caller_callback, void *caller_ptr)
366 {
367 callback = caller_callback;
368 ptr_caller = caller_ptr;
369 }
370
371 /* ----------------------------------------------------------------------
372 get access to internal data structures
373 ------------------------------------------------------------------------- */
374
extract(const char * str,int & dim)375 void *FixExternal::extract(const char *str, int &dim)
376 {
377 if (strcmp(str, "fexternal") == 0) {
378 dim = 2;
379 return (void *) fexternal;
380 }
381 return nullptr;
382 }
383