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