Task assignment for bonded interactions on CUDA GPUs
[alexxy/gromacs.git] / src / gromacs / listed-forces / manage-threading.cpp
index 1541edd81a314f5ea6b0086030254e02968fa77d..7c50b774aa798615c277e984e954304829e33961 100644 (file)
 
 #include <algorithm>
 #include <array>
+#include <string>
 
 #include "gromacs/listed-forces/listed-forces.h"
 #include "gromacs/mdlib/gmx_omp_nthreads.h"
+#include "gromacs/mdtypes/inputrec.h"
 #include "gromacs/pbcutil/ishift.h"
 #include "gromacs/topology/ifunc.h"
+#include "gromacs/topology/topology.h"
+#include "gromacs/utility/arrayref.h"
 #include "gromacs/utility/exceptions.h"
 #include "gromacs/utility/fatalerror.h"
 #include "gromacs/utility/gmxassert.h"
@@ -248,6 +252,8 @@ static void divide_bondeds_over_threads(bonded_threading_t *bt,
         int            nrToAssignToCpuThreads = il.nr;
 
         if (useGpuForBondeds &&
+                     // TODO remove the next line when we have GPU bonded kernels
+            false && // NOLINT readability-simplify-boolean-expr
             ftypeGpuIndex < ftypesOnGpu.size() &&
             ftypesOnGpu[ftypeGpuIndex] == ftype)
         {
@@ -370,6 +376,109 @@ static void convertIlistToNbnxnOrder(const t_ilist            &src,
     }
 }
 
+namespace gmx
+{
+
+//! Returns whether there are any interactions suitable for a GPU.
+static bool someInteractionsCanRunOnGpu(const InteractionLists &ilists)
+{
+    for (int ftype : ftypesOnGpu)
+    {
+        if (!ilists[ftype].iatoms.empty())
+        {
+            // Perturbation is not implemented in the GPU bonded
+            // kernels. If all the interactions were actually
+            // perturbed, then that will be detected later on each
+            // domain, and work will never run on the GPU. This is
+            // very unlikely to occur, and has little run-time cost,
+            // so we don't complicate the code by catering for it
+            // here.
+            return true;
+        }
+    }
+    return false;
+}
+
+//! Returns whether there are any interactions suitable for a GPU.
+static bool bondedInteractionsCanRunOnGpu(const gmx_mtop_t &mtop)
+{
+    // Check the regular molecule types
+    for (const auto &moltype : mtop.moltype)
+    {
+        if (someInteractionsCanRunOnGpu(moltype.ilist))
+        {
+            return true;
+        }
+    }
+    // Check the inter-molecular interactions.
+    if (mtop.intermolecular_ilist)
+    {
+        if (someInteractionsCanRunOnGpu(*mtop.intermolecular_ilist))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
+/*! \brief Help build a descriptive message in \c error if there are
+ * \c errorReasons why bondeds on a GPU are not supported.
+ *
+ * \returns Whether the lack of errorReasons indicate there is support. */
+static bool
+addMessageIfNotSupported(ArrayRef <const std::string> errorReasons,
+                         std::string                 *error)
+{
+    bool isSupported = errorReasons.empty();
+    if (!isSupported && error)
+    {
+        *error  = "Bonded interactions cannot run on GPUs: ";
+        *error += joinStrings(errorReasons, "; ") + ".";
+    }
+    return isSupported;
+}
+
+bool buildSupportsGpuBondeds(std::string *error)
+{
+    std::vector<std::string> errorReasons;
+    if (GMX_DOUBLE)
+    {
+        errorReasons.emplace_back("not supported with double precision");
+    }
+    if (GMX_GPU == GMX_GPU_OPENCL)
+    {
+        errorReasons.emplace_back("not supported with OpenCL build of GROMACS");
+    }
+    else if (GMX_GPU == GMX_GPU_NONE)
+    {
+        errorReasons.emplace_back("not supported with CPU-only build of GROMACS");
+    }
+    return addMessageIfNotSupported(errorReasons, error);
+}
+
+bool inputSupportsGpuBondeds(const t_inputrec &ir,
+                             const gmx_mtop_t &mtop,
+                             std::string      *error)
+{
+    std::vector<std::string> errorReasons;
+
+    if (!bondedInteractionsCanRunOnGpu(mtop))
+    {
+        errorReasons.emplace_back("No supported bonded interactions are present");
+    }
+    if (ir.cutoff_scheme == ecutsGROUP)
+    {
+        errorReasons.emplace_back("group cutoff scheme");
+    }
+    if (!EI_DYNAMICS(ir.eI))
+    {
+        errorReasons.emplace_back("not a dynamical integrator");
+    }
+    return addMessageIfNotSupported(errorReasons, error);
+}
+
+} // namespace gmx
+
 //! Divides bonded interactions over threads and GPU
 void assign_bondeds_to_gpu(GpuBondedLists           *gpuBondedLists,
                            gmx::ArrayRef<const int>  nbnxnAtomOrder,