Use common device context header for OpenCL
authorArtem Zhmurov <zhmurov@gmail.com>
Mon, 14 Sep 2020 09:46:19 +0000 (09:46 +0000)
committerMark Abraham <mark.j.abraham@gmail.com>
Mon, 14 Sep 2020 09:46:19 +0000 (09:46 +0000)
This moves the OpenCL specific definition into under macro in the main header.
Makes device stream and context definitions more consistent.

16 files changed:
src/gromacs/ewald/tests/CMakeLists.txt
src/gromacs/ewald/tests/pmebsplinetest.cpp
src/gromacs/ewald/tests/pmegathertest.cpp
src/gromacs/ewald/tests/pmesolvetest.cpp
src/gromacs/ewald/tests/pmesplinespreadtest.cpp
src/gromacs/ewald/tests/pmetestcommon.cpp
src/gromacs/ewald/tests/pmetestcommon.h
src/programs/mdrun/tests/pmetest.cpp
src/testutils/CMakeLists.txt
src/testutils/test_device.cpp [moved from src/gromacs/ewald/tests/testhardwarecontext.cpp with 57% similarity]
src/testutils/test_device.h [moved from src/gromacs/ewald/tests/testhardwarecontext.h with 56% similarity]
src/testutils/test_hardware_environment.cpp [moved from src/gromacs/ewald/tests/testhardwarecontexts.cpp with 74% similarity]
src/testutils/test_hardware_environment.h [moved from src/gromacs/ewald/tests/testhardwarecontexts.h with 82% similarity]
src/testutils/testinit.cpp
src/testutils/testinit.h
src/testutils/unittest_main.cpp

index 4ce31f5767905ea1b3b7860af6665e16d692422a..61da015d2dae8308ef455d75098398bcb863373e 100644 (file)
@@ -39,9 +39,4 @@ gmx_add_unit_test(EwaldUnitTests ewald-test HARDWARE_DETECTION
         pmesolvetest.cpp
         pmesplinespreadtest.cpp
         pmetestcommon.cpp
-        testhardwarecontexts.cpp
-)
-
-gmx_add_libgromacs_sources(
-    testhardwarecontext.cpp
 )
index f136fabe4fb0a80d467f56c36c7387cdad7d720c..b6a4e9e35f81b69e24f16180b26b36bfcabcd898 100644 (file)
@@ -55,7 +55,6 @@
 #include "testutils/testasserts.h"
 
 #include "pmetestcommon.h"
-#include "testhardwarecontexts.h"
 
 namespace gmx
 {
index eae6448323fe323e9d1eef384c727752258ffbd1..1190caea1de56244be0bf6bb1da966608fb86ce4 100644 (file)
 #include "gromacs/utility/stringutil.h"
 
 #include "testutils/refdata.h"
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
 #include "pmetestcommon.h"
-#include "testhardwarecontexts.h"
 
 namespace gmx
 {
@@ -226,7 +226,7 @@ private:
 
 public:
     PmeGatherTest() = default;
-    //! Sets the input atom data references once
+    //! Sets the input atom data references and programs once
     static void SetUpTestCase()
     {
         size_t start = 0;
@@ -254,7 +254,9 @@ public:
             }
             s_inputAtomDataSets_[atomCount] = atomData;
         }
+        s_pmeTestHardwareContexts = createPmeTestHardwareContextList();
     }
+
     //! The test
     void runTest()
     {
@@ -278,11 +280,12 @@ public:
         inputRec.epsilon_r   = 1.0;
 
         TestReferenceData refData;
-        for (const auto& context : getPmeTestEnv()->getHardwareContexts())
+        for (const auto& pmeTestHardwareContext : s_pmeTestHardwareContexts)
         {
-            CodePath   codePath = context->codePath();
-            const bool supportedInput =
-                    pmeSupportsInputForMode(*getPmeTestEnv()->hwinfo(), &inputRec, codePath);
+            pmeTestHardwareContext->activate();
+            CodePath   codePath       = pmeTestHardwareContext->codePath();
+            const bool supportedInput = pmeSupportsInputForMode(
+                    *getTestHardwareEnvironment()->hwinfo(), &inputRec, codePath);
             if (!supportedInput)
             {
                 /* Testing the failure for the unsupported input */
@@ -293,18 +296,20 @@ public:
 
             /* Describing the test uniquely */
             SCOPED_TRACE(
-                    formatString("Testing force gathering with %s %sfor PME grid size %d %d %d"
+                    formatString("Testing force gathering on %s for PME grid size %d %d %d"
                                  ", order %d, %zu atoms",
-                                 codePathToString(codePath), context->description().c_str(),
-                                 gridSize[XX], gridSize[YY], gridSize[ZZ], pmeOrder, atomCount));
+                                 pmeTestHardwareContext->description().c_str(), gridSize[XX],
+                                 gridSize[YY], gridSize[ZZ], pmeOrder, atomCount));
 
             PmeSafePointer pmeSafe =
-                    pmeInitWrapper(&inputRec, codePath, context->deviceContext(),
-                                   context->deviceStream(), context->pmeGpuProgram(), box);
+                    pmeInitWrapper(&inputRec, codePath, pmeTestHardwareContext->deviceContext(),
+                                   pmeTestHardwareContext->deviceStream(),
+                                   pmeTestHardwareContext->pmeGpuProgram(), box);
             std::unique_ptr<StatePropagatorDataGpu> stateGpu =
                     (codePath == CodePath::GPU)
-                            ? makeStatePropagatorDataGpu(*pmeSafe.get(), context->deviceContext(),
-                                                         context->deviceStream())
+                            ? makeStatePropagatorDataGpu(*pmeSafe.get(),
+                                                         pmeTestHardwareContext->deviceContext(),
+                                                         pmeTestHardwareContext->deviceStream())
                             : nullptr;
 
             pmeInitAtoms(pmeSafe.get(), stateGpu.get(), codePath, inputAtomData.coordinates,
@@ -337,8 +342,12 @@ public:
             forceChecker.checkSequence(forces.begin(), forces.end(), "Forces");
         }
     }
+
+    static std::vector<std::unique_ptr<PmeTestHardwareContext>> s_pmeTestHardwareContexts;
 };
 
+std::vector<std::unique_ptr<PmeTestHardwareContext>> PmeGatherTest::s_pmeTestHardwareContexts;
+
 // An instance of static atom data
 InputDataByAtomCount PmeGatherTest::s_inputAtomDataSets_;
 
index 07f31629f6317c136379dd135a59135675946af7..578fa8caa748b2925eb1977902f6426c9b18d491 100644 (file)
 #include "gromacs/utility/stringutil.h"
 
 #include "testutils/refdata.h"
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
 #include "pmetestcommon.h"
-#include "testhardwarecontexts.h"
 
 namespace gmx
 {
@@ -75,6 +75,9 @@ class PmeSolveTest : public ::testing::TestWithParam<SolveInputParameters>
 public:
     PmeSolveTest() = default;
 
+    //! Sets the programs once
+    static void SetUpTestCase() { s_pmeTestHardwareContexts = createPmeTestHardwareContextList(); }
+
     //! The test
     void runTest()
     {
@@ -107,16 +110,17 @@ public:
         }
 
         TestReferenceData refData;
-        for (const auto& context : getPmeTestEnv()->getHardwareContexts())
+        for (const auto& pmeTestHardwareContext : s_pmeTestHardwareContexts)
         {
-            CodePath   codePath = context->codePath();
-            const bool supportedInput =
-                    pmeSupportsInputForMode(*getPmeTestEnv()->hwinfo(), &inputRec, codePath);
+            pmeTestHardwareContext->activate();
+            CodePath   codePath       = pmeTestHardwareContext->codePath();
+            const bool supportedInput = pmeSupportsInputForMode(
+                    *getTestHardwareEnvironment()->hwinfo(), &inputRec, codePath);
             if (!supportedInput)
             {
                 /* Testing the failure for the unsupported input */
-                EXPECT_THROW_GMX(pmeInitEmpty(&inputRec, codePath, nullptr, nullptr, nullptr, box,
-                                              ewaldCoeff_q, ewaldCoeff_lj),
+                EXPECT_THROW_GMX(pmeInitWrapper(&inputRec, codePath, nullptr, nullptr, nullptr, box,
+                                                ewaldCoeff_q, ewaldCoeff_lj),
                                  NotImplementedError);
                 continue;
             }
@@ -133,17 +137,18 @@ public:
                 {
                     /* Describing the test*/
                     SCOPED_TRACE(formatString(
-                            "Testing solving (%s, %s, %s energy/virial) with %s %sfor PME grid "
+                            "Testing solving (%s, %s, %s energy/virial) on %s for PME grid "
                             "size %d %d %d, Ewald coefficients %g %g",
                             (method == PmeSolveAlgorithm::LennardJones) ? "Lennard-Jones" : "Coulomb",
                             gridOrdering.second.c_str(), computeEnergyAndVirial ? "with" : "without",
-                            codePathToString(codePath), context->description().c_str(),
-                            gridSize[XX], gridSize[YY], gridSize[ZZ], ewaldCoeff_q, ewaldCoeff_lj));
+                            pmeTestHardwareContext->description().c_str(), gridSize[XX],
+                            gridSize[YY], gridSize[ZZ], ewaldCoeff_q, ewaldCoeff_lj));
 
                     /* Running the test */
-                    PmeSafePointer pmeSafe = pmeInitEmpty(
-                            &inputRec, codePath, context->deviceContext(), context->deviceStream(),
-                            context->pmeGpuProgram(), box, ewaldCoeff_q, ewaldCoeff_lj);
+                    PmeSafePointer pmeSafe = pmeInitWrapper(
+                            &inputRec, codePath, pmeTestHardwareContext->deviceContext(),
+                            pmeTestHardwareContext->deviceStream(),
+                            pmeTestHardwareContext->pmeGpuProgram(), box, ewaldCoeff_q, ewaldCoeff_lj);
                     pmeSetComplexGrid(pmeSafe.get(), codePath, gridOrdering.first, nonZeroGridValues);
                     const real cellVolume = box[0] * box[4] * box[8];
                     // FIXME - this is box[XX][XX] * box[YY][YY] * box[ZZ][ZZ], should be stored in the PME structure
@@ -244,8 +249,12 @@ public:
             }
         }
     }
+
+    static std::vector<std::unique_ptr<PmeTestHardwareContext>> s_pmeTestHardwareContexts;
 };
 
+std::vector<std::unique_ptr<PmeTestHardwareContext>> PmeSolveTest::s_pmeTestHardwareContexts;
+
 /*! \brief Test for PME solving */
 TEST_P(PmeSolveTest, ReproducesOutputs)
 {
index ef975b2d084389d1a7fe7240bacb55227440710d..4765acbddbd5319308e5d67be406273c26da3313 100644 (file)
 #include "gromacs/utility/stringutil.h"
 
 #include "testutils/refdata.h"
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
 #include "pmetestcommon.h"
-#include "testhardwarecontexts.h"
 
 namespace gmx
 {
@@ -83,6 +83,10 @@ class PmeSplineAndSpreadTest : public ::testing::TestWithParam<SplineAndSpreadIn
 {
 public:
     PmeSplineAndSpreadTest() = default;
+
+    //! Sets the programs once
+    static void SetUpTestCase() { s_pmeTestHardwareContexts = createPmeTestHardwareContextList(); }
+
     //! The test
     void runTest()
     {
@@ -105,8 +109,6 @@ public:
         inputRec.coulombtype = eelPME;
         inputRec.epsilon_r   = 1.0;
 
-        TestReferenceData refData;
-
         const std::map<PmeSplineAndSpreadOptions, std::string> optionsToTest = {
             { PmeSplineAndSpreadOptions::SplineAndSpreadUnified,
               "spline computation and charge spreading (fused)" },
@@ -124,11 +126,13 @@ public:
         bool   gridValuesSizeAssigned = false;
         size_t previousGridValuesSize;
 
-        for (const auto& context : getPmeTestEnv()->getHardwareContexts())
+        TestReferenceData refData;
+        for (const auto& pmeTestHardwareContext : s_pmeTestHardwareContexts)
         {
-            CodePath   codePath = context->codePath();
-            const bool supportedInput =
-                    pmeSupportsInputForMode(*getPmeTestEnv()->hwinfo(), &inputRec, codePath);
+            pmeTestHardwareContext->activate();
+            CodePath   codePath       = pmeTestHardwareContext->codePath();
+            const bool supportedInput = pmeSupportsInputForMode(
+                    *getTestHardwareEnvironment()->hwinfo(), &inputRec, codePath);
             if (!supportedInput)
             {
                 /* Testing the failure for the unsupported input */
@@ -142,20 +146,22 @@ public:
                 /* Describing the test uniquely in case it fails */
 
                 SCOPED_TRACE(formatString(
-                        "Testing %s with %s %sfor PME grid size %d %d %d"
+                        "Testing %s on %s for PME grid size %d %d %d"
                         ", order %d, %zu atoms",
-                        option.second.c_str(), codePathToString(codePath), context->description().c_str(),
+                        option.second.c_str(), pmeTestHardwareContext->description().c_str(),
                         gridSize[XX], gridSize[YY], gridSize[ZZ], pmeOrder, atomCount));
 
                 /* Running the test */
 
                 PmeSafePointer pmeSafe =
-                        pmeInitWrapper(&inputRec, codePath, context->deviceContext(),
-                                       context->deviceStream(), context->pmeGpuProgram(), box);
+                        pmeInitWrapper(&inputRec, codePath, pmeTestHardwareContext->deviceContext(),
+                                       pmeTestHardwareContext->deviceStream(),
+                                       pmeTestHardwareContext->pmeGpuProgram(), box);
                 std::unique_ptr<StatePropagatorDataGpu> stateGpu =
                         (codePath == CodePath::GPU)
-                                ? makeStatePropagatorDataGpu(*pmeSafe.get(), context->deviceContext(),
-                                                             context->deviceStream())
+                                ? makeStatePropagatorDataGpu(*pmeSafe.get(),
+                                                             pmeTestHardwareContext->deviceContext(),
+                                                             pmeTestHardwareContext->deviceStream())
                                 : nullptr;
 
                 pmeInitAtoms(pmeSafe.get(), stateGpu.get(), codePath, coordinates, charges);
@@ -262,8 +268,12 @@ public:
             }
         }
     }
+
+    static std::vector<std::unique_ptr<PmeTestHardwareContext>> s_pmeTestHardwareContexts;
 };
 
+std::vector<std::unique_ptr<PmeTestHardwareContext>> PmeSplineAndSpreadTest::s_pmeTestHardwareContexts;
+
 
 /*! \brief Test for spline parameter computation and charge spreading. */
 TEST_P(PmeSplineAndSpreadTest, ReproducesOutputs)
index 2e28fb4aa298ff54bdc71dc74256d58adaae02e2..c3f5bd2514b7cfa8d0188d60833798f9d5689340 100644 (file)
@@ -59,7 +59,7 @@
 #include "gromacs/ewald/pme_solve.h"
 #include "gromacs/ewald/pme_spread.h"
 #include "gromacs/fft/parallel_3dfft.h"
-#include "gromacs/gpu_utils/device_stream_manager.h"
+#include "gromacs/gpu_utils/device_context.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/math/invertmatrix.h"
 #include "gromacs/mdtypes/commrec.h"
 #include "gromacs/utility/logger.h"
 #include "gromacs/utility/stringutil.h"
 
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testasserts.h"
 
-#include "testhardwarecontexts.h"
-
 namespace gmx
 {
 namespace test
@@ -151,21 +150,6 @@ PmeSafePointer pmeInitWrapper(const t_inputrec*    inputRec,
     return pme;
 }
 
-//! Simple PME initialization based on input, no atom data
-PmeSafePointer pmeInitEmpty(const t_inputrec*    inputRec,
-                            const CodePath       mode,
-                            const DeviceContext* deviceContext,
-                            const DeviceStream*  deviceStream,
-                            const PmeGpuProgram* pmeGpuProgram,
-                            const Matrix3x3&     box,
-                            const real           ewaldCoeff_q,
-                            const real           ewaldCoeff_lj)
-{
-    return pmeInitWrapper(inputRec, mode, deviceContext, deviceStream, pmeGpuProgram, box,
-                          ewaldCoeff_q, ewaldCoeff_lj);
-    // hiding the fact that PME actually needs to know the number of atoms in advance
-}
-
 PmeSafePointer pmeInitEmpty(const t_inputrec* inputRec)
 {
     const Matrix3x3 defaultBox = { { 1.0F, 0.0F, 0.0F, 0.0F, 1.0F, 0.0F, 0.0F, 0.0F, 1.0F } };
@@ -889,5 +873,58 @@ PmeOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t* pme, CodePath mode, P
     return output;
 }
 
+const char* codePathToString(CodePath codePath)
+{
+    switch (codePath)
+    {
+        case CodePath::CPU: return "CPU";
+        case CodePath::GPU: return "GPU";
+        default: GMX_THROW(NotImplementedError("This CodePath should support codePathToString"));
+    }
+}
+
+PmeTestHardwareContext::PmeTestHardwareContext() : codePath_(CodePath::CPU) {}
+
+PmeTestHardwareContext::PmeTestHardwareContext(TestDevice* testDevice) :
+    codePath_(CodePath::CPU),
+    testDevice_(testDevice)
+{
+    setActiveDevice(testDevice_->deviceInfo());
+    pmeGpuProgram_ = buildPmeGpuProgram(testDevice_->deviceContext());
+}
+
+//! Returns a human-readable context description line
+std::string PmeTestHardwareContext::description() const
+{
+    switch (codePath_)
+    {
+        case CodePath::CPU: return "CPU";
+        case CodePath::GPU: return "GPU (" + testDevice_->description() + ")";
+        default: return "Unknown code path.";
+    }
+}
+
+void PmeTestHardwareContext::activate() const
+{
+    if (codePath_ == CodePath::GPU)
+    {
+        setActiveDevice(testDevice_->deviceInfo());
+    }
+}
+
+std::vector<std::unique_ptr<PmeTestHardwareContext>> createPmeTestHardwareContextList()
+{
+    std::vector<std::unique_ptr<PmeTestHardwareContext>> pmeTestHardwareContextList;
+    // Add CPU
+    pmeTestHardwareContextList.emplace_back(std::make_unique<PmeTestHardwareContext>());
+    // Add GPU devices
+    const auto& testDeviceList = getTestHardwareEnvironment()->getTestDeviceList();
+    for (const auto& testDevice : testDeviceList)
+    {
+        pmeTestHardwareContextList.emplace_back(std::make_unique<PmeTestHardwareContext>(testDevice.get()));
+    }
+    return pmeTestHardwareContextList;
+}
+
 } // namespace test
 } // namespace gmx
index 8e6e135409cba986af20d6f81c8325dbd37ba011..6b1e6d136c492872fdb7dfb4a56a09c78f454620 100644 (file)
 #include "gromacs/mdtypes/state_propagator_data_gpu.h"
 #include "gromacs/utility/unique_cptr.h"
 
+#include "testutils/test_device.h"
+
 namespace gmx
 {
 
-class DeviceStreamManager;
 template<typename>
 class ArrayRef;
 
 namespace test
 {
 
-// Forward declaration
-enum class CodePath;
+//! Hardware code path being tested
+enum class CodePath : int
+{
+    //! CPU code path
+    CPU,
+    //! GPU code path
+    GPU,
+    //! Total number of code paths
+    Count
+};
+
+//! Return a string useful for human-readable messages describing a \c codePath.
+const char* codePathToString(CodePath codePath);
 
 // Convenience typedefs
 //! A safe pointer type for PME.
@@ -101,10 +113,14 @@ typedef SparseGridValuesOutput<t_complex> SparseComplexGridValuesOutput;
 //! TODO: make proper C++ matrix for the whole Gromacs, get rid of this
 typedef std::array<real, DIM * DIM> Matrix3x3;
 //! PME solver type
-enum class PmeSolveAlgorithm
+enum class PmeSolveAlgorithm : int
 {
+    //! Coulomb electrostatics
     Coulomb,
+    //! Lennard-Jones
     LennardJones,
+    //! Total number of solvers
+    Count
 };
 
 // Misc.
@@ -130,15 +146,6 @@ PmeSafePointer pmeInitWrapper(const t_inputrec*    inputRec,
                               const Matrix3x3&     box,
                               real                 ewaldCoeff_q  = 1.0F,
                               real                 ewaldCoeff_lj = 1.0F);
-//! Simple PME initialization (no atom data)
-PmeSafePointer pmeInitEmpty(const t_inputrec*    inputRec,
-                            CodePath             mode,
-                            const DeviceContext* deviceContext,
-                            const DeviceStream*  deviceStream,
-                            const PmeGpuProgram* pmeGpuProgram,
-                            const Matrix3x3&     box,
-                            real                 ewaldCoeff_q,
-                            real                 ewaldCoeff_lj);
 
 //! Simple PME initialization based on inputrec only
 PmeSafePointer pmeInitEmpty(const t_inputrec* inputRec);
@@ -199,7 +206,47 @@ SparseRealGridValuesOutput pmeGetRealGrid(const gmx_pme_t* pme, CodePath mode);
 SparseComplexGridValuesOutput pmeGetComplexGrid(const gmx_pme_t* pme, CodePath mode, GridOrdering gridOrdering);
 //! Getting the reciprocal energy and virial
 PmeOutput pmeGetReciprocalEnergyAndVirial(const gmx_pme_t* pme, CodePath mode, PmeSolveAlgorithm method);
+
+struct PmeTestHardwareContext
+{
+    //! Hardware path for the code being tested.
+    CodePath codePath_;
+    //! Returns a human-readable context description line
+    std::string description() const;
+    //! Pointer to the global test hardware device (if on GPU)
+    TestDevice* testDevice_ = nullptr;
+    //! PME GPU program if needed
+    PmeGpuProgramStorage pmeGpuProgram_ = nullptr;
+    // Constructor for CPU context
+    PmeTestHardwareContext();
+    // Constructor for GPU context
+    explicit PmeTestHardwareContext(TestDevice* testDevice);
+
+    //! Get the code path
+    CodePath codePath() const { return codePath_; }
+    //! Get the PME GPU program
+    const PmeGpuProgram* pmeGpuProgram() const
+    {
+        return codePath() == CodePath::GPU ? pmeGpuProgram_.get() : nullptr;
+    }
+
+    const DeviceContext* deviceContext() const
+    {
+        return codePath() == CodePath::GPU ? &testDevice_->deviceContext() : nullptr;
+    }
+
+    const DeviceStream* deviceStream() const
+    {
+        return codePath() == CodePath::GPU ? &testDevice_->deviceStream() : nullptr;
+    }
+
+    //! Activate the context (set the device)
+    void activate() const;
+};
+
+std::vector<std::unique_ptr<PmeTestHardwareContext>> createPmeTestHardwareContextList();
+
 } // namespace test
 } // namespace gmx
 
-#endif
+#endif // GMX_EWALD_PME_TEST_COMMON_H
index fbc0e231cf037d5ecb61f2d22c381bd3212fd675..4aa1219ccade36859d14b72dc98c4831486c2e9e 100644 (file)
@@ -57,6 +57,7 @@
 #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/cstringutil.h"
 #include "gromacs/utility/gmxmpi.h"
@@ -83,23 +84,12 @@ namespace
 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_hasCompatibleGpus;
     //! 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_hasCompatibleGpus = false;
-
-void PmeTest::SetUpTestCase()
-{
-    s_hasCompatibleGpus = canComputeOnDevice();
-}
-
 void PmeTest::runTest(const RunModesList& runModes)
 {
     const std::string inputFile = "spc-and-methanol";
@@ -127,7 +117,7 @@ void PmeTest::runTest(const RunModesList& runModes)
     {
         SCOPED_TRACE("mdrun " + joinStrings(mode.second, " "));
         auto modeTargetsGpus = (mode.first.find("Gpu") != std::string::npos);
-        if (modeTargetsGpus && !s_hasCompatibleGpus)
+        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
index 6ac9aacbe4b3fd66d4194c9e64a9cdf82e4aa526..1928150068c2f249ea87618092f2848525f97604 100644 (file)
@@ -54,6 +54,8 @@ set(TESTUTILS_SOURCES
     testasserts.cpp
     testfilemanager.cpp
     testfileredirector.cpp
+    test_device.cpp
+    test_hardware_environment.cpp
     testinit.cpp
     testmatchers.cpp
     testoptions.cpp
@@ -66,7 +68,20 @@ if(NOT HAVE_TINYXML2)
     list(APPEND TESTUTILS_SOURCES ../external/tinyxml2/tinyxml2.cpp)
 endif()
 
-add_library(testutils STATIC ${UNITTEST_TARGET_OPTIONS} ${TESTUTILS_SOURCES})
+if (GMX_GPU_CUDA)
+    # Work around FindCUDA that prevents using target_link_libraries()
+    # with keywords otherwise...
+    set(CUDA_LIBRARIES PRIVATE ${CUDA_LIBRARIES})
+    if (NOT GMX_CLANG_CUDA)
+        gmx_cuda_add_library(testutils ${TESTUTILS_SOURCES})
+    else()
+        add_library(testutils STATIC ${TESTUTILS_SOURCES})
+    endif()
+    target_link_libraries(testutils PRIVATE ${CUDA_CUFFT_LIBRARIES})
+else()
+    add_library(testutils STATIC ${UNITTEST_TARGET_OPTIONS} ${TESTUTILS_SOURCES})
+endif()
+
 gmx_target_compile_options(testutils)
 target_compile_definitions(testutils PRIVATE HAVE_CONFIG_H)
 target_include_directories(testutils SYSTEM BEFORE PRIVATE ${PROJECT_SOURCE_DIR}/src/external/thread_mpi/include)
similarity index 57%
rename from src/gromacs/ewald/tests/testhardwarecontext.cpp
rename to src/testutils/test_device.cpp
index 6e2c455efafc8a3a05054e62e18fd50e506cd707..8d3865eee0a8920e4588a631230cfa175cf5a544 100644 (file)
  * \author Aleksei Iupinov <a.yupinov@gmail.com>
  * \author Artem Zhmurov <zhmurov@gmail.com>
  *
- * \ingroup module_ewald
+ * \ingroup module_testutils
  */
-
 #include "gmxpre.h"
 
-#include "testhardwarecontext.h"
+#include "test_device.h"
 
 #include <memory>
 
-#include "gromacs/ewald/pme.h"
 #include "gromacs/gpu_utils/device_context.h"
 #include "gromacs/gpu_utils/device_stream.h"
 #include "gromacs/gpu_utils/gpu_utils.h"
+#include "gromacs/gpu_utils/gputraits.h"
 #include "gromacs/hardware/detecthardware.h"
 #include "gromacs/hardware/hw_info.h"
 #include "gromacs/utility/basenetwork.h"
@@ -64,60 +63,64 @@ namespace gmx
 namespace test
 {
 
-TestHardwareContext::TestHardwareContext(CodePath codePath, const char* description) :
-    codePath_(codePath),
-    description_(description)
+class TestDevice::Impl
 {
-    GMX_RELEASE_ASSERT(codePath == CodePath::CPU,
-                       "A GPU code path should provide DeviceInformation to the "
-                       "TestHerdwareContext constructor.");
-    deviceContext_ = nullptr;
-    deviceStream_  = nullptr;
-}
+public:
+    Impl(const char* description);
+    Impl(const char* description, const DeviceInformation& deviceInfo);
+    ~Impl();
+    //! Returns a human-readable context description line
+    std::string description() const { return description_; }
+    //! Returns the device info pointer
+    const DeviceInformation& deviceInfo() const { return deviceContext_.deviceInfo(); }
+    //! Get the device context
+    const DeviceContext& deviceContext() const { return deviceContext_; }
+    //! Get the device stream
+    const DeviceStream& deviceStream() const { return deviceStream_; }
+
+private:
+    //! Readable description
+    std::string description_;
+    //! Device context
+    DeviceContext deviceContext_;
+    //! Device stream
+    DeviceStream deviceStream_;
+};
 
-TestHardwareContext::TestHardwareContext(CodePath                 codePath,
-                                         const char*              description,
-                                         const DeviceInformation& deviceInfo) :
-    codePath_(codePath),
-    description_(description)
+TestDevice::Impl::Impl(const char* description, const DeviceInformation& deviceInfo) :
+    description_(description),
+    deviceContext_(deviceInfo),
+    deviceStream_(deviceContext_, DeviceStreamPriority::Normal, false)
 {
-    GMX_RELEASE_ASSERT(codePath == CodePath::GPU,
-                       "TestHardwareContext tries to construct DeviceContext and PmeGpuProgram "
-                       "in CPU build.");
-    deviceContext_ = new DeviceContext(deviceInfo);
-    deviceStream_  = new DeviceStream(*deviceContext_, DeviceStreamPriority::Normal, false);
-    program_       = buildPmeGpuProgram(*deviceContext_);
 }
 
-TestHardwareContext::~TestHardwareContext()
+TestDevice::Impl::~Impl() = default;
+
+TestDevice::TestDevice(const char* description, const DeviceInformation& deviceInfo) :
+    impl_(new Impl(description, deviceInfo))
 {
-    delete (deviceStream_);
-    delete (deviceContext_);
 }
 
-const DeviceInformation* TestHardwareContext::deviceInfo() const
+TestDevice::~TestDevice() = default;
+
+std::string TestDevice::description() const
 {
-    return &deviceContext_->deviceInfo();
+    return impl_->description();
 }
 
-const DeviceContext* TestHardwareContext::deviceContext() const
+const DeviceInformation& TestDevice::deviceInfo() const
 {
-    return deviceContext_;
+    return impl_->deviceInfo();
 }
-//! Get the device stream
-const DeviceStream* TestHardwareContext::deviceStream() const
+
+const DeviceContext& TestDevice::deviceContext() const
 {
-    return deviceStream_;
+    return impl_->deviceContext();
 }
 
-const char* codePathToString(CodePath codePath)
+const DeviceStream& TestDevice::deviceStream() const
 {
-    switch (codePath)
-    {
-        case CodePath::CPU: return "CPU";
-        case CodePath::GPU: return "GPU";
-        default: GMX_THROW(NotImplementedError("This CodePath should support codePathToString"));
-    }
+    return impl_->deviceStream();
 }
 
 } // namespace test
similarity index 56%
rename from src/gromacs/ewald/tests/testhardwarecontext.h
rename to src/testutils/test_device.h
index fa5ebd9da8d4320524ae09499129c11be31cf5dd..3fa7915fcf5a5107185fcfdd15440e3c212cefe0 100644 (file)
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-#ifndef GMX_EWALD_TEST_HARDWARE_CONTEXT_H
-#define GMX_EWALD_TEST_HARDWARE_CONTEXT_H
+#ifndef GMX_TESTUTILS_TEST_DEVICE_H
+#define GMX_TESTUTILS_TEST_DEVICE_H
 
 /*! \internal \file
  * \brief
- * Describes test environment class which performs hardware enumeration for unit tests.
+ * Describes test environment class which performs GPU device enumeration for unit tests.
  *
  * \author Aleksei Iupinov <a.yupinov@gmail.com>
  * \author Artem Zhmurov <zhmurov@gmail.com>
- * \ingroup module_ewald
+ *
+ * \ingroup module_testutils
  */
 
 #include <map>
 #include <string>
 #include <vector>
 
-#include "gromacs/ewald/pme_gpu_program.h"
+#include "gromacs/utility/classhelpers.h"
 #include "gromacs/utility/gmxassert.h"
 
 class DeviceContext;
@@ -59,54 +60,35 @@ namespace gmx
 {
 namespace test
 {
-//! Hardware code path being tested
-enum class CodePath
-{
-    CPU,
-    GPU
-};
-
-//! Return a string useful for human-readable messages describing a \c codePath.
-const char* codePathToString(CodePath codePath);
 
 /*! \internal \brief
- * A structure to describe a hardware context  that persists over the lifetime
- * of the test binary - an abstraction over PmeGpuProgram with a human-readable string.
+ * A structure to describe a hardware context that persists over the lifetime
+ * of the test binary.
  */
-struct TestHardwareContext
+class TestDevice
 {
-    //! Hardware path for the code being tested.
-    CodePath codePath_;
-    //! Readable description
-    std::string description_;
-    //! Device context
-    DeviceContext* deviceContext_ = nullptr;
-    //! Device stream
-    DeviceStream* deviceStream_ = nullptr;
-    //! Persistent compiled GPU kernels for PME.
-    PmeGpuProgramStorage program_;
-
 public:
-    //! Retuns the code path for this context.
-    CodePath codePath() const { return codePath_; }
     //! Returns a human-readable context description line
-    std::string description() const { return description_; }
+    std::string description() const;
     //! Returns the device info pointer
-    const DeviceInformation* deviceInfo() const;
+    const DeviceInformation& deviceInfo() const;
     //! Get the device context
-    const DeviceContext* deviceContext() const;
+    const DeviceContext& deviceContext() const;
     //! Get the device stream
-    const DeviceStream* deviceStream() const;
-    //! Returns the persistent PME GPU kernels
-    const PmeGpuProgram* pmeGpuProgram() const { return program_.get(); }
-    //! Constructs the context for CPU builds
-    TestHardwareContext(CodePath codePath, const char* description);
-    //! Constructs the context for GPU builds
-    TestHardwareContext(CodePath codePath, const char* description, const DeviceInformation& deviceInfo);
+    const DeviceStream& deviceStream() const;
+    //! Creates the device context and stream for tests on the GPU
+    TestDevice(const char* description, const DeviceInformation& deviceInfo);
     //! Destructor
-    ~TestHardwareContext();
+    ~TestDevice();
+
+private:
+    //! Implementation type.
+    class Impl;
+    //! Implementation object.
+    PrivateImplPointer<Impl> impl_;
 };
 
 } // namespace test
 } // namespace gmx
-#endif
+
+#endif // GMX_TESTUTILS_TEST_DEVICE_H
similarity index 74%
rename from src/gromacs/ewald/tests/testhardwarecontexts.cpp
rename to src/testutils/test_hardware_environment.cpp
index 5b7cb0532793440c1653807d72af3c724397c52e..4164527f9dcc633af26149be64b128c18fb21c54 100644 (file)
  * Implements test environment class which performs hardware enumeration for unit tests.
  *
  * \author Aleksei Iupinov <a.yupinov@gmail.com>
- * \ingroup module_ewald
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_testutils
  */
 
 #include "gmxpre.h"
 
-#include "testhardwarecontexts.h"
+#include "test_hardware_environment.h"
 
 #include <memory>
 
-#include "gromacs/ewald/pme.h"
+#include "gromacs/gpu_utils/gpu_utils.h"
 #include "gromacs/hardware/detecthardware.h"
 #include "gromacs/hardware/device_management.h"
 #include "gromacs/hardware/hw_info.h"
@@ -70,21 +72,21 @@ namespace test
  * so there is no deinitialization issue.  See
  * https://isocpp.org/wiki/faq/ctors for discussion of alternatives
  * and trade-offs. */
-const PmeTestEnvironment* getPmeTestEnv()
+const TestHardwareEnvironment* getTestHardwareEnvironment()
 {
-    static PmeTestEnvironment* pmeTestEnvironment = nullptr;
-    if (pmeTestEnvironment == nullptr)
+    static TestHardwareEnvironment* testHardwareEnvironment = nullptr;
+    if (testHardwareEnvironment == nullptr)
     {
         // Ownership of the TestEnvironment is taken by GoogleTest, so nothing can leak
-        pmeTestEnvironment = static_cast<PmeTestEnvironment*>(
-                ::testing::AddGlobalTestEnvironment(new PmeTestEnvironment));
+        testHardwareEnvironment = static_cast<TestHardwareEnvironment*>(
+                ::testing::AddGlobalTestEnvironment(new TestHardwareEnvironment));
     }
-    return pmeTestEnvironment;
+    return testHardwareEnvironment;
 }
 
 void callAddGlobalTestEnvironment()
 {
-    getPmeTestEnv();
+    getTestHardwareEnvironment();
 }
 
 //! Simple hardware initialization
@@ -94,30 +96,22 @@ static gmx_hw_info_t* hardwareInit()
     return gmx_detect_hardware(MDLogger{}, physicalNodeComm);
 }
 
-void PmeTestEnvironment::SetUp()
+void TestHardwareEnvironment::SetUp()
 {
-    hardwareContexts_.emplace_back(std::make_unique<TestHardwareContext>(CodePath::CPU, "(CPU) "));
-
+    testDeviceList_.clear();
     hardwareInfo_ = hardwareInit();
-    if (!pme_gpu_supports_build(nullptr) || !pme_gpu_supports_hardware(*hardwareInfo_, nullptr))
-    {
-        // PME can only run on the CPU, so don't make any more test contexts.
-        return;
-    }
     // Constructing contexts for all compatible GPUs - will be empty on non-GPU builds
     for (const DeviceInformation& compatibleDeviceInfo : getCompatibleDevices(hardwareInfo_->deviceInfoList))
     {
         setActiveDevice(compatibleDeviceInfo);
-        std::string deviceDescription = getDeviceInformationString(compatibleDeviceInfo);
-        std::string description       = "(GPU " + deviceDescription + ") ";
-        hardwareContexts_.emplace_back(std::make_unique<TestHardwareContext>(
-                CodePath::GPU, description.c_str(), compatibleDeviceInfo));
+        std::string description = getDeviceInformationString(compatibleDeviceInfo);
+        testDeviceList_.emplace_back(std::make_unique<TestDevice>(description.c_str(), compatibleDeviceInfo));
     }
 }
 
-void PmeTestEnvironment::TearDown()
+void TestHardwareEnvironment::TearDown()
 {
-    hardwareContexts_.clear();
+    testDeviceList_.clear();
 }
 
 } // namespace test
similarity index 82%
rename from src/gromacs/ewald/tests/testhardwarecontexts.h
rename to src/testutils/test_hardware_environment.h
index 42d7245a8e68549d925e4d41b0d57b1cc761509a..d7b52922592ac4958ca97ff62ea5a7449499143d 100644 (file)
  * To help us fund GROMACS development, we humbly ask that you cite
  * the research papers on the package. Check out http://www.gromacs.org.
  */
-#ifndef GMX_EWALD_TEST_HARDWARE_CONTEXTS_H
-#define GMX_EWALD_TEST_HARDWARE_CONTEXTS_H
+#ifndef GMX_TESTUTILS_TEST_HARDWARE_ENVIRONMENT_H
+#define GMX_TESTUTILS_TEST_HARDWARE_ENVIRONMENT_H
 
 /*! \internal \file
  * \brief
  * Describes test environment class which performs hardware enumeration for unit tests.
  *
  * \author Aleksei Iupinov <a.yupinov@gmail.com>
- * \ingroup module_ewald
+ * \author Artem Zhmurov <zhmurov@gmail.com>
+ *
+ * \ingroup module_testutils
  */
 
 #include <map>
 
 #include <gtest/gtest.h>
 
-#include "gromacs/ewald/pme_gpu_program.h"
-#include "gromacs/hardware/device_management.h"
 #include "gromacs/utility/gmxassert.h"
 
-#include "testhardwarecontext.h"
+#include "testutils/test_device.h"
 
 struct gmx_hw_info_t;
 
@@ -61,19 +61,16 @@ namespace gmx
 namespace test
 {
 
-//! A container of handles to hardware contexts
-typedef std::vector<std::unique_ptr<TestHardwareContext>> TestHardwareContexts;
-
 /*! \internal \brief
  * This class performs one-time test initialization (enumerating the hardware)
  */
-class PmeTestEnvironment : public ::testing::Environment
+class TestHardwareEnvironment : public ::testing::Environment
 {
 private:
     //! General hardware info
     gmx_hw_info_t* hardwareInfo_;
     //! Storage of hardware contexts
-    TestHardwareContexts hardwareContexts_;
+    std::vector<std::unique_ptr<TestDevice>> testDeviceList_;
 
 public:
     //! This is called by GTest framework once to query the hardware
@@ -81,13 +78,16 @@ public:
     //! This is called by GTest framework once release the hardware
     void TearDown() override;
     //! Get available hardware contexts.
-    const TestHardwareContexts& getHardwareContexts() const { return hardwareContexts_; }
+    const std::vector<std::unique_ptr<TestDevice>>& getTestDeviceList() const
+    {
+        return testDeviceList_;
+    }
     //! Get available hardware information.
     const gmx_hw_info_t* hwinfo() const { return hardwareInfo_; }
 };
 
 //! Get the test environment
-const PmeTestEnvironment* getPmeTestEnv();
+const TestHardwareEnvironment* getTestHardwareEnvironment();
 
 /*! \brief This constructs the test environment during setup of the
  * unit test so that they can use the hardware context. */
@@ -95,4 +95,4 @@ void callAddGlobalTestEnvironment();
 
 } // namespace test
 } // namespace gmx
-#endif
+#endif // GMX_TESTUTILS_TEST_HARDWARE_ENVIRONMENT_H
index 0e16df6fdf2f4c500990446e60b2fb351ace88f1..753b814a0f3669d90f5a31ef8b80b335b47d1ccc 100644 (file)
@@ -70,6 +70,7 @@
 
 #include "testutils/mpi_printer.h"
 #include "testutils/refdata.h"
+#include "testutils/test_hardware_environment.h"
 #include "testutils/testfilemanager.h"
 #include "testutils/testoptions.h"
 
index c1bd19d86ee5bab6a5a1f9555b6414f45c0c6272..4f7e3b40aa0a6cbed246f32f0ddeae24e9849073 100644 (file)
@@ -89,19 +89,6 @@ void initTestUtils(const char* dataPath,
 void finalizeTestUtils();
 //! \endcond
 
-/*! \brief Declare a function that all unit test implementations can use
- * to set up any environment that they need.
- *
- * When registering the unit test in CMake, the HARDWARE_DETECTION
- * flag requires that the code for that unit test implements this
- * function.  Otherwise, a default stub implementation is provided.
- *
- * This approach conforms to the recommendation by GoogleTest to
- * arrange for the code that sets up the global test environment to be
- * called from main, rather than potentially rely on brittle static
- * initialization order. */
-void callAddGlobalTestEnvironment();
-
 } // namespace test
 } // namespace gmx
 
index 1faf7979dd20cb7e1de2a3e1de0f83cd14621db6..e5bb19f76a82d1412fe04f1e2acaec63c22d952b 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2010-2017, The GROMACS development team.
- * Copyright (c) 2019, by the GROMACS development team, led by
+ * Copyright (c) 2019,2020, 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.
 #ifndef TEST_USES_HARDWARE_DETECTION
 //! Whether the test expects/supports running with knowledge of the hardware.
 #    define TEST_USES_HARDWARE_DETECTION false
-namespace gmx
-{
-namespace test
-{
-//! Implement a stub definition for tests that don't ask for a real one.
-void callAddGlobalTestEnvironment(){};
-} // namespace test
-} // namespace gmx
 #endif
 
 /*! \brief