Add PME integration test
authorMark Abraham <mark.j.abraham@gmail.com>
Thu, 2 Nov 2017 18:51:16 +0000 (19:51 +0100)
committerMark Abraham <mark.j.abraham@gmail.com>
Fri, 24 Nov 2017 13:50:12 +0000 (14:50 +0100)
As support for executing PME on GPUs is integrated, this test will
make a simple way to ensure things work as expected regardless of
what build type and hardware is present.

Change-Id: I2ab95b6f84eef9fe18b6858a8886221706706a14

src/programs/mdrun/tests/CMakeLists.txt
src/programs/mdrun/tests/pmetest.cpp [new file with mode: 0644]
src/programs/mdrun/tests/refdata/PmeTest_ReproducesEnergies.xml [new file with mode: 0644]

index 06740977dafb14344c70e5c172ff700ec036733d..675277e65d4e23580813a4ec7e64330dc6b8ae3e 100644 (file)
@@ -49,6 +49,7 @@ gmx_add_gtest_executable(
     energyreader.cpp
     grompp.cpp
     initialconstraints.cpp
+    pmetest.cpp
     rerun.cpp
     trajectory_writing.cpp
     trajectoryreader.cpp
diff --git a/src/programs/mdrun/tests/pmetest.cpp b/src/programs/mdrun/tests/pmetest.cpp
new file mode 100644 (file)
index 0000000..7262f40
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 2016,2017, by the GROMACS development team, led by
+ * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
+ * and including many others, as listed in the AUTHORS file in the
+ * top-level source directory and at http://www.gromacs.org.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * This implements basic PME sanity test (using single-rank mdrun).
+ * It runs the input system with PME for several steps (on CPU and GPU, if available),
+ * and checks the reciprocal and conserved energies.
+ * TODO: implement multi-rank tests as well.
+ *
+ * \author Aleksei Iupinov <a.yupinov@gmail.com>
+ * \ingroup module_mdrun_integration_tests
+ */
+#include "gmxpre.h"
+
+#include "config.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/hardware/gpu_hw_info.h"
+#include "gromacs/utility/cstringutil.h"
+#include "gromacs/utility/stringutil.h"
+
+#include "testutils/refdata.h"
+
+#include "energyreader.h"
+#include "moduletest.h"
+
+namespace gmx
+{
+namespace test
+{
+namespace
+{
+
+//! A basic PME runner
+class PmeTest : public MdrunTestFixture
+{
+    public:
+        //! Before any test is run, work out whether any compatible GPUs exist.
+        static void SetUpTestCase();
+        //! Store whether any compatible GPUs exist.
+        static bool s_hasCompatibleCudaGpus;
+};
+
+bool PmeTest::s_hasCompatibleCudaGpus = false;
+
+void PmeTest::SetUpTestCase()
+{
+    gmx_gpu_info_t gpuInfo {};
+    char           detection_error[STRLEN];
+    GMX_UNUSED_VALUE(detection_error); //TODO
+    // It would be nicer to do this detection once and have mdrun
+    // re-use it, but this is OK. Note that this also caters for when
+    // there is no GPU support in the build.
+    if (GMX_GPU == GMX_GPU_CUDA &&
+        (detect_gpus(&gpuInfo, detection_error) >= 0) &&
+        gpuInfo.n_dev_compatible > 0)
+    {
+        s_hasCompatibleCudaGpus = true;
+    }
+    free_gpu_info(&gpuInfo);
+}
+
+TEST_F(PmeTest, ReproducesEnergies)
+{
+    const int   nsteps     = 20;
+    std::string theMdpFile = formatString("coulombtype     = PME\n"
+                                          "nstcalcenergy   = 1\n"
+                                          "nstenergy       = 1\n"
+                                          "pme-order       = 4\n"
+                                          "nsteps          = %d\n",
+                                          nsteps);
+
+    runner_.useStringAsMdpFile(theMdpFile);
+
+    const std::string inputFile = "spc-and-methanol";
+    runner_.useTopGroAndNdxFromDatabase(inputFile.c_str());
+
+    EXPECT_EQ(0, runner_.callGrompp());
+
+    //TODO test all proper/improper combinations in more thorough way?
+    std::map < std::string, std::vector < const char *>> runModes;
+    runModes["PmeOnCpu"] = {};
+    // TODO uncomment these and replace the above as functionality
+    // gets implemented.
+    //runModes["PmeOnCpu"] = {"-pme", "cpu"};
+    //runModes["PmeAuto"]  = {"-pme", "auto"};
+    //runModes["PmeOnGpuFftOnCpu"] = {"-pme", "gpu", "-pmefft", "cpu"};
+    //runModes["PmeOnGpuFftOnGpu"] = {"-pme", "gpu", "-pmefft", "gpu"};
+    //runModes["PmeOnGpuFftAuto"] = {"-pme", "gpu", "-pmefft", "auto"};
+    TestReferenceData    refData;
+    TestReferenceChecker rootChecker(refData.rootChecker());
+
+    for (const auto &mode : runModes)
+    {
+        auto modeTargetsGpus = (mode.first.find("Gpu") != std::string::npos);
+        if (modeTargetsGpus && !s_hasCompatibleCudaGpus)
+        {
+            // This run mode will cause a fatal error from mdrun when
+            // it can't find GPUs, which is not something we're trying
+            // to test here.
+            continue;
+        }
+
+        runner_.edrFileName_ = fileManager_.getTemporaryFilePath(inputFile + "_" + mode.first + ".edr");
+
+        CommandLine commandLine(mode.second);
+        commandLine.append("-notunepme"); // for reciprocal energy reproducibility
+        ASSERT_EQ(0, runner_.callMdrun(commandLine));
+
+        auto energyReader      = openEnergyFileToReadFields(runner_.edrFileName_, {"Coul. recip.", "Total Energy", "Kinetic En."});
+        auto conservedChecker  = rootChecker.checkCompound("Energy", "Conserved");
+        auto reciprocalChecker = rootChecker.checkCompound("Energy", "Reciprocal");
+        for (int i = 0; i <= nsteps; i++)
+        {
+            EnergyFrame frame            = energyReader->frame();
+            std::string stepNum          = gmx::formatString("%d", i);
+            const real  conservedEnergy  = frame.at("Total Energy");
+            const real  reciprocalEnergy = frame.at("Coul. recip.");
+            if (i == 0)
+            {
+                // use first step values as references for tolerance
+                const real startingKineticEnergy = frame.at("Kinetic En.");
+                const auto conservedTolerance    = relativeToleranceAsFloatingPoint(startingKineticEnergy, 2e-5);
+                const auto reciprocalTolerance   = relativeToleranceAsFloatingPoint(reciprocalEnergy, 2e-5);
+                reciprocalChecker.setDefaultTolerance(reciprocalTolerance);
+                conservedChecker.setDefaultTolerance(conservedTolerance);
+            }
+            conservedChecker.checkReal(conservedEnergy, stepNum.c_str());
+            reciprocalChecker.checkReal(reciprocalEnergy, stepNum.c_str());
+        }
+    }
+}
+
+}
+}
+}
diff --git a/src/programs/mdrun/tests/refdata/PmeTest_ReproducesEnergies.xml b/src/programs/mdrun/tests/refdata/PmeTest_ReproducesEnergies.xml
new file mode 100644 (file)
index 0000000..2b05e34
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="referencedata.xsl"?>
+<ReferenceData>
+  <Energy Name="Conserved">
+    <Real Name="0">39.455055</Real>
+    <Real Name="1">39.454601</Real>
+    <Real Name="2">39.453709</Real>
+    <Real Name="3">39.453194</Real>
+    <Real Name="4">39.452827</Real>
+    <Real Name="5">39.451984</Real>
+    <Real Name="6">39.450413</Real>
+    <Real Name="7">39.448856</Real>
+    <Real Name="8">39.448353</Real>
+    <Real Name="9">39.448929</Real>
+    <Real Name="10">39.450169</Real>
+    <Real Name="11">39.4506</Real>
+    <Real Name="12">39.450726</Real>
+    <Real Name="13">39.450962</Real>
+    <Real Name="14">39.452282</Real>
+    <Real Name="15">39.454075</Real>
+    <Real Name="16">39.455303</Real>
+    <Real Name="17">39.455303</Real>
+    <Real Name="18">39.454796</Real>
+    <Real Name="19">39.454231</Real>
+    <Real Name="20">39.453823</Real>
+  </Energy>
+  <Energy Name="Reciprocal">
+    <Real Name="0">5.7102599</Real>
+    <Real Name="1">5.7090158</Real>
+    <Real Name="2">5.7027955</Real>
+    <Real Name="3">5.6922712</Real>
+    <Real Name="4">5.678308</Real>
+    <Real Name="5">5.6615648</Real>
+    <Real Name="6">5.6426487</Real>
+    <Real Name="7">5.6213832</Real>
+    <Real Name="8">5.5974426</Real>
+    <Real Name="9">5.5703311</Real>
+    <Real Name="10">5.5399575</Real>
+    <Real Name="11">5.5064549</Real>
+    <Real Name="12">5.4712629</Real>
+    <Real Name="13">5.4358544</Real>
+    <Real Name="14">5.4023991</Real>
+    <Real Name="15">5.3728585</Real>
+    <Real Name="16">5.3487535</Real>
+    <Real Name="17">5.3307228</Real>
+    <Real Name="18">5.3190498</Real>
+    <Real Name="19">5.3129902</Real>
+    <Real Name="20">5.3116117</Real>
+  </Energy>
+</ReferenceData>