5a6a4507bc3845ce3d261a1d3933d56d8f3eb94c
[alexxy/gromacs.git] / src / gromacs / selection / indexutil.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2009,2010,2011,2012,2013 by the GROMACS development team.
5  * Copyright (c) 2014,2015,2016,2017,2018 by the GROMACS development team.
6  * Copyright (c) 2019,2020, 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.
10  *
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.
15  *
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.
20  *
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.
25  *
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.
33  *
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.
36  */
37 /*! \internal \file
38  * \brief
39  * Implements functions in indexutil.h.
40  *
41  * \author Teemu Murtola <teemu.murtola@gmail.com>
42  * \ingroup module_selection
43  */
44 #include "gmxpre.h"
45
46 #include "indexutil.h"
47
48 #include <cstdlib>
49 #include <cstring>
50
51 #include <algorithm>
52 #include <numeric>
53 #include <string>
54 #include <vector>
55
56 #include "gromacs/topology/block.h"
57 #include "gromacs/topology/index.h"
58 #include "gromacs/topology/mtop_lookup.h"
59 #include "gromacs/topology/mtop_util.h"
60 #include "gromacs/topology/topology.h"
61 #include "gromacs/utility/exceptions.h"
62 #include "gromacs/utility/gmxassert.h"
63 #include "gromacs/utility/smalloc.h"
64 #include "gromacs/utility/stringutil.h"
65 #include "gromacs/utility/textwriter.h"
66
67 namespace gmx
68 {
69
70 IndexGroupsAndNames::IndexGroupsAndNames(const t_blocka& indexGroup, ArrayRef<char const* const> groupNames) :
71     indexGroup_{ indexGroup }
72 {
73     std::copy(groupNames.begin(), groupNames.end(), std::back_inserter(groupNames_));
74     GMX_ASSERT(indexGroup_.nr == ssize(groupNames),
75                "Number of groups must match number of group names.");
76 }
77
78 bool IndexGroupsAndNames::containsGroupName(const std::string& groupName) const
79 {
80     return std::any_of(
81             std::begin(groupNames_), std::end(groupNames_),
82             [&groupName](const std::string& name) { return equalCaseInsensitive(groupName, name); });
83 }
84
85 std::vector<index> IndexGroupsAndNames::indices(const std::string& groupName) const
86 {
87     if (!containsGroupName(groupName))
88     {
89         GMX_THROW(
90                 InconsistentInputError(
91                         std::string("Group ") + groupName
92                         + " referenced in the .mdp file was not found in the index file.\n"
93                           "Group names must match either [moleculetype] names or custom index "
94                           "group\n"
95                           "names, in which case you must supply an index file to the '-n' option\n"
96                           "of grompp."));
97     }
98     const auto groupNamePosition = std::find_if(
99             std::begin(groupNames_), std::end(groupNames_),
100             [&groupName](const std::string& name) { return equalCaseInsensitive(groupName, name); });
101     const auto groupIndex = std::distance(std::begin(groupNames_), groupNamePosition);
102     const auto groupSize  = indexGroup_.index[groupIndex + 1] - indexGroup_.index[groupIndex];
103     std::vector<index> groupIndices(groupSize);
104     const auto         startingIndex = indexGroup_.index[groupIndex];
105     std::iota(std::begin(groupIndices), std::end(groupIndices), startingIndex);
106     std::transform(std::begin(groupIndices), std::end(groupIndices), std::begin(groupIndices),
107                    [blockLookup = indexGroup_.a](auto i) { return blockLookup[i]; });
108     return groupIndices;
109 }
110
111 } // namespace gmx
112
113 /********************************************************************
114  * gmx_ana_indexgrps_t functions
115  ********************************************************************/
116
117 /*! \internal \brief
118  * Stores a set of index groups.
119  */
120 struct gmx_ana_indexgrps_t
121 {
122     //! Initializes an empty set of groups.
123     explicit gmx_ana_indexgrps_t(int nr) : g(nr) { names.reserve(nr); }
124     ~gmx_ana_indexgrps_t()
125     {
126         for (auto& indexGrp : g)
127         {
128             gmx_ana_index_deinit(&indexGrp);
129         }
130     }
131
132
133     /** Array of index groups. */
134     std::vector<gmx_ana_index_t> g;
135     /** Group names. */
136     std::vector<std::string> names;
137 };
138
139 /*!
140  * \param[out] g     Index group structure.
141  * \param[in]  top   Topology structure.
142  * \param[in]  fnm   File name for the index file.
143  *   Memory is automatically allocated.
144  *
145  * One or both of \p top or \p fnm can be NULL.
146  * If \p top is NULL, an index file is required and the groups are read
147  * from the file (uses Gromacs routine init_index()).
148  * If \p fnm is NULL, default groups are constructed based on the
149  * topology (uses Gromacs routine analyse()).
150  * If both are null, the index group structure is initialized empty.
151  */
152 void gmx_ana_indexgrps_init(gmx_ana_indexgrps_t** g, gmx_mtop_t* top, const char* fnm)
153 {
154     t_blocka* block = nullptr;
155     char**    names = nullptr;
156
157     if (fnm)
158     {
159         block = init_index(fnm, &names);
160     }
161     else if (top)
162     {
163         block = new_blocka();
164         // TODO: Propagate mtop further.
165         t_atoms atoms = gmx_mtop_global_atoms(top);
166         analyse(&atoms, block, &names, FALSE, FALSE);
167         done_atom(&atoms);
168     }
169     else
170     {
171         *g = new gmx_ana_indexgrps_t(0);
172         return;
173     }
174
175     try
176     {
177         *g = new gmx_ana_indexgrps_t(block->nr);
178         for (int i = 0; i < block->nr; ++i)
179         {
180             gmx_ana_index_t* grp = &(*g)->g[i];
181
182             grp->isize = block->index[i + 1] - block->index[i];
183             snew(grp->index, grp->isize);
184             for (int j = 0; j < grp->isize; ++j)
185             {
186                 grp->index[j] = block->a[block->index[i] + j];
187             }
188             grp->nalloc_index = grp->isize;
189             (*g)->names.emplace_back(names[i]);
190         }
191     }
192     catch (...)
193     {
194         for (int i = 0; i < block->nr; ++i)
195         {
196             sfree(names[i]);
197         }
198         sfree(names);
199         done_blocka(block);
200         sfree(block);
201         throw;
202     }
203     for (int i = 0; i < block->nr; ++i)
204     {
205         sfree(names[i]);
206     }
207     sfree(names);
208     done_blocka(block);
209     sfree(block);
210 }
211
212 /*!
213  * \param[in] g  Index groups structure.
214  *
215  * The pointer \p g is invalid after the call.
216  */
217 void gmx_ana_indexgrps_free(gmx_ana_indexgrps_t* g)
218 {
219     delete g;
220 }
221
222
223 /*!
224  * \param[out] dest     Output structure.
225  * \param[out] destName Receives the name of the group if found.
226  * \param[in]  src      Input index groups.
227  * \param[in]  n        Number of the group to extract.
228  * \returns true if \p n is a valid group in \p src, false otherwise.
229  */
230 bool gmx_ana_indexgrps_extract(gmx_ana_index_t* dest, std::string* destName, gmx_ana_indexgrps_t* src, int n)
231 {
232     destName->clear();
233     if (n < 0 || n >= gmx::index(src->g.size()))
234     {
235         dest->isize = 0;
236         return false;
237     }
238
239     if (destName != nullptr)
240     {
241         *destName = src->names[n];
242     }
243     gmx_ana_index_copy(dest, &src->g[n], true);
244     return true;
245 }
246
247 /*!
248  * \param[out] dest     Output structure.
249  * \param[out] destName Receives the name of the group if found.
250  * \param[in]  src      Input index groups.
251  * \param[in]  name     Name (or part of the name) of the group to extract.
252  * \returns true if \p name is a valid group in \p src, false otherwise.
253  *
254  * Uses the Gromacs routine find_group() to find the actual group;
255  * the comparison is case-insensitive.
256  */
257 bool gmx_ana_indexgrps_find(gmx_ana_index_t* dest, std::string* destName, gmx_ana_indexgrps_t* src, const char* name)
258 {
259     const char** names;
260
261     destName->clear();
262     snew(names, src->g.size());
263     for (size_t i = 0; i < src->g.size(); ++i)
264     {
265         names[i] = src->names[i].c_str();
266     }
267     int n = find_group(const_cast<char*>(name), src->g.size(), const_cast<char**>(names));
268     sfree(names);
269     if (n < 0)
270     {
271         dest->isize = 0;
272         return false;
273     }
274
275     return gmx_ana_indexgrps_extract(dest, destName, src, n);
276 }
277
278 /*!
279  * \param[in]  writer Writer to use for output.
280  * \param[in]  g      Index groups to print.
281  * \param[in]  maxn   Maximum number of indices to print
282  *      (-1 = print all, 0 = print only names).
283  */
284 void gmx_ana_indexgrps_print(gmx::TextWriter* writer, gmx_ana_indexgrps_t* g, int maxn)
285 {
286     for (gmx::index i = 0; i < gmx::ssize(g->g); ++i)
287     {
288         writer->writeString(gmx::formatString(" Group %2zd \"%s\" ", i, g->names[i].c_str()));
289         gmx_ana_index_dump(writer, &g->g[i], maxn);
290     }
291 }
292
293 /********************************************************************
294  * gmx_ana_index_t functions
295  ********************************************************************/
296
297 /*!
298  * \param[in,out] g      Index group structure.
299  * \param[in]     isize  Maximum number of atoms to reserve space for.
300  */
301 void gmx_ana_index_reserve(gmx_ana_index_t* g, int isize)
302 {
303     if (g->nalloc_index < isize)
304     {
305         srenew(g->index, isize);
306         g->nalloc_index = isize;
307     }
308 }
309
310 /*!
311  * \param[in,out] g      Index group structure.
312  *
313  * Resizes the memory allocated for holding the indices such that the
314  * current contents fit.
315  */
316 void gmx_ana_index_squeeze(gmx_ana_index_t* g)
317 {
318     srenew(g->index, g->isize);
319     g->nalloc_index = g->isize;
320 }
321
322 /*!
323  * \param[out] g      Output structure.
324  *
325  * Any contents of \p g are discarded without freeing.
326  */
327 void gmx_ana_index_clear(gmx_ana_index_t* g)
328 {
329     g->isize        = 0;
330     g->index        = nullptr;
331     g->nalloc_index = 0;
332 }
333
334 /*!
335  * \param[out] g      Output structure.
336  * \param[in]  isize  Number of atoms in the new group.
337  * \param[in]  index  Array of \p isize atoms (can be NULL if \p isize is 0).
338  * \param[in]  nalloc Number of elements allocated for \p index
339  *   (if 0, \p index is not freed in gmx_ana_index_deinit())
340  *
341  * No copy if \p index is made.
342  */
343 void gmx_ana_index_set(gmx_ana_index_t* g, int isize, int* index, int nalloc)
344 {
345     g->isize        = isize;
346     g->index        = index;
347     g->nalloc_index = nalloc;
348 }
349
350 /*!
351  * \param[out] g      Output structure.
352  * \param[in]  natoms Number of atoms.
353  */
354 void gmx_ana_index_init_simple(gmx_ana_index_t* g, int natoms)
355 {
356     int i;
357
358     g->isize = natoms;
359     snew(g->index, natoms);
360     for (i = 0; i < natoms; ++i)
361     {
362         g->index[i] = i;
363     }
364     g->nalloc_index = natoms;
365 }
366
367 /*!
368  * \param[in] g  Index group structure.
369  *
370  * The pointer \p g is not freed.
371  */
372 void gmx_ana_index_deinit(gmx_ana_index_t* g)
373 {
374     if (g->nalloc_index > 0)
375     {
376         sfree(g->index);
377     }
378     gmx_ana_index_clear(g);
379 }
380
381 /*!
382  * \param[out] dest   Destination index group.
383  * \param[in]  src    Source index group.
384  * \param[in]  bAlloc If true, memory is allocated at \p dest; otherwise,
385  *   it is assumed that enough memory has been allocated for index.
386  */
387 void gmx_ana_index_copy(gmx_ana_index_t* dest, gmx_ana_index_t* src, bool bAlloc)
388 {
389     dest->isize = src->isize;
390     if (bAlloc)
391     {
392         snew(dest->index, dest->isize);
393         dest->nalloc_index = dest->isize;
394     }
395     if (dest->isize > 0)
396     {
397         std::memcpy(dest->index, src->index, dest->isize * sizeof(*dest->index));
398     }
399 }
400
401 /*!
402  * \param[in]  writer Writer to use for output.
403  * \param[in]  g      Index group to print.
404  * \param[in]  maxn   Maximum number of indices to print (-1 = print all).
405  */
406 void gmx_ana_index_dump(gmx::TextWriter* writer, gmx_ana_index_t* g, int maxn)
407 {
408     writer->writeString(gmx::formatString("(%d atoms)", g->isize));
409     if (maxn != 0)
410     {
411         writer->writeString(":");
412         int n = g->isize;
413         if (maxn >= 0 && n > maxn)
414         {
415             n = maxn;
416         }
417         for (int j = 0; j < n; ++j)
418         {
419             writer->writeString(gmx::formatString(" %d", g->index[j] + 1));
420         }
421         if (n < g->isize)
422         {
423             writer->writeString(" ...");
424         }
425     }
426     writer->ensureLineBreak();
427 }
428
429 int gmx_ana_index_get_max_index(gmx_ana_index_t* g)
430 {
431     if (g->isize == 0)
432     {
433         return 0;
434     }
435     else
436     {
437         return *std::max_element(g->index, g->index + g->isize);
438     }
439 }
440
441 /*!
442  * \param[in]  g      Index group to check.
443  * \returns    true if the index group is sorted and has no duplicates,
444  *   false otherwise.
445  */
446 bool gmx_ana_index_check_sorted(gmx_ana_index_t* g)
447 {
448     int i;
449
450     for (i = 0; i < g->isize - 1; ++i)
451     {
452         if (g->index[i + 1] <= g->index[i])
453         {
454             return false;
455         }
456     }
457     return true;
458 }
459
460 bool gmx_ana_index_check_range(gmx_ana_index_t* g, int natoms)
461 {
462     for (int i = 0; i < g->isize; ++i)
463     {
464         if (g->index[i] < 0 || g->index[i] >= natoms)
465         {
466             return false;
467         }
468     }
469     return true;
470 }
471
472 /********************************************************************
473  * Set operations
474  ********************************************************************/
475
476 /*!
477  * \param[in,out] g  Index group to be sorted.
478  */
479 void gmx_ana_index_sort(gmx_ana_index_t* g)
480 {
481     std::sort(g->index, g->index + g->isize);
482 }
483
484 void gmx_ana_index_remove_duplicates(gmx_ana_index_t* g)
485 {
486     int j = 0;
487     for (int i = 0; i < g->isize; ++i)
488     {
489         if (i == 0 || g->index[i - 1] != g->index[i])
490         {
491             g->index[j] = g->index[i];
492             ++j;
493         }
494     }
495     g->isize = j;
496 }
497
498 /*!
499  * \param[in]  a      Index group to check.
500  * \param[in]  b      Index group to check.
501  * \returns    true if \p a and \p b are equal, false otherwise.
502  */
503 bool gmx_ana_index_equals(gmx_ana_index_t* a, gmx_ana_index_t* b)
504 {
505     int i;
506
507     if (a->isize != b->isize)
508     {
509         return false;
510     }
511     for (i = 0; i < a->isize; ++i)
512     {
513         if (a->index[i] != b->index[i])
514         {
515             return false;
516         }
517     }
518     return true;
519 }
520
521 /*!
522  * \param[in]  a      Index group to check against.
523  * \param[in]  b      Index group to check.
524  * \returns    true if \p b is contained in \p a,
525  *   false otherwise.
526  *
527  * If the elements are not in the same order in both groups, the function
528  * fails. However, the groups do not need to be sorted.
529  */
530 bool gmx_ana_index_contains(gmx_ana_index_t* a, gmx_ana_index_t* b)
531 {
532     int i, j;
533
534     for (i = j = 0; j < b->isize; ++i, ++j)
535     {
536         while (i < a->isize && a->index[i] != b->index[j])
537         {
538             ++i;
539         }
540         if (i == a->isize)
541         {
542             return false;
543         }
544     }
545     return true;
546 }
547
548 /*!
549  * \param[out] dest Output index group (the intersection of \p a and \p b).
550  * \param[in]  a    First index group.
551  * \param[in]  b    Second index group.
552  *
553  * \p dest can be the same as \p a or \p b.
554  */
555 void gmx_ana_index_intersection(gmx_ana_index_t* dest, gmx_ana_index_t* a, gmx_ana_index_t* b)
556 {
557     int i, j, k;
558
559     for (i = j = k = 0; i < a->isize && j < b->isize; ++i)
560     {
561         while (j < b->isize && b->index[j] < a->index[i])
562         {
563             ++j;
564         }
565         if (j < b->isize && b->index[j] == a->index[i])
566         {
567             dest->index[k++] = b->index[j++];
568         }
569     }
570     dest->isize = k;
571 }
572
573 /*!
574  * \param[out] dest Output index group (the difference \p a - \p b).
575  * \param[in]  a    First index group.
576  * \param[in]  b    Second index group.
577  *
578  * \p dest can equal \p a, but not \p b.
579  */
580 void gmx_ana_index_difference(gmx_ana_index_t* dest, gmx_ana_index_t* a, gmx_ana_index_t* b)
581 {
582     int i, j, k;
583
584     for (i = j = k = 0; i < a->isize; ++i)
585     {
586         while (j < b->isize && b->index[j] < a->index[i])
587         {
588             ++j;
589         }
590         if (j == b->isize || b->index[j] != a->index[i])
591         {
592             dest->index[k++] = a->index[i];
593         }
594     }
595     dest->isize = k;
596 }
597
598 /*!
599  * \param[in]  a    First index group.
600  * \param[in]  b    Second index group.
601  * \returns    Size of the difference \p a - \p b.
602  */
603 int gmx_ana_index_difference_size(gmx_ana_index_t* a, gmx_ana_index_t* b)
604 {
605     int i, j, k;
606
607     for (i = j = k = 0; i < a->isize; ++i)
608     {
609         while (j < b->isize && b->index[j] < a->index[i])
610         {
611             ++j;
612         }
613         if (j == b->isize || b->index[j] != a->index[i])
614         {
615             ++k;
616         }
617     }
618     return k;
619 }
620
621 /*!
622  * \param[out] dest1 Output group 1 (will equal \p g).
623  * \param[out] dest2 Output group 2 (will equal \p src - \p g).
624  * \param[in]  src   Group to be partitioned.
625  * \param[in]  g     One partition.
626  *
627  * \pre \p g is a subset of \p src and both sets are sorted
628  * \pre \p dest1 has allocated storage to store \p src
629  * \post \p dest1 == \p g
630  * \post \p dest2 == \p src - \p g
631  *
632  * No storage should be allocated for \p dest2; after the call,
633  * \p dest2->index points to the memory allocated for \p dest1
634  * (to a part that is not used by \p dest1).
635  *
636  * The calculation can be performed in-place by setting \p dest1 equal to
637  * \p src.
638  */
639 void gmx_ana_index_partition(gmx_ana_index_t* dest1, gmx_ana_index_t* dest2, gmx_ana_index_t* src, gmx_ana_index_t* g)
640 {
641     int i, j, k;
642
643     dest2->index = dest1->index + g->isize;
644     dest2->isize = src->isize - g->isize;
645     for (i = g->isize - 1, j = src->isize - 1, k = dest2->isize - 1; i >= 0; --i, --j)
646     {
647         while (j >= 0 && src->index[j] != g->index[i])
648         {
649             dest2->index[k--] = src->index[j--];
650         }
651     }
652     while (j >= 0)
653     {
654         dest2->index[k--] = src->index[j--];
655     }
656     gmx_ana_index_copy(dest1, g, false);
657 }
658
659 /*!
660  * \param[out] dest Output index group (the union of \p a and \p b).
661  * \param[in]  a    First index group.
662  * \param[in]  b    Second index group.
663  *
664  * \p a and \p b can have common items.
665  * \p dest can equal \p a or \p b.
666  *
667  * \see gmx_ana_index_merge()
668  */
669 void gmx_ana_index_union(gmx_ana_index_t* dest, gmx_ana_index_t* a, gmx_ana_index_t* b)
670 {
671     int dsize;
672     int i, j, k;
673
674     dsize       = gmx_ana_index_difference_size(b, a);
675     i           = a->isize - 1;
676     j           = b->isize - 1;
677     dest->isize = a->isize + dsize;
678     for (k = dest->isize - 1; k >= 0; k--)
679     {
680         if (i < 0 || (j >= 0 && a->index[i] < b->index[j]))
681         {
682             dest->index[k] = b->index[j--];
683         }
684         else
685         {
686             if (j >= 0 && a->index[i] == b->index[j])
687             {
688                 --j;
689             }
690             dest->index[k] = a->index[i--];
691         }
692     }
693 }
694
695 void gmx_ana_index_union_unsorted(gmx_ana_index_t* dest, gmx_ana_index_t* a, gmx_ana_index_t* b)
696 {
697     if (gmx_ana_index_check_sorted(b))
698     {
699         gmx_ana_index_union(dest, a, b);
700     }
701     else
702     {
703         gmx_ana_index_t tmp;
704         gmx_ana_index_copy(&tmp, b, true);
705         gmx_ana_index_sort(&tmp);
706         gmx_ana_index_remove_duplicates(&tmp);
707         gmx_ana_index_union(dest, a, &tmp);
708         gmx_ana_index_deinit(&tmp);
709     }
710 }
711
712 /*!
713  * \param[out] dest Output index group (the union of \p a and \p b).
714  * \param[in]  a    First index group.
715  * \param[in]  b    Second index group.
716  *
717  * \p a and \p b should not have common items.
718  * \p dest can equal \p a or \p b.
719  *
720  * \see gmx_ana_index_union()
721  */
722 void gmx_ana_index_merge(gmx_ana_index_t* dest, gmx_ana_index_t* a, gmx_ana_index_t* b)
723 {
724     int i, j, k;
725
726     i           = a->isize - 1;
727     j           = b->isize - 1;
728     dest->isize = a->isize + b->isize;
729     for (k = dest->isize - 1; k >= 0; k--)
730     {
731         if (i < 0 || (j >= 0 && a->index[i] < b->index[j]))
732         {
733             dest->index[k] = b->index[j--];
734         }
735         else
736         {
737             dest->index[k] = a->index[i--];
738         }
739     }
740 }
741
742 /********************************************************************
743  * gmx_ana_indexmap_t and related things
744  ********************************************************************/
745
746 /*! \brief
747  * Helper for splitting a sequence of atom indices into groups.
748  *
749  * \param[in]     atomIndex  Index of the next atom in the sequence.
750  * \param[in]     top        Topology structure.
751  * \param[in]     type       Type of group to split into.
752  * \param[in,out] id         Variable to receive the next group id.
753  * \returns  `true` if \p atomIndex starts a new group in the sequence, i.e.,
754  *     if \p *id was changed.
755  *
756  * \p *id should be initialized to `-1` before first call of this function, and
757  * then each atom index in the sequence passed to the function in turn.
758  *
759  * \ingroup module_selection
760  */
761 static bool next_group_index(int atomIndex, const gmx_mtop_t* top, e_index_t type, int* id)
762 {
763     int prev = *id;
764     switch (type)
765     {
766         case INDEX_ATOM: *id = atomIndex; break;
767         case INDEX_RES:
768         {
769             int resind, molb = 0;
770             mtopGetAtomAndResidueName(top, atomIndex, &molb, nullptr, nullptr, nullptr, &resind);
771             *id = resind;
772             break;
773         }
774         case INDEX_MOL:
775         {
776             int molb = 0;
777             *id      = mtopGetMoleculeIndex(top, atomIndex, &molb);
778             break;
779         }
780         case INDEX_UNKNOWN:
781         case INDEX_ALL: *id = 0; break;
782     }
783     return prev != *id;
784 }
785
786 /*!
787  * \param[in,out] t    Output block.
788  * \param[in]  top  Topology structure
789  *   (only used if \p type is \ref INDEX_RES or \ref INDEX_MOL, can be NULL
790  *   otherwise).
791  * \param[in]  g    Index group
792  *   (can be NULL if \p type is \ref INDEX_UNKNOWN).
793  * \param[in]  type Type of partitioning to make.
794  * \param[in]  bComplete
795  *   If true, the index group is expanded to include any residue/molecule
796  *   (depending on \p type) that is partially contained in the group.
797  *   If \p type is not INDEX_RES or INDEX_MOL, this has no effect.
798  *
799  * \p m should have been initialized somehow (calloc() is enough).
800  * \p g should be sorted.
801  */
802 void gmx_ana_index_make_block(t_blocka* t, const gmx_mtop_t* top, gmx_ana_index_t* g, e_index_t type, bool bComplete)
803 {
804     if (type == INDEX_UNKNOWN)
805     {
806         sfree(t->a);
807         srenew(t->index, 2);
808         t->nr           = 1;
809         t->nalloc_index = 2;
810         t->index[0]     = 0;
811         t->index[1]     = 0;
812         t->nra          = 0;
813         t->a            = nullptr;
814         t->nalloc_a     = 0;
815         return;
816     }
817
818     // TODO: Check callers and either check these there as well, or turn these
819     // into exceptions.
820     GMX_RELEASE_ASSERT(top != nullptr || (type != INDEX_RES && type != INDEX_MOL),
821                        "Topology must be provided for residue or molecule blocks");
822     GMX_RELEASE_ASSERT(type != INDEX_MOL || top->haveMoleculeIndices,
823                        "Molecule information must be present for molecule blocks");
824
825     /* bComplete only does something for INDEX_RES or INDEX_MOL, so turn it
826      * off otherwise. */
827     if (type != INDEX_RES && type != INDEX_MOL)
828     {
829         bComplete = false;
830     }
831     /* Allocate memory for the atom array and fill it unless we are using
832      * completion. */
833     if (bComplete)
834     {
835         t->nra = 0;
836         /* We may allocate some extra memory here because we don't know in
837          * advance how much will be needed. */
838         if (t->nalloc_a < top->natoms)
839         {
840             srenew(t->a, top->natoms);
841             t->nalloc_a = top->natoms;
842         }
843     }
844     else
845     {
846         t->nra = g->isize;
847         if (t->nalloc_a < g->isize)
848         {
849             srenew(t->a, g->isize);
850             t->nalloc_a = g->isize;
851         }
852         if (t->nra > 0)
853         {
854             std::memcpy(t->a, g->index, g->isize * sizeof(*(t->a)));
855         }
856     }
857
858     /* Allocate memory for the block index. We don't know in advance
859      * how much will be needed, so we allocate some extra and free it in the
860      * end. */
861     if (t->nalloc_index < g->isize + 1)
862     {
863         srenew(t->index, g->isize + 1);
864         t->nalloc_index = g->isize + 1;
865     }
866     /* Clear counters */
867     t->nr    = 0;
868     int id   = -1;
869     int molb = 0;
870     for (int i = 0; i < g->isize; ++i)
871     {
872         const int ai = g->index[i];
873         /* Find the ID number of the atom/residue/molecule corresponding to
874          * the atom. */
875         if (next_group_index(ai, top, type, &id))
876         {
877             /* If this is the first atom in a new block, initialize the block. */
878             if (bComplete)
879             {
880                 /* For completion, we first set the start of the block. */
881                 t->index[t->nr++] = t->nra;
882                 /* And then we find all the atoms that should be included. */
883                 switch (type)
884                 {
885                     case INDEX_RES:
886                     {
887                         int molnr, atnr_mol;
888                         mtopGetMolblockIndex(top, ai, &molb, &molnr, &atnr_mol);
889                         const t_atoms& mol_atoms    = top->moltype[top->molblock[molb].type].atoms;
890                         int            last_atom    = atnr_mol + 1;
891                         const int      currentResid = mol_atoms.atom[atnr_mol].resind;
892                         while (last_atom < mol_atoms.nr && mol_atoms.atom[last_atom].resind == currentResid)
893                         {
894                             ++last_atom;
895                         }
896                         int first_atom = atnr_mol - 1;
897                         while (first_atom >= 0 && mol_atoms.atom[first_atom].resind == currentResid)
898                         {
899                             --first_atom;
900                         }
901                         const MoleculeBlockIndices& molBlock = top->moleculeBlockIndices[molb];
902                         int                         first_mol_atom = molBlock.globalAtomStart;
903                         first_mol_atom += molnr * molBlock.numAtomsPerMolecule;
904                         first_atom = first_mol_atom + first_atom + 1;
905                         last_atom  = first_mol_atom + last_atom - 1;
906                         for (int j = first_atom; j <= last_atom; ++j)
907                         {
908                             t->a[t->nra++] = j;
909                         }
910                         break;
911                     }
912                     case INDEX_MOL:
913                     {
914                         int molnr, atnr_mol;
915                         mtopGetMolblockIndex(top, ai, &molb, &molnr, &atnr_mol);
916                         const MoleculeBlockIndices& blockIndices = top->moleculeBlockIndices[molb];
917                         const int                   atomStart    = blockIndices.globalAtomStart
918                                               + (id - blockIndices.moleculeIndexStart)
919                                                         * blockIndices.numAtomsPerMolecule;
920                         for (int j = 0; j < blockIndices.numAtomsPerMolecule; ++j)
921                         {
922                             t->a[t->nra++] = atomStart + j;
923                         }
924                         break;
925                     }
926                     default: /* Should not be reached */
927                         GMX_RELEASE_ASSERT(false, "Unreachable code was reached");
928                         break;
929                 }
930             }
931             else
932             {
933                 /* If not using completion, simply store the start of the block. */
934                 t->index[t->nr++] = i;
935             }
936         }
937     }
938     /* Set the end of the last block */
939     t->index[t->nr] = t->nra;
940     /* Free any unnecessary memory */
941     srenew(t->index, t->nr + 1);
942     t->nalloc_index = t->nr + 1;
943     if (bComplete)
944     {
945         srenew(t->a, t->nra);
946         t->nalloc_a = t->nra;
947     }
948 }
949
950 /*!
951  * \param[in] g   Index group to check.
952  * \param[in] b   Block data to check against.
953  * \returns   true if \p g consists of one or more complete blocks from \p b,
954  *   false otherwise.
955  *
956  * The atoms in \p g are assumed to be sorted.
957  */
958 bool gmx_ana_index_has_full_blocks(const gmx_ana_index_t* g, const gmx::RangePartitioning* b)
959 {
960     int i, j, bi;
961
962     i = bi = 0;
963     /* Each round in the loop matches one block */
964     while (i < g->isize)
965     {
966         /* Find the block that begins with the first unmatched atom */
967         while (bi < b->numBlocks() && *b->block(bi).begin() != g->index[i])
968         {
969             ++bi;
970         }
971         /* If not found, or if too large, return */
972         if (bi == b->numBlocks() || i + b->block(bi).size() > g->isize)
973         {
974             return false;
975         }
976         /* Check that the block matches the index */
977         for (j = *b->block(bi).begin(); j < *b->block(bi).end(); ++j, ++i)
978         {
979             if (g->index[i] != j)
980             {
981                 return false;
982             }
983         }
984         /* Move the search to the next block */
985         ++bi;
986     }
987     return true;
988 }
989
990 /*!
991  * \param[in] g   Index group to check.
992  * \param[in] b   Block data to check against.
993  * \returns   true if \p g consists of one or more complete blocks from \p b,
994  *   false otherwise.
995  *
996  * The atoms in \p g and \p b->a are assumed to be in the same order.
997  */
998 bool gmx_ana_index_has_full_ablocks(gmx_ana_index_t* g, t_blocka* b)
999 {
1000     int i, j, bi;
1001
1002     i = bi = 0;
1003     /* Each round in the loop matches one block */
1004     while (i < g->isize)
1005     {
1006         /* Find the block that begins with the first unmatched atom */
1007         while (bi < b->nr && b->a[b->index[bi]] != g->index[i])
1008         {
1009             ++bi;
1010         }
1011         /* If not found, or if too large, return */
1012         if (bi == b->nr || i + b->index[bi + 1] - b->index[bi] > g->isize)
1013         {
1014             return false;
1015         }
1016         /* Check that the block matches the index */
1017         for (j = b->index[bi]; j < b->index[bi + 1]; ++j, ++i)
1018         {
1019             if (b->a[j] != g->index[i])
1020             {
1021                 return false;
1022             }
1023         }
1024         /* Move the search to the next block */
1025         ++bi;
1026     }
1027     return true;
1028 }
1029
1030 /*!
1031  * \brief Returns if an atom is at a residue boundary.
1032  *
1033  * \param[in]     top   Topology data.
1034  * \param[in]     a     Atom index to check, should be -1 <= \p a < top->natoms.
1035  * \param[in,out] molb  The molecule block of atom a
1036  * \returns       true if atoms \p a and \p a + 1 are in different residues, false otherwise.
1037  */
1038 static bool is_at_residue_boundary(const gmx_mtop_t* top, int a, int* molb)
1039 {
1040     if (a == -1 || a + 1 == top->natoms)
1041     {
1042         return true;
1043     }
1044     int resindA;
1045     mtopGetAtomAndResidueName(top, a, molb, nullptr, nullptr, nullptr, &resindA);
1046     int resindAPlusOne;
1047     mtopGetAtomAndResidueName(top, a + 1, molb, nullptr, nullptr, nullptr, &resindAPlusOne);
1048     return resindAPlusOne != resindA;
1049 }
1050
1051 /*!
1052  * \param[in] g     Index group to check.
1053  * \param[in] type  Block data to check against.
1054  * \param[in] top   Topology data.
1055  * \returns   true if \p g consists of one or more complete elements of type
1056  *   \p type, false otherwise.
1057  *
1058  * \p g is assumed to be sorted, otherwise may return false negatives.
1059  *
1060  * If \p type is \ref INDEX_ATOM, the return value is always true.
1061  * If \p type is \ref INDEX_UNKNOWN or \ref INDEX_ALL, the return value is
1062  * always false.
1063  */
1064 bool gmx_ana_index_has_complete_elems(gmx_ana_index_t* g, e_index_t type, const gmx_mtop_t* top)
1065 {
1066     if (g->isize == 0)
1067     {
1068         return true;
1069     }
1070
1071     // TODO: Consider whether unsorted groups need to be supported better.
1072     switch (type)
1073     {
1074         case INDEX_UNKNOWN:
1075         case INDEX_ALL: return false;
1076
1077         case INDEX_ATOM: return true;
1078
1079         case INDEX_RES:
1080         {
1081             int molb  = 0;
1082             int aPrev = -1;
1083             for (int i = 0; i < g->isize; ++i)
1084             {
1085                 const int a = g->index[i];
1086                 // Check if a is consecutive or on a residue boundary
1087                 if (a != aPrev + 1)
1088                 {
1089                     if (!is_at_residue_boundary(top, aPrev, &molb))
1090                     {
1091                         return false;
1092                     }
1093                     if (!is_at_residue_boundary(top, a - 1, &molb))
1094                     {
1095                         return false;
1096                     }
1097                 }
1098                 aPrev = a;
1099             }
1100             GMX_ASSERT(g->isize > 0, "We return above when isize=0");
1101             const int a = g->index[g->isize - 1];
1102             if (!is_at_residue_boundary(top, a, &molb))
1103             {
1104                 return false;
1105             }
1106             break;
1107         }
1108
1109         case INDEX_MOL:
1110         {
1111             auto molecules = gmx_mtop_molecules(*top);
1112             return gmx_ana_index_has_full_blocks(g, &molecules);
1113         }
1114     }
1115     return true;
1116 }
1117
1118 /*!
1119  * \param[out] m      Output structure.
1120  *
1121  * Any contents of \p m are discarded without freeing.
1122  */
1123 void gmx_ana_indexmap_clear(gmx_ana_indexmap_t* m)
1124 {
1125     m->type              = INDEX_UNKNOWN;
1126     m->refid             = nullptr;
1127     m->mapid             = nullptr;
1128     m->mapb.nr           = 0;
1129     m->mapb.index        = nullptr;
1130     m->mapb.nalloc_index = 0;
1131     m->mapb.nra          = 0;
1132     m->mapb.a            = nullptr;
1133     m->mapb.nalloc_a     = 0;
1134     m->orgid             = nullptr;
1135     m->b.nr              = 0;
1136     m->b.index           = nullptr;
1137     m->b.nra             = 0;
1138     m->b.a               = nullptr;
1139     m->b.nalloc_index    = 0;
1140     m->b.nalloc_a        = 0;
1141     m->bStatic           = true;
1142 }
1143
1144 /*!
1145  * \param[in,out] m      Mapping structure.
1146  * \param[in]     nr     Maximum number of blocks to reserve space for.
1147  * \param[in]     isize  Maximum number of atoms to reserve space for.
1148  */
1149 void gmx_ana_indexmap_reserve(gmx_ana_indexmap_t* m, int nr, int isize)
1150 {
1151     if (m->mapb.nalloc_index < nr + 1)
1152     {
1153         srenew(m->refid, nr);
1154         srenew(m->mapid, nr);
1155         srenew(m->orgid, nr);
1156         srenew(m->mapb.index, nr + 1);
1157         srenew(m->b.index, nr + 1);
1158         m->mapb.nalloc_index = nr + 1;
1159         m->b.nalloc_index    = nr + 1;
1160     }
1161     if (m->b.nalloc_a < isize)
1162     {
1163         srenew(m->b.a, isize);
1164         m->b.nalloc_a = isize;
1165     }
1166 }
1167
1168 /*!
1169  * \param[in,out] m    Mapping structure to initialize.
1170  * \param[in]     g    Index group to map
1171  *   (can be NULL if \p type is \ref INDEX_UNKNOWN).
1172  * \param[in]     top  Topology structure
1173  *   (can be NULL if \p type is not \ref INDEX_RES or \ref INDEX_MOL).
1174  * \param[in]     type Type of mapping to construct.
1175  *
1176  * Initializes a new index group mapping.
1177  * The index group provided to gmx_ana_indexmap_update() should always be a
1178  * subset of the \p g given here.
1179  *
1180  * \p m should have been initialized somehow (calloc() is enough).
1181  */
1182 void gmx_ana_indexmap_init(gmx_ana_indexmap_t* m, gmx_ana_index_t* g, const gmx_mtop_t* top, e_index_t type)
1183 {
1184     m->type = type;
1185     gmx_ana_index_make_block(&m->b, top, g, type, false);
1186     gmx_ana_indexmap_reserve(m, m->b.nr, m->b.nra);
1187     int id = -1;
1188     for (int i = 0; i < m->b.nr; ++i)
1189     {
1190         const int ii = (type == INDEX_UNKNOWN ? 0 : m->b.a[m->b.index[i]]);
1191         next_group_index(ii, top, type, &id);
1192         m->refid[i] = i;
1193         m->mapid[i] = id;
1194         m->orgid[i] = id;
1195     }
1196     m->mapb.nr  = m->b.nr;
1197     m->mapb.nra = m->b.nra;
1198     m->mapb.a   = m->b.a;
1199     std::memcpy(m->mapb.index, m->b.index, (m->b.nr + 1) * sizeof(*(m->mapb.index)));
1200     m->bStatic = true;
1201 }
1202
1203 int gmx_ana_indexmap_init_orgid_group(gmx_ana_indexmap_t* m, const gmx_mtop_t* top, e_index_t type)
1204 {
1205     GMX_RELEASE_ASSERT(m->bStatic,
1206                        "Changing original IDs is not supported after starting "
1207                        "to use the mapping");
1208     GMX_RELEASE_ASSERT(top != nullptr || (type != INDEX_RES && type != INDEX_MOL),
1209                        "Topology must be provided for residue or molecule blocks");
1210     // Check that all atoms in each block belong to the same group.
1211     // This is a separate loop for better error handling (no state is modified
1212     // if there is an error.
1213     if (type == INDEX_RES || type == INDEX_MOL)
1214     {
1215         int id = -1;
1216         for (int i = 0; i < m->b.nr; ++i)
1217         {
1218             const int ii = m->b.a[m->b.index[i]];
1219             if (next_group_index(ii, top, type, &id))
1220             {
1221                 for (int j = m->b.index[i] + 1; j < m->b.index[i + 1]; ++j)
1222                 {
1223                     if (next_group_index(m->b.a[j], top, type, &id))
1224                     {
1225                         std::string message("Grouping into residues/molecules is ambiguous");
1226                         GMX_THROW(gmx::InconsistentInputError(message));
1227                     }
1228                 }
1229             }
1230         }
1231     }
1232     // Do a second loop, where things are actually set.
1233     int id    = -1;
1234     int group = -1;
1235     for (int i = 0; i < m->b.nr; ++i)
1236     {
1237         const int ii = (type == INDEX_UNKNOWN ? 0 : m->b.a[m->b.index[i]]);
1238         if (next_group_index(ii, top, type, &id))
1239         {
1240             ++group;
1241         }
1242         m->mapid[i] = group;
1243         m->orgid[i] = group;
1244     }
1245     // Count also the last group.
1246     ++group;
1247     return group;
1248 }
1249
1250 /*!
1251  * \param[in,out] m    Mapping structure to initialize.
1252  * \param[in]     b    Block information to use for data.
1253  *
1254  * Frees some memory that is not necessary for static index group mappings.
1255  * Internal pointers are set to point to data in \p b; it is the responsibility
1256  * of the caller to ensure that the block information matches the contents of
1257  * the mapping.
1258  * After this function has been called, the index group provided to
1259  * gmx_ana_indexmap_update() should always be the same as \p g given here.
1260  *
1261  * This function breaks modularity of the index group mapping interface in an
1262  * ugly way, but allows reducing memory usage of static selections by a
1263  * significant amount.
1264  */
1265 void gmx_ana_indexmap_set_static(gmx_ana_indexmap_t* m, t_blocka* b)
1266 {
1267     sfree(m->mapid);
1268     sfree(m->mapb.index);
1269     sfree(m->b.index);
1270     sfree(m->b.a);
1271     m->mapb.nalloc_index = 0;
1272     m->mapb.nalloc_a     = 0;
1273     m->b.nalloc_index    = 0;
1274     m->b.nalloc_a        = 0;
1275     m->mapid             = m->orgid;
1276     m->mapb.index        = b->index;
1277     m->mapb.a            = b->a;
1278     m->b.index           = b->index;
1279     m->b.a               = b->a;
1280 }
1281
1282 /*!
1283  * \param[in,out] dest Destination data structure.
1284  * \param[in]     src  Source mapping.
1285  * \param[in]     bFirst If true, memory is allocated for \p dest and a full
1286  *   copy is made; otherwise, only variable parts are copied, and no memory
1287  *   is allocated.
1288  *
1289  * \p dest should have been initialized somehow (calloc() is enough).
1290  */
1291 void gmx_ana_indexmap_copy(gmx_ana_indexmap_t* dest, gmx_ana_indexmap_t* src, bool bFirst)
1292 {
1293     if (bFirst)
1294     {
1295         gmx_ana_indexmap_reserve(dest, src->b.nr, src->b.nra);
1296         dest->type  = src->type;
1297         dest->b.nr  = src->b.nr;
1298         dest->b.nra = src->b.nra;
1299         std::memcpy(dest->orgid, src->orgid, dest->b.nr * sizeof(*dest->orgid));
1300         std::memcpy(dest->b.index, src->b.index, (dest->b.nr + 1) * sizeof(*dest->b.index));
1301         if (dest->b.nra > 0)
1302         {
1303             std::memcpy(dest->b.a, src->b.a, dest->b.nra * sizeof(*dest->b.a));
1304         }
1305     }
1306     dest->mapb.nr  = src->mapb.nr;
1307     dest->mapb.nra = src->mapb.nra;
1308     if (src->mapb.nalloc_a > 0)
1309     {
1310         if (bFirst)
1311         {
1312             snew(dest->mapb.a, src->mapb.nalloc_a);
1313             dest->mapb.nalloc_a = src->mapb.nalloc_a;
1314         }
1315         std::memcpy(dest->mapb.a, src->mapb.a, dest->mapb.nra * sizeof(*dest->mapb.a));
1316     }
1317     else
1318     {
1319         dest->mapb.a = src->mapb.a;
1320     }
1321     std::memcpy(dest->refid, src->refid, dest->mapb.nr * sizeof(*dest->refid));
1322     std::memcpy(dest->mapid, src->mapid, dest->mapb.nr * sizeof(*dest->mapid));
1323     std::memcpy(dest->mapb.index, src->mapb.index, (dest->mapb.nr + 1) * sizeof(*dest->mapb.index));
1324     dest->bStatic = src->bStatic;
1325 }
1326
1327 /*! \brief
1328  * Helper function to set the source atoms in an index map.
1329  *
1330  * \param[in,out] m     Mapping structure.
1331  * \param[in]     isize Number of atoms in the \p index array.
1332  * \param[in]     index List of atoms.
1333  */
1334 static void set_atoms(gmx_ana_indexmap_t* m, int isize, int* index)
1335 {
1336     m->mapb.nra = isize;
1337     if (m->mapb.nalloc_a == 0)
1338     {
1339         m->mapb.a = index;
1340     }
1341     else
1342     {
1343         for (int i = 0; i < isize; ++i)
1344         {
1345             m->mapb.a[i] = index[i];
1346         }
1347     }
1348 }
1349
1350 /*!
1351  * \param[in,out] m         Mapping structure.
1352  * \param[in]     g         Current index group.
1353  * \param[in]     bMaskOnly true if the unused blocks should be masked with
1354  *   -1 instead of removing them.
1355  *
1356  * Updates the index group mapping with the new index group \p g.
1357  *
1358  * \see gmx_ana_indexmap_t
1359  */
1360 void gmx_ana_indexmap_update(gmx_ana_indexmap_t* m, gmx_ana_index_t* g, bool bMaskOnly)
1361 {
1362     int i, j, bi, bj;
1363
1364     /* Process the simple cases first */
1365     if (m->type == INDEX_UNKNOWN && m->b.nra == 0)
1366     {
1367         return;
1368     }
1369     if (m->type == INDEX_ALL)
1370     {
1371         set_atoms(m, g->isize, g->index);
1372         if (m->b.nr > 0)
1373         {
1374             m->mapb.index[1] = g->isize;
1375         }
1376         return;
1377     }
1378     /* Reset the reference IDs and mapping if necessary */
1379     const bool bToFull  = (g->isize == m->b.nra);
1380     const bool bWasFull = (m->mapb.nra == m->b.nra);
1381     if (bToFull || bMaskOnly)
1382     {
1383         if (!m->bStatic)
1384         {
1385             for (bj = 0; bj < m->b.nr; ++bj)
1386             {
1387                 m->refid[bj] = bj;
1388             }
1389         }
1390         if (!bWasFull)
1391         {
1392             for (bj = 0; bj < m->b.nr; ++bj)
1393             {
1394                 m->mapid[bj] = m->orgid[bj];
1395             }
1396             for (bj = 0; bj <= m->b.nr; ++bj)
1397             {
1398                 m->mapb.index[bj] = m->b.index[bj];
1399             }
1400         }
1401         set_atoms(m, m->b.nra, m->b.a);
1402         m->mapb.nr = m->b.nr;
1403     }
1404     /* Exit immediately if the group is static */
1405     if (bToFull)
1406     {
1407         m->bStatic = true;
1408         return;
1409     }
1410
1411     if (bMaskOnly)
1412     {
1413         for (i = j = bj = 0; i < g->isize; ++i, ++j)
1414         {
1415             /* Find the next atom in the block */
1416             while (m->b.a[j] != g->index[i])
1417             {
1418                 ++j;
1419             }
1420             /* Mark blocks that did not contain any atoms */
1421             while (bj < m->b.nr && m->b.index[bj + 1] <= j)
1422             {
1423                 m->refid[bj++] = -1;
1424             }
1425             /* Advance the block index if we have reached the next block */
1426             if (m->b.index[bj] <= j)
1427             {
1428                 ++bj;
1429             }
1430         }
1431         /* Mark the last blocks as not accessible */
1432         while (bj < m->b.nr)
1433         {
1434             m->refid[bj++] = -1;
1435         }
1436     }
1437     else
1438     {
1439         set_atoms(m, g->isize, g->index);
1440         for (i = j = bi = 0, bj = -1; i < g->isize; ++i)
1441         {
1442             /* Find the next atom in the block */
1443             while (m->b.a[j] != g->index[i])
1444             {
1445                 ++j;
1446             }
1447             /* If we have reached a new block, add it */
1448             if (m->b.index[bj + 1] <= j)
1449             {
1450                 /* Skip any blocks in between */
1451                 while (bj < m->b.nr && m->b.index[bj + 1] <= j)
1452                 {
1453                     ++bj;
1454                 }
1455                 m->refid[bi]      = bj;
1456                 m->mapid[bi]      = m->orgid[bj];
1457                 m->mapb.index[bi] = i;
1458                 bi++;
1459             }
1460         }
1461         /* Update the number of blocks */
1462         m->mapb.index[bi] = g->isize;
1463         m->mapb.nr        = bi;
1464     }
1465     m->bStatic = false;
1466 }
1467
1468 /*!
1469  * \param[in,out] m         Mapping structure to free.
1470  *
1471  * All the memory allocated for the mapping structure is freed, and
1472  * the pointers set to NULL.
1473  * The pointer \p m is not freed.
1474  */
1475 void gmx_ana_indexmap_deinit(gmx_ana_indexmap_t* m)
1476 {
1477     sfree(m->refid);
1478     if (m->mapid != m->orgid)
1479     {
1480         sfree(m->mapid);
1481     }
1482     if (m->mapb.nalloc_index > 0)
1483     {
1484         sfree(m->mapb.index);
1485     }
1486     if (m->mapb.nalloc_a > 0)
1487     {
1488         sfree(m->mapb.a);
1489     }
1490     sfree(m->orgid);
1491     if (m->b.nalloc_index > 0)
1492     {
1493         sfree(m->b.index);
1494     }
1495     if (m->b.nalloc_a > 0)
1496     {
1497         sfree(m->b.a);
1498     }
1499     gmx_ana_indexmap_clear(m);
1500 }