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