Rename files to follow style
[alexxy/gromacs.git] / src / gromacs / gmxpreprocess / readir.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014,2015,2016,2017, The GROMACS development team.
7  * Copyright (c) 2018,2019,2020,2021, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 #include "gmxpre.h"
39
40 #include "readir.h"
41
42 #include <cctype>
43 #include <climits>
44 #include <cmath>
45 #include <cstdlib>
46
47 #include <algorithm>
48 #include <memory>
49 #include <numeric>
50 #include <string>
51
52 #include "gromacs/applied_forces/awh/read_params.h"
53 #include "gromacs/fileio/readinp.h"
54 #include "gromacs/fileio/warninp.h"
55 #include "gromacs/gmxlib/network.h"
56 #include "gromacs/gmxpreprocess/toputil.h"
57 #include "gromacs/math/functions.h"
58 #include "gromacs/math/units.h"
59 #include "gromacs/math/utilities.h"
60 #include "gromacs/math/vec.h"
61 #include "gromacs/mdlib/calc_verletbuf.h"
62 #include "gromacs/mdrun/mdmodules.h"
63 #include "gromacs/mdtypes/awh_params.h"
64 #include "gromacs/mdtypes/inputrec.h"
65 #include "gromacs/mdtypes/md_enums.h"
66 #include "gromacs/mdtypes/multipletimestepping.h"
67 #include "gromacs/mdtypes/pull_params.h"
68 #include "gromacs/options/options.h"
69 #include "gromacs/options/treesupport.h"
70 #include "gromacs/pbcutil/pbc.h"
71 #include "gromacs/selection/indexutil.h"
72 #include "gromacs/topology/block.h"
73 #include "gromacs/topology/ifunc.h"
74 #include "gromacs/topology/index.h"
75 #include "gromacs/topology/mtop_util.h"
76 #include "gromacs/topology/symtab.h"
77 #include "gromacs/topology/topology.h"
78 #include "gromacs/utility/arrayref.h"
79 #include "gromacs/utility/cstringutil.h"
80 #include "gromacs/utility/exceptions.h"
81 #include "gromacs/utility/fatalerror.h"
82 #include "gromacs/utility/filestream.h"
83 #include "gromacs/utility/gmxassert.h"
84 #include "gromacs/utility/ikeyvaluetreeerror.h"
85 #include "gromacs/utility/keyvaluetree.h"
86 #include "gromacs/utility/keyvaluetreebuilder.h"
87 #include "gromacs/utility/keyvaluetreemdpwriter.h"
88 #include "gromacs/utility/keyvaluetreetransform.h"
89 #include "gromacs/utility/mdmodulesnotifiers.h"
90 #include "gromacs/utility/smalloc.h"
91 #include "gromacs/utility/strconvert.h"
92 #include "gromacs/utility/stringcompare.h"
93 #include "gromacs/utility/stringutil.h"
94 #include "gromacs/utility/textwriter.h"
95
96 #define MAXPTR 254
97 #define NOGID 255
98
99 /* Resource parameters
100  * Do not change any of these until you read the instruction
101  * in readinp.h. Some cpp's do not take spaces after the backslash
102  * (like the c-shell), which will give you a very weird compiler
103  * message.
104  */
105
106 struct gmx_inputrec_strings
107 {
108     char tcgrps[STRLEN], tau_t[STRLEN], ref_t[STRLEN], freeze[STRLEN], frdim[STRLEN],
109             energy[STRLEN], user1[STRLEN], user2[STRLEN], vcm[STRLEN], x_compressed_groups[STRLEN],
110             couple_moltype[STRLEN], orirefitgrp[STRLEN], egptable[STRLEN], egpexcl[STRLEN],
111             wall_atomtype[STRLEN], wall_density[STRLEN], deform[STRLEN], QMMM[STRLEN], imd_grp[STRLEN];
112     gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, std::string> fep_lambda;
113     char                                                                   lambda_weights[STRLEN];
114     std::vector<std::string>                                               pullGroupNames;
115     std::vector<std::string>                                               rotateGroupNames;
116     char anneal[STRLEN], anneal_npoints[STRLEN], anneal_time[STRLEN], anneal_temp[STRLEN];
117 };
118
119 static gmx_inputrec_strings* inputrecStrings = nullptr;
120
121 void init_inputrec_strings()
122 {
123     if (inputrecStrings)
124     {
125         gmx_incons(
126                 "Attempted to call init_inputrec_strings before calling done_inputrec_strings. "
127                 "Only one inputrec (i.e. .mdp file) can be parsed at a time.");
128     }
129     inputrecStrings = new gmx_inputrec_strings();
130 }
131
132 void done_inputrec_strings()
133 {
134     delete inputrecStrings;
135     inputrecStrings = nullptr;
136 }
137
138
139 enum
140 {
141     egrptpALL,         /* All particles have to be a member of a group.     */
142     egrptpALL_GENREST, /* A rest group with name is generated for particles *
143                         * that are not part of any group.                   */
144     egrptpPART,        /* As egrptpALL_GENREST, but no name is generated    *
145                         * for the rest group.                               */
146     egrptpONE          /* Merge all selected groups into one group,         *
147                         * make a rest group for the remaining particles.    */
148 };
149
150 static const char* constraints[eshNR + 1] = { "none",     "h-bonds",    "all-bonds",
151                                               "h-angles", "all-angles", nullptr };
152
153 static const char* couple_lam[ecouplamNR + 1] = { "vdw-q", "vdw", "q", "none", nullptr };
154
155 static void getSimTemps(int ntemps, t_simtemp* simtemp, gmx::ArrayRef<double> temperature_lambdas)
156 {
157
158     int i;
159
160     for (i = 0; i < ntemps; i++)
161     {
162         /* simple linear scaling -- allows more control */
163         if (simtemp->eSimTempScale == SimulatedTempering::Linear)
164         {
165             simtemp->temperatures[i] =
166                     simtemp->simtemp_low
167                     + (simtemp->simtemp_high - simtemp->simtemp_low) * temperature_lambdas[i];
168         }
169         else if (simtemp->eSimTempScale
170                  == SimulatedTempering::Geometric) /* should give roughly equal acceptance for constant heat capacity . . . */
171         {
172             simtemp->temperatures[i] = simtemp->simtemp_low
173                                        * std::pow(simtemp->simtemp_high / simtemp->simtemp_low,
174                                                   static_cast<real>((1.0 * i) / (ntemps - 1)));
175         }
176         else if (simtemp->eSimTempScale == SimulatedTempering::Exponential)
177         {
178             simtemp->temperatures[i] = simtemp->simtemp_low
179                                        + (simtemp->simtemp_high - simtemp->simtemp_low)
180                                                  * (std::expm1(temperature_lambdas[i]) / std::expm1(1.0));
181         }
182         else
183         {
184             char errorstr[128];
185             sprintf(errorstr, "eSimTempScale=%s not defined", enumValueToString(simtemp->eSimTempScale));
186             gmx_fatal(FARGS, "%s", errorstr);
187         }
188     }
189 }
190
191
192 static void _low_check(bool b, const char* s, warninp_t wi)
193 {
194     if (b)
195     {
196         warning_error(wi, s);
197     }
198 }
199
200 static void check_nst(const char* desc_nst, int nst, const char* desc_p, int* p, warninp_t wi)
201 {
202     char buf[STRLEN];
203
204     if (*p > 0 && *p % nst != 0)
205     {
206         /* Round up to the next multiple of nst */
207         *p = ((*p) / nst + 1) * nst;
208         sprintf(buf, "%s should be a multiple of %s, changing %s to %d\n", desc_p, desc_nst, desc_p, *p);
209         warning(wi, buf);
210     }
211 }
212
213 //! Convert legacy mdp entries to modern ones.
214 static void process_interaction_modifier(InteractionModifiers* eintmod)
215 {
216     if (*eintmod == InteractionModifiers::PotShiftVerletUnsupported)
217     {
218         *eintmod = InteractionModifiers::PotShift;
219     }
220 }
221
222 void check_ir(const char*                    mdparin,
223               const gmx::MDModulesNotifiers& mdModulesNotifiers,
224               t_inputrec*                    ir,
225               t_gromppopts*                  opts,
226               warninp_t                      wi)
227 /* Check internal consistency.
228  * NOTE: index groups are not set here yet, don't check things
229  * like temperature coupling group options here, but in triple_check
230  */
231 {
232     /* Strange macro: first one fills the err_buf, and then one can check
233      * the condition, which will print the message and increase the error
234      * counter.
235      */
236 #define CHECK(b) _low_check(b, err_buf, wi)
237     char        err_buf[256], warn_buf[STRLEN];
238     int         i, j;
239     real        dt_pcoupl;
240     t_lambda*   fep    = ir->fepvals.get();
241     t_expanded* expand = ir->expandedvals.get();
242
243     set_warning_line(wi, mdparin, -1);
244
245     /* We cannot check MTS requirements with an invalid MTS setup
246      * and we will already have generated errors with an invalid MTS setup.
247      */
248     if (gmx::haveValidMtsSetup(*ir))
249     {
250         std::vector<std::string> errorMessages = gmx::checkMtsRequirements(*ir);
251
252         for (const auto& errorMessage : errorMessages)
253         {
254             warning_error(wi, errorMessage.c_str());
255         }
256     }
257
258     if (ir->coulombtype == CoulombInteractionType::RFNecUnsupported)
259     {
260         std::string message =
261                 gmx::formatString("%s electrostatics is no longer supported",
262                                   enumValueToString(CoulombInteractionType::RFNecUnsupported));
263         warning_error(wi, message);
264     }
265
266     /* BASIC CUT-OFF STUFF */
267     if (ir->rcoulomb < 0)
268     {
269         warning_error(wi, "rcoulomb should be >= 0");
270     }
271     if (ir->rvdw < 0)
272     {
273         warning_error(wi, "rvdw should be >= 0");
274     }
275     if (ir->rlist < 0 && !(ir->cutoff_scheme == CutoffScheme::Verlet && ir->verletbuf_tol > 0))
276     {
277         warning_error(wi, "rlist should be >= 0");
278     }
279     sprintf(err_buf,
280             "nstlist can not be smaller than 0. (If you were trying to use the heuristic "
281             "neighbour-list update scheme for efficient buffering for improved energy "
282             "conservation, please use the Verlet cut-off scheme instead.)");
283     CHECK(ir->nstlist < 0);
284
285     process_interaction_modifier(&ir->coulomb_modifier);
286     process_interaction_modifier(&ir->vdw_modifier);
287
288     if (ir->cutoff_scheme == CutoffScheme::Group)
289     {
290         gmx_fatal(FARGS,
291                   "The group cutoff scheme has been removed since GROMACS 2020. "
292                   "Please use the Verlet cutoff scheme.");
293     }
294     if (ir->cutoff_scheme == CutoffScheme::Verlet)
295     {
296         real rc_max;
297
298         /* Normal Verlet type neighbor-list, currently only limited feature support */
299         if (inputrec2nboundeddim(ir) < 3)
300         {
301             warning_error(wi, "With Verlet lists only full pbc or pbc=xy with walls is supported");
302         }
303
304         // We don't (yet) have general Verlet kernels for rcoulomb!=rvdw
305         if (ir->rcoulomb != ir->rvdw)
306         {
307             // Since we have PME coulomb + LJ cut-off kernels with rcoulomb>rvdw
308             // for PME load balancing, we can support this exception.
309             bool bUsesPmeTwinRangeKernel =
310                     (EEL_PME_EWALD(ir->coulombtype) && ir->vdwtype == VanDerWaalsType::Cut
311                      && ir->rcoulomb > ir->rvdw);
312             if (!bUsesPmeTwinRangeKernel)
313             {
314                 warning_error(wi,
315                               "With Verlet lists rcoulomb!=rvdw is not supported (except for "
316                               "rcoulomb>rvdw with PME electrostatics)");
317             }
318         }
319
320         if (ir->vdwtype == VanDerWaalsType::Shift || ir->vdwtype == VanDerWaalsType::Switch)
321         {
322             if (ir->vdw_modifier == InteractionModifiers::None
323                 || ir->vdw_modifier == InteractionModifiers::PotShift)
324             {
325                 ir->vdw_modifier =
326                         (ir->vdwtype == VanDerWaalsType::Shift ? InteractionModifiers::ForceSwitch
327                                                                : InteractionModifiers::PotSwitch);
328
329                 sprintf(warn_buf,
330                         "Replacing vdwtype=%s by the equivalent combination of vdwtype=%s and "
331                         "vdw_modifier=%s",
332                         enumValueToString(ir->vdwtype),
333                         enumValueToString(VanDerWaalsType::Cut),
334                         enumValueToString(ir->vdw_modifier));
335                 warning_note(wi, warn_buf);
336
337                 ir->vdwtype = VanDerWaalsType::Cut;
338             }
339             else
340             {
341                 sprintf(warn_buf,
342                         "Unsupported combination of vdwtype=%s and vdw_modifier=%s",
343                         enumValueToString(ir->vdwtype),
344                         enumValueToString(ir->vdw_modifier));
345                 warning_error(wi, warn_buf);
346             }
347         }
348
349         if (!(ir->vdwtype == VanDerWaalsType::Cut || ir->vdwtype == VanDerWaalsType::Pme))
350         {
351             warning_error(wi,
352                           "With Verlet lists only cut-off and PME LJ interactions are supported");
353         }
354         if (!(ir->coulombtype == CoulombInteractionType::Cut || EEL_RF(ir->coulombtype)
355               || EEL_PME(ir->coulombtype) || ir->coulombtype == CoulombInteractionType::Ewald))
356         {
357             warning_error(wi,
358                           "With Verlet lists only cut-off, reaction-field, PME and Ewald "
359                           "electrostatics are supported");
360         }
361         if (!(ir->coulomb_modifier == InteractionModifiers::None
362               || ir->coulomb_modifier == InteractionModifiers::PotShift))
363         {
364             sprintf(warn_buf, "coulomb_modifier=%s is not supported", enumValueToString(ir->coulomb_modifier));
365             warning_error(wi, warn_buf);
366         }
367
368         if (EEL_USER(ir->coulombtype))
369         {
370             sprintf(warn_buf,
371                     "Coulomb type %s is not supported with the verlet scheme",
372                     enumValueToString(ir->coulombtype));
373             warning_error(wi, warn_buf);
374         }
375
376         if (ir->nstlist <= 0)
377         {
378             warning_error(wi, "With Verlet lists nstlist should be larger than 0");
379         }
380
381         if (ir->nstlist < 10)
382         {
383             warning_note(wi,
384                          "With Verlet lists the optimal nstlist is >= 10, with GPUs >= 20. Note "
385                          "that with the Verlet scheme, nstlist has no effect on the accuracy of "
386                          "your simulation.");
387         }
388
389         rc_max = std::max(ir->rvdw, ir->rcoulomb);
390
391         if (EI_TPI(ir->eI))
392         {
393             /* With TPI we set the pairlist cut-off later using the radius of the insterted molecule */
394             ir->verletbuf_tol = 0;
395             ir->rlist         = rc_max;
396         }
397         else if (ir->verletbuf_tol <= 0)
398         {
399             if (ir->verletbuf_tol == 0)
400             {
401                 warning_error(wi, "Can not have Verlet buffer tolerance of exactly 0");
402             }
403
404             if (ir->rlist < rc_max)
405             {
406                 warning_error(wi,
407                               "With verlet lists rlist can not be smaller than rvdw or rcoulomb");
408             }
409
410             if (ir->rlist == rc_max && ir->nstlist > 1)
411             {
412                 warning_note(
413                         wi,
414                         "rlist is equal to rvdw and/or rcoulomb: there is no explicit Verlet "
415                         "buffer. The cluster pair list does have a buffering effect, but choosing "
416                         "a larger rlist might be necessary for good energy conservation.");
417             }
418         }
419         else
420         {
421             if (ir->rlist > rc_max)
422             {
423                 warning_note(wi,
424                              "You have set rlist larger than the interaction cut-off, but you also "
425                              "have verlet-buffer-tolerance > 0. Will set rlist using "
426                              "verlet-buffer-tolerance.");
427             }
428
429             if (ir->nstlist == 1)
430             {
431                 /* No buffer required */
432                 ir->rlist = rc_max;
433             }
434             else
435             {
436                 if (EI_DYNAMICS(ir->eI))
437                 {
438                     if (inputrec2nboundeddim(ir) < 3)
439                     {
440                         warning_error(wi,
441                                       "The box volume is required for calculating rlist from the "
442                                       "energy drift with verlet-buffer-tolerance > 0. You are "
443                                       "using at least one unbounded dimension, so no volume can be "
444                                       "computed. Either use a finite box, or set rlist yourself "
445                                       "together with verlet-buffer-tolerance = -1.");
446                     }
447                     /* Set rlist temporarily so we can continue processing */
448                     ir->rlist = rc_max;
449                 }
450                 else
451                 {
452                     /* Set the buffer to 5% of the cut-off */
453                     ir->rlist = (1.0 + verlet_buffer_ratio_nodynamics) * rc_max;
454                 }
455             }
456         }
457     }
458
459     /* GENERAL INTEGRATOR STUFF */
460     if (!EI_MD(ir->eI))
461     {
462         if (ir->etc != TemperatureCoupling::No)
463         {
464             if (EI_RANDOM(ir->eI))
465             {
466                 sprintf(warn_buf,
467                         "Setting tcoupl from '%s' to 'no'. %s handles temperature coupling "
468                         "implicitly. See the documentation for more information on which "
469                         "parameters affect temperature for %s.",
470                         enumValueToString(ir->etc),
471                         enumValueToString(ir->eI),
472                         enumValueToString(ir->eI));
473             }
474             else
475             {
476                 sprintf(warn_buf,
477                         "Setting tcoupl from '%s' to 'no'. Temperature coupling does not apply to "
478                         "%s.",
479                         enumValueToString(ir->etc),
480                         enumValueToString(ir->eI));
481             }
482             warning_note(wi, warn_buf);
483         }
484         ir->etc = TemperatureCoupling::No;
485     }
486     if (ir->eI == IntegrationAlgorithm::VVAK)
487     {
488         sprintf(warn_buf,
489                 "Integrator method %s is implemented primarily for validation purposes; for "
490                 "molecular dynamics, you should probably be using %s or %s",
491                 enumValueToString(IntegrationAlgorithm::VVAK),
492                 enumValueToString(IntegrationAlgorithm::MD),
493                 enumValueToString(IntegrationAlgorithm::VV));
494         warning_note(wi, warn_buf);
495     }
496     if (!EI_DYNAMICS(ir->eI))
497     {
498         if (ir->epc != PressureCoupling::No)
499         {
500             sprintf(warn_buf,
501                     "Setting pcoupl from '%s' to 'no'. Pressure coupling does not apply to %s.",
502                     enumValueToString(ir->epc),
503                     enumValueToString(ir->eI));
504             warning_note(wi, warn_buf);
505         }
506         ir->epc = PressureCoupling::No;
507     }
508     if (EI_DYNAMICS(ir->eI))
509     {
510         if (ir->nstcalcenergy < 0)
511         {
512             ir->nstcalcenergy = ir_optimal_nstcalcenergy(ir);
513             if (ir->nstenergy != 0 && ir->nstenergy < ir->nstcalcenergy)
514             {
515                 /* nstcalcenergy larger than nstener does not make sense.
516                  * We ideally want nstcalcenergy=nstener.
517                  */
518                 if (ir->nstlist > 0)
519                 {
520                     ir->nstcalcenergy = std::gcd(ir->nstenergy, ir->nstlist);
521                 }
522                 else
523                 {
524                     ir->nstcalcenergy = ir->nstenergy;
525                 }
526             }
527         }
528         else if ((ir->nstenergy > 0 && ir->nstcalcenergy > ir->nstenergy)
529                  || (ir->efep != FreeEnergyPerturbationType::No && ir->fepvals->nstdhdl > 0
530                      && (ir->nstcalcenergy > ir->fepvals->nstdhdl)))
531
532         {
533             const char* nsten    = "nstenergy";
534             const char* nstdh    = "nstdhdl";
535             const char* min_name = nsten;
536             int         min_nst  = ir->nstenergy;
537
538             /* find the smallest of ( nstenergy, nstdhdl ) */
539             if (ir->efep != FreeEnergyPerturbationType::No && ir->fepvals->nstdhdl > 0
540                 && (ir->nstenergy == 0 || ir->fepvals->nstdhdl < ir->nstenergy))
541             {
542                 min_nst  = ir->fepvals->nstdhdl;
543                 min_name = nstdh;
544             }
545             /* If the user sets nstenergy small, we should respect that */
546             sprintf(warn_buf, "Setting nstcalcenergy (%d) equal to %s (%d)", ir->nstcalcenergy, min_name, min_nst);
547             warning_note(wi, warn_buf);
548             ir->nstcalcenergy = min_nst;
549         }
550
551         if (ir->epc != PressureCoupling::No)
552         {
553             if (ir->nstpcouple < 0)
554             {
555                 ir->nstpcouple = ir_optimal_nstpcouple(ir);
556             }
557             if (ir->useMts && ir->nstpcouple % ir->mtsLevels.back().stepFactor != 0)
558             {
559                 warning_error(wi,
560                               "With multiple time stepping, nstpcouple should be a mutiple of "
561                               "mts-factor");
562             }
563         }
564
565         if (ir->nstcalcenergy > 0)
566         {
567             if (ir->efep != FreeEnergyPerturbationType::No)
568             {
569                 /* nstdhdl should be a multiple of nstcalcenergy */
570                 check_nst("nstcalcenergy", ir->nstcalcenergy, "nstdhdl", &ir->fepvals->nstdhdl, wi);
571             }
572             if (ir->bExpanded)
573             {
574                 /* nstexpanded should be a multiple of nstcalcenergy */
575                 check_nst("nstcalcenergy", ir->nstcalcenergy, "nstexpanded", &ir->expandedvals->nstexpanded, wi);
576             }
577             /* for storing exact averages nstenergy should be
578              * a multiple of nstcalcenergy
579              */
580             check_nst("nstcalcenergy", ir->nstcalcenergy, "nstenergy", &ir->nstenergy, wi);
581         }
582
583         // Inquire all MDModules, if their parameters match with the energy
584         // calculation frequency
585         gmx::EnergyCalculationFrequencyErrors energyCalculationFrequencyErrors(ir->nstcalcenergy);
586         mdModulesNotifiers.preProcessingNotifier_.notify(&energyCalculationFrequencyErrors);
587
588         // Emit all errors from the energy calculation frequency checks
589         for (const std::string& energyFrequencyErrorMessage :
590              energyCalculationFrequencyErrors.errorMessages())
591         {
592             warning_error(wi, energyFrequencyErrorMessage);
593         }
594     }
595
596     if (ir->nsteps == 0 && !ir->bContinuation)
597     {
598         warning_note(wi,
599                      "For a correct single-point energy evaluation with nsteps = 0, use "
600                      "continuation = yes to avoid constraining the input coordinates.");
601     }
602
603     /* LD STUFF */
604     if ((EI_SD(ir->eI) || ir->eI == IntegrationAlgorithm::BD) && ir->bContinuation && ir->ld_seed != -1)
605     {
606         warning_note(wi,
607                      "You are doing a continuation with SD or BD, make sure that ld_seed is "
608                      "different from the previous run (using ld_seed=-1 will ensure this)");
609     }
610
611     /* TPI STUFF */
612     if (EI_TPI(ir->eI))
613     {
614         sprintf(err_buf, "TPI only works with pbc = %s", c_pbcTypeNames[PbcType::Xyz].c_str());
615         CHECK(ir->pbcType != PbcType::Xyz);
616         sprintf(err_buf, "with TPI nstlist should be larger than zero");
617         CHECK(ir->nstlist <= 0);
618         sprintf(err_buf, "TPI does not work with full electrostatics other than PME");
619         CHECK(EEL_FULL(ir->coulombtype) && !EEL_PME(ir->coulombtype));
620     }
621
622     /* SHAKE / LINCS */
623     if ((opts->nshake > 0) && (opts->bMorse))
624     {
625         sprintf(warn_buf, "Using morse bond-potentials while constraining bonds is useless");
626         warning(wi, warn_buf);
627     }
628
629     if ((EI_SD(ir->eI) || ir->eI == IntegrationAlgorithm::BD) && ir->bContinuation && ir->ld_seed != -1)
630     {
631         warning_note(wi,
632                      "You are doing a continuation with SD or BD, make sure that ld_seed is "
633                      "different from the previous run (using ld_seed=-1 will ensure this)");
634     }
635     /* verify simulated tempering options */
636
637     if (ir->bSimTemp)
638     {
639         bool bAllTempZero = TRUE;
640         for (i = 0; i < fep->n_lambda; i++)
641         {
642             sprintf(err_buf,
643                     "Entry %d for %s must be between 0 and 1, instead is %g",
644                     i,
645                     enumValueToString(FreeEnergyPerturbationCouplingType::Temperature),
646                     fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Temperature)][i]);
647             CHECK((fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Temperature)][i] < 0)
648                   || (fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Temperature)][i]
649                       > 1));
650             if (fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Temperature)][i] > 0)
651             {
652                 bAllTempZero = FALSE;
653             }
654         }
655         sprintf(err_buf, "if simulated tempering is on, temperature-lambdas may not be all zero");
656         CHECK(bAllTempZero == TRUE);
657
658         sprintf(err_buf, "Simulated tempering is currently only compatible with md-vv");
659         CHECK(ir->eI != IntegrationAlgorithm::VV);
660
661         /* check compatability of the temperature coupling with simulated tempering */
662
663         if (ir->etc == TemperatureCoupling::NoseHoover)
664         {
665             sprintf(warn_buf,
666                     "Nose-Hoover based temperature control such as [%s] my not be "
667                     "entirelyconsistent with simulated tempering",
668                     enumValueToString(ir->etc));
669             warning_note(wi, warn_buf);
670         }
671
672         /* check that the temperatures make sense */
673
674         sprintf(err_buf,
675                 "Higher simulated tempering temperature (%g) must be >= than the simulated "
676                 "tempering lower temperature (%g)",
677                 ir->simtempvals->simtemp_high,
678                 ir->simtempvals->simtemp_low);
679         CHECK(ir->simtempvals->simtemp_high <= ir->simtempvals->simtemp_low);
680
681         sprintf(err_buf,
682                 "Higher simulated tempering temperature (%g) must be >= zero",
683                 ir->simtempvals->simtemp_high);
684         CHECK(ir->simtempvals->simtemp_high <= 0);
685
686         sprintf(err_buf,
687                 "Lower simulated tempering temperature (%g) must be >= zero",
688                 ir->simtempvals->simtemp_low);
689         CHECK(ir->simtempvals->simtemp_low <= 0);
690     }
691
692     /* verify free energy options */
693
694     if (ir->efep != FreeEnergyPerturbationType::No)
695     {
696         fep = ir->fepvals.get();
697         sprintf(err_buf, "The soft-core power is %d and can only be 1 or 2", fep->sc_power);
698         CHECK(fep->sc_alpha != 0 && fep->sc_power != 1 && fep->sc_power != 2);
699
700         sprintf(err_buf,
701                 "The soft-core sc-r-power is %d and can only be 6. (sc-r-power 48 is no longer "
702                 "supported.)",
703                 static_cast<int>(fep->sc_r_power));
704         CHECK(fep->sc_alpha != 0 && fep->sc_r_power != 6.0);
705
706         sprintf(err_buf,
707                 "Can't use positive delta-lambda (%g) if initial state/lambda does not start at "
708                 "zero",
709                 fep->delta_lambda);
710         CHECK(fep->delta_lambda > 0 && ((fep->init_fep_state > 0) || (fep->init_lambda > 0)));
711
712         sprintf(err_buf,
713                 "Can't use positive delta-lambda (%g) with expanded ensemble simulations",
714                 fep->delta_lambda);
715         CHECK(fep->delta_lambda > 0 && (ir->efep == FreeEnergyPerturbationType::Expanded));
716
717         sprintf(err_buf, "Can only use expanded ensemble with md-vv (for now)");
718         CHECK(!(EI_VV(ir->eI)) && (ir->efep == FreeEnergyPerturbationType::Expanded));
719
720         sprintf(err_buf, "Free-energy not implemented for Ewald");
721         CHECK(ir->coulombtype == CoulombInteractionType::Ewald);
722
723         /* check validty of lambda inputs */
724         if (fep->n_lambda == 0)
725         {
726             /* Clear output in case of no states:*/
727             sprintf(err_buf, "init-lambda-state set to %d: no lambda states are defined.", fep->init_fep_state);
728             CHECK((fep->init_fep_state >= 0) && (fep->n_lambda == 0));
729         }
730         else
731         {
732             sprintf(err_buf,
733                     "initial thermodynamic state %d does not exist, only goes to %d",
734                     fep->init_fep_state,
735                     fep->n_lambda - 1);
736             CHECK((fep->init_fep_state >= fep->n_lambda));
737         }
738
739         sprintf(err_buf,
740                 "Lambda state must be set, either with init-lambda-state or with init-lambda");
741         CHECK((fep->init_fep_state < 0) && (fep->init_lambda < 0));
742
743         sprintf(err_buf,
744                 "init-lambda=%g while init-lambda-state=%d. Lambda state must be set either with "
745                 "init-lambda-state or with init-lambda, but not both",
746                 fep->init_lambda,
747                 fep->init_fep_state);
748         CHECK((fep->init_fep_state >= 0) && (fep->init_lambda >= 0));
749
750
751         if ((fep->init_lambda >= 0) && (fep->delta_lambda == 0))
752         {
753             int n_lambda_terms;
754             n_lambda_terms = 0;
755             for (i = 0; i < static_cast<int>(FreeEnergyPerturbationCouplingType::Count); i++)
756             {
757                 if (fep->separate_dvdl[i])
758                 {
759                     n_lambda_terms++;
760                 }
761             }
762             if (n_lambda_terms > 1)
763             {
764                 sprintf(warn_buf,
765                         "If lambda vector states (fep-lambdas, coul-lambdas etc.) are set, don't "
766                         "use init-lambda to set lambda state (except for slow growth). Use "
767                         "init-lambda-state instead.");
768                 warning(wi, warn_buf);
769             }
770
771             if (n_lambda_terms < 2 && fep->n_lambda > 0)
772             {
773                 warning_note(wi,
774                              "init-lambda is deprecated for setting lambda state (except for slow "
775                              "growth). Use init-lambda-state instead.");
776             }
777         }
778
779         for (j = 0; j < static_cast<int>(FreeEnergyPerturbationCouplingType::Count); j++)
780         {
781             for (i = 0; i < fep->n_lambda; i++)
782             {
783                 auto enumValue = static_cast<FreeEnergyPerturbationCouplingType>(j);
784                 sprintf(err_buf,
785                         "Entry %d for %s must be between 0 and 1, instead is %g",
786                         i,
787                         enumValueToString(enumValue),
788                         fep->all_lambda[j][i]);
789                 CHECK((fep->all_lambda[j][i] < 0) || (fep->all_lambda[j][i] > 1));
790             }
791         }
792
793         if ((fep->sc_alpha > 0) && (!fep->bScCoul))
794         {
795             for (i = 0; i < fep->n_lambda; i++)
796             {
797                 sprintf(err_buf,
798                         "For state %d, vdw-lambdas (%f) is changing with vdw softcore, while "
799                         "coul-lambdas (%f) is nonzero without coulomb softcore: this will lead to "
800                         "crashes, and is not supported.",
801                         i,
802                         fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Vdw)][i],
803                         fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Coul)][i]);
804                 CHECK((fep->sc_alpha > 0)
805                       && (((fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Coul)][i] > 0.0)
806                            && (fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Coul)][i] < 1.0))
807                           && ((fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Vdw)][i] > 0.0)
808                               && (fep->all_lambda[static_cast<int>(FreeEnergyPerturbationCouplingType::Vdw)][i]
809                                   < 1.0))));
810             }
811         }
812
813         if ((fep->bScCoul) && (EEL_PME(ir->coulombtype)))
814         {
815             real sigma, lambda, r_sc;
816
817             sigma = 0.34;
818             /* Maximum estimate for A and B charges equal with lambda power 1 */
819             lambda = 0.5;
820             r_sc = std::pow(lambda * fep->sc_alpha * std::pow(sigma / ir->rcoulomb, fep->sc_r_power) + 1.0,
821                             1.0 / fep->sc_r_power);
822             sprintf(warn_buf,
823                     "With PME there is a minor soft core effect present at the cut-off, "
824                     "proportional to (LJsigma/rcoulomb)^%g. This could have a minor effect on "
825                     "energy conservation, but usually other effects dominate. With a common sigma "
826                     "value of %g nm the fraction of the particle-particle potential at the cut-off "
827                     "at lambda=%g is around %.1e, while ewald-rtol is %.1e.",
828                     fep->sc_r_power,
829                     sigma,
830                     lambda,
831                     r_sc - 1.0,
832                     ir->ewald_rtol);
833             warning_note(wi, warn_buf);
834         }
835
836         /*  Free Energy Checks -- In an ideal world, slow growth and FEP would
837             be treated differently, but that's the next step */
838
839         for (i = 0; i < static_cast<int>(FreeEnergyPerturbationCouplingType::Count); i++)
840         {
841             auto enumValue = static_cast<FreeEnergyPerturbationCouplingType>(i);
842             for (j = 0; j < fep->n_lambda; j++)
843             {
844                 sprintf(err_buf, "%s[%d] must be between 0 and 1", enumValueToString(enumValue), j);
845                 CHECK((fep->all_lambda[i][j] < 0) || (fep->all_lambda[i][j] > 1));
846             }
847         }
848     }
849
850     if ((ir->bSimTemp) || (ir->efep == FreeEnergyPerturbationType::Expanded))
851     {
852         fep = ir->fepvals.get();
853
854         /* checking equilibration of weights inputs for validity */
855
856         sprintf(err_buf,
857                 "weight-equil-number-all-lambda (%d) is ignored if lmc-weights-equil is not equal "
858                 "to %s",
859                 expand->equil_n_at_lam,
860                 enumValueToString(LambdaWeightWillReachEquilibrium::NumAtLambda));
861         CHECK((expand->equil_n_at_lam > 0)
862               && (expand->elmceq != LambdaWeightWillReachEquilibrium::NumAtLambda));
863
864         sprintf(err_buf,
865                 "weight-equil-number-samples (%d) is ignored if lmc-weights-equil is not equal to "
866                 "%s",
867                 expand->equil_samples,
868                 enumValueToString(LambdaWeightWillReachEquilibrium::Samples));
869         CHECK((expand->equil_samples > 0) && (expand->elmceq != LambdaWeightWillReachEquilibrium::Samples));
870
871         sprintf(err_buf,
872                 "weight-equil-number-steps (%d) is ignored if lmc-weights-equil is not equal to %s",
873                 expand->equil_steps,
874                 enumValueToString(LambdaWeightWillReachEquilibrium::Steps));
875         CHECK((expand->equil_steps > 0) && (expand->elmceq != LambdaWeightWillReachEquilibrium::Steps));
876
877         sprintf(err_buf,
878                 "weight-equil-wl-delta (%d) is ignored if lmc-weights-equil is not equal to %s",
879                 expand->equil_samples,
880                 enumValueToString(LambdaWeightWillReachEquilibrium::WLDelta));
881         CHECK((expand->equil_wl_delta > 0) && (expand->elmceq != LambdaWeightWillReachEquilibrium::WLDelta));
882
883         sprintf(err_buf,
884                 "weight-equil-count-ratio (%f) is ignored if lmc-weights-equil is not equal to %s",
885                 expand->equil_ratio,
886                 enumValueToString(LambdaWeightWillReachEquilibrium::Ratio));
887         CHECK((expand->equil_ratio > 0) && (expand->elmceq != LambdaWeightWillReachEquilibrium::Ratio));
888
889         sprintf(err_buf,
890                 "weight-equil-number-all-lambda (%d) must be a positive integer if "
891                 "lmc-weights-equil=%s",
892                 expand->equil_n_at_lam,
893                 enumValueToString(LambdaWeightWillReachEquilibrium::NumAtLambda));
894         CHECK((expand->equil_n_at_lam <= 0)
895               && (expand->elmceq == LambdaWeightWillReachEquilibrium::NumAtLambda));
896
897         sprintf(err_buf,
898                 "weight-equil-number-samples (%d) must be a positive integer if "
899                 "lmc-weights-equil=%s",
900                 expand->equil_samples,
901                 enumValueToString(LambdaWeightWillReachEquilibrium::Samples));
902         CHECK((expand->equil_samples <= 0) && (expand->elmceq == LambdaWeightWillReachEquilibrium::Samples));
903
904         sprintf(err_buf,
905                 "weight-equil-number-steps (%d) must be a positive integer if lmc-weights-equil=%s",
906                 expand->equil_steps,
907                 enumValueToString(LambdaWeightWillReachEquilibrium::Steps));
908         CHECK((expand->equil_steps <= 0) && (expand->elmceq == LambdaWeightWillReachEquilibrium::Steps));
909
910         sprintf(err_buf,
911                 "weight-equil-wl-delta (%f) must be > 0 if lmc-weights-equil=%s",
912                 expand->equil_wl_delta,
913                 enumValueToString(LambdaWeightWillReachEquilibrium::WLDelta));
914         CHECK((expand->equil_wl_delta <= 0)
915               && (expand->elmceq == LambdaWeightWillReachEquilibrium::WLDelta));
916
917         sprintf(err_buf,
918                 "weight-equil-count-ratio (%f) must be > 0 if lmc-weights-equil=%s",
919                 expand->equil_ratio,
920                 enumValueToString(LambdaWeightWillReachEquilibrium::Ratio));
921         CHECK((expand->equil_ratio <= 0) && (expand->elmceq == LambdaWeightWillReachEquilibrium::Ratio));
922
923         sprintf(err_buf,
924                 "lmc-weights-equil=%s only possible when lmc-stats = %s or lmc-stats %s",
925                 enumValueToString(LambdaWeightWillReachEquilibrium::WLDelta),
926                 enumValueToString(LambdaWeightCalculation::WL),
927                 enumValueToString(LambdaWeightCalculation::WWL));
928         CHECK((expand->elmceq == LambdaWeightWillReachEquilibrium::WLDelta) && (!EWL(expand->elamstats)));
929
930         sprintf(err_buf, "lmc-repeats (%d) must be greater than 0", expand->lmc_repeats);
931         CHECK((expand->lmc_repeats <= 0));
932         sprintf(err_buf, "minimum-var-min (%d) must be greater than 0", expand->minvarmin);
933         CHECK((expand->minvarmin <= 0));
934         sprintf(err_buf, "weight-c-range (%d) must be greater or equal to 0", expand->c_range);
935         CHECK((expand->c_range < 0));
936         sprintf(err_buf,
937                 "init-lambda-state (%d) must be zero if lmc-forced-nstart (%d)> 0 and lmc-move != "
938                 "'no'",
939                 fep->init_fep_state,
940                 expand->lmc_forced_nstart);
941         CHECK((fep->init_fep_state != 0) && (expand->lmc_forced_nstart > 0)
942               && (expand->elmcmove != LambdaMoveCalculation::No));
943         sprintf(err_buf, "lmc-forced-nstart (%d) must not be negative", expand->lmc_forced_nstart);
944         CHECK((expand->lmc_forced_nstart < 0));
945         sprintf(err_buf,
946                 "init-lambda-state (%d) must be in the interval [0,number of lambdas)",
947                 fep->init_fep_state);
948         CHECK((fep->init_fep_state < 0) || (fep->init_fep_state >= fep->n_lambda));
949
950         sprintf(err_buf, "init-wl-delta (%f) must be greater than or equal to 0", expand->init_wl_delta);
951         CHECK((expand->init_wl_delta < 0));
952         sprintf(err_buf, "wl-ratio (%f) must be between 0 and 1", expand->wl_ratio);
953         CHECK((expand->wl_ratio <= 0) || (expand->wl_ratio >= 1));
954         sprintf(err_buf, "wl-scale (%f) must be between 0 and 1", expand->wl_scale);
955         CHECK((expand->wl_scale <= 0) || (expand->wl_scale >= 1));
956
957         /* if there is no temperature control, we need to specify an MC temperature */
958         if (!integratorHasReferenceTemperature(ir)
959             && (expand->elmcmove != LambdaMoveCalculation::No) && (expand->mc_temp <= 0.0))
960         {
961             sprintf(err_buf,
962                     "If there is no temperature control, and lmc-mcmove!='no', mc_temp must be set "
963                     "to a positive number");
964             warning_error(wi, err_buf);
965         }
966         if (expand->nstTij > 0)
967         {
968             sprintf(err_buf, "nstlog must be non-zero");
969             CHECK(ir->nstlog == 0);
970             // Avoid modulus by zero in the case that already triggered an error exit.
971             if (ir->nstlog != 0)
972             {
973                 sprintf(err_buf,
974                         "nst-transition-matrix (%d) must be an integer multiple of nstlog (%d)",
975                         expand->nstTij,
976                         ir->nstlog);
977                 CHECK((expand->nstTij % ir->nstlog) != 0);
978             }
979         }
980     }
981
982     /* PBC/WALLS */
983     sprintf(err_buf, "walls only work with pbc=%s", c_pbcTypeNames[PbcType::XY].c_str());
984     CHECK(ir->nwall && ir->pbcType != PbcType::XY);
985
986     /* VACUUM STUFF */
987     if (ir->pbcType != PbcType::Xyz && ir->nwall != 2)
988     {
989         if (ir->pbcType == PbcType::No)
990         {
991             if (ir->epc != PressureCoupling::No)
992             {
993                 warning(wi, "Turning off pressure coupling for vacuum system");
994                 ir->epc = PressureCoupling::No;
995             }
996         }
997         else
998         {
999             sprintf(err_buf,
1000                     "Can not have pressure coupling with pbc=%s",
1001                     c_pbcTypeNames[ir->pbcType].c_str());
1002             CHECK(ir->epc != PressureCoupling::No);
1003         }
1004         sprintf(err_buf, "Can not have Ewald with pbc=%s", c_pbcTypeNames[ir->pbcType].c_str());
1005         CHECK(EEL_FULL(ir->coulombtype));
1006
1007         sprintf(err_buf,
1008                 "Can not have dispersion correction with pbc=%s",
1009                 c_pbcTypeNames[ir->pbcType].c_str());
1010         CHECK(ir->eDispCorr != DispersionCorrectionType::No);
1011     }
1012
1013     if (ir->rlist == 0.0)
1014     {
1015         sprintf(err_buf,
1016                 "can only have neighborlist cut-off zero (=infinite)\n"
1017                 "with coulombtype = %s or coulombtype = %s\n"
1018                 "without periodic boundary conditions (pbc = %s) and\n"
1019                 "rcoulomb and rvdw set to zero",
1020                 enumValueToString(CoulombInteractionType::Cut),
1021                 enumValueToString(CoulombInteractionType::User),
1022                 c_pbcTypeNames[PbcType::No].c_str());
1023         CHECK(((ir->coulombtype != CoulombInteractionType::Cut)
1024                && (ir->coulombtype != CoulombInteractionType::User))
1025               || (ir->pbcType != PbcType::No) || (ir->rcoulomb != 0.0) || (ir->rvdw != 0.0));
1026
1027         if (ir->nstlist > 0)
1028         {
1029             warning_note(wi,
1030                          "Simulating without cut-offs can be (slightly) faster with nstlist=0, "
1031                          "nstype=simple and only one MPI rank");
1032         }
1033     }
1034
1035     /* COMM STUFF */
1036     if (ir->nstcomm == 0)
1037     {
1038         // TODO Change this behaviour. There should be exactly one way
1039         // to turn off an algorithm.
1040         ir->comm_mode = ComRemovalAlgorithm::No;
1041     }
1042     if (ir->comm_mode != ComRemovalAlgorithm::No)
1043     {
1044         if (ir->nstcomm < 0)
1045         {
1046             // TODO Such input was once valid. Now that we've been
1047             // helpful for a few years, we should reject such input,
1048             // lest we have to support every historical decision
1049             // forever.
1050             warning(wi,
1051                     "If you want to remove the rotation around the center of mass, you should set "
1052                     "comm_mode = Angular instead of setting nstcomm < 0. nstcomm is modified to "
1053                     "its absolute value");
1054             ir->nstcomm = abs(ir->nstcomm);
1055         }
1056
1057         if (ir->nstcalcenergy > 0 && ir->nstcomm < ir->nstcalcenergy)
1058         {
1059             warning_note(wi,
1060                          "nstcomm < nstcalcenergy defeats the purpose of nstcalcenergy, setting "
1061                          "nstcomm to nstcalcenergy");
1062             ir->nstcomm = ir->nstcalcenergy;
1063         }
1064
1065         if (ir->comm_mode == ComRemovalAlgorithm::Angular)
1066         {
1067             sprintf(err_buf,
1068                     "Can not remove the rotation around the center of mass with periodic "
1069                     "molecules");
1070             CHECK(ir->bPeriodicMols);
1071             if (ir->pbcType != PbcType::No)
1072             {
1073                 warning(wi,
1074                         "Removing the rotation around the center of mass in a periodic system, "
1075                         "this can lead to artifacts. Only use this on a single (cluster of) "
1076                         "molecules. This cluster should not cross periodic boundaries.");
1077             }
1078         }
1079     }
1080
1081     if (EI_STATE_VELOCITY(ir->eI) && !EI_SD(ir->eI) && ir->pbcType == PbcType::No
1082         && ir->comm_mode != ComRemovalAlgorithm::Angular)
1083     {
1084         sprintf(warn_buf,
1085                 "Tumbling and flying ice-cubes: We are not removing rotation around center of mass "
1086                 "in a non-periodic system. You should probably set comm_mode = ANGULAR or use "
1087                 "integrator = %s.",
1088                 enumValueToString(IntegrationAlgorithm::SD1));
1089         warning_note(wi, warn_buf);
1090     }
1091
1092     /* TEMPERATURE COUPLING */
1093     if (ir->etc == TemperatureCoupling::Yes)
1094     {
1095         ir->etc = TemperatureCoupling::Berendsen;
1096         warning_note(wi,
1097                      "Old option for temperature coupling given: "
1098                      "changing \"yes\" to \"Berendsen\"\n");
1099     }
1100
1101     if ((ir->etc == TemperatureCoupling::NoseHoover) || (ir->epc == PressureCoupling::Mttk))
1102     {
1103         if (ir->opts.nhchainlength < 1)
1104         {
1105             sprintf(warn_buf,
1106                     "number of Nose-Hoover chains (currently %d) cannot be less than 1,reset to "
1107                     "1\n",
1108                     ir->opts.nhchainlength);
1109             ir->opts.nhchainlength = 1;
1110             warning(wi, warn_buf);
1111         }
1112
1113         if (ir->etc == TemperatureCoupling::NoseHoover && !EI_VV(ir->eI) && ir->opts.nhchainlength > 1)
1114         {
1115             warning_note(
1116                     wi,
1117                     "leapfrog does not yet support Nose-Hoover chains, nhchainlength reset to 1");
1118             ir->opts.nhchainlength = 1;
1119         }
1120     }
1121     else
1122     {
1123         ir->opts.nhchainlength = 0;
1124     }
1125
1126     if (ir->eI == IntegrationAlgorithm::VVAK)
1127     {
1128         sprintf(err_buf,
1129                 "%s implemented primarily for validation, and requires nsttcouple = 1 and "
1130                 "nstpcouple = 1.",
1131                 enumValueToString(IntegrationAlgorithm::VVAK));
1132         CHECK((ir->nsttcouple != 1) || (ir->nstpcouple != 1));
1133     }
1134
1135     if (ETC_ANDERSEN(ir->etc))
1136     {
1137         sprintf(err_buf,
1138                 "%s temperature control not supported for integrator %s.",
1139                 enumValueToString(ir->etc),
1140                 enumValueToString(ir->eI));
1141         CHECK(!(EI_VV(ir->eI)));
1142
1143         if (ir->nstcomm > 0 && (ir->etc == TemperatureCoupling::Andersen))
1144         {
1145             sprintf(warn_buf,
1146                     "Center of mass removal not necessary for %s.  All velocities of coupled "
1147                     "groups are rerandomized periodically, so flying ice cube errors will not "
1148                     "occur.",
1149                     enumValueToString(ir->etc));
1150             warning_note(wi, warn_buf);
1151         }
1152
1153         sprintf(err_buf,
1154                 "nstcomm must be 1, not %d for %s, as velocities of atoms in coupled groups are "
1155                 "randomized every time step",
1156                 ir->nstcomm,
1157                 enumValueToString(ir->etc));
1158         CHECK(ir->nstcomm > 1 && (ir->etc == TemperatureCoupling::Andersen));
1159     }
1160
1161     if (ir->etc == TemperatureCoupling::Berendsen)
1162     {
1163         sprintf(warn_buf,
1164                 "The %s thermostat does not generate the correct kinetic energy distribution. You "
1165                 "might want to consider using the %s thermostat.",
1166                 enumValueToString(ir->etc),
1167                 enumValueToString(TemperatureCoupling::VRescale));
1168         warning_note(wi, warn_buf);
1169     }
1170
1171     if ((ir->etc == TemperatureCoupling::NoseHoover || ETC_ANDERSEN(ir->etc))
1172         && ir->epc == PressureCoupling::Berendsen)
1173     {
1174         sprintf(warn_buf,
1175                 "Using Berendsen pressure coupling invalidates the "
1176                 "true ensemble for the thermostat");
1177         warning(wi, warn_buf);
1178     }
1179
1180     /* PRESSURE COUPLING */
1181     if (ir->epc == PressureCoupling::Isotropic)
1182     {
1183         ir->epc = PressureCoupling::Berendsen;
1184         warning_note(wi,
1185                      "Old option for pressure coupling given: "
1186                      "changing \"Isotropic\" to \"Berendsen\"\n");
1187     }
1188
1189     if (ir->epc != PressureCoupling::No)
1190     {
1191         dt_pcoupl = ir->nstpcouple * ir->delta_t;
1192
1193         sprintf(err_buf, "tau-p must be > 0 instead of %g\n", ir->tau_p);
1194         CHECK(ir->tau_p <= 0);
1195
1196         if (ir->tau_p / dt_pcoupl < pcouple_min_integration_steps(ir->epc) - 10 * GMX_REAL_EPS)
1197         {
1198             sprintf(warn_buf,
1199                     "For proper integration of the %s barostat, tau-p (%g) should be at least %d "
1200                     "times larger than nstpcouple*dt (%g)",
1201                     enumValueToString(ir->epc),
1202                     ir->tau_p,
1203                     pcouple_min_integration_steps(ir->epc),
1204                     dt_pcoupl);
1205             warning(wi, warn_buf);
1206         }
1207
1208         sprintf(err_buf,
1209                 "compressibility must be > 0 when using pressure"
1210                 " coupling %s\n",
1211                 enumValueToString(ir->epc));
1212         CHECK(ir->compress[XX][XX] < 0 || ir->compress[YY][YY] < 0 || ir->compress[ZZ][ZZ] < 0
1213               || (trace(ir->compress) == 0 && ir->compress[YY][XX] <= 0 && ir->compress[ZZ][XX] <= 0
1214                   && ir->compress[ZZ][YY] <= 0));
1215
1216         if (PressureCoupling::ParrinelloRahman == ir->epc && opts->bGenVel)
1217         {
1218             sprintf(warn_buf,
1219                     "You are generating velocities so I am assuming you "
1220                     "are equilibrating a system. You are using "
1221                     "%s pressure coupling, but this can be "
1222                     "unstable for equilibration. If your system crashes, try "
1223                     "equilibrating first with Berendsen pressure coupling. If "
1224                     "you are not equilibrating the system, you can probably "
1225                     "ignore this warning.",
1226                     enumValueToString(ir->epc));
1227             warning(wi, warn_buf);
1228         }
1229     }
1230
1231     if (!EI_VV(ir->eI))
1232     {
1233         if (ir->epc == PressureCoupling::Mttk)
1234         {
1235             warning_error(wi, "MTTK pressure coupling requires a Velocity-verlet integrator");
1236         }
1237     }
1238
1239     /* ELECTROSTATICS */
1240     /* More checks are in triple check (grompp.c) */
1241
1242     if (ir->coulombtype == CoulombInteractionType::Switch)
1243     {
1244         sprintf(warn_buf,
1245                 "coulombtype = %s is only for testing purposes and can lead to serious "
1246                 "artifacts, advice: use coulombtype = %s",
1247                 enumValueToString(ir->coulombtype),
1248                 enumValueToString(CoulombInteractionType::RFZero));
1249         warning(wi, warn_buf);
1250     }
1251
1252     if (EEL_RF(ir->coulombtype) && ir->epsilon_rf == 1 && ir->epsilon_r != 1)
1253     {
1254         sprintf(warn_buf,
1255                 "epsilon-r = %g and epsilon-rf = 1 with reaction field, proceeding assuming old "
1256                 "format and exchanging epsilon-r and epsilon-rf",
1257                 ir->epsilon_r);
1258         warning(wi, warn_buf);
1259         ir->epsilon_rf = ir->epsilon_r;
1260         ir->epsilon_r  = 1.0;
1261     }
1262
1263     if (ir->epsilon_r == 0)
1264     {
1265         sprintf(err_buf,
1266                 "It is pointless to use long-range electrostatics with infinite relative "
1267                 "permittivity."
1268                 "Since you are effectively turning of electrostatics, a plain cutoff will be much "
1269                 "faster.");
1270         CHECK(EEL_FULL(ir->coulombtype));
1271     }
1272
1273     if (getenv("GMX_DO_GALACTIC_DYNAMICS") == nullptr)
1274     {
1275         sprintf(err_buf, "epsilon-r must be >= 0 instead of %g\n", ir->epsilon_r);
1276         CHECK(ir->epsilon_r < 0);
1277     }
1278
1279     if (EEL_RF(ir->coulombtype))
1280     {
1281         /* reaction field (at the cut-off) */
1282
1283         if (ir->coulombtype == CoulombInteractionType::RFZero && ir->epsilon_rf != 0)
1284         {
1285             sprintf(warn_buf,
1286                     "With coulombtype = %s, epsilon-rf must be 0, assuming you meant epsilon_rf=0",
1287                     enumValueToString(ir->coulombtype));
1288             warning(wi, warn_buf);
1289             ir->epsilon_rf = 0.0;
1290         }
1291
1292         sprintf(err_buf, "epsilon-rf must be >= epsilon-r");
1293         CHECK((ir->epsilon_rf < ir->epsilon_r && ir->epsilon_rf != 0) || (ir->epsilon_r == 0));
1294         if (ir->epsilon_rf == ir->epsilon_r)
1295         {
1296             sprintf(warn_buf,
1297                     "Using epsilon-rf = epsilon-r with %s does not make sense",
1298                     enumValueToString(ir->coulombtype));
1299             warning(wi, warn_buf);
1300         }
1301     }
1302     /* Allow rlist>rcoulomb for tabulated long range stuff. This just
1303      * means the interaction is zero outside rcoulomb, but it helps to
1304      * provide accurate energy conservation.
1305      */
1306     if (ir_coulomb_might_be_zero_at_cutoff(ir))
1307     {
1308         if (ir_coulomb_switched(ir))
1309         {
1310             sprintf(err_buf,
1311                     "With coulombtype = %s rcoulomb_switch must be < rcoulomb. Or, better: Use the "
1312                     "potential modifier options!",
1313                     enumValueToString(ir->coulombtype));
1314             CHECK(ir->rcoulomb_switch >= ir->rcoulomb);
1315         }
1316     }
1317
1318     if (ir->coulombtype == CoulombInteractionType::Switch || ir->coulombtype == CoulombInteractionType::Shift)
1319     {
1320         sprintf(err_buf,
1321                 "Explicit switch/shift coulomb interactions cannot be used in combination with a "
1322                 "secondary coulomb-modifier.");
1323         CHECK(ir->coulomb_modifier != InteractionModifiers::None);
1324     }
1325     if (ir->vdwtype == VanDerWaalsType::Switch || ir->vdwtype == VanDerWaalsType::Shift)
1326     {
1327         sprintf(err_buf,
1328                 "Explicit switch/shift vdw interactions cannot be used in combination with a "
1329                 "secondary vdw-modifier.");
1330         CHECK(ir->vdw_modifier != InteractionModifiers::None);
1331     }
1332
1333     if (ir->coulombtype == CoulombInteractionType::Switch || ir->coulombtype == CoulombInteractionType::Shift
1334         || ir->vdwtype == VanDerWaalsType::Switch || ir->vdwtype == VanDerWaalsType::Shift)
1335     {
1336         sprintf(warn_buf,
1337                 "The switch/shift interaction settings are just for compatibility; you will get "
1338                 "better "
1339                 "performance from applying potential modifiers to your interactions!\n");
1340         warning_note(wi, warn_buf);
1341     }
1342
1343     if (ir->coulombtype == CoulombInteractionType::PmeSwitch
1344         || ir->coulomb_modifier == InteractionModifiers::PotSwitch)
1345     {
1346         if (ir->rcoulomb_switch / ir->rcoulomb < 0.9499)
1347         {
1348             real percentage = 100 * (ir->rcoulomb - ir->rcoulomb_switch) / ir->rcoulomb;
1349             sprintf(warn_buf,
1350                     "The switching range should be 5%% or less (currently %.2f%% using a switching "
1351                     "range of %4f-%4f) for accurate electrostatic energies, energy conservation "
1352                     "will be good regardless, since ewald_rtol = %g.",
1353                     percentage,
1354                     ir->rcoulomb_switch,
1355                     ir->rcoulomb,
1356                     ir->ewald_rtol);
1357             warning(wi, warn_buf);
1358         }
1359     }
1360
1361     if (ir->vdwtype == VanDerWaalsType::Switch || ir->vdw_modifier == InteractionModifiers::PotSwitch)
1362     {
1363         if (ir->rvdw_switch == 0)
1364         {
1365             sprintf(warn_buf,
1366                     "rvdw-switch is equal 0 even though you are using a switched Lennard-Jones "
1367                     "potential.  This suggests it was not set in the mdp, which can lead to large "
1368                     "energy errors.  In GROMACS, 0.05 to 0.1 nm is often a reasonable vdw "
1369                     "switching range.");
1370             warning(wi, warn_buf);
1371         }
1372     }
1373
1374     if (EEL_FULL(ir->coulombtype))
1375     {
1376         if (ir->coulombtype == CoulombInteractionType::PmeSwitch
1377             || ir->coulombtype == CoulombInteractionType::PmeUser
1378             || ir->coulombtype == CoulombInteractionType::PmeUserSwitch)
1379         {
1380             sprintf(err_buf,
1381                     "With coulombtype = %s, rcoulomb must be <= rlist",
1382                     enumValueToString(ir->coulombtype));
1383             CHECK(ir->rcoulomb > ir->rlist);
1384         }
1385     }
1386
1387     if (EEL_PME(ir->coulombtype) || EVDW_PME(ir->vdwtype))
1388     {
1389         // TODO: Move these checks into the ewald module with the options class
1390         int orderMin = 3;
1391         int orderMax = (ir->coulombtype == CoulombInteractionType::P3mAD ? 8 : 12);
1392
1393         if (ir->pme_order < orderMin || ir->pme_order > orderMax)
1394         {
1395             sprintf(warn_buf,
1396                     "With coulombtype = %s, you should have %d <= pme-order <= %d",
1397                     enumValueToString(ir->coulombtype),
1398                     orderMin,
1399                     orderMax);
1400             warning_error(wi, warn_buf);
1401         }
1402     }
1403
1404     if (ir->nwall == 2 && EEL_FULL(ir->coulombtype))
1405     {
1406         if (ir->ewald_geometry == EwaldGeometry::ThreeD)
1407         {
1408             sprintf(warn_buf,
1409                     "With pbc=%s you should use ewald-geometry=%s",
1410                     c_pbcTypeNames[ir->pbcType].c_str(),
1411                     enumValueToString(EwaldGeometry::ThreeDC));
1412             warning(wi, warn_buf);
1413         }
1414         /* This check avoids extra pbc coding for exclusion corrections */
1415         sprintf(err_buf, "wall-ewald-zfac should be >= 2");
1416         CHECK(ir->wall_ewald_zfac < 2);
1417     }
1418     if ((ir->ewald_geometry == EwaldGeometry::ThreeDC) && (ir->pbcType != PbcType::XY)
1419         && EEL_FULL(ir->coulombtype))
1420     {
1421         sprintf(warn_buf,
1422                 "With %s and ewald_geometry = %s you should use pbc = %s",
1423                 enumValueToString(ir->coulombtype),
1424                 enumValueToString(EwaldGeometry::ThreeDC),
1425                 c_pbcTypeNames[PbcType::XY].c_str());
1426         warning(wi, warn_buf);
1427     }
1428     if ((ir->epsilon_surface != 0) && EEL_FULL(ir->coulombtype))
1429     {
1430         sprintf(err_buf, "Cannot have periodic molecules with epsilon_surface > 0");
1431         CHECK(ir->bPeriodicMols);
1432         sprintf(warn_buf, "With epsilon_surface > 0 all molecules should be neutral.");
1433         warning_note(wi, warn_buf);
1434         sprintf(warn_buf,
1435                 "With epsilon_surface > 0 you can only use domain decomposition "
1436                 "when there are only small molecules with all bonds constrained (mdrun will check "
1437                 "for this).");
1438         warning_note(wi, warn_buf);
1439     }
1440
1441     if (ir_vdw_switched(ir))
1442     {
1443         sprintf(err_buf, "With switched vdw forces or potentials, rvdw-switch must be < rvdw");
1444         CHECK(ir->rvdw_switch >= ir->rvdw);
1445
1446         if (ir->rvdw_switch < 0.5 * ir->rvdw)
1447         {
1448             sprintf(warn_buf,
1449                     "You are applying a switch function to vdw forces or potentials from %g to %g "
1450                     "nm, which is more than half the interaction range, whereas switch functions "
1451                     "are intended to act only close to the cut-off.",
1452                     ir->rvdw_switch,
1453                     ir->rvdw);
1454             warning_note(wi, warn_buf);
1455         }
1456     }
1457
1458     if (ir->vdwtype == VanDerWaalsType::Pme)
1459     {
1460         if (!(ir->vdw_modifier == InteractionModifiers::None
1461               || ir->vdw_modifier == InteractionModifiers::PotShift))
1462         {
1463             sprintf(err_buf,
1464                     "With vdwtype = %s, the only supported modifiers are %s and %s",
1465                     enumValueToString(ir->vdwtype),
1466                     enumValueToString(InteractionModifiers::PotShift),
1467                     enumValueToString(InteractionModifiers::None));
1468             warning_error(wi, err_buf);
1469         }
1470     }
1471
1472     if (ir->vdwtype == VanDerWaalsType::User && ir->eDispCorr != DispersionCorrectionType::No)
1473     {
1474         warning_note(wi,
1475                      "You have selected user tables with dispersion correction, the dispersion "
1476                      "will be corrected to -C6/r^6 beyond rvdw_switch (the tabulated interaction "
1477                      "between rvdw_switch and rvdw will not be double counted). Make sure that you "
1478                      "really want dispersion correction to -C6/r^6.");
1479     }
1480
1481     if (ir->eI == IntegrationAlgorithm::LBFGS
1482         && (ir->coulombtype == CoulombInteractionType::Cut || ir->vdwtype == VanDerWaalsType::Cut)
1483         && ir->rvdw != 0)
1484     {
1485         warning(wi, "For efficient BFGS minimization, use switch/shift/pme instead of cut-off.");
1486     }
1487
1488     if (ir->eI == IntegrationAlgorithm::LBFGS && ir->nbfgscorr <= 0)
1489     {
1490         warning(wi, "Using L-BFGS with nbfgscorr<=0 just gets you steepest descent.");
1491     }
1492
1493     /* IMPLICIT SOLVENT */
1494     if (ir->coulombtype == CoulombInteractionType::GBNotused)
1495     {
1496         sprintf(warn_buf, "Invalid option %s for coulombtype", enumValueToString(ir->coulombtype));
1497         warning_error(wi, warn_buf);
1498     }
1499
1500     if (ir->bQMMM)
1501     {
1502         warning_error(wi, "The QMMM integration you are trying to use is no longer supported");
1503     }
1504
1505     if (ir->bAdress)
1506     {
1507         gmx_fatal(FARGS, "AdResS simulations are no longer supported");
1508     }
1509
1510     // cosine acceleration is only supported in leap-frog
1511     if (ir->cos_accel != 0.0 && ir->eI != IntegrationAlgorithm::MD)
1512     {
1513         warning_error(wi, "cos-acceleration is only supported by integrator = md");
1514     }
1515 }
1516
1517 /* interpret a number of doubles from a string and put them in an array,
1518    after allocating space for them.
1519    str = the input string
1520    n = the (pre-allocated) number of doubles read
1521    r = the output array of doubles. */
1522 static std::vector<real> parse_n_real(const std::string& str, int* n, warninp_t wi)
1523 {
1524     auto values = gmx::splitString(str);
1525     *n          = values.size();
1526
1527     std::vector<real> r;
1528     for (int i = 0; i < *n; i++)
1529     {
1530         try
1531         {
1532             r.emplace_back(gmx::fromString<real>(values[i]));
1533         }
1534         catch (gmx::GromacsException&)
1535         {
1536             warning_error(wi,
1537                           "Invalid value " + values[i]
1538                                   + " in string in mdp file. Expected a real number.");
1539         }
1540     }
1541     return r;
1542 }
1543
1544
1545 static void do_fep_params(t_inputrec* ir, gmx::ArrayRef<std::string> fep_lambda, char weights[STRLEN], warninp_t wi)
1546 {
1547
1548     int         i, j, max_n_lambda, nweights;
1549     t_lambda*   fep    = ir->fepvals.get();
1550     t_expanded* expand = ir->expandedvals.get();
1551     gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, std::vector<real>> count_fep_lambdas;
1552     bool                                                                         bOneLambda = TRUE;
1553     gmx::EnumerationArray<FreeEnergyPerturbationCouplingType, int>               nfep;
1554
1555     /* FEP input processing */
1556     /* first, identify the number of lambda values for each type.
1557        All that are nonzero must have the same number */
1558
1559     for (auto i : keysOf(nfep))
1560     {
1561         count_fep_lambdas[i] = parse_n_real(fep_lambda[static_cast<int>(i)], &(nfep[i]), wi);
1562     }
1563
1564     /* now, determine the number of components.  All must be either zero, or equal. */
1565
1566     max_n_lambda = 0;
1567     for (auto i : keysOf(nfep))
1568     {
1569         if (nfep[i] > max_n_lambda)
1570         {
1571             max_n_lambda = nfep[i]; /* here's a nonzero one.  All of them
1572                                        must have the same number if its not zero.*/
1573             break;
1574         }
1575     }
1576
1577     for (auto i : keysOf(nfep))
1578     {
1579         if (nfep[i] == 0)
1580         {
1581             ir->fepvals->separate_dvdl[i] = FALSE;
1582         }
1583         else if (nfep[i] == max_n_lambda)
1584         {
1585             if (i != FreeEnergyPerturbationCouplingType::Temperature) /* we treat this differently -- not really a reason to compute
1586                                          the derivative with respect to the temperature currently */
1587             {
1588                 ir->fepvals->separate_dvdl[i] = TRUE;
1589             }
1590         }
1591         else
1592         {
1593             gmx_fatal(FARGS,
1594                       "Number of lambdas (%d) for FEP type %s not equal to number of other types "
1595                       "(%d)",
1596                       nfep[i],
1597                       enumValueToString(i),
1598                       max_n_lambda);
1599         }
1600     }
1601     /* we don't print out dhdl if the temperature is changing, since we can't correctly define dhdl in this case */
1602     ir->fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Temperature] = FALSE;
1603
1604     /* the number of lambdas is the number we've read in, which is either zero
1605        or the same for all */
1606     fep->n_lambda = max_n_lambda;
1607
1608     /* if init_lambda is defined, we need to set lambda */
1609     if ((fep->init_lambda > 0) && (fep->n_lambda == 0))
1610     {
1611         ir->fepvals->separate_dvdl[FreeEnergyPerturbationCouplingType::Fep] = TRUE;
1612     }
1613     /* otherwise allocate the space for all of the lambdas, and transfer the data */
1614     for (auto i : keysOf(nfep))
1615     {
1616         fep->all_lambda[i].resize(fep->n_lambda);
1617         if (nfep[i] > 0) /* if it's zero, then the count_fep_lambda arrays
1618                             are zero */
1619         {
1620             for (j = 0; j < fep->n_lambda; j++)
1621             {
1622                 fep->all_lambda[i][j] = static_cast<double>(count_fep_lambdas[i][j]);
1623             }
1624         }
1625     }
1626
1627     /* "fep-vals" is either zero or the full number. If zero, we'll need to define fep-lambdas for
1628        internal bookkeeping -- for now, init_lambda */
1629
1630     if ((nfep[FreeEnergyPerturbationCouplingType::Fep] == 0) && (fep->init_lambda >= 0))
1631     {
1632         for (i = 0; i < fep->n_lambda; i++)
1633         {
1634             fep->all_lambda[FreeEnergyPerturbationCouplingType::Fep][i] = fep->init_lambda;
1635         }
1636     }
1637
1638     /* check to see if only a single component lambda is defined, and soft core is defined.
1639        In this case, turn on coulomb soft core */
1640
1641     if (max_n_lambda == 0)
1642     {
1643         bOneLambda = TRUE;
1644     }
1645     else
1646     {
1647         for (auto i : keysOf(nfep))
1648         {
1649             if ((nfep[i] != 0) && (i != FreeEnergyPerturbationCouplingType::Fep))
1650             {
1651                 bOneLambda = FALSE;
1652             }
1653         }
1654     }
1655     if ((bOneLambda) && (fep->sc_alpha > 0))
1656     {
1657         fep->bScCoul = TRUE;
1658     }
1659
1660     /* Fill in the others with the efptFEP if they are not explicitly
1661        specified (i.e. nfep[i] == 0).  This means if fep is not defined,
1662        they are all zero. */
1663
1664     for (auto i : keysOf(nfep))
1665     {
1666         if ((nfep[i] == 0) && (i != FreeEnergyPerturbationCouplingType::Fep))
1667         {
1668             for (j = 0; j < fep->n_lambda; j++)
1669             {
1670                 fep->all_lambda[i][j] = fep->all_lambda[FreeEnergyPerturbationCouplingType::Fep][j];
1671             }
1672         }
1673     }
1674
1675
1676     /* now read in the weights */
1677     expand->init_lambda_weights = parse_n_real(weights, &nweights, wi);
1678     if (nweights == 0)
1679     {
1680         expand->init_lambda_weights.resize(fep->n_lambda); /* initialize to zero */
1681     }
1682     else if (nweights != fep->n_lambda)
1683     {
1684         gmx_fatal(FARGS,
1685                   "Number of weights (%d) is not equal to number of lambda values (%d)",
1686                   nweights,
1687                   fep->n_lambda);
1688     }
1689     if ((expand->nstexpanded < 0) && (ir->efep != FreeEnergyPerturbationType::No))
1690     {
1691         expand->nstexpanded = fep->nstdhdl;
1692         /* if you don't specify nstexpanded when doing expanded ensemble free energy calcs, it is set to nstdhdl */
1693     }
1694 }
1695
1696
1697 static void do_simtemp_params(t_inputrec* ir)
1698 {
1699     ir->simtempvals->temperatures.resize(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 = std::make_unique<gmx::AwhParams>(&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::MDModulesNotifiers& mdModulesNotifiers,
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     mdModulesNotifiers.preProcessingNotifier_.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     const t_iparams* pr;
4116
4117     clear_ivec(AbsRef);
4118
4119     if (!posres_only)
4120     {
4121         /* Check the COM */
4122         for (d = 0; d < DIM; d++)
4123         {
4124             AbsRef[d] = (d < ndof_com(ir) ? 0 : 1);
4125         }
4126         /* Check for freeze groups */
4127         for (g = 0; g < ir->opts.ngfrz; g++)
4128         {
4129             for (d = 0; d < DIM; d++)
4130             {
4131                 if (ir->opts.nFreeze[g][d] != 0)
4132                 {
4133                     AbsRef[d] = 1;
4134                 }
4135             }
4136         }
4137     }
4138
4139     /* Check for position restraints */
4140     for (const auto ilist : IListRange(sys))
4141     {
4142         if (ilist.nmol() > 0 && (AbsRef[XX] == 0 || AbsRef[YY] == 0 || AbsRef[ZZ] == 0))
4143         {
4144             for (int i = 0; i < ilist.list()[F_POSRES].size(); i += 2)
4145             {
4146                 pr = &sys.ffparams.iparams[ilist.list()[F_POSRES].iatoms[i]];
4147                 for (d = 0; d < DIM; d++)
4148                 {
4149                     if (pr->posres.fcA[d] != 0)
4150                     {
4151                         AbsRef[d] = 1;
4152                     }
4153                 }
4154             }
4155             for (i = 0; i < ilist.list()[F_FBPOSRES].size(); i += 2)
4156             {
4157                 /* Check for flat-bottom posres */
4158                 pr = &sys.ffparams.iparams[ilist.list()[F_FBPOSRES].iatoms[i]];
4159                 if (pr->fbposres.k != 0)
4160                 {
4161                     switch (pr->fbposres.geom)
4162                     {
4163                         case efbposresSPHERE: AbsRef[XX] = AbsRef[YY] = AbsRef[ZZ] = 1; break;
4164                         case efbposresCYLINDERX: AbsRef[YY] = AbsRef[ZZ] = 1; break;
4165                         case efbposresCYLINDERY: AbsRef[XX] = AbsRef[ZZ] = 1; break;
4166                         case efbposresCYLINDER:
4167                         /* efbposres is a synonym for efbposresCYLINDERZ for backwards compatibility */
4168                         case efbposresCYLINDERZ: AbsRef[XX] = AbsRef[YY] = 1; break;
4169                         case efbposresX: /* d=XX */
4170                         case efbposresY: /* d=YY */
4171                         case efbposresZ: /* d=ZZ */
4172                             d         = pr->fbposres.geom - efbposresX;
4173                             AbsRef[d] = 1;
4174                             break;
4175                         default:
4176                             gmx_fatal(FARGS,
4177                                       " Invalid geometry for flat-bottom position restraint.\n"
4178                                       "Expected nr between 1 and %d. Found %d\n",
4179                                       efbposresNR - 1,
4180                                       pr->fbposres.geom);
4181                     }
4182                 }
4183             }
4184         }
4185     }
4186
4187     return (AbsRef[XX] != 0 && AbsRef[YY] != 0 && AbsRef[ZZ] != 0);
4188 }
4189
4190 static void check_combination_rule_differences(const gmx_mtop_t& mtop,
4191                                                int               state,
4192                                                bool* bC6ParametersWorkWithGeometricRules,
4193                                                bool* bC6ParametersWorkWithLBRules,
4194                                                bool* bLBRulesPossible)
4195 {
4196     int         ntypes, tpi, tpj;
4197     int*        typecount;
4198     real        tol;
4199     double      c6i, c6j, c12i, c12j;
4200     double      c6, c6_geometric, c6_LB;
4201     double      sigmai, sigmaj, epsi, epsj;
4202     bool        bCanDoLBRules, bCanDoGeometricRules;
4203     const char* ptr;
4204
4205     /* A tolerance of 1e-5 seems reasonable for (possibly hand-typed)
4206      * force-field floating point parameters.
4207      */
4208     tol = 1e-5;
4209     ptr = getenv("GMX_LJCOMB_TOL");
4210     if (ptr != nullptr)
4211     {
4212         double dbl;
4213         double gmx_unused canary;
4214
4215         if (sscanf(ptr, "%lf%lf", &dbl, &canary) != 1)
4216         {
4217             gmx_fatal(
4218                     FARGS, "Could not parse a single floating-point number from GMX_LJCOMB_TOL (%s)", ptr);
4219         }
4220         tol = dbl;
4221     }
4222
4223     *bC6ParametersWorkWithLBRules        = TRUE;
4224     *bC6ParametersWorkWithGeometricRules = TRUE;
4225     bCanDoLBRules                        = TRUE;
4226     ntypes                               = mtop.ffparams.atnr;
4227     snew(typecount, ntypes);
4228     gmx_mtop_count_atomtypes(mtop, state, typecount);
4229     *bLBRulesPossible = TRUE;
4230     for (tpi = 0; tpi < ntypes; ++tpi)
4231     {
4232         c6i  = mtop.ffparams.iparams[(ntypes + 1) * tpi].lj.c6;
4233         c12i = mtop.ffparams.iparams[(ntypes + 1) * tpi].lj.c12;
4234         for (tpj = tpi; tpj < ntypes; ++tpj)
4235         {
4236             c6j          = mtop.ffparams.iparams[(ntypes + 1) * tpj].lj.c6;
4237             c12j         = mtop.ffparams.iparams[(ntypes + 1) * tpj].lj.c12;
4238             c6           = mtop.ffparams.iparams[ntypes * tpi + tpj].lj.c6;
4239             c6_geometric = std::sqrt(c6i * c6j);
4240             if (!gmx_numzero(c6_geometric))
4241             {
4242                 if (!gmx_numzero(c12i) && !gmx_numzero(c12j))
4243                 {
4244                     sigmai = gmx::sixthroot(c12i / c6i);
4245                     sigmaj = gmx::sixthroot(c12j / c6j);
4246                     epsi   = c6i * c6i / (4.0 * c12i);
4247                     epsj   = c6j * c6j / (4.0 * c12j);
4248                     c6_LB  = 4.0 * std::sqrt(epsi * epsj) * gmx::power6(0.5 * (sigmai + sigmaj));
4249                 }
4250                 else
4251                 {
4252                     *bLBRulesPossible = FALSE;
4253                     c6_LB             = c6_geometric;
4254                 }
4255                 bCanDoLBRules = gmx_within_tol(c6_LB, c6, tol);
4256             }
4257
4258             if (!bCanDoLBRules)
4259             {
4260                 *bC6ParametersWorkWithLBRules = FALSE;
4261             }
4262
4263             bCanDoGeometricRules = gmx_within_tol(c6_geometric, c6, tol);
4264
4265             if (!bCanDoGeometricRules)
4266             {
4267                 *bC6ParametersWorkWithGeometricRules = FALSE;
4268             }
4269         }
4270     }
4271     sfree(typecount);
4272 }
4273
4274 static void check_combination_rules(const t_inputrec* ir, const gmx_mtop_t& mtop, warninp_t wi)
4275 {
4276     bool bLBRulesPossible, bC6ParametersWorkWithGeometricRules, bC6ParametersWorkWithLBRules;
4277
4278     check_combination_rule_differences(
4279             mtop, 0, &bC6ParametersWorkWithGeometricRules, &bC6ParametersWorkWithLBRules, &bLBRulesPossible);
4280     if (ir->ljpme_combination_rule == LongRangeVdW::LB)
4281     {
4282         if (!bC6ParametersWorkWithLBRules || !bLBRulesPossible)
4283         {
4284             warning(wi,
4285                     "You are using arithmetic-geometric combination rules "
4286                     "in LJ-PME, but your non-bonded C6 parameters do not "
4287                     "follow these rules.");
4288         }
4289     }
4290     else
4291     {
4292         if (!bC6ParametersWorkWithGeometricRules)
4293         {
4294             if (ir->eDispCorr != DispersionCorrectionType::No)
4295             {
4296                 warning_note(wi,
4297                              "You are using geometric combination rules in "
4298                              "LJ-PME, but your non-bonded C6 parameters do "
4299                              "not follow these rules. "
4300                              "This will introduce very small errors in the forces and energies in "
4301                              "your simulations. Dispersion correction will correct total energy "
4302                              "and/or pressure for isotropic systems, but not forces or surface "
4303                              "tensions.");
4304             }
4305             else
4306             {
4307                 warning_note(wi,
4308                              "You are using geometric combination rules in "
4309                              "LJ-PME, but your non-bonded C6 parameters do "
4310                              "not follow these rules. "
4311                              "This will introduce very small errors in the forces and energies in "
4312                              "your simulations. If your system is homogeneous, consider using "
4313                              "dispersion correction "
4314                              "for the total energy and pressure.");
4315             }
4316         }
4317     }
4318 }
4319
4320 void triple_check(const char* mdparin, t_inputrec* ir, gmx_mtop_t* sys, warninp_t wi)
4321 {
4322     // Not meeting MTS requirements should have resulted in a fatal error, so we can assert here
4323     GMX_ASSERT(gmx::checkMtsRequirements(*ir).empty(), "All MTS requirements should be met here");
4324
4325     char                      err_buf[STRLEN];
4326     int                       i, m, c, nmol;
4327     bool                      bCharge;
4328     gmx_mtop_atomloop_block_t aloopb;
4329     ivec                      AbsRef;
4330     char                      warn_buf[STRLEN];
4331
4332     set_warning_line(wi, mdparin, -1);
4333
4334     if (absolute_reference(ir, *sys, false, AbsRef))
4335     {
4336         warning_note(wi,
4337                      "Removing center of mass motion in the presence of position restraints might "
4338                      "cause artifacts. When you are using position restraints to equilibrate a "
4339                      "macro-molecule, the artifacts are usually negligible.");
4340     }
4341
4342     if (ir->cutoff_scheme == CutoffScheme::Verlet && ir->verletbuf_tol > 0 && ir->nstlist > 1
4343         && ((EI_MD(ir->eI) || EI_SD(ir->eI))
4344             && (ir->etc == TemperatureCoupling::VRescale || ir->etc == TemperatureCoupling::Berendsen)))
4345     {
4346         /* Check if a too small Verlet buffer might potentially
4347          * cause more drift than the thermostat can couple off.
4348          */
4349         /* Temperature error fraction for warning and suggestion */
4350         const real T_error_warn    = 0.002;
4351         const real T_error_suggest = 0.001;
4352         /* For safety: 2 DOF per atom (typical with constraints) */
4353         const real nrdf_at = 2;
4354         real       T, tau, max_T_error;
4355         int        i;
4356
4357         T   = 0;
4358         tau = 0;
4359         for (i = 0; i < ir->opts.ngtc; i++)
4360         {
4361             T   = std::max(T, ir->opts.ref_t[i]);
4362             tau = std::max(tau, ir->opts.tau_t[i]);
4363         }
4364         if (T > 0)
4365         {
4366             /* This is a worst case estimate of the temperature error,
4367              * assuming perfect buffer estimation and no cancelation
4368              * of errors. The factor 0.5 is because energy distributes
4369              * equally over Ekin and Epot.
4370              */
4371             max_T_error = 0.5 * tau * ir->verletbuf_tol / (nrdf_at * gmx::c_boltz * T);
4372             if (max_T_error > T_error_warn)
4373             {
4374                 sprintf(warn_buf,
4375                         "With a verlet-buffer-tolerance of %g kJ/mol/ps, a reference temperature "
4376                         "of %g and a tau_t of %g, your temperature might be off by up to %.1f%%. "
4377                         "To ensure the error is below %.1f%%, decrease verlet-buffer-tolerance to "
4378                         "%.0e or decrease tau_t.",
4379                         ir->verletbuf_tol,
4380                         T,
4381                         tau,
4382                         100 * max_T_error,
4383                         100 * T_error_suggest,
4384                         ir->verletbuf_tol * T_error_suggest / max_T_error);
4385                 warning(wi, warn_buf);
4386             }
4387         }
4388     }
4389
4390     if (ETC_ANDERSEN(ir->etc))
4391     {
4392         int i;
4393
4394         for (i = 0; i < ir->opts.ngtc; i++)
4395         {
4396             sprintf(err_buf,
4397                     "all tau_t must currently be equal using Andersen temperature control, "
4398                     "violated for group %d",
4399                     i);
4400             CHECK(ir->opts.tau_t[0] != ir->opts.tau_t[i]);
4401             sprintf(err_buf,
4402                     "all tau_t must be positive using Andersen temperature control, "
4403                     "tau_t[%d]=%10.6f",
4404                     i,
4405                     ir->opts.tau_t[i]);
4406             CHECK(ir->opts.tau_t[i] < 0);
4407         }
4408
4409         if (ir->etc == TemperatureCoupling::AndersenMassive && ir->comm_mode != ComRemovalAlgorithm::No)
4410         {
4411             for (i = 0; i < ir->opts.ngtc; i++)
4412             {
4413                 int nsteps = gmx::roundToInt(ir->opts.tau_t[i] / ir->delta_t);
4414                 sprintf(err_buf,
4415                         "tau_t/delta_t for group %d for temperature control method %s must be a "
4416                         "multiple of nstcomm (%d), as velocities of atoms in coupled groups are "
4417                         "randomized every time step. The input tau_t (%8.3f) leads to %d steps per "
4418                         "randomization",
4419                         i,
4420                         enumValueToString(ir->etc),
4421                         ir->nstcomm,
4422                         ir->opts.tau_t[i],
4423                         nsteps);
4424                 CHECK(nsteps % ir->nstcomm != 0);
4425             }
4426         }
4427     }
4428
4429     if (EI_DYNAMICS(ir->eI) && !EI_SD(ir->eI) && ir->eI != IntegrationAlgorithm::BD
4430         && ir->comm_mode == ComRemovalAlgorithm::No
4431         && !(absolute_reference(ir, *sys, FALSE, AbsRef) || ir->nsteps <= 10) && !ETC_ANDERSEN(ir->etc))
4432     {
4433         warning(wi,
4434                 "You are not using center of mass motion removal (mdp option comm-mode), numerical "
4435                 "rounding errors can lead to build up of kinetic energy of the center of mass");
4436     }
4437
4438     if (ir->epc == PressureCoupling::ParrinelloRahman && ir->etc == TemperatureCoupling::NoseHoover)
4439     {
4440         real tau_t_max = 0;
4441         for (int g = 0; g < ir->opts.ngtc; g++)
4442         {
4443             tau_t_max = std::max(tau_t_max, ir->opts.tau_t[g]);
4444         }
4445         if (ir->tau_p < 1.9 * tau_t_max)
4446         {
4447             std::string message = gmx::formatString(
4448                     "With %s T-coupling and %s p-coupling, "
4449                     "%s (%g) should be at least twice as large as %s (%g) to avoid resonances",
4450                     enumValueToString(ir->etc),
4451                     enumValueToString(ir->epc),
4452                     "tau-p",
4453                     ir->tau_p,
4454                     "tau-t",
4455                     tau_t_max);
4456             warning(wi, message.c_str());
4457         }
4458     }
4459
4460     /* Check for pressure coupling with absolute position restraints */
4461     if (ir->epc != PressureCoupling::No && ir->refcoord_scaling == RefCoordScaling::No)
4462     {
4463         absolute_reference(ir, *sys, TRUE, AbsRef);
4464         {
4465             for (m = 0; m < DIM; m++)
4466             {
4467                 if (AbsRef[m] && norm2(ir->compress[m]) > 0)
4468                 {
4469                     warning(wi,
4470                             "You are using pressure coupling with absolute position restraints, "
4471                             "this will give artifacts. Use the refcoord_scaling option.");
4472                     break;
4473                 }
4474             }
4475         }
4476     }
4477
4478     bCharge = FALSE;
4479     aloopb  = gmx_mtop_atomloop_block_init(*sys);
4480     const t_atom* atom;
4481     while (gmx_mtop_atomloop_block_next(aloopb, &atom, &nmol))
4482     {
4483         if (atom->q != 0 || atom->qB != 0)
4484         {
4485             bCharge = TRUE;
4486         }
4487     }
4488
4489     if (!bCharge)
4490     {
4491         if (EEL_FULL(ir->coulombtype))
4492         {
4493             sprintf(err_buf,
4494                     "You are using full electrostatics treatment %s for a system without charges.\n"
4495                     "This costs a lot of performance for just processing zeros, consider using %s "
4496                     "instead.\n",
4497                     enumValueToString(ir->coulombtype),
4498                     enumValueToString(CoulombInteractionType::Cut));
4499             warning(wi, err_buf);
4500         }
4501     }
4502     else
4503     {
4504         if (ir->coulombtype == CoulombInteractionType::Cut && ir->rcoulomb > 0)
4505         {
4506             sprintf(err_buf,
4507                     "You are using a plain Coulomb cut-off, which might produce artifacts.\n"
4508                     "You might want to consider using %s electrostatics.\n",
4509                     enumValueToString(CoulombInteractionType::Pme));
4510             warning_note(wi, err_buf);
4511         }
4512     }
4513
4514     /* Check if combination rules used in LJ-PME are the same as in the force field */
4515     if (EVDW_PME(ir->vdwtype))
4516     {
4517         check_combination_rules(ir, *sys, wi);
4518     }
4519
4520     /* Generalized reaction field */
4521     if (ir->coulombtype == CoulombInteractionType::GRFNotused)
4522     {
4523         warning_error(wi,
4524                       "Generalized reaction-field electrostatics is no longer supported. "
4525                       "You can use normal reaction-field instead and compute the reaction-field "
4526                       "constant by hand.");
4527     }
4528
4529     if (ir->efep != FreeEnergyPerturbationType::No && ir->fepvals->sc_alpha != 0
4530         && !gmx_within_tol(sys->ffparams.reppow, 12.0, 10 * GMX_DOUBLE_EPS))
4531     {
4532         gmx_fatal(FARGS, "Soft-core interactions are only supported with VdW repulsion power 12");
4533     }
4534
4535     if (ir->bPull)
4536     {
4537         bool bWarned;
4538
4539         bWarned = FALSE;
4540         for (i = 0; i < ir->pull->ncoord && !bWarned; i++)
4541         {
4542             if (ir->pull->coord[i].group[0] == 0 || ir->pull->coord[i].group[1] == 0)
4543             {
4544                 absolute_reference(ir, *sys, FALSE, AbsRef);
4545                 for (m = 0; m < DIM; m++)
4546                 {
4547                     if (ir->pull->coord[i].dim[m] && !AbsRef[m])
4548                     {
4549                         warning(wi,
4550                                 "You are using an absolute reference for pulling, but the rest of "
4551                                 "the system does not have an absolute reference. This will lead to "
4552                                 "artifacts.");
4553                         bWarned = TRUE;
4554                         break;
4555                     }
4556                 }
4557             }
4558         }
4559
4560         for (i = 0; i < 3; i++)
4561         {
4562             for (m = 0; m <= i; m++)
4563             {
4564                 if ((ir->epc != PressureCoupling::No && ir->compress[i][m] != 0) || ir->deform[i][m] != 0)
4565                 {
4566                     for (c = 0; c < ir->pull->ncoord; c++)
4567                     {
4568                         if (ir->pull->coord[c].eGeom == PullGroupGeometry::DirectionPBC
4569                             && ir->pull->coord[c].vec[m] != 0)
4570                         {
4571                             gmx_fatal(FARGS,
4572                                       "Can not have dynamic box while using pull geometry '%s' "
4573                                       "(dim %c)",
4574                                       enumValueToString(ir->pull->coord[c].eGeom),
4575                                       'x' + m);
4576                         }
4577                     }
4578                 }
4579             }
4580         }
4581     }
4582
4583     check_disre(*sys);
4584 }
4585
4586 void double_check(t_inputrec* ir, matrix box, bool bHasNormalConstraints, bool bHasAnyConstraints, warninp_t wi)
4587 {
4588     char        warn_buf[STRLEN];
4589     const char* ptr;
4590
4591     ptr = check_box(ir->pbcType, box);
4592     if (ptr)
4593     {
4594         warning_error(wi, ptr);
4595     }
4596
4597     if (bHasNormalConstraints && ir->eConstrAlg == ConstraintAlgorithm::Shake)
4598     {
4599         if (ir->shake_tol <= 0.0)
4600         {
4601             sprintf(warn_buf, "ERROR: shake-tol must be > 0 instead of %g\n", ir->shake_tol);
4602             warning_error(wi, warn_buf);
4603         }
4604     }
4605
4606     if ((ir->eConstrAlg == ConstraintAlgorithm::Lincs) && bHasNormalConstraints)
4607     {
4608         /* If we have Lincs constraints: */
4609         if (ir->eI == IntegrationAlgorithm::MD && ir->etc == TemperatureCoupling::No
4610             && ir->eConstrAlg == ConstraintAlgorithm::Lincs && ir->nLincsIter == 1)
4611         {
4612             sprintf(warn_buf,
4613                     "For energy conservation with LINCS, lincs_iter should be 2 or larger.\n");
4614             warning_note(wi, warn_buf);
4615         }
4616
4617         if ((ir->eI == IntegrationAlgorithm::CG || ir->eI == IntegrationAlgorithm::LBFGS)
4618             && (ir->nProjOrder < 8))
4619         {
4620             sprintf(warn_buf,
4621                     "For accurate %s with LINCS constraints, lincs-order should be 8 or more.",
4622                     enumValueToString(ir->eI));
4623             warning_note(wi, warn_buf);
4624         }
4625         if (ir->epc == PressureCoupling::Mttk)
4626         {
4627             warning_error(wi, "MTTK not compatible with lincs -- use shake instead.");
4628         }
4629     }
4630
4631     if (bHasAnyConstraints && ir->epc == PressureCoupling::Mttk)
4632     {
4633         warning_error(wi, "Constraints are not implemented with MTTK pressure control.");
4634     }
4635
4636     if (ir->LincsWarnAngle > 90.0)
4637     {
4638         sprintf(warn_buf, "lincs-warnangle can not be larger than 90 degrees, setting it to 90.\n");
4639         warning(wi, warn_buf);
4640         ir->LincsWarnAngle = 90.0;
4641     }
4642
4643     if (ir->pbcType != PbcType::No)
4644     {
4645         if (ir->nstlist == 0)
4646         {
4647             warning(wi,
4648                     "With nstlist=0 atoms are only put into the box at step 0, therefore drifting "
4649                     "atoms might cause the simulation to crash.");
4650         }
4651         if (gmx::square(ir->rlist) >= max_cutoff2(ir->pbcType, box))
4652         {
4653             sprintf(warn_buf,
4654                     "ERROR: The cut-off length is longer than half the shortest box vector or "
4655                     "longer than the smallest box diagonal element. Increase the box size or "
4656                     "decrease rlist.\n");
4657             warning_error(wi, warn_buf);
4658         }
4659     }
4660 }