2 * This file is part of the GROMACS molecular simulation package.
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 by 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.
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.
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.
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.
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.
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.
40 #include "read_params.h"
42 #include "gromacs/applied_forces/awh/awh.h"
43 #include "gromacs/fileio/readinp.h"
44 #include "gromacs/fileio/warninp.h"
45 #include "gromacs/math/units.h"
46 #include "gromacs/math/utilities.h"
47 #include "gromacs/math/vec.h"
48 #include "gromacs/mdtypes/awh_params.h"
49 #include "gromacs/mdtypes/inputrec.h"
50 #include "gromacs/mdtypes/md_enums.h"
51 #include "gromacs/mdtypes/multipletimestepping.h"
52 #include "gromacs/mdtypes/pull_params.h"
53 #include "gromacs/pbcutil/pbc.h"
54 #include "gromacs/pulling/pull.h"
55 #include "gromacs/random/seed.h"
56 #include "gromacs/topology/mtop_util.h"
57 #include "gromacs/utility/cstringutil.h"
58 #include "gromacs/utility/fatalerror.h"
59 #include "gromacs/utility/smalloc.h"
60 #include "gromacs/utility/stringutil.h"
62 #include "biasparams.h"
63 #include "biassharing.h"
68 const char* eawhtarget_names[eawhtargetNR + 1] = { "constant",
74 const char* eawhgrowth_names[eawhgrowthNR + 1] = { "exp-linear", "linear", nullptr };
76 const char* eawhpotential_names[eawhpotentialNR + 1] = { "convolved", "umbrella", nullptr };
78 const char* eawhcoordprovider_names[eawhcoordproviderNR + 1] = { "pull", "fep-lambda", nullptr };
84 * Check multiple time-stepping consistency between AWH and pull and/or FEP
86 * \param[in] inputrec Inputput parameter struct.
87 * \param[in,out] wi Struct for bookeeping warnings.
89 void checkMtsConsistency(const t_inputrec& inputrec, warninp_t wi)
96 GMX_RELEASE_ASSERT(inputrec.mtsLevels.size() == 2, "Only 2 MTS levels supported here");
98 bool usesPull = false;
100 for (int b = 0; b < inputrec.awhParams->numBias; b++)
102 const auto& biasParams = inputrec.awhParams->awhBiasParams[b];
103 for (int d = 0; d < biasParams.ndim; d++)
105 switch (biasParams.dimParams[d].eCoordProvider)
107 case eawhcoordproviderPULL: usesPull = true; break;
108 case eawhcoordproviderFREE_ENERGY_LAMBDA: usesFep = true; break;
109 default: GMX_RELEASE_ASSERT(false, "Unsupported coord provider");
113 const int awhMtsLevel = forceGroupMtsLevel(inputrec.mtsLevels, MtsForceGroups::Awh);
114 if (usesPull && forceGroupMtsLevel(inputrec.mtsLevels, MtsForceGroups::Pull) != awhMtsLevel)
117 "When AWH is applied to pull coordinates, pull and AWH should be computed at "
118 "the same MTS level");
120 if (usesFep && awhMtsLevel != ssize(inputrec.mtsLevels) - 1)
123 "When AWH is applied to the free-energy lambda with MTS, AWH should be "
124 "computed at the slow MTS level");
127 if (inputrec.awhParams->nstSampleCoord % inputrec.mtsLevels[awhMtsLevel].stepFactor != 0)
130 "With MTS applied to AWH, awh-nstsample should be a multiple of mts-factor");
135 * Check the parameters of an AWH bias pull dimension.
137 * \param[in] prefix Prefix for dimension parameters.
138 * \param[in,out] dimParams AWH dimensional parameters.
139 * \param[in] pull_params Pull parameters.
140 * \param[in,out] wi Struct for bookeeping warnings.
142 void checkPullDimParams(const std::string& prefix,
143 AwhDimParams* dimParams,
144 const pull_params_t& pull_params,
147 if (dimParams->coordIndex < 0)
150 "Failed to read a valid coordinate index for %s-coord-index. "
151 "Note that the pull coordinate indexing starts at 1.",
154 if (dimParams->coordIndex >= pull_params.ncoord)
157 "The given AWH coordinate index (%d) is larger than the number of pull "
159 dimParams->coordIndex + 1,
162 if (pull_params.coord[dimParams->coordIndex].rate != 0)
164 auto message = formatString(
165 "Setting pull-coord%d-rate (%g) is incompatible with AWH biasing this coordinate",
166 dimParams->coordIndex + 1,
167 pull_params.coord[dimParams->coordIndex].rate);
168 warning_error(wi, message);
171 if (gmx_within_tol(dimParams->end - dimParams->origin, 0, GMX_REAL_EPS))
173 auto message = formatString(
174 "The given interval length given by %s-start (%g) and %s-end (%g) is zero. "
175 "This will result in only one point along this axis in the coordinate value grid.",
180 warning(wi, message);
183 if (dimParams->forceConstant <= 0)
185 warning_error(wi, "The force AWH bias force constant should be > 0");
188 /* Grid params for each axis */
189 PullGroupGeometry eGeom = pull_params.coord[dimParams->coordIndex].eGeom;
191 /* Check that the requested interval is in allowed range */
192 if (eGeom == PullGroupGeometry::Distance)
194 if (dimParams->origin < 0 || dimParams->end < 0)
197 "%s-start (%g) or %s-end (%g) set to a negative value. With pull "
198 "geometry distance coordinate values are non-negative. "
199 "Perhaps you want to use geometry %s instead?",
204 enumValueToString(PullGroupGeometry::Direction));
207 else if (eGeom == PullGroupGeometry::Angle || eGeom == PullGroupGeometry::AngleAxis)
209 if (dimParams->origin < 0 || dimParams->end > 180)
212 "%s-start (%g) and %s-end (%g) are outside of the allowed range "
213 "0 to 180 deg for pull geometries %s and %s ",
218 enumValueToString(PullGroupGeometry::Angle),
219 enumValueToString(PullGroupGeometry::AngleAxis));
222 else if (eGeom == PullGroupGeometry::Dihedral)
224 if (dimParams->origin < -180 || dimParams->end > 180)
227 "%s-start (%g) and %s-end (%g) are outside of the allowed range "
228 "-180 to 180 deg for pull geometry %s. ",
233 enumValueToString(PullGroupGeometry::Dihedral));
239 * Check parameters of an AWH bias in a free energy lambda state dimension.
241 * \param[in] prefix Prefix for dimension parameters.
242 * \param[in,out] dimParams AWH dimensional parameters.
243 * \param[in] lambdaParams The free energy lambda related parameters.
244 * \param[in] efep This is the type of FEP calculation (efep enumerator).
245 * \param[in,out] wi Struct for bookeeping warnings.
247 void checkFepLambdaDimParams(const std::string& prefix,
248 const AwhDimParams* dimParams,
249 const t_lambda* lambdaParams,
250 const FreeEnergyPerturbationType efep,
258 "There must be free energy input if using AWH to steer the free energy lambda "
262 if (lambdaParams->lambda_neighbors != -1)
265 "When running AWH coupled to the free energy lambda state all lambda states "
266 "should be used as neighbors in order to get correct probabilities, i.e. "
267 "calc-lambda-neighbors (%d) must be %d.",
268 lambdaParams->lambda_neighbors,
272 if (efep == FreeEnergyPerturbationType::SlowGrowth || lambdaParams->delta_lambda != 0)
275 "AWH coupled to the free energy lambda state is not compatible with slow-growth "
276 "and delta-lambda must be 0.");
279 if (efep == FreeEnergyPerturbationType::Expanded)
282 "AWH is not treated like other expanded ensemble methods. Do not use expanded.");
285 if (dimParams->origin < 0)
287 opt = prefix + "-start";
289 "When running AWH coupled to the free energy lambda state the lower lambda state "
290 "for AWH, %s (%.0f), must be >= 0.",
294 if (dimParams->end >= lambdaParams->n_lambda)
296 opt = prefix + "-end";
298 "When running AWH coupled to the free energy lambda state the upper lambda state "
299 "for AWH, %s (%.0f), must be < n_lambda (%d).",
302 lambdaParams->n_lambda);
304 if (gmx_within_tol(dimParams->end - dimParams->origin, 0, GMX_REAL_EPS))
306 auto message = formatString(
307 "The given interval length given by %s-start (%g) and %s-end (%g) is zero. "
308 "This will result in only one lambda point along this free energy lambda state "
309 "axis in the coordinate value grid.",
314 warning(wi, message);
317 if (dimParams->forceConstant != 0)
321 "The force AWH bias force constant is not used with free energy lambda state as "
322 "coordinate provider.");
327 * Check that AWH FEP is not combined with incompatible decoupling.
329 * \param[in] mtop System topology.
330 * \param[in,out] wi Struct for bookeeping warnings.
332 void checkFepLambdaDimDecouplingConsistency(const gmx_mtop_t& mtop, warninp_t wi)
334 if (haveFepPerturbedMasses(mtop))
337 "Masses may not be perturbed when using the free energy lambda state as AWH "
338 "coordinate provider. If you are using fep-lambdas to specify lambda states "
339 "make sure that you also specify mass-lambdas without perturbation.");
341 if (havePerturbedConstraints(mtop))
344 "Constraints may not be perturbed when using the free energy lambda state as AWH "
345 "coordinate provider. If you are using fep-lambdas to specify lambda states "
346 "make sure that you also specify mass-lambdas without perturbation.");
351 * Read parameters of an AWH bias dimension.
353 * \param[in,out] inp Input file entries.
354 * \param[in] prefix Prefix for dimension parameters.
355 * \param[in,out] dimParams AWH dimensional parameters.
356 * \param[in,out] wi Struct for bookeeping warnings.
357 * \param[in] bComment True if comments should be printed.
359 void readDimParams(std::vector<t_inpfile>* inp,
360 const std::string& prefix,
361 AwhDimParams* dimParams,
368 printStringNoNewline(
370 "The provider of the reaction coordinate, "
371 "currently only 'pull' and 'fep-lambda' (free energy lambda state) is supported");
373 opt = prefix + "-coord-provider";
374 dimParams->eCoordProvider = get_eeenum(inp, opt, eawhcoordprovider_names, wi);
378 printStringNoNewline(inp, "The coordinate index for this dimension");
380 opt = prefix + "-coord-index";
382 coordIndexInput = get_eint(inp, opt, 1, wi);
384 /* The pull coordinate indices start at 1 in the input file, at 0 internally */
385 dimParams->coordIndex = coordIndexInput - 1;
389 printStringNoNewline(inp, "Start and end values for each coordinate dimension");
391 opt = prefix + "-start";
392 dimParams->origin = get_ereal(inp, opt, 0., wi);
393 opt = prefix + "-end";
394 dimParams->end = get_ereal(inp, opt, 0., wi);
398 printStringNoNewline(
399 inp, "The force constant for this coordinate (kJ/mol/nm^2 or kJ/mol/rad^2)");
401 opt = prefix + "-force-constant";
402 dimParams->forceConstant = get_ereal(inp, opt, 0, wi);
406 printStringNoNewline(inp, "Estimated diffusion constant (nm^2/ps, rad^2/ps or ps^-1)");
408 opt = prefix + "-diffusion";
409 dimParams->diffusion = get_ereal(inp, opt, 0, wi);
413 printStringNoNewline(inp,
414 "Diameter that needs to be sampled around a point before it is "
415 "considered covered. In FEP dimensions the cover diameter is "
416 "specified in lambda states.");
418 opt = prefix + "-cover-diameter";
419 dimParams->coverDiameter = get_ereal(inp, opt, 0, wi);
423 * Check the parameters of an AWH bias dimension.
425 * \param[in] prefix Prefix for dimension parameters.
426 * \param[in,out] dimParams AWH dimensional parameters.
427 * \param[in] ir Input parameter struct.
428 * \param[in,out] wi Struct for bookeeping warnings.
430 void checkDimParams(const std::string& prefix, AwhDimParams* dimParams, const t_inputrec* ir, warninp_t wi)
432 if (dimParams->eCoordProvider == eawhcoordproviderPULL)
437 "AWH biasing along a pull dimension is only compatible with COM pulling "
440 checkPullDimParams(prefix, dimParams, *ir->pull, wi);
442 else if (dimParams->eCoordProvider == eawhcoordproviderFREE_ENERGY_LAMBDA)
444 if (ir->efep == FreeEnergyPerturbationType::No)
447 "AWH biasing along a free energy lambda state dimension is only compatible "
448 "with free energy turned on");
450 checkFepLambdaDimParams(prefix, dimParams, ir->fepvals.get(), ir->efep, wi);
455 "AWH biasing can only be applied to pull and free energy lambda state "
461 * Check consistency of input at the AWH bias level.
463 * \param[in] awhBiasParams AWH bias parameters.
464 * \param[in,out] wi Struct for bookkeeping warnings.
466 void checkInputConsistencyAwhBias(const AwhBiasParams& awhBiasParams, warninp_t wi)
468 /* Covering diameter and sharing warning. */
469 for (int d = 0; d < awhBiasParams.ndim; d++)
471 double coverDiameter = awhBiasParams.dimParams[d].coverDiameter;
472 if (awhBiasParams.shareGroup <= 0 && coverDiameter > 0)
475 "The covering diameter is only relevant to set for bias sharing simulations.");
481 * Read parameters of an AWH bias.
483 * \param[in,out] inp Input file entries.
484 * \param[in,out] awhBiasParams AWH dimensional parameters.
485 * \param[in] prefix Prefix for bias parameters.
486 * \param[in,out] wi Struct for bookeeping warnings.
487 * \param[in] bComment True if comments should be printed.
489 void readBiasParams(std::vector<t_inpfile>* inp,
490 AwhBiasParams* awhBiasParams,
491 const std::string& prefix,
497 printStringNoNewline(inp, "Estimated initial PMF error (kJ/mol)");
500 std::string opt = prefix + "-error-init";
501 awhBiasParams->errorInitial = get_ereal(inp, opt, 10, wi);
505 printStringNoNewline(inp,
506 "Growth rate of the reference histogram determining the bias update "
507 "size: exp-linear or linear");
509 opt = prefix + "-growth";
510 awhBiasParams->eGrowth = get_eeenum(inp, opt, eawhgrowth_names, wi);
514 printStringNoNewline(inp,
515 "Start the simulation by equilibrating histogram towards the target "
516 "distribution: no or yes");
518 opt = prefix + "-equilibrate-histogram";
519 awhBiasParams->equilibrateHistogram = (getEnum<Boolean>(inp, opt.c_str(), wi) != Boolean::No);
523 printStringNoNewline(
524 inp, "Target distribution type: constant, cutoff, boltzmann or local-boltzmann");
526 opt = prefix + "-target";
527 awhBiasParams->eTarget = get_eeenum(inp, opt, eawhtarget_names, wi);
531 printStringNoNewline(inp,
532 "Boltzmann beta scaling factor for target distribution types "
533 "'boltzmann' and 'boltzmann-local'");
535 opt = prefix + "-target-beta-scaling";
536 awhBiasParams->targetBetaScaling = get_ereal(inp, opt, 0, wi);
540 printStringNoNewline(inp, "Free energy cutoff value for target distribution type 'cutoff'");
542 opt = prefix + "-target-cutoff";
543 awhBiasParams->targetCutoff = get_ereal(inp, opt, 0, wi);
547 printStringNoNewline(inp, "Initialize PMF and target with user data: no or yes");
549 opt = prefix + "-user-data";
550 awhBiasParams->bUserData = getEnum<Boolean>(inp, opt.c_str(), wi) != Boolean::No;
554 printStringNoNewline(inp, "Group index to share the bias with, 0 means not shared");
556 opt = prefix + "-share-group";
557 awhBiasParams->shareGroup = get_eint(inp, opt, 0, wi);
561 printStringNoNewline(inp, "Dimensionality of the coordinate");
563 opt = prefix + "-ndim";
564 awhBiasParams->ndim = get_eint(inp, opt, 0, wi);
566 /* Check this before starting to read the AWH dimension parameters. */
567 if (awhBiasParams->ndim <= 0 || awhBiasParams->ndim > c_biasMaxNumDim)
569 gmx_fatal(FARGS, "%s (%d) needs to be > 0 and at most %d\n", opt.c_str(), awhBiasParams->ndim, c_biasMaxNumDim);
571 snew(awhBiasParams->dimParams, awhBiasParams->ndim);
572 for (int d = 0; d < awhBiasParams->ndim; d++)
574 bComment = bComment && d == 0;
575 std::string prefixdim = prefix + formatString("-dim%d", d + 1);
576 readDimParams(inp, prefixdim, &awhBiasParams->dimParams[d], wi, bComment);
581 * Check the parameters of an AWH bias.
583 * \param[in] awhBiasParams AWH dimensional parameters.
584 * \param[in] prefix Prefix for bias parameters.
585 * \param[in] ir Input parameter struct.
586 * \param[in,out] wi Struct for bookeeping warnings.
588 void checkBiasParams(const AwhBiasParams* awhBiasParams, const std::string& prefix, const t_inputrec* ir, warninp_t wi)
590 std::string opt = prefix + "-error-init";
591 if (awhBiasParams->errorInitial <= 0)
593 gmx_fatal(FARGS, "%s needs to be > 0.", opt.c_str());
596 opt = prefix + "-equilibrate-histogram";
597 if (awhBiasParams->equilibrateHistogram && awhBiasParams->eGrowth != eawhgrowthEXP_LINEAR)
600 formatString("Option %s will only have an effect for histogram growth type '%s'.",
602 EAWHGROWTH(eawhgrowthEXP_LINEAR));
603 warning(wi, message);
606 if ((awhBiasParams->eTarget == eawhtargetLOCALBOLTZMANN)
607 && (awhBiasParams->eGrowth == eawhgrowthEXP_LINEAR))
609 auto message = formatString(
610 "Target type '%s' combined with histogram growth type '%s' is not "
611 "expected to give stable bias updates. You probably want to use growth type "
613 EAWHTARGET(eawhtargetLOCALBOLTZMANN),
614 EAWHGROWTH(eawhgrowthEXP_LINEAR),
615 EAWHGROWTH(eawhgrowthLINEAR));
616 warning(wi, message);
619 opt = prefix + "-target-beta-scaling";
620 switch (awhBiasParams->eTarget)
622 case eawhtargetBOLTZMANN:
623 case eawhtargetLOCALBOLTZMANN:
624 if (awhBiasParams->targetBetaScaling < 0 || awhBiasParams->targetBetaScaling > 1)
627 "%s = %g is not useful for target type %s.",
629 awhBiasParams->targetBetaScaling,
630 EAWHTARGET(awhBiasParams->eTarget));
634 if (awhBiasParams->targetBetaScaling != 0)
638 "Value for %s (%g) set explicitly but will not be used for target type %s.",
640 awhBiasParams->targetBetaScaling,
641 EAWHTARGET(awhBiasParams->eTarget));
646 opt = prefix + "-target-cutoff";
647 switch (awhBiasParams->eTarget)
649 case eawhtargetCUTOFF:
650 if (awhBiasParams->targetCutoff <= 0)
653 "%s = %g is not useful for target type %s.",
655 awhBiasParams->targetCutoff,
656 EAWHTARGET(awhBiasParams->eTarget));
660 if (awhBiasParams->targetCutoff != 0)
664 "Value for %s (%g) set explicitly but will not be used for target type %s.",
666 awhBiasParams->targetCutoff,
667 EAWHTARGET(awhBiasParams->eTarget));
672 opt = prefix + "-share-group";
673 if (awhBiasParams->shareGroup < 0)
675 warning_error(wi, "AWH bias share-group should be >= 0");
678 opt = prefix + "-ndim";
679 if (awhBiasParams->ndim <= 0 || awhBiasParams->ndim > c_biasMaxNumDim)
681 gmx_fatal(FARGS, "%s (%d) needs to be > 0 and at most %d\n", opt.c_str(), awhBiasParams->ndim, c_biasMaxNumDim);
683 if (awhBiasParams->ndim > 2)
686 "For awh-dim > 2 the estimate based on the diffusion and the initial error is "
687 "currently only a rough guideline."
688 " You should verify its usefulness for your system before production runs!");
690 for (int d = 0; d < awhBiasParams->ndim; d++)
692 std::string prefixdim = prefix + formatString("-dim%d", d + 1);
693 checkDimParams(prefixdim, &awhBiasParams->dimParams[d], ir, wi);
696 /* Check consistencies here that cannot be checked at read time at a lower level. */
697 checkInputConsistencyAwhBias(*awhBiasParams, wi);
701 * Check consistency of input at the AWH level.
703 * \param[in] awhParams AWH parameters.
704 * \param[in,out] wi Struct for bookkeeping warnings.
706 void checkInputConsistencyAwh(const AwhParams& awhParams, warninp_t wi)
708 /* Each pull coord can map to at most 1 AWH coord.
709 * Check that we have a shared bias when requesting multisim sharing.
711 bool haveSharedBias = false;
712 for (int k1 = 0; k1 < awhParams.numBias; k1++)
714 const AwhBiasParams& awhBiasParams1 = awhParams.awhBiasParams[k1];
716 if (awhBiasParams1.shareGroup > 0)
718 haveSharedBias = true;
721 /* k1 is the reference AWH, k2 is the AWH we compare with (can be equal to k1) */
722 for (int k2 = k1; k2 < awhParams.numBias; k2++)
724 for (int d1 = 0; d1 < awhBiasParams1.ndim; d1++)
726 if (awhBiasParams1.dimParams[d1].eCoordProvider == eawhcoordproviderFREE_ENERGY_LAMBDA)
730 const AwhBiasParams& awhBiasParams2 = awhParams.awhBiasParams[k2];
732 /* d1 is the reference dimension of the reference AWH. d2 is the dim index of the AWH to compare with. */
733 for (int d2 = 0; d2 < awhBiasParams2.ndim; d2++)
735 if (awhBiasParams2.dimParams[d2].eCoordProvider == eawhcoordproviderFREE_ENERGY_LAMBDA)
739 /* Give an error if (d1, k1) is different from (d2, k2) but the pull coordinate is the same */
740 if ((d1 != d2 || k1 != k2)
741 && (awhBiasParams1.dimParams[d1].coordIndex == awhBiasParams2.dimParams[d2].coordIndex))
743 char errormsg[STRLEN];
745 "One pull coordinate (%d) cannot be mapped to two separate AWH "
746 "dimensions (awh%d-dim%d and awh%d-dim%d). "
747 "If this is really what you want to do you will have to duplicate "
748 "this pull coordinate.",
749 awhBiasParams1.dimParams[d1].coordIndex + 1,
754 gmx_fatal(FARGS, "%s", errormsg);
761 if (awhParams.shareBiasMultisim && !haveSharedBias)
764 "Sharing of biases over multiple simulations is requested, but no bias is marked "
765 "as shared (share-group > 0)");
768 /* mdrun does not support this (yet), but will check again */
769 if (haveBiasSharingWithinSimulation(awhParams))
772 "You have shared biases within a single simulation, but mdrun does not support "
778 AwhParams* readAwhParams(std::vector<t_inpfile>* inp, warninp_t wi)
780 AwhParams* awhParams;
784 /* Parameters common for all biases */
786 printStringNoNewline(inp, "The way to apply the biasing potential: convolved or umbrella");
787 opt = "awh-potential";
788 awhParams->ePotential = get_eeenum(inp, opt, eawhpotential_names, wi);
790 printStringNoNewline(inp,
791 "The random seed used for sampling the umbrella center in the case of "
792 "umbrella type potential");
794 awhParams->seed = get_eint(inp, opt, -1, wi);
795 if (awhParams->seed == -1)
797 awhParams->seed = static_cast<int>(gmx::makeRandomSeed());
798 fprintf(stderr, "Setting the AWH bias MC random seed to %" PRId64 "\n", awhParams->seed);
801 printStringNoNewline(inp, "Data output interval in number of steps");
803 awhParams->nstOut = get_eint(inp, opt, 100000, wi);
805 printStringNoNewline(inp, "Coordinate sampling interval in number of steps");
806 opt = "awh-nstsample";
807 awhParams->nstSampleCoord = get_eint(inp, opt, 10, wi);
809 printStringNoNewline(inp, "Free energy and bias update interval in number of samples");
810 opt = "awh-nsamples-update";
811 awhParams->numSamplesUpdateFreeEnergy = get_eint(inp, opt, 10, wi);
813 printStringNoNewline(
814 inp, "When true, biases with share-group>0 are shared between multiple simulations");
815 opt = "awh-share-multisim";
816 awhParams->shareBiasMultisim = (getEnum<Boolean>(inp, opt.c_str(), wi) != Boolean::No);
818 printStringNoNewline(inp, "The number of independent AWH biases");
820 awhParams->numBias = get_eint(inp, opt, 1, wi);
821 /* Check this before starting to read the AWH biases. */
822 if (awhParams->numBias <= 0)
824 gmx_fatal(FARGS, "%s needs to be an integer > 0", opt.c_str());
827 /* Read the parameters specific to each AWH bias */
828 snew(awhParams->awhBiasParams, awhParams->numBias);
830 for (int k = 0; k < awhParams->numBias; k++)
832 bool bComment = (k == 0);
833 std::string prefixawh = formatString("awh%d", k + 1);
834 readBiasParams(inp, &awhParams->awhBiasParams[k], prefixawh, wi, bComment);
840 void checkAwhParams(const AwhParams* awhParams, const t_inputrec* ir, warninp_t wi)
844 checkMtsConsistency(*ir, wi);
847 if (awhParams->nstOut <= 0)
849 auto message = formatString("Not writing AWH output with AWH (%s = %d) does not make sense",
852 warning_error(wi, message);
854 /* This restriction can be removed by changing a flag of print_ebin() */
855 if (ir->nstenergy == 0 || awhParams->nstOut % ir->nstenergy != 0)
857 auto message = formatString(
858 "%s (%d) should be a multiple of nstenergy (%d)", opt.c_str(), awhParams->nstOut, ir->nstenergy);
859 warning_error(wi, message);
862 opt = "awh-nsamples-update";
863 if (awhParams->numSamplesUpdateFreeEnergy <= 0)
865 warning_error(wi, opt + " needs to be an integer > 0");
868 for (int k = 0; k < awhParams->numBias; k++)
870 std::string prefixawh = formatString("awh%d", k + 1);
871 checkBiasParams(&awhParams->awhBiasParams[k], prefixawh, ir, wi);
874 /* Do a final consistency check before returning */
875 checkInputConsistencyAwh(*awhParams, wi);
877 if (ir->init_step != 0)
879 warning_error(wi, "With AWH init-step should be 0");
884 * Gets the period of a pull coordinate.
886 * \param[in] pullCoordParams The parameters for the pull coordinate.
887 * \param[in] pbc The PBC setup
888 * \param[in] intervalLength The length of the AWH interval for this pull coordinate
889 * \returns the period (or 0 if not periodic).
891 static double get_pull_coord_period(const t_pull_coord& pullCoordParams, const t_pbc& pbc, const real intervalLength)
895 if (pullCoordParams.eGeom == PullGroupGeometry::Direction)
897 const real margin = 0.001;
898 // Make dims periodic when the interval covers > 95%
899 const real periodicFraction = 0.95;
901 // Check if the pull direction is along a box vector
902 for (int dim = 0; dim < pbc.ndim_ePBC; dim++)
904 const real boxLength = norm(pbc.box[dim]);
905 const real innerProduct = iprod(pullCoordParams.vec, pbc.box[dim]);
906 if (innerProduct >= (1 - margin) * boxLength && innerProduct <= (1 + margin) * boxLength)
908 if (intervalLength > (1 + margin) * boxLength)
911 "The AWH interval (%f nm) for a pull coordinate is larger than the "
917 if (intervalLength > periodicFraction * boxLength)
924 else if (pullCoordParams.eGeom == PullGroupGeometry::Dihedral)
926 /* The dihedral angle is periodic in -180 to 180 deg */
934 * Checks if the given interval is defined in the correct periodic interval.
936 * \param[in] origin Start value of interval.
937 * \param[in] end End value of interval.
938 * \param[in] period Period (or 0 if not periodic).
939 * \returns true if the end point values are in the correct periodic interval.
941 static bool intervalIsInPeriodicInterval(double origin, double end, double period)
943 return (period == 0) || (std::fabs(origin) <= 0.5 * period && std::fabs(end) <= 0.5 * period);
947 * Checks if a value is within an interval.
949 * \param[in] origin Start value of interval.
950 * \param[in] end End value of interval.
951 * \param[in] period Period (or 0 if not periodic).
952 * \param[in] value Value to check.
953 * \returns true if the value is within the interval.
955 static bool valueIsInInterval(double origin, double end, double period, double value)
963 /* The interval closes within the periodic interval */
964 bIn_interval = (value >= origin) && (value <= end);
968 /* The interval wraps around the periodic boundary */
969 bIn_interval = ((value >= origin) && (value <= 0.5 * period))
970 || ((value >= -0.5 * period) && (value <= end));
975 bIn_interval = (value >= origin) && (value <= end);
982 * Check if the starting configuration is consistent with the given interval.
984 * \param[in] awhParams AWH parameters.
985 * \param[in,out] wi Struct for bookeeping warnings.
987 static void checkInputConsistencyInterval(const AwhParams* awhParams, warninp_t wi)
989 for (int k = 0; k < awhParams->numBias; k++)
991 AwhBiasParams* awhBiasParams = &awhParams->awhBiasParams[k];
992 for (int d = 0; d < awhBiasParams->ndim; d++)
994 AwhDimParams* dimParams = &awhBiasParams->dimParams[d];
995 int coordIndex = dimParams->coordIndex;
996 double origin = dimParams->origin, end = dimParams->end, period = dimParams->period;
997 double coordValueInit = dimParams->coordValueInit;
999 if ((period == 0) && (origin > end))
1002 "For the non-periodic pull coordinates awh%d-dim%d-start (%f) cannot be "
1003 "larger than awh%d-dim%d-end (%f)",
1012 /* Currently we assume symmetric periodic intervals, meaning we use [-period/2, period/2] as the reference interval.
1013 Make sure the AWH interval is within this reference interval.
1015 Note: we could fairly simply allow using a more general interval (e.g. [x, x + period]) but it complicates
1016 things slightly and I don't see that there is a great need for it. It would also mean that the interval would
1017 depend on AWH input. Also, for dihedral angles you would always want the reference interval to be -180, +180,
1018 independent of AWH parameters.
1020 if (!intervalIsInPeriodicInterval(origin, end, period))
1023 "When using AWH with periodic pull coordinate geometries "
1024 "awh%d-dim%d-start (%.8g) and "
1025 "awh%d-dim%d-end (%.8g) should cover at most one period (%.8g) and take "
1026 "values in between "
1027 "minus half a period and plus half a period, i.e. in the interval [%.8g, "
1040 /* Warn if the pull initial coordinate value is not in the grid */
1041 if (!valueIsInInterval(origin, end, period, coordValueInit))
1043 auto message = formatString(
1044 "The initial coordinate value (%.8g) for pull coordinate index %d falls "
1046 "of the sampling nterval awh%d-dim%d-start (%.8g) to awh%d-dim%d-end "
1048 "This can lead to large initial forces pulling the coordinate towards the "
1049 "sampling interval.",
1058 warning(wi, message);
1065 * Sets AWH parameters, for one AWH pull dimension.
1067 * \param[in,out] dimParams AWH dimension parameters.
1068 * \param[in] biasIndex The index of the bias containing this AWH pull dimension.
1069 * \param[in] dimIndex The index of this AWH pull dimension.
1070 * \param[in] pull_params Pull parameters.
1071 * \param[in,out] pull_work Pull working struct to register AWH bias in.
1072 * \param[in] pbc A pbc information structure.
1073 * \param[in] compressibility Compressibility matrix for pressure coupling,
1074 * pass all 0 without pressure coupling.
1075 * \param[in,out] wi Struct for bookeeping warnings.
1078 static void setStateDependentAwhPullDimParams(AwhDimParams* dimParams,
1079 const int biasIndex,
1081 const pull_params_t* pull_params,
1084 const tensor& compressibility,
1087 const t_pull_coord& pullCoordParams = pull_params->coord[dimParams->coordIndex];
1089 if (pullCoordParams.eGeom == PullGroupGeometry::DirectionPBC)
1092 "AWH does not support pull geometry '%s'. "
1093 "If the maximum distance between the groups is always "
1094 "less than half the box size, "
1095 "you can use geometry '%s' instead.",
1096 enumValueToString(PullGroupGeometry::DirectionPBC),
1097 enumValueToString(PullGroupGeometry::Direction));
1100 dimParams->period = get_pull_coord_period(pullCoordParams, pbc, dimParams->end - dimParams->origin);
1101 // We would like to check for scaling, but we don't have the full inputrec available here
1102 if (dimParams->period > 0
1103 && !(pullCoordParams.eGeom == PullGroupGeometry::Angle
1104 || pullCoordParams.eGeom == PullGroupGeometry::Dihedral))
1106 bool coordIsScaled = false;
1107 for (int d2 = 0; d2 < DIM; d2++)
1109 if (pullCoordParams.vec[d2] != 0 && norm2(compressibility[d2]) != 0)
1111 coordIsScaled = true;
1116 std::string mesg = gmx::formatString(
1117 "AWH dimension %d of bias %d is periodic with pull geometry '%s', "
1118 "while you should are applying pressure scaling to the "
1119 "corresponding box vector, this is not supported.",
1122 enumValueToString(pullCoordParams.eGeom));
1123 warning(wi, mesg.c_str());
1127 /* The initial coordinate value, converted to external user units. */
1128 dimParams->coordValueInit = get_pull_coord_value(pull_work, dimParams->coordIndex, &pbc);
1130 dimParams->coordValueInit *= pull_conversion_factor_internal2userinput(&pullCoordParams);
1133 void setStateDependentAwhParams(AwhParams* awhParams,
1134 const pull_params_t& pull_params,
1138 const tensor& compressibility,
1139 const t_grpopts* inputrecGroupOptions,
1140 const real initLambda,
1141 const gmx_mtop_t& mtop,
1144 /* The temperature is not really state depenendent but is not known
1145 * when read_awhParams is called (in get ir).
1146 * It is known first after do_index has been called in grompp.cpp.
1148 if (inputrecGroupOptions->ref_t == nullptr || inputrecGroupOptions->ref_t[0] <= 0)
1150 gmx_fatal(FARGS, "AWH biasing is only supported for temperatures > 0");
1152 for (int i = 1; i < inputrecGroupOptions->ngtc; i++)
1154 if (inputrecGroupOptions->ref_t[i] != inputrecGroupOptions->ref_t[0])
1157 "AWH biasing is currently only supported for identical temperatures for all "
1158 "temperature coupling groups");
1163 set_pbc(&pbc, pbcType, box);
1165 for (int k = 0; k < awhParams->numBias; k++)
1167 AwhBiasParams* awhBiasParams = &awhParams->awhBiasParams[k];
1168 for (int d = 0; d < awhBiasParams->ndim; d++)
1170 AwhDimParams* dimParams = &awhBiasParams->dimParams[d];
1171 if (dimParams->eCoordProvider == eawhcoordproviderPULL)
1173 setStateDependentAwhPullDimParams(
1174 dimParams, k, d, &pull_params, pull_work, pbc, compressibility, wi);
1178 dimParams->coordValueInit = initLambda;
1179 checkFepLambdaDimDecouplingConsistency(mtop, wi);
1183 checkInputConsistencyInterval(awhParams, wi);
1185 /* Register AWH as external potential with pull (for AWH dimensions that use pull as
1186 * reaction coordinate provider) to check consistency. */
1187 Awh::registerAwhWithPull(*awhParams, pull_work);