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