42b34a45ebb65dfad3a75720547581f460664f7d
[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                                          nullptr,
1395                                          nullBox,
1396                                          PTCouplingArrays(),
1397                                          0,
1398                                          vir,
1399                                          pres,
1400                                          nullptr,
1401                                          mu_tot,
1402                                          constr);
1403
1404         EnergyOutput::printHeader(fplog, step, step);
1405         energyOutput.printStepToEnergyFile(
1406                 mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step, step, fr->fcdata.get(), nullptr);
1407     }
1408
1409     /* Estimate/guess the initial stepsize */
1410     stepsize = inputrec->em_stepsize / s_min->fnorm;
1411
1412     if (MASTER(cr))
1413     {
1414         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1415         fprintf(stderr, "   F-max             = %12.5e on atom %d\n", s_min->fmax, s_min->a_fmax + 1);
1416         fprintf(stderr, "   F-Norm            = %12.5e\n", s_min->fnorm / sqrtNumAtoms);
1417         fprintf(stderr, "\n");
1418         /* and copy to the log file too... */
1419         fprintf(fplog, "   F-max             = %12.5e on atom %d\n", s_min->fmax, s_min->a_fmax + 1);
1420         fprintf(fplog, "   F-Norm            = %12.5e\n", s_min->fnorm / sqrtNumAtoms);
1421         fprintf(fplog, "\n");
1422     }
1423     /* Start the loop over CG steps.
1424      * Each successful step is counted, and we continue until
1425      * we either converge or reach the max number of steps.
1426      */
1427     converged = FALSE;
1428     for (step = 0; (number_steps < 0 || step <= number_steps) && !converged; step++)
1429     {
1430
1431         /* start taking steps in a new direction
1432          * First time we enter the routine, beta=0, and the direction is
1433          * simply the negative gradient.
1434          */
1435
1436         /* Calculate the new direction in p, and the gradient in this direction, gpa */
1437         gmx::ArrayRef<gmx::RVec>       pm  = s_min->s.cg_p;
1438         gmx::ArrayRef<const gmx::RVec> sfm = s_min->f.view().force();
1439         double                         gpa = 0;
1440         int                            gf  = 0;
1441         for (int i = 0; i < mdatoms->homenr; i++)
1442         {
1443             if (mdatoms->cFREEZE)
1444             {
1445                 gf = mdatoms->cFREEZE[i];
1446             }
1447             for (m = 0; m < DIM; m++)
1448             {
1449                 if (!inputrec->opts.nFreeze[gf][m])
1450                 {
1451                     pm[i][m] = sfm[i][m] + beta * pm[i][m];
1452                     gpa -= pm[i][m] * sfm[i][m];
1453                     /* f is negative gradient, thus the sign */
1454                 }
1455                 else
1456                 {
1457                     pm[i][m] = 0;
1458                 }
1459             }
1460         }
1461
1462         /* Sum the gradient along the line across CPUs */
1463         if (PAR(cr))
1464         {
1465             gmx_sumd(1, &gpa, cr);
1466         }
1467
1468         /* Calculate the norm of the search vector */
1469         get_f_norm_max(cr, &(inputrec->opts), mdatoms, pm, &pnorm, nullptr, nullptr);
1470
1471         /* Just in case stepsize reaches zero due to numerical precision... */
1472         if (stepsize <= 0)
1473         {
1474             stepsize = inputrec->em_stepsize / pnorm;
1475         }
1476
1477         /*
1478          * Double check the value of the derivative in the search direction.
1479          * If it is positive it must be due to the old information in the
1480          * CG formula, so just remove that and start over with beta=0.
1481          * This corresponds to a steepest descent step.
1482          */
1483         if (gpa > 0)
1484         {
1485             beta = 0;
1486             step--;   /* Don't count this step since we are restarting */
1487             continue; /* Go back to the beginning of the big for-loop */
1488         }
1489
1490         /* Calculate minimum allowed stepsize, before the average (norm)
1491          * relative change in coordinate is smaller than precision
1492          */
1493         minstep      = 0;
1494         auto s_min_x = makeArrayRef(s_min->s.x);
1495         for (int i = 0; i < mdatoms->homenr; i++)
1496         {
1497             for (m = 0; m < DIM; m++)
1498             {
1499                 tmp = fabs(s_min_x[i][m]);
1500                 if (tmp < 1.0)
1501                 {
1502                     tmp = 1.0;
1503                 }
1504                 tmp = pm[i][m] / tmp;
1505                 minstep += tmp * tmp;
1506             }
1507         }
1508         /* Add up from all CPUs */
1509         if (PAR(cr))
1510         {
1511             gmx_sumd(1, &minstep, cr);
1512         }
1513
1514         minstep = GMX_REAL_EPS / sqrt(minstep / (3 * top_global.natoms));
1515
1516         if (stepsize < minstep)
1517         {
1518             converged = TRUE;
1519             break;
1520         }
1521
1522         /* Write coordinates if necessary */
1523         do_x = do_per_step(step, inputrec->nstxout);
1524         do_f = do_per_step(step, inputrec->nstfout);
1525
1526         write_em_traj(
1527                 fplog, cr, outf, do_x, do_f, nullptr, top_global, inputrec, step, s_min, state_global, observablesHistory);
1528
1529         /* Take a step downhill.
1530          * In theory, we should minimize the function along this direction.
1531          * That is quite possible, but it turns out to take 5-10 function evaluations
1532          * for each line. However, we dont really need to find the exact minimum -
1533          * it is much better to start a new CG step in a modified direction as soon
1534          * as we are close to it. This will save a lot of energy evaluations.
1535          *
1536          * In practice, we just try to take a single step.
1537          * If it worked (i.e. lowered the energy), we increase the stepsize but
1538          * the continue straight to the next CG step without trying to find any minimum.
1539          * If it didn't work (higher energy), there must be a minimum somewhere between
1540          * the old position and the new one.
1541          *
1542          * Due to the finite numerical accuracy, it turns out that it is a good idea
1543          * to even accept a SMALL increase in energy, if the derivative is still downhill.
1544          * This leads to lower final energies in the tests I've done. / Erik
1545          */
1546         s_a->epot = s_min->epot;
1547         a         = 0.0;
1548         c         = a + stepsize; /* reference position along line is zero */
1549
1550         if (haveDDAtomOrdering(*cr) && s_min->s.ddp_count < cr->dd->ddp_count)
1551         {
1552             em_dd_partition_system(fplog,
1553                                    mdlog,
1554                                    step,
1555                                    cr,
1556                                    top_global,
1557                                    inputrec,
1558                                    imdSession,
1559                                    pull_work,
1560                                    s_min,
1561                                    top,
1562                                    mdAtoms,
1563                                    fr,
1564                                    vsite,
1565                                    constr,
1566                                    nrnb,
1567                                    wcycle);
1568         }
1569
1570         /* Take a trial step (new coords in s_c) */
1571         do_em_step(cr, inputrec, mdatoms, s_min, c, s_min->s.cg_p.constArrayRefWithPadding(), s_c, constr, -1);
1572
1573         neval++;
1574         /* Calculate energy for the trial step */
1575         energyEvaluator.run(s_c, mu_tot, vir, pres, -1, FALSE, step);
1576         observablesReducer.markAsReadyToReduce();
1577
1578         /* Calc derivative along line */
1579         const rvec*                    pc  = s_c->s.cg_p.rvec_array();
1580         gmx::ArrayRef<const gmx::RVec> sfc = s_c->f.view().force();
1581         double                         gpc = 0;
1582         for (int i = 0; i < mdatoms->homenr; i++)
1583         {
1584             for (m = 0; m < DIM; m++)
1585             {
1586                 gpc -= pc[i][m] * sfc[i][m]; /* f is negative gradient, thus the sign */
1587             }
1588         }
1589         /* Sum the gradient along the line across CPUs */
1590         if (PAR(cr))
1591         {
1592             gmx_sumd(1, &gpc, cr);
1593         }
1594
1595         /* This is the max amount of increase in energy we tolerate */
1596         tmp = std::sqrt(GMX_REAL_EPS) * fabs(s_a->epot);
1597
1598         /* Accept the step if the energy is lower, or if it is not significantly higher
1599          * and the line derivative is still negative.
1600          */
1601         if (s_c->epot < s_a->epot || (gpc < 0 && s_c->epot < (s_a->epot + tmp)))
1602         {
1603             foundlower = TRUE;
1604             /* Great, we found a better energy. Increase step for next iteration
1605              * if we are still going down, decrease it otherwise
1606              */
1607             if (gpc < 0)
1608             {
1609                 stepsize *= 1.618034; /* The golden section */
1610             }
1611             else
1612             {
1613                 stepsize *= 0.618034; /* 1/golden section */
1614             }
1615         }
1616         else
1617         {
1618             /* New energy is the same or higher. We will have to do some work
1619              * to find a smaller value in the interval. Take smaller step next time!
1620              */
1621             foundlower = FALSE;
1622             stepsize *= 0.618034;
1623         }
1624
1625
1626         /* OK, if we didn't find a lower value we will have to locate one now - there must
1627          * be one in the interval [a=0,c].
1628          * The same thing is valid here, though: Don't spend dozens of iterations to find
1629          * the line minimum. We try to interpolate based on the derivative at the endpoints,
1630          * and only continue until we find a lower value. In most cases this means 1-2 iterations.
1631          *
1632          * I also have a safeguard for potentially really pathological functions so we never
1633          * take more than 20 steps before we give up ...
1634          *
1635          * If we already found a lower value we just skip this step and continue to the update.
1636          */
1637         double gpb;
1638         if (!foundlower)
1639         {
1640             nminstep = 0;
1641
1642             do
1643             {
1644                 /* Select a new trial point.
1645                  * If the derivatives at points a & c have different sign we interpolate to zero,
1646                  * otherwise just do a bisection.
1647                  */
1648                 if (gpa < 0 && gpc > 0)
1649                 {
1650                     b = a + gpa * (a - c) / (gpc - gpa);
1651                 }
1652                 else
1653                 {
1654                     b = 0.5 * (a + c);
1655                 }
1656
1657                 /* safeguard if interpolation close to machine accuracy causes errors:
1658                  * never go outside the interval
1659                  */
1660                 if (b <= a || b >= c)
1661                 {
1662                     b = 0.5 * (a + c);
1663                 }
1664
1665                 if (haveDDAtomOrdering(*cr) && s_min->s.ddp_count != cr->dd->ddp_count)
1666                 {
1667                     /* Reload the old state */
1668                     em_dd_partition_system(fplog,
1669                                            mdlog,
1670                                            -1,
1671                                            cr,
1672                                            top_global,
1673                                            inputrec,
1674                                            imdSession,
1675                                            pull_work,
1676                                            s_min,
1677                                            top,
1678                                            mdAtoms,
1679                                            fr,
1680                                            vsite,
1681                                            constr,
1682                                            nrnb,
1683                                            wcycle);
1684                 }
1685
1686                 /* Take a trial step to this new point - new coords in s_b */
1687                 do_em_step(cr, inputrec, mdatoms, s_min, b, s_min->s.cg_p.constArrayRefWithPadding(), s_b, constr, -1);
1688
1689                 neval++;
1690                 /* Calculate energy for the trial step */
1691                 energyEvaluator.run(s_b, mu_tot, vir, pres, -1, FALSE, step);
1692                 observablesReducer.markAsReadyToReduce();
1693
1694                 /* p does not change within a step, but since the domain decomposition
1695                  * might change, we have to use cg_p of s_b here.
1696                  */
1697                 const rvec*                    pb  = s_b->s.cg_p.rvec_array();
1698                 gmx::ArrayRef<const gmx::RVec> sfb = s_b->f.view().force();
1699                 gpb                                = 0;
1700                 for (int i = 0; i < mdatoms->homenr; i++)
1701                 {
1702                     for (m = 0; m < DIM; m++)
1703                     {
1704                         gpb -= pb[i][m] * sfb[i][m]; /* f is negative gradient, thus the sign */
1705                     }
1706                 }
1707                 /* Sum the gradient along the line across CPUs */
1708                 if (PAR(cr))
1709                 {
1710                     gmx_sumd(1, &gpb, cr);
1711                 }
1712
1713                 if (debug)
1714                 {
1715                     fprintf(debug, "CGE: EpotA %f EpotB %f EpotC %f gpb %f\n", s_a->epot, s_b->epot, s_c->epot, gpb);
1716                 }
1717
1718                 epot_repl = s_b->epot;
1719
1720                 /* Keep one of the intervals based on the value of the derivative at the new point */
1721                 if (gpb > 0)
1722                 {
1723                     /* Replace c endpoint with b */
1724                     swap_em_state(&s_b, &s_c);
1725                     c   = b;
1726                     gpc = gpb;
1727                 }
1728                 else
1729                 {
1730                     /* Replace a endpoint with b */
1731                     swap_em_state(&s_b, &s_a);
1732                     a   = b;
1733                     gpa = gpb;
1734                 }
1735
1736                 /*
1737                  * Stop search as soon as we find a value smaller than the endpoints.
1738                  * Never run more than 20 steps, no matter what.
1739                  */
1740                 nminstep++;
1741             } while ((epot_repl > s_a->epot || epot_repl > s_c->epot) && (nminstep < 20));
1742
1743             if (std::fabs(epot_repl - s_min->epot) < fabs(s_min->epot) * GMX_REAL_EPS || nminstep >= 20)
1744             {
1745                 /* OK. We couldn't find a significantly lower energy.
1746                  * If beta==0 this was steepest descent, and then we give up.
1747                  * If not, set beta=0 and restart with steepest descent before quitting.
1748                  */
1749                 if (beta == 0.0)
1750                 {
1751                     /* Converged */
1752                     converged = TRUE;
1753                     break;
1754                 }
1755                 else
1756                 {
1757                     /* Reset memory before giving up */
1758                     beta = 0.0;
1759                     continue;
1760                 }
1761             }
1762
1763             /* Select min energy state of A & C, put the best in B.
1764              */
1765             if (s_c->epot < s_a->epot)
1766             {
1767                 if (debug)
1768                 {
1769                     fprintf(debug, "CGE: C (%f) is lower than A (%f), moving C to B\n", s_c->epot, s_a->epot);
1770                 }
1771                 swap_em_state(&s_b, &s_c);
1772                 gpb = gpc;
1773             }
1774             else
1775             {
1776                 if (debug)
1777                 {
1778                     fprintf(debug, "CGE: A (%f) is lower than C (%f), moving A to B\n", s_a->epot, s_c->epot);
1779                 }
1780                 swap_em_state(&s_b, &s_a);
1781                 gpb = gpa;
1782             }
1783         }
1784         else
1785         {
1786             if (debug)
1787             {
1788                 fprintf(debug, "CGE: Found a lower energy %f, moving C to B\n", s_c->epot);
1789             }
1790             swap_em_state(&s_b, &s_c);
1791             gpb = gpc;
1792         }
1793
1794         /* new search direction */
1795         /* beta = 0 means forget all memory and restart with steepest descents. */
1796         if (nstcg && ((step % nstcg) == 0))
1797         {
1798             beta = 0.0;
1799         }
1800         else
1801         {
1802             /* s_min->fnorm cannot be zero, because then we would have converged
1803              * and broken out.
1804              */
1805
1806             /* Polak-Ribiere update.
1807              * Change to fnorm2/fnorm2_old for Fletcher-Reeves
1808              */
1809             beta = pr_beta(cr, &inputrec->opts, mdatoms, top_global, s_min, s_b);
1810         }
1811         /* Limit beta to prevent oscillations */
1812         if (fabs(beta) > 5.0)
1813         {
1814             beta = 0.0;
1815         }
1816
1817
1818         /* update positions */
1819         swap_em_state(&s_min, &s_b);
1820         gpa = gpb;
1821
1822         /* Print it if necessary */
1823         if (MASTER(cr))
1824         {
1825             if (mdrunOptions.verbose)
1826             {
1827                 double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1828                 fprintf(stderr,
1829                         "\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n",
1830                         step,
1831                         s_min->epot,
1832                         s_min->fnorm / sqrtNumAtoms,
1833                         s_min->fmax,
1834                         s_min->a_fmax + 1);
1835                 fflush(stderr);
1836             }
1837             /* Store the new (lower) energies */
1838             matrix nullBox = {};
1839             energyOutput.addDataAtEnergyStep(false,
1840                                              false,
1841                                              static_cast<double>(step),
1842                                              mdatoms->tmass,
1843                                              enerd,
1844                                              nullptr,
1845                                              nullptr,
1846                                              nullBox,
1847                                              PTCouplingArrays(),
1848                                              0,
1849                                              vir,
1850                                              pres,
1851                                              nullptr,
1852                                              mu_tot,
1853                                              constr);
1854
1855             do_log = do_per_step(step, inputrec->nstlog);
1856             do_ene = do_per_step(step, inputrec->nstenergy);
1857
1858             imdSession->fillEnergyRecord(step, TRUE);
1859
1860             if (do_log)
1861             {
1862                 EnergyOutput::printHeader(fplog, step, step);
1863             }
1864             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf),
1865                                                do_ene,
1866                                                FALSE,
1867                                                FALSE,
1868                                                do_log ? fplog : nullptr,
1869                                                step,
1870                                                step,
1871                                                fr->fcdata.get(),
1872                                                nullptr);
1873         }
1874
1875         /* Send energies and positions to the IMD client if bIMD is TRUE. */
1876         if (MASTER(cr) && imdSession->run(step, TRUE, state_global->box, state_global->x, 0))
1877         {
1878             imdSession->sendPositionsAndEnergies();
1879         }
1880
1881         /* Stop when the maximum force lies below tolerance.
1882          * If we have reached machine precision, converged is already set to true.
1883          */
1884         converged = converged || (s_min->fmax < inputrec->em_tol);
1885         observablesReducer.markAsReadyToReduce();
1886     } /* End of the loop */
1887
1888     if (converged)
1889     {
1890         step--; /* we never took that last step in this case */
1891     }
1892     if (s_min->fmax > inputrec->em_tol)
1893     {
1894         if (MASTER(cr))
1895         {
1896             warn_step(fplog, inputrec->em_tol, s_min->fmax, step - 1 == number_steps, FALSE);
1897         }
1898         converged = FALSE;
1899     }
1900
1901     if (MASTER(cr))
1902     {
1903         /* If we printed energy and/or logfile last step (which was the last step)
1904          * we don't have to do it again, but otherwise print the final values.
1905          */
1906         if (!do_log)
1907         {
1908             /* Write final value to log since we didn't do anything the last step */
1909             EnergyOutput::printHeader(fplog, step, step);
1910         }
1911         if (!do_ene || !do_log)
1912         {
1913             /* Write final energy file entries */
1914             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf),
1915                                                !do_ene,
1916                                                FALSE,
1917                                                FALSE,
1918                                                !do_log ? fplog : nullptr,
1919                                                step,
1920                                                step,
1921                                                fr->fcdata.get(),
1922                                                nullptr);
1923         }
1924     }
1925
1926     /* Print some stuff... */
1927     if (MASTER(cr))
1928     {
1929         fprintf(stderr, "\nwriting lowest energy coordinates.\n");
1930     }
1931
1932     /* IMPORTANT!
1933      * For accurate normal mode calculation it is imperative that we
1934      * store the last conformation into the full precision binary trajectory.
1935      *
1936      * However, we should only do it if we did NOT already write this step
1937      * above (which we did if do_x or do_f was true).
1938      */
1939     /* Note that with 0 < nstfout != nstxout we can end up with two frames
1940      * in the trajectory with the same step number.
1941      */
1942     do_x = !do_per_step(step, inputrec->nstxout);
1943     do_f = (inputrec->nstfout > 0 && !do_per_step(step, inputrec->nstfout));
1944
1945     write_em_traj(
1946             fplog, cr, outf, do_x, do_f, ftp2fn(efSTO, nfile, fnm), top_global, inputrec, step, s_min, state_global, observablesHistory);
1947
1948
1949     if (MASTER(cr))
1950     {
1951         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
1952         print_converged(stderr, CG, inputrec->em_tol, step, converged, number_steps, s_min, sqrtNumAtoms);
1953         print_converged(fplog, CG, inputrec->em_tol, step, converged, number_steps, s_min, sqrtNumAtoms);
1954
1955         fprintf(fplog, "\nPerformed %d energy evaluations in total.\n", neval);
1956     }
1957
1958     finish_em(cr, outf, walltime_accounting, wcycle);
1959
1960     /* To print the actual number of steps we needed somewhere */
1961     walltime_accounting_set_nsteps_done(walltime_accounting, step);
1962 }
1963
1964
1965 void LegacySimulator::do_lbfgs()
1966 {
1967     static const char* LBFGS = "Low-Memory BFGS Minimizer";
1968     em_state_t         ems;
1969     gmx_global_stat_t  gstat;
1970     auto*              mdatoms = mdAtoms->mdatoms();
1971
1972     GMX_LOG(mdlog.info)
1973             .asParagraph()
1974             .appendText(
1975                     "Note that activating L-BFGS energy minimization via the "
1976                     "integrator .mdp option and the command gmx mdrun may "
1977                     "be available in a different form in a future version of GROMACS, "
1978                     "e.g. gmx minimize and an .mdp option.");
1979
1980     if (haveDDAtomOrdering(*cr))
1981     {
1982         gmx_fatal(FARGS, "L_BFGS is currently not supported");
1983     }
1984     if (PAR(cr))
1985     {
1986         gmx_fatal(FARGS, "L-BFGS minimization only supports a single rank");
1987     }
1988
1989     if (nullptr != constr)
1990     {
1991         gmx_fatal(
1992                 FARGS,
1993                 "The combination of constraints and L-BFGS minimization is not implemented. Either "
1994                 "do not use constraints, or use another minimizer (e.g. steepest descent).");
1995     }
1996
1997     const int n        = 3 * state_global->natoms;
1998     const int nmaxcorr = inputrec->nbfgscorr;
1999
2000     std::vector<real> p(n);
2001     std::vector<real> rho(nmaxcorr);
2002     std::vector<real> alpha(nmaxcorr);
2003
2004     std::vector<std::vector<real>> dx(nmaxcorr);
2005     for (auto& dxCorr : dx)
2006     {
2007         dxCorr.resize(n);
2008     }
2009
2010     std::vector<std::vector<real>> dg(nmaxcorr);
2011     for (auto& dgCorr : dg)
2012     {
2013         dgCorr.resize(n);
2014     }
2015
2016     int step  = 0;
2017     int neval = 0;
2018
2019     ObservablesReducer observablesReducer = observablesReducerBuilder->build();
2020
2021     /* Init em */
2022     init_em(fplog,
2023             mdlog,
2024             LBFGS,
2025             cr,
2026             inputrec,
2027             imdSession,
2028             pull_work,
2029             state_global,
2030             top_global,
2031             &ems,
2032             top,
2033             nrnb,
2034             fr,
2035             mdAtoms,
2036             &gstat,
2037             vsite,
2038             constr,
2039             nullptr);
2040     const bool        simulationsShareState = false;
2041     gmx_mdoutf*       outf                  = init_mdoutf(fplog,
2042                                    nfile,
2043                                    fnm,
2044                                    mdrunOptions,
2045                                    cr,
2046                                    outputProvider,
2047                                    mdModulesNotifiers,
2048                                    inputrec,
2049                                    top_global,
2050                                    nullptr,
2051                                    wcycle,
2052                                    StartingBehavior::NewSimulation,
2053                                    simulationsShareState,
2054                                    ms);
2055     gmx::EnergyOutput energyOutput(mdoutf_get_fp_ene(outf),
2056                                    top_global,
2057                                    *inputrec,
2058                                    pull_work,
2059                                    nullptr,
2060                                    false,
2061                                    StartingBehavior::NewSimulation,
2062                                    simulationsShareState,
2063                                    mdModulesNotifiers);
2064
2065     const int start = 0;
2066     const int end   = mdatoms->homenr;
2067
2068     /* We need 4 working states */
2069     em_state_t  s0{}, s1{}, s2{}, s3{};
2070     em_state_t* sa   = &s0;
2071     em_state_t* sb   = &s1;
2072     em_state_t* sc   = &s2;
2073     em_state_t* last = &s3;
2074     /* Initialize by copying the state from ems (we could skip x and f here) */
2075     *sa = ems;
2076     *sb = ems;
2077     *sc = ems;
2078
2079     /* Print to log file */
2080     print_em_start(fplog, cr, walltime_accounting, wcycle, LBFGS);
2081
2082     /* Max number of steps */
2083     const int number_steps = inputrec->nsteps;
2084
2085     /* Create a 3*natoms index to tell whether each degree of freedom is frozen */
2086     std::vector<bool> frozen(n);
2087     int               gf = 0;
2088     for (int i = start; i < end; i++)
2089     {
2090         if (mdatoms->cFREEZE)
2091         {
2092             gf = mdatoms->cFREEZE[i];
2093         }
2094         for (int m = 0; m < DIM; m++)
2095         {
2096             frozen[3 * i + m] = (inputrec->opts.nFreeze[gf][m] != 0);
2097         }
2098     }
2099     if (MASTER(cr))
2100     {
2101         sp_header(stderr, LBFGS, inputrec->em_tol, number_steps);
2102     }
2103     if (fplog)
2104     {
2105         sp_header(fplog, LBFGS, inputrec->em_tol, number_steps);
2106     }
2107
2108     if (vsite)
2109     {
2110         vsite->construct(state_global->x, {}, state_global->box, VSiteOperation::Positions);
2111     }
2112
2113     /* Call the force routine and some auxiliary (neighboursearching etc.) */
2114     /* do_force always puts the charge groups in the box and shifts again
2115      * We do not unshift, so molecules are always whole
2116      */
2117     neval++;
2118     EnergyEvaluator energyEvaluator{ fplog,
2119                                      mdlog,
2120                                      cr,
2121                                      ms,
2122                                      top_global,
2123                                      top,
2124                                      inputrec,
2125                                      imdSession,
2126                                      pull_work,
2127                                      nrnb,
2128                                      wcycle,
2129                                      gstat,
2130                                      &observablesReducer,
2131                                      vsite,
2132                                      constr,
2133                                      mdAtoms,
2134                                      fr,
2135                                      runScheduleWork,
2136                                      enerd };
2137     rvec            mu_tot;
2138     tensor          vir;
2139     tensor          pres;
2140     energyEvaluator.run(&ems, mu_tot, vir, pres, -1, TRUE, step);
2141
2142     if (MASTER(cr))
2143     {
2144         /* Copy stuff to the energy bin for easy printing etc. */
2145         matrix nullBox = {};
2146         energyOutput.addDataAtEnergyStep(false,
2147                                          false,
2148                                          static_cast<double>(step),
2149                                          mdatoms->tmass,
2150                                          enerd,
2151                                          nullptr,
2152                                          nullptr,
2153                                          nullBox,
2154                                          PTCouplingArrays(),
2155                                          0,
2156                                          vir,
2157                                          pres,
2158                                          nullptr,
2159                                          mu_tot,
2160                                          constr);
2161
2162         EnergyOutput::printHeader(fplog, step, step);
2163         energyOutput.printStepToEnergyFile(
2164                 mdoutf_get_fp_ene(outf), TRUE, FALSE, FALSE, fplog, step, step, fr->fcdata.get(), nullptr);
2165     }
2166
2167     /* Set the initial step.
2168      * since it will be multiplied by the non-normalized search direction
2169      * vector (force vector the first time), we scale it by the
2170      * norm of the force.
2171      */
2172
2173     if (MASTER(cr))
2174     {
2175         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
2176         fprintf(stderr, "Using %d BFGS correction steps.\n\n", nmaxcorr);
2177         fprintf(stderr, "   F-max             = %12.5e on atom %d\n", ems.fmax, ems.a_fmax + 1);
2178         fprintf(stderr, "   F-Norm            = %12.5e\n", ems.fnorm / sqrtNumAtoms);
2179         fprintf(stderr, "\n");
2180         /* and copy to the log file too... */
2181         fprintf(fplog, "Using %d BFGS correction steps.\n\n", nmaxcorr);
2182         fprintf(fplog, "   F-max             = %12.5e on atom %d\n", ems.fmax, ems.a_fmax + 1);
2183         fprintf(fplog, "   F-Norm            = %12.5e\n", ems.fnorm / sqrtNumAtoms);
2184         fprintf(fplog, "\n");
2185     }
2186
2187     // Point is an index to the memory of search directions, where 0 is the first one.
2188     int point = 0;
2189
2190     // Set initial search direction to the force (-gradient), or 0 for frozen particles.
2191     real* fInit = static_cast<real*>(ems.f.view().force().data()[0]);
2192     for (int i = 0; i < n; i++)
2193     {
2194         if (!frozen[i])
2195         {
2196             dx[point][i] = fInit[i]; /* Initial search direction */
2197         }
2198         else
2199         {
2200             dx[point][i] = 0;
2201         }
2202     }
2203
2204     // Stepsize will be modified during the search, and actually it is not critical
2205     // (the main efficiency in the algorithm comes from changing directions), but
2206     // we still need an initial value, so estimate it as the inverse of the norm
2207     // so we take small steps where the potential fluctuates a lot.
2208     double stepsize = 1.0 / ems.fnorm;
2209
2210     /* Start the loop over BFGS steps.
2211      * Each successful step is counted, and we continue until
2212      * we either converge or reach the max number of steps.
2213      */
2214
2215     bool do_log = true;
2216     bool do_ene = true;
2217
2218     int ncorr = 0;
2219
2220     /* Set the gradient from the force */
2221     bool converged = false;
2222     for (int step = 0; (number_steps < 0 || step <= number_steps) && !converged; step++)
2223     {
2224
2225         /* Write coordinates if necessary */
2226         const bool do_x = do_per_step(step, inputrec->nstxout);
2227         const bool do_f = do_per_step(step, inputrec->nstfout);
2228
2229         int mdof_flags = 0;
2230         if (do_x)
2231         {
2232             mdof_flags |= MDOF_X;
2233         }
2234
2235         if (do_f)
2236         {
2237             mdof_flags |= MDOF_F;
2238         }
2239
2240         if (inputrec->bIMD)
2241         {
2242             mdof_flags |= MDOF_IMD;
2243         }
2244
2245         gmx::WriteCheckpointDataHolder checkpointDataHolder;
2246         mdoutf_write_to_trajectory_files(fplog,
2247                                          cr,
2248                                          outf,
2249                                          mdof_flags,
2250                                          top_global.natoms,
2251                                          step,
2252                                          static_cast<real>(step),
2253                                          &ems.s,
2254                                          state_global,
2255                                          observablesHistory,
2256                                          ems.f.view().force(),
2257                                          &checkpointDataHolder);
2258
2259         /* Do the linesearching in the direction dx[point][0..(n-1)] */
2260
2261         /* make s a pointer to current search direction - point=0 first time we get here */
2262         gmx::ArrayRef<const real> s = dx[point];
2263
2264         const real* xx = static_cast<real*>(ems.s.x.rvec_array()[0]);
2265         const real* ff = static_cast<real*>(ems.f.view().force().data()[0]);
2266
2267         // calculate line gradient in position A
2268         double gpa = 0;
2269         for (int i = 0; i < n; i++)
2270         {
2271             gpa -= s[i] * ff[i];
2272         }
2273
2274         /* Calculate minimum allowed stepsize along the line, before the average (norm)
2275          * relative change in coordinate is smaller than precision
2276          */
2277         double minstep = 0;
2278         for (int i = 0; i < n; i++)
2279         {
2280             double tmp = fabs(xx[i]);
2281             if (tmp < 1.0)
2282             {
2283                 tmp = 1.0;
2284             }
2285             tmp = s[i] / tmp;
2286             minstep += tmp * tmp;
2287         }
2288         minstep = GMX_REAL_EPS / sqrt(minstep / n);
2289
2290         if (stepsize < minstep)
2291         {
2292             converged = true;
2293             break;
2294         }
2295
2296         // Before taking any steps along the line, store the old position
2297         *last            = ems;
2298         real*      lastx = static_cast<real*>(last->s.x.data()[0]);
2299         real*      lastf = static_cast<real*>(last->f.view().force().data()[0]);
2300         const real Epot0 = ems.epot;
2301
2302         *sa = ems;
2303
2304         /* Take a step downhill.
2305          * In theory, we should find the actual minimum of the function in this
2306          * direction, somewhere along the line.
2307          * That is quite possible, but it turns out to take 5-10 function evaluations
2308          * for each line. However, we dont really need to find the exact minimum -
2309          * it is much better to start a new BFGS step in a modified direction as soon
2310          * as we are close to it. This will save a lot of energy evaluations.
2311          *
2312          * In practice, we just try to take a single step.
2313          * If it worked (i.e. lowered the energy), we increase the stepsize but
2314          * continue straight to the next BFGS step without trying to find any minimum,
2315          * i.e. we change the search direction too. If the line was smooth, it is
2316          * likely we are in a smooth region, and then it makes sense to take longer
2317          * steps in the modified search direction too.
2318          *
2319          * If it didn't work (higher energy), there must be a minimum somewhere between
2320          * the old position and the new one. Then we need to start by finding a lower
2321          * value before we change search direction. Since the energy was apparently
2322          * quite rough, we need to decrease the step size.
2323          *
2324          * Due to the finite numerical accuracy, it turns out that it is a good idea
2325          * to accept a SMALL increase in energy, if the derivative is still downhill.
2326          * This leads to lower final energies in the tests I've done. / Erik
2327          */
2328
2329         // State "A" is the first position along the line.
2330         // reference position along line is initially zero
2331         real a = 0;
2332
2333         // Check stepsize first. We do not allow displacements
2334         // larger than emstep.
2335         //
2336         real c;
2337         real maxdelta;
2338         do
2339         {
2340             // Pick a new position C by adding stepsize to A.
2341             c = a + stepsize;
2342
2343             // Calculate what the largest change in any individual coordinate
2344             // would be (translation along line * gradient along line)
2345             maxdelta = 0;
2346             for (int i = 0; i < n; i++)
2347             {
2348                 real delta = c * s[i];
2349                 if (delta > maxdelta)
2350                 {
2351                     maxdelta = delta;
2352                 }
2353             }
2354             // If any displacement is larger than the stepsize limit, reduce the step
2355             if (maxdelta > inputrec->em_stepsize)
2356             {
2357                 stepsize *= 0.1;
2358             }
2359         } while (maxdelta > inputrec->em_stepsize);
2360
2361         // Take a trial step and move the coordinate array xc[] to position C
2362         real* xc = static_cast<real*>(sc->s.x.rvec_array()[0]);
2363         for (int i = 0; i < n; i++)
2364         {
2365             xc[i] = lastx[i] + c * s[i];
2366         }
2367
2368         neval++;
2369         // Calculate energy for the trial step in position C
2370         energyEvaluator.run(sc, mu_tot, vir, pres, step, FALSE, step);
2371
2372         // Calc line gradient in position C
2373         real*  fc  = static_cast<real*>(sc->f.view().force()[0]);
2374         double gpc = 0;
2375         for (int i = 0; i < n; i++)
2376         {
2377             gpc -= s[i] * fc[i]; /* f is negative gradient, thus the sign */
2378         }
2379         /* Sum the gradient along the line across CPUs */
2380         if (PAR(cr))
2381         {
2382             gmx_sumd(1, &gpc, cr);
2383         }
2384
2385         // This is the max amount of increase in energy we tolerate.
2386         // By allowing VERY small changes (close to numerical precision) we
2387         // frequently find even better (lower) final energies.
2388         double tmp = std::sqrt(GMX_REAL_EPS) * fabs(sa->epot);
2389
2390         // Accept the step if the energy is lower in the new position C (compared to A),
2391         // or if it is not significantly higher and the line derivative is still negative.
2392         bool foundlower = sc->epot < sa->epot || (gpc < 0 && sc->epot < (sa->epot + tmp));
2393         // If true, great, we found a better energy. We no longer try to alter the
2394         // stepsize, but simply accept this new better position. The we select a new
2395         // search direction instead, which will be much more efficient than continuing
2396         // to take smaller steps along a line. Set fnorm based on the new C position,
2397         // which will be used to update the stepsize to 1/fnorm further down.
2398
2399         // If false, the energy is NOT lower in point C, i.e. it will be the same
2400         // or higher than in point A. In this case it is pointless to move to point C,
2401         // so we will have to do more iterations along the same line to find a smaller
2402         // value in the interval [A=0.0,C].
2403         // Here, A is still 0.0, but that will change when we do a search in the interval
2404         // [0.0,C] below. That search we will do by interpolation or bisection rather
2405         // than with the stepsize, so no need to modify it. For the next search direction
2406         // it will be reset to 1/fnorm anyway.
2407
2408         double step_taken;
2409         if (!foundlower)
2410         {
2411             // OK, if we didn't find a lower value we will have to locate one now - there must
2412             // be one in the interval [a,c].
2413             // The same thing is valid here, though: Don't spend dozens of iterations to find
2414             // the line minimum. We try to interpolate based on the derivative at the endpoints,
2415             // and only continue until we find a lower value. In most cases this means 1-2 iterations.
2416             // I also have a safeguard for potentially really pathological functions so we never
2417             // take more than 20 steps before we give up.
2418             // If we already found a lower value we just skip this step and continue to the update.
2419             real fnorm    = 0;
2420             int  nminstep = 0;
2421             do
2422             {
2423                 // Select a new trial point B in the interval [A,C].
2424                 // If the derivatives at points a & c have different sign we interpolate to zero,
2425                 // otherwise just do a bisection since there might be multiple minima/maxima
2426                 // inside the interval.
2427                 real b;
2428                 if (gpa < 0 && gpc > 0)
2429                 {
2430                     b = a + gpa * (a - c) / (gpc - gpa);
2431                 }
2432                 else
2433                 {
2434                     b = 0.5 * (a + c);
2435                 }
2436
2437                 /* safeguard if interpolation close to machine accuracy causes errors:
2438                  * never go outside the interval
2439                  */
2440                 if (b <= a || b >= c)
2441                 {
2442                     b = 0.5 * (a + c);
2443                 }
2444
2445                 // Take a trial step to point B
2446                 real* xb = static_cast<real*>(sb->s.x.rvec_array()[0]);
2447                 for (int i = 0; i < n; i++)
2448                 {
2449                     xb[i] = lastx[i] + b * s[i];
2450                 }
2451
2452                 neval++;
2453                 // Calculate energy for the trial step in point B
2454                 energyEvaluator.run(sb, mu_tot, vir, pres, step, FALSE, step);
2455                 fnorm = sb->fnorm;
2456
2457                 // Calculate gradient in point B
2458                 real*  fb  = static_cast<real*>(sb->f.view().force()[0]);
2459                 double gpb = 0;
2460                 for (int i = 0; i < n; i++)
2461                 {
2462                     gpb -= s[i] * fb[i]; /* f is negative gradient, thus the sign */
2463                 }
2464                 /* Sum the gradient along the line across CPUs */
2465                 if (PAR(cr))
2466                 {
2467                     gmx_sumd(1, &gpb, cr);
2468                 }
2469
2470                 // Keep one of the intervals [A,B] or [B,C] based on the value of the derivative
2471                 // at the new point B, and rename the endpoints of this new interval A and C.
2472                 if (gpb > 0)
2473                 {
2474                     /* Replace c endpoint with b */
2475                     c = b;
2476                     /* copy state b to c */
2477                     *sc = *sb;
2478                 }
2479                 else
2480                 {
2481                     /* Replace a endpoint with b */
2482                     a = b;
2483                     /* copy state b to a */
2484                     *sa = *sb;
2485                 }
2486
2487                 /*
2488                  * Stop search as soon as we find a value smaller than the endpoints,
2489                  * or if the tolerance is below machine precision.
2490                  * Never run more than 20 steps, no matter what.
2491                  */
2492                 nminstep++;
2493             } while ((sb->epot > sa->epot || sb->epot > sc->epot) && (nminstep < 20));
2494
2495             if (std::fabs(sb->epot - Epot0) < GMX_REAL_EPS || nminstep >= 20)
2496             {
2497                 /* OK. We couldn't find a significantly lower energy.
2498                  * If ncorr==0 this was steepest descent, and then we give up.
2499                  * If not, reset memory to restart as steepest descent before quitting.
2500                  */
2501                 if (ncorr == 0)
2502                 {
2503                     /* Converged */
2504                     converged = true;
2505                     break;
2506                 }
2507                 else
2508                 {
2509                     /* Reset memory */
2510                     ncorr = 0;
2511                     /* Search in gradient direction */
2512                     for (int i = 0; i < n; i++)
2513                     {
2514                         dx[point][i] = ff[i];
2515                     }
2516                     /* Reset stepsize */
2517                     stepsize = 1.0 / fnorm;
2518                     continue;
2519                 }
2520             }
2521
2522             /* Select min energy state of A & C, put the best in xx/ff/Epot
2523              */
2524             if (sc->epot < sa->epot)
2525             {
2526                 /* Use state C */
2527                 ems        = *sc;
2528                 step_taken = c;
2529             }
2530             else
2531             {
2532                 /* Use state A */
2533                 ems        = *sa;
2534                 step_taken = a;
2535             }
2536         }
2537         else
2538         {
2539             /* found lower */
2540             /* Use state C */
2541             ems        = *sc;
2542             step_taken = c;
2543         }
2544
2545         /* Update the memory information, and calculate a new
2546          * approximation of the inverse hessian
2547          */
2548
2549         /* Have new data in Epot, xx, ff */
2550         if (ncorr < nmaxcorr)
2551         {
2552             ncorr++;
2553         }
2554
2555         for (int i = 0; i < n; i++)
2556         {
2557             dg[point][i] = lastf[i] - ff[i];
2558             dx[point][i] *= step_taken;
2559         }
2560
2561         real dgdg = 0;
2562         real dgdx = 0;
2563         for (int i = 0; i < n; i++)
2564         {
2565             dgdg += dg[point][i] * dg[point][i];
2566             dgdx += dg[point][i] * dx[point][i];
2567         }
2568
2569         const real diag = dgdx / dgdg;
2570
2571         rho[point] = 1.0 / dgdx;
2572         point++;
2573
2574         if (point >= nmaxcorr)
2575         {
2576             point = 0;
2577         }
2578
2579         /* Update */
2580         for (int i = 0; i < n; i++)
2581         {
2582             p[i] = ff[i];
2583         }
2584
2585         int cp = point;
2586
2587         /* Recursive update. First go back over the memory points */
2588         for (int k = 0; k < ncorr; k++)
2589         {
2590             cp--;
2591             if (cp < 0)
2592             {
2593                 cp = ncorr - 1;
2594             }
2595
2596             real sq = 0;
2597             for (int i = 0; i < n; i++)
2598             {
2599                 sq += dx[cp][i] * p[i];
2600             }
2601
2602             alpha[cp] = rho[cp] * sq;
2603
2604             for (int i = 0; i < n; i++)
2605             {
2606                 p[i] -= alpha[cp] * dg[cp][i];
2607             }
2608         }
2609
2610         for (int i = 0; i < n; i++)
2611         {
2612             p[i] *= diag;
2613         }
2614
2615         /* And then go forward again */
2616         for (int k = 0; k < ncorr; k++)
2617         {
2618             real yr = 0;
2619             for (int i = 0; i < n; i++)
2620             {
2621                 yr += p[i] * dg[cp][i];
2622             }
2623
2624             real beta = rho[cp] * yr;
2625             beta      = alpha[cp] - beta;
2626
2627             for (int i = 0; i < n; i++)
2628             {
2629                 p[i] += beta * dx[cp][i];
2630             }
2631
2632             cp++;
2633             if (cp >= ncorr)
2634             {
2635                 cp = 0;
2636             }
2637         }
2638
2639         for (int i = 0; i < n; i++)
2640         {
2641             if (!frozen[i])
2642             {
2643                 dx[point][i] = p[i];
2644             }
2645             else
2646             {
2647                 dx[point][i] = 0;
2648             }
2649         }
2650
2651         /* Print it if necessary */
2652         if (MASTER(cr))
2653         {
2654             if (mdrunOptions.verbose)
2655             {
2656                 double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
2657                 fprintf(stderr,
2658                         "\rStep %d, Epot=%12.6e, Fnorm=%9.3e, Fmax=%9.3e (atom %d)\n",
2659                         step,
2660                         ems.epot,
2661                         ems.fnorm / sqrtNumAtoms,
2662                         ems.fmax,
2663                         ems.a_fmax + 1);
2664                 fflush(stderr);
2665             }
2666             /* Store the new (lower) energies */
2667             matrix nullBox = {};
2668             energyOutput.addDataAtEnergyStep(false,
2669                                              false,
2670                                              static_cast<double>(step),
2671                                              mdatoms->tmass,
2672                                              enerd,
2673                                              nullptr,
2674                                              nullptr,
2675                                              nullBox,
2676                                              PTCouplingArrays(),
2677                                              0,
2678                                              vir,
2679                                              pres,
2680                                              nullptr,
2681                                              mu_tot,
2682                                              constr);
2683
2684             do_log = do_per_step(step, inputrec->nstlog);
2685             do_ene = do_per_step(step, inputrec->nstenergy);
2686
2687             imdSession->fillEnergyRecord(step, TRUE);
2688
2689             if (do_log)
2690             {
2691                 EnergyOutput::printHeader(fplog, step, step);
2692             }
2693             energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf),
2694                                                do_ene,
2695                                                FALSE,
2696                                                FALSE,
2697                                                do_log ? fplog : nullptr,
2698                                                step,
2699                                                step,
2700                                                fr->fcdata.get(),
2701                                                nullptr);
2702         }
2703
2704         /* Send x and E to IMD client, if bIMD is TRUE. */
2705         if (imdSession->run(step, TRUE, state_global->box, state_global->x, 0) && MASTER(cr))
2706         {
2707             imdSession->sendPositionsAndEnergies();
2708         }
2709
2710         // Reset stepsize in we are doing more iterations
2711         stepsize = 1.0;
2712
2713         /* Stop when the maximum force lies below tolerance.
2714          * If we have reached machine precision, converged is already set to true.
2715          */
2716         converged = converged || (ems.fmax < inputrec->em_tol);
2717         observablesReducer.markAsReadyToReduce();
2718     } /* End of the loop */
2719
2720     if (converged)
2721     {
2722         step--; /* we never took that last step in this case */
2723     }
2724     if (ems.fmax > inputrec->em_tol)
2725     {
2726         if (MASTER(cr))
2727         {
2728             warn_step(fplog, inputrec->em_tol, ems.fmax, step - 1 == number_steps, FALSE);
2729         }
2730         converged = FALSE;
2731     }
2732
2733     /* If we printed energy and/or logfile last step (which was the last step)
2734      * we don't have to do it again, but otherwise print the final values.
2735      */
2736     if (!do_log) /* Write final value to log since we didn't do anythin last step */
2737     {
2738         EnergyOutput::printHeader(fplog, step, step);
2739     }
2740     if (!do_ene || !do_log) /* Write final energy file entries */
2741     {
2742         energyOutput.printStepToEnergyFile(mdoutf_get_fp_ene(outf),
2743                                            !do_ene,
2744                                            FALSE,
2745                                            FALSE,
2746                                            !do_log ? fplog : nullptr,
2747                                            step,
2748                                            step,
2749                                            fr->fcdata.get(),
2750                                            nullptr);
2751     }
2752
2753     /* Print some stuff... */
2754     if (MASTER(cr))
2755     {
2756         fprintf(stderr, "\nwriting lowest energy coordinates.\n");
2757     }
2758
2759     /* IMPORTANT!
2760      * For accurate normal mode calculation it is imperative that we
2761      * store the last conformation into the full precision binary trajectory.
2762      *
2763      * However, we should only do it if we did NOT already write this step
2764      * above (which we did if do_x or do_f was true).
2765      */
2766     const bool do_x = !do_per_step(step, inputrec->nstxout);
2767     const bool do_f = !do_per_step(step, inputrec->nstfout);
2768     write_em_traj(
2769             fplog, cr, outf, do_x, do_f, ftp2fn(efSTO, nfile, fnm), top_global, inputrec, step, &ems, state_global, observablesHistory);
2770
2771     if (MASTER(cr))
2772     {
2773         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
2774         print_converged(stderr, LBFGS, inputrec->em_tol, step, converged, number_steps, &ems, sqrtNumAtoms);
2775         print_converged(fplog, LBFGS, inputrec->em_tol, step, converged, number_steps, &ems, sqrtNumAtoms);
2776
2777         fprintf(fplog, "\nPerformed %d energy evaluations in total.\n", neval);
2778     }
2779
2780     finish_em(cr, outf, walltime_accounting, wcycle);
2781
2782     /* To print the actual number of steps we needed somewhere */
2783     walltime_accounting_set_nsteps_done(walltime_accounting, step);
2784 }
2785
2786 void LegacySimulator::do_steep()
2787 {
2788     const char*       SD = "Steepest Descents";
2789     gmx_global_stat_t gstat;
2790     real              stepsize;
2791     real              ustep;
2792     gmx_bool          bDone, bAbort, do_x, do_f;
2793     tensor            vir, pres;
2794     rvec              mu_tot = { 0 };
2795     int               nsteps;
2796     int               count          = 0;
2797     int               steps_accepted = 0;
2798     auto*             mdatoms        = mdAtoms->mdatoms();
2799
2800     GMX_LOG(mdlog.info)
2801             .asParagraph()
2802             .appendText(
2803                     "Note that activating steepest-descent energy minimization via the "
2804                     "integrator .mdp option and the command gmx mdrun may "
2805                     "be available in a different form in a future version of GROMACS, "
2806                     "e.g. gmx minimize and an .mdp option.");
2807
2808     /* Create 2 states on the stack and extract pointers that we will swap */
2809     em_state_t  s0{}, s1{};
2810     em_state_t* s_min = &s0;
2811     em_state_t* s_try = &s1;
2812
2813     ObservablesReducer observablesReducer = observablesReducerBuilder->build();
2814
2815     /* Init em and store the local state in s_try */
2816     init_em(fplog,
2817             mdlog,
2818             SD,
2819             cr,
2820             inputrec,
2821             imdSession,
2822             pull_work,
2823             state_global,
2824             top_global,
2825             s_try,
2826             top,
2827             nrnb,
2828             fr,
2829             mdAtoms,
2830             &gstat,
2831             vsite,
2832             constr,
2833             nullptr);
2834     const bool        simulationsShareState = false;
2835     gmx_mdoutf*       outf                  = init_mdoutf(fplog,
2836                                    nfile,
2837                                    fnm,
2838                                    mdrunOptions,
2839                                    cr,
2840                                    outputProvider,
2841                                    mdModulesNotifiers,
2842                                    inputrec,
2843                                    top_global,
2844                                    nullptr,
2845                                    wcycle,
2846                                    StartingBehavior::NewSimulation,
2847                                    simulationsShareState,
2848                                    ms);
2849     gmx::EnergyOutput energyOutput(mdoutf_get_fp_ene(outf),
2850                                    top_global,
2851                                    *inputrec,
2852                                    pull_work,
2853                                    nullptr,
2854                                    false,
2855                                    StartingBehavior::NewSimulation,
2856                                    simulationsShareState,
2857                                    mdModulesNotifiers);
2858
2859     /* Print to log file  */
2860     print_em_start(fplog, cr, walltime_accounting, wcycle, SD);
2861
2862     /* Set variables for stepsize (in nm). This is the largest
2863      * step that we are going to make in any direction.
2864      */
2865     ustep    = inputrec->em_stepsize;
2866     stepsize = 0;
2867
2868     /* Max number of steps  */
2869     nsteps = inputrec->nsteps;
2870
2871     if (MASTER(cr))
2872     {
2873         /* Print to the screen  */
2874         sp_header(stderr, SD, inputrec->em_tol, nsteps);
2875     }
2876     if (fplog)
2877     {
2878         sp_header(fplog, SD, inputrec->em_tol, nsteps);
2879     }
2880     EnergyEvaluator energyEvaluator{ fplog,
2881                                      mdlog,
2882                                      cr,
2883                                      ms,
2884                                      top_global,
2885                                      top,
2886                                      inputrec,
2887                                      imdSession,
2888                                      pull_work,
2889                                      nrnb,
2890                                      wcycle,
2891                                      gstat,
2892                                      &observablesReducer,
2893                                      vsite,
2894                                      constr,
2895                                      mdAtoms,
2896                                      fr,
2897                                      runScheduleWork,
2898                                      enerd };
2899
2900     /**** HERE STARTS THE LOOP ****
2901      * count is the counter for the number of steps
2902      * bDone will be TRUE when the minimization has converged
2903      * bAbort will be TRUE when nsteps steps have been performed or when
2904      * the stepsize becomes smaller than is reasonable for machine precision
2905      */
2906     count  = 0;
2907     bDone  = FALSE;
2908     bAbort = FALSE;
2909     while (!bDone && !bAbort)
2910     {
2911         bAbort = (nsteps >= 0) && (count == nsteps);
2912
2913         /* set new coordinates, except for first step */
2914         bool validStep = true;
2915         if (count > 0)
2916         {
2917             validStep = do_em_step(
2918                     cr, inputrec, mdatoms, s_min, stepsize, s_min->f.view().forceWithPadding(), s_try, constr, count);
2919         }
2920
2921         if (validStep)
2922         {
2923             energyEvaluator.run(s_try, mu_tot, vir, pres, count, count == 0, count);
2924         }
2925         else
2926         {
2927             // Signal constraint error during stepping with energy=inf
2928             s_try->epot = std::numeric_limits<real>::infinity();
2929         }
2930
2931         if (MASTER(cr))
2932         {
2933             EnergyOutput::printHeader(fplog, count, count);
2934         }
2935
2936         if (count == 0)
2937         {
2938             s_min->epot = s_try->epot;
2939         }
2940
2941         /* Print it if necessary  */
2942         if (MASTER(cr))
2943         {
2944             if (mdrunOptions.verbose)
2945             {
2946                 fprintf(stderr,
2947                         "Step=%5d, Dmax= %6.1e nm, Epot= %12.5e Fmax= %11.5e, atom= %d%c",
2948                         count,
2949                         ustep,
2950                         s_try->epot,
2951                         s_try->fmax,
2952                         s_try->a_fmax + 1,
2953                         ((count == 0) || (s_try->epot < s_min->epot)) ? '\n' : '\r');
2954                 fflush(stderr);
2955             }
2956
2957             if ((count == 0) || (s_try->epot < s_min->epot))
2958             {
2959                 /* Store the new (lower) energies  */
2960                 matrix nullBox = {};
2961                 energyOutput.addDataAtEnergyStep(false,
2962                                                  false,
2963                                                  static_cast<double>(count),
2964                                                  mdatoms->tmass,
2965                                                  enerd,
2966                                                  nullptr,
2967                                                  nullptr,
2968                                                  nullBox,
2969                                                  PTCouplingArrays(),
2970                                                  0,
2971                                                  vir,
2972                                                  pres,
2973                                                  nullptr,
2974                                                  mu_tot,
2975                                                  constr);
2976
2977                 imdSession->fillEnergyRecord(count, TRUE);
2978
2979                 const bool do_dr = do_per_step(steps_accepted, inputrec->nstdisreout);
2980                 const bool do_or = do_per_step(steps_accepted, inputrec->nstorireout);
2981                 energyOutput.printStepToEnergyFile(
2982                         mdoutf_get_fp_ene(outf), TRUE, do_dr, do_or, fplog, count, count, fr->fcdata.get(), nullptr);
2983                 fflush(fplog);
2984             }
2985         }
2986
2987         /* Now if the new energy is smaller than the previous...
2988          * or if this is the first step!
2989          * or if we did random steps!
2990          */
2991
2992         if ((count == 0) || (s_try->epot < s_min->epot))
2993         {
2994             steps_accepted++;
2995
2996             /* Test whether the convergence criterion is met...  */
2997             bDone = (s_try->fmax < inputrec->em_tol);
2998
2999             /* Copy the arrays for force, positions and energy  */
3000             /* The 'Min' array always holds the coords and forces of the minimal
3001                sampled energy  */
3002             swap_em_state(&s_min, &s_try);
3003             if (count > 0)
3004             {
3005                 ustep *= 1.2;
3006             }
3007
3008             /* Write to trn, if necessary */
3009             do_x = do_per_step(steps_accepted, inputrec->nstxout);
3010             do_f = do_per_step(steps_accepted, inputrec->nstfout);
3011             write_em_traj(
3012                     fplog, cr, outf, do_x, do_f, nullptr, top_global, inputrec, count, s_min, state_global, observablesHistory);
3013         }
3014         else
3015         {
3016             /* If energy is not smaller make the step smaller...  */
3017             ustep *= 0.5;
3018
3019             if (haveDDAtomOrdering(*cr) && s_min->s.ddp_count != cr->dd->ddp_count)
3020             {
3021                 /* Reload the old state */
3022                 em_dd_partition_system(fplog,
3023                                        mdlog,
3024                                        count,
3025                                        cr,
3026                                        top_global,
3027                                        inputrec,
3028                                        imdSession,
3029                                        pull_work,
3030                                        s_min,
3031                                        top,
3032                                        mdAtoms,
3033                                        fr,
3034                                        vsite,
3035                                        constr,
3036                                        nrnb,
3037                                        wcycle);
3038             }
3039         }
3040
3041         // If the force is very small after finishing minimization,
3042         // we risk dividing by zero when calculating the step size.
3043         // So we check first if the minimization has stopped before
3044         // trying to obtain a new step size.
3045         if (!bDone)
3046         {
3047             /* Determine new step  */
3048             stepsize = ustep / s_min->fmax;
3049         }
3050
3051         /* Check if stepsize is too small, with 1 nm as a characteristic length */
3052 #if GMX_DOUBLE
3053         if (count == nsteps || ustep < 1e-12)
3054 #else
3055         if (count == nsteps || ustep < 1e-6)
3056 #endif
3057         {
3058             if (MASTER(cr))
3059             {
3060                 warn_step(fplog, inputrec->em_tol, s_min->fmax, count == nsteps, constr != nullptr);
3061             }
3062             bAbort = TRUE;
3063         }
3064
3065         /* Send IMD energies and positions, if bIMD is TRUE. */
3066         if (imdSession->run(count,
3067                             TRUE,
3068                             MASTER(cr) ? state_global->box : nullptr,
3069                             MASTER(cr) ? state_global->x : gmx::ArrayRef<gmx::RVec>(),
3070                             0)
3071             && MASTER(cr))
3072         {
3073             imdSession->sendPositionsAndEnergies();
3074         }
3075
3076         count++;
3077         observablesReducer.markAsReadyToReduce();
3078     } /* End of the loop  */
3079
3080     /* Print some data...  */
3081     if (MASTER(cr))
3082     {
3083         fprintf(stderr, "\nwriting lowest energy coordinates.\n");
3084     }
3085     write_em_traj(fplog,
3086                   cr,
3087                   outf,
3088                   TRUE,
3089                   inputrec->nstfout != 0,
3090                   ftp2fn(efSTO, nfile, fnm),
3091                   top_global,
3092                   inputrec,
3093                   count,
3094                   s_min,
3095                   state_global,
3096                   observablesHistory);
3097
3098     if (MASTER(cr))
3099     {
3100         double sqrtNumAtoms = sqrt(static_cast<double>(state_global->natoms));
3101
3102         print_converged(stderr, SD, inputrec->em_tol, count, bDone, nsteps, s_min, sqrtNumAtoms);
3103         print_converged(fplog, SD, inputrec->em_tol, count, bDone, nsteps, s_min, sqrtNumAtoms);
3104     }
3105
3106     finish_em(cr, outf, walltime_accounting, wcycle);
3107
3108     /* To print the actual number of steps we needed somewhere */
3109     {
3110         // TODO: Avoid changing inputrec (#3854)
3111         auto* nonConstInputrec   = const_cast<t_inputrec*>(inputrec);
3112         nonConstInputrec->nsteps = count;
3113     }
3114
3115     walltime_accounting_set_nsteps_done(walltime_accounting, count);
3116 }
3117
3118 void LegacySimulator::do_nm()
3119 {
3120     const char*         NM = "Normal Mode Analysis";
3121     int                 nnodes;
3122     gmx_global_stat_t   gstat;
3123     tensor              vir, pres;
3124     rvec                mu_tot = { 0 };
3125     rvec*               dfdx;
3126     gmx_bool            bSparse; /* use sparse matrix storage format */
3127     size_t              sz;
3128     gmx_sparsematrix_t* sparse_matrix = nullptr;
3129     real*               full_matrix   = nullptr;
3130
3131     /* added with respect to mdrun */
3132     int   row, col;
3133     real  der_range = 10.0 * std::sqrt(GMX_REAL_EPS);
3134     real  x_min;
3135     bool  bIsMaster = MASTER(cr);
3136     auto* mdatoms   = mdAtoms->mdatoms();
3137
3138     GMX_LOG(mdlog.info)
3139             .asParagraph()
3140             .appendText(
3141                     "Note that activating normal-mode analysis via the integrator "
3142                     ".mdp option and the command gmx mdrun may "
3143                     "be available in a different form in a future version of GROMACS, "
3144                     "e.g. gmx normal-modes.");
3145
3146     if (constr != nullptr)
3147     {
3148         gmx_fatal(
3149                 FARGS,
3150                 "Constraints present with Normal Mode Analysis, this combination is not supported");
3151     }
3152
3153     gmx_shellfc_t* shellfc;
3154
3155     em_state_t state_work{};
3156
3157     fr->longRangeNonbondeds->updateAfterPartition(*mdAtoms->mdatoms());
3158     ObservablesReducer observablesReducer = observablesReducerBuilder->build();
3159
3160     /* Init em and store the local state in state_minimum */
3161     init_em(fplog,
3162             mdlog,
3163             NM,
3164             cr,
3165             inputrec,
3166             imdSession,
3167             pull_work,
3168             state_global,
3169             top_global,
3170             &state_work,
3171             top,
3172             nrnb,
3173             fr,
3174             mdAtoms,
3175             &gstat,
3176             vsite,
3177             constr,
3178             &shellfc);
3179     const bool  simulationsShareState = false;
3180     gmx_mdoutf* outf                  = init_mdoutf(fplog,
3181                                    nfile,
3182                                    fnm,
3183                                    mdrunOptions,
3184                                    cr,
3185                                    outputProvider,
3186                                    mdModulesNotifiers,
3187                                    inputrec,
3188                                    top_global,
3189                                    nullptr,
3190                                    wcycle,
3191                                    StartingBehavior::NewSimulation,
3192                                    simulationsShareState,
3193                                    ms);
3194
3195     std::vector<int>       atom_index = get_atom_index(top_global);
3196     std::vector<gmx::RVec> fneg(atom_index.size(), { 0, 0, 0 });
3197     snew(dfdx, atom_index.size());
3198
3199 #if !GMX_DOUBLE
3200     if (bIsMaster)
3201     {
3202         fprintf(stderr,
3203                 "NOTE: This version of GROMACS has been compiled in single precision,\n"
3204                 "      which MIGHT not be accurate enough for normal mode analysis.\n"
3205                 "      GROMACS now uses sparse matrix storage, so the memory requirements\n"
3206                 "      are fairly modest even if you recompile in double precision.\n\n");
3207     }
3208 #endif
3209
3210     /* Check if we can/should use sparse storage format.
3211      *
3212      * Sparse format is only useful when the Hessian itself is sparse, which it
3213      * will be when we use a cutoff.
3214      * For small systems (n<1000) it is easier to always use full matrix format, though.
3215      */
3216     if (EEL_FULL(fr->ic->eeltype) || fr->rlist == 0.0)
3217     {
3218         GMX_LOG(mdlog.warning)
3219                 .appendText("Non-cutoff electrostatics used, forcing full Hessian format.");
3220         bSparse = FALSE;
3221     }
3222     else if (atom_index.size() < 1000)
3223     {
3224         GMX_LOG(mdlog.warning)
3225                 .appendTextFormatted("Small system size (N=%zu), using full Hessian format.",
3226                                      atom_index.size());
3227         bSparse = FALSE;
3228     }
3229     else
3230     {
3231         GMX_LOG(mdlog.warning).appendText("Using compressed symmetric sparse Hessian format.");
3232         bSparse = TRUE;
3233     }
3234
3235     /* Number of dimensions, based on real atoms, that is not vsites or shell */
3236     sz = DIM * atom_index.size();
3237
3238     fprintf(stderr, "Allocating Hessian memory...\n\n");
3239
3240     if (bSparse)
3241     {
3242         sparse_matrix                       = gmx_sparsematrix_init(sz);
3243         sparse_matrix->compressed_symmetric = TRUE;
3244     }
3245     else
3246     {
3247         snew(full_matrix, sz * sz);
3248     }
3249
3250     /* Write start time and temperature */
3251     print_em_start(fplog, cr, walltime_accounting, wcycle, NM);
3252
3253     /* fudge nr of steps to nr of atoms */
3254     {
3255         // TODO: Avoid changing inputrec (#3854)
3256         auto* nonConstInputrec   = const_cast<t_inputrec*>(inputrec);
3257         nonConstInputrec->nsteps = atom_index.size() * 2;
3258     }
3259
3260     if (bIsMaster)
3261     {
3262         fprintf(stderr,
3263                 "starting normal mode calculation '%s'\n%" PRId64 " steps.\n\n",
3264                 *(top_global.name),
3265                 inputrec->nsteps);
3266     }
3267
3268     nnodes = cr->nnodes;
3269
3270     /* Make evaluate_energy do a single node force calculation */
3271     cr->nnodes = 1;
3272     EnergyEvaluator energyEvaluator{ fplog,
3273                                      mdlog,
3274                                      cr,
3275                                      ms,
3276                                      top_global,
3277                                      top,
3278                                      inputrec,
3279                                      imdSession,
3280                                      pull_work,
3281                                      nrnb,
3282                                      wcycle,
3283                                      gstat,
3284                                      &observablesReducer,
3285                                      vsite,
3286                                      constr,
3287                                      mdAtoms,
3288                                      fr,
3289                                      runScheduleWork,
3290                                      enerd };
3291     energyEvaluator.run(&state_work, mu_tot, vir, pres, -1, TRUE, 0);
3292     cr->nnodes = nnodes;
3293
3294     /* if forces are not small, warn user */
3295     get_state_f_norm_max(cr, &(inputrec->opts), mdatoms, &state_work);
3296
3297     GMX_LOG(mdlog.warning).appendTextFormatted("Maximum force:%12.5e", state_work.fmax);
3298     if (state_work.fmax > 1.0e-3)
3299     {
3300         GMX_LOG(mdlog.warning)
3301                 .appendText(
3302                         "The force is probably not small enough to "
3303                         "ensure that you are at a minimum.\n"
3304                         "Be aware that negative eigenvalues may occur\n"
3305                         "when the resulting matrix is diagonalized.");
3306     }
3307
3308     /***********************************************************
3309      *
3310      *      Loop over all pairs in matrix
3311      *
3312      *      do_force called twice. Once with positive and
3313      *      once with negative displacement
3314      *
3315      ************************************************************/
3316
3317     /* Steps are divided one by one over the nodes */
3318     bool bNS          = true;
3319     auto state_work_x = makeArrayRef(state_work.s.x);
3320     auto state_work_f = state_work.f.view().force();
3321     for (index aid = cr->nodeid; aid < ssize(atom_index); aid += nnodes)
3322     {
3323         size_t atom = atom_index[aid];
3324         for (size_t d = 0; d < DIM; d++)
3325         {
3326             int64_t step        = 0;
3327             int     force_flags = GMX_FORCE_STATECHANGED | GMX_FORCE_ALLFORCES;
3328             double  t           = 0;
3329
3330             x_min = state_work_x[atom][d];
3331
3332             for (unsigned int dx = 0; (dx < 2); dx++)
3333             {
3334                 if (dx == 0)
3335                 {
3336                     state_work_x[atom][d] = x_min - der_range;
3337                 }
3338                 else
3339                 {
3340                     state_work_x[atom][d] = x_min + der_range;
3341                 }
3342
3343                 /* Make evaluate_energy do a single node force calculation */
3344                 cr->nnodes = 1;
3345                 if (shellfc)
3346                 {
3347                     /* Now is the time to relax the shells */
3348                     relax_shell_flexcon(fplog,
3349                                         cr,
3350                                         ms,
3351                                         mdrunOptions.verbose,
3352                                         nullptr,
3353                                         step,
3354                                         inputrec,
3355                                         imdSession,
3356                                         pull_work,
3357                                         bNS,
3358                                         force_flags,
3359                                         top,
3360                                         constr,
3361                                         enerd,
3362                                         state_work.s.natoms,
3363                                         state_work.s.x.arrayRefWithPadding(),
3364                                         state_work.s.v.arrayRefWithPadding(),
3365                                         state_work.s.box,
3366                                         state_work.s.lambda,
3367                                         &state_work.s.hist,
3368                                         &state_work.f.view(),
3369                                         vir,
3370                                         *mdatoms,
3371                                         fr->longRangeNonbondeds.get(),
3372                                         nrnb,
3373                                         wcycle,
3374                                         shellfc,
3375                                         fr,
3376                                         runScheduleWork,
3377                                         t,
3378                                         mu_tot,
3379                                         vsite,
3380                                         DDBalanceRegionHandler(nullptr));
3381                     bNS = false;
3382                     step++;
3383                 }
3384                 else
3385                 {
3386                     energyEvaluator.run(&state_work, mu_tot, vir, pres, aid * 2 + dx, FALSE, step);
3387                 }
3388
3389                 cr->nnodes = nnodes;
3390
3391                 if (dx == 0)
3392                 {
3393                     std::copy(state_work_f.begin(), state_work_f.begin() + atom_index.size(), fneg.begin());
3394                 }
3395             }
3396
3397             /* x is restored to original */
3398             state_work_x[atom][d] = x_min;
3399
3400             for (size_t j = 0; j < atom_index.size(); j++)
3401             {
3402                 for (size_t k = 0; (k < DIM); k++)
3403                 {
3404                     dfdx[j][k] = -(state_work_f[atom_index[j]][k] - fneg[j][k]) / (2 * der_range);
3405                 }
3406             }
3407
3408             if (!bIsMaster)
3409             {
3410 #if GMX_MPI
3411 #    define mpi_type GMX_MPI_REAL
3412                 MPI_Send(dfdx[0], atom_index.size() * DIM, mpi_type, MASTER(cr), cr->nodeid, cr->mpi_comm_mygroup);
3413 #endif
3414             }
3415             else
3416             {
3417                 for (index node = 0; (node < nnodes && aid + node < ssize(atom_index)); node++)
3418                 {
3419                     if (node > 0)
3420                     {
3421 #if GMX_MPI
3422                         MPI_Status stat;
3423                         MPI_Recv(dfdx[0], atom_index.size() * DIM, mpi_type, node, node, cr->mpi_comm_mygroup, &stat);
3424 #    undef mpi_type
3425 #endif
3426                     }
3427
3428                     row = (aid + node) * DIM + d;
3429
3430                     for (size_t j = 0; j < atom_index.size(); j++)
3431                     {
3432                         for (size_t k = 0; k < DIM; k++)
3433                         {
3434                             col = j * DIM + k;
3435
3436                             if (bSparse)
3437                             {
3438                                 if (col >= row && dfdx[j][k] != 0.0)
3439                                 {
3440                                     gmx_sparsematrix_increment_value(sparse_matrix, row, col, dfdx[j][k]);
3441                                 }
3442                             }
3443                             else
3444                             {
3445                                 full_matrix[row * sz + col] = dfdx[j][k];
3446                             }
3447                         }
3448                     }
3449                 }
3450             }
3451
3452             if (mdrunOptions.verbose && fplog)
3453             {
3454                 fflush(fplog);
3455             }
3456         }
3457         /* write progress */
3458         if (bIsMaster && mdrunOptions.verbose)
3459         {
3460             fprintf(stderr,
3461                     "\rFinished step %d out of %td",
3462                     std::min<int>(atom + nnodes, atom_index.size()),
3463                     ssize(atom_index));
3464             fflush(stderr);
3465         }
3466     }
3467
3468     if (bIsMaster)
3469     {
3470         fprintf(stderr, "\n\nWriting Hessian...\n");
3471         gmx_mtxio_write(ftp2fn(efMTX, nfile, fnm), sz, sz, full_matrix, sparse_matrix);
3472     }
3473
3474     finish_em(cr, outf, walltime_accounting, wcycle);
3475
3476     walltime_accounting_set_nsteps_done(walltime_accounting, atom_index.size() * 2);
3477 }
3478
3479 } // namespace gmx