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