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