b490d79901d05ae1bae88b9f44c48933165b66ed
[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 };
110
111 void executeRerunTest(TestFileManager*            fileManager,
112                       SimulationRunner*           runner,
113                       const std::string&          simulationName,
114                       int                         numWarningsToTolerate,
115                       const MdpFieldValues&       mdpFieldValues,
116                       const EnergyTermsToCompare& energyTermsToCompare,
117                       const TrajectoryComparison& trajectoryComparison)
118 {
119     // TODO At some point we should also test PME-only ranks.
120     int numRanksAvailable = getNumberOfTestMpiRanks();
121     if (!isNumberOfPpRanksSupported(simulationName, numRanksAvailable))
122     {
123         fprintf(stdout,
124                 "Test system '%s' cannot run with %d ranks.\n"
125                 "The supported numbers are: %s\n",
126                 simulationName.c_str(), numRanksAvailable,
127                 reportNumbersOfPpRanksSupported(simulationName).c_str());
128         return;
129     }
130
131     // Set file names
132     auto simulator1TrajectoryFileName = fileManager->getTemporaryFilePath("sim1.trr");
133     auto simulator1EdrFileName        = fileManager->getTemporaryFilePath("sim1.edr");
134     auto simulator2TrajectoryFileName = fileManager->getTemporaryFilePath("sim2.trr");
135     auto simulator2EdrFileName        = fileManager->getTemporaryFilePath("sim2.edr");
136
137     // Run grompp
138     runner->tprFileName_ = fileManager->getTemporaryFilePath("sim.tpr");
139     runner->useTopGroAndNdxFromDatabase(simulationName);
140     runner->useStringAsMdpFile(prepareMdpFileContents(mdpFieldValues));
141     auto options = std::vector<SimulationOptionTuple>();
142     if (numWarningsToTolerate > 0)
143     {
144         options.emplace_back(SimulationOptionTuple("-maxwarn", std::to_string(numWarningsToTolerate)));
145     }
146     runGrompp(runner, options);
147
148     // Do first mdrun
149     runner->fullPrecisionTrajectoryFileName_ = simulator1TrajectoryFileName;
150     runner->edrFileName_                     = simulator1EdrFileName;
151     runMdrun(runner);
152
153     // Do second mdrun
154     runner->fullPrecisionTrajectoryFileName_ = simulator2TrajectoryFileName;
155     runner->edrFileName_                     = simulator2EdrFileName;
156     runMdrun(runner, { SimulationOptionTuple("-rerun", simulator1TrajectoryFileName) });
157
158     // Compare simulation results
159     compareEnergies(simulator1EdrFileName, simulator2EdrFileName, energyTermsToCompare);
160     compareTrajectories(simulator1TrajectoryFileName, simulator2TrajectoryFileName, trajectoryComparison);
161 }
162
163 TEST_P(MdrunRerunTest, WithinTolerances)
164 {
165     auto params         = GetParam();
166     auto simulationName = std::get<0>(params);
167     auto integrator     = std::get<1>(params);
168     SCOPED_TRACE(
169             formatString("Comparing normal and rerun of simulation '%s' "
170                          "with integrator '%s'",
171                          simulationName.c_str(), integrator.c_str()));
172
173     auto mdpFieldValues =
174             prepareMdpFieldValues(simulationName.c_str(), integrator.c_str(), "no", "no");
175
176     // bd is much less reproducible in a rerun than the other integrators
177     const int            toleranceScaleFactor = (integrator == "bd") ? 2 : 1;
178     EnergyTermsToCompare energyTermsToCompare{ {
179             { interaction_function[F_EPOT].longname,
180               relativeToleranceAsPrecisionDependentUlp(10.0, 24 * toleranceScaleFactor,
181                                                        40 * toleranceScaleFactor) },
182     } };
183
184     // Specify how trajectory frame matching must work
185     TrajectoryComparison trajectoryComparison{ trajectoryMatchSettings,
186                                                TrajectoryComparison::s_defaultTrajectoryTolerances };
187
188     int numWarningsToTolerate = 0;
189     executeRerunTest(&fileManager_, &runner_, simulationName, numWarningsToTolerate, mdpFieldValues,
190                      energyTermsToCompare, trajectoryComparison);
191 }
192
193 // TODO The time for OpenCL kernel compilation means these tests time
194 // out. Once that compilation is cached for the whole process, these
195 // tests can run in such configurations.
196 #if GMX_GPU != GMX_GPU_OPENCL
197 INSTANTIATE_TEST_CASE_P(
198         NormalMdrunIsReproduced,
199         MdrunRerunTest,
200         ::testing::Combine(::testing::Values("argon12", "tip3p5", "alanine_vsite_vacuo"),
201                            ::testing::Values("md", "md-vv", "bd", "sd")));
202 #else
203 INSTANTIATE_TEST_CASE_P(
204         DISABLED_NormalMdrunIsReproduced,
205         MdrunRerunTest,
206         ::testing::Combine(::testing::Values("argon12", "tip3p5", "alanine_vsite_vacuo"),
207                            ::testing::Values("md", "md-vv", "bd", "sd")));
208 #endif
209
210 class MdrunRerunFreeEnergyTest :
211     public MdrunTestFixture,
212     public ::testing::WithParamInterface<std::tuple<std::string, std::string, int>>
213 {
214 };
215
216 TEST_P(MdrunRerunFreeEnergyTest, WithinTolerances)
217 {
218     auto params          = GetParam();
219     auto simulationName  = std::get<0>(params);
220     auto integrator      = std::get<1>(params);
221     auto initLambdaState = std::get<2>(params);
222     SCOPED_TRACE(
223             formatString("Comparing normal and rerun of simulation '%s' "
224                          "with integrator '%s' for initial lambda state %d",
225                          simulationName.c_str(), integrator.c_str(), initLambdaState));
226
227     auto mdpFieldValues =
228             prepareMdpFieldValues(simulationName.c_str(), integrator.c_str(), "no", "no");
229     mdpFieldValues["other"] += formatString("\ninit-lambda-state = %d", initLambdaState);
230
231     EnergyTermsToCompare energyTermsToCompare{
232         { { interaction_function[F_EPOT].longname, relativeToleranceAsPrecisionDependentUlp(10.0, 24, 32) },
233           { interaction_function[F_DVDL_COUL].longname, relativeToleranceAsPrecisionDependentUlp(1.0, 8, 8) },
234           { interaction_function[F_DVDL_VDW].longname, relativeToleranceAsPrecisionDependentUlp(1.0, 8, 8) },
235           { interaction_function[F_DVDL_BONDED].longname,
236             relativeToleranceAsPrecisionDependentUlp(1.0, 8, 8) },
237           { interaction_function[F_DVDL_RESTRAINT].longname,
238             relativeToleranceAsPrecisionDependentUlp(1.0, 8, 8) } }
239     };
240
241     // Specify how trajectory frame matching must work
242     TrajectoryComparison trajectoryComparison{ MdrunRerunTest::trajectoryMatchSettings,
243                                                TrajectoryComparison::s_defaultTrajectoryTolerances };
244
245     // The md integrator triggers a warning for nearly decoupled
246     // states, which we need to suppress. TODO sometimes?
247     int numWarningsToTolerate = (integrator == "md") ? 1 : 0;
248     executeRerunTest(&fileManager_, &runner_, simulationName, numWarningsToTolerate, mdpFieldValues,
249                      energyTermsToCompare, trajectoryComparison);
250 }
251
252 // TODO The time for OpenCL kernel compilation means these tests time
253 // out. Once that compilation is cached for the whole process, these
254 // tests can run in such configurations.
255 #if GMX_GPU != GMX_GPU_OPENCL
256 INSTANTIATE_TEST_CASE_P(MdrunIsReproduced,
257                         MdrunRerunFreeEnergyTest,
258                         ::testing::Combine(::testing::Values("nonanol_vacuo"),
259                                            ::testing::Values("md", "md-vv", "sd"),
260                                            ::testing::Range(0, 11)));
261 #else
262 INSTANTIATE_TEST_CASE_P(DISABLED_MdrunIsReproduced,
263                         MdrunRerunFreeEnergyTest,
264                         ::testing::Combine(::testing::Values("nonanol_vacuo"),
265                                            ::testing::Values("md", "md-vv", "sd"),
266                                            ::testing::Range(0, 11)));
267 #endif
268
269 } // namespace
270 } // namespace test
271 } // namespace gmx