2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2009,2010,2011,2012,2013,2014,2015, 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.
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.
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.
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.
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.
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.
37 * Implements functions in indexutil.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_selection
44 #include "indexutil.h"
53 #include "gromacs/topology/block.h"
54 #include "gromacs/topology/index.h"
55 #include "gromacs/topology/topology.h"
56 #include "gromacs/utility/exceptions.h"
57 #include "gromacs/utility/gmxassert.h"
58 #include "gromacs/utility/smalloc.h"
60 /********************************************************************
61 * gmx_ana_indexgrps_t functions
62 ********************************************************************/
65 * Stores a set of index groups.
67 struct gmx_ana_indexgrps_t
69 //! Initializes an empty set of groups.
70 explicit gmx_ana_indexgrps_t(int nr) : nr(nr), g(NULL)
75 ~gmx_ana_indexgrps_t()
77 for (int i = 0; i < nr; ++i)
79 gmx_ana_index_deinit(&g[i]);
84 /** Number of index groups. */
86 /** Array of index groups. */
89 std::vector<std::string> names;
93 * \param[out] g Index group structure.
94 * \param[in] top Topology structure.
95 * \param[in] fnm File name for the index file.
96 * Memory is automatically allocated.
98 * One or both of \p top or \p fnm can be NULL.
99 * If \p top is NULL, an index file is required and the groups are read
100 * from the file (uses Gromacs routine init_index()).
101 * If \p fnm is NULL, default groups are constructed based on the
102 * topology (uses Gromacs routine analyse()).
103 * If both are null, the index group structure is initialized empty.
106 gmx_ana_indexgrps_init(gmx_ana_indexgrps_t **g, t_topology *top,
109 t_blocka *block = NULL;
114 block = init_index(fnm, &names);
118 block = new_blocka();
119 analyse(&top->atoms, block, &names, FALSE, FALSE);
123 *g = new gmx_ana_indexgrps_t(0);
129 *g = new gmx_ana_indexgrps_t(block->nr);
130 for (int i = 0; i < block->nr; ++i)
132 gmx_ana_index_t *grp = &(*g)->g[i];
134 grp->isize = block->index[i+1] - block->index[i];
135 snew(grp->index, grp->isize);
136 for (int j = 0; j < grp->isize; ++j)
138 grp->index[j] = block->a[block->index[i]+j];
140 grp->nalloc_index = grp->isize;
141 (*g)->names.push_back(names[i]);
146 for (int i = 0; i < block->nr; ++i)
155 for (int i = 0; i < block->nr; ++i)
165 * \param[in] g Index groups structure.
167 * The pointer \p g is invalid after the call.
170 gmx_ana_indexgrps_free(gmx_ana_indexgrps_t *g)
176 * \param[out] g Index group structure.
177 * \returns true if \p g is empty, i.e., has 0 index groups.
180 gmx_ana_indexgrps_is_empty(gmx_ana_indexgrps_t *g)
186 * \param[in] g Index groups structure.
187 * \param[in] n Index group number to get.
188 * \returns Pointer to the \p n'th index group in \p g.
190 * The returned pointer should not be freed.
193 gmx_ana_indexgrps_get_grp(gmx_ana_indexgrps_t *g, int n)
195 if (n < 0 || n >= g->nr)
203 * \param[out] dest Output structure.
204 * \param[out] destName Receives the name of the group if found.
205 * \param[in] src Input index groups.
206 * \param[in] n Number of the group to extract.
207 * \returns true if \p n is a valid group in \p src, false otherwise.
210 gmx_ana_indexgrps_extract(gmx_ana_index_t *dest, std::string *destName,
211 gmx_ana_indexgrps_t *src, int n)
214 if (n < 0 || n >= src->nr)
220 if (destName != NULL)
222 *destName = src->names[n];
224 gmx_ana_index_copy(dest, &src->g[n], true);
229 * \param[out] dest Output structure.
230 * \param[out] destName Receives the name of the group if found.
231 * \param[in] src Input index groups.
232 * \param[in] name Name (or part of the name) of the group to extract.
233 * \returns true if \p name is a valid group in \p src, false otherwise.
235 * Uses the Gromacs routine find_group() to find the actual group;
236 * the comparison is case-insensitive.
239 gmx_ana_indexgrps_find(gmx_ana_index_t *dest, std::string *destName,
240 gmx_ana_indexgrps_t *src,
246 snew(names, src->nr);
247 for (int i = 0; i < src->nr; ++i)
249 names[i] = src->names[i].c_str();
251 int n = find_group(const_cast<char *>(name), src->nr,
252 const_cast<char **>(names));
260 return gmx_ana_indexgrps_extract(dest, destName, src, n);
264 * \param[in] fp Where to print the output.
265 * \param[in] g Index groups to print.
266 * \param[in] maxn Maximum number of indices to print
267 * (-1 = print all, 0 = print only names).
270 gmx_ana_indexgrps_print(FILE *fp, gmx_ana_indexgrps_t *g, int maxn)
272 for (int i = 0; i < g->nr; ++i)
274 fprintf(fp, " Group %2d \"%s\" ", i, g->names[i].c_str());
275 gmx_ana_index_dump(fp, &g->g[i], maxn);
279 /********************************************************************
280 * gmx_ana_index_t functions
281 ********************************************************************/
284 * \param[in,out] g Index group structure.
285 * \param[in] isize Maximum number of atoms to reserve space for.
288 gmx_ana_index_reserve(gmx_ana_index_t *g, int isize)
290 if (g->nalloc_index < isize)
292 srenew(g->index, isize);
293 g->nalloc_index = isize;
298 * \param[in,out] g Index group structure.
300 * Resizes the memory allocated for holding the indices such that the
301 * current contents fit.
304 gmx_ana_index_squeeze(gmx_ana_index_t *g)
306 srenew(g->index, g->isize);
307 g->nalloc_index = g->isize;
311 * \param[out] g Output structure.
313 * Any contents of \p g are discarded without freeing.
316 gmx_ana_index_clear(gmx_ana_index_t *g)
324 * \param[out] g Output structure.
325 * \param[in] isize Number of atoms in the new group.
326 * \param[in] index Array of \p isize atoms (can be NULL if \p isize is 0).
327 * \param[in] nalloc Number of elements allocated for \p index
328 * (if 0, \p index is not freed in gmx_ana_index_deinit())
330 * No copy if \p index is made.
333 gmx_ana_index_set(gmx_ana_index_t *g, int isize, atom_id *index, int nalloc)
337 g->nalloc_index = nalloc;
341 * \param[out] g Output structure.
342 * \param[in] natoms Number of atoms.
345 gmx_ana_index_init_simple(gmx_ana_index_t *g, int natoms)
350 snew(g->index, natoms);
351 for (i = 0; i < natoms; ++i)
355 g->nalloc_index = natoms;
359 * \param[in] g Index group structure.
361 * The pointer \p g is not freed.
364 gmx_ana_index_deinit(gmx_ana_index_t *g)
366 if (g->nalloc_index > 0)
370 gmx_ana_index_clear(g);
374 * \param[out] dest Destination index group.
375 * \param[in] src Source index group.
376 * \param[in] bAlloc If true, memory is allocated at \p dest; otherwise,
377 * it is assumed that enough memory has been allocated for index.
379 * A deep copy of the name is only made if \p bAlloc is true.
382 gmx_ana_index_copy(gmx_ana_index_t *dest, gmx_ana_index_t *src, bool bAlloc)
384 dest->isize = src->isize;
389 snew(dest->index, dest->isize);
390 dest->nalloc_index = dest->isize;
392 std::memcpy(dest->index, src->index, dest->isize*sizeof(*dest->index));
397 * \param[in] fp Where to print the output.
398 * \param[in] g Index group to print.
399 * \param[in] maxn Maximum number of indices to print (-1 = print all).
402 gmx_ana_index_dump(FILE *fp, gmx_ana_index_t *g, int maxn)
406 fprintf(fp, "(%d atoms)", g->isize);
411 if (maxn >= 0 && n > maxn)
415 for (j = 0; j < n; ++j)
417 fprintf(fp, " %d", g->index[j]+1);
428 gmx_ana_index_get_max_index(gmx_ana_index_t *g)
436 return *std::max_element(g->index, g->index + g->isize);
441 * \param[in] g Index group to check.
442 * \returns true if the index group is sorted and has no duplicates,
446 gmx_ana_index_check_sorted(gmx_ana_index_t *g)
450 for (i = 0; i < g->isize-1; ++i)
452 if (g->index[i+1] <= g->index[i])
461 gmx_ana_index_check_range(gmx_ana_index_t *g, int natoms)
463 for (int i = 0; i < g->isize; ++i)
465 if (g->index[i] < 0 || g->index[i] >= natoms)
473 /********************************************************************
475 ********************************************************************/
477 /** Helper function for gmx_ana_index_sort(). */
479 cmp_atomid(const void *a, const void *b)
481 if (*(atom_id *)a < *(atom_id *)b)
485 if (*(atom_id *)a > *(atom_id *)b)
493 * \param[in,out] g Index group to be sorted.
496 gmx_ana_index_sort(gmx_ana_index_t *g)
498 std::qsort(g->index, g->isize, sizeof(*g->index), cmp_atomid);
502 * \param[in] a Index group to check.
503 * \param[in] b Index group to check.
504 * \returns true if \p a and \p b are equal, false otherwise.
507 gmx_ana_index_equals(gmx_ana_index_t *a, gmx_ana_index_t *b)
511 if (a->isize != b->isize)
515 for (i = 0; i < a->isize; ++i)
517 if (a->index[i] != b->index[i])
526 * \param[in] a Index group to check against.
527 * \param[in] b Index group to check.
528 * \returns true if \p b is contained in \p a,
531 * If the elements are not in the same order in both groups, the function
532 * fails. However, the groups do not need to be sorted.
535 gmx_ana_index_contains(gmx_ana_index_t *a, gmx_ana_index_t *b)
539 for (i = j = 0; j < b->isize; ++i, ++j)
541 while (i < a->isize && a->index[i] != b->index[j])
554 * \param[out] dest Output index group (the intersection of \p a and \p b).
555 * \param[in] a First index group.
556 * \param[in] b Second index group.
558 * \p dest can be the same as \p a or \p b.
561 gmx_ana_index_intersection(gmx_ana_index_t *dest,
562 gmx_ana_index_t *a, gmx_ana_index_t *b)
566 for (i = j = k = 0; i < a->isize && j < b->isize; ++i)
568 while (j < b->isize && b->index[j] < a->index[i])
572 if (j < b->isize && b->index[j] == a->index[i])
574 dest->index[k++] = b->index[j++];
581 * \param[out] dest Output index group (the difference \p a - \p b).
582 * \param[in] a First index group.
583 * \param[in] b Second index group.
585 * \p dest can equal \p a, but not \p b.
588 gmx_ana_index_difference(gmx_ana_index_t *dest,
589 gmx_ana_index_t *a, gmx_ana_index_t *b)
593 for (i = j = k = 0; i < a->isize; ++i)
595 while (j < b->isize && b->index[j] < a->index[i])
599 if (j == b->isize || b->index[j] != a->index[i])
601 dest->index[k++] = a->index[i];
608 * \param[in] a First index group.
609 * \param[in] b Second index group.
610 * \returns Size of the difference \p a - \p b.
613 gmx_ana_index_difference_size(gmx_ana_index_t *a, gmx_ana_index_t *b)
617 for (i = j = k = 0; i < a->isize; ++i)
619 while (j < b->isize && b->index[j] < a->index[i])
623 if (j == b->isize || b->index[j] != a->index[i])
632 * \param[out] dest1 Output group 1 (will equal \p g).
633 * \param[out] dest2 Output group 2 (will equal \p src - \p g).
634 * \param[in] src Group to be partitioned.
635 * \param[in] g One partition.
637 * \pre \p g is a subset of \p src and both sets are sorted
638 * \pre \p dest1 has allocated storage to store \p src
639 * \post \p dest1 == \p g
640 * \post \p dest2 == \p src - \p g
642 * No storage should be allocated for \p dest2; after the call,
643 * \p dest2->index points to the memory allocated for \p dest1
644 * (to a part that is not used by \p dest1).
646 * The calculation can be performed in-place by setting \p dest1 equal to
650 gmx_ana_index_partition(gmx_ana_index_t *dest1, gmx_ana_index_t *dest2,
651 gmx_ana_index_t *src, gmx_ana_index_t *g)
655 dest2->index = dest1->index + g->isize;
656 dest2->isize = src->isize - g->isize;
657 for (i = g->isize-1, j = src->isize-1, k = dest2->isize-1; i >= 0; --i, --j)
659 while (j >= 0 && src->index[j] != g->index[i])
661 dest2->index[k--] = src->index[j--];
666 dest2->index[k--] = src->index[j--];
668 gmx_ana_index_copy(dest1, g, false);
672 * \param[out] dest Output index group (the union of \p a and \p b).
673 * \param[in] a First index group.
674 * \param[in] b Second index group.
676 * \p a and \p b can have common items.
677 * \p dest can equal \p a or \p b.
679 * \see gmx_ana_index_merge()
682 gmx_ana_index_union(gmx_ana_index_t *dest,
683 gmx_ana_index_t *a, gmx_ana_index_t *b)
688 dsize = gmx_ana_index_difference_size(b, a);
691 dest->isize = a->isize + dsize;
692 for (k = dest->isize - 1; k >= 0; k--)
694 if (i < 0 || (j >= 0 && a->index[i] < b->index[j]))
696 dest->index[k] = b->index[j--];
700 if (j >= 0 && a->index[i] == b->index[j])
704 dest->index[k] = a->index[i--];
710 * \param[out] dest Output index group (the union of \p a and \p b).
711 * \param[in] a First index group.
712 * \param[in] b Second index group.
714 * \p a and \p b should not have common items.
715 * \p dest can equal \p a or \p b.
717 * \see gmx_ana_index_union()
720 gmx_ana_index_merge(gmx_ana_index_t *dest,
721 gmx_ana_index_t *a, gmx_ana_index_t *b)
727 dest->isize = a->isize + b->isize;
728 for (k = dest->isize - 1; k >= 0; k--)
730 if (i < 0 || (j >= 0 && a->index[i] < b->index[j]))
732 dest->index[k] = b->index[j--];
736 dest->index[k] = a->index[i--];
741 /********************************************************************
742 * gmx_ana_indexmap_t and related things
743 ********************************************************************/
746 * Helper for splitting a sequence of atom indices into groups.
748 * \param[in] atomIndex Index of the next atom in the sequence.
749 * \param[in] top Topology structure.
750 * \param[in] type Type of group to split into.
751 * \param[in,out] id Variable to receive the next group id.
752 * \returns `true` if \p atomIndex starts a new group in the sequence, i.e.,
753 * if \p *id was changed.
755 * \p *id should be initialized to `-1` before first call of this function, and
756 * then each atom index in the sequence passed to the function in turn.
758 * \ingroup module_selection
761 next_group_index(int atomIndex, t_topology *top, e_index_t type, int *id)
770 *id = top->atoms.atom[atomIndex].resind;
773 if (*id >= 0 && top->mols.index[*id] > atomIndex)
777 while (*id < top->mols.nr && atomIndex >= top->mols.index[*id+1])
781 GMX_ASSERT(*id < top->mols.nr, "Molecules do not span all the atoms");
792 * \param[in,out] t Output block.
793 * \param[in] top Topology structure
794 * (only used if \p type is \ref INDEX_RES or \ref INDEX_MOL, can be NULL
796 * \param[in] g Index group
797 * (can be NULL if \p type is \ref INDEX_UNKNOWN).
798 * \param[in] type Type of partitioning to make.
799 * \param[in] bComplete
800 * If true, the index group is expanded to include any residue/molecule
801 * (depending on \p type) that is partially contained in the group.
802 * If \p type is not INDEX_RES or INDEX_MOL, this has no effect.
804 * \p m should have been initialized somehow (calloc() is enough).
805 * \p g should be sorted.
808 gmx_ana_index_make_block(t_blocka *t, t_topology *top, gmx_ana_index_t *g,
809 e_index_t type, bool bComplete)
811 if (type == INDEX_UNKNOWN)
825 // TODO: Check callers and either check these there as well, or turn these
827 GMX_RELEASE_ASSERT(top != NULL || (type != INDEX_RES && type != INDEX_MOL),
828 "Topology must be provided for residue or molecule blocks");
829 GMX_RELEASE_ASSERT(!(type == INDEX_MOL && top->mols.nr == 0),
830 "Molecule information must be present for molecule blocks");
832 /* bComplete only does something for INDEX_RES or INDEX_MOL, so turn it
834 if (type != INDEX_RES && type != INDEX_MOL)
838 /* Allocate memory for the atom array and fill it unless we are using
843 /* We may allocate some extra memory here because we don't know in
844 * advance how much will be needed. */
845 if (t->nalloc_a < top->atoms.nr)
847 srenew(t->a, top->atoms.nr);
848 t->nalloc_a = top->atoms.nr;
854 if (t->nalloc_a < g->isize)
856 srenew(t->a, g->isize);
857 t->nalloc_a = g->isize;
859 std::memcpy(t->a, g->index, g->isize*sizeof(*(t->a)));
862 /* Allocate memory for the block index. We don't know in advance
863 * how much will be needed, so we allocate some extra and free it in the
865 if (t->nalloc_index < g->isize + 1)
867 srenew(t->index, g->isize + 1);
868 t->nalloc_index = g->isize + 1;
872 int j = 0; /* j is used by residue completion for the first atom not stored */
874 for (int i = 0; i < g->isize; ++i)
876 /* Find the ID number of the atom/residue/molecule corresponding to
878 if (next_group_index(g->index[i], top, type, &id))
880 /* If this is the first atom in a new block, initialize the block. */
883 /* For completion, we first set the start of the block. */
884 t->index[t->nr++] = t->nra;
885 /* And then we find all the atoms that should be included. */
889 while (top->atoms.atom[j].resind != id)
893 while (j < top->atoms.nr && top->atoms.atom[j].resind == id)
901 for (j = top->mols.index[id]; j < top->mols.index[id+1]; ++j)
907 default: /* Should not be reached */
908 GMX_RELEASE_ASSERT(false, "Unreachable code was reached");
914 /* If not using completion, simply store the start of the block. */
915 t->index[t->nr++] = i;
919 /* Set the end of the last block */
920 t->index[t->nr] = t->nra;
921 /* Free any unnecessary memory */
922 srenew(t->index, t->nr+1);
923 t->nalloc_index = t->nr+1;
926 srenew(t->a, t->nra);
927 t->nalloc_a = t->nra;
932 * \param[in] g Index group to check.
933 * \param[in] b Block data to check against.
934 * \returns true if \p g consists of one or more complete blocks from \p b,
937 * The atoms in \p g are assumed to be sorted.
940 gmx_ana_index_has_full_blocks(gmx_ana_index_t *g, t_block *b)
945 /* Each round in the loop matches one block */
948 /* Find the block that begins with the first unmatched atom */
949 while (bi < b->nr && b->index[bi] != g->index[i])
953 /* If not found, or if too large, return */
954 if (bi == b->nr || i + b->index[bi+1] - b->index[bi] > g->isize)
958 /* Check that the block matches the index */
959 for (j = b->index[bi]; j < b->index[bi+1]; ++j, ++i)
961 if (g->index[i] != j)
966 /* Move the search to the next block */
973 * \param[in] g Index group to check.
974 * \param[in] b Block data to check against.
975 * \returns true if \p g consists of one or more complete blocks from \p b,
978 * The atoms in \p g and \p b->a are assumed to be in the same order.
981 gmx_ana_index_has_full_ablocks(gmx_ana_index_t *g, t_blocka *b)
986 /* Each round in the loop matches one block */
989 /* Find the block that begins with the first unmatched atom */
990 while (bi < b->nr && b->a[b->index[bi]] != g->index[i])
994 /* If not found, or if too large, return */
995 if (bi == b->nr || i + b->index[bi+1] - b->index[bi] > g->isize)
999 /* Check that the block matches the index */
1000 for (j = b->index[bi]; j < b->index[bi+1]; ++j, ++i)
1002 if (b->a[j] != g->index[i])
1007 /* Move the search to the next block */
1014 * \param[in] g Index group to check.
1015 * \param[in] type Block data to check against.
1016 * \param[in] top Topology data.
1017 * \returns true if \p g consists of one or more complete elements of type
1018 * \p type, false otherwise.
1020 * \p g is assumed to be sorted, otherwise may return false negatives.
1022 * If \p type is \ref INDEX_ATOM, the return value is always true.
1023 * If \p type is \ref INDEX_UNKNOWN or \ref INDEX_ALL, the return value is
1027 gmx_ana_index_has_complete_elems(gmx_ana_index_t *g, e_index_t type,
1030 // TODO: Consider whether unsorted groups need to be supported better.
1046 for (i = 0; i < g->isize; ++i)
1049 id = top->atoms.atom[ai].resind;
1052 if (ai > 0 && top->atoms.atom[ai-1].resind == id)
1056 if (i > 0 && g->index[i-1] < top->atoms.nr - 1
1057 && top->atoms.atom[g->index[i-1]+1].resind == prev)
1064 if (g->index[i-1] < top->atoms.nr - 1
1065 && top->atoms.atom[g->index[i-1]+1].resind == prev)
1073 return gmx_ana_index_has_full_blocks(g, &top->mols);
1079 * \param[out] m Output structure.
1081 * Any contents of \p m are discarded without freeing.
1084 gmx_ana_indexmap_clear(gmx_ana_indexmap_t *m)
1086 m->type = INDEX_UNKNOWN;
1090 m->mapb.index = NULL;
1091 m->mapb.nalloc_index = 0;
1094 m->mapb.nalloc_a = 0;
1100 m->b.nalloc_index = 0;
1106 * \param[in,out] m Mapping structure.
1107 * \param[in] nr Maximum number of blocks to reserve space for.
1108 * \param[in] isize Maximum number of atoms to reserve space for.
1111 gmx_ana_indexmap_reserve(gmx_ana_indexmap_t *m, int nr, int isize)
1113 if (m->mapb.nalloc_index < nr + 1)
1115 srenew(m->refid, nr);
1116 srenew(m->mapid, nr);
1117 srenew(m->orgid, nr);
1118 srenew(m->mapb.index, nr + 1);
1119 srenew(m->b.index, nr + 1);
1120 m->mapb.nalloc_index = nr + 1;
1121 m->b.nalloc_index = nr + 1;
1123 if (m->b.nalloc_a < isize)
1125 srenew(m->b.a, isize);
1126 m->b.nalloc_a = isize;
1131 * \param[in,out] m Mapping structure to initialize.
1132 * \param[in] g Index group to map
1133 * (can be NULL if \p type is \ref INDEX_UNKNOWN).
1134 * \param[in] top Topology structure
1135 * (can be NULL if \p type is not \ref INDEX_RES or \ref INDEX_MOL).
1136 * \param[in] type Type of mapping to construct.
1138 * Initializes a new index group mapping.
1139 * The index group provided to gmx_ana_indexmap_update() should always be a
1140 * subset of the \p g given here.
1142 * \p m should have been initialized somehow (calloc() is enough).
1145 gmx_ana_indexmap_init(gmx_ana_indexmap_t *m, gmx_ana_index_t *g,
1146 t_topology *top, e_index_t type)
1149 gmx_ana_index_make_block(&m->b, top, g, type, false);
1150 gmx_ana_indexmap_reserve(m, m->b.nr, m->b.nra);
1152 for (int i = 0; i < m->b.nr; ++i)
1154 const int ii = (type == INDEX_UNKNOWN ? 0 : m->b.a[m->b.index[i]]);
1155 next_group_index(ii, top, type, &id);
1160 m->mapb.nr = m->b.nr;
1161 m->mapb.nra = m->b.nra;
1163 std::memcpy(m->mapb.index, m->b.index, (m->b.nr+1)*sizeof(*(m->mapb.index)));
1168 gmx_ana_indexmap_init_orgid_group(gmx_ana_indexmap_t *m, t_topology *top,
1171 GMX_RELEASE_ASSERT(m->bStatic,
1172 "Changing original IDs is not supported after starting "
1173 "to use the mapping");
1174 GMX_RELEASE_ASSERT(top != NULL || (type != INDEX_RES && type != INDEX_MOL),
1175 "Topology must be provided for residue or molecule blocks");
1176 GMX_RELEASE_ASSERT(!(type == INDEX_MOL && top->mols.nr == 0),
1177 "Molecule information must be present for molecule blocks");
1178 // Check that all atoms in each block belong to the same group.
1179 // This is a separate loop for better error handling (no state is modified
1180 // if there is an error.
1181 if (type == INDEX_RES || type == INDEX_MOL)
1184 for (int i = 0; i < m->b.nr; ++i)
1186 const int ii = m->b.a[m->b.index[i]];
1187 if (next_group_index(ii, top, type, &id))
1189 for (int j = m->b.index[i] + 1; j < m->b.index[i+1]; ++j)
1191 if (next_group_index(m->b.a[j], top, type, &id))
1193 std::string message("Grouping into residues/molecules is ambiguous");
1194 GMX_THROW(gmx::InconsistentInputError(message));
1200 // Do a second loop, where things are actually set.
1203 for (int i = 0; i < m->b.nr; ++i)
1205 const int ii = (type == INDEX_UNKNOWN ? 0 : m->b.a[m->b.index[i]]);
1206 if (next_group_index(ii, top, type, &id))
1210 m->mapid[i] = group;
1211 m->orgid[i] = group;
1213 // Count also the last group.
1219 * \param[in,out] m Mapping structure to initialize.
1220 * \param[in] b Block information to use for data.
1222 * Frees some memory that is not necessary for static index group mappings.
1223 * Internal pointers are set to point to data in \p b; it is the responsibility
1224 * of the caller to ensure that the block information matches the contents of
1226 * After this function has been called, the index group provided to
1227 * gmx_ana_indexmap_update() should always be the same as \p g given here.
1229 * This function breaks modularity of the index group mapping interface in an
1230 * ugly way, but allows reducing memory usage of static selections by a
1231 * significant amount.
1234 gmx_ana_indexmap_set_static(gmx_ana_indexmap_t *m, t_blocka *b)
1237 sfree(m->mapb.index);
1240 m->mapb.nalloc_index = 0;
1241 m->mapb.nalloc_a = 0;
1242 m->b.nalloc_index = 0;
1244 m->mapid = m->orgid;
1245 m->mapb.index = b->index;
1247 m->b.index = b->index;
1252 * \param[in,out] dest Destination data structure.
1253 * \param[in] src Source mapping.
1254 * \param[in] bFirst If true, memory is allocated for \p dest and a full
1255 * copy is made; otherwise, only variable parts are copied, and no memory
1258 * \p dest should have been initialized somehow (calloc() is enough).
1261 gmx_ana_indexmap_copy(gmx_ana_indexmap_t *dest, gmx_ana_indexmap_t *src, bool bFirst)
1265 gmx_ana_indexmap_reserve(dest, src->b.nr, src->b.nra);
1266 dest->type = src->type;
1267 dest->b.nr = src->b.nr;
1268 dest->b.nra = src->b.nra;
1269 std::memcpy(dest->orgid, src->orgid, dest->b.nr*sizeof(*dest->orgid));
1270 std::memcpy(dest->b.index, src->b.index, (dest->b.nr+1)*sizeof(*dest->b.index));
1271 std::memcpy(dest->b.a, src->b.a, dest->b.nra*sizeof(*dest->b.a));
1273 dest->mapb.nr = src->mapb.nr;
1274 dest->mapb.nra = src->mapb.nra;
1275 if (src->mapb.nalloc_a > 0)
1279 snew(dest->mapb.a, src->mapb.nalloc_a);
1280 dest->mapb.nalloc_a = src->mapb.nalloc_a;
1282 std::memcpy(dest->mapb.a, src->mapb.a, dest->mapb.nra*sizeof(*dest->mapb.a));
1286 dest->mapb.a = src->mapb.a;
1288 std::memcpy(dest->refid, src->refid, dest->mapb.nr*sizeof(*dest->refid));
1289 std::memcpy(dest->mapid, src->mapid, dest->mapb.nr*sizeof(*dest->mapid));
1290 std::memcpy(dest->mapb.index, src->mapb.index, (dest->mapb.nr+1)*sizeof(*dest->mapb.index));
1291 dest->bStatic = src->bStatic;
1295 * Helper function to set the source atoms in an index map.
1297 * \param[in,out] m Mapping structure.
1298 * \param[in] isize Number of atoms in the \p index array.
1299 * \param[in] index List of atoms.
1302 set_atoms(gmx_ana_indexmap_t *m, int isize, int *index)
1304 m->mapb.nra = isize;
1305 if (m->mapb.nalloc_a == 0)
1311 for (int i = 0; i < isize; ++i)
1313 m->mapb.a[i] = index[i];
1319 * \param[in,out] m Mapping structure.
1320 * \param[in] g Current index group.
1321 * \param[in] bMaskOnly true if the unused blocks should be masked with
1322 * -1 instead of removing them.
1324 * Updates the index group mapping with the new index group \p g.
1326 * \see gmx_ana_indexmap_t
1329 gmx_ana_indexmap_update(gmx_ana_indexmap_t *m, gmx_ana_index_t *g,
1334 /* Process the simple cases first */
1335 if (m->type == INDEX_UNKNOWN && m->b.nra == 0)
1339 if (m->type == INDEX_ALL)
1341 set_atoms(m, g->isize, g->index);
1344 m->mapb.index[1] = g->isize;
1348 /* Reset the reference IDs and mapping if necessary */
1349 const bool bToFull = (g->isize == m->b.nra);
1350 const bool bWasFull = (m->mapb.nra == m->b.nra);
1351 if (bToFull || bMaskOnly)
1355 for (bj = 0; bj < m->b.nr; ++bj)
1362 for (bj = 0; bj < m->b.nr; ++bj)
1364 m->mapid[bj] = m->orgid[bj];
1366 for (bj = 0; bj <= m->b.nr; ++bj)
1368 m->mapb.index[bj] = m->b.index[bj];
1371 set_atoms(m, m->b.nra, m->b.a);
1372 m->mapb.nr = m->b.nr;
1374 /* Exit immediately if the group is static */
1383 for (i = j = bj = 0; i < g->isize; ++i, ++j)
1385 /* Find the next atom in the block */
1386 while (m->b.a[j] != g->index[i])
1390 /* Mark blocks that did not contain any atoms */
1391 while (bj < m->b.nr && m->b.index[bj+1] <= j)
1393 m->refid[bj++] = -1;
1395 /* Advance the block index if we have reached the next block */
1396 if (m->b.index[bj] <= j)
1401 /* Mark the last blocks as not accessible */
1402 while (bj < m->b.nr)
1404 m->refid[bj++] = -1;
1409 set_atoms(m, g->isize, g->index);
1410 for (i = j = bi = 0, bj = -1; i < g->isize; ++i)
1412 /* Find the next atom in the block */
1413 while (m->b.a[j] != g->index[i])
1417 /* If we have reached a new block, add it */
1418 if (m->b.index[bj+1] <= j)
1420 /* Skip any blocks in between */
1421 while (bj < m->b.nr && m->b.index[bj+1] <= j)
1426 m->mapid[bi] = m->orgid[bj];
1427 m->mapb.index[bi] = i;
1431 /* Update the number of blocks */
1432 m->mapb.index[bi] = g->isize;
1439 * \param[in,out] m Mapping structure to free.
1441 * All the memory allocated for the mapping structure is freed, and
1442 * the pointers set to NULL.
1443 * The pointer \p m is not freed.
1446 gmx_ana_indexmap_deinit(gmx_ana_indexmap_t *m)
1449 if (m->mapid != m->orgid)
1453 if (m->mapb.nalloc_index > 0)
1455 sfree(m->mapb.index);
1457 if (m->mapb.nalloc_a > 0)
1462 if (m->b.nalloc_index > 0)
1466 if (m->b.nalloc_a > 0)
1470 gmx_ana_indexmap_clear(m);