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