4bc2e2946e229397becd7045b3bf00bcf5b9cd6b
[alexxy/gromacs.git] / src / gromacs / topology / mtop_util.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2008,2009,2010,
5  * Copyright (c) 2012,2013,2014,2015,2016,2017,2018,2019, 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.
9  *
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.
14  *
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.
19  *
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.
24  *
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.
32  *
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.
35  */
36 #include "gmxpre.h"
37
38 #include "mtop_util.h"
39
40 #include <climits>
41 #include <cstddef>
42 #include <cstdio>
43 #include <cstdlib>
44 #include <cstring>
45
46 #include "gromacs/math/vectypes.h"
47 #include "gromacs/topology/atoms.h"
48 #include "gromacs/topology/block.h"
49 #include "gromacs/topology/exclusionblocks.h"
50 #include "gromacs/topology/idef.h"
51 #include "gromacs/topology/ifunc.h"
52 #include "gromacs/topology/topology.h"
53 #include "gromacs/topology/topsort.h"
54 #include "gromacs/utility/arrayref.h"
55 #include "gromacs/utility/fatalerror.h"
56 #include "gromacs/utility/real.h"
57 #include "gromacs/utility/smalloc.h"
58
59 static int gmx_mtop_maxresnr(const gmx_mtop_t* mtop, int maxres_renum)
60 {
61     int maxresnr = 0;
62
63     for (const gmx_moltype_t& moltype : mtop->moltype)
64     {
65         const t_atoms& atoms = moltype.atoms;
66         if (atoms.nres > maxres_renum)
67         {
68             for (int r = 0; r < atoms.nres; r++)
69             {
70                 if (atoms.resinfo[r].nr > maxresnr)
71                 {
72                     maxresnr = atoms.resinfo[r].nr;
73                 }
74             }
75         }
76     }
77
78     return maxresnr;
79 }
80
81 static void buildMolblockIndices(gmx_mtop_t* mtop)
82 {
83     mtop->moleculeBlockIndices.resize(mtop->molblock.size());
84
85     int atomIndex          = 0;
86     int residueIndex       = 0;
87     int residueNumberStart = mtop->maxresnr + 1;
88     int moleculeIndexStart = 0;
89     for (size_t mb = 0; mb < mtop->molblock.size(); mb++)
90     {
91         const gmx_molblock_t& molb         = mtop->molblock[mb];
92         MoleculeBlockIndices& indices      = mtop->moleculeBlockIndices[mb];
93         const int             numResPerMol = mtop->moltype[molb.type].atoms.nres;
94
95         indices.numAtomsPerMolecule = mtop->moltype[molb.type].atoms.nr;
96         indices.globalAtomStart     = atomIndex;
97         indices.globalResidueStart  = residueIndex;
98         atomIndex += molb.nmol * indices.numAtomsPerMolecule;
99         residueIndex += molb.nmol * numResPerMol;
100         indices.globalAtomEnd      = atomIndex;
101         indices.residueNumberStart = residueNumberStart;
102         if (numResPerMol <= mtop->maxres_renum)
103         {
104             residueNumberStart += molb.nmol * numResPerMol;
105         }
106         indices.moleculeIndexStart = moleculeIndexStart;
107         moleculeIndexStart += molb.nmol;
108     }
109 }
110
111 void gmx_mtop_finalize(gmx_mtop_t* mtop)
112 {
113     char* env;
114
115     if (mtop->molblock.size() == 1 && mtop->molblock[0].nmol == 1)
116     {
117         /* We have a single molecule only, no renumbering needed.
118          * This case also covers an mtop converted from pdb/gro/... input,
119          * so we retain the original residue numbering.
120          */
121         mtop->maxres_renum = 0;
122     }
123     else
124     {
125         /* We only renumber single residue molecules. Their intra-molecular
126          * residue numbering is anyhow irrelevant.
127          */
128         mtop->maxres_renum = 1;
129     }
130
131     env = getenv("GMX_MAXRESRENUM");
132     if (env != nullptr)
133     {
134         sscanf(env, "%d", &mtop->maxres_renum);
135     }
136     if (mtop->maxres_renum == -1)
137     {
138         /* -1 signals renumber residues in all molecules */
139         mtop->maxres_renum = INT_MAX;
140     }
141
142     mtop->maxresnr = gmx_mtop_maxresnr(mtop, mtop->maxres_renum);
143
144     buildMolblockIndices(mtop);
145 }
146
147 void gmx_mtop_count_atomtypes(const gmx_mtop_t* mtop, int state, int typecount[])
148 {
149     for (int i = 0; i < mtop->ffparams.atnr; ++i)
150     {
151         typecount[i] = 0;
152     }
153     for (const gmx_molblock_t& molb : mtop->molblock)
154     {
155         const t_atoms& atoms = mtop->moltype[molb.type].atoms;
156         for (int i = 0; i < atoms.nr; ++i)
157         {
158             int tpi;
159             if (state == 0)
160             {
161                 tpi = atoms.atom[i].type;
162             }
163             else
164             {
165                 tpi = atoms.atom[i].typeB;
166             }
167             typecount[tpi] += molb.nmol;
168         }
169     }
170 }
171
172 int gmx_mtop_num_molecules(const gmx_mtop_t& mtop)
173 {
174     int numMolecules = 0;
175     for (const gmx_molblock_t& molb : mtop.molblock)
176     {
177         numMolecules += molb.nmol;
178     }
179     return numMolecules;
180 }
181
182 int gmx_mtop_nres(const gmx_mtop_t* mtop)
183 {
184     int nres = 0;
185     for (const gmx_molblock_t& molb : mtop->molblock)
186     {
187         nres += molb.nmol * mtop->moltype[molb.type].atoms.nres;
188     }
189     return nres;
190 }
191
192 AtomIterator::AtomIterator(const gmx_mtop_t& mtop, int globalAtomNumber) :
193     mtop_(&mtop),
194     mblock_(0),
195     atoms_(&mtop.moltype[mtop.molblock[0].type].atoms),
196     currentMolecule_(0),
197     highestResidueNumber_(mtop.maxresnr),
198     localAtomNumber_(0),
199     globalAtomNumber_(globalAtomNumber)
200 {
201     GMX_ASSERT(globalAtomNumber == 0 || globalAtomNumber == mtop.natoms,
202                "Starting at other atoms not implemented yet");
203 }
204
205 AtomIterator& AtomIterator::operator++()
206 {
207     localAtomNumber_++;
208     globalAtomNumber_++;
209
210     if (localAtomNumber_ >= atoms_->nr)
211     {
212         if (atoms_->nres <= mtop_->maxresnr)
213         {
214             /* Single residue molecule, increase the count with one */
215             highestResidueNumber_ += atoms_->nres;
216         }
217         currentMolecule_++;
218         localAtomNumber_ = 0;
219         if (currentMolecule_ >= mtop_->molblock[mblock_].nmol)
220         {
221             mblock_++;
222             if (mblock_ >= mtop_->molblock.size())
223             {
224                 return *this;
225             }
226             atoms_           = &mtop_->moltype[mtop_->molblock[mblock_].type].atoms;
227             currentMolecule_ = 0;
228         }
229     }
230     return *this;
231 }
232
233 AtomIterator AtomIterator::operator++(int)
234 {
235     AtomIterator temp = *this;
236     ++(*this);
237     return temp;
238 }
239
240 bool AtomIterator::operator==(const AtomIterator& o) const
241 {
242     return mtop_ == o.mtop_ && globalAtomNumber_ == o.globalAtomNumber_;
243 }
244
245 bool AtomIterator::operator!=(const AtomIterator& o) const
246 {
247     return !(*this == o);
248 }
249
250 const t_atom& AtomProxy::atom() const
251 {
252     return it_->atoms_->atom[it_->localAtomNumber_];
253 }
254
255 int AtomProxy::globalAtomNumber() const
256 {
257     return it_->globalAtomNumber_;
258 }
259
260 const char* AtomProxy::atomName() const
261 {
262     return *(it_->atoms_->atomname[it_->localAtomNumber_]);
263 }
264
265 const char* AtomProxy::residueName() const
266 {
267     int residueIndexInMolecule = it_->atoms_->atom[it_->localAtomNumber_].resind;
268     return *(it_->atoms_->resinfo[residueIndexInMolecule].name);
269 }
270
271 int AtomProxy::residueNumber() const
272 {
273     int residueIndexInMolecule = it_->atoms_->atom[it_->localAtomNumber_].resind;
274     if (it_->atoms_->nres <= it_->mtop_->maxres_renum)
275     {
276         return it_->highestResidueNumber_ + 1 + residueIndexInMolecule;
277     }
278     else
279     {
280         return it_->atoms_->resinfo[residueIndexInMolecule].nr;
281     }
282 }
283
284 const gmx_moltype_t& AtomProxy::moleculeType() const
285 {
286     return it_->mtop_->moltype[it_->mtop_->molblock[it_->mblock_].type];
287 }
288
289 int AtomProxy::atomNumberInMol() const
290 {
291     return it_->localAtomNumber_;
292 }
293
294 typedef struct gmx_mtop_atomloop_block
295 {
296     const gmx_mtop_t* mtop;
297     size_t            mblock;
298     const t_atoms*    atoms;
299     int               at_local;
300 } t_gmx_mtop_atomloop_block;
301
302 gmx_mtop_atomloop_block_t gmx_mtop_atomloop_block_init(const gmx_mtop_t* mtop)
303 {
304     struct gmx_mtop_atomloop_block* aloop;
305
306     snew(aloop, 1);
307
308     aloop->mtop     = mtop;
309     aloop->mblock   = 0;
310     aloop->atoms    = &mtop->moltype[mtop->molblock[aloop->mblock].type].atoms;
311     aloop->at_local = -1;
312
313     return aloop;
314 }
315
316 static void gmx_mtop_atomloop_block_destroy(gmx_mtop_atomloop_block_t aloop)
317 {
318     sfree(aloop);
319 }
320
321 gmx_bool gmx_mtop_atomloop_block_next(gmx_mtop_atomloop_block_t aloop, const t_atom** atom, int* nmol)
322 {
323     if (aloop == nullptr)
324     {
325         gmx_incons("gmx_mtop_atomloop_all_next called without calling gmx_mtop_atomloop_all_init");
326     }
327
328     aloop->at_local++;
329
330     if (aloop->at_local >= aloop->atoms->nr)
331     {
332         aloop->mblock++;
333         if (aloop->mblock >= aloop->mtop->molblock.size())
334         {
335             gmx_mtop_atomloop_block_destroy(aloop);
336             return FALSE;
337         }
338         aloop->atoms    = &aloop->mtop->moltype[aloop->mtop->molblock[aloop->mblock].type].atoms;
339         aloop->at_local = 0;
340     }
341
342     *atom = &aloop->atoms->atom[aloop->at_local];
343     *nmol = aloop->mtop->molblock[aloop->mblock].nmol;
344
345     return TRUE;
346 }
347
348 typedef struct gmx_mtop_ilistloop
349 {
350     const gmx_mtop_t* mtop;
351     int               mblock;
352 } t_gmx_mtop_ilist;
353
354 gmx_mtop_ilistloop_t gmx_mtop_ilistloop_init(const gmx_mtop_t* mtop)
355 {
356     struct gmx_mtop_ilistloop* iloop;
357
358     snew(iloop, 1);
359
360     iloop->mtop   = mtop;
361     iloop->mblock = -1;
362
363     return iloop;
364 }
365
366 gmx_mtop_ilistloop_t gmx_mtop_ilistloop_init(const gmx_mtop_t& mtop)
367 {
368     return gmx_mtop_ilistloop_init(&mtop);
369 }
370
371 static void gmx_mtop_ilistloop_destroy(gmx_mtop_ilistloop_t iloop)
372 {
373     sfree(iloop);
374 }
375
376 const InteractionLists* gmx_mtop_ilistloop_next(gmx_mtop_ilistloop_t iloop, int* nmol)
377 {
378     if (iloop == nullptr)
379     {
380         gmx_incons("gmx_mtop_ilistloop_next called without calling gmx_mtop_ilistloop_init");
381     }
382
383     iloop->mblock++;
384     if (iloop->mblock >= gmx::ssize(iloop->mtop->molblock))
385     {
386         if (iloop->mblock == gmx::ssize(iloop->mtop->molblock) && iloop->mtop->bIntermolecularInteractions)
387         {
388             *nmol = 1;
389             return iloop->mtop->intermolecular_ilist.get();
390         }
391
392         gmx_mtop_ilistloop_destroy(iloop);
393         return nullptr;
394     }
395
396     *nmol = iloop->mtop->molblock[iloop->mblock].nmol;
397
398     return &iloop->mtop->moltype[iloop->mtop->molblock[iloop->mblock].type].ilist;
399 }
400 typedef struct gmx_mtop_ilistloop_all
401 {
402     const gmx_mtop_t* mtop;
403     size_t            mblock;
404     int               mol;
405     int               a_offset;
406 } t_gmx_mtop_ilist_all;
407
408 gmx_mtop_ilistloop_all_t gmx_mtop_ilistloop_all_init(const gmx_mtop_t* mtop)
409 {
410     struct gmx_mtop_ilistloop_all* iloop;
411
412     snew(iloop, 1);
413
414     iloop->mtop     = mtop;
415     iloop->mblock   = 0;
416     iloop->mol      = -1;
417     iloop->a_offset = 0;
418
419     return iloop;
420 }
421
422 static void gmx_mtop_ilistloop_all_destroy(gmx_mtop_ilistloop_all_t iloop)
423 {
424     sfree(iloop);
425 }
426
427 const InteractionLists* gmx_mtop_ilistloop_all_next(gmx_mtop_ilistloop_all_t iloop, int* atnr_offset)
428 {
429
430     if (iloop == nullptr)
431     {
432         gmx_incons(
433                 "gmx_mtop_ilistloop_all_next called without calling gmx_mtop_ilistloop_all_init");
434     }
435
436     if (iloop->mol >= 0)
437     {
438         iloop->a_offset += iloop->mtop->moleculeBlockIndices[iloop->mblock].numAtomsPerMolecule;
439     }
440
441     iloop->mol++;
442
443     /* Inter-molecular interactions, if present, are indexed with
444      * iloop->mblock == iloop->mtop->nmolblock, thus we should separately
445      * check for this value in this conditional.
446      */
447     if (iloop->mblock == iloop->mtop->molblock.size()
448         || iloop->mol >= iloop->mtop->molblock[iloop->mblock].nmol)
449     {
450         iloop->mblock++;
451         iloop->mol = 0;
452         if (iloop->mblock >= iloop->mtop->molblock.size())
453         {
454             if (iloop->mblock == iloop->mtop->molblock.size() && iloop->mtop->bIntermolecularInteractions)
455             {
456                 *atnr_offset = 0;
457                 return iloop->mtop->intermolecular_ilist.get();
458             }
459
460             gmx_mtop_ilistloop_all_destroy(iloop);
461             return nullptr;
462         }
463     }
464
465     *atnr_offset = iloop->a_offset;
466
467     return &iloop->mtop->moltype[iloop->mtop->molblock[iloop->mblock].type].ilist;
468 }
469
470 int gmx_mtop_ftype_count(const gmx_mtop_t* mtop, int ftype)
471 {
472     gmx_mtop_ilistloop_t iloop;
473     int                  n, nmol;
474
475     n = 0;
476
477     iloop = gmx_mtop_ilistloop_init(mtop);
478     while (const InteractionLists* il = gmx_mtop_ilistloop_next(iloop, &nmol))
479     {
480         n += nmol * (*il)[ftype].size() / (1 + NRAL(ftype));
481     }
482
483     if (mtop->bIntermolecularInteractions)
484     {
485         n += (*mtop->intermolecular_ilist)[ftype].size() / (1 + NRAL(ftype));
486     }
487
488     return n;
489 }
490
491 int gmx_mtop_ftype_count(const gmx_mtop_t& mtop, int ftype)
492 {
493     return gmx_mtop_ftype_count(&mtop, ftype);
494 }
495
496 int gmx_mtop_interaction_count(const gmx_mtop_t& mtop, const int unsigned if_flags)
497 {
498     int n = 0;
499
500     gmx_mtop_ilistloop_t iloop = gmx_mtop_ilistloop_init(mtop);
501     int                  nmol;
502     while (const InteractionLists* il = gmx_mtop_ilistloop_next(iloop, &nmol))
503     {
504         for (int ftype = 0; ftype < F_NRE; ftype++)
505         {
506             if ((interaction_function[ftype].flags & if_flags) == if_flags)
507             {
508                 n += nmol * (*il)[ftype].size() / (1 + NRAL(ftype));
509             }
510         }
511     }
512
513     if (mtop.bIntermolecularInteractions)
514     {
515         for (int ftype = 0; ftype < F_NRE; ftype++)
516         {
517             if ((interaction_function[ftype].flags & if_flags) == if_flags)
518             {
519                 n += (*mtop.intermolecular_ilist)[ftype].size() / (1 + NRAL(ftype));
520             }
521         }
522     }
523
524     return n;
525 }
526
527 static void atomcat(t_atoms* dest, const t_atoms* src, int copies, int maxres_renum, int* maxresnr)
528 {
529     int i, j, l, size;
530     int srcnr  = src->nr;
531     int destnr = dest->nr;
532
533     if (dest->nr == 0)
534     {
535         dest->haveMass    = src->haveMass;
536         dest->haveType    = src->haveType;
537         dest->haveCharge  = src->haveCharge;
538         dest->haveBState  = src->haveBState;
539         dest->havePdbInfo = src->havePdbInfo;
540     }
541     else
542     {
543         dest->haveMass    = dest->haveMass && src->haveMass;
544         dest->haveType    = dest->haveType && src->haveType;
545         dest->haveCharge  = dest->haveCharge && src->haveCharge;
546         dest->haveBState  = dest->haveBState && src->haveBState;
547         dest->havePdbInfo = dest->havePdbInfo && src->havePdbInfo;
548     }
549
550     if (srcnr)
551     {
552         size = destnr + copies * srcnr;
553         srenew(dest->atom, size);
554         srenew(dest->atomname, size);
555         if (dest->haveType)
556         {
557             srenew(dest->atomtype, size);
558             if (dest->haveBState)
559             {
560                 srenew(dest->atomtypeB, size);
561             }
562         }
563         if (dest->havePdbInfo)
564         {
565             srenew(dest->pdbinfo, size);
566         }
567     }
568     if (src->nres)
569     {
570         size = dest->nres + copies * src->nres;
571         srenew(dest->resinfo, size);
572     }
573
574     /* residue information */
575     for (l = dest->nres, j = 0; (j < copies); j++, l += src->nres)
576     {
577         memcpy(reinterpret_cast<char*>(&(dest->resinfo[l])), reinterpret_cast<char*>(&(src->resinfo[0])),
578                static_cast<size_t>(src->nres * sizeof(src->resinfo[0])));
579     }
580
581     for (l = destnr, j = 0; (j < copies); j++, l += srcnr)
582     {
583         memcpy(reinterpret_cast<char*>(&(dest->atom[l])), reinterpret_cast<char*>(&(src->atom[0])),
584                static_cast<size_t>(srcnr * sizeof(src->atom[0])));
585         memcpy(reinterpret_cast<char*>(&(dest->atomname[l])),
586                reinterpret_cast<char*>(&(src->atomname[0])),
587                static_cast<size_t>(srcnr * sizeof(src->atomname[0])));
588         if (dest->haveType)
589         {
590             memcpy(reinterpret_cast<char*>(&(dest->atomtype[l])),
591                    reinterpret_cast<char*>(&(src->atomtype[0])),
592                    static_cast<size_t>(srcnr * sizeof(src->atomtype[0])));
593             if (dest->haveBState)
594             {
595                 memcpy(reinterpret_cast<char*>(&(dest->atomtypeB[l])),
596                        reinterpret_cast<char*>(&(src->atomtypeB[0])),
597                        static_cast<size_t>(srcnr * sizeof(src->atomtypeB[0])));
598             }
599         }
600         if (dest->havePdbInfo)
601         {
602             memcpy(reinterpret_cast<char*>(&(dest->pdbinfo[l])),
603                    reinterpret_cast<char*>(&(src->pdbinfo[0])),
604                    static_cast<size_t>(srcnr * sizeof(src->pdbinfo[0])));
605         }
606     }
607
608     /* Increment residue indices */
609     for (l = destnr, j = 0; (j < copies); j++)
610     {
611         for (i = 0; (i < srcnr); i++, l++)
612         {
613             dest->atom[l].resind = dest->nres + j * src->nres + src->atom[i].resind;
614         }
615     }
616
617     if (src->nres <= maxres_renum)
618     {
619         /* Single residue molecule, continue counting residues */
620         for (j = 0; (j < copies); j++)
621         {
622             for (l = 0; l < src->nres; l++)
623             {
624                 (*maxresnr)++;
625                 dest->resinfo[dest->nres + j * src->nres + l].nr = *maxresnr;
626             }
627         }
628     }
629
630     dest->nres += copies * src->nres;
631     dest->nr += copies * src->nr;
632 }
633
634 t_atoms gmx_mtop_global_atoms(const gmx_mtop_t* mtop)
635 {
636     t_atoms atoms;
637
638     init_t_atoms(&atoms, 0, FALSE);
639
640     int maxresnr = mtop->maxresnr;
641     for (const gmx_molblock_t& molb : mtop->molblock)
642     {
643         atomcat(&atoms, &mtop->moltype[molb.type].atoms, molb.nmol, mtop->maxres_renum, &maxresnr);
644     }
645
646     return atoms;
647 }
648
649 /*
650  * The cat routines below are old code from src/kernel/topcat.c
651  */
652
653 static void blockacat(t_blocka* dest, const t_blocka* src, int copies, int dnum, int snum)
654 {
655     int i, j, l, size;
656     int destnr  = dest->nr;
657     int destnra = dest->nra;
658
659     if (src->nr)
660     {
661         size = (dest->nr + copies * src->nr + 1);
662         srenew(dest->index, size);
663     }
664     if (src->nra)
665     {
666         size = (dest->nra + copies * src->nra);
667         srenew(dest->a, size);
668     }
669
670     for (l = destnr, j = 0; (j < copies); j++)
671     {
672         for (i = 0; (i < src->nr); i++)
673         {
674             dest->index[l++] = dest->nra + src->index[i];
675         }
676         dest->nra += src->nra;
677     }
678     for (l = destnra, j = 0; (j < copies); j++)
679     {
680         for (i = 0; (i < src->nra); i++)
681         {
682             dest->a[l++] = dnum + src->a[i];
683         }
684         dnum += snum;
685         dest->nr += src->nr;
686     }
687     dest->index[dest->nr] = dest->nra;
688     dest->nalloc_index    = dest->nr;
689     dest->nalloc_a        = dest->nra;
690 }
691
692 static void ilistcat(int ftype, t_ilist* dest, const InteractionList& src, int copies, int dnum, int snum)
693 {
694     int nral, c, i, a;
695
696     nral = NRAL(ftype);
697
698     dest->nalloc = dest->nr + copies * src.size();
699     srenew(dest->iatoms, dest->nalloc);
700
701     for (c = 0; c < copies; c++)
702     {
703         for (i = 0; i < src.size();)
704         {
705             dest->iatoms[dest->nr++] = src.iatoms[i++];
706             for (a = 0; a < nral; a++)
707             {
708                 dest->iatoms[dest->nr++] = dnum + src.iatoms[i++];
709             }
710         }
711         dnum += snum;
712     }
713 }
714
715 static void set_posres_params(t_idef* idef, const gmx_molblock_t* molb, int i0, int a_offset)
716 {
717     t_ilist*   il;
718     int        i1, i, a_molb;
719     t_iparams* ip;
720
721     il                          = &idef->il[F_POSRES];
722     i1                          = il->nr / 2;
723     idef->iparams_posres_nalloc = i1;
724     srenew(idef->iparams_posres, idef->iparams_posres_nalloc);
725     for (i = i0; i < i1; i++)
726     {
727         ip = &idef->iparams_posres[i];
728         /* Copy the force constants */
729         *ip    = idef->iparams[il->iatoms[i * 2]];
730         a_molb = il->iatoms[i * 2 + 1] - a_offset;
731         if (molb->posres_xA.empty())
732         {
733             gmx_incons("Position restraint coordinates are missing");
734         }
735         ip->posres.pos0A[XX] = molb->posres_xA[a_molb][XX];
736         ip->posres.pos0A[YY] = molb->posres_xA[a_molb][YY];
737         ip->posres.pos0A[ZZ] = molb->posres_xA[a_molb][ZZ];
738         if (!molb->posres_xB.empty())
739         {
740             ip->posres.pos0B[XX] = molb->posres_xB[a_molb][XX];
741             ip->posres.pos0B[YY] = molb->posres_xB[a_molb][YY];
742             ip->posres.pos0B[ZZ] = molb->posres_xB[a_molb][ZZ];
743         }
744         else
745         {
746             ip->posres.pos0B[XX] = ip->posres.pos0A[XX];
747             ip->posres.pos0B[YY] = ip->posres.pos0A[YY];
748             ip->posres.pos0B[ZZ] = ip->posres.pos0A[ZZ];
749         }
750         /* Set the parameter index for idef->iparams_posre */
751         il->iatoms[i * 2] = i;
752     }
753 }
754
755 static void set_fbposres_params(t_idef* idef, const gmx_molblock_t* molb, int i0, int a_offset)
756 {
757     t_ilist*   il;
758     int        i1, i, a_molb;
759     t_iparams* ip;
760
761     il                            = &idef->il[F_FBPOSRES];
762     i1                            = il->nr / 2;
763     idef->iparams_fbposres_nalloc = i1;
764     srenew(idef->iparams_fbposres, idef->iparams_fbposres_nalloc);
765     for (i = i0; i < i1; i++)
766     {
767         ip = &idef->iparams_fbposres[i];
768         /* Copy the force constants */
769         *ip    = idef->iparams[il->iatoms[i * 2]];
770         a_molb = il->iatoms[i * 2 + 1] - a_offset;
771         if (molb->posres_xA.empty())
772         {
773             gmx_incons("Position restraint coordinates are missing");
774         }
775         /* Take flat-bottom posres reference from normal position restraints */
776         ip->fbposres.pos0[XX] = molb->posres_xA[a_molb][XX];
777         ip->fbposres.pos0[YY] = molb->posres_xA[a_molb][YY];
778         ip->fbposres.pos0[ZZ] = molb->posres_xA[a_molb][ZZ];
779         /* Note: no B-type for flat-bottom posres */
780
781         /* Set the parameter index for idef->iparams_posre */
782         il->iatoms[i * 2] = i;
783     }
784 }
785
786 /*! \brief Copy idef structure from mtop.
787  *
788  * Makes a deep copy of an idef data structure from a gmx_mtop_t.
789  * Used to initialize legacy topology types.
790  *
791  * \param[in] mtop Reference to input mtop.
792  * \param[in] idef Pointer to idef to populate.
793  * \param[in] mergeConstr Decide if constraints will be merged.
794  * \param[in] freeEnergyInteractionsAtEnd Decide if free energy stuff should
795  *              be added at the end.
796  */
797 static void copyIdefFromMtop(const gmx_mtop_t& mtop, t_idef* idef, bool freeEnergyInteractionsAtEnd, bool mergeConstr)
798 {
799     const gmx_ffparams_t* ffp = &mtop.ffparams;
800
801     idef->ntypes = ffp->numTypes();
802     idef->atnr   = ffp->atnr;
803     /* we can no longer copy the pointers to the mtop members,
804      * because they will become invalid as soon as mtop gets free'd.
805      * We also need to make sure to only operate on valid data!
806      */
807
808     if (!ffp->functype.empty())
809     {
810         snew(idef->functype, ffp->functype.size());
811         std::copy(ffp->functype.data(), ffp->functype.data() + ffp->functype.size(), idef->functype);
812     }
813     else
814     {
815         idef->functype = nullptr;
816     }
817     if (!ffp->iparams.empty())
818     {
819         snew(idef->iparams, ffp->iparams.size());
820         std::copy(ffp->iparams.data(), ffp->iparams.data() + ffp->iparams.size(), idef->iparams);
821     }
822     else
823     {
824         idef->iparams = nullptr;
825     }
826     idef->iparams_posres          = nullptr;
827     idef->iparams_posres_nalloc   = 0;
828     idef->iparams_fbposres        = nullptr;
829     idef->iparams_fbposres_nalloc = 0;
830     idef->fudgeQQ                 = ffp->fudgeQQ;
831     idef->cmap_grid               = new gmx_cmap_t;
832     *idef->cmap_grid              = ffp->cmap_grid;
833     idef->ilsort                  = ilsortUNKNOWN;
834
835     for (int ftype = 0; ftype < F_NRE; ftype++)
836     {
837         idef->il[ftype].nr     = 0;
838         idef->il[ftype].nalloc = 0;
839         idef->il[ftype].iatoms = nullptr;
840     }
841
842     int natoms = 0;
843     for (const gmx_molblock_t& molb : mtop.molblock)
844     {
845         const gmx_moltype_t& molt = mtop.moltype[molb.type];
846
847         int srcnr  = molt.atoms.nr;
848         int destnr = natoms;
849
850         int nposre_old   = idef->il[F_POSRES].nr;
851         int nfbposre_old = idef->il[F_FBPOSRES].nr;
852         for (int ftype = 0; ftype < F_NRE; ftype++)
853         {
854             if (mergeConstr && ftype == F_CONSTR && molt.ilist[F_CONSTRNC].size() > 0)
855             {
856                 /* Merge all constrains into one ilist.
857                  * This simplifies the constraint code.
858                  */
859                 for (int mol = 0; mol < molb.nmol; mol++)
860                 {
861                     ilistcat(ftype, &idef->il[F_CONSTR], molt.ilist[F_CONSTR], 1,
862                              destnr + mol * srcnr, srcnr);
863                     ilistcat(ftype, &idef->il[F_CONSTR], molt.ilist[F_CONSTRNC], 1,
864                              destnr + mol * srcnr, srcnr);
865                 }
866             }
867             else if (!(mergeConstr && ftype == F_CONSTRNC))
868             {
869                 ilistcat(ftype, &idef->il[ftype], molt.ilist[ftype], molb.nmol, destnr, srcnr);
870             }
871         }
872         if (idef->il[F_POSRES].nr > nposre_old)
873         {
874             /* Executing this line line stops gmxdump -sys working
875              * correctly. I'm not aware there's an elegant fix. */
876             set_posres_params(idef, &molb, nposre_old / 2, natoms);
877         }
878         if (idef->il[F_FBPOSRES].nr > nfbposre_old)
879         {
880             set_fbposres_params(idef, &molb, nfbposre_old / 2, natoms);
881         }
882
883         natoms += molb.nmol * srcnr;
884     }
885
886     if (mtop.bIntermolecularInteractions)
887     {
888         for (int ftype = 0; ftype < F_NRE; ftype++)
889         {
890             ilistcat(ftype, &idef->il[ftype], (*mtop.intermolecular_ilist)[ftype], 1, 0, mtop.natoms);
891         }
892     }
893
894     if (freeEnergyInteractionsAtEnd && gmx_mtop_bondeds_free_energy(&mtop))
895     {
896         std::vector<real> qA(mtop.natoms);
897         std::vector<real> qB(mtop.natoms);
898         for (const AtomProxy atomP : AtomRange(mtop))
899         {
900             const t_atom& local = atomP.atom();
901             int           index = atomP.globalAtomNumber();
902             qA[index]           = local.q;
903             qB[index]           = local.qB;
904         }
905         gmx_sort_ilist_fe(idef, qA.data(), qB.data());
906     }
907     else
908     {
909         idef->ilsort = ilsortNO_FE;
910     }
911 }
912
913 /*! \brief Copy atomtypes from mtop
914  *
915  * Makes a deep copy of t_atomtypes from gmx_mtop_t.
916  * Used to initialize legacy topology types.
917  *
918  * \param[in] mtop Reference to input mtop.
919  * \param[in] atomtypes Pointer to atomtypes to populate.
920  */
921 static void copyAtomtypesFromMtop(const gmx_mtop_t& mtop, t_atomtypes* atomtypes)
922 {
923     atomtypes->nr = mtop.atomtypes.nr;
924     if (mtop.atomtypes.atomnumber)
925     {
926         snew(atomtypes->atomnumber, mtop.atomtypes.nr);
927         std::copy(mtop.atomtypes.atomnumber, mtop.atomtypes.atomnumber + mtop.atomtypes.nr,
928                   atomtypes->atomnumber);
929     }
930     else
931     {
932         atomtypes->atomnumber = nullptr;
933     }
934 }
935
936 /*! \brief Copy excls from mtop.
937  *
938  * Makes a deep copy of excls(t_blocka) from gmx_mtop_t.
939  * Used to initialize legacy topology types.
940  *
941  * \param[in] mtop  Reference to input mtop.
942  * \param[in] excls Pointer to final excls data structure.
943  */
944 static void copyExclsFromMtop(const gmx_mtop_t& mtop, t_blocka* excls)
945 {
946     init_blocka(excls);
947     int natoms = 0;
948     for (const gmx_molblock_t& molb : mtop.molblock)
949     {
950         const gmx_moltype_t& molt = mtop.moltype[molb.type];
951
952         int srcnr  = molt.atoms.nr;
953         int destnr = natoms;
954
955         blockacat(excls, &molt.excls, molb.nmol, destnr, srcnr);
956
957         natoms += molb.nmol * srcnr;
958     }
959 }
960
961 /*! \brief Updates inter-molecular exclusion lists
962  *
963  * This function updates inter-molecular exclusions to exclude all
964  * non-bonded interactions between a given list of atoms
965  *
966  * \param[inout]    excls   existing exclusions in local topology
967  * \param[in]       ids     list of global IDs of atoms
968  */
969 static void addMimicExclusions(t_blocka* excls, const gmx::ArrayRef<const int> ids)
970 {
971     t_blocka inter_excl{};
972     init_blocka(&inter_excl);
973     size_t n_q = ids.size();
974
975     inter_excl.nr  = excls->nr;
976     inter_excl.nra = n_q * n_q;
977
978     size_t total_nra = n_q * n_q;
979
980     snew(inter_excl.index, excls->nr + 1);
981     snew(inter_excl.a, total_nra);
982
983     for (int i = 0; i < excls->nr; ++i)
984     {
985         inter_excl.index[i] = 0;
986     }
987
988     /* Here we loop over the list of QM atom ids
989      *  and create exclusions between all of them resulting in
990      *  n_q * n_q sized exclusion list
991      */
992     int prev_index = 0;
993     for (int k = 0; k < inter_excl.nr; ++k)
994     {
995         inter_excl.index[k] = prev_index;
996         for (long i = 0; i < ids.ssize(); i++)
997         {
998             if (k != ids[i])
999             {
1000                 continue;
1001             }
1002             size_t index             = n_q * i;
1003             inter_excl.index[ids[i]] = index;
1004             prev_index               = index + n_q;
1005             for (size_t j = 0; j < n_q; ++j)
1006             {
1007                 inter_excl.a[n_q * i + j] = ids[j];
1008             }
1009         }
1010     }
1011     inter_excl.index[ids[n_q - 1] + 1] = n_q * n_q;
1012
1013     inter_excl.index[inter_excl.nr] = n_q * n_q;
1014
1015     std::vector<gmx::ExclusionBlock> qmexcl2(excls->nr);
1016     gmx::blockaToExclusionBlocks(&inter_excl, qmexcl2);
1017
1018     // Merge the created exclusion list with the existing one
1019     gmx::mergeExclusions(excls, qmexcl2);
1020 }
1021
1022 static void gen_local_top(const gmx_mtop_t& mtop,
1023                           bool              freeEnergyInteractionsAtEnd,
1024                           bool              bMergeConstr,
1025                           gmx_localtop_t*   top)
1026 {
1027     copyAtomtypesFromMtop(mtop, &top->atomtypes);
1028     copyIdefFromMtop(mtop, &top->idef, freeEnergyInteractionsAtEnd, bMergeConstr);
1029     copyExclsFromMtop(mtop, &top->excls);
1030     if (!mtop.intermolecularExclusionGroup.empty())
1031     {
1032         addMimicExclusions(&top->excls, mtop.intermolecularExclusionGroup);
1033     }
1034 }
1035
1036 void gmx_mtop_generate_local_top(const gmx_mtop_t& mtop, gmx_localtop_t* top, bool freeEnergyInteractionsAtEnd)
1037 {
1038     gen_local_top(mtop, freeEnergyInteractionsAtEnd, true, top);
1039 }
1040
1041 /*! \brief Fills an array with molecule begin/end atom indices
1042  *
1043  * \param[in]  mtop   The global topology
1044  * \param[out] index  Array of size nr. of molecules + 1 to be filled with molecule begin/end indices
1045  */
1046 static void fillMoleculeIndices(const gmx_mtop_t& mtop, gmx::ArrayRef<int> index)
1047 {
1048     int globalAtomIndex   = 0;
1049     int globalMolIndex    = 0;
1050     index[globalMolIndex] = globalAtomIndex;
1051     for (const gmx_molblock_t& molb : mtop.molblock)
1052     {
1053         int numAtomsPerMolecule = mtop.moltype[molb.type].atoms.nr;
1054         for (int mol = 0; mol < molb.nmol; mol++)
1055         {
1056             globalAtomIndex += numAtomsPerMolecule;
1057             globalMolIndex += 1;
1058             index[globalMolIndex] = globalAtomIndex;
1059         }
1060     }
1061 }
1062
1063 gmx::RangePartitioning gmx_mtop_molecules(const gmx_mtop_t& mtop)
1064 {
1065     gmx::RangePartitioning mols;
1066
1067     for (const gmx_molblock_t& molb : mtop.molblock)
1068     {
1069         int numAtomsPerMolecule = mtop.moltype[molb.type].atoms.nr;
1070         for (int mol = 0; mol < molb.nmol; mol++)
1071         {
1072             mols.appendBlock(numAtomsPerMolecule);
1073         }
1074     }
1075
1076     return mols;
1077 }
1078
1079 /*! \brief Creates and returns a deprecated t_block struct with molecule indices
1080  *
1081  * \param[in] mtop  The global topology
1082  */
1083 static t_block gmx_mtop_molecules_t_block(const gmx_mtop_t& mtop)
1084 {
1085     t_block mols;
1086
1087     mols.nr           = gmx_mtop_num_molecules(mtop);
1088     mols.nalloc_index = mols.nr + 1;
1089     snew(mols.index, mols.nalloc_index);
1090
1091     fillMoleculeIndices(mtop, gmx::arrayRefFromArray(mols.index, mols.nr + 1));
1092
1093     return mols;
1094 }
1095
1096 static void gen_t_topology(const gmx_mtop_t& mtop,
1097                            bool              freeEnergyInteractionsAtEnd,
1098                            bool              bMergeConstr,
1099                            t_topology*       top)
1100 {
1101     copyAtomtypesFromMtop(mtop, &top->atomtypes);
1102     copyIdefFromMtop(mtop, &top->idef, freeEnergyInteractionsAtEnd, bMergeConstr);
1103     copyExclsFromMtop(mtop, &top->excls);
1104
1105     top->name                        = mtop.name;
1106     top->atoms                       = gmx_mtop_global_atoms(&mtop);
1107     top->mols                        = gmx_mtop_molecules_t_block(mtop);
1108     top->bIntermolecularInteractions = mtop.bIntermolecularInteractions;
1109     top->symtab                      = mtop.symtab;
1110 }
1111
1112 t_topology gmx_mtop_t_to_t_topology(gmx_mtop_t* mtop, bool freeMTop)
1113 {
1114     t_topology top;
1115
1116     gen_t_topology(*mtop, false, false, &top);
1117
1118     if (freeMTop)
1119     {
1120         // Clear pointers and counts, such that the pointers copied to top
1121         // keep pointing to valid data after destroying mtop.
1122         mtop->symtab.symbuf = nullptr;
1123         mtop->symtab.nr     = 0;
1124     }
1125     return top;
1126 }
1127
1128 std::vector<int> get_atom_index(const gmx_mtop_t* mtop)
1129 {
1130
1131     std::vector<int> atom_index;
1132     for (const AtomProxy atomP : AtomRange(*mtop))
1133     {
1134         const t_atom& local = atomP.atom();
1135         int           index = atomP.globalAtomNumber();
1136         if (local.ptype == eptAtom)
1137         {
1138             atom_index.push_back(index);
1139         }
1140     }
1141     return atom_index;
1142 }
1143
1144 void convertAtomsToMtop(t_symtab* symtab, char** name, t_atoms* atoms, gmx_mtop_t* mtop)
1145 {
1146     mtop->symtab = *symtab;
1147
1148     mtop->name = name;
1149
1150     mtop->moltype.clear();
1151     mtop->moltype.resize(1);
1152     mtop->moltype.back().atoms = *atoms;
1153
1154     mtop->molblock.resize(1);
1155     mtop->molblock[0].type = 0;
1156     mtop->molblock[0].nmol = 1;
1157
1158     mtop->bIntermolecularInteractions = FALSE;
1159
1160     mtop->natoms = atoms->nr;
1161
1162     mtop->haveMoleculeIndices = false;
1163
1164     gmx_mtop_finalize(mtop);
1165 }