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