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