2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2006 - 2014, The GROMACS development team.
5 * Copyright (c) 2015,2016,2017,2018,2019,2020, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
39 * \brief This file defines functions used by the domdec module
40 * while managing the construction, use and error checking for
41 * topologies local to a DD rank.
43 * \author Berk Hess <hess@kth.se>
44 * \ingroup module_domdec
57 #include "gromacs/domdec/domdec.h"
58 #include "gromacs/domdec/domdec_network.h"
59 #include "gromacs/domdec/ga2la.h"
60 #include "gromacs/gmxlib/network.h"
61 #include "gromacs/math/vec.h"
62 #include "gromacs/mdlib/forcerec.h"
63 #include "gromacs/mdlib/gmx_omp_nthreads.h"
64 #include "gromacs/mdlib/vsite.h"
65 #include "gromacs/mdtypes/commrec.h"
66 #include "gromacs/mdtypes/forcerec.h"
67 #include "gromacs/mdtypes/inputrec.h"
68 #include "gromacs/mdtypes/md_enums.h"
69 #include "gromacs/mdtypes/mdatom.h"
70 #include "gromacs/mdtypes/state.h"
71 #include "gromacs/pbcutil/mshift.h"
72 #include "gromacs/pbcutil/pbc.h"
73 #include "gromacs/topology/ifunc.h"
74 #include "gromacs/topology/mtop_util.h"
75 #include "gromacs/topology/topsort.h"
76 #include "gromacs/utility/cstringutil.h"
77 #include "gromacs/utility/exceptions.h"
78 #include "gromacs/utility/fatalerror.h"
79 #include "gromacs/utility/gmxassert.h"
80 #include "gromacs/utility/listoflists.h"
81 #include "gromacs/utility/logger.h"
82 #include "gromacs/utility/smalloc.h"
83 #include "gromacs/utility/strconvert.h"
84 #include "gromacs/utility/stringstream.h"
85 #include "gromacs/utility/stringutil.h"
86 #include "gromacs/utility/textwriter.h"
88 #include "domdec_constraints.h"
89 #include "domdec_internal.h"
90 #include "domdec_vsite.h"
94 using gmx::ListOfLists
;
97 /*! \brief The number of integer item in the local state, used for broadcasting of the state */
98 #define NITEM_DD_INIT_LOCAL_STATE 5
100 struct reverse_ilist_t
102 std::vector
<int> index
; /* Index for each atom into il */
103 std::vector
<int> il
; /* ftype|type|a0|...|an|ftype|... */
104 int numAtomsInMolecule
; /* The number of atoms in this molecule */
107 struct MolblockIndices
115 /*! \brief Struct for thread local work data for local topology generation */
118 /*! \brief Constructor
120 * \param[in] ffparams The interaction parameters, the lifetime of the created object should not exceed the lifetime of the passed parameters
122 thread_work_t(const gmx_ffparams_t
& ffparams
) : idef(ffparams
) {}
124 InteractionDefinitions idef
; /**< Partial local topology */
125 std::unique_ptr
<gmx::VsitePbc
> vsitePbc
= nullptr; /**< vsite PBC structure */
126 int nbonded
= 0; /**< The number of bondeds in this struct */
127 ListOfLists
<int> excl
; /**< List of exclusions */
128 int excl_count
= 0; /**< The total exclusion count for \p excl */
131 /*! \brief Struct for the reverse topology: links bonded interactions to atomsx */
132 struct gmx_reverse_top_t
134 //! @cond Doxygen_Suppress
135 //! \brief Are there constraints in this revserse top?
136 bool bConstr
= false;
137 //! \brief Are there settles in this revserse top?
138 bool bSettle
= false;
139 //! \brief All bonded interactions have to be assigned?
140 bool bBCheck
= false;
141 //! \brief Are there bondeds/exclusions between atoms?
142 bool bInterAtomicInteractions
= false;
143 //! \brief Reverse ilist for all moltypes
144 std::vector
<reverse_ilist_t
> ril_mt
;
145 //! \brief The size of ril_mt[?].index summed over all entries
146 int ril_mt_tot_size
= 0;
147 //! \brief The sorting state of bondeds for free energy
148 int ilsort
= ilsortUNKNOWN
;
149 //! \brief molblock to global atom index for quick lookup of molblocks on atom index
150 std::vector
<MolblockIndices
> mbi
;
152 //! \brief Do we have intermolecular interactions?
153 bool bIntermolecularInteractions
= false;
154 //! \brief Intermolecular reverse ilist
155 reverse_ilist_t ril_intermol
;
157 /* Work data structures for multi-threading */
158 //! \brief Thread work array for local topology generation
159 std::vector
<thread_work_t
> th_work
;
163 /*! \brief Returns the number of atom entries for il in gmx_reverse_top_t */
164 static int nral_rt(int ftype
)
169 if (interaction_function
[ftype
].flags
& IF_VSITE
)
171 /* With vsites the reverse topology contains an extra entry
172 * for storing if constructing atoms are vsites.
180 /*! \brief Return whether interactions of type \p ftype need to be assigned exactly once */
181 static gmx_bool
dd_check_ftype(int ftype
, gmx_bool bBCheck
, gmx_bool bConstr
, gmx_bool bSettle
)
183 return ((((interaction_function
[ftype
].flags
& IF_BOND
) != 0U)
184 && ((interaction_function
[ftype
].flags
& IF_VSITE
) == 0U)
185 && (bBCheck
|| ((interaction_function
[ftype
].flags
& IF_LIMZERO
) == 0U)))
186 || (bConstr
&& (ftype
== F_CONSTR
|| ftype
== F_CONSTRNC
)) || (bSettle
&& ftype
== F_SETTLE
));
189 /*! \brief Checks whether interactions have been assigned for one function type
191 * Loops over a list of interactions in the local topology of one function type
192 * and flags each of the interactions as assigned in the global \p isAssigned list.
193 * Exits with an inconsistency error when an interaction is assigned more than once.
195 static void flagInteractionsForType(const int ftype
,
196 const InteractionList
& il
,
197 const reverse_ilist_t
& ril
,
198 const gmx::Range
<int>& atomRange
,
199 const int numAtomsPerMolecule
,
200 gmx::ArrayRef
<const int> globalAtomIndices
,
201 gmx::ArrayRef
<int> isAssigned
)
203 const int nril_mol
= ril
.index
[numAtomsPerMolecule
];
204 const int nral
= NRAL(ftype
);
206 for (int i
= 0; i
< il
.size(); i
+= 1 + nral
)
208 // ia[0] is the interaction type, ia[1, ...] the atom indices
209 const int* ia
= il
.iatoms
.data() + i
;
210 // Extract the global atom index of the first atom in this interaction
211 const int a0
= globalAtomIndices
[ia
[1]];
212 /* Check if this interaction is in
213 * the currently checked molblock.
215 if (atomRange
.isInRange(a0
))
217 // The molecule index in the list of this molecule type
218 const int moleculeIndex
= (a0
- atomRange
.begin()) / numAtomsPerMolecule
;
219 const int atomOffset
= (a0
- atomRange
.begin()) - moleculeIndex
* numAtomsPerMolecule
;
220 const int globalAtomStartInMolecule
= atomRange
.begin() + moleculeIndex
* numAtomsPerMolecule
;
221 int j_mol
= ril
.index
[atomOffset
];
223 while (j_mol
< ril
.index
[atomOffset
+ 1] && !found
)
225 const int j
= moleculeIndex
* nril_mol
+ j_mol
;
226 const int ftype_j
= ril
.il
[j_mol
];
227 /* Here we need to check if this interaction has
228 * not already been assigned, since we could have
229 * multiply defined interactions.
231 if (ftype
== ftype_j
&& ia
[0] == ril
.il
[j_mol
+ 1] && isAssigned
[j
] == 0)
233 /* Check the atoms */
235 for (int a
= 0; a
< nral
; a
++)
237 if (globalAtomIndices
[ia
[1 + a
]]
238 != globalAtomStartInMolecule
+ ril
.il
[j_mol
+ 2 + a
])
248 j_mol
+= 2 + nral_rt(ftype_j
);
252 gmx_incons("Some interactions seem to be assigned multiple times");
258 /*! \brief Help print error output when interactions are missing in a molblock
260 * \note This function needs to be called on all ranks (contains a global summation)
262 static std::string
printMissingInteractionsMolblock(t_commrec
* cr
,
263 const gmx_reverse_top_t
& rt
,
264 const char* moltypename
,
265 const reverse_ilist_t
& ril
,
266 const gmx::Range
<int>& atomRange
,
267 const int numAtomsPerMolecule
,
268 const int numMolecules
,
269 const InteractionDefinitions
& idef
)
271 const int nril_mol
= ril
.index
[numAtomsPerMolecule
];
272 std::vector
<int> isAssigned(numMolecules
* nril_mol
, 0);
273 gmx::StringOutputStream stream
;
274 gmx::TextWriter
log(&stream
);
276 for (int ftype
= 0; ftype
< F_NRE
; ftype
++)
278 if (dd_check_ftype(ftype
, rt
.bBCheck
, rt
.bConstr
, rt
.bSettle
))
280 flagInteractionsForType(ftype
, idef
.il
[ftype
], ril
, atomRange
, numAtomsPerMolecule
,
281 cr
->dd
->globalAtomIndices
, isAssigned
);
285 gmx_sumi(isAssigned
.size(), isAssigned
.data(), cr
);
287 const int numMissingToPrint
= 10;
289 for (int mol
= 0; mol
< numMolecules
; mol
++)
292 while (j_mol
< nril_mol
)
294 int ftype
= ril
.il
[j_mol
];
295 int nral
= NRAL(ftype
);
296 int j
= mol
* nril_mol
+ j_mol
;
297 if (isAssigned
[j
] == 0 && !(interaction_function
[ftype
].flags
& IF_VSITE
))
299 if (DDMASTER(cr
->dd
))
303 log
.writeLineFormatted("Molecule type '%s'", moltypename
);
304 log
.writeLineFormatted(
305 "the first %d missing interactions, except for exclusions:",
308 log
.writeStringFormatted("%20s atoms", interaction_function
[ftype
].longname
);
310 for (a
= 0; a
< nral
; a
++)
312 log
.writeStringFormatted("%5d", ril
.il
[j_mol
+ 2 + a
] + 1);
316 log
.writeString(" ");
319 log
.writeString(" global");
320 for (a
= 0; a
< nral
; a
++)
322 log
.writeStringFormatted("%6d", atomRange
.begin() + mol
* numAtomsPerMolecule
323 + ril
.il
[j_mol
+ 2 + a
] + 1);
325 log
.ensureLineBreak();
328 if (i
>= numMissingToPrint
)
333 j_mol
+= 2 + nral_rt(ftype
);
337 return stream
.toString();
340 /*! \brief Help print error output when interactions are missing */
341 static void printMissingInteractionsAtoms(const gmx::MDLogger
& mdlog
,
343 const gmx_mtop_t
& mtop
,
344 const InteractionDefinitions
& idef
)
346 const gmx_reverse_top_t
& rt
= *cr
->dd
->reverse_top
;
348 /* Print the atoms in the missing interactions per molblock */
350 for (const gmx_molblock_t
& molb
: mtop
.molblock
)
352 const gmx_moltype_t
& moltype
= mtop
.moltype
[molb
.type
];
353 const int a_start
= a_end
;
354 a_end
= a_start
+ molb
.nmol
* moltype
.atoms
.nr
;
355 const gmx::Range
<int> atomRange(a_start
, a_end
);
357 auto warning
= printMissingInteractionsMolblock(cr
, rt
, *(moltype
.name
), rt
.ril_mt
[molb
.type
],
358 atomRange
, moltype
.atoms
.nr
, molb
.nmol
, idef
);
360 GMX_LOG(mdlog
.warning
).appendText(warning
);
364 void dd_print_missing_interactions(const gmx::MDLogger
& mdlog
,
367 const gmx_mtop_t
* top_global
,
368 const gmx_localtop_t
* top_local
,
369 gmx::ArrayRef
<const gmx::RVec
> x
,
372 int ndiff_tot
, cl
[F_NRE
], n
, ndiff
, rest_global
, rest_local
;
378 GMX_LOG(mdlog
.warning
)
380 "Not all bonded interactions have been properly assigned to the domain "
381 "decomposition cells");
383 ndiff_tot
= local_count
- dd
->nbonded_global
;
385 for (ftype
= 0; ftype
< F_NRE
; ftype
++)
388 cl
[ftype
] = top_local
->idef
.il
[ftype
].size() / (1 + nral
);
391 gmx_sumi(F_NRE
, cl
, cr
);
395 GMX_LOG(mdlog
.warning
).appendText("A list of missing interactions:");
396 rest_global
= dd
->nbonded_global
;
397 rest_local
= local_count
;
398 for (ftype
= 0; ftype
< F_NRE
; ftype
++)
400 /* In the reverse and local top all constraints are merged
401 * into F_CONSTR. So in the if statement we skip F_CONSTRNC
402 * and add these constraints when doing F_CONSTR.
404 if (((interaction_function
[ftype
].flags
& IF_BOND
)
405 && (dd
->reverse_top
->bBCheck
|| !(interaction_function
[ftype
].flags
& IF_LIMZERO
)))
406 || (dd
->reverse_top
->bConstr
&& ftype
== F_CONSTR
)
407 || (dd
->reverse_top
->bSettle
&& ftype
== F_SETTLE
))
409 n
= gmx_mtop_ftype_count(top_global
, ftype
);
410 if (ftype
== F_CONSTR
)
412 n
+= gmx_mtop_ftype_count(top_global
, F_CONSTRNC
);
414 ndiff
= cl
[ftype
] - n
;
417 GMX_LOG(mdlog
.warning
)
418 .appendTextFormatted("%20s of %6d missing %6d",
419 interaction_function
[ftype
].longname
, n
, -ndiff
);
422 rest_local
-= cl
[ftype
];
426 ndiff
= rest_local
- rest_global
;
429 GMX_LOG(mdlog
.warning
).appendTextFormatted("%20s of %6d missing %6d", "exclusions", rest_global
, -ndiff
);
433 printMissingInteractionsAtoms(mdlog
, cr
, *top_global
, top_local
->idef
);
434 write_dd_pdb("dd_dump_err", 0, "dump", top_global
, cr
, -1, as_rvec_array(x
.data()), box
);
436 std::string errorMessage
;
441 "One or more interactions were assigned to multiple domains of the domain "
442 "decompostion. Please report this bug.";
446 errorMessage
= gmx::formatString(
447 "%d of the %d bonded interactions could not be calculated because some atoms "
448 "involved moved further apart than the multi-body cut-off distance (%g nm) or the "
449 "two-body cut-off distance (%g nm), see option -rdd, for pairs and tabulated bonds "
450 "also see option -ddcheck",
451 -ndiff_tot
, cr
->dd
->nbonded_global
, dd_cutoff_multibody(dd
), dd_cutoff_twobody(dd
));
453 gmx_fatal_collective(FARGS
, cr
->mpi_comm_mygroup
, MASTER(cr
), "%s", errorMessage
.c_str());
456 /*! \brief Return global topology molecule information for global atom index \p i_gl */
457 static void global_atomnr_to_moltype_ind(const gmx_reverse_top_t
* rt
, int i_gl
, int* mb
, int* mt
, int* mol
, int* i_mol
)
459 const MolblockIndices
* mbi
= rt
->mbi
.data();
461 int end
= rt
->mbi
.size(); /* exclusive */
464 /* binary search for molblock_ind */
467 mid
= (start
+ end
) / 2;
468 if (i_gl
>= mbi
[mid
].a_end
)
472 else if (i_gl
< mbi
[mid
].a_start
)
486 *mol
= (i_gl
- mbi
->a_start
) / mbi
->natoms_mol
;
487 *i_mol
= (i_gl
- mbi
->a_start
) - (*mol
) * mbi
->natoms_mol
;
490 /*! \brief Returns the maximum number of exclusions per atom */
491 static int getMaxNumExclusionsPerAtom(const ListOfLists
<int>& excls
)
494 for (gmx::index at
= 0; at
< excls
.ssize(); at
++)
496 const auto list
= excls
[at
];
497 const int numExcls
= list
.ssize();
499 GMX_RELEASE_ASSERT(numExcls
!= 1 || list
[0] == at
,
500 "With 1 exclusion we expect a self-exclusion");
502 maxNumExcls
= std::max(maxNumExcls
, numExcls
);
508 /*! \brief Run the reverse ilist generation and store it in r_il when \p bAssign = TRUE */
509 static int low_make_reverse_ilist(const InteractionLists
& il_mt
,
515 gmx::ArrayRef
<const int> r_index
,
516 gmx::ArrayRef
<int> r_il
,
517 gmx_bool bLinkToAllAtoms
,
520 int ftype
, j
, nlink
, link
;
525 for (ftype
= 0; ftype
< F_NRE
; ftype
++)
527 if ((interaction_function
[ftype
].flags
& (IF_BOND
| IF_VSITE
))
528 || (bConstr
&& (ftype
== F_CONSTR
|| ftype
== F_CONSTRNC
)) || (bSettle
&& ftype
== F_SETTLE
))
530 const bool bVSite
= ((interaction_function
[ftype
].flags
& IF_VSITE
) != 0U);
531 const int nral
= NRAL(ftype
);
532 const auto& il
= il_mt
[ftype
];
533 for (int i
= 0; i
< il
.size(); i
+= 1 + nral
)
535 const int* ia
= il
.iatoms
.data() + i
;
540 /* We don't need the virtual sites for the cg-links */
550 /* Couple to the first atom in the interaction */
553 for (link
= 0; link
< nlink
; link
++)
558 GMX_ASSERT(!r_il
.empty(), "with bAssign not allowed to be empty");
559 GMX_ASSERT(!r_index
.empty(), "with bAssign not allowed to be empty");
560 r_il
[r_index
[a
] + count
[a
]] = (ftype
== F_CONSTRNC
? F_CONSTR
: ftype
);
561 r_il
[r_index
[a
] + count
[a
] + 1] = ia
[0];
562 for (j
= 1; j
< 1 + nral
; j
++)
564 /* Store the molecular atom number */
565 r_il
[r_index
[a
] + count
[a
] + 1 + j
] = ia
[j
];
568 if (interaction_function
[ftype
].flags
& IF_VSITE
)
572 /* Add an entry to iatoms for storing
573 * which of the constructing atoms are
576 r_il
[r_index
[a
] + count
[a
] + 2 + nral
] = 0;
577 for (j
= 2; j
< 1 + nral
; j
++)
579 if (atom
[ia
[j
]].ptype
== eptVSite
)
581 r_il
[r_index
[a
] + count
[a
] + 2 + nral
] |= (2 << j
);
588 /* We do not count vsites since they are always
589 * uniquely assigned and can be assigned
590 * to multiple nodes with recursive vsites.
592 if (bBCheck
|| !(interaction_function
[ftype
].flags
& IF_LIMZERO
))
597 count
[a
] += 2 + nral_rt(ftype
);
606 /*! \brief Make the reverse ilist: a list of bonded interactions linked to atoms */
607 static int make_reverse_ilist(const InteractionLists
& ilist
,
608 const t_atoms
* atoms
,
612 gmx_bool bLinkToAllAtoms
,
613 reverse_ilist_t
* ril_mt
)
615 int nat_mt
, *count
, i
, nint_mt
;
617 /* Count the interactions */
620 low_make_reverse_ilist(ilist
, atoms
->atom
, count
, bConstr
, bSettle
, bBCheck
, {}, {},
621 bLinkToAllAtoms
, FALSE
);
623 ril_mt
->index
.push_back(0);
624 for (i
= 0; i
< nat_mt
; i
++)
626 ril_mt
->index
.push_back(ril_mt
->index
[i
] + count
[i
]);
629 ril_mt
->il
.resize(ril_mt
->index
[nat_mt
]);
631 /* Store the interactions */
632 nint_mt
= low_make_reverse_ilist(ilist
, atoms
->atom
, count
, bConstr
, bSettle
, bBCheck
,
633 ril_mt
->index
, ril_mt
->il
, bLinkToAllAtoms
, TRUE
);
637 ril_mt
->numAtomsInMolecule
= atoms
->nr
;
642 /*! \brief Generate the reverse topology */
643 static gmx_reverse_top_t
make_reverse_top(const gmx_mtop_t
* mtop
,
650 gmx_reverse_top_t rt
;
652 /* Should we include constraints (for SHAKE) in rt? */
653 rt
.bConstr
= bConstr
;
654 rt
.bSettle
= bSettle
;
655 rt
.bBCheck
= bBCheck
;
657 rt
.bInterAtomicInteractions
= mtop
->bIntermolecularInteractions
;
658 rt
.ril_mt
.resize(mtop
->moltype
.size());
659 rt
.ril_mt_tot_size
= 0;
660 std::vector
<int> nint_mt
;
661 for (size_t mt
= 0; mt
< mtop
->moltype
.size(); mt
++)
663 const gmx_moltype_t
& molt
= mtop
->moltype
[mt
];
664 if (molt
.atoms
.nr
> 1)
666 rt
.bInterAtomicInteractions
= true;
669 /* Make the atom to interaction list for this molecule type */
670 int numberOfInteractions
= make_reverse_ilist(
671 molt
.ilist
, &molt
.atoms
, rt
.bConstr
, rt
.bSettle
, rt
.bBCheck
, FALSE
, &rt
.ril_mt
[mt
]);
672 nint_mt
.push_back(numberOfInteractions
);
674 rt
.ril_mt_tot_size
+= rt
.ril_mt
[mt
].index
[molt
.atoms
.nr
];
678 fprintf(debug
, "The total size of the atom to interaction index is %d integers\n",
683 for (const gmx_molblock_t
& molblock
: mtop
->molblock
)
685 *nint
+= molblock
.nmol
* nint_mt
[molblock
.type
];
688 /* Make an intermolecular reverse top, if necessary */
689 rt
.bIntermolecularInteractions
= mtop
->bIntermolecularInteractions
;
690 if (rt
.bIntermolecularInteractions
)
692 t_atoms atoms_global
;
694 atoms_global
.nr
= mtop
->natoms
;
695 atoms_global
.atom
= nullptr; /* Only used with virtual sites */
697 GMX_RELEASE_ASSERT(mtop
->intermolecular_ilist
,
698 "We should have an ilist when intermolecular interactions are on");
700 *nint
+= make_reverse_ilist(*mtop
->intermolecular_ilist
, &atoms_global
, rt
.bConstr
,
701 rt
.bSettle
, rt
.bBCheck
, FALSE
, &rt
.ril_intermol
);
704 if (bFE
&& gmx_mtop_bondeds_free_energy(mtop
))
706 rt
.ilsort
= ilsortFE_UNSORTED
;
710 rt
.ilsort
= ilsortNO_FE
;
713 /* Make a molblock index for fast searching */
715 for (size_t mb
= 0; mb
< mtop
->molblock
.size(); mb
++)
717 const gmx_molblock_t
& molb
= mtop
->molblock
[mb
];
718 const int numAtomsPerMol
= mtop
->moltype
[molb
.type
].atoms
.nr
;
721 i
+= molb
.nmol
* numAtomsPerMol
;
723 mbi
.natoms_mol
= numAtomsPerMol
;
724 mbi
.type
= molb
.type
;
725 rt
.mbi
.push_back(mbi
);
728 for (int th
= 0; th
< gmx_omp_nthreads_get(emntDomdec
); th
++)
730 rt
.th_work
.emplace_back(mtop
->ffparams
);
736 void dd_make_reverse_top(FILE* fplog
,
738 const gmx_mtop_t
* mtop
,
739 const gmx::VirtualSitesHandler
* vsite
,
740 const t_inputrec
* ir
,
745 fprintf(fplog
, "\nLinking all bonded interactions to atoms\n");
748 /* If normal and/or settle constraints act only within charge groups,
749 * we can store them in the reverse top and simply assign them to domains.
750 * Otherwise we need to assign them to multiple domains and set up
751 * the parallel version constraint algorithm(s).
754 dd
->reverse_top
= new gmx_reverse_top_t
;
756 make_reverse_top(mtop
, ir
->efep
!= efepNO
, !dd
->comm
->systemInfo
.haveSplitConstraints
,
757 !dd
->comm
->systemInfo
.haveSplitSettles
, bBCheck
, &dd
->nbonded_global
);
759 dd
->haveExclusions
= false;
760 for (const gmx_molblock_t
& molb
: mtop
->molblock
)
762 const int maxNumExclusionsPerAtom
= getMaxNumExclusionsPerAtom(mtop
->moltype
[molb
.type
].excls
);
763 // We checked above that max 1 exclusion means only self exclusions
764 if (maxNumExclusionsPerAtom
> 1)
766 dd
->haveExclusions
= true;
770 if (vsite
&& vsite
->numInterUpdategroupVirtualSites() > 0)
775 "There are %d inter update-group virtual sites,\n"
776 "will an extra communication step for selected coordinates and forces\n",
777 vsite
->numInterUpdategroupVirtualSites());
779 init_domdec_vsites(dd
, vsite
->numInterUpdategroupVirtualSites());
782 if (dd
->comm
->systemInfo
.haveSplitConstraints
|| dd
->comm
->systemInfo
.haveSplitSettles
)
784 init_domdec_constraints(dd
, mtop
);
788 fprintf(fplog
, "\n");
792 /*! \brief Store a vsite interaction at the end of \p il
794 * This routine is very similar to add_ifunc, but vsites interactions
795 * have more work to do than other kinds of interactions, and the
796 * complex way nral (and thus vector contents) depends on ftype
797 * confuses static analysis tools unless we fuse the vsite
798 * atom-indexing organization code with the ifunc-adding code, so that
799 * they can see that nral is the same value. */
800 static inline void add_ifunc_for_vsites(t_iatom
* tiatoms
,
801 const gmx_ga2la_t
& ga2la
,
807 const t_iatom
* iatoms
,
811 tiatoms
[0] = iatoms
[0];
815 /* We know the local index of the first atom */
820 /* Convert later in make_local_vsites */
821 tiatoms
[1] = -a_gl
- 1;
824 GMX_ASSERT(nral
>= 2 && nral
<= 5, "Invalid nral for vsites");
825 for (int k
= 2; k
< 1 + nral
; k
++)
827 int ak_gl
= a_gl
+ iatoms
[k
] - a_mol
;
828 if (const int* homeIndex
= ga2la
.findHome(ak_gl
))
830 tiatoms
[k
] = *homeIndex
;
834 /* Copy the global index, convert later in make_local_vsites */
835 tiatoms
[k
] = -(ak_gl
+ 1);
837 // Note that ga2la_get_home always sets the third parameter if
840 il
->push_back(tiatoms
[0], nral
, tiatoms
+ 1);
843 /*! \brief Store a position restraint in idef and iatoms, complex because the parameters are different for each entry */
844 static void add_posres(int mol
,
846 int numAtomsInMolecule
,
847 const gmx_molblock_t
* molb
,
849 const t_iparams
* ip_in
,
850 InteractionDefinitions
* idef
)
852 /* This position restraint has not been added yet,
853 * so it's index is the current number of position restraints.
855 const int n
= idef
->il
[F_POSRES
].size() / 2;
857 /* Get the position restraint coordinates from the molblock */
858 const int a_molb
= mol
* numAtomsInMolecule
+ a_mol
;
859 GMX_ASSERT(a_molb
< ssize(molb
->posres_xA
),
860 "We need a sufficient number of position restraint coordinates");
861 /* Copy the force constants */
862 t_iparams ip
= ip_in
[iatoms
[0]];
863 ip
.posres
.pos0A
[XX
] = molb
->posres_xA
[a_molb
][XX
];
864 ip
.posres
.pos0A
[YY
] = molb
->posres_xA
[a_molb
][YY
];
865 ip
.posres
.pos0A
[ZZ
] = molb
->posres_xA
[a_molb
][ZZ
];
866 if (!molb
->posres_xB
.empty())
868 ip
.posres
.pos0B
[XX
] = molb
->posres_xB
[a_molb
][XX
];
869 ip
.posres
.pos0B
[YY
] = molb
->posres_xB
[a_molb
][YY
];
870 ip
.posres
.pos0B
[ZZ
] = molb
->posres_xB
[a_molb
][ZZ
];
874 ip
.posres
.pos0B
[XX
] = ip
.posres
.pos0A
[XX
];
875 ip
.posres
.pos0B
[YY
] = ip
.posres
.pos0A
[YY
];
876 ip
.posres
.pos0B
[ZZ
] = ip
.posres
.pos0A
[ZZ
];
878 /* Set the parameter index for idef->iparams_posres */
880 idef
->iparams_posres
.push_back(ip
);
882 GMX_ASSERT(int(idef
->iparams_posres
.size()) == n
+ 1,
883 "The index of the parameter type should match n");
886 /*! \brief Store a flat-bottomed position restraint in idef and iatoms, complex because the parameters are different for each entry */
887 static void add_fbposres(int mol
,
889 int numAtomsInMolecule
,
890 const gmx_molblock_t
* molb
,
892 const t_iparams
* ip_in
,
893 InteractionDefinitions
* idef
)
895 /* This flat-bottom position restraint has not been added yet,
896 * so it's index is the current number of position restraints.
898 const int n
= idef
->il
[F_FBPOSRES
].size() / 2;
900 /* Get the position restraint coordinats from the molblock */
901 const int a_molb
= mol
* numAtomsInMolecule
+ a_mol
;
902 GMX_ASSERT(a_molb
< ssize(molb
->posres_xA
),
903 "We need a sufficient number of position restraint coordinates");
904 /* Copy the force constants */
905 t_iparams ip
= ip_in
[iatoms
[0]];
906 /* Take reference positions from A position of normal posres */
907 ip
.fbposres
.pos0
[XX
] = molb
->posres_xA
[a_molb
][XX
];
908 ip
.fbposres
.pos0
[YY
] = molb
->posres_xA
[a_molb
][YY
];
909 ip
.fbposres
.pos0
[ZZ
] = molb
->posres_xA
[a_molb
][ZZ
];
911 /* Note: no B-type for flat-bottom posres */
913 /* Set the parameter index for idef->iparams_fbposres */
915 idef
->iparams_fbposres
.push_back(ip
);
917 GMX_ASSERT(int(idef
->iparams_fbposres
.size()) == n
+ 1,
918 "The index of the parameter type should match n");
921 /*! \brief Store a virtual site interaction, complex because of PBC and recursion */
922 static void add_vsite(const gmx_ga2la_t
& ga2la
,
923 gmx::ArrayRef
<const int> index
,
924 gmx::ArrayRef
<const int> rtil
,
931 const t_iatom
* iatoms
,
932 InteractionDefinitions
* idef
)
935 t_iatom tiatoms
[1 + MAXATOMLIST
];
936 int j
, ftype_r
, nral_r
;
938 /* Add this interaction to the local topology */
939 add_ifunc_for_vsites(tiatoms
, ga2la
, nral
, bHomeA
, a
, a_gl
, a_mol
, iatoms
, &idef
->il
[ftype
]);
941 if (iatoms
[1 + nral
])
943 /* Check for recursion */
944 for (k
= 2; k
< 1 + nral
; k
++)
946 if ((iatoms
[1 + nral
] & (2 << k
)) && (tiatoms
[k
] < 0))
948 /* This construction atoms is a vsite and not a home atom */
951 fprintf(debug
, "Constructing atom %d of vsite atom %d is a vsite and non-home\n",
952 iatoms
[k
] + 1, a_mol
+ 1);
954 /* Find the vsite construction */
956 /* Check all interactions assigned to this atom */
957 j
= index
[iatoms
[k
]];
958 while (j
< index
[iatoms
[k
] + 1])
961 nral_r
= NRAL(ftype_r
);
962 if (interaction_function
[ftype_r
].flags
& IF_VSITE
)
964 /* Add this vsite (recursion) */
965 add_vsite(ga2la
, index
, rtil
, ftype_r
, nral_r
, FALSE
, -1,
966 a_gl
+ iatoms
[k
] - iatoms
[1], iatoms
[k
], rtil
.data() + j
, idef
);
968 j
+= 1 + nral_rt(ftype_r
);
975 /*! \brief Returns the squared distance between atoms \p i and \p j */
976 static real
dd_dist2(t_pbc
* pbc_null
, const rvec
* x
, const int i
, int j
)
982 pbc_dx_aiuc(pbc_null
, x
[i
], x
[j
], dx
);
986 rvec_sub(x
[i
], x
[j
], dx
);
992 /*! \brief Append t_idef structures 1 to nsrc in src to *dest */
993 static void combine_idef(InteractionDefinitions
* dest
, gmx::ArrayRef
<const thread_work_t
> src
)
997 for (ftype
= 0; ftype
< F_NRE
; ftype
++)
1000 for (gmx::index s
= 1; s
< src
.ssize(); s
++)
1002 n
+= src
[s
].idef
.il
[ftype
].size();
1006 for (gmx::index s
= 1; s
< src
.ssize(); s
++)
1008 dest
->il
[ftype
].append(src
[s
].idef
.il
[ftype
]);
1011 /* Position restraints need an additional treatment */
1012 if (ftype
== F_POSRES
|| ftype
== F_FBPOSRES
)
1014 int nposres
= dest
->il
[ftype
].size() / 2;
1015 std::vector
<t_iparams
>& iparams_dest
=
1016 (ftype
== F_POSRES
? dest
->iparams_posres
: dest
->iparams_fbposres
);
1018 /* Set nposres to the number of original position restraints in dest */
1019 for (gmx::index s
= 1; s
< src
.ssize(); s
++)
1021 nposres
-= src
[s
].idef
.il
[ftype
].size() / 2;
1024 for (gmx::index s
= 1; s
< src
.ssize(); s
++)
1026 const std::vector
<t_iparams
>& iparams_src
=
1027 (ftype
== F_POSRES
? src
[s
].idef
.iparams_posres
: src
[s
].idef
.iparams_fbposres
);
1028 iparams_dest
.insert(iparams_dest
.end(), iparams_src
.begin(), iparams_src
.end());
1030 /* Correct the indices into iparams_posres */
1031 for (int i
= 0; i
< src
[s
].idef
.il
[ftype
].size() / 2; i
++)
1033 /* Correct the index into iparams_posres */
1034 dest
->il
[ftype
].iatoms
[nposres
* 2] = nposres
;
1039 int(iparams_dest
.size()) == nposres
,
1040 "The number of parameters should match the number of restraints");
1046 /*! \brief Check and when available assign bonded interactions for local atom i
1048 static inline void check_assign_interactions_atom(int i
,
1052 int numAtomsInMolecule
,
1053 gmx::ArrayRef
<const int> index
,
1054 gmx::ArrayRef
<const int> rtil
,
1055 gmx_bool bInterMolInteractions
,
1058 const gmx_domdec_t
* dd
,
1059 const gmx_domdec_zones_t
* zones
,
1060 const gmx_molblock_t
* molb
,
1067 const t_iparams
* ip_in
,
1068 InteractionDefinitions
* idef
,
1073 gmx::ArrayRef
<const DDPairInteractionRanges
> iZones
= zones
->iZones
;
1078 t_iatom tiatoms
[1 + MAXATOMLIST
];
1080 const int ftype
= rtil
[j
++];
1081 auto iatoms
= gmx::constArrayRefFromArray(rtil
.data() + j
, rtil
.size() - j
);
1082 const int nral
= NRAL(ftype
);
1083 if (interaction_function
[ftype
].flags
& IF_VSITE
)
1085 assert(!bInterMolInteractions
);
1086 /* The vsite construction goes where the vsite itself is */
1089 add_vsite(*dd
->ga2la
, index
, rtil
, ftype
, nral
, TRUE
, i
, i_gl
, i_mol
, iatoms
.data(), idef
);
1097 tiatoms
[0] = iatoms
[0];
1101 assert(!bInterMolInteractions
);
1102 /* Assign single-body interactions to the home zone */
1107 if (ftype
== F_POSRES
)
1109 add_posres(mol
, i_mol
, numAtomsInMolecule
, molb
, tiatoms
, ip_in
, idef
);
1111 else if (ftype
== F_FBPOSRES
)
1113 add_fbposres(mol
, i_mol
, numAtomsInMolecule
, molb
, tiatoms
, ip_in
, idef
);
1123 /* This is a two-body interaction, we can assign
1124 * analogous to the non-bonded assignments.
1128 if (!bInterMolInteractions
)
1130 /* Get the global index using the offset in the molecule */
1131 k_gl
= i_gl
+ iatoms
[2] - i_mol
;
1137 if (const auto* entry
= dd
->ga2la
->find(k_gl
))
1139 int kz
= entry
->cell
;
1144 /* Check zone interaction assignments */
1145 bUse
= ((iz
< iZones
.ssize() && iz
<= kz
&& iZones
[iz
].jZoneRange
.isInRange(kz
))
1146 || (kz
< iZones
.ssize() && iz
> kz
&& iZones
[kz
].jZoneRange
.isInRange(iz
)));
1149 GMX_ASSERT(ftype
!= F_CONSTR
|| (iz
== 0 && kz
== 0),
1150 "Constraint assigned here should only involve home atoms");
1153 tiatoms
[2] = entry
->la
;
1154 /* If necessary check the cgcm distance */
1155 if (bRCheck2B
&& dd_dist2(pbc_null
, cg_cm
, tiatoms
[1], tiatoms
[2]) >= rc2
)
1168 /* Assign this multi-body bonded interaction to
1169 * the local node if we have all the atoms involved
1170 * (local or communicated) and the minimum zone shift
1171 * in each dimension is zero, for dimensions
1172 * with 2 DD cells an extra check may be necessary.
1174 ivec k_zero
, k_plus
;
1180 for (k
= 1; k
<= nral
&& bUse
; k
++)
1183 if (!bInterMolInteractions
)
1185 /* Get the global index using the offset in the molecule */
1186 k_gl
= i_gl
+ iatoms
[k
] - i_mol
;
1192 const auto* entry
= dd
->ga2la
->find(k_gl
);
1193 if (entry
== nullptr || entry
->cell
>= zones
->n
)
1195 /* We do not have this atom of this interaction
1196 * locally, or it comes from more than one cell
1205 tiatoms
[k
] = entry
->la
;
1206 for (d
= 0; d
< DIM
; d
++)
1208 if (zones
->shift
[entry
->cell
][d
] == 0)
1219 bUse
= (bUse
&& (k_zero
[XX
] != 0) && (k_zero
[YY
] != 0) && (k_zero
[ZZ
] != 0));
1224 for (d
= 0; (d
< DIM
&& bUse
); d
++)
1226 /* Check if the cg_cm distance falls within
1227 * the cut-off to avoid possible multiple
1228 * assignments of bonded interactions.
1230 if (rcheck
[d
] && k_plus
[d
]
1231 && dd_dist2(pbc_null
, cg_cm
, tiatoms
[k_zero
[d
]], tiatoms
[k_plus
[d
]]) >= rc2
)
1240 /* Add this interaction to the local topology */
1241 idef
->il
[ftype
].push_back(tiatoms
[0], nral
, tiatoms
+ 1);
1242 /* Sum so we can check in global_stat
1243 * if we have everything.
1245 if (bBCheck
|| !(interaction_function
[ftype
].flags
& IF_LIMZERO
))
1251 j
+= 1 + nral_rt(ftype
);
1255 /*! \brief This function looks up and assigns bonded interactions for zone iz.
1257 * With thread parallelizing each thread acts on a different atom range:
1258 * at_start to at_end.
1260 static int make_bondeds_zone(gmx_domdec_t
* dd
,
1261 const gmx_domdec_zones_t
* zones
,
1262 const std::vector
<gmx_molblock_t
>& molb
,
1269 const t_iparams
* ip_in
,
1270 InteractionDefinitions
* idef
,
1272 const gmx::Range
<int>& atomRange
)
1274 int mb
, mt
, mol
, i_mol
;
1276 gmx_reverse_top_t
* rt
;
1279 rt
= dd
->reverse_top
;
1281 bBCheck
= rt
->bBCheck
;
1285 for (int i
: atomRange
)
1287 /* Get the global atom number */
1288 const int i_gl
= dd
->globalAtomIndices
[i
];
1289 global_atomnr_to_moltype_ind(rt
, i_gl
, &mb
, &mt
, &mol
, &i_mol
);
1290 /* Check all intramolecular interactions assigned to this atom */
1291 gmx::ArrayRef
<const int> index
= rt
->ril_mt
[mt
].index
;
1292 gmx::ArrayRef
<const t_iatom
> rtil
= rt
->ril_mt
[mt
].il
;
1294 check_assign_interactions_atom(i
, i_gl
, mol
, i_mol
, rt
->ril_mt
[mt
].numAtomsInMolecule
,
1295 index
, rtil
, FALSE
, index
[i_mol
], index
[i_mol
+ 1], dd
,
1296 zones
, &molb
[mb
], bRCheckMB
, rcheck
, bRCheck2B
, rc2
,
1297 pbc_null
, cg_cm
, ip_in
, idef
, izone
, bBCheck
, &nbonded_local
);
1300 if (rt
->bIntermolecularInteractions
)
1302 /* Check all intermolecular interactions assigned to this atom */
1303 index
= rt
->ril_intermol
.index
;
1304 rtil
= rt
->ril_intermol
.il
;
1306 check_assign_interactions_atom(i
, i_gl
, mol
, i_mol
, rt
->ril_mt
[mt
].numAtomsInMolecule
,
1307 index
, rtil
, TRUE
, index
[i_gl
], index
[i_gl
+ 1], dd
, zones
,
1308 &molb
[mb
], bRCheckMB
, rcheck
, bRCheck2B
, rc2
, pbc_null
,
1309 cg_cm
, ip_in
, idef
, izone
, bBCheck
, &nbonded_local
);
1313 return nbonded_local
;
1316 /*! \brief Set the exclusion data for i-zone \p iz */
1317 static void make_exclusions_zone(gmx_domdec_t
* dd
,
1318 gmx_domdec_zones_t
* zones
,
1319 const std::vector
<gmx_moltype_t
>& moltype
,
1321 ListOfLists
<int>* lexcls
,
1325 const gmx::ArrayRef
<const int> intermolecularExclusionGroup
)
1327 const gmx_ga2la_t
& ga2la
= *dd
->ga2la
;
1329 const auto& jAtomRange
= zones
->iZones
[iz
].jAtomRange
;
1331 const gmx::index oldNumLists
= lexcls
->ssize();
1333 std::vector
<int> exclusionsForAtom
;
1334 for (int at
= at_start
; at
< at_end
; at
++)
1336 exclusionsForAtom
.clear();
1338 if (GET_CGINFO_EXCL_INTER(cginfo
[at
]))
1340 int a_gl
, mb
, mt
, mol
, a_mol
;
1342 /* Copy the exclusions from the global top */
1343 a_gl
= dd
->globalAtomIndices
[at
];
1344 global_atomnr_to_moltype_ind(dd
->reverse_top
, a_gl
, &mb
, &mt
, &mol
, &a_mol
);
1345 const auto excls
= moltype
[mt
].excls
[a_mol
];
1346 for (const int aj_mol
: excls
)
1348 if (const auto* jEntry
= ga2la
.find(a_gl
+ aj_mol
- a_mol
))
1350 /* This check is not necessary, but it can reduce
1351 * the number of exclusions in the list, which in turn
1352 * can speed up the pair list construction a bit.
1354 if (jAtomRange
.isInRange(jEntry
->la
))
1356 exclusionsForAtom
.push_back(jEntry
->la
);
1362 bool isExcludedAtom
= !intermolecularExclusionGroup
.empty()
1363 && std::find(intermolecularExclusionGroup
.begin(),
1364 intermolecularExclusionGroup
.end(), dd
->globalAtomIndices
[at
])
1365 != intermolecularExclusionGroup
.end();
1369 for (int qmAtomGlobalIndex
: intermolecularExclusionGroup
)
1371 if (const auto* entry
= dd
->ga2la
->find(qmAtomGlobalIndex
))
1373 exclusionsForAtom
.push_back(entry
->la
);
1378 /* Append the exclusions for this atom to the topology */
1379 lexcls
->pushBack(exclusionsForAtom
);
1383 lexcls
->ssize() - oldNumLists
== at_end
- at_start
,
1384 "The number of exclusion list should match the number of atoms in the range");
1387 /*! \brief Generate and store all required local bonded interactions in \p idef and local exclusions in \p lexcls */
1388 static int make_local_bondeds_excls(gmx_domdec_t
* dd
,
1389 gmx_domdec_zones_t
* zones
,
1390 const gmx_mtop_t
* mtop
,
1398 InteractionDefinitions
* idef
,
1399 ListOfLists
<int>* lexcls
,
1406 gmx_reverse_top_t
* rt
;
1408 if (dd
->reverse_top
->bInterAtomicInteractions
)
1410 nzone_bondeds
= zones
->n
;
1414 /* Only single charge group (or atom) molecules, so interactions don't
1415 * cross zone boundaries and we only need to assign in the home zone.
1420 /* We only use exclusions from i-zones to i- and j-zones */
1421 const int numIZonesForExclusions
= (dd
->haveExclusions
? zones
->iZones
.size() : 0);
1423 rt
= dd
->reverse_top
;
1427 /* Clear the counts */
1434 for (int izone
= 0; izone
< nzone_bondeds
; izone
++)
1436 cg0
= zones
->cg_range
[izone
];
1437 cg1
= zones
->cg_range
[izone
+ 1];
1439 const int numThreads
= rt
->th_work
.size();
1440 #pragma omp parallel for num_threads(numThreads) schedule(static)
1441 for (int thread
= 0; thread
< numThreads
; thread
++)
1446 InteractionDefinitions
* idef_t
;
1448 cg0t
= cg0
+ ((cg1
- cg0
) * thread
) / numThreads
;
1449 cg1t
= cg0
+ ((cg1
- cg0
) * (thread
+ 1)) / numThreads
;
1457 idef_t
= &rt
->th_work
[thread
].idef
;
1461 rt
->th_work
[thread
].nbonded
= make_bondeds_zone(
1462 dd
, zones
, mtop
->molblock
, bRCheckMB
, rcheck
, bRCheck2B
, rc2
, pbc_null
,
1463 cg_cm
, idef
->iparams
.data(), idef_t
, izone
, gmx::Range
<int>(cg0t
, cg1t
));
1465 if (izone
< numIZonesForExclusions
)
1467 ListOfLists
<int>* excl_t
;
1470 // Thread 0 stores exclusions directly in the final storage
1475 // Threads > 0 store in temporary storage, starting at list index 0
1476 excl_t
= &rt
->th_work
[thread
].excl
;
1480 /* No charge groups and no distance check required */
1481 make_exclusions_zone(dd
, zones
, mtop
->moltype
, cginfo
, excl_t
, izone
, cg0t
,
1482 cg1t
, mtop
->intermolecularExclusionGroup
);
1485 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR
1488 if (rt
->th_work
.size() > 1)
1490 combine_idef(idef
, rt
->th_work
);
1493 for (const thread_work_t
& th_work
: rt
->th_work
)
1495 nbonded_local
+= th_work
.nbonded
;
1498 if (izone
< numIZonesForExclusions
)
1500 for (std::size_t th
= 1; th
< rt
->th_work
.size(); th
++)
1502 lexcls
->appendListOfLists(rt
->th_work
[th
].excl
);
1504 for (const thread_work_t
& th_work
: rt
->th_work
)
1506 *excl_count
+= th_work
.excl_count
;
1513 fprintf(debug
, "We have %d exclusions, check count %d\n", lexcls
->numElements(), *excl_count
);
1516 return nbonded_local
;
1519 void dd_make_local_top(gmx_domdec_t
* dd
,
1520 gmx_domdec_zones_t
* zones
,
1527 const gmx_mtop_t
& mtop
,
1528 gmx_localtop_t
* ltop
)
1530 gmx_bool bRCheckMB
, bRCheck2B
;
1534 t_pbc pbc
, *pbc_null
= nullptr;
1538 fprintf(debug
, "Making local topology\n");
1544 if (dd
->reverse_top
->bInterAtomicInteractions
)
1546 /* We need to check to which cell bondeds should be assigned */
1547 rc
= dd_cutoff_twobody(dd
);
1550 fprintf(debug
, "Two-body bonded cut-off distance is %g\n", rc
);
1553 /* Should we check cg_cm distances when assigning bonded interactions? */
1554 for (d
= 0; d
< DIM
; d
++)
1557 /* Only need to check for dimensions where the part of the box
1558 * that is not communicated is smaller than the cut-off.
1560 if (d
< npbcdim
&& dd
->numCells
[d
] > 1
1561 && (dd
->numCells
[d
] - npulse
[d
]) * cellsize_min
[d
] < 2 * rc
)
1563 if (dd
->numCells
[d
] == 2)
1568 /* Check for interactions between two atoms,
1569 * where we can allow interactions up to the cut-off,
1570 * instead of up to the smallest cell dimension.
1576 fprintf(debug
, "dim %d cellmin %f bonded rcheck[%d] = %d, bRCheck2B = %s\n", d
,
1577 cellsize_min
[d
], d
, rcheck
[d
], gmx::boolToString(bRCheck2B
));
1580 if (bRCheckMB
|| bRCheck2B
)
1584 pbc_null
= set_pbc_dd(&pbc
, fr
->pbcType
, dd
->numCells
, TRUE
, box
);
1593 dd
->nbonded_local
= make_local_bondeds_excls(dd
, zones
, &mtop
, fr
->cginfo
.data(), bRCheckMB
,
1594 rcheck
, bRCheck2B
, rc
, pbc_null
, cgcm_or_x
,
1595 <op
->idef
, <op
->excls
, &nexcl
);
1597 /* The ilist is not sorted yet,
1598 * we can only do this when we have the charge arrays.
1600 ltop
->idef
.ilsort
= ilsortUNKNOWN
;
1603 void dd_sort_local_top(gmx_domdec_t
* dd
, const t_mdatoms
* mdatoms
, gmx_localtop_t
* ltop
)
1605 if (dd
->reverse_top
->ilsort
== ilsortNO_FE
)
1607 ltop
->idef
.ilsort
= ilsortNO_FE
;
1611 gmx_sort_ilist_fe(<op
->idef
, mdatoms
->chargeA
, mdatoms
->chargeB
);
1615 void dd_init_local_state(gmx_domdec_t
* dd
, const t_state
* state_global
, t_state
* state_local
)
1617 int buf
[NITEM_DD_INIT_LOCAL_STATE
];
1621 buf
[0] = state_global
->flags
;
1622 buf
[1] = state_global
->ngtc
;
1623 buf
[2] = state_global
->nnhpres
;
1624 buf
[3] = state_global
->nhchainlength
;
1625 buf
[4] = state_global
->dfhist
? state_global
->dfhist
->nlambda
: 0;
1627 dd_bcast(dd
, NITEM_DD_INIT_LOCAL_STATE
* sizeof(int), buf
);
1629 init_gtc_state(state_local
, buf
[1], buf
[2], buf
[3]);
1630 init_dfhist_state(state_local
, buf
[4]);
1631 state_local
->flags
= buf
[0];
1634 /*! \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 */
1635 static void check_link(t_blocka
* link
, int cg_gl
, int cg_gl_j
)
1641 for (k
= link
->index
[cg_gl
]; k
< link
->index
[cg_gl
+ 1]; k
++)
1643 GMX_RELEASE_ASSERT(link
->a
, "Inconsistent NULL pointer while making charge-group links");
1644 if (link
->a
[k
] == cg_gl_j
)
1651 GMX_RELEASE_ASSERT(link
->a
|| link
->index
[cg_gl
+ 1] + 1 > link
->nalloc_a
,
1652 "Inconsistent allocation of link");
1653 /* Add this charge group link */
1654 if (link
->index
[cg_gl
+ 1] + 1 > link
->nalloc_a
)
1656 link
->nalloc_a
= over_alloc_large(link
->index
[cg_gl
+ 1] + 1);
1657 srenew(link
->a
, link
->nalloc_a
);
1659 link
->a
[link
->index
[cg_gl
+ 1]] = cg_gl_j
;
1660 link
->index
[cg_gl
+ 1]++;
1664 t_blocka
* makeBondedLinks(const gmx_mtop_t
& mtop
, gmx::ArrayRef
<cginfo_mb_t
> cginfo_mb
)
1667 cginfo_mb_t
* cgi_mb
;
1669 /* For each atom make a list of other atoms in the system
1670 * that a linked to it via bonded interactions
1671 * which are also stored in reverse_top.
1674 reverse_ilist_t ril_intermol
;
1675 if (mtop
.bIntermolecularInteractions
)
1679 atoms
.nr
= mtop
.natoms
;
1680 atoms
.atom
= nullptr;
1682 GMX_RELEASE_ASSERT(mtop
.intermolecular_ilist
,
1683 "We should have an ilist when intermolecular interactions are on");
1685 make_reverse_ilist(*mtop
.intermolecular_ilist
, &atoms
, FALSE
, FALSE
, FALSE
, TRUE
, &ril_intermol
);
1689 snew(link
->index
, mtop
.natoms
+ 1);
1696 for (size_t mb
= 0; mb
< mtop
.molblock
.size(); mb
++)
1698 const gmx_molblock_t
& molb
= mtop
.molblock
[mb
];
1703 const gmx_moltype_t
& molt
= mtop
.moltype
[molb
.type
];
1704 /* Make a reverse ilist in which the interactions are linked
1705 * to all atoms, not only the first atom as in gmx_reverse_top.
1706 * The constraints are discarded here.
1708 reverse_ilist_t ril
;
1709 make_reverse_ilist(molt
.ilist
, &molt
.atoms
, FALSE
, FALSE
, FALSE
, TRUE
, &ril
);
1711 cgi_mb
= &cginfo_mb
[mb
];
1714 for (mol
= 0; mol
< (mtop
.bIntermolecularInteractions
? molb
.nmol
: 1); mol
++)
1716 for (int a
= 0; a
< molt
.atoms
.nr
; a
++)
1718 int cg_gl
= cg_offset
+ a
;
1719 link
->index
[cg_gl
+ 1] = link
->index
[cg_gl
];
1720 int i
= ril
.index
[a
];
1721 while (i
< ril
.index
[a
+ 1])
1723 int ftype
= ril
.il
[i
++];
1724 int nral
= NRAL(ftype
);
1725 /* Skip the ifunc index */
1727 for (int j
= 0; j
< nral
; j
++)
1729 int aj
= ril
.il
[i
+ j
];
1732 check_link(link
, cg_gl
, cg_offset
+ aj
);
1735 i
+= nral_rt(ftype
);
1738 if (mtop
.bIntermolecularInteractions
)
1740 int i
= ril_intermol
.index
[cg_gl
];
1741 while (i
< ril_intermol
.index
[cg_gl
+ 1])
1743 int ftype
= ril_intermol
.il
[i
++];
1744 int nral
= NRAL(ftype
);
1745 /* Skip the ifunc index */
1747 for (int j
= 0; j
< nral
; j
++)
1749 /* Here we assume we have no charge groups;
1750 * this has been checked above.
1752 int aj
= ril_intermol
.il
[i
+ j
];
1753 check_link(link
, cg_gl
, aj
);
1755 i
+= nral_rt(ftype
);
1758 if (link
->index
[cg_gl
+ 1] - link
->index
[cg_gl
] > 0)
1760 SET_CGINFO_BOND_INTER(cgi_mb
->cginfo
[a
]);
1765 cg_offset
+= molt
.atoms
.nr
;
1767 int nlink_mol
= link
->index
[cg_offset
] - link
->index
[cg_offset
- molt
.atoms
.nr
];
1771 fprintf(debug
, "molecule type '%s' %d atoms has %d atom links through bonded interac.\n",
1772 *molt
.name
, molt
.atoms
.nr
, nlink_mol
);
1775 if (molb
.nmol
> mol
)
1777 /* Copy the data for the rest of the molecules in this block */
1778 link
->nalloc_a
+= (molb
.nmol
- mol
) * nlink_mol
;
1779 srenew(link
->a
, link
->nalloc_a
);
1780 for (; mol
< molb
.nmol
; mol
++)
1782 for (int a
= 0; a
< molt
.atoms
.nr
; a
++)
1784 int cg_gl
= cg_offset
+ a
;
1785 link
->index
[cg_gl
+ 1] = link
->index
[cg_gl
+ 1 - molt
.atoms
.nr
] + nlink_mol
;
1786 for (int j
= link
->index
[cg_gl
]; j
< link
->index
[cg_gl
+ 1]; j
++)
1788 link
->a
[j
] = link
->a
[j
- nlink_mol
] + molt
.atoms
.nr
;
1790 if (link
->index
[cg_gl
+ 1] - link
->index
[cg_gl
] > 0
1791 && cg_gl
- cgi_mb
->cg_start
< cgi_mb
->cg_mod
)
1793 SET_CGINFO_BOND_INTER(cgi_mb
->cginfo
[cg_gl
- cgi_mb
->cg_start
]);
1797 cg_offset
+= molt
.atoms
.nr
;
1804 fprintf(debug
, "Of the %d atoms %d are linked via bonded interactions\n", mtop
.natoms
, ncgi
);
1816 } bonded_distance_t
;
1818 /*! \brief Compare distance^2 \p r2 against the distance in \p bd and if larger store it along with \p ftype and atom indices \p a1 and \p a2 */
1819 static void update_max_bonded_distance(real r2
, int ftype
, int a1
, int a2
, bonded_distance_t
* bd
)
1830 /*! \brief Set the distance, function type and atom indices for the longest distance between atoms of molecule type \p molt for two-body and multi-body bonded interactions */
1831 static void bonded_cg_distance_mol(const gmx_moltype_t
* molt
,
1834 ArrayRef
<const RVec
> x
,
1835 bonded_distance_t
* bd_2b
,
1836 bonded_distance_t
* bd_mb
)
1838 for (int ftype
= 0; ftype
< F_NRE
; ftype
++)
1840 if (dd_check_ftype(ftype
, bBCheck
, FALSE
, FALSE
))
1842 const auto& il
= molt
->ilist
[ftype
];
1843 int nral
= NRAL(ftype
);
1846 for (int i
= 0; i
< il
.size(); i
+= 1 + nral
)
1848 for (int ai
= 0; ai
< nral
; ai
++)
1850 int atomI
= il
.iatoms
[i
+ 1 + ai
];
1851 for (int aj
= ai
+ 1; aj
< nral
; aj
++)
1853 int atomJ
= il
.iatoms
[i
+ 1 + aj
];
1856 real rij2
= distance2(x
[atomI
], x
[atomJ
]);
1858 update_max_bonded_distance(rij2
, ftype
, atomI
, atomJ
,
1859 (nral
== 2) ? bd_2b
: bd_mb
);
1869 const auto& excls
= molt
->excls
;
1870 for (gmx::index ai
= 0; ai
< excls
.ssize(); ai
++)
1872 for (const int aj
: excls
[ai
])
1876 real rij2
= distance2(x
[ai
], x
[aj
]);
1878 /* There is no function type for exclusions, use -1 */
1879 update_max_bonded_distance(rij2
, -1, ai
, aj
, bd_2b
);
1886 /*! \brief Set the distance, function type and atom indices for the longest atom distance involved in intermolecular interactions for two-body and multi-body bonded interactions */
1887 static void bonded_distance_intermol(const InteractionLists
& ilists_intermol
,
1889 ArrayRef
<const RVec
> x
,
1892 bonded_distance_t
* bd_2b
,
1893 bonded_distance_t
* bd_mb
)
1897 set_pbc(&pbc
, pbcType
, box
);
1899 for (int ftype
= 0; ftype
< F_NRE
; ftype
++)
1901 if (dd_check_ftype(ftype
, bBCheck
, FALSE
, FALSE
))
1903 const auto& il
= ilists_intermol
[ftype
];
1904 int nral
= NRAL(ftype
);
1906 /* No nral>1 check here, since intermol interactions always
1907 * have nral>=2 (and the code is also correct for nral=1).
1909 for (int i
= 0; i
< il
.size(); i
+= 1 + nral
)
1911 for (int ai
= 0; ai
< nral
; ai
++)
1913 int atom_i
= il
.iatoms
[i
+ 1 + ai
];
1915 for (int aj
= ai
+ 1; aj
< nral
; aj
++)
1920 int atom_j
= il
.iatoms
[i
+ 1 + aj
];
1922 pbc_dx(&pbc
, x
[atom_i
], x
[atom_j
], dx
);
1926 update_max_bonded_distance(rij2
, ftype
, atom_i
, atom_j
, (nral
== 2) ? bd_2b
: bd_mb
);
1934 //! Returns whether \p molt has at least one virtual site
1935 static bool moltypeHasVsite(const gmx_moltype_t
& molt
)
1937 bool hasVsite
= false;
1938 for (int i
= 0; i
< F_NRE
; i
++)
1940 if ((interaction_function
[i
].flags
& IF_VSITE
) && !molt
.ilist
[i
].empty())
1949 //! Returns coordinates not broken over PBC for a molecule
1950 static void getWholeMoleculeCoordinates(const gmx_moltype_t
* molt
,
1951 const gmx_ffparams_t
* ffparams
,
1955 ArrayRef
<const RVec
> x
,
1960 if (pbcType
!= PbcType::No
)
1962 mk_mshift(nullptr, graph
, pbcType
, box
, as_rvec_array(x
.data()));
1964 shift_x(graph
, box
, as_rvec_array(x
.data()), as_rvec_array(xs
.data()));
1965 /* By doing an extra mk_mshift the molecules that are broken
1966 * because they were e.g. imported from another software
1967 * will be made whole again. Such are the healing powers
1970 mk_mshift(nullptr, graph
, pbcType
, box
, as_rvec_array(xs
.data()));
1974 /* We copy the coordinates so the original coordinates remain
1975 * unchanged, just to be 100% sure that we do not affect
1976 * binary reproducibility of simulations.
1979 for (i
= 0; i
< n
; i
++)
1981 copy_rvec(x
[i
], xs
[i
]);
1985 if (moltypeHasVsite(*molt
))
1987 gmx::constructVirtualSites(xs
, ffparams
->iparams
, molt
->ilist
);
1991 void dd_bonded_cg_distance(const gmx::MDLogger
& mdlog
,
1992 const gmx_mtop_t
* mtop
,
1993 const t_inputrec
* ir
,
1994 ArrayRef
<const RVec
> x
,
2000 gmx_bool bExclRequired
;
2002 bonded_distance_t bd_2b
= { 0, -1, -1, -1 };
2003 bonded_distance_t bd_mb
= { 0, -1, -1, -1 };
2005 bExclRequired
= inputrecExclForces(ir
);
2010 for (const gmx_molblock_t
& molb
: mtop
->molblock
)
2012 const gmx_moltype_t
& molt
= mtop
->moltype
[molb
.type
];
2013 if (molt
.atoms
.nr
== 1 || molb
.nmol
== 0)
2015 at_offset
+= molb
.nmol
* molt
.atoms
.nr
;
2020 if (ir
->pbcType
!= PbcType::No
)
2022 graph
= mk_graph_moltype(molt
);
2025 std::vector
<RVec
> xs(molt
.atoms
.nr
);
2026 for (int mol
= 0; mol
< molb
.nmol
; mol
++)
2028 getWholeMoleculeCoordinates(&molt
, &mtop
->ffparams
, ir
->pbcType
, &graph
, box
,
2029 x
.subArray(at_offset
, molt
.atoms
.nr
), xs
);
2031 bonded_distance_t bd_mol_2b
= { 0, -1, -1, -1 };
2032 bonded_distance_t bd_mol_mb
= { 0, -1, -1, -1 };
2034 bonded_cg_distance_mol(&molt
, bBCheck
, bExclRequired
, xs
, &bd_mol_2b
, &bd_mol_mb
);
2036 /* Process the mol data adding the atom index offset */
2037 update_max_bonded_distance(bd_mol_2b
.r2
, bd_mol_2b
.ftype
, at_offset
+ bd_mol_2b
.a1
,
2038 at_offset
+ bd_mol_2b
.a2
, &bd_2b
);
2039 update_max_bonded_distance(bd_mol_mb
.r2
, bd_mol_mb
.ftype
, at_offset
+ bd_mol_mb
.a1
,
2040 at_offset
+ bd_mol_mb
.a2
, &bd_mb
);
2042 at_offset
+= molt
.atoms
.nr
;
2047 if (mtop
->bIntermolecularInteractions
)
2049 GMX_RELEASE_ASSERT(mtop
->intermolecular_ilist
,
2050 "We should have an ilist when intermolecular interactions are on");
2052 bonded_distance_intermol(*mtop
->intermolecular_ilist
, bBCheck
, x
, ir
->pbcType
, box
, &bd_2b
, &bd_mb
);
2055 *r_2b
= sqrt(bd_2b
.r2
);
2056 *r_mb
= sqrt(bd_mb
.r2
);
2058 if (*r_2b
> 0 || *r_mb
> 0)
2060 GMX_LOG(mdlog
.info
).appendText("Initial maximum distances in bonded interactions:");
2064 .appendTextFormatted(
2065 " two-body bonded interactions: %5.3f nm, %s, atoms %d %d", *r_2b
,
2066 (bd_2b
.ftype
>= 0) ? interaction_function
[bd_2b
.ftype
].longname
: "Exclusion",
2067 bd_2b
.a1
+ 1, bd_2b
.a2
+ 1);
2072 .appendTextFormatted(
2073 " multi-body bonded interactions: %5.3f nm, %s, atoms %d %d", *r_mb
,
2074 interaction_function
[bd_mb
.ftype
].longname
, bd_mb
.a1
+ 1, bd_mb
.a2
+ 1);