2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2009,2010,2011,2012,2013,2014,2015,2016,2017, 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/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"
64 /********************************************************************
65 * gmx_ana_indexgrps_t functions
66 ********************************************************************/
69 * Stores a set of index groups.
71 struct gmx_ana_indexgrps_t
73 //! Initializes an empty set of groups.
74 explicit gmx_ana_indexgrps_t(int nr
) : nr(nr
), g(nullptr)
79 ~gmx_ana_indexgrps_t()
81 for (int i
= 0; i
< nr
; ++i
)
83 gmx_ana_index_deinit(&g
[i
]);
88 /** Number of index groups. */
90 /** Array of index groups. */
93 std::vector
<std::string
> names
;
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.
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.
110 gmx_ana_indexgrps_init(gmx_ana_indexgrps_t
**g
, gmx_mtop_t
*top
,
113 t_blocka
*block
= nullptr;
114 char **names
= nullptr;
118 block
= init_index(fnm
, &names
);
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
);
130 *g
= new gmx_ana_indexgrps_t(0);
136 *g
= new gmx_ana_indexgrps_t(block
->nr
);
137 for (int i
= 0; i
< block
->nr
; ++i
)
139 gmx_ana_index_t
*grp
= &(*g
)->g
[i
];
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
)
145 grp
->index
[j
] = block
->a
[block
->index
[i
]+j
];
147 grp
->nalloc_index
= grp
->isize
;
148 (*g
)->names
.emplace_back(names
[i
]);
153 for (int i
= 0; i
< block
->nr
; ++i
)
162 for (int i
= 0; i
< block
->nr
; ++i
)
172 * \param[in] g Index groups structure.
174 * The pointer \p g is invalid after the call.
177 gmx_ana_indexgrps_free(gmx_ana_indexgrps_t
*g
)
183 * \param[out] g Index group structure.
184 * \returns true if \p g is empty, i.e., has 0 index groups.
187 gmx_ana_indexgrps_is_empty(gmx_ana_indexgrps_t
*g
)
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.
197 * The returned pointer should not be freed.
200 gmx_ana_indexgrps_get_grp(gmx_ana_indexgrps_t
*g
, int n
)
202 if (n
< 0 || n
>= g
->nr
)
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.
217 gmx_ana_indexgrps_extract(gmx_ana_index_t
*dest
, std::string
*destName
,
218 gmx_ana_indexgrps_t
*src
, int n
)
221 if (n
< 0 || n
>= src
->nr
)
227 if (destName
!= nullptr)
229 *destName
= src
->names
[n
];
231 gmx_ana_index_copy(dest
, &src
->g
[n
], true);
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.
242 * Uses the Gromacs routine find_group() to find the actual group;
243 * the comparison is case-insensitive.
246 gmx_ana_indexgrps_find(gmx_ana_index_t
*dest
, std::string
*destName
,
247 gmx_ana_indexgrps_t
*src
,
253 snew(names
, src
->nr
);
254 for (int i
= 0; i
< src
->nr
; ++i
)
256 names
[i
] = src
->names
[i
].c_str();
258 int n
= find_group(const_cast<char *>(name
), src
->nr
,
259 const_cast<char **>(names
));
267 return gmx_ana_indexgrps_extract(dest
, destName
, src
, n
);
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).
277 gmx_ana_indexgrps_print(gmx::TextWriter
*writer
, gmx_ana_indexgrps_t
*g
, int maxn
)
279 for (int i
= 0; i
< g
->nr
; ++i
)
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
);
287 /********************************************************************
288 * gmx_ana_index_t functions
289 ********************************************************************/
292 * \param[in,out] g Index group structure.
293 * \param[in] isize Maximum number of atoms to reserve space for.
296 gmx_ana_index_reserve(gmx_ana_index_t
*g
, int isize
)
298 if (g
->nalloc_index
< isize
)
300 srenew(g
->index
, isize
);
301 g
->nalloc_index
= isize
;
306 * \param[in,out] g Index group structure.
308 * Resizes the memory allocated for holding the indices such that the
309 * current contents fit.
312 gmx_ana_index_squeeze(gmx_ana_index_t
*g
)
314 srenew(g
->index
, g
->isize
);
315 g
->nalloc_index
= g
->isize
;
319 * \param[out] g Output structure.
321 * Any contents of \p g are discarded without freeing.
324 gmx_ana_index_clear(gmx_ana_index_t
*g
)
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())
338 * No copy if \p index is made.
341 gmx_ana_index_set(gmx_ana_index_t
*g
, int isize
, int *index
, int nalloc
)
345 g
->nalloc_index
= nalloc
;
349 * \param[out] g Output structure.
350 * \param[in] natoms Number of atoms.
353 gmx_ana_index_init_simple(gmx_ana_index_t
*g
, int natoms
)
358 snew(g
->index
, natoms
);
359 for (i
= 0; i
< natoms
; ++i
)
363 g
->nalloc_index
= natoms
;
367 * \param[in] g Index group structure.
369 * The pointer \p g is not freed.
372 gmx_ana_index_deinit(gmx_ana_index_t
*g
)
374 if (g
->nalloc_index
> 0)
378 gmx_ana_index_clear(g
);
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.
388 gmx_ana_index_copy(gmx_ana_index_t
*dest
, gmx_ana_index_t
*src
, bool bAlloc
)
390 dest
->isize
= src
->isize
;
393 snew(dest
->index
, dest
->isize
);
394 dest
->nalloc_index
= dest
->isize
;
398 std::memcpy(dest
->index
, src
->index
, dest
->isize
*sizeof(*dest
->index
));
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).
408 gmx_ana_index_dump(gmx::TextWriter
*writer
, gmx_ana_index_t
*g
, int maxn
)
410 writer
->writeString(gmx::formatString("(%d atoms)", g
->isize
));
413 writer
->writeString(":");
415 if (maxn
>= 0 && n
> maxn
)
419 for (int j
= 0; j
< n
; ++j
)
421 writer
->writeString(gmx::formatString(" %d", g
->index
[j
]+1));
425 writer
->writeString(" ...");
428 writer
->ensureLineBreak();
432 gmx_ana_index_get_max_index(gmx_ana_index_t
*g
)
440 return *std::max_element(g
->index
, g
->index
+ g
->isize
);
445 * \param[in] g Index group to check.
446 * \returns true if the index group is sorted and has no duplicates,
450 gmx_ana_index_check_sorted(gmx_ana_index_t
*g
)
454 for (i
= 0; i
< g
->isize
-1; ++i
)
456 if (g
->index
[i
+1] <= g
->index
[i
])
465 gmx_ana_index_check_range(gmx_ana_index_t
*g
, int natoms
)
467 for (int i
= 0; i
< g
->isize
; ++i
)
469 if (g
->index
[i
] < 0 || g
->index
[i
] >= natoms
)
477 /********************************************************************
479 ********************************************************************/
481 /** Helper function for gmx_ana_index_sort(). */
483 cmp_atomid(const void *a
, const void *b
)
485 if (*(int *)a
< *(int *)b
)
489 if (*(int *)a
> *(int *)b
)
497 * \param[in,out] g Index group to be sorted.
500 gmx_ana_index_sort(gmx_ana_index_t
*g
)
502 std::qsort(g
->index
, g
->isize
, sizeof(*g
->index
), cmp_atomid
);
506 gmx_ana_index_remove_duplicates(gmx_ana_index_t
*g
)
509 for (int i
= 0; i
< g
->isize
; ++i
)
511 if (i
== 0 || g
->index
[i
-1] != g
->index
[i
])
513 g
->index
[j
] = g
->index
[i
];
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.
526 gmx_ana_index_equals(gmx_ana_index_t
*a
, gmx_ana_index_t
*b
)
530 if (a
->isize
!= b
->isize
)
534 for (i
= 0; i
< a
->isize
; ++i
)
536 if (a
->index
[i
] != b
->index
[i
])
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,
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.
554 gmx_ana_index_contains(gmx_ana_index_t
*a
, gmx_ana_index_t
*b
)
558 for (i
= j
= 0; j
< b
->isize
; ++i
, ++j
)
560 while (i
< a
->isize
&& a
->index
[i
] != b
->index
[j
])
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.
577 * \p dest can be the same as \p a or \p b.
580 gmx_ana_index_intersection(gmx_ana_index_t
*dest
,
581 gmx_ana_index_t
*a
, gmx_ana_index_t
*b
)
585 for (i
= j
= k
= 0; i
< a
->isize
&& j
< b
->isize
; ++i
)
587 while (j
< b
->isize
&& b
->index
[j
] < a
->index
[i
])
591 if (j
< b
->isize
&& b
->index
[j
] == a
->index
[i
])
593 dest
->index
[k
++] = b
->index
[j
++];
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.
604 * \p dest can equal \p a, but not \p b.
607 gmx_ana_index_difference(gmx_ana_index_t
*dest
,
608 gmx_ana_index_t
*a
, gmx_ana_index_t
*b
)
612 for (i
= j
= k
= 0; i
< a
->isize
; ++i
)
614 while (j
< b
->isize
&& b
->index
[j
] < a
->index
[i
])
618 if (j
== b
->isize
|| b
->index
[j
] != a
->index
[i
])
620 dest
->index
[k
++] = a
->index
[i
];
627 * \param[in] a First index group.
628 * \param[in] b Second index group.
629 * \returns Size of the difference \p a - \p b.
632 gmx_ana_index_difference_size(gmx_ana_index_t
*a
, gmx_ana_index_t
*b
)
636 for (i
= j
= k
= 0; i
< a
->isize
; ++i
)
638 while (j
< b
->isize
&& b
->index
[j
] < a
->index
[i
])
642 if (j
== b
->isize
|| b
->index
[j
] != a
->index
[i
])
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.
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
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).
665 * The calculation can be performed in-place by setting \p dest1 equal to
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
)
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
)
678 while (j
>= 0 && src
->index
[j
] != g
->index
[i
])
680 dest2
->index
[k
--] = src
->index
[j
--];
685 dest2
->index
[k
--] = src
->index
[j
--];
687 gmx_ana_index_copy(dest1
, g
, false);
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.
695 * \p a and \p b can have common items.
696 * \p dest can equal \p a or \p b.
698 * \see gmx_ana_index_merge()
701 gmx_ana_index_union(gmx_ana_index_t
*dest
,
702 gmx_ana_index_t
*a
, gmx_ana_index_t
*b
)
707 dsize
= gmx_ana_index_difference_size(b
, a
);
710 dest
->isize
= a
->isize
+ dsize
;
711 for (k
= dest
->isize
- 1; k
>= 0; k
--)
713 if (i
< 0 || (j
>= 0 && a
->index
[i
] < b
->index
[j
]))
715 dest
->index
[k
] = b
->index
[j
--];
719 if (j
>= 0 && a
->index
[i
] == b
->index
[j
])
723 dest
->index
[k
] = a
->index
[i
--];
729 gmx_ana_index_union_unsorted(gmx_ana_index_t
*dest
,
730 gmx_ana_index_t
*a
, gmx_ana_index_t
*b
)
732 if (gmx_ana_index_check_sorted(b
))
734 gmx_ana_index_union(dest
, a
, b
);
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
);
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.
752 * \p a and \p b should not have common items.
753 * \p dest can equal \p a or \p b.
755 * \see gmx_ana_index_union()
758 gmx_ana_index_merge(gmx_ana_index_t
*dest
,
759 gmx_ana_index_t
*a
, gmx_ana_index_t
*b
)
765 dest
->isize
= a
->isize
+ b
->isize
;
766 for (k
= dest
->isize
- 1; k
>= 0; k
--)
768 if (i
< 0 || (j
>= 0 && a
->index
[i
] < b
->index
[j
]))
770 dest
->index
[k
] = b
->index
[j
--];
774 dest
->index
[k
] = a
->index
[i
--];
779 /********************************************************************
780 * gmx_ana_indexmap_t and related things
781 ********************************************************************/
784 * Helper for splitting a sequence of atom indices into groups.
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.
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.
796 * \ingroup module_selection
799 next_group_index(int atomIndex
, const gmx_mtop_t
*top
, e_index_t type
, int *id
)
809 int resind
, molb
= 0;
810 mtopGetAtomAndResidueName(top
, atomIndex
, &molb
, nullptr, nullptr, nullptr, &resind
);
815 if (*id
>= 0 && top
->mols
.index
[*id
] > atomIndex
)
819 while (*id
< top
->mols
.nr
&& atomIndex
>= top
->mols
.index
[*id
+1])
823 GMX_ASSERT(*id
< top
->mols
.nr
, "Molecules do not span all the atoms");
834 * \param[in,out] t Output block.
835 * \param[in] top Topology structure
836 * (only used if \p type is \ref INDEX_RES or \ref INDEX_MOL, can be NULL
838 * \param[in] g Index group
839 * (can be NULL if \p type is \ref INDEX_UNKNOWN).
840 * \param[in] type Type of partitioning to make.
841 * \param[in] bComplete
842 * If true, the index group is expanded to include any residue/molecule
843 * (depending on \p type) that is partially contained in the group.
844 * If \p type is not INDEX_RES or INDEX_MOL, this has no effect.
846 * \p m should have been initialized somehow (calloc() is enough).
847 * \p g should be sorted.
850 gmx_ana_index_make_block(t_blocka
*t
, const gmx_mtop_t
*top
, gmx_ana_index_t
*g
,
851 e_index_t type
, bool bComplete
)
853 if (type
== INDEX_UNKNOWN
)
867 // TODO: Check callers and either check these there as well, or turn these
869 GMX_RELEASE_ASSERT(top
!= nullptr || (type
!= INDEX_RES
&& type
!= INDEX_MOL
),
870 "Topology must be provided for residue or molecule blocks");
871 GMX_RELEASE_ASSERT(!(type
== INDEX_MOL
&& top
->mols
.nr
== 0),
872 "Molecule information must be present for molecule blocks");
874 /* bComplete only does something for INDEX_RES or INDEX_MOL, so turn it
876 if (type
!= INDEX_RES
&& type
!= INDEX_MOL
)
880 /* Allocate memory for the atom array and fill it unless we are using
885 /* We may allocate some extra memory here because we don't know in
886 * advance how much will be needed. */
887 if (t
->nalloc_a
< top
->natoms
)
889 srenew(t
->a
, top
->natoms
);
890 t
->nalloc_a
= top
->natoms
;
896 if (t
->nalloc_a
< g
->isize
)
898 srenew(t
->a
, g
->isize
);
899 t
->nalloc_a
= g
->isize
;
901 std::memcpy(t
->a
, g
->index
, g
->isize
*sizeof(*(t
->a
)));
904 /* Allocate memory for the block index. We don't know in advance
905 * how much will be needed, so we allocate some extra and free it in the
907 if (t
->nalloc_index
< g
->isize
+ 1)
909 srenew(t
->index
, g
->isize
+ 1);
910 t
->nalloc_index
= g
->isize
+ 1;
916 for (int i
= 0; i
< g
->isize
; ++i
)
918 const int ai
= g
->index
[i
];
919 /* Find the ID number of the atom/residue/molecule corresponding to
921 if (next_group_index(ai
, top
, type
, &id
))
923 /* If this is the first atom in a new block, initialize the block. */
926 /* For completion, we first set the start of the block. */
927 t
->index
[t
->nr
++] = t
->nra
;
928 /* And then we find all the atoms that should be included. */
934 mtopGetMolblockIndex(top
, ai
, &molb
, &molnr
, &atnr_mol
);
935 const t_atoms
&mol_atoms
= top
->moltype
[top
->molblock
[molb
].type
].atoms
;
936 int last_atom
= atnr_mol
+ 1;
937 while (last_atom
< mol_atoms
.nr
938 && mol_atoms
.atom
[last_atom
].resind
== id
)
942 int first_atom
= atnr_mol
- 1;
943 while (first_atom
>= 0
944 && mol_atoms
.atom
[first_atom
].resind
== id
)
948 int first_mol_atom
= top
->molblock
[molb
].globalAtomStart
;
949 first_mol_atom
+= molnr
*top
->molblock
[molb
].natoms_mol
;
950 first_atom
= first_mol_atom
+ first_atom
+ 1;
951 last_atom
= first_mol_atom
+ last_atom
- 1;
952 for (int j
= first_atom
; j
<= last_atom
; ++j
)
959 for (int j
= top
->mols
.index
[id
]; j
< top
->mols
.index
[id
+1]; ++j
)
965 default: /* Should not be reached */
966 GMX_RELEASE_ASSERT(false, "Unreachable code was reached");
972 /* If not using completion, simply store the start of the block. */
973 t
->index
[t
->nr
++] = i
;
977 /* Set the end of the last block */
978 t
->index
[t
->nr
] = t
->nra
;
979 /* Free any unnecessary memory */
980 srenew(t
->index
, t
->nr
+1);
981 t
->nalloc_index
= t
->nr
+1;
984 srenew(t
->a
, t
->nra
);
985 t
->nalloc_a
= t
->nra
;
990 * \param[in] g Index group to check.
991 * \param[in] b Block data to check against.
992 * \returns true if \p g consists of one or more complete blocks from \p b,
995 * The atoms in \p g are assumed to be sorted.
998 gmx_ana_index_has_full_blocks(const gmx_ana_index_t
*g
, const t_block
*b
)
1003 /* Each round in the loop matches one block */
1004 while (i
< g
->isize
)
1006 /* Find the block that begins with the first unmatched atom */
1007 while (bi
< b
->nr
&& b
->index
[bi
] != g
->index
[i
])
1011 /* If not found, or if too large, return */
1012 if (bi
== b
->nr
|| i
+ b
->index
[bi
+1] - b
->index
[bi
] > g
->isize
)
1016 /* Check that the block matches the index */
1017 for (j
= b
->index
[bi
]; j
< b
->index
[bi
+1]; ++j
, ++i
)
1019 if (g
->index
[i
] != j
)
1024 /* Move the search to the next block */
1031 * \param[in] g Index group to check.
1032 * \param[in] b Block data to check against.
1033 * \returns true if \p g consists of one or more complete blocks from \p b,
1036 * The atoms in \p g and \p b->a are assumed to be in the same order.
1039 gmx_ana_index_has_full_ablocks(gmx_ana_index_t
*g
, t_blocka
*b
)
1044 /* Each round in the loop matches one block */
1045 while (i
< g
->isize
)
1047 /* Find the block that begins with the first unmatched atom */
1048 while (bi
< b
->nr
&& b
->a
[b
->index
[bi
]] != g
->index
[i
])
1052 /* If not found, or if too large, return */
1053 if (bi
== b
->nr
|| i
+ b
->index
[bi
+1] - b
->index
[bi
] > g
->isize
)
1057 /* Check that the block matches the index */
1058 for (j
= b
->index
[bi
]; j
< b
->index
[bi
+1]; ++j
, ++i
)
1060 if (b
->a
[j
] != g
->index
[i
])
1065 /* Move the search to the next block */
1072 * \brief Returns if an atom is at a residue boundary.
1074 * \param[in] top Topology data.
1075 * \param[in] a Atom index to check, should be -1 <= \p a < top->natoms.
1076 * \param[in,out] molb The molecule block of atom a
1077 * \returns true if atoms \p a and \p a + 1 are in different residues, false otherwise.
1079 static bool is_at_residue_boundary(const gmx_mtop_t
*top
, int a
, int *molb
)
1081 if (a
== -1 || a
+ 1 == top
->natoms
)
1086 mtopGetAtomAndResidueName(top
, a
, molb
,
1087 nullptr, nullptr, nullptr, &resindA
);
1089 mtopGetAtomAndResidueName(top
, a
+ 1, molb
,
1090 nullptr, nullptr, nullptr, &resindAPlusOne
);
1091 return resindAPlusOne
!= resindA
;
1095 * \param[in] g Index group to check.
1096 * \param[in] type Block data to check against.
1097 * \param[in] top Topology data.
1098 * \returns true if \p g consists of one or more complete elements of type
1099 * \p type, false otherwise.
1101 * \p g is assumed to be sorted, otherwise may return false negatives.
1103 * If \p type is \ref INDEX_ATOM, the return value is always true.
1104 * If \p type is \ref INDEX_UNKNOWN or \ref INDEX_ALL, the return value is
1108 gmx_ana_index_has_complete_elems(gmx_ana_index_t
*g
, e_index_t type
,
1109 const gmx_mtop_t
*top
)
1116 // TODO: Consider whether unsorted groups need to be supported better.
1130 for (int i
= 0; i
< g
->isize
; ++i
)
1132 const int a
= g
->index
[i
];
1133 // Check if a is consecutive or on a residue boundary
1136 if (!is_at_residue_boundary(top
, aPrev
, &molb
))
1140 if (!is_at_residue_boundary(top
, a
- 1, &molb
))
1147 GMX_ASSERT(g
->isize
> 0, "We return above when isize=0");
1148 const int a
= g
->index
[g
->isize
- 1];
1149 if (!is_at_residue_boundary(top
, a
, &molb
))
1157 return gmx_ana_index_has_full_blocks(g
, &top
->mols
);
1163 * \param[out] m Output structure.
1165 * Any contents of \p m are discarded without freeing.
1168 gmx_ana_indexmap_clear(gmx_ana_indexmap_t
*m
)
1170 m
->type
= INDEX_UNKNOWN
;
1174 m
->mapb
.index
= nullptr;
1175 m
->mapb
.nalloc_index
= 0;
1177 m
->mapb
.a
= nullptr;
1178 m
->mapb
.nalloc_a
= 0;
1181 m
->b
.index
= nullptr;
1184 m
->b
.nalloc_index
= 0;
1190 * \param[in,out] m Mapping structure.
1191 * \param[in] nr Maximum number of blocks to reserve space for.
1192 * \param[in] isize Maximum number of atoms to reserve space for.
1195 gmx_ana_indexmap_reserve(gmx_ana_indexmap_t
*m
, int nr
, int isize
)
1197 if (m
->mapb
.nalloc_index
< nr
+ 1)
1199 srenew(m
->refid
, nr
);
1200 srenew(m
->mapid
, nr
);
1201 srenew(m
->orgid
, nr
);
1202 srenew(m
->mapb
.index
, nr
+ 1);
1203 srenew(m
->b
.index
, nr
+ 1);
1204 m
->mapb
.nalloc_index
= nr
+ 1;
1205 m
->b
.nalloc_index
= nr
+ 1;
1207 if (m
->b
.nalloc_a
< isize
)
1209 srenew(m
->b
.a
, isize
);
1210 m
->b
.nalloc_a
= isize
;
1215 * \param[in,out] m Mapping structure to initialize.
1216 * \param[in] g Index group to map
1217 * (can be NULL if \p type is \ref INDEX_UNKNOWN).
1218 * \param[in] top Topology structure
1219 * (can be NULL if \p type is not \ref INDEX_RES or \ref INDEX_MOL).
1220 * \param[in] type Type of mapping to construct.
1222 * Initializes a new index group mapping.
1223 * The index group provided to gmx_ana_indexmap_update() should always be a
1224 * subset of the \p g given here.
1226 * \p m should have been initialized somehow (calloc() is enough).
1229 gmx_ana_indexmap_init(gmx_ana_indexmap_t
*m
, gmx_ana_index_t
*g
,
1230 const gmx_mtop_t
*top
, e_index_t type
)
1233 gmx_ana_index_make_block(&m
->b
, top
, g
, type
, false);
1234 gmx_ana_indexmap_reserve(m
, m
->b
.nr
, m
->b
.nra
);
1236 for (int i
= 0; i
< m
->b
.nr
; ++i
)
1238 const int ii
= (type
== INDEX_UNKNOWN
? 0 : m
->b
.a
[m
->b
.index
[i
]]);
1239 next_group_index(ii
, top
, type
, &id
);
1244 m
->mapb
.nr
= m
->b
.nr
;
1245 m
->mapb
.nra
= m
->b
.nra
;
1247 std::memcpy(m
->mapb
.index
, m
->b
.index
, (m
->b
.nr
+1)*sizeof(*(m
->mapb
.index
)));
1252 gmx_ana_indexmap_init_orgid_group(gmx_ana_indexmap_t
*m
, const gmx_mtop_t
*top
,
1255 GMX_RELEASE_ASSERT(m
->bStatic
,
1256 "Changing original IDs is not supported after starting "
1257 "to use the mapping");
1258 GMX_RELEASE_ASSERT(top
!= nullptr || (type
!= INDEX_RES
&& type
!= INDEX_MOL
),
1259 "Topology must be provided for residue or molecule blocks");
1260 GMX_RELEASE_ASSERT(!(type
== INDEX_MOL
&& top
->mols
.nr
== 0),
1261 "Molecule information must be present for molecule blocks");
1262 // Check that all atoms in each block belong to the same group.
1263 // This is a separate loop for better error handling (no state is modified
1264 // if there is an error.
1265 if (type
== INDEX_RES
|| type
== INDEX_MOL
)
1268 for (int i
= 0; i
< m
->b
.nr
; ++i
)
1270 const int ii
= m
->b
.a
[m
->b
.index
[i
]];
1271 if (next_group_index(ii
, top
, type
, &id
))
1273 for (int j
= m
->b
.index
[i
] + 1; j
< m
->b
.index
[i
+1]; ++j
)
1275 if (next_group_index(m
->b
.a
[j
], top
, type
, &id
))
1277 std::string
message("Grouping into residues/molecules is ambiguous");
1278 GMX_THROW(gmx::InconsistentInputError(message
));
1284 // Do a second loop, where things are actually set.
1287 for (int i
= 0; i
< m
->b
.nr
; ++i
)
1289 const int ii
= (type
== INDEX_UNKNOWN
? 0 : m
->b
.a
[m
->b
.index
[i
]]);
1290 if (next_group_index(ii
, top
, type
, &id
))
1294 m
->mapid
[i
] = group
;
1295 m
->orgid
[i
] = group
;
1297 // Count also the last group.
1303 * \param[in,out] m Mapping structure to initialize.
1304 * \param[in] b Block information to use for data.
1306 * Frees some memory that is not necessary for static index group mappings.
1307 * Internal pointers are set to point to data in \p b; it is the responsibility
1308 * of the caller to ensure that the block information matches the contents of
1310 * After this function has been called, the index group provided to
1311 * gmx_ana_indexmap_update() should always be the same as \p g given here.
1313 * This function breaks modularity of the index group mapping interface in an
1314 * ugly way, but allows reducing memory usage of static selections by a
1315 * significant amount.
1318 gmx_ana_indexmap_set_static(gmx_ana_indexmap_t
*m
, t_blocka
*b
)
1321 sfree(m
->mapb
.index
);
1324 m
->mapb
.nalloc_index
= 0;
1325 m
->mapb
.nalloc_a
= 0;
1326 m
->b
.nalloc_index
= 0;
1328 m
->mapid
= m
->orgid
;
1329 m
->mapb
.index
= b
->index
;
1331 m
->b
.index
= b
->index
;
1336 * \param[in,out] dest Destination data structure.
1337 * \param[in] src Source mapping.
1338 * \param[in] bFirst If true, memory is allocated for \p dest and a full
1339 * copy is made; otherwise, only variable parts are copied, and no memory
1342 * \p dest should have been initialized somehow (calloc() is enough).
1345 gmx_ana_indexmap_copy(gmx_ana_indexmap_t
*dest
, gmx_ana_indexmap_t
*src
, bool bFirst
)
1349 gmx_ana_indexmap_reserve(dest
, src
->b
.nr
, src
->b
.nra
);
1350 dest
->type
= src
->type
;
1351 dest
->b
.nr
= src
->b
.nr
;
1352 dest
->b
.nra
= src
->b
.nra
;
1353 std::memcpy(dest
->orgid
, src
->orgid
, dest
->b
.nr
*sizeof(*dest
->orgid
));
1354 std::memcpy(dest
->b
.index
, src
->b
.index
, (dest
->b
.nr
+1)*sizeof(*dest
->b
.index
));
1355 std::memcpy(dest
->b
.a
, src
->b
.a
, dest
->b
.nra
*sizeof(*dest
->b
.a
));
1357 dest
->mapb
.nr
= src
->mapb
.nr
;
1358 dest
->mapb
.nra
= src
->mapb
.nra
;
1359 if (src
->mapb
.nalloc_a
> 0)
1363 snew(dest
->mapb
.a
, src
->mapb
.nalloc_a
);
1364 dest
->mapb
.nalloc_a
= src
->mapb
.nalloc_a
;
1366 std::memcpy(dest
->mapb
.a
, src
->mapb
.a
, dest
->mapb
.nra
*sizeof(*dest
->mapb
.a
));
1370 dest
->mapb
.a
= src
->mapb
.a
;
1372 std::memcpy(dest
->refid
, src
->refid
, dest
->mapb
.nr
*sizeof(*dest
->refid
));
1373 std::memcpy(dest
->mapid
, src
->mapid
, dest
->mapb
.nr
*sizeof(*dest
->mapid
));
1374 std::memcpy(dest
->mapb
.index
, src
->mapb
.index
, (dest
->mapb
.nr
+1)*sizeof(*dest
->mapb
.index
));
1375 dest
->bStatic
= src
->bStatic
;
1379 * Helper function to set the source atoms in an index map.
1381 * \param[in,out] m Mapping structure.
1382 * \param[in] isize Number of atoms in the \p index array.
1383 * \param[in] index List of atoms.
1386 set_atoms(gmx_ana_indexmap_t
*m
, int isize
, int *index
)
1388 m
->mapb
.nra
= isize
;
1389 if (m
->mapb
.nalloc_a
== 0)
1395 for (int i
= 0; i
< isize
; ++i
)
1397 m
->mapb
.a
[i
] = index
[i
];
1403 * \param[in,out] m Mapping structure.
1404 * \param[in] g Current index group.
1405 * \param[in] bMaskOnly true if the unused blocks should be masked with
1406 * -1 instead of removing them.
1408 * Updates the index group mapping with the new index group \p g.
1410 * \see gmx_ana_indexmap_t
1413 gmx_ana_indexmap_update(gmx_ana_indexmap_t
*m
, gmx_ana_index_t
*g
,
1418 /* Process the simple cases first */
1419 if (m
->type
== INDEX_UNKNOWN
&& m
->b
.nra
== 0)
1423 if (m
->type
== INDEX_ALL
)
1425 set_atoms(m
, g
->isize
, g
->index
);
1428 m
->mapb
.index
[1] = g
->isize
;
1432 /* Reset the reference IDs and mapping if necessary */
1433 const bool bToFull
= (g
->isize
== m
->b
.nra
);
1434 const bool bWasFull
= (m
->mapb
.nra
== m
->b
.nra
);
1435 if (bToFull
|| bMaskOnly
)
1439 for (bj
= 0; bj
< m
->b
.nr
; ++bj
)
1446 for (bj
= 0; bj
< m
->b
.nr
; ++bj
)
1448 m
->mapid
[bj
] = m
->orgid
[bj
];
1450 for (bj
= 0; bj
<= m
->b
.nr
; ++bj
)
1452 m
->mapb
.index
[bj
] = m
->b
.index
[bj
];
1455 set_atoms(m
, m
->b
.nra
, m
->b
.a
);
1456 m
->mapb
.nr
= m
->b
.nr
;
1458 /* Exit immediately if the group is static */
1467 for (i
= j
= bj
= 0; i
< g
->isize
; ++i
, ++j
)
1469 /* Find the next atom in the block */
1470 while (m
->b
.a
[j
] != g
->index
[i
])
1474 /* Mark blocks that did not contain any atoms */
1475 while (bj
< m
->b
.nr
&& m
->b
.index
[bj
+1] <= j
)
1477 m
->refid
[bj
++] = -1;
1479 /* Advance the block index if we have reached the next block */
1480 if (m
->b
.index
[bj
] <= j
)
1485 /* Mark the last blocks as not accessible */
1486 while (bj
< m
->b
.nr
)
1488 m
->refid
[bj
++] = -1;
1493 set_atoms(m
, g
->isize
, g
->index
);
1494 for (i
= j
= bi
= 0, bj
= -1; i
< g
->isize
; ++i
)
1496 /* Find the next atom in the block */
1497 while (m
->b
.a
[j
] != g
->index
[i
])
1501 /* If we have reached a new block, add it */
1502 if (m
->b
.index
[bj
+1] <= j
)
1504 /* Skip any blocks in between */
1505 while (bj
< m
->b
.nr
&& m
->b
.index
[bj
+1] <= j
)
1510 m
->mapid
[bi
] = m
->orgid
[bj
];
1511 m
->mapb
.index
[bi
] = i
;
1515 /* Update the number of blocks */
1516 m
->mapb
.index
[bi
] = g
->isize
;
1523 * \param[in,out] m Mapping structure to free.
1525 * All the memory allocated for the mapping structure is freed, and
1526 * the pointers set to NULL.
1527 * The pointer \p m is not freed.
1530 gmx_ana_indexmap_deinit(gmx_ana_indexmap_t
*m
)
1533 if (m
->mapid
!= m
->orgid
)
1537 if (m
->mapb
.nalloc_index
> 0)
1539 sfree(m
->mapb
.index
);
1541 if (m
->mapb
.nalloc_a
> 0)
1546 if (m
->b
.nalloc_index
> 0)
1550 if (m
->b
.nalloc_a
> 0)
1554 gmx_ana_indexmap_clear(m
);