// \todo pass by value
builder.addElectrostatics(options_.pme_opt_choices[0], options_.pme_fft_opt_choices[0]);
builder.addBondedTaskAssignment(options_.bonded_opt_choices[0]);
+ builder.addUpdateTaskAssignment(options_.update_opt_choices[0]);
builder.addNeighborList(options_.nstlist_cmdline);
builder.addReplicaExchange(options_.replExParams);
// \todo take ownership of multisim resources (ms)
gmx_membed_t *membed,
gmx_walltime_accounting *walltime_accounting,
std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder,
- bool doRerun) :
+ bool doRerun,
+ bool useGpuForUpdate) :
fplog(fplog),
cr(cr),
ms(ms),
membed(membed),
walltime_accounting(walltime_accounting),
stopHandlerBuilder(std::move(stopHandlerBuilder)),
- doRerun(doRerun)
+ doRerun(doRerun),
+ useGpuForUpdate(useGpuForUpdate)
{}
protected:
std::unique_ptr<StopHandlerBuilder> stopHandlerBuilder;
//! Whether we're doing a rerun.
bool doRerun;
+ //! Whether we will use the GPU for calculating the update.
+ bool useGpuForUpdate;
};
{ nullptr, "auto", "cpu", "gpu", nullptr };
const char *bonded_opt_choices[5] =
{ nullptr, "auto", "cpu", "gpu", nullptr };
+ const char *update_opt_choices[5] =
+ { nullptr, "auto", "cpu", "gpu", nullptr };
const char *gpuIdsAvailable = "";
const char *userGpuTaskAssignment = "";
+
ImdOptions &imdOptions = mdrunOptions.imdOptions;
- t_pargs pa[47] = {
+ t_pargs pa[48] = {
{ "-dd", FALSE, etRVEC, {&realddxyz},
"Domain decomposition grid, 0 is optimize" },
"Perform PME FFT calculations on" },
{ "-bonded", FALSE, etENUM, {bonded_opt_choices},
"Perform bonded calculations on" },
+ { "-update", FALSE, etENUM, {update_opt_choices},
+ "Perform update and constraints on"},
{ "-v", FALSE, etBOOL, {&mdrunOptions.verbose},
"Be loud and noisy" },
{ "-pforce", FALSE, etREAL, {&pforce},
using gmx::SimulationSignaller;
-//! Whether the GPU versions of Leap-Frog integrator and LINCS and SETTLE constraints
-static const bool c_useGpuUpdateConstrain = (getenv("GMX_UPDATE_CONSTRAIN_GPU") != nullptr);
-
void gmx::LegacySimulator::do_md()
{
// TODO Historically, the EM and MD "integrators" used different
upd.setNumAtoms(state->natoms);
}
- if (c_useGpuUpdateConstrain)
+ if (useGpuForUpdate)
{
GMX_RELEASE_ASSERT(ir->eI == eiMD, "Only md integrator is supported on the GPU.");
GMX_RELEASE_ASSERT(ir->etc != etcNOSEHOOVER, "Nose Hoover temperature coupling is not supported on the GPU.");
GMX_RELEASE_ASSERT(!mdatoms->haveVsites, "Virtual sites are not supported on the GPU");
GMX_RELEASE_ASSERT(ed == nullptr, "Essential dynamics is not supported with GPU-based update constraints.");
GMX_LOG(mdlog.info).asParagraph().
- appendText("Using CUDA GPU-based update and constraints module.");
+ appendText("Updating coordinates on the GPU.");
integrator = std::make_unique<UpdateConstrainCuda>(*ir, *top_global);
integrator->set(top.idef, *mdatoms, ekind->ngtc);
t_pbc pbc;
std::copy(state->x.begin(), state->x.end(), cbuf.begin());
}
- if (c_useGpuUpdateConstrain)
+ if (useGpuForUpdate)
{
if (bNS)
{
newRunner.pme_opt = pme_opt;
newRunner.pme_fft_opt = pme_fft_opt;
newRunner.bonded_opt = bonded_opt;
+ newRunner.update_opt = update_opt;
newRunner.nstlist_cmdline = nstlist_cmdline;
newRunner.replExParams = replExParams;
newRunner.pforce = pforce;
auto pmeTarget = findTaskTarget(pme_opt);
auto pmeFftTarget = findTaskTarget(pme_fft_opt);
auto bondedTarget = findTaskTarget(bonded_opt);
+ auto updateTarget = findTaskTarget(update_opt);
PmeRunMode pmeRunMode = PmeRunMode::None;
// Here we assume that SIMMASTER(cr) does not change even after the
// Note that when bonded interactions run on a GPU they always run
// alongside a nonbonded task, so do not influence task assignment
// even though they affect the force calculation workload.
- bool useGpuForNonbonded = false;
- bool useGpuForPme = false;
- bool useGpuForBonded = false;
+ bool useGpuForNonbonded = false;
+ bool useGpuForPme = false;
+ bool useGpuForBonded = false;
+ bool useGpuForUpdate = false;
+ bool gpusWereDetected = hwinfo->ngpu_compatible_tot > 0;
try
{
// It's possible that there are different numbers of GPUs on
// different nodes, which is the user's responsibilty to
// handle. If unsuitable, we will notice that during task
// assignment.
- bool gpusWereDetected = hwinfo->ngpu_compatible_tot > 0;
auto canUseGpuForNonbonded = buildSupportsNonbondedOnGpu(nullptr);
useGpuForNonbonded = decideWhetherToUseGpusForNonbonded(nonbondedTarget, userGpuTaskAssignment,
emulateGpuNonbonded,
fr->cginfo_mb);
}
+ if (updateTarget == TaskTarget::Gpu)
+ {
+ if (SIMMASTER(cr))
+ {
+ gmx_fatal(FARGS, "It is currently not possible to redirect the calculation "
+ "of update and constraints to the GPU!");
+ }
+ }
+
+ // Before we start the actual simulator, try if we can run the update task on the GPU.
+ useGpuForUpdate = decideWhetherToUseGpuForUpdate(DOMAINDECOMP(cr),
+ useGpuForNonbonded,
+ updateTarget,
+ gpusWereDetected,
+ *inputrec,
+ *mdAtoms,
+ doEssentialDynamics,
+ fcd->orires.nr != 0,
+ fcd->disres.nsystems != 0);
+
// TODO This is not the right place to manage the lifetime of
// this data structure, but currently it's the easiest way to
// make it work.
membed,
walltime_accounting,
std::move(stopHandlerBuilder_),
- doRerun);
+ doRerun,
+ useGpuForUpdate);
simulator->run();
if (inputrec->bPull)
void addBondedTaskAssignment(const char* bonded_opt);
+ void addUpdateTaskAssignment(const char* update_opt);
+
void addHardwareOptions(const gmx_hw_opt_t &hardwareOptions);
void addFilenames(ArrayRef <const t_filenm> filenames);
// Default parameters copied from runner.h
// \todo Clarify source(s) of default parameters.
- const char* nbpu_opt_ = nullptr;
- const char* pme_opt_ = nullptr;
- const char* pme_fft_opt_ = nullptr;
- const char *bonded_opt_ = nullptr;
+ const char* nbpu_opt_ = nullptr;
+ const char* pme_opt_ = nullptr;
+ const char* pme_fft_opt_ = nullptr;
+ const char *bonded_opt_ = nullptr;
+ const char *update_opt_ = nullptr;
MdrunOptions mdrunOptions_;
GMX_THROW(gmx::APIError("MdrunnerBuilder::addBondedTaskAssignment() is required before build()"));
}
+ if (update_opt_)
+ {
+ newRunner.update_opt = update_opt_;
+ }
+ else
+ {
+ GMX_THROW(gmx::APIError("MdrunnerBuilder::addUpdateTaskAssignment() is required before build() "));
+ }
+
+
newRunner.restraintManager_ = std::make_unique<gmx::RestraintManager>();
if (stopHandlerBuilder_)
bonded_opt_ = bonded_opt;
}
+void Mdrunner::BuilderImplementation::addUpdateTaskAssignment(const char* update_opt)
+{
+ update_opt_ = update_opt;
+}
+
void Mdrunner::BuilderImplementation::addHardwareOptions(const gmx_hw_opt_t &hardwareOptions)
{
hardwareOptions_ = hardwareOptions;
return *this;
}
+MdrunnerBuilder &MdrunnerBuilder::addUpdateTaskAssignment(const char* update_opt)
+{
+ impl_->addUpdateTaskAssignment(update_opt);
+ return *this;
+}
+
Mdrunner MdrunnerBuilder::build()
{
return impl_->build();
*/
const char *bonded_opt = nullptr;
+ /*! \brief Target update calculation for "cpu", "gpu", or "auto". Default is "auto".
+ *
+ * \internal
+ * \todo replace with string or enum class and initialize with sensible value.
+ */
+ const char *update_opt = nullptr;
+
//! Command-line override for the duration of a neighbor list with the Verlet scheme.
int nstlist_cmdline = 0;
//! Parameters for replica-exchange simulations.
*/
MdrunnerBuilder &addBondedTaskAssignment(const char *bonded_opt);
+ /*! \brief
+ * Assign responsibility for tasks for update and constrain calculation.
+ *
+ * Required. Director code should provide valid options for
+ * update and constraint task assignment. The builder does not apply any
+ * defaults, so client code should be prepared to provide
+ * (e.g.) "auto" in the event no user input or logic provides
+ * an alternative argument.
+ *
+ * \param[in] update_opt Target update calculation for "cpu", "gpu", or "auto".
+ *
+ * Calling must guarantee that the pointed-to C strings are valid through
+ * simulation launch.
+ *
+ * \internal
+ * The arguments are passed as references to elements of arrays of C strings.
+ * \todo Replace with modern strings or (better) enum classes.
+ * \todo Make optional and/or encapsulate into task assignment module.
+ */
+ MdrunnerBuilder &addUpdateTaskAssignment(const char *update_opt);
+
/*!
* \brief Provide access to the multisim communicator to use.
*
#include "gromacs/hardware/hardwaretopology.h"
#include "gromacs/hardware/hw_info.h"
#include "gromacs/mdlib/gmx_omp_nthreads.h"
+#include "gromacs/mdlib/mdatoms.h"
#include "gromacs/mdtypes/commrec.h"
#include "gromacs/mdtypes/inputrec.h"
#include "gromacs/mdtypes/md_enums.h"
+#include "gromacs/mdtypes/mdrunoptions.h"
#include "gromacs/taskassignment/taskassignment.h"
#include "gromacs/topology/topology.h"
#include "gromacs/utility/baseversion.h"
return gpusWereDetected && usingOurCpuForPmeOrEwald;
}
+bool decideWhetherToUseGpuForUpdate(bool isDomainDecomposition,
+ bool useGpuForNonbonded,
+ TaskTarget updateTarget,
+ bool gpusWereDetected,
+ const t_inputrec &inputrec,
+ const MDAtoms &mdatoms,
+ bool useEssentialDynamics,
+ bool doOrientationRestraints,
+ bool doDistanceRestraints)
+{
+ if (updateTarget == TaskTarget::Cpu)
+ {
+ return false;
+ }
+
+ std::string errorMessage;
+
+ if (isDomainDecomposition)
+ {
+ errorMessage += "Domain decomposition is not supported.\n";
+ }
+ if (!useGpuForNonbonded)
+ {
+ errorMessage += "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 != GMX_GPU_CUDA)
+ {
+ errorMessage += "Only a CUDA build is supported.\n";
+ }
+ if (inputrec.eI != eiMD)
+ {
+ errorMessage += "Only the md integrator is supported.\n";
+ }
+ if (inputrec.etc == etcNOSEHOOVER)
+ {
+ errorMessage += "Nose-Hoover temperature coupling is not supported.\n";
+ }
+ if (inputrec.epc != epcNO && inputrec.epc != epcPARRINELLORAHMAN)
+ {
+ errorMessage += "Only Parrinello-Rahman pressure control is supported.\n";
+ }
+ if (mdatoms.mdatoms()->haveVsites)
+ {
+ errorMessage += "Virtual sites are not supported.\n";
+ }
+ if (useEssentialDynamics)
+ {
+ errorMessage += "Essential dynamics is not supported.\n";
+ }
+ if (inputrec.bPull || inputrec.pull)
+ {
+ errorMessage += "Pulling is not supported.\n";
+ }
+ if (doOrientationRestraints)
+ {
+ errorMessage += "Orientation restraints are not supported.\n";
+ }
+ if (doDistanceRestraints)
+ {
+ errorMessage += "Distance restraints are not supported.\n";
+ }
+ if (inputrec.efep != efepNO)
+ {
+ errorMessage += "Free energy perturbations are not supported.\n";
+ }
+ if (!errorMessage.empty())
+ {
+ if (updateTarget == TaskTarget::Gpu)
+ {
+ std::string prefix = gmx::formatString("Update task on the GPU was required,\n"
+ "but the following condition(s) were not satisfied:\n");
+ GMX_THROW(InconsistentInputError((prefix + errorMessage).c_str()));
+ }
+ return false;
+ }
+
+ return true;
+}
+
} // namespace gmx
Yes
};
+class MDAtoms;
+
/*! \brief Decide whether this thread-MPI simulation will run
* nonbonded tasks on GPUs.
*
int numPmeRanksPerSimulation,
bool gpusWereDetected);
+/*! \brief Decide whether to use GPU for update.
+ *
+ * \param[in] isDomainDecomposition Whether there more than one domain.
+ * \param[in] useGpuForNonbonded Whether GPUs will be used for nonbonded interactions.
+ * \param[in] updateTarget User choice for running simulation on GPU.
+ * \param[in] gpusWereDetected Whether compatible GPUs were detected on any node.
+ * \param[in] inputrec The user input.
+ * \param[in] mdatoms Information about simulation atoms.
+ * \param[in] useEssentialDynamics If essential dynamics is active.
+ * \param[in] doOrientationRestraints If orientation restraints are enabled.
+ * \param[in] doDistanceRestraints If distance restraints are enabled.
+ *
+ * \returns Whether complete simulation can be run on GPU.
+ * \throws std::bad_alloc If out of memory
+ * InconsistentInputError If the user requirements are inconsistent.
+ */
+bool decideWhetherToUseGpuForUpdate(bool isDomainDecomposition,
+ bool useGpuForNonbonded,
+ TaskTarget updateTarget,
+ bool gpusWereDetected,
+ const t_inputrec &inputrec,
+ const MDAtoms &mdatoms,
+ bool useEssentialDynamics,
+ bool doOrientationRestraints,
+ bool doDistanceRestraints);
+
+
} // namespace gmx
#endif
// \todo pass by value
builder.addElectrostatics(options.pme_opt_choices[0], options.pme_fft_opt_choices[0]);
builder.addBondedTaskAssignment(options.bonded_opt_choices[0]);
+ builder.addUpdateTaskAssignment(options.update_opt_choices[0]);
builder.addNeighborList(options.nstlist_cmdline);
builder.addReplicaExchange(options.replExParams);
// \todo take ownership of multisim resources (ms)
[-pinstride <int>] [-gpu_id <string>] [-gputasks <string>] [-[no]ddcheck]
[-rdd <real>] [-rcon <real>] [-dlb <enum>] [-dds <real>] [-nb <enum>]
[-nstlist <int>] [-[no]tunepme] [-pme <enum>] [-pmefft <enum>]
- [-bonded <enum>] [-[no]v] [-pforce <real>] [-[no]reprod] [-cpt <real>]
- [-[no]cpnum] [-[no]append] [-nsteps <int>] [-maxh <real>] [-replex <int>]
- [-nex <int>] [-reseed <int>]
+ [-bonded <enum>] [-update <enum>] [-[no]v] [-pforce <real>] [-[no]reprod]
+ [-cpt <real>] [-[no]cpnum] [-[no]append] [-nsteps <int>] [-maxh <real>]
+ [-replex <int>] [-nex <int>] [-reseed <int>]
DESCRIPTION
Perform PME FFT calculations on: auto, cpu, gpu
-bonded <enum> (auto)
Perform bonded calculations on: auto, cpu, gpu
+ -update <enum> (auto)
+ Perform update and constraints on: auto, cpu, gpu
-[no]v (no)
Be loud and noisy
-pforce <real> (-1)
},
{
interaction_function[F_EKIN].longname,
- relativeToleranceAsPrecisionDependentUlp(10.0, 100, 40)
+ relativeToleranceAsPrecisionDependentUlp(60.0, 100, 40)
},
{
interaction_function[F_PRES].longname,