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