Replaced NOTSET from typedefs.h by local solutions.
[gromacs.git] / src / gromacs / topology / index.cpp
blob51b95406a907c3ecf68851bb125553440de88742
1 /*
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-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
37 #include "gmxpre.h"
39 #include "index.h"
41 #include <assert.h>
42 #include <ctype.h>
43 #include <stdlib.h>
44 #include <string.h>
46 #include <algorithm>
48 #include "gromacs/fileio/gmxfio.h"
49 #include "gromacs/fileio/strdb.h"
50 #include "gromacs/legacyheaders/txtdump.h"
51 #include "gromacs/topology/atoms.h"
52 #include "gromacs/topology/block.h"
53 #include "gromacs/topology/invblock.h"
54 #include "gromacs/topology/residuetypes.h"
55 #include "gromacs/utility/arraysize.h"
56 #include "gromacs/utility/cstringutil.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/smalloc.h"
60 static gmx_bool gmx_ask_yesno(gmx_bool bASK)
62 char c;
64 if (bASK)
68 c = toupper(fgetc(stdin));
70 while ((c != 'Y') && (c != 'N'));
72 return (c == 'Y');
74 else
76 return FALSE;
80 void write_index(const char *outf, t_blocka *b, char **gnames, gmx_bool bDuplicate, int natoms)
82 FILE *out;
83 int i, j, k;
85 out = gmx_fio_fopen(outf, "w");
86 /* fprintf(out,"%5d %5d\n",b->nr,b->nra); */
87 for (i = 0; (i < b->nr); i++)
89 fprintf(out, "[ %s ]\n", gnames[i]);
90 for (k = 0, j = b->index[i]; j < b->index[i+1]; j++, k++)
92 fprintf(out, "%4d ", b->a[j]+1);
93 if ((k % 15) == 14)
95 fprintf(out, "\n");
98 fprintf(out, "\n");
101 /* Duplicate copy, useful for computational electrophysiology double-layer setups */
102 if (bDuplicate)
104 fprintf(stderr, "Duplicating the whole system with an atom offset of %d atoms.\n", natoms);
105 for (i = 0; (i < b->nr); i++)
107 fprintf(out, "[ %s_copy ]\n", gnames[i]);
108 for (k = 0, j = b->index[i]; j < b->index[i+1]; j++, k++)
110 fprintf(out, "%4d ", b->a[j]+1 + natoms );
111 if ((k % 15) == 14)
113 fprintf(out, "\n");
116 fprintf(out, "\n");
120 gmx_fio_fclose(out);
123 void add_grp(t_blocka *b, char ***gnames, int nra, atom_id a[], const char *name)
125 int i;
127 srenew(b->index, b->nr+2);
128 srenew(*gnames, b->nr+1);
129 (*gnames)[b->nr] = gmx_strdup(name);
131 srenew(b->a, b->nra+nra);
132 for (i = 0; (i < nra); i++)
134 b->a[b->nra++] = a[i];
136 b->nr++;
137 b->index[b->nr] = b->nra;
140 /* compare index in `a' with group in `b' at `index',
141 when `index'<0 it is relative to end of `b' */
142 static gmx_bool grp_cmp(t_blocka *b, int nra, atom_id a[], int index)
144 int i;
146 if (index < 0)
148 index = b->nr-1+index;
150 if (index >= b->nr)
152 gmx_fatal(FARGS, "no such index group %d in t_blocka (nr=%d)", index, b->nr);
154 /* compare sizes */
155 if (nra != b->index[index+1] - b->index[index])
157 return FALSE;
159 for (i = 0; i < nra; i++)
161 if (a[i] != b->a[b->index[index]+i])
163 return FALSE;
166 return TRUE;
169 static void
170 p_status(const char *const *restype, int nres,
171 const char *const *typenames, int ntypes)
173 int i, j;
174 int * counter;
176 snew(counter, ntypes);
177 for (i = 0; i < ntypes; i++)
179 counter[i] = 0;
181 for (i = 0; i < nres; i++)
183 for (j = 0; j < ntypes; j++)
185 if (!gmx_strcasecmp(restype[i], typenames[j]))
187 counter[j]++;
192 for (i = 0; (i < ntypes); i++)
194 if (counter[i] > 0)
196 printf("There are: %5d %10s residues\n", counter[i], typenames[i]);
200 sfree(counter);
204 static atom_id *
205 mk_aid(t_atoms *atoms, const char ** restype, const char * typestring, int *nra, gmx_bool bMatch)
206 /* Make an array of atom_ids for all atoms with residuetypes matching typestring, or the opposite if bMatch is false */
208 atom_id *a;
209 int i;
210 int res;
212 snew(a, atoms->nr);
213 *nra = 0;
214 for (i = 0; (i < atoms->nr); i++)
216 res = !gmx_strcasecmp(restype[atoms->atom[i].resind], typestring);
217 if (bMatch == FALSE)
219 res = !res;
221 if (res)
223 a[(*nra)++] = i;
227 return a;
230 typedef struct {
231 char *rname;
232 gmx_bool bNeg;
233 char *gname;
234 } restp_t;
236 static void analyse_other(const char ** restype, t_atoms *atoms,
237 t_blocka *gb, char ***gn, gmx_bool bASK, gmx_bool bVerb)
239 restp_t *restp = NULL;
240 char **attp = NULL;
241 char *rname, *aname;
242 atom_id *aid, *aaid;
243 int i, j, k, l, resind, naid, naaid, natp, nrestp = 0;
245 for (i = 0; (i < atoms->nres); i++)
247 if (gmx_strcasecmp(restype[i], "Protein") && gmx_strcasecmp(restype[i], "DNA") && gmx_strcasecmp(restype[i], "RNA") && gmx_strcasecmp(restype[i], "Water"))
249 break;
252 if (i < atoms->nres)
254 /* we have others */
255 if (bVerb)
257 printf("Analysing residues not classified as Protein/DNA/RNA/Water and splitting into groups...\n");
259 for (k = 0; (k < atoms->nr); k++)
261 resind = atoms->atom[k].resind;
262 rname = *atoms->resinfo[resind].name;
263 if (gmx_strcasecmp(restype[resind], "Protein") && gmx_strcasecmp(restype[resind], "DNA") &&
264 gmx_strcasecmp(restype[resind], "RNA") && gmx_strcasecmp(restype[resind], "Water"))
267 for (l = 0; (l < nrestp); l++)
269 if (strcmp(restp[l].rname, rname) == 0)
271 break;
274 if (l == nrestp)
276 srenew(restp, nrestp+1);
277 restp[nrestp].rname = gmx_strdup(rname);
278 restp[nrestp].bNeg = FALSE;
279 restp[nrestp].gname = gmx_strdup(rname);
280 nrestp++;
284 for (i = 0; (i < nrestp); i++)
286 snew(aid, atoms->nr);
287 naid = 0;
288 for (j = 0; (j < atoms->nr); j++)
290 rname = *atoms->resinfo[atoms->atom[j].resind].name;
291 if ((strcmp(restp[i].rname, rname) == 0 && !restp[i].bNeg) ||
292 (strcmp(restp[i].rname, rname) != 0 && restp[i].bNeg))
294 aid[naid++] = j;
297 add_grp(gb, gn, naid, aid, restp[i].gname);
298 if (bASK)
300 printf("split %s into atoms (y/n) ? ", restp[i].gname);
301 fflush(stdout);
302 if (gmx_ask_yesno(bASK))
304 natp = 0;
305 for (k = 0; (k < naid); k++)
307 aname = *atoms->atomname[aid[k]];
308 for (l = 0; (l < natp); l++)
310 if (strcmp(aname, attp[l]) == 0)
312 break;
315 if (l == natp)
317 srenew(attp, ++natp);
318 attp[natp-1] = aname;
321 if (natp > 1)
323 for (l = 0; (l < natp); l++)
325 snew(aaid, naid);
326 naaid = 0;
327 for (k = 0; (k < naid); k++)
329 aname = *atoms->atomname[aid[k]];
330 if (strcmp(aname, attp[l]) == 0)
332 aaid[naaid++] = aid[k];
335 add_grp(gb, gn, naaid, aaid, attp[l]);
336 sfree(aaid);
339 sfree(attp);
340 attp = NULL;
343 sfree(aid);
344 sfree(restp[i].rname);
345 sfree(restp[i].gname);
347 sfree(restp);
351 /*! \brief
352 * Cata necessary to construct a single (protein) index group in
353 * analyse_prot().
355 typedef struct gmx_help_make_index_group
357 /** The set of atom names that will be used to form this index group */
358 const char **defining_atomnames;
359 /** Size of the defining_atomnames array */
360 int num_defining_atomnames;
361 /** Name of this index group */
362 const char *group_name;
363 /** Whether the above atom names name the atoms in the group, or
364 those not in the group */
365 gmx_bool bTakeComplement;
366 /** The index in wholename gives the first item in the arrays of
367 atomnames that should be tested with 'gmx_strncasecmp' in stead of
368 gmx_strcasecmp, or -1 if all items should be tested with strcasecmp
369 This is comparable to using a '*' wildcard at the end of specific
370 atom names, but that is more involved to implement...
372 int wholename;
373 /** Only create this index group if it differs from the one specified in compareto,
374 where -1 means to always create this group. */
375 int compareto;
376 } t_gmx_help_make_index_group;
378 static void analyse_prot(const char ** restype, t_atoms *atoms,
379 t_blocka *gb, char ***gn, gmx_bool bASK, gmx_bool bVerb)
381 /* lists of atomnames to be used in constructing index groups: */
382 static const char *pnoh[] = { "H", "HN" };
383 static const char *pnodum[] = {
384 "MN1", "MN2", "MCB1", "MCB2", "MCG1", "MCG2",
385 "MCD1", "MCD2", "MCE1", "MCE2", "MNZ1", "MNZ2"
387 static const char *calpha[] = { "CA" };
388 static const char *bb[] = { "N", "CA", "C" };
389 static const char *mc[] = { "N", "CA", "C", "O", "O1", "O2", "OC1", "OC2", "OT", "OXT" };
390 static const char *mcb[] = { "N", "CA", "CB", "C", "O", "O1", "O2", "OC1", "OC2", "OT", "OXT" };
391 static const char *mch[] = {
392 "N", "CA", "C", "O", "O1", "O2", "OC1", "OC2", "OT", "OXT",
393 "H1", "H2", "H3", "H", "HN"
396 static const t_gmx_help_make_index_group constructing_data[] =
397 {{ NULL, 0, "Protein", TRUE, -1, -1},
398 { pnoh, asize(pnoh), "Protein-H", TRUE, 0, -1},
399 { calpha, asize(calpha), "C-alpha", FALSE, -1, -1},
400 { bb, asize(bb), "Backbone", FALSE, -1, -1},
401 { mc, asize(mc), "MainChain", FALSE, -1, -1},
402 { mcb, asize(mcb), "MainChain+Cb", FALSE, -1, -1},
403 { mch, asize(mch), "MainChain+H", FALSE, -1, -1},
404 { mch, asize(mch), "SideChain", TRUE, -1, -1},
405 { mch, asize(mch), "SideChain-H", TRUE, 11, -1},
406 { pnodum, asize(pnodum), "Prot-Masses", TRUE, -1, 0}, };
407 const int num_index_groups = asize(constructing_data);
409 int n, j;
410 atom_id *aid;
411 int nra, npres;
412 gmx_bool match;
413 char ndx_name[STRLEN], *atnm;
414 int i;
416 if (bVerb)
418 printf("Analysing Protein...\n");
420 snew(aid, atoms->nr);
422 /* calculate the number of protein residues */
423 npres = 0;
424 for (i = 0; (i < atoms->nres); i++)
426 if (0 == gmx_strcasecmp(restype[i], "Protein"))
428 npres++;
431 /* find matching or complement atoms */
432 for (i = 0; (i < (int)num_index_groups); i++)
434 nra = 0;
435 for (n = 0; (n < atoms->nr); n++)
437 if (0 == gmx_strcasecmp(restype[atoms->atom[n].resind], "Protein"))
439 match = FALSE;
440 for (j = 0; (j < constructing_data[i].num_defining_atomnames); j++)
442 /* skip digits at beginning of atomname, e.g. 1H */
443 atnm = *atoms->atomname[n];
444 while (isdigit(atnm[0]))
446 atnm++;
448 if ( (constructing_data[i].wholename == -1) || (j < constructing_data[i].wholename) )
450 if (0 == gmx_strcasecmp(constructing_data[i].defining_atomnames[j], atnm))
452 match = TRUE;
455 else
457 if (0 == gmx_strncasecmp(constructing_data[i].defining_atomnames[j], atnm, strlen(constructing_data[i].defining_atomnames[j])))
459 match = TRUE;
463 if (constructing_data[i].bTakeComplement != match)
465 aid[nra++] = n;
469 /* if we want to add this group always or it differs from previous
470 group, add it: */
471 if (-1 == constructing_data[i].compareto || !grp_cmp(gb, nra, aid, constructing_data[i].compareto-i) )
473 add_grp(gb, gn, nra, aid, constructing_data[i].group_name);
477 if (bASK)
479 for (i = 0; (i < (int)num_index_groups); i++)
481 printf("Split %12s into %5d residues (y/n) ? ", constructing_data[i].group_name, npres);
482 if (gmx_ask_yesno(bASK))
484 int resind;
485 nra = 0;
486 for (n = 0; ((atoms->atom[n].resind < npres) && (n < atoms->nr)); )
488 resind = atoms->atom[n].resind;
489 for (; ((atoms->atom[n].resind == resind) && (n < atoms->nr)); n++)
491 match = FALSE;
492 for (j = 0; (j < constructing_data[i].num_defining_atomnames); j++)
494 if (0 == gmx_strcasecmp(constructing_data[i].defining_atomnames[j], *atoms->atomname[n]))
496 match = TRUE;
499 if (constructing_data[i].bTakeComplement != match)
501 aid[nra++] = n;
504 /* copy the residuename to the tail of the groupname */
505 if (nra > 0)
507 t_resinfo *ri;
508 ri = &atoms->resinfo[resind];
509 sprintf(ndx_name, "%s_%s%d%c",
510 constructing_data[i].group_name, *ri->name, ri->nr, ri->ic == ' ' ? '\0' : ri->ic);
511 add_grp(gb, gn, nra, aid, ndx_name);
512 nra = 0;
517 printf("Make group with sidechain and C=O swapped (y/n) ? ");
518 if (gmx_ask_yesno(bASK))
520 /* Make swap sidechain C=O index */
521 int resind, hold;
522 nra = 0;
523 for (n = 0; ((atoms->atom[n].resind < npres) && (n < atoms->nr)); )
525 resind = atoms->atom[n].resind;
526 hold = -1;
527 for (; ((atoms->atom[n].resind == resind) && (n < atoms->nr)); n++)
529 if (strcmp("CA", *atoms->atomname[n]) == 0)
531 aid[nra++] = n;
532 hold = nra;
533 nra += 2;
535 else if (strcmp("C", *atoms->atomname[n]) == 0)
537 if (hold == -1)
539 gmx_incons("Atom naming problem");
541 aid[hold] = n;
543 else if (strcmp("O", *atoms->atomname[n]) == 0)
545 if (hold == -1)
547 gmx_incons("Atom naming problem");
549 aid[hold+1] = n;
551 else if (strcmp("O1", *atoms->atomname[n]) == 0)
553 if (hold == -1)
555 gmx_incons("Atom naming problem");
557 aid[hold+1] = n;
559 else
561 aid[nra++] = n;
565 /* copy the residuename to the tail of the groupname */
566 if (nra > 0)
568 add_grp(gb, gn, nra, aid, "SwapSC-CO");
572 sfree(aid);
576 void analyse(t_atoms *atoms, t_blocka *gb, char ***gn, gmx_bool bASK, gmx_bool bVerb)
578 gmx_residuetype_t*rt = NULL;
579 char *resnm;
580 atom_id *aid;
581 const char ** restype;
582 int nra;
583 int i, k;
584 int ntypes;
585 char ** p_typename;
586 int iwater, iion;
587 int nwater, nion;
588 int found;
590 if (bVerb)
592 printf("Analysing residue names:\n");
594 /* Create system group, every single atom */
595 snew(aid, atoms->nr);
596 for (i = 0; i < atoms->nr; i++)
598 aid[i] = i;
600 add_grp(gb, gn, atoms->nr, aid, "System");
601 sfree(aid);
603 /* For every residue, get a pointer to the residue type name */
604 gmx_residuetype_init(&rt);
605 assert(rt);
607 snew(restype, atoms->nres);
608 ntypes = 0;
609 p_typename = NULL;
610 for (i = 0; i < atoms->nres; i++)
612 resnm = *atoms->resinfo[i].name;
613 gmx_residuetype_get_type(rt, resnm, &(restype[i]));
615 /* Note that this does not lead to a N*N loop, but N*K, where
616 * K is the number of residue _types_, which is small and independent of N.
618 found = 0;
619 for (k = 0; k < ntypes && !found; k++)
621 assert(p_typename != NULL);
622 found = !strcmp(restype[i], p_typename[k]);
624 if (!found)
626 srenew(p_typename, ntypes+1);
627 p_typename[ntypes++] = gmx_strdup(restype[i]);
631 if (bVerb)
633 p_status(restype, atoms->nres, p_typename, ntypes);
636 for (k = 0; k < ntypes; k++)
638 aid = mk_aid(atoms, restype, p_typename[k], &nra, TRUE);
640 /* Check for special types to do fancy stuff with */
642 if (!gmx_strcasecmp(p_typename[k], "Protein") && nra > 0)
644 sfree(aid);
645 /* PROTEIN */
646 analyse_prot(restype, atoms, gb, gn, bASK, bVerb);
648 /* Create a Non-Protein group */
649 aid = mk_aid(atoms, restype, "Protein", &nra, FALSE);
650 if ((nra > 0) && (nra < atoms->nr))
652 add_grp(gb, gn, nra, aid, "non-Protein");
654 sfree(aid);
656 else if (!gmx_strcasecmp(p_typename[k], "Water") && nra > 0)
658 add_grp(gb, gn, nra, aid, p_typename[k]);
659 /* Add this group as 'SOL' too, for backward compatibility with older gromacs versions */
660 add_grp(gb, gn, nra, aid, "SOL");
662 sfree(aid);
664 /* Solvent, create a negated group too */
665 aid = mk_aid(atoms, restype, "Water", &nra, FALSE);
666 if ((nra > 0) && (nra < atoms->nr))
668 add_grp(gb, gn, nra, aid, "non-Water");
670 sfree(aid);
672 else if (nra > 0)
674 /* Other groups */
675 add_grp(gb, gn, nra, aid, p_typename[k]);
676 sfree(aid);
677 analyse_other(restype, atoms, gb, gn, bASK, bVerb);
679 sfree(p_typename[k]);
682 sfree(p_typename);
683 sfree(restype);
684 gmx_residuetype_destroy(rt);
686 /* Create a merged water_and_ions group */
687 iwater = -1;
688 iion = -1;
689 nwater = 0;
690 nion = 0;
692 for (i = 0; i < gb->nr; i++)
694 if (!gmx_strcasecmp((*gn)[i], "Water"))
696 iwater = i;
697 nwater = gb->index[i+1]-gb->index[i];
699 else if (!gmx_strcasecmp((*gn)[i], "Ion"))
701 iion = i;
702 nion = gb->index[i+1]-gb->index[i];
706 if (nwater > 0 && nion > 0)
708 srenew(gb->index, gb->nr+2);
709 srenew(*gn, gb->nr+1);
710 (*gn)[gb->nr] = gmx_strdup("Water_and_ions");
711 srenew(gb->a, gb->nra+nwater+nion);
712 if (nwater > 0)
714 for (i = gb->index[iwater]; i < gb->index[iwater+1]; i++)
716 gb->a[gb->nra++] = gb->a[i];
719 if (nion > 0)
721 for (i = gb->index[iion]; i < gb->index[iion+1]; i++)
723 gb->a[gb->nra++] = gb->a[i];
726 gb->nr++;
727 gb->index[gb->nr] = gb->nra;
732 void check_index(char *gname, int n, atom_id index[], char *traj, int natoms)
734 int i;
736 for (i = 0; i < n; i++)
738 if (index[i] >= natoms)
740 gmx_fatal(FARGS, "%s atom number (index[%d]=%d) is larger than the number of atoms in %s (%d)",
741 gname ? gname : "Index", i+1, index[i]+1,
742 traj ? traj : "the trajectory", natoms);
744 else if (index[i] < 0)
746 gmx_fatal(FARGS, "%s atom number (index[%d]=%d) is less than zero",
747 gname ? gname : "Index", i+1, index[i]+1);
752 t_blocka *init_index(const char *gfile, char ***grpname)
754 FILE *in;
755 t_blocka *b;
756 int maxentries;
757 int i, j;
758 char line[STRLEN], *pt, str[STRLEN];
760 in = gmx_fio_fopen(gfile, "r");
761 snew(b, 1);
762 b->nr = 0;
763 b->index = NULL;
764 b->nra = 0;
765 b->a = NULL;
766 *grpname = NULL;
767 maxentries = 0;
768 while (get_a_line(in, line, STRLEN))
770 if (get_header(line, str))
772 b->nr++;
773 srenew(b->index, b->nr+1);
774 srenew(*grpname, b->nr);
775 if (b->nr == 1)
777 b->index[0] = 0;
779 b->index[b->nr] = b->index[b->nr-1];
780 (*grpname)[b->nr-1] = gmx_strdup(str);
782 else
784 if (b->nr == 0)
786 gmx_fatal(FARGS, "The first header of your indexfile is invalid");
788 pt = line;
789 while (sscanf(pt, "%s", str) == 1)
791 i = b->index[b->nr];
792 if (i >= maxentries)
794 maxentries += 1024;
795 srenew(b->a, maxentries);
797 assert(b->a != NULL); // for clang analyzer
798 b->a[i] = strtol(str, NULL, 10)-1;
799 b->index[b->nr]++;
800 (b->nra)++;
801 pt = strstr(pt, str)+strlen(str);
805 gmx_fio_fclose(in);
807 for (i = 0; (i < b->nr); i++)
809 assert(b->a != NULL); // for clang analyzer
810 for (j = b->index[i]; (j < b->index[i+1]); j++)
812 if (b->a[j] < 0)
814 fprintf(stderr, "\nWARNING: negative index %d in group %s\n\n",
815 b->a[j], (*grpname)[i]);
820 return b;
823 static void minstring(char *str)
825 int i;
827 for (i = 0; (i < (int)strlen(str)); i++)
829 if (str[i] == '-')
831 str[i] = '_';
836 int find_group(const char *s, int ngrps, char **grpname)
838 int aa, i, n;
839 char string[STRLEN];
840 gmx_bool bMultiple;
841 bMultiple = FALSE;
842 n = strlen(s);
843 aa = -1;
844 /* first look for whole name match */
845 if (aa == -1)
847 for (i = 0; i < ngrps; i++)
849 if (gmx_strcasecmp_min(s, grpname[i]) == 0)
851 if (aa != -1)
853 bMultiple = TRUE;
855 aa = i;
859 /* second look for first string match */
860 if (aa == -1)
862 for (i = 0; i < ngrps; i++)
864 if (gmx_strncasecmp_min(s, grpname[i], n) == 0)
866 if (aa != -1)
868 bMultiple = TRUE;
870 aa = i;
874 /* last look for arbitrary substring match */
875 if (aa == -1)
877 char key[STRLEN];
878 strncpy(key, s, sizeof(key)-1);
879 key[STRLEN-1] = '\0';
880 upstring(key);
881 minstring(key);
882 for (i = 0; i < ngrps; i++)
884 strcpy(string, grpname[i]);
885 upstring(string);
886 minstring(string);
887 if (strstr(string, key) != NULL)
889 if (aa != -1)
891 bMultiple = TRUE;
893 aa = i;
897 if (bMultiple)
899 printf("Error: Multiple groups '%s' selected\n", s);
900 aa = -1;
902 return aa;
905 static int qgroup(int *a, int ngrps, char **grpname)
907 char s[STRLEN];
908 int aa;
909 gmx_bool bInRange;
910 char *end;
914 fprintf(stderr, "Select a group: ");
917 if (scanf("%s", s) != 1)
919 gmx_fatal(FARGS, "Cannot read from input");
921 trim(s); /* remove spaces */
923 while (strlen(s) == 0);
924 aa = strtol(s, &end, 10);
925 if (aa == 0 && end[0] != '\0') /* string entered */
927 aa = find_group(s, ngrps, grpname);
929 bInRange = (aa >= 0 && aa < ngrps);
930 if (!bInRange)
932 printf("Error: No such group '%s'\n", s);
935 while (!bInRange);
936 printf("Selected %d: '%s'\n", aa, grpname[aa]);
937 *a = aa;
938 return aa;
941 static void rd_groups(t_blocka *grps, char **grpname, char *gnames[],
942 int ngrps, int isize[], atom_id *index[], int grpnr[])
944 int i, j, gnr1;
946 if (grps->nr == 0)
948 gmx_fatal(FARGS, "Error: no groups in indexfile");
950 for (i = 0; (i < grps->nr); i++)
952 fprintf(stderr, "Group %5d (%15s) has %5d elements\n", i, grpname[i],
953 grps->index[i+1]-grps->index[i]);
955 for (i = 0; (i < ngrps); i++)
957 if (grps->nr > 1)
961 gnr1 = qgroup(&grpnr[i], grps->nr, grpname);
962 if ((gnr1 < 0) || (gnr1 >= grps->nr))
964 fprintf(stderr, "Select between %d and %d.\n", 0, grps->nr-1);
967 while ((gnr1 < 0) || (gnr1 >= grps->nr));
969 else
971 fprintf(stderr, "There is one group in the index\n");
972 gnr1 = 0;
974 gnames[i] = gmx_strdup(grpname[gnr1]);
975 isize[i] = grps->index[gnr1+1]-grps->index[gnr1];
976 snew(index[i], isize[i]);
977 for (j = 0; (j < isize[i]); j++)
979 index[i][j] = grps->a[grps->index[gnr1]+j];
984 void rd_index(const char *statfile, int ngrps, int isize[],
985 atom_id *index[], char *grpnames[])
987 char **gnames;
988 t_blocka *grps;
989 int *grpnr;
991 snew(grpnr, ngrps);
992 if (!statfile)
994 gmx_fatal(FARGS, "No index file specified");
996 grps = init_index(statfile, &gnames);
997 rd_groups(grps, gnames, grpnames, ngrps, isize, index, grpnr);
1000 void rd_index_nrs(char *statfile, int ngrps, int isize[],
1001 atom_id *index[], char *grpnames[], int grpnr[])
1003 char **gnames;
1004 t_blocka *grps;
1006 if (!statfile)
1008 gmx_fatal(FARGS, "No index file specified");
1010 grps = init_index(statfile, &gnames);
1012 rd_groups(grps, gnames, grpnames, ngrps, isize, index, grpnr);
1015 void get_index(t_atoms *atoms, const char *fnm, int ngrps,
1016 int isize[], atom_id *index[], char *grpnames[])
1018 char ***gnames;
1019 t_blocka *grps = NULL;
1020 int *grpnr;
1022 snew(grpnr, ngrps);
1023 snew(gnames, 1);
1024 if (fnm != NULL)
1026 grps = init_index(fnm, gnames);
1028 else if (atoms)
1030 snew(grps, 1);
1031 snew(grps->index, 1);
1032 analyse(atoms, grps, gnames, FALSE, FALSE);
1034 else
1036 gmx_incons("You need to supply a valid atoms structure or a valid index file name");
1039 rd_groups(grps, *gnames, grpnames, ngrps, isize, index, grpnr);
1042 t_cluster_ndx *cluster_index(FILE *fplog, const char *ndx)
1044 t_cluster_ndx *c;
1045 int i;
1047 snew(c, 1);
1048 c->clust = init_index(ndx, &c->grpname);
1049 c->maxframe = -1;
1050 for (i = 0; (i < c->clust->nra); i++)
1052 c->maxframe = std::max(c->maxframe, c->clust->a[i]);
1054 fprintf(fplog ? fplog : stdout,
1055 "There are %d clusters containing %d structures, highest framenr is %d\n",
1056 c->clust->nr, c->clust->nra, c->maxframe);
1057 if (debug)
1059 pr_blocka(debug, 0, "clust", c->clust, TRUE);
1060 for (i = 0; (i < c->clust->nra); i++)
1062 if ((c->clust->a[i] < 0) || (c->clust->a[i] > c->maxframe))
1064 gmx_fatal(FARGS, "Range check error for c->clust->a[%d] = %d\n"
1065 "should be within 0 and %d", i, c->clust->a[i], c->maxframe+1);
1069 c->inv_clust = make_invblocka(c->clust, c->maxframe);
1071 return c;