clang-tidy-11 fixes for gmxpreprocess
[alexxy/gromacs.git] / src / gromacs / gmxpreprocess / readir.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017, The GROMACS development team.
7  * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 #include "gmxpre.h"
39
40 #include "readir.h"
41
42 #include <cctype>
43 #include <climits>
44 #include <cmath>
45 #include <cstdlib>
46
47 #include <algorithm>
48 #include <memory>
49 #include <numeric>
50 #include <string>
51
52 #include "gromacs/applied_forces/awh/read_params.h"
53 #include "gromacs/fileio/readinp.h"
54 #include "gromacs/fileio/warninp.h"
55 #include "gromacs/gmxlib/network.h"
56 #include "gromacs/gmxpreprocess/toputil.h"
57 #include "gromacs/math/functions.h"
58 #include "gromacs/math/units.h"
59 #include "gromacs/math/utilities.h"
60 #include "gromacs/math/vec.h"
61 #include "gromacs/mdlib/calc_verletbuf.h"
62 #include "gromacs/mdrun/mdmodules.h"
63 #include "gromacs/mdtypes/awh_params.h"
64 #include "gromacs/mdtypes/inputrec.h"
65 #include "gromacs/mdtypes/md_enums.h"
66 #include "gromacs/mdtypes/multipletimestepping.h"
67 #include "gromacs/mdtypes/pull_params.h"
68 #include "gromacs/options/options.h"
69 #include "gromacs/options/treesupport.h"
70 #include "gromacs/pbcutil/pbc.h"
71 #include "gromacs/selection/indexutil.h"
72 #include "gromacs/topology/block.h"
73 #include "gromacs/topology/ifunc.h"
74 #include "gromacs/topology/index.h"
75 #include "gromacs/topology/mtop_util.h"
76 #include "gromacs/topology/symtab.h"
77 #include "gromacs/topology/topology.h"
78 #include "gromacs/utility/arrayref.h"
79 #include "gromacs/utility/cstringutil.h"
80 #include "gromacs/utility/exceptions.h"
81 #include "gromacs/utility/fatalerror.h"
82 #include "gromacs/utility/filestream.h"
83 #include "gromacs/utility/gmxassert.h"
84 #include "gromacs/utility/ikeyvaluetreeerror.h"
85 #include "gromacs/utility/keyvaluetree.h"
86 #include "gromacs/utility/keyvaluetreebuilder.h"
87 #include "gromacs/utility/keyvaluetreemdpwriter.h"
88 #include "gromacs/utility/keyvaluetreetransform.h"
89 #include "gromacs/utility/mdmodulesnotifiers.h"
90 #include "gromacs/utility/smalloc.h"
91 #include "gromacs/utility/strconvert.h"
92 #include "gromacs/utility/stringcompare.h"
93 #include "gromacs/utility/stringutil.h"
94 #include "gromacs/utility/textwriter.h"
95
96 #define MAXPTR 254
97 #define NOGID 255
98
99 /* Resource parameters
100  * Do not change any of these until you read the instruction
101  * in readinp.h. Some cpp's do not take spaces after the backslash
102  * (like the c-shell), which will give you a very weird compiler
103  * message.
104  */
105
106 struct gmx_inputrec_strings
107 {
108     char tcgrps[STRLEN], tau_t[STRLEN], ref_t[STRLEN], freeze[STRLEN], frdim[STRLEN],
109             energy[STRLEN], user1[STRLEN], user2[STRLEN], vcm[STRLEN], x_compressed_groups[STRLEN],
110             couple_moltype[STRLEN], orirefitgrp[STRLEN], egptable[STRLEN], egpexcl[STRLEN],
111             wall_atomtype[STRLEN], wall_density[STRLEN], deform[STRLEN], QMMM[STRLEN], imd_grp[STRLEN];
112     gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, std::string> fep_lambda;
113     char                                                                   lambda_weights[STRLEN];
114     std::vector<std::string>                                               pullGroupNames;
115     std::vector<std::string>                                               rotateGroupNames;
116     char anneal[STRLEN], anneal_npoints[STRLEN], anneal_time[STRLEN], anneal_temp[STRLEN];
117 };
118
119 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
120 static gmx_inputrec_strings* inputrecStrings = nullptr;
121
122 void init_inputrec_strings()
123 {
124     if (inputrecStrings)
125     {
126         gmx_incons(
127                 "Attempted to call init_inputrec_strings before calling done_inputrec_strings. "
128                 "Only one inputrec (i.e. .mdp file) can be parsed at a time.");
129     }
130     inputrecStrings = new gmx_inputrec_strings();
131 }
132
133 void done_inputrec_strings()
134 {
135     delete inputrecStrings;
136     inputrecStrings = nullptr;
137 }
138
139
140 enum
141 {
142     egrptpALL,         /* All particles have to be a member of a group.     */
143     egrptpALL_GENREST, /* A rest group with name is generated for particles *
144                         * that are not part of any group.                   */
145     egrptpPART,        /* As egrptpALL_GENREST, but no name is generated    *
146                         * for the rest group.                               */
147     egrptpONE          /* Merge all selected groups into one group,         *
148                         * make a rest group for the remaining particles.    */
149 };
150
151 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
152 static const char* constraints[eshNR + 1] = { "none",     "h-bonds",    "all-bonds",
153                                               "h-angles", "all-angles", nullptr };
154
155 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
156 static const char* couple_lam[ecouplamNR + 1] = { "vdw-q", "vdw", "q", "none", nullptr };
157
158 static void getSimTemps(int ntemps, t_simtemp* simtemp, gmx::ArrayRef<double> temperature_lambdas)
159 {
160
161     int i;
162
163     for (i = 0; i < ntemps; i++)
164     {
165         /* simple linear scaling -- allows more control */
166         if (simtemp->eSimTempScale == SimulatedTempering::Linear)
167         {
168             simtemp->temperatures[i] =
169                     simtemp->simtemp_low
170                     + (simtemp->simtemp_high - simtemp->simtemp_low) * temperature_lambdas[i];
171         }
172         else if (simtemp->eSimTempScale
173                  == SimulatedTempering::Geometric) /* should give roughly equal acceptance for constant heat capacity . . . */
174         {
175             simtemp->temperatures[i] = simtemp->simtemp_low
176                                        * std::pow(simtemp->simtemp_high / simtemp->simtemp_low,
177                                                   static_cast<real>((1.0 * i) / (ntemps - 1)));
178         }
179         else if (simtemp->eSimTempScale == SimulatedTempering::Exponential)
180         {
181             simtemp->temperatures[i] = simtemp->simtemp_low
182                                        + (simtemp->simtemp_high - simtemp->simtemp_low)
183                                                  * (std::expm1(temperature_lambdas[i]) / std::expm1(1.0));
184         }
185         else
186         {
187             char errorstr[128];
188             sprintf(errorstr, "eSimTempScale=%s not defined", enumValueToString(simtemp->eSimTempScale));
189             gmx_fatal(FARGS, "%s", errorstr);
190         }
191     }
192 }
193
194
195 static void _low_check(bool b, const char* s, warninp_t wi)
196 {
197     if (b)
198     {
199         warning_error(wi, s);
200     }
201 }
202
203 static void check_nst(const char* desc_nst, int nst, const char* desc_p, int* p, warninp_t wi)
204 {
205     char buf[STRLEN];
206
207     if (*p > 0 && *p % nst != 0)
208     {
209         /* Round up to the next multiple of nst */
210         *p = ((*p) / nst + 1) * nst;
211         sprintf(buf, "%s should be a multiple of %s, changing %s to %d\n", desc_p, desc_nst, desc_p, *p);
212         warning(wi, buf);
213     }
214 }
215
216 //! Convert legacy mdp entries to modern ones.
217 static void process_interaction_modifier(InteractionModifiers* eintmod)
218 {
219     if (*eintmod == InteractionModifiers::PotShiftVerletUnsupported)
220     {
221         *eintmod = InteractionModifiers::PotShift;
222     }
223 }
224
225 void check_ir(const char*                    mdparin,
226               const gmx::MDModulesNotifiers& mdModulesNotifiers,
227               t_inputrec*                    ir,
228               t_gromppopts*                  opts,
229               warninp_t                      wi)
230 /* Check internal consistency.
231  * NOTE: index groups are not set here yet, don't check things
232  * like temperature coupling group options here, but in triple_check
233  */
234 {
235     /* Strange macro: first one fills the err_buf, and then one can check
236      * the condition, which will print the message and increase the error
237      * counter.
238      */
239 #define CHECK(b) _low_check(b, err_buf, wi)
240     char        err_buf[256], warn_buf[STRLEN];
241     int         i, j;
242     real        dt_pcoupl;
243     t_lambda*   fep    = ir->fepvals.get();
244     t_expanded* expand = ir->expandedvals.get();
245
246     set_warning_line(wi, mdparin, -1);
247
248     /* We cannot check MTS requirements with an invalid MTS setup
249      * and we will already have generated errors with an invalid MTS setup.
250      */
251     if (gmx::haveValidMtsSetup(*ir))
252     {
253         std::vector<std::string> errorMessages = gmx::checkMtsRequirements(*ir);
254
255         for (const auto& errorMessage : errorMessages)
256         {
257             warning_error(wi, errorMessage.c_str());
258         }
259     }
260
261     if (ir->coulombtype == CoulombInteractionType::RFNecUnsupported)
262     {
263         std::string message =
264                 gmx::formatString("%s electrostatics is no longer supported",
265                                   enumValueToString(CoulombInteractionType::RFNecUnsupported));
266         warning_error(wi, message);
267     }
268
269     /* BASIC CUT-OFF STUFF */
270     if (ir->rcoulomb < 0)
271     {
272         warning_error(wi, "rcoulomb should be >= 0");
273     }
274     if (ir->rvdw < 0)
275     {
276         warning_error(wi, "rvdw should be >= 0");
277     }
278     if (ir->rlist < 0 && !(ir->cutoff_scheme == CutoffScheme::Verlet && ir->verletbuf_tol > 0))
279     {
280         warning_error(wi, "rlist should be >= 0");
281     }
282     sprintf(err_buf,
283             "nstlist can not be smaller than 0. (If you were trying to use the heuristic "
284             "neighbour-list update scheme for efficient buffering for improved energy "
285             "conservation, please use the Verlet cut-off scheme instead.)");
286     CHECK(ir->nstlist < 0);
287
288     process_interaction_modifier(&ir->coulomb_modifier);
289     process_interaction_modifier(&ir->vdw_modifier);
290
291     if (ir->cutoff_scheme == CutoffScheme::Group)
292     {
293         gmx_fatal(FARGS,
294                   "The group cutoff scheme has been removed since GROMACS 2020. "
295                   "Please use the Verlet cutoff scheme.");
296     }
297     if (ir->cutoff_scheme == CutoffScheme::Verlet)
298     {
299         real rc_max;
300
301         /* Normal Verlet type neighbor-list, currently only limited feature support */
302         if (inputrec2nboundeddim(ir) < 3)
303         {
304             warning_error(wi, "With Verlet lists only full pbc or pbc=xy with walls is supported");
305         }
306
307         // We don't (yet) have general Verlet kernels for rcoulomb!=rvdw
308         if (ir->rcoulomb != ir->rvdw)
309         {
310             // Since we have PME coulomb + LJ cut-off kernels with rcoulomb>rvdw
311             // for PME load balancing, we can support this exception.
312             bool bUsesPmeTwinRangeKernel =
313                     (EEL_PME_EWALD(ir->coulombtype) && ir->vdwtype == VanDerWaalsType::Cut
314                      && ir->rcoulomb > ir->rvdw);
315             if (!bUsesPmeTwinRangeKernel)
316             {
317                 warning_error(wi,
318                               "With Verlet lists rcoulomb!=rvdw is not supported (except for "
319                               "rcoulomb>rvdw with PME electrostatics)");
320             }
321         }
322
323         if (ir->vdwtype == VanDerWaalsType::Shift || ir->vdwtype == VanDerWaalsType::Switch)
324         {
325             if (ir->vdw_modifier == InteractionModifiers::None
326                 || ir->vdw_modifier == InteractionModifiers::PotShift)
327             {
328                 ir->vdw_modifier =
329                         (ir->vdwtype == VanDerWaalsType::Shift ? InteractionModifiers::ForceSwitch
330                                                                : InteractionModifiers::PotSwitch);
331
332                 sprintf(warn_buf,
333                         "Replacing vdwtype=%s by the equivalent combination of vdwtype=%s and "
334                         "vdw_modifier=%s",
335                         enumValueToString(ir->vdwtype),
336                         enumValueToString(VanDerWaalsType::Cut),
337                         enumValueToString(ir->vdw_modifier));
338                 warning_note(wi, warn_buf);
339
340                 ir->vdwtype = VanDerWaalsType::Cut;
341             }
342             else
343             {
344                 sprintf(warn_buf,
345                         "Unsupported combination of vdwtype=%s and vdw_modifier=%s",
346                         enumValueToString(ir->vdwtype),
347                         enumValueToString(ir->vdw_modifier));
348                 warning_error(wi, warn_buf);
349             }
350         }
351
352         if (!(ir->vdwtype == VanDerWaalsType::Cut || ir->vdwtype == VanDerWaalsType::Pme))
353         {
354             warning_error(wi,
355                           "With Verlet lists only cut-off and PME LJ interactions are supported");
356         }
357         if (!(ir->coulombtype == CoulombInteractionType::Cut || EEL_RF(ir->coulombtype)
358               || EEL_PME(ir->coulombtype) || ir->coulombtype == CoulombInteractionType::Ewald))
359         {
360             warning_error(wi,
361                           "With Verlet lists only cut-off, reaction-field, PME and Ewald "
362                           "electrostatics are supported");
363         }
364         if (!(ir->coulomb_modifier == InteractionModifiers::None
365               || ir->coulomb_modifier == InteractionModifiers::PotShift))
366         {
367             sprintf(warn_buf, "coulomb_modifier=%s is not supported", enumValueToString(ir->coulomb_modifier));
368             warning_error(wi, warn_buf);
369         }
370
371         if (EEL_USER(ir->coulombtype))
372         {
373             sprintf(warn_buf,
374                     "Coulomb type %s is not supported with the verlet scheme",
375                     enumValueToString(ir->coulombtype));
376             warning_error(wi, warn_buf);
377         }
378
379         if (ir->nstlist <= 0)
380         {
381             warning_error(wi, "With Verlet lists nstlist should be larger than 0");
382         }
383
384         if (ir->nstlist < 10)
385         {
386             warning_note(wi,
387                          "With Verlet lists the optimal nstlist is >= 10, with GPUs >= 20. Note "
388                          "that with the Verlet scheme, nstlist has no effect on the accuracy of "
389                          "your simulation.");
390         }
391
392         rc_max = std::max(ir->rvdw, ir->rcoulomb);
393
394         if (EI_TPI(ir->eI))
395         {
396             /* With TPI we set the pairlist cut-off later using the radius of the insterted molecule */
397             ir->verletbuf_tol = 0;
398             ir->rlist         = rc_max;
399         }
400         else if (ir->verletbuf_tol <= 0)
401         {
402             if (ir->verletbuf_tol == 0)
403             {
404                 warning_error(wi, "Can not have Verlet buffer tolerance of exactly 0");
405             }
406
407             if (ir->rlist < rc_max)
408             {
409                 warning_error(wi,
410                               "With verlet lists rlist can not be smaller than rvdw or rcoulomb");
411             }
412
413             if (ir->rlist == rc_max && ir->nstlist > 1)
414             {
415                 warning_note(
416                         wi,
417                         "rlist is equal to rvdw and/or rcoulomb: there is no explicit Verlet "
418                         "buffer. The cluster pair list does have a buffering effect, but choosing "
419                         "a larger rlist might be necessary for good energy conservation.");
420             }
421         }
422         else
423         {
424             if (ir->rlist > rc_max)
425             {
426                 warning_note(wi,
427                              "You have set rlist larger than the interaction cut-off, but you also "
428                              "have verlet-buffer-tolerance > 0. Will set rlist using "
429                              "verlet-buffer-tolerance.");
430             }
431
432             if (ir->nstlist == 1)
433             {
434                 /* No buffer required */
435                 ir->rlist = rc_max;
436             }
437             else
438             {
439                 if (EI_DYNAMICS(ir->eI))
440                 {
441                     if (inputrec2nboundeddim(ir) < 3)
442                     {
443                         warning_error(wi,
444                                       "The box volume is required for calculating rlist from the "
445                                       "energy drift with verlet-buffer-tolerance > 0. You are "
446                                       "using at least one unbounded dimension, so no volume can be "
447                                       "computed. Either use a finite box, or set rlist yourself "
448                                       "together with verlet-buffer-tolerance = -1.");
449                     }
450                     /* Set rlist temporarily so we can continue processing */
451                     ir->rlist = rc_max;
452                 }
453                 else
454                 {
455                     /* Set the buffer to 5% of the cut-off */
456                     ir->rlist = (1.0 + verlet_buffer_ratio_nodynamics) * rc_max;
457                 }
458             }
459         }
460     }
461
462     /* GENERAL INTEGRATOR STUFF */
463     if (!EI_MD(ir->eI))
464     {
465         if (ir->etc != TemperatureCoupling::No)
466         {
467             if (EI_RANDOM(ir->eI))
468             {
469                 sprintf(warn_buf,
470                         "Setting tcoupl from '%s' to 'no'. %s handles temperature coupling "
471                         "implicitly. See the documentation for more information on which "
472                         "parameters affect temperature for %s.",
473                         enumValueToString(ir->etc),
474                         enumValueToString(ir->eI),
475                         enumValueToString(ir->eI));
476             }
477             else
478             {
479                 sprintf(warn_buf,
480                         "Setting tcoupl from '%s' to 'no'. Temperature coupling does not apply to "
481                         "%s.",
482                         enumValueToString(ir->etc),
483                         enumValueToString(ir->eI));
484             }
485             warning_note(wi, warn_buf);
486         }
487         ir->etc = TemperatureCoupling::No;
488     }
489     if (ir->eI == IntegrationAlgorithm::VVAK)
490     {
491         sprintf(warn_buf,
492                 "Integrator method %s is implemented primarily for validation purposes; for "
493                 "molecular dynamics, you should probably be using %s or %s",
494                 enumValueToString(IntegrationAlgorithm::VVAK),
495                 enumValueToString(IntegrationAlgorithm::MD),
496                 enumValueToString(IntegrationAlgorithm::VV));
497         warning_note(wi, warn_buf);
498     }
499     if (!EI_DYNAMICS(ir->eI))
500     {
501         if (ir->epc != PressureCoupling::No)
502         {
503             sprintf(warn_buf,
504                     "Setting pcoupl from '%s' to 'no'. Pressure coupling does not apply to %s.",
505                     enumValueToString(ir->epc),
506                     enumValueToString(ir->eI));
507             warning_note(wi, warn_buf);
508         }
509         ir->epc = PressureCoupling::No;
510     }
511     if (EI_DYNAMICS(ir->eI))
512     {
513         if (ir->nstcalcenergy < 0)
514         {
515             ir->nstcalcenergy = ir_optimal_nstcalcenergy(ir);
516             if (ir->nstenergy != 0 && ir->nstenergy < ir->nstcalcenergy)
517             {
518                 /* nstcalcenergy larger than nstener does not make sense.
519                  * We ideally want nstcalcenergy=nstener.
520                  */
521                 if (ir->nstlist > 0)
522                 {
523                     ir->nstcalcenergy = std::gcd(ir->nstenergy, ir->nstlist);
524                 }
525                 else
526                 {
527                     ir->nstcalcenergy = ir->nstenergy;
528                 }
529             }
530         }
531         else if ((ir->nstenergy > 0 && ir->nstcalcenergy > ir->nstenergy)
532                  || (ir->efep != FreeEnergyPerturbationType::No && ir->fepvals->nstdhdl > 0
533                      && (ir->nstcalcenergy > ir->fepvals->nstdhdl)))
534
535         {
536             const char* nsten    = "nstenergy";
537             const char* nstdh    = "nstdhdl";
538             const char* min_name = nsten;
539             int         min_nst  = ir->nstenergy;
540
541             /* find the smallest of ( nstenergy, nstdhdl ) */
542             if (ir->efep != FreeEnergyPerturbationType::No && ir->fepvals->nstdhdl > 0
543                 && (ir->nstenergy == 0 || ir->fepvals->nstdhdl < ir->nstenergy))
544             {
545                 min_nst  = ir->fepvals->nstdhdl;
546                 min_name = nstdh;
547             }
548             /* If the user sets nstenergy small, we should respect that */
549             sprintf(warn_buf, "Setting nstcalcenergy (%d) equal to %s (%d)", ir->nstcalcenergy, min_name, min_nst);
550             warning_note(wi, warn_buf);
551             ir->nstcalcenergy = min_nst;
552         }
553
554         if (ir->epc != PressureCoupling::No)
555         {
556             if (ir->nstpcouple < 0)
557             {
558                 ir->nstpcouple = ir_optimal_nstpcouple(ir);
559             }
560             if (ir->useMts && ir->nstpcouple % ir->mtsLevels.back().stepFactor != 0)
561             {
562                 warning_error(wi,
563                               "With multiple time stepping, nstpcouple should be a mutiple of "
564                               "mts-factor");
565             }
566         }
567
568         if (ir->nstcalcenergy > 0)
569         {
570             if (ir->efep != FreeEnergyPerturbationType::No)
571             {
572                 /* nstdhdl should be a multiple of nstcalcenergy */
573                 check_nst("nstcalcenergy", ir->nstcalcenergy, "nstdhdl", &ir->fepvals->nstdhdl, wi);
574             }
575             if (ir->bExpanded)
576             {
577                 /* nstexpanded should be a multiple of nstcalcenergy */
578                 check_nst("nstcalcenergy", ir->nstcalcenergy, "nstexpanded", &ir->expandedvals->nstexpanded, wi);
579             }
580             /* for storing exact averages nstenergy should be
581              * a multiple of nstcalcenergy
582              */
583             check_nst("nstcalcenergy", ir->nstcalcenergy, "nstenergy", &ir->nstenergy, wi);
584         }
585
586         // Inquire all MDModules, if their parameters match with the energy
587         // calculation frequency
588         gmx::EnergyCalculationFrequencyErrors energyCalculationFrequencyErrors(ir->nstcalcenergy);
589         mdModulesNotifiers.preProcessingNotifier_.notify(&energyCalculationFrequencyErrors);
590
591         // Emit all errors from the energy calculation frequency checks
592         for (const std::string& energyFrequencyErrorMessage :
593              energyCalculationFrequencyErrors.errorMessages())
594         {
595             warning_error(wi, energyFrequencyErrorMessage);
596         }
597     }
598
599     if (ir->nsteps == 0 && !ir->bContinuation)
600     {
601         warning_note(wi,
602                      "For a correct single-point energy evaluation with nsteps = 0, use "
603                      "continuation = yes to avoid constraining the input coordinates.");
604     }
605
606     /* LD STUFF */
607     if ((EI_SD(ir->eI) || ir->eI == IntegrationAlgorithm::BD) && ir->bContinuation && ir->ld_seed != -1)
608     {
609         warning_note(wi,
610                      "You are doing a continuation with SD or BD, make sure that ld_seed is "
611                      "different from the previous run (using ld_seed=-1 will ensure this)");
612     }
613
614     /* TPI STUFF */
615     if (EI_TPI(ir->eI))
616     {
617         sprintf(err_buf, "TPI only works with pbc = %s", c_pbcTypeNames[PbcType::Xyz].c_str());
618         CHECK(ir->pbcType != PbcType::Xyz);
619         sprintf(err_buf, "with TPI nstlist should be larger than zero");
620         CHECK(ir->nstlist <= 0);
621         sprintf(err_buf, "TPI does not work with full electrostatics other than PME");
622         CHECK(EEL_FULL(ir->coulombtype) && !EEL_PME(ir->coulombtype));
623     }
624
625     /* SHAKE / LINCS */
626     if ((opts->nshake > 0) && (opts->bMorse))
627     {
628         sprintf(warn_buf, "Using morse bond-potentials while constraining bonds is useless");
629         warning(wi, warn_buf);
630     }
631
632     if ((EI_SD(ir->eI) || ir->eI == IntegrationAlgorithm::BD) && ir->bContinuation && ir->ld_seed != -1)
633     {
634         warning_note(wi,
635                      "You are doing a continuation with SD or BD, make sure that ld_seed is "
636                      "different from the previous run (using ld_seed=-1 will ensure this)");
637     }
638     /* verify simulated tempering options */
639
640     if (ir->bSimTemp)
641     {
642         bool bAllTempZero = TRUE;
643         for (i = 0; i < fep->n_lambda; i++)
644         {
645             sprintf(err_buf,
646                     "Entry %d for %s must be between 0 and 1, instead is %g",
647                     i,
648                     enumValueToString(FreeEnergyPerturbationCouplingType::Temperature),
649                     fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Temperature)][i]);
650             CHECK((fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Temperature)][i] < 0)
651                   || (fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Temperature)][i]
652                       > 1));
653             if (fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Temperature)][i] > 0)
654             {
655                 bAllTempZero = FALSE;
656             }
657         }
658         sprintf(err_buf, "if simulated tempering is on, temperature-lambdas may not be all zero");
659         CHECK(bAllTempZero == TRUE);
660
661         sprintf(err_buf, "Simulated tempering is currently only compatible with md-vv");
662         CHECK(ir->eI != IntegrationAlgorithm::VV);
663
664         /* check compatability of the temperature coupling with simulated tempering */
665
666         if (ir->etc == TemperatureCoupling::NoseHoover)
667         {
668             sprintf(warn_buf,
669                     "Nose-Hoover based temperature control such as [%s] my not be "
670                     "entirelyconsistent with simulated tempering",
671                     enumValueToString(ir->etc));
672             warning_note(wi, warn_buf);
673         }
674
675         /* check that the temperatures make sense */
676
677         sprintf(err_buf,
678                 "Higher simulated tempering temperature (%g) must be >= than the simulated "
679                 "tempering lower temperature (%g)",
680                 ir->simtempvals->simtemp_high,
681                 ir->simtempvals->simtemp_low);
682         CHECK(ir->simtempvals->simtemp_high <= ir->simtempvals->simtemp_low);
683
684         sprintf(err_buf,
685                 "Higher simulated tempering temperature (%g) must be >= zero",
686                 ir->simtempvals->simtemp_high);
687         CHECK(ir->simtempvals->simtemp_high <= 0);
688
689         sprintf(err_buf,
690                 "Lower simulated tempering temperature (%g) must be >= zero",
691                 ir->simtempvals->simtemp_low);
692         CHECK(ir->simtempvals->simtemp_low <= 0);
693     }
694
695     /* verify free energy options */
696
697     if (ir->efep != FreeEnergyPerturbationType::No)
698     {
699         fep = ir->fepvals.get();
700         sprintf(err_buf, "The soft-core power is %d and can only be 1 or 2", fep->sc_power);
701         CHECK(fep->sc_alpha != 0 && fep->sc_power != 1 && fep->sc_power != 2);
702
703         sprintf(err_buf,
704                 "The soft-core sc-r-power is %d and can only be 6. (sc-r-power 48 is no longer "
705                 "supported.)",
706                 static_cast<int>(fep->sc_r_power));
707         CHECK(fep->sc_alpha != 0 && fep->sc_r_power != 6.0);
708
709         sprintf(err_buf,
710                 "Can't use positive delta-lambda (%g) if initial state/lambda does not start at "
711                 "zero",
712                 fep->delta_lambda);
713         CHECK(fep->delta_lambda > 0 && ((fep->init_fep_state > 0) || (fep->init_lambda > 0)));
714
715         sprintf(err_buf,
716                 "Can't use positive delta-lambda (%g) with expanded ensemble simulations",
717                 fep->delta_lambda);
718         CHECK(fep->delta_lambda > 0 && (ir->efep == FreeEnergyPerturbationType::Expanded));
719
720         sprintf(err_buf, "Can only use expanded ensemble with md-vv (for now)");
721         CHECK(!(EI_VV(ir->eI)) && (ir->efep == FreeEnergyPerturbationType::Expanded));
722
723         sprintf(err_buf, "Free-energy not implemented for Ewald");
724         CHECK(ir->coulombtype == CoulombInteractionType::Ewald);
725
726         /* check validty of lambda inputs */
727         if (fep->n_lambda == 0)
728         {
729             /* Clear output in case of no states:*/
730             sprintf(err_buf, "init-lambda-state set to %d: no lambda states are defined.", fep->init_fep_state);
731             CHECK((fep->init_fep_state >= 0) && (fep->n_lambda == 0));
732         }
733         else
734         {
735             sprintf(err_buf,
736                     "initial thermodynamic state %d does not exist, only goes to %d",
737                     fep->init_fep_state,
738                     fep->n_lambda - 1);
739             CHECK((fep->init_fep_state >= fep->n_lambda));
740         }
741
742         sprintf(err_buf,
743                 "Lambda state must be set, either with init-lambda-state or with init-lambda");
744         CHECK((fep->init_fep_state < 0) && (fep->init_lambda < 0));
745
746         sprintf(err_buf,
747                 "init-lambda=%g while init-lambda-state=%d. Lambda state must be set either with "
748                 "init-lambda-state or with init-lambda, but not both",
749                 fep->init_lambda,
750                 fep->init_fep_state);
751         CHECK((fep->init_fep_state >= 0) && (fep->init_lambda >= 0));
752
753
754         if ((fep->init_lambda >= 0) && (fep->delta_lambda == 0))
755         {
756             int n_lambda_terms;
757             n_lambda_terms = 0;
758             for (i = 0; i < static_cast<int>(FreeEnergyPerturbationCouplingType::Count); i++)
759             {
760                 if (fep->separate_dvdl[i])
761                 {
762                     n_lambda_terms++;
763                 }
764             }
765             if (n_lambda_terms > 1)
766             {
767                 sprintf(warn_buf,
768                         "If lambda vector states (fep-lambdas, coul-lambdas etc.) are set, don't "
769                         "use init-lambda to set lambda state (except for slow growth). Use "
770                         "init-lambda-state instead.");
771                 warning(wi, warn_buf);
772             }
773
774             if (n_lambda_terms < 2 && fep->n_lambda > 0)
775             {
776                 warning_note(wi,
777                              "init-lambda is deprecated for setting lambda state (except for slow "
778                              "growth). Use init-lambda-state instead.");
779             }
780         }
781
782         for (j = 0; j < static_cast<int>(FreeEnergyPerturbationCouplingType::Count); j++)
783         {
784             for (i = 0; i < fep->n_lambda; i++)
785             {
786                 auto enumValue = static_cast<FreeEnergyPerturbationCouplingType>(j);
787                 sprintf(err_buf,
788                         "Entry %d for %s must be between 0 and 1, instead is %g",
789                         i,
790                         enumValueToString(enumValue),
791                         fep->all_lambda[j][i]);
792                 CHECK((fep->all_lambda[j][i] < 0) || (fep->all_lambda[j][i] > 1));
793             }
794         }
795
796         if ((fep->sc_alpha > 0) && (!fep->bScCoul))
797         {
798             for (i = 0; i < fep->n_lambda; i++)
799             {
800                 sprintf(err_buf,
801                         "For state %d, vdw-lambdas (%f) is changing with vdw softcore, while "
802                         "coul-lambdas (%f) is nonzero without coulomb softcore: this will lead to "
803                         "crashes, and is not supported.",
804                         i,
805                         fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Vdw)][i],
806                         fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Coul)][i]);
807                 CHECK((fep->sc_alpha > 0)
808                       && (((fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Coul)][i] > 0.0)
809                            && (fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Coul)][i] < 1.0))
810                           && ((fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Vdw)][i] > 0.0)
811                               && (fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Vdw)][i]
812                                   < 1.0))));
813             }
814         }
815
816         if ((fep->bScCoul) && (EEL_PME(ir->coulombtype)))
817         {
818             real sigma, lambda, r_sc;
819
820             sigma = 0.34;
821             /* Maximum estimate for A and B charges equal with lambda power 1 */
822             lambda = 0.5;
823             r_sc = std::pow(lambda * fep->sc_alpha * std::pow(sigma / ir->rcoulomb, fep->sc_r_power) + 1.0,
824                             1.0 / fep->sc_r_power);
825             sprintf(warn_buf,
826                     "With PME there is a minor soft core effect present at the cut-off, "
827                     "proportional to (LJsigma/rcoulomb)^%g. This could have a minor effect on "
828                     "energy conservation, but usually other effects dominate. With a common sigma "
829                     "value of %g nm the fraction of the particle-particle potential at the cut-off "
830                     "at lambda=%g is around %.1e, while ewald-rtol is %.1e.",
831                     fep->sc_r_power,
832                     sigma,
833                     lambda,
834                     r_sc - 1.0,
835                     ir->ewald_rtol);
836             warning_note(wi, warn_buf);
837         }
838
839         /*  Free Energy Checks -- In an ideal world, slow growth and FEP would
840             be treated differently, but that's the next step */
841
842         for (i = 0; i < static_cast<int>(FreeEnergyPerturbationCouplingType::Count); i++)
843         {
844             auto enumValue = static_cast<FreeEnergyPerturbationCouplingType>(i);
845             for (j = 0; j < fep->n_lambda; j++)
846             {
847                 sprintf(err_buf, "%s[%d] must be between 0 and 1", enumValueToString(enumValue), j);
848                 CHECK((fep->all_lambda[i][j] < 0) || (fep->all_lambda[i][j] > 1));
849             }
850         }
851     }
852
853     if ((ir->bSimTemp) || (ir->efep == FreeEnergyPerturbationType::Expanded))
854     {
855         fep = ir->fepvals.get();
856
857         /* checking equilibration of weights inputs for validity */
858
859         sprintf(err_buf,
860                 "weight-equil-number-all-lambda (%d) is ignored if lmc-weights-equil is not equal "
861                 "to %s",
862                 expand->equil_n_at_lam,
863                 enumValueToString(LambdaWeightWillReachEquilibrium::NumAtLambda));
864         CHECK((expand->equil_n_at_lam > 0)
865               && (expand->elmceq != LambdaWeightWillReachEquilibrium::NumAtLambda));
866
867         sprintf(err_buf,
868                 "weight-equil-number-samples (%d) is ignored if lmc-weights-equil is not equal to "
869                 "%s",
870                 expand->equil_samples,
871                 enumValueToString(LambdaWeightWillReachEquilibrium::Samples));
872         CHECK((expand->equil_samples > 0) && (expand->elmceq != LambdaWeightWillReachEquilibrium::Samples));
873
874         sprintf(err_buf,
875                 "weight-equil-number-steps (%d) is ignored if lmc-weights-equil is not equal to %s",
876                 expand->equil_steps,
877                 enumValueToString(LambdaWeightWillReachEquilibrium::Steps));
878         CHECK((expand->equil_steps > 0) && (expand->elmceq != LambdaWeightWillReachEquilibrium::Steps));
879
880         sprintf(err_buf,
881                 "weight-equil-wl-delta (%d) is ignored if lmc-weights-equil is not equal to %s",
882                 expand->equil_samples,
883                 enumValueToString(LambdaWeightWillReachEquilibrium::WLDelta));
884         CHECK((expand->equil_wl_delta > 0) && (expand->elmceq != LambdaWeightWillReachEquilibrium::WLDelta));
885
886         sprintf(err_buf,
887                 "weight-equil-count-ratio (%f) is ignored if lmc-weights-equil is not equal to %s",
888                 expand->equil_ratio,
889                 enumValueToString(LambdaWeightWillReachEquilibrium::Ratio));
890         CHECK((expand->equil_ratio > 0) && (expand->elmceq != LambdaWeightWillReachEquilibrium::Ratio));
891
892         sprintf(err_buf,
893                 "weight-equil-number-all-lambda (%d) must be a positive integer if "
894                 "lmc-weights-equil=%s",
895                 expand->equil_n_at_lam,
896                 enumValueToString(LambdaWeightWillReachEquilibrium::NumAtLambda));
897         CHECK((expand->equil_n_at_lam <= 0)
898               && (expand->elmceq == LambdaWeightWillReachEquilibrium::NumAtLambda));
899
900         sprintf(err_buf,
901                 "weight-equil-number-samples (%d) must be a positive integer if "
902                 "lmc-weights-equil=%s",
903                 expand->equil_samples,
904                 enumValueToString(LambdaWeightWillReachEquilibrium::Samples));
905         CHECK((expand->equil_samples <= 0) && (expand->elmceq == LambdaWeightWillReachEquilibrium::Samples));
906
907         sprintf(err_buf,
908                 "weight-equil-number-steps (%d) must be a positive integer if lmc-weights-equil=%s",
909                 expand->equil_steps,
910                 enumValueToString(LambdaWeightWillReachEquilibrium::Steps));
911         CHECK((expand->equil_steps <= 0) && (expand->elmceq == LambdaWeightWillReachEquilibrium::Steps));
912
913         sprintf(err_buf,
914                 "weight-equil-wl-delta (%f) must be > 0 if lmc-weights-equil=%s",
915                 expand->equil_wl_delta,
916                 enumValueToString(LambdaWeightWillReachEquilibrium::WLDelta));
917         CHECK((expand->equil_wl_delta <= 0)
918               && (expand->elmceq == LambdaWeightWillReachEquilibrium::WLDelta));
919
920         sprintf(err_buf,
921                 "weight-equil-count-ratio (%f) must be > 0 if lmc-weights-equil=%s",
922                 expand->equil_ratio,
923                 enumValueToString(LambdaWeightWillReachEquilibrium::Ratio));
924         CHECK((expand->equil_ratio <= 0) && (expand->elmceq == LambdaWeightWillReachEquilibrium::Ratio));
925
926         sprintf(err_buf,
927                 "lmc-weights-equil=%s only possible when lmc-stats = %s or lmc-stats %s",
928                 enumValueToString(LambdaWeightWillReachEquilibrium::WLDelta),
929                 enumValueToString(LambdaWeightCalculation::WL),
930                 enumValueToString(LambdaWeightCalculation::WWL));
931         CHECK((expand->elmceq == LambdaWeightWillReachEquilibrium::WLDelta) && (!EWL(expand->elamstats)));
932
933         sprintf(err_buf, "lmc-repeats (%d) must be greater than 0", expand->lmc_repeats);
934         CHECK((expand->lmc_repeats <= 0));
935         sprintf(err_buf, "minimum-var-min (%d) must be greater than 0", expand->minvarmin);
936         CHECK((expand->minvarmin <= 0));
937         sprintf(err_buf, "weight-c-range (%d) must be greater or equal to 0", expand->c_range);
938         CHECK((expand->c_range < 0));
939         sprintf(err_buf,
940                 "init-lambda-state (%d) must be zero if lmc-forced-nstart (%d)> 0 and lmc-move != "
941                 "'no'",
942                 fep->init_fep_state,
943                 expand->lmc_forced_nstart);
944         CHECK((fep->init_fep_state != 0) && (expand->lmc_forced_nstart > 0)
945               && (expand->elmcmove != LambdaMoveCalculation::No));
946         sprintf(err_buf, "lmc-forced-nstart (%d) must not be negative", expand->lmc_forced_nstart);
947         CHECK((expand->lmc_forced_nstart < 0));
948         sprintf(err_buf,
949                 "init-lambda-state (%d) must be in the interval [0,number of lambdas)",
950                 fep->init_fep_state);
951         CHECK((fep->init_fep_state < 0) || (fep->init_fep_state >= fep->n_lambda));
952
953         sprintf(err_buf, "init-wl-delta (%f) must be greater than or equal to 0", expand->init_wl_delta);
954         CHECK((expand->init_wl_delta < 0));
955         sprintf(err_buf, "wl-ratio (%f) must be between 0 and 1", expand->wl_ratio);
956         CHECK((expand->wl_ratio <= 0) || (expand->wl_ratio >= 1));
957         sprintf(err_buf, "wl-scale (%f) must be between 0 and 1", expand->wl_scale);
958         CHECK((expand->wl_scale <= 0) || (expand->wl_scale >= 1));
959
960         /* if there is no temperature control, we need to specify an MC temperature */
961         if (!integratorHasReferenceTemperature(ir)
962             && (expand->elmcmove != LambdaMoveCalculation::No) && (expand->mc_temp <= 0.0))
963         {
964             sprintf(err_buf,
965                     "If there is no temperature control, and lmc-mcmove!='no', mc_temp must be set "
966                     "to a positive number");
967             warning_error(wi, err_buf);
968         }
969         if (expand->nstTij > 0)
970         {
971             sprintf(err_buf, "nstlog must be non-zero");
972             CHECK(ir->nstlog == 0);
973             // Avoid modulus by zero in the case that already triggered an error exit.
974             if (ir->nstlog != 0)
975             {
976                 sprintf(err_buf,
977                         "nst-transition-matrix (%d) must be an integer multiple of nstlog (%d)",
978                         expand->nstTij,
979                         ir->nstlog);
980                 CHECK((expand->nstTij % ir->nstlog) != 0);
981             }
982         }
983     }
984
985     /* PBC/WALLS */
986     sprintf(err_buf, "walls only work with pbc=%s", c_pbcTypeNames[PbcType::XY].c_str());
987     CHECK(ir->nwall && ir->pbcType != PbcType::XY);
988
989     /* VACUUM STUFF */
990     if (ir->pbcType != PbcType::Xyz && ir->nwall != 2)
991     {
992         if (ir->pbcType == PbcType::No)
993         {
994             if (ir->epc != PressureCoupling::No)
995             {
996                 warning(wi, "Turning off pressure coupling for vacuum system");
997                 ir->epc = PressureCoupling::No;
998             }
999         }
1000         else
1001         {
1002             sprintf(err_buf,
1003                     "Can not have pressure coupling with pbc=%s",
1004                     c_pbcTypeNames[ir->pbcType].c_str());
1005             CHECK(ir->epc != PressureCoupling::No);
1006         }
1007         sprintf(err_buf, "Can not have Ewald with pbc=%s", c_pbcTypeNames[ir->pbcType].c_str());
1008         CHECK(EEL_FULL(ir->coulombtype));
1009
1010         sprintf(err_buf,
1011                 "Can not have dispersion correction with pbc=%s",
1012                 c_pbcTypeNames[ir->pbcType].c_str());
1013         CHECK(ir->eDispCorr != DispersionCorrectionType::No);
1014     }
1015
1016     if (ir->rlist == 0.0)
1017     {
1018         sprintf(err_buf,
1019                 "can only have neighborlist cut-off zero (=infinite)\n"
1020                 "with coulombtype = %s or coulombtype = %s\n"
1021                 "without periodic boundary conditions (pbc = %s) and\n"
1022                 "rcoulomb and rvdw set to zero",
1023                 enumValueToString(CoulombInteractionType::Cut),
1024                 enumValueToString(CoulombInteractionType::User),
1025                 c_pbcTypeNames[PbcType::No].c_str());
1026         CHECK(((ir->coulombtype != CoulombInteractionType::Cut)
1027                && (ir->coulombtype != CoulombInteractionType::User))
1028               || (ir->pbcType != PbcType::No) || (ir->rcoulomb != 0.0) || (ir->rvdw != 0.0));
1029
1030         if (ir->nstlist > 0)
1031         {
1032             warning_note(wi,
1033                          "Simulating without cut-offs can be (slightly) faster with nstlist=0, "
1034                          "nstype=simple and only one MPI rank");
1035         }
1036     }
1037
1038     /* COMM STUFF */
1039     if (ir->nstcomm == 0)
1040     {
1041         // TODO Change this behaviour. There should be exactly one way
1042         // to turn off an algorithm.
1043         ir->comm_mode = ComRemovalAlgorithm::No;
1044     }
1045     if (ir->comm_mode != ComRemovalAlgorithm::No)
1046     {
1047         if (ir->nstcomm < 0)
1048         {
1049             // TODO Such input was once valid. Now that we've been
1050             // helpful for a few years, we should reject such input,
1051             // lest we have to support every historical decision
1052             // forever.
1053             warning(wi,
1054                     "If you want to remove the rotation around the center of mass, you should set "
1055                     "comm_mode = Angular instead of setting nstcomm < 0. nstcomm is modified to "
1056                     "its absolute value");
1057             ir->nstcomm = abs(ir->nstcomm);
1058         }
1059
1060         if (ir->nstcalcenergy > 0 && ir->nstcomm < ir->nstcalcenergy)
1061         {
1062             warning_note(wi,
1063                          "nstcomm < nstcalcenergy defeats the purpose of nstcalcenergy, setting "
1064                          "nstcomm to nstcalcenergy");
1065             ir->nstcomm = ir->nstcalcenergy;
1066         }
1067
1068         if (ir->comm_mode == ComRemovalAlgorithm::Angular)
1069         {
1070             sprintf(err_buf,
1071                     "Can not remove the rotation around the center of mass with periodic "
1072                     "molecules");
1073             CHECK(ir->bPeriodicMols);
1074             if (ir->pbcType != PbcType::No)
1075             {
1076                 warning(wi,
1077                         "Removing the rotation around the center of mass in a periodic system, "
1078                         "this can lead to artifacts. Only use this on a single (cluster of) "
1079                         "molecules. This cluster should not cross periodic boundaries.");
1080             }
1081         }
1082     }
1083
1084     if (EI_STATE_VELOCITY(ir->eI) && !EI_SD(ir->eI) && ir->pbcType == PbcType::No
1085         && ir->comm_mode != ComRemovalAlgorithm::Angular)
1086     {
1087         sprintf(warn_buf,
1088                 "Tumbling and flying ice-cubes: We are not removing rotation around center of mass "
1089                 "in a non-periodic system. You should probably set comm_mode = ANGULAR or use "
1090                 "integrator = %s.",
1091                 enumValueToString(IntegrationAlgorithm::SD1));
1092         warning_note(wi, warn_buf);
1093     }
1094
1095     /* TEMPERATURE COUPLING */
1096     if (ir->etc == TemperatureCoupling::Yes)
1097     {
1098         ir->etc = TemperatureCoupling::Berendsen;
1099         warning_note(wi,
1100                      "Old option for temperature coupling given: "
1101                      "changing \"yes\" to \"Berendsen\"\n");
1102     }
1103
1104     if ((ir->etc == TemperatureCoupling::NoseHoover) || (ir->epc == PressureCoupling::Mttk))
1105     {
1106         if (ir->opts.nhchainlength < 1)
1107         {
1108             sprintf(warn_buf,
1109                     "number of Nose-Hoover chains (currently %d) cannot be less than 1,reset to "
1110                     "1\n",
1111                     ir->opts.nhchainlength);
1112             ir->opts.nhchainlength = 1;
1113             warning(wi, warn_buf);
1114         }
1115
1116         if (ir->etc == TemperatureCoupling::NoseHoover && !EI_VV(ir->eI) && ir->opts.nhchainlength > 1)
1117         {
1118             warning_note(
1119                     wi,
1120                     "leapfrog does not yet support Nose-Hoover chains, nhchainlength reset to 1");
1121             ir->opts.nhchainlength = 1;
1122         }
1123     }
1124     else
1125     {
1126         ir->opts.nhchainlength = 0;
1127     }
1128
1129     if (ir->eI == IntegrationAlgorithm::VVAK)
1130     {
1131         sprintf(err_buf,
1132                 "%s implemented primarily for validation, and requires nsttcouple = 1 and "
1133                 "nstpcouple = 1.",
1134                 enumValueToString(IntegrationAlgorithm::VVAK));
1135         CHECK((ir->nsttcouple != 1) || (ir->nstpcouple != 1));
1136     }
1137
1138     if (ETC_ANDERSEN(ir->etc))
1139     {
1140         sprintf(err_buf,
1141                 "%s temperature control not supported for integrator %s.",
1142                 enumValueToString(ir->etc),
1143                 enumValueToString(ir->eI));
1144         CHECK(!(EI_VV(ir->eI)));
1145
1146         if (ir->nstcomm > 0 && (ir->etc == TemperatureCoupling::Andersen))
1147         {
1148             sprintf(warn_buf,
1149                     "Center of mass removal not necessary for %s.  All velocities of coupled "
1150                     "groups are rerandomized periodically, so flying ice cube errors will not "
1151                     "occur.",
1152                     enumValueToString(ir->etc));
1153             warning_note(wi, warn_buf);
1154         }
1155
1156         sprintf(err_buf,
1157                 "nstcomm must be 1, not %d for %s, as velocities of atoms in coupled groups are "
1158                 "randomized every time step",
1159                 ir->nstcomm,
1160                 enumValueToString(ir->etc));
1161         CHECK(ir->nstcomm > 1 && (ir->etc == TemperatureCoupling::Andersen));
1162     }
1163
1164     if (ir->etc == TemperatureCoupling::Berendsen)
1165     {
1166         sprintf(warn_buf,
1167                 "The %s thermostat does not generate the correct kinetic energy distribution. You "
1168                 "might want to consider using the %s thermostat.",
1169                 enumValueToString(ir->etc),
1170                 enumValueToString(TemperatureCoupling::VRescale));
1171         warning_note(wi, warn_buf);
1172     }
1173
1174     if ((ir->etc == TemperatureCoupling::NoseHoover || ETC_ANDERSEN(ir->etc))
1175         && ir->epc == PressureCoupling::Berendsen)
1176     {
1177         sprintf(warn_buf,
1178                 "Using Berendsen pressure coupling invalidates the "
1179                 "true ensemble for the thermostat");
1180         warning(wi, warn_buf);
1181     }
1182
1183     /* PRESSURE COUPLING */
1184     if (ir->epc == PressureCoupling::Isotropic)
1185     {
1186         ir->epc = PressureCoupling::Berendsen;
1187         warning_note(wi,
1188                      "Old option for pressure coupling given: "
1189                      "changing \"Isotropic\" to \"Berendsen\"\n");
1190     }
1191
1192     if (ir->epc != PressureCoupling::No)
1193     {
1194         dt_pcoupl = ir->nstpcouple * ir->delta_t;
1195
1196         sprintf(err_buf, "tau-p must be > 0 instead of %g\n", ir->tau_p);
1197         CHECK(ir->tau_p <= 0);
1198
1199         if (ir->tau_p / dt_pcoupl < pcouple_min_integration_steps(ir->epc) - 10 * GMX_REAL_EPS)
1200         {
1201             sprintf(warn_buf,
1202                     "For proper integration of the %s barostat, tau-p (%g) should be at least %d "
1203                     "times larger than nstpcouple*dt (%g)",
1204                     enumValueToString(ir->epc),
1205                     ir->tau_p,
1206                     pcouple_min_integration_steps(ir->epc),
1207                     dt_pcoupl);
1208             warning(wi, warn_buf);
1209         }
1210
1211         sprintf(err_buf,
1212                 "compressibility must be > 0 when using pressure"
1213                 " coupling %s\n",
1214                 enumValueToString(ir->epc));
1215         CHECK(ir->compress[XX][XX] < 0 || ir->compress[YY][YY] < 0 || ir->compress[ZZ][ZZ] < 0
1216               || (trace(ir->compress) == 0 && ir->compress[YY][XX] <= 0 && ir->compress[ZZ][XX] <= 0
1217                   && ir->compress[ZZ][YY] <= 0));
1218
1219         if (PressureCoupling::ParrinelloRahman == ir->epc && opts->bGenVel)
1220         {
1221             sprintf(warn_buf,
1222                     "You are generating velocities so I am assuming you "
1223                     "are equilibrating a system. You are using "
1224                     "%s pressure coupling, but this can be "
1225                     "unstable for equilibration. If your system crashes, try "
1226                     "equilibrating first with Berendsen pressure coupling. If "
1227                     "you are not equilibrating the system, you can probably "
1228                     "ignore this warning.",
1229                     enumValueToString(ir->epc));
1230             warning(wi, warn_buf);
1231         }
1232     }
1233
1234     if (!EI_VV(ir->eI))
1235     {
1236         if (ir->epc == PressureCoupling::Mttk)
1237         {
1238             warning_error(wi, "MTTK pressure coupling requires a Velocity-verlet integrator");
1239         }
1240     }
1241
1242     /* ELECTROSTATICS */
1243     /* More checks are in triple check (grompp.c) */
1244
1245     if (ir->coulombtype == CoulombInteractionType::Switch)
1246     {
1247         sprintf(warn_buf,
1248                 "coulombtype = %s is only for testing purposes and can lead to serious "
1249                 "artifacts, advice: use coulombtype = %s",
1250                 enumValueToString(ir->coulombtype),
1251                 enumValueToString(CoulombInteractionType::RFZero));
1252         warning(wi, warn_buf);
1253     }
1254
1255     if (EEL_RF(ir->coulombtype) && ir->epsilon_rf == 1 && ir->epsilon_r != 1)
1256     {
1257         sprintf(warn_buf,
1258                 "epsilon-r = %g and epsilon-rf = 1 with reaction field, proceeding assuming old "
1259                 "format and exchanging epsilon-r and epsilon-rf",
1260                 ir->epsilon_r);
1261         warning(wi, warn_buf);
1262         ir->epsilon_rf = ir->epsilon_r;
1263         ir->epsilon_r  = 1.0;
1264     }
1265
1266     if (ir->epsilon_r == 0)
1267     {
1268         sprintf(err_buf,
1269                 "It is pointless to use long-range electrostatics with infinite relative "
1270                 "permittivity."
1271                 "Since you are effectively turning of electrostatics, a plain cutoff will be much "
1272                 "faster.");
1273         CHECK(EEL_FULL(ir->coulombtype));
1274     }
1275
1276     if (getenv("GMX_DO_GALACTIC_DYNAMICS") == nullptr)
1277     {
1278         sprintf(err_buf, "epsilon-r must be >= 0 instead of %g\n", ir->epsilon_r);
1279         CHECK(ir->epsilon_r < 0);
1280     }
1281
1282     if (EEL_RF(ir->coulombtype))
1283     {
1284         /* reaction field (at the cut-off) */
1285
1286         if (ir->coulombtype == CoulombInteractionType::RFZero && ir->epsilon_rf != 0)
1287         {
1288             sprintf(warn_buf,
1289                     "With coulombtype = %s, epsilon-rf must be 0, assuming you meant epsilon_rf=0",
1290                     enumValueToString(ir->coulombtype));
1291             warning(wi, warn_buf);
1292             ir->epsilon_rf = 0.0;
1293         }
1294
1295         sprintf(err_buf, "epsilon-rf must be >= epsilon-r");
1296         CHECK((ir->epsilon_rf < ir->epsilon_r && ir->epsilon_rf != 0) || (ir->epsilon_r == 0));
1297         if (ir->epsilon_rf == ir->epsilon_r)
1298         {
1299             sprintf(warn_buf,
1300                     "Using epsilon-rf = epsilon-r with %s does not make sense",
1301                     enumValueToString(ir->coulombtype));
1302             warning(wi, warn_buf);
1303         }
1304     }
1305     /* Allow rlist>rcoulomb for tabulated long range stuff. This just
1306      * means the interaction is zero outside rcoulomb, but it helps to
1307      * provide accurate energy conservation.
1308      */
1309     if (ir_coulomb_might_be_zero_at_cutoff(ir))
1310     {
1311         if (ir_coulomb_switched(ir))
1312         {
1313             sprintf(err_buf,
1314                     "With coulombtype = %s rcoulomb_switch must be < rcoulomb. Or, better: Use the "
1315                     "potential modifier options!",
1316                     enumValueToString(ir->coulombtype));
1317             CHECK(ir->rcoulomb_switch >= ir->rcoulomb);
1318         }
1319     }
1320
1321     if (ir->coulombtype == CoulombInteractionType::Switch || ir->coulombtype == CoulombInteractionType::Shift)
1322     {
1323         sprintf(err_buf,
1324                 "Explicit switch/shift coulomb interactions cannot be used in combination with a "
1325                 "secondary coulomb-modifier.");
1326         CHECK(ir->coulomb_modifier != InteractionModifiers::None);
1327     }
1328     if (ir->vdwtype == VanDerWaalsType::Switch || ir->vdwtype == VanDerWaalsType::Shift)
1329     {
1330         sprintf(err_buf,
1331                 "Explicit switch/shift vdw interactions cannot be used in combination with a "
1332                 "secondary vdw-modifier.");
1333         CHECK(ir->vdw_modifier != InteractionModifiers::None);
1334     }
1335
1336     if (ir->coulombtype == CoulombInteractionType::Switch || ir->coulombtype == CoulombInteractionType::Shift
1337         || ir->vdwtype == VanDerWaalsType::Switch || ir->vdwtype == VanDerWaalsType::Shift)
1338     {
1339         sprintf(warn_buf,
1340                 "The switch/shift interaction settings are just for compatibility; you will get "
1341                 "better "
1342                 "performance from applying potential modifiers to your interactions!\n");
1343         warning_note(wi, warn_buf);
1344     }
1345
1346     if (ir->coulombtype == CoulombInteractionType::PmeSwitch
1347         || ir->coulomb_modifier == InteractionModifiers::PotSwitch)
1348     {
1349         if (ir->rcoulomb_switch / ir->rcoulomb < 0.9499)
1350         {
1351             real percentage = 100 * (ir->rcoulomb - ir->rcoulomb_switch) / ir->rcoulomb;
1352             sprintf(warn_buf,
1353                     "The switching range should be 5%% or less (currently %.2f%% using a switching "
1354                     "range of %4f-%4f) for accurate electrostatic energies, energy conservation "
1355                     "will be good regardless, since ewald_rtol = %g.",
1356                     percentage,
1357                     ir->rcoulomb_switch,
1358                     ir->rcoulomb,
1359                     ir->ewald_rtol);
1360             warning(wi, warn_buf);
1361         }
1362     }
1363
1364     if (ir->vdwtype == VanDerWaalsType::Switch || ir->vdw_modifier == InteractionModifiers::PotSwitch)
1365     {
1366         if (ir->rvdw_switch == 0)
1367         {
1368             sprintf(warn_buf,
1369                     "rvdw-switch is equal 0 even though you are using a switched Lennard-Jones "
1370                     "potential.  This suggests it was not set in the mdp, which can lead to large "
1371                     "energy errors.  In GROMACS, 0.05 to 0.1 nm is often a reasonable vdw "
1372                     "switching range.");
1373             warning(wi, warn_buf);
1374         }
1375     }
1376
1377     if (EEL_FULL(ir->coulombtype))
1378     {
1379         if (ir->coulombtype == CoulombInteractionType::PmeSwitch
1380             || ir->coulombtype == CoulombInteractionType::PmeUser
1381             || ir->coulombtype == CoulombInteractionType::PmeUserSwitch)
1382         {
1383             sprintf(err_buf,
1384                     "With coulombtype = %s, rcoulomb must be <= rlist",
1385                     enumValueToString(ir->coulombtype));
1386             CHECK(ir->rcoulomb > ir->rlist);
1387         }
1388     }
1389
1390     if (EEL_PME(ir->coulombtype) || EVDW_PME(ir->vdwtype))
1391     {
1392         // TODO: Move these checks into the ewald module with the options class
1393         int orderMin = 3;
1394         int orderMax = (ir->coulombtype == CoulombInteractionType::P3mAD ? 8 : 12);
1395
1396         if (ir->pme_order < orderMin || ir->pme_order > orderMax)
1397         {
1398             sprintf(warn_buf,
1399                     "With coulombtype = %s, you should have %d <= pme-order <= %d",
1400                     enumValueToString(ir->coulombtype),
1401                     orderMin,
1402                     orderMax);
1403             warning_error(wi, warn_buf);
1404         }
1405     }
1406
1407     if (ir->nwall == 2 && EEL_FULL(ir->coulombtype))
1408     {
1409         if (ir->ewald_geometry == EwaldGeometry::ThreeD)
1410         {
1411             sprintf(warn_buf,
1412                     "With pbc=%s you should use ewald-geometry=%s",
1413                     c_pbcTypeNames[ir->pbcType].c_str(),
1414                     enumValueToString(EwaldGeometry::ThreeDC));
1415             warning(wi, warn_buf);
1416         }
1417         /* This check avoids extra pbc coding for exclusion corrections */
1418         sprintf(err_buf, "wall-ewald-zfac should be >= 2");
1419         CHECK(ir->wall_ewald_zfac < 2);
1420     }
1421     if ((ir->ewald_geometry == EwaldGeometry::ThreeDC) && (ir->pbcType != PbcType::XY)
1422         && EEL_FULL(ir->coulombtype))
1423     {
1424         sprintf(warn_buf,
1425                 "With %s and ewald_geometry = %s you should use pbc = %s",
1426                 enumValueToString(ir->coulombtype),
1427                 enumValueToString(EwaldGeometry::ThreeDC),
1428                 c_pbcTypeNames[PbcType::XY].c_str());
1429         warning(wi, warn_buf);
1430     }
1431     if ((ir->epsilon_surface != 0) && EEL_FULL(ir->coulombtype))
1432     {
1433         sprintf(err_buf, "Cannot have periodic molecules with epsilon_surface > 0");
1434         CHECK(ir->bPeriodicMols);
1435         sprintf(warn_buf, "With epsilon_surface > 0 all molecules should be neutral.");
1436         warning_note(wi, warn_buf);
1437         sprintf(warn_buf,
1438                 "With epsilon_surface > 0 you can only use domain decomposition "
1439                 "when there are only small molecules with all bonds constrained (mdrun will check "
1440                 "for this).");
1441         warning_note(wi, warn_buf);
1442     }
1443
1444     if (ir_vdw_switched(ir))
1445     {
1446         sprintf(err_buf, "With switched vdw forces or potentials, rvdw-switch must be < rvdw");
1447         CHECK(ir->rvdw_switch >= ir->rvdw);
1448
1449         if (ir->rvdw_switch < 0.5 * ir->rvdw)
1450         {
1451             sprintf(warn_buf,
1452                     "You are applying a switch function to vdw forces or potentials from %g to %g "
1453                     "nm, which is more than half the interaction range, whereas switch functions "
1454                     "are intended to act only close to the cut-off.",
1455                     ir->rvdw_switch,
1456                     ir->rvdw);
1457             warning_note(wi, warn_buf);
1458         }
1459     }
1460
1461     if (ir->vdwtype == VanDerWaalsType::Pme)
1462     {
1463         if (!(ir->vdw_modifier == InteractionModifiers::None
1464               || ir->vdw_modifier == InteractionModifiers::PotShift))
1465         {
1466             sprintf(err_buf,
1467                     "With vdwtype = %s, the only supported modifiers are %s and %s",
1468                     enumValueToString(ir->vdwtype),
1469                     enumValueToString(InteractionModifiers::PotShift),
1470                     enumValueToString(InteractionModifiers::None));
1471             warning_error(wi, err_buf);
1472         }
1473     }
1474
1475     if (ir->vdwtype == VanDerWaalsType::User && ir->eDispCorr != DispersionCorrectionType::No)
1476     {
1477         warning_note(wi,
1478                      "You have selected user tables with dispersion correction, the dispersion "
1479                      "will be corrected to -C6/r^6 beyond rvdw_switch (the tabulated interaction "
1480                      "between rvdw_switch and rvdw will not be double counted). Make sure that you "
1481                      "really want dispersion correction to -C6/r^6.");
1482     }
1483
1484     if (ir->eI == IntegrationAlgorithm::LBFGS
1485         && (ir->coulombtype == CoulombInteractionType::Cut || ir->vdwtype == VanDerWaalsType::Cut)
1486         && ir->rvdw != 0)
1487     {
1488         warning(wi, "For efficient BFGS minimization, use switch/shift/pme instead of cut-off.");
1489     }
1490
1491     if (ir->eI == IntegrationAlgorithm::LBFGS && ir->nbfgscorr <= 0)
1492     {
1493         warning(wi, "Using L-BFGS with nbfgscorr<=0 just gets you steepest descent.");
1494     }
1495
1496     /* IMPLICIT SOLVENT */
1497     if (ir->coulombtype == CoulombInteractionType::GBNotused)
1498     {
1499         sprintf(warn_buf, "Invalid option %s for coulombtype", enumValueToString(ir->coulombtype));
1500         warning_error(wi, warn_buf);
1501     }
1502
1503     if (ir->bQMMM)
1504     {
1505         warning_error(wi, "The QMMM integration you are trying to use is no longer supported");
1506     }
1507
1508     if (ir->bAdress)
1509     {
1510         gmx_fatal(FARGS, "AdResS simulations are no longer supported");
1511     }
1512
1513     // cosine acceleration is only supported in leap-frog
1514     if (ir->cos_accel != 0.0 && ir->eI != IntegrationAlgorithm::MD)
1515     {
1516         warning_error(wi, "cos-acceleration is only supported by integrator = md");
1517     }
1518 }
1519
1520 /* interpret a number of doubles from a string and put them in an array,
1521    after allocating space for them.
1522    str = the input string
1523    n = the (pre-allocated) number of doubles read
1524    r = the output array of doubles. */
1525 static std::vector<real> parse_n_real(const std::string& str, int* n, warninp_t wi)
1526 {
1527     auto values = gmx::splitString(str);
1528     *n          = values.size();
1529
1530     std::vector<real> r;
1531     for (int i = 0; i < *n; i++)
1532     {
1533         try
1534         {
1535             r.emplace_back(gmx::fromString<real>(values[i]));
1536         }
1537         catch (gmx::GromacsException&)
1538         {
1539             warning_error(wi,
1540                           "Invalid value " + values[i]
1541                                   + " in string in mdp file. Expected a real number.");
1542         }
1543     }
1544     return r;
1545 }
1546
1547
1548 static void do_fep_params(t_inputrec* ir, gmx::ArrayRef<std::string> fep_lambda, char weights[STRLEN], warninp_t wi)
1549 {
1550
1551     int         i, j, max_n_lambda, nweights;
1552     t_lambda*   fep    = ir->fepvals.get();
1553     t_expanded* expand = ir->expandedvals.get();
1554     gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, std::vector<real>> count_fep_lambdas;
1555     bool                                                                         bOneLambda = TRUE;
1556     gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, int>               nfep;
1557
1558     /* FEP input processing */
1559     /* first, identify the number of lambda values for each type.
1560        All that are nonzero must have the same number */
1561
1562     for (auto i : keysOf(nfep))
1563     {
1564         count_fep_lambdas[i] = parse_n_real(fep_lambda[static_cast<int>(i)], &(nfep[i]), wi);
1565     }
1566
1567     /* now, determine the number of components.  All must be either zero, or equal. */
1568
1569     max_n_lambda = 0;
1570     for (auto i : keysOf(nfep))
1571     {
1572         if (nfep[i] > max_n_lambda)
1573         {
1574             max_n_lambda = nfep[i]; /* here's a nonzero one.  All of them
1575                                        must have the same number if its not zero.*/
1576             break;
1577         }
1578     }
1579
1580     for (auto i : keysOf(nfep))
1581     {
1582         if (nfep[i] == 0)
1583         {
1584             ir->fepvals->separate_dvdl[i] = FALSE;
1585         }
1586         else if (nfep[i] == max_n_lambda)
1587         {
1588             if (i != FreeEnergyPerturbationCouplingType::Temperature) /* we treat this differently -- not really a reason to compute
1589                                          the derivative with respect to the temperature currently */
1590             {
1591                 ir->fepvals->separate_dvdl[i] = TRUE;
1592             }
1593         }
1594         else
1595         {
1596             gmx_fatal(FARGS,
1597                       "Number of lambdas (%d) for FEP type %s not equal to number of other types "
1598                       "(%d)",
1599                       nfep[i],
1600                       enumValueToString(i),
1601                       max_n_lambda);
1602         }
1603     }
1604     /* we don't print out dhdl if the temperature is changing, since we can't correctly define dhdl in this case */
1605     ir->fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Temperature] = FALSE;
1606
1607     /* the number of lambdas is the number we've read in, which is either zero
1608        or the same for all */
1609     fep->n_lambda = max_n_lambda;
1610
1611     /* if init_lambda is defined, we need to set lambda */
1612     if ((fep->init_lambda > 0) && (fep->n_lambda == 0))
1613     {
1614         ir->fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Fep] = TRUE;
1615     }
1616     /* otherwise allocate the space for all of the lambdas, and transfer the data */
1617     for (auto i : keysOf(nfep))
1618     {
1619         fep->all_lambda[i].resize(fep->n_lambda);
1620         if (nfep[i] > 0) /* if it's zero, then the count_fep_lambda arrays
1621                             are zero */
1622         {
1623             for (j = 0; j < fep->n_lambda; j++)
1624             {
1625                 fep->all_lambda[i][j] = static_cast<double>(count_fep_lambdas[i][j]);
1626             }
1627         }
1628     }
1629
1630     /* "fep-vals" is either zero or the full number. If zero, we'll need to define fep-lambdas for
1631        internal bookkeeping -- for now, init_lambda */
1632
1633     if ((nfep[FreeEnergyPerturbationCouplingType::Fep] == 0) && (fep->init_lambda >= 0))
1634     {
1635         for (i = 0; i < fep->n_lambda; i++)
1636         {
1637             fep->all_lambda[FreeEnergyPerturbationCouplingType::Fep][i] = fep->init_lambda;
1638         }
1639     }
1640
1641     /* check to see if only a single component lambda is defined, and soft core is defined.
1642        In this case, turn on coulomb soft core */
1643
1644     if (max_n_lambda == 0)
1645     {
1646         bOneLambda = TRUE;
1647     }
1648     else
1649     {
1650         for (auto i : keysOf(nfep))
1651         {
1652             if ((nfep[i] != 0) && (i != FreeEnergyPerturbationCouplingType::Fep))
1653             {
1654                 bOneLambda = FALSE;
1655             }
1656         }
1657     }
1658     if ((bOneLambda) && (fep->sc_alpha > 0))
1659     {
1660         fep->bScCoul = TRUE;
1661     }
1662
1663     /* Fill in the others with the efptFEP if they are not explicitly
1664        specified (i.e. nfep[i] == 0).  This means if fep is not defined,
1665        they are all zero. */
1666
1667     for (auto i : keysOf(nfep))
1668     {
1669         if ((nfep[i] == 0) && (i != FreeEnergyPerturbationCouplingType::Fep))
1670         {
1671             for (j = 0; j < fep->n_lambda; j++)
1672             {
1673                 fep->all_lambda[i][j] = fep->all_lambda[FreeEnergyPerturbationCouplingType::Fep][j];
1674             }
1675         }
1676     }
1677
1678
1679     /* now read in the weights */
1680     expand->init_lambda_weights = parse_n_real(weights, &nweights, wi);
1681     if (nweights == 0)
1682     {
1683         expand->init_lambda_weights.resize(fep->n_lambda); /* initialize to zero */
1684     }
1685     else if (nweights != fep->n_lambda)
1686     {
1687         gmx_fatal(FARGS,
1688                   "Number of weights (%d) is not equal to number of lambda values (%d)",
1689                   nweights,
1690                   fep->n_lambda);
1691     }
1692     if ((expand->nstexpanded < 0) && (ir->efep != FreeEnergyPerturbationType::No))
1693     {
1694         expand->nstexpanded = fep->nstdhdl;
1695         /* if you don't specify nstexpanded when doing expanded ensemble free energy calcs, it is set to nstdhdl */
1696     }
1697 }
1698
1699
1700 static void do_simtemp_params(t_inputrec* ir)
1701 {
1702     ir->simtempvals->temperatures.resize(ir->fepvals->n_lambda);
1703     getSimTemps(ir->fepvals->n_lambda,
1704                 ir->simtempvals.get(),
1705                 ir->fepvals->all_lambda[FreeEnergyPerturbationCouplingType::Temperature]);
1706 }
1707
1708 template<typename T>
1709 void convertInts(warninp_t wi, gmx::ArrayRef<const std::string> inputs, const char* name, T* outputs)
1710 {
1711     int i = 0;
1712     for (const auto& input : inputs)
1713     {
1714         try
1715         {
1716             outputs[i] = gmx::fromStdString<T>(input);
1717         }
1718         catch (gmx::GromacsException&)
1719         {
1720             auto message = gmx::formatString(
1721                     "Invalid value for mdp option %s. %s should only consist of integers separated "
1722                     "by spaces.",
1723                     name,
1724                     name);
1725             warning_error(wi, message);
1726         }
1727         ++i;
1728     }
1729 }
1730
1731 static void convertReals(warninp_t wi, gmx::ArrayRef<const std::string> inputs, const char* name, real* outputs)
1732 {
1733     int i = 0;
1734     for (const auto& input : inputs)
1735     {
1736         try
1737         {
1738             outputs[i] = gmx::fromString<real>(input);
1739         }
1740         catch (gmx::GromacsException&)
1741         {
1742             auto message = gmx::formatString(
1743                     "Invalid value for mdp option %s. %s should only consist of real numbers "
1744                     "separated by spaces.",
1745                     name,
1746                     name);
1747             warning_error(wi, message);
1748         }
1749         ++i;
1750     }
1751 }
1752
1753 static void do_wall_params(t_inputrec* ir, char* wall_atomtype, char* wall_density, t_gromppopts* opts, warninp_t wi)
1754 {
1755     opts->wall_atomtype[0] = nullptr;
1756     opts->wall_atomtype[1] = nullptr;
1757
1758     ir->wall_atomtype[0] = -1;
1759     ir->wall_atomtype[1] = -1;
1760     ir->wall_density[0]  = 0;
1761     ir->wall_density[1]  = 0;
1762
1763     if (ir->nwall > 0)
1764     {
1765         auto wallAtomTypes = gmx::splitString(wall_atomtype);
1766         if (wallAtomTypes.size() != size_t(ir->nwall))
1767         {
1768             gmx_fatal(FARGS,
1769                       "Expected %d elements for wall_atomtype, found %zu",
1770                       ir->nwall,
1771                       wallAtomTypes.size());
1772         }
1773         GMX_RELEASE_ASSERT(ir->nwall < 3, "Invalid number of walls");
1774         for (int i = 0; i < ir->nwall; i++)
1775         {
1776             opts->wall_atomtype[i] = gmx_strdup(wallAtomTypes[i].c_str());
1777         }
1778
1779         if (ir->wall_type == WallType::NineThree || ir->wall_type == WallType::TenFour)
1780         {
1781             auto wallDensity = gmx::splitString(wall_density);
1782             if (wallDensity.size() != size_t(ir->nwall))
1783             {
1784                 gmx_fatal(FARGS,
1785                           "Expected %d elements for wall-density, found %zu",
1786                           ir->nwall,
1787                           wallDensity.size());
1788             }
1789             convertReals(wi, wallDensity, "wall-density", ir->wall_density);
1790             for (int i = 0; i < ir->nwall; i++)
1791             {
1792                 if (ir->wall_density[i] <= 0)
1793                 {
1794                     gmx_fatal(FARGS, "wall-density[%d] = %f\n", i, ir->wall_density[i]);
1795                 }
1796             }
1797         }
1798     }
1799 }
1800
1801 static void add_wall_energrps(SimulationGroups* groups, int nwall, t_symtab* symtab)
1802 {
1803     if (nwall > 0)
1804     {
1805         AtomGroupIndices* grps = &(groups->groups[SimulationAtomGroupType::EnergyOutput]);
1806         for (int i = 0; i < nwall; i++)
1807         {
1808             groups->groupNames.emplace_back(put_symtab(symtab, gmx::formatString("wall%d", i).c_str()));
1809             grps->emplace_back(groups->groupNames.size() - 1);
1810         }
1811     }
1812 }
1813
1814 static void read_expandedparams(std::vector<t_inpfile>* inp, t_expanded* expand, warninp_t wi)
1815 {
1816     /* read expanded ensemble parameters */
1817     printStringNewline(inp, "expanded ensemble variables");
1818     expand->nstexpanded = get_eint(inp, "nstexpanded", -1, wi);
1819     expand->elamstats   = getEnum<LambdaWeightCalculation>(inp, "lmc-stats", wi);
1820     expand->elmcmove    = getEnum<LambdaMoveCalculation>(inp, "lmc-move", wi);
1821     expand->elmceq      = getEnum<LambdaWeightWillReachEquilibrium>(inp, "lmc-weights-equil", wi);
1822     expand->equil_n_at_lam = get_eint(inp, "weight-equil-number-all-lambda", -1, wi);
1823     expand->equil_samples  = get_eint(inp, "weight-equil-number-samples", -1, wi);
1824     expand->equil_steps    = get_eint(inp, "weight-equil-number-steps", -1, wi);
1825     expand->equil_wl_delta = get_ereal(inp, "weight-equil-wl-delta", -1, wi);
1826     expand->equil_ratio    = get_ereal(inp, "weight-equil-count-ratio", -1, wi);
1827     printStringNewline(inp, "Seed for Monte Carlo in lambda space");
1828     expand->lmc_seed          = get_eint(inp, "lmc-seed", -1, wi);
1829     expand->mc_temp           = get_ereal(inp, "mc-temperature", -1, wi);
1830     expand->lmc_repeats       = get_eint(inp, "lmc-repeats", 1, wi);
1831     expand->gibbsdeltalam     = get_eint(inp, "lmc-gibbsdelta", -1, wi);
1832     expand->lmc_forced_nstart = get_eint(inp, "lmc-forced-nstart", 0, wi);
1833     expand->bSymmetrizedTMatrix =
1834             (getEnum<Boolean>(inp, "symmetrized-transition-matrix", wi) != Boolean::No);
1835     expand->nstTij        = get_eint(inp, "nst-transition-matrix", -1, wi);
1836     expand->minvarmin     = get_eint(inp, "mininum-var-min", 100, wi); /*default is reasonable */
1837     expand->c_range       = get_eint(inp, "weight-c-range", 0, wi);    /* default is just C=0 */
1838     expand->wl_scale      = get_ereal(inp, "wl-scale", 0.8, wi);
1839     expand->wl_ratio      = get_ereal(inp, "wl-ratio", 0.8, wi);
1840     expand->init_wl_delta = get_ereal(inp, "init-wl-delta", 1.0, wi);
1841     expand->bWLoneovert   = (getEnum<Boolean>(inp, "wl-oneovert", wi) != Boolean::No);
1842 }
1843
1844 /*! \brief Return whether an end state with the given coupling-lambda
1845  * value describes fully-interacting VDW.
1846  *
1847  * \param[in]  couple_lambda_value  Enumeration ecouplam value describing the end state
1848  * \return                          Whether VDW is on (i.e. the user chose vdw or vdw-q in the .mdp file)
1849  */
1850 static bool couple_lambda_has_vdw_on(int couple_lambda_value)
1851 {
1852     return (couple_lambda_value == ecouplamVDW || couple_lambda_value == ecouplamVDWQ);
1853 }
1854
1855 namespace
1856 {
1857
1858 class MdpErrorHandler : public gmx::IKeyValueTreeErrorHandler
1859 {
1860 public:
1861     explicit MdpErrorHandler(warninp_t wi) : wi_(wi), mapping_(nullptr) {}
1862
1863     void setBackMapping(const gmx::IKeyValueTreeBackMapping& mapping) { mapping_ = &mapping; }
1864
1865     bool onError(gmx::UserInputError* ex, const gmx::KeyValueTreePath& context) override
1866     {
1867         ex->prependContext(
1868                 gmx::formatString("Error in mdp option \"%s\":", getOptionName(context).c_str()));
1869         std::string message = gmx::formatExceptionMessageToString(*ex);
1870         warning_error(wi_, message.c_str());
1871         return true;
1872     }
1873
1874 private:
1875     std::string getOptionName(const gmx::KeyValueTreePath& context)
1876     {
1877         if (mapping_ != nullptr)
1878         {
1879             gmx::KeyValueTreePath path = mapping_->originalPath(context);
1880             GMX_ASSERT(path.size() == 1, "Inconsistent mapping back to mdp options");
1881             return path[0];
1882         }
1883         GMX_ASSERT(context.size() == 1, "Inconsistent context for mdp option parsing");
1884         return context[0];
1885     }
1886
1887     warninp_t                            wi_;
1888     const gmx::IKeyValueTreeBackMapping* mapping_;
1889 };
1890
1891 } // namespace
1892
1893 void get_ir(const char*     mdparin,
1894             const char*     mdparout,
1895             gmx::MDModules* mdModules,
1896             t_inputrec*     ir,
1897             t_gromppopts*   opts,
1898             WriteMdpHeader  writeMdpHeader,
1899             warninp_t       wi)
1900 {
1901     char*       dumstr[2];
1902     double      dumdub[2][6];
1903     int         i, j, m;
1904     char        warn_buf[STRLEN];
1905     t_lambda*   fep    = ir->fepvals.get();
1906     t_expanded* expand = ir->expandedvals.get();
1907
1908     const char* no_names[] = { "no", nullptr };
1909
1910     init_inputrec_strings();
1911     gmx::TextInputFile     stream(mdparin);
1912     std::vector<t_inpfile> inp = read_inpfile(&stream, mdparin, wi);
1913
1914     snew(dumstr[0], STRLEN);
1915     snew(dumstr[1], STRLEN);
1916
1917     /* ignore the following deprecated commands */
1918     replace_inp_entry(inp, "title", nullptr);
1919     replace_inp_entry(inp, "cpp", nullptr);
1920     replace_inp_entry(inp, "domain-decomposition", nullptr);
1921     replace_inp_entry(inp, "andersen-seed", nullptr);
1922     replace_inp_entry(inp, "dihre", nullptr);
1923     replace_inp_entry(inp, "dihre-fc", nullptr);
1924     replace_inp_entry(inp, "dihre-tau", nullptr);
1925     replace_inp_entry(inp, "nstdihreout", nullptr);
1926     replace_inp_entry(inp, "nstcheckpoint", nullptr);
1927     replace_inp_entry(inp, "optimize-fft", nullptr);
1928     replace_inp_entry(inp, "adress_type", nullptr);
1929     replace_inp_entry(inp, "adress_const_wf", nullptr);
1930     replace_inp_entry(inp, "adress_ex_width", nullptr);
1931     replace_inp_entry(inp, "adress_hy_width", nullptr);
1932     replace_inp_entry(inp, "adress_ex_forcecap", nullptr);
1933     replace_inp_entry(inp, "adress_interface_correction", nullptr);
1934     replace_inp_entry(inp, "adress_site", nullptr);
1935     replace_inp_entry(inp, "adress_reference_coords", nullptr);
1936     replace_inp_entry(inp, "adress_tf_grp_names", nullptr);
1937     replace_inp_entry(inp, "adress_cg_grp_names", nullptr);
1938     replace_inp_entry(inp, "adress_do_hybridpairs", nullptr);
1939     replace_inp_entry(inp, "rlistlong", nullptr);
1940     replace_inp_entry(inp, "nstcalclr", nullptr);
1941     replace_inp_entry(inp, "pull-print-com2", nullptr);
1942     replace_inp_entry(inp, "gb-algorithm", nullptr);
1943     replace_inp_entry(inp, "nstgbradii", nullptr);
1944     replace_inp_entry(inp, "rgbradii", nullptr);
1945     replace_inp_entry(inp, "gb-epsilon-solvent", nullptr);
1946     replace_inp_entry(inp, "gb-saltconc", nullptr);
1947     replace_inp_entry(inp, "gb-obc-alpha", nullptr);
1948     replace_inp_entry(inp, "gb-obc-beta", nullptr);
1949     replace_inp_entry(inp, "gb-obc-gamma", nullptr);
1950     replace_inp_entry(inp, "gb-dielectric-offset", nullptr);
1951     replace_inp_entry(inp, "sa-algorithm", nullptr);
1952     replace_inp_entry(inp, "sa-surface-tension", nullptr);
1953     replace_inp_entry(inp, "ns-type", nullptr);
1954
1955     /* replace the following commands with the clearer new versions*/
1956     replace_inp_entry(inp, "unconstrained-start", "continuation");
1957     replace_inp_entry(inp, "foreign-lambda", "fep-lambdas");
1958     replace_inp_entry(inp, "verlet-buffer-drift", "verlet-buffer-tolerance");
1959     replace_inp_entry(inp, "nstxtcout", "nstxout-compressed");
1960     replace_inp_entry(inp, "xtc-grps", "compressed-x-grps");
1961     replace_inp_entry(inp, "xtc-precision", "compressed-x-precision");
1962     replace_inp_entry(inp, "pull-print-com1", "pull-print-com");
1963
1964     printStringNewline(&inp, "VARIOUS PREPROCESSING OPTIONS");
1965     printStringNoNewline(&inp, "Preprocessor information: use cpp syntax.");
1966     printStringNoNewline(&inp, "e.g.: -I/home/joe/doe -I/home/mary/roe");
1967     setStringEntry(&inp, "include", opts->include, nullptr);
1968     printStringNoNewline(
1969             &inp, "e.g.: -DPOSRES -DFLEXIBLE (note these variable names are case sensitive)");
1970     setStringEntry(&inp, "define", opts->define, nullptr);
1971
1972     printStringNewline(&inp, "RUN CONTROL PARAMETERS");
1973     ir->eI = getEnum<IntegrationAlgorithm>(&inp, "integrator", wi);
1974     printStringNoNewline(&inp, "Start time and timestep in ps");
1975     ir->init_t  = get_ereal(&inp, "tinit", 0.0, wi);
1976     ir->delta_t = get_ereal(&inp, "dt", 0.001, wi);
1977     ir->nsteps  = get_eint64(&inp, "nsteps", 0, wi);
1978     printStringNoNewline(&inp, "For exact run continuation or redoing part of a run");
1979     ir->init_step = get_eint64(&inp, "init-step", 0, wi);
1980     printStringNoNewline(
1981             &inp, "Part index is updated automatically on checkpointing (keeps files separate)");
1982     ir->simulation_part = get_eint(&inp, "simulation-part", 1, wi);
1983     printStringNoNewline(&inp, "Multiple time-stepping");
1984     ir->useMts = (getEnum<Boolean>(&inp, "mts", wi) != Boolean::No);
1985     if (ir->useMts)
1986     {
1987         gmx::GromppMtsOpts& mtsOpts = opts->mtsOpts;
1988         mtsOpts.numLevels           = get_eint(&inp, "mts-levels", 2, wi);
1989         mtsOpts.level2Forces = setStringEntry(&inp, "mts-level2-forces", "longrange-nonbonded");
1990         mtsOpts.level2Factor = get_eint(&inp, "mts-level2-factor", 2, wi);
1991
1992         // We clear after reading without dynamics to not force the user to remove MTS mdp options
1993         if (!EI_DYNAMICS(ir->eI))
1994         {
1995             ir->useMts = false;
1996         }
1997     }
1998     printStringNoNewline(&inp, "mode for center of mass motion removal");
1999     ir->comm_mode = getEnum<ComRemovalAlgorithm>(&inp, "comm-mode", wi);
2000     printStringNoNewline(&inp, "number of steps for center of mass motion removal");
2001     ir->nstcomm = get_eint(&inp, "nstcomm", 100, wi);
2002     printStringNoNewline(&inp, "group(s) for center of mass motion removal");
2003     setStringEntry(&inp, "comm-grps", inputrecStrings->vcm, nullptr);
2004
2005     printStringNewline(&inp, "LANGEVIN DYNAMICS OPTIONS");
2006     printStringNoNewline(&inp, "Friction coefficient (amu/ps) and random seed");
2007     ir->bd_fric = get_ereal(&inp, "bd-fric", 0.0, wi);
2008     ir->ld_seed = get_eint64(&inp, "ld-seed", -1, wi);
2009
2010     /* Em stuff */
2011     printStringNewline(&inp, "ENERGY MINIMIZATION OPTIONS");
2012     printStringNoNewline(&inp, "Force tolerance and initial step-size");
2013     ir->em_tol      = get_ereal(&inp, "emtol", 10.0, wi);
2014     ir->em_stepsize = get_ereal(&inp, "emstep", 0.01, wi);
2015     printStringNoNewline(&inp, "Max number of iterations in relax-shells");
2016     ir->niter = get_eint(&inp, "niter", 20, wi);
2017     printStringNoNewline(&inp, "Step size (ps^2) for minimization of flexible constraints");
2018     ir->fc_stepsize = get_ereal(&inp, "fcstep", 0, wi);
2019     printStringNoNewline(&inp, "Frequency of steepest descents steps when doing CG");
2020     ir->nstcgsteep = get_eint(&inp, "nstcgsteep", 1000, wi);
2021     ir->nbfgscorr  = get_eint(&inp, "nbfgscorr", 10, wi);
2022
2023     printStringNewline(&inp, "TEST PARTICLE INSERTION OPTIONS");
2024     ir->rtpi = get_ereal(&inp, "rtpi", 0.05, wi);
2025
2026     /* Output options */
2027     printStringNewline(&inp, "OUTPUT CONTROL OPTIONS");
2028     printStringNoNewline(&inp, "Output frequency for coords (x), velocities (v) and forces (f)");
2029     ir->nstxout = get_eint(&inp, "nstxout", 0, wi);
2030     ir->nstvout = get_eint(&inp, "nstvout", 0, wi);
2031     ir->nstfout = get_eint(&inp, "nstfout", 0, wi);
2032     printStringNoNewline(&inp, "Output frequency for energies to log file and energy file");
2033     ir->nstlog        = get_eint(&inp, "nstlog", 1000, wi);
2034     ir->nstcalcenergy = get_eint(&inp, "nstcalcenergy", 100, wi);
2035     ir->nstenergy     = get_eint(&inp, "nstenergy", 1000, wi);
2036     printStringNoNewline(&inp, "Output frequency and precision for .xtc file");
2037     ir->nstxout_compressed      = get_eint(&inp, "nstxout-compressed", 0, wi);
2038     ir->x_compression_precision = get_ereal(&inp, "compressed-x-precision", 1000.0, wi);
2039     printStringNoNewline(&inp, "This selects the subset of atoms for the compressed");
2040     printStringNoNewline(&inp, "trajectory file. You can select multiple groups. By");
2041     printStringNoNewline(&inp, "default, all atoms will be written.");
2042     setStringEntry(&inp, "compressed-x-grps", inputrecStrings->x_compressed_groups, nullptr);
2043     printStringNoNewline(&inp, "Selection of energy groups");
2044     setStringEntry(&inp, "energygrps", inputrecStrings->energy, nullptr);
2045
2046     /* Neighbor searching */
2047     printStringNewline(&inp, "NEIGHBORSEARCHING PARAMETERS");
2048     printStringNoNewline(&inp, "cut-off scheme (Verlet: particle based cut-offs)");
2049     ir->cutoff_scheme = getEnum<CutoffScheme>(&inp, "cutoff-scheme", wi);
2050     printStringNoNewline(&inp, "nblist update frequency");
2051     ir->nstlist = get_eint(&inp, "nstlist", 10, wi);
2052     printStringNoNewline(&inp, "Periodic boundary conditions: xyz, no, xy");
2053     // TODO This conversion should be removed when proper std:string handling will be added to get_eeenum(...), etc.
2054     std::vector<const char*> pbcTypesNamesChar;
2055     for (const auto& pbcTypeName : c_pbcTypeNames)
2056     {
2057         pbcTypesNamesChar.push_back(pbcTypeName.c_str());
2058     }
2059     ir->pbcType       = static_cast<PbcType>(get_eeenum(&inp, "pbc", pbcTypesNamesChar.data(), wi));
2060     ir->bPeriodicMols = getEnum<Boolean>(&inp, "periodic-molecules", wi) != Boolean::No;
2061     printStringNoNewline(&inp,
2062                          "Allowed energy error due to the Verlet buffer in kJ/mol/ps per atom,");
2063     printStringNoNewline(&inp, "a value of -1 means: use rlist");
2064     ir->verletbuf_tol = get_ereal(&inp, "verlet-buffer-tolerance", 0.005, wi);
2065     printStringNoNewline(&inp, "nblist cut-off");
2066     ir->rlist = get_ereal(&inp, "rlist", 1.0, wi);
2067     printStringNoNewline(&inp, "long-range cut-off for switched potentials");
2068
2069     /* Electrostatics */
2070     printStringNewline(&inp, "OPTIONS FOR ELECTROSTATICS AND VDW");
2071     printStringNoNewline(&inp, "Method for doing electrostatics");
2072     ir->coulombtype      = getEnum<CoulombInteractionType>(&inp, "coulombtype", wi);
2073     ir->coulomb_modifier = getEnum<InteractionModifiers>(&inp, "coulomb-modifier", wi);
2074     printStringNoNewline(&inp, "cut-off lengths");
2075     ir->rcoulomb_switch = get_ereal(&inp, "rcoulomb-switch", 0.0, wi);
2076     ir->rcoulomb        = get_ereal(&inp, "rcoulomb", 1.0, wi);
2077     printStringNoNewline(&inp, "Relative dielectric constant for the medium and the reaction field");
2078     ir->epsilon_r  = get_ereal(&inp, "epsilon-r", 1.0, wi);
2079     ir->epsilon_rf = get_ereal(&inp, "epsilon-rf", 0.0, wi);
2080     printStringNoNewline(&inp, "Method for doing Van der Waals");
2081     ir->vdwtype      = getEnum<VanDerWaalsType>(&inp, "vdw-type", wi);
2082     ir->vdw_modifier = getEnum<InteractionModifiers>(&inp, "vdw-modifier", wi);
2083     printStringNoNewline(&inp, "cut-off lengths");
2084     ir->rvdw_switch = get_ereal(&inp, "rvdw-switch", 0.0, wi);
2085     ir->rvdw        = get_ereal(&inp, "rvdw", 1.0, wi);
2086     printStringNoNewline(&inp, "Apply long range dispersion corrections for Energy and Pressure");
2087     ir->eDispCorr = getEnum<DispersionCorrectionType>(&inp, "DispCorr", wi);
2088     printStringNoNewline(&inp, "Extension of the potential lookup tables beyond the cut-off");
2089     ir->tabext = get_ereal(&inp, "table-extension", 1.0, wi);
2090     printStringNoNewline(&inp, "Separate tables between energy group pairs");
2091     setStringEntry(&inp, "energygrp-table", inputrecStrings->egptable, nullptr);
2092     printStringNoNewline(&inp, "Spacing for the PME/PPPM FFT grid");
2093     ir->fourier_spacing = get_ereal(&inp, "fourierspacing", 0.12, wi);
2094     printStringNoNewline(&inp, "FFT grid size, when a value is 0 fourierspacing will be used");
2095     ir->nkx = get_eint(&inp, "fourier-nx", 0, wi);
2096     ir->nky = get_eint(&inp, "fourier-ny", 0, wi);
2097     ir->nkz = get_eint(&inp, "fourier-nz", 0, wi);
2098     printStringNoNewline(&inp, "EWALD/PME/PPPM parameters");
2099     ir->pme_order              = get_eint(&inp, "pme-order", 4, wi);
2100     ir->ewald_rtol             = get_ereal(&inp, "ewald-rtol", 0.00001, wi);
2101     ir->ewald_rtol_lj          = get_ereal(&inp, "ewald-rtol-lj", 0.001, wi);
2102     ir->ljpme_combination_rule = getEnum<LongRangeVdW>(&inp, "lj-pme-comb-rule", wi);
2103     ir->ewald_geometry         = getEnum<EwaldGeometry>(&inp, "ewald-geometry", wi);
2104     ir->epsilon_surface        = get_ereal(&inp, "epsilon-surface", 0.0, wi);
2105
2106     /* Implicit solvation is no longer supported, but we need grompp
2107        to be able to refuse old .mdp files that would have built a tpr
2108        to run it. Thus, only "no" is accepted. */
2109     ir->implicit_solvent = (get_eeenum(&inp, "implicit-solvent", no_names, wi) != 0);
2110
2111     /* Coupling stuff */
2112     printStringNewline(&inp, "OPTIONS FOR WEAK COUPLING ALGORITHMS");
2113     printStringNoNewline(&inp, "Temperature coupling");
2114     ir->etc                = getEnum<TemperatureCoupling>(&inp, "tcoupl", wi);
2115     ir->nsttcouple         = get_eint(&inp, "nsttcouple", -1, wi);
2116     ir->opts.nhchainlength = get_eint(&inp, "nh-chain-length", 10, wi);
2117     ir->bPrintNHChains = (getEnum<Boolean>(&inp, "print-nose-hoover-chain-variables", wi) != Boolean::No);
2118     printStringNoNewline(&inp, "Groups to couple separately");
2119     setStringEntry(&inp, "tc-grps", inputrecStrings->tcgrps, nullptr);
2120     printStringNoNewline(&inp, "Time constant (ps) and reference temperature (K)");
2121     setStringEntry(&inp, "tau-t", inputrecStrings->tau_t, nullptr);
2122     setStringEntry(&inp, "ref-t", inputrecStrings->ref_t, nullptr);
2123     printStringNoNewline(&inp, "pressure coupling");
2124     ir->epc        = getEnum<PressureCoupling>(&inp, "pcoupl", wi);
2125     ir->epct       = getEnum<PressureCouplingType>(&inp, "pcoupltype", wi);
2126     ir->nstpcouple = get_eint(&inp, "nstpcouple", -1, wi);
2127     printStringNoNewline(&inp, "Time constant (ps), compressibility (1/bar) and reference P (bar)");
2128     ir->tau_p = get_ereal(&inp, "tau-p", 1.0, wi);
2129     setStringEntry(&inp, "compressibility", dumstr[0], nullptr);
2130     setStringEntry(&inp, "ref-p", dumstr[1], nullptr);
2131     printStringNoNewline(&inp, "Scaling of reference coordinates, No, All or COM");
2132     ir->refcoord_scaling = getEnum<RefCoordScaling>(&inp, "refcoord-scaling", wi);
2133
2134     /* QMMM */
2135     printStringNewline(&inp, "OPTIONS FOR QMMM calculations");
2136     ir->bQMMM = (getEnum<Boolean>(&inp, "QMMM", wi) != Boolean::No);
2137     printStringNoNewline(&inp, "Groups treated with MiMiC");
2138     setStringEntry(&inp, "QMMM-grps", inputrecStrings->QMMM, nullptr);
2139
2140     /* Simulated annealing */
2141     printStringNewline(&inp, "SIMULATED ANNEALING");
2142     printStringNoNewline(&inp, "Type of annealing for each temperature group (no/single/periodic)");
2143     setStringEntry(&inp, "annealing", inputrecStrings->anneal, nullptr);
2144     printStringNoNewline(&inp,
2145                          "Number of time points to use for specifying annealing in each group");
2146     setStringEntry(&inp, "annealing-npoints", inputrecStrings->anneal_npoints, nullptr);
2147     printStringNoNewline(&inp, "List of times at the annealing points for each group");
2148     setStringEntry(&inp, "annealing-time", inputrecStrings->anneal_time, nullptr);
2149     printStringNoNewline(&inp, "Temp. at each annealing point, for each group.");
2150     setStringEntry(&inp, "annealing-temp", inputrecStrings->anneal_temp, nullptr);
2151
2152     /* Startup run */
2153     printStringNewline(&inp, "GENERATE VELOCITIES FOR STARTUP RUN");
2154     opts->bGenVel = (getEnum<Boolean>(&inp, "gen-vel", wi) != Boolean::No);
2155     opts->tempi   = get_ereal(&inp, "gen-temp", 300.0, wi);
2156     opts->seed    = get_eint(&inp, "gen-seed", -1, wi);
2157
2158     /* Shake stuff */
2159     printStringNewline(&inp, "OPTIONS FOR BONDS");
2160     opts->nshake = get_eeenum(&inp, "constraints", constraints, wi);
2161     printStringNoNewline(&inp, "Type of constraint algorithm");
2162     ir->eConstrAlg = getEnum<ConstraintAlgorithm>(&inp, "constraint-algorithm", wi);
2163     printStringNoNewline(&inp, "Do not constrain the start configuration");
2164     ir->bContinuation = (getEnum<Boolean>(&inp, "continuation", wi) != Boolean::No);
2165     printStringNoNewline(&inp,
2166                          "Use successive overrelaxation to reduce the number of shake iterations");
2167     ir->bShakeSOR = (getEnum<Boolean>(&inp, "Shake-SOR", wi) != Boolean::No);
2168     printStringNoNewline(&inp, "Relative tolerance of shake");
2169     ir->shake_tol = get_ereal(&inp, "shake-tol", 0.0001, wi);
2170     printStringNoNewline(&inp, "Highest order in the expansion of the constraint coupling matrix");
2171     ir->nProjOrder = get_eint(&inp, "lincs-order", 4, wi);
2172     printStringNoNewline(&inp, "Number of iterations in the final step of LINCS. 1 is fine for");
2173     printStringNoNewline(&inp, "normal simulations, but use 2 to conserve energy in NVE runs.");
2174     printStringNoNewline(&inp, "For energy minimization with constraints it should be 4 to 8.");
2175     ir->nLincsIter = get_eint(&inp, "lincs-iter", 1, wi);
2176     printStringNoNewline(&inp, "Lincs will write a warning to the stderr if in one step a bond");
2177     printStringNoNewline(&inp, "rotates over more degrees than");
2178     ir->LincsWarnAngle = get_ereal(&inp, "lincs-warnangle", 30.0, wi);
2179     printStringNoNewline(&inp, "Convert harmonic bonds to morse potentials");
2180     opts->bMorse = (getEnum<Boolean>(&inp, "morse", wi) != Boolean::No);
2181
2182     /* Energy group exclusions */
2183     printStringNewline(&inp, "ENERGY GROUP EXCLUSIONS");
2184     printStringNoNewline(
2185             &inp, "Pairs of energy groups for which all non-bonded interactions are excluded");
2186     setStringEntry(&inp, "energygrp-excl", inputrecStrings->egpexcl, nullptr);
2187
2188     /* Walls */
2189     printStringNewline(&inp, "WALLS");
2190     printStringNoNewline(
2191             &inp, "Number of walls, type, atom types, densities and box-z scale factor for Ewald");
2192     ir->nwall         = get_eint(&inp, "nwall", 0, wi);
2193     ir->wall_type     = getEnum<WallType>(&inp, "wall-type", wi);
2194     ir->wall_r_linpot = get_ereal(&inp, "wall-r-linpot", -1, wi);
2195     setStringEntry(&inp, "wall-atomtype", inputrecStrings->wall_atomtype, nullptr);
2196     setStringEntry(&inp, "wall-density", inputrecStrings->wall_density, nullptr);
2197     ir->wall_ewald_zfac = get_ereal(&inp, "wall-ewald-zfac", 3, wi);
2198
2199     /* COM pulling */
2200     printStringNewline(&inp, "COM PULLING");
2201     ir->bPull = (getEnum<Boolean>(&inp, "pull", wi) != Boolean::No);
2202     if (ir->bPull)
2203     {
2204         ir->pull                        = std::make_unique<pull_params_t>();
2205         inputrecStrings->pullGroupNames = read_pullparams(&inp, ir->pull.get(), wi);
2206
2207         if (ir->useMts)
2208         {
2209             for (int c = 0; c < ir->pull->ncoord; c++)
2210             {
2211                 if (ir->pull->coord[c].eType == PullingAlgorithm::Constraint)
2212                 {
2213                     warning_error(wi,
2214                                   "Constraint COM pulling is not supported in combination with "
2215                                   "multiple time stepping");
2216                     break;
2217                 }
2218             }
2219         }
2220     }
2221
2222     /* AWH biasing
2223        NOTE: needs COM pulling or free energy input */
2224     printStringNewline(&inp, "AWH biasing");
2225     ir->bDoAwh = (getEnum<Boolean>(&inp, "awh", wi) != Boolean::No);
2226     if (ir->bDoAwh)
2227     {
2228         ir->awhParams = std::make_unique<gmx::AwhParams>(&inp, wi);
2229     }
2230
2231     /* Enforced rotation */
2232     printStringNewline(&inp, "ENFORCED ROTATION");
2233     printStringNoNewline(&inp, "Enforced rotation: No or Yes");
2234     ir->bRot = (getEnum<Boolean>(&inp, "rotation", wi) != Boolean::No);
2235     if (ir->bRot)
2236     {
2237         snew(ir->rot, 1);
2238         inputrecStrings->rotateGroupNames = read_rotparams(&inp, ir->rot, wi);
2239     }
2240
2241     /* Interactive MD */
2242     ir->bIMD = FALSE;
2243     printStringNewline(&inp, "Group to display and/or manipulate in interactive MD session");
2244     setStringEntry(&inp, "IMD-group", inputrecStrings->imd_grp, nullptr);
2245     if (inputrecStrings->imd_grp[0] != '\0')
2246     {
2247         snew(ir->imd, 1);
2248         ir->bIMD = TRUE;
2249     }
2250
2251     /* Refinement */
2252     printStringNewline(&inp, "NMR refinement stuff");
2253     printStringNoNewline(&inp, "Distance restraints type: No, Simple or Ensemble");
2254     ir->eDisre = getEnum<DistanceRestraintRefinement>(&inp, "disre", wi);
2255     printStringNoNewline(
2256             &inp, "Force weighting of pairs in one distance restraint: Conservative or Equal");
2257     ir->eDisreWeighting = getEnum<DistanceRestraintWeighting>(&inp, "disre-weighting", wi);
2258     printStringNoNewline(&inp, "Use sqrt of the time averaged times the instantaneous violation");
2259     ir->bDisreMixed = (getEnum<Boolean>(&inp, "disre-mixed", wi) != Boolean::No);
2260     ir->dr_fc       = get_ereal(&inp, "disre-fc", 1000.0, wi);
2261     ir->dr_tau      = get_ereal(&inp, "disre-tau", 0.0, wi);
2262     printStringNoNewline(&inp, "Output frequency for pair distances to energy file");
2263     ir->nstdisreout = get_eint(&inp, "nstdisreout", 100, wi);
2264     printStringNoNewline(&inp, "Orientation restraints: No or Yes");
2265     opts->bOrire = (getEnum<Boolean>(&inp, "orire", wi) != Boolean::No);
2266     printStringNoNewline(&inp, "Orientation restraints force constant and tau for time averaging");
2267     ir->orires_fc  = get_ereal(&inp, "orire-fc", 0.0, wi);
2268     ir->orires_tau = get_ereal(&inp, "orire-tau", 0.0, wi);
2269     setStringEntry(&inp, "orire-fitgrp", inputrecStrings->orirefitgrp, nullptr);
2270     printStringNoNewline(&inp, "Output frequency for trace(SD) and S to energy file");
2271     ir->nstorireout = get_eint(&inp, "nstorireout", 100, wi);
2272
2273     /* free energy variables */
2274     printStringNewline(&inp, "Free energy variables");
2275     ir->efep = getEnum<FreeEnergyPerturbationType>(&inp, "free-energy", wi);
2276     setStringEntry(&inp, "couple-moltype", inputrecStrings->couple_moltype, nullptr);
2277     opts->couple_lam0  = get_eeenum(&inp, "couple-lambda0", couple_lam, wi);
2278     opts->couple_lam1  = get_eeenum(&inp, "couple-lambda1", couple_lam, wi);
2279     opts->bCoupleIntra = (getEnum<Boolean>(&inp, "couple-intramol", wi) != Boolean::No);
2280
2281     fep->init_lambda = get_ereal(&inp, "init-lambda", -1, wi); /* start with -1 so
2282                                                                          we can recognize if
2283                                                                          it was not entered */
2284     fep->init_fep_state = get_eint(&inp, "init-lambda-state", -1, wi);
2285     fep->delta_lambda   = get_ereal(&inp, "delta-lambda", 0.0, wi);
2286     fep->nstdhdl        = get_eint(&inp, "nstdhdl", 50, wi);
2287     inputrecStrings->fep_lambda[FreeEnergyPerturbationCouplingType::Fep] =
2288             setStringEntry(&inp, "fep-lambdas", "");
2289     inputrecStrings->fep_lambda[FreeEnergyPerturbationCouplingType::Mass] =
2290             setStringEntry(&inp, "mass-lambdas", "");
2291     inputrecStrings->fep_lambda[FreeEnergyPerturbationCouplingType::Coul] =
2292             setStringEntry(&inp, "coul-lambdas", "");
2293     inputrecStrings->fep_lambda[FreeEnergyPerturbationCouplingType::Vdw] =
2294             setStringEntry(&inp, "vdw-lambdas", "");
2295     inputrecStrings->fep_lambda[FreeEnergyPerturbationCouplingType::Bonded] =
2296             setStringEntry(&inp, "bonded-lambdas", "");
2297     inputrecStrings->fep_lambda[FreeEnergyPerturbationCouplingType::Restraint] =
2298             setStringEntry(&inp, "restraint-lambdas", "");
2299     inputrecStrings->fep_lambda[FreeEnergyPerturbationCouplingType::Temperature] =
2300             setStringEntry(&inp, "temperature-lambdas", "");
2301     fep->lambda_neighbors = get_eint(&inp, "calc-lambda-neighbors", 1, wi);
2302     setStringEntry(&inp, "init-lambda-weights", inputrecStrings->lambda_weights, nullptr);
2303     fep->edHdLPrintEnergy   = getEnum<FreeEnergyPrintEnergy>(&inp, "dhdl-print-energy", wi);
2304     fep->sc_alpha           = get_ereal(&inp, "sc-alpha", 0.0, wi);
2305     fep->sc_power           = get_eint(&inp, "sc-power", 1, wi);
2306     fep->sc_r_power         = get_ereal(&inp, "sc-r-power", 6.0, wi);
2307     fep->sc_sigma           = get_ereal(&inp, "sc-sigma", 0.3, wi);
2308     fep->bScCoul            = (getEnum<Boolean>(&inp, "sc-coul", wi) != Boolean::No);
2309     fep->dh_hist_size       = get_eint(&inp, "dh_hist_size", 0, wi);
2310     fep->dh_hist_spacing    = get_ereal(&inp, "dh_hist_spacing", 0.1, wi);
2311     fep->separate_dhdl_file = getEnum<SeparateDhdlFile>(&inp, "separate-dhdl-file", wi);
2312     fep->dhdl_derivatives   = getEnum<DhDlDerivativeCalculation>(&inp, "dhdl-derivatives", wi);
2313     fep->dh_hist_size       = get_eint(&inp, "dh_hist_size", 0, wi);
2314     fep->dh_hist_spacing    = get_ereal(&inp, "dh_hist_spacing", 0.1, wi);
2315
2316     /* Non-equilibrium MD stuff */
2317     printStringNewline(&inp, "Non-equilibrium MD stuff");
2318     setStringEntry(&inp, "freezegrps", inputrecStrings->freeze, nullptr);
2319     setStringEntry(&inp, "freezedim", inputrecStrings->frdim, nullptr);
2320     ir->cos_accel = get_ereal(&inp, "cos-acceleration", 0, wi);
2321     setStringEntry(&inp, "deform", inputrecStrings->deform, nullptr);
2322
2323     /* simulated tempering variables */
2324     printStringNewline(&inp, "simulated tempering variables");
2325     ir->bSimTemp = (getEnum<Boolean>(&inp, "simulated-tempering", wi) != Boolean::No);
2326     ir->simtempvals->eSimTempScale = getEnum<SimulatedTempering>(&inp, "simulated-tempering-scaling", wi);
2327     ir->simtempvals->simtemp_low  = get_ereal(&inp, "sim-temp-low", 300.0, wi);
2328     ir->simtempvals->simtemp_high = get_ereal(&inp, "sim-temp-high", 300.0, wi);
2329
2330     /* expanded ensemble variables */
2331     if (ir->efep == FreeEnergyPerturbationType::Expanded || ir->bSimTemp)
2332     {
2333         read_expandedparams(&inp, expand, wi);
2334     }
2335
2336     /* Electric fields */
2337     {
2338         gmx::KeyValueTreeObject      convertedValues = flatKeyValueTreeFromInpFile(inp);
2339         gmx::KeyValueTreeTransformer transform;
2340         transform.rules()->addRule().keyMatchType("/", gmx::StringCompareType::CaseAndDashInsensitive);
2341         mdModules->initMdpTransform(transform.rules());
2342         for (const auto& path : transform.mappedPaths())
2343         {
2344             GMX_ASSERT(path.size() == 1, "Inconsistent mapping back to mdp options");
2345             mark_einp_set(inp, path[0].c_str());
2346         }
2347         MdpErrorHandler errorHandler(wi);
2348         auto            result = transform.transform(convertedValues, &errorHandler);
2349         ir->params             = new gmx::KeyValueTreeObject(result.object());
2350         mdModules->adjustInputrecBasedOnModules(ir);
2351         errorHandler.setBackMapping(result.backMapping());
2352         mdModules->assignOptionsToModules(*ir->params, &errorHandler);
2353     }
2354
2355     /* Ion/water position swapping ("computational electrophysiology") */
2356     printStringNewline(&inp,
2357                        "Ion/water position swapping for computational electrophysiology setups");
2358     printStringNoNewline(&inp, "Swap positions along direction: no, X, Y, Z");
2359     ir->eSwapCoords = getEnum<SwapType>(&inp, "swapcoords", wi);
2360     if (ir->eSwapCoords != SwapType::No)
2361     {
2362         char buf[STRLEN];
2363         int  nIonTypes;
2364
2365
2366         snew(ir->swap, 1);
2367         printStringNoNewline(&inp, "Swap attempt frequency");
2368         ir->swap->nstswap = get_eint(&inp, "swap-frequency", 1, wi);
2369         printStringNoNewline(&inp, "Number of ion types to be controlled");
2370         nIonTypes = get_eint(&inp, "iontypes", 1, wi);
2371         if (nIonTypes < 1)
2372         {
2373             warning_error(wi, "You need to provide at least one ion type for position exchanges.");
2374         }
2375         ir->swap->ngrp = nIonTypes + static_cast<int>(SwapGroupSplittingType::Count);
2376         snew(ir->swap->grp, ir->swap->ngrp);
2377         for (i = 0; i < ir->swap->ngrp; i++)
2378         {
2379             snew(ir->swap->grp[i].molname, STRLEN);
2380         }
2381         printStringNoNewline(&inp,
2382                              "Two index groups that contain the compartment-partitioning atoms");
2383         setStringEntry(&inp,
2384                        "split-group0",
2385                        ir->swap->grp[static_cast<int>(SwapGroupSplittingType::Split0)].molname,
2386                        nullptr);
2387         setStringEntry(&inp,
2388                        "split-group1",
2389                        ir->swap->grp[static_cast<int>(SwapGroupSplittingType::Split1)].molname,
2390                        nullptr);
2391         printStringNoNewline(&inp,
2392                              "Use center of mass of split groups (yes/no), otherwise center of "
2393                              "geometry is used");
2394         ir->swap->massw_split[0] = (getEnum<Boolean>(&inp, "massw-split0", wi) != Boolean::No);
2395         ir->swap->massw_split[1] = (getEnum<Boolean>(&inp, "massw-split1", wi) != Boolean::No);
2396
2397         printStringNoNewline(&inp, "Name of solvent molecules");
2398         setStringEntry(&inp,
2399                        "solvent-group",
2400                        ir->swap->grp[static_cast<int>(SwapGroupSplittingType::Solvent)].molname,
2401                        nullptr);
2402
2403         printStringNoNewline(&inp,
2404                              "Split cylinder: radius, upper and lower extension (nm) (this will "
2405                              "define the channels)");
2406         printStringNoNewline(&inp,
2407                              "Note that the split cylinder settings do not have an influence on "
2408                              "the swapping protocol,");
2409         printStringNoNewline(
2410                 &inp,
2411                 "however, if correctly defined, the permeation events are recorded per channel");
2412         ir->swap->cyl0r = get_ereal(&inp, "cyl0-r", 2.0, wi);
2413         ir->swap->cyl0u = get_ereal(&inp, "cyl0-up", 1.0, wi);
2414         ir->swap->cyl0l = get_ereal(&inp, "cyl0-down", 1.0, wi);
2415         ir->swap->cyl1r = get_ereal(&inp, "cyl1-r", 2.0, wi);
2416         ir->swap->cyl1u = get_ereal(&inp, "cyl1-up", 1.0, wi);
2417         ir->swap->cyl1l = get_ereal(&inp, "cyl1-down", 1.0, wi);
2418
2419         printStringNoNewline(
2420                 &inp,
2421                 "Average the number of ions per compartment over these many swap attempt steps");
2422         ir->swap->nAverage = get_eint(&inp, "coupl-steps", 10, wi);
2423
2424         printStringNoNewline(
2425                 &inp, "Names of the ion types that can be exchanged with solvent molecules,");
2426         printStringNoNewline(
2427                 &inp, "and the requested number of ions of this type in compartments A and B");
2428         printStringNoNewline(&inp, "-1 means fix the numbers as found in step 0");
2429         for (i = 0; i < nIonTypes; i++)
2430         {
2431             int ig = static_cast<int>(SwapGroupSplittingType::Count) + i;
2432
2433             sprintf(buf, "iontype%d-name", i);
2434             setStringEntry(&inp, buf, ir->swap->grp[ig].molname, nullptr);
2435             sprintf(buf, "iontype%d-in-A", i);
2436             ir->swap->grp[ig].nmolReq[0] = get_eint(&inp, buf, -1, wi);
2437             sprintf(buf, "iontype%d-in-B", i);
2438             ir->swap->grp[ig].nmolReq[1] = get_eint(&inp, buf, -1, wi);
2439         }
2440
2441         printStringNoNewline(
2442                 &inp,
2443                 "By default (i.e. bulk offset = 0.0), ion/water exchanges happen between layers");
2444         printStringNoNewline(
2445                 &inp,
2446                 "at maximum distance (= bulk concentration) to the split group layers. However,");
2447         printStringNoNewline(&inp,
2448                              "an offset b (-1.0 < b < +1.0) can be specified to offset the bulk "
2449                              "layer from the middle at 0.0");
2450         printStringNoNewline(&inp,
2451                              "towards one of the compartment-partitioning layers (at +/- 1.0).");
2452         ir->swap->bulkOffset[0] = get_ereal(&inp, "bulk-offsetA", 0.0, wi);
2453         ir->swap->bulkOffset[1] = get_ereal(&inp, "bulk-offsetB", 0.0, wi);
2454         if (!(ir->swap->bulkOffset[0] > -1.0 && ir->swap->bulkOffset[0] < 1.0)
2455             || !(ir->swap->bulkOffset[1] > -1.0 && ir->swap->bulkOffset[1] < 1.0))
2456         {
2457             warning_error(wi, "Bulk layer offsets must be > -1.0 and < 1.0 !");
2458         }
2459
2460         printStringNoNewline(
2461                 &inp, "Start to swap ions if threshold difference to requested count is reached");
2462         ir->swap->threshold = get_ereal(&inp, "threshold", 1.0, wi);
2463     }
2464
2465     /* AdResS is no longer supported, but we need grompp to be able to
2466        refuse to process old .mdp files that used it. */
2467     ir->bAdress = (get_eeenum(&inp, "adress", no_names, wi) != 0);
2468
2469     /* User defined thingies */
2470     printStringNewline(&inp, "User defined thingies");
2471     setStringEntry(&inp, "user1-grps", inputrecStrings->user1, nullptr);
2472     setStringEntry(&inp, "user2-grps", inputrecStrings->user2, nullptr);
2473     ir->userint1  = get_eint(&inp, "userint1", 0, wi);
2474     ir->userint2  = get_eint(&inp, "userint2", 0, wi);
2475     ir->userint3  = get_eint(&inp, "userint3", 0, wi);
2476     ir->userint4  = get_eint(&inp, "userint4", 0, wi);
2477     ir->userreal1 = get_ereal(&inp, "userreal1", 0, wi);
2478     ir->userreal2 = get_ereal(&inp, "userreal2", 0, wi);
2479     ir->userreal3 = get_ereal(&inp, "userreal3", 0, wi);
2480     ir->userreal4 = get_ereal(&inp, "userreal4", 0, wi);
2481 #undef CTYPE
2482
2483     {
2484         gmx::TextOutputFile stream(mdparout);
2485         write_inpfile(&stream, mdparout, &inp, FALSE, writeMdpHeader, wi);
2486
2487         // Transform module data into a flat key-value tree for output.
2488         gmx::KeyValueTreeBuilder       builder;
2489         gmx::KeyValueTreeObjectBuilder builderObject = builder.rootObject();
2490         mdModules->buildMdpOutput(&builderObject);
2491         {
2492             gmx::TextWriter writer(&stream);
2493             writeKeyValueTreeAsMdp(&writer, builder.build());
2494         }
2495         stream.close();
2496     }
2497
2498     /* Process options if necessary */
2499     for (m = 0; m < 2; m++)
2500     {
2501         for (i = 0; i < 2 * DIM; i++)
2502         {
2503             dumdub[m][i] = 0.0;
2504         }
2505         if (ir->epc != PressureCoupling::No)
2506         {
2507             switch (ir->epct)
2508             {
2509                 case PressureCouplingType::Isotropic:
2510                     if (sscanf(dumstr[m], "%lf", &(dumdub[m][XX])) != 1)
2511                     {
2512                         warning_error(
2513                                 wi,
2514                                 "Pressure coupling incorrect number of values (I need exactly 1)");
2515                     }
2516                     dumdub[m][YY] = dumdub[m][ZZ] = dumdub[m][XX];
2517                     break;
2518                 case PressureCouplingType::SemiIsotropic:
2519                 case PressureCouplingType::SurfaceTension:
2520                     if (sscanf(dumstr[m], "%lf%lf", &(dumdub[m][XX]), &(dumdub[m][ZZ])) != 2)
2521                     {
2522                         warning_error(
2523                                 wi,
2524                                 "Pressure coupling incorrect number of values (I need exactly 2)");
2525                     }
2526                     dumdub[m][YY] = dumdub[m][XX];
2527                     break;
2528                 case PressureCouplingType::Anisotropic:
2529                     if (sscanf(dumstr[m],
2530                                "%lf%lf%lf%lf%lf%lf",
2531                                &(dumdub[m][XX]),
2532                                &(dumdub[m][YY]),
2533                                &(dumdub[m][ZZ]),
2534                                &(dumdub[m][3]),
2535                                &(dumdub[m][4]),
2536                                &(dumdub[m][5]))
2537                         != 6)
2538                     {
2539                         warning_error(
2540                                 wi,
2541                                 "Pressure coupling incorrect number of values (I need exactly 6)");
2542                     }
2543                     break;
2544                 default:
2545                     gmx_fatal(FARGS,
2546                               "Pressure coupling type %s not implemented yet",
2547                               enumValueToString(ir->epct));
2548             }
2549         }
2550     }
2551     clear_mat(ir->ref_p);
2552     clear_mat(ir->compress);
2553     for (i = 0; i < DIM; i++)
2554     {
2555         ir->ref_p[i][i]    = dumdub[1][i];
2556         ir->compress[i][i] = dumdub[0][i];
2557     }
2558     if (ir->epct == PressureCouplingType::Anisotropic)
2559     {
2560         ir->ref_p[XX][YY] = dumdub[1][3];
2561         ir->ref_p[XX][ZZ] = dumdub[1][4];
2562         ir->ref_p[YY][ZZ] = dumdub[1][5];
2563         if (ir->ref_p[XX][YY] != 0 && ir->ref_p[XX][ZZ] != 0 && ir->ref_p[YY][ZZ] != 0)
2564         {
2565             warning(wi,
2566                     "All off-diagonal reference pressures are non-zero. Are you sure you want to "
2567                     "apply a threefold shear stress?\n");
2568         }
2569         ir->compress[XX][YY] = dumdub[0][3];
2570         ir->compress[XX][ZZ] = dumdub[0][4];
2571         ir->compress[YY][ZZ] = dumdub[0][5];
2572         for (i = 0; i < DIM; i++)
2573         {
2574             for (m = 0; m < i; m++)
2575             {
2576                 ir->ref_p[i][m]    = ir->ref_p[m][i];
2577                 ir->compress[i][m] = ir->compress[m][i];
2578             }
2579         }
2580     }
2581
2582     if (ir->comm_mode == ComRemovalAlgorithm::No)
2583     {
2584         ir->nstcomm = 0;
2585     }
2586
2587     opts->couple_moltype = nullptr;
2588     if (strlen(inputrecStrings->couple_moltype) > 0)
2589     {
2590         if (ir->efep != FreeEnergyPerturbationType::No)
2591         {
2592             opts->couple_moltype = gmx_strdup(inputrecStrings->couple_moltype);
2593             if (opts->couple_lam0 == opts->couple_lam1)
2594             {
2595                 warning(wi, "The lambda=0 and lambda=1 states for coupling are identical");
2596             }
2597             if (ir->eI == IntegrationAlgorithm::MD
2598                 && (opts->couple_lam0 == ecouplamNONE || opts->couple_lam1 == ecouplamNONE))
2599             {
2600                 warning_note(
2601                         wi,
2602                         "For proper sampling of the (nearly) decoupled state, stochastic dynamics "
2603                         "should be used");
2604             }
2605         }
2606         else
2607         {
2608             warning_note(wi,
2609                          "Free energy is turned off, so we will not decouple the molecule listed "
2610                          "in your input.");
2611         }
2612     }
2613     /* FREE ENERGY AND EXPANDED ENSEMBLE OPTIONS */
2614     if (ir->efep != FreeEnergyPerturbationType::No)
2615     {
2616         if (fep->delta_lambda != 0)
2617         {
2618             ir->efep = FreeEnergyPerturbationType::SlowGrowth;
2619         }
2620     }
2621
2622     if (fep->edHdLPrintEnergy == FreeEnergyPrintEnergy::Yes)
2623     {
2624         fep->edHdLPrintEnergy = FreeEnergyPrintEnergy::Total;
2625         warning_note(wi,
2626                      "Old option for dhdl-print-energy given: "
2627                      "changing \"yes\" to \"total\"\n");
2628     }
2629
2630     if (ir->bSimTemp && (fep->edHdLPrintEnergy == FreeEnergyPrintEnergy::No))
2631     {
2632         /* always print out the energy to dhdl if we are doing
2633            expanded ensemble, since we need the total energy for
2634            analysis if the temperature is changing. In some
2635            conditions one may only want the potential energy, so
2636            we will allow that if the appropriate mdp setting has
2637            been enabled. Otherwise, total it is:
2638          */
2639         fep->edHdLPrintEnergy = FreeEnergyPrintEnergy::Total;
2640     }
2641
2642     if ((ir->efep != FreeEnergyPerturbationType::No) || ir->bSimTemp)
2643     {
2644         ir->bExpanded = FALSE;
2645         if ((ir->efep == FreeEnergyPerturbationType::Expanded) || ir->bSimTemp)
2646         {
2647             ir->bExpanded = TRUE;
2648         }
2649         do_fep_params(ir, inputrecStrings->fep_lambda, inputrecStrings->lambda_weights, wi);
2650         if (ir->bSimTemp) /* done after fep params */
2651         {
2652             do_simtemp_params(ir);
2653         }
2654
2655         /* Because sc-coul (=FALSE by default) only acts on the lambda state
2656          * setup and not on the old way of specifying the free-energy setup,
2657          * we should check for using soft-core when not needed, since that
2658          * can complicate the sampling significantly.
2659          * Note that we only check for the automated coupling setup.
2660          * If the (advanced) user does FEP through manual topology changes,
2661          * this check will not be triggered.
2662          */
2663         if (ir->efep != FreeEnergyPerturbationType::No && ir->fepvals->n_lambda == 0
2664             && ir->fepvals->sc_alpha != 0
2665             && (couple_lambda_has_vdw_on(opts->couple_lam0) && couple_lambda_has_vdw_on(opts->couple_lam1)))
2666         {
2667             warning(wi,
2668                     "You are using soft-core interactions while the Van der Waals interactions are "
2669                     "not decoupled (note that the sc-coul option is only active when using lambda "
2670                     "states). Although this will not lead to errors, you will need much more "
2671                     "sampling than without soft-core interactions. Consider using sc-alpha=0.");
2672         }
2673     }
2674     else
2675     {
2676         ir->fepvals->n_lambda = 0;
2677     }
2678
2679     /* WALL PARAMETERS */
2680
2681     do_wall_params(ir, inputrecStrings->wall_atomtype, inputrecStrings->wall_density, opts, wi);
2682
2683     /* ORIENTATION RESTRAINT PARAMETERS */
2684
2685     if (opts->bOrire && gmx::splitString(inputrecStrings->orirefitgrp).size() != 1)
2686     {
2687         warning_error(wi, "ERROR: Need one orientation restraint fit group\n");
2688     }
2689
2690     /* DEFORMATION PARAMETERS */
2691
2692     clear_mat(ir->deform);
2693     for (i = 0; i < 6; i++)
2694     {
2695         dumdub[0][i] = 0;
2696     }
2697
2698     double gmx_unused canary;
2699     int               ndeform = sscanf(inputrecStrings->deform,
2700                          "%lf %lf %lf %lf %lf %lf %lf",
2701                          &(dumdub[0][0]),
2702                          &(dumdub[0][1]),
2703                          &(dumdub[0][2]),
2704                          &(dumdub[0][3]),
2705                          &(dumdub[0][4]),
2706                          &(dumdub[0][5]),
2707                          &canary);
2708
2709     if (strlen(inputrecStrings->deform) > 0 && ndeform != 6)
2710     {
2711         warning_error(wi,
2712                       gmx::formatString(
2713                               "Cannot parse exactly 6 box deformation velocities from string '%s'",
2714                               inputrecStrings->deform)
2715                               .c_str());
2716     }
2717     for (i = 0; i < 3; i++)
2718     {
2719         ir->deform[i][i] = dumdub[0][i];
2720     }
2721     ir->deform[YY][XX] = dumdub[0][3];
2722     ir->deform[ZZ][XX] = dumdub[0][4];
2723     ir->deform[ZZ][YY] = dumdub[0][5];
2724     if (ir->epc != PressureCoupling::No)
2725     {
2726         for (i = 0; i < 3; i++)
2727         {
2728             for (j = 0; j <= i; j++)
2729             {
2730                 if (ir->deform[i][j] != 0 && ir->compress[i][j] != 0)
2731                 {
2732                     warning_error(wi, "A box element has deform set and compressibility > 0");
2733                 }
2734             }
2735         }
2736         for (i = 0; i < 3; i++)
2737         {
2738             for (j = 0; j < i; j++)
2739             {
2740                 if (ir->deform[i][j] != 0)
2741                 {
2742                     for (m = j; m < DIM; m++)
2743                     {
2744                         if (ir->compress[m][j] != 0)
2745                         {
2746                             sprintf(warn_buf,
2747                                     "An off-diagonal box element has deform set while "
2748                                     "compressibility > 0 for the same component of another box "
2749                                     "vector, this might lead to spurious periodicity effects.");
2750                             warning(wi, warn_buf);
2751                         }
2752                     }
2753                 }
2754             }
2755         }
2756     }
2757
2758     /* Ion/water position swapping checks */
2759     if (ir->eSwapCoords != SwapType::No)
2760     {
2761         if (ir->swap->nstswap < 1)
2762         {
2763             warning_error(wi, "swap_frequency must be 1 or larger when ion swapping is requested");
2764         }
2765         if (ir->swap->nAverage < 1)
2766         {
2767             warning_error(wi, "coupl_steps must be 1 or larger.\n");
2768         }
2769         if (ir->swap->threshold < 1.0)
2770         {
2771             warning_error(wi, "Ion count threshold must be at least 1.\n");
2772         }
2773     }
2774
2775     /* Set up MTS levels, this needs to happen before checking AWH parameters */
2776     if (ir->useMts)
2777     {
2778         std::vector<std::string> errorMessages;
2779         ir->mtsLevels = gmx::setupMtsLevels(opts->mtsOpts, &errorMessages);
2780
2781         for (const auto& errorMessage : errorMessages)
2782         {
2783             warning_error(wi, errorMessage.c_str());
2784         }
2785     }
2786
2787     if (ir->bDoAwh)
2788     {
2789         gmx::checkAwhParams(*ir->awhParams, *ir, wi);
2790     }
2791
2792     sfree(dumstr[0]);
2793     sfree(dumstr[1]);
2794 }
2795
2796 /* We would like gn to be const as well, but C doesn't allow this */
2797 /* TODO this is utility functionality (search for the index of a
2798    string in a collection), so should be refactored and located more
2799    centrally. */
2800 int search_string(const char* s, int ng, char* gn[])
2801 {
2802     int i;
2803
2804     for (i = 0; (i < ng); i++)
2805     {
2806         if (gmx_strcasecmp(s, gn[i]) == 0)
2807         {
2808             return i;
2809         }
2810     }
2811
2812     gmx_fatal(FARGS,
2813               "Group %s referenced in the .mdp file was not found in the index file.\n"
2814               "Group names must match either [moleculetype] names or custom index group\n"
2815               "names, in which case you must supply an index file to the '-n' option\n"
2816               "of grompp.",
2817               s);
2818 }
2819
2820 static void atomGroupRangeValidation(int natoms, int groupIndex, const t_blocka& block)
2821 {
2822     /* Now go over the atoms in the group */
2823     for (int j = block.index[groupIndex]; (j < block.index[groupIndex + 1]); j++)
2824     {
2825         int aj = block.a[j];
2826
2827         /* Range checking */
2828         if ((aj < 0) || (aj >= natoms))
2829         {
2830             gmx_fatal(FARGS, "Invalid atom number %d in indexfile", aj + 1);
2831         }
2832     }
2833 }
2834
2835 static void do_numbering(int                        natoms,
2836                          SimulationGroups*          groups,
2837                          gmx::ArrayRef<std::string> groupsFromMdpFile,
2838                          t_blocka*                  block,
2839                          char*                      gnames[],
2840                          SimulationAtomGroupType    gtype,
2841                          int                        restnm,
2842                          int                        grptp,
2843                          bool                       bVerbose,
2844                          warninp_t                  wi)
2845 {
2846     unsigned short*   cbuf;
2847     AtomGroupIndices* grps = &(groups->groups[gtype]);
2848     int               ntot = 0;
2849     const char*       title;
2850     char              warn_buf[STRLEN];
2851
2852     title = shortName(gtype);
2853
2854     snew(cbuf, natoms);
2855     /* Mark all id's as not set */
2856     for (int i = 0; (i < natoms); i++)
2857     {
2858         cbuf[i] = NOGID;
2859     }
2860
2861     for (int i = 0; i != groupsFromMdpFile.ssize(); ++i)
2862     {
2863         /* Lookup the group name in the block structure */
2864         const int gid = search_string(groupsFromMdpFile[i].c_str(), block->nr, gnames);
2865         if ((grptp != egrptpONE) || (i == 0))
2866         {
2867             grps->emplace_back(gid);
2868         }
2869         GMX_ASSERT(block, "Can't have a nullptr block");
2870         atomGroupRangeValidation(natoms, gid, *block);
2871         /* Now go over the atoms in the group */
2872         for (int j = block->index[gid]; (j < block->index[gid + 1]); j++)
2873         {
2874             const int aj = block->a[j];
2875             /* Lookup up the old group number */
2876             const int ognr = cbuf[aj];
2877             if (ognr != NOGID)
2878             {
2879                 gmx_fatal(FARGS, "Atom %d in multiple %s groups (%d and %d)", aj + 1, title, ognr + 1, i + 1);
2880             }
2881             else
2882             {
2883                 /* Store the group number in buffer */
2884                 if (grptp == egrptpONE)
2885                 {
2886                     cbuf[aj] = 0;
2887                 }
2888                 else
2889                 {
2890                     cbuf[aj] = i;
2891                 }
2892                 ntot++;
2893             }
2894         }
2895     }
2896
2897     /* Now check whether we have done all atoms */
2898     if (ntot != natoms)
2899     {
2900         if (grptp == egrptpALL)
2901         {
2902             gmx_fatal(FARGS, "%d atoms are not part of any of the %s groups", natoms - ntot, title);
2903         }
2904         else if (grptp == egrptpPART)
2905         {
2906             sprintf(warn_buf, "%d atoms are not part of any of the %s groups", natoms - ntot, title);
2907             warning_note(wi, warn_buf);
2908         }
2909         /* Assign all atoms currently unassigned to a rest group */
2910         for (int j = 0; (j < natoms); j++)
2911         {
2912             if (cbuf[j] == NOGID)
2913             {
2914                 cbuf[j] = grps->size();
2915             }
2916         }
2917         if (grptp != egrptpPART)
2918         {
2919             if (bVerbose)
2920             {
2921                 fprintf(stderr, "Making dummy/rest group for %s containing %d elements\n", title, natoms - ntot);
2922             }
2923             /* Add group name "rest" */
2924             grps->emplace_back(restnm);
2925
2926             /* Assign the rest name to all atoms not currently assigned to a group */
2927             for (int j = 0; (j < natoms); j++)
2928             {
2929                 if (cbuf[j] == NOGID)
2930                 {
2931                     // group size was not updated before this here, so need to use -1.
2932                     cbuf[j] = grps->size() - 1;
2933                 }
2934             }
2935         }
2936     }
2937
2938     if (grps->size() == 1 && (ntot == 0 || ntot == natoms))
2939     {
2940         /* All atoms are part of one (or no) group, no index required */
2941         groups->groupNumbers[gtype].clear();
2942     }
2943     else
2944     {
2945         for (int j = 0; (j < natoms); j++)
2946         {
2947             groups->groupNumbers[gtype].emplace_back(cbuf[j]);
2948         }
2949     }
2950
2951     sfree(cbuf);
2952 }
2953
2954 static void calc_nrdf(const gmx_mtop_t* mtop, t_inputrec* ir, char** gnames)
2955 {
2956     t_grpopts*     opts;
2957     pull_params_t* pull;
2958     int            natoms, imin, jmin;
2959     int *          nrdf2, *na_vcm, na_tot;
2960     double *       nrdf_tc, *nrdf_vcm, nrdf_uc, *nrdf_vcm_sub;
2961     ivec*          dof_vcm;
2962     int            as;
2963
2964     /* Calculate nrdf.
2965      * First calc 3xnr-atoms for each group
2966      * then subtract half a degree of freedom for each constraint
2967      *
2968      * Only atoms and nuclei contribute to the degrees of freedom...
2969      */
2970
2971     opts = &ir->opts;
2972
2973     const SimulationGroups& groups = mtop->groups;
2974     natoms                         = mtop->natoms;
2975
2976     /* Allocate one more for a possible rest group */
2977     /* We need to sum degrees of freedom into doubles,
2978      * since floats give too low nrdf's above 3 million atoms.
2979      */
2980     snew(nrdf_tc, groups.groups[SimulationAtomGroupType::TemperatureCoupling].size() + 1);
2981     snew(nrdf_vcm, groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval].size() + 1);
2982     snew(dof_vcm, groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval].size() + 1);
2983     snew(na_vcm, groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval].size() + 1);
2984     snew(nrdf_vcm_sub, groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval].size() + 1);
2985
2986     for (gmx::index i = 0; i < gmx::ssize(groups.groups[SimulationAtomGroupType::TemperatureCoupling]); i++)
2987     {
2988         nrdf_tc[i] = 0;
2989     }
2990     for (gmx::index i = 0;
2991          i < gmx::ssize(groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval]) + 1;
2992          i++)
2993     {
2994         nrdf_vcm[i] = 0;
2995         clear_ivec(dof_vcm[i]);
2996         na_vcm[i]       = 0;
2997         nrdf_vcm_sub[i] = 0;
2998     }
2999     snew(nrdf2, natoms);
3000     for (const AtomProxy atomP : AtomRange(*mtop))
3001     {
3002         const t_atom& local = atomP.atom();
3003         int           i     = atomP.globalAtomNumber();
3004         nrdf2[i]            = 0;
3005         if (local.ptype == ParticleType::Atom || local.ptype == ParticleType::Nucleus)
3006         {
3007             int g = getGroupType(groups, SimulationAtomGroupType::Freeze, i);
3008             for (int d = 0; d < DIM; d++)
3009             {
3010                 if (opts->nFreeze[g][d] == 0)
3011                 {
3012                     /* Add one DOF for particle i (counted as 2*1) */
3013                     nrdf2[i] += 2;
3014                     /* VCM group i has dim d as a DOF */
3015                     dof_vcm[getGroupType(groups, SimulationAtomGroupType::MassCenterVelocityRemoval, i)][d] =
3016                             1;
3017                 }
3018             }
3019             nrdf_tc[getGroupType(groups, SimulationAtomGroupType::TemperatureCoupling, i)] +=
3020                     0.5 * nrdf2[i];
3021             nrdf_vcm[getGroupType(groups, SimulationAtomGroupType::MassCenterVelocityRemoval, i)] +=
3022                     0.5 * nrdf2[i];
3023         }
3024     }
3025
3026     as = 0;
3027     for (const gmx_molblock_t& molb : mtop->molblock)
3028     {
3029         const gmx_moltype_t& molt = mtop->moltype[molb.type];
3030         const t_atom*        atom = molt.atoms.atom;
3031         for (int mol = 0; mol < molb.nmol; mol++)
3032         {
3033             for (int ftype = F_CONSTR; ftype <= F_CONSTRNC; ftype++)
3034             {
3035                 gmx::ArrayRef<const int> ia = molt.ilist[ftype].iatoms;
3036                 for (int i = 0; i < molt.ilist[ftype].size();)
3037                 {
3038                     /* Subtract degrees of freedom for the constraints,
3039                      * if the particles still have degrees of freedom left.
3040                      * If one of the particles is a vsite or a shell, then all
3041                      * constraint motion will go there, but since they do not
3042                      * contribute to the constraints the degrees of freedom do not
3043                      * change.
3044                      */
3045                     int ai = as + ia[i + 1];
3046                     int aj = as + ia[i + 2];
3047                     if (((atom[ia[i + 1]].ptype == ParticleType::Nucleus)
3048                          || (atom[ia[i + 1]].ptype == ParticleType::Atom))
3049                         && ((atom[ia[i + 2]].ptype == ParticleType::Nucleus)
3050                             || (atom[ia[i + 2]].ptype == ParticleType::Atom)))
3051                     {
3052                         if (nrdf2[ai] > 0)
3053                         {
3054                             jmin = 1;
3055                         }
3056                         else
3057                         {
3058                             jmin = 2;
3059                         }
3060                         if (nrdf2[aj] > 0)
3061                         {
3062                             imin = 1;
3063                         }
3064                         else
3065                         {
3066                             imin = 2;
3067                         }
3068                         imin = std::min(imin, nrdf2[ai]);
3069                         jmin = std::min(jmin, nrdf2[aj]);
3070                         nrdf2[ai] -= imin;
3071                         nrdf2[aj] -= jmin;
3072                         nrdf_tc[getGroupType(groups, SimulationAtomGroupType::TemperatureCoupling, ai)] -=
3073                                 0.5 * imin;
3074                         nrdf_tc[getGroupType(groups, SimulationAtomGroupType::TemperatureCoupling, aj)] -=
3075                                 0.5 * jmin;
3076                         nrdf_vcm[getGroupType(groups, SimulationAtomGroupType::MassCenterVelocityRemoval, ai)] -=
3077                                 0.5 * imin;
3078                         nrdf_vcm[getGroupType(groups, SimulationAtomGroupType::MassCenterVelocityRemoval, aj)] -=
3079                                 0.5 * jmin;
3080                     }
3081                     i += interaction_function[ftype].nratoms + 1;
3082                 }
3083             }
3084             gmx::ArrayRef<const int> ia = molt.ilist[F_SETTLE].iatoms;
3085             for (int i = 0; i < molt.ilist[F_SETTLE].size();)
3086             {
3087                 /* Subtract 1 dof from every atom in the SETTLE */
3088                 for (int j = 0; j < 3; j++)
3089                 {
3090                     int ai = as + ia[i + 1 + j];
3091                     imin   = std::min(2, nrdf2[ai]);
3092                     nrdf2[ai] -= imin;
3093                     nrdf_tc[getGroupType(groups, SimulationAtomGroupType::TemperatureCoupling, ai)] -=
3094                             0.5 * imin;
3095                     nrdf_vcm[getGroupType(groups, SimulationAtomGroupType::MassCenterVelocityRemoval, ai)] -=
3096                             0.5 * imin;
3097                 }
3098                 i += 4;
3099             }
3100             as += molt.atoms.nr;
3101         }
3102     }
3103
3104     if (ir->bPull)
3105     {
3106         /* Correct nrdf for the COM constraints.
3107          * We correct using the TC and VCM group of the first atom
3108          * in the reference and pull group. If atoms in one pull group
3109          * belong to different TC or VCM groups it is anyhow difficult
3110          * to determine the optimal nrdf assignment.
3111          */
3112         pull = ir->pull.get();
3113
3114         for (int i = 0; i < pull->ncoord; i++)
3115         {
3116             if (pull->coord[i].eType != PullingAlgorithm::Constraint)
3117             {
3118                 continue;
3119             }
3120
3121             imin = 1;
3122
3123             for (int j = 0; j < 2; j++)
3124             {
3125                 const t_pull_group* pgrp;
3126
3127                 pgrp = &pull->group[pull->coord[i].group[j]];
3128
3129                 if (!pgrp->ind.empty())
3130                 {
3131                     /* Subtract 1/2 dof from each group */
3132                     int ai = pgrp->ind[0];
3133                     nrdf_tc[getGroupType(groups, SimulationAtomGroupType::TemperatureCoupling, ai)] -=
3134                             0.5 * imin;
3135                     nrdf_vcm[getGroupType(groups, SimulationAtomGroupType::MassCenterVelocityRemoval, ai)] -=
3136                             0.5 * imin;
3137                     if (nrdf_tc[getGroupType(groups, SimulationAtomGroupType::TemperatureCoupling, ai)] < 0)
3138                     {
3139                         gmx_fatal(FARGS,
3140                                   "Center of mass pulling constraints caused the number of degrees "
3141                                   "of freedom for temperature coupling group %s to be negative",
3142                                   gnames[groups.groups[SimulationAtomGroupType::TemperatureCoupling][getGroupType(
3143                                           groups, SimulationAtomGroupType::TemperatureCoupling, ai)]]);
3144                     }
3145                 }
3146                 else
3147                 {
3148                     /* We need to subtract the whole DOF from group j=1 */
3149                     imin += 1;
3150                 }
3151             }
3152         }
3153     }
3154
3155     if (ir->nstcomm != 0)
3156     {
3157         GMX_RELEASE_ASSERT(!groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval].empty(),
3158                            "Expect at least one group when removing COM motion");
3159
3160         /* We remove COM motion up to dim ndof_com() */
3161         const int ndim_rm_vcm = ndof_com(ir);
3162
3163         /* Subtract ndim_rm_vcm (or less with frozen dimensions) from
3164          * the number of degrees of freedom in each vcm group when COM
3165          * translation is removed and 6 when rotation is removed as well.
3166          * Note that we do not and should not include the rest group here.
3167          */
3168         for (gmx::index j = 0;
3169              j < gmx::ssize(groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval]);
3170              j++)
3171         {
3172             switch (ir->comm_mode)
3173             {
3174                 case ComRemovalAlgorithm::Linear:
3175                 case ComRemovalAlgorithm::LinearAccelerationCorrection:
3176                     nrdf_vcm_sub[j] = 0;
3177                     for (int d = 0; d < ndim_rm_vcm; d++)
3178                     {
3179                         if (dof_vcm[j][d])
3180                         {
3181                             nrdf_vcm_sub[j]++;
3182                         }
3183                     }
3184                     break;
3185                 case ComRemovalAlgorithm::Angular: nrdf_vcm_sub[j] = 6; break;
3186                 default: gmx_incons("Checking comm_mode");
3187             }
3188         }
3189
3190         for (gmx::index i = 0;
3191              i < gmx::ssize(groups.groups[SimulationAtomGroupType::TemperatureCoupling]);
3192              i++)
3193         {
3194             /* Count the number of atoms of TC group i for every VCM group */
3195             for (gmx::index j = 0;
3196                  j < gmx::ssize(groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval]) + 1;
3197                  j++)
3198             {
3199                 na_vcm[j] = 0;
3200             }
3201             na_tot = 0;
3202             for (int ai = 0; ai < natoms; ai++)
3203             {
3204                 if (getGroupType(groups, SimulationAtomGroupType::TemperatureCoupling, ai) == i)
3205                 {
3206                     na_vcm[getGroupType(groups, SimulationAtomGroupType::MassCenterVelocityRemoval, ai)]++;
3207                     na_tot++;
3208                 }
3209             }
3210             /* Correct for VCM removal according to the fraction of each VCM
3211              * group present in this TC group.
3212              */
3213             nrdf_uc    = nrdf_tc[i];
3214             nrdf_tc[i] = 0;
3215             for (gmx::index j = 0;
3216                  j < gmx::ssize(groups.groups[SimulationAtomGroupType::MassCenterVelocityRemoval]) + 1;
3217                  j++)
3218             {
3219                 if (nrdf_vcm[j] > nrdf_vcm_sub[j])
3220                 {
3221                     nrdf_tc[i] += nrdf_uc * (static_cast<double>(na_vcm[j]) / static_cast<double>(na_tot))
3222                                   * (nrdf_vcm[j] - nrdf_vcm_sub[j]) / nrdf_vcm[j];
3223                 }
3224             }
3225         }
3226     }
3227     for (int i = 0; (i < gmx::ssize(groups.groups[SimulationAtomGroupType::TemperatureCoupling])); i++)
3228     {
3229         opts->nrdf[i] = nrdf_tc[i];
3230         if (opts->nrdf[i] < 0)
3231         {
3232             opts->nrdf[i] = 0;
3233         }
3234         fprintf(stderr,
3235                 "Number of degrees of freedom in T-Coupling group %s is %.2f\n",
3236                 gnames[groups.groups[SimulationAtomGroupType::TemperatureCoupling][i]],
3237                 opts->nrdf[i]);
3238     }
3239
3240     sfree(nrdf2);
3241     sfree(nrdf_tc);
3242     sfree(nrdf_vcm);
3243     sfree(dof_vcm);
3244     sfree(na_vcm);
3245     sfree(nrdf_vcm_sub);
3246 }
3247
3248 static bool do_egp_flag(t_inputrec* ir, SimulationGroups* groups, const char* option, const char* val, int flag)
3249 {
3250     /* The maximum number of energy group pairs would be MAXPTR*(MAXPTR+1)/2.
3251      * But since this is much larger than STRLEN, such a line can not be parsed.
3252      * The real maximum is the number of names that fit in a string: STRLEN/2.
3253      */
3254 #define EGP_MAX (STRLEN / 2)
3255     int  j, k, nr;
3256     bool bSet;
3257
3258     auto names = gmx::splitString(val);
3259     if (names.size() % 2 != 0)
3260     {
3261         gmx_fatal(FARGS, "The number of groups for %s is odd", option);
3262     }
3263     nr   = groups->groups[SimulationAtomGroupType::EnergyOutput].size();
3264     bSet = FALSE;
3265     for (size_t i = 0; i < names.size() / 2; i++)
3266     {
3267         // TODO this needs to be replaced by a solution using std::find_if
3268         j = 0;
3269         while ((j < nr)
3270                && gmx_strcasecmp(
3271                           names[2 * i].c_str(),
3272                           *(groups->groupNames[groups->groups[SimulationAtomGroupType::EnergyOutput][j]])))
3273         {
3274             j++;
3275         }
3276         if (j == nr)
3277         {
3278             gmx_fatal(FARGS, "%s in %s is not an energy group\n", names[2 * i].c_str(), option);
3279         }
3280         k = 0;
3281         while ((k < nr)
3282                && gmx_strcasecmp(
3283                           names[2 * i + 1].c_str(),
3284                           *(groups->groupNames[groups->groups[SimulationAtomGroupType::EnergyOutput][k]])))
3285         {
3286             k++;
3287         }
3288         if (k == nr)
3289         {
3290             gmx_fatal(FARGS, "%s in %s is not an energy group\n", names[2 * i + 1].c_str(), option);
3291         }
3292         if ((j < nr) && (k < nr))
3293         {
3294             ir->opts.egp_flags[nr * j + k] |= flag;
3295             ir->opts.egp_flags[nr * k + j] |= flag;
3296             bSet = TRUE;
3297         }
3298     }
3299
3300     return bSet;
3301 }
3302
3303
3304 static void make_swap_groups(t_swapcoords* swap, t_blocka* grps, char** gnames)
3305 {
3306     int          ig = -1, i = 0, gind;
3307     t_swapGroup* swapg;
3308
3309
3310     /* Just a quick check here, more thorough checks are in mdrun */
3311     if (strcmp(swap->grp[static_cast<int>(SwapGroupSplittingType::Split0)].molname,
3312                swap->grp[static_cast<int>(SwapGroupSplittingType::Split1)].molname)
3313         == 0)
3314     {
3315         gmx_fatal(FARGS,
3316                   "The split groups can not both be '%s'.",
3317                   swap->grp[static_cast<int>(SwapGroupSplittingType::Split0)].molname);
3318     }
3319
3320     /* Get the index atoms of the split0, split1, solvent, and swap groups */
3321     for (ig = 0; ig < swap->ngrp; ig++)
3322     {
3323         swapg      = &swap->grp[ig];
3324         gind       = search_string(swap->grp[ig].molname, grps->nr, gnames);
3325         swapg->nat = grps->index[gind + 1] - grps->index[gind];
3326
3327         if (swapg->nat > 0)
3328         {
3329             fprintf(stderr,
3330                     "%s group '%s' contains %d atoms.\n",
3331                     ig < 3 ? enumValueToString(static_cast<SwapGroupSplittingType>(ig)) : "Swap",
3332                     swap->grp[ig].molname,
3333                     swapg->nat);
3334             snew(swapg->ind, swapg->nat);
3335             for (i = 0; i < swapg->nat; i++)
3336             {
3337                 swapg->ind[i] = grps->a[grps->index[gind] + i];
3338             }
3339         }
3340         else
3341         {
3342             gmx_fatal(FARGS, "Swap group %s does not contain any atoms.", swap->grp[ig].molname);
3343         }
3344     }
3345 }
3346
3347
3348 static void make_IMD_group(t_IMD* IMDgroup, char* IMDgname, t_blocka* grps, char** gnames)
3349 {
3350     int ig, i;
3351
3352
3353     ig            = search_string(IMDgname, grps->nr, gnames);
3354     IMDgroup->nat = grps->index[ig + 1] - grps->index[ig];
3355
3356     if (IMDgroup->nat > 0)
3357     {
3358         fprintf(stderr,
3359                 "Group '%s' with %d atoms can be activated for interactive molecular dynamics "
3360                 "(IMD).\n",
3361                 IMDgname,
3362                 IMDgroup->nat);
3363         snew(IMDgroup->ind, IMDgroup->nat);
3364         for (i = 0; i < IMDgroup->nat; i++)
3365         {
3366             IMDgroup->ind[i] = grps->a[grps->index[ig] + i];
3367         }
3368     }
3369 }
3370
3371 /* Checks whether atoms are both part of a COM removal group and frozen.
3372  * If a fully frozen atom is part of a COM removal group, it is removed
3373  * from the COM removal group. A note is issued if such atoms are present.
3374  * A warning is issued for atom with one or two dimensions frozen that
3375  * are part of a COM removal group (mdrun would need to compute COM mass
3376  * per dimension to handle this correctly).
3377  * Also issues a warning when non-frozen atoms are not part of a COM
3378  * removal group while COM removal is active.
3379  */
3380 static void checkAndUpdateVcmFreezeGroupConsistency(SimulationGroups* groups,
3381                                                     const int         numAtoms,
3382                                                     const t_grpopts&  opts,
3383                                                     warninp_t         wi)
3384 {
3385     const int vcmRestGroup =
3386             std::max(int(groups->groups[SimulationAtomGroupType::MassCenterVelocityRemoval].size()), 1);
3387
3388     int numFullyFrozenVcmAtoms     = 0;
3389     int numPartiallyFrozenVcmAtoms = 0;
3390     int numNonVcmAtoms             = 0;
3391     for (int a = 0; a < numAtoms; a++)
3392     {
3393         const int freezeGroup   = getGroupType(*groups, SimulationAtomGroupType::Freeze, a);
3394         int       numFrozenDims = 0;
3395         for (int d = 0; d < DIM; d++)
3396         {
3397             numFrozenDims += opts.nFreeze[freezeGroup][d];
3398         }
3399
3400         const int vcmGroup = getGroupType(*groups, SimulationAtomGroupType::MassCenterVelocityRemoval, a);
3401         if (vcmGroup < vcmRestGroup)
3402         {
3403             if (numFrozenDims == DIM)
3404             {
3405                 /* Do not remove COM motion for this fully frozen atom */
3406                 if (groups->groupNumbers[SimulationAtomGroupType::MassCenterVelocityRemoval].empty())
3407                 {
3408                     groups->groupNumbers[SimulationAtomGroupType::MassCenterVelocityRemoval].resize(
3409                             numAtoms, 0);
3410                 }
3411                 groups->groupNumbers[SimulationAtomGroupType::MassCenterVelocityRemoval][a] = vcmRestGroup;
3412                 numFullyFrozenVcmAtoms++;
3413             }
3414             else if (numFrozenDims > 0)
3415             {
3416                 numPartiallyFrozenVcmAtoms++;
3417             }
3418         }
3419         else if (numFrozenDims < DIM)
3420         {
3421             numNonVcmAtoms++;
3422         }
3423     }
3424
3425     if (numFullyFrozenVcmAtoms > 0)
3426     {
3427         std::string warningText = gmx::formatString(
3428                 "There are %d atoms that are fully frozen and part of COMM removal group(s), "
3429                 "removing these atoms from the COMM removal group(s)",
3430                 numFullyFrozenVcmAtoms);
3431         warning_note(wi, warningText.c_str());
3432     }
3433     if (numPartiallyFrozenVcmAtoms > 0 && numPartiallyFrozenVcmAtoms < numAtoms)
3434     {
3435         std::string warningText = gmx::formatString(
3436                 "There are %d atoms that are frozen along less then %d dimensions and part of COMM "
3437                 "removal group(s), due to limitations in the code these still contribute to the "
3438                 "mass of the COM along frozen dimensions and therefore the COMM correction will be "
3439                 "too small.",
3440                 numPartiallyFrozenVcmAtoms,
3441                 DIM);
3442         warning(wi, warningText.c_str());
3443     }
3444     if (numNonVcmAtoms > 0)
3445     {
3446         std::string warningText = gmx::formatString(
3447                 "%d atoms are not part of any center of mass motion removal group.\n"
3448                 "This may lead to artifacts.\n"
3449                 "In most cases one should use one group for the whole system.",
3450                 numNonVcmAtoms);
3451         warning(wi, warningText.c_str());
3452     }
3453 }
3454
3455 void do_index(const char*                    mdparin,
3456               const char*                    ndx,
3457               gmx_mtop_t*                    mtop,
3458               bool                           bVerbose,
3459               const gmx::MDModulesNotifiers& mdModulesNotifiers,
3460               t_inputrec*                    ir,
3461               warninp_t                      wi)
3462 {
3463     t_blocka* defaultIndexGroups;
3464     int       natoms;
3465     t_symtab* symtab;
3466     t_atoms   atoms_all;
3467     char**    gnames;
3468     int       nr;
3469     real      tau_min;
3470     int       nstcmin;
3471     int       i, j, k, restnm;
3472     bool      bExcl, bTable, bAnneal;
3473     char      warn_buf[STRLEN];
3474
3475     if (bVerbose)
3476     {
3477         fprintf(stderr, "processing index file...\n");
3478     }
3479     if (ndx == nullptr)
3480     {
3481         snew(defaultIndexGroups, 1);
3482         snew(defaultIndexGroups->index, 1);
3483         snew(gnames, 1);
3484         atoms_all = gmx_mtop_global_atoms(*mtop);
3485         analyse(&atoms_all, defaultIndexGroups, &gnames, FALSE, TRUE);
3486         done_atom(&atoms_all);
3487     }
3488     else
3489     {
3490         defaultIndexGroups = init_index(ndx, &gnames);
3491     }
3492
3493     SimulationGroups* groups = &mtop->groups;
3494     natoms                   = mtop->natoms;
3495     symtab                   = &mtop->symtab;
3496
3497     for (int i = 0; (i < defaultIndexGroups->nr); i++)
3498     {
3499         groups->groupNames.emplace_back(put_symtab(symtab, gnames[i]));
3500     }
3501     groups->groupNames.emplace_back(put_symtab(symtab, "rest"));
3502     restnm = groups->groupNames.size() - 1;
3503     GMX_RELEASE_ASSERT(restnm == defaultIndexGroups->nr, "Size of allocations must match");
3504     srenew(gnames, defaultIndexGroups->nr + 1);
3505     gnames[restnm] = *(groups->groupNames.back());
3506
3507     set_warning_line(wi, mdparin, -1);
3508
3509     auto temperatureCouplingTauValues       = gmx::splitString(inputrecStrings->tau_t);
3510     auto temperatureCouplingReferenceValues = gmx::splitString(inputrecStrings->ref_t);
3511     auto temperatureCouplingGroupNames      = gmx::splitString(inputrecStrings->tcgrps);
3512     if (temperatureCouplingTauValues.size() != temperatureCouplingGroupNames.size()
3513         || temperatureCouplingReferenceValues.size() != temperatureCouplingGroupNames.size())
3514     {
3515         gmx_fatal(FARGS,
3516                   "Invalid T coupling input: %zu groups, %zu ref-t values and "
3517                   "%zu tau-t values",
3518                   temperatureCouplingGroupNames.size(),
3519                   temperatureCouplingReferenceValues.size(),
3520                   temperatureCouplingTauValues.size());
3521     }
3522
3523     const bool useReferenceTemperature = integratorHasReferenceTemperature(ir);
3524     do_numbering(natoms,
3525                  groups,
3526                  temperatureCouplingGroupNames,
3527                  defaultIndexGroups,
3528                  gnames,
3529                  SimulationAtomGroupType::TemperatureCoupling,
3530                  restnm,
3531                  useReferenceTemperature ? egrptpALL : egrptpALL_GENREST,
3532                  bVerbose,
3533                  wi);
3534     nr            = groups->groups[SimulationAtomGroupType::TemperatureCoupling].size();
3535     ir->opts.ngtc = nr;
3536     snew(ir->opts.nrdf, nr);
3537     snew(ir->opts.tau_t, nr);
3538     snew(ir->opts.ref_t, nr);
3539     if (ir->eI == IntegrationAlgorithm::BD && ir->bd_fric == 0)
3540     {
3541         fprintf(stderr, "bd-fric=0, so tau-t will be used as the inverse friction constant(s)\n");
3542     }
3543
3544     if (useReferenceTemperature)
3545     {
3546         if (size_t(nr) != temperatureCouplingReferenceValues.size())
3547         {
3548             gmx_fatal(FARGS, "Not enough ref-t and tau-t values!");
3549         }
3550
3551         tau_min = 1e20;
3552         convertReals(wi, temperatureCouplingTauValues, "tau-t", ir->opts.tau_t);
3553         for (i = 0; (i < nr); i++)
3554         {
3555             if ((ir->eI == IntegrationAlgorithm::BD) && ir->opts.tau_t[i] <= 0)
3556             {
3557                 sprintf(warn_buf,
3558                         "With integrator %s tau-t should be larger than 0",
3559                         enumValueToString(ir->eI));
3560                 warning_error(wi, warn_buf);
3561             }
3562
3563             if (ir->etc != TemperatureCoupling::VRescale && ir->opts.tau_t[i] == 0)
3564             {
3565                 warning_note(
3566                         wi,
3567                         "tau-t = -1 is the value to signal that a group should not have "
3568                         "temperature coupling. Treating your use of tau-t = 0 as if you used -1.");
3569             }
3570
3571             if (ir->opts.tau_t[i] >= 0)
3572             {
3573                 tau_min = std::min(tau_min, ir->opts.tau_t[i]);
3574             }
3575         }
3576         if (ir->etc != TemperatureCoupling::No && ir->nsttcouple == -1)
3577         {
3578             ir->nsttcouple = ir_optimal_nsttcouple(ir);
3579         }
3580
3581         if (EI_VV(ir->eI))
3582         {
3583             if ((ir->etc == TemperatureCoupling::NoseHoover) && (ir->epc == PressureCoupling::Berendsen))
3584             {
3585                 gmx_fatal(FARGS,
3586                           "Cannot do Nose-Hoover temperature with Berendsen pressure control with "
3587                           "md-vv; use either vrescale temperature with berendsen pressure or "
3588                           "Nose-Hoover temperature with MTTK pressure");
3589             }
3590             if (ir->epc == PressureCoupling::Mttk)
3591             {
3592                 if (ir->etc != TemperatureCoupling::NoseHoover)
3593                 {
3594                     gmx_fatal(FARGS,
3595                               "Cannot do MTTK pressure coupling without Nose-Hoover temperature "
3596                               "control");
3597                 }
3598                 else
3599                 {
3600                     if (ir->nstpcouple != ir->nsttcouple)
3601                     {
3602                         int mincouple  = std::min(ir->nstpcouple, ir->nsttcouple);
3603                         ir->nstpcouple = ir->nsttcouple = mincouple;
3604                         sprintf(warn_buf,
3605                                 "for current Trotter decomposition methods with vv, nsttcouple and "
3606                                 "nstpcouple must be equal.  Both have been reset to "
3607                                 "min(nsttcouple,nstpcouple) = %d",
3608                                 mincouple);
3609                         warning_note(wi, warn_buf);
3610                     }
3611                 }
3612             }
3613         }
3614         /* velocity verlet with averaged kinetic energy KE = 0.5*(v(t+1/2) - v(t-1/2)) is implemented
3615            primarily for testing purposes, and does not work with temperature coupling other than 1 */
3616
3617         if (ETC_ANDERSEN(ir->etc))
3618         {
3619             if (ir->nsttcouple != 1)
3620             {
3621                 ir->nsttcouple = 1;
3622                 sprintf(warn_buf,
3623                         "Andersen temperature control methods assume nsttcouple = 1; there is no "
3624                         "need for larger nsttcouple > 1, since no global parameters are computed. "
3625                         "nsttcouple has been reset to 1");
3626                 warning_note(wi, warn_buf);
3627             }
3628         }
3629         nstcmin = tcouple_min_integration_steps(ir->etc);
3630         if (nstcmin > 1)
3631         {
3632             if (tau_min / (ir->delta_t * ir->nsttcouple) < nstcmin - 10 * GMX_REAL_EPS)
3633             {
3634                 sprintf(warn_buf,
3635                         "For proper integration of the %s thermostat, tau-t (%g) should be at "
3636                         "least %d times larger than nsttcouple*dt (%g)",
3637                         enumValueToString(ir->etc),
3638                         tau_min,
3639                         nstcmin,
3640                         ir->nsttcouple * ir->delta_t);
3641                 warning(wi, warn_buf);
3642             }
3643         }
3644         convertReals(wi, temperatureCouplingReferenceValues, "ref-t", ir->opts.ref_t);
3645         for (i = 0; (i < nr); i++)
3646         {
3647             if (ir->opts.ref_t[i] < 0)
3648             {
3649                 gmx_fatal(FARGS, "ref-t for group %d negative", i);
3650             }
3651         }
3652         /* set the lambda mc temperature to the md integrator temperature (which should be defined
3653            if we are in this conditional) if mc_temp is negative */
3654         if (ir->expandedvals->mc_temp < 0)
3655         {
3656             ir->expandedvals->mc_temp = ir->opts.ref_t[0]; /*for now, set to the first reft */
3657         }
3658     }
3659
3660     /* Simulated annealing for each group. There are nr groups */
3661     auto simulatedAnnealingGroupNames = gmx::splitString(inputrecStrings->anneal);
3662     if (simulatedAnnealingGroupNames.size() == 1
3663         && gmx::equalCaseInsensitive(simulatedAnnealingGroupNames[0], "N", 1))
3664     {
3665         simulatedAnnealingGroupNames.resize(0);
3666     }
3667     if (!simulatedAnnealingGroupNames.empty() && gmx::ssize(simulatedAnnealingGroupNames) != nr)
3668     {
3669         gmx_fatal(FARGS,
3670                   "Wrong number of annealing values: %zu (for %d groups)\n",
3671                   simulatedAnnealingGroupNames.size(),
3672                   nr);
3673     }
3674     else
3675     {
3676         snew(ir->opts.annealing, nr);
3677         snew(ir->opts.anneal_npoints, nr);
3678         snew(ir->opts.anneal_time, nr);
3679         snew(ir->opts.anneal_temp, nr);
3680         for (i = 0; i < nr; i++)
3681         {
3682             ir->opts.annealing[i]      = SimulatedAnnealing::No;
3683             ir->opts.anneal_npoints[i] = 0;
3684             ir->opts.anneal_time[i]    = nullptr;
3685             ir->opts.anneal_temp[i]    = nullptr;
3686         }
3687         if (!simulatedAnnealingGroupNames.empty())
3688         {
3689             bAnneal = FALSE;
3690             for (i = 0; i < nr; i++)
3691             {
3692                 if (gmx::equalCaseInsensitive(simulatedAnnealingGroupNames[i], "N", 1))
3693                 {
3694                     ir->opts.annealing[i] = SimulatedAnnealing::No;
3695                 }
3696                 else if (gmx::equalCaseInsensitive(simulatedAnnealingGroupNames[i], "S", 1))
3697                 {
3698                     ir->opts.annealing[i] = SimulatedAnnealing::Single;
3699                     bAnneal               = TRUE;
3700                 }
3701                 else if (gmx::equalCaseInsensitive(simulatedAnnealingGroupNames[i], "P", 1))
3702                 {
3703                     ir->opts.annealing[i] = SimulatedAnnealing::Periodic;
3704                     bAnneal               = TRUE;
3705                 }
3706             }
3707             if (bAnneal)
3708             {
3709                 /* Read the other fields too */
3710                 auto simulatedAnnealingPoints = gmx::splitString(inputrecStrings->anneal_npoints);
3711                 if (simulatedAnnealingPoints.size() != simulatedAnnealingGroupNames.size())
3712                 {
3713                     gmx_fatal(FARGS,
3714                               "Found %zu annealing-npoints values for %zu groups\n",
3715                               simulatedAnnealingPoints.size(),
3716                               simulatedAnnealingGroupNames.size());
3717                 }
3718                 convertInts(wi, simulatedAnnealingPoints, "annealing points", ir->opts.anneal_npoints);
3719                 size_t numSimulatedAnnealingFields = 0;
3720                 for (i = 0; i < nr; i++)
3721                 {
3722                     if (ir->opts.anneal_npoints[i] == 1)
3723                     {
3724                         gmx_fatal(
3725                                 FARGS,
3726                                 "Please specify at least a start and an end point for annealing\n");
3727                     }
3728                     snew(ir->opts.anneal_time[i], ir->opts.anneal_npoints[i]);
3729                     snew(ir->opts.anneal_temp[i], ir->opts.anneal_npoints[i]);
3730                     numSimulatedAnnealingFields += ir->opts.anneal_npoints[i];
3731                 }
3732
3733                 auto simulatedAnnealingTimes = gmx::splitString(inputrecStrings->anneal_time);
3734
3735                 if (simulatedAnnealingTimes.size() != numSimulatedAnnealingFields)
3736                 {
3737                     gmx_fatal(FARGS,
3738                               "Found %zu annealing-time values, wanted %zu\n",
3739                               simulatedAnnealingTimes.size(),
3740                               numSimulatedAnnealingFields);
3741                 }
3742                 auto simulatedAnnealingTemperatures = gmx::splitString(inputrecStrings->anneal_temp);
3743                 if (simulatedAnnealingTemperatures.size() != numSimulatedAnnealingFields)
3744                 {
3745                     gmx_fatal(FARGS,
3746                               "Found %zu annealing-temp values, wanted %zu\n",
3747                               simulatedAnnealingTemperatures.size(),
3748                               numSimulatedAnnealingFields);
3749                 }
3750
3751                 std::vector<real> allSimulatedAnnealingTimes(numSimulatedAnnealingFields);
3752                 std::vector<real> allSimulatedAnnealingTemperatures(numSimulatedAnnealingFields);
3753                 convertReals(wi, simulatedAnnealingTimes, "anneal-time", allSimulatedAnnealingTimes.data());
3754                 convertReals(wi,
3755                              simulatedAnnealingTemperatures,
3756                              "anneal-temp",
3757                              allSimulatedAnnealingTemperatures.data());
3758                 for (i = 0, k = 0; i < nr; i++)
3759                 {
3760                     for (j = 0; j < ir->opts.anneal_npoints[i]; j++)
3761                     {
3762                         ir->opts.anneal_time[i][j] = allSimulatedAnnealingTimes[k];
3763                         ir->opts.anneal_temp[i][j] = allSimulatedAnnealingTemperatures[k];
3764                         if (j == 0)
3765                         {
3766                             if (ir->opts.anneal_time[i][0] > (ir->init_t + GMX_REAL_EPS))
3767                             {
3768                                 gmx_fatal(FARGS, "First time point for annealing > init_t.\n");
3769                             }
3770                         }
3771                         else
3772                         {
3773                             /* j>0 */
3774                             if (ir->opts.anneal_time[i][j] < ir->opts.anneal_time[i][j - 1])
3775                             {
3776                                 gmx_fatal(FARGS,
3777                                           "Annealing timepoints out of order: t=%f comes after "
3778                                           "t=%f\n",
3779                                           ir->opts.anneal_time[i][j],
3780                                           ir->opts.anneal_time[i][j - 1]);
3781                             }
3782                         }
3783                         if (ir->opts.anneal_temp[i][j] < 0)
3784                         {
3785                             gmx_fatal(FARGS,
3786                                       "Found negative temperature in annealing: %f\n",
3787                                       ir->opts.anneal_temp[i][j]);
3788                         }
3789                         k++;
3790                     }
3791                 }
3792                 /* Print out some summary information, to make sure we got it right */
3793                 for (i = 0; i < nr; i++)
3794                 {
3795                     if (ir->opts.annealing[i] != SimulatedAnnealing::No)
3796                     {
3797                         j = groups->groups[SimulationAtomGroupType::TemperatureCoupling][i];
3798                         fprintf(stderr,
3799                                 "Simulated annealing for group %s: %s, %d timepoints\n",
3800                                 *(groups->groupNames[j]),
3801                                 enumValueToString(ir->opts.annealing[i]),
3802                                 ir->opts.anneal_npoints[i]);
3803                         fprintf(stderr, "Time (ps)   Temperature (K)\n");
3804                         /* All terms except the last one */
3805                         for (j = 0; j < (ir->opts.anneal_npoints[i] - 1); j++)
3806                         {
3807                             fprintf(stderr,
3808                                     "%9.1f      %5.1f\n",
3809                                     ir->opts.anneal_time[i][j],
3810                                     ir->opts.anneal_temp[i][j]);
3811                         }
3812
3813                         /* Finally the last one */
3814                         j = ir->opts.anneal_npoints[i] - 1;
3815                         if (ir->opts.annealing[i] == SimulatedAnnealing::Single)
3816                         {
3817                             fprintf(stderr,
3818                                     "%9.1f-     %5.1f\n",
3819                                     ir->opts.anneal_time[i][j],
3820                                     ir->opts.anneal_temp[i][j]);
3821                         }
3822                         else
3823                         {
3824                             fprintf(stderr,
3825                                     "%9.1f      %5.1f\n",
3826                                     ir->opts.anneal_time[i][j],
3827                                     ir->opts.anneal_temp[i][j]);
3828                             if (std::fabs(ir->opts.anneal_temp[i][j] - ir->opts.anneal_temp[i][0]) > GMX_REAL_EPS)
3829                             {
3830                                 warning_note(wi,
3831                                              "There is a temperature jump when your annealing "
3832                                              "loops back.\n");
3833                             }
3834                         }
3835                     }
3836                 }
3837             }
3838         }
3839     }
3840
3841     if (ir->bPull)
3842     {
3843         for (int i = 1; i < ir->pull->ngroup; i++)
3844         {
3845             const int gid = search_string(
3846                     inputrecStrings->pullGroupNames[i].c_str(), defaultIndexGroups->nr, gnames);
3847             GMX_ASSERT(defaultIndexGroups, "Must have initialized default index groups");
3848             atomGroupRangeValidation(natoms, gid, *defaultIndexGroups);
3849         }
3850
3851         process_pull_groups(ir->pull->group, inputrecStrings->pullGroupNames, defaultIndexGroups, gnames);
3852
3853         checkPullCoords(ir->pull->group, ir->pull->coord);
3854     }
3855
3856     if (ir->bRot)
3857     {
3858         make_rotation_groups(ir->rot, inputrecStrings->rotateGroupNames, defaultIndexGroups, gnames);
3859     }
3860
3861     if (ir->eSwapCoords != SwapType::No)
3862     {
3863         make_swap_groups(ir->swap, defaultIndexGroups, gnames);
3864     }
3865
3866     /* Make indices for IMD session */
3867     if (ir->bIMD)
3868     {
3869         make_IMD_group(ir->imd, inputrecStrings->imd_grp, defaultIndexGroups, gnames);
3870     }
3871
3872     gmx::IndexGroupsAndNames defaultIndexGroupsAndNames(
3873             *defaultIndexGroups, gmx::arrayRefFromArray(gnames, defaultIndexGroups->nr));
3874     mdModulesNotifiers.preProcessingNotifier_.notify(defaultIndexGroupsAndNames);
3875
3876     auto freezeDims       = gmx::splitString(inputrecStrings->frdim);
3877     auto freezeGroupNames = gmx::splitString(inputrecStrings->freeze);
3878     if (freezeDims.size() != DIM * freezeGroupNames.size())
3879     {
3880         gmx_fatal(FARGS,
3881                   "Invalid Freezing input: %zu groups and %zu freeze values",
3882                   freezeGroupNames.size(),
3883                   freezeDims.size());
3884     }
3885     do_numbering(natoms,
3886                  groups,
3887                  freezeGroupNames,
3888                  defaultIndexGroups,
3889                  gnames,
3890                  SimulationAtomGroupType::Freeze,
3891                  restnm,
3892                  egrptpALL_GENREST,
3893                  bVerbose,
3894                  wi);
3895     nr             = groups->groups[SimulationAtomGroupType::Freeze].size();
3896     ir->opts.ngfrz = nr;
3897     snew(ir->opts.nFreeze, nr);
3898     for (i = k = 0; (size_t(i) < freezeGroupNames.size()); i++)
3899     {
3900         for (j = 0; (j < DIM); j++, k++)
3901         {
3902             ir->opts.nFreeze[i][j] = static_cast<int>(gmx::equalCaseInsensitive(freezeDims[k], "Y", 1));
3903             if (!ir->opts.nFreeze[i][j])
3904             {
3905                 if (!gmx::equalCaseInsensitive(freezeDims[k], "N", 1))
3906                 {
3907                     sprintf(warn_buf,
3908                             "Please use Y(ES) or N(O) for freezedim only "
3909                             "(not %s)",
3910                             freezeDims[k].c_str());
3911                     warning(wi, warn_buf);
3912                 }
3913             }
3914         }
3915     }
3916     for (; (i < nr); i++)
3917     {
3918         for (j = 0; (j < DIM); j++)
3919         {
3920             ir->opts.nFreeze[i][j] = 0;
3921         }
3922     }
3923
3924     auto energyGroupNames = gmx::splitString(inputrecStrings->energy);
3925     do_numbering(natoms,
3926                  groups,
3927                  energyGroupNames,
3928                  defaultIndexGroups,
3929                  gnames,
3930                  SimulationAtomGroupType::EnergyOutput,
3931                  restnm,
3932                  egrptpALL_GENREST,
3933                  bVerbose,
3934                  wi);
3935     add_wall_energrps(groups, ir->nwall, symtab);
3936     ir->opts.ngener    = groups->groups[SimulationAtomGroupType::EnergyOutput].size();
3937     auto vcmGroupNames = gmx::splitString(inputrecStrings->vcm);
3938     do_numbering(natoms,
3939                  groups,
3940                  vcmGroupNames,
3941                  defaultIndexGroups,
3942                  gnames,
3943                  SimulationAtomGroupType::MassCenterVelocityRemoval,
3944                  restnm,
3945                  vcmGroupNames.empty() ? egrptpALL_GENREST : egrptpPART,
3946                  bVerbose,
3947                  wi);
3948
3949     if (ir->comm_mode != ComRemovalAlgorithm::No)
3950     {
3951         checkAndUpdateVcmFreezeGroupConsistency(groups, natoms, ir->opts, wi);
3952     }
3953
3954     /* Now we have filled the freeze struct, so we can calculate NRDF */
3955     calc_nrdf(mtop, ir, gnames);
3956
3957     auto user1GroupNames = gmx::splitString(inputrecStrings->user1);
3958     do_numbering(natoms,
3959                  groups,
3960                  user1GroupNames,
3961                  defaultIndexGroups,
3962                  gnames,
3963                  SimulationAtomGroupType::User1,
3964                  restnm,
3965                  egrptpALL_GENREST,
3966                  bVerbose,
3967                  wi);
3968     auto user2GroupNames = gmx::splitString(inputrecStrings->user2);
3969     do_numbering(natoms,
3970                  groups,
3971                  user2GroupNames,
3972                  defaultIndexGroups,
3973                  gnames,
3974                  SimulationAtomGroupType::User2,
3975                  restnm,
3976                  egrptpALL_GENREST,
3977                  bVerbose,
3978                  wi);
3979     auto compressedXGroupNames = gmx::splitString(inputrecStrings->x_compressed_groups);
3980     do_numbering(natoms,
3981                  groups,
3982                  compressedXGroupNames,
3983                  defaultIndexGroups,
3984                  gnames,
3985                  SimulationAtomGroupType::CompressedPositionOutput,
3986                  restnm,
3987                  egrptpONE,
3988                  bVerbose,
3989                  wi);
3990     auto orirefFitGroupNames = gmx::splitString(inputrecStrings->orirefitgrp);
3991     do_numbering(natoms,
3992                  groups,
3993                  orirefFitGroupNames,
3994                  defaultIndexGroups,
3995                  gnames,
3996                  SimulationAtomGroupType::OrientationRestraintsFit,
3997                  restnm,
3998                  egrptpALL_GENREST,
3999                  bVerbose,
4000                  wi);
4001
4002     /* MiMiC QMMM input processing */
4003     auto qmGroupNames = gmx::splitString(inputrecStrings->QMMM);
4004     if (qmGroupNames.size() > 1)
4005     {
4006         gmx_fatal(FARGS, "Currently, having more than one QM group in MiMiC is not supported");
4007     }
4008     /* group rest, if any, is always MM! */
4009     do_numbering(natoms,
4010                  groups,
4011                  qmGroupNames,
4012                  defaultIndexGroups,
4013                  gnames,
4014                  SimulationAtomGroupType::QuantumMechanics,
4015                  restnm,
4016                  egrptpALL_GENREST,
4017                  bVerbose,
4018                  wi);
4019     ir->opts.ngQM = qmGroupNames.size();
4020
4021     /* end of MiMiC QMMM input */
4022
4023     if (bVerbose)
4024     {
4025         for (auto group : gmx::keysOf(groups->groups))
4026         {
4027             fprintf(stderr, "%-16s has %zu element(s):", shortName(group), groups->groups[group].size());
4028             for (const auto& entry : groups->groups[group])
4029             {
4030                 fprintf(stderr, " %s", *(groups->groupNames[entry]));
4031             }
4032             fprintf(stderr, "\n");
4033         }
4034     }
4035
4036     nr = groups->groups[SimulationAtomGroupType::EnergyOutput].size();
4037     snew(ir->opts.egp_flags, nr * nr);
4038
4039     bExcl = do_egp_flag(ir, groups, "energygrp-excl", inputrecStrings->egpexcl, EGP_EXCL);
4040     if (bExcl && ir->cutoff_scheme == CutoffScheme::Verlet)
4041     {
4042         warning_error(wi, "Energy group exclusions are currently not supported");
4043     }
4044     if (bExcl && EEL_FULL(ir->coulombtype))
4045     {
4046         warning(wi, "Can not exclude the lattice Coulomb energy between energy groups");
4047     }
4048
4049     bTable = do_egp_flag(ir, groups, "energygrp-table", inputrecStrings->egptable, EGP_TABLE);
4050     if (bTable && !(ir->vdwtype == VanDerWaalsType::User)
4051         && !(ir->coulombtype == CoulombInteractionType::User)
4052         && !(ir->coulombtype == CoulombInteractionType::PmeUser)
4053         && !(ir->coulombtype == CoulombInteractionType::PmeUserSwitch))
4054     {
4055         gmx_fatal(FARGS,
4056                   "Can only have energy group pair tables in combination with user tables for VdW "
4057                   "and/or Coulomb");
4058     }
4059
4060     /* final check before going out of scope if simulated tempering variables
4061      * need to be set to default values.
4062      */
4063     if ((ir->expandedvals->nstexpanded < 0) && ir->bSimTemp)
4064     {
4065         ir->expandedvals->nstexpanded = 2 * static_cast<int>(ir->opts.tau_t[0] / ir->delta_t);
4066         warning(wi,
4067                 gmx::formatString(
4068                         "the value for nstexpanded was not specified for "
4069                         " expanded ensemble simulated tempering. It is set to 2*tau_t (%d) "
4070                         "by default, but it is recommended to set it to an explicit value!",
4071                         ir->expandedvals->nstexpanded));
4072     }
4073     for (i = 0; (i < defaultIndexGroups->nr); i++)
4074     {
4075         sfree(gnames[i]);
4076     }
4077     sfree(gnames);
4078     done_blocka(defaultIndexGroups);
4079     sfree(defaultIndexGroups);
4080 }
4081
4082
4083 static void check_disre(const gmx_mtop_t& mtop)
4084 {
4085     if (gmx_mtop_ftype_count(mtop, F_DISRES) > 0)
4086     {
4087         const gmx_ffparams_t& ffparams  = mtop.ffparams;
4088         int                   ndouble   = 0;
4089         int                   old_label = -1;
4090         for (int i = 0; i < ffparams.numTypes(); i++)
4091         {
4092             int ftype = ffparams.functype[i];
4093             if (ftype == F_DISRES)
4094             {
4095                 int label = ffparams.iparams[i].disres.label;
4096                 if (label == old_label)
4097                 {
4098                     fprintf(stderr, "Distance restraint index %d occurs twice\n", label);
4099                     ndouble++;
4100                 }
4101                 old_label = label;
4102             }
4103         }
4104         if (ndouble > 0)
4105         {
4106             gmx_fatal(FARGS,
4107                       "Found %d double distance restraint indices,\n"
4108                       "probably the parameters for multiple pairs in one restraint "
4109                       "are not identical\n",
4110                       ndouble);
4111         }
4112     }
4113 }
4114
4115 static bool absolute_reference(const t_inputrec* ir, const gmx_mtop_t& sys, const bool posres_only, ivec AbsRef)
4116 {
4117     int              d, g, i;
4118     const t_iparams* pr;
4119
4120     clear_ivec(AbsRef);
4121
4122     if (!posres_only)
4123     {
4124         /* Check the COM */
4125         for (d = 0; d < DIM; d++)
4126         {
4127             AbsRef[d] = (d < ndof_com(ir) ? 0 : 1);
4128         }
4129         /* Check for freeze groups */
4130         for (g = 0; g < ir->opts.ngfrz; g++)
4131         {
4132             for (d = 0; d < DIM; d++)
4133             {
4134                 if (ir->opts.nFreeze[g][d] != 0)
4135                 {
4136                     AbsRef[d] = 1;
4137                 }
4138             }
4139         }
4140     }
4141
4142     /* Check for position restraints */
4143     for (const auto ilist : IListRange(sys))
4144     {
4145         if (ilist.nmol() > 0 && (AbsRef[XX] == 0 || AbsRef[YY] == 0 || AbsRef[ZZ] == 0))
4146         {
4147             for (int i = 0; i < ilist.list()[F_POSRES].size(); i += 2)
4148             {
4149                 pr = &sys.ffparams.iparams[ilist.list()[F_POSRES].iatoms[i]];
4150                 for (d = 0; d < DIM; d++)
4151                 {
4152                     if (pr->posres.fcA[d] != 0)
4153                     {
4154                         AbsRef[d] = 1;
4155                     }
4156                 }
4157             }
4158             for (i = 0; i < ilist.list()[F_FBPOSRES].size(); i += 2)
4159             {
4160                 /* Check for flat-bottom posres */
4161                 pr = &sys.ffparams.iparams[ilist.list()[F_FBPOSRES].iatoms[i]];
4162                 if (pr->fbposres.k != 0)
4163                 {
4164                     switch (pr->fbposres.geom)
4165                     {
4166                         case efbposresSPHERE: AbsRef[XX] = AbsRef[YY] = AbsRef[ZZ] = 1; break;
4167                         case efbposresCYLINDERX: AbsRef[YY] = AbsRef[ZZ] = 1; break;
4168                         case efbposresCYLINDERY: AbsRef[XX] = AbsRef[ZZ] = 1; break;
4169                         case efbposresCYLINDER:
4170                         /* efbposres is a synonym for efbposresCYLINDERZ for backwards compatibility */
4171                         case efbposresCYLINDERZ: AbsRef[XX] = AbsRef[YY] = 1; break;
4172                         case efbposresX: /* d=XX */
4173                         case efbposresY: /* d=YY */
4174                         case efbposresZ: /* d=ZZ */
4175                             d         = pr->fbposres.geom - efbposresX;
4176                             AbsRef[d] = 1;
4177                             break;
4178                         default:
4179                             gmx_fatal(FARGS,
4180                                       " Invalid geometry for flat-bottom position restraint.\n"
4181                                       "Expected nr between 1 and %d. Found %d\n",
4182                                       efbposresNR - 1,
4183                                       pr->fbposres.geom);
4184                     }
4185                 }
4186             }
4187         }
4188     }
4189
4190     return (AbsRef[XX] != 0 && AbsRef[YY] != 0 && AbsRef[ZZ] != 0);
4191 }
4192
4193 static void check_combination_rule_differences(const gmx_mtop_t& mtop,
4194                                                int               state,
4195                                                bool* bC6ParametersWorkWithGeometricRules,
4196                                                bool* bC6ParametersWorkWithLBRules,
4197                                                bool* bLBRulesPossible)
4198 {
4199     int         ntypes, tpi, tpj;
4200     int*        typecount;
4201     real        tol;
4202     double      c6i, c6j, c12i, c12j;
4203     double      c6, c6_geometric, c6_LB;
4204     double      sigmai, sigmaj, epsi, epsj;
4205     bool        bCanDoLBRules, bCanDoGeometricRules;
4206     const char* ptr;
4207
4208     /* A tolerance of 1e-5 seems reasonable for (possibly hand-typed)
4209      * force-field floating point parameters.
4210      */
4211     tol = 1e-5;
4212     ptr = getenv("GMX_LJCOMB_TOL");
4213     if (ptr != nullptr)
4214     {
4215         double dbl;
4216         double gmx_unused canary;
4217
4218         if (sscanf(ptr, "%lf%lf", &dbl, &canary) != 1)
4219         {
4220             gmx_fatal(
4221                     FARGS, "Could not parse a single floating-point number from GMX_LJCOMB_TOL (%s)", ptr);
4222         }
4223         tol = dbl;
4224     }
4225
4226     *bC6ParametersWorkWithLBRules        = TRUE;
4227     *bC6ParametersWorkWithGeometricRules = TRUE;
4228     bCanDoLBRules                        = TRUE;
4229     ntypes                               = mtop.ffparams.atnr;
4230     snew(typecount, ntypes);
4231     gmx_mtop_count_atomtypes(mtop, state, typecount);
4232     *bLBRulesPossible = TRUE;
4233     for (tpi = 0; tpi < ntypes; ++tpi)
4234     {
4235         c6i  = mtop.ffparams.iparams[(ntypes + 1) * tpi].lj.c6;
4236         c12i = mtop.ffparams.iparams[(ntypes + 1) * tpi].lj.c12;
4237         for (tpj = tpi; tpj < ntypes; ++tpj)
4238         {
4239             c6j          = mtop.ffparams.iparams[(ntypes + 1) * tpj].lj.c6;
4240             c12j         = mtop.ffparams.iparams[(ntypes + 1) * tpj].lj.c12;
4241             c6           = mtop.ffparams.iparams[ntypes * tpi + tpj].lj.c6;
4242             c6_geometric = std::sqrt(c6i * c6j);
4243             if (!gmx_numzero(c6_geometric))
4244             {
4245                 if (!gmx_numzero(c12i) && !gmx_numzero(c12j))
4246                 {
4247                     sigmai = gmx::sixthroot(c12i / c6i);
4248                     sigmaj = gmx::sixthroot(c12j / c6j);
4249                     epsi   = c6i * c6i / (4.0 * c12i);
4250                     epsj   = c6j * c6j / (4.0 * c12j);
4251                     c6_LB  = 4.0 * std::sqrt(epsi * epsj) * gmx::power6(0.5 * (sigmai + sigmaj));
4252                 }
4253                 else
4254                 {
4255                     *bLBRulesPossible = FALSE;
4256                     c6_LB             = c6_geometric;
4257                 }
4258                 bCanDoLBRules = gmx_within_tol(c6_LB, c6, tol);
4259             }
4260
4261             if (!bCanDoLBRules)
4262             {
4263                 *bC6ParametersWorkWithLBRules = FALSE;
4264             }
4265
4266             bCanDoGeometricRules = gmx_within_tol(c6_geometric, c6, tol);
4267
4268             if (!bCanDoGeometricRules)
4269             {
4270                 *bC6ParametersWorkWithGeometricRules = FALSE;
4271             }
4272         }
4273     }
4274     sfree(typecount);
4275 }
4276
4277 static void check_combination_rules(const t_inputrec* ir, const gmx_mtop_t& mtop, warninp_t wi)
4278 {
4279     bool bLBRulesPossible, bC6ParametersWorkWithGeometricRules, bC6ParametersWorkWithLBRules;
4280
4281     check_combination_rule_differences(
4282             mtop, 0, &bC6ParametersWorkWithGeometricRules, &bC6ParametersWorkWithLBRules, &bLBRulesPossible);
4283     if (ir->ljpme_combination_rule == LongRangeVdW::LB)
4284     {
4285         if (!bC6ParametersWorkWithLBRules || !bLBRulesPossible)
4286         {
4287             warning(wi,
4288                     "You are using arithmetic-geometric combination rules "
4289                     "in LJ-PME, but your non-bonded C6 parameters do not "
4290                     "follow these rules.");
4291         }
4292     }
4293     else
4294     {
4295         if (!bC6ParametersWorkWithGeometricRules)
4296         {
4297             if (ir->eDispCorr != DispersionCorrectionType::No)
4298             {
4299                 warning_note(wi,
4300                              "You are using geometric combination rules in "
4301                              "LJ-PME, but your non-bonded C6 parameters do "
4302                              "not follow these rules. "
4303                              "This will introduce very small errors in the forces and energies in "
4304                              "your simulations. Dispersion correction will correct total energy "
4305                              "and/or pressure for isotropic systems, but not forces or surface "
4306                              "tensions.");
4307             }
4308             else
4309             {
4310                 warning_note(wi,
4311                              "You are using geometric combination rules in "
4312                              "LJ-PME, but your non-bonded C6 parameters do "
4313                              "not follow these rules. "
4314                              "This will introduce very small errors in the forces and energies in "
4315                              "your simulations. If your system is homogeneous, consider using "
4316                              "dispersion correction "
4317                              "for the total energy and pressure.");
4318             }
4319         }
4320     }
4321 }
4322
4323 void triple_check(const char* mdparin, t_inputrec* ir, gmx_mtop_t* sys, warninp_t wi)
4324 {
4325     // Not meeting MTS requirements should have resulted in a fatal error, so we can assert here
4326     GMX_ASSERT(gmx::checkMtsRequirements(*ir).empty(), "All MTS requirements should be met here");
4327
4328     char                      err_buf[STRLEN];
4329     int                       i, m, c, nmol;
4330     bool                      bCharge;
4331     gmx_mtop_atomloop_block_t aloopb;
4332     ivec                      AbsRef;
4333     char                      warn_buf[STRLEN];
4334
4335     set_warning_line(wi, mdparin, -1);
4336
4337     if (absolute_reference(ir, *sys, false, AbsRef))
4338     {
4339         warning_note(wi,
4340                      "Removing center of mass motion in the presence of position restraints might "
4341                      "cause artifacts. When you are using position restraints to equilibrate a "
4342                      "macro-molecule, the artifacts are usually negligible.");
4343     }
4344
4345     if (ir->cutoff_scheme == CutoffScheme::Verlet && ir->verletbuf_tol > 0 && ir->nstlist > 1
4346         && ((EI_MD(ir->eI) || EI_SD(ir->eI))
4347             && (ir->etc == TemperatureCoupling::VRescale || ir->etc == TemperatureCoupling::Berendsen)))
4348     {
4349         /* Check if a too small Verlet buffer might potentially
4350          * cause more drift than the thermostat can couple off.
4351          */
4352         /* Temperature error fraction for warning and suggestion */
4353         const real T_error_warn    = 0.002;
4354         const real T_error_suggest = 0.001;
4355         /* For safety: 2 DOF per atom (typical with constraints) */
4356         const real nrdf_at = 2;
4357         real       T, tau, max_T_error;
4358         int        i;
4359
4360         T   = 0;
4361         tau = 0;
4362         for (i = 0; i < ir->opts.ngtc; i++)
4363         {
4364             T   = std::max(T, ir->opts.ref_t[i]);
4365             tau = std::max(tau, ir->opts.tau_t[i]);
4366         }
4367         if (T > 0)
4368         {
4369             /* This is a worst case estimate of the temperature error,
4370              * assuming perfect buffer estimation and no cancelation
4371              * of errors. The factor 0.5 is because energy distributes
4372              * equally over Ekin and Epot.
4373              */
4374             max_T_error = 0.5 * tau * ir->verletbuf_tol / (nrdf_at * gmx::c_boltz * T);
4375             if (max_T_error > T_error_warn)
4376             {
4377                 sprintf(warn_buf,
4378                         "With a verlet-buffer-tolerance of %g kJ/mol/ps, a reference temperature "
4379                         "of %g and a tau_t of %g, your temperature might be off by up to %.1f%%. "
4380                         "To ensure the error is below %.1f%%, decrease verlet-buffer-tolerance to "
4381                         "%.0e or decrease tau_t.",
4382                         ir->verletbuf_tol,
4383                         T,
4384                         tau,
4385                         100 * max_T_error,
4386                         100 * T_error_suggest,
4387                         ir->verletbuf_tol * T_error_suggest / max_T_error);
4388                 warning(wi, warn_buf);
4389             }
4390         }
4391     }
4392
4393     if (ETC_ANDERSEN(ir->etc))
4394     {
4395         int i;
4396
4397         for (i = 0; i < ir->opts.ngtc; i++)
4398         {
4399             sprintf(err_buf,
4400                     "all tau_t must currently be equal using Andersen temperature control, "
4401                     "violated for group %d",
4402                     i);
4403             CHECK(ir->opts.tau_t[0] != ir->opts.tau_t[i]);
4404             sprintf(err_buf,
4405                     "all tau_t must be positive using Andersen temperature control, "
4406                     "tau_t[%d]=%10.6f",
4407                     i,
4408                     ir->opts.tau_t[i]);
4409             CHECK(ir->opts.tau_t[i] < 0);
4410         }
4411
4412         if (ir->etc == TemperatureCoupling::AndersenMassive && ir->comm_mode != ComRemovalAlgorithm::No)
4413         {
4414             for (i = 0; i < ir->opts.ngtc; i++)
4415             {
4416                 int nsteps = gmx::roundToInt(ir->opts.tau_t[i] / ir->delta_t);
4417                 sprintf(err_buf,
4418                         "tau_t/delta_t for group %d for temperature control method %s must be a "
4419                         "multiple of nstcomm (%d), as velocities of atoms in coupled groups are "
4420                         "randomized every time step. The input tau_t (%8.3f) leads to %d steps per "
4421                         "randomization",
4422                         i,
4423                         enumValueToString(ir->etc),
4424                         ir->nstcomm,
4425                         ir->opts.tau_t[i],
4426                         nsteps);
4427                 CHECK(nsteps % ir->nstcomm != 0);
4428             }
4429         }
4430     }
4431
4432     if (EI_DYNAMICS(ir->eI) && !EI_SD(ir->eI) && ir->eI != IntegrationAlgorithm::BD
4433         && ir->comm_mode == ComRemovalAlgorithm::No
4434         && !(absolute_reference(ir, *sys, FALSE, AbsRef) || ir->nsteps <= 10) && !ETC_ANDERSEN(ir->etc))
4435     {
4436         warning(wi,
4437                 "You are not using center of mass motion removal (mdp option comm-mode), numerical "
4438                 "rounding errors can lead to build up of kinetic energy of the center of mass");
4439     }
4440
4441     if (ir->epc == PressureCoupling::ParrinelloRahman && ir->etc == TemperatureCoupling::NoseHoover)
4442     {
4443         real tau_t_max = 0;
4444         for (int g = 0; g < ir->opts.ngtc; g++)
4445         {
4446             tau_t_max = std::max(tau_t_max, ir->opts.tau_t[g]);
4447         }
4448         if (ir->tau_p < 1.9 * tau_t_max)
4449         {
4450             std::string message = gmx::formatString(
4451                     "With %s T-coupling and %s p-coupling, "
4452                     "%s (%g) should be at least twice as large as %s (%g) to avoid resonances",
4453                     enumValueToString(ir->etc),
4454                     enumValueToString(ir->epc),
4455                     "tau-p",
4456                     ir->tau_p,
4457                     "tau-t",
4458                     tau_t_max);
4459             warning(wi, message.c_str());
4460         }
4461     }
4462
4463     /* Check for pressure coupling with absolute position restraints */
4464     if (ir->epc != PressureCoupling::No && ir->refcoord_scaling == RefCoordScaling::No)
4465     {
4466         absolute_reference(ir, *sys, TRUE, AbsRef);
4467         {
4468             for (m = 0; m < DIM; m++)
4469             {
4470                 if (AbsRef[m] && norm2(ir->compress[m]) > 0)
4471                 {
4472                     warning(wi,
4473                             "You are using pressure coupling with absolute position restraints, "
4474                             "this will give artifacts. Use the refcoord_scaling option.");
4475                     break;
4476                 }
4477             }
4478         }
4479     }
4480
4481     bCharge = FALSE;
4482     aloopb  = gmx_mtop_atomloop_block_init(*sys);
4483     const t_atom* atom;
4484     while (gmx_mtop_atomloop_block_next(aloopb, &atom, &nmol))
4485     {
4486         if (atom->q != 0 || atom->qB != 0)
4487         {
4488             bCharge = TRUE;
4489         }
4490     }
4491
4492     if (!bCharge)
4493     {
4494         if (EEL_FULL(ir->coulombtype))
4495         {
4496             sprintf(err_buf,
4497                     "You are using full electrostatics treatment %s for a system without charges.\n"
4498                     "This costs a lot of performance for just processing zeros, consider using %s "
4499                     "instead.\n",
4500                     enumValueToString(ir->coulombtype),
4501                     enumValueToString(CoulombInteractionType::Cut));
4502             warning(wi, err_buf);
4503         }
4504     }
4505     else
4506     {
4507         if (ir->coulombtype == CoulombInteractionType::Cut && ir->rcoulomb > 0)
4508         {
4509             sprintf(err_buf,
4510                     "You are using a plain Coulomb cut-off, which might produce artifacts.\n"
4511                     "You might want to consider using %s electrostatics.\n",
4512                     enumValueToString(CoulombInteractionType::Pme));
4513             warning_note(wi, err_buf);
4514         }
4515     }
4516
4517     /* Check if combination rules used in LJ-PME are the same as in the force field */
4518     if (EVDW_PME(ir->vdwtype))
4519     {
4520         check_combination_rules(ir, *sys, wi);
4521     }
4522
4523     /* Generalized reaction field */
4524     if (ir->coulombtype == CoulombInteractionType::GRFNotused)
4525     {
4526         warning_error(wi,
4527                       "Generalized reaction-field electrostatics is no longer supported. "
4528                       "You can use normal reaction-field instead and compute the reaction-field "
4529                       "constant by hand.");
4530     }
4531
4532     if (ir->efep != FreeEnergyPerturbationType::No && ir->fepvals->sc_alpha != 0
4533         && !gmx_within_tol(sys->ffparams.reppow, 12.0, 10 * GMX_DOUBLE_EPS))
4534     {
4535         gmx_fatal(FARGS, "Soft-core interactions are only supported with VdW repulsion power 12");
4536     }
4537
4538     if (ir->bPull)
4539     {
4540         bool bWarned;
4541
4542         bWarned = FALSE;
4543         for (i = 0; i < ir->pull->ncoord && !bWarned; i++)
4544         {
4545             if (ir->pull->coord[i].group[0] == 0 || ir->pull->coord[i].group[1] == 0)
4546             {
4547                 absolute_reference(ir, *sys, FALSE, AbsRef);
4548                 for (m = 0; m < DIM; m++)
4549                 {
4550                     if (ir->pull->coord[i].dim[m] && !AbsRef[m])
4551                     {
4552                         warning(wi,
4553                                 "You are using an absolute reference for pulling, but the rest of "
4554                                 "the system does not have an absolute reference. This will lead to "
4555                                 "artifacts.");
4556                         bWarned = TRUE;
4557                         break;
4558                     }
4559                 }
4560             }
4561         }
4562
4563         for (i = 0; i < 3; i++)
4564         {
4565             for (m = 0; m <= i; m++)
4566             {
4567                 if ((ir->epc != PressureCoupling::No && ir->compress[i][m] != 0) || ir->deform[i][m] != 0)
4568                 {
4569                     for (c = 0; c < ir->pull->ncoord; c++)
4570                     {
4571                         if (ir->pull->coord[c].eGeom == PullGroupGeometry::DirectionPBC
4572                             && ir->pull->coord[c].vec[m] != 0)
4573                         {
4574                             gmx_fatal(FARGS,
4575                                       "Can not have dynamic box while using pull geometry '%s' "
4576                                       "(dim %c)",
4577                                       enumValueToString(ir->pull->coord[c].eGeom),
4578                                       'x' + m);
4579                         }
4580                     }
4581                 }
4582             }
4583         }
4584     }
4585
4586     check_disre(*sys);
4587 }
4588
4589 void double_check(t_inputrec* ir, matrix box, bool bHasNormalConstraints, bool bHasAnyConstraints, warninp_t wi)
4590 {
4591     char        warn_buf[STRLEN];
4592     const char* ptr;
4593
4594     ptr = check_box(ir->pbcType, box);
4595     if (ptr)
4596     {
4597         warning_error(wi, ptr);
4598     }
4599
4600     if (bHasNormalConstraints && ir->eConstrAlg == ConstraintAlgorithm::Shake)
4601     {
4602         if (ir->shake_tol <= 0.0)
4603         {
4604             sprintf(warn_buf, "ERROR: shake-tol must be > 0 instead of %g\n", ir->shake_tol);
4605             warning_error(wi, warn_buf);
4606         }
4607     }
4608
4609     if ((ir->eConstrAlg == ConstraintAlgorithm::Lincs) && bHasNormalConstraints)
4610     {
4611         /* If we have Lincs constraints: */
4612         if (ir->eI == IntegrationAlgorithm::MD && ir->etc == TemperatureCoupling::No
4613             && ir->eConstrAlg == ConstraintAlgorithm::Lincs && ir->nLincsIter == 1)
4614         {
4615             sprintf(warn_buf,
4616                     "For energy conservation with LINCS, lincs_iter should be 2 or larger.\n");
4617             warning_note(wi, warn_buf);
4618         }
4619
4620         if ((ir->eI == IntegrationAlgorithm::CG || ir->eI == IntegrationAlgorithm::LBFGS)
4621             && (ir->nProjOrder < 8))
4622         {
4623             sprintf(warn_buf,
4624                     "For accurate %s with LINCS constraints, lincs-order should be 8 or more.",
4625                     enumValueToString(ir->eI));
4626             warning_note(wi, warn_buf);
4627         }
4628         if (ir->epc == PressureCoupling::Mttk)
4629         {
4630             warning_error(wi, "MTTK not compatible with lincs -- use shake instead.");
4631         }
4632     }
4633
4634     if (bHasAnyConstraints && ir->epc == PressureCoupling::Mttk)
4635     {
4636         warning_error(wi, "Constraints are not implemented with MTTK pressure control.");
4637     }
4638
4639     if (ir->LincsWarnAngle > 90.0)
4640     {
4641         sprintf(warn_buf, "lincs-warnangle can not be larger than 90 degrees, setting it to 90.\n");
4642         warning(wi, warn_buf);
4643         ir->LincsWarnAngle = 90.0;
4644     }
4645
4646     if (ir->pbcType != PbcType::No)
4647     {
4648         if (ir->nstlist == 0)
4649         {
4650             warning(wi,
4651                     "With nstlist=0 atoms are only put into the box at step 0, therefore drifting "
4652                     "atoms might cause the simulation to crash.");
4653         }
4654         if (gmx::square(ir->rlist) >= max_cutoff2(ir->pbcType, box))
4655         {
4656             sprintf(warn_buf,
4657                     "ERROR: The cut-off length is longer than half the shortest box vector or "
4658                     "longer than the smallest box diagonal element. Increase the box size or "
4659                     "decrease rlist.\n");
4660             warning_error(wi, warn_buf);
4661         }
4662     }
4663 }