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 /* ----------------------------------------------------------------------
16    Contributing author: Aidan Thompson (SNL)
17                         improved CG and backtrack ls, added quadratic ls
18    Sources: Numerical Recipes frprmn routine
19             "Conjugate Gradient Method Without the Agonizing Pain" by
20             JR Shewchuk, https://www.cs.cmu.edu/~quake-papers/painless-conjugate-gradient.pdf
21 ------------------------------------------------------------------------- */
22 
23 #include "min.h"
24 
25 #include "angle.h"
26 #include "atom.h"
27 #include "atom_vec.h"
28 #include "bond.h"
29 #include "comm.h"
30 #include "compute.h"
31 #include "dihedral.h"
32 #include "domain.h"
33 #include "error.h"
34 #include "fix_minimize.h"
35 #include "force.h"
36 #include "improper.h"
37 #include "kspace.h"
38 #include "math_const.h"
39 #include "memory.h"
40 #include "modify.h"
41 #include "neighbor.h"
42 #include "output.h"
43 #include "pair.h"
44 #include "thermo.h"
45 #include "timer.h"
46 #include "update.h"
47 
48 #include <cmath>
49 #include <cstring>
50 
51 using namespace LAMMPS_NS;
52 using namespace MathConst;
53 
54 /* ---------------------------------------------------------------------- */
55 
Min(LAMMPS * lmp)56 Min::Min(LAMMPS *lmp) : Pointers(lmp)
57 {
58   dmax = 0.1;
59   searchflag = 0;
60   linestyle = 1;
61   normstyle = TWO;
62 
63   delaystep = 20;
64   dtgrow = 1.1;
65   dtshrink = 0.5;
66   alpha0 = 0.25;
67   alphashrink = 0.99;
68   tmax = 10.0;
69   tmin = 0.02;
70   integrator = 0;
71   halfstepback_flag = 1;
72   delaystep_start_flag = 1;
73   max_vdotf_negatif = 2000;
74   alpha_final = 0.0;
75 
76   elist_global = elist_atom = nullptr;
77   vlist_global = vlist_atom = cvlist_atom = nullptr;
78 
79   nextra_global = 0;
80   fextra = nullptr;
81 
82   nextra_atom = 0;
83   xextra_atom = fextra_atom = nullptr;
84   extra_peratom = extra_nlen = nullptr;
85   extra_max = nullptr;
86   requestor = nullptr;
87 
88   external_force_clear = 0;
89 
90   kokkosable = 0;
91 }
92 
93 /* ---------------------------------------------------------------------- */
94 
~Min()95 Min::~Min()
96 {
97   delete [] elist_global;
98   delete [] elist_atom;
99   delete [] vlist_global;
100   delete [] vlist_atom;
101   delete [] cvlist_atom;
102 
103   delete [] fextra;
104 
105   memory->sfree(xextra_atom);
106   memory->sfree(fextra_atom);
107   memory->destroy(extra_peratom);
108   memory->destroy(extra_nlen);
109   memory->destroy(extra_max);
110   memory->sfree(requestor);
111 }
112 
113 /* ---------------------------------------------------------------------- */
114 
init()115 void Min::init()
116 {
117   if (lmp->kokkos && !kokkosable)
118     error->all(FLERR,"Must use a Kokkos-enabled min style "
119                "(e.g. min_style cg/kk) with Kokkos minimize");
120 
121   // create fix needed for storing atom-based quantities
122   // will delete it at end of run
123 
124   fix_minimize = (FixMinimize *) modify->add_fix("MINIMIZE all MINIMIZE");
125 
126   // clear out extra global and per-atom dof
127   // will receive requests for new per-atom dof during pair init()
128   // can then add vectors to fix_minimize in setup()
129 
130   nextra_global = 0;
131   delete [] fextra;
132   fextra = nullptr;
133 
134   nextra_atom = 0;
135   memory->sfree(xextra_atom);
136   memory->sfree(fextra_atom);
137   memory->destroy(extra_peratom);
138   memory->destroy(extra_nlen);
139   memory->destroy(extra_max);
140   memory->sfree(requestor);
141   xextra_atom = fextra_atom = nullptr;
142   extra_peratom = extra_nlen = nullptr;
143   extra_max = nullptr;
144   requestor = nullptr;
145 
146   // virial_style:
147   // VIRIAL_PAIR if computed explicitly in pair via sum over pair interactions
148   // VIRIAL_FDOTR if computed implicitly in pair by
149   //   virial_fdotr_compute() via sum over ghosts
150 
151   if (force->newton_pair) virial_style = VIRIAL_FDOTR;
152   else virial_style = VIRIAL_PAIR;
153 
154   // setup lists of computes for global and per-atom PE and pressure
155 
156   ev_setup();
157 
158   // detect if fix omp is present for clearing force arrays
159 
160   int ifix = modify->find_fix("package_omp");
161   if (ifix >= 0) external_force_clear = 1;
162 
163   // set flags for arrays to clear in force_clear()
164 
165   torqueflag = extraflag = 0;
166   if (atom->torque_flag) torqueflag = 1;
167   if (atom->avec->forceclearflag) extraflag = 1;
168 
169   // allow pair and Kspace compute() to be turned off via modify flags
170 
171   if (force->pair && force->pair->compute_flag) pair_compute_flag = 1;
172   else pair_compute_flag = 0;
173   if (force->kspace && force->kspace->compute_flag) kspace_compute_flag = 1;
174   else kspace_compute_flag = 0;
175 
176   // orthogonal vs triclinic simulation box
177 
178   triclinic = domain->triclinic;
179 
180   // reset reneighboring criteria if necessary
181 
182   neigh_every = neighbor->every;
183   neigh_delay = neighbor->delay;
184   neigh_dist_check = neighbor->dist_check;
185 
186   if (neigh_every != 1 || neigh_delay != 0 || neigh_dist_check != 1) {
187     if (comm->me == 0)
188       error->warning(FLERR, "Using 'neigh_modify every 1 delay 0 check"
189                      " yes' setting during minimization");
190   }
191 
192   neighbor->every = 1;
193   neighbor->delay = 0;
194   neighbor->dist_check = 1;
195 
196   niter = neval = 0;
197 
198   // store timestep size (important for variable timestep minimizer)
199 
200   dtinit = update->dt;
201 }
202 
203 /* ----------------------------------------------------------------------
204    setup before run
205 ------------------------------------------------------------------------- */
206 
setup(int flag)207 void Min::setup(int flag)
208 {
209   if (comm->me == 0 && screen) {
210     fmt::print(screen,"Setting up {} style minimization ...\n",
211                update->minimize_style);
212     if (flag) {
213       fmt::print(screen,"  Unit style    : {}\n", update->unit_style);
214       fmt::print(screen,"  Current step  : {}\n", update->ntimestep);
215       timer->print_timeout(screen);
216     }
217   }
218   update->setupflag = 1;
219 
220   // setup extra global dof due to fixes
221   // cannot be done in init() b/c update init() is before modify init()
222 
223   nextra_global = modify->min_dof();
224   if (nextra_global) {
225     fextra = new double[nextra_global];
226     if (comm->me == 0 && screen)
227       fprintf(screen,"WARNING: Energy due to %d extra global DOFs will"
228               " be included in minimizer energies\n",nextra_global);
229   }
230 
231   // compute for potential energy
232 
233   int id = modify->find_compute("thermo_pe");
234   if (id < 0) error->all(FLERR,"Minimization could not find thermo_pe compute");
235   pe_compute = modify->compute[id];
236 
237   // style-specific setup does two tasks
238   // setup extra global dof vectors
239   // setup extra per-atom dof vectors due to requests from Pair classes
240   // cannot be done in init() b/c update init() is before modify/pair init()
241 
242   setup_style();
243 
244   // ndoftotal = total dof for entire minimization problem
245   // dof for atoms, extra per-atom, extra global
246 
247   bigint ndofme = 3 * static_cast<bigint>(atom->nlocal);
248   for (int m = 0; m < nextra_atom; m++)
249     ndofme += extra_peratom[m]*static_cast<bigint>(atom->nlocal);
250   MPI_Allreduce(&ndofme,&ndoftotal,1,MPI_LMP_BIGINT,MPI_SUM,world);
251   ndoftotal += nextra_global;
252 
253   // setup domain, communication and neighboring
254   // acquire ghosts
255   // build neighbor lists
256 
257   atom->setup();
258   modify->setup_pre_exchange();
259   if (triclinic) domain->x2lamda(atom->nlocal);
260   domain->pbc();
261   domain->reset_box();
262   comm->setup();
263   if (neighbor->style) neighbor->setup_bins();
264   comm->exchange();
265   if (atom->sortfreq > 0) atom->sort();
266   comm->borders();
267   if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost);
268   domain->image_check();
269   domain->box_too_small_check();
270   modify->setup_pre_neighbor();
271   neighbor->build(1);
272   modify->setup_post_neighbor();
273   neighbor->ncalls = 0;
274 
275   // remove these restriction eventually
276 
277   if (searchflag == 0) {
278     if (nextra_global)
279       error->all(FLERR,
280                  "Cannot use a damped dynamics min style with fix box/relax");
281     if (nextra_atom)
282       error->all(FLERR,
283                  "Cannot use a damped dynamics min style with per-atom DOF");
284   }
285 
286   if (strcmp(update->minimize_style,"hftn") == 0) {
287     if (nextra_global)
288       error->all(FLERR, "Cannot use hftn min style with fix box/relax");
289     if (nextra_atom)
290       error->all(FLERR, "Cannot use hftn min style with per-atom DOF");
291   }
292 
293   // atoms may have migrated in comm->exchange()
294 
295   reset_vectors();
296 
297   // compute all forces
298 
299   force->setup();
300   ev_set(update->ntimestep);
301   force_clear();
302   modify->setup_pre_force(vflag);
303 
304   if (pair_compute_flag) force->pair->compute(eflag,vflag);
305   else if (force->pair) force->pair->compute_dummy(eflag,vflag);
306 
307   if (atom->molecular != Atom::ATOMIC) {
308     if (force->bond) force->bond->compute(eflag,vflag);
309     if (force->angle) force->angle->compute(eflag,vflag);
310     if (force->dihedral) force->dihedral->compute(eflag,vflag);
311     if (force->improper) force->improper->compute(eflag,vflag);
312   }
313 
314   if (force->kspace) {
315     force->kspace->setup();
316     if (kspace_compute_flag) force->kspace->compute(eflag,vflag);
317     else force->kspace->compute_dummy(eflag,vflag);
318   }
319 
320   modify->setup_pre_reverse(eflag,vflag);
321   if (force->newton) comm->reverse_comm();
322 
323   // update per-atom minimization variables stored by pair styles
324 
325   if (nextra_atom)
326     for (int m = 0; m < nextra_atom; m++)
327       requestor[m]->min_xf_get(m);
328 
329   modify->setup(vflag);
330   output->setup(flag);
331   update->setupflag = 0;
332 
333   // stats for initial thermo output
334 
335   ecurrent = pe_compute->compute_scalar();
336   if (nextra_global) ecurrent += modify->min_energy(fextra);
337   if (output->thermo->normflag) ecurrent /= atom->natoms;
338 
339   einitial = ecurrent;
340   fnorm2_init = sqrt(fnorm_sqr());
341   fnorminf_init = sqrt(fnorm_inf());
342 }
343 
344 /* ----------------------------------------------------------------------
345    setup without output or one-time post-init setup
346    flag = 0 = just force calculation
347    flag = 1 = reneighbor and force calculation
348 ------------------------------------------------------------------------- */
349 
setup_minimal(int flag)350 void Min::setup_minimal(int flag)
351 {
352   update->setupflag = 1;
353 
354   // setup domain, communication and neighboring
355   // acquire ghosts
356   // build neighbor lists
357 
358   if (flag) {
359     modify->setup_pre_exchange();
360     if (triclinic) domain->x2lamda(atom->nlocal);
361     domain->pbc();
362     domain->reset_box();
363     comm->setup();
364     if (neighbor->style) neighbor->setup_bins();
365     comm->exchange();
366     comm->borders();
367     if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost);
368     domain->image_check();
369     domain->box_too_small_check();
370     modify->setup_pre_neighbor();
371     neighbor->build(1);
372     modify->setup_post_neighbor();
373     neighbor->ncalls = 0;
374   }
375 
376   // atoms may have migrated in comm->exchange()
377 
378   reset_vectors();
379 
380   // compute all forces
381 
382   ev_set(update->ntimestep);
383   force_clear();
384   modify->setup_pre_force(vflag);
385 
386   if (pair_compute_flag) force->pair->compute(eflag,vflag);
387   else if (force->pair) force->pair->compute_dummy(eflag,vflag);
388 
389   if (atom->molecular != Atom::ATOMIC) {
390     if (force->bond) force->bond->compute(eflag,vflag);
391     if (force->angle) force->angle->compute(eflag,vflag);
392     if (force->dihedral) force->dihedral->compute(eflag,vflag);
393     if (force->improper) force->improper->compute(eflag,vflag);
394   }
395 
396   if (force->kspace) {
397     force->kspace->setup();
398     if (kspace_compute_flag) force->kspace->compute(eflag,vflag);
399     else force->kspace->compute_dummy(eflag,vflag);
400   }
401 
402   modify->setup_pre_reverse(eflag,vflag);
403   if (force->newton) comm->reverse_comm();
404 
405   // update per-atom minimization variables stored by pair styles
406 
407   if (nextra_atom)
408     for (int m = 0; m < nextra_atom; m++)
409       requestor[m]->min_xf_get(m);
410 
411   modify->setup(vflag);
412   update->setupflag = 0;
413 
414   // stats for Finish to print
415 
416   ecurrent = pe_compute->compute_scalar();
417   if (nextra_global) ecurrent += modify->min_energy(fextra);
418   if (output->thermo->normflag) ecurrent /= atom->natoms;
419 
420   einitial = ecurrent;
421   fnorm2_init = sqrt(fnorm_sqr());
422   fnorminf_init = sqrt(fnorm_inf());
423 }
424 
425 /* ----------------------------------------------------------------------
426    perform minimization, calling iterate() for N steps
427 ------------------------------------------------------------------------- */
428 
run(int n)429 void Min::run(int n)
430 {
431   // minimizer iterations
432 
433   stop_condition = iterate(n);
434   stopstr = stopstrings(stop_condition);
435 
436   // if early exit from iterate loop:
437   // set update->nsteps to niter for Finish stats to print
438   // set output->next values to this timestep
439   // call energy_force() to insure vflag is set when forces computed
440   // output->write does final output for thermo, dump, restart files
441   // add ntimestep to all computes that store invocation times
442   //   since are hardwiring call to thermo/dumps and computes may not be ready
443 
444   if (stop_condition != MAXITER) {
445     update->nsteps = niter;
446 
447     if (update->restrict_output == 0) {
448       for (int idump = 0; idump < output->ndump; idump++)
449         output->next_dump[idump] = update->ntimestep;
450       output->next_dump_any = update->ntimestep;
451       if (output->restart_flag) {
452         output->next_restart = update->ntimestep;
453         if (output->restart_every_single)
454           output->next_restart_single = update->ntimestep;
455         if (output->restart_every_double)
456           output->next_restart_double = update->ntimestep;
457       }
458     }
459     output->next_thermo = update->ntimestep;
460 
461     modify->addstep_compute_all(update->ntimestep);
462     ecurrent = energy_force(0);
463     output->write(update->ntimestep);
464   }
465 }
466 
467 /* ---------------------------------------------------------------------- */
468 
cleanup()469 void Min::cleanup()
470 {
471   modify->post_run();
472 
473   // stats for Finish to print
474 
475   efinal = ecurrent;
476   fnorm2_final = sqrt(fnorm_sqr());
477   fnorminf_final = sqrt(fnorm_inf());
478 
479   // reset reneighboring criteria
480 
481   neighbor->every = neigh_every;
482   neighbor->delay = neigh_delay;
483   neighbor->dist_check = neigh_dist_check;
484 
485   // delete fix at end of run, so its atom arrays won't persist
486 
487   modify->delete_fix("MINIMIZE");
488   domain->box_too_small_check();
489 
490   // reset timestep size (important for variable timestep minimizer)
491 
492   update->dt = dtinit;
493 }
494 
495 /* ----------------------------------------------------------------------
496    evaluate potential energy and forces
497    may migrate atoms due to reneighboring
498    return new energy, which should include nextra_global dof
499    return negative gradient stored in atom->f
500    return negative gradient for nextra_global dof in fextra
501 ------------------------------------------------------------------------- */
502 
energy_force(int resetflag)503 double Min::energy_force(int resetflag)
504 {
505   // check for reneighboring
506   // always communicate since minimizer moved atoms
507 
508   int nflag = neighbor->decide();
509 
510   if (nflag == 0) {
511     timer->stamp();
512     comm->forward_comm();
513     timer->stamp(Timer::COMM);
514   } else {
515     if (modify->n_min_pre_exchange) {
516       timer->stamp();
517       modify->min_pre_exchange();
518       timer->stamp(Timer::MODIFY);
519     }
520     if (triclinic) domain->x2lamda(atom->nlocal);
521     domain->pbc();
522     if (domain->box_change) {
523       domain->reset_box();
524       comm->setup();
525       if (neighbor->style) neighbor->setup_bins();
526     }
527     timer->stamp();
528     comm->exchange();
529     if (atom->sortfreq > 0 &&
530         update->ntimestep >= atom->nextsort) atom->sort();
531     comm->borders();
532     if (triclinic) domain->lamda2x(atom->nlocal+atom->nghost);
533     timer->stamp(Timer::COMM);
534     if (modify->n_min_pre_neighbor) {
535       modify->min_pre_neighbor();
536       timer->stamp(Timer::MODIFY);
537     }
538     neighbor->build(1);
539     timer->stamp(Timer::NEIGH);
540     if (modify->n_min_post_neighbor) {
541       modify->min_post_neighbor();
542       timer->stamp(Timer::MODIFY);
543     }
544   }
545 
546   ev_set(update->ntimestep);
547   force_clear();
548 
549   timer->stamp();
550 
551   if (modify->n_min_pre_force) {
552     modify->min_pre_force(vflag);
553     timer->stamp(Timer::MODIFY);
554   }
555 
556   if (pair_compute_flag) {
557     force->pair->compute(eflag,vflag);
558     timer->stamp(Timer::PAIR);
559   }
560 
561   if (atom->molecular != Atom::ATOMIC) {
562     if (force->bond) force->bond->compute(eflag,vflag);
563     if (force->angle) force->angle->compute(eflag,vflag);
564     if (force->dihedral) force->dihedral->compute(eflag,vflag);
565     if (force->improper) force->improper->compute(eflag,vflag);
566     timer->stamp(Timer::BOND);
567   }
568 
569   if (kspace_compute_flag) {
570     force->kspace->compute(eflag,vflag);
571     timer->stamp(Timer::KSPACE);
572   }
573 
574   if (modify->n_min_pre_reverse) {
575     modify->min_pre_reverse(eflag,vflag);
576     timer->stamp(Timer::MODIFY);
577   }
578 
579   if (force->newton) {
580     comm->reverse_comm();
581     timer->stamp(Timer::COMM);
582   }
583 
584   // update per-atom minimization variables stored by pair styles
585 
586   if (nextra_atom)
587     for (int m = 0; m < nextra_atom; m++)
588       requestor[m]->min_xf_get(m);
589 
590   // fixes that affect minimization
591 
592   if (modify->n_min_post_force) {
593      timer->stamp();
594      modify->min_post_force(vflag);
595      timer->stamp(Timer::MODIFY);
596   }
597 
598   // compute potential energy of system
599   // normalize if thermo PE does
600 
601   double energy = pe_compute->compute_scalar();
602   if (nextra_global) energy += modify->min_energy(fextra);
603   if (output->thermo->normflag) energy /= atom->natoms;
604 
605   // if reneighbored, atoms migrated
606   // if resetflag = 1, update x0 of atoms crossing PBC
607   // reset vectors used by lo-level minimizer
608 
609   if (nflag) {
610     if (resetflag) fix_minimize->reset_coords();
611     reset_vectors();
612   }
613 
614   return energy;
615 }
616 
617 /* ----------------------------------------------------------------------
618    clear force on own & ghost atoms
619    clear other arrays as needed
620 ------------------------------------------------------------------------- */
621 
force_clear()622 void Min::force_clear()
623 {
624   if (external_force_clear) return;
625 
626   // clear global force array
627   // if either newton flag is set, also include ghosts
628 
629   size_t nbytes = sizeof(double) * atom->nlocal;
630   if (force->newton) nbytes += sizeof(double) * atom->nghost;
631 
632   if (nbytes) {
633     memset(&atom->f[0][0],0,3*nbytes);
634     if (torqueflag) memset(&atom->torque[0][0],0,3*nbytes);
635     if (extraflag) atom->avec->force_clear(0,nbytes);
636   }
637 }
638 
639 /* ----------------------------------------------------------------------
640    pair style makes request to add a per-atom variables to minimization
641    requestor stores callback to pair class to invoke during min
642      to get current variable and forces on it and to update the variable
643    return flag that pair can use if it registers multiple variables
644 ------------------------------------------------------------------------- */
645 
request(Pair * pair,int peratom,double maxvalue)646 int Min::request(Pair *pair, int peratom, double maxvalue)
647 {
648   int n = nextra_atom + 1;
649   xextra_atom = (double **) memory->srealloc(xextra_atom,n*sizeof(double *),
650                                              "min:xextra_atom");
651   fextra_atom = (double **) memory->srealloc(fextra_atom,n*sizeof(double *),
652                                              "min:fextra_atom");
653   memory->grow(extra_peratom,n,"min:extra_peratom");
654   memory->grow(extra_nlen,n,"min:extra_nlen");
655   memory->grow(extra_max,n,"min:extra_max");
656   requestor = (Pair **) memory->srealloc(requestor,n*sizeof(Pair *),
657                                          "min:requestor");
658 
659   requestor[nextra_atom] = pair;
660   extra_peratom[nextra_atom] = peratom;
661   extra_max[nextra_atom] = maxvalue;
662   nextra_atom++;
663   return nextra_atom-1;
664 }
665 
666 /* ---------------------------------------------------------------------- */
667 
modify_params(int narg,char ** arg)668 void Min::modify_params(int narg, char **arg)
669 {
670   if (narg == 0) error->all(FLERR,"Illegal min_modify command");
671 
672   int iarg = 0;
673   while (iarg < narg) {
674     if (strcmp(arg[iarg],"dmax") == 0) {
675       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
676       dmax = utils::numeric(FLERR,arg[iarg+1],false,lmp);
677       iarg += 2;
678     } else if (strcmp(arg[iarg],"delaystep") == 0) {
679       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
680       delaystep = utils::numeric(FLERR,arg[iarg+1],false,lmp);
681       iarg += 2;
682     } else if (strcmp(arg[iarg],"dtgrow") == 0) {
683       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
684       dtgrow = utils::numeric(FLERR,arg[iarg+1],false,lmp);
685       iarg += 2;
686     } else if (strcmp(arg[iarg],"dtshrink") == 0) {
687       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
688       dtshrink = utils::numeric(FLERR,arg[iarg+1],false,lmp);
689       iarg += 2;
690     } else if (strcmp(arg[iarg],"alpha0") == 0) {
691       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
692       alpha0 = utils::numeric(FLERR,arg[iarg+1],false,lmp);
693       iarg += 2;
694     } else if (strcmp(arg[iarg],"alphashrink") == 0) {
695       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
696       alphashrink = utils::numeric(FLERR,arg[iarg+1],false,lmp);
697       iarg += 2;
698     } else if (strcmp(arg[iarg],"tmax") == 0) {
699       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
700       tmax = utils::numeric(FLERR,arg[iarg+1],false,lmp);
701       iarg += 2;
702     } else if (strcmp(arg[iarg],"tmin") == 0) {
703       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
704       tmin = utils::numeric(FLERR,arg[iarg+1],false,lmp);
705       iarg += 2;
706     } else if (strcmp(arg[iarg],"halfstepback") == 0) {
707       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
708       if (strcmp(arg[iarg+1],"yes") == 0) halfstepback_flag = 1;
709       else if (strcmp(arg[iarg+1],"no") == 0) halfstepback_flag = 0;
710       else error->all(FLERR,"Illegal min_modify command");
711       iarg += 2;
712     } else if (strcmp(arg[iarg],"initialdelay") == 0) {
713       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
714       if (strcmp(arg[iarg+1],"yes") == 0) delaystep_start_flag = 1;
715       else if (strcmp(arg[iarg+1],"no") == 0) delaystep_start_flag = 0;
716       else error->all(FLERR,"Illegal min_modify command");
717       iarg += 2;
718     } else if (strcmp(arg[iarg],"vdfmax") == 0) {
719       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
720       max_vdotf_negatif = utils::numeric(FLERR,arg[iarg+1],false,lmp);
721       iarg += 2;
722     } else if (strcmp(arg[iarg],"integrator") == 0) {
723       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
724       if (strcmp(arg[iarg+1],"eulerimplicit") == 0) integrator = 0;
725       else if (strcmp(arg[iarg+1],"verlet") == 0) integrator = 1;
726       else if (strcmp(arg[iarg+1],"leapfrog") == 0) integrator = 2;
727       else if (strcmp(arg[iarg+1],"eulerexplicit") == 0) integrator = 3;
728       else error->all(FLERR,"Illegal min_modify command");
729       iarg += 2;
730     } else if (strcmp(arg[iarg],"line") == 0) {
731       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
732       if (strcmp(arg[iarg+1],"backtrack") == 0) linestyle = 0;
733       else if (strcmp(arg[iarg+1],"quadratic") == 0) linestyle = 1;
734       else if (strcmp(arg[iarg+1],"forcezero") == 0) linestyle = 2;
735       else if (strcmp(arg[iarg+1],"spin_cubic") == 0) linestyle = 3;
736       else if (strcmp(arg[iarg+1],"spin_none") == 0) linestyle = 4;
737       else error->all(FLERR,"Illegal min_modify command");
738       iarg += 2;
739     } else if (strcmp(arg[iarg],"norm") == 0) {
740       if (iarg+2 > narg) error->all(FLERR,"Illegal min_modify command");
741       if (strcmp(arg[iarg+1],"two") == 0) normstyle = TWO;
742       else if (strcmp(arg[iarg+1],"max") == 0) normstyle = MAX;
743       else if (strcmp(arg[iarg+1],"inf") == 0) normstyle = INF;
744       else error->all(FLERR,"Illegal min_modify command");
745       iarg += 2;
746     } else {
747       int n = modify_param(narg-iarg,&arg[iarg]);
748       if (n == 0) error->all(FLERR,"Illegal fix_modify command");
749       iarg += n;
750     }
751   }
752 }
753 
754 /* ----------------------------------------------------------------------
755    setup lists of computes for global and per-atom PE and pressure
756 ------------------------------------------------------------------------- */
757 
ev_setup()758 void Min::ev_setup()
759 {
760   delete [] elist_global;
761   delete [] elist_atom;
762   delete [] vlist_global;
763   delete [] vlist_atom;
764   delete [] cvlist_atom;
765   elist_global = elist_atom = nullptr;
766   vlist_global = vlist_atom = cvlist_atom = nullptr;
767 
768   nelist_global = nelist_atom = 0;
769   nvlist_global = nvlist_atom = ncvlist_atom = 0;
770   for (int i = 0; i < modify->ncompute; i++) {
771     if (modify->compute[i]->peflag) nelist_global++;
772     if (modify->compute[i]->peatomflag) nelist_atom++;
773     if (modify->compute[i]->pressflag) nvlist_global++;
774     if (modify->compute[i]->pressatomflag & 1) nvlist_atom++;
775     if (modify->compute[i]->pressatomflag & 2) ncvlist_atom++;
776   }
777 
778   if (nelist_global) elist_global = new Compute*[nelist_global];
779   if (nelist_atom) elist_atom = new Compute*[nelist_atom];
780   if (nvlist_global) vlist_global = new Compute*[nvlist_global];
781   if (nvlist_atom) vlist_atom = new Compute*[nvlist_atom];
782   if (ncvlist_atom) cvlist_atom = new Compute*[ncvlist_atom];
783 
784   nelist_global = nelist_atom = 0;
785   nvlist_global = nvlist_atom = ncvlist_atom = 0;
786   for (int i = 0; i < modify->ncompute; i++) {
787     if (modify->compute[i]->peflag)
788       elist_global[nelist_global++] = modify->compute[i];
789     if (modify->compute[i]->peatomflag)
790       elist_atom[nelist_atom++] = modify->compute[i];
791     if (modify->compute[i]->pressflag)
792       vlist_global[nvlist_global++] = modify->compute[i];
793     if (modify->compute[i]->pressatomflag & 1)
794       vlist_atom[nvlist_atom++] = modify->compute[i];
795     if (modify->compute[i]->pressatomflag & 2)
796       cvlist_atom[ncvlist_atom++] = modify->compute[i];
797   }
798 }
799 
800 /* ----------------------------------------------------------------------
801    set eflag,vflag for current iteration
802    invoke matchstep() on all timestep-dependent computes to clear their arrays
803    eflag/vflag based on computes that need info on this ntimestep
804    always set eflag_global = 1, since need energy every iteration
805    eflag: set any or no bits
806      ENERGY_GLOBAL bit for global energy
807      ENERGY_ATOM   bit for per-atom energy
808    vflag: set any or no bits, but GLOBAL/FDOTR bit cannot both be set
809      VIRIAL_PAIR     bit for global virial as sum of pairwise terms
810      VIRIAL_FDOTR    bit for global virial via F dot r
811      VIRIAL_ATOM     bit for per-atom virial
812      VIRIAL_CENTROID bit for per-atom centroid virial
813    all force components (pair,bond,angle,...,kspace) use eflag/vflag
814      in their ev_setup() method to set local energy/virial flags
815 ------------------------------------------------------------------------- */
816 
ev_set(bigint ntimestep)817 void Min::ev_set(bigint ntimestep)
818 {
819   int i,flag;
820 
821   int eflag_global = 1;
822   for (i = 0; i < nelist_global; i++)
823     elist_global[i]->matchstep(ntimestep);
824 
825   flag = 0;
826   int eflag_atom = 0;
827   for (i = 0; i < nelist_atom; i++)
828     if (elist_atom[i]->matchstep(ntimestep)) flag = 1;
829   if (flag) eflag_atom = ENERGY_ATOM;
830 
831   if (eflag_global) update->eflag_global = update->ntimestep;
832   if (eflag_atom) update->eflag_atom = update->ntimestep;
833   eflag = eflag_global + eflag_atom;
834 
835   flag = 0;
836   int vflag_global = 0;
837   for (i = 0; i < nvlist_global; i++)
838     if (vlist_global[i]->matchstep(ntimestep)) flag = 1;
839   if (flag) vflag_global = virial_style;
840 
841   flag = 0;
842   int vflag_atom = 0;
843   for (i = 0; i < nvlist_atom; i++)
844     if (vlist_atom[i]->matchstep(ntimestep)) flag = 1;
845   if (flag) vflag_atom = VIRIAL_ATOM;
846 
847   flag = 0;
848   int cvflag_atom = 0;
849   for (i = 0; i < ncvlist_atom; i++)
850     if (cvlist_atom[i]->matchstep(ntimestep)) flag = 1;
851   if (flag) cvflag_atom = VIRIAL_CENTROID;
852 
853   if (vflag_global) update->vflag_global = update->ntimestep;
854   if (vflag_atom || cvflag_atom) update->vflag_atom = update->ntimestep;
855   vflag = vflag_global + vflag_atom + cvflag_atom;
856 }
857 
858 /* ----------------------------------------------------------------------
859    compute and return ||force||_2^2
860 ------------------------------------------------------------------------- */
861 
fnorm_sqr()862 double Min::fnorm_sqr()
863 {
864   int i,n;
865   double *fatom;
866 
867   double local_norm2_sqr = 0.0;
868   for (i = 0; i < nvec; i++) local_norm2_sqr += fvec[i]*fvec[i];
869   if (nextra_atom) {
870     for (int m = 0; m < nextra_atom; m++) {
871       fatom = fextra_atom[m];
872       n = extra_nlen[m];
873       for (i = 0; i < n; i++)
874         local_norm2_sqr += fatom[i]*fatom[i];
875     }
876   }
877 
878   double norm2_sqr = 0.0;
879   MPI_Allreduce(&local_norm2_sqr,&norm2_sqr,1,MPI_DOUBLE,MPI_SUM,world);
880 
881   if (nextra_global)
882     for (i = 0; i < nextra_global; i++)
883       norm2_sqr += fextra[i]*fextra[i];
884 
885   return norm2_sqr;
886 }
887 
888 /* ----------------------------------------------------------------------
889    compute and return ||force||_inf
890 ------------------------------------------------------------------------- */
891 
fnorm_inf()892 double Min::fnorm_inf()
893 {
894   int i,n;
895   double *fatom;
896 
897   double local_norm_inf = 0.0;
898   for (i = 0; i < nvec; i++)
899     local_norm_inf = MAX(fvec[i]*fvec[i],local_norm_inf);
900   if (nextra_atom) {
901     for (int m = 0; m < nextra_atom; m++) {
902       fatom = fextra_atom[m];
903       n = extra_nlen[m];
904       for (i = 0; i < n; i++)
905         local_norm_inf = MAX(fatom[i]*fatom[i],local_norm_inf);
906     }
907   }
908 
909   double norm_inf = 0.0;
910   MPI_Allreduce(&local_norm_inf,&norm_inf,1,MPI_DOUBLE,MPI_MAX,world);
911 
912   if (nextra_global)
913     for (i = 0; i < nextra_global; i++)
914       norm_inf = MAX(fextra[i]*fextra[i],norm_inf);
915 
916   return norm_inf;
917 }
918 
919 /* ----------------------------------------------------------------------
920    compute and return ||force||_max (inf norm per-vector)
921 ------------------------------------------------------------------------- */
922 
fnorm_max()923 double Min::fnorm_max()
924 {
925   int i,n;
926   double fdotf,*fatom;
927 
928   double local_norm_max = 0.0;
929   for (i = 0; i < nvec; i+=3) {
930     fdotf = fvec[i]*fvec[i]+fvec[i+1]*fvec[i+1]+fvec[i+2]*fvec[i+2];
931     local_norm_max = MAX(fdotf,local_norm_max);
932   }
933   if (nextra_atom) {
934     for (int m = 0; m < nextra_atom; m++) {
935       fatom = fextra_atom[m];
936       n = extra_nlen[m];
937       for (i = 0; i < n; i+=3) {
938         fdotf = fatom[i]*fatom[i]+fatom[i+1]*fatom[i+1]+fatom[i+2]*fatom[i+2];
939         local_norm_max = MAX(fdotf,local_norm_max);
940       }
941     }
942   }
943 
944   double norm_max = 0.0;
945   MPI_Allreduce(&local_norm_max,&norm_max,1,MPI_DOUBLE,MPI_MAX,world);
946 
947   if (nextra_global) {
948     for (i = 0; i < nextra_global; i+=3) {
949       fdotf = fextra[i]*fextra[i];
950       norm_max = MAX(fdotf,norm_max);
951     }
952   }
953   return norm_max;
954 }
955 
956 /* ----------------------------------------------------------------------
957    compute and return  sum_i||mag. torque_i||_2 (in eV)
958 ------------------------------------------------------------------------- */
959 
total_torque()960 double Min::total_torque()
961 {
962   double ftotsqone,ftotsqall;
963   int nlocal = atom->nlocal;
964   double hbar = force->hplanck/MY_2PI;
965   double tx,ty,tz;
966   double **sp = atom->sp;
967   double **fm = atom->fm;
968 
969   ftotsqone = ftotsqall = 0.0;
970   for (int i = 0; i < nlocal; i++) {
971     tx = fm[i][1]*sp[i][2] - fm[i][2]*sp[i][1];
972     ty = fm[i][2]*sp[i][0] - fm[i][0]*sp[i][2];
973     tz = fm[i][0]*sp[i][1] - fm[i][1]*sp[i][0];
974     ftotsqone += tx*tx + ty*ty + tz*tz;
975   }
976 
977   // summing all fmsqtot on this replica
978 
979   MPI_Allreduce(&ftotsqone,&ftotsqall,1,MPI_DOUBLE,MPI_SUM,world);
980 
981   // multiply it by hbar so that units are in eV
982 
983   return sqrt(ftotsqall) * hbar;
984 }
985 
986 /* ----------------------------------------------------------------------
987    compute and return max_i ||mag. torque components|| (in eV)
988 ------------------------------------------------------------------------- */
989 
inf_torque()990 double Min::inf_torque()
991 {
992   double fmaxsqone,fmaxsqall;
993   int nlocal = atom->nlocal;
994   double hbar = force->hplanck/MY_2PI;
995   double tx,ty,tz;
996   double **sp = atom->sp;
997   double **fm = atom->fm;
998 
999   fmaxsqone = fmaxsqall = 0.0;
1000   for (int i = 0; i < nlocal; i++) {
1001     tx = fm[i][1]*sp[i][2] - fm[i][2]*sp[i][1];
1002     ty = fm[i][2]*sp[i][0] - fm[i][0]*sp[i][2];
1003     tz = fm[i][0]*sp[i][1] - fm[i][1]*sp[i][0];
1004     fmaxsqone = MAX(fmaxsqone,tx*tx);
1005     fmaxsqone = MAX(fmaxsqone,ty*ty);
1006     fmaxsqone = MAX(fmaxsqone,tz*tz);
1007   }
1008 
1009   // finding max fm on this replica
1010 
1011   fmaxsqall = fmaxsqone;
1012   MPI_Allreduce(&fmaxsqone,&fmaxsqall,1,MPI_DOUBLE,MPI_MAX,world);
1013 
1014   // multiply it by hbar so that units are in eV
1015 
1016   return sqrt(fmaxsqall) * hbar;
1017 }
1018 
1019 /* ----------------------------------------------------------------------
1020    compute and return max_i ||mag. torque_i|| (in eV)
1021 ------------------------------------------------------------------------- */
1022 
max_torque()1023 double Min::max_torque()
1024 {
1025   double fmsq,fmaxsqone,fmaxsqall;
1026   int nlocal = atom->nlocal;
1027   double hbar = force->hplanck/MY_2PI;
1028   double tx,ty,tz;
1029   double **sp = atom->sp;
1030   double **fm = atom->fm;
1031 
1032   fmaxsqone = fmaxsqall = 0.0;
1033   for (int i = 0; i < nlocal; i++) {
1034     tx = fm[i][1]*sp[i][2] - fm[i][2]*sp[i][1];
1035     ty = fm[i][2]*sp[i][0] - fm[i][0]*sp[i][2];
1036     tz = fm[i][0]*sp[i][1] - fm[i][1]*sp[i][0];
1037     fmsq = tx*tx + ty*ty + tz*tz;
1038     fmaxsqone = MAX(fmaxsqone,fmsq);
1039   }
1040 
1041   // finding max fm on this replica
1042 
1043   fmaxsqall = fmaxsqone;
1044   MPI_Allreduce(&fmaxsqone,&fmaxsqall,1,MPI_DOUBLE,MPI_MAX,world);
1045 
1046   // multiply it by hbar so that units are in eV
1047 
1048   return sqrt(fmaxsqall) * hbar;
1049 }
1050 
1051 /* ----------------------------------------------------------------------
1052    possible stop conditions
1053 ------------------------------------------------------------------------- */
1054 
stopstrings(int n)1055 char *Min::stopstrings(int n)
1056 {
1057   const char *strings[] = {"max iterations",
1058                            "max force evaluations",
1059                            "energy tolerance",
1060                            "force tolerance",
1061                            "search direction is not downhill",
1062                            "linesearch alpha is zero",
1063                            "forces are zero",
1064                            "quadratic factors are zero",
1065                            "trust region too small",
1066                            "HFTN minimizer error",
1067                            "walltime limit reached",
1068                            "max iterations with v.f negative"};
1069   return (char *) strings[n];
1070 }
1071