* This file is part of the GROMACS molecular simulation package.
*
* Copyright (c) 2015,2016,2017,2018,2019 by the GROMACS development team.
- * Copyright (c) 2020, by the GROMACS development team, led by
+ * Copyright (c) 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 "gromacs/hardware/detecthardware.h"
#include "gromacs/hardware/hardwaretopology.h"
#include "gromacs/hardware/hw_info.h"
+#include "gromacs/listed_forces/listed_forces_gpu.h"
#include "gromacs/mdlib/gmx_omp_nthreads.h"
#include "gromacs/mdlib/update_constrain_gpu.h"
#include "gromacs/mdtypes/commrec.h"
#include "gromacs/utility/fatalerror.h"
#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/logger.h"
+#include "gromacs/utility/message_string_collector.h"
#include "gromacs/utility/stringutil.h"
{
//! Helper variable to localise the text of an often repeated message.
-const char* g_specifyEverythingFormatString =
+const char* const g_specifyEverythingFormatString =
"When you use mdrun -gputasks, %s must be set to non-default "
"values, so that the device IDs can be interpreted correctly."
#if GMX_GPU
// OpenCL standard, but the only current relevant case for GROMACS
// is AMD OpenCL, which offers this variable.
"GPU_DEVICE_ORDINAL"
-# elif GMX_GPU_SYCL
- // SYCL-TODO:
- "How to restrict visible devices in SYCL?"
+# elif GMX_GPU_SYCL && GMX_SYCL_DPCPP
+ // https://github.com/intel/llvm/blob/sycl/sycl/doc/EnvironmentVariables.md
+ "SYCL_DEVICE_FILTER"
+# elif GMX_GPU_SYCL && GMX_SYCL_HIPSYCL
+ // Not true if we use hipSYCL over CUDA or IntelLLVM, but in that case the user probably
+ // knows what they are doing.
+ // https://rocmdocs.amd.com/en/latest/Other_Solutions/Other-Solutions.html#hip-environment-variables
+ "HIP_VISIBLE_DEVICES"
# else
# error "Unreachable branch"
# endif
} // namespace
bool decideWhetherToUseGpusForNonbondedWithThreadMpi(const TaskTarget nonbondedTarget,
- const int numDevicesToUse,
+ const bool haveAvailableDevices,
const std::vector<int>& userGpuTaskAssignment,
const EmulateGpuNonbonded emulateGpuNonbonded,
const bool buildSupportsNonbondedOnGpu,
// all potential ranks can use, and can use that in a global
// decision that will later be consistent.
// If we get here, then the user permitted or required GPUs.
- return (numDevicesToUse > 0);
+ return haveAvailableDevices;
+}
+
+static bool canUseGpusForPme(const bool useGpuForNonbonded,
+ const TaskTarget pmeTarget,
+ const TaskTarget pmeFftTarget,
+ const gmx_hw_info_t& hardwareInfo,
+ const t_inputrec& inputrec,
+ std::string* errorMessage)
+{
+ if (pmeTarget == TaskTarget::Cpu)
+ {
+ return false;
+ }
+
+ std::string tempString;
+ gmx::MessageStringCollector errorReasons;
+ // Before changing the prefix string, make sure that it is not searched for in regression tests.
+ errorReasons.startContext("Cannot compute PME interactions on a GPU, because:");
+ errorReasons.appendIf(!useGpuForNonbonded, "Nonbonded interactions must also run on GPUs.");
+ errorReasons.appendIf(!pme_gpu_supports_build(&tempString), tempString);
+ errorReasons.appendIf(!pme_gpu_supports_hardware(hardwareInfo, &tempString), tempString);
+ errorReasons.appendIf(!pme_gpu_supports_input(inputrec, &tempString), tempString);
+ if (pmeFftTarget == TaskTarget::Cpu)
+ {
+ // User requested PME FFT on CPU, so we check whether we are able to use PME Mixed mode.
+ errorReasons.appendIf(!pme_gpu_mixed_mode_supports_input(inputrec, &tempString), tempString);
+ }
+ errorReasons.finishContext();
+
+ if (errorReasons.isEmpty())
+ {
+ return true;
+ }
+ else
+ {
+ if (pmeTarget == TaskTarget::Gpu && errorMessage != nullptr)
+ {
+ *errorMessage = errorReasons.toString();
+ }
+ return false;
+ }
}
bool decideWhetherToUseGpusForPmeWithThreadMpi(const bool useGpuForNonbonded,
const TaskTarget pmeTarget,
+ const TaskTarget pmeFftTarget,
const int numDevicesToUse,
const std::vector<int>& userGpuTaskAssignment,
const gmx_hw_info_t& hardwareInfo,
const int numPmeRanksPerSimulation)
{
// First, exclude all cases where we can't run PME on GPUs.
- if ((pmeTarget == TaskTarget::Cpu) || !useGpuForNonbonded || !pme_gpu_supports_build(nullptr)
- || !pme_gpu_supports_hardware(hardwareInfo, nullptr) || !pme_gpu_supports_input(inputrec, nullptr))
+ if (!canUseGpusForPme(useGpuForNonbonded, pmeTarget, pmeFftTarget, hardwareInfo, inputrec, nullptr))
{
- // PME can't run on a GPU. If the user required that, we issue
- // an error later.
+ // PME can't run on a GPU. If the user required that, we issue an error later.
return false;
}
bool decideWhetherToUseGpusForPme(const bool useGpuForNonbonded,
const TaskTarget pmeTarget,
+ const TaskTarget pmeFftTarget,
const std::vector<int>& userGpuTaskAssignment,
const gmx_hw_info_t& hardwareInfo,
const t_inputrec& inputrec,
const int numPmeRanksPerSimulation,
const bool gpusWereDetected)
{
- if (pmeTarget == TaskTarget::Cpu)
- {
- return false;
- }
-
- if (!useGpuForNonbonded)
- {
- if (pmeTarget == TaskTarget::Gpu)
- {
- GMX_THROW(NotImplementedError(
- "PME on GPUs is only supported when nonbonded interactions run on GPUs also."));
- }
- return false;
- }
-
std::string message;
- if (!pme_gpu_supports_build(&message))
+ if (!canUseGpusForPme(useGpuForNonbonded, pmeTarget, pmeFftTarget, hardwareInfo, inputrec, &message))
{
- if (pmeTarget == TaskTarget::Gpu)
+ if (!message.empty())
{
- GMX_THROW(NotImplementedError("Cannot compute PME interactions on a GPU, because " + message));
- }
- return false;
- }
- if (!pme_gpu_supports_hardware(hardwareInfo, &message))
- {
- if (pmeTarget == TaskTarget::Gpu)
- {
- GMX_THROW(NotImplementedError("Cannot compute PME interactions on a GPU, because " + message));
- }
- return false;
- }
- if (!pme_gpu_supports_input(inputrec, &message))
- {
- if (pmeTarget == TaskTarget::Gpu)
- {
- GMX_THROW(NotImplementedError("Cannot compute PME interactions on a GPU, because " + message));
+ GMX_THROW(InconsistentInputError(message));
}
return false;
}
}
}
-bool decideWhetherToUseGpusForBonded(const bool useGpuForNonbonded,
- const bool useGpuForPme,
- const TaskTarget bondedTarget,
- const bool canUseGpuForBonded,
- const bool usingLJPme,
- const bool usingElecPmeOrEwald,
- const int numPmeRanksPerSimulation,
- const bool gpusWereDetected)
+bool decideWhetherToUseGpusForBonded(bool useGpuForNonbonded,
+ bool useGpuForPme,
+ TaskTarget bondedTarget,
+ const t_inputrec& inputrec,
+ const gmx_mtop_t& mtop,
+ int numPmeRanksPerSimulation,
+ bool gpusWereDetected)
{
if (bondedTarget == TaskTarget::Cpu)
{
return false;
}
- if (!canUseGpuForBonded)
+ std::string errorMessage;
+
+ if (!buildSupportsListedForcesGpu(&errorMessage))
{
if (bondedTarget == TaskTarget::Gpu)
{
- GMX_THROW(InconsistentInputError(
- "Bonded interactions on the GPU were required, but not supported for these "
- "simulation settings. Change your settings, or do not require using GPUs."));
+ GMX_THROW(InconsistentInputError(errorMessage.c_str()));
+ }
+
+ return false;
+ }
+
+ if (!inputSupportsListedForcesGpu(inputrec, mtop, &errorMessage))
+ {
+ if (bondedTarget == TaskTarget::Gpu)
+ {
+ GMX_THROW(InconsistentInputError(errorMessage.c_str()));
}
return false;
// is busy, for which we currently only check PME or Ewald.
// (It would be better to dynamically assign bondeds based on timings)
// Note that here we assume that the auto setting of PME ranks will not
- // choose seperate PME ranks when nonBonded are assigned to the GPU.
+ // choose separate PME ranks when nonBonded are assigned to the GPU.
bool usingOurCpuForPmeOrEwald =
- (usingLJPme || (usingElecPmeOrEwald && !useGpuForPme && numPmeRanksPerSimulation <= 0));
+ (EVDW_PME(inputrec.vdwtype)
+ || (EEL_PME_EWALD(inputrec.coulombtype) && !useGpuForPme && numPmeRanksPerSimulation <= 0));
return gpusWereDetected && usingOurCpuForPmeOrEwald;
}
const gmx_mtop_t& mtop,
const bool useEssentialDynamics,
const bool doOrientationRestraints,
- const bool useReplicaExchange,
+ const bool haveFrozenAtoms,
const bool doRerun,
const DevelopmentFeatureFlags& devFlags,
const gmx::MDLogger& mdlog)
if (isDomainDecomposition)
{
- if (!devFlags.enableGpuHaloExchange)
+ if (hasAnyConstraints && !useUpdateGroups)
{
- errorMessage += "Domain decomposition without GPU halo exchange is not supported.\n ";
- }
- else
- {
- if (hasAnyConstraints && !useUpdateGroups)
- {
- errorMessage +=
- "Domain decomposition is only supported with constraints when update "
- "groups "
- "are used. This means constraining all bonds is not supported, except for "
- "small molecules, and box sizes close to half the pair-list cutoff are not "
- "supported.\n ";
- }
-
- if (pmeUsesCpu)
- {
- errorMessage += "With domain decomposition, PME must run fully on the GPU.\n";
- }
+ errorMessage +=
+ "Domain decomposition is only supported with constraints when update "
+ "groups "
+ "are used. This means constraining all bonds is not supported, except for "
+ "small molecules, and box sizes close to half the pair-list cutoff are not "
+ "supported.\n ";
}
}
{
errorMessage += "With separate PME rank(s), PME must run fully on the GPU.\n";
}
-
- if (!devFlags.enableGpuPmePPComm)
- {
- errorMessage += "With separate PME rank(s), PME must use direct communication.\n";
- }
}
if (inputrec.useMts)
errorMessage += "Multiple time stepping is not supported.\n";
}
- if (inputrec.eConstrAlg == econtSHAKE && hasAnyConstraints && gmx_mtop_ftype_count(mtop, F_CONSTR) > 0)
+ if (inputrec.eConstrAlg == ConstraintAlgorithm::Shake && hasAnyConstraints
+ && gmx_mtop_ftype_count(mtop, F_CONSTR) > 0)
{
errorMessage += "SHAKE constraints are not supported.\n";
}
errorMessage +=
"Either PME or short-ranged non-bonded interaction tasks must run on the GPU.\n";
}
-
if (!gpusWereDetected)
{
errorMessage += "Compatible GPUs must have been found.\n";
}
- if (!GMX_GPU_CUDA)
+ if (!(GMX_GPU_CUDA || GMX_GPU_SYCL))
{
- errorMessage += "Only a CUDA build is supported.\n";
+ errorMessage += "Only CUDA and SYCL builds are supported.\n";
}
- if (inputrec.eI != eiMD)
+ if (inputrec.eI != IntegrationAlgorithm::MD)
{
errorMessage += "Only the md integrator is supported.\n";
}
- if (inputrec.etc == etcNOSEHOOVER)
+ if (inputrec.etc == TemperatureCoupling::NoseHoover)
{
errorMessage += "Nose-Hoover temperature coupling is not supported.\n";
}
- if (!(inputrec.epc == epcNO || inputrec.epc == epcPARRINELLORAHMAN
- || inputrec.epc == epcBERENDSEN || inputrec.epc == epcCRESCALE))
+ if (!(inputrec.epc == PressureCoupling::No || inputrec.epc == PressureCoupling::ParrinelloRahman
+ || inputrec.epc == PressureCoupling::Berendsen || inputrec.epc == PressureCoupling::CRescale))
{
errorMessage +=
"Only Parrinello-Rahman, Berendsen, and C-rescale pressure coupling are "
"supported.\n";
}
+ if (inputrec.cos_accel != 0 || inputrec.useConstantAcceleration)
+ {
+ errorMessage += "Acceleration is not supported.\n";
+ }
if (EEL_PME_EWALD(inputrec.coulombtype) && inputrec.epsilon_surface != 0)
{
// The graph is needed, but not supported
// The graph is needed, but not supported
errorMessage += "Orientation restraints are not supported.\n";
}
- if (inputrec.efep != efepNO
- && (haveFreeEnergyType(inputrec, efptBONDED) || haveFreeEnergyType(inputrec, efptMASS)))
+ if (inputrec.efep != FreeEnergyPerturbationType::No
+ && (haveFepPerturbedMasses(mtop) || havePerturbedConstraints(mtop)))
{
errorMessage += "Free energy perturbation for mass and constraints are not supported.\n";
}
const auto particleTypes = gmx_mtop_particletype_count(mtop);
- if (particleTypes[eptShell] > 0)
+ if (particleTypes[ParticleType::Shell] > 0)
{
errorMessage += "Shells are not supported.\n";
}
- if (useReplicaExchange)
- {
- errorMessage += "Replica exchange simulations are not supported.\n";
- }
- if (inputrec.eSwapCoords != eswapNO)
+ if (inputrec.eSwapCoords != SwapType::No)
{
errorMessage += "Swapping the coordinates is not supported.\n";
}
"The number of coupled constraints is higher than supported in the GPU LINCS "
"code.\n";
}
+ if (hasAnyConstraints && !UpdateConstrainGpu::areConstraintsSupported())
+ {
+ errorMessage += "Chosen GPU implementation does not support constraints.\n";
+ }
+ if (haveFrozenAtoms)
+ {
+ // There is a known bug with frozen atoms and GPU update, see Issue #3920.
+ errorMessage += "Frozen atoms not supported.\n";
+ }
if (!errorMessage.empty())
{