2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2009, The GROMACS development team,
6 * check out http://www.gromacs.org for more information.
7 * Copyright (c) 2012, by the GROMACS development team, led by
8 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
9 * others, as listed in the AUTHORS file in the top-level source
10 * directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
39 * \brief Implementation of functions in indexutil.h.
49 #include <gmx_fatal.h>
51 #include <indexutil.h>
53 /********************************************************************
54 * gmx_ana_indexgrps_t functions
55 ********************************************************************/
58 * Stores a set of index groups.
60 struct gmx_ana_indexgrps_t
62 /** Number of index groups. */
64 /** Array of index groups. */
69 * \param[out] g Index group structure.
70 * \param[in] ngrps Number of groups for which memory is allocated.
73 gmx_ana_indexgrps_alloc(gmx_ana_indexgrps_t **g, int ngrps)
81 * \param[out] g Index group structure.
82 * \param[in] ngrps Number of index groups.
83 * \param[in] isize Array of index group sizes.
84 * \param[in] index Array of pointers to indices of each group.
85 * \param[in] name Array of names of the groups.
86 * \param[in] bFree If TRUE, the \p isize, \p index and \p name arrays
87 * are freed after they have been copied.
90 gmx_ana_indexgrps_set(gmx_ana_indexgrps_t **g, int ngrps, int *isize,
91 atom_id **index, char **name, gmx_bool bFree)
95 gmx_ana_indexgrps_alloc(g, ngrps);
96 for (i = 0; i < ngrps; ++i)
98 gmx_ana_index_set(&(*g)->g[i], isize[i], index[i], name[i], isize[i]);
109 * \param[out] g Index group structure.
110 * \param[in] top Topology structure.
111 * \param[in] fnm File name for the index file.
112 * Memory is automatically allocated.
114 * One or both of \p top or \p fnm can be NULL.
115 * If \p top is NULL, an index file is required and the groups are read
116 * from the file (uses Gromacs routine init_index()).
117 * If \p fnm is NULL, default groups are constructed based on the
118 * topology (uses Gromacs routine analyse()).
119 * If both are null, the index group structure is initialized empty.
122 gmx_ana_indexgrps_init(gmx_ana_indexgrps_t **g, t_topology *top,
125 t_blocka *block = NULL;
131 block = init_index(fnm, &names);
135 block = new_blocka();
136 analyse(&top->atoms, block, &names, FALSE, FALSE);
146 gmx_ana_indexgrps_alloc(g, block->nr);
147 for (i = 0; i < block->nr; ++i)
149 gmx_ana_index_t *grp = &(*g)->g[i];
151 grp->isize = block->index[i+1] - block->index[i];
152 snew(grp->index, grp->isize);
153 for (j = 0; j < grp->isize; ++j)
155 grp->index[j] = block->a[block->index[i]+j];
157 grp->name = names[i];
158 grp->nalloc_index = grp->isize;
167 * \param[out] g Index group structure.
168 * \param[in] top Topology structure.
169 * \param[in] fnm File name for the index file.
170 * \param[in] ngrps Number of required groups.
171 * Memory is automatically allocated.
173 * One of \p top or \p fnm can be NULL, but not both.
174 * If \p top is NULL, an index file is required and the groups are read
175 * from the file (uses Gromacs routine rd_index()).
176 * If \p fnm is NULL, default groups are constructed based on the
177 * topology (uses Gromacs routine get_index()).
180 gmx_ana_indexgrps_get(gmx_ana_indexgrps_t **g, t_topology *top,
181 const char *fnm, int ngrps)
192 rd_index(fnm, ngrps, isize, index, name);
196 get_index(&(top->atoms), fnm, ngrps, isize, index, name);
198 gmx_ana_indexgrps_set(g, ngrps, isize, index, name, TRUE);
202 * \param[out] g Index group structure.
203 * \param[in] fnm File name for the index file.
204 * \param[in] ngrps Number of required groups.
205 * Memory is automatically allocated.
207 * This is a convenience function for calling the Gromacs routine
211 gmx_ana_indexgrps_rd(gmx_ana_indexgrps_t **g, const char *fnm, int ngrps)
213 gmx_ana_indexgrps_get(g, NULL, fnm, ngrps);
217 * \param[in] g Index groups structure.
219 * The pointer \p g is invalid after the call.
222 gmx_ana_indexgrps_free(gmx_ana_indexgrps_t *g)
231 for (i = 0; i < g->nr; ++i)
233 gmx_ana_index_deinit(&g->g[i]);
242 * \param[out] dest Destination index groups.
243 * \param[in] src Source index groups.
245 * A deep copy is made for all fields, including the group names.
248 gmx_ana_indexgrps_clone(gmx_ana_indexgrps_t **dest, gmx_ana_indexgrps_t *src)
252 gmx_ana_indexgrps_alloc(dest, src->nr);
253 for (g = 0; g < src->nr; ++g)
255 gmx_ana_index_copy(&(*dest)->g[g], &src->g[g], TRUE);
260 * \param[out] g Index group structure.
261 * \returns TRUE if \p g is empty, i.e., has 0 index groups.
264 gmx_ana_indexgrps_is_empty(gmx_ana_indexgrps_t *g)
270 * \param[in] g Index groups structure.
271 * \param[in] n Index group number to get.
272 * \returns Pointer to the \p n'th index group in \p g.
274 * The returned pointer should not be freed.
277 gmx_ana_indexgrps_get_grp(gmx_ana_indexgrps_t *g, int n)
279 if (n < 0 || n >= g->nr)
287 * \param[out] dest Output structure.
288 * \param[in] src Input index groups.
289 * \param[in] n Number of the group to extract.
290 * \returns TRUE if \p n is a valid group in \p src, FALSE otherwise.
293 gmx_ana_indexgrps_extract(gmx_ana_index_t *dest, gmx_ana_indexgrps_t *src, int n)
295 if (n < 0 || n >= src->nr)
301 gmx_ana_index_copy(dest, &src->g[n], TRUE);
306 * \param[out] dest Output structure.
307 * \param[in] src Input index groups.
308 * \param[in] name Name (or part of the name) of the group to extract.
309 * \returns TRUE if \p name is a valid group in \p src, FALSE otherwise.
311 * Uses the Gromacs routine find_group() to find the actual group;
312 * the comparison is case-insensitive.
315 gmx_ana_indexgrps_find(gmx_ana_index_t *dest, gmx_ana_indexgrps_t *src, char *name)
320 snew(names, src->nr);
321 for (i = 0; i < src->nr; ++i)
323 names[i] = src->g[i].name;
325 i = find_group(name, src->nr, names);
333 return gmx_ana_indexgrps_extract(dest, src, i);
337 * \param[in] g Index groups to print.
338 * \param[in] maxn Maximum number of indices to print
339 * (-1 = print all, 0 = print only names).
342 gmx_ana_indexgrps_print(gmx_ana_indexgrps_t *g, int maxn)
346 for (i = 0; i < g->nr; ++i)
348 fprintf(stderr, " %2d: ", i);
349 gmx_ana_index_dump(&g->g[i], i, maxn);
353 /********************************************************************
354 * gmx_ana_index_t functions
355 ********************************************************************/
358 * \param[in,out] g Index group structure.
359 * \param[in] isize Maximum number of atoms to reserve space for.
362 gmx_ana_index_reserve(gmx_ana_index_t *g, int isize)
364 if (g->nalloc_index < isize)
366 srenew(g->index, isize);
367 g->nalloc_index = isize;
372 * \param[in,out] g Index group structure.
374 * Resizes the memory allocated for holding the indices such that the
375 * current contents fit.
378 gmx_ana_index_squeeze(gmx_ana_index_t *g)
380 srenew(g->index, g->isize);
381 g->nalloc_index = g->isize;
385 * \param[out] g Output structure.
387 * Any contents of \p g are discarded without freeing.
390 gmx_ana_index_clear(gmx_ana_index_t *g)
399 * \param[out] g Output structure.
400 * \param[in] isize Number of atoms in the new group.
401 * \param[in] index Array of \p isize atoms (can be NULL if \p isize is 0).
402 * \param[in] name Name for the new group (can be NULL).
403 * \param[in] nalloc Number of elements allocated for \p index
404 * (if 0, \p index is not freed in gmx_ana_index_deinit())
406 * No copy if \p index is made.
409 gmx_ana_index_set(gmx_ana_index_t *g, int isize, atom_id *index, char *name,
415 g->nalloc_index = nalloc;
419 * \param[out] g Output structure.
420 * \param[in] natoms Number of atoms.
421 * \param[in] name Name for the new group (can be NULL).
424 gmx_ana_index_init_simple(gmx_ana_index_t *g, int natoms, char *name)
429 snew(g->index, natoms);
430 for (i = 0; i < natoms; ++i)
435 g->nalloc_index = natoms;
439 * \param[in] g Index group structure.
441 * The pointer \p g is not freed.
444 gmx_ana_index_deinit(gmx_ana_index_t *g)
446 if (g->nalloc_index > 0)
451 gmx_ana_index_clear(g);
455 * \param[out] dest Destination index group.
456 * \param[in] src Source index group.
457 * \param[in] bAlloc If TRUE, memory is allocated at \p dest; otherwise,
458 * it is assumed that enough memory has been allocated for index.
460 * A deep copy of the name is only made if \p bAlloc is TRUE.
463 gmx_ana_index_copy(gmx_ana_index_t *dest, gmx_ana_index_t *src, gmx_bool bAlloc)
465 dest->isize = src->isize;
470 snew(dest->index, dest->isize);
471 dest->nalloc_index = dest->isize;
473 memcpy(dest->index, src->index, dest->isize*sizeof(*dest->index));
475 if (bAlloc && src->name)
477 dest->name = strdup(src->name);
479 else if (bAlloc || src->name)
481 dest->name = src->name;
486 * \param[in] g Index group to print.
487 * \param[in] i Group number to use if the name is NULL.
488 * \param[in] maxn Maximum number of indices to print (-1 = print all).
491 gmx_ana_index_dump(gmx_ana_index_t *g, int i, int maxn)
497 fprintf(stderr, "\"%s\"", g->name);
501 fprintf(stderr, "Group %d", i+1);
503 fprintf(stderr, " (%d atoms)", g->isize);
506 fprintf(stderr, ":");
508 if (maxn >= 0 && n > maxn)
512 for (j = 0; j < n; ++j)
514 fprintf(stderr, " %d", g->index[j]+1);
518 fprintf(stderr, " ...");
521 fprintf(stderr, "\n");
525 * \param[in] g Input index group.
526 * \param[in] natoms Number of atoms to check against.
528 * If any atom index in the index group is less than zero or >= \p natoms,
529 * gmx_fatal() is called.
532 gmx_ana_index_check(gmx_ana_index_t *g, int natoms)
536 for (j = 0; j < g->isize; ++j)
538 if (g->index[j] >= natoms)
540 gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) "
541 "larger than number of atoms in trajectory (%d atoms)",
542 g->index[j], g->name, g->isize, natoms);
544 else if (g->index[j] < 0)
546 gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) "
548 g->index[j], g->name, g->isize);
554 * \param[in] g Index group to check.
555 * \returns TRUE if the index group is sorted and has no duplicates,
559 gmx_ana_index_check_sorted(gmx_ana_index_t *g)
563 for (i = 0; i < g->isize-1; ++i)
565 if (g->index[i+1] <= g->index[i])
573 /********************************************************************
575 ********************************************************************/
577 /** Helper function for gmx_ana_index_sort(). */
579 cmp_atomid(const void *a, const void *b)
581 if (*(atom_id *)a < *(atom_id *)b) return -1;
582 if (*(atom_id *)a > *(atom_id *)b) return 1;
587 * \param[in,out] g Index group to be sorted.
590 gmx_ana_index_sort(gmx_ana_index_t *g)
592 qsort(g->index, g->isize, sizeof(*g->index), cmp_atomid);
596 * \param[in] a Index group to check.
597 * \param[in] b Index group to check.
598 * \returns TRUE if \p a and \p b are equal, FALSE otherwise.
601 gmx_ana_index_equals(gmx_ana_index_t *a, gmx_ana_index_t *b)
605 if (a->isize != b->isize)
609 for (i = 0; i < a->isize; ++i)
611 if (a->index[i] != b->index[i])
620 * \param[in] a Index group to check against.
621 * \param[in] b Index group to check.
622 * \returns TRUE if \p b is contained in \p a,
625 * If the elements are not in the same order in both groups, the function
626 * fails. However, the groups do not need to be sorted.
629 gmx_ana_index_contains(gmx_ana_index_t *a, gmx_ana_index_t *b)
633 for (i = j = 0; j < b->isize; ++i, ++j) {
634 while (i < a->isize && a->index[i] != b->index[j])
647 * \param[out] dest Output index group (the intersection of \p a and \p b).
648 * \param[in] a First index group.
649 * \param[in] b Second index group.
651 * \p dest can be the same as \p a or \p b.
654 gmx_ana_index_intersection(gmx_ana_index_t *dest,
655 gmx_ana_index_t *a, gmx_ana_index_t *b)
659 for (i = j = k = 0; i < a->isize && j < b->isize; ++i) {
660 while (j < b->isize && b->index[j] < a->index[i])
664 if (j < b->isize && b->index[j] == a->index[i])
666 dest->index[k++] = b->index[j++];
673 * \param[out] dest Output index group (the difference \p a - \p b).
674 * \param[in] a First index group.
675 * \param[in] b Second index group.
677 * \p dest can equal \p a, but not \p b.
680 gmx_ana_index_difference(gmx_ana_index_t *dest,
681 gmx_ana_index_t *a, gmx_ana_index_t *b)
685 for (i = j = k = 0; i < a->isize; ++i)
687 while (j < b->isize && b->index[j] < a->index[i])
691 if (j == b->isize || b->index[j] != a->index[i])
693 dest->index[k++] = a->index[i];
700 * \param[in] a First index group.
701 * \param[in] b Second index group.
702 * \returns Size of the difference \p a - \p b.
705 gmx_ana_index_difference_size(gmx_ana_index_t *a, gmx_ana_index_t *b)
709 for (i = j = k = 0; i < a->isize; ++i)
711 while (j < b->isize && b->index[j] < a->index[i])
715 if (j == b->isize || b->index[j] != a->index[i])
724 * \param[out] dest1 Output group 1 (will equal \p g).
725 * \param[out] dest2 Output group 2 (will equal \p src - \p g).
726 * \param[in] src Group to be partitioned.
727 * \param[in] g One partition.
729 * \pre \p g is a subset of \p src and both sets are sorted
730 * \pre \p dest1 has allocated storage to store \p src
731 * \post \p dest1 == \p g
732 * \post \p dest2 == \p src - \p g
734 * No storage should be allocated for \p dest2; after the call,
735 * \p dest2->index points to the memory allocated for \p dest1
736 * (to a part that is not used by \p dest1).
738 * The calculation can be performed in-place by setting \p dest1 equal to
742 gmx_ana_index_partition(gmx_ana_index_t *dest1, gmx_ana_index_t *dest2,
743 gmx_ana_index_t *src, gmx_ana_index_t *g)
748 dest2->index = dest1->index + g->isize;
749 dest2->isize = src->isize - g->isize;
750 for (i = g->isize-1, j = src->isize-1, k = dest2->isize-1; i >= 0; --i, --j)
752 while (j >= 0 && src->index[j] != g->index[i])
754 dest2->index[k--] = src->index[j--];
759 dest2->index[k--] = src->index[j--];
761 gmx_ana_index_copy(dest1, g, FALSE);
765 * \param[out] dest Output index group (the union of \p a and \p b).
766 * \param[in] a First index group.
767 * \param[in] b Second index group.
769 * \p a and \p b can have common items.
770 * \p dest can equal \p a or \p b.
772 * \see gmx_ana_index_merge()
775 gmx_ana_index_union(gmx_ana_index_t *dest,
776 gmx_ana_index_t *a, gmx_ana_index_t *b)
781 dsize = gmx_ana_index_difference_size(b, a);
784 dest->isize = a->isize + dsize;
785 for (k = dest->isize - 1; k >= 0; k--)
787 if (i < 0 || (j >= 0 && a->index[i] < b->index[j]))
789 dest->index[k] = b->index[j--];
793 if (j >= 0 && a->index[i] == b->index[j])
797 dest->index[k] = a->index[i--];
803 * \param[out] dest Output index group (the union of \p a and \p b).
804 * \param[in] a First index group.
805 * \param[in] b Second index group.
807 * \p a and \p b should not have common items.
808 * \p dest can equal \p a or \p b.
810 * \see gmx_ana_index_union()
813 gmx_ana_index_merge(gmx_ana_index_t *dest,
814 gmx_ana_index_t *a, gmx_ana_index_t *b)
820 dest->isize = a->isize + b->isize;
821 for (k = dest->isize - 1; k >= 0; k--)
823 if (i < 0 || (j >= 0 && a->index[i] < b->index[j]))
825 dest->index[k] = b->index[j--];
829 dest->index[k] = a->index[i--];
834 /********************************************************************
835 * gmx_ana_indexmap_t and related things
836 ********************************************************************/
839 * \param[in,out] t Output block.
840 * \param[in] top Topology structure
841 * (only used if \p type is \ref INDEX_RES or \ref INDEX_MOL, can be NULL
843 * \param[in] g Index group
844 * (can be NULL if \p type is \ref INDEX_UNKNOWN).
845 * \param[in] type Type of partitioning to make.
846 * \param[in] bComplete
847 * If TRUE, the index group is expanded to include any residue/molecule
848 * (depending on \p type) that is partially contained in the group.
849 * If \p type is not INDEX_RES or INDEX_MOL, this has no effect.
851 * \p m should have been initialized somehow (calloc() is enough) unless
852 * \p type is INDEX_UNKNOWN.
853 * \p g should be sorted.
856 gmx_ana_index_make_block(t_blocka *t, t_topology *top, gmx_ana_index_t *g,
857 e_index_t type, gmx_bool bComplete)
862 if (type == INDEX_UNKNOWN)
875 /* bComplete only does something for INDEX_RES or INDEX_MOL, so turn it
877 if (type != INDEX_RES && type != INDEX_MOL)
881 /* Allocate memory for the atom array and fill it unless we are using
886 /* We may allocate some extra memory here because we don't know in
887 * advance how much will be needed. */
888 if (t->nalloc_a < top->atoms.nr)
890 srenew(t->a, top->atoms.nr);
891 t->nalloc_a = top->atoms.nr;
897 if (t->nalloc_a < g->isize)
899 srenew(t->a, g->isize);
900 t->nalloc_a = g->isize;
902 memcpy(t->a, g->index, g->isize*sizeof(*(t->a)));
905 /* Allocate memory for the block index. We don't know in advance
906 * how much will be needed, so we allocate some extra and free it in the
908 if (t->nalloc_index < g->isize + 1)
910 srenew(t->index, g->isize + 1);
911 t->nalloc_index = g->isize + 1;
915 j = 0; /* j is used by residue completion for the first atom not stored */
917 for (i = 0; i < g->isize; ++i)
920 /* Find the ID number of the atom/residue/molecule corresponding to
928 id = top->atoms.atom[ai].resind;
931 while (ai >= top->mols.index[id+1])
936 case INDEX_UNKNOWN: /* Should not occur */
941 /* If this is the first atom in a new block, initialize the block. */
946 /* For completion, we first set the start of the block. */
947 t->index[t->nr++] = t->nra;
948 /* And then we find all the atoms that should be included. */
952 while (top->atoms.atom[j].resind != id)
956 while (j < top->atoms.nr && top->atoms.atom[j].resind == id)
964 for (j = top->mols.index[id]; j < top->mols.index[id+1]; ++j)
970 default: /* Should not be reached */
971 gmx_bug("internal error");
977 /* If not using completion, simply store the start of the block. */
978 t->index[t->nr++] = i;
983 /* Set the end of the last block */
984 t->index[t->nr] = t->nra;
985 /* Free any unnecessary memory */
986 srenew(t->index, t->nr+1);
987 t->nalloc_index = t->nr+1;
990 srenew(t->a, t->nra);
991 t->nalloc_a = t->nra;
996 * \param[in] g Index group to check.
997 * \param[in] b Block data to check against.
998 * \returns TRUE if \p g consists of one or more complete blocks from \p b,
1001 * The atoms in \p g are assumed to be sorted.
1004 gmx_ana_index_has_full_blocks(gmx_ana_index_t *g, t_block *b)
1009 /* Each round in the loop matches one block */
1010 while (i < g->isize)
1012 /* Find the block that begins with the first unmatched atom */
1013 while (bi < b->nr && b->index[bi] != g->index[i])
1017 /* If not found, or if too large, return */
1018 if (bi == b->nr || i + b->index[bi+1] - b->index[bi] > g->isize)
1022 /* Check that the block matches the index */
1023 for (j = b->index[bi]; j < b->index[bi+1]; ++j, ++i)
1025 if (g->index[i] != j)
1030 /* Move the search to the next block */
1037 * \param[in] g Index group to check.
1038 * \param[in] b Block data to check against.
1039 * \returns TRUE if \p g consists of one or more complete blocks from \p b,
1042 * The atoms in \p g and \p b->a are assumed to be in the same order.
1045 gmx_ana_index_has_full_ablocks(gmx_ana_index_t *g, t_blocka *b)
1050 /* Each round in the loop matches one block */
1051 while (i < g->isize)
1053 /* Find the block that begins with the first unmatched atom */
1054 while (bi < b->nr && b->a[b->index[bi]] != g->index[i])
1058 /* If not found, or if too large, return */
1059 if (bi == b->nr || i + b->index[bi+1] - b->index[bi] > g->isize)
1063 /* Check that the block matches the index */
1064 for (j = b->index[bi]; j < b->index[bi+1]; ++j, ++i)
1066 if (b->a[j] != g->index[i])
1071 /* Move the search to the next block */
1078 * \param[in] g Index group to check.
1079 * \param[in] type Block data to check against.
1080 * \param[in] top Topology data.
1081 * \returns TRUE if \p g consists of one or more complete elements of type
1082 * \p type, FALSE otherwise.
1084 * If \p type is \ref INDEX_ATOM, the return value is always TRUE.
1085 * If \p type is \ref INDEX_UNKNOWN or \ref INDEX_ALL, the return value is
1089 gmx_ana_index_has_complete_elems(gmx_ana_index_t *g, e_index_t type,
1107 for (i = 0; i < g->isize; ++i)
1110 id = top->atoms.atom[ai].resind;
1113 if (ai > 0 && top->atoms.atom[ai-1].resind == id)
1117 if (i > 0 && g->index[i-1] < top->atoms.nr - 1
1118 && top->atoms.atom[g->index[i-1]+1].resind == prev)
1125 if (g->index[i-1] < top->atoms.nr - 1
1126 && top->atoms.atom[g->index[i-1]+1].resind == prev)
1134 return gmx_ana_index_has_full_blocks(g, &top->mols);
1140 * \param[out] m Output structure.
1142 * Any contents of \p m are discarded without freeing.
1145 gmx_ana_indexmap_clear(gmx_ana_indexmap_t *m)
1147 m->type = INDEX_UNKNOWN;
1152 m->mapb.index = NULL;
1153 m->mapb.nalloc_index = 0;
1159 m->b.nalloc_index = 0;
1162 m->bMapStatic = TRUE;
1166 * \param[in,out] m Mapping structure.
1167 * \param[in] nr Maximum number of blocks to reserve space for.
1168 * \param[in] isize Maximum number of atoms to reserve space for.
1171 gmx_ana_indexmap_reserve(gmx_ana_indexmap_t *m, int nr, int isize)
1173 if (m->mapb.nalloc_index < nr + 1)
1175 srenew(m->refid, nr);
1176 srenew(m->mapid, nr);
1177 srenew(m->orgid, nr);
1178 srenew(m->mapb.index, nr + 1);
1179 srenew(m->b.index, nr + 1);
1180 m->mapb.nalloc_index = nr + 1;
1181 m->b.nalloc_index = nr + 1;
1183 if (m->b.nalloc_a < isize)
1185 srenew(m->b.a, isize);
1186 m->b.nalloc_a = isize;
1191 * \param[in,out] m Mapping structure to initialize.
1192 * \param[in] g Index group to map
1193 * (can be NULL if \p type is \ref INDEX_UNKNOWN).
1194 * \param[in] top Topology structure
1195 * (can be NULL if \p type is not \ref INDEX_RES or \ref INDEX_MOL).
1196 * \param[in] type Type of mapping to construct.
1198 * Initializes a new index group mapping.
1199 * The index group provided to gmx_ana_indexmap_update() should always be a
1200 * subset of the \p g given here.
1202 * \p m should have been initialized somehow (calloc() is enough).
1205 gmx_ana_indexmap_init(gmx_ana_indexmap_t *m, gmx_ana_index_t *g,
1206 t_topology *top, e_index_t type)
1211 gmx_ana_index_make_block(&m->b, top, g, type, FALSE);
1212 gmx_ana_indexmap_reserve(m, m->b.nr, m->b.nra);
1214 for (i = mi = 0; i < m->nr; ++i)
1216 ii = (type == INDEX_UNKNOWN ? 0 : m->b.a[m->b.index[i]]);
1223 m->orgid[i] = top->atoms.atom[ii].resind;
1226 while (top->mols.index[mi+1] <= ii)
1238 for (i = 0; i < m->nr; ++i)
1241 m->mapid[i] = m->orgid[i];
1244 memcpy(m->mapb.index, m->b.index, (m->nr+1)*sizeof(*(m->mapb.index)));
1246 m->bMapStatic = TRUE;
1250 * \param[in,out] m Mapping structure to initialize.
1251 * \param[in] b Block information to use for data.
1253 * Frees some memory that is not necessary for static index group mappings.
1254 * Internal pointers are set to point to data in \p b; it is the responsibility
1255 * of the caller to ensure that the block information matches the contents of
1257 * After this function has been called, the index group provided to
1258 * gmx_ana_indexmap_update() should always be the same as \p g given here.
1260 * This function breaks modularity of the index group mapping interface in an
1261 * ugly way, but allows reducing memory usage of static selections by a
1262 * significant amount.
1265 gmx_ana_indexmap_set_static(gmx_ana_indexmap_t *m, t_blocka *b)
1268 m->mapid = m->orgid;
1270 m->b.nalloc_index = 0;
1271 m->b.index = b->index;
1272 sfree(m->mapb.index);
1273 m->mapb.nalloc_index = 0;
1274 m->mapb.index = m->b.index;
1281 * \param[in,out] dest Destination data structure.
1282 * \param[in] src Source mapping.
1283 * \param[in] bFirst If TRUE, memory is allocated for \p dest and a full
1284 * copy is made; otherwise, only variable parts are copied, and no memory
1287 * \p dest should have been initialized somehow (calloc() is enough).
1290 gmx_ana_indexmap_copy(gmx_ana_indexmap_t *dest, gmx_ana_indexmap_t *src, gmx_bool bFirst)
1294 gmx_ana_indexmap_reserve(dest, src->b.nr, src->b.nra);
1295 dest->type = src->type;
1296 dest->b.nr = src->b.nr;
1297 dest->b.nra = src->b.nra;
1298 memcpy(dest->orgid, src->orgid, dest->b.nr*sizeof(*dest->orgid));
1299 memcpy(dest->b.index, src->b.index, (dest->b.nr+1)*sizeof(*dest->b.index));
1300 memcpy(dest->b.a, src->b.a, dest->b.nra*sizeof(*dest->b.a));
1303 dest->mapb.nr = src->mapb.nr;
1304 memcpy(dest->refid, src->refid, dest->nr*sizeof(*dest->refid));
1305 memcpy(dest->mapid, src->mapid, dest->nr*sizeof(*dest->mapid));
1306 memcpy(dest->mapb.index, src->mapb.index,(dest->mapb.nr+1)*sizeof(*dest->mapb.index));
1307 dest->bStatic = src->bStatic;
1308 dest->bMapStatic = src->bMapStatic;
1312 * \param[in,out] m Mapping structure.
1313 * \param[in] g Current index group.
1314 * \param[in] bMaskOnly TRUE if the unused blocks should be masked with
1315 * -1 instead of removing them.
1317 * Updates the index group mapping with the new index group \p g.
1319 * \see gmx_ana_indexmap_t
1322 gmx_ana_indexmap_update(gmx_ana_indexmap_t *m, gmx_ana_index_t *g,
1328 /* Process the simple cases first */
1329 if (m->type == INDEX_UNKNOWN && m->b.nra == 0)
1333 if (m->type == INDEX_ALL)
1337 m->mapb.index[1] = g->isize;
1341 /* Reset the reference IDs and mapping if necessary */
1342 bStatic = (g->isize == m->b.nra && m->nr == m->b.nr);
1343 if (bStatic || bMaskOnly)
1347 for (bj = 0; bj < m->b.nr; ++bj)
1354 for (bj = 0; bj < m->b.nr; ++bj)
1356 m->mapid[bj] = m->orgid[bj];
1358 for (bj = 0; bj <= m->b.nr; ++bj)
1360 m->mapb.index[bj] = m->b.index[bj];
1362 m->bMapStatic = TRUE;
1365 /* Exit immediately if the group is static */
1375 for (i = j = bj = 0; i < g->isize; ++i, ++j)
1377 /* Find the next atom in the block */
1378 while (m->b.a[j] != g->index[i])
1382 /* Mark blocks that did not contain any atoms */
1383 while (bj < m->b.nr && m->b.index[bj+1] <= j)
1385 m->refid[bj++] = -1;
1387 /* Advance the block index if we have reached the next block */
1388 if (m->b.index[bj] <= j)
1393 /* Mark the last blocks as not accessible */
1394 while (bj < m->b.nr)
1396 m->refid[bj++] = -1;
1401 for (i = j = bi = 0, bj = -1; i < g->isize; ++i)
1403 /* Find the next atom in the block */
1404 while (m->b.a[j] != g->index[i])
1408 /* If we have reached a new block, add it */
1409 if (m->b.index[bj+1] <= j)
1411 /* Skip any blocks in between */
1412 while (bj < m->b.nr && m->b.index[bj+1] <= j)
1417 m->mapid[bi] = m->orgid[bj];
1418 m->mapb.index[bi] = i;
1422 /* Update the number of blocks */
1423 m->mapb.index[bi] = g->isize;
1425 m->bMapStatic = FALSE;
1432 * \param[in,out] m Mapping structure to free.
1434 * All the memory allocated for the mapping structure is freed, and
1435 * the pointers set to NULL.
1436 * The pointer \p m is not freed.
1439 gmx_ana_indexmap_deinit(gmx_ana_indexmap_t *m)
1442 if (m->mapid != m->orgid)
1446 if (m->mapb.nalloc_index > 0)
1448 sfree(m->mapb.index);
1451 if (m->b.nalloc_index > 0)
1455 if (m->b.nalloc_a > 0)
1459 gmx_ana_indexmap_clear(m);