Add end-to-end tests of energy minimization
[alexxy/gromacs.git] / src / programs / mdrun / tests / energycomparison.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2018, 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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35
36 /*! \internal \file
37  * \brief Implementions of related classes for tests that want to
38  * inspect energies produced by mdrun.
39  *
40  * \author Mark Abraham <mark.j.abraham@gmail.com>
41  * \ingroup module_mdrun_integration_tests
42  */
43 #include "gmxpre.h"
44
45 #include "energycomparison.h"
46
47 #include <gtest/gtest.h>
48 #include <gtest/gtest-spi.h>
49
50 #include "gromacs/trajectory/energyframe.h"
51 #include "gromacs/utility/basenetwork.h"
52 #include "gromacs/utility/strconvert.h"
53 #include "gromacs/utility/stringutil.h"
54
55 #include "testutils/refdata.h"
56 #include "testutils/testasserts.h"
57
58 #include "energyreader.h"
59 #include "moduletest.h"
60
61 namespace gmx
62 {
63 namespace test
64 {
65
66 void compareEnergyFrames(const EnergyFrame      &reference,
67                          const EnergyFrame      &test,
68                          const EnergyTolerances &tolerances)
69 {
70     for (auto referenceIt = reference.begin(); referenceIt != reference.end(); ++referenceIt)
71     {
72         auto &energyName = referenceIt->first;
73         SCOPED_TRACE("Comparing " +  energyName + " between frames");
74         auto  testIt = test.find(energyName);
75         if (testIt != test.end())
76         {
77             auto &energyValueInReference = referenceIt->second;
78             auto &energyValueInTest      = testIt->second;
79             EXPECT_REAL_EQ_TOL(energyValueInReference, energyValueInTest, tolerances.at(energyName));
80         }
81         else
82         {
83             ADD_FAILURE() << "Could not find energy component from reference frame in test frame";
84         }
85     }
86 }
87
88 void
89 checkEnergiesAgainstReferenceData(const std::string      &energyFilename,
90                                   const EnergyTolerances &energiesToMatch,
91                                   TestReferenceChecker   *checker)
92 {
93     const bool thisRankChecks = (gmx_node_rank() == 0);
94
95     if (thisRankChecks)
96     {
97         auto namesOfEnergiesToMatch = getKeys(energiesToMatch);
98         auto energyReader           = openEnergyFileToReadFields(energyFilename,
99                                                                  namesOfEnergiesToMatch);
100
101         std::unordered_map<std::string, TestReferenceChecker> checkers;
102         for (const auto &energyToMatch : energiesToMatch)
103         {
104             const auto &energyName = energyToMatch.first;
105             checkers[energyName] = checker->checkCompound("Energy", energyName.c_str());
106             const auto &energyTolerance = energyToMatch.second;
107             checkers[energyName].setDefaultTolerance(energyTolerance);
108         }
109
110         // We can't assume that frame names based purely on frame
111         // contents are unique. For example, CG can write multiple
112         // frames with the same step number. But we need a unique
113         // identifier so we match the intended reference data, so we
114         // keep track of the number of the frame read from the file.
115         int frameNumber = 0;
116         while (energyReader->readNextFrame())
117         {
118             const EnergyFrame &frame     = energyReader->frame();
119             const std::string  frameName = frame.frameName() + " in frame " + toString(frameNumber);
120
121             for (const auto &energyToMatch : energiesToMatch)
122             {
123                 const std::string &energyName  = energyToMatch.first;
124                 const real         energyValue = frame.at(energyName);
125
126                 SCOPED_TRACE("Comparing " +  energyName + " in " + frameName);
127                 checkers[energyName].checkReal(energyValue, frameName.c_str());
128             }
129             ++frameNumber;
130         }
131     }
132     else
133     {
134         EXPECT_NONFATAL_FAILURE(checker->checkUnusedEntries(), ""); // skip checks on other ranks
135     }
136 }
137
138 }  // namespace test
139 }  // namespace gmx