Apply re-formatting to C++ in src/ tree.
[alexxy/gromacs.git] / src / programs / mdrun / tests / rerun.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
5  * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36
37 /*! \internal \file
38  * \brief
39  * Tests for the mdrun -rerun functionality
40  *
41  * \author Mark Abraham <mark.j.abraham@gmail.com>
42  * \ingroup module_mdrun_integration_tests
43  */
44 #include "gmxpre.h"
45
46 #include "config.h"
47
48 #include "gromacs/topology/ifunc.h"
49 #include "gromacs/utility/stringutil.h"
50
51 #include "testutils/mpitest.h"
52 #include "testutils/simulationdatabase.h"
53
54 #include "moduletest.h"
55 #include "simulatorcomparison.h"
56
57 namespace gmx
58 {
59 namespace test
60 {
61 namespace
62 {
63 /*! \brief Test fixture base for mdrun -rerun
64  *
65  * This test ensures mdrun can run a simulation, writing a trajectory
66  * and matching energies, and reproduce the same energies from a rerun
67  * to within a tight tolerance. It says nothing about whether a rerun
68  * can reproduce energies from a trajectory generated with older code,
69  * since that is not a useful property. Whether mdrun produced correct
70  * energies then and now needs different kinds of testing, but if
71  * true, this test ensures the rerun has the expected property.
72  *
73  * The limitations of mdrun and its output means that reproducing the
74  * same energies is currently only meaningful for integration without
75  * thermostats or barostats, however the present form of the test
76  * infrastructure has in-principle support for such, if that is ever
77  * needed/useful.
78  *
79  * We should also not compare pressure, because with constraints the
80  * non-search steps need a much larger tolerance, and per Issue #1868
81  * we should stop computing pressure in reruns anyway.
82  *
83  * Similarly, per 1868, in the present implementation the kinetic
84  * energy quantities are not generally reproducible, either.
85  *
86  * The choices for tolerance are arbitrary but sufficient.  Rerun does
87  * pair search every frame, so it cannot in general exactly reproduce
88  * quantities from a normal run, because the accumulation order
89  * differs. (Nor does it reproduce pair-search frames exactly,
90  * either). */
91 class MdrunRerunTest :
92     public MdrunTestFixture,
93     public ::testing::WithParamInterface<std::tuple<std::string, std::string>>
94 {
95 public:
96     //! Trajectory components to compare
97     static const TrajectoryFrameMatchSettings trajectoryMatchSettings;
98 };
99
100 // Compare box, positions and forces, but not velocities
101 // (velocities are ignored in reruns)
102 const TrajectoryFrameMatchSettings MdrunRerunTest::trajectoryMatchSettings = {
103     true,
104     true,
105     true,
106     ComparisonConditions::MustCompare,
107     ComparisonConditions::NoComparison,
108     ComparisonConditions::MustCompare,
109     MaxNumFrames::compareAllFrames()
110 };
111
112 void executeRerunTest(TestFileManager*            fileManager,
113                       SimulationRunner*           runner,
114                       const std::string&          simulationName,
115                       int                         numWarningsToTolerate,
116                       const MdpFieldValues&       mdpFieldValues,
117                       const EnergyTermsToCompare& energyTermsToCompare,
118                       const TrajectoryComparison& trajectoryComparison)
119 {
120     // TODO At some point we should also test PME-only ranks.
121     int numRanksAvailable = getNumberOfTestMpiRanks();
122     if (!isNumberOfPpRanksSupported(simulationName, numRanksAvailable))
123     {
124         fprintf(stdout,
125                 "Test system '%s' cannot run with %d ranks.\n"
126                 "The supported numbers are: %s\n",
127                 simulationName.c_str(),
128                 numRanksAvailable,
129                 reportNumbersOfPpRanksSupported(simulationName).c_str());
130         return;
131     }
132
133     // Set file names
134     auto simulator1TrajectoryFileName = fileManager->getTemporaryFilePath("sim1.trr");
135     auto simulator1EdrFileName        = fileManager->getTemporaryFilePath("sim1.edr");
136     auto simulator2TrajectoryFileName = fileManager->getTemporaryFilePath("sim2.trr");
137     auto simulator2EdrFileName        = fileManager->getTemporaryFilePath("sim2.edr");
138
139     // Run grompp
140     runner->tprFileName_ = fileManager->getTemporaryFilePath("sim.tpr");
141     runner->useTopGroAndNdxFromDatabase(simulationName);
142     runner->useStringAsMdpFile(prepareMdpFileContents(mdpFieldValues));
143     auto options = std::vector<SimulationOptionTuple>();
144     if (numWarningsToTolerate > 0)
145     {
146         options.emplace_back(SimulationOptionTuple("-maxwarn", std::to_string(numWarningsToTolerate)));
147     }
148     runGrompp(runner, options);
149
150     // Do first mdrun
151     runner->fullPrecisionTrajectoryFileName_ = simulator1TrajectoryFileName;
152     runner->edrFileName_                     = simulator1EdrFileName;
153     runMdrun(runner);
154
155     // Do second mdrun
156     runner->fullPrecisionTrajectoryFileName_ = simulator2TrajectoryFileName;
157     runner->edrFileName_                     = simulator2EdrFileName;
158     runMdrun(runner, { SimulationOptionTuple("-rerun", simulator1TrajectoryFileName) });
159
160     // Compare simulation results
161     compareEnergies(simulator1EdrFileName, simulator2EdrFileName, energyTermsToCompare);
162     compareTrajectories(simulator1TrajectoryFileName, simulator2TrajectoryFileName, trajectoryComparison);
163 }
164
165 TEST_P(MdrunRerunTest, WithinTolerances)
166 {
167     auto params         = GetParam();
168     auto simulationName = std::get<0>(params);
169     auto integrator     = std::get<1>(params);
170     SCOPED_TRACE(
171             formatString("Comparing normal and rerun of simulation '%s' "
172                          "with integrator '%s'",
173                          simulationName.c_str(),
174                          integrator.c_str()));
175
176     auto mdpFieldValues =
177             prepareMdpFieldValues(simulationName.c_str(), integrator.c_str(), "no", "no");
178
179     // bd is much less reproducible in a rerun than the other integrators
180     const int            toleranceScaleFactor = (integrator == "bd") ? 2 : 1;
181     EnergyTermsToCompare energyTermsToCompare{ {
182             { interaction_function[F_EPOT].longname,
183               relativeToleranceAsPrecisionDependentUlp(
184                       10.0, 24 * toleranceScaleFactor, 40 * toleranceScaleFactor) },
185     } };
186
187     // Specify how trajectory frame matching must work
188     TrajectoryComparison trajectoryComparison{ trajectoryMatchSettings,
189                                                TrajectoryComparison::s_defaultTrajectoryTolerances };
190
191     int numWarningsToTolerate = 0;
192     executeRerunTest(&fileManager_,
193                      &runner_,
194                      simulationName,
195                      numWarningsToTolerate,
196                      mdpFieldValues,
197                      energyTermsToCompare,
198                      trajectoryComparison);
199 }
200
201 // TODO The time for OpenCL kernel compilation means these tests time
202 // out. Once that compilation is cached for the whole process, these
203 // tests can run in such configurations.
204 #if !GMX_GPU_OPENCL
205 INSTANTIATE_TEST_CASE_P(
206         NormalMdrunIsReproduced,
207         MdrunRerunTest,
208         ::testing::Combine(::testing::Values("argon12", "tip3p5", "alanine_vsite_vacuo"),
209                            ::testing::Values("md", "md-vv", "bd", "sd")));
210 #else
211 INSTANTIATE_TEST_CASE_P(
212         DISABLED_NormalMdrunIsReproduced,
213         MdrunRerunTest,
214         ::testing::Combine(::testing::Values("argon12", "tip3p5", "alanine_vsite_vacuo"),
215                            ::testing::Values("md", "md-vv", "bd", "sd")));
216 #endif
217
218 class MdrunRerunFreeEnergyTest :
219     public MdrunTestFixture,
220     public ::testing::WithParamInterface<std::tuple<std::string, std::string, int>>
221 {
222 };
223
224 TEST_P(MdrunRerunFreeEnergyTest, WithinTolerances)
225 {
226     auto params          = GetParam();
227     auto simulationName  = std::get<0>(params);
228     auto integrator      = std::get<1>(params);
229     auto initLambdaState = std::get<2>(params);
230     SCOPED_TRACE(
231             formatString("Comparing normal and rerun of simulation '%s' "
232                          "with integrator '%s' for initial lambda state %d",
233                          simulationName.c_str(),
234                          integrator.c_str(),
235                          initLambdaState));
236
237     auto mdpFieldValues =
238             prepareMdpFieldValues(simulationName.c_str(), integrator.c_str(), "no", "no");
239     mdpFieldValues["other"] += formatString("\ninit-lambda-state = %d", initLambdaState);
240
241     EnergyTermsToCompare energyTermsToCompare{
242         { { interaction_function[F_EPOT].longname, relativeToleranceAsPrecisionDependentUlp(10.0, 24, 32) },
243           { interaction_function[F_DVDL_COUL].longname, relativeToleranceAsPrecisionDependentUlp(1.0, 8, 8) },
244           { interaction_function[F_DVDL_VDW].longname, relativeToleranceAsPrecisionDependentUlp(1.0, 8, 8) },
245           { interaction_function[F_DVDL_BONDED].longname,
246             relativeToleranceAsPrecisionDependentUlp(1.0, 8, 8) },
247           { interaction_function[F_DVDL_RESTRAINT].longname,
248             relativeToleranceAsPrecisionDependentUlp(1.0, 8, 8) } }
249     };
250
251     // Specify how trajectory frame matching must work
252     TrajectoryComparison trajectoryComparison{ MdrunRerunTest::trajectoryMatchSettings,
253                                                TrajectoryComparison::s_defaultTrajectoryTolerances };
254
255     // The md integrator triggers a warning for nearly decoupled
256     // states, which we need to suppress. TODO sometimes?
257     int numWarningsToTolerate = (integrator == "md") ? 1 : 0;
258     executeRerunTest(&fileManager_,
259                      &runner_,
260                      simulationName,
261                      numWarningsToTolerate,
262                      mdpFieldValues,
263                      energyTermsToCompare,
264                      trajectoryComparison);
265 }
266
267 // TODO The time for OpenCL kernel compilation means these tests time
268 // out. Once that compilation is cached for the whole process, these
269 // tests can run in such configurations.
270 #if !GMX_GPU_OPENCL
271 INSTANTIATE_TEST_CASE_P(MdrunIsReproduced,
272                         MdrunRerunFreeEnergyTest,
273                         ::testing::Combine(::testing::Values("nonanol_vacuo"),
274                                            ::testing::Values("md", "md-vv", "sd"),
275                                            ::testing::Range(0, 11)));
276 #else
277 INSTANTIATE_TEST_CASE_P(DISABLED_MdrunIsReproduced,
278                         MdrunRerunFreeEnergyTest,
279                         ::testing::Combine(::testing::Values("nonanol_vacuo"),
280                                            ::testing::Values("md", "md-vv", "sd"),
281                                            ::testing::Range(0, 11)));
282 #endif
283
284 } // namespace
285 } // namespace test
286 } // namespace gmx