Move nbnxm setup responsibility to its own files and add tests
authorJoe Jordan <ejjordan12@gmail.com>
Wed, 9 Jun 2021 14:04:20 +0000 (14:04 +0000)
committerPaul Bauer <paul.bauer.q@gmail.com>
Wed, 9 Jun 2021 14:04:20 +0000 (14:04 +0000)
api/nblib/CMakeLists.txt
api/nblib/gmxsetup.cpp
api/nblib/nbnxmsetuphelpers.cpp [new file with mode: 0644]
api/nblib/nbnxmsetuphelpers.h [new file with mode: 0644]
api/nblib/tests/CMakeLists.txt
api/nblib/tests/nbnxmsetup.cpp [new file with mode: 0644]
api/nblib/tests/nbnxnsetup.cpp [deleted file]

index 6cbc8f91245675890fe37b7c8ace217f8f267dc0..5a7d5f3898bd0ca3a8ccb6c84ae1a8a5a87705c4 100644 (file)
@@ -118,6 +118,7 @@ target_sources(nblib
         integrator.cpp
         interactions.cpp
         molecules.cpp
+        nbnxmsetuphelpers.cpp
         particlesequencer.cpp
         particletype.cpp
         simulationstate.cpp
index d5c17addd7c63ebde05ce829176e7a1539bd8a45..ba679f8451dbbca8ca9be980565ecf626942bdee 100644 (file)
 #include "gromacs/utility/smalloc.h"
 #include "nblib/exception.h"
 #include "nblib/kerneloptions.h"
+#include "nblib/nbnxmsetuphelpers.h"
 #include "nblib/particletype.h"
 #include "nblib/simulationstate.h"
 
 namespace nblib
 {
 
-//! Helper to translate between the different enumeration values.
-static Nbnxm::KernelType translateBenchmarkEnum(const SimdKernels& kernel)
-{
-    int kernelInt = static_cast<int>(kernel);
-    return static_cast<Nbnxm::KernelType>(kernelInt);
-}
-
-/*! \brief Checks the kernel setup
- *
- * Returns an error string when the kernel is not available.
- */
-static void checkKernelSetup(const NBKernelOptions& options)
-{
-    if (options.nbnxmSimd >= SimdKernels::Count || options.nbnxmSimd == SimdKernels::SimdAuto)
-    {
-        throw InputException("Need a valid kernel SIMD type");
-    }
-    // Check SIMD support
-    if ((options.nbnxmSimd != SimdKernels::SimdNo && !GMX_SIMD)
-#ifndef GMX_NBNXN_SIMD_4XN
-        || options.nbnxmSimd == SimdKernels::Simd4XM
-#endif
-#ifndef GMX_NBNXN_SIMD_2XNN
-        || options.nbnxmSimd == SimdKernels::Simd2XMM
-#endif
-    )
-    {
-        throw InputException("The requested SIMD kernel was not set up at configuration time");
-    }
-}
-
 NbvSetupUtil::NbvSetupUtil() : gmxForceCalculator_(std::make_unique<GmxForceCalculator>()) {}
 
 void NbvSetupUtil::setExecutionContext(const NBKernelOptions& options)
 {
-    // Todo: find a more general way to initialize hardware
-    gmx_omp_nthreads_set(ModuleMultiThread::Pairsearch, options.numOpenMPThreads);
-    gmx_omp_nthreads_set(ModuleMultiThread::Nonbonded, options.numOpenMPThreads);
+    setGmxNonBondedNThreads(options.numOpenMPThreads);
 }
 
 Nbnxm::KernelSetup NbvSetupUtil::getKernelSetup(const NBKernelOptions& options)
 {
-    checkKernelSetup(options);
-
-    Nbnxm::KernelSetup kernelSetup;
-
-    // The int enum options.nbnxnSimd is set up to match Nbnxm::KernelType + 1
-    kernelSetup.kernelType = translateBenchmarkEnum(options.nbnxmSimd);
-    // The plain-C kernel does not support analytical ewald correction
-    if (kernelSetup.kernelType == Nbnxm::KernelType::Cpu4x4_PlainC)
-    {
-        kernelSetup.ewaldExclusionType = Nbnxm::EwaldExclusionType::Table;
-    }
-    else
-    {
-        kernelSetup.ewaldExclusionType = options.useTabulatedEwaldCorr
-                                                 ? Nbnxm::EwaldExclusionType::Table
-                                                 : Nbnxm::EwaldExclusionType::Analytical;
-    }
-
-    return kernelSetup;
+    return createKernelSetupCPU(options);
 }
 
 void NbvSetupUtil::setParticleInfoAllVdv(const size_t numParticles)
 
 {
-    particleInfoAllVdw_.resize(numParticles);
-    for (size_t particleI = 0; particleI < numParticles; particleI++)
-    {
-        particleInfoAllVdw_[particleI] |= gmx::sc_atomInfo_HasVdw;
-        particleInfoAllVdw_[particleI] |= gmx::sc_atomInfo_HasCharge;
-    }
+    particleInfoAllVdw_ = createParticleInfoAllVdv(numParticles);
 }
 
 void NbvSetupUtil::setNonBondedParameters(const std::vector<ParticleType>& particleTypes,
                                           const NonBondedInteractionMap&   nonBondedInteractionMap)
 {
-    /* Todo: Refactor nbnxm to take nonbondedParameters_ directly
-     *
-     * initial self-handling of combination rules
-     * size: 2*(numParticleTypes^2)
-     */
-    nonbondedParameters_.reserve(2 * particleTypes.size() * particleTypes.size());
-
-    constexpr real c6factor  = 6.0;
-    constexpr real c12factor = 12.0;
-
-    for (const ParticleType& particleType1 : particleTypes)
-    {
-        for (const ParticleType& particleType2 : particleTypes)
-        {
-            nonbondedParameters_.push_back(
-                    nonBondedInteractionMap.getC6(particleType1.name(), particleType2.name()) * c6factor);
-            nonbondedParameters_.push_back(
-                    nonBondedInteractionMap.getC12(particleType1.name(), particleType2.name()) * c12factor);
-        }
-    }
+    nonbondedParameters_ = createNonBondedParameters(particleTypes, nonBondedInteractionMap);
 }
 
 void NbvSetupUtil::setAtomProperties(const std::vector<int>&  particleTypeIdOfAllParticles,
@@ -182,7 +108,7 @@ void NbvSetupUtil::setupNbnxmInstance(const size_t numParticleTypes, const NBKer
     // Note: the options and Nbnxm combination rule enums values should match
     const int combinationRule = static_cast<int>(options.ljCombinationRule);
 
-    checkKernelSetup(options); // throws exception is setup is invalid
+    checkKernelSetup(options.nbnxmSimd); // throws exception is setup is invalid
 
     Nbnxm::KernelSetup kernelSetup = getKernelSetup(options);
 
@@ -209,95 +135,20 @@ void NbvSetupUtil::setupNbnxmInstance(const size_t numParticleTypes, const NBKer
     gmxForceCalculator_->nbv_ = std::move(nbv);
 }
 
-//! Computes the Ewald splitting coefficient for Coulomb
-static real ewaldCoeff(const real ewald_rtol, const real pairlistCutoff)
-{
-    return calc_ewaldcoeff_q(pairlistCutoff, ewald_rtol);
-}
-
 void NbvSetupUtil::setupStepWorkload(const NBKernelOptions& options)
 {
-    gmxForceCalculator_->stepWork_->computeForces          = true;
-    gmxForceCalculator_->stepWork_->computeNonbondedForces = true;
-
-    if (options.computeVirialAndEnergy)
-    {
-        gmxForceCalculator_->stepWork_->computeVirial = true;
-        gmxForceCalculator_->stepWork_->computeEnergy = true;
-    }
+    gmxForceCalculator_->stepWork_ = std::make_unique<gmx::StepWorkload>(createStepWorkload(options));
 }
 
 void NbvSetupUtil::setupInteractionConst(const NBKernelOptions& options)
 {
-    gmxForceCalculator_->interactionConst_->vdwtype      = VanDerWaalsType::Cut;
-    gmxForceCalculator_->interactionConst_->vdw_modifier = InteractionModifiers::PotShift;
-    gmxForceCalculator_->interactionConst_->rvdw         = options.pairlistCutoff;
-
-    switch (options.coulombType)
-    {
-        case CoulombType::Pme:
-            gmxForceCalculator_->interactionConst_->eeltype = CoulombInteractionType::Pme;
-            break;
-        case CoulombType::Cutoff:
-            gmxForceCalculator_->interactionConst_->eeltype = CoulombInteractionType::Cut;
-            break;
-        case CoulombType::ReactionField:
-            gmxForceCalculator_->interactionConst_->eeltype = CoulombInteractionType::RF;
-            break;
-        case CoulombType::Count: throw InputException("Unsupported electrostatic interaction");
-    }
-    gmxForceCalculator_->interactionConst_->coulomb_modifier = InteractionModifiers::PotShift;
-    gmxForceCalculator_->interactionConst_->rcoulomb         = options.pairlistCutoff;
-    // Note: values correspond to ic->coulomb_modifier = InteractionModifiers::PotShift
-    gmxForceCalculator_->interactionConst_->dispersion_shift.cpot =
-            -1.0 / gmx::power6(gmxForceCalculator_->interactionConst_->rvdw);
-    gmxForceCalculator_->interactionConst_->repulsion_shift.cpot =
-            -1.0 / gmx::power12(gmxForceCalculator_->interactionConst_->rvdw);
-
-    // These are the initialized values but we leave them here so that later
-    // these can become options.
-    gmxForceCalculator_->interactionConst_->epsilon_r                = 1.0;
-    gmxForceCalculator_->interactionConst_->reactionFieldPermitivity = 1.0;
-
-    /* Set the Coulomb energy conversion factor */
-    if (gmxForceCalculator_->interactionConst_->epsilon_r != 0)
-    {
-        gmxForceCalculator_->interactionConst_->epsfac =
-                ONE_4PI_EPS0 / gmxForceCalculator_->interactionConst_->epsilon_r;
-    }
-    else
-    {
-        /* eps = 0 is infinite dieletric: no Coulomb interactions */
-        gmxForceCalculator_->interactionConst_->epsfac = 0;
-    }
-
-    calc_rffac(nullptr,
-               gmxForceCalculator_->interactionConst_->epsilon_r,
-               gmxForceCalculator_->interactionConst_->reactionFieldPermitivity,
-               gmxForceCalculator_->interactionConst_->rcoulomb,
-               &gmxForceCalculator_->interactionConst_->reactionFieldCoefficient,
-               &gmxForceCalculator_->interactionConst_->reactionFieldShift);
-
-    if (EEL_PME_EWALD(gmxForceCalculator_->interactionConst_->eeltype))
-    {
-        // Ewald coefficients, we ignore the potential shift
-        gmxForceCalculator_->interactionConst_->ewaldcoeff_q = ewaldCoeff(1e-5, options.pairlistCutoff);
-        if (gmxForceCalculator_->interactionConst_->ewaldcoeff_q <= 0)
-        {
-            throw InputException("Ewald coefficient should be > 0");
-        }
-        gmxForceCalculator_->interactionConst_->coulombEwaldTables =
-                std::make_unique<EwaldCorrectionTables>();
-        init_interaction_const_tables(nullptr, gmxForceCalculator_->interactionConst_.get(), 0, 0);
-    }
+    gmxForceCalculator_->interactionConst_ =
+            std::make_unique<interaction_const_t>(createInteractionConst(options));
 }
 
 void NbvSetupUtil::setupForceRec(const matrix& box)
 {
-    assert((gmxForceCalculator_->forcerec_ && "Forcerec not initialized"));
-    gmxForceCalculator_->forcerec_->nbfp = nonbondedParameters_;
-    gmxForceCalculator_->forcerec_->shift_vec.resize(numShiftVectors);
-    calc_shifts(box, gmxForceCalculator_->forcerec_->shift_vec);
+    updateForcerec(gmxForceCalculator_->forcerec_.get(), box);
 }
 
 void NbvSetupUtil::setParticlesOnGrid(const std::vector<Vec3>& coordinates, const Box& box)
diff --git a/api/nblib/nbnxmsetuphelpers.cpp b/api/nblib/nbnxmsetuphelpers.cpp
new file mode 100644 (file)
index 0000000..709ced3
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Utilities to setup GROMACS data structures for non-bonded force calculations.
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include "gromacs/ewald/ewald_utils.h"
+#include "gromacs/gpu_utils/device_stream_manager.h"
+#include "gromacs/gmxlib/nrnb.h"
+#include "gromacs/mdlib/forcerec.h"
+#include "gromacs/mdlib/gmx_omp_nthreads.h"
+#include "gromacs/mdlib/rf_util.h"
+#include "gromacs/mdtypes/forcerec.h"
+#include "gromacs/mdtypes/interaction_const.h"
+#include "gromacs/mdtypes/simulation_workload.h"
+#include "gromacs/nbnxm/atomdata.h"
+#include "gromacs/nbnxm/gpu_data_mgmt.h"
+#include "gromacs/nbnxm/nbnxm_gpu.h"
+#include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/nbnxm/nbnxm_simd.h"
+#include "gromacs/nbnxm/pairlistset.h"
+#include "gromacs/nbnxm/pairlistsets.h"
+#include "gromacs/nbnxm/pairsearch.h"
+#include "gromacs/pbcutil/pbc.h"
+#include "gromacs/utility/logger.h"
+#include "gromacs/utility/smalloc.h"
+#include "nblib/exception.h"
+#include "nblib/kerneloptions.h"
+#include "nblib/particletype.h"
+
+#include "nbnxmsetuphelpers.h"
+
+namespace nblib
+{
+
+int64_t findNumEnergyGroups(gmx::ArrayRef<int64_t> particleInteractionFlags)
+{
+    auto groupId = [](int code1, int code2) {
+        return (code1 & gmx::sc_atomInfo_EnergyGroupIdMask) < (code2 & gmx::sc_atomInfo_EnergyGroupIdMask);
+    };
+
+    int maxElement = *std::max_element(
+            std::begin(particleInteractionFlags), std::end(particleInteractionFlags), groupId);
+    return ((maxElement + 1) & gmx::sc_atomInfo_EnergyGroupIdMask);
+}
+
+Nbnxm::KernelType translateBenchmarkEnum(const SimdKernels& kernel)
+{
+    int kernelInt = static_cast<int>(kernel);
+    return static_cast<Nbnxm::KernelType>(kernelInt);
+}
+
+void checkKernelSetup(const SimdKernels nbnxmSimd)
+{
+    if (nbnxmSimd >= SimdKernels::Count || nbnxmSimd == SimdKernels::SimdAuto)
+    {
+        throw InputException("Need a valid kernel SIMD type");
+    }
+    // Check SIMD support
+    if ((nbnxmSimd != SimdKernels::SimdNo && !GMX_SIMD)
+#ifndef GMX_NBNXN_SIMD_4XN
+        || nbnxmSimd == SimdKernels::Simd4XM
+#endif
+#ifndef GMX_NBNXN_SIMD_2XNN
+        || nbnxmSimd == SimdKernels::Simd2XMM
+#endif
+    )
+    {
+        throw InputException("The requested SIMD kernel was not set up at configuration time");
+    }
+}
+
+Nbnxm::KernelSetup createKernelSetupCPU(const NBKernelOptions& options)
+{
+    checkKernelSetup(options.nbnxmSimd);
+
+    Nbnxm::KernelSetup kernelSetup;
+
+    // The int enum options.nbnxnSimd is set up to match Nbnxm::KernelType + 1
+    kernelSetup.kernelType = translateBenchmarkEnum(options.nbnxmSimd);
+
+    // The plain-C kernel does not support analytical ewald correction
+    if (kernelSetup.kernelType == Nbnxm::KernelType::Cpu4x4_PlainC)
+    {
+        kernelSetup.ewaldExclusionType = Nbnxm::EwaldExclusionType::Table;
+    }
+    else
+    {
+        kernelSetup.ewaldExclusionType = options.useTabulatedEwaldCorr
+                                                 ? Nbnxm::EwaldExclusionType::Table
+                                                 : Nbnxm::EwaldExclusionType::Analytical;
+    }
+
+    return kernelSetup;
+}
+
+std::vector<int64_t> createParticleInfoAllVdv(const size_t numParticles)
+
+{
+    std::vector<int64_t> particleInfoAllVdw(numParticles);
+    for (size_t particleI = 0; particleI < numParticles; particleI++)
+    {
+        particleInfoAllVdw[particleI] |= gmx::sc_atomInfo_HasVdw;
+        particleInfoAllVdw[particleI] |= gmx::sc_atomInfo_HasCharge;
+    }
+    return particleInfoAllVdw;
+}
+
+std::vector<real> createNonBondedParameters(const std::vector<ParticleType>& particleTypes,
+                                            const NonBondedInteractionMap& nonBondedInteractionMap)
+{
+    /* Todo: Refactor nbnxm to take nonbondedParameters_ directly
+     *
+     * initial self-handling of combination rules
+     * size: 2*(numParticleTypes^2)
+     */
+    std::vector<real> nonbondedParameters;
+    nonbondedParameters.reserve(2 * particleTypes.size() * particleTypes.size());
+
+    constexpr real c6factor  = 6.0;
+    constexpr real c12factor = 12.0;
+
+    for (const ParticleType& particleType1 : particleTypes)
+    {
+        for (const ParticleType& particleType2 : particleTypes)
+        {
+            nonbondedParameters.push_back(
+                    nonBondedInteractionMap.getC6(particleType1.name(), particleType2.name()) * c6factor);
+            nonbondedParameters.push_back(
+                    nonBondedInteractionMap.getC12(particleType1.name(), particleType2.name()) * c12factor);
+        }
+    }
+    return nonbondedParameters;
+}
+
+gmx::StepWorkload createStepWorkload([[maybe_unused]] const NBKernelOptions& options)
+{
+    gmx::StepWorkload stepWorkload;
+    stepWorkload.computeForces          = true;
+    stepWorkload.computeNonbondedForces = true;
+    stepWorkload.useGpuFBufferOps       = false;
+    stepWorkload.useGpuXBufferOps       = false;
+
+    return stepWorkload;
+}
+
+real ewaldCoeff(const real ewald_rtol, const real pairlistCutoff)
+{
+    return calc_ewaldcoeff_q(pairlistCutoff, ewald_rtol);
+}
+
+interaction_const_t createInteractionConst(const NBKernelOptions& options)
+{
+    interaction_const_t interactionConst;
+    interactionConst.vdwtype      = VanDerWaalsType::Cut;
+    interactionConst.vdw_modifier = InteractionModifiers::PotShift;
+    interactionConst.rvdw         = options.pairlistCutoff;
+
+    switch (options.coulombType)
+    {
+        case CoulombType::Pme: interactionConst.eeltype = CoulombInteractionType::Pme; break;
+        case CoulombType::Cutoff: interactionConst.eeltype = CoulombInteractionType::Cut; break;
+        case CoulombType::ReactionField:
+            interactionConst.eeltype = CoulombInteractionType::RF;
+            break;
+        case CoulombType::Count: throw InputException("Unsupported electrostatic interaction");
+    }
+    interactionConst.coulomb_modifier = InteractionModifiers::PotShift;
+    interactionConst.rcoulomb         = options.pairlistCutoff;
+    // Note: values correspond to ic->coulomb_modifier = eintmodPOTSHIFT
+    interactionConst.dispersion_shift.cpot = -1.0 / gmx::power6(interactionConst.rvdw);
+    interactionConst.repulsion_shift.cpot  = -1.0 / gmx::power12(interactionConst.rvdw);
+
+    // These are the initialized values but we leave them here so that later
+    // these can become options.
+    interactionConst.epsilon_r                = 1.0;
+    interactionConst.reactionFieldPermitivity = 1.0;
+
+    /* Set the Coulomb energy conversion factor */
+    if (interactionConst.epsilon_r != 0)
+    {
+        interactionConst.epsfac = ONE_4PI_EPS0 / interactionConst.epsilon_r;
+    }
+    else
+    {
+        /* eps = 0 is infinite dieletric: no Coulomb interactions */
+        interactionConst.epsfac = 0;
+    }
+
+    calc_rffac(nullptr,
+               interactionConst.epsilon_r,
+               interactionConst.reactionFieldPermitivity,
+               interactionConst.rcoulomb,
+               &interactionConst.reactionFieldCoefficient,
+               &interactionConst.reactionFieldShift);
+
+
+    if (EEL_PME_EWALD(interactionConst.eeltype))
+    {
+        // Ewald coefficients, we ignore the potential shift
+        interactionConst.ewaldcoeff_q = ewaldCoeff(1e-5, options.pairlistCutoff);
+        if (interactionConst.ewaldcoeff_q <= 0)
+        {
+            throw InputException("Ewald coefficient should be > 0");
+        }
+        interactionConst.coulombEwaldTables = std::make_unique<EwaldCorrectionTables>();
+        init_interaction_const_tables(nullptr, &interactionConst, 0, 0);
+    }
+    return interactionConst;
+}
+
+void setGmxNonBondedNThreads(int numThreads)
+{
+    gmx_omp_nthreads_set(ModuleMultiThread::Pairsearch, numThreads);
+    gmx_omp_nthreads_set(ModuleMultiThread::Nonbonded, numThreads);
+}
+
+void updateForcerec(t_forcerec* forcerec, const matrix& box)
+{
+    assert(forcerec != nullptr && "Forcerec not initialized");
+    forcerec->shift_vec.resize(numShiftVectors);
+    calc_shifts(box, forcerec->shift_vec);
+}
+
+
+} // namespace nblib
diff --git a/api/nblib/nbnxmsetuphelpers.h b/api/nblib/nbnxmsetuphelpers.h
new file mode 100644 (file)
index 0000000..decaa71
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief Translation layer to GROMACS data structures for force calculations.
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#ifndef NBLIB_NBNXMSETUPHELPERS_H
+#define NBLIB_NBNXMSETUPHELPERS_H
+
+#include "nblib/kerneloptions.h"
+#include "nblib/interactions.h"
+#include "nblib/particletype.h"
+
+struct nonbonded_verlet_t;
+struct t_forcerec;
+struct t_nrnb;
+struct interaction_const_t;
+struct gmx_enerdata_t;
+struct DeviceInformation;
+
+namespace gmx
+{
+class StepWorkload;
+class SimulationWorkload;
+class DeviceStreamManager;
+template<class T>
+class ArrayRef;
+} // namespace gmx
+
+namespace Nbnxm
+{
+enum class KernelType;
+struct KernelSetup;
+} // namespace Nbnxm
+
+namespace nblib
+{
+
+/*! \brief determine number of energy groups in an array of particle info flags
+ *
+ * Note: If the maximum energy group ID in the input is N, it is assumed that
+ * all the energy groups with IDs from 0...N-1 also exist.
+ */
+int64_t findNumEnergyGroups(gmx::ArrayRef<int64_t> particleInteractionFlags);
+
+//! Helper to translate between the different enumeration values.
+Nbnxm::KernelType translateBenchmarkEnum(const SimdKernels& kernel);
+
+/*! \brief Checks the kernel setup
+ *
+ * Throws an exception when the kernel is not available.
+ */
+void checkKernelSetup(SimdKernels nbnxmSimd);
+
+//! Creates and returns the kernel setup
+Nbnxm::KernelSetup createKernelSetupCPU(const NBKernelOptions& options);
+
+//! Create Particle info array to mark those that undergo VdV interaction
+std::vector<int64_t> createParticleInfoAllVdv(size_t numParticles);
+
+//! Create the non-bonded parameter vector in GROMACS format
+std::vector<real> createNonBondedParameters(const std::vector<ParticleType>& particleTypes,
+                                            const NonBondedInteractionMap& nonBondedInteractionMap);
+
+//! Create a step work object
+gmx::StepWorkload createStepWorkload(const NBKernelOptions& options);
+
+//! Computes the Ewald splitting coefficient for Coulomb
+real ewaldCoeff(real ewald_rtol, real pairlistCutoff);
+
+//! Creates an interaction_const_t object from NBKernelOptions
+interaction_const_t createInteractionConst(const NBKernelOptions& options);
+
+//! Set number of OpenMP threads in the GROMACS backend
+void setGmxNonBondedNThreads(int numThreads);
+
+//! Update the shift vectors in t_forcerec
+void updateForcerec(t_forcerec* forcerec, const matrix& box);
+
+} // namespace nblib
+
+#endif // NBLIB_NBNXMSETUPHELPERS_H
index d224dbe7eb7841178de2d7fd86604fc3ee18b7c3..ae965706f9cbc17b6122c480a80e2fb738cc3056 100644 (file)
@@ -59,6 +59,7 @@ gmx_add_gtest_executable(
         particletype.cpp
         pbcholder.cpp
         molecules.cpp
+        nbnxmsetup.cpp
         topology.cpp
     )
 target_link_libraries(${exename} PRIVATE mdrun_test_infrastructure)
diff --git a/api/nblib/tests/nbnxmsetup.cpp b/api/nblib/tests/nbnxmsetup.cpp
new file mode 100644 (file)
index 0000000..5b02de6
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * This file is part of the GROMACS molecular simulation package.
+ *
+ * Copyright (c) 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.
+ *
+ * GROMACS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1
+ * of the License, or (at your option) any later version.
+ *
+ * GROMACS is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GROMACS; if not, see
+ * http://www.gnu.org/licenses, or write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
+ *
+ * If you want to redistribute modifications to GROMACS, please
+ * consider that scientific software is very special. Version
+ * control is crucial - bugs must be traceable. We will be happy to
+ * consider code for inclusion in the official distribution, but
+ * derived work must not be called official GROMACS. Details are found
+ * in the README & COPYING files - if they are missing, get the
+ * official version at http://www.gromacs.org.
+ *
+ * To help us fund GROMACS development, we humbly ask that you cite
+ * the research papers on the package. Check out http://www.gromacs.org.
+ */
+/*! \internal \file
+ * \brief
+ * Tests for nbnxm setup utilities
+ *
+ * \author Victor Holanda <victor.holanda@cscs.ch>
+ * \author Joe Jordan <ejjordan@kth.se>
+ * \author Prashanth Kanduri <kanduri@cscs.ch>
+ * \author Sebastian Keller <keller@cscs.ch>
+ */
+#include <cmath>
+
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/mdtypes/forcerec.h"
+#include "gromacs/nbnxm/nbnxm.h"
+#include "gromacs/nbnxm/nbnxm_simd.h"
+#include "nblib/box.h"
+#include "nblib/nbnxmsetuphelpers.h"
+
+#include "testutils/testasserts.h"
+
+namespace nblib
+{
+namespace test
+{
+namespace
+{
+TEST(NbnxmSetupTest, findNumEnergyGroups)
+{
+    std::vector<int64_t> v(10);
+    int                  arbitraryGid = 7;
+
+    // this sets some bit outside the range of bits used for the group ID
+    // having all bits zero except those used for the group ID can otherwise hide bugs
+    v[5] |= gmx::sc_atomInfo_HasCharge;
+    v[5] = (v[5] & ~gmx::sc_atomInfo_EnergyGroupIdMask) | arbitraryGid;
+
+    int nEnergyGroups = arbitraryGid + 1;
+    EXPECT_EQ(nEnergyGroups, findNumEnergyGroups(v));
+}
+
+TEST(NbnxmSetupTest, canTranslateBenchmarkEnumAuto)
+{
+    auto kernel = SimdKernels::SimdAuto;
+    EXPECT_EQ(translateBenchmarkEnum(kernel), Nbnxm::KernelType::NotSet);
+}
+
+TEST(NbnxmSetupTest, canTranslateBenchmarkEnumNo)
+{
+    auto kernel = SimdKernels::SimdNo;
+    EXPECT_EQ(translateBenchmarkEnum(kernel), Nbnxm::KernelType::Cpu4x4_PlainC);
+}
+
+TEST(NbnxmSetupTest, canTranslateBenchmarkEnum2XM)
+{
+    auto kernel = SimdKernels::Simd2XMM;
+    EXPECT_EQ(translateBenchmarkEnum(kernel), Nbnxm::KernelType::Cpu4xN_Simd_2xNN);
+}
+
+TEST(NbnxmSetupTest, canTranslateBenchmarkEnum4XM)
+{
+    auto kernel = SimdKernels::Simd4XM;
+    EXPECT_EQ(translateBenchmarkEnum(kernel), Nbnxm::KernelType::Cpu4xN_Simd_4xN);
+}
+
+TEST(NbnxmSetupTest, CheckKernelSetupThrowsAuto)
+{
+    EXPECT_ANY_THROW(checkKernelSetup(SimdKernels::SimdAuto));
+}
+
+TEST(NbnxmSetupTest, CheckKernelSetupThrowsCount)
+{
+    EXPECT_ANY_THROW(checkKernelSetup(SimdKernels::Count));
+}
+
+TEST(NbnxmSetupTest, canCreateKernelSetupPlain)
+{
+    NBKernelOptions nbKernelOptions;
+    nbKernelOptions.nbnxmSimd      = SimdKernels::SimdNo;
+    Nbnxm::KernelSetup kernelSetup = createKernelSetupCPU(nbKernelOptions);
+    EXPECT_EQ(kernelSetup.kernelType, Nbnxm::KernelType::Cpu4x4_PlainC);
+    EXPECT_EQ(kernelSetup.ewaldExclusionType, Nbnxm::EwaldExclusionType::Table);
+}
+
+TEST(NbnxmSetupTest, canCreateParticleInfoAllVdv)
+{
+    size_t  numParticles = 2;
+    int64_t mask         = 0;
+    mask |= gmx::sc_atomInfo_HasVdw;
+    mask |= gmx::sc_atomInfo_HasCharge;
+    std::vector<int64_t> refParticles  = { mask, mask };
+    std::vector<int64_t> testParticles = createParticleInfoAllVdv(numParticles);
+    EXPECT_EQ(refParticles, testParticles);
+}
+
+TEST(NbnxmSetupTest, ewaldCoeffWorks)
+{
+    real                              ewald     = ewaldCoeff(1e-5, 1.0);
+    gmx::test::FloatingPointTolerance tolerance = gmx::test::absoluteTolerance(1e-5);
+    EXPECT_REAL_EQ_TOL(ewald, 3.12341, tolerance);
+}
+
+TEST(NbnxmSetupTest, updateForcerecWorks)
+{
+    t_forcerec forcerec;
+    Box        box(3);
+    EXPECT_NO_THROW(updateForcerec(&forcerec, box.legacyMatrix()));
+}
+
+// The following tests check if the user is allowed to specify configurations not permitted due
+// to conflicting compile time setup flags
+
+TEST(NbnxmSetupTest, canCheckKernelSetup)
+{
+    NBKernelOptions nbKernelOptions;
+    nbKernelOptions.nbnxmSimd = SimdKernels::SimdNo;
+#ifdef GMX_NBNXN_SIMD_4XN
+    nbKernelOptions.nbnxmSimd = SimdKernels::Simd4XM;
+#endif
+#ifdef GMX_NBNXN_SIMD_2XNN
+    nbKernelOptions.nbnxmSimd = SimdKernels::Simd2XMM;
+#endif
+    EXPECT_NO_THROW(checkKernelSetup(nbKernelOptions.nbnxmSimd));
+}
+
+// check if the user is allowed to ask for SimdKernels::Simd2XMM when NBLIB is not compiled with it
+#ifndef GMX_NBNXN_SIMD_2XNN
+TEST(NbnxmSetupTest, cannotCreateKernelSetupCPU2XM)
+{
+    NBKernelOptions nbKernelOptions;
+    nbKernelOptions.nbnxmSimd             = SimdKernels::Simd2XMM;
+    nbKernelOptions.useTabulatedEwaldCorr = true;
+    EXPECT_ANY_THROW(createKernelSetupCPU(nbKernelOptions));
+}
+#endif
+
+// check if the user is allowed to ask for SimdKernels::Simd4XM when NBLIB is not compiled with it
+#ifndef GMX_NBNXN_SIMD_4XN
+TEST(NbnxmSetupTest, cannotCreateKernelSetupCPU4XM)
+{
+    NBKernelOptions nbKernelOptions;
+    nbKernelOptions.nbnxmSimd             = SimdKernels::Simd4XM;
+    nbKernelOptions.useTabulatedEwaldCorr = false;
+    EXPECT_ANY_THROW(createKernelSetupCPU(nbKernelOptions));
+}
+#endif
+
+} // namespace
+} // namespace test
+} // namespace nblib
diff --git a/api/nblib/tests/nbnxnsetup.cpp b/api/nblib/tests/nbnxnsetup.cpp
deleted file mode 100644 (file)
index ada24d5..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * This file is part of the GROMACS molecular simulation package.
- *
- * Copyright (c) 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.
- *
- * GROMACS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1
- * of the License, or (at your option) any later version.
- *
- * GROMACS is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with GROMACS; if not, see
- * http://www.gnu.org/licenses, or write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
- *
- * If you want to redistribute modifications to GROMACS, please
- * consider that scientific software is very special. Version
- * control is crucial - bugs must be traceable. We will be happy to
- * consider code for inclusion in the official distribution, but
- * derived work must not be called official GROMACS. Details are found
- * in the README & COPYING files - if they are missing, get the
- * official version at http://www.gromacs.org.
- *
- * To help us fund GROMACS development, we humbly ask that you cite
- * the research papers on the package. Check out http://www.gromacs.org.
- */
-/*! \internal \file
- * \brief
- * This implements nbnxn setup tests
- *
- * \author Victor Holanda <victor.holanda@cscs.ch>
- * \author Joe Jordan <ejjordan@kth.se>
- * \author Prashanth Kanduri <kanduri@cscs.ch>
- * \author Sebastian Keller <keller@cscs.ch>
- */
-#include "nblib/gmxsetup.h"
-#include "nblib/tests/testhelpers.h"
-#include "nblib/tests/testsystems.h"
-
-#include "testutils/testasserts.h"
-
-namespace nblib
-{
-
-namespace test
-{
-
-TEST(NBlibTest, CanConstructNbvSetupUtil)
-{
-    ArgonSimulationStateBuilder argonSimulationStateBuilder;
-    SimulationState             system = argonSimulationStateBuilder.setupSimulationState();
-    EXPECT_NO_THROW(NbvSetupUtil());
-}
-
-} // namespace test
-
-} // namespace nblib