4c9fbf8d93d4ee2683d65002bad612ccbe9a9be5
[alexxy/gromacs.git] / src / gromacs / gmxpreprocess / grompp.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 by 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 #include "gmxpre.h"
39
40 #include "grompp.h"
41
42 #include <array>
43 #include <cerrno>
44 #include <climits>
45 #include <cmath>
46 #include <cstring>
47
48 #include <algorithm>
49 #include <memory>
50 #include <vector>
51
52 #include <sys/types.h>
53
54 #include "gromacs/applied_forces/awh/read_params.h"
55 #include "gromacs/commandline/pargs.h"
56 #include "gromacs/ewald/ewald_utils.h"
57 #include "gromacs/ewald/pme.h"
58 #include "gromacs/fft/calcgrid.h"
59 #include "gromacs/fileio/confio.h"
60 #include "gromacs/fileio/enxio.h"
61 #include "gromacs/fileio/tpxio.h"
62 #include "gromacs/fileio/trxio.h"
63 #include "gromacs/fileio/warninp.h"
64 #include "gromacs/gmxpreprocess/add_par.h"
65 #include "gromacs/gmxpreprocess/convparm.h"
66 #include "gromacs/gmxpreprocess/gen_maxwell_velocities.h"
67 #include "gromacs/gmxpreprocess/gpp_atomtype.h"
68 #include "gromacs/gmxpreprocess/grompp_impl.h"
69 #include "gromacs/gmxpreprocess/notset.h"
70 #include "gromacs/gmxpreprocess/readir.h"
71 #include "gromacs/gmxpreprocess/tomorse.h"
72 #include "gromacs/gmxpreprocess/topio.h"
73 #include "gromacs/gmxpreprocess/toputil.h"
74 #include "gromacs/gmxpreprocess/vsite_parm.h"
75 #include "gromacs/imd/imd.h"
76 #include "gromacs/math/functions.h"
77 #include "gromacs/math/invertmatrix.h"
78 #include "gromacs/math/units.h"
79 #include "gromacs/math/vec.h"
80 #include "gromacs/mdlib/calc_verletbuf.h"
81 #include "gromacs/mdlib/compute_io.h"
82 #include "gromacs/mdlib/constr.h"
83 #include "gromacs/mdlib/md_support.h"
84 #include "gromacs/mdlib/perf_est.h"
85 #include "gromacs/mdlib/vsite.h"
86 #include "gromacs/mdrun/mdmodules.h"
87 #include "gromacs/mdtypes/inputrec.h"
88 #include "gromacs/mdtypes/md_enums.h"
89 #include "gromacs/mdtypes/nblist.h"
90 #include "gromacs/mdtypes/state.h"
91 #include "gromacs/pbcutil/boxutilities.h"
92 #include "gromacs/pbcutil/pbc.h"
93 #include "gromacs/pulling/pull.h"
94 #include "gromacs/random/seed.h"
95 #include "gromacs/topology/ifunc.h"
96 #include "gromacs/topology/mtop_util.h"
97 #include "gromacs/topology/symtab.h"
98 #include "gromacs/topology/topology.h"
99 #include "gromacs/trajectory/trajectoryframe.h"
100 #include "gromacs/utility/arraysize.h"
101 #include "gromacs/utility/cstringutil.h"
102 #include "gromacs/utility/exceptions.h"
103 #include "gromacs/utility/fatalerror.h"
104 #include "gromacs/utility/filestream.h"
105 #include "gromacs/utility/futil.h"
106 #include "gromacs/utility/gmxassert.h"
107 #include "gromacs/utility/keyvaluetreebuilder.h"
108 #include "gromacs/utility/listoflists.h"
109 #include "gromacs/utility/logger.h"
110 #include "gromacs/utility/loggerbuilder.h"
111 #include "gromacs/utility/mdmodulesnotifiers.h"
112 #include "gromacs/utility/smalloc.h"
113 #include "gromacs/utility/snprintf.h"
114
115 /* TODO The implementation details should move to their own source file. */
116 InteractionOfType::InteractionOfType(gmx::ArrayRef<const int>  atoms,
117                                      gmx::ArrayRef<const real> params,
118                                      const std::string&        name) :
119     atoms_(atoms.begin(), atoms.end()), interactionTypeName_(name)
120 {
121     GMX_RELEASE_ASSERT(
122             params.size() <= forceParam_.size(),
123             gmx::formatString("Cannot have more parameters than the maximum number possible (%d)", MAXFORCEPARAM)
124                     .c_str());
125     std::array<real, MAXFORCEPARAM>::iterator forceParamIt = forceParam_.begin();
126     for (const auto param : params)
127     {
128         *forceParamIt++ = param;
129     }
130     while (forceParamIt != forceParam_.end())
131     {
132         *forceParamIt++ = NOTSET;
133     }
134 }
135
136 const int& InteractionOfType::ai() const
137 {
138     GMX_RELEASE_ASSERT(!atoms_.empty(), "Need to have at least one atom set");
139     return atoms_[0];
140 }
141
142 const int& InteractionOfType::aj() const
143 {
144     GMX_RELEASE_ASSERT(atoms_.size() > 1, "Need to have at least two atoms set");
145     return atoms_[1];
146 }
147
148 const int& InteractionOfType::ak() const
149 {
150     GMX_RELEASE_ASSERT(atoms_.size() > 2, "Need to have at least three atoms set");
151     return atoms_[2];
152 }
153
154 const int& InteractionOfType::al() const
155 {
156     GMX_RELEASE_ASSERT(atoms_.size() > 3, "Need to have at least four atoms set");
157     return atoms_[3];
158 }
159
160 const int& InteractionOfType::am() const
161 {
162     GMX_RELEASE_ASSERT(atoms_.size() > 4, "Need to have at least five atoms set");
163     return atoms_[4];
164 }
165
166 const real& InteractionOfType::c0() const
167 {
168     return forceParam_[0];
169 }
170
171 const real& InteractionOfType::c1() const
172 {
173     return forceParam_[1];
174 }
175
176 const real& InteractionOfType::c2() const
177 {
178     return forceParam_[2];
179 }
180
181 const std::string& InteractionOfType::interactionTypeName() const
182 {
183     return interactionTypeName_;
184 }
185
186 void InteractionOfType::sortBondAtomIds()
187 {
188     if (aj() < ai())
189     {
190         std::swap(atoms_[0], atoms_[1]);
191     }
192 }
193
194 void InteractionOfType::sortAngleAtomIds()
195 {
196     if (ak() < ai())
197     {
198         std::swap(atoms_[0], atoms_[2]);
199     }
200 }
201
202 void InteractionOfType::sortDihedralAtomIds()
203 {
204     if (al() < ai())
205     {
206         std::swap(atoms_[0], atoms_[3]);
207         std::swap(atoms_[1], atoms_[2]);
208     }
209 }
210
211 void InteractionOfType::sortAtomIds()
212 {
213     if (isBond())
214     {
215         sortBondAtomIds();
216     }
217     else if (isAngle())
218     {
219         sortAngleAtomIds();
220     }
221     else if (isDihedral())
222     {
223         sortDihedralAtomIds();
224     }
225     else
226     {
227         GMX_THROW(gmx::InternalError(
228                 "Can not sort parameters other than bonds, angles or dihedrals"));
229     }
230 };
231
232 void InteractionOfType::setForceParameter(int pos, real value)
233 {
234     GMX_RELEASE_ASSERT(pos < MAXFORCEPARAM,
235                        "Can't set parameter beyond the maximum number of parameters");
236     forceParam_[pos] = value;
237 }
238
239 void MoleculeInformation::initMolInfo()
240 {
241     init_block(&mols);
242     excls.clear();
243     init_t_atoms(&atoms, 0, FALSE);
244 }
245
246 void MoleculeInformation::partialCleanUp()
247 {
248     done_block(&mols);
249 }
250
251 void MoleculeInformation::fullCleanUp()
252 {
253     done_atom(&atoms);
254     done_block(&mols);
255 }
256
257 static int rm_interactions(int ifunc, gmx::ArrayRef<MoleculeInformation> mols)
258 {
259     int n = 0;
260     /* For all the molecule types */
261     for (auto& mol : mols)
262     {
263         n += mol.interactions[ifunc].size();
264         mol.interactions[ifunc].interactionTypes.clear();
265     }
266     return n;
267 }
268
269 static int check_atom_names(const char*          fn1,
270                             const char*          fn2,
271                             gmx_mtop_t*          mtop,
272                             const t_atoms*       at,
273                             const gmx::MDLogger& logger)
274 {
275     int      m, i, j, nmismatch;
276     t_atoms* tat;
277
278     constexpr int c_maxNumberOfMismatches = 20;
279
280     if (mtop->natoms != at->nr)
281     {
282         gmx_incons("comparing atom names");
283     }
284
285     nmismatch = 0;
286     i         = 0;
287     for (const gmx_molblock_t& molb : mtop->molblock)
288     {
289         tat = &mtop->moltype[molb.type].atoms;
290         for (m = 0; m < molb.nmol; m++)
291         {
292             for (j = 0; j < tat->nr; j++)
293             {
294                 if (strcmp(*(tat->atomname[j]), *(at->atomname[i])) != 0)
295                 {
296                     if (nmismatch < c_maxNumberOfMismatches)
297                     {
298                         GMX_LOG(logger.warning)
299                                 .asParagraph()
300                                 .appendTextFormatted(
301                                         "atom name %d in %s and %s does not match (%s - %s)",
302                                         i + 1,
303                                         fn1,
304                                         fn2,
305                                         *(tat->atomname[j]),
306                                         *(at->atomname[i]));
307                     }
308                     else if (nmismatch == c_maxNumberOfMismatches)
309                     {
310                         GMX_LOG(logger.warning)
311                                 .asParagraph()
312                                 .appendTextFormatted("(more than %d non-matching atom names)",
313                                                      c_maxNumberOfMismatches);
314                     }
315                     nmismatch++;
316                 }
317                 i++;
318             }
319         }
320     }
321
322     return nmismatch;
323 }
324
325 static void check_bonds_timestep(const gmx_mtop_t* mtop, double dt, warninp* wi)
326 {
327     /* This check is not intended to ensure accurate integration,
328      * rather it is to signal mistakes in the mdp settings.
329      * A common mistake is to forget to turn on constraints
330      * for MD after energy minimization with flexible bonds.
331      * This check can also detect too large time steps for flexible water
332      * models, but such errors will often be masked by the constraints
333      * mdp options, which turns flexible water into water with bond constraints,
334      * but without an angle constraint. Unfortunately such incorrect use
335      * of water models can not easily be detected without checking
336      * for specific model names.
337      *
338      * The stability limit of leap-frog or velocity verlet is 4.44 steps
339      * per oscillational period.
340      * But accurate bonds distributions are lost far before that limit.
341      * To allow relatively common schemes (although not common with Gromacs)
342      * of dt=1 fs without constraints and dt=2 fs with only H-bond constraints
343      * we set the note limit to 10.
344      */
345     int  min_steps_warn = 5;
346     int  min_steps_note = 10;
347     int  ftype;
348     int  i, a1, a2, w_a1, w_a2, j;
349     real twopi2, limit2, fc, re, m1, m2, period2, w_period2;
350     bool bFound, bWater, bWarn;
351
352     /* Get the interaction parameters */
353     gmx::ArrayRef<const t_iparams> ip = mtop->ffparams.iparams;
354
355     twopi2 = gmx::square(2 * M_PI);
356
357     limit2 = gmx::square(min_steps_note * dt);
358
359     w_a1 = w_a2 = -1;
360     w_period2   = -1.0;
361
362     const gmx_moltype_t* w_moltype = nullptr;
363     for (const gmx_moltype_t& moltype : mtop->moltype)
364     {
365         const t_atom*           atom  = moltype.atoms.atom;
366         const InteractionLists& ilist = moltype.ilist;
367         const InteractionList&  ilc   = ilist[F_CONSTR];
368         const InteractionList&  ils   = ilist[F_SETTLE];
369         for (ftype = 0; ftype < F_NRE; ftype++)
370         {
371             if (!(ftype == F_BONDS || ftype == F_G96BONDS || ftype == F_HARMONIC))
372             {
373                 continue;
374             }
375
376             const InteractionList& ilb = ilist[ftype];
377             for (i = 0; i < ilb.size(); i += 3)
378             {
379                 fc = ip[ilb.iatoms[i]].harmonic.krA;
380                 re = ip[ilb.iatoms[i]].harmonic.rA;
381                 if (ftype == F_G96BONDS)
382                 {
383                     /* Convert squared sqaure fc to harmonic fc */
384                     fc = 2 * fc * re;
385                 }
386                 a1 = ilb.iatoms[i + 1];
387                 a2 = ilb.iatoms[i + 2];
388                 m1 = atom[a1].m;
389                 m2 = atom[a2].m;
390                 if (fc > 0 && m1 > 0 && m2 > 0)
391                 {
392                     period2 = twopi2 * m1 * m2 / ((m1 + m2) * fc);
393                 }
394                 else
395                 {
396                     period2 = GMX_FLOAT_MAX;
397                 }
398                 if (debug)
399                 {
400                     fprintf(debug, "fc %g m1 %g m2 %g period %g\n", fc, m1, m2, std::sqrt(period2));
401                 }
402                 if (period2 < limit2)
403                 {
404                     bFound = false;
405                     for (j = 0; j < ilc.size(); j += 3)
406                     {
407                         if ((ilc.iatoms[j + 1] == a1 && ilc.iatoms[j + 2] == a2)
408                             || (ilc.iatoms[j + 1] == a2 && ilc.iatoms[j + 2] == a1))
409                         {
410                             bFound = true;
411                         }
412                     }
413                     for (j = 0; j < ils.size(); j += 4)
414                     {
415                         if ((a1 == ils.iatoms[j + 1] || a1 == ils.iatoms[j + 2] || a1 == ils.iatoms[j + 3])
416                             && (a2 == ils.iatoms[j + 1] || a2 == ils.iatoms[j + 2]
417                                 || a2 == ils.iatoms[j + 3]))
418                         {
419                             bFound = true;
420                         }
421                     }
422                     if (!bFound && (w_moltype == nullptr || period2 < w_period2))
423                     {
424                         w_moltype = &moltype;
425                         w_a1      = a1;
426                         w_a2      = a2;
427                         w_period2 = period2;
428                     }
429                 }
430             }
431         }
432     }
433
434     if (w_moltype != nullptr)
435     {
436         bWarn = (w_period2 < gmx::square(min_steps_warn * dt));
437         /* A check that would recognize most water models */
438         bWater = ((*w_moltype->atoms.atomname[0])[0] == 'O' && w_moltype->atoms.nr <= 5);
439         std::string warningMessage = gmx::formatString(
440                 "The bond in molecule-type %s between atoms %d %s and %d %s has an estimated "
441                 "oscillational period of %.1e ps, which is less than %d times the time step of "
442                 "%.1e ps.\n"
443                 "%s",
444                 *w_moltype->name,
445                 w_a1 + 1,
446                 *w_moltype->atoms.atomname[w_a1],
447                 w_a2 + 1,
448                 *w_moltype->atoms.atomname[w_a2],
449                 std::sqrt(w_period2),
450                 bWarn ? min_steps_warn : min_steps_note,
451                 dt,
452                 bWater ? "Maybe you asked for fexible water."
453                        : "Maybe you forgot to change the constraints mdp option.");
454         if (bWarn)
455         {
456             warning(wi, warningMessage.c_str());
457         }
458         else
459         {
460             warning_note(wi, warningMessage.c_str());
461         }
462     }
463 }
464
465 static void check_vel(gmx_mtop_t* mtop, rvec v[])
466 {
467     for (const AtomProxy atomP : AtomRange(*mtop))
468     {
469         const t_atom& local = atomP.atom();
470         int           i     = atomP.globalAtomNumber();
471         if (local.ptype == ParticleType::Shell || local.ptype == ParticleType::Bond
472             || local.ptype == ParticleType::VSite)
473         {
474             clear_rvec(v[i]);
475         }
476     }
477 }
478
479 static void check_shells_inputrec(gmx_mtop_t* mtop, t_inputrec* ir, warninp* wi)
480 {
481     int nshells = 0;
482
483     for (const AtomProxy atomP : AtomRange(*mtop))
484     {
485         const t_atom& local = atomP.atom();
486         if (local.ptype == ParticleType::Shell || local.ptype == ParticleType::Bond)
487         {
488             nshells++;
489         }
490     }
491     if ((nshells > 0) && (ir->nstcalcenergy != 1))
492     {
493         set_warning_line(wi, "unknown", -1);
494         std::string warningMessage = gmx::formatString(
495                 "There are %d shells, changing nstcalcenergy from %d to 1", nshells, ir->nstcalcenergy);
496         ir->nstcalcenergy = 1;
497         warning(wi, warningMessage.c_str());
498     }
499 }
500
501 /* TODO Decide whether this function can be consolidated with
502  * gmx_mtop_ftype_count */
503 static int nint_ftype(gmx_mtop_t* mtop, gmx::ArrayRef<const MoleculeInformation> mi, int ftype)
504 {
505     int nint = 0;
506     for (const gmx_molblock_t& molb : mtop->molblock)
507     {
508         nint += molb.nmol * mi[molb.type].interactions[ftype].size();
509     }
510
511     return nint;
512 }
513
514 /* This routine reorders the molecule type array
515  * in the order of use in the molblocks,
516  * unused molecule types are deleted.
517  */
518 static void renumber_moltypes(gmx_mtop_t* sys, std::vector<MoleculeInformation>* molinfo)
519 {
520
521     std::vector<int> order;
522     for (gmx_molblock_t& molblock : sys->molblock)
523     {
524         const auto found = std::find_if(order.begin(), order.end(), [&molblock](const auto& entry) {
525             return molblock.type == entry;
526         });
527         if (found == order.end())
528         {
529             /* This type did not occur yet, add it */
530             order.push_back(molblock.type);
531             molblock.type = order.size() - 1;
532         }
533         else
534         {
535             molblock.type = std::distance(order.begin(), found);
536         }
537     }
538
539     /* We still need to reorder the molinfo structs */
540     std::vector<MoleculeInformation> minew(order.size());
541     int                              index = 0;
542     for (auto& mi : *molinfo)
543     {
544         const auto found = std::find(order.begin(), order.end(), index);
545         if (found != order.end())
546         {
547             int pos    = std::distance(order.begin(), found);
548             minew[pos] = mi;
549         }
550         else
551         {
552             // Need to manually clean up memory ....
553             mi.fullCleanUp();
554         }
555         index++;
556     }
557
558     *molinfo = minew;
559 }
560
561 static void molinfo2mtop(gmx::ArrayRef<const MoleculeInformation> mi, gmx_mtop_t* mtop)
562 {
563     mtop->moltype.resize(mi.size());
564     int pos = 0;
565     for (const auto& mol : mi)
566     {
567         gmx_moltype_t& molt = mtop->moltype[pos];
568         molt.name           = mol.name;
569         molt.atoms          = mol.atoms;
570         /* ilists are copied later */
571         molt.excls = mol.excls;
572         pos++;
573     }
574 }
575
576 static void new_status(const char*                           topfile,
577                        const char*                           topppfile,
578                        const char*                           confin,
579                        t_gromppopts*                         opts,
580                        t_inputrec*                           ir,
581                        gmx_bool                              bZero,
582                        bool                                  bGenVel,
583                        bool                                  bVerbose,
584                        t_state*                              state,
585                        PreprocessingAtomTypes*               atypes,
586                        gmx_mtop_t*                           sys,
587                        std::vector<MoleculeInformation>*     mi,
588                        std::unique_ptr<MoleculeInformation>* intermolecular_interactions,
589                        gmx::ArrayRef<InteractionsOfType>     interactions,
590                        CombinationRule*                      comb,
591                        double*                               reppow,
592                        real*                                 fudgeQQ,
593                        gmx_bool                              bMorse,
594                        warninp*                              wi,
595                        const gmx::MDLogger&                  logger)
596 {
597     std::vector<gmx_molblock_t> molblock;
598     int                         i, nmismatch;
599     bool                        ffParametrizedWithHBondConstraints;
600
601     /* TOPOLOGY processing */
602     sys->name = do_top(bVerbose,
603                        topfile,
604                        topppfile,
605                        opts,
606                        bZero,
607                        &(sys->symtab),
608                        interactions,
609                        comb,
610                        reppow,
611                        fudgeQQ,
612                        atypes,
613                        mi,
614                        intermolecular_interactions,
615                        ir,
616                        &molblock,
617                        &ffParametrizedWithHBondConstraints,
618                        wi,
619                        logger);
620
621     sys->molblock.clear();
622
623     sys->natoms = 0;
624     for (const gmx_molblock_t& molb : molblock)
625     {
626         if (!sys->molblock.empty() && molb.type == sys->molblock.back().type)
627         {
628             /* Merge consecutive blocks with the same molecule type */
629             sys->molblock.back().nmol += molb.nmol;
630         }
631         else if (molb.nmol > 0)
632         {
633             /* Add a new molblock to the topology */
634             sys->molblock.push_back(molb);
635         }
636         sys->natoms += molb.nmol * (*mi)[sys->molblock.back().type].atoms.nr;
637     }
638     if (sys->molblock.empty())
639     {
640         gmx_fatal(FARGS, "No molecules were defined in the system");
641     }
642
643     renumber_moltypes(sys, mi);
644
645     if (bMorse)
646     {
647         convert_harmonics(*mi, atypes);
648     }
649
650     if (ir->eDisre == DistanceRestraintRefinement::None)
651     {
652         i = rm_interactions(F_DISRES, *mi);
653         if (i > 0)
654         {
655             set_warning_line(wi, "unknown", -1);
656             std::string warningMessage =
657                     gmx::formatString("disre = no, removed %d distance restraints", i);
658             warning_note(wi, warningMessage.c_str());
659         }
660     }
661     if (!opts->bOrire)
662     {
663         i = rm_interactions(F_ORIRES, *mi);
664         if (i > 0)
665         {
666             set_warning_line(wi, "unknown", -1);
667             std::string warningMessage =
668                     gmx::formatString("orire = no, removed %d orientation restraints", i);
669             warning_note(wi, warningMessage.c_str());
670         }
671     }
672
673     /* Copy structures from msys to sys */
674     molinfo2mtop(*mi, sys);
675
676     sys->finalize();
677
678     /* Discourage using the, previously common, setup of applying constraints
679      * to all bonds with force fields that have been parametrized with H-bond
680      * constraints only. Do not print note with large timesteps or vsites.
681      */
682     if (opts->nshake == eshALLBONDS && ffParametrizedWithHBondConstraints && ir->delta_t < 0.0026
683         && gmx_mtop_ftype_count(*sys, F_VSITE3FD) == 0)
684     {
685         set_warning_line(wi, "unknown", -1);
686         warning_note(wi,
687                      "You are using constraints on all bonds, whereas the forcefield "
688                      "has been parametrized only with constraints involving hydrogen atoms. "
689                      "We suggest using constraints = h-bonds instead, this will also improve "
690                      "performance.");
691     }
692
693     /* COORDINATE file processing */
694     if (bVerbose)
695     {
696         GMX_LOG(logger.info).asParagraph().appendTextFormatted("processing coordinates...");
697     }
698
699     t_topology* conftop;
700     rvec*       x = nullptr;
701     rvec*       v = nullptr;
702     snew(conftop, 1);
703     read_tps_conf(confin, conftop, nullptr, &x, &v, state->box, FALSE);
704     state->natoms = conftop->atoms.nr;
705     if (state->natoms != sys->natoms)
706     {
707         gmx_fatal(FARGS,
708                   "number of coordinates in coordinate file (%s, %d)\n"
709                   "             does not match topology (%s, %d)",
710                   confin,
711                   state->natoms,
712                   topfile,
713                   sys->natoms);
714     }
715     /* It would be nice to get rid of the copies below, but we don't know
716      * a priori if the number of atoms in confin matches what we expect.
717      */
718     state->flags |= enumValueToBitMask(StateEntry::X);
719     if (v != nullptr)
720     {
721         state->flags |= enumValueToBitMask(StateEntry::V);
722     }
723     state_change_natoms(state, state->natoms);
724     std::copy(x, x + state->natoms, state->x.data());
725     sfree(x);
726     if (v != nullptr)
727     {
728         std::copy(v, v + state->natoms, state->v.data());
729         sfree(v);
730     }
731     /* This call fixes the box shape for runs with pressure scaling */
732     set_box_rel(ir, state);
733
734     nmismatch = check_atom_names(topfile, confin, sys, &conftop->atoms, logger);
735     done_top(conftop);
736     sfree(conftop);
737
738     if (nmismatch)
739     {
740         std::string warningMessage = gmx::formatString(
741                 "%d non-matching atom name%s\n"
742                 "atom names from %s will be used\n"
743                 "atom names from %s will be ignored\n",
744                 nmismatch,
745                 (nmismatch == 1) ? "" : "s",
746                 topfile,
747                 confin);
748         warning(wi, warningMessage.c_str());
749     }
750
751     /* Do more checks, mostly related to constraints */
752     if (bVerbose)
753     {
754         GMX_LOG(logger.info)
755                 .asParagraph()
756                 .appendTextFormatted("double-checking input for internal consistency...");
757     }
758     {
759         bool bHasNormalConstraints =
760                 0 < (nint_ftype(sys, *mi, F_CONSTR) + nint_ftype(sys, *mi, F_CONSTRNC));
761         bool bHasAnyConstraints = bHasNormalConstraints || 0 < nint_ftype(sys, *mi, F_SETTLE);
762         double_check(ir, state->box, bHasNormalConstraints, bHasAnyConstraints, wi);
763     }
764
765     if (bGenVel)
766     {
767         real* mass;
768
769         snew(mass, state->natoms);
770         for (const AtomProxy atomP : AtomRange(*sys))
771         {
772             const t_atom& local = atomP.atom();
773             int           i     = atomP.globalAtomNumber();
774             mass[i]             = local.m;
775         }
776
777         if (opts->seed == -1)
778         {
779             opts->seed = static_cast<int>(gmx::makeRandomSeed());
780             GMX_LOG(logger.info).asParagraph().appendTextFormatted("Setting gen_seed to %d", opts->seed);
781         }
782         state->flags |= enumValueToBitMask(StateEntry::V);
783         maxwell_speed(opts->tempi, opts->seed, sys, state->v.rvec_array(), logger);
784
785         stop_cm(logger, state->natoms, mass, state->x.rvec_array(), state->v.rvec_array());
786         sfree(mass);
787     }
788 }
789
790 static void copy_state(const char* slog, t_trxframe* fr, bool bReadVel, t_state* state, double* use_time)
791 {
792     if (fr->not_ok & FRAME_NOT_OK)
793     {
794         gmx_fatal(FARGS, "Can not start from an incomplete frame");
795     }
796     if (!fr->bX)
797     {
798         gmx_fatal(FARGS, "Did not find a frame with coordinates in file %s", slog);
799     }
800
801     std::copy(fr->x, fr->x + state->natoms, state->x.data());
802     if (bReadVel)
803     {
804         if (!fr->bV)
805         {
806             gmx_incons("Trajecory frame unexpectedly does not contain velocities");
807         }
808         std::copy(fr->v, fr->v + state->natoms, state->v.data());
809     }
810     if (fr->bBox)
811     {
812         copy_mat(fr->box, state->box);
813     }
814
815     *use_time = fr->time;
816 }
817
818 static void cont_status(const char*             slog,
819                         const char*             ener,
820                         bool                    bNeedVel,
821                         bool                    bGenVel,
822                         real                    fr_time,
823                         t_inputrec*             ir,
824                         t_state*                state,
825                         gmx_mtop_t*             sys,
826                         const gmx_output_env_t* oenv,
827                         const gmx::MDLogger&    logger)
828 /* If fr_time == -1 read the last frame available which is complete */
829 {
830     bool         bReadVel;
831     t_trxframe   fr;
832     t_trxstatus* fp;
833     double       use_time;
834
835     bReadVel = (bNeedVel && !bGenVel);
836
837     GMX_LOG(logger.info)
838             .asParagraph()
839             .appendTextFormatted("Reading Coordinates%s and Box size from old trajectory",
840                                  bReadVel ? ", Velocities" : "");
841     if (fr_time == -1)
842     {
843         GMX_LOG(logger.info).asParagraph().appendTextFormatted("Will read whole trajectory");
844     }
845     else
846     {
847         GMX_LOG(logger.info).asParagraph().appendTextFormatted("Will read till time %g", fr_time);
848     }
849     if (!bReadVel)
850     {
851         if (bGenVel)
852         {
853             GMX_LOG(logger.info)
854                     .asParagraph()
855                     .appendTextFormatted(
856                             "Velocities generated: "
857                             "ignoring velocities in input trajectory");
858         }
859         read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X);
860     }
861     else
862     {
863         read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X | TRX_NEED_V);
864
865         if (!fr.bV)
866         {
867             GMX_LOG(logger.warning)
868                     .asParagraph()
869                     .appendTextFormatted(
870                             "WARNING: Did not find a frame with velocities in file %s,\n"
871                             "         all velocities will be set to zero!",
872                             slog);
873             for (auto& vi : makeArrayRef(state->v))
874             {
875                 vi = { 0, 0, 0 };
876             }
877             close_trx(fp);
878             /* Search for a frame without velocities */
879             bReadVel = false;
880             read_first_frame(oenv, &fp, slog, &fr, TRX_NEED_X);
881         }
882     }
883
884     state->natoms = fr.natoms;
885
886     if (sys->natoms != state->natoms)
887     {
888         gmx_fatal(FARGS,
889                   "Number of atoms in Topology "
890                   "is not the same as in Trajectory");
891     }
892     copy_state(slog, &fr, bReadVel, state, &use_time);
893
894     /* Find the appropriate frame */
895     while ((fr_time == -1 || fr.time < fr_time) && read_next_frame(oenv, fp, &fr))
896     {
897         copy_state(slog, &fr, bReadVel, state, &use_time);
898     }
899
900     close_trx(fp);
901
902     /* Set the relative box lengths for preserving the box shape.
903      * Note that this call can lead to differences in the last bit
904      * with respect to using gmx convert-tpr to create a [REF].tpx[ref] file.
905      */
906     set_box_rel(ir, state);
907
908     GMX_LOG(logger.info).asParagraph().appendTextFormatted("Using frame at t = %g ps", use_time);
909     GMX_LOG(logger.info).asParagraph().appendTextFormatted("Starting time for run is %g ps", ir->init_t);
910
911     if ((ir->epc != PressureCoupling::No || ir->etc == TemperatureCoupling::NoseHoover) && ener)
912     {
913         get_enx_state(ener, use_time, sys->groups, ir, state);
914         preserve_box_shape(ir, state->box_rel, state->boxv);
915     }
916 }
917
918 static void read_posres(gmx_mtop_t*                              mtop,
919                         gmx::ArrayRef<const MoleculeInformation> molinfo,
920                         gmx_bool                                 bTopB,
921                         const char*                              fn,
922                         RefCoordScaling                          rc_scaling,
923                         PbcType                                  pbcType,
924                         rvec                                     com,
925                         warninp*                                 wi,
926                         const gmx::MDLogger&                     logger)
927 {
928     gmx_bool*   hadAtom;
929     rvec *      x, *v;
930     dvec        sum;
931     double      totmass;
932     t_topology* top;
933     matrix      box, invbox;
934     int         natoms, npbcdim = 0;
935     int         a, nat_molb;
936     t_atom*     atom;
937
938     snew(top, 1);
939     read_tps_conf(fn, top, nullptr, &x, &v, box, FALSE);
940     natoms = top->atoms.nr;
941     done_top(top);
942     sfree(top);
943     if (natoms != mtop->natoms)
944     {
945         std::string warningMessage = gmx::formatString(
946                 "The number of atoms in %s (%d) does not match the number of atoms in the topology "
947                 "(%d). Will assume that the first %d atoms in the topology and %s match.",
948                 fn,
949                 natoms,
950                 mtop->natoms,
951                 std::min(mtop->natoms, natoms),
952                 fn);
953         warning(wi, warningMessage.c_str());
954     }
955
956     npbcdim = numPbcDimensions(pbcType);
957     GMX_RELEASE_ASSERT(npbcdim <= DIM, "Invalid npbcdim");
958     clear_rvec(com);
959     if (rc_scaling != RefCoordScaling::No)
960     {
961         copy_mat(box, invbox);
962         for (int j = npbcdim; j < DIM; j++)
963         {
964             clear_rvec(invbox[j]);
965             invbox[j][j] = 1;
966         }
967         gmx::invertBoxMatrix(invbox, invbox);
968     }
969
970     /* Copy the reference coordinates to mtop */
971     clear_dvec(sum);
972     totmass = 0;
973     a       = 0;
974     snew(hadAtom, natoms);
975     for (gmx_molblock_t& molb : mtop->molblock)
976     {
977         nat_molb                       = molb.nmol * mtop->moltype[molb.type].atoms.nr;
978         const InteractionsOfType* pr   = &(molinfo[molb.type].interactions[F_POSRES]);
979         const InteractionsOfType* prfb = &(molinfo[molb.type].interactions[F_FBPOSRES]);
980         if (pr->size() > 0 || prfb->size() > 0)
981         {
982             atom = mtop->moltype[molb.type].atoms.atom;
983             for (const auto& restraint : pr->interactionTypes)
984             {
985                 int ai = restraint.ai();
986                 if (ai >= natoms)
987                 {
988                     gmx_fatal(FARGS,
989                               "Position restraint atom index (%d) in moltype '%s' is larger than "
990                               "number of atoms in %s (%d).\n",
991                               ai + 1,
992                               *molinfo[molb.type].name,
993                               fn,
994                               natoms);
995                 }
996                 hadAtom[ai] = TRUE;
997                 if (rc_scaling == RefCoordScaling::Com)
998                 {
999                     /* Determine the center of mass of the posres reference coordinates */
1000                     for (int j = 0; j < npbcdim; j++)
1001                     {
1002                         sum[j] += atom[ai].m * x[a + ai][j];
1003                     }
1004                     totmass += atom[ai].m;
1005                 }
1006             }
1007             /* Same for flat-bottomed posres, but do not count an atom twice for COM */
1008             for (const auto& restraint : prfb->interactionTypes)
1009             {
1010                 int ai = restraint.ai();
1011                 if (ai >= natoms)
1012                 {
1013                     gmx_fatal(FARGS,
1014                               "Position restraint atom index (%d) in moltype '%s' is larger than "
1015                               "number of atoms in %s (%d).\n",
1016                               ai + 1,
1017                               *molinfo[molb.type].name,
1018                               fn,
1019                               natoms);
1020                 }
1021                 if (rc_scaling == RefCoordScaling::Com && !hadAtom[ai])
1022                 {
1023                     /* Determine the center of mass of the posres reference coordinates */
1024                     for (int j = 0; j < npbcdim; j++)
1025                     {
1026                         sum[j] += atom[ai].m * x[a + ai][j];
1027                     }
1028                     totmass += atom[ai].m;
1029                 }
1030             }
1031             if (!bTopB)
1032             {
1033                 molb.posres_xA.resize(nat_molb);
1034                 for (int i = 0; i < nat_molb; i++)
1035                 {
1036                     copy_rvec(x[a + i], molb.posres_xA[i]);
1037                 }
1038             }
1039             else
1040             {
1041                 molb.posres_xB.resize(nat_molb);
1042                 for (int i = 0; i < nat_molb; i++)
1043                 {
1044                     copy_rvec(x[a + i], molb.posres_xB[i]);
1045                 }
1046             }
1047         }
1048         a += nat_molb;
1049     }
1050     if (rc_scaling == RefCoordScaling::Com)
1051     {
1052         if (totmass == 0)
1053         {
1054             gmx_fatal(FARGS, "The total mass of the position restraint atoms is 0");
1055         }
1056         for (int j = 0; j < npbcdim; j++)
1057         {
1058             com[j] = sum[j] / totmass;
1059         }
1060         GMX_LOG(logger.info)
1061                 .asParagraph()
1062                 .appendTextFormatted(
1063                         "The center of mass of the position restraint coord's is %6.3f %6.3f %6.3f",
1064                         com[XX],
1065                         com[YY],
1066                         com[ZZ]);
1067     }
1068
1069     if (rc_scaling != RefCoordScaling::No)
1070     {
1071         GMX_ASSERT(npbcdim <= DIM, "Only DIM dimensions can have PBC");
1072
1073         for (gmx_molblock_t& molb : mtop->molblock)
1074         {
1075             nat_molb = molb.nmol * mtop->moltype[molb.type].atoms.nr;
1076             if (!molb.posres_xA.empty() || !molb.posres_xB.empty())
1077             {
1078                 std::vector<gmx::RVec>& xp = (!bTopB ? molb.posres_xA : molb.posres_xB);
1079                 for (int i = 0; i < nat_molb; i++)
1080                 {
1081                     for (int j = 0; j < npbcdim; j++)
1082                     {
1083                         if (rc_scaling == RefCoordScaling::All)
1084                         {
1085                             /* Convert from Cartesian to crystal coordinates */
1086                             xp[i][j] *= invbox[j][j];
1087                             for (int k = j + 1; k < npbcdim; k++)
1088                             {
1089                                 xp[i][j] += invbox[k][j] * xp[i][k];
1090                             }
1091                         }
1092                         else if (rc_scaling == RefCoordScaling::Com)
1093                         {
1094                             /* Subtract the center of mass */
1095                             xp[i][j] -= com[j];
1096                         }
1097                     }
1098                 }
1099             }
1100         }
1101
1102         if (rc_scaling == RefCoordScaling::Com)
1103         {
1104             /* Convert the COM from Cartesian to crystal coordinates */
1105             for (int j = 0; j < npbcdim; j++)
1106             {
1107                 com[j] *= invbox[j][j];
1108                 for (int k = j + 1; k < npbcdim; k++)
1109                 {
1110                     com[j] += invbox[k][j] * com[k];
1111                 }
1112             }
1113         }
1114     }
1115
1116     sfree(x);
1117     sfree(v);
1118     sfree(hadAtom);
1119 }
1120
1121 static void gen_posres(gmx_mtop_t*                              mtop,
1122                        gmx::ArrayRef<const MoleculeInformation> mi,
1123                        const char*                              fnA,
1124                        const char*                              fnB,
1125                        RefCoordScaling                          rc_scaling,
1126                        PbcType                                  pbcType,
1127                        rvec                                     com,
1128                        rvec                                     comB,
1129                        warninp*                                 wi,
1130                        const gmx::MDLogger&                     logger)
1131 {
1132     read_posres(mtop, mi, FALSE, fnA, rc_scaling, pbcType, com, wi, logger);
1133     /* It is safer to simply read the b-state posres rather than trying
1134      * to be smart and copy the positions.
1135      */
1136     read_posres(mtop, mi, TRUE, fnB, rc_scaling, pbcType, comB, wi, logger);
1137 }
1138
1139 static void set_wall_atomtype(PreprocessingAtomTypes* at,
1140                               t_gromppopts*           opts,
1141                               t_inputrec*             ir,
1142                               warninp*                wi,
1143                               const gmx::MDLogger&    logger)
1144 {
1145     int i;
1146
1147     if (ir->nwall > 0)
1148     {
1149         GMX_LOG(logger.info).asParagraph().appendTextFormatted("Searching the wall atom type(s)");
1150     }
1151     for (i = 0; i < ir->nwall; i++)
1152     {
1153         auto atomType = at->atomTypeFromName(opts->wall_atomtype[i]);
1154         if (!atomType.has_value())
1155         {
1156             std::string warningMessage = gmx::formatString(
1157                     "Specified wall atom type %s is not defined", opts->wall_atomtype[i]);
1158             warning_error(wi, warningMessage.c_str());
1159         }
1160         else
1161         {
1162             ir->wall_atomtype[i] = *atomType;
1163         }
1164     }
1165 }
1166
1167 static int nrdf_internal(const t_atoms* atoms)
1168 {
1169     int i, nmass, nrdf;
1170
1171     nmass = 0;
1172     for (i = 0; i < atoms->nr; i++)
1173     {
1174         /* Vsite ptype might not be set here yet, so also check the mass */
1175         if ((atoms->atom[i].ptype == ParticleType::Atom || atoms->atom[i].ptype == ParticleType::Nucleus)
1176             && atoms->atom[i].m > 0)
1177         {
1178             nmass++;
1179         }
1180     }
1181     switch (nmass)
1182     {
1183         case 0: // Fall through intended
1184         case 1: nrdf = 0; break;
1185         case 2: nrdf = 1; break;
1186         default: nrdf = nmass * 3 - 6; break;
1187     }
1188
1189     return nrdf;
1190 }
1191
1192 static void spline1d(double dx, const double* y, int n, double* u, double* y2)
1193 {
1194     int    i;
1195     double p, q;
1196
1197     y2[0] = 0.0;
1198     u[0]  = 0.0;
1199
1200     for (i = 1; i < n - 1; i++)
1201     {
1202         p     = 0.5 * y2[i - 1] + 2.0;
1203         y2[i] = -0.5 / p;
1204         q     = (y[i + 1] - 2.0 * y[i] + y[i - 1]) / dx;
1205         u[i]  = (3.0 * q / dx - 0.5 * u[i - 1]) / p;
1206     }
1207
1208     y2[n - 1] = 0.0;
1209
1210     for (i = n - 2; i >= 0; i--)
1211     {
1212         y2[i] = y2[i] * y2[i + 1] + u[i];
1213     }
1214 }
1215
1216
1217 static void
1218 interpolate1d(double xmin, double dx, const double* ya, const double* y2a, double x, double* y, double* y1)
1219 {
1220     int    ix;
1221     double a, b;
1222
1223     ix = static_cast<int>((x - xmin) / dx);
1224
1225     a = (xmin + (ix + 1) * dx - x) / dx;
1226     b = (x - xmin - ix * dx) / dx;
1227
1228     *y = a * ya[ix] + b * ya[ix + 1]
1229          + ((a * a * a - a) * y2a[ix] + (b * b * b - b) * y2a[ix + 1]) * (dx * dx) / 6.0;
1230     *y1 = (ya[ix + 1] - ya[ix]) / dx - (3.0 * a * a - 1.0) / 6.0 * dx * y2a[ix]
1231           + (3.0 * b * b - 1.0) / 6.0 * dx * y2a[ix + 1];
1232 }
1233
1234
1235 static void setup_cmap(int grid_spacing, int nc, gmx::ArrayRef<const real> grid, gmx_cmap_t* cmap_grid)
1236 {
1237     int    i, j, k, ii, jj, kk, idx;
1238     int    offset;
1239     double dx, xmin, v, v1, v2, v12;
1240     double phi, psi;
1241
1242     std::vector<double> tmp_u(2 * grid_spacing, 0.0);
1243     std::vector<double> tmp_u2(2 * grid_spacing, 0.0);
1244     std::vector<double> tmp_yy(2 * grid_spacing, 0.0);
1245     std::vector<double> tmp_y1(2 * grid_spacing, 0.0);
1246     std::vector<double> tmp_t2(2 * grid_spacing * 2 * grid_spacing, 0.0);
1247     std::vector<double> tmp_grid(2 * grid_spacing * 2 * grid_spacing, 0.0);
1248
1249     dx   = 360.0 / grid_spacing;
1250     xmin = -180.0 - dx * grid_spacing / 2;
1251
1252     for (kk = 0; kk < nc; kk++)
1253     {
1254         /* Compute an offset depending on which cmap we are using
1255          * Offset will be the map number multiplied with the
1256          * grid_spacing * grid_spacing * 2
1257          */
1258         offset = kk * grid_spacing * grid_spacing * 2;
1259
1260         for (i = 0; i < 2 * grid_spacing; i++)
1261         {
1262             ii = (i + grid_spacing - grid_spacing / 2) % grid_spacing;
1263
1264             for (j = 0; j < 2 * grid_spacing; j++)
1265             {
1266                 jj = (j + grid_spacing - grid_spacing / 2) % grid_spacing;
1267                 tmp_grid[i * grid_spacing * 2 + j] = grid[offset + ii * grid_spacing + jj];
1268             }
1269         }
1270
1271         for (i = 0; i < 2 * grid_spacing; i++)
1272         {
1273             spline1d(dx,
1274                      &(tmp_grid[2 * grid_spacing * i]),
1275                      2 * grid_spacing,
1276                      tmp_u.data(),
1277                      &(tmp_t2[2 * grid_spacing * i]));
1278         }
1279
1280         for (i = grid_spacing / 2; i < grid_spacing + grid_spacing / 2; i++)
1281         {
1282             ii  = i - grid_spacing / 2;
1283             phi = ii * dx - 180.0;
1284
1285             for (j = grid_spacing / 2; j < grid_spacing + grid_spacing / 2; j++)
1286             {
1287                 jj  = j - grid_spacing / 2;
1288                 psi = jj * dx - 180.0;
1289
1290                 for (k = 0; k < 2 * grid_spacing; k++)
1291                 {
1292                     interpolate1d(xmin,
1293                                   dx,
1294                                   &(tmp_grid[2 * grid_spacing * k]),
1295                                   &(tmp_t2[2 * grid_spacing * k]),
1296                                   psi,
1297                                   &tmp_yy[k],
1298                                   &tmp_y1[k]);
1299                 }
1300
1301                 spline1d(dx, tmp_yy.data(), 2 * grid_spacing, tmp_u.data(), tmp_u2.data());
1302                 interpolate1d(xmin, dx, tmp_yy.data(), tmp_u2.data(), phi, &v, &v1);
1303                 spline1d(dx, tmp_y1.data(), 2 * grid_spacing, tmp_u.data(), tmp_u2.data());
1304                 interpolate1d(xmin, dx, tmp_y1.data(), tmp_u2.data(), phi, &v2, &v12);
1305
1306                 idx                                       = ii * grid_spacing + jj;
1307                 cmap_grid->cmapdata[kk].cmap[idx * 4]     = grid[offset + ii * grid_spacing + jj];
1308                 cmap_grid->cmapdata[kk].cmap[idx * 4 + 1] = v1;
1309                 cmap_grid->cmapdata[kk].cmap[idx * 4 + 2] = v2;
1310                 cmap_grid->cmapdata[kk].cmap[idx * 4 + 3] = v12;
1311             }
1312         }
1313     }
1314 }
1315
1316 static void init_cmap_grid(gmx_cmap_t* cmap_grid, int ngrid, int grid_spacing)
1317 {
1318     int i, nelem;
1319
1320     cmap_grid->grid_spacing = grid_spacing;
1321     nelem                   = cmap_grid->grid_spacing * cmap_grid->grid_spacing;
1322
1323     cmap_grid->cmapdata.resize(ngrid);
1324
1325     for (i = 0; i < ngrid; i++)
1326     {
1327         cmap_grid->cmapdata[i].cmap.resize(4 * nelem);
1328     }
1329 }
1330
1331
1332 static int count_constraints(const gmx_mtop_t* mtop, gmx::ArrayRef<const MoleculeInformation> mi, warninp* wi)
1333 {
1334     int count, count_mol;
1335
1336     count = 0;
1337     for (const gmx_molblock_t& molb : mtop->molblock)
1338     {
1339         count_mol                                            = 0;
1340         gmx::ArrayRef<const InteractionsOfType> interactions = mi[molb.type].interactions;
1341
1342         for (int i = 0; i < F_NRE; i++)
1343         {
1344             if (i == F_SETTLE)
1345             {
1346                 count_mol += 3 * interactions[i].size();
1347             }
1348             else if (interaction_function[i].flags & IF_CONSTRAINT)
1349             {
1350                 count_mol += interactions[i].size();
1351             }
1352         }
1353
1354         if (count_mol > nrdf_internal(&mi[molb.type].atoms))
1355         {
1356             std::string warningMessage = gmx::formatString(
1357                     "Molecule type '%s' has %d constraints.\n"
1358                     "For stability and efficiency there should not be more constraints than "
1359                     "internal number of degrees of freedom: %d.\n",
1360                     *mi[molb.type].name,
1361                     count_mol,
1362                     nrdf_internal(&mi[molb.type].atoms));
1363             warning(wi, warningMessage.c_str());
1364         }
1365         count += molb.nmol * count_mol;
1366     }
1367
1368     return count;
1369 }
1370
1371 static real calc_temp(const gmx_mtop_t* mtop, const t_inputrec* ir, rvec* v)
1372 {
1373     double sum_mv2 = 0;
1374     for (const AtomProxy atomP : AtomRange(*mtop))
1375     {
1376         const t_atom& local = atomP.atom();
1377         int           i     = atomP.globalAtomNumber();
1378         sum_mv2 += local.m * norm2(v[i]);
1379     }
1380
1381     double nrdf = 0;
1382     for (int g = 0; g < ir->opts.ngtc; g++)
1383     {
1384         nrdf += ir->opts.nrdf[g];
1385     }
1386
1387     return sum_mv2 / (nrdf * gmx::c_boltz);
1388 }
1389
1390 static real get_max_reference_temp(const t_inputrec* ir, warninp* wi)
1391 {
1392     real ref_t;
1393     int  i;
1394     bool bNoCoupl;
1395
1396     ref_t    = 0;
1397     bNoCoupl = false;
1398     for (i = 0; i < ir->opts.ngtc; i++)
1399     {
1400         if (ir->opts.tau_t[i] < 0)
1401         {
1402             bNoCoupl = true;
1403         }
1404         else
1405         {
1406             ref_t = std::max(ref_t, ir->opts.ref_t[i]);
1407         }
1408     }
1409
1410     if (bNoCoupl)
1411     {
1412         std::string warningMessage = gmx::formatString(
1413                 "Some temperature coupling groups do not use temperature coupling. We will assume "
1414                 "their temperature is not more than %.3f K. If their temperature is higher, the "
1415                 "energy error and the Verlet buffer might be underestimated.",
1416                 ref_t);
1417         warning(wi, warningMessage.c_str());
1418     }
1419
1420     return ref_t;
1421 }
1422
1423 /* Checks if there are unbound atoms in moleculetype molt.
1424  * Prints a note for each unbound atoms and a warning if any is present.
1425  */
1426 static void checkForUnboundAtoms(const gmx_moltype_t* molt, gmx_bool bVerbose, warninp* wi, const gmx::MDLogger& logger)
1427 {
1428     const t_atoms* atoms = &molt->atoms;
1429
1430     if (atoms->nr == 1)
1431     {
1432         /* Only one atom, there can't be unbound atoms */
1433         return;
1434     }
1435
1436     std::vector<int> count(atoms->nr, 0);
1437
1438     for (int ftype = 0; ftype < F_NRE; ftype++)
1439     {
1440         if (((interaction_function[ftype].flags & IF_BOND) && NRAL(ftype) == 2 && ftype != F_CONNBONDS)
1441             || (interaction_function[ftype].flags & IF_CONSTRAINT) || ftype == F_SETTLE)
1442         {
1443             const InteractionList& il   = molt->ilist[ftype];
1444             const int              nral = NRAL(ftype);
1445
1446             for (int i = 0; i < il.size(); i += 1 + nral)
1447             {
1448                 for (int j = 0; j < nral; j++)
1449                 {
1450                     count[il.iatoms[i + 1 + j]]++;
1451                 }
1452             }
1453         }
1454     }
1455
1456     int numDanglingAtoms = 0;
1457     for (int a = 0; a < atoms->nr; a++)
1458     {
1459         if (atoms->atom[a].ptype != ParticleType::VSite && count[a] == 0)
1460         {
1461             if (bVerbose)
1462             {
1463                 GMX_LOG(logger.warning)
1464                         .asParagraph()
1465                         .appendTextFormatted(
1466                                 "Atom %d '%s' in moleculetype '%s' is not bound by a potential or "
1467                                 "constraint to any other atom in the same moleculetype.",
1468                                 a + 1,
1469                                 *atoms->atomname[a],
1470                                 *molt->name);
1471             }
1472             numDanglingAtoms++;
1473         }
1474     }
1475
1476     if (numDanglingAtoms > 0)
1477     {
1478         std::string warningMessage = gmx::formatString(
1479                 "In moleculetype '%s' %d atoms are not bound by a potential or constraint to any "
1480                 "other atom in the same moleculetype. Although technically this might not cause "
1481                 "issues in a simulation, this often means that the user forgot to add a "
1482                 "bond/potential/constraint or put multiple molecules in the same moleculetype "
1483                 "definition by mistake. Run with -v to get information for each atom.",
1484                 *molt->name,
1485                 numDanglingAtoms);
1486         warning_note(wi, warningMessage.c_str());
1487     }
1488 }
1489
1490 /* Checks all moleculetypes for unbound atoms */
1491 static void checkForUnboundAtoms(const gmx_mtop_t* mtop, gmx_bool bVerbose, warninp* wi, const gmx::MDLogger& logger)
1492 {
1493     for (const gmx_moltype_t& molt : mtop->moltype)
1494     {
1495         checkForUnboundAtoms(&molt, bVerbose, wi, logger);
1496     }
1497 }
1498
1499 /*! \brief Checks if there are decoupled modes in moleculetype \p molt.
1500  *
1501  * The specific decoupled modes this routine check for are angle modes
1502  * where the two bonds are constrained and the atoms a both ends are only
1503  * involved in a single constraint; the mass of the two atoms needs to
1504  * differ by more than \p massFactorThreshold.
1505  */
1506 static bool haveDecoupledModeInMol(const gmx_moltype_t&           molt,
1507                                    gmx::ArrayRef<const t_iparams> iparams,
1508                                    real                           massFactorThreshold)
1509 {
1510     if (molt.ilist[F_CONSTR].empty() && molt.ilist[F_CONSTRNC].empty())
1511     {
1512         return false;
1513     }
1514
1515     const t_atom* atom = molt.atoms.atom;
1516
1517     const auto atomToConstraints =
1518             gmx::make_at2con(molt, iparams, gmx::FlexibleConstraintTreatment::Exclude);
1519
1520     bool haveDecoupledMode = false;
1521     for (int ftype = 0; ftype < F_NRE; ftype++)
1522     {
1523         if (interaction_function[ftype].flags & IF_ATYPE)
1524         {
1525             const int              nral = NRAL(ftype);
1526             const InteractionList& il   = molt.ilist[ftype];
1527             for (int i = 0; i < il.size(); i += 1 + nral)
1528             {
1529                 /* Here we check for the mass difference between the atoms
1530                  * at both ends of the angle, that the atoms at the ends have
1531                  * 1 contraint and the atom in the middle at least 3; we check
1532                  * that the 3 atoms are linked by constraints below.
1533                  * We check for at least three constraints for the middle atom,
1534                  * since with only the two bonds in the angle, we have 3-atom
1535                  * molecule, which has much more thermal exhange in this single
1536                  * angle mode than molecules with more atoms.
1537                  * Note that this check also catches molecules where more atoms
1538                  * are connected to one or more atoms in the angle, but by
1539                  * bond potentials instead of angles. But such cases will not
1540                  * occur in "normal" molecules and it doesn't hurt running
1541                  * those with higher accuracy settings as well.
1542                  */
1543                 int a0 = il.iatoms[1 + i];
1544                 int a1 = il.iatoms[1 + i + 1];
1545                 int a2 = il.iatoms[1 + i + 2];
1546                 if ((atom[a0].m > atom[a2].m * massFactorThreshold || atom[a2].m > atom[a0].m * massFactorThreshold)
1547                     && atomToConstraints[a0].ssize() == 1 && atomToConstraints[a2].ssize() == 1
1548                     && atomToConstraints[a1].ssize() >= 3)
1549                 {
1550                     int constraint0 = atomToConstraints[a0][0];
1551                     int constraint2 = atomToConstraints[a2][0];
1552
1553                     bool foundAtom0 = false;
1554                     bool foundAtom2 = false;
1555                     for (const int constraint : atomToConstraints[a1])
1556                     {
1557                         if (constraint == constraint0)
1558                         {
1559                             foundAtom0 = true;
1560                         }
1561                         if (constraint == constraint2)
1562                         {
1563                             foundAtom2 = true;
1564                         }
1565                     }
1566                     if (foundAtom0 && foundAtom2)
1567                     {
1568                         haveDecoupledMode = true;
1569                     }
1570                 }
1571             }
1572         }
1573     }
1574
1575     return haveDecoupledMode;
1576 }
1577
1578 /*! \brief Checks if the Verlet buffer and constraint accuracy is sufficient for decoupled dynamic modes.
1579  *
1580  * When decoupled modes are present and the accuray in insufficient,
1581  * this routine issues a warning if the accuracy is insufficient.
1582  */
1583 static void checkDecoupledModeAccuracy(const gmx_mtop_t* mtop, const t_inputrec* ir, warninp* wi)
1584 {
1585     /* We only have issues with decoupled modes with normal MD.
1586      * With stochastic dynamics equipartitioning is enforced strongly.
1587      */
1588     if (!EI_MD(ir->eI))
1589     {
1590         return;
1591     }
1592
1593     /* When atoms of very different mass are involved in an angle potential
1594      * and both bonds in the angle are constrained, the dynamic modes in such
1595      * angles have very different periods and significant energy exchange
1596      * takes several nanoseconds. Thus even a small amount of error in
1597      * different algorithms can lead to violation of equipartitioning.
1598      * The parameters below are mainly based on an all-atom chloroform model
1599      * with all bonds constrained. Equipartitioning is off by more than 1%
1600      * (need to run 5-10 ns) when the difference in mass between H and Cl
1601      * is more than a factor 13 and the accuracy is less than the thresholds
1602      * given below. This has been verified on some other molecules.
1603      *
1604      * Note that the buffer and shake parameters have unit length and
1605      * energy/time, respectively, so they will "only" work correctly
1606      * for atomistic force fields using MD units.
1607      */
1608     const real massFactorThreshold      = 13.0;
1609     const real bufferToleranceThreshold = 1e-4;
1610     const int  lincsIterationThreshold  = 2;
1611     const int  lincsOrderThreshold      = 4;
1612     const real shakeToleranceThreshold  = 0.005 * ir->delta_t;
1613
1614     bool lincsWithSufficientTolerance =
1615             (ir->eConstrAlg == ConstraintAlgorithm::Lincs
1616              && ir->nLincsIter >= lincsIterationThreshold && ir->nProjOrder >= lincsOrderThreshold);
1617     bool shakeWithSufficientTolerance = (ir->eConstrAlg == ConstraintAlgorithm::Shake
1618                                          && ir->shake_tol <= 1.1 * shakeToleranceThreshold);
1619     if (ir->cutoff_scheme == CutoffScheme::Verlet && ir->verletbuf_tol <= 1.1 * bufferToleranceThreshold
1620         && (lincsWithSufficientTolerance || shakeWithSufficientTolerance))
1621     {
1622         return;
1623     }
1624
1625     bool haveDecoupledMode = false;
1626     for (const gmx_moltype_t& molt : mtop->moltype)
1627     {
1628         if (haveDecoupledModeInMol(molt, mtop->ffparams.iparams, massFactorThreshold))
1629         {
1630             haveDecoupledMode = true;
1631         }
1632     }
1633
1634     if (haveDecoupledMode)
1635     {
1636         std::string message = gmx::formatString(
1637                 "There are atoms at both ends of an angle, connected by constraints "
1638                 "and with masses that differ by more than a factor of %g. This means "
1639                 "that there are likely dynamic modes that are only very weakly coupled.",
1640                 massFactorThreshold);
1641         if (ir->cutoff_scheme == CutoffScheme::Verlet)
1642         {
1643             message += gmx::formatString(
1644                     " To ensure good equipartitioning, you need to either not use "
1645                     "constraints on all bonds (but, if possible, only on bonds involving "
1646                     "hydrogens) or use integrator = %s or decrease one or more tolerances: "
1647                     "verlet-buffer-tolerance <= %g, LINCS iterations >= %d, LINCS order "
1648                     ">= %d or SHAKE tolerance <= %g",
1649                     enumValueToString(IntegrationAlgorithm::SD1),
1650                     bufferToleranceThreshold,
1651                     lincsIterationThreshold,
1652                     lincsOrderThreshold,
1653                     shakeToleranceThreshold);
1654         }
1655         else
1656         {
1657             message += gmx::formatString(
1658                     " To ensure good equipartitioning, we suggest to switch to the %s "
1659                     "cutoff-scheme, since that allows for better control over the Verlet "
1660                     "buffer size and thus over the energy drift.",
1661                     enumValueToString(CutoffScheme::Verlet));
1662         }
1663         warning(wi, message);
1664     }
1665 }
1666
1667 static void set_verlet_buffer(const gmx_mtop_t*    mtop,
1668                               t_inputrec*          ir,
1669                               real                 buffer_temp,
1670                               matrix               box,
1671                               warninp*             wi,
1672                               const gmx::MDLogger& logger)
1673 {
1674     GMX_LOG(logger.info)
1675             .asParagraph()
1676             .appendTextFormatted(
1677                     "Determining Verlet buffer for a tolerance of %g kJ/mol/ps at %g K",
1678                     ir->verletbuf_tol,
1679                     buffer_temp);
1680
1681     /* Calculate the buffer size for simple atom vs atoms list */
1682     VerletbufListSetup listSetup1x1;
1683     listSetup1x1.cluster_size_i = 1;
1684     listSetup1x1.cluster_size_j = 1;
1685     const real rlist_1x1        = calcVerletBufferSize(
1686             *mtop, det(box), *ir, ir->nstlist, ir->nstlist - 1, buffer_temp, listSetup1x1);
1687
1688     /* Set the pair-list buffer size in ir */
1689     VerletbufListSetup listSetup4x4 = verletbufGetSafeListSetup(ListSetupType::CpuNoSimd);
1690     ir->rlist                       = calcVerletBufferSize(
1691             *mtop, det(box), *ir, ir->nstlist, ir->nstlist - 1, buffer_temp, listSetup4x4);
1692
1693     const int n_nonlin_vsite = gmx::countNonlinearVsites(*mtop);
1694     if (n_nonlin_vsite > 0)
1695     {
1696         std::string warningMessage = gmx::formatString(
1697                 "There are %d non-linear virtual site constructions. Their contribution to the "
1698                 "energy error is approximated. In most cases this does not affect the error "
1699                 "significantly.",
1700                 n_nonlin_vsite);
1701         warning_note(wi, warningMessage);
1702     }
1703
1704     GMX_LOG(logger.info)
1705             .asParagraph()
1706             .appendTextFormatted(
1707                     "Calculated rlist for %dx%d atom pair-list as %.3f nm, buffer size %.3f nm",
1708                     1,
1709                     1,
1710                     rlist_1x1,
1711                     rlist_1x1 - std::max(ir->rvdw, ir->rcoulomb));
1712
1713     GMX_LOG(logger.info)
1714             .asParagraph()
1715             .appendTextFormatted(
1716                     "Set rlist, assuming %dx%d atom pair-list, to %.3f nm, buffer size %.3f nm",
1717                     listSetup4x4.cluster_size_i,
1718                     listSetup4x4.cluster_size_j,
1719                     ir->rlist,
1720                     ir->rlist - std::max(ir->rvdw, ir->rcoulomb));
1721
1722     GMX_LOG(logger.info)
1723             .asParagraph()
1724             .appendTextFormatted(
1725                     "Note that mdrun will redetermine rlist based on the actual pair-list setup");
1726
1727     if (gmx::square(ir->rlist) >= max_cutoff2(ir->pbcType, box))
1728     {
1729         gmx_fatal(FARGS,
1730                   "The pair-list cut-off (%g nm) is longer than half the shortest box vector or "
1731                   "longer than the smallest box diagonal element (%g nm). Increase the box size or "
1732                   "decrease nstlist or increase verlet-buffer-tolerance.",
1733                   ir->rlist,
1734                   std::sqrt(max_cutoff2(ir->pbcType, box)));
1735     }
1736 }
1737
1738 int gmx_grompp(int argc, char* argv[])
1739 {
1740     const char* desc[] = {
1741         "[THISMODULE] (the gromacs preprocessor)",
1742         "reads a molecular topology file, checks the validity of the",
1743         "file, expands the topology from a molecular description to an atomic",
1744         "description. The topology file contains information about",
1745         "molecule types and the number of molecules, the preprocessor",
1746         "copies each molecule as needed. ",
1747         "There is no limitation on the number of molecule types. ",
1748         "Bonds and bond-angles can be converted into constraints, separately",
1749         "for hydrogens and heavy atoms.",
1750         "Then a coordinate file is read and velocities can be generated",
1751         "from a Maxwellian distribution if requested.",
1752         "[THISMODULE] also reads parameters for [gmx-mdrun] ",
1753         "(eg. number of MD steps, time step, cut-off).",
1754         "Eventually a binary file is produced that can serve as the sole input",
1755         "file for the MD program.[PAR]",
1756
1757         "[THISMODULE] uses the atom names from the topology file. The atom names",
1758         "in the coordinate file (option [TT]-c[tt]) are only read to generate",
1759         "warnings when they do not match the atom names in the topology.",
1760         "Note that the atom names are irrelevant for the simulation as",
1761         "only the atom types are used for generating interaction parameters.[PAR]",
1762
1763         "[THISMODULE] uses a built-in preprocessor to resolve includes, macros, ",
1764         "etc. The preprocessor supports the following keywords::",
1765         "",
1766         "    #ifdef VARIABLE",
1767         "    #ifndef VARIABLE",
1768         "    #else",
1769         "    #endif",
1770         "    #define VARIABLE",
1771         "    #undef VARIABLE",
1772         "    #include \"filename\"",
1773         "    #include <filename>",
1774         "",
1775         "The functioning of these statements in your topology may be modulated by",
1776         "using the following two flags in your [REF].mdp[ref] file::",
1777         "",
1778         "    define = -DVARIABLE1 -DVARIABLE2",
1779         "    include = -I/home/john/doe",
1780         "",
1781         "For further information a C-programming textbook may help you out.",
1782         "Specifying the [TT]-pp[tt] flag will get the pre-processed",
1783         "topology file written out so that you can verify its contents.[PAR]",
1784
1785         "When using position restraints, a file with restraint coordinates",
1786         "must be supplied with [TT]-r[tt] (can be the same file as supplied",
1787         "for [TT]-c[tt]). For free energy calculations, separate reference",
1788         "coordinates for the B topology can be supplied with [TT]-rb[tt],",
1789         "otherwise they will be equal to those of the A topology.[PAR]",
1790
1791         "Starting coordinates can be read from trajectory with [TT]-t[tt].",
1792         "The last frame with coordinates and velocities will be read,",
1793         "unless the [TT]-time[tt] option is used. Only if this information",
1794         "is absent will the coordinates in the [TT]-c[tt] file be used.",
1795         "Note that these velocities will not be used when [TT]gen_vel = yes[tt]",
1796         "in your [REF].mdp[ref] file. An energy file can be supplied with",
1797         "[TT]-e[tt] to read Nose-Hoover and/or Parrinello-Rahman coupling",
1798         "variables.[PAR]",
1799
1800         "[THISMODULE] can be used to restart simulations (preserving",
1801         "continuity) by supplying just a checkpoint file with [TT]-t[tt].",
1802         "However, for simply changing the number of run steps to extend",
1803         "a run, using [gmx-convert-tpr] is more convenient than [THISMODULE].",
1804         "You then supply the old checkpoint file directly to [gmx-mdrun]",
1805         "with [TT]-cpi[tt]. If you wish to change the ensemble or things",
1806         "like output frequency, then supplying the checkpoint file to",
1807         "[THISMODULE] with [TT]-t[tt] along with a new [REF].mdp[ref] file",
1808         "with [TT]-f[tt] is the recommended procedure. Actually preserving",
1809         "the ensemble (if possible) still requires passing the checkpoint",
1810         "file to [gmx-mdrun] [TT]-cpi[tt].[PAR]",
1811
1812         "By default, all bonded interactions which have constant energy due to",
1813         "virtual site constructions will be removed. If this constant energy is",
1814         "not zero, this will result in a shift in the total energy. All bonded",
1815         "interactions can be kept by turning off [TT]-rmvsbds[tt]. Additionally,",
1816         "all constraints for distances which will be constant anyway because",
1817         "of virtual site constructions will be removed. If any constraints remain",
1818         "which involve virtual sites, a fatal error will result.[PAR]",
1819
1820         "To verify your run input file, please take note of all warnings",
1821         "on the screen, and correct where necessary. Do also look at the contents",
1822         "of the [TT]mdout.mdp[tt] file; this contains comment lines, as well as",
1823         "the input that [THISMODULE] has read. If in doubt, you can start [THISMODULE]",
1824         "with the [TT]-debug[tt] option which will give you more information",
1825         "in a file called [TT]grompp.log[tt] (along with real debug info). You",
1826         "can see the contents of the run input file with the [gmx-dump]",
1827         "program. [gmx-check] can be used to compare the contents of two",
1828         "run input files.[PAR]",
1829
1830         "The [TT]-maxwarn[tt] option can be used to override warnings printed",
1831         "by [THISMODULE] that otherwise halt output. In some cases, warnings are",
1832         "harmless, but usually they are not. The user is advised to carefully",
1833         "interpret the output messages before attempting to bypass them with",
1834         "this option."
1835     };
1836     std::vector<MoleculeInformation>     mi;
1837     std::unique_ptr<MoleculeInformation> intermolecular_interactions;
1838     int                                  nvsite;
1839     CombinationRule                      comb;
1840     real                                 fudgeQQ;
1841     double                               reppow;
1842     const char*                          mdparin;
1843     bool                                 bNeedVel, bGenVel;
1844     gmx_output_env_t*                    oenv;
1845     gmx_bool                             bVerbose = FALSE;
1846     warninp*                             wi;
1847
1848     t_filenm fnm[] = { { efMDP, nullptr, nullptr, ffREAD },
1849                        { efMDP, "-po", "mdout", ffWRITE },
1850                        { efSTX, "-c", nullptr, ffREAD },
1851                        { efSTX, "-r", "restraint", ffOPTRD },
1852                        { efSTX, "-rb", "restraint", ffOPTRD },
1853                        { efNDX, nullptr, nullptr, ffOPTRD },
1854                        { efTOP, nullptr, nullptr, ffREAD },
1855                        { efTOP, "-pp", "processed", ffOPTWR },
1856                        { efTPR, "-o", nullptr, ffWRITE },
1857                        { efTRN, "-t", nullptr, ffOPTRD },
1858                        { efEDR, "-e", nullptr, ffOPTRD },
1859                        /* This group is needed by the VMD viewer as the start configuration for IMD sessions: */
1860                        { efGRO, "-imd", "imdgroup", ffOPTWR },
1861                        { efTRN, "-ref", "rotref", ffOPTRW | ffALLOW_MISSING },
1862                        /* This group is needed by the QMMM MDModule: */
1863                        { efQMI, "-qmi", nullptr, ffOPTRD } };
1864 #define NFILE asize(fnm)
1865
1866     /* Command line options */
1867     gmx_bool bRenum   = TRUE;
1868     gmx_bool bRmVSBds = TRUE, bZero = FALSE;
1869     int      i, maxwarn             = 0;
1870     real     fr_time = -1;
1871     t_pargs  pa[]    = {
1872         { "-v", FALSE, etBOOL, { &bVerbose }, "Be loud and noisy" },
1873         { "-time", FALSE, etREAL, { &fr_time }, "Take frame at or first after this time." },
1874         { "-rmvsbds",
1875           FALSE,
1876           etBOOL,
1877           { &bRmVSBds },
1878           "Remove constant bonded interactions with virtual sites" },
1879         { "-maxwarn",
1880           FALSE,
1881           etINT,
1882           { &maxwarn },
1883           "Number of allowed warnings during input processing. Not for normal use and may "
1884           "generate unstable systems" },
1885         { "-zero",
1886           FALSE,
1887           etBOOL,
1888           { &bZero },
1889           "Set parameters for bonded interactions without defaults to zero instead of "
1890           "generating an error" },
1891         { "-renum",
1892           FALSE,
1893           etBOOL,
1894           { &bRenum },
1895           "Renumber atomtypes and minimize number of atomtypes" }
1896     };
1897
1898     /* Parse the command line */
1899     if (!parse_common_args(&argc, argv, 0, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
1900     {
1901         return 0;
1902     }
1903
1904     /* Initiate some variables */
1905     gmx::MDModules mdModules;
1906     t_inputrec     irInstance;
1907     t_inputrec*    ir = &irInstance;
1908     t_gromppopts   optsInstance;
1909     t_gromppopts*  opts = &optsInstance;
1910     snew(opts->include, STRLEN);
1911     snew(opts->define, STRLEN);
1912
1913     gmx::LoggerBuilder builder;
1914     builder.addTargetStream(gmx::MDLogger::LogLevel::Info, &gmx::TextOutputFile::standardOutput());
1915     builder.addTargetStream(gmx::MDLogger::LogLevel::Warning, &gmx::TextOutputFile::standardError());
1916     gmx::LoggerOwner    logOwner(builder.build());
1917     const gmx::MDLogger logger(logOwner.logger());
1918
1919
1920     wi = init_warning(TRUE, maxwarn);
1921
1922     /* PARAMETER file processing */
1923     mdparin = opt2fn("-f", NFILE, fnm);
1924     set_warning_line(wi, mdparin, -1);
1925     try
1926     {
1927         get_ir(mdparin, opt2fn("-po", NFILE, fnm), &mdModules, ir, opts, WriteMdpHeader::yes, wi);
1928     }
1929     GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
1930
1931     // Now that the MDModules have their options assigned from get_ir, subscribe
1932     // to eventual notifications during pre-processing their data
1933     mdModules.subscribeToPreProcessingNotifications();
1934
1935     // Notify MDModules of existing logger
1936     mdModules.notifiers().preProcessingNotifier_.notify(logger);
1937
1938     // Notify MDModules of existing warninp
1939     mdModules.notifiers().preProcessingNotifier_.notify(wi);
1940
1941     // Notify QMMM MDModule of external QM input file command-line option
1942     {
1943         gmx::QMInputFileName qmInputFileName = { ftp2bSet(efQMI, NFILE, fnm), ftp2fn(efQMI, NFILE, fnm) };
1944         mdModules.notifiers().preProcessingNotifier_.notify(qmInputFileName);
1945     }
1946
1947     if (bVerbose)
1948     {
1949         GMX_LOG(logger.info)
1950                 .asParagraph()
1951                 .appendTextFormatted("checking input for internal consistency...");
1952     }
1953     check_ir(mdparin, mdModules.notifiers(), ir, opts, wi);
1954
1955     if (ir->ld_seed == -1)
1956     {
1957         ir->ld_seed = static_cast<int>(gmx::makeRandomSeed());
1958         GMX_LOG(logger.info)
1959                 .asParagraph()
1960                 .appendTextFormatted("Setting the LD random seed to %" PRId64 "", ir->ld_seed);
1961     }
1962
1963     if (ir->expandedvals->lmc_seed == -1)
1964     {
1965         ir->expandedvals->lmc_seed = static_cast<int>(gmx::makeRandomSeed());
1966         GMX_LOG(logger.info)
1967                 .asParagraph()
1968                 .appendTextFormatted("Setting the lambda MC random seed to %d", ir->expandedvals->lmc_seed);
1969     }
1970
1971     bNeedVel = EI_STATE_VELOCITY(ir->eI);
1972     bGenVel  = (bNeedVel && opts->bGenVel);
1973     if (bGenVel && ir->bContinuation)
1974     {
1975         std::string warningMessage = gmx::formatString(
1976                 "Generating velocities is inconsistent with attempting "
1977                 "to continue a previous run. Choose only one of "
1978                 "gen-vel = yes and continuation = yes.");
1979         warning_error(wi, warningMessage);
1980     }
1981
1982     std::array<InteractionsOfType, F_NRE> interactions;
1983     gmx_mtop_t                            sys;
1984     PreprocessingAtomTypes                atypes;
1985     if (debug)
1986     {
1987         pr_symtab(debug, 0, "Just opened", &sys.symtab);
1988     }
1989
1990     const char* fn = ftp2fn(efTOP, NFILE, fnm);
1991     if (!gmx_fexist(fn))
1992     {
1993         gmx_fatal(FARGS, "%s does not exist", fn);
1994     }
1995
1996     t_state state;
1997     new_status(fn,
1998                opt2fn_null("-pp", NFILE, fnm),
1999                opt2fn("-c", NFILE, fnm),
2000                opts,
2001                ir,
2002                bZero,
2003                bGenVel,
2004                bVerbose,
2005                &state,
2006                &atypes,
2007                &sys,
2008                &mi,
2009                &intermolecular_interactions,
2010                interactions,
2011                &comb,
2012                &reppow,
2013                &fudgeQQ,
2014                opts->bMorse,
2015                wi,
2016                logger);
2017
2018     if (debug)
2019     {
2020         pr_symtab(debug, 0, "After new_status", &sys.symtab);
2021     }
2022
2023     nvsite = 0;
2024     /* set parameters for virtual site construction (not for vsiten) */
2025     for (size_t mt = 0; mt < sys.moltype.size(); mt++)
2026     {
2027         nvsite += set_vsites(bVerbose, &sys.moltype[mt].atoms, &atypes, mi[mt].interactions, logger);
2028     }
2029     /* now throw away all obsolete bonds, angles and dihedrals: */
2030     /* note: constraints are ALWAYS removed */
2031     if (nvsite)
2032     {
2033         for (size_t mt = 0; mt < sys.moltype.size(); mt++)
2034         {
2035             clean_vsite_bondeds(mi[mt].interactions, sys.moltype[mt].atoms.nr, bRmVSBds, logger);
2036         }
2037     }
2038
2039     if ((count_constraints(&sys, mi, wi) != 0) && (ir->eConstrAlg == ConstraintAlgorithm::Shake))
2040     {
2041         if (ir->eI == IntegrationAlgorithm::CG || ir->eI == IntegrationAlgorithm::LBFGS)
2042         {
2043             std::string warningMessage =
2044                     gmx::formatString("Can not do %s with %s, use %s",
2045                                       enumValueToString(ir->eI),
2046                                       enumValueToString(ConstraintAlgorithm::Shake),
2047                                       enumValueToString(ConstraintAlgorithm::Lincs));
2048             warning_error(wi, warningMessage);
2049         }
2050         if (ir->bPeriodicMols)
2051         {
2052             std::string warningMessage =
2053                     gmx::formatString("Can not do periodic molecules with %s, use %s",
2054                                       enumValueToString(ConstraintAlgorithm::Shake),
2055                                       enumValueToString(ConstraintAlgorithm::Lincs));
2056             warning_error(wi, warningMessage);
2057         }
2058     }
2059
2060     if (EI_SD(ir->eI) && ir->etc != TemperatureCoupling::No)
2061     {
2062         warning_note(wi, "Temperature coupling is ignored with SD integrators.");
2063     }
2064
2065     /* Check for errors in the input now, since they might cause problems
2066      * during processing further down.
2067      */
2068     check_warning_error(wi, FARGS);
2069
2070     if (nint_ftype(&sys, mi, F_POSRES) > 0 || nint_ftype(&sys, mi, F_FBPOSRES) > 0)
2071     {
2072         if (ir->epc == PressureCoupling::ParrinelloRahman || ir->epc == PressureCoupling::Mttk)
2073         {
2074             std::string warningMessage = gmx::formatString(
2075                     "You are combining position restraints with %s pressure coupling, which can "
2076                     "lead to instabilities. If you really want to combine position restraints with "
2077                     "pressure coupling, we suggest to use %s pressure coupling instead.",
2078                     enumValueToString(ir->epc),
2079                     enumValueToString(PressureCoupling::Berendsen));
2080             warning_note(wi, warningMessage);
2081         }
2082
2083         const char* fn = opt2fn("-r", NFILE, fnm);
2084         const char* fnB;
2085
2086         if (!gmx_fexist(fn))
2087         {
2088             gmx_fatal(FARGS,
2089                       "Cannot find position restraint file %s (option -r).\n"
2090                       "From GROMACS-2018, you need to specify the position restraint "
2091                       "coordinate files explicitly to avoid mistakes, although you can "
2092                       "still use the same file as you specify for the -c option.",
2093                       fn);
2094         }
2095
2096         if (opt2bSet("-rb", NFILE, fnm))
2097         {
2098             fnB = opt2fn("-rb", NFILE, fnm);
2099             if (!gmx_fexist(fnB))
2100             {
2101                 gmx_fatal(FARGS,
2102                           "Cannot find B-state position restraint file %s (option -rb).\n"
2103                           "From GROMACS-2018, you need to specify the position restraint "
2104                           "coordinate files explicitly to avoid mistakes, although you can "
2105                           "still use the same file as you specify for the -c option.",
2106                           fn);
2107             }
2108         }
2109         else
2110         {
2111             fnB = fn;
2112         }
2113
2114         if (bVerbose)
2115         {
2116             std::string message = gmx::formatString("Reading position restraint coords from %s", fn);
2117             if (strcmp(fn, fnB) != 0)
2118             {
2119                 message += gmx::formatString(" and %s", fnB);
2120             }
2121             GMX_LOG(logger.info).asParagraph().appendText(message);
2122         }
2123         gen_posres(&sys, mi, fn, fnB, ir->refcoord_scaling, ir->pbcType, ir->posres_com, ir->posres_comB, wi, logger);
2124     }
2125
2126     /* If we are using CMAP, setup the pre-interpolation grid */
2127     if (interactions[F_CMAP].ncmap() > 0)
2128     {
2129         init_cmap_grid(&sys.ffparams.cmap_grid,
2130                        interactions[F_CMAP].cmapAngles,
2131                        interactions[F_CMAP].cmakeGridSpacing);
2132         setup_cmap(interactions[F_CMAP].cmakeGridSpacing,
2133                    interactions[F_CMAP].cmapAngles,
2134                    interactions[F_CMAP].cmap,
2135                    &sys.ffparams.cmap_grid);
2136     }
2137
2138     set_wall_atomtype(&atypes, opts, ir, wi, logger);
2139     if (bRenum)
2140     {
2141         atypes.renumberTypes(interactions, &sys, ir->wall_atomtype, bVerbose);
2142     }
2143
2144     if (ir->implicit_solvent)
2145     {
2146         gmx_fatal(FARGS, "Implicit solvation is no longer supported");
2147     }
2148
2149     /* PELA: Copy the atomtype data to the topology atomtype list */
2150     atypes.copyTot_atomtypes(&(sys.atomtypes));
2151
2152     if (debug)
2153     {
2154         pr_symtab(debug, 0, "After atype.renumberTypes", &sys.symtab);
2155     }
2156
2157     if (bVerbose)
2158     {
2159         GMX_LOG(logger.info).asParagraph().appendTextFormatted("converting bonded parameters...");
2160     }
2161
2162     const int ntype = atypes.size();
2163     convertInteractionsOfType(
2164             ntype, interactions, mi, intermolecular_interactions.get(), comb, reppow, fudgeQQ, &sys);
2165
2166     if (debug)
2167     {
2168         pr_symtab(debug, 0, "After converInteractionsOfType", &sys.symtab);
2169     }
2170
2171     /* set ptype to VSite for virtual sites */
2172     for (gmx_moltype_t& moltype : sys.moltype)
2173     {
2174         set_vsites_ptype(FALSE, &moltype, logger);
2175     }
2176     if (debug)
2177     {
2178         pr_symtab(debug, 0, "After virtual sites", &sys.symtab);
2179     }
2180     /* Check velocity for virtual sites and shells */
2181     if (bGenVel)
2182     {
2183         check_vel(&sys, state.v.rvec_array());
2184     }
2185
2186     /* check for shells and inpurecs */
2187     check_shells_inputrec(&sys, ir, wi);
2188
2189     /* check masses */
2190     check_mol(&sys, wi);
2191
2192     if (haveFepPerturbedMassesInSettles(sys))
2193     {
2194         warning_error(wi,
2195                       "SETTLE is not implemented for atoms whose mass is perturbed. "
2196                       "You might instead use normal constraints.");
2197     }
2198
2199     checkForUnboundAtoms(&sys, bVerbose, wi, logger);
2200
2201     if (EI_DYNAMICS(ir->eI) && ir->eI != IntegrationAlgorithm::BD)
2202     {
2203         check_bonds_timestep(&sys, ir->delta_t, wi);
2204     }
2205
2206     checkDecoupledModeAccuracy(&sys, ir, wi);
2207
2208     if (EI_ENERGY_MINIMIZATION(ir->eI) && 0 == ir->nsteps)
2209     {
2210         warning_note(
2211                 wi,
2212                 "Zero-step energy minimization will alter the coordinates before calculating the "
2213                 "energy. If you just want the energy of a single point, try zero-step MD (with "
2214                 "unconstrained_start = yes). To do multiple single-point energy evaluations of "
2215                 "different configurations of the same topology, use mdrun -rerun.");
2216     }
2217
2218     check_warning_error(wi, FARGS);
2219
2220     if (bVerbose)
2221     {
2222         GMX_LOG(logger.info).asParagraph().appendTextFormatted("initialising group options...");
2223     }
2224     do_index(mdparin, ftp2fn_null(efNDX, NFILE, fnm), &sys, bVerbose, mdModules.notifiers(), ir, wi);
2225
2226     // Notify topology to MdModules for pre-processing after all indexes were built
2227     mdModules.notifiers().preProcessingNotifier_.notify(&sys);
2228
2229     if (ir->cutoff_scheme == CutoffScheme::Verlet && ir->verletbuf_tol > 0)
2230     {
2231         if (EI_DYNAMICS(ir->eI) && inputrec2nboundeddim(ir) == 3)
2232         {
2233             real buffer_temp;
2234
2235             if (EI_MD(ir->eI) && ir->etc == TemperatureCoupling::No)
2236             {
2237                 if (bGenVel)
2238                 {
2239                     buffer_temp = opts->tempi;
2240                 }
2241                 else
2242                 {
2243                     buffer_temp = calc_temp(&sys, ir, state.v.rvec_array());
2244                 }
2245                 if (buffer_temp > 0)
2246                 {
2247                     std::string warningMessage = gmx::formatString(
2248                             "NVE simulation: will use the initial temperature of %.3f K for "
2249                             "determining the Verlet buffer size",
2250                             buffer_temp);
2251                     warning_note(wi, warningMessage);
2252                 }
2253                 else
2254                 {
2255                     std::string warningMessage = gmx::formatString(
2256                             "NVE simulation with an initial temperature of zero: will use a Verlet "
2257                             "buffer of %d%%. Check your energy drift!",
2258                             gmx::roundToInt(verlet_buffer_ratio_NVE_T0 * 100));
2259                     warning_note(wi, warningMessage);
2260                 }
2261             }
2262             else
2263             {
2264                 buffer_temp = get_max_reference_temp(ir, wi);
2265             }
2266
2267             if (EI_MD(ir->eI) && ir->etc == TemperatureCoupling::No && buffer_temp == 0)
2268             {
2269                 /* NVE with initial T=0: we add a fixed ratio to rlist.
2270                  * Since we don't actually use verletbuf_tol, we set it to -1
2271                  * so it can't be misused later.
2272                  */
2273                 ir->rlist *= 1.0 + verlet_buffer_ratio_NVE_T0;
2274                 ir->verletbuf_tol = -1;
2275             }
2276             else
2277             {
2278                 /* We warn for NVE simulations with a drift tolerance that
2279                  * might result in a 1(.1)% drift over the total run-time.
2280                  * Note that we can't warn when nsteps=0, since we don't
2281                  * know how many steps the user intends to run.
2282                  */
2283                 if (EI_MD(ir->eI) && ir->etc == TemperatureCoupling::No && ir->nstlist > 1 && ir->nsteps > 0)
2284                 {
2285                     const real driftTolerance = 0.01;
2286                     /* We use 2 DOF per atom = 2kT pot+kin energy,
2287                      * to be on the safe side with constraints.
2288                      */
2289                     const real totalEnergyDriftPerAtomPerPicosecond =
2290                             2 * gmx::c_boltz * buffer_temp / (ir->nsteps * ir->delta_t);
2291
2292                     if (ir->verletbuf_tol > 1.1 * driftTolerance * totalEnergyDriftPerAtomPerPicosecond)
2293                     {
2294                         std::string warningMessage = gmx::formatString(
2295                                 "You are using a Verlet buffer tolerance of %g kJ/mol/ps for an "
2296                                 "NVE simulation of length %g ps, which can give a final drift of "
2297                                 "%d%%. For conserving energy to %d%% when using constraints, you "
2298                                 "might need to set verlet-buffer-tolerance to %.1e.",
2299                                 ir->verletbuf_tol,
2300                                 ir->nsteps * ir->delta_t,
2301                                 gmx::roundToInt(ir->verletbuf_tol / totalEnergyDriftPerAtomPerPicosecond * 100),
2302                                 gmx::roundToInt(100 * driftTolerance),
2303                                 driftTolerance * totalEnergyDriftPerAtomPerPicosecond);
2304                         warning_note(wi, warningMessage);
2305                     }
2306                 }
2307
2308                 set_verlet_buffer(&sys, ir, buffer_temp, state.box, wi, logger);
2309             }
2310         }
2311     }
2312
2313     /* Init the temperature coupling state */
2314     init_gtc_state(&state, ir->opts.ngtc, 0, ir->opts.nhchainlength); /* need to add nnhpres here? */
2315
2316     if (debug)
2317     {
2318         pr_symtab(debug, 0, "After index", &sys.symtab);
2319     }
2320
2321     triple_check(mdparin, ir, &sys, wi);
2322     close_symtab(&sys.symtab);
2323     if (debug)
2324     {
2325         pr_symtab(debug, 0, "After close", &sys.symtab);
2326     }
2327
2328     if (ir->eI == IntegrationAlgorithm::Mimic)
2329     {
2330         generate_qmexcl(&sys, ir, logger);
2331     }
2332
2333     if (ftp2bSet(efTRN, NFILE, fnm))
2334     {
2335         if (bVerbose)
2336         {
2337             GMX_LOG(logger.info)
2338                     .asParagraph()
2339                     .appendTextFormatted("getting data from old trajectory ...");
2340         }
2341         cont_status(ftp2fn(efTRN, NFILE, fnm),
2342                     ftp2fn_null(efEDR, NFILE, fnm),
2343                     bNeedVel,
2344                     bGenVel,
2345                     fr_time,
2346                     ir,
2347                     &state,
2348                     &sys,
2349                     oenv,
2350                     logger);
2351     }
2352
2353     if (ir->pbcType == PbcType::XY && ir->nwall != 2)
2354     {
2355         clear_rvec(state.box[ZZ]);
2356     }
2357
2358     if (EEL_FULL(ir->coulombtype) || EVDW_PME(ir->vdwtype))
2359     {
2360         /* Calculate the optimal grid dimensions */
2361         matrix          scaledBox;
2362         EwaldBoxZScaler boxScaler(inputrecPbcXY2Walls(ir), ir->wall_ewald_zfac);
2363         boxScaler.scaleBox(state.box, scaledBox);
2364
2365         if (ir->nkx > 0 && ir->nky > 0 && ir->nkz > 0)
2366         {
2367             /* Mark fourier_spacing as not used */
2368             ir->fourier_spacing = 0;
2369         }
2370         else if (ir->nkx != 0 && ir->nky != 0 && ir->nkz != 0)
2371         {
2372             set_warning_line(wi, mdparin, -1);
2373             warning_error(
2374                     wi, "Some of the Fourier grid sizes are set, but all of them need to be set.");
2375         }
2376         const int minGridSize = minimalPmeGridSize(ir->pme_order);
2377         calcFftGrid(stdout, scaledBox, ir->fourier_spacing, minGridSize, &(ir->nkx), &(ir->nky), &(ir->nkz));
2378         if (ir->nkx < minGridSize || ir->nky < minGridSize || ir->nkz < minGridSize)
2379         {
2380             warning_error(wi,
2381                           "The PME grid size should be >= 2*(pme-order - 1); either manually "
2382                           "increase the grid size or decrease pme-order");
2383         }
2384     }
2385
2386     /* MRS: eventually figure out better logic for initializing the fep
2387        values that makes declaring the lambda and declaring the state not
2388        potentially conflict if not handled correctly. */
2389     if (ir->efep != FreeEnergyPerturbationType::No)
2390     {
2391         state.fep_state = ir->fepvals->init_fep_state;
2392         for (i = 0; i < static_cast<int>(FreeEnergyPerturbationCouplingType::Count); i++)
2393         {
2394             /* init_lambda trumps state definitions*/
2395             if (ir->fepvals->init_lambda >= 0)
2396             {
2397                 state.lambda[i] = ir->fepvals->init_lambda;
2398             }
2399             else
2400             {
2401                 if (ir->fepvals->all_lambda[i].empty())
2402                 {
2403                     gmx_fatal(FARGS, "Values of lambda not set for a free energy calculation!");
2404                 }
2405                 else
2406                 {
2407                     state.lambda[i] = ir->fepvals->all_lambda[i][state.fep_state];
2408                 }
2409             }
2410         }
2411     }
2412
2413     pull_t* pull = nullptr;
2414
2415     if (ir->bPull)
2416     {
2417         pull = set_pull_init(
2418                 ir, sys, state.x, state.box, state.lambda[FreeEnergyPerturbationCouplingType::Mass], wi);
2419     }
2420
2421     /* Modules that supply external potential for pull coordinates
2422      * should register those potentials here. finish_pull() will check
2423      * that providers have been registerd for all external potentials.
2424      */
2425
2426     if (ir->bDoAwh)
2427     {
2428         tensor compressibility = { { 0 } };
2429         if (ir->epc != PressureCoupling::No)
2430         {
2431             copy_mat(ir->compress, compressibility);
2432         }
2433         setStateDependentAwhParams(
2434                 ir->awhParams.get(),
2435                 *ir->pull,
2436                 pull,
2437                 state.box,
2438                 ir->pbcType,
2439                 compressibility,
2440                 &ir->opts,
2441                 ir->efep != FreeEnergyPerturbationType::No ? ir->fepvals->all_lambda[static_cast<int>(
2442                         FreeEnergyPerturbationCouplingType::Fep)][ir->fepvals->init_fep_state]
2443                                                            : 0,
2444                 sys,
2445                 wi);
2446     }
2447
2448     if (ir->bPull)
2449     {
2450         finish_pull(pull);
2451     }
2452
2453     if (ir->bRot)
2454     {
2455         set_reference_positions(ir->rot,
2456                                 state.x.rvec_array(),
2457                                 state.box,
2458                                 opt2fn("-ref", NFILE, fnm),
2459                                 opt2bSet("-ref", NFILE, fnm),
2460                                 wi);
2461     }
2462
2463     /*  reset_multinr(sys); */
2464
2465     if (EEL_PME(ir->coulombtype))
2466     {
2467         float ratio = pme_load_estimate(sys, *ir, state.box);
2468         GMX_LOG(logger.info)
2469                 .asParagraph()
2470                 .appendTextFormatted(
2471                         "Estimate for the relative computational load of the PME mesh part: %.2f", ratio);
2472         /* With free energy we might need to do PME both for the A and B state
2473          * charges. This will double the cost, but the optimal performance will
2474          * then probably be at a slightly larger cut-off and grid spacing.
2475          */
2476         if ((ir->efep == FreeEnergyPerturbationType::No && ratio > 1.0 / 2.0)
2477             || (ir->efep != FreeEnergyPerturbationType::No && ratio > 2.0 / 3.0))
2478         {
2479             warning_note(
2480                     wi,
2481                     "The optimal PME mesh load for parallel simulations is below 0.5\n"
2482                     "and for highly parallel simulations between 0.25 and 0.33,\n"
2483                     "for higher performance, increase the cut-off and the PME grid spacing.\n");
2484             if (ir->efep != FreeEnergyPerturbationType::No)
2485             {
2486                 warning_note(wi,
2487                              "For free energy simulations, the optimal load limit increases from "
2488                              "0.5 to 0.667\n");
2489             }
2490         }
2491     }
2492
2493     {
2494         double      cio = compute_io(ir, sys.natoms, sys.groups, F_NRE, 1);
2495         std::string warningMessage =
2496                 gmx::formatString("This run will generate roughly %.0f Mb of data", cio);
2497         if (cio > 2000)
2498         {
2499             set_warning_line(wi, mdparin, -1);
2500             warning_note(wi, warningMessage);
2501         }
2502         else
2503         {
2504             GMX_LOG(logger.info).asParagraph().appendText(warningMessage);
2505         }
2506     }
2507
2508     // Hand over box and coordiantes to MdModules before they evaluate their final parameters
2509     {
2510         gmx::CoordinatesAndBoxPreprocessed coordinatesAndBoxPreprocessed;
2511         coordinatesAndBoxPreprocessed.coordinates_ = state.x.arrayRefWithPadding();
2512         copy_mat(state.box, coordinatesAndBoxPreprocessed.box_);
2513         coordinatesAndBoxPreprocessed.pbc_ = ir->pbcType;
2514         mdModules.notifiers().preProcessingNotifier_.notify(coordinatesAndBoxPreprocessed);
2515     }
2516
2517     // Add the md modules internal parameters that are not mdp options
2518     // e.g., atom indices
2519
2520     {
2521         gmx::KeyValueTreeBuilder internalParameterBuilder;
2522         mdModules.notifiers().preProcessingNotifier_.notify(internalParameterBuilder.rootObject());
2523         ir->internalParameters =
2524                 std::make_unique<gmx::KeyValueTreeObject>(internalParameterBuilder.build());
2525     }
2526
2527     if (ir->comm_mode != ComRemovalAlgorithm::No)
2528     {
2529         const int nstglobalcomm = computeGlobalCommunicationPeriod(ir);
2530         if (ir->nstcomm % nstglobalcomm != 0)
2531         {
2532             warning_note(
2533                     wi,
2534                     gmx::formatString(
2535                             "COM removal frequency is set to (%d).\n"
2536                             "Other settings require a global communication frequency of %d.\n"
2537                             "Note that this will require additional global communication steps,\n"
2538                             "which will reduce performance when using multiple ranks.\n"
2539                             "Consider setting nstcomm to a multiple of %d.",
2540                             ir->nstcomm,
2541                             nstglobalcomm,
2542                             nstglobalcomm));
2543         }
2544     }
2545
2546     if (bVerbose)
2547     {
2548         GMX_LOG(logger.info).asParagraph().appendTextFormatted("writing run input file...");
2549     }
2550
2551     done_warning(wi, FARGS);
2552     write_tpx_state(ftp2fn(efTPR, NFILE, fnm), ir, &state, sys);
2553
2554     /* Output IMD group, if bIMD is TRUE */
2555     gmx::write_IMDgroup_to_file(ir->bIMD, ir, &state, sys, NFILE, fnm);
2556
2557     sfree(opts->define);
2558     sfree(opts->wall_atomtype[0]);
2559     sfree(opts->wall_atomtype[1]);
2560     sfree(opts->include);
2561     sfree(opts->couple_moltype);
2562
2563     for (auto& mol : mi)
2564     {
2565         // Some of the contents of molinfo have been stolen, so
2566         // fullCleanUp can't be called.
2567         mol.partialCleanUp();
2568     }
2569     done_inputrec_strings();
2570     output_env_done(oenv);
2571
2572     return 0;
2573 }