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