Add InteractionDefinitions
[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/manage_threading.h"
72 #include "gromacs/math/functions.h"
73 #include "gromacs/math/vec.h"
74 #include "gromacs/mdlib/constr.h"
75 #include "gromacs/mdlib/dispersioncorrection.h"
76 #include "gromacs/mdlib/ebin.h"
77 #include "gromacs/mdlib/enerdata_utils.h"
78 #include "gromacs/mdlib/energyoutput.h"
79 #include "gromacs/mdlib/force.h"
80 #include "gromacs/mdlib/force_flags.h"
81 #include "gromacs/mdlib/forcerec.h"
82 #include "gromacs/mdlib/gmx_omp_nthreads.h"
83 #include "gromacs/mdlib/md_support.h"
84 #include "gromacs/mdlib/mdatoms.h"
85 #include "gromacs/mdlib/stat.h"
86 #include "gromacs/mdlib/tgroup.h"
87 #include "gromacs/mdlib/trajectory_writing.h"
88 #include "gromacs/mdlib/update.h"
89 #include "gromacs/mdlib/vsite.h"
90 #include "gromacs/mdrunutility/handlerestart.h"
91 #include "gromacs/mdrunutility/printtime.h"
92 #include "gromacs/mdtypes/commrec.h"
93 #include "gromacs/mdtypes/forcerec.h"
94 #include "gromacs/mdtypes/inputrec.h"
95 #include "gromacs/mdtypes/interaction_const.h"
96 #include "gromacs/mdtypes/md_enums.h"
97 #include "gromacs/mdtypes/mdatom.h"
98 #include "gromacs/mdtypes/mdrunoptions.h"
99 #include "gromacs/mdtypes/state.h"
100 #include "gromacs/pbcutil/mshift.h"
101 #include "gromacs/pbcutil/pbc.h"
102 #include "gromacs/timing/wallcycle.h"
103 #include "gromacs/timing/walltime_accounting.h"
104 #include "gromacs/topology/mtop_util.h"
105 #include "gromacs/topology/topology.h"
106 #include "gromacs/utility/cstringutil.h"
107 #include "gromacs/utility/exceptions.h"
108 #include "gromacs/utility/fatalerror.h"
109 #include "gromacs/utility/logger.h"
110 #include "gromacs/utility/smalloc.h"
111
112 #include "legacysimulator.h"
113 #include "shellfc.h"
114
115 using gmx::ArrayRef;
116 using gmx::MdrunScheduleWorkload;
117 using gmx::RVec;
118
119 //! Utility structure for manipulating states during EM
120 typedef struct
121 {
122     //! Copy of the global state
123     t_state s;
124     //! Force array
125     PaddedHostVector<gmx::RVec> f;
126     //! Potential energy
127     real epot;
128     //! Norm of the force
129     real fnorm;
130     //! Maximum force
131     real fmax;
132     //! Direction
133     int a_fmax;
134 } em_state_t;
135
136 //! Print the EM starting conditions
137 static void print_em_start(FILE*                     fplog,
138                            const t_commrec*          cr,
139                            gmx_walltime_accounting_t walltime_accounting,
140                            gmx_wallcycle_t           wcycle,
141                            const char*               name)
142 {
143     walltime_accounting_start_time(walltime_accounting);
144     wallcycle_start(wcycle, ewcRUN);
145     print_start(fplog, cr, walltime_accounting, name);
146 }
147
148 //! Stop counting time for EM
149 static void em_time_end(gmx_walltime_accounting_t walltime_accounting, gmx_wallcycle_t wcycle)
150 {
151     wallcycle_stop(wcycle, ewcRUN);
152
153     walltime_accounting_end_time(walltime_accounting);
154 }
155
156 //! Printing a log file and console header
157 static void sp_header(FILE* out, const char* minimizer, real ftol, int nsteps)
158 {
159     fprintf(out, "\n");
160     fprintf(out, "%s:\n", minimizer);
161     fprintf(out, "   Tolerance (Fmax)   = %12.5e\n", ftol);
162     fprintf(out, "   Number of steps    = %12d\n", nsteps);
163 }
164
165 //! Print warning message
166 static void warn_step(FILE* fp, real ftol, real fmax, gmx_bool bLastStep, gmx_bool bConstrain)
167 {
168     constexpr bool realIsDouble = GMX_DOUBLE;
169     char           buffer[2048];
170
171     if (!std::isfinite(fmax))
172     {
173         sprintf(buffer,
174                 "\nEnergy minimization has stopped because the force "
175                 "on at least one atom is not finite. This usually means "
176                 "atoms are overlapping. Modify the input coordinates to "
177                 "remove atom overlap or use soft-core potentials with "
178                 "the free energy code to avoid infinite forces.\n%s",
179                 !realIsDouble ? "You could also be lucky that switching to double precision "
180                                 "is sufficient to obtain finite forces.\n"
181                               : "");
182     }
183     else if (bLastStep)
184     {
185         sprintf(buffer,
186                 "\nEnergy minimization reached the maximum number "
187                 "of steps before the forces reached the requested "
188                 "precision Fmax < %g.\n",
189                 ftol);
190     }
191     else
192     {
193         sprintf(buffer,
194                 "\nEnergy minimization has stopped, but the forces have "
195                 "not converged to the requested precision Fmax < %g (which "
196                 "may not be possible for your system). It stopped "
197                 "because the algorithm tried to make a new step whose size "
198                 "was too small, or there was no change in the energy since "
199                 "last step. Either way, we regard the minimization as "
200                 "converged to within the available machine precision, "
201                 "given your starting configuration and EM parameters.\n%s%s",
202                 ftol,
203                 !realIsDouble ? "\nDouble precision normally gives you higher accuracy, but "
204                                 "this is often not needed for preparing to run molecular "
205                                 "dynamics.\n"
206                               : "",
207                 bConstrain ? "You might need to increase your constraint accuracy, or turn\n"
208                              "off constraints altogether (set constraints = none in mdp file)\n"
209                            : "");
210     }
211
212     fputs(wrap_lines(buffer, 78, 0, FALSE), stderr);
213     fputs(wrap_lines(buffer, 78, 0, FALSE), fp);
214 }
215
216 //! Print message about convergence of the EM
217 static void print_converged(FILE*             fp,
218                             const char*       alg,
219                             real              ftol,
220                             int64_t           count,
221                             gmx_bool          bDone,
222                             int64_t           nsteps,
223                             const em_state_t* ems,
224                             double            sqrtNumAtoms)
225 {
226     char buf[STEPSTRSIZE];
227
228     if (bDone)
229     {
230         fprintf(fp, "\n%s converged to Fmax < %g in %s steps\n", alg, ftol, gmx_step_str(count, buf));
231     }
232     else if (count < nsteps)
233     {
234         fprintf(fp,
235                 "\n%s converged to machine precision in %s steps,\n"
236                 "but did not reach the requested Fmax < %g.\n",
237                 alg, gmx_step_str(count, buf), ftol);
238     }
239     else
240     {
241         fprintf(fp, "\n%s did not converge to Fmax < %g in %s steps.\n", alg, ftol,
242                 gmx_step_str(count, buf));
243     }
244
245 #if GMX_DOUBLE
246     fprintf(fp, "Potential Energy  = %21.14e\n", ems->epot);
247     fprintf(fp, "Maximum force     = %21.14e on atom %d\n", ems->fmax, ems->a_fmax + 1);
248     fprintf(fp, "Norm of force     = %21.14e\n", ems->fnorm / sqrtNumAtoms);
249 #else
250     fprintf(fp, "Potential Energy  = %14.7e\n", ems->epot);
251     fprintf(fp, "Maximum force     = %14.7e on atom %d\n", ems->fmax, ems->a_fmax + 1);
252     fprintf(fp, "Norm of force     = %14.7e\n", ems->fnorm / sqrtNumAtoms);
253 #endif
254 }
255
256 //! Compute the norm and max of the force array in parallel
257 static void get_f_norm_max(const t_commrec* cr,
258                            t_grpopts*       opts,
259                            t_mdatoms*       mdatoms,
260                            const rvec*      f,
261                            real*            fnorm,
262                            real*            fmax,
263                            int*             a_fmax)
264 {
265     double fnorm2, *sum;
266     real   fmax2, fam;
267     int    la_max, a_max, start, end, i, m, gf;
268
269     /* This routine finds the largest force and returns it.
270      * On parallel machines the global max is taken.
271      */
272     fnorm2 = 0;
273     fmax2  = 0;
274     la_max = -1;
275     start  = 0;
276     end    = mdatoms->homenr;
277     if (mdatoms->cFREEZE)
278     {
279         for (i = start; i < end; i++)
280         {
281             gf  = mdatoms->cFREEZE[i];
282             fam = 0;
283             for (m = 0; m < DIM; m++)
284             {
285                 if (!opts->nFreeze[gf][m])
286                 {
287                     fam += gmx::square(f[i][m]);
288                 }
289             }
290             fnorm2 += fam;
291             if (fam > fmax2)
292             {
293                 fmax2  = fam;
294                 la_max = i;
295             }
296         }
297     }
298     else
299     {
300         for (i = start; i < end; i++)
301         {
302             fam = norm2(f[i]);
303             fnorm2 += fam;
304             if (fam > fmax2)
305             {
306                 fmax2  = fam;
307                 la_max = i;
308             }
309         }
310     }
311
312     if (la_max >= 0 && DOMAINDECOMP(cr))
313     {
314         a_max = cr->dd->globalAtomIndices[la_max];
315     }
316     else
317     {
318         a_max = la_max;
319     }
320     if (PAR(cr))
321     {
322         snew(sum, 2 * cr->nnodes + 1);
323         sum[2 * cr->nodeid]     = fmax2;
324         sum[2 * cr->nodeid + 1] = a_max;
325         sum[2 * cr->nnodes]     = fnorm2;
326         gmx_sumd(2 * cr->nnodes + 1, sum, cr);
327         fnorm2 = sum[2 * cr->nnodes];
328         /* Determine the global maximum */
329         for (i = 0; i < cr->nnodes; i++)
330         {
331             if (sum[2 * i] > fmax2)
332             {
333                 fmax2 = sum[2 * i];
334                 a_max = gmx::roundToInt(sum[2 * i + 1]);
335             }
336         }
337         sfree(sum);
338     }
339
340     if (fnorm)
341     {
342         *fnorm = sqrt(fnorm2);
343     }
344     if (fmax)
345     {
346         *fmax = sqrt(fmax2);
347     }
348     if (a_fmax)
349     {
350         *a_fmax = a_max;
351     }
352 }
353
354 //! Compute the norm of the force
355 static void get_state_f_norm_max(const t_commrec* cr, t_grpopts* opts, t_mdatoms* mdatoms, em_state_t* ems)
356 {
357     get_f_norm_max(cr, opts, mdatoms, ems->f.rvec_array(), &ems->fnorm, &ems->fmax, &ems->a_fmax);
358 }
359
360 //! Initialize the energy minimization
361 static void init_em(FILE*                fplog,
362                     const gmx::MDLogger& mdlog,
363                     const char*          title,
364                     const t_commrec*     cr,
365                     t_inputrec*          ir,
366                     gmx::ImdSession*     imdSession,
367                     pull_t*              pull_work,
368                     t_state*             state_global,
369                     gmx_mtop_t*          top_global,
370                     em_state_t*          ems,
371                     gmx_localtop_t*      top,
372                     t_nrnb*              nrnb,
373                     t_forcerec*          fr,
374                     t_graph**            graph,
375                     gmx::MDAtoms*        mdAtoms,
376                     gmx_global_stat_t*   gstat,
377                     gmx_vsite_t*         vsite,
378                     gmx::Constraints*    constr,
379                     gmx_shellfc_t**      shellfc)
380 {
381     real dvdl_constr;
382
383     if (fplog)
384     {
385         fprintf(fplog, "Initiating %s\n", title);
386     }
387
388     if (MASTER(cr))
389     {
390         state_global->ngtc = 0;
391     }
392     initialize_lambdas(fplog, *ir, MASTER(cr), &(state_global->fep_state), state_global->lambda, nullptr);
393
394     if (ir->eI == eiNM)
395     {
396         GMX_ASSERT(shellfc != nullptr, "With NM we always support shells");
397
398         *shellfc = init_shell_flexcon(stdout, top_global, constr ? constr->numFlexibleConstraints() : 0,
399                                       ir->nstcalcenergy, DOMAINDECOMP(cr));
400     }
401     else
402     {
403         GMX_ASSERT(EI_ENERGY_MINIMIZATION(ir->eI),
404                    "This else currently only handles energy minimizers, consider if your algorithm "
405                    "needs shell/flexible-constraint support");
406
407         /* With energy minimization, shells and flexible constraints are
408          * automatically minimized when treated like normal DOFS.
409          */
410         if (shellfc != nullptr)
411         {
412             *shellfc = nullptr;
413         }
414     }
415
416     auto mdatoms = mdAtoms->mdatoms();
417     if (DOMAINDECOMP(cr))
418     {
419         dd_init_local_state(cr->dd, state_global, &ems->s);
420
421         /* Distribute the charge groups over the nodes from the master node */
422         dd_partition_system(fplog, mdlog, ir->init_step, cr, TRUE, 1, state_global, *top_global, ir,
423                             imdSession, pull_work, &ems->s, &ems->f, mdAtoms, top, fr, vsite,
424                             constr, nrnb, nullptr, FALSE);
425         dd_store_state(cr->dd, &ems->s);
426
427         *graph = nullptr;
428     }
429     else
430     {
431         state_change_natoms(state_global, state_global->natoms);
432         /* Just copy the state */
433         ems->s = *state_global;
434         state_change_natoms(&ems->s, ems->s.natoms);
435         ems->f.resizeWithPadding(ems->s.natoms);
436
437         mdAlgorithmsSetupAtomData(cr, ir, *top_global, top, fr, graph, mdAtoms, constr, vsite,
438                                   shellfc ? *shellfc : nullptr);
439
440         if (vsite)
441         {
442             set_vsite_top(vsite, top, mdatoms);
443         }
444     }
445
446     update_mdatoms(mdAtoms->mdatoms(), ems->s.lambda[efptMASS]);
447
448     if (constr)
449     {
450         // TODO how should this cross-module support dependency be managed?
451         if (ir->eConstrAlg == econtSHAKE && gmx_mtop_ftype_count(top_global, F_CONSTR) > 0)
452         {
453             gmx_fatal(FARGS, "Can not do energy minimization with %s, use %s\n",
454                       econstr_names[econtSHAKE], econstr_names[econtLINCS]);
455         }
456
457         if (!ir->bContinuation)
458         {
459             /* Constrain the starting coordinates */
460             dvdl_constr = 0;
461             constr->apply(TRUE, TRUE, -1, 0, 1.0, ems->s.x.arrayRefWithPadding(),
462                           ems->s.x.arrayRefWithPadding(), ArrayRef<RVec>(), ems->s.box,
463                           ems->s.lambda[efptFEP], &dvdl_constr, gmx::ArrayRefWithPadding<RVec>(),
464                           nullptr, gmx::ConstraintVariable::Positions);
465         }
466     }
467
468     if (PAR(cr))
469     {
470         *gstat = global_stat_init(ir);
471     }
472     else
473     {
474         *gstat = nullptr;
475     }
476
477     calc_shifts(ems->s.box, fr->shift_vec);
478 }
479
480 //! Finalize the minimization
481 static void finish_em(const t_commrec*          cr,
482                       gmx_mdoutf_t              outf,
483                       gmx_walltime_accounting_t walltime_accounting,
484                       gmx_wallcycle_t           wcycle)
485 {
486     if (!thisRankHasDuty(cr, DUTY_PME))
487     {
488         /* Tell the PME only node to finish */
489         gmx_pme_send_finish(cr);
490     }
491
492     done_mdoutf(outf);
493
494     em_time_end(walltime_accounting, wcycle);
495 }
496
497 //! Swap two different EM states during minimization
498 static void swap_em_state(em_state_t** ems1, em_state_t** ems2)
499 {
500     em_state_t* tmp;
501
502     tmp   = *ems1;
503     *ems1 = *ems2;
504     *ems2 = tmp;
505 }
506
507 //! Save the EM trajectory
508 static void write_em_traj(FILE*               fplog,
509                           const t_commrec*    cr,
510                           gmx_mdoutf_t        outf,
511                           gmx_bool            bX,
512                           gmx_bool            bF,
513                           const char*         confout,
514                           gmx_mtop_t*         top_global,
515                           t_inputrec*         ir,
516                           int64_t             step,
517                           em_state_t*         state,
518                           t_state*            state_global,
519                           ObservablesHistory* observablesHistory)
520 {
521     int mdof_flags = 0;
522
523     if (bX)
524     {
525         mdof_flags |= MDOF_X;
526     }
527     if (bF)
528     {
529         mdof_flags |= MDOF_F;
530     }
531
532     /* If we want IMD output, set appropriate MDOF flag */
533     if (ir->bIMD)
534     {
535         mdof_flags |= MDOF_IMD;
536     }
537
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);
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, state->s.x, globalXRef);
551             }
552         }
553         else
554         {
555             /* Copy the local state pointer */
556             state_global = &state->s;
557         }
558
559         if (MASTER(cr))
560         {
561             if (ir->pbcType != PbcType::No && !ir->bPeriodicMols && DOMAINDECOMP(cr))
562             {
563                 /* Make molecules whole only for confout writing */
564                 do_pbc_mtop(ir->pbcType, state->s.box, top_global, state_global->x.rvec_array());
565             }
566
567             write_sto_conf_mtop(confout, *top_global->name, top_global,
568                                 state_global->x.rvec_array(), nullptr, ir->pbcType, state->s.box);
569         }
570     }
571 }
572
573 //! \brief Do one minimization step
574 //
575 // \returns true when the step succeeded, false when a constraint error occurred
576 static bool do_em_step(const t_commrec*                   cr,
577                        t_inputrec*                        ir,
578                        t_mdatoms*                         md,
579                        em_state_t*                        ems1,
580                        real                               a,
581                        const PaddedHostVector<gmx::RVec>* force,
582                        em_state_t*                        ems2,
583                        gmx::Constraints*                  constr,
584                        int64_t                            count)
585
586 {
587     t_state *s1, *s2;
588     int      start, end;
589     real     dvdl_constr;
590     int nthreads gmx_unused;
591
592     bool validStep = true;
593
594     s1 = &ems1->s;
595     s2 = &ems2->s;
596
597     if (DOMAINDECOMP(cr) && s1->ddp_count != cr->dd->ddp_count)
598     {
599         gmx_incons("state mismatch in do_em_step");
600     }
601
602     s2->flags = s1->flags;
603
604     if (s2->natoms != s1->natoms)
605     {
606         state_change_natoms(s2, s1->natoms);
607         ems2->f.resizeWithPadding(s2->natoms);
608     }
609     if (DOMAINDECOMP(cr) && s2->cg_gl.size() != s1->cg_gl.size())
610     {
611         s2->cg_gl.resize(s1->cg_gl.size());
612     }
613
614     copy_mat(s1->box, s2->box);
615     /* Copy free energy state */
616     s2->lambda = s1->lambda;
617     copy_mat(s1->box, s2->box);
618
619     start = 0;
620     end   = md->homenr;
621
622     nthreads = gmx_omp_nthreads_get(emntUpdate);
623 #pragma omp parallel num_threads(nthreads)
624     {
625         const rvec* x1 = s1->x.rvec_array();
626         rvec*       x2 = s2->x.rvec_array();
627         const rvec* f  = force->rvec_array();
628
629         int gf = 0;
630 #pragma omp for schedule(static) nowait
631         for (int i = start; i < end; i++)
632         {
633             try
634             {
635                 if (md->cFREEZE)
636                 {
637                     gf = md->cFREEZE[i];
638                 }
639                 for (int m = 0; m < DIM; m++)
640                 {
641                     if (ir->opts.nFreeze[gf][m])
642                     {
643                         x2[i][m] = x1[i][m];
644                     }
645                     else
646                     {
647                         x2[i][m] = x1[i][m] + a * f[i][m];
648                     }
649                 }
650             }
651             GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
652         }
653
654         if (s2->flags & (1 << estCGP))
655         {
656             /* Copy the CG p vector */
657             const rvec* p1 = s1->cg_p.rvec_array();
658             rvec*       p2 = s2->cg_p.rvec_array();
659 #pragma omp for schedule(static) nowait
660             for (int i = start; i < end; i++)
661             {
662                 // Trivial OpenMP block that does not throw
663                 copy_rvec(p1[i], p2[i]);
664             }
665         }
666
667         if (DOMAINDECOMP(cr))
668         {
669             /* OpenMP does not supported unsigned loop variables */
670 #pragma omp for schedule(static) nowait
671             for (gmx::index i = 0; i < gmx::ssize(s2->cg_gl); i++)
672             {
673                 s2->cg_gl[i] = s1->cg_gl[i];
674             }
675         }
676     }
677
678     if (DOMAINDECOMP(cr))
679     {
680         s2->ddp_count       = s1->ddp_count;
681         s2->ddp_count_cg_gl = s1->ddp_count_cg_gl;
682     }
683
684     if (constr)
685     {
686         dvdl_constr = 0;
687         validStep   = constr->apply(
688                 TRUE, TRUE, count, 0, 1.0, s1->x.arrayRefWithPadding(), s2->x.arrayRefWithPadding(),
689                 ArrayRef<RVec>(), s2->box, s2->lambda[efptBONDED], &dvdl_constr,
690                 gmx::ArrayRefWithPadding<RVec>(), nullptr, gmx::ConstraintVariable::Positions);
691
692         if (cr->nnodes > 1)
693         {
694             /* This global reduction will affect performance at high
695              * parallelization, but we can not really avoid it.
696              * But usually EM is not run at high parallelization.
697              */
698             int reductionBuffer = static_cast<int>(!validStep);
699             gmx_sumi(1, &reductionBuffer, cr);
700             validStep = (reductionBuffer == 0);
701         }
702
703         // We should move this check to the different minimizers
704         if (!validStep && ir->eI != eiSteep)
705         {
706             gmx_fatal(FARGS,
707                       "The coordinates could not be constrained. Minimizer '%s' can not handle "
708                       "constraint failures, use minimizer '%s' before using '%s'.",
709                       EI(ir->eI), EI(eiSteep), EI(ir->eI));
710         }
711     }
712
713     return validStep;
714 }
715
716 //! Prepare EM for using domain decomposition parallellization
717 static void em_dd_partition_system(FILE*                fplog,
718                                    const gmx::MDLogger& mdlog,
719                                    int                  step,
720                                    const t_commrec*     cr,
721                                    gmx_mtop_t*          top_global,
722                                    t_inputrec*          ir,
723                                    gmx::ImdSession*     imdSession,
724                                    pull_t*              pull_work,
725                                    em_state_t*          ems,
726                                    gmx_localtop_t*      top,
727                                    gmx::MDAtoms*        mdAtoms,
728                                    t_forcerec*          fr,
729                                    gmx_vsite_t*         vsite,
730                                    gmx::Constraints*    constr,
731                                    t_nrnb*              nrnb,
732                                    gmx_wallcycle_t      wcycle)
733 {
734     /* Repartition the domain decomposition */
735     dd_partition_system(fplog, mdlog, step, cr, FALSE, 1, nullptr, *top_global, ir, imdSession, pull_work,
736                         &ems->s, &ems->f, mdAtoms, top, fr, vsite, constr, nrnb, wcycle, FALSE);
737     dd_store_state(cr->dd, &ems->s);
738 }
739
740 namespace
741 {
742
743 /*! \brief Class to handle the work of setting and doing an energy evaluation.
744  *
745  * This class is a mere aggregate of parameters to pass to evaluate an
746  * energy, so that future changes to names and types of them consume
747  * less time when refactoring other code.
748  *
749  * Aggregate initialization is used, for which the chief risk is that
750  * if a member is added at the end and not all initializer lists are
751  * updated, then the member will be value initialized, which will
752  * typically mean initialization to zero.
753  *
754  * Use a braced initializer list to construct one of these. */
755 class EnergyEvaluator
756 {
757 public:
758     /*! \brief Evaluates an energy on the state in \c ems.
759      *
760      * \todo In practice, the same objects mu_tot, vir, and pres
761      * are always passed to this function, so we would rather have
762      * them as data members. However, their C-array types are
763      * unsuited for aggregate initialization. When the types
764      * improve, the call signature of this method can be reduced.
765      */
766     void run(em_state_t* ems, rvec mu_tot, tensor vir, tensor pres, int64_t count, gmx_bool bFirst);
767     //! Handles logging (deprecated).
768     FILE* fplog;
769     //! Handles logging.
770     const gmx::MDLogger& mdlog;
771     //! Handles communication.
772     const t_commrec* cr;
773     //! Coordinates multi-simulations.
774     const gmx_multisim_t* ms;
775     //! Holds the simulation topology.
776     gmx_mtop_t* top_global;
777     //! Holds the domain topology.
778     gmx_localtop_t* top;
779     //! User input options.
780     t_inputrec* inputrec;
781     //! The Interactive Molecular Dynamics session.
782     gmx::ImdSession* imdSession;
783     //! The pull work object.
784     pull_t* pull_work;
785     //! Manages flop accounting.
786     t_nrnb* nrnb;
787     //! Manages wall cycle accounting.
788     gmx_wallcycle_t wcycle;
789     //! Coordinates global reduction.
790     gmx_global_stat_t gstat;
791     //! Handles virtual sites.
792     gmx_vsite_t* vsite;
793     //! Handles constraints.
794     gmx::Constraints* constr;
795     //! Handles strange things.
796     t_fcdata* fcd;
797     //! Molecular graph for SHAKE.
798     t_graph* graph;
799     //! Per-atom data for this domain.
800     gmx::MDAtoms* mdAtoms;
801     //! Handles how to calculate the forces.
802     t_forcerec* fr;
803     //! Schedule of force-calculation work each step for this task.
804     MdrunScheduleWorkload* runScheduleWork;
805     //! Stores the computed energies.
806     gmx_enerdata_t* enerd;
807 };
808
809 void EnergyEvaluator::run(em_state_t* ems, rvec mu_tot, tensor vir, tensor pres, int64_t count, gmx_bool bFirst)
810 {
811     real     t;
812     gmx_bool bNS;
813     tensor   force_vir, shake_vir, ekin;
814     real     dvdl_constr;
815     real     terminate = 0;
816
817     /* Set the time to the initial time, the time does not change during EM */
818     t = inputrec->init_t;
819
820     if (bFirst || (DOMAINDECOMP(cr) && ems->s.ddp_count < cr->dd->ddp_count))
821     {
822         /* This is the first state or an old state used before the last ns */
823         bNS = TRUE;
824     }
825     else
826     {
827         bNS = FALSE;
828         if (inputrec->nstlist > 0)
829         {
830             bNS = TRUE;
831         }
832     }
833
834     if (vsite)
835     {
836         construct_vsites(vsite, ems->s.x.rvec_array(), 1, nullptr, top->idef.iparams, top->idef.il,
837                          fr->pbcType, fr->bMolPBC, cr, ems->s.box);
838     }
839
840     if (DOMAINDECOMP(cr) && bNS)
841     {
842         /* Repartition the domain decomposition */
843         em_dd_partition_system(fplog, mdlog, count, cr, top_global, inputrec, imdSession, pull_work,
844                                ems, top, mdAtoms, fr, vsite, constr, nrnb, wcycle);
845     }
846
847     /* Calc force & energy on new trial position  */
848     /* do_force always puts the charge groups in the box and shifts again
849      * We do not unshift, so molecules are always whole in congrad.c
850      */
851     do_force(fplog, cr, ms, inputrec, nullptr, nullptr, imdSession, pull_work, count, nrnb, wcycle,
852              top, ems->s.box, ems->s.x.arrayRefWithPadding(), &ems->s.hist,
853              ems->f.arrayRefWithPadding(), force_vir, mdAtoms->mdatoms(), enerd, fcd, ems->s.lambda,
854              graph, fr, runScheduleWork, vsite, mu_tot, t, nullptr,
855              GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES | GMX_FORCE_VIRIAL | GMX_FORCE_ENERGY
856                      | (bNS ? GMX_FORCE_NS : 0),
857              DDBalanceRegionHandler(cr));
858
859     /* Clear the unused shake virial and pressure */
860     clear_mat(shake_vir);
861     clear_mat(pres);
862
863     /* Communicate stuff when parallel */
864     if (PAR(cr) && inputrec->eI != eiNM)
865     {
866         wallcycle_start(wcycle, ewcMoveE);
867
868         global_stat(gstat, cr, enerd, force_vir, shake_vir, inputrec, nullptr, nullptr, nullptr, 1,
869                     &terminate, nullptr, FALSE, CGLO_ENERGY | CGLO_PRESSURE | CGLO_CONSTRAINT);
870
871         wallcycle_stop(wcycle, ewcMoveE);
872     }
873
874     if (fr->dispersionCorrection)
875     {
876         /* Calculate long range corrections to pressure and energy */
877         const DispersionCorrection::Correction correction =
878                 fr->dispersionCorrection->calculate(ems->s.box, ems->s.lambda[efptVDW]);
879
880         enerd->term[F_DISPCORR] = correction.energy;
881         enerd->term[F_EPOT] += correction.energy;
882         enerd->term[F_PRES] += correction.pressure;
883         enerd->term[F_DVDL] += correction.dvdl;
884     }
885     else
886     {
887         enerd->term[F_DISPCORR] = 0;
888     }
889
890     ems->epot = enerd->term[F_EPOT];
891
892     if (constr)
893     {
894         /* Project out the constraint components of the force */
895         dvdl_constr = 0;
896         auto f      = ems->f.arrayRefWithPadding();
897         constr->apply(FALSE, FALSE, count, 0, 1.0, ems->s.x.arrayRefWithPadding(), f,
898                       f.unpaddedArrayRef(), ems->s.box, ems->s.lambda[efptBONDED], &dvdl_constr,
899                       gmx::ArrayRefWithPadding<RVec>(), &shake_vir, gmx::ConstraintVariable::ForceDispl);
900         enerd->term[F_DVDL_CONSTR] += dvdl_constr;
901         m_add(force_vir, shake_vir, vir);
902     }
903     else
904     {
905         copy_mat(force_vir, vir);
906     }
907
908     clear_mat(ekin);
909     enerd->term[F_PRES] = calc_pres(fr->pbcType, inputrec->nwall, ems->s.box, ekin, vir, pres);
910
911     sum_dhdl(enerd, ems->s.lambda, *inputrec->fepvals);
912
913     if (EI_ENERGY_MINIMIZATION(inputrec->eI))
914     {
915         get_state_f_norm_max(cr, &(inputrec->opts), mdAtoms->mdatoms(), ems);
916     }
917 }
918
919 } // namespace
920
921 //! Parallel utility summing energies and forces
922 static double reorder_partsum(const t_commrec* cr,
923                               t_grpopts*       opts,
924                               gmx_mtop_t*      top_global,
925                               em_state_t*      s_min,
926                               em_state_t*      s_b)
927 {
928     if (debug)
929     {
930         fprintf(debug, "Doing reorder_partsum\n");
931     }
932
933     const rvec* fm = s_min->f.rvec_array();
934     const rvec* fb = s_b->f.rvec_array();
935
936     /* Collect fm in a global vector fmg.
937      * This conflicts with the spirit of domain decomposition,
938      * but to fully optimize this a much more complicated algorithm is required.
939      */
940     const int natoms = top_global->natoms;
941     rvec*     fmg;
942     snew(fmg, natoms);
943
944     gmx::ArrayRef<const int> indicesMin = s_min->s.cg_gl;
945     int                      i          = 0;
946     for (int a : indicesMin)
947     {
948         copy_rvec(fm[i], fmg[a]);
949         i++;
950     }
951     gmx_sum(top_global->natoms * 3, fmg[0], cr);
952
953     /* Now we will determine the part of the sum for the cgs in state s_b */
954     gmx::ArrayRef<const int> indicesB = s_b->s.cg_gl;
955
956     double partsum                  = 0;
957     i                               = 0;
958     int                          gf = 0;
959     gmx::ArrayRef<unsigned char> grpnrFREEZE =
960             top_global->groups.groupNumbers[SimulationAtomGroupType::Freeze];
961     for (int a : indicesB)
962     {
963         if (!grpnrFREEZE.empty())
964         {
965             gf = grpnrFREEZE[i];
966         }
967         for (int m = 0; m < DIM; m++)
968         {
969             if (!opts->nFreeze[gf][m])
970             {
971                 partsum += (fb[i][m] - fmg[a][m]) * fb[i][m];
972             }
973         }
974         i++;
975     }
976
977     sfree(fmg);
978
979     return partsum;
980 }
981
982 //! Print some stuff, like beta, whatever that means.
983 static real pr_beta(const t_commrec* cr,
984                     t_grpopts*       opts,
985                     t_mdatoms*       mdatoms,
986                     gmx_mtop_t*      top_global,
987                     em_state_t*      s_min,
988                     em_state_t*      s_b)
989 {
990     double sum;
991
992     /* This is just the classical Polak-Ribiere calculation of beta;
993      * it looks a bit complicated since we take freeze groups into account,
994      * and might have to sum it in parallel runs.
995      */
996
997     if (!DOMAINDECOMP(cr)
998         || (s_min->s.ddp_count == cr->dd->ddp_count && s_b->s.ddp_count == cr->dd->ddp_count))
999     {
1000         const rvec* fm = s_min->f.rvec_array();
1001         const rvec* fb = s_b->f.rvec_array();
1002         sum            = 0;
1003         int gf         = 0;
1004         /* This part of code can be incorrect with DD,
1005          * since the atom ordering in s_b and s_min might differ.
1006          */
1007         for (int i = 0; i < mdatoms->homenr; i++)
1008         {
1009             if (mdatoms->cFREEZE)
1010             {
1011                 gf = mdatoms->cFREEZE[i];
1012             }
1013             for (int m = 0; m < DIM; m++)
1014             {
1015                 if (!opts->nFreeze[gf][m])
1016                 {
1017                     sum += (fb[i][m] - fm[i][m]) * fb[i][m];
1018                 }
1019             }
1020         }
1021     }
1022     else
1023     {
1024         /* We need to reorder cgs while summing */
1025         sum = reorder_partsum(cr, opts, top_global, s_min, s_b);
1026     }
1027     if (PAR(cr))
1028     {
1029         gmx_sumd(1, &sum, cr);
1030     }
1031
1032     return sum / gmx::square(s_min->fnorm);
1033 }
1034
1035 namespace gmx
1036 {
1037
1038 void LegacySimulator::do_cg()
1039 {
1040     const char* CG = "Polak-Ribiere Conjugate Gradients";
1041
1042     gmx_localtop_t    top(top_global->ffparams);
1043     gmx_global_stat_t gstat;
1044     t_graph*          graph;
1045     double            tmp, minstep;
1046     real              stepsize;
1047     real              a, b, c, beta = 0.0;
1048     real              epot_repl = 0;
1049     real              pnorm;
1050     gmx_bool          converged, foundlower;
1051     rvec              mu_tot = { 0 };
1052     gmx_bool          do_log = FALSE, do_ene = FALSE, do_x, do_f;
1053     tensor            vir, pres;
1054     int               number_steps, neval = 0, nstcg = inputrec->nstcgsteep;
1055     int               m, step, nminstep;
1056     auto              mdatoms = mdAtoms->mdatoms();
1057
1058     GMX_LOG(mdlog.info)
1059             .asParagraph()
1060             .appendText(
1061                     "Note that activating conjugate gradient energy minimization via the "
1062                     "integrator .mdp option and the command gmx mdrun may "
1063                     "be available in a different form in a future version of GROMACS, "
1064                     "e.g. gmx minimize and an .mdp option.");
1065
1066     step = 0;
1067
1068     if (MASTER(cr))
1069     {
1070         // In CG, the state is extended with a search direction
1071         state_global->flags |= (1 << estCGP);
1072
1073         // Ensure the extra per-atom state array gets allocated
1074         state_change_natoms(state_global, state_global->natoms);
1075
1076         // Initialize the search direction to zero
1077         for (RVec& cg_p : state_global->cg_p)
1078         {
1079             cg_p = { 0, 0, 0 };
1080         }
1081     }
1082
1083     /* Create 4 states on the stack and extract pointers that we will swap */
1084     em_state_t  s0{}, s1{}, s2{}, s3{};
1085     em_state_t* s_min = &s0;
1086     em_state_t* s_a   = &s1;
1087     em_state_t* s_b   = &s2;
1088     em_state_t* s_c   = &s3;
1089
1090     /* Init em and store the local state in s_min */
1091     init_em(fplog, mdlog, CG, cr, inputrec, imdSession, pull_work, state_global, top_global, s_min,
1092             &top, nrnb, fr, &graph, mdAtoms, &gstat, vsite, constr, nullptr);
1093     const bool        simulationsShareState = false;
1094     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
1095                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
1096                                    StartingBehavior::NewSimulation, simulationsShareState, ms);
1097     gmx::EnergyOutput energyOutput(mdoutf_get_fp_ene(outf), top_global, inputrec, pull_work, nullptr,
1098                                    false, StartingBehavior::NewSimulation, mdModulesNotifier);
1099
1100     /* Print to log file */
1101     print_em_start(fplog, cr, walltime_accounting, wcycle, CG);
1102
1103     /* Max number of steps */
1104     number_steps = inputrec->nsteps;
1105
1106     if (MASTER(cr))
1107     {
1108         sp_header(stderr, CG, inputrec->em_tol, number_steps);
1109     }
1110     if (fplog)
1111     {
1112         sp_header(fplog, CG, inputrec->em_tol, number_steps);
1113     }
1114
1115     EnergyEvaluator energyEvaluator{
1116         fplog,      mdlog,     cr,      ms,     top_global,      &top,  inputrec,
1117         imdSession, pull_work, nrnb,    wcycle, gstat,           vsite, constr,
1118         fcd,        graph,     mdAtoms, fr,     runScheduleWork, enerd
1119     };
1120     /* Call the force routine and some auxiliary (neighboursearching etc.) */
1121     /* do_force always puts the charge groups in the box and shifts again
1122      * We do not unshift, so molecules are always whole in congrad.c
1123      */
1124     energyEvaluator.run(s_min, mu_tot, vir, pres, -1, TRUE);
1125
1126     if (MASTER(cr))
1127     {
1128         /* Copy stuff to the energy bin for easy printing etc. */
1129         matrix nullBox = {};
1130         energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
1131                                          enerd, nullptr, nullptr, nullptr, nullBox, nullptr,
1132                                          nullptr, vir, pres, nullptr, mu_tot, constr);
1133
1134         energyOutput.printHeader(fplog, step, step);
1135         energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step,
1136                                            step, fcd, nullptr);
1137     }
1138
1139     /* Estimate/guess the initial stepsize */
1140     stepsize = inputrec->em_stepsize / s_min->fnorm;
1141
1142     if (MASTER(cr))
1143     {
1144         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1145         fprintf(stderr, "   F-max             = %12.5e on atom %d\n", s_min->fmax, s_min->a_fmax + 1);
1146         fprintf(stderr, "   F-Norm            = %12.5e\n", s_min->fnorm / sqrtNumAtoms);
1147         fprintf(stderr, "\n");
1148         /* and copy to the log file too... */
1149         fprintf(fplog, "   F-max             = %12.5e on atom %d\n", s_min->fmax, s_min->a_fmax + 1);
1150         fprintf(fplog, "   F-Norm            = %12.5e\n", s_min->fnorm / sqrtNumAtoms);
1151         fprintf(fplog, "\n");
1152     }
1153     /* Start the loop over CG steps.
1154      * Each successful step is counted, and we continue until
1155      * we either converge or reach the max number of steps.
1156      */
1157     converged = FALSE;
1158     for (step = 0; (number_steps < 0 || step <= number_steps) && !converged; step++)
1159     {
1160
1161         /* start taking steps in a new direction
1162          * First time we enter the routine, beta=0, and the direction is
1163          * simply the negative gradient.
1164          */
1165
1166         /* Calculate the new direction in p, and the gradient in this direction, gpa */
1167         rvec*       pm  = s_min->s.cg_p.rvec_array();
1168         const rvec* sfm = s_min->f.rvec_array();
1169         double      gpa = 0;
1170         int         gf  = 0;
1171         for (int i = 0; i < mdatoms->homenr; i++)
1172         {
1173             if (mdatoms->cFREEZE)
1174             {
1175                 gf = mdatoms->cFREEZE[i];
1176             }
1177             for (m = 0; m < DIM; m++)
1178             {
1179                 if (!inputrec->opts.nFreeze[gf][m])
1180                 {
1181                     pm[i][m] = sfm[i][m] + beta * pm[i][m];
1182                     gpa -= pm[i][m] * sfm[i][m];
1183                     /* f is negative gradient, thus the sign */
1184                 }
1185                 else
1186                 {
1187                     pm[i][m] = 0;
1188                 }
1189             }
1190         }
1191
1192         /* Sum the gradient along the line across CPUs */
1193         if (PAR(cr))
1194         {
1195             gmx_sumd(1, &gpa, cr);
1196         }
1197
1198         /* Calculate the norm of the search vector */
1199         get_f_norm_max(cr, &(inputrec->opts), mdatoms, pm, &pnorm, nullptr, nullptr);
1200
1201         /* Just in case stepsize reaches zero due to numerical precision... */
1202         if (stepsize <= 0)
1203         {
1204             stepsize = inputrec->em_stepsize / pnorm;
1205         }
1206
1207         /*
1208          * Double check the value of the derivative in the search direction.
1209          * If it is positive it must be due to the old information in the
1210          * CG formula, so just remove that and start over with beta=0.
1211          * This corresponds to a steepest descent step.
1212          */
1213         if (gpa > 0)
1214         {
1215             beta = 0;
1216             step--;   /* Don't count this step since we are restarting */
1217             continue; /* Go back to the beginning of the big for-loop */
1218         }
1219
1220         /* Calculate minimum allowed stepsize, before the average (norm)
1221          * relative change in coordinate is smaller than precision
1222          */
1223         minstep      = 0;
1224         auto s_min_x = makeArrayRef(s_min->s.x);
1225         for (int i = 0; i < mdatoms->homenr; i++)
1226         {
1227             for (m = 0; m < DIM; m++)
1228             {
1229                 tmp = fabs(s_min_x[i][m]);
1230                 if (tmp < 1.0)
1231                 {
1232                     tmp = 1.0;
1233                 }
1234                 tmp = pm[i][m] / tmp;
1235                 minstep += tmp * tmp;
1236             }
1237         }
1238         /* Add up from all CPUs */
1239         if (PAR(cr))
1240         {
1241             gmx_sumd(1, &minstep, cr);
1242         }
1243
1244         minstep = GMX_REAL_EPS / sqrt(minstep / (3 * top_global->natoms));
1245
1246         if (stepsize < minstep)
1247         {
1248             converged = TRUE;
1249             break;
1250         }
1251
1252         /* Write coordinates if necessary */
1253         do_x = do_per_step(step, inputrec->nstxout);
1254         do_f = do_per_step(step, inputrec->nstfout);
1255
1256         write_em_traj(fplog, cr, outf, do_x, do_f, nullptr, top_global, inputrec, step, s_min,
1257                       state_global, observablesHistory);
1258
1259         /* Take a step downhill.
1260          * In theory, we should minimize the function along this direction.
1261          * That is quite possible, but it turns out to take 5-10 function evaluations
1262          * for each line. However, we dont really need to find the exact minimum -
1263          * it is much better to start a new CG step in a modified direction as soon
1264          * as we are close to it. This will save a lot of energy evaluations.
1265          *
1266          * In practice, we just try to take a single step.
1267          * If it worked (i.e. lowered the energy), we increase the stepsize but
1268          * the continue straight to the next CG step without trying to find any minimum.
1269          * If it didn't work (higher energy), there must be a minimum somewhere between
1270          * the old position and the new one.
1271          *
1272          * Due to the finite numerical accuracy, it turns out that it is a good idea
1273          * to even accept a SMALL increase in energy, if the derivative is still downhill.
1274          * This leads to lower final energies in the tests I've done. / Erik
1275          */
1276         s_a->epot = s_min->epot;
1277         a         = 0.0;
1278         c         = a + stepsize; /* reference position along line is zero */
1279
1280         if (DOMAINDECOMP(cr) && s_min->s.ddp_count < cr->dd->ddp_count)
1281         {
1282             em_dd_partition_system(fplog, mdlog, step, cr, top_global, inputrec, imdSession,
1283                                    pull_work, s_min, &top, mdAtoms, fr, vsite, constr, nrnb, wcycle);
1284         }
1285
1286         /* Take a trial step (new coords in s_c) */
1287         do_em_step(cr, inputrec, mdatoms, s_min, c, &s_min->s.cg_p, s_c, 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         const rvec* sfc = s_c->f.rvec_array();
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, &s_min->s.cg_p, s_b, constr, -1);
1389
1390                 neval++;
1391                 /* Calculate energy for the trial step */
1392                 energyEvaluator.run(s_b, mu_tot, vir, pres, -1, FALSE);
1393
1394                 /* p does not change within a step, but since the domain decomposition
1395                  * might change, we have to use cg_p of s_b here.
1396                  */
1397                 const rvec* pb  = s_b->s.cg_p.rvec_array();
1398                 const rvec* sfb = s_b->f.rvec_array();
1399                 gpb             = 0;
1400                 for (int i = 0; i < mdatoms->homenr; i++)
1401                 {
1402                     for (m = 0; m < DIM; m++)
1403                     {
1404                         gpb -= pb[i][m] * sfb[i][m]; /* f is negative gradient, thus the sign */
1405                     }
1406                 }
1407                 /* Sum the gradient along the line across CPUs */
1408                 if (PAR(cr))
1409                 {
1410                     gmx_sumd(1, &gpb, cr);
1411                 }
1412
1413                 if (debug)
1414                 {
1415                     fprintf(debug, "CGE: EpotA %f EpotB %f EpotC %f gpb %f\n", s_a->epot, s_b->epot,
1416                             s_c->epot, gpb);
1417                 }
1418
1419                 epot_repl = s_b->epot;
1420
1421                 /* Keep one of the intervals based on the value of the derivative at the new point */
1422                 if (gpb > 0)
1423                 {
1424                     /* Replace c endpoint with b */
1425                     swap_em_state(&s_b, &s_c);
1426                     c   = b;
1427                     gpc = gpb;
1428                 }
1429                 else
1430                 {
1431                     /* Replace a endpoint with b */
1432                     swap_em_state(&s_b, &s_a);
1433                     a   = b;
1434                     gpa = gpb;
1435                 }
1436
1437                 /*
1438                  * Stop search as soon as we find a value smaller than the endpoints.
1439                  * Never run more than 20 steps, no matter what.
1440                  */
1441                 nminstep++;
1442             } while ((epot_repl > s_a->epot || epot_repl > s_c->epot) && (nminstep < 20));
1443
1444             if (std::fabs(epot_repl - s_min->epot) < fabs(s_min->epot) * GMX_REAL_EPS || nminstep >= 20)
1445             {
1446                 /* OK. We couldn't find a significantly lower energy.
1447                  * If beta==0 this was steepest descent, and then we give up.
1448                  * If not, set beta=0 and restart with steepest descent before quitting.
1449                  */
1450                 if (beta == 0.0)
1451                 {
1452                     /* Converged */
1453                     converged = TRUE;
1454                     break;
1455                 }
1456                 else
1457                 {
1458                     /* Reset memory before giving up */
1459                     beta = 0.0;
1460                     continue;
1461                 }
1462             }
1463
1464             /* Select min energy state of A & C, put the best in B.
1465              */
1466             if (s_c->epot < s_a->epot)
1467             {
1468                 if (debug)
1469                 {
1470                     fprintf(debug, "CGE: C (%f) is lower than A (%f), moving C to B\n", s_c->epot,
1471                             s_a->epot);
1472                 }
1473                 swap_em_state(&s_b, &s_c);
1474                 gpb = gpc;
1475             }
1476             else
1477             {
1478                 if (debug)
1479                 {
1480                     fprintf(debug, "CGE: A (%f) is lower than C (%f), moving A to B\n", s_a->epot,
1481                             s_c->epot);
1482                 }
1483                 swap_em_state(&s_b, &s_a);
1484                 gpb = gpa;
1485             }
1486         }
1487         else
1488         {
1489             if (debug)
1490             {
1491                 fprintf(debug, "CGE: Found a lower energy %f, moving C to B\n", s_c->epot);
1492             }
1493             swap_em_state(&s_b, &s_c);
1494             gpb = gpc;
1495         }
1496
1497         /* new search direction */
1498         /* beta = 0 means forget all memory and restart with steepest descents. */
1499         if (nstcg && ((step % nstcg) == 0))
1500         {
1501             beta = 0.0;
1502         }
1503         else
1504         {
1505             /* s_min->fnorm cannot be zero, because then we would have converged
1506              * and broken out.
1507              */
1508
1509             /* Polak-Ribiere update.
1510              * Change to fnorm2/fnorm2_old for Fletcher-Reeves
1511              */
1512             beta = pr_beta(cr, &inputrec->opts, mdatoms, top_global, s_min, s_b);
1513         }
1514         /* Limit beta to prevent oscillations */
1515         if (fabs(beta) > 5.0)
1516         {
1517             beta = 0.0;
1518         }
1519
1520
1521         /* update positions */
1522         swap_em_state(&s_min, &s_b);
1523         gpa = gpb;
1524
1525         /* Print it if necessary */
1526         if (MASTER(cr))
1527         {
1528             if (mdrunOptions.verbose)
1529             {
1530                 double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1531                 fprintf(stderr, "\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n", step,
1532                         s_min->epot, s_min->fnorm / sqrtNumAtoms, s_min->fmax, s_min->a_fmax + 1);
1533                 fflush(stderr);
1534             }
1535             /* Store the new (lower) energies */
1536             matrix nullBox = {};
1537             energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
1538                                              enerd, nullptr, nullptr, nullptr, nullBox, nullptr,
1539                                              nullptr, vir, pres, nullptr, mu_tot, constr);
1540
1541             do_log = do_per_step(step, inputrec->nstlog);
1542             do_ene = do_per_step(step, inputrec->nstenergy);
1543
1544             imdSession->fillEnergyRecord(step, TRUE);
1545
1546             if (do_log)
1547             {
1548                 energyOutput.printHeader(fplog, step, step);
1549             }
1550             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), do_ene, FALSE, FALSE,
1551                                                do_log ? fplog : nullptr, step, step, fcd, nullptr);
1552         }
1553
1554         /* Send energies and positions to the IMD client if bIMD is TRUE. */
1555         if (MASTER(cr) && imdSession->run(step, TRUE, state_global->box, state_global->x.rvec_array(), 0))
1556         {
1557             imdSession->sendPositionsAndEnergies();
1558         }
1559
1560         /* Stop when the maximum force lies below tolerance.
1561          * If we have reached machine precision, converged is already set to true.
1562          */
1563         converged = converged || (s_min->fmax < inputrec->em_tol);
1564
1565     } /* End of the loop */
1566
1567     if (converged)
1568     {
1569         step--; /* we never took that last step in this case */
1570     }
1571     if (s_min->fmax > inputrec->em_tol)
1572     {
1573         if (MASTER(cr))
1574         {
1575             warn_step(fplog, inputrec->em_tol, s_min->fmax, step - 1 == number_steps, FALSE);
1576         }
1577         converged = FALSE;
1578     }
1579
1580     if (MASTER(cr))
1581     {
1582         /* If we printed energy and/or logfile last step (which was the last step)
1583          * we don't have to do it again, but otherwise print the final values.
1584          */
1585         if (!do_log)
1586         {
1587             /* Write final value to log since we didn't do anything the last step */
1588             energyOutput.printHeader(fplog, step, step);
1589         }
1590         if (!do_ene || !do_log)
1591         {
1592             /* Write final energy file entries */
1593             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), !do_ene, FALSE, FALSE,
1594                                                !do_log ? fplog : nullptr, step, step, fcd, nullptr);
1595         }
1596     }
1597
1598     /* Print some stuff... */
1599     if (MASTER(cr))
1600     {
1601         fprintf(stderr, "\nwriting lowest energy coordinates.\n");
1602     }
1603
1604     /* IMPORTANT!
1605      * For accurate normal mode calculation it is imperative that we
1606      * store the last conformation into the full precision binary trajectory.
1607      *
1608      * However, we should only do it if we did NOT already write this step
1609      * above (which we did if do_x or do_f was true).
1610      */
1611     /* Note that with 0 < nstfout != nstxout we can end up with two frames
1612      * in the trajectory with the same step number.
1613      */
1614     do_x = !do_per_step(step, inputrec->nstxout);
1615     do_f = (inputrec->nstfout > 0 && !do_per_step(step, inputrec->nstfout));
1616
1617     write_em_traj(fplog, cr, outf, do_x, do_f, ftp2fn(efSTO, nfile, fnm), top_global, inputrec,
1618                   step, s_min, state_global, observablesHistory);
1619
1620
1621     if (MASTER(cr))
1622     {
1623         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1624         print_converged(stderr, CG, inputrec->em_tol, step, converged, number_steps, s_min, sqrtNumAtoms);
1625         print_converged(fplog, CG, inputrec->em_tol, step, converged, number_steps, s_min, sqrtNumAtoms);
1626
1627         fprintf(fplog, "\nPerformed %d energy evaluations in total.\n", neval);
1628     }
1629
1630     finish_em(cr, outf, walltime_accounting, wcycle);
1631
1632     /* To print the actual number of steps we needed somewhere */
1633     walltime_accounting_set_nsteps_done(walltime_accounting, step);
1634 }
1635
1636
1637 void LegacySimulator::do_lbfgs()
1638 {
1639     static const char* LBFGS = "Low-Memory BFGS Minimizer";
1640     em_state_t         ems;
1641     gmx_localtop_t     top(top_global->ffparams);
1642     gmx_global_stat_t  gstat;
1643     t_graph*           graph;
1644     int                ncorr, nmaxcorr, point, cp, neval, nminstep;
1645     double             stepsize, step_taken, gpa, gpb, gpc, tmp, minstep;
1646     real *             rho, *alpha, *p, *s, **dx, **dg;
1647     real               a, b, c, maxdelta, delta;
1648     real               diag, Epot0;
1649     real               dgdx, dgdg, sq, yr, beta;
1650     gmx_bool           converged;
1651     rvec               mu_tot = { 0 };
1652     gmx_bool           do_log, do_ene, do_x, do_f, foundlower, *frozen;
1653     tensor             vir, pres;
1654     int                start, end, number_steps;
1655     int                i, k, m, n, gf, step;
1656     int                mdof_flags;
1657     auto               mdatoms = mdAtoms->mdatoms();
1658
1659     GMX_LOG(mdlog.info)
1660             .asParagraph()
1661             .appendText(
1662                     "Note that activating L-BFGS energy minimization via the "
1663                     "integrator .mdp option and the command gmx mdrun may "
1664                     "be available in a different form in a future version of GROMACS, "
1665                     "e.g. gmx minimize and an .mdp option.");
1666
1667     if (PAR(cr))
1668     {
1669         gmx_fatal(FARGS, "L-BFGS minimization only supports a single rank");
1670     }
1671
1672     if (nullptr != constr)
1673     {
1674         gmx_fatal(
1675                 FARGS,
1676                 "The combination of constraints and L-BFGS minimization is not implemented. Either "
1677                 "do not use constraints, or use another minimizer (e.g. steepest descent).");
1678     }
1679
1680     n        = 3 * state_global->natoms;
1681     nmaxcorr = inputrec->nbfgscorr;
1682
1683     snew(frozen, n);
1684
1685     snew(p, n);
1686     snew(rho, nmaxcorr);
1687     snew(alpha, nmaxcorr);
1688
1689     snew(dx, nmaxcorr);
1690     for (i = 0; i < nmaxcorr; i++)
1691     {
1692         snew(dx[i], n);
1693     }
1694
1695     snew(dg, nmaxcorr);
1696     for (i = 0; i < nmaxcorr; i++)
1697     {
1698         snew(dg[i], n);
1699     }
1700
1701     step  = 0;
1702     neval = 0;
1703
1704     /* Init em */
1705     init_em(fplog, mdlog, LBFGS, cr, inputrec, imdSession, pull_work, state_global, top_global,
1706             &ems, &top, nrnb, fr, &graph, mdAtoms, &gstat, vsite, constr, nullptr);
1707     const bool        simulationsShareState = false;
1708     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
1709                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
1710                                    StartingBehavior::NewSimulation, simulationsShareState, ms);
1711     gmx::EnergyOutput energyOutput(mdoutf_get_fp_ene(outf), top_global, inputrec, pull_work, nullptr,
1712                                    false, StartingBehavior::NewSimulation, mdModulesNotifier);
1713
1714     start = 0;
1715     end   = mdatoms->homenr;
1716
1717     /* We need 4 working states */
1718     em_state_t  s0{}, s1{}, s2{}, s3{};
1719     em_state_t* sa   = &s0;
1720     em_state_t* sb   = &s1;
1721     em_state_t* sc   = &s2;
1722     em_state_t* last = &s3;
1723     /* Initialize by copying the state from ems (we could skip x and f here) */
1724     *sa = ems;
1725     *sb = ems;
1726     *sc = ems;
1727
1728     /* Print to log file */
1729     print_em_start(fplog, cr, walltime_accounting, wcycle, LBFGS);
1730
1731     do_log = do_ene = do_x = do_f = TRUE;
1732
1733     /* Max number of steps */
1734     number_steps = inputrec->nsteps;
1735
1736     /* Create a 3*natoms index to tell whether each degree of freedom is frozen */
1737     gf = 0;
1738     for (i = start; i < end; i++)
1739     {
1740         if (mdatoms->cFREEZE)
1741         {
1742             gf = mdatoms->cFREEZE[i];
1743         }
1744         for (m = 0; m < DIM; m++)
1745         {
1746             frozen[3 * i + m] = (inputrec->opts.nFreeze[gf][m] != 0);
1747         }
1748     }
1749     if (MASTER(cr))
1750     {
1751         sp_header(stderr, LBFGS, inputrec->em_tol, number_steps);
1752     }
1753     if (fplog)
1754     {
1755         sp_header(fplog, LBFGS, inputrec->em_tol, number_steps);
1756     }
1757
1758     if (vsite)
1759     {
1760         construct_vsites(vsite, state_global->x.rvec_array(), 1, nullptr, top.idef.iparams,
1761                          top.idef.il, fr->pbcType, fr->bMolPBC, cr, state_global->box);
1762     }
1763
1764     /* Call the force routine and some auxiliary (neighboursearching etc.) */
1765     /* do_force always puts the charge groups in the box and shifts again
1766      * We do not unshift, so molecules are always whole
1767      */
1768     neval++;
1769     EnergyEvaluator energyEvaluator{
1770         fplog,      mdlog,     cr,      ms,     top_global,      &top,  inputrec,
1771         imdSession, pull_work, nrnb,    wcycle, gstat,           vsite, constr,
1772         fcd,        graph,     mdAtoms, fr,     runScheduleWork, enerd
1773     };
1774     energyEvaluator.run(&ems, mu_tot, vir, pres, -1, TRUE);
1775
1776     if (MASTER(cr))
1777     {
1778         /* Copy stuff to the energy bin for easy printing etc. */
1779         matrix nullBox = {};
1780         energyOutput.addDataAtEnergyStep(false, false, static_cast<double>(step), mdatoms->tmass,
1781                                          enerd, nullptr, nullptr, nullptr, nullBox, nullptr,
1782                                          nullptr, vir, pres, nullptr, mu_tot, constr);
1783
1784         energyOutput.printHeader(fplog, step, step);
1785         energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step,
1786                                            step, fcd, nullptr);
1787     }
1788
1789     /* Set the initial step.
1790      * since it will be multiplied by the non-normalized search direction
1791      * vector (force vector the first time), we scale it by the
1792      * norm of the force.
1793      */
1794
1795     if (MASTER(cr))
1796     {
1797         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1798         fprintf(stderr, "Using %d BFGS correction steps.\n\n", nmaxcorr);
1799         fprintf(stderr, "   F-max             = %12.5e on atom %d\n", ems.fmax, ems.a_fmax + 1);
1800         fprintf(stderr, "   F-Norm            = %12.5e\n", ems.fnorm / sqrtNumAtoms);
1801         fprintf(stderr, "\n");
1802         /* and copy to the log file too... */
1803         fprintf(fplog, "Using %d BFGS correction steps.\n\n", nmaxcorr);
1804         fprintf(fplog, "   F-max             = %12.5e on atom %d\n", ems.fmax, ems.a_fmax + 1);
1805         fprintf(fplog, "   F-Norm            = %12.5e\n", ems.fnorm / sqrtNumAtoms);
1806         fprintf(fplog, "\n");
1807     }
1808
1809     // Point is an index to the memory of search directions, where 0 is the first one.
1810     point = 0;
1811
1812     // Set initial search direction to the force (-gradient), or 0 for frozen particles.
1813     real* fInit = static_cast<real*>(ems.f.rvec_array()[0]);
1814     for (i = 0; i < n; i++)
1815     {
1816         if (!frozen[i])
1817         {
1818             dx[point][i] = fInit[i]; /* Initial search direction */
1819         }
1820         else
1821         {
1822             dx[point][i] = 0;
1823         }
1824     }
1825
1826     // Stepsize will be modified during the search, and actually it is not critical
1827     // (the main efficiency in the algorithm comes from changing directions), but
1828     // we still need an initial value, so estimate it as the inverse of the norm
1829     // so we take small steps where the potential fluctuates a lot.
1830     stepsize = 1.0 / ems.fnorm;
1831
1832     /* Start the loop over BFGS steps.
1833      * Each successful step is counted, and we continue until
1834      * we either converge or reach the max number of steps.
1835      */
1836
1837     ncorr = 0;
1838
1839     /* Set the gradient from the force */
1840     converged = FALSE;
1841     for (step = 0; (number_steps < 0 || step <= number_steps) && !converged; step++)
1842     {
1843
1844         /* Write coordinates if necessary */
1845         do_x = do_per_step(step, inputrec->nstxout);
1846         do_f = do_per_step(step, inputrec->nstfout);
1847
1848         mdof_flags = 0;
1849         if (do_x)
1850         {
1851             mdof_flags |= MDOF_X;
1852         }
1853
1854         if (do_f)
1855         {
1856             mdof_flags |= MDOF_F;
1857         }
1858
1859         if (inputrec->bIMD)
1860         {
1861             mdof_flags |= MDOF_IMD;
1862         }
1863
1864         mdoutf_write_to_trajectory_files(fplog, cr, outf, mdof_flags, top_global->natoms, step,
1865                                          static_cast<real>(step), &ems.s, state_global,
1866                                          observablesHistory, ems.f);
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.rvec_array()[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.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.rvec_array()[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.rvec_array()[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, nullptr, nullBox, nullptr,
2266                                              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, fcd, nullptr);
2279         }
2280
2281         /* Send x and E to IMD client, if bIMD is TRUE. */
2282         if (imdSession->run(step, TRUE, state_global->box, state_global->x.rvec_array(), 0) && MASTER(cr))
2283         {
2284             imdSession->sendPositionsAndEnergies();
2285         }
2286
2287         // Reset stepsize in we are doing more iterations
2288         stepsize = 1.0;
2289
2290         /* Stop when the maximum force lies below tolerance.
2291          * If we have reached machine precision, converged is already set to true.
2292          */
2293         converged = converged || (ems.fmax < inputrec->em_tol);
2294
2295     } /* End of the loop */
2296
2297     if (converged)
2298     {
2299         step--; /* we never took that last step in this case */
2300     }
2301     if (ems.fmax > inputrec->em_tol)
2302     {
2303         if (MASTER(cr))
2304         {
2305             warn_step(fplog, inputrec->em_tol, ems.fmax, step - 1 == number_steps, FALSE);
2306         }
2307         converged = FALSE;
2308     }
2309
2310     /* If we printed energy and/or logfile last step (which was the last step)
2311      * we don't have to do it again, but otherwise print the final values.
2312      */
2313     if (!do_log) /* Write final value to log since we didn't do anythin last step */
2314     {
2315         energyOutput.printHeader(fplog, step, step);
2316     }
2317     if (!do_ene || !do_log) /* Write final energy file entries */
2318     {
2319         energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), !do_ene, FALSE, FALSE,
2320                                            !do_log ? fplog : nullptr, step, step, fcd, nullptr);
2321     }
2322
2323     /* Print some stuff... */
2324     if (MASTER(cr))
2325     {
2326         fprintf(stderr, "\nwriting lowest energy coordinates.\n");
2327     }
2328
2329     /* IMPORTANT!
2330      * For accurate normal mode calculation it is imperative that we
2331      * store the last conformation into the full precision binary trajectory.
2332      *
2333      * However, we should only do it if we did NOT already write this step
2334      * above (which we did if do_x or do_f was true).
2335      */
2336     do_x = !do_per_step(step, inputrec->nstxout);
2337     do_f = !do_per_step(step, inputrec->nstfout);
2338     write_em_traj(fplog, cr, outf, do_x, do_f, ftp2fn(efSTO, nfile, fnm), top_global, inputrec,
2339                   step, &ems, state_global, observablesHistory);
2340
2341     if (MASTER(cr))
2342     {
2343         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
2344         print_converged(stderr, LBFGS, inputrec->em_tol, step, converged, number_steps, &ems, sqrtNumAtoms);
2345         print_converged(fplog, LBFGS, inputrec->em_tol, step, converged, number_steps, &ems, sqrtNumAtoms);
2346
2347         fprintf(fplog, "\nPerformed %d energy evaluations in total.\n", neval);
2348     }
2349
2350     finish_em(cr, outf, walltime_accounting, wcycle);
2351
2352     /* To print the actual number of steps we needed somewhere */
2353     walltime_accounting_set_nsteps_done(walltime_accounting, step);
2354 }
2355
2356 void LegacySimulator::do_steep()
2357 {
2358     const char*       SD = "Steepest Descents";
2359     gmx_localtop_t    top(top_global->ffparams);
2360     gmx_global_stat_t gstat;
2361     t_graph*          graph;
2362     real              stepsize;
2363     real              ustep;
2364     gmx_bool          bDone, bAbort, do_x, do_f;
2365     tensor            vir, pres;
2366     rvec              mu_tot = { 0 };
2367     int               nsteps;
2368     int               count          = 0;
2369     int               steps_accepted = 0;
2370     auto              mdatoms        = mdAtoms->mdatoms();
2371
2372     GMX_LOG(mdlog.info)
2373             .asParagraph()
2374             .appendText(
2375                     "Note that activating steepest-descent energy minimization via the "
2376                     "integrator .mdp option and the command gmx mdrun may "
2377                     "be available in a different form in a future version of GROMACS, "
2378                     "e.g. gmx minimize and an .mdp option.");
2379
2380     /* Create 2 states on the stack and extract pointers that we will swap */
2381     em_state_t  s0{}, s1{};
2382     em_state_t* s_min = &s0;
2383     em_state_t* s_try = &s1;
2384
2385     /* Init em and store the local state in s_try */
2386     init_em(fplog, mdlog, SD, cr, inputrec, imdSession, pull_work, state_global, top_global, s_try,
2387             &top, nrnb, fr, &graph, mdAtoms, &gstat, vsite, constr, nullptr);
2388     const bool        simulationsShareState = false;
2389     gmx_mdoutf*       outf = init_mdoutf(fplog, nfile, fnm, mdrunOptions, cr, outputProvider,
2390                                    mdModulesNotifier, inputrec, top_global, nullptr, wcycle,
2391                                    StartingBehavior::NewSimulation, simulationsShareState, ms);
2392     gmx::EnergyOutput energyOutput(mdoutf_get_fp_ene(outf), top_global, inputrec, pull_work, nullptr,
2393                                    false, StartingBehavior::NewSimulation, mdModulesNotifier);
2394
2395     /* Print to log file  */
2396     print_em_start(fplog, cr, walltime_accounting, wcycle, SD);
2397
2398     /* Set variables for stepsize (in nm). This is the largest
2399      * step that we are going to make in any direction.
2400      */
2401     ustep    = inputrec->em_stepsize;
2402     stepsize = 0;
2403
2404     /* Max number of steps  */
2405     nsteps = inputrec->nsteps;
2406
2407     if (MASTER(cr))
2408     {
2409         /* Print to the screen  */
2410         sp_header(stderr, SD, inputrec->em_tol, nsteps);
2411     }
2412     if (fplog)
2413     {
2414         sp_header(fplog, SD, inputrec->em_tol, nsteps);
2415     }
2416     EnergyEvaluator energyEvaluator{
2417         fplog,      mdlog,     cr,      ms,     top_global,      &top,  inputrec,
2418         imdSession, pull_work, nrnb,    wcycle, gstat,           vsite, constr,
2419         fcd,        graph,     mdAtoms, fr,     runScheduleWork, enerd
2420     };
2421
2422     /**** HERE STARTS THE LOOP ****
2423      * count is the counter for the number of steps
2424      * bDone will be TRUE when the minimization has converged
2425      * bAbort will be TRUE when nsteps steps have been performed or when
2426      * the stepsize becomes smaller than is reasonable for machine precision
2427      */
2428     count  = 0;
2429     bDone  = FALSE;
2430     bAbort = FALSE;
2431     while (!bDone && !bAbort)
2432     {
2433         bAbort = (nsteps >= 0) && (count == nsteps);
2434
2435         /* set new coordinates, except for first step */
2436         bool validStep = true;
2437         if (count > 0)
2438         {
2439             validStep = do_em_step(cr, inputrec, mdatoms, s_min, stepsize, &s_min->f, 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), mdatoms->tmass,
2478                                                  enerd, nullptr, nullptr, nullptr, nullBox, nullptr,
2479                                                  nullptr, vir, pres, nullptr, mu_tot, constr);
2480
2481                 imdSession->fillEnergyRecord(count, TRUE);
2482
2483                 const bool do_dr = do_per_step(steps_accepted, inputrec->nstdisreout);
2484                 const bool do_or = do_per_step(steps_accepted, inputrec->nstorireout);
2485                 energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf), TRUE, do_dr, do_or,
2486                                                    fplog, count, count, fcd, nullptr);
2487                 fflush(fplog);
2488             }
2489         }
2490
2491         /* Now if the new energy is smaller than the previous...
2492          * or if this is the first step!
2493          * or if we did random steps!
2494          */
2495
2496         if ((count == 0) || (s_try->epot < s_min->epot))
2497         {
2498             steps_accepted++;
2499
2500             /* Test whether the convergence criterion is met...  */
2501             bDone = (s_try->fmax < inputrec->em_tol);
2502
2503             /* Copy the arrays for force, positions and energy  */
2504             /* The 'Min' array always holds the coords and forces of the minimal
2505                sampled energy  */
2506             swap_em_state(&s_min, &s_try);
2507             if (count > 0)
2508             {
2509                 ustep *= 1.2;
2510             }
2511
2512             /* Write to trn, if necessary */
2513             do_x = do_per_step(steps_accepted, inputrec->nstxout);
2514             do_f = do_per_step(steps_accepted, inputrec->nstfout);
2515             write_em_traj(fplog, cr, outf, do_x, do_f, nullptr, top_global, inputrec, count, s_min,
2516                           state_global, observablesHistory);
2517         }
2518         else
2519         {
2520             /* If energy is not smaller make the step smaller...  */
2521             ustep *= 0.5;
2522
2523             if (DOMAINDECOMP(cr) && s_min->s.ddp_count != cr->dd->ddp_count)
2524             {
2525                 /* Reload the old state */
2526                 em_dd_partition_system(fplog, mdlog, count, cr, top_global, inputrec, imdSession,
2527                                        pull_work, s_min, &top, mdAtoms, fr, vsite, constr, nrnb, wcycle);
2528             }
2529         }
2530
2531         // If the force is very small after finishing minimization,
2532         // we risk dividing by zero when calculating the step size.
2533         // So we check first if the minimization has stopped before
2534         // trying to obtain a new step size.
2535         if (!bDone)
2536         {
2537             /* Determine new step  */
2538             stepsize = ustep / s_min->fmax;
2539         }
2540
2541         /* Check if stepsize is too small, with 1 nm as a characteristic length */
2542 #if GMX_DOUBLE
2543         if (count == nsteps || ustep < 1e-12)
2544 #else
2545         if (count == nsteps || ustep < 1e-6)
2546 #endif
2547         {
2548             if (MASTER(cr))
2549             {
2550                 warn_step(fplog, inputrec->em_tol, s_min->fmax, count == nsteps, constr != nullptr);
2551             }
2552             bAbort = TRUE;
2553         }
2554
2555         /* Send IMD energies and positions, if bIMD is TRUE. */
2556         if (imdSession->run(count, TRUE, state_global->box,
2557                             MASTER(cr) ? state_global->x.rvec_array() : nullptr, 0)
2558             && MASTER(cr))
2559         {
2560             imdSession->sendPositionsAndEnergies();
2561         }
2562
2563         count++;
2564     } /* End of the loop  */
2565
2566     /* Print some data...  */
2567     if (MASTER(cr))
2568     {
2569         fprintf(stderr, "\nwriting lowest energy coordinates.\n");
2570     }
2571     write_em_traj(fplog, cr, outf, TRUE, inputrec->nstfout != 0, ftp2fn(efSTO, nfile, fnm),
2572                   top_global, inputrec, count, s_min, state_global, observablesHistory);
2573
2574     if (MASTER(cr))
2575     {
2576         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
2577
2578         print_converged(stderr, SD, inputrec->em_tol, count, bDone, nsteps, s_min, sqrtNumAtoms);
2579         print_converged(fplog, SD, inputrec->em_tol, count, bDone, nsteps, s_min, sqrtNumAtoms);
2580     }
2581
2582     finish_em(cr, outf, walltime_accounting, wcycle);
2583
2584     /* To print the actual number of steps we needed somewhere */
2585     inputrec->nsteps = count;
2586
2587     walltime_accounting_set_nsteps_done(walltime_accounting, count);
2588 }
2589
2590 void LegacySimulator::do_nm()
2591 {
2592     const char*         NM = "Normal Mode Analysis";
2593     int                 nnodes;
2594     gmx_localtop_t      top(top_global->ffparams);
2595     gmx_global_stat_t   gstat;
2596     t_graph*            graph;
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, &graph, 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{
2711         fplog,      mdlog,     cr,      ms,     top_global,      &top,  inputrec,
2712         imdSession, pull_work, nrnb,    wcycle, gstat,           vsite, constr,
2713         fcd,        graph,     mdAtoms, fr,     runScheduleWork, enerd
2714     };
2715     energyEvaluator.run(&state_work, mu_tot, vir, pres, -1, TRUE);
2716     cr->nnodes = nnodes;
2717
2718     /* if forces are not small, warn user */
2719     get_state_f_norm_max(cr, &(inputrec->opts), mdatoms, &state_work);
2720
2721     GMX_LOG(mdlog.warning).appendTextFormatted("Maximum force:%12.5e", state_work.fmax);
2722     if (state_work.fmax > 1.0e-3)
2723     {
2724         GMX_LOG(mdlog.warning)
2725                 .appendText(
2726                         "The force is probably not small enough to "
2727                         "ensure that you are at a minimum.\n"
2728                         "Be aware that negative eigenvalues may occur\n"
2729                         "when the resulting matrix is diagonalized.");
2730     }
2731
2732     /***********************************************************
2733      *
2734      *      Loop over all pairs in matrix
2735      *
2736      *      do_force called twice. Once with positive and
2737      *      once with negative displacement
2738      *
2739      ************************************************************/
2740
2741     /* Steps are divided one by one over the nodes */
2742     bool bNS          = true;
2743     auto state_work_x = makeArrayRef(state_work.s.x);
2744     auto state_work_f = makeArrayRef(state_work.f);
2745     for (index aid = cr->nodeid; aid < ssize(atom_index); aid += nnodes)
2746     {
2747         size_t atom = atom_index[aid];
2748         for (size_t d = 0; d < DIM; d++)
2749         {
2750             int64_t step        = 0;
2751             int     force_flags = GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES;
2752             double  t           = 0;
2753
2754             x_min = state_work_x[atom][d];
2755
2756             for (unsigned int dx = 0; (dx < 2); dx++)
2757             {
2758                 if (dx == 0)
2759                 {
2760                     state_work_x[atom][d] = x_min - der_range;
2761                 }
2762                 else
2763                 {
2764                     state_work_x[atom][d] = x_min + der_range;
2765                 }
2766
2767                 /* Make evaluate_energy do a single node force calculation */
2768                 cr->nnodes = 1;
2769                 if (shellfc)
2770                 {
2771                     /* Now is the time to relax the shells */
2772                     relax_shell_flexcon(fplog, cr, ms, mdrunOptions.verbose, nullptr, step, inputrec,
2773                                         imdSession, pull_work, bNS, force_flags, &top, constr, enerd,
2774                                         fcd, state_work.s.natoms, state_work.s.x.arrayRefWithPadding(),
2775                                         state_work.s.v.arrayRefWithPadding(), state_work.s.box,
2776                                         state_work.s.lambda, &state_work.s.hist,
2777                                         state_work.f.arrayRefWithPadding(), vir, mdatoms, nrnb,
2778                                         wcycle, graph, shellfc, fr, runScheduleWork, t, mu_tot,
2779                                         vsite, DDBalanceRegionHandler(nullptr));
2780                     bNS = false;
2781                     step++;
2782                 }
2783                 else
2784                 {
2785                     energyEvaluator.run(&state_work, mu_tot, vir, pres, aid * 2 + dx, FALSE);
2786                 }
2787
2788                 cr->nnodes = nnodes;
2789
2790                 if (dx == 0)
2791                 {
2792                     std::copy(state_work_f.begin(), state_work_f.begin() + atom_index.size(),
2793                               fneg.begin());
2794                 }
2795             }
2796
2797             /* x is restored to original */
2798             state_work_x[atom][d] = x_min;
2799
2800             for (size_t j = 0; j < atom_index.size(); j++)
2801             {
2802                 for (size_t k = 0; (k < DIM); k++)
2803                 {
2804                     dfdx[j][k] = -(state_work_f[atom_index[j]][k] - fneg[j][k]) / (2 * der_range);
2805                 }
2806             }
2807
2808             if (!bIsMaster)
2809             {
2810 #if GMX_MPI
2811 #    define mpi_type GMX_MPI_REAL
2812                 MPI_Send(dfdx[0], atom_index.size() * DIM, mpi_type, MASTER(cr), cr->nodeid,
2813                          cr->mpi_comm_mygroup);
2814 #endif
2815             }
2816             else
2817             {
2818                 for (index node = 0; (node < nnodes && aid + node < ssize(atom_index)); node++)
2819                 {
2820                     if (node > 0)
2821                     {
2822 #if GMX_MPI
2823                         MPI_Status stat;
2824                         MPI_Recv(dfdx[0], atom_index.size() * DIM, mpi_type, node, node,
2825                                  cr->mpi_comm_mygroup, &stat);
2826 #    undef mpi_type
2827 #endif
2828                     }
2829
2830                     row = (aid + node) * DIM + d;
2831
2832                     for (size_t j = 0; j < atom_index.size(); j++)
2833                     {
2834                         for (size_t k = 0; k < DIM; k++)
2835                         {
2836                             col = j * DIM + k;
2837
2838                             if (bSparse)
2839                             {
2840                                 if (col >= row && dfdx[j][k] != 0.0)
2841                                 {
2842                                     gmx_sparsematrix_increment_value(sparse_matrix, row, col, dfdx[j][k]);
2843                                 }
2844                             }
2845                             else
2846                             {
2847                                 full_matrix[row * sz + col] = dfdx[j][k];
2848                             }
2849                         }
2850                     }
2851                 }
2852             }
2853
2854             if (mdrunOptions.verbose && fplog)
2855             {
2856                 fflush(fplog);
2857             }
2858         }
2859         /* write progress */
2860         if (bIsMaster && mdrunOptions.verbose)
2861         {
2862             fprintf(stderr, "\rFinished step %d out of %td",
2863                     std::min<int>(atom + nnodes, atom_index.size()), ssize(atom_index));
2864             fflush(stderr);
2865         }
2866     }
2867
2868     if (bIsMaster)
2869     {
2870         fprintf(stderr, "\n\nWriting Hessian...\n");
2871         gmx_mtxio_write(ftp2fn(efMTX, nfile, fnm), sz, sz, full_matrix, sparse_matrix);
2872     }
2873
2874     finish_em(cr, outf, walltime_accounting, wcycle);
2875
2876     walltime_accounting_set_nsteps_done(walltime_accounting, atom_index.size() * 2);
2877 }
2878
2879 } // namespace gmx