Introduce SimulatorBuilder
[gromacs.git] / src / gromacs / awh / tests / bias.cpp
blob4477b7239d515b68466eacdb11b958b784cd83a4
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2017,2018,2019, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
35 #include "gmxpre.h"
37 #include "gromacs/awh/bias.h"
39 #include <cmath>
41 #include <memory>
42 #include <tuple>
43 #include <vector>
45 #include <gmock/gmock.h>
46 #include <gtest/gtest.h>
48 #include "gromacs/awh/correlationgrid.h"
49 #include "gromacs/awh/pointstate.h"
50 #include "gromacs/mdtypes/awh_params.h"
51 #include "gromacs/utility/stringutil.h"
53 #include "testutils/refdata.h"
54 #include "testutils/testasserts.h"
56 namespace gmx
59 namespace test
62 /*! \internal \brief
63 * Struct that gathers all input for setting up and using a Bias
65 struct AwhTestParameters
67 AwhTestParameters() = default;
68 //!Move constructor
69 AwhTestParameters(AwhTestParameters &&o) noexcept : beta(o.beta), awhDimParams(o.awhDimParams),
70 awhBiasParams(o.awhBiasParams), awhParams(o.awhParams),
71 dimParams(std::move(o.dimParams))
73 awhBiasParams.dimParams = &awhDimParams;
74 awhParams.awhBiasParams = &awhBiasParams;
76 double beta; //!< 1/(kB*T)
78 AwhDimParams awhDimParams; //!< Dimension parameters pointed to by \p awhBiasParams
79 AwhBiasParams awhBiasParams; //!< Bias parameters pointed to by \[ awhParams
80 AwhParams awhParams; //!< AWH parameters, this is the struct to actually use
82 std::vector<DimParams> dimParams; //!< Dimension parameters for setting up Bias
85 //! Helper function to set up the C-style AWH parameters for the test
86 static AwhTestParameters getAwhTestParameters(int eawhgrowth,
87 int eawhpotential)
89 AwhTestParameters params;
91 params.beta = 0.4;
93 AwhDimParams &awhDimParams = params.awhDimParams;
95 awhDimParams.period = 0;
96 awhDimParams.diffusion = 0.1;
97 awhDimParams.origin = 0.5;
98 awhDimParams.end = 1.5;
99 awhDimParams.coordValueInit = awhDimParams.origin;
100 awhDimParams.coverDiameter = 0;
102 AwhBiasParams &awhBiasParams = params.awhBiasParams;
104 awhBiasParams.ndim = 1;
105 awhBiasParams.dimParams = &awhDimParams;
106 awhBiasParams.eTarget = eawhtargetCONSTANT;
107 awhBiasParams.targetBetaScaling = 0;
108 awhBiasParams.targetCutoff = 0;
109 awhBiasParams.eGrowth = eawhgrowth;
110 awhBiasParams.bUserData = FALSE;
111 awhBiasParams.errorInitial = 0.5/params.beta;
112 awhBiasParams.shareGroup = 0;
113 awhBiasParams.equilibrateHistogram = FALSE;
115 double convFactor = 1;
116 double k = 1000;
117 int64_t seed = 93471803;
119 params.dimParams.emplace_back(convFactor, k, params.beta);
121 AwhParams &awhParams = params.awhParams;
123 awhParams.numBias = 1;
124 awhParams.awhBiasParams = &awhBiasParams;
125 awhParams.seed = seed;
126 awhParams.nstOut = 0;
127 awhParams.nstSampleCoord = 1;
128 awhParams.numSamplesUpdateFreeEnergy = 10;
129 awhParams.ePotential = eawhpotential;
130 awhParams.shareBiasMultisim = FALSE;
132 return params;
135 //! Database of 21 test coordinates that represent a trajectory */
136 const double g_coords[] = {
137 0.62,
138 0.70,
139 0.68,
140 0.80,
141 0.93,
142 0.87,
143 1.16,
144 1.14,
145 0.95,
146 0.89,
147 0.91,
148 0.86,
149 0.88,
150 0.79,
151 0.75,
152 0.82,
153 0.74,
154 0.70,
155 0.68,
156 0.71,
157 0.73
160 //! Convenience typedef: growth type enum, potential type enum, disable update skips
161 typedef std::tuple<int, int, BiasParams::DisableUpdateSkips> BiasTestParameters;
163 /*! \brief Test fixture for testing Bias updates
165 class BiasTest : public ::testing::TestWithParam<BiasTestParameters>
167 public:
168 //! Random seed for AWH MC sampling
169 int64_t seed_;
171 //! Coordinates representing a trajectory in time
172 std::vector<double> coordinates_;
173 //! The awh Bias
174 std::unique_ptr<Bias> bias_;
176 BiasTest() :
177 coordinates_(std::begin(g_coords), std::end(g_coords))
179 /* We test all combinations of:
180 * eawhgrowth:
181 * eawhgrowthLINEAR: final, normal update phase
182 * ewahgrowthEXP_LINEAR: intial phase, updated size is constant
183 * eawhpotential (should only affect the force output):
184 * eawhpotentialUMBRELLA: MC on lambda (umbrella potential location)
185 * eawhpotentialCONVOLVED: MD on a convolved potential landscape
186 * disableUpdateSkips (should not affect the results):
187 * BiasParams::DisableUpdateSkips::yes: update the point state for every sample
188 * BiasParams::DisableUpdateSkips::no: update the point state at an interval > 1 sample
190 * Note: It would be nice to explicitly check that eawhpotential
191 * and disableUpdateSkips do not affect the point state.
192 * But the reference data will also ensure this.
194 int eawhgrowth;
195 int eawhpotential;
196 BiasParams::DisableUpdateSkips disableUpdateSkips;
197 std::tie(eawhgrowth, eawhpotential, disableUpdateSkips) = GetParam();
199 /* Set up a basic AWH setup with a single, 1D bias with parameters
200 * such that we can measure the effects of different parameters.
201 * The idea is to, among other things, have part of the interval
202 * not covered by samples.
204 const AwhTestParameters params = getAwhTestParameters(eawhgrowth, eawhpotential);
206 seed_ = params.awhParams.seed;
208 double mdTimeStep = 0.1;
210 int numSamples = coordinates_.size() - 1; // No sample taken at step 0
211 GMX_RELEASE_ASSERT(numSamples % params.awhParams.numSamplesUpdateFreeEnergy == 0, "This test is intended to reproduce the situation when the might need to write output during a normal AWH run, therefore the number of samples should be a multiple of the free-energy update interval (but the test should also runs fine without this condition).");
213 bias_ = std::make_unique<Bias>(-1, params.awhParams, params.awhBiasParams, params.dimParams, params.beta, mdTimeStep, 1, "", Bias::ThisRankWillDoIO::No, disableUpdateSkips);
217 TEST_P(BiasTest, ForcesBiasPmf)
219 gmx::test::TestReferenceData data;
220 gmx::test::TestReferenceChecker checker(data.rootChecker());
222 Bias &bias = *bias_;
224 /* Make strings with the properties we expect to be different in the tests.
225 * These also helps to interpret the reference data.
227 std::vector<std::string> props;
228 props.push_back(formatString("stage: %s", bias.state().inInitialStage() ? "initial" : "final"));
229 props.push_back(formatString("convolve forces: %s", bias.params().convolveForce ? "yes" : "no"));
230 props.push_back(formatString("skip updates: %s", bias.params().skipUpdates() ? "yes" : "no"));
232 SCOPED_TRACE(gmx::formatString("%s, %s, %s", props[0].c_str(), props[1].c_str(), props[2].c_str()));
234 std::vector<double> force, pot, potJump;
236 double coordMaxValue = 0;
237 double potentialJump = 0;
238 int64_t step = 0;
239 for (auto &coord : coordinates_)
241 coordMaxValue = std::max(coordMaxValue, std::abs(coord));
243 awh_dvec coordValue = { coord, 0, 0, 0 };
244 double potential = 0;
245 gmx::ArrayRef<const double> biasForce =
246 bias.calcForceAndUpdateBias(coordValue,
247 &potential, &potentialJump,
248 nullptr, nullptr, step, step, seed_,
249 nullptr);
251 force.push_back(biasForce[0]);
252 pot.push_back(potential);
253 potJump.push_back(potentialJump);
255 step++;
258 /* When skipping updates, ensure all skipped updates are performed here.
259 * This should result in the same bias state as at output in a normal run.
261 if (bias.params().skipUpdates())
263 bias.doSkippedUpdatesForAllPoints();
266 std::vector<double> pointBias, logPmfsum;
267 for (auto &point : bias.state().points())
269 pointBias.push_back(point.bias());
270 logPmfsum.push_back(point.logPmfSum());
273 /* The umbrella force is computed from the coordinate deviation.
274 * In taking this deviation we lose a lot of precision, so we should
275 * compare against k*max(coord) instead of the instantaneous force.
277 const double kCoordMax = bias.dimParams()[0].k*coordMaxValue;
279 constexpr int ulpTol = 10;
281 checker.checkSequence(props.begin(), props.end(), "Properties");
282 checker.setDefaultTolerance(absoluteTolerance(kCoordMax*GMX_DOUBLE_EPS*ulpTol));
283 checker.checkSequence(force.begin(), force.end(), "Force");
284 checker.checkSequence(pot.begin(), pot.end(), "Potential");
285 checker.checkSequence(potJump.begin(), potJump.end(), "PotentialJump");
286 checker.setDefaultTolerance(relativeToleranceAsUlp(1.0, ulpTol));
287 checker.checkSequence(pointBias.begin(), pointBias.end(), "PointBias");
288 checker.checkSequence(logPmfsum.begin(), logPmfsum.end(), "PointLogPmfsum");
291 /* Scan initial/final phase, MC/convolved force and update skip (not) allowed
292 * Both the convolving and skipping should not affect the bias and PMF.
293 * It would be nice if the test would explicitly check for this.
294 * Currently this is tested through identical reference data.
296 INSTANTIATE_TEST_CASE_P(WithParameters, BiasTest,
297 ::testing::Combine(
298 ::testing::Values(eawhgrowthLINEAR, eawhgrowthEXP_LINEAR),
299 ::testing::Values(eawhpotentialUMBRELLA, eawhpotentialCONVOLVED),
300 ::testing::Values(BiasParams::DisableUpdateSkips::yes, BiasParams::DisableUpdateSkips::no)));
302 // Test that we detect coverings and exit the initial stage at the correct step
303 TEST(BiasTest, DetectsCovering)
305 const AwhTestParameters params = getAwhTestParameters(eawhgrowthEXP_LINEAR, eawhpotentialCONVOLVED);
306 const AwhDimParams &awhDimParams = params.awhParams.awhBiasParams[0].dimParams[0];
308 const double mdTimeStep = 0.1;
310 Bias bias(-1, params.awhParams, params.awhBiasParams, params.dimParams, params.beta, mdTimeStep, 1, "", Bias::ThisRankWillDoIO::No);
312 /* We use a trajectory of the sum of two sines to cover the reaction
313 * coordinate range in a semi-realistic way. The period is 4*pi=12.57.
314 * We get out of the initial stage after 4 coverings at step 300.
316 const int64_t exitStepRef = 300;
317 const double midPoint = 0.5*(awhDimParams.end + awhDimParams.origin);
318 const double halfWidth = 0.5*(awhDimParams.end - awhDimParams.origin);
320 bool inInitialStage = bias.state().inInitialStage();
321 /* Normally this loop exits at exitStepRef, but we extend with failure */
322 int64_t step;
323 for (step = 0; step <= 2*exitStepRef; step++)
325 double t = step*mdTimeStep;
326 double coord = midPoint + halfWidth*(0.5*std::sin(t) + 0.55*std::sin(1.5*t));
328 awh_dvec coordValue = { coord, 0, 0, 0 };
329 double potential = 0;
330 double potentialJump = 0;
331 bias.calcForceAndUpdateBias(coordValue,
332 &potential, &potentialJump,
333 nullptr, nullptr,
334 step, step, params.awhParams.seed, nullptr);
336 inInitialStage = bias.state().inInitialStage();
337 if (!inInitialStage)
339 break;
343 EXPECT_EQ(false, inInitialStage);
344 if (!inInitialStage)
346 EXPECT_EQ(exitStepRef, step);
350 } // namespace test
351 } // namespace gmx