Add hasPerturbedChargeIn14Interaction to atomInfo bits
authorMark Abraham <mark.j.abraham@gmail.com>
Mon, 7 Jun 2021 23:37:24 +0000 (23:37 +0000)
committerPascal Merz <pascal.merz@me.com>
Mon, 7 Jun 2021 23:37:24 +0000 (23:37 +0000)
src/gromacs/domdec/localtopology.cpp
src/gromacs/domdec/localtopology.h
src/gromacs/domdec/mdsetup.cpp
src/gromacs/mdlib/forcerec.cpp
src/gromacs/mdtypes/atominfo.h
src/gromacs/topology/mtop_util.cpp
src/gromacs/topology/mtop_util.h
src/gromacs/topology/tests/mtop.cpp
src/gromacs/topology/tests/topsort.cpp
src/gromacs/topology/topsort.cpp
src/gromacs/topology/topsort.h

index ea4a68c72546b52cef4f0ba43d083368d32cd234..dfe8f85703c616f54d8686bda76fa4e9dbfe7df1 100644 (file)
@@ -1019,11 +1019,11 @@ int dd_make_local_top(gmx_domdec_t*        dd,
     return numBondedInteractionsToReduce;
 }
 
-void dd_sort_local_top(const gmx_domdec_t& dd, const t_mdatoms* mdatoms, gmx_localtop_t* ltop)
+void dd_sort_local_top(const gmx_domdec_t& dd, gmx::ArrayRef<const int64_t> atomInfo, gmx_localtop_t* ltop)
 {
     if (dd.reverse_top->doSorting())
     {
-        gmx_sort_ilist_fe(&ltop->idef, mdatoms->chargeA, mdatoms->chargeB);
+        gmx_sort_ilist_fe(&ltop->idef, atomInfo);
     }
     else
     {
index 6adfc265546e3dd6101c048c80697e22b89129cd..8a66ea43ed84515092fdfb51ac88521f5ef14bc6 100644 (file)
@@ -74,6 +74,6 @@ int dd_make_local_top(struct gmx_domdec_t*           dd,
                       gmx_localtop_t*                ltop);
 
 /*! \brief Sort ltop->ilist when we are doing free energy. */
-void dd_sort_local_top(const gmx_domdec_t& dd, const t_mdatoms* mdatoms, gmx_localtop_t* ltop);
+void dd_sort_local_top(const gmx_domdec_t& dd, gmx::ArrayRef<const int64_t> atomInfo, gmx_localtop_t* ltop);
 
 #endif
index 631d167227c536d881cb7ddeae714296a9337830..1176ddb4e178650b9697c3a366fc38576cc20b5e 100644 (file)
@@ -110,7 +110,7 @@ void mdAlgorithmsSetupAtomData(const t_commrec*     cr,
     t_mdatoms* mdatoms = mdAtoms->mdatoms();
     if (usingDomDec)
     {
-        dd_sort_local_top(*cr->dd, mdatoms, top);
+        dd_sort_local_top(*cr->dd, fr->atomInfo, top);
     }
     else
     {
index 3cbb1124e7007bb344682b12c4fb90ea3efc8013..f5e784134a17bdd0b88f0bf5c450468452e9e5e3 100644 (file)
@@ -330,9 +330,16 @@ makeAtomInfoForEachMoleculeBlock(const gmx_mtop_t& mtop, const t_forcerec* fr)
                 {
                     atomInfo |= gmx::sc_atomInfo_HasCharge;
                 }
-                if (fr->efep != FreeEnergyPerturbationType::No && PERTURBED(atom))
+                if (fr->efep != FreeEnergyPerturbationType::No)
                 {
-                    atomInfo |= gmx::sc_atomInfo_FreeEnergyPerturbation;
+                    if (PERTURBED(atom))
+                    {
+                        atomInfo |= gmx::sc_atomInfo_FreeEnergyPerturbation;
+                    }
+                    if (atomHasPerturbedChargeIn14Interaction(a, molt))
+                    {
+                        atomInfo |= gmx::sc_atomInfo_HasPerturbedChargeIn14Interaction;
+                    }
                 }
             }
         }
index 8697dc2501856120026ad1f6fdd034d0e74be4e9..104b1f072e7f2386f1e5939f0673bdb0b30c764c 100644 (file)
@@ -59,13 +59,14 @@ namespace gmx
  * here, reserving bits 0-7 for the energy-group ID.
  */
 //! \{
-static constexpr int64_t sc_atomInfo_FreeEnergyPerturbation = 1 << 15;
-static constexpr int64_t sc_atomInfo_Exclusion              = 1 << 17;
-static constexpr int64_t sc_atomInfo_Constraint             = 1 << 20;
-static constexpr int64_t sc_atomInfo_Settle                 = 1 << 21;
-static constexpr int64_t sc_atomInfo_BondCommunication      = 1 << 22;
-static constexpr int64_t sc_atomInfo_HasVdw                 = 1 << 23;
-static constexpr int64_t sc_atomInfo_HasCharge              = 1 << 24;
+static constexpr int64_t sc_atomInfo_FreeEnergyPerturbation            = 1 << 15;
+static constexpr int64_t sc_atomInfo_HasPerturbedChargeIn14Interaction = 1 << 16;
+static constexpr int64_t sc_atomInfo_Exclusion                         = 1 << 17;
+static constexpr int64_t sc_atomInfo_Constraint                        = 1 << 20;
+static constexpr int64_t sc_atomInfo_Settle                            = 1 << 21;
+static constexpr int64_t sc_atomInfo_BondCommunication                 = 1 << 22;
+static constexpr int64_t sc_atomInfo_HasVdw                            = 1 << 23;
+static constexpr int64_t sc_atomInfo_HasCharge                         = 1 << 24;
 //! \}
 //! The first 8 bits are reserved for energy-group ID
 static constexpr int64_t sc_atomInfo_EnergyGroupIdMask = 0b11111111;
index 1e92998ad7025764ec2edc8f2ef62184487d9498..30e1b96147b0c158d0aad98a4eca20a62ea150b4 100644 (file)
@@ -45,6 +45,7 @@
 #include <cstring>
 
 #include "gromacs/math/vectypes.h"
+#include "gromacs/mdtypes/atominfo.h"
 #include "gromacs/topology/atoms.h"
 #include "gromacs/topology/block.h"
 #include "gromacs/topology/exclusionblocks.h"
@@ -828,18 +829,52 @@ static void addMimicExclusions(gmx::ListOfLists<int>* excls, const gmx::ArrayRef
     gmx::mergeExclusions(excls, qmexcl2);
 }
 
+bool atomHasPerturbedChargeIn14Interaction(const int atomIndex, const gmx_moltype_t& molt)
+{
+    if (molt.atoms.nr > 0)
+    {
+        // Is the charge perturbed at all?
+        const t_atom& atom = molt.atoms.atom[atomIndex];
+        if (atom.q != atom.qB)
+        {
+            // Loop over 1-4 interactions
+            const InteractionList&   ilist  = molt.ilist[F_LJ14];
+            gmx::ArrayRef<const int> iatoms = ilist.iatoms;
+            const int                nral   = NRAL(F_LJ14);
+            for (int i = 0; i < ilist.size(); i += nral + 1)
+            {
+                // Compare the atom indices in this 1-4 interaction to
+                // atomIndex.
+                int ia1 = iatoms[i + 1];
+                int ia2 = iatoms[i + 2];
+                if ((atomIndex == ia1) || (atomIndex == ia2))
+                {
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
 static void sortFreeEnergyInteractionsAtEnd(const gmx_mtop_t& mtop, InteractionDefinitions* idef)
 {
-    std::vector<real> qA(mtop.natoms);
-    std::vector<real> qB(mtop.natoms);
-    for (const AtomProxy atomP : AtomRange(mtop))
+    std::vector<int64_t> atomInfo(mtop.natoms, 0);
+
+    for (size_t mb = 0; mb < mtop.molblock.size(); mb++)
     {
-        const t_atom& local = atomP.atom();
-        int           index = atomP.globalAtomNumber();
-        qA[index]           = local.q;
-        qB[index]           = local.qB;
+        const gmx_molblock_t& molb = mtop.molblock[mb];
+        const gmx_moltype_t&  molt = mtop.moltype[molb.type];
+        for (int a = 0; a < molt.atoms.nr; a++)
+        {
+            if (atomHasPerturbedChargeIn14Interaction(a, molt))
+            {
+                atomInfo[a] |= gmx::sc_atomInfo_HasPerturbedChargeIn14Interaction;
+                break;
+            }
+        }
     }
-    gmx_sort_ilist_fe(idef, qA.data(), qB.data());
+    gmx_sort_ilist_fe(idef, atomInfo);
 }
 
 static void gen_local_top(const gmx_mtop_t& mtop,
index 4d0e8779465b73ab3ae4c8888994b44cf0ef7d04..22c90e0cdbe4b595592d16ba914f3de4baebe88c 100644 (file)
@@ -262,6 +262,20 @@ gmx::EnumerationArray<ParticleType, int> gmx_mtop_particletype_count(const gmx_m
 /* Returns a single t_atoms struct for the whole system */
 t_atoms gmx_mtop_global_atoms(const gmx_mtop_t& mtop);
 
+/*! \brief Return whether the atom with the given index is within a
+ * 1-4 interaction within the given molecule type and its charge is
+ * perturbed.
+ *
+ * Some 1-4 interactions like F_COUL14 have the charges stored in the
+ * iparams list, but others do not. The commonly used F_LJ14 gets its
+ * charges from the topology, so we need more detailed checks for it,
+ * when FEP is active.
+ *
+ * \param[in] atomIndex  Index within range [0, molt.nr)
+ * \param[in] molt       Moleculetype to consider
+ * \return               Whether this atom is in a 1-4 interaction and charge is perturbed
+ * */
+bool atomHasPerturbedChargeIn14Interaction(int atomIndex, const gmx_moltype_t& molt);
 
 /*! \brief
  * Populate a 'local' topology for the whole system.
index 08713b39eb85787ef0d3f523d46169fc64464d48..a52f0801336319af91d34fdc0b92d6bf9ddafef1 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * This file is part of the GROMACS molecular simulation package.
  *
- * Copyright (c) 2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2019,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.
  * \brief
  * Implements test of mtop routines
  *
- * \author Roland Schulz <roland.schulz@intel.com>
+ * \author Mark Abraham <mark.j.abraham@gmail.com>
  * \ingroup module_topology
  */
 #include "gmxpre.h"
 
+#include "gromacs/topology/mtop_util.h"
+
 #include <gtest/gtest.h>
 
-#include "gromacs/topology/mtop_util.h"
-#include "gromacs/utility/basedefinitions.h"
+#include "gromacs/topology/ifunc.h"
 
 namespace gmx
 {
@@ -163,6 +164,71 @@ TEST(MtopTest, CanFindResidueStartAndEndAtoms)
     }
 }
 
+TEST(MtopTest, AtomHasPerturbedChargeIn14Interaction)
+{
+    gmx_moltype_t molt;
+    molt.atoms.nr   = 0;
+    molt.atoms.atom = nullptr;
+    EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(0, molt)) << "empty moltype has none";
+
+    {
+        SCOPED_TRACE("Use a moltype that has no perturbed charges at all");
+        const int numAtoms = 4;
+        init_t_atoms(&molt.atoms, numAtoms, false);
+        for (int i = 0; i != numAtoms; ++i)
+        {
+            molt.atoms.atom[i].q  = 1.0;
+            molt.atoms.atom[i].qB = 1.0;
+        }
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(0, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(1, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(2, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(3, molt));
+    }
+
+    {
+        SCOPED_TRACE("Use a moltype that has a perturbed charge, but no interactions");
+        molt.atoms.atom[1].q  = 1.0;
+        molt.atoms.atom[1].qB = -1.0;
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(0, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(1, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(2, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(3, molt));
+    }
+
+    {
+        SCOPED_TRACE("Use a moltype that has a perturbed charge but no 1-4 interactions");
+        std::array<int, 2> bondAtoms = { 0, 1 };
+        molt.ilist[F_BONDS].push_back(0, bondAtoms);
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(0, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(1, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(2, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(3, molt));
+    }
+
+    {
+        SCOPED_TRACE(
+                "Use a moltype that has a perturbed charge, but not on an atom with a 1-4 "
+                "interaction");
+        std::array<int, 2> pairAtoms = { 2, 3 };
+        molt.ilist[F_LJ14].push_back(0, pairAtoms);
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(0, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(1, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(2, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(3, molt));
+    }
+
+    {
+        SCOPED_TRACE("Use a moltype that has a perturbed charge on an atom with a 1-4 interaction");
+        std::array<int, 2> perturbedPairAtoms = { 1, 2 };
+        molt.ilist[F_LJ14].push_back(0, perturbedPairAtoms);
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(0, molt));
+        EXPECT_TRUE(atomHasPerturbedChargeIn14Interaction(1, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(2, molt));
+        EXPECT_FALSE(atomHasPerturbedChargeIn14Interaction(3, molt));
+    }
+}
+
 } // namespace
 
 } // namespace gmx
index 577f154b2059f877b30a350f6e2995e7a45f5049..f9efe2bfc7aafe9fe3f365da6e0984cff89b5047 100644 (file)
@@ -63,12 +63,11 @@ using testing::Pointwise;
 
 TEST(TopSortTest, WorksOnEmptyIdef)
 {
-    gmx_ffparams_t         forcefieldParams;
-    InteractionDefinitions idef(forcefieldParams);
-    real*                  emptyChargeA = nullptr;
-    real*                  emptyChargeB = nullptr;
+    gmx_ffparams_t          forcefieldParams;
+    InteractionDefinitions  idef(forcefieldParams);
+    ArrayRef<const int64_t> emptyAtomInfo;
 
-    gmx_sort_ilist_fe(&idef, emptyChargeA, emptyChargeB);
+    gmx_sort_ilist_fe(&idef, emptyAtomInfo);
 
     EXPECT_EQ(0, idef.numNonperturbedInteractions[F_BONDS]) << "empty idef has no perturbed bonds";
     EXPECT_THAT(idef.il[F_BONDS].iatoms, IsEmpty());
@@ -120,10 +119,9 @@ TEST(TopSortTest, WorksOnIdefWithNoPerturbedInteraction)
     // F_LJ14
     forcefieldParams.iparams.push_back(makeUnperturbedLJ14Params(100, 10000));
     idef.il[F_LJ14].push_back(forcefieldParams.iparams.size() - 1, bondAtoms);
-    std::vector<real> chargeA{ -1, 2 };
-    std::vector<real> chargeB{ -1, 2 };
+    std::vector<int64_t> atomInfo{ 0, 0 };
 
-    gmx_sort_ilist_fe(&idef, chargeA.data(), chargeB.data());
+    gmx_sort_ilist_fe(&idef, atomInfo);
 
     EXPECT_EQ(1, idef.numNonperturbedInteractions[F_BONDS] / (NRAL(F_BONDS) + 1))
             << "idef with no perturbed bonds has no perturbed bonds";
@@ -155,10 +153,9 @@ TEST(TopSortTest, WorksOnIdefWithPerturbedInteractions)
     forcefieldParams.iparams.push_back(makePerturbedLJ14Params(100, 10000, 200, 20000));
     idef.il[F_LJ14].push_back(forcefieldParams.iparams.size() - 1, perturbedBondAtoms);
     // Perturb the charge of atom 2, affecting the non-perturbed LJ14 above
-    std::vector<real> chargeA{ -1, 2, -2, 0 };
-    std::vector<real> chargeB{ -1, 2, 4, 0 };
+    std::vector<int64_t> atomInfo{ 0, 0, sc_atomInfo_HasPerturbedChargeIn14Interaction, 0 };
 
-    gmx_sort_ilist_fe(&idef, chargeA.data(), chargeB.data());
+    gmx_sort_ilist_fe(&idef, atomInfo);
 
     EXPECT_EQ(1, idef.numNonperturbedInteractions[F_BONDS] / (NRAL(F_BONDS) + 1))
             << "idef with a perturbed bond has a perturbed bond";
@@ -191,10 +188,9 @@ TEST(TopSortTest, SortsIdefWithPerturbedInteractions)
     forcefieldParams.iparams.push_back(makeUnperturbedLJ14Params(100, 10000));
     idef.il[F_LJ14].push_back(forcefieldParams.iparams.size() - 1, bondAtoms);
     // Perturb the charge of atom 2, affecting the non-perturbed LJ14 above
-    std::vector<real> chargeA{ -1, 2, -2, 0 };
-    std::vector<real> chargeB{ -1, 2, 4, 0 };
+    std::vector<int64_t> atomInfo{ 0, 0, sc_atomInfo_HasPerturbedChargeIn14Interaction, 0 };
 
-    gmx_sort_ilist_fe(&idef, chargeA.data(), chargeB.data());
+    gmx_sort_ilist_fe(&idef, atomInfo);
 
     EXPECT_EQ(1, idef.numNonperturbedInteractions[F_BONDS] / (NRAL(F_BONDS) + 1))
             << "idef with a perturbed bond has a perturbed bond";
@@ -236,10 +232,10 @@ TEST(TopSortTest, SortsMoreComplexIdefWithPerturbedInteractions)
     forcefieldParams.iparams.push_back(makeUnperturbedLJ14Params(100, 10000));
     idef.il[F_LJ14].push_back(forcefieldParams.iparams.size() - 1, moreBondAtoms);
     // Perturb the charge of atom 2, affecting the non-perturbed LJ14 above
-    std::vector<real> chargeA{ -1, 2, -2, 0, 1, -2, 3, -3 };
-    std::vector<real> chargeB{ -1, 2, 4, 0, 1, -2, 3, -3 };
+    std::vector<int64_t> atomInfo{ 0, 0, sc_atomInfo_HasPerturbedChargeIn14Interaction, 0, 0, 0,
+                                   0, 0 };
 
-    gmx_sort_ilist_fe(&idef, chargeA.data(), chargeB.data());
+    gmx_sort_ilist_fe(&idef, atomInfo);
 
     EXPECT_EQ(2, idef.numNonperturbedInteractions[F_BONDS] / (NRAL(F_BONDS) + 1))
             << "idef with some perturbed bonds has some perturbed bonds";
index 8270ca0307df18a937f245025a11c05ded571df4..98048319e6089bdbc788a9858538a7c526fa8d86 100644 (file)
@@ -41,6 +41,7 @@
 
 #include <cstdio>
 
+#include "gromacs/mdtypes/atominfo.h"
 #include "gromacs/topology/ifunc.h"
 #include "gromacs/topology/topology.h"
 #include "gromacs/utility/arrayref.h"
@@ -131,13 +132,12 @@ static gmx_bool ip_pert(int ftype, const t_iparams* ip)
     return bPert;
 }
 
-static gmx_bool ip_q_pert(int ftype, const t_iatom* ia, const t_iparams* ip, const real* qA, const real* qB)
+
+//! Return whether the two atom indices in a 1-4 interaction have perturbed charges per \c atomInfo
+static bool hasPerturbedChargeIn14Interaction(int atom0, int atom1, gmx::ArrayRef<const int64_t> atomInfo)
 {
-    /* 1-4 interactions do not have the charges stored in the iparams list,
-     * so we need a separate check for those.
-     */
-    return (ip_pert(ftype, ip + ia[0])
-            || (ftype == F_LJ14 && (qA[ia[1]] != qB[ia[1]] || qA[ia[2]] != qB[ia[2]])));
+    return (bool(atomInfo[atom0] & gmx::sc_atomInfo_HasPerturbedChargeIn14Interaction)
+            || bool(atomInfo[atom1] & gmx::sc_atomInfo_HasPerturbedChargeIn14Interaction));
 }
 
 gmx_bool gmx_mtop_bondeds_free_energy(const gmx_mtop_t* mtop)
@@ -176,13 +176,8 @@ gmx_bool gmx_mtop_bondeds_free_energy(const gmx_mtop_t* mtop)
     return bPert;
 }
 
-void gmx_sort_ilist_fe(InteractionDefinitions* idef, const real* qA, const real* qB)
+void gmx_sort_ilist_fe(InteractionDefinitions* idef, gmx::ArrayRef<const int64_t> atomInfo)
 {
-    if (qB == nullptr)
-    {
-        qB = qA;
-    }
-
     bool havePerturbedInteractions = false;
 
     int      iabuf_nalloc = 0;
@@ -201,7 +196,9 @@ void gmx_sort_ilist_fe(InteractionDefinitions* idef, const real* qA, const real*
             while (i < ilist->size())
             {
                 /* Check if this interaction is perturbed */
-                if (ip_q_pert(ftype, iatoms + i, idef->iparams.data(), qA, qB))
+                if (ip_pert(ftype, idef->iparams.data() + iatoms[i])
+                    || (ftype == F_LJ14
+                        && hasPerturbedChargeIn14Interaction(iatoms[i + 1], iatoms[i + 2], atomInfo)))
                 {
                     /* Copy to the perturbed buffer */
                     if (ib + 1 + nral > iabuf_nalloc)
index 2237b659031cf9fbf8b142bc09089bff87e3685b..fd471a95a821c4bca566e0b4370cbd1fb1e3c272 100644 (file)
@@ -2,7 +2,7 @@
  * This file is part of the GROMACS molecular simulation package.
  *
  * Copyright (c) 2008,2009,2010,2013,2014 by the GROMACS development team.
- * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
+ * Copyright (c) 2018,2019,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.
 struct gmx_mtop_t;
 class InteractionDefinitions;
 
+namespace gmx
+{
+template<typename>
+class ArrayRef;
+} // namespace gmx
+
 /* Returns if there are perturbed bonded interactions */
 gmx_bool gmx_mtop_bondeds_free_energy(const struct gmx_mtop_t* mtop);
 
 /* Sort all the bonded ilists in idef to have the perturbed ones at the end
  * and set nr_nr_nonperturbed in ilist.
  */
-void gmx_sort_ilist_fe(InteractionDefinitions* idef, const real* qA, const real* qB);
+void gmx_sort_ilist_fe(InteractionDefinitions* idef, gmx::ArrayRef<const int64_t> atomInfo);
 
 #endif