*/
/*! \internal \file
* \brief
- * This implements basic PME sanity test (using single-rank mdrun).
+ * This implements basic PME sanity tests.
* It runs the input system with PME for several steps (on CPU and GPU, if available),
* and checks the reciprocal and conserved energies.
- * TODO: implement multi-rank tests as well.
+ * As part of mdrun-test, this will always run single rank PME simulation.
+ * As part of mdrun-mpi-test, this will run same as above when a single rank is requested,
+ * or a simulation with a single separate PME rank ("-npme 1") when multiple ranks are requested.
+ * \todo Extend and generalize this for more multi-rank tests (-npme 0, -npme 2, etc).
+ * \todo Implement death tests (e.g. for PME GPU decomposition).
*
* \author Aleksei Iupinov <a.yupinov@gmail.com>
* \ingroup module_mdrun_integration_tests
#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/utility/cstringutil.h"
+#include "gromacs/utility/gmxmpi.h"
#include "gromacs/utility/stringutil.h"
+#include "testutils/mpitest.h"
#include "testutils/refdata.h"
#include "energyreader.h"
const std::string inputFile = "spc-and-methanol";
runner_.useTopGroAndNdxFromDatabase(inputFile.c_str());
+ // 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.
+ const bool parallelRun = (getNumberOfTestMpiRanks() > 1);
+ const bool useSeparatePme = parallelRun;
+
EXPECT_EQ(0, runner_.callGrompp());
//TODO test all proper/improper combinations in more thorough way?
runModes["PmeOnGpuFftAuto"] = {"-pme", "gpu", "-pmefft", "auto"};
TestReferenceData refData;
TestReferenceChecker rootChecker(refData.rootChecker());
-
+ const bool thisRankChecks = (gmx_node_rank() == 0);
+ if (!thisRankChecks)
+ {
+ EXPECT_NONFATAL_FAILURE(rootChecker.checkUnusedEntries(), ""); // skip checks on other ranks
+ }
for (const auto &mode : runModes)
{
auto modeTargetsGpus = (mode.first.find("Gpu") != std::string::npos);
CommandLine commandLine(mode.second);
commandLine.append("-notunepme"); // for reciprocal energy reproducibility
+ if (useSeparatePme)
+ {
+ commandLine.addOption("-npme", 1);
+ }
ASSERT_EQ(0, runner_.callMdrun(commandLine));
- auto energyReader = openEnergyFileToReadFields(runner_.edrFileName_, {"Coul. recip.", "Total Energy", "Kinetic En."});
- auto conservedChecker = rootChecker.checkCompound("Energy", "Conserved");
- auto reciprocalChecker = rootChecker.checkCompound("Energy", "Reciprocal");
- for (int i = 0; i <= nsteps; i++)
+ if (thisRankChecks)
{
- EnergyFrame frame = energyReader->frame();
- std::string stepNum = gmx::formatString("%d", i);
- const real conservedEnergy = frame.at("Total Energy");
- const real reciprocalEnergy = frame.at("Coul. recip.");
- if (i == 0)
+ auto energyReader = openEnergyFileToReadFields(runner_.edrFileName_, {"Coul. recip.", "Total Energy", "Kinetic En."});
+ auto conservedChecker = rootChecker.checkCompound("Energy", "Conserved");
+ auto reciprocalChecker = rootChecker.checkCompound("Energy", "Reciprocal");
+ for (int i = 0; i <= nsteps; i++)
{
- // 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, 2e-5);
- reciprocalChecker.setDefaultTolerance(reciprocalTolerance);
- conservedChecker.setDefaultTolerance(conservedTolerance);
+ EnergyFrame frame = energyReader->frame();
+ std::string stepNum = gmx::formatString("%d", i);
+ const real conservedEnergy = frame.at("Total Energy");
+ const real reciprocalEnergy = frame.at("Coul. recip.");
+ if (i == 0)
+ {
+ // 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);
+ reciprocalChecker.setDefaultTolerance(reciprocalTolerance);
+ conservedChecker.setDefaultTolerance(conservedTolerance);
+ }
+ conservedChecker.checkReal(conservedEnergy, stepNum.c_str());
+ reciprocalChecker.checkReal(reciprocalEnergy, stepNum.c_str());
}
- conservedChecker.checkReal(conservedEnergy, stepNum.c_str());
- reciprocalChecker.checkReal(reciprocalEnergy, stepNum.c_str());
}
+ // FIXME: without this barrier, one of the mdruns was somehow having a non-PME inputrec (!)
+#if GMX_LIB_MPI
+ if (parallelRun)
+ {
+ MPI_Barrier(MPI_COMM_WORLD);
+ }
+#endif
+ }
+
+ // This is a workaround for the output files to not be deleted in a parallel run.
+ // TODO: consider moving the barrier into the file manager destructor.
+#if GMX_LIB_MPI
+ if (parallelRun)
+ {
+ MPI_Barrier(MPI_COMM_WORLD);
}
+#endif
}
}