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