+/*
+ * 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 Defines a function that makes the list of links between
+ * atoms connected by bonded interactions.
+ *
+ * \author Berk Hess <hess@kth.se>
+ * \ingroup module_domdec
+ */
+
+#include "gmxpre.h"
+
+#include "gromacs/domdec/makebondedlinks.h"
+
+#include "gromacs/domdec/domdec_internal.h"
+#include "gromacs/domdec/options.h"
+#include "gromacs/domdec/reversetopology.h"
+#include "gromacs/mdtypes/forcerec.h"
+#include "gromacs/utility/fatalerror.h"
+#include "gromacs/utility/smalloc.h"
+#include "gromacs/topology/block.h"
+#include "gromacs/topology/mtop_util.h"
+
+using gmx::ArrayRef;
+using gmx::DDBondedChecking;
+
+/*! \brief Check if a link is stored in \p link between charge groups \p cg_gl and \p cg_gl_j and if not so, store a link */
+static void check_link(t_blocka* link, int cg_gl, int cg_gl_j)
+{
+ bool bFound = false;
+ for (int k = link->index[cg_gl]; k < link->index[cg_gl + 1]; k++)
+ {
+ GMX_RELEASE_ASSERT(link->a, "Inconsistent NULL pointer while making charge-group links");
+ if (link->a[k] == cg_gl_j)
+ {
+ bFound = TRUE;
+ }
+ }
+ if (!bFound)
+ {
+ GMX_RELEASE_ASSERT(link->a || link->index[cg_gl + 1] + 1 > link->nalloc_a,
+ "Inconsistent allocation of link");
+ /* Add this charge group link */
+ if (link->index[cg_gl + 1] + 1 > link->nalloc_a)
+ {
+ link->nalloc_a = over_alloc_large(link->index[cg_gl + 1] + 1);
+ srenew(link->a, link->nalloc_a);
+ }
+ link->a[link->index[cg_gl + 1]] = cg_gl_j;
+ link->index[cg_gl + 1]++;
+ }
+}
+
+void makeBondedLinks(gmx_domdec_t* dd,
+ const gmx_mtop_t& mtop,
+ gmx::ArrayRef<AtomInfoWithinMoleculeBlock> atomInfoForEachMoleculeBlock)
+{
+
+ if (!dd->comm->systemInfo.filterBondedCommunication)
+ {
+ /* Only communicate atoms based on cut-off */
+ dd->comm->bondedLinks = nullptr;
+ return;
+ }
+
+ t_blocka* link = nullptr;
+
+ /* For each atom make a list of other atoms in the system
+ * that a linked to it via bonded interactions
+ * which are also stored in reverse_top.
+ */
+
+ reverse_ilist_t ril_intermol;
+ if (mtop.bIntermolecularInteractions)
+ {
+ t_atoms atoms;
+
+ atoms.nr = mtop.natoms;
+ atoms.atom = nullptr;
+
+ GMX_RELEASE_ASSERT(mtop.intermolecular_ilist,
+ "We should have an ilist when intermolecular interactions are on");
+
+ ReverseTopOptions rtOptions(DDBondedChecking::ExcludeZeroLimit);
+ make_reverse_ilist(
+ *mtop.intermolecular_ilist, &atoms, rtOptions, AtomLinkRule::AllAtomsInBondeds, &ril_intermol);
+ }
+
+ snew(link, 1);
+ snew(link->index, mtop.natoms + 1);
+ link->nalloc_a = 0;
+ link->a = nullptr;
+
+ link->index[0] = 0;
+ int indexOfFirstAtomInMolecule = 0;
+ int numLinkedAtoms = 0;
+ for (size_t mb = 0; mb < mtop.molblock.size(); mb++)
+ {
+ const gmx_molblock_t& molb = mtop.molblock[mb];
+ if (molb.nmol == 0)
+ {
+ continue;
+ }
+ const gmx_moltype_t& molt = mtop.moltype[molb.type];
+ /* Make a reverse ilist in which the interactions are linked
+ * to all atoms, not only the first atom as in gmx_reverse_top.
+ * The constraints are discarded here.
+ */
+ ReverseTopOptions rtOptions(DDBondedChecking::ExcludeZeroLimit);
+ reverse_ilist_t ril;
+ make_reverse_ilist(molt.ilist, &molt.atoms, rtOptions, AtomLinkRule::AllAtomsInBondeds, &ril);
+
+ AtomInfoWithinMoleculeBlock* atomInfoOfMoleculeBlock = &atomInfoForEachMoleculeBlock[mb];
+
+ int mol = 0;
+ for (mol = 0; mol < (mtop.bIntermolecularInteractions ? molb.nmol : 1); mol++)
+ {
+ for (int a = 0; a < molt.atoms.nr; a++)
+ {
+ int atomIndex = indexOfFirstAtomInMolecule + a;
+ link->index[atomIndex + 1] = link->index[atomIndex];
+ int i = ril.index[a];
+ while (i < ril.index[a + 1])
+ {
+ int ftype = ril.il[i++];
+ int nral = NRAL(ftype);
+ /* Skip the ifunc index */
+ i++;
+ for (int j = 0; j < nral; j++)
+ {
+ int aj = ril.il[i + j];
+ if (aj != a)
+ {
+ check_link(link, atomIndex, indexOfFirstAtomInMolecule + aj);
+ }
+ }
+ i += nral_rt(ftype);
+ }
+
+ if (mtop.bIntermolecularInteractions)
+ {
+ int i = ril_intermol.index[atomIndex];
+ while (i < ril_intermol.index[atomIndex + 1])
+ {
+ int ftype = ril_intermol.il[i++];
+ int nral = NRAL(ftype);
+ /* Skip the ifunc index */
+ i++;
+ for (int j = 0; j < nral; j++)
+ {
+ /* Here we assume we have no charge groups;
+ * this has been checked above.
+ */
+ int aj = ril_intermol.il[i + j];
+ check_link(link, atomIndex, aj);
+ }
+ i += nral_rt(ftype);
+ }
+ }
+ if (link->index[atomIndex + 1] - link->index[atomIndex] > 0)
+ {
+ SET_CGINFO_BOND_INTER(atomInfoOfMoleculeBlock->atomInfo[a]);
+ numLinkedAtoms++;
+ }
+ }
+
+ indexOfFirstAtomInMolecule += molt.atoms.nr;
+ }
+ int nlink_mol = link->index[indexOfFirstAtomInMolecule]
+ - link->index[indexOfFirstAtomInMolecule - molt.atoms.nr];
+
+ if (debug)
+ {
+ fprintf(debug,
+ "molecule type '%s' %d atoms has %d atom links through bonded interac.\n",
+ *molt.name,
+ molt.atoms.nr,
+ nlink_mol);
+ }
+
+ if (molb.nmol > mol)
+ {
+ /* Copy the data for the rest of the molecules in this block */
+ link->nalloc_a += (molb.nmol - mol) * nlink_mol;
+ srenew(link->a, link->nalloc_a);
+ for (; mol < molb.nmol; mol++)
+ {
+ for (int a = 0; a < molt.atoms.nr; a++)
+ {
+ int atomIndex = indexOfFirstAtomInMolecule + a;
+ link->index[atomIndex + 1] = link->index[atomIndex + 1 - molt.atoms.nr] + nlink_mol;
+ for (int j = link->index[atomIndex]; j < link->index[atomIndex + 1]; j++)
+ {
+ link->a[j] = link->a[j - nlink_mol] + molt.atoms.nr;
+ }
+ if (link->index[atomIndex + 1] - link->index[atomIndex] > 0
+ && atomIndex - atomInfoOfMoleculeBlock->indexOfFirstAtomInMoleculeBlock
+ < gmx::ssize(atomInfoOfMoleculeBlock->atomInfo))
+ {
+ SET_CGINFO_BOND_INTER(
+ atomInfoOfMoleculeBlock
+ ->atomInfo[atomIndex - atomInfoOfMoleculeBlock->indexOfFirstAtomInMoleculeBlock]);
+ numLinkedAtoms++;
+ }
+ }
+ indexOfFirstAtomInMolecule += molt.atoms.nr;
+ }
+ }
+ }
+
+ if (debug)
+ {
+ fprintf(debug, "Of the %d atoms %d are linked via bonded interactions\n", mtop.natoms, numLinkedAtoms);
+ }
+
+ dd->comm->bondedLinks = link;
+}