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