44276f96b1fbbdd9e268c3a0a33596b6522e8b11
[alexxy/gromacs.git] / src / gromacs / mdrun / minimize.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017 The GROMACS development team.
7  * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 /*! \internal \file
39  *
40  * \brief This file defines integrators for energy minimization
41  *
42  * \author Berk Hess <hess@kth.se>
43  * \author Erik Lindahl <erik@kth.se>
44  * \ingroup module_mdrun
45  */
46 #include "gmxpre.h"
47
48 #include "config.h"
49
50 #include <cmath>
51 #include <cstring>
52 #include <ctime>
53
54 #include <algorithm>
55 #include <vector>
56
57 #include "gromacs/commandline/filenm.h"
58 #include "gromacs/domdec/collect.h"
59 #include "gromacs/domdec/dlbtiming.h"
60 #include "gromacs/domdec/domdec.h"
61 #include "gromacs/domdec/domdec_struct.h"
62 #include "gromacs/domdec/mdsetup.h"
63 #include "gromacs/domdec/partition.h"
64 #include "gromacs/ewald/pme_pp.h"
65 #include "gromacs/fileio/confio.h"
66 #include "gromacs/fileio/mtxio.h"
67 #include "gromacs/gmxlib/network.h"
68 #include "gromacs/gmxlib/nrnb.h"
69 #include "gromacs/imd/imd.h"
70 #include "gromacs/linearalgebra/sparsematrix.h"
71 #include "gromacs/listed_forces/listed_forces.h"
72 #include "gromacs/math/functions.h"
73 #include "gromacs/math/vec.h"
74 #include "gromacs/mdlib/constr.h"
75 #include "gromacs/mdlib/coupling.h"
76 #include "gromacs/mdlib/dispersioncorrection.h"
77 #include "gromacs/mdlib/ebin.h"
78 #include "gromacs/mdlib/enerdata_utils.h"
79 #include "gromacs/mdlib/energyoutput.h"
80 #include "gromacs/mdlib/force.h"
81 #include "gromacs/mdlib/force_flags.h"
82 #include "gromacs/mdlib/forcerec.h"
83 #include "gromacs/mdlib/gmx_omp_nthreads.h"
84 #include "gromacs/mdlib/md_support.h"
85 #include "gromacs/mdlib/mdatoms.h"
86 #include "gromacs/mdlib/stat.h"
87 #include "gromacs/mdlib/tgroup.h"
88 #include "gromacs/mdlib/trajectory_writing.h"
89 #include "gromacs/mdlib/update.h"
90 #include "gromacs/mdlib/vsite.h"
91 #include "gromacs/mdrunutility/handlerestart.h"
92 #include "gromacs/mdrunutility/printtime.h"
93 #include "gromacs/mdtypes/checkpointdata.h"
94 #include "gromacs/mdtypes/commrec.h"
95 #include "gromacs/mdtypes/forcebuffers.h"
96 #include "gromacs/mdtypes/forcerec.h"
97 #include "gromacs/mdtypes/inputrec.h"
98 #include "gromacs/mdtypes/interaction_const.h"
99 #include "gromacs/mdtypes/md_enums.h"
100 #include "gromacs/mdtypes/mdatom.h"
101 #include "gromacs/mdtypes/mdrunoptions.h"
102 #include "gromacs/mdtypes/state.h"
103 #include "gromacs/pbcutil/pbc.h"
104 #include "gromacs/timing/wallcycle.h"
105 #include "gromacs/timing/walltime_accounting.h"
106 #include "gromacs/topology/mtop_util.h"
107 #include "gromacs/topology/topology.h"
108 #include "gromacs/utility/cstringutil.h"
109 #include "gromacs/utility/exceptions.h"
110 #include "gromacs/utility/fatalerror.h"
111 #include "gromacs/utility/logger.h"
112 #include "gromacs/utility/smalloc.h"
113
114 #include "legacysimulator.h"
115 #include "shellfc.h"
116
117 using gmx::ArrayRef;
118 using gmx::MdrunScheduleWorkload;
119 using gmx::RVec;
120 using gmx::VirtualSitesHandler;
121
122 //! Utility structure for manipulating states during EM
123 typedef struct em_state
124 {
125     //! Copy of the global state
126     t_state s;
127     //! Force array
128     gmx::ForceBuffers f;
129     //! Potential energy
130     real epot;
131     //! Norm of the force
132     real fnorm;
133     //! Maximum force
134     real fmax;
135     //! Direction
136     int a_fmax;
137 } em_state_t;
138
139 //! Print the EM starting conditions
140 static void print_em_start(FILE*                     fplog,
141                            const t_commrec*          cr,
142                            gmx_walltime_accounting_t walltime_accounting,
143                            gmx_wallcycle_t           wcycle,
144                            const char*               name)
145 {
146     walltime_accounting_start_time(walltime_accounting);
147     wallcycle_start(wcycle, ewcRUN);
148     print_start(fplog, cr, walltime_accounting, name);
149 }
150
151 //! Stop counting time for EM
152 static void em_time_end(gmx_walltime_accounting_t walltime_accounting, gmx_wallcycle_t wcycle)
153 {
154     wallcycle_stop(wcycle, ewcRUN);
155
156     walltime_accounting_end_time(walltime_accounting);
157 }
158
159 //! Printing a log file and console header
160 static void sp_header(FILE* out, const char* minimizer, real ftol, int nsteps)
161 {
162     fprintf(out, "\n");
163     fprintf(out, "%s:\n", minimizer);
164     fprintf(out, "   Tolerance (Fmax)   = %12.5e\n", ftol);
165     fprintf(out, "   Number of steps    = %12d\n", nsteps);
166 }
167
168 //! Print warning message
169 static void warn_step(FILE* fp, real ftol, real fmax, gmx_bool bLastStep, gmx_bool bConstrain)
170 {
171     constexpr bool realIsDouble = GMX_DOUBLE;
172     char           buffer[2048];
173
174     if (!std::isfinite(fmax))
175     {
176         sprintf(buffer,
177                 "\nEnergy minimization has stopped because the force "
178                 "on at least one atom is not finite. This usually means "
179                 "atoms are overlapping. Modify the input coordinates to "
180                 "remove atom overlap or use soft-core potentials with "
181                 "the free energy code to avoid infinite forces.\n%s",
182                 !realIsDouble ? "You could also be lucky that switching to double precision "
183                                 "is sufficient to obtain finite forces.\n"
184                               : "");
185     }
186     else if (bLastStep)
187     {
188         sprintf(buffer,
189                 "\nEnergy minimization reached the maximum number "
190                 "of steps before the forces reached the requested "
191                 "precision Fmax < %g.\n",
192                 ftol);
193     }
194     else
195     {
196         sprintf(buffer,
197                 "\nEnergy minimization has stopped, but the forces have "
198                 "not converged to the requested precision Fmax < %g (which "
199                 "may not be possible for your system). It stopped "
200                 "because the algorithm tried to make a new step whose size "
201                 "was too small, or there was no change in the energy since "
202                 "last step. Either way, we regard the minimization as "
203                 "converged to within the available machine precision, "
204                 "given your starting configuration and EM parameters.\n%s%s",
205                 ftol,
206                 !realIsDouble ? "\nDouble precision normally gives you higher accuracy, but "
207                                 "this is often not needed for preparing to run molecular "
208                                 "dynamics.\n"
209                               : "",
210                 bConstrain ? "You might need to increase your constraint accuracy, or turn\n"
211                              "off constraints altogether (set constraints = none in mdp file)\n"
212                            : "");
213     }
214
215     fputs(wrap_lines(buffer, 78, 0, FALSE), stderr);
216     fputs(wrap_lines(buffer, 78, 0, FALSE), fp);
217 }
218
219 //! Print message about convergence of the EM
220 static void print_converged(FILE*             fp,
221                             const char*       alg,
222                             real              ftol,
223                             int64_t           count,
224                             gmx_bool          bDone,
225                             int64_t           nsteps,
226                             const em_state_t* ems,
227                             double            sqrtNumAtoms)
228 {
229     char buf[STEPSTRSIZE];
230
231     if (bDone)
232     {
233         fprintf(fp, "\n%s converged to Fmax < %g in %s steps\n", alg, ftol, gmx_step_str(count, buf));
234     }
235     else if (count < nsteps)
236     {
237         fprintf(fp,
238                 "\n%s converged to machine precision in %s steps,\n"
239                 "but did not reach the requested Fmax < %g.\n",
240                 alg, gmx_step_str(count, buf), ftol);
241     }
242     else
243     {
244         fprintf(fp, "\n%s did not converge to Fmax < %g in %s steps.\n", alg, ftol,
245                 gmx_step_str(count, buf));
246     }
247
248 #if GMX_DOUBLE
249     fprintf(fp, "Potential Energy  = %21.14e\n", ems->epot);
250     fprintf(fp, "Maximum force     = %21.14e on atom %d\n", ems->fmax, ems->a_fmax + 1);
251     fprintf(fp, "Norm of force     = %21.14e\n", ems->fnorm / sqrtNumAtoms);
252 #else
253     fprintf(fp, "Potential Energy  = %14.7e\n", ems->epot);
254     fprintf(fp, "Maximum force     = %14.7e on atom %d\n", ems->fmax, ems->a_fmax + 1);
255     fprintf(fp, "Norm of force     = %14.7e\n", ems->fnorm / sqrtNumAtoms);
256 #endif
257 }
258
259 //! Compute the norm and max of the force array in parallel
260 static void get_f_norm_max(const t_commrec*               cr,
261                            t_grpopts*                     opts,
262                            t_mdatoms*                     mdatoms,
263                            gmx::ArrayRef<const gmx::RVec> f,
264                            real*                          fnorm,
265                            real*                          fmax,
266                            int*                           a_fmax)
267 {
268     double fnorm2, *sum;
269     real   fmax2, fam;
270     int    la_max, a_max, start, end, i, m, gf;
271
272     /* This routine finds the largest force and returns it.
273      * On parallel machines the global max is taken.
274      */
275     fnorm2 = 0;
276     fmax2  = 0;
277     la_max = -1;
278     start  = 0;
279     end    = mdatoms->homenr;
280     if (mdatoms->cFREEZE)
281     {
282         for (i = start; i < end; i++)
283         {
284             gf  = mdatoms->cFREEZE[i];
285             fam = 0;
286             for (m = 0; m < DIM; m++)
287             {
288                 if (!opts->nFreeze[gf][m])
289                 {
290                     fam += gmx::square(f[i][m]);
291                 }
292             }
293             fnorm2 += fam;
294             if (fam > fmax2)
295             {
296                 fmax2  = fam;
297                 la_max = i;
298             }
299         }
300     }
301     else
302     {
303         for (i = start; i < end; i++)
304         {
305             fam = norm2(f[i]);
306             fnorm2 += fam;
307             if (fam > fmax2)
308             {
309                 fmax2  = fam;
310                 la_max = i;
311             }
312         }
313     }
314
315     if (la_max >= 0 && DOMAINDECOMP(cr))
316     {
317         a_max = cr->dd->globalAtomIndices[la_max];
318     }
319     else
320     {
321         a_max = la_max;
322     }
323     if (PAR(cr))
324     {
325         snew(sum, 2 * cr->nnodes + 1);
326         sum[2 * cr->nodeid]     = fmax2;
327         sum[2 * cr->nodeid + 1] = a_max;
328         sum[2 * cr->nnodes]     = fnorm2;
329         gmx_sumd(2 * cr->nnodes + 1, sum, cr);
330         fnorm2 = sum[2 * cr->nnodes];
331         /* Determine the global maximum */
332         for (i = 0; i < cr->nnodes; i++)
333         {
334             if (sum[2 * i] > fmax2)
335             {
336                 fmax2 = sum[2 * i];
337                 a_max = gmx::roundToInt(sum[2 * i + 1]);
338             }
339         }
340         sfree(sum);
341     }
342
343     if (fnorm)
344     {
345         *fnorm = sqrt(fnorm2);
346     }
347     if (fmax)
348     {
349         *fmax = sqrt(fmax2);
350     }
351     if (a_fmax)
352     {
353         *a_fmax = a_max;
354     }
355 }
356
357 //! Compute the norm of the force
358 static void get_state_f_norm_max(const t_commrec* cr, t_grpopts* opts, t_mdatoms* mdatoms, em_state_t* ems)
359 {
360     get_f_norm_max(cr, opts, mdatoms, ems->f.view().force(), &ems->fnorm, &ems->fmax, &ems->a_fmax);
361 }
362
363 //! Initialize the energy minimization
364 static void init_em(FILE*                fplog,
365                     const gmx::MDLogger& mdlog,
366                     const char*          title,
367                     const t_commrec*     cr,
368                     t_inputrec*          ir,
369                     gmx::ImdSession*     imdSession,
370                     pull_t*              pull_work,
371                     t_state*             state_global,
372                     const gmx_mtop_t*    top_global,
373                     em_state_t*          ems,
374                     gmx_localtop_t*      top,
375                     t_nrnb*              nrnb,
376                     t_forcerec*          fr,
377                     gmx::MDAtoms*        mdAtoms,
378                     gmx_global_stat_t*   gstat,
379                     VirtualSitesHandler* vsite,
380                     gmx::Constraints*    constr,
381                     gmx_shellfc_t**      shellfc)
382 {
383     real dvdl_constr;
384
385     if (fplog)
386     {
387         fprintf(fplog, "Initiating %s\n", title);
388     }
389
390     if (MASTER(cr))
391     {
392         state_global->ngtc = 0;
393     }
394     int*                fep_state = MASTER(cr) ? &state_global->fep_state : nullptr;
395     gmx::ArrayRef<real> lambda    = MASTER(cr) ? state_global->lambda : gmx::ArrayRef<real>();
396     initialize_lambdas(fplog, *ir, MASTER(cr), fep_state, lambda);
397
398     if (ir->eI == eiNM)
399     {
400         GMX_ASSERT(shellfc != nullptr, "With NM we always support shells");
401
402         *shellfc =
403                 init_shell_flexcon(stdout, top_global, constr ? constr->numFlexibleConstraints() : 0,
404                                    ir->nstcalcenergy, DOMAINDECOMP(cr), thisRankHasDuty(cr, DUTY_PME));
405     }
406     else
407     {
408         GMX_ASSERT(EI_ENERGY_MINIMIZATION(ir->eI),
409                    "This else currently only handles energy minimizers, consider if your algorithm "
410                    "needs shell/flexible-constraint support");
411
412         /* With energy minimization, shells and flexible constraints are
413          * automatically minimized when treated like normal DOFS.
414          */
415         if (shellfc != nullptr)
416         {
417             *shellfc = nullptr;
418         }
419     }
420
421     if (DOMAINDECOMP(cr))
422     {
423         dd_init_local_state(cr->dd, state_global, &ems->s);
424
425         /* Distribute the charge groups over the nodes from the master node */
426         dd_partition_system(fplog, mdlog, ir->init_step, cr, TRUE, 1, state_global, *top_global, ir,
427                             imdSession, pull_work, &ems->s, &ems->f, mdAtoms, top, fr, vsite,
428                             constr, nrnb, nullptr, FALSE);
429         dd_store_state(cr->dd, &ems->s);
430     }
431     else
432     {
433         state_change_natoms(state_global, state_global->natoms);
434         /* Just copy the state */
435         ems->s = *state_global;
436         state_change_natoms(&ems->s, ems->s.natoms);
437
438         mdAlgorithmsSetupAtomData(cr, ir, *top_global, top, fr, &ems->f, mdAtoms, constr, vsite,
439                                   shellfc ? *shellfc : nullptr);
440     }
441
442     update_mdatoms(mdAtoms->mdatoms(), ems->s.lambda[efptMASS]);
443
444     if (constr)
445     {
446         // TODO how should this cross-module support dependency be managed?
447         if (ir->eConstrAlg == econtSHAKE && gmx_mtop_ftype_count(top_global, F_CONSTR) > 0)
448         {
449             gmx_fatal(FARGS, "Can not do energy minimization with %s, use %s\n",
450                       econstr_names[econtSHAKE], econstr_names[econtLINCS]);
451         }
452
453         if (!ir->bContinuation)
454         {
455             /* Constrain the starting coordinates */
456             bool needsLogging  = true;
457             bool computeEnergy = true;
458             bool computeVirial = false;
459             dvdl_constr        = 0;
460             constr->apply(needsLogging, computeEnergy, -1, 0, 1.0, ems->s.x.arrayRefWithPadding(),
461                           ems->s.x.arrayRefWithPadding(), ArrayRef<RVec>(), ems->s.box,
462                           ems->s.lambda[efptFEP], &dvdl_constr, gmx::ArrayRefWithPadding<RVec>(),
463                           computeVirial, nullptr, gmx::ConstraintVariable::Positions);
464         }
465     }
466
467     if (PAR(cr))
468     {
469         *gstat = global_stat_init(ir);
470     }
471     else
472     {
473         *gstat = nullptr;
474     }
475
476     calc_shifts(ems->s.box, fr->shift_vec);
477 }
478
479 //! Finalize the minimization
480 static void finish_em(const t_commrec*          cr,
481                       gmx_mdoutf_t              outf,
482                       gmx_walltime_accounting_t walltime_accounting,
483                       gmx_wallcycle_t           wcycle)
484 {
485     if (!thisRankHasDuty(cr, DUTY_PME))
486     {
487         /* Tell the PME only node to finish */
488         gmx_pme_send_finish(cr);
489     }
490
491     done_mdoutf(outf);
492
493     em_time_end(walltime_accounting, wcycle);
494 }
495
496 //! Swap two different EM states during minimization
497 static void swap_em_state(em_state_t** ems1, em_state_t** ems2)
498 {
499     em_state_t* tmp;
500
501     tmp   = *ems1;
502     *ems1 = *ems2;
503     *ems2 = tmp;
504 }
505
506 //! Save the EM trajectory
507 static void write_em_traj(FILE*               fplog,
508                           const t_commrec*    cr,
509                           gmx_mdoutf_t        outf,
510                           gmx_bool            bX,
511                           gmx_bool            bF,
512                           const char*         confout,
513                           const gmx_mtop_t*   top_global,
514                           t_inputrec*         ir,
515                           int64_t             step,
516                           em_state_t*         state,
517                           t_state*            state_global,
518                           ObservablesHistory* observablesHistory)
519 {
520     int mdof_flags = 0;
521
522     if (bX)
523     {
524         mdof_flags |= MDOF_X;
525     }
526     if (bF)
527     {
528         mdof_flags |= MDOF_F;
529     }
530
531     /* If we want IMD output, set appropriate MDOF flag */
532     if (ir->bIMD)
533     {
534         mdof_flags |= MDOF_IMD;
535     }
536
537     gmx::WriteCheckpointDataHolder checkpointDataHolder;
538     mdoutf_write_to_trajectory_files(fplog, cr, outf, mdof_flags, top_global->natoms, step,
539                                      static_cast<double>(step), &state->s, state_global,
540                                      observablesHistory, state->f.view().force(), &checkpointDataHolder);
541
542     if (confout != nullptr)
543     {
544         if (DOMAINDECOMP(cr))
545         {
546             /* If bX=true, x was collected to state_global in the call above */
547             if (!bX)
548             {
549                 auto globalXRef = MASTER(cr) ? state_global->x : gmx::ArrayRef<gmx::RVec>();
550                 dd_collect_vec(cr->dd, state->s.ddp_count, state->s.ddp_count_cg_gl, state->s.cg_gl,
551                                state->s.x, globalXRef);
552             }
553         }
554         else
555         {
556             /* Copy the local state pointer */
557             state_global = &state->s;
558         }
559
560         if (MASTER(cr))
561         {
562             if (ir->pbcType != PbcType::No && !ir->bPeriodicMols && DOMAINDECOMP(cr))
563             {
564                 /* Make molecules whole only for confout writing */
565                 do_pbc_mtop(ir->pbcType, state->s.box, top_global, state_global->x.rvec_array());
566             }
567
568             write_sto_conf_mtop(confout, *top_global->name, top_global,
569                                 state_global->x.rvec_array(), nullptr, ir->pbcType, state->s.box);
570         }
571     }
572 }
573
574 //! \brief Do one minimization step
575 //
576 // \returns true when the step succeeded, false when a constraint error occurred
577 static bool do_em_step(const t_commrec*                          cr,
578                        t_inputrec*                               ir,
579                        t_mdatoms*                                md,
580                        em_state_t*                               ems1,
581                        real                                      a,
582                        gmx::ArrayRefWithPadding<const gmx::RVec> force,
583                        em_state_t*                               ems2,
584                        gmx::Constraints*                         constr,
585                        int64_t                                   count)
586
587 {
588     t_state *s1, *s2;
589     int      start, end;
590     real     dvdl_constr;
591     int nthreads gmx_unused;
592
593     bool validStep = true;
594
595     s1 = &ems1->s;
596     s2 = &ems2->s;
597
598     if (DOMAINDECOMP(cr) && s1->ddp_count != cr->dd->ddp_count)
599     {
600         gmx_incons("state mismatch in do_em_step");
601     }
602
603     s2->flags = s1->flags;
604
605     if (s2->natoms != s1->natoms)
606     {
607         state_change_natoms(s2, s1->natoms);
608         ems2->f.resize(s2->natoms);
609     }
610     if (DOMAINDECOMP(cr) && s2->cg_gl.size() != s1->cg_gl.size())
611     {
612         s2->cg_gl.resize(s1->cg_gl.size());
613     }
614
615     copy_mat(s1->box, s2->box);
616     /* Copy free energy state */
617     s2->lambda = s1->lambda;
618     copy_mat(s1->box, s2->box);
619
620     start = 0;
621     end   = md->homenr;
622
623     nthreads = gmx_omp_nthreads_get(emntUpdate);
624 #pragma omp parallel num_threads(nthreads)
625     {
626         const rvec* x1 = s1->x.rvec_array();
627         rvec*       x2 = s2->x.rvec_array();
628         const rvec* f  = as_rvec_array(force.unpaddedArrayRef().data());
629
630         int gf = 0;
631 #pragma omp for schedule(static) nowait
632         for (int i = start; i < end; i++)
633         {
634             try
635             {
636                 if (md->cFREEZE)
637                 {
638                     gf = md->cFREEZE[i];
639                 }
640                 for (int m = 0; m < DIM; m++)
641                 {
642                     if (ir->opts.nFreeze[gf][m])
643                     {
644                         x2[i][m] = x1[i][m];
645                     }
646                     else
647                     {
648                         x2[i][m] = x1[i][m] + a * f[i][m];
649                     }
650                 }
651             }
652             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
653         }
654
655         if (s2->flags & (1 << estCGP))
656         {
657             /* Copy the CG p vector */
658             const rvec* p1 = s1->cg_p.rvec_array();
659             rvec*       p2 = s2->cg_p.rvec_array();
660 #pragma omp for schedule(static) nowait
661             for (int i = start; i < end; i++)
662             {
663                 // Trivial OpenMP block that does not throw
664                 copy_rvec(p1[i], p2[i]);
665             }
666         }
667
668         if (DOMAINDECOMP(cr))
669         {
670             /* OpenMP does not supported unsigned loop variables */
671 #pragma omp for schedule(static) nowait
672             for (gmx::index i = 0; i < gmx::ssize(s2->cg_gl); i++)
673             {
674                 s2->cg_gl[i] = s1->cg_gl[i];
675             }
676         }
677     }
678
679     if (DOMAINDECOMP(cr))
680     {
681         s2->ddp_count       = s1->ddp_count;
682         s2->ddp_count_cg_gl = s1->ddp_count_cg_gl;
683     }
684
685     if (constr)
686     {
687         dvdl_constr = 0;
688         validStep   = constr->apply(
689                 TRUE, TRUE, count, 0, 1.0, s1->x.arrayRefWithPadding(), s2->x.arrayRefWithPadding(),
690                 ArrayRef<RVec>(), s2->box, s2->lambda[efptBONDED], &dvdl_constr,
691                 gmx::ArrayRefWithPadding<RVec>(), false, nullptr, gmx::ConstraintVariable::Positions);
692
693         if (cr->nnodes > 1)
694         {
695             /* This global reduction will affect performance at high
696              * parallelization, but we can not really avoid it.
697              * But usually EM is not run at high parallelization.
698              */
699             int reductionBuffer = static_cast<int>(!validStep);
700             gmx_sumi(1, &reductionBuffer, cr);
701             validStep = (reductionBuffer == 0);
702         }
703
704         // We should move this check to the different minimizers
705         if (!validStep && ir->eI != eiSteep)
706         {
707             gmx_fatal(FARGS,
708                       "The coordinates could not be constrained. Minimizer '%s' can not handle "
709                       "constraint failures, use minimizer '%s' before using '%s'.",
710                       EI(ir->eI), EI(eiSteep), EI(ir->eI));
711         }
712     }
713
714     return validStep;
715 }
716
717 //! Prepare EM for using domain decomposition parallellization
718 static void em_dd_partition_system(FILE*                fplog,
719                                    const gmx::MDLogger& mdlog,
720                                    int                  step,
721                                    const t_commrec*     cr,
722                                    const gmx_mtop_t*    top_global,
723                                    t_inputrec*          ir,
724                                    gmx::ImdSession*     imdSession,
725                                    pull_t*              pull_work,
726                                    em_state_t*          ems,
727                                    gmx_localtop_t*      top,
728                                    gmx::MDAtoms*        mdAtoms,
729                                    t_forcerec*          fr,
730                                    VirtualSitesHandler* vsite,
731                                    gmx::Constraints*    constr,
732                                    t_nrnb*              nrnb,
733                                    gmx_wallcycle_t      wcycle)
734 {
735     /* Repartition the domain decomposition */
736     dd_partition_system(fplog, mdlog, step, cr, FALSE, 1, nullptr, *top_global, ir, imdSession, pull_work,
737                         &ems->s, &ems->f, mdAtoms, top, fr, vsite, constr, nrnb, wcycle, FALSE);
738     dd_store_state(cr->dd, &ems->s);
739 }
740
741 namespace
742 {
743
744 /*! \brief Class to handle the work of setting and doing an energy evaluation.
745  *
746  * This class is a mere aggregate of parameters to pass to evaluate an
747  * energy, so that future changes to names and types of them consume
748  * less time when refactoring other code.
749  *
750  * Aggregate initialization is used, for which the chief risk is that
751  * if a member is added at the end and not all initializer lists are
752  * updated, then the member will be value initialized, which will
753  * typically mean initialization to zero.
754  *
755  * Use a braced initializer list to construct one of these. */
756 class EnergyEvaluator
757 {
758 public:
759     /*! \brief Evaluates an energy on the state in \c ems.
760      *
761      * \todo In practice, the same objects mu_tot, vir, and pres
762      * are always passed to this function, so we would rather have
763      * them as data members. However, their C-array types are
764      * unsuited for aggregate initialization. When the types
765      * improve, the call signature of this method can be reduced.
766      */
767     void run(em_state_t* ems, rvec mu_tot, tensor vir, tensor pres, int64_t count, gmx_bool bFirst);
768     //! Handles logging (deprecated).
769     FILE* fplog;
770     //! Handles logging.
771     const gmx::MDLogger& mdlog;
772     //! Handles communication.
773     const t_commrec* cr;
774     //! Coordinates multi-simulations.
775     const gmx_multisim_t* ms;
776     //! Holds the simulation topology.
777     const gmx_mtop_t* top_global;
778     //! Holds the domain topology.
779     gmx_localtop_t* top;
780     //! User input options.
781     t_inputrec* inputrec;
782     //! The Interactive Molecular Dynamics session.
783     gmx::ImdSession* imdSession;
784     //! The pull work object.
785     pull_t* pull_work;
786     //! Manages flop accounting.
787     t_nrnb* nrnb;
788     //! Manages wall cycle accounting.
789     gmx_wallcycle_t wcycle;
790     //! Coordinates global reduction.
791     gmx_global_stat_t gstat;
792     //! Handles virtual sites.
793     VirtualSitesHandler* vsite;
794     //! Handles constraints.
795     gmx::Constraints* constr;
796     //! Per-atom data for this domain.
797     gmx::MDAtoms* mdAtoms;
798     //! Handles how to calculate the forces.
799     t_forcerec* fr;
800     //! Schedule of force-calculation work each step for this task.
801     MdrunScheduleWorkload* runScheduleWork;
802     //! Stores the computed energies.
803     gmx_enerdata_t* enerd;
804 };
805
806 void EnergyEvaluator::run(em_state_t* ems, rvec mu_tot, tensor vir, tensor pres, int64_t count, gmx_bool bFirst)
807 {
808     real     t;
809     gmx_bool bNS;
810     tensor   force_vir, shake_vir, ekin;
811     real     dvdl_constr;
812     real     terminate = 0;
813
814     /* Set the time to the initial time, the time does not change during EM */
815     t = inputrec->init_t;
816
817     if (bFirst || (DOMAINDECOMP(cr) && ems->s.ddp_count < cr->dd->ddp_count))
818     {
819         /* This is the first state or an old state used before the last ns */
820         bNS = TRUE;
821     }
822     else
823     {
824         bNS = FALSE;
825         if (inputrec->nstlist > 0)
826         {
827             bNS = TRUE;
828         }
829     }
830
831     if (vsite)
832     {
833         vsite->construct(ems->s.x, 1, {}, ems->s.box);
834     }
835
836     if (DOMAINDECOMP(cr) && bNS)
837     {
838         /* Repartition the domain decomposition */
839         em_dd_partition_system(fplog, mdlog, count, cr, top_global, inputrec, imdSession, pull_work,
840                                ems, top, mdAtoms, fr, vsite, constr, nrnb, wcycle);
841     }
842
843     /* Calc force & energy on new trial position  */
844     /* do_force always puts the charge groups in the box and shifts again
845      * We do not unshift, so molecules are always whole in congrad.c
846      */
847     do_force(fplog, cr, ms, inputrec, nullptr, nullptr, imdSession, pull_work, count, nrnb, wcycle,
848              top, ems->s.box, ems->s.x.arrayRefWithPadding(), &ems->s.hist, &ems->f.view(), force_vir,
849              mdAtoms->mdatoms(), enerd, ems->s.lambda, fr, runScheduleWork, vsite, mu_tot, t, nullptr,
850              GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES | GMX_FORCE_VIRIAL | GMX_FORCE_ENERGY
851                      | (bNS ? GMX_FORCE_NS : 0),
852              DDBalanceRegionHandler(cr));
853
854     /* Clear the unused shake virial and pressure */
855     clear_mat(shake_vir);
856     clear_mat(pres);
857
858     /* Communicate stuff when parallel */
859     if (PAR(cr) && inputrec->eI != eiNM)
860     {
861         wallcycle_start(wcycle, ewcMoveE);
862
863         global_stat(gstat, cr, enerd, force_vir, shake_vir, inputrec, nullptr, nullptr, nullptr, 1,
864                     &terminate, nullptr, FALSE, CGLO_ENERGY | CGLO_PRESSURE | CGLO_CONSTRAINT);
865
866         wallcycle_stop(wcycle, ewcMoveE);
867     }
868
869     if (fr->dispersionCorrection)
870     {
871         /* Calculate long range corrections to pressure and energy */
872         const DispersionCorrection::Correction correction =
873                 fr->dispersionCorrection->calculate(ems->s.box, ems->s.lambda[efptVDW]);
874
875         enerd->term[F_DISPCORR] = correction.energy;
876         enerd->term[F_EPOT] += correction.energy;
877         enerd->term[F_PRES] += correction.pressure;
878         enerd->term[F_DVDL] += correction.dvdl;
879     }
880     else
881     {
882         enerd->term[F_DISPCORR] = 0;
883     }
884
885     ems->epot = enerd->term[F_EPOT];
886
887     if (constr)
888     {
889         /* Project out the constraint components of the force */
890         bool needsLogging  = false;
891         bool computeEnergy = false;
892         bool computeVirial = true;
893         dvdl_constr        = 0;
894         auto f             = ems->f.view().forceWithPadding();
895         constr->apply(needsLogging, computeEnergy, count, 0, 1.0, ems->s.x.arrayRefWithPadding(), f,
896                       f.unpaddedArrayRef(), ems->s.box, ems->s.lambda[efptBONDED], &dvdl_constr,
897                       gmx::ArrayRefWithPadding<RVec>(), computeVirial, shake_vir,
898                       gmx::ConstraintVariable::ForceDispl);
899         enerd->term[F_DVDL_CONSTR] += dvdl_constr;
900         m_add(force_vir, shake_vir, vir);
901     }
902     else
903     {
904         copy_mat(force_vir, vir);
905     }
906
907     clear_mat(ekin);
908     enerd->term[F_PRES] = calc_pres(fr->pbcType, inputrec->nwall, ems->s.box, ekin, vir, pres);
909
910     if (inputrec->efep != efepNO)
911     {
912         accumulateKineticLambdaComponents(enerd, ems->s.lambda, *inputrec->fepvals);
913     }
914
915     if (EI_ENERGY_MINIMIZATION(inputrec->eI))
916     {
917         get_state_f_norm_max(cr, &(inputrec->opts), mdAtoms->mdatoms(), ems);
918     }
919 }
920
921 } // namespace
922
923 //! Parallel utility summing energies and forces
924 static double reorder_partsum(const t_commrec*  cr,
925                               t_grpopts*        opts,
926                               const gmx_mtop_t* top_global,
927                               const em_state_t* s_min,
928                               const em_state_t* s_b)
929 {
930     if (debug)
931     {
932         fprintf(debug, "Doing reorder_partsum\n");
933     }
934
935     auto fm = s_min->f.view().force();
936     auto fb = s_b->f.view().force();
937
938     /* Collect fm in a global vector fmg.
939      * This conflicts with the spirit of domain decomposition,
940      * but to fully optimize this a much more complicated algorithm is required.
941      */
942     const int natoms = top_global->natoms;
943     rvec*     fmg;
944     snew(fmg, natoms);
945
946     gmx::ArrayRef<const int> indicesMin = s_min->s.cg_gl;
947     int                      i          = 0;
948     for (int a : indicesMin)
949     {
950         copy_rvec(fm[i], fmg[a]);
951         i++;
952     }
953     gmx_sum(top_global->natoms * 3, fmg[0], cr);
954
955     /* Now we will determine the part of the sum for the cgs in state s_b */
956     gmx::ArrayRef<const int> indicesB = s_b->s.cg_gl;
957
958     double partsum                        = 0;
959     i                                     = 0;
960     int                                gf = 0;
961     gmx::ArrayRef<const unsigned char> grpnrFREEZE =
962             top_global->groups.groupNumbers[SimulationAtomGroupType::Freeze];
963     for (int a : indicesB)
964     {
965         if (!grpnrFREEZE.empty())
966         {
967             gf = grpnrFREEZE[i];
968         }
969         for (int m = 0; m < DIM; m++)
970         {
971             if (!opts->nFreeze[gf][m])
972             {
973                 partsum += (fb[i][m] - fmg[a][m]) * fb[i][m];
974             }
975         }
976         i++;
977     }
978
979     sfree(fmg);
980
981     return partsum;
982 }
983
984 //! Print some stuff, like beta, whatever that means.
985 static real pr_beta(const t_commrec*  cr,
986                     t_grpopts*        opts,
987                     t_mdatoms*        mdatoms,
988                     const gmx_mtop_t* top_global,
989                     const em_state_t* s_min,
990                     const em_state_t* s_b)
991 {
992     double sum;
993
994     /* This is just the classical Polak-Ribiere calculation of beta;
995      * it looks a bit complicated since we take freeze groups into account,
996      * and might have to sum it in parallel runs.
997      */
998
999     if (!DOMAINDECOMP(cr)
1000         || (s_min->s.ddp_count == cr->dd->ddp_count && s_b->s.ddp_count == cr->dd->ddp_count))
1001     {
1002         auto fm = s_min->f.view().force();
1003         auto fb = s_b->f.view().force();
1004         sum     = 0;
1005         int gf  = 0;
1006         /* This part of code can be incorrect with DD,
1007          * since the atom ordering in s_b and s_min might differ.
1008          */
1009         for (int i = 0; i < mdatoms->homenr; i++)
1010         {
1011             if (mdatoms->cFREEZE)
1012             {
1013                 gf = mdatoms->cFREEZE[i];
1014             }
1015             for (int m = 0; m < DIM; m++)
1016             {
1017                 if (!opts->nFreeze[gf][m])
1018                 {
1019                     sum += (fb[i][m] - fm[i][m]) * fb[i][m];
1020                 }
1021             }
1022         }
1023     }
1024     else
1025     {
1026         /* We need to reorder cgs while summing */
1027         sum = reorder_partsum(cr, opts, top_global, s_min, s_b);
1028     }
1029     if (PAR(cr))
1030     {
1031         gmx_sumd(1, &sum, cr);
1032     }
1033
1034     return sum / gmx::square(s_min->fnorm);
1035 }
1036
1037 namespace gmx
1038 {
1039
1040 void LegacySimulator::do_cg()
1041 {
1042     const char* CG = "Polak-Ribiere Conjugate Gradients";
1043
1044     gmx_localtop_t    top(top_global->ffparams);
1045     gmx_global_stat_t gstat;
1046     double            tmp, minstep;
1047     real              stepsize;
1048     real              a, b, c, beta = 0.0;
1049     real              epot_repl = 0;
1050     real              pnorm;
1051     gmx_bool          converged, foundlower;
1052     rvec              mu_tot = { 0 };
1053     gmx_bool          do_log = FALSE, do_ene = FALSE, do_x, do_f;
1054     tensor            vir, pres;
1055     int               number_steps, neval = 0, nstcg = inputrec->nstcgsteep;
1056     int               m, step, nminstep;
1057     auto              mdatoms = mdAtoms->mdatoms();
1058
1059     GMX_LOG(mdlog.info)
1060             .asParagraph()
1061             .appendText(
1062                     "Note that activating conjugate gradient energy minimization via the "
1063                     "integrator .mdp option and the command gmx mdrun may "
1064                     "be available in a different form in a future version of GROMACS, "
1065                     "e.g. gmx minimize and an .mdp option.");
1066
1067     step = 0;
1068
1069     if (MASTER(cr))
1070     {
1071         // In CG, the state is extended with a search direction
1072         state_global->flags |= (1 << estCGP);
1073
1074         // Ensure the extra per-atom state array gets allocated
1075         state_change_natoms(state_global, state_global->natoms);
1076
1077         // Initialize the search direction to zero
1078         for (RVec& cg_p : state_global->cg_p)
1079         {
1080             cg_p = { 0, 0, 0 };
1081         }
1082     }
1083
1084     /* Create 4 states on the stack and extract pointers that we will swap */
1085     em_state_t  s0{}, s1{}, s2{}, s3{};
1086     em_state_t* s_min = &s0;
1087     em_state_t* s_a   = &s1;
1088     em_state_t* s_b   = &s2;
1089     em_state_t* s_c   = &s3;
1090
1091     /* Init em and store the local state in s_min */
1092     init_em(fplog, mdlog, CG, cr, inputrec, imdSession, pull_work, state_global, top_global, s_min,
1093             &top, nrnb, fr, mdAtoms, &gstat, vsite, constr, nullptr);
1094     const bool        simulationsShareState = false;
1095     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
1096                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
1097                                    StartingBehavior::NewSimulation, simulationsShareState, ms);
1098     gmx::EnergyOutput energyOutput(mdoutf_get_fp_ene(outf), top_global, inputrec, pull_work, nullptr,
1099                                    false, StartingBehavior::NewSimulation, mdModulesNotifier);
1100
1101     /* Print to log file */
1102     print_em_start(fplog, cr, walltime_accounting, wcycle, CG);
1103
1104     /* Max number of steps */
1105     number_steps = inputrec->nsteps;
1106
1107     if (MASTER(cr))
1108     {
1109         sp_header(stderr, CG, inputrec->em_tol, number_steps);
1110     }
1111     if (fplog)
1112     {
1113         sp_header(fplog, CG, inputrec->em_tol, number_steps);
1114     }
1115
1116     EnergyEvaluator energyEvaluator{ fplog,    mdlog,      cr,        ms,   top_global,      &top,
1117                                      inputrec, imdSession, pull_work, nrnb, wcycle,          gstat,
1118                                      vsite,    constr,     mdAtoms,   fr,   runScheduleWork, enerd };
1119     /* Call the force routine and some auxiliary (neighboursearching etc.) */
1120     /* do_force always puts the charge groups in the box and shifts again
1121      * We do not unshift, so molecules are always whole in congrad.c
1122      */
1123     energyEvaluator.run(s_min, mu_tot, vir, pres, -1, TRUE);
1124
1125     if (MASTER(cr))
1126     {
1127         /* Copy stuff to the energy bin for easy printing etc. */
1128         matrix nullBox = {};
1129         energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
1130                                          enerd, nullptr, nullptr, nullBox, PTCouplingArrays(), 0,
1131                                          nullptr, nullptr, vir, pres, nullptr, mu_tot, constr);
1132
1133         EnergyOutput::printHeader(fplog, step, step);
1134         energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step,
1135                                            step, fr->fcdata.get(), nullptr);
1136     }
1137
1138     /* Estimate/guess the initial stepsize */
1139     stepsize = inputrec->em_stepsize / s_min->fnorm;
1140
1141     if (MASTER(cr))
1142     {
1143         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1144         fprintf(stderr, "   F-max             = %12.5e on atom %d\n", s_min->fmax, s_min->a_fmax + 1);
1145         fprintf(stderr, "   F-Norm            = %12.5e\n", s_min->fnorm / sqrtNumAtoms);
1146         fprintf(stderr, "\n");
1147         /* and copy to the log file too... */
1148         fprintf(fplog, "   F-max             = %12.5e on atom %d\n", s_min->fmax, s_min->a_fmax + 1);
1149         fprintf(fplog, "   F-Norm            = %12.5e\n", s_min->fnorm / sqrtNumAtoms);
1150         fprintf(fplog, "\n");
1151     }
1152     /* Start the loop over CG steps.
1153      * Each successful step is counted, and we continue until
1154      * we either converge or reach the max number of steps.
1155      */
1156     converged = FALSE;
1157     for (step = 0; (number_steps < 0 || step <= number_steps) && !converged; step++)
1158     {
1159
1160         /* start taking steps in a new direction
1161          * First time we enter the routine, beta=0, and the direction is
1162          * simply the negative gradient.
1163          */
1164
1165         /* Calculate the new direction in p, and the gradient in this direction, gpa */
1166         gmx::ArrayRef<gmx::RVec>       pm  = s_min->s.cg_p;
1167         gmx::ArrayRef<const gmx::RVec> sfm = s_min->f.view().force();
1168         double                         gpa = 0;
1169         int                            gf  = 0;
1170         for (int i = 0; i < mdatoms->homenr; i++)
1171         {
1172             if (mdatoms->cFREEZE)
1173             {
1174                 gf = mdatoms->cFREEZE[i];
1175             }
1176             for (m = 0; m < DIM; m++)
1177             {
1178                 if (!inputrec->opts.nFreeze[gf][m])
1179                 {
1180                     pm[i][m] = sfm[i][m] + beta * pm[i][m];
1181                     gpa -= pm[i][m] * sfm[i][m];
1182                     /* f is negative gradient, thus the sign */
1183                 }
1184                 else
1185                 {
1186                     pm[i][m] = 0;
1187                 }
1188             }
1189         }
1190
1191         /* Sum the gradient along the line across CPUs */
1192         if (PAR(cr))
1193         {
1194             gmx_sumd(1, &gpa, cr);
1195         }
1196
1197         /* Calculate the norm of the search vector */
1198         get_f_norm_max(cr, &(inputrec->opts), mdatoms, pm, &pnorm, nullptr, nullptr);
1199
1200         /* Just in case stepsize reaches zero due to numerical precision... */
1201         if (stepsize <= 0)
1202         {
1203             stepsize = inputrec->em_stepsize / pnorm;
1204         }
1205
1206         /*
1207          * Double check the value of the derivative in the search direction.
1208          * If it is positive it must be due to the old information in the
1209          * CG formula, so just remove that and start over with beta=0.
1210          * This corresponds to a steepest descent step.
1211          */
1212         if (gpa > 0)
1213         {
1214             beta = 0;
1215             step--;   /* Don't count this step since we are restarting */
1216             continue; /* Go back to the beginning of the big for-loop */
1217         }
1218
1219         /* Calculate minimum allowed stepsize, before the average (norm)
1220          * relative change in coordinate is smaller than precision
1221          */
1222         minstep      = 0;
1223         auto s_min_x = makeArrayRef(s_min->s.x);
1224         for (int i = 0; i < mdatoms->homenr; i++)
1225         {
1226             for (m = 0; m < DIM; m++)
1227             {
1228                 tmp = fabs(s_min_x[i][m]);
1229                 if (tmp < 1.0)
1230                 {
1231                     tmp = 1.0;
1232                 }
1233                 tmp = pm[i][m] / tmp;
1234                 minstep += tmp * tmp;
1235             }
1236         }
1237         /* Add up from all CPUs */
1238         if (PAR(cr))
1239         {
1240             gmx_sumd(1, &minstep, cr);
1241         }
1242
1243         minstep = GMX_REAL_EPS / sqrt(minstep / (3 * top_global->natoms));
1244
1245         if (stepsize < minstep)
1246         {
1247             converged = TRUE;
1248             break;
1249         }
1250
1251         /* Write coordinates if necessary */
1252         do_x = do_per_step(step, inputrec->nstxout);
1253         do_f = do_per_step(step, inputrec->nstfout);
1254
1255         write_em_traj(fplog, cr, outf, do_x, do_f, nullptr, top_global, inputrec, step, s_min,
1256                       state_global, observablesHistory);
1257
1258         /* Take a step downhill.
1259          * In theory, we should minimize the function along this direction.
1260          * That is quite possible, but it turns out to take 5-10 function evaluations
1261          * for each line. However, we dont really need to find the exact minimum -
1262          * it is much better to start a new CG step in a modified direction as soon
1263          * as we are close to it. This will save a lot of energy evaluations.
1264          *
1265          * In practice, we just try to take a single step.
1266          * If it worked (i.e. lowered the energy), we increase the stepsize but
1267          * the continue straight to the next CG step without trying to find any minimum.
1268          * If it didn't work (higher energy), there must be a minimum somewhere between
1269          * the old position and the new one.
1270          *
1271          * Due to the finite numerical accuracy, it turns out that it is a good idea
1272          * to even accept a SMALL increase in energy, if the derivative is still downhill.
1273          * This leads to lower final energies in the tests I've done. / Erik
1274          */
1275         s_a->epot = s_min->epot;
1276         a         = 0.0;
1277         c         = a + stepsize; /* reference position along line is zero */
1278
1279         if (DOMAINDECOMP(cr) && s_min->s.ddp_count < cr->dd->ddp_count)
1280         {
1281             em_dd_partition_system(fplog, mdlog, step, cr, top_global, inputrec, imdSession,
1282                                    pull_work, s_min, &top, mdAtoms, fr, vsite, constr, nrnb, wcycle);
1283         }
1284
1285         /* Take a trial step (new coords in s_c) */
1286         do_em_step(cr, inputrec, mdatoms, s_min, c, s_min->s.cg_p.constArrayRefWithPadding(), s_c,
1287                    constr, -1);
1288
1289         neval++;
1290         /* Calculate energy for the trial step */
1291         energyEvaluator.run(s_c, mu_tot, vir, pres, -1, FALSE);
1292
1293         /* Calc derivative along line */
1294         const rvec*                    pc  = s_c->s.cg_p.rvec_array();
1295         gmx::ArrayRef<const gmx::RVec> sfc = s_c->f.view().force();
1296         double                         gpc = 0;
1297         for (int i = 0; i < mdatoms->homenr; i++)
1298         {
1299             for (m = 0; m < DIM; m++)
1300             {
1301                 gpc -= pc[i][m] * sfc[i][m]; /* f is negative gradient, thus the sign */
1302             }
1303         }
1304         /* Sum the gradient along the line across CPUs */
1305         if (PAR(cr))
1306         {
1307             gmx_sumd(1, &gpc, cr);
1308         }
1309
1310         /* This is the max amount of increase in energy we tolerate */
1311         tmp = std::sqrt(GMX_REAL_EPS) * fabs(s_a->epot);
1312
1313         /* Accept the step if the energy is lower, or if it is not significantly higher
1314          * and the line derivative is still negative.
1315          */
1316         if (s_c->epot < s_a->epot || (gpc < 0 && s_c->epot < (s_a->epot + tmp)))
1317         {
1318             foundlower = TRUE;
1319             /* Great, we found a better energy. Increase step for next iteration
1320              * if we are still going down, decrease it otherwise
1321              */
1322             if (gpc < 0)
1323             {
1324                 stepsize *= 1.618034; /* The golden section */
1325             }
1326             else
1327             {
1328                 stepsize *= 0.618034; /* 1/golden section */
1329             }
1330         }
1331         else
1332         {
1333             /* New energy is the same or higher. We will have to do some work
1334              * to find a smaller value in the interval. Take smaller step next time!
1335              */
1336             foundlower = FALSE;
1337             stepsize *= 0.618034;
1338         }
1339
1340
1341         /* OK, if we didn't find a lower value we will have to locate one now - there must
1342          * be one in the interval [a=0,c].
1343          * The same thing is valid here, though: Don't spend dozens of iterations to find
1344          * the line minimum. We try to interpolate based on the derivative at the endpoints,
1345          * and only continue until we find a lower value. In most cases this means 1-2 iterations.
1346          *
1347          * I also have a safeguard for potentially really pathological functions so we never
1348          * take more than 20 steps before we give up ...
1349          *
1350          * If we already found a lower value we just skip this step and continue to the update.
1351          */
1352         double gpb;
1353         if (!foundlower)
1354         {
1355             nminstep = 0;
1356
1357             do
1358             {
1359                 /* Select a new trial point.
1360                  * If the derivatives at points a & c have different sign we interpolate to zero,
1361                  * otherwise just do a bisection.
1362                  */
1363                 if (gpa < 0 && gpc > 0)
1364                 {
1365                     b = a + gpa * (a - c) / (gpc - gpa);
1366                 }
1367                 else
1368                 {
1369                     b = 0.5 * (a + c);
1370                 }
1371
1372                 /* safeguard if interpolation close to machine accuracy causes errors:
1373                  * never go outside the interval
1374                  */
1375                 if (b <= a || b >= c)
1376                 {
1377                     b = 0.5 * (a + c);
1378                 }
1379
1380                 if (DOMAINDECOMP(cr) && s_min->s.ddp_count != cr->dd->ddp_count)
1381                 {
1382                     /* Reload the old state */
1383                     em_dd_partition_system(fplog, mdlog, -1, cr, top_global, inputrec, imdSession, pull_work,
1384                                            s_min, &top, mdAtoms, fr, vsite, constr, nrnb, wcycle);
1385                 }
1386
1387                 /* Take a trial step to this new point - new coords in s_b */
1388                 do_em_step(cr, inputrec, mdatoms, s_min, b,
1389                            s_min->s.cg_p.constArrayRefWithPadding(), s_b, constr, -1);
1390
1391                 neval++;
1392                 /* Calculate energy for the trial step */
1393                 energyEvaluator.run(s_b, mu_tot, vir, pres, -1, FALSE);
1394
1395                 /* p does not change within a step, but since the domain decomposition
1396                  * might change, we have to use cg_p of s_b here.
1397                  */
1398                 const rvec*                    pb  = s_b->s.cg_p.rvec_array();
1399                 gmx::ArrayRef<const gmx::RVec> sfb = s_b->f.view().force();
1400                 gpb                                = 0;
1401                 for (int i = 0; i < mdatoms->homenr; i++)
1402                 {
1403                     for (m = 0; m < DIM; m++)
1404                     {
1405                         gpb -= pb[i][m] * sfb[i][m]; /* f is negative gradient, thus the sign */
1406                     }
1407                 }
1408                 /* Sum the gradient along the line across CPUs */
1409                 if (PAR(cr))
1410                 {
1411                     gmx_sumd(1, &gpb, cr);
1412                 }
1413
1414                 if (debug)
1415                 {
1416                     fprintf(debug, "CGE: EpotA %f EpotB %f EpotC %f gpb %f\n", s_a->epot, s_b->epot,
1417                             s_c->epot, gpb);
1418                 }
1419
1420                 epot_repl = s_b->epot;
1421
1422                 /* Keep one of the intervals based on the value of the derivative at the new point */
1423                 if (gpb > 0)
1424                 {
1425                     /* Replace c endpoint with b */
1426                     swap_em_state(&s_b, &s_c);
1427                     c   = b;
1428                     gpc = gpb;
1429                 }
1430                 else
1431                 {
1432                     /* Replace a endpoint with b */
1433                     swap_em_state(&s_b, &s_a);
1434                     a   = b;
1435                     gpa = gpb;
1436                 }
1437
1438                 /*
1439                  * Stop search as soon as we find a value smaller than the endpoints.
1440                  * Never run more than 20 steps, no matter what.
1441                  */
1442                 nminstep++;
1443             } while ((epot_repl > s_a->epot || epot_repl > s_c->epot) && (nminstep < 20));
1444
1445             if (std::fabs(epot_repl - s_min->epot) < fabs(s_min->epot) * GMX_REAL_EPS || nminstep >= 20)
1446             {
1447                 /* OK. We couldn't find a significantly lower energy.
1448                  * If beta==0 this was steepest descent, and then we give up.
1449                  * If not, set beta=0 and restart with steepest descent before quitting.
1450                  */
1451                 if (beta == 0.0)
1452                 {
1453                     /* Converged */
1454                     converged = TRUE;
1455                     break;
1456                 }
1457                 else
1458                 {
1459                     /* Reset memory before giving up */
1460                     beta = 0.0;
1461                     continue;
1462                 }
1463             }
1464
1465             /* Select min energy state of A & C, put the best in B.
1466              */
1467             if (s_c->epot < s_a->epot)
1468             {
1469                 if (debug)
1470                 {
1471                     fprintf(debug, "CGE: C (%f) is lower than A (%f), moving C to B\n", s_c->epot,
1472                             s_a->epot);
1473                 }
1474                 swap_em_state(&s_b, &s_c);
1475                 gpb = gpc;
1476             }
1477             else
1478             {
1479                 if (debug)
1480                 {
1481                     fprintf(debug, "CGE: A (%f) is lower than C (%f), moving A to B\n", s_a->epot,
1482                             s_c->epot);
1483                 }
1484                 swap_em_state(&s_b, &s_a);
1485                 gpb = gpa;
1486             }
1487         }
1488         else
1489         {
1490             if (debug)
1491             {
1492                 fprintf(debug, "CGE: Found a lower energy %f, moving C to B\n", s_c->epot);
1493             }
1494             swap_em_state(&s_b, &s_c);
1495             gpb = gpc;
1496         }
1497
1498         /* new search direction */
1499         /* beta = 0 means forget all memory and restart with steepest descents. */
1500         if (nstcg && ((step % nstcg) == 0))
1501         {
1502             beta = 0.0;
1503         }
1504         else
1505         {
1506             /* s_min->fnorm cannot be zero, because then we would have converged
1507              * and broken out.
1508              */
1509
1510             /* Polak-Ribiere update.
1511              * Change to fnorm2/fnorm2_old for Fletcher-Reeves
1512              */
1513             beta = pr_beta(cr, &inputrec->opts, mdatoms, top_global, s_min, s_b);
1514         }
1515         /* Limit beta to prevent oscillations */
1516         if (fabs(beta) > 5.0)
1517         {
1518             beta = 0.0;
1519         }
1520
1521
1522         /* update positions */
1523         swap_em_state(&s_min, &s_b);
1524         gpa = gpb;
1525
1526         /* Print it if necessary */
1527         if (MASTER(cr))
1528         {
1529             if (mdrunOptions.verbose)
1530             {
1531                 double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1532                 fprintf(stderr, "\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n", step,
1533                         s_min->epot, s_min->fnorm / sqrtNumAtoms, s_min->fmax, s_min->a_fmax + 1);
1534                 fflush(stderr);
1535             }
1536             /* Store the new (lower) energies */
1537             matrix nullBox = {};
1538             energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
1539                                              enerd, nullptr, nullptr, nullBox, PTCouplingArrays(), 0,
1540                                              nullptr, nullptr, vir, pres, nullptr, mu_tot, constr);
1541
1542             do_log = do_per_step(step, inputrec->nstlog);
1543             do_ene = do_per_step(step, inputrec->nstenergy);
1544
1545             imdSession->fillEnergyRecord(step, TRUE);
1546
1547             if (do_log)
1548             {
1549                 EnergyOutput::printHeader(fplog, step, step);
1550             }
1551             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), do_ene, FALSE, FALSE,
1552                                                do_log ? fplog : nullptr, step, step,
1553                                                fr->fcdata.get(), nullptr);
1554         }
1555
1556         /* Send energies and positions to the IMD client if bIMD is TRUE. */
1557         if (MASTER(cr) && imdSession->run(step, TRUE, state_global->box, state_global->x.rvec_array(), 0))
1558         {
1559             imdSession->sendPositionsAndEnergies();
1560         }
1561
1562         /* Stop when the maximum force lies below tolerance.
1563          * If we have reached machine precision, converged is already set to true.
1564          */
1565         converged = converged || (s_min->fmax < inputrec->em_tol);
1566
1567     } /* End of the loop */
1568
1569     if (converged)
1570     {
1571         step--; /* we never took that last step in this case */
1572     }
1573     if (s_min->fmax > inputrec->em_tol)
1574     {
1575         if (MASTER(cr))
1576         {
1577             warn_step(fplog, inputrec->em_tol, s_min->fmax, step - 1 == number_steps, FALSE);
1578         }
1579         converged = FALSE;
1580     }
1581
1582     if (MASTER(cr))
1583     {
1584         /* If we printed energy and/or logfile last step (which was the last step)
1585          * we don't have to do it again, but otherwise print the final values.
1586          */
1587         if (!do_log)
1588         {
1589             /* Write final value to log since we didn't do anything the last step */
1590             EnergyOutput::printHeader(fplog, step, step);
1591         }
1592         if (!do_ene || !do_log)
1593         {
1594             /* Write final energy file entries */
1595             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), !do_ene, FALSE, FALSE,
1596                                                !do_log ? fplog : nullptr, step, step,
1597                                                fr->fcdata.get(), nullptr);
1598         }
1599     }
1600
1601     /* Print some stuff... */
1602     if (MASTER(cr))
1603     {
1604         fprintf(stderr, "\nwriting lowest energy coordinates.\n");
1605     }
1606
1607     /* IMPORTANT!
1608      * For accurate normal mode calculation it is imperative that we
1609      * store the last conformation into the full precision binary trajectory.
1610      *
1611      * However, we should only do it if we did NOT already write this step
1612      * above (which we did if do_x or do_f was true).
1613      */
1614     /* Note that with 0 < nstfout != nstxout we can end up with two frames
1615      * in the trajectory with the same step number.
1616      */
1617     do_x = !do_per_step(step, inputrec->nstxout);
1618     do_f = (inputrec->nstfout > 0 && !do_per_step(step, inputrec->nstfout));
1619
1620     write_em_traj(fplog, cr, outf, do_x, do_f, ftp2fn(efSTO, nfile, fnm), top_global, inputrec,
1621                   step, s_min, state_global, observablesHistory);
1622
1623
1624     if (MASTER(cr))
1625     {
1626         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1627         print_converged(stderr, CG, inputrec->em_tol, step, converged, number_steps, s_min, sqrtNumAtoms);
1628         print_converged(fplog, CG, inputrec->em_tol, step, converged, number_steps, s_min, sqrtNumAtoms);
1629
1630         fprintf(fplog, "\nPerformed %d energy evaluations in total.\n", neval);
1631     }
1632
1633     finish_em(cr, outf, walltime_accounting, wcycle);
1634
1635     /* To print the actual number of steps we needed somewhere */
1636     walltime_accounting_set_nsteps_done(walltime_accounting, step);
1637 }
1638
1639
1640 void LegacySimulator::do_lbfgs()
1641 {
1642     static const char* LBFGS = "Low-Memory BFGS Minimizer";
1643     em_state_t         ems;
1644     gmx_localtop_t     top(top_global->ffparams);
1645     gmx_global_stat_t  gstat;
1646     int                ncorr, nmaxcorr, point, cp, neval, nminstep;
1647     double             stepsize, step_taken, gpa, gpb, gpc, tmp, minstep;
1648     real *             rho, *alpha, *p, *s, **dx, **dg;
1649     real               a, b, c, maxdelta, delta;
1650     real               diag, Epot0;
1651     real               dgdx, dgdg, sq, yr, beta;
1652     gmx_bool           converged;
1653     rvec               mu_tot = { 0 };
1654     gmx_bool           do_log, do_ene, do_x, do_f, foundlower, *frozen;
1655     tensor             vir, pres;
1656     int                start, end, number_steps;
1657     int                i, k, m, n, gf, step;
1658     int                mdof_flags;
1659     auto               mdatoms = mdAtoms->mdatoms();
1660
1661     GMX_LOG(mdlog.info)
1662             .asParagraph()
1663             .appendText(
1664                     "Note that activating L-BFGS energy minimization via the "
1665                     "integrator .mdp option and the command gmx mdrun may "
1666                     "be available in a different form in a future version of GROMACS, "
1667                     "e.g. gmx minimize and an .mdp option.");
1668
1669     if (PAR(cr))
1670     {
1671         gmx_fatal(FARGS, "L-BFGS minimization only supports a single rank");
1672     }
1673
1674     if (nullptr != constr)
1675     {
1676         gmx_fatal(
1677                 FARGS,
1678                 "The combination of constraints and L-BFGS minimization is not implemented. Either "
1679                 "do not use constraints, or use another minimizer (e.g. steepest descent).");
1680     }
1681
1682     n        = 3 * state_global->natoms;
1683     nmaxcorr = inputrec->nbfgscorr;
1684
1685     snew(frozen, n);
1686
1687     snew(p, n);
1688     snew(rho, nmaxcorr);
1689     snew(alpha, nmaxcorr);
1690
1691     snew(dx, nmaxcorr);
1692     for (i = 0; i < nmaxcorr; i++)
1693     {
1694         snew(dx[i], n);
1695     }
1696
1697     snew(dg, nmaxcorr);
1698     for (i = 0; i < nmaxcorr; i++)
1699     {
1700         snew(dg[i], n);
1701     }
1702
1703     step  = 0;
1704     neval = 0;
1705
1706     /* Init em */
1707     init_em(fplog, mdlog, LBFGS, cr, inputrec, imdSession, pull_work, state_global, top_global,
1708             &ems, &top, nrnb, fr, mdAtoms, &gstat, vsite, constr, nullptr);
1709     const bool        simulationsShareState = false;
1710     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
1711                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
1712                                    StartingBehavior::NewSimulation, simulationsShareState, ms);
1713     gmx::EnergyOutput energyOutput(mdoutf_get_fp_ene(outf), top_global, inputrec, pull_work, nullptr,
1714                                    false, StartingBehavior::NewSimulation, mdModulesNotifier);
1715
1716     start = 0;
1717     end   = mdatoms->homenr;
1718
1719     /* We need 4 working states */
1720     em_state_t  s0{}, s1{}, s2{}, s3{};
1721     em_state_t* sa   = &s0;
1722     em_state_t* sb   = &s1;
1723     em_state_t* sc   = &s2;
1724     em_state_t* last = &s3;
1725     /* Initialize by copying the state from ems (we could skip x and f here) */
1726     *sa = ems;
1727     *sb = ems;
1728     *sc = ems;
1729
1730     /* Print to log file */
1731     print_em_start(fplog, cr, walltime_accounting, wcycle, LBFGS);
1732
1733     do_log = do_ene = do_x = do_f = TRUE;
1734
1735     /* Max number of steps */
1736     number_steps = inputrec->nsteps;
1737
1738     /* Create a 3*natoms index to tell whether each degree of freedom is frozen */
1739     gf = 0;
1740     for (i = start; i < end; i++)
1741     {
1742         if (mdatoms->cFREEZE)
1743         {
1744             gf = mdatoms->cFREEZE[i];
1745         }
1746         for (m = 0; m < DIM; m++)
1747         {
1748             frozen[3 * i + m] = (inputrec->opts.nFreeze[gf][m] != 0);
1749         }
1750     }
1751     if (MASTER(cr))
1752     {
1753         sp_header(stderr, LBFGS, inputrec->em_tol, number_steps);
1754     }
1755     if (fplog)
1756     {
1757         sp_header(fplog, LBFGS, inputrec->em_tol, number_steps);
1758     }
1759
1760     if (vsite)
1761     {
1762         vsite->construct(state_global->x, 1, {}, state_global->box);
1763     }
1764
1765     /* Call the force routine and some auxiliary (neighboursearching etc.) */
1766     /* do_force always puts the charge groups in the box and shifts again
1767      * We do not unshift, so molecules are always whole
1768      */
1769     neval++;
1770     EnergyEvaluator energyEvaluator{ fplog,    mdlog,      cr,        ms,   top_global,      &top,
1771                                      inputrec, imdSession, pull_work, nrnb, wcycle,          gstat,
1772                                      vsite,    constr,     mdAtoms,   fr,   runScheduleWork, enerd };
1773     energyEvaluator.run(&ems, mu_tot, vir, pres, -1, TRUE);
1774
1775     if (MASTER(cr))
1776     {
1777         /* Copy stuff to the energy bin for easy printing etc. */
1778         matrix nullBox = {};
1779         energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
1780                                          enerd, nullptr, nullptr, nullBox, PTCouplingArrays(), 0,
1781                                          nullptr, nullptr, vir, pres, nullptr, mu_tot, constr);
1782
1783         EnergyOutput::printHeader(fplog, step, step);
1784         energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step,
1785                                            step, fr->fcdata.get(), nullptr);
1786     }
1787
1788     /* Set the initial step.
1789      * since it will be multiplied by the non-normalized search direction
1790      * vector (force vector the first time), we scale it by the
1791      * norm of the force.
1792      */
1793
1794     if (MASTER(cr))
1795     {
1796         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1797         fprintf(stderr, "Using %d BFGS correction steps.\n\n", nmaxcorr);
1798         fprintf(stderr, "   F-max             = %12.5e on atom %d\n", ems.fmax, ems.a_fmax + 1);
1799         fprintf(stderr, "   F-Norm            = %12.5e\n", ems.fnorm / sqrtNumAtoms);
1800         fprintf(stderr, "\n");
1801         /* and copy to the log file too... */
1802         fprintf(fplog, "Using %d BFGS correction steps.\n\n", nmaxcorr);
1803         fprintf(fplog, "   F-max             = %12.5e on atom %d\n", ems.fmax, ems.a_fmax + 1);
1804         fprintf(fplog, "   F-Norm            = %12.5e\n", ems.fnorm / sqrtNumAtoms);
1805         fprintf(fplog, "\n");
1806     }
1807
1808     // Point is an index to the memory of search directions, where 0 is the first one.
1809     point = 0;
1810
1811     // Set initial search direction to the force (-gradient), or 0 for frozen particles.
1812     real* fInit = static_cast<real*>(ems.f.view().force().data()[0]);
1813     for (i = 0; i < n; i++)
1814     {
1815         if (!frozen[i])
1816         {
1817             dx[point][i] = fInit[i]; /* Initial search direction */
1818         }
1819         else
1820         {
1821             dx[point][i] = 0;
1822         }
1823     }
1824
1825     // Stepsize will be modified during the search, and actually it is not critical
1826     // (the main efficiency in the algorithm comes from changing directions), but
1827     // we still need an initial value, so estimate it as the inverse of the norm
1828     // so we take small steps where the potential fluctuates a lot.
1829     stepsize = 1.0 / ems.fnorm;
1830
1831     /* Start the loop over BFGS steps.
1832      * Each successful step is counted, and we continue until
1833      * we either converge or reach the max number of steps.
1834      */
1835
1836     ncorr = 0;
1837
1838     /* Set the gradient from the force */
1839     converged = FALSE;
1840     for (step = 0; (number_steps < 0 || step <= number_steps) && !converged; step++)
1841     {
1842
1843         /* Write coordinates if necessary */
1844         do_x = do_per_step(step, inputrec->nstxout);
1845         do_f = do_per_step(step, inputrec->nstfout);
1846
1847         mdof_flags = 0;
1848         if (do_x)
1849         {
1850             mdof_flags |= MDOF_X;
1851         }
1852
1853         if (do_f)
1854         {
1855             mdof_flags |= MDOF_F;
1856         }
1857
1858         if (inputrec->bIMD)
1859         {
1860             mdof_flags |= MDOF_IMD;
1861         }
1862
1863         gmx::WriteCheckpointDataHolder checkpointDataHolder;
1864         mdoutf_write_to_trajectory_files(fplog, cr, outf, mdof_flags, top_global->natoms, step,
1865                                          static_cast<real>(step), &ems.s, state_global, observablesHistory,
1866                                          ems.f.view().force(), &checkpointDataHolder);
1867
1868         /* Do the linesearching in the direction dx[point][0..(n-1)] */
1869
1870         /* make s a pointer to current search direction - point=0 first time we get here */
1871         s = dx[point];
1872
1873         real* xx = static_cast<real*>(ems.s.x.rvec_array()[0]);
1874         real* ff = static_cast<real*>(ems.f.view().force().data()[0]);
1875
1876         // calculate line gradient in position A
1877         for (gpa = 0, i = 0; i < n; i++)
1878         {
1879             gpa -= s[i] * ff[i];
1880         }
1881
1882         /* Calculate minimum allowed stepsize along the line, before the average (norm)
1883          * relative change in coordinate is smaller than precision
1884          */
1885         for (minstep = 0, i = 0; i < n; i++)
1886         {
1887             tmp = fabs(xx[i]);
1888             if (tmp < 1.0)
1889             {
1890                 tmp = 1.0;
1891             }
1892             tmp = s[i] / tmp;
1893             minstep += tmp * tmp;
1894         }
1895         minstep = GMX_REAL_EPS / sqrt(minstep / n);
1896
1897         if (stepsize < minstep)
1898         {
1899             converged = TRUE;
1900             break;
1901         }
1902
1903         // Before taking any steps along the line, store the old position
1904         *last       = ems;
1905         real* lastx = static_cast<real*>(last->s.x.data()[0]);
1906         real* lastf = static_cast<real*>(last->f.view().force().data()[0]);
1907         Epot0       = ems.epot;
1908
1909         *sa = ems;
1910
1911         /* Take a step downhill.
1912          * In theory, we should find the actual minimum of the function in this
1913          * direction, somewhere along the line.
1914          * That is quite possible, but it turns out to take 5-10 function evaluations
1915          * for each line. However, we dont really need to find the exact minimum -
1916          * it is much better to start a new BFGS step in a modified direction as soon
1917          * as we are close to it. This will save a lot of energy evaluations.
1918          *
1919          * In practice, we just try to take a single step.
1920          * If it worked (i.e. lowered the energy), we increase the stepsize but
1921          * continue straight to the next BFGS step without trying to find any minimum,
1922          * i.e. we change the search direction too. If the line was smooth, it is
1923          * likely we are in a smooth region, and then it makes sense to take longer
1924          * steps in the modified search direction too.
1925          *
1926          * If it didn't work (higher energy), there must be a minimum somewhere between
1927          * the old position and the new one. Then we need to start by finding a lower
1928          * value before we change search direction. Since the energy was apparently
1929          * quite rough, we need to decrease the step size.
1930          *
1931          * Due to the finite numerical accuracy, it turns out that it is a good idea
1932          * to accept a SMALL increase in energy, if the derivative is still downhill.
1933          * This leads to lower final energies in the tests I've done. / Erik
1934          */
1935
1936         // State "A" is the first position along the line.
1937         // reference position along line is initially zero
1938         a = 0.0;
1939
1940         // Check stepsize first. We do not allow displacements
1941         // larger than emstep.
1942         //
1943         do
1944         {
1945             // Pick a new position C by adding stepsize to A.
1946             c = a + stepsize;
1947
1948             // Calculate what the largest change in any individual coordinate
1949             // would be (translation along line * gradient along line)
1950             maxdelta = 0;
1951             for (i = 0; i < n; i++)
1952             {
1953                 delta = c * s[i];
1954                 if (delta > maxdelta)
1955                 {
1956                     maxdelta = delta;
1957                 }
1958             }
1959             // If any displacement is larger than the stepsize limit, reduce the step
1960             if (maxdelta > inputrec->em_stepsize)
1961             {
1962                 stepsize *= 0.1;
1963             }
1964         } while (maxdelta > inputrec->em_stepsize);
1965
1966         // Take a trial step and move the coordinate array xc[] to position C
1967         real* xc = static_cast<real*>(sc->s.x.rvec_array()[0]);
1968         for (i = 0; i < n; i++)
1969         {
1970             xc[i] = lastx[i] + c * s[i];
1971         }
1972
1973         neval++;
1974         // Calculate energy for the trial step in position C
1975         energyEvaluator.run(sc, mu_tot, vir, pres, step, FALSE);
1976
1977         // Calc line gradient in position C
1978         real* fc = static_cast<real*>(sc->f.view().force()[0]);
1979         for (gpc = 0, i = 0; i < n; i++)
1980         {
1981             gpc -= s[i] * fc[i]; /* f is negative gradient, thus the sign */
1982         }
1983         /* Sum the gradient along the line across CPUs */
1984         if (PAR(cr))
1985         {
1986             gmx_sumd(1, &gpc, cr);
1987         }
1988
1989         // This is the max amount of increase in energy we tolerate.
1990         // By allowing VERY small changes (close to numerical precision) we
1991         // frequently find even better (lower) final energies.
1992         tmp = std::sqrt(GMX_REAL_EPS) * fabs(sa->epot);
1993
1994         // Accept the step if the energy is lower in the new position C (compared to A),
1995         // or if it is not significantly higher and the line derivative is still negative.
1996         foundlower = sc->epot < sa->epot || (gpc < 0 && sc->epot < (sa->epot + tmp));
1997         // If true, great, we found a better energy. We no longer try to alter the
1998         // stepsize, but simply accept this new better position. The we select a new
1999         // search direction instead, which will be much more efficient than continuing
2000         // to take smaller steps along a line. Set fnorm based on the new C position,
2001         // which will be used to update the stepsize to 1/fnorm further down.
2002
2003         // If false, the energy is NOT lower in point C, i.e. it will be the same
2004         // or higher than in point A. In this case it is pointless to move to point C,
2005         // so we will have to do more iterations along the same line to find a smaller
2006         // value in the interval [A=0.0,C].
2007         // Here, A is still 0.0, but that will change when we do a search in the interval
2008         // [0.0,C] below. That search we will do by interpolation or bisection rather
2009         // than with the stepsize, so no need to modify it. For the next search direction
2010         // it will be reset to 1/fnorm anyway.
2011
2012         if (!foundlower)
2013         {
2014             // OK, if we didn't find a lower value we will have to locate one now - there must
2015             // be one in the interval [a,c].
2016             // The same thing is valid here, though: Don't spend dozens of iterations to find
2017             // the line minimum. We try to interpolate based on the derivative at the endpoints,
2018             // and only continue until we find a lower value. In most cases this means 1-2 iterations.
2019             // I also have a safeguard for potentially really pathological functions so we never
2020             // take more than 20 steps before we give up.
2021             // If we already found a lower value we just skip this step and continue to the update.
2022             real fnorm = 0;
2023             nminstep   = 0;
2024             do
2025             {
2026                 // Select a new trial point B in the interval [A,C].
2027                 // If the derivatives at points a & c have different sign we interpolate to zero,
2028                 // otherwise just do a bisection since there might be multiple minima/maxima
2029                 // inside the interval.
2030                 if (gpa < 0 && gpc > 0)
2031                 {
2032                     b = a + gpa * (a - c) / (gpc - gpa);
2033                 }
2034                 else
2035                 {
2036                     b = 0.5 * (a + c);
2037                 }
2038
2039                 /* safeguard if interpolation close to machine accuracy causes errors:
2040                  * never go outside the interval
2041                  */
2042                 if (b <= a || b >= c)
2043                 {
2044                     b = 0.5 * (a + c);
2045                 }
2046
2047                 // Take a trial step to point B
2048                 real* xb = static_cast<real*>(sb->s.x.rvec_array()[0]);
2049                 for (i = 0; i < n; i++)
2050                 {
2051                     xb[i] = lastx[i] + b * s[i];
2052                 }
2053
2054                 neval++;
2055                 // Calculate energy for the trial step in point B
2056                 energyEvaluator.run(sb, mu_tot, vir, pres, step, FALSE);
2057                 fnorm = sb->fnorm;
2058
2059                 // Calculate gradient in point B
2060                 real* fb = static_cast<real*>(sb->f.view().force()[0]);
2061                 for (gpb = 0, i = 0; i < n; i++)
2062                 {
2063                     gpb -= s[i] * fb[i]; /* f is negative gradient, thus the sign */
2064                 }
2065                 /* Sum the gradient along the line across CPUs */
2066                 if (PAR(cr))
2067                 {
2068                     gmx_sumd(1, &gpb, cr);
2069                 }
2070
2071                 // Keep one of the intervals [A,B] or [B,C] based on the value of the derivative
2072                 // at the new point B, and rename the endpoints of this new interval A and C.
2073                 if (gpb > 0)
2074                 {
2075                     /* Replace c endpoint with b */
2076                     c = b;
2077                     /* copy state b to c */
2078                     *sc = *sb;
2079                 }
2080                 else
2081                 {
2082                     /* Replace a endpoint with b */
2083                     a = b;
2084                     /* copy state b to a */
2085                     *sa = *sb;
2086                 }
2087
2088                 /*
2089                  * Stop search as soon as we find a value smaller than the endpoints,
2090                  * or if the tolerance is below machine precision.
2091                  * Never run more than 20 steps, no matter what.
2092                  */
2093                 nminstep++;
2094             } while ((sb->epot > sa->epot || sb->epot > sc->epot) && (nminstep < 20));
2095
2096             if (std::fabs(sb->epot - Epot0) < GMX_REAL_EPS || nminstep >= 20)
2097             {
2098                 /* OK. We couldn't find a significantly lower energy.
2099                  * If ncorr==0 this was steepest descent, and then we give up.
2100                  * If not, reset memory to restart as steepest descent before quitting.
2101                  */
2102                 if (ncorr == 0)
2103                 {
2104                     /* Converged */
2105                     converged = TRUE;
2106                     break;
2107                 }
2108                 else
2109                 {
2110                     /* Reset memory */
2111                     ncorr = 0;
2112                     /* Search in gradient direction */
2113                     for (i = 0; i < n; i++)
2114                     {
2115                         dx[point][i] = ff[i];
2116                     }
2117                     /* Reset stepsize */
2118                     stepsize = 1.0 / fnorm;
2119                     continue;
2120                 }
2121             }
2122
2123             /* Select min energy state of A & C, put the best in xx/ff/Epot
2124              */
2125             if (sc->epot < sa->epot)
2126             {
2127                 /* Use state C */
2128                 ems        = *sc;
2129                 step_taken = c;
2130             }
2131             else
2132             {
2133                 /* Use state A */
2134                 ems        = *sa;
2135                 step_taken = a;
2136             }
2137         }
2138         else
2139         {
2140             /* found lower */
2141             /* Use state C */
2142             ems        = *sc;
2143             step_taken = c;
2144         }
2145
2146         /* Update the memory information, and calculate a new
2147          * approximation of the inverse hessian
2148          */
2149
2150         /* Have new data in Epot, xx, ff */
2151         if (ncorr < nmaxcorr)
2152         {
2153             ncorr++;
2154         }
2155
2156         for (i = 0; i < n; i++)
2157         {
2158             dg[point][i] = lastf[i] - ff[i];
2159             dx[point][i] *= step_taken;
2160         }
2161
2162         dgdg = 0;
2163         dgdx = 0;
2164         for (i = 0; i < n; i++)
2165         {
2166             dgdg += dg[point][i] * dg[point][i];
2167             dgdx += dg[point][i] * dx[point][i];
2168         }
2169
2170         diag = dgdx / dgdg;
2171
2172         rho[point] = 1.0 / dgdx;
2173         point++;
2174
2175         if (point >= nmaxcorr)
2176         {
2177             point = 0;
2178         }
2179
2180         /* Update */
2181         for (i = 0; i < n; i++)
2182         {
2183             p[i] = ff[i];
2184         }
2185
2186         cp = point;
2187
2188         /* Recursive update. First go back over the memory points */
2189         for (k = 0; k < ncorr; k++)
2190         {
2191             cp--;
2192             if (cp < 0)
2193             {
2194                 cp = ncorr - 1;
2195             }
2196
2197             sq = 0;
2198             for (i = 0; i < n; i++)
2199             {
2200                 sq += dx[cp][i] * p[i];
2201             }
2202
2203             alpha[cp] = rho[cp] * sq;
2204
2205             for (i = 0; i < n; i++)
2206             {
2207                 p[i] -= alpha[cp] * dg[cp][i];
2208             }
2209         }
2210
2211         for (i = 0; i < n; i++)
2212         {
2213             p[i] *= diag;
2214         }
2215
2216         /* And then go forward again */
2217         for (k = 0; k < ncorr; k++)
2218         {
2219             yr = 0;
2220             for (i = 0; i < n; i++)
2221             {
2222                 yr += p[i] * dg[cp][i];
2223             }
2224
2225             beta = rho[cp] * yr;
2226             beta = alpha[cp] - beta;
2227
2228             for (i = 0; i < n; i++)
2229             {
2230                 p[i] += beta * dx[cp][i];
2231             }
2232
2233             cp++;
2234             if (cp >= ncorr)
2235             {
2236                 cp = 0;
2237             }
2238         }
2239
2240         for (i = 0; i < n; i++)
2241         {
2242             if (!frozen[i])
2243             {
2244                 dx[point][i] = p[i];
2245             }
2246             else
2247             {
2248                 dx[point][i] = 0;
2249             }
2250         }
2251
2252         /* Print it if necessary */
2253         if (MASTER(cr))
2254         {
2255             if (mdrunOptions.verbose)
2256             {
2257                 double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
2258                 fprintf(stderr, "\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n", step,
2259                         ems.epot, ems.fnorm / sqrtNumAtoms, ems.fmax, ems.a_fmax + 1);
2260                 fflush(stderr);
2261             }
2262             /* Store the new (lower) energies */
2263             matrix nullBox = {};
2264             energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
2265                                              enerd, nullptr, nullptr, nullBox, PTCouplingArrays(), 0,
2266                                              nullptr, nullptr, vir, pres, nullptr, mu_tot, constr);
2267
2268             do_log = do_per_step(step, inputrec->nstlog);
2269             do_ene = do_per_step(step, inputrec->nstenergy);
2270
2271             imdSession->fillEnergyRecord(step, TRUE);
2272
2273             if (do_log)
2274             {
2275                 EnergyOutput::printHeader(fplog, step, step);
2276             }
2277             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), do_ene, FALSE, FALSE,
2278                                                do_log ? fplog : nullptr, step, step,
2279                                                fr->fcdata.get(), nullptr);
2280         }
2281
2282         /* Send x and E to IMD client, if bIMD is TRUE. */
2283         if (imdSession->run(step, TRUE, state_global->box, state_global->x.rvec_array(), 0) && MASTER(cr))
2284         {
2285             imdSession->sendPositionsAndEnergies();
2286         }
2287
2288         // Reset stepsize in we are doing more iterations
2289         stepsize = 1.0;
2290
2291         /* Stop when the maximum force lies below tolerance.
2292          * If we have reached machine precision, converged is already set to true.
2293          */
2294         converged = converged || (ems.fmax < inputrec->em_tol);
2295
2296     } /* End of the loop */
2297
2298     if (converged)
2299     {
2300         step--; /* we never took that last step in this case */
2301     }
2302     if (ems.fmax > inputrec->em_tol)
2303     {
2304         if (MASTER(cr))
2305         {
2306             warn_step(fplog, inputrec->em_tol, ems.fmax, step - 1 == number_steps, FALSE);
2307         }
2308         converged = FALSE;
2309     }
2310
2311     /* If we printed energy and/or logfile last step (which was the last step)
2312      * we don't have to do it again, but otherwise print the final values.
2313      */
2314     if (!do_log) /* Write final value to log since we didn't do anythin last step */
2315     {
2316         EnergyOutput::printHeader(fplog, step, step);
2317     }
2318     if (!do_ene || !do_log) /* Write final energy file entries */
2319     {
2320         energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), !do_ene, FALSE, FALSE,
2321                                            !do_log ? fplog : nullptr, step, step, fr->fcdata.get(),
2322                                            nullptr);
2323     }
2324
2325     /* Print some stuff... */
2326     if (MASTER(cr))
2327     {
2328         fprintf(stderr, "\nwriting lowest energy coordinates.\n");
2329     }
2330
2331     /* IMPORTANT!
2332      * For accurate normal mode calculation it is imperative that we
2333      * store the last conformation into the full precision binary trajectory.
2334      *
2335      * However, we should only do it if we did NOT already write this step
2336      * above (which we did if do_x or do_f was true).
2337      */
2338     do_x = !do_per_step(step, inputrec->nstxout);
2339     do_f = !do_per_step(step, inputrec->nstfout);
2340     write_em_traj(fplog, cr, outf, do_x, do_f, ftp2fn(efSTO, nfile, fnm), top_global, inputrec,
2341                   step, &ems, state_global, observablesHistory);
2342
2343     if (MASTER(cr))
2344     {
2345         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
2346         print_converged(stderr, LBFGS, inputrec->em_tol, step, converged, number_steps, &ems, sqrtNumAtoms);
2347         print_converged(fplog, LBFGS, inputrec->em_tol, step, converged, number_steps, &ems, sqrtNumAtoms);
2348
2349         fprintf(fplog, "\nPerformed %d energy evaluations in total.\n", neval);
2350     }
2351
2352     finish_em(cr, outf, walltime_accounting, wcycle);
2353
2354     /* To print the actual number of steps we needed somewhere */
2355     walltime_accounting_set_nsteps_done(walltime_accounting, step);
2356 }
2357
2358 void LegacySimulator::do_steep()
2359 {
2360     const char*       SD = "Steepest Descents";
2361     gmx_localtop_t    top(top_global->ffparams);
2362     gmx_global_stat_t gstat;
2363     real              stepsize;
2364     real              ustep;
2365     gmx_bool          bDone, bAbort, do_x, do_f;
2366     tensor            vir, pres;
2367     rvec              mu_tot = { 0 };
2368     int               nsteps;
2369     int               count          = 0;
2370     int               steps_accepted = 0;
2371     auto              mdatoms        = mdAtoms->mdatoms();
2372
2373     GMX_LOG(mdlog.info)
2374             .asParagraph()
2375             .appendText(
2376                     "Note that activating steepest-descent energy minimization via the "
2377                     "integrator .mdp option and the command gmx mdrun may "
2378                     "be available in a different form in a future version of GROMACS, "
2379                     "e.g. gmx minimize and an .mdp option.");
2380
2381     /* Create 2 states on the stack and extract pointers that we will swap */
2382     em_state_t  s0{}, s1{};
2383     em_state_t* s_min = &s0;
2384     em_state_t* s_try = &s1;
2385
2386     /* Init em and store the local state in s_try */
2387     init_em(fplog, mdlog, SD, cr, inputrec, imdSession, pull_work, state_global, top_global, s_try,
2388             &top, nrnb, fr, mdAtoms, &gstat, vsite, constr, nullptr);
2389     const bool        simulationsShareState = false;
2390     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
2391                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
2392                                    StartingBehavior::NewSimulation, simulationsShareState, ms);
2393     gmx::EnergyOutput energyOutput(mdoutf_get_fp_ene(outf), top_global, inputrec, pull_work, nullptr,
2394                                    false, StartingBehavior::NewSimulation, mdModulesNotifier);
2395
2396     /* Print to log file  */
2397     print_em_start(fplog, cr, walltime_accounting, wcycle, SD);
2398
2399     /* Set variables for stepsize (in nm). This is the largest
2400      * step that we are going to make in any direction.
2401      */
2402     ustep    = inputrec->em_stepsize;
2403     stepsize = 0;
2404
2405     /* Max number of steps  */
2406     nsteps = inputrec->nsteps;
2407
2408     if (MASTER(cr))
2409     {
2410         /* Print to the screen  */
2411         sp_header(stderr, SD, inputrec->em_tol, nsteps);
2412     }
2413     if (fplog)
2414     {
2415         sp_header(fplog, SD, inputrec->em_tol, nsteps);
2416     }
2417     EnergyEvaluator energyEvaluator{ fplog,    mdlog,      cr,        ms,   top_global,      &top,
2418                                      inputrec, imdSession, pull_work, nrnb, wcycle,          gstat,
2419                                      vsite,    constr,     mdAtoms,   fr,   runScheduleWork, enerd };
2420
2421     /**** HERE STARTS THE LOOP ****
2422      * count is the counter for the number of steps
2423      * bDone will be TRUE when the minimization has converged
2424      * bAbort will be TRUE when nsteps steps have been performed or when
2425      * the stepsize becomes smaller than is reasonable for machine precision
2426      */
2427     count  = 0;
2428     bDone  = FALSE;
2429     bAbort = FALSE;
2430     while (!bDone && !bAbort)
2431     {
2432         bAbort = (nsteps >= 0) && (count == nsteps);
2433
2434         /* set new coordinates, except for first step */
2435         bool validStep = true;
2436         if (count > 0)
2437         {
2438             validStep = do_em_step(cr, inputrec, mdatoms, s_min, stepsize,
2439                                    s_min->f.view().forceWithPadding(), s_try, constr, count);
2440         }
2441
2442         if (validStep)
2443         {
2444             energyEvaluator.run(s_try, mu_tot, vir, pres, count, count == 0);
2445         }
2446         else
2447         {
2448             // Signal constraint error during stepping with energy=inf
2449             s_try->epot = std::numeric_limits<real>::infinity();
2450         }
2451
2452         if (MASTER(cr))
2453         {
2454             EnergyOutput::printHeader(fplog, count, count);
2455         }
2456
2457         if (count == 0)
2458         {
2459             s_min->epot = s_try->epot;
2460         }
2461
2462         /* Print it if necessary  */
2463         if (MASTER(cr))
2464         {
2465             if (mdrunOptions.verbose)
2466             {
2467                 fprintf(stderr, "Step=%5d, Dmax= %6.1e nm, Epot= %12.5e Fmax= %11.5e, atom= %d%c",
2468                         count, ustep, s_try->epot, s_try->fmax, s_try->a_fmax + 1,
2469                         ((count == 0) || (s_try->epot < s_min->epot)) ? '\n' : '\r');
2470                 fflush(stderr);
2471             }
2472
2473             if ((count == 0) || (s_try->epot < s_min->epot))
2474             {
2475                 /* Store the new (lower) energies  */
2476                 matrix nullBox = {};
2477                 energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(count),
2478                                                  mdatoms->tmass, enerd, nullptr, nullptr, nullBox,
2479                                                  PTCouplingArrays(), 0, nullptr, nullptr, vir, pres,
2480                                                  nullptr, mu_tot, constr);
2481
2482                 imdSession->fillEnergyRecord(count, TRUE);
2483
2484                 const bool do_dr = do_per_step(steps_accepted, inputrec->nstdisreout);
2485                 const bool do_or = do_per_step(steps_accepted, inputrec->nstorireout);
2486                 energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), TRUE, do_dr, do_or,
2487                                                    fplog, count, count, fr->fcdata.get(), nullptr);
2488                 fflush(fplog);
2489             }
2490         }
2491
2492         /* Now if the new energy is smaller than the previous...
2493          * or if this is the first step!
2494          * or if we did random steps!
2495          */
2496
2497         if ((count == 0) || (s_try->epot < s_min->epot))
2498         {
2499             steps_accepted++;
2500
2501             /* Test whether the convergence criterion is met...  */
2502             bDone = (s_try->fmax < inputrec->em_tol);
2503
2504             /* Copy the arrays for force, positions and energy  */
2505             /* The 'Min' array always holds the coords and forces of the minimal
2506                sampled energy  */
2507             swap_em_state(&s_min, &s_try);
2508             if (count > 0)
2509             {
2510                 ustep *= 1.2;
2511             }
2512
2513             /* Write to trn, if necessary */
2514             do_x = do_per_step(steps_accepted, inputrec->nstxout);
2515             do_f = do_per_step(steps_accepted, inputrec->nstfout);
2516             write_em_traj(fplog, cr, outf, do_x, do_f, nullptr, top_global, inputrec, count, s_min,
2517                           state_global, observablesHistory);
2518         }
2519         else
2520         {
2521             /* If energy is not smaller make the step smaller...  */
2522             ustep *= 0.5;
2523
2524             if (DOMAINDECOMP(cr) && s_min->s.ddp_count != cr->dd->ddp_count)
2525             {
2526                 /* Reload the old state */
2527                 em_dd_partition_system(fplog, mdlog, count, cr, top_global, inputrec, imdSession,
2528                                        pull_work, s_min, &top, mdAtoms, fr, vsite, constr, nrnb, wcycle);
2529             }
2530         }
2531
2532         // If the force is very small after finishing minimization,
2533         // we risk dividing by zero when calculating the step size.
2534         // So we check first if the minimization has stopped before
2535         // trying to obtain a new step size.
2536         if (!bDone)
2537         {
2538             /* Determine new step  */
2539             stepsize = ustep / s_min->fmax;
2540         }
2541
2542         /* Check if stepsize is too small, with 1 nm as a characteristic length */
2543 #if GMX_DOUBLE
2544         if (count == nsteps || ustep < 1e-12)
2545 #else
2546         if (count == nsteps || ustep < 1e-6)
2547 #endif
2548         {
2549             if (MASTER(cr))
2550             {
2551                 warn_step(fplog, inputrec->em_tol, s_min->fmax, count == nsteps, constr != nullptr);
2552             }
2553             bAbort = TRUE;
2554         }
2555
2556         /* Send IMD energies and positions, if bIMD is TRUE. */
2557         if (imdSession->run(count, TRUE, MASTER(cr) ? state_global->box : nullptr,
2558                             MASTER(cr) ? state_global->x.rvec_array() : nullptr, 0)
2559             && MASTER(cr))
2560         {
2561             imdSession->sendPositionsAndEnergies();
2562         }
2563
2564         count++;
2565     } /* End of the loop  */
2566
2567     /* Print some data...  */
2568     if (MASTER(cr))
2569     {
2570         fprintf(stderr, "\nwriting lowest energy coordinates.\n");
2571     }
2572     write_em_traj(fplog, cr, outf, TRUE, inputrec->nstfout != 0, ftp2fn(efSTO, nfile, fnm),
2573                   top_global, inputrec, count, s_min, state_global, observablesHistory);
2574
2575     if (MASTER(cr))
2576     {
2577         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
2578
2579         print_converged(stderr, SD, inputrec->em_tol, count, bDone, nsteps, s_min, sqrtNumAtoms);
2580         print_converged(fplog, SD, inputrec->em_tol, count, bDone, nsteps, s_min, sqrtNumAtoms);
2581     }
2582
2583     finish_em(cr, outf, walltime_accounting, wcycle);
2584
2585     /* To print the actual number of steps we needed somewhere */
2586     inputrec->nsteps = count;
2587
2588     walltime_accounting_set_nsteps_done(walltime_accounting, count);
2589 }
2590
2591 void LegacySimulator::do_nm()
2592 {
2593     const char*         NM = "Normal Mode Analysis";
2594     int                 nnodes;
2595     gmx_localtop_t      top(top_global->ffparams);
2596     gmx_global_stat_t   gstat;
2597     tensor              vir, pres;
2598     rvec                mu_tot = { 0 };
2599     rvec*               dfdx;
2600     gmx_bool            bSparse; /* use sparse matrix storage format */
2601     size_t              sz;
2602     gmx_sparsematrix_t* sparse_matrix = nullptr;
2603     real*               full_matrix   = nullptr;
2604
2605     /* added with respect to mdrun */
2606     int  row, col;
2607     real der_range = 10.0 * std::sqrt(GMX_REAL_EPS);
2608     real x_min;
2609     bool bIsMaster = MASTER(cr);
2610     auto mdatoms   = mdAtoms->mdatoms();
2611
2612     GMX_LOG(mdlog.info)
2613             .asParagraph()
2614             .appendText(
2615                     "Note that activating normal-mode analysis via the integrator "
2616                     ".mdp option and the command gmx mdrun may "
2617                     "be available in a different form in a future version of GROMACS, "
2618                     "e.g. gmx normal-modes.");
2619
2620     if (constr != nullptr)
2621     {
2622         gmx_fatal(
2623                 FARGS,
2624                 "Constraints present with Normal Mode Analysis, this combination is not supported");
2625     }
2626
2627     gmx_shellfc_t* shellfc;
2628
2629     em_state_t state_work{};
2630
2631     /* Init em and store the local state in state_minimum */
2632     init_em(fplog, mdlog, NM, cr, inputrec, imdSession, pull_work, state_global, top_global,
2633             &state_work, &top, nrnb, fr, mdAtoms, &gstat, vsite, constr, &shellfc);
2634     const bool  simulationsShareState = false;
2635     gmx_mdoutf* outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
2636                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
2637                                    StartingBehavior::NewSimulation, simulationsShareState, ms);
2638
2639     std::vector<int>       atom_index = get_atom_index(top_global);
2640     std::vector<gmx::RVec> fneg(atom_index.size(), { 0, 0, 0 });
2641     snew(dfdx, atom_index.size());
2642
2643 #if !GMX_DOUBLE
2644     if (bIsMaster)
2645     {
2646         fprintf(stderr,
2647                 "NOTE: This version of GROMACS has been compiled in single precision,\n"
2648                 "      which MIGHT not be accurate enough for normal mode analysis.\n"
2649                 "      GROMACS now uses sparse matrix storage, so the memory requirements\n"
2650                 "      are fairly modest even if you recompile in double precision.\n\n");
2651     }
2652 #endif
2653
2654     /* Check if we can/should use sparse storage format.
2655      *
2656      * Sparse format is only useful when the Hessian itself is sparse, which it
2657      * will be when we use a cutoff.
2658      * For small systems (n<1000) it is easier to always use full matrix format, though.
2659      */
2660     if (EEL_FULL(fr->ic->eeltype) || fr->rlist == 0.0)
2661     {
2662         GMX_LOG(mdlog.warning)
2663                 .appendText("Non-cutoff electrostatics used, forcing full Hessian format.");
2664         bSparse = FALSE;
2665     }
2666     else if (atom_index.size() < 1000)
2667     {
2668         GMX_LOG(mdlog.warning)
2669                 .appendTextFormatted("Small system size (N=%zu), using full Hessian format.",
2670                                      atom_index.size());
2671         bSparse = FALSE;
2672     }
2673     else
2674     {
2675         GMX_LOG(mdlog.warning).appendText("Using compressed symmetric sparse Hessian format.");
2676         bSparse = TRUE;
2677     }
2678
2679     /* Number of dimensions, based on real atoms, that is not vsites or shell */
2680     sz = DIM * atom_index.size();
2681
2682     fprintf(stderr, "Allocating Hessian memory...\n\n");
2683
2684     if (bSparse)
2685     {
2686         sparse_matrix                       = gmx_sparsematrix_init(sz);
2687         sparse_matrix->compressed_symmetric = TRUE;
2688     }
2689     else
2690     {
2691         snew(full_matrix, sz * sz);
2692     }
2693
2694     /* Write start time and temperature */
2695     print_em_start(fplog, cr, walltime_accounting, wcycle, NM);
2696
2697     /* fudge nr of steps to nr of atoms */
2698     inputrec->nsteps = atom_index.size() * 2;
2699
2700     if (bIsMaster)
2701     {
2702         fprintf(stderr, "starting normal mode calculation '%s'\n%" PRId64 " steps.\n\n",
2703                 *(top_global->name), inputrec->nsteps);
2704     }
2705
2706     nnodes = cr->nnodes;
2707
2708     /* Make evaluate_energy do a single node force calculation */
2709     cr->nnodes = 1;
2710     EnergyEvaluator energyEvaluator{ fplog,    mdlog,      cr,        ms,   top_global,      &top,
2711                                      inputrec, imdSession, pull_work, nrnb, wcycle,          gstat,
2712                                      vsite,    constr,     mdAtoms,   fr,   runScheduleWork, enerd };
2713     energyEvaluator.run(&state_work, mu_tot, vir, pres, -1, TRUE);
2714     cr->nnodes = nnodes;
2715
2716     /* if forces are not small, warn user */
2717     get_state_f_norm_max(cr, &(inputrec->opts), mdatoms, &state_work);
2718
2719     GMX_LOG(mdlog.warning).appendTextFormatted("Maximum force:%12.5e", state_work.fmax);
2720     if (state_work.fmax > 1.0e-3)
2721     {
2722         GMX_LOG(mdlog.warning)
2723                 .appendText(
2724                         "The force is probably not small enough to "
2725                         "ensure that you are at a minimum.\n"
2726                         "Be aware that negative eigenvalues may occur\n"
2727                         "when the resulting matrix is diagonalized.");
2728     }
2729
2730     /***********************************************************
2731      *
2732      *      Loop over all pairs in matrix
2733      *
2734      *      do_force called twice. Once with positive and
2735      *      once with negative displacement
2736      *
2737      ************************************************************/
2738
2739     /* Steps are divided one by one over the nodes */
2740     bool bNS          = true;
2741     auto state_work_x = makeArrayRef(state_work.s.x);
2742     auto state_work_f = state_work.f.view().force();
2743     for (index aid = cr->nodeid; aid < ssize(atom_index); aid += nnodes)
2744     {
2745         size_t atom = atom_index[aid];
2746         for (size_t d = 0; d < DIM; d++)
2747         {
2748             int64_t step        = 0;
2749             int     force_flags = GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES;
2750             double  t           = 0;
2751
2752             x_min = state_work_x[atom][d];
2753
2754             for (unsigned int dx = 0; (dx < 2); dx++)
2755             {
2756                 if (dx == 0)
2757                 {
2758                     state_work_x[atom][d] = x_min - der_range;
2759                 }
2760                 else
2761                 {
2762                     state_work_x[atom][d] = x_min + der_range;
2763                 }
2764
2765                 /* Make evaluate_energy do a single node force calculation */
2766                 cr->nnodes = 1;
2767                 if (shellfc)
2768                 {
2769                     /* Now is the time to relax the shells */
2770                     relax_shell_flexcon(fplog, cr, ms, mdrunOptions.verbose, nullptr, step, inputrec,
2771                                         imdSession, pull_work, bNS, force_flags, &top, constr, enerd,
2772                                         state_work.s.natoms, state_work.s.x.arrayRefWithPadding(),
2773                                         state_work.s.v.arrayRefWithPadding(), state_work.s.box,
2774                                         state_work.s.lambda, &state_work.s.hist, &state_work.f.view(),
2775                                         vir, mdatoms, nrnb, wcycle, shellfc, fr, runScheduleWork, t,
2776                                         mu_tot, vsite, DDBalanceRegionHandler(nullptr));
2777                     bNS = false;
2778                     step++;
2779                 }
2780                 else
2781                 {
2782                     energyEvaluator.run(&state_work, mu_tot, vir, pres, aid * 2 + dx, FALSE);
2783                 }
2784
2785                 cr->nnodes = nnodes;
2786
2787                 if (dx == 0)
2788                 {
2789                     std::copy(state_work_f.begin(), state_work_f.begin() + atom_index.size(),
2790                               fneg.begin());
2791                 }
2792             }
2793
2794             /* x is restored to original */
2795             state_work_x[atom][d] = x_min;
2796
2797             for (size_t j = 0; j < atom_index.size(); j++)
2798             {
2799                 for (size_t k = 0; (k < DIM); k++)
2800                 {
2801                     dfdx[j][k] = -(state_work_f[atom_index[j]][k] - fneg[j][k]) / (2 * der_range);
2802                 }
2803             }
2804
2805             if (!bIsMaster)
2806             {
2807 #if GMX_MPI
2808 #    define mpi_type GMX_MPI_REAL
2809                 MPI_Send(dfdx[0], atom_index.size() * DIM, mpi_type, MASTER(cr), cr->nodeid,
2810                          cr->mpi_comm_mygroup);
2811 #endif
2812             }
2813             else
2814             {
2815                 for (index node = 0; (node < nnodes && aid + node < ssize(atom_index)); node++)
2816                 {
2817                     if (node > 0)
2818                     {
2819 #if GMX_MPI
2820                         MPI_Status stat;
2821                         MPI_Recv(dfdx[0], atom_index.size() * DIM, mpi_type, node, node,
2822                                  cr->mpi_comm_mygroup, &stat);
2823 #    undef mpi_type
2824 #endif
2825                     }
2826
2827                     row = (aid + node) * DIM + d;
2828
2829                     for (size_t j = 0; j < atom_index.size(); j++)
2830                     {
2831                         for (size_t k = 0; k < DIM; k++)
2832                         {
2833                             col = j * DIM + k;
2834
2835                             if (bSparse)
2836                             {
2837                                 if (col >= row && dfdx[j][k] != 0.0)
2838                                 {
2839                                     gmx_sparsematrix_increment_value(sparse_matrix, row, col, dfdx[j][k]);
2840                                 }
2841                             }
2842                             else
2843                             {
2844                                 full_matrix[row * sz + col] = dfdx[j][k];
2845                             }
2846                         }
2847                     }
2848                 }
2849             }
2850
2851             if (mdrunOptions.verbose && fplog)
2852             {
2853                 fflush(fplog);
2854             }
2855         }
2856         /* write progress */
2857         if (bIsMaster && mdrunOptions.verbose)
2858         {
2859             fprintf(stderr, "\rFinished step %d out of %td",
2860                     std::min<int>(atom + nnodes, atom_index.size()), ssize(atom_index));
2861             fflush(stderr);
2862         }
2863     }
2864
2865     if (bIsMaster)
2866     {
2867         fprintf(stderr, "\n\nWriting Hessian...\n");
2868         gmx_mtxio_write(ftp2fn(efMTX, nfile, fnm), sz, sz, full_matrix, sparse_matrix);
2869     }
2870
2871     finish_em(cr, outf, walltime_accounting, wcycle);
2872
2873     walltime_accounting_set_nsteps_done(walltime_accounting, atom_index.size() * 2);
2874 }
2875
2876 } // namespace gmx