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