Generalize constraints on MPI rank counts for tests
[alexxy/gromacs.git] / src / programs / mdrun / tests / pmetest.cpp
index dc4cff55e132d5e5c4d19c95d9eee7b13f4242aa..7dadd5d0ea9ca03032a09491bfe0d9801f661966 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2016,2017,2018, by the GROMACS development team, led by
+ * Copyright (c) 2016,2017,2018,2019,2020,2021, 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.
  */
 #include "gmxpre.h"
 
-#include "config.h"
-
 #include <map>
 #include <string>
 #include <vector>
 
 #include <gtest/gtest-spi.h>
 
-#include "gromacs/gpu_utils/gpu_utils.h"
-#include "gromacs/hardware/gpu_hw_info.h"
+#include "gromacs/ewald/pme.h"
+#include "gromacs/hardware/detecthardware.h"
+#include "gromacs/hardware/device_management.h"
+#include "gromacs/hardware/hw_info.h"
+#include "gromacs/trajectory/energyframe.h"
+#include "gromacs/utility/basenetwork.h"
 #include "gromacs/utility/cstringutil.h"
 #include "gromacs/utility/gmxmpi.h"
+#include "gromacs/utility/physicalnodecommunicator.h"
 #include "gromacs/utility/stringutil.h"
 
 #include "testutils/mpitest.h"
@@ -80,40 +83,17 @@ namespace
  * \todo Consider also using GpuTest class. */
 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;
-        //! Convenience typedef
-        using RunModesList = std::map < std::string, std::vector < const char *>>;
-        //! Runs the test with the given inputs
-        void runTest(const RunModesList &runModes);
+public:
+    //! Convenience typedef
+    using RunModesList = std::map<std::string, std::vector<const char*>>;
+    //! Runs the test with the given inputs
+    void runTest(const RunModesList& runModes);
 };
 
-bool PmeTest::s_hasCompatibleCudaGpus = false;
-
-void PmeTest::SetUpTestCase()
-{
-    gmx_gpu_info_t gpuInfo {};
-    // 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.
-    //
-    // TODO report any error messages gracefully.
-    if (GMX_GPU == GMX_GPU_CUDA &&
-        canDetectGpus(nullptr))
-    {
-        findGpus(&gpuInfo);
-        s_hasCompatibleCudaGpus = (gpuInfo.n_dev_compatible > 0);
-    }
-    free_gpu_info(&gpuInfo);
-}
-
-void PmeTest::runTest(const RunModesList &runModes)
+void PmeTest::runTest(const RunModesList& runModes)
 {
     const std::string inputFile = "spc-and-methanol";
-    runner_.useTopGroAndNdxFromDatabase(inputFile.c_str());
+    runner_.useTopGroAndNdxFromDatabase(inputFile);
 
     // With single rank we can and will always test PP+PME as part of mdrun-test.
     // With multiple ranks we can additionally test a single PME-only rank within mdrun-mpi-test.
@@ -129,22 +109,37 @@ void PmeTest::runTest(const RunModesList &runModes)
     {
         EXPECT_NONFATAL_FAILURE(rootChecker.checkUnusedEntries(), ""); // skip checks on other ranks
     }
-    for (const auto &mode : runModes)
+
+    auto hardwareInfo_ =
+            gmx_detect_hardware(PhysicalNodeCommunicator(MPI_COMM_WORLD, gmx_physicalnode_id_hash()));
+
+    for (const auto& mode : runModes)
     {
+        SCOPED_TRACE("mdrun " + joinStrings(mode.second, " "));
         auto modeTargetsGpus = (mode.first.find("Gpu") != std::string::npos);
-        if (modeTargetsGpus && !s_hasCompatibleCudaGpus)
+        if (modeTargetsGpus && getCompatibleDevices(hardwareInfo_->deviceInfoList).empty())
         {
             // 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;
         }
+        auto modeTargetsPmeOnGpus = (mode.first.find("PmeOnGpu") != std::string::npos);
+        if (modeTargetsPmeOnGpus
+            && !(pme_gpu_supports_build(nullptr) && pme_gpu_supports_hardware(*hardwareInfo_, nullptr)))
+        {
+            // This run mode will cause a fatal error from mdrun when
+            // it finds an unsuitable device, which is not something
+            // we're trying to test here.
+            continue;
+        }
 
-        runner_.edrFileName_ = fileManager_.getTemporaryFilePath(inputFile + "_" + mode.first + ".edr");
+        runner_.edrFileName_ =
+                fileManager_.getTemporaryFilePath(inputFile + "_" + mode.first + ".edr");
 
         CommandLine commandLine(mode.second);
 
-        const bool  usePmeTuning = (mode.first.find("Tune") != std::string::npos);
+        const bool usePmeTuning = (mode.first.find("Tune") != std::string::npos);
         if (usePmeTuning)
         {
             commandLine.append("-tunepme");
@@ -163,22 +158,25 @@ void PmeTest::runTest(const RunModesList &runModes)
 
         if (thisRankChecks)
         {
-            auto energyReader      = openEnergyFileToReadFields(runner_.edrFileName_, {"Coul. recip.", "Total Energy", "Kinetic En."});
+            auto energyReader = openEnergyFileToReadTerms(
+                    runner_.edrFileName_, { "Coul. recip.", "Total Energy", "Kinetic En." });
             auto conservedChecker  = rootChecker.checkCompound("Energy", "Conserved");
             auto reciprocalChecker = rootChecker.checkCompound("Energy", "Reciprocal");
             bool firstIteration    = true;
             while (energyReader->readNextFrame())
             {
-                const EnergyFrame &frame            = energyReader->frame();
-                const std::string  stepName         = frame.getFrameName();
+                const EnergyFrameframe            = energyReader->frame();
+                const std::string  stepName         = frame.frameName();
                 const real         conservedEnergy  = frame.at("Total Energy");
                 const real         reciprocalEnergy = frame.at("Coul. recip.");
                 if (firstIteration)
                 {
                     // 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, 3e-5);
+                    const auto conservedTolerance =
+                            relativeToleranceAsFloatingPoint(startingKineticEnergy, 2e-5);
+                    const auto reciprocalTolerance =
+                            relativeToleranceAsFloatingPoint(reciprocalEnergy, 3e-5);
                     reciprocalChecker.setDefaultTolerance(reciprocalTolerance);
                     conservedChecker.setDefaultTolerance(conservedTolerance);
                     firstIteration = false;
@@ -196,27 +194,27 @@ void PmeTest::runTest(const RunModesList &runModes)
 TEST_F(PmeTest, ReproducesEnergies)
 {
     const int         nsteps     = 20;
-    const std::string theMdpFile = formatString("coulombtype     = PME\n"
-                                                "nstcalcenergy   = 1\n"
-                                                "nstenergy       = 1\n"
-                                                "pme-order       = 4\n"
-                                                "nsteps          = %d\n",
-                                                nsteps
-                                                );
+    const std::string theMdpFile = formatString(
+            "coulombtype     = PME\n"
+            "nstcalcenergy   = 1\n"
+            "nstenergy       = 1\n"
+            "pme-order       = 4\n"
+            "nsteps          = %d\n",
+            nsteps);
 
     runner_.useStringAsMdpFile(theMdpFile);
 
-    //TODO test all proper/improper combinations in more thorough way?
+    // TODO test all proper/improper combinations in more thorough way?
     RunModesList runModes;
-    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"};
+    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" };
     // same manual modes but marked for PME tuning
-    runModes["PmeOnCpuTune"]         = {"-pme", "cpu"};
-    runModes["PmeOnGpuFftOnCpuTune"] = {"-pme", "gpu", "-pmefft", "cpu"};
-    runModes["PmeOnGpuFftOnGpuTune"] = {"-pme", "gpu", "-pmefft", "gpu"};
+    runModes["PmeOnCpuTune"]         = { "-pme", "cpu" };
+    runModes["PmeOnGpuFftOnCpuTune"] = { "-pme", "gpu", "-pmefft", "cpu" };
+    runModes["PmeOnGpuFftOnGpuTune"] = { "-pme", "gpu", "-pmefft", "gpu" };
 
     runTest(runModes);
 }
@@ -224,30 +222,52 @@ TEST_F(PmeTest, ReproducesEnergies)
 TEST_F(PmeTest, ScalesTheBox)
 {
     const int         nsteps     = 0;
-    const std::string theMdpFile = formatString("coulombtype     = PME\n"
-                                                "nstcalcenergy   = 1\n"
-                                                "nstenergy       = 1\n"
-                                                "pme-order       = 4\n"
-                                                "pbc             = xy\n"
-                                                "nwall           = 2\n"
-                                                "ewald-geometry  = 3dc\n"
-                                                "wall_atomtype   = OMet CMet\n"
-                                                "wall_density    = 9 9.0\n"
-                                                "wall-ewald-zfac = 5\n"
-                                                "nsteps          = %d\n",
-                                                nsteps
-                                                );
+    const std::string theMdpFile = formatString(
+            "coulombtype     = PME\n"
+            "nstcalcenergy   = 1\n"
+            "nstenergy       = 1\n"
+            "pme-order       = 4\n"
+            "pbc             = xyz\n"
+            "nsteps          = %d\n",
+            nsteps);
 
     runner_.useStringAsMdpFile(theMdpFile);
 
     RunModesList runModes;
-    runModes["PmeOnCpu"]         = {"-pme", "cpu"};
-    runModes["PmeOnGpuFftOnCpu"] = {"-pme", "gpu", "-pmefft", "cpu"};
-    runModes["PmeOnGpuFftOnGpu"] = {"-pme", "gpu", "-pmefft", "gpu"};
+    runModes["PmeOnCpu"]         = { "-pme", "cpu" };
+    runModes["PmeOnGpuFftOnCpu"] = { "-pme", "gpu", "-pmefft", "cpu" };
+    runModes["PmeOnGpuFftOnGpu"] = { "-pme", "gpu", "-pmefft", "gpu" };
 
     runTest(runModes);
 }
 
+TEST_F(PmeTest, ScalesTheBoxWithWalls)
+{
+    const int         nsteps     = 0;
+    const std::string theMdpFile = formatString(
+            "coulombtype     = PME\n"
+            "nstcalcenergy   = 1\n"
+            "nstenergy       = 1\n"
+            "pme-order       = 4\n"
+            "pbc             = xy\n"
+            "nwall           = 2\n"
+            "ewald-geometry  = 3dc\n"
+            "wall_atomtype   = CMet H\n"
+            "wall_density    = 9 9.0\n"
+            "wall-ewald-zfac = 5\n"
+            "nsteps          = %d\n",
+            nsteps);
+
+    runner_.useStringAsMdpFile(theMdpFile);
+
+    RunModesList runModes;
+    runModes["PmeOnCpu"]         = { "-pme", "cpu" };
+    runModes["PmeOnGpuFftOnCpu"] = { "-pme", "gpu", "-pmefft", "cpu" };
+    runModes["PmeOnGpuFftOnGpu"] = { "-pme", "gpu", "-pmefft", "gpu" };
+
+    runTest(runModes);
 }
-}
-}
+
+} // namespace
+} // namespace test
+} // namespace gmx