2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2008,2009,2010, The GROMACS development team.
5 * Copyright (c) 2012,2013,2014,2015,2016 The GROMACS development team.
6 * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
39 #include "mtop_util.h"
47 #include "gromacs/math/vectypes.h"
48 #include "gromacs/mdtypes/atominfo.h"
49 #include "gromacs/topology/atoms.h"
50 #include "gromacs/topology/block.h"
51 #include "gromacs/topology/exclusionblocks.h"
52 #include "gromacs/topology/idef.h"
53 #include "gromacs/topology/ifunc.h"
54 #include "gromacs/topology/topology.h"
55 #include "gromacs/topology/topsort.h"
56 #include "gromacs/utility/arrayref.h"
57 #include "gromacs/utility/enumerationhelpers.h"
58 #include "gromacs/utility/fatalerror.h"
59 #include "gromacs/utility/real.h"
60 #include "gromacs/utility/smalloc.h"
62 void gmx_mtop_count_atomtypes(const gmx_mtop_t& mtop, int state, int typecount[])
64 for (int i = 0; i < mtop.ffparams.atnr; ++i)
68 for (const gmx_molblock_t& molb : mtop.molblock)
70 const t_atoms& atoms = mtop.moltype[molb.type].atoms;
71 for (int i = 0; i < atoms.nr; ++i)
73 const int tpi = (state == 0) ? atoms.atom[i].type : atoms.atom[i].typeB;
74 typecount[tpi] += molb.nmol;
79 int gmx_mtop_num_molecules(const gmx_mtop_t& mtop)
82 for (const gmx_molblock_t& molb : mtop.molblock)
84 numMolecules += molb.nmol;
89 int gmx_mtop_nres(const gmx_mtop_t& mtop)
92 for (const gmx_molblock_t& molb : mtop.molblock)
94 nres += molb.nmol * mtop.moltype[molb.type].atoms.nres;
99 AtomIterator::AtomIterator(const gmx_mtop_t& mtop, int globalAtomNumber) :
102 atoms_(&mtop.moltype[mtop.molblock[0].type].atoms),
104 highestResidueNumber_(mtop.maxResNumberNotRenumbered()),
106 globalAtomNumber_(globalAtomNumber)
108 GMX_ASSERT(globalAtomNumber == 0 || globalAtomNumber == mtop.natoms,
109 "Starting at other atoms not implemented yet");
112 AtomIterator& AtomIterator::operator++()
117 if (localAtomNumber_ >= atoms_->nr)
119 if (atoms_->nres <= mtop_->maxResiduesPerMoleculeToTriggerRenumber())
121 /* Single residue molecule, increase the count with one */
122 highestResidueNumber_ += atoms_->nres;
125 localAtomNumber_ = 0;
126 if (currentMolecule_ >= mtop_->molblock[mblock_].nmol)
129 if (mblock_ >= mtop_->molblock.size())
133 atoms_ = &mtop_->moltype[mtop_->molblock[mblock_].type].atoms;
134 currentMolecule_ = 0;
140 bool AtomIterator::operator==(const AtomIterator& o) const
142 return mtop_ == o.mtop_ && globalAtomNumber_ == o.globalAtomNumber_;
145 const t_atom& AtomProxy::atom() const
147 return it_->atoms_->atom[it_->localAtomNumber_];
150 int AtomProxy::globalAtomNumber() const
152 return it_->globalAtomNumber_;
155 const char* AtomProxy::atomName() const
157 return *(it_->atoms_->atomname[it_->localAtomNumber_]);
160 const char* AtomProxy::residueName() const
162 int residueIndexInMolecule = it_->atoms_->atom[it_->localAtomNumber_].resind;
163 return *(it_->atoms_->resinfo[residueIndexInMolecule].name);
166 int AtomProxy::residueNumber() const
168 int residueIndexInMolecule = it_->atoms_->atom[it_->localAtomNumber_].resind;
169 if (it_->atoms_->nres <= it_->mtop_->maxResiduesPerMoleculeToTriggerRenumber())
171 return it_->highestResidueNumber_ + 1 + residueIndexInMolecule;
175 return it_->atoms_->resinfo[residueIndexInMolecule].nr;
179 const gmx_moltype_t& AtomProxy::moleculeType() const
181 return it_->mtop_->moltype[it_->mtop_->molblock[it_->mblock_].type];
184 int AtomProxy::atomNumberInMol() const
186 return it_->localAtomNumber_;
189 struct gmx_mtop_atomloop_block
191 const gmx_mtop_t* mtop;
193 const t_atoms* atoms;
197 gmx_mtop_atomloop_block_t gmx_mtop_atomloop_block_init(const gmx_mtop_t& mtop)
199 struct gmx_mtop_atomloop_block* aloop = nullptr;
205 aloop->atoms = &mtop.moltype[mtop.molblock[aloop->mblock].type].atoms;
206 aloop->at_local = -1;
211 static void gmx_mtop_atomloop_block_destroy(gmx_mtop_atomloop_block_t aloop)
216 gmx_bool gmx_mtop_atomloop_block_next(gmx_mtop_atomloop_block_t aloop, const t_atom** atom, int* nmol)
218 if (aloop == nullptr)
220 gmx_incons("gmx_mtop_atomloop_all_next called without calling gmx_mtop_atomloop_all_init");
225 if (aloop->at_local >= aloop->atoms->nr)
228 if (aloop->mblock >= aloop->mtop->molblock.size())
230 gmx_mtop_atomloop_block_destroy(aloop);
233 aloop->atoms = &aloop->mtop->moltype[aloop->mtop->molblock[aloop->mblock].type].atoms;
237 *atom = &aloop->atoms->atom[aloop->at_local];
238 *nmol = aloop->mtop->molblock[aloop->mblock].nmol;
243 IListIterator::IListIterator(const gmx_mtop_t& mtop, size_t molblock) :
244 mtop_(&mtop), mblock_(molblock)
248 IListIterator& IListIterator::operator++()
254 bool IListIterator::operator==(const IListIterator& o) const
256 return mtop_ == o.mtop_ && mblock_ == o.mblock_;
259 const InteractionLists& IListProxy::list() const
261 // one past the end means we want to take the
262 // intermolecular list instead.
263 if (it_->mblock_ == it_->mtop_->molblock.size())
265 return *it_->mtop_->intermolecular_ilist;
269 return it_->mtop_->moltype[it_->mtop_->molblock[it_->mblock_].type].ilist;
273 int IListProxy::nmol() const
275 // one past the end means we want to take the
276 // intermolecular list instead.
277 if (it_->mblock_ == it_->mtop_->molblock.size())
283 return it_->mtop_->molblock[it_->mblock_].nmol;
287 IListRange::IListRange(const gmx_mtop_t& mtop) : begin_(mtop), end_(mtop, mtop.molblock.size())
289 if (mtop.bIntermolecularInteractions)
291 end_ = IListIterator(mtop, mtop.molblock.size() + 1);
295 int gmx_mtop_ftype_count(const gmx_mtop_t& mtop, int ftype)
299 for (const IListProxy il : IListRange(mtop))
301 n += il.nmol() * il.list()[ftype].size() / (1 + NRAL(ftype));
304 if (mtop.bIntermolecularInteractions)
306 n += (*mtop.intermolecular_ilist)[ftype].size() / (1 + NRAL(ftype));
312 int gmx_mtop_interaction_count(const gmx_mtop_t& mtop, const int unsigned if_flags)
316 for (const IListProxy il : IListRange(mtop))
318 for (int ftype = 0; ftype < F_NRE; ftype++)
320 if ((interaction_function[ftype].flags & if_flags) == if_flags)
322 n += il.nmol() * il.list()[ftype].size() / (1 + NRAL(ftype));
327 if (mtop.bIntermolecularInteractions)
329 for (int ftype = 0; ftype < F_NRE; ftype++)
331 if ((interaction_function[ftype].flags & if_flags) == if_flags)
333 n += (*mtop.intermolecular_ilist)[ftype].size() / (1 + NRAL(ftype));
341 gmx::EnumerationArray<ParticleType, int> gmx_mtop_particletype_count(const gmx_mtop_t& mtop)
343 gmx::EnumerationArray<ParticleType, int> count = { { 0 } };
345 for (const auto& molblock : mtop.molblock)
347 const t_atoms& atoms = mtop.moltype[molblock.type].atoms;
348 for (int a = 0; a < atoms.nr; a++)
350 count[atoms.atom[a].ptype] += molblock.nmol;
357 static void atomcat(t_atoms* dest, const t_atoms* src, int copies, int maxres_renum, int* maxresnr)
359 int i = 0, j = 0, l = 0, size = 0;
361 int destnr = dest->nr;
365 dest->haveMass = src->haveMass;
366 dest->haveType = src->haveType;
367 dest->haveCharge = src->haveCharge;
368 dest->haveBState = src->haveBState;
369 dest->havePdbInfo = src->havePdbInfo;
373 dest->haveMass = dest->haveMass && src->haveMass;
374 dest->haveType = dest->haveType && src->haveType;
375 dest->haveCharge = dest->haveCharge && src->haveCharge;
376 dest->haveBState = dest->haveBState && src->haveBState;
377 dest->havePdbInfo = dest->havePdbInfo && src->havePdbInfo;
382 size = destnr + copies * srcnr;
383 srenew(dest->atom, size);
384 srenew(dest->atomname, size);
387 srenew(dest->atomtype, size);
388 if (dest->haveBState)
390 srenew(dest->atomtypeB, size);
393 if (dest->havePdbInfo)
395 srenew(dest->pdbinfo, size);
400 size = dest->nres + copies * src->nres;
401 srenew(dest->resinfo, size);
404 /* residue information */
405 for (l = dest->nres, j = 0; (j < copies); j++, l += src->nres)
407 memcpy(reinterpret_cast<char*>(&(dest->resinfo[l])),
408 reinterpret_cast<char*>(&(src->resinfo[0])),
409 static_cast<size_t>(src->nres * sizeof(src->resinfo[0])));
412 for (l = destnr, j = 0; (j < copies); j++, l += srcnr)
414 memcpy(reinterpret_cast<char*>(&(dest->atom[l])),
415 reinterpret_cast<char*>(&(src->atom[0])),
416 static_cast<size_t>(srcnr * sizeof(src->atom[0])));
417 memcpy(reinterpret_cast<char*>(&(dest->atomname[l])),
418 reinterpret_cast<char*>(&(src->atomname[0])),
419 static_cast<size_t>(srcnr * sizeof(src->atomname[0])));
422 memcpy(reinterpret_cast<char*>(&(dest->atomtype[l])),
423 reinterpret_cast<char*>(&(src->atomtype[0])),
424 static_cast<size_t>(srcnr * sizeof(src->atomtype[0])));
425 if (dest->haveBState)
427 memcpy(reinterpret_cast<char*>(&(dest->atomtypeB[l])),
428 reinterpret_cast<char*>(&(src->atomtypeB[0])),
429 static_cast<size_t>(srcnr * sizeof(src->atomtypeB[0])));
432 if (dest->havePdbInfo)
434 memcpy(reinterpret_cast<char*>(&(dest->pdbinfo[l])),
435 reinterpret_cast<char*>(&(src->pdbinfo[0])),
436 static_cast<size_t>(srcnr * sizeof(src->pdbinfo[0])));
440 /* Increment residue indices */
441 for (l = destnr, j = 0; (j < copies); j++)
443 for (i = 0; (i < srcnr); i++, l++)
445 dest->atom[l].resind = dest->nres + j * src->nres + src->atom[i].resind;
449 if (src->nres <= maxres_renum)
451 /* Single residue molecule, continue counting residues */
452 for (j = 0; (j < copies); j++)
454 for (l = 0; l < src->nres; l++)
457 dest->resinfo[dest->nres + j * src->nres + l].nr = *maxresnr;
462 dest->nres += copies * src->nres;
463 dest->nr += copies * src->nr;
466 t_atoms gmx_mtop_global_atoms(const gmx_mtop_t& mtop)
470 init_t_atoms(&atoms, 0, FALSE);
472 int maxresnr = mtop.maxResNumberNotRenumbered();
473 for (const gmx_molblock_t& molb : mtop.molblock)
476 &mtop.moltype[molb.type].atoms,
478 mtop.maxResiduesPerMoleculeToTriggerRenumber(),
486 * The cat routines below are old code from src/kernel/topcat.c
489 static void ilistcat(int ftype, InteractionList* dest, const InteractionList& src, int copies, int dnum, int snum)
491 const int nral = NRAL(ftype);
493 size_t destIndex = dest->iatoms.size();
494 dest->iatoms.resize(dest->iatoms.size() + copies * src.size());
496 for (int c = 0; c < copies; c++)
498 for (int i = 0; i < src.size();)
500 dest->iatoms[destIndex++] = src.iatoms[i++];
501 for (int a = 0; a < nral; a++)
503 dest->iatoms[destIndex++] = dnum + src.iatoms[i++];
510 static void ilistcat(int ftype, t_ilist* dest, const InteractionList& src, int copies, int dnum, int snum)
512 const int nral = NRAL(ftype);
514 dest->nalloc = dest->nr + copies * src.size();
515 srenew(dest->iatoms, dest->nalloc);
517 for (int c = 0; c < copies; c++)
519 for (int i = 0; i < src.size();)
521 dest->iatoms[dest->nr++] = src.iatoms[i++];
522 for (int a = 0; a < nral; a++)
524 dest->iatoms[dest->nr++] = dnum + src.iatoms[i++];
531 static const t_iparams& getIparams(const InteractionDefinitions& idef, const int index)
533 return idef.iparams[index];
536 static const t_iparams& getIparams(const t_idef& idef, const int index)
538 return idef.iparams[index];
541 static void resizeIParams(std::vector<t_iparams>* iparams, const int newSize)
543 iparams->resize(newSize);
546 static void resizeIParams(t_iparams** iparams, const int newSize)
548 srenew(*iparams, newSize);
551 template<typename IdefType>
552 static void set_posres_params(IdefType* idef, const gmx_molblock_t* molb, int i0, int a_offset)
554 auto* il = &idef->il[F_POSRES];
555 int i1 = il->size() / 2;
556 resizeIParams(&idef->iparams_posres, i1);
557 for (int i = i0; i < i1; i++)
559 t_iparams* ip = &idef->iparams_posres[i];
560 /* Copy the force constants */
561 *ip = getIparams(*idef, il->iatoms[i * 2]);
562 int a_molb = il->iatoms[i * 2 + 1] - a_offset;
563 if (molb->posres_xA.empty())
565 gmx_incons("Position restraint coordinates are missing");
567 ip->posres.pos0A[XX] = molb->posres_xA[a_molb][XX];
568 ip->posres.pos0A[YY] = molb->posres_xA[a_molb][YY];
569 ip->posres.pos0A[ZZ] = molb->posres_xA[a_molb][ZZ];
570 if (!molb->posres_xB.empty())
572 ip->posres.pos0B[XX] = molb->posres_xB[a_molb][XX];
573 ip->posres.pos0B[YY] = molb->posres_xB[a_molb][YY];
574 ip->posres.pos0B[ZZ] = molb->posres_xB[a_molb][ZZ];
578 ip->posres.pos0B[XX] = ip->posres.pos0A[XX];
579 ip->posres.pos0B[YY] = ip->posres.pos0A[YY];
580 ip->posres.pos0B[ZZ] = ip->posres.pos0A[ZZ];
582 /* Set the parameter index for idef->iparams_posre */
583 il->iatoms[i * 2] = i;
587 template<typename IdefType>
588 static void set_fbposres_params(IdefType* idef, const gmx_molblock_t* molb, int i0, int a_offset)
590 auto* il = &idef->il[F_FBPOSRES];
591 int i1 = il->size() / 2;
592 resizeIParams(&idef->iparams_fbposres, i1);
593 for (int i = i0; i < i1; i++)
595 t_iparams* ip = &idef->iparams_fbposres[i];
596 /* Copy the force constants */
597 *ip = getIparams(*idef, il->iatoms[i * 2]);
598 int a_molb = il->iatoms[i * 2 + 1] - a_offset;
599 if (molb->posres_xA.empty())
601 gmx_incons("Position restraint coordinates are missing");
603 /* Take flat-bottom posres reference from normal position restraints */
604 ip->fbposres.pos0[XX] = molb->posres_xA[a_molb][XX];
605 ip->fbposres.pos0[YY] = molb->posres_xA[a_molb][YY];
606 ip->fbposres.pos0[ZZ] = molb->posres_xA[a_molb][ZZ];
607 /* Note: no B-type for flat-bottom posres */
609 /* Set the parameter index for idef->iparams_posre */
610 il->iatoms[i * 2] = i;
614 /*! \brief Copy parameters to idef structure from mtop.
616 * Makes a deep copy of the force field parameters data structure from a gmx_mtop_t.
617 * Used to initialize legacy topology types.
619 * \param[in] mtop Reference to input mtop.
620 * \param[in] idef Pointer to idef to populate.
622 static void copyFFParametersFromMtop(const gmx_mtop_t& mtop, t_idef* idef)
624 const gmx_ffparams_t* ffp = &mtop.ffparams;
626 idef->ntypes = ffp->numTypes();
627 idef->atnr = ffp->atnr;
628 /* we can no longer copy the pointers to the mtop members,
629 * because they will become invalid as soon as mtop gets free'd.
630 * We also need to make sure to only operate on valid data!
633 if (!ffp->functype.empty())
635 snew(idef->functype, ffp->functype.size());
636 std::copy(ffp->functype.data(), ffp->functype.data() + ffp->functype.size(), idef->functype);
640 idef->functype = nullptr;
642 if (!ffp->iparams.empty())
644 snew(idef->iparams, ffp->iparams.size());
645 std::copy(ffp->iparams.data(), ffp->iparams.data() + ffp->iparams.size(), idef->iparams);
649 idef->iparams = nullptr;
651 idef->iparams_posres = nullptr;
652 idef->iparams_fbposres = nullptr;
653 idef->fudgeQQ = ffp->fudgeQQ;
654 idef->ilsort = ilsortUNKNOWN;
657 /*! \brief Copy idef structure from mtop.
659 * Makes a deep copy of an idef data structure from a gmx_mtop_t.
660 * Used to initialize legacy topology types.
662 * \param[in] mtop Reference to input mtop.
663 * \param[in] idef Pointer to idef to populate.
664 * \param[in] mergeConstr Decide if constraints will be merged.
666 template<typename IdefType>
667 static void copyIListsFromMtop(const gmx_mtop_t& mtop, IdefType* idef, bool mergeConstr)
670 for (const gmx_molblock_t& molb : mtop.molblock)
672 const gmx_moltype_t& molt = mtop.moltype[molb.type];
674 int srcnr = molt.atoms.nr;
677 int nposre_old = idef->il[F_POSRES].size();
678 int nfbposre_old = idef->il[F_FBPOSRES].size();
679 for (int ftype = 0; ftype < F_NRE; ftype++)
681 if (mergeConstr && ftype == F_CONSTR && !molt.ilist[F_CONSTRNC].empty())
683 /* Merge all constrains into one ilist.
684 * This simplifies the constraint code.
686 for (int mol = 0; mol < molb.nmol; mol++)
688 ilistcat(ftype, &idef->il[F_CONSTR], molt.ilist[F_CONSTR], 1, destnr + mol * srcnr, srcnr);
689 ilistcat(ftype, &idef->il[F_CONSTR], molt.ilist[F_CONSTRNC], 1, destnr + mol * srcnr, srcnr);
692 else if (!(mergeConstr && ftype == F_CONSTRNC))
694 ilistcat(ftype, &idef->il[ftype], molt.ilist[ftype], molb.nmol, destnr, srcnr);
697 if (idef->il[F_POSRES].size() > nposre_old)
699 /* Executing this line line stops gmxdump -sys working
700 * correctly. I'm not aware there's an elegant fix. */
701 set_posres_params(idef, &molb, nposre_old / 2, natoms);
703 if (idef->il[F_FBPOSRES].size() > nfbposre_old)
705 set_fbposres_params(idef, &molb, nfbposre_old / 2, natoms);
708 natoms += molb.nmol * srcnr;
711 if (mtop.bIntermolecularInteractions)
713 for (int ftype = 0; ftype < F_NRE; ftype++)
715 ilistcat(ftype, &idef->il[ftype], (*mtop.intermolecular_ilist)[ftype], 1, 0, mtop.natoms);
719 // We have not (yet) sorted free-energy interactions to the end of the ilists
720 idef->ilsort = ilsortNO_FE;
723 /*! \brief Copy atomtypes from mtop
725 * Makes a deep copy of t_atomtypes from gmx_mtop_t.
726 * Used to initialize legacy topology types.
728 * \param[in] mtop Reference to input mtop.
729 * \param[in] atomtypes Pointer to atomtypes to populate.
731 static void copyAtomtypesFromMtop(const gmx_mtop_t& mtop, t_atomtypes* atomtypes)
733 atomtypes->nr = mtop.atomtypes.nr;
734 if (mtop.atomtypes.atomnumber)
736 snew(atomtypes->atomnumber, mtop.atomtypes.nr);
737 std::copy(mtop.atomtypes.atomnumber,
738 mtop.atomtypes.atomnumber + mtop.atomtypes.nr,
739 atomtypes->atomnumber);
743 atomtypes->atomnumber = nullptr;
747 /*! \brief Generate a single list of lists of exclusions for the whole system
749 * \param[in] mtop Reference to input mtop.
751 static gmx::ListOfLists<int> globalExclusionLists(const gmx_mtop_t& mtop)
753 gmx::ListOfLists<int> excls;
756 for (const gmx_molblock_t& molb : mtop.molblock)
758 const gmx_moltype_t& molt = mtop.moltype[molb.type];
760 for (int mol = 0; mol < molb.nmol; mol++)
762 excls.appendListOfLists(molt.excls, atomIndex);
764 atomIndex += molt.atoms.nr;
771 /*! \brief Updates inter-molecular exclusion lists
773 * This function updates inter-molecular exclusions to exclude all
774 * non-bonded interactions between a given list of atoms
776 * \param[inout] excls existing exclusions in local topology
777 * \param[in] ids list of global IDs of atoms
779 static void addMimicExclusions(gmx::ListOfLists<int>* excls, const gmx::ArrayRef<const int> ids)
781 t_blocka inter_excl{};
782 init_blocka(&inter_excl);
783 size_t n_q = ids.size();
785 inter_excl.nr = excls->ssize();
786 inter_excl.nra = n_q * n_q;
788 size_t total_nra = n_q * n_q;
790 snew(inter_excl.index, excls->ssize() + 1);
791 snew(inter_excl.a, total_nra);
793 for (int i = 0; i < inter_excl.nr; ++i)
795 inter_excl.index[i] = 0;
798 /* Here we loop over the list of QM atom ids
799 * and create exclusions between all of them resulting in
800 * n_q * n_q sized exclusion list
803 for (int k = 0; k < inter_excl.nr; ++k)
805 inter_excl.index[k] = prev_index;
806 for (long i = 0; i < ids.ssize(); i++)
812 size_t index = n_q * i;
813 inter_excl.index[ids[i]] = index;
814 prev_index = index + n_q;
815 for (size_t j = 0; j < n_q; ++j)
817 inter_excl.a[n_q * i + j] = ids[j];
821 inter_excl.index[ids[n_q - 1] + 1] = n_q * n_q;
823 inter_excl.index[inter_excl.nr] = n_q * n_q;
825 std::vector<gmx::ExclusionBlock> qmexcl2(excls->size());
826 gmx::blockaToExclusionBlocks(&inter_excl, qmexcl2);
828 // Merge the created exclusion list with the existing one
829 gmx::mergeExclusions(excls, qmexcl2);
832 bool atomHasPerturbedChargeIn14Interaction(const int atomIndex, const gmx_moltype_t& molt)
834 if (molt.atoms.nr > 0)
836 // Is the charge perturbed at all?
837 const t_atom& atom = molt.atoms.atom[atomIndex];
838 if (atom.q != atom.qB)
840 // Loop over 1-4 interactions
841 const InteractionList& ilist = molt.ilist[F_LJ14];
842 gmx::ArrayRef<const int> iatoms = ilist.iatoms;
843 const int nral = NRAL(F_LJ14);
844 for (int i = 0; i < ilist.size(); i += nral + 1)
846 // Compare the atom indices in this 1-4 interaction to
848 int ia1 = iatoms[i + 1];
849 int ia2 = iatoms[i + 2];
850 if ((atomIndex == ia1) || (atomIndex == ia2))
860 static void sortFreeEnergyInteractionsAtEnd(const gmx_mtop_t& mtop, InteractionDefinitions* idef)
862 std::vector<int64_t> atomInfo(mtop.natoms, 0);
864 for (size_t mb = 0; mb < mtop.molblock.size(); mb++)
866 const gmx_molblock_t& molb = mtop.molblock[mb];
867 const gmx_moltype_t& molt = mtop.moltype[molb.type];
868 for (int a = 0; a < molt.atoms.nr; a++)
870 if (atomHasPerturbedChargeIn14Interaction(a, molt))
872 atomInfo[a] |= gmx::sc_atomInfo_HasPerturbedChargeIn14Interaction;
877 gmx_sort_ilist_fe(idef, atomInfo);
880 static void gen_local_top(const gmx_mtop_t& mtop,
881 bool freeEnergyInteractionsAtEnd,
885 copyIListsFromMtop(mtop, &top->idef, bMergeConstr);
886 if (freeEnergyInteractionsAtEnd)
888 sortFreeEnergyInteractionsAtEnd(mtop, &top->idef);
890 top->excls = globalExclusionLists(mtop);
891 if (!mtop.intermolecularExclusionGroup.empty())
893 addMimicExclusions(&top->excls, mtop.intermolecularExclusionGroup);
897 void gmx_mtop_generate_local_top(const gmx_mtop_t& mtop, gmx_localtop_t* top, bool freeEnergyInteractionsAtEnd)
899 gen_local_top(mtop, freeEnergyInteractionsAtEnd, true, top);
902 /*! \brief Fills an array with molecule begin/end atom indices
904 * \param[in] mtop The global topology
905 * \param[out] index Array of size nr. of molecules + 1 to be filled with molecule begin/end indices
907 static void fillMoleculeIndices(const gmx_mtop_t& mtop, gmx::ArrayRef<int> index)
909 int globalAtomIndex = 0;
910 int globalMolIndex = 0;
911 index[globalMolIndex] = globalAtomIndex;
912 for (const gmx_molblock_t& molb : mtop.molblock)
914 int numAtomsPerMolecule = mtop.moltype[molb.type].atoms.nr;
915 for (int mol = 0; mol < molb.nmol; mol++)
917 globalAtomIndex += numAtomsPerMolecule;
919 index[globalMolIndex] = globalAtomIndex;
924 gmx::RangePartitioning gmx_mtop_molecules(const gmx_mtop_t& mtop)
926 gmx::RangePartitioning mols;
928 for (const gmx_molblock_t& molb : mtop.molblock)
930 int numAtomsPerMolecule = mtop.moltype[molb.type].atoms.nr;
931 for (int mol = 0; mol < molb.nmol; mol++)
933 mols.appendBlock(numAtomsPerMolecule);
940 std::vector<gmx::Range<int>> atomRangeOfEachResidue(const gmx_moltype_t& moltype)
942 std::vector<gmx::Range<int>> atomRanges;
943 int currentResidueNumber = moltype.atoms.atom[0].resind;
945 // Go through all atoms in a molecule to store first and last atoms in each residue.
946 for (int i = 0; i < moltype.atoms.nr; i++)
948 int residueOfThisAtom = moltype.atoms.atom[i].resind;
949 if (residueOfThisAtom != currentResidueNumber)
951 // This atom belongs to the next residue, so record the range for the previous residue,
952 // remembering that end points to one place past the last atom.
954 atomRanges.emplace_back(startAtom, endAtom);
955 // Prepare for the current residue
957 currentResidueNumber = residueOfThisAtom;
960 // special treatment for last residue in this molecule.
961 atomRanges.emplace_back(startAtom, moltype.atoms.nr);
966 /*! \brief Creates and returns a deprecated t_block struct with molecule indices
968 * \param[in] mtop The global topology
970 static t_block gmx_mtop_molecules_t_block(const gmx_mtop_t& mtop)
974 mols.nr = gmx_mtop_num_molecules(mtop);
975 mols.nalloc_index = mols.nr + 1;
976 snew(mols.index, mols.nalloc_index);
978 fillMoleculeIndices(mtop, gmx::arrayRefFromArray(mols.index, mols.nr + 1));
983 static void gen_t_topology(const gmx_mtop_t& mtop, bool bMergeConstr, t_topology* top)
985 copyAtomtypesFromMtop(mtop, &top->atomtypes);
986 for (int ftype = 0; ftype < F_NRE; ftype++)
988 top->idef.il[ftype].nr = 0;
989 top->idef.il[ftype].nalloc = 0;
990 top->idef.il[ftype].iatoms = nullptr;
992 copyFFParametersFromMtop(mtop, &top->idef);
993 copyIListsFromMtop(mtop, &top->idef, bMergeConstr);
995 top->name = mtop.name;
996 top->atoms = gmx_mtop_global_atoms(mtop);
997 top->mols = gmx_mtop_molecules_t_block(mtop);
998 top->bIntermolecularInteractions = mtop.bIntermolecularInteractions;
999 top->symtab = mtop.symtab;
1002 t_topology gmx_mtop_t_to_t_topology(gmx_mtop_t* mtop, bool freeMTop)
1006 gen_t_topology(*mtop, false, &top);
1010 // Clear pointers and counts, such that the pointers copied to top
1011 // keep pointing to valid data after destroying mtop.
1012 mtop->symtab.symbuf = nullptr;
1013 mtop->symtab.nr = 0;
1018 std::vector<int> get_atom_index(const gmx_mtop_t& mtop)
1021 std::vector<int> atom_index;
1022 for (const AtomProxy atomP : AtomRange(mtop))
1024 const t_atom& local = atomP.atom();
1025 int index = atomP.globalAtomNumber();
1026 if (local.ptype == ParticleType::Atom)
1028 atom_index.push_back(index);
1034 void convertAtomsToMtop(t_symtab* symtab, char** name, t_atoms* atoms, gmx_mtop_t* mtop)
1036 mtop->symtab = *symtab;
1040 mtop->moltype.clear();
1041 mtop->moltype.resize(1);
1042 mtop->moltype.back().atoms = *atoms;
1044 mtop->molblock.resize(1);
1045 mtop->molblock[0].type = 0;
1046 mtop->molblock[0].nmol = 1;
1048 mtop->bIntermolecularInteractions = FALSE;
1050 mtop->natoms = atoms->nr;
1052 mtop->haveMoleculeIndices = false;
1057 bool haveFepPerturbedNBInteractions(const gmx_mtop_t& mtop)
1059 for (const gmx_moltype_t& molt : mtop.moltype)
1061 for (int a = 0; a < molt.atoms.nr; a++)
1063 if (PERTURBED(molt.atoms.atom[a]))
1073 bool haveFepPerturbedMasses(const gmx_mtop_t& mtop)
1075 for (const gmx_moltype_t& molt : mtop.moltype)
1077 for (int a = 0; a < molt.atoms.nr; a++)
1079 const t_atom& atom = molt.atoms.atom[a];
1080 if (atom.m != atom.mB)
1090 bool haveFepPerturbedMassesInSettles(const gmx_mtop_t& mtop)
1092 for (const gmx_moltype_t& molt : mtop.moltype)
1094 if (!molt.ilist[F_SETTLE].empty())
1096 for (int a = 0; a < molt.atoms.nr; a++)
1098 const t_atom& atom = molt.atoms.atom[a];
1099 if (atom.m != atom.mB)
1109 bool havePerturbedConstraints(const gmx_mtop_t& mtop)
1111 // This code assumes that all perturbed constraints parameters are actually used
1112 const auto& ffparams = mtop.ffparams;
1114 for (gmx::index i = 0; i < gmx::ssize(ffparams.functype); i++)
1116 if (ffparams.functype[i] == F_CONSTR || ffparams.functype[i] == F_CONSTRNC)
1118 const auto& iparams = ffparams.iparams[i];
1119 if (iparams.constr.dA != iparams.constr.dB)