Move more tools out of gmxana
[gromacs.git] / src / gromacs / tools / make_ndx.cpp
bloba3758b259a5feceddceb5a87b318f31b9eeb9154
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) 2012,2013,2014,2015,2016,2017,2018,2019, 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 "make_ndx.h"
41 #include <cctype>
42 #include <cstring>
44 #include <algorithm>
46 #include "gromacs/commandline/pargs.h"
47 #include "gromacs/fileio/confio.h"
48 #include "gromacs/math/vec.h"
49 #include "gromacs/topology/block.h"
50 #include "gromacs/topology/index.h"
51 #include "gromacs/topology/topology.h"
52 #include "gromacs/utility/arraysize.h"
53 #include "gromacs/utility/cstringutil.h"
54 #include "gromacs/utility/fatalerror.h"
55 #include "gromacs/utility/futil.h"
56 #include "gromacs/utility/smalloc.h"
58 /* It's not nice to have size limits, but we should not spend more time
59 * on this ancient tool, but instead use the new selection library.
61 #define MAXNAMES 1024
62 #define NAME_LEN 1024
63 static const int NOTSET = -92637;
65 static gmx_bool bCase = FALSE;
67 static int or_groups(int nr1, const int *at1, int nr2, const int *at2,
68 int *nr, int *at)
70 int i1, i2, max = 0;
71 gmx_bool bNotIncr;
73 *nr = 0;
75 bNotIncr = FALSE;
76 for (i1 = 0; i1 < nr1; i1++)
78 if ((i1 > 0) && (at1[i1] <= max))
80 bNotIncr = TRUE;
82 max = at1[i1];
84 for (i1 = 0; i1 < nr2; i1++)
86 if ((i1 > 0) && (at2[i1] <= max))
88 bNotIncr = TRUE;
90 max = at2[i1];
93 if (bNotIncr)
95 printf("One of your groups is not ascending\n");
97 else
99 i1 = 0;
100 i2 = 0;
101 *nr = 0;
102 while ((i1 < nr1) || (i2 < nr2))
104 if ((i2 == nr2) || ((i1 < nr1) && (at1[i1] < at2[i2])))
106 at[*nr] = at1[i1];
107 (*nr)++;
108 i1++;
110 else
112 if ((i2 < nr2) && ((i1 == nr1) || (at1[i1] > at2[i2])))
114 at[*nr] = at2[i2];
115 (*nr)++;
117 i2++;
121 printf("Merged two groups with OR: %d %d -> %d\n", nr1, nr2, *nr);
124 return *nr;
127 static int and_groups(int nr1, const int *at1, int nr2, const int *at2,
128 int *nr, int *at)
130 int i1, i2;
132 *nr = 0;
133 for (i1 = 0; i1 < nr1; i1++)
135 for (i2 = 0; i2 < nr2; i2++)
137 if (at1[i1] == at2[i2])
139 at[*nr] = at1[i1];
140 (*nr)++;
145 printf("Merged two groups with AND: %d %d -> %d\n", nr1, nr2, *nr);
147 return *nr;
150 static gmx_bool is_name_char(char c)
152 /* This string should contain all characters that can not be
153 * the first letter of a name due to the make_ndx syntax.
155 const char *spec = " !&|";
157 return (c != '\0' && std::strchr(spec, c) == nullptr);
160 static int parse_names(char **string, int *n_names, char **names)
162 int i;
164 *n_names = 0;
165 while (is_name_char((*string)[0]) || (*string)[0] == ' ')
167 if (is_name_char((*string)[0]))
169 if (*n_names >= MAXNAMES)
171 gmx_fatal(FARGS, "To many names: %d\n", *n_names+1);
173 i = 0;
174 while (is_name_char((*string)[i]))
176 names[*n_names][i] = (*string)[i];
177 i++;
178 if (i > NAME_LEN)
180 printf("Name is too long, the maximum is %d characters\n", NAME_LEN);
181 return 0;
184 names[*n_names][i] = '\0';
185 if (!bCase)
187 upstring(names[*n_names]);
189 *string += i;
190 (*n_names)++;
192 else
194 (*string)++;
198 return *n_names;
201 static gmx_bool parse_int_char(char **string, int *nr, char *c)
203 char *orig;
204 gmx_bool bRet;
206 orig = *string;
208 while ((*string)[0] == ' ')
210 (*string)++;
213 bRet = FALSE;
215 *c = ' ';
217 if (std::isdigit((*string)[0]))
219 *nr = (*string)[0]-'0';
220 (*string)++;
221 while (std::isdigit((*string)[0]))
223 *nr = (*nr)*10+(*string)[0]-'0';
224 (*string)++;
226 if (std::isalpha((*string)[0]))
228 *c = (*string)[0];
229 (*string)++;
231 /* Check if there is at most one non-digit character */
232 if (!std::isalnum((*string)[0]))
234 bRet = TRUE;
236 else
238 *string = orig;
241 else
243 *nr = NOTSET;
246 return bRet;
249 static gmx_bool parse_int(char **string, int *nr)
251 char *orig, c;
252 gmx_bool bRet;
254 orig = *string;
255 bRet = parse_int_char(string, nr, &c);
256 if (bRet && c != ' ')
258 *string = orig;
259 bRet = FALSE;
262 return bRet;
265 static gmx_bool isquote(char c)
267 return (c == '\"');
270 static gmx_bool parse_string(char **string, int *nr, int ngrps, char **grpname)
272 char *s, *sp;
273 char c;
275 while ((*string)[0] == ' ')
277 (*string)++;
280 (*nr) = NOTSET;
281 if (isquote((*string)[0]))
283 c = (*string)[0];
284 (*string)++;
285 s = gmx_strdup((*string));
286 sp = std::strchr(s, c);
287 if (sp != nullptr)
289 (*string) += sp-s + 1;
290 sp[0] = '\0';
291 (*nr) = find_group(s, ngrps, grpname);
295 return (*nr) != NOTSET;
298 static int select_atomnumbers(char **string, const t_atoms *atoms, int n1,
299 int *nr, int *index, char *gname)
301 char buf[STRLEN];
302 int i, up;
304 *nr = 0;
305 while ((*string)[0] == ' ')
307 (*string)++;
309 if ((*string)[0] == '-')
311 (*string)++;
312 parse_int(string, &up);
313 if ((n1 < 1) || (n1 > atoms->nr) || (up < 1) || (up > atoms->nr))
315 printf("Invalid atom range\n");
317 else
319 for (i = n1-1; i <= up-1; i++)
321 index[*nr] = i;
322 (*nr)++;
324 printf("Found %d atom%s in range %d-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
325 if (n1 == up)
327 sprintf(buf, "a_%d", n1);
329 else
331 sprintf(buf, "a_%d-%d", n1, up);
333 std::strcpy(gname, buf);
336 else
338 i = n1;
339 sprintf(gname, "a");
342 if ((i-1 >= 0) && (i-1 < atoms->nr))
344 index[*nr] = i-1;
345 (*nr)++;
346 sprintf(buf, "_%d", i);
347 std::strcat(gname, buf);
349 else
351 printf("Invalid atom number %d\n", i);
352 *nr = 0;
355 while ((*nr != 0) && (parse_int(string, &i)));
358 return *nr;
361 static int select_residuenumbers(char **string, const t_atoms *atoms,
362 int n1, char c,
363 int *nr, int *index, char *gname)
365 char buf[STRLEN];
366 int i, j, up;
367 t_resinfo *ri;
369 *nr = 0;
370 while ((*string)[0] == ' ')
372 (*string)++;
374 if ((*string)[0] == '-')
376 /* Residue number range selection */
377 if (c != ' ')
379 printf("Error: residue insertion codes can not be used with residue range selection\n");
380 return 0;
382 (*string)++;
383 parse_int(string, &up);
385 for (i = 0; i < atoms->nr; i++)
387 ri = &atoms->resinfo[atoms->atom[i].resind];
388 for (j = n1; (j <= up); j++)
390 if (ri->nr == j && (c == ' ' || ri->ic == c))
392 index[*nr] = i;
393 (*nr)++;
397 printf("Found %d atom%s with res.nr. in range %d-%d\n",
398 *nr, (*nr == 1) ? "" : "s", n1, up);
399 if (n1 == up)
401 sprintf(buf, "r_%d", n1);
403 else
405 sprintf(buf, "r_%d-%d", n1, up);
407 std::strcpy(gname, buf);
409 else
411 /* Individual residue number/insertion code selection */
412 j = n1;
413 sprintf(gname, "r");
416 for (i = 0; i < atoms->nr; i++)
418 ri = &atoms->resinfo[atoms->atom[i].resind];
419 if (ri->nr == j && ri->ic == c)
421 index[*nr] = i;
422 (*nr)++;
425 sprintf(buf, "_%d", j);
426 std::strcat(gname, buf);
428 while (parse_int_char(string, &j, &c));
431 return *nr;
434 static int select_residueindices(char **string, const t_atoms *atoms,
435 int n1, char c,
436 int *nr, int *index, char *gname)
438 /*this should be similar to select_residuenumbers except select by index (sequential numbering in file)*/
439 /*resind+1 for 1-indexing*/
440 char buf[STRLEN];
441 int i, j, up;
442 t_resinfo *ri;
444 *nr = 0;
445 while ((*string)[0] == ' ')
447 (*string)++;
449 if ((*string)[0] == '-')
451 /* Residue number range selection */
452 if (c != ' ')
454 printf("Error: residue insertion codes can not be used with residue range selection\n");
455 return 0;
457 (*string)++;
458 parse_int(string, &up);
460 for (i = 0; i < atoms->nr; i++)
462 ri = &atoms->resinfo[atoms->atom[i].resind];
463 for (j = n1; (j <= up); j++)
465 if (atoms->atom[i].resind+1 == j && (c == ' ' || ri->ic == c))
467 index[*nr] = i;
468 (*nr)++;
472 printf("Found %d atom%s with resind.+1 in range %d-%d\n",
473 *nr, (*nr == 1) ? "" : "s", n1, up);
474 if (n1 == up)
476 sprintf(buf, "r_%d", n1);
478 else
480 sprintf(buf, "r_%d-%d", n1, up);
482 std::strcpy(gname, buf);
484 else
486 /* Individual residue number/insertion code selection */
487 j = n1;
488 sprintf(gname, "r");
491 for (i = 0; i < atoms->nr; i++)
493 ri = &atoms->resinfo[atoms->atom[i].resind];
494 if (atoms->atom[i].resind+1 == j && ri->ic == c)
496 index[*nr] = i;
497 (*nr)++;
500 sprintf(buf, "_%d", j);
501 std::strcat(gname, buf);
503 while (parse_int_char(string, &j, &c));
506 return *nr;
510 static gmx_bool atoms_from_residuenumbers(const t_atoms *atoms, int group, t_blocka *block,
511 int *nr, int *index, char *gname)
513 int i, j, j0, j1, resnr, nres;
515 j0 = block->index[group];
516 j1 = block->index[group+1];
517 nres = atoms->nres;
518 for (j = j0; j < j1; j++)
520 if (block->a[j] >= nres)
522 printf("Index %s contains number>nres (%d>%d)\n",
523 gname, block->a[j]+1, nres);
524 return FALSE;
527 for (i = 0; i < atoms->nr; i++)
529 resnr = atoms->resinfo[atoms->atom[i].resind].nr;
530 for (j = j0; j < j1; j++)
532 if (block->a[j]+1 == resnr)
534 index[*nr] = i;
535 (*nr)++;
536 break;
540 printf("Found %d atom%s in %d residues from group %s\n",
541 *nr, (*nr == 1) ? "" : "s", j1-j0, gname);
542 return *nr != 0;
545 static gmx_bool comp_name(const char *name, const char *search)
547 gmx_bool matches = TRUE;
549 // Loop while name and search are not end-of-string and matches is true
550 for (; *name && *search && matches; name++, search++)
552 if (*search == '?')
554 // still matching, continue to next character
555 continue;
557 else if (*search == '*')
559 if (*(search+1))
561 printf("WARNING: Currently '*' is only supported at the end of an expression\n");
563 // if * is the last char in search string, we have a match,
564 // otherwise we just failed. Return in either case, we're done.
565 return (*(search+1) == '\0');
568 // Compare one character
569 if (bCase)
571 matches = (*name == *search);
573 else
575 matches = (std::toupper(*name) == std::toupper(*search));
579 matches = matches && (*name == '\0' && (*search == '\0' || *search == '*'));
581 return matches;
584 static int select_chainnames(const t_atoms *atoms, int n_names, char **names,
585 int *nr, int *index)
587 char name[2];
588 int j;
589 int i;
591 name[1] = 0;
592 *nr = 0;
593 for (i = 0; i < atoms->nr; i++)
595 name[0] = atoms->resinfo[atoms->atom[i].resind].chainid;
596 j = 0;
597 while (j < n_names && !comp_name(name, names[j]))
599 j++;
601 if (j < n_names)
603 index[*nr] = i;
604 (*nr)++;
607 printf("Found %d atom%s with chain identifier%s",
608 *nr, (*nr == 1) ? "" : "s", (n_names == 1) ? "" : "s");
609 for (j = 0; (j < n_names); j++)
611 printf(" %s", names[j]);
613 printf("\n");
615 return *nr;
618 static int select_atomnames(const t_atoms *atoms, int n_names, char **names,
619 int *nr, int *index, gmx_bool bType)
621 char *name;
622 int j;
623 int i;
625 *nr = 0;
626 for (i = 0; i < atoms->nr; i++)
628 if (bType)
630 name = *(atoms->atomtype[i]);
632 else
634 name = *(atoms->atomname[i]);
636 j = 0;
637 while (j < n_names && !comp_name(name, names[j]))
639 j++;
641 if (j < n_names)
643 index[*nr] = i;
644 (*nr)++;
647 printf("Found %d atoms with %s%s",
648 *nr, bType ? "type" : "name", (n_names == 1) ? "" : "s");
649 for (j = 0; (j < n_names); j++)
651 printf(" %s", names[j]);
653 printf("\n");
655 return *nr;
658 static int select_residuenames(const t_atoms *atoms, int n_names, char **names,
659 int *nr, int *index)
661 char *name;
662 int j;
663 int i;
665 *nr = 0;
666 for (i = 0; i < atoms->nr; i++)
668 name = *(atoms->resinfo[atoms->atom[i].resind].name);
669 j = 0;
670 while (j < n_names && !comp_name(name, names[j]))
672 j++;
674 if (j < n_names)
676 index[*nr] = i;
677 (*nr)++;
680 printf("Found %d atoms with residue name%s", *nr, (n_names == 1) ? "" : "s");
681 for (j = 0; (j < n_names); j++)
683 printf(" %s", names[j]);
685 printf("\n");
687 return *nr;
690 static void copy2block(int n, const int *index, t_blocka *block)
692 int i, n0;
694 block->nr++;
695 n0 = block->nra;
696 block->nra = n0+n;
697 srenew(block->index, block->nr+1);
698 block->index[block->nr] = n0+n;
699 srenew(block->a, n0+n);
700 for (i = 0; (i < n); i++)
702 block->a[n0+i] = index[i];
706 static void make_gname(int n, char **names, char *gname)
708 int i;
710 std::strcpy(gname, names[0]);
711 for (i = 1; i < n; i++)
713 std::strcat(gname, "_");
714 std::strcat(gname, names[i]);
718 static void copy_group(int g, t_blocka *block, int *nr, int *index)
720 int i, i0;
722 i0 = block->index[g];
723 *nr = block->index[g+1]-i0;
724 for (i = 0; i < *nr; i++)
726 index[i] = block->a[i0+i];
730 static void remove_group(int nr, int nr2, t_blocka *block, char ***gn)
732 int i, j, shift;
733 char *name;
735 if (nr2 == NOTSET)
737 nr2 = nr;
740 for (j = 0; j <= nr2-nr; j++)
742 if ((nr < 0) || (nr >= block->nr))
744 printf("Group %d does not exist\n", nr+j);
746 else
748 shift = block->index[nr+1]-block->index[nr];
749 for (i = block->index[nr+1]; i < block->nra; i++)
751 block->a[i-shift] = block->a[i];
754 for (i = nr; i < block->nr; i++)
756 block->index[i] = block->index[i+1]-shift;
758 name = gmx_strdup((*gn)[nr]);
759 sfree((*gn)[nr]);
760 for (i = nr; i < block->nr-1; i++)
762 (*gn)[i] = (*gn)[i+1];
764 block->nr--;
765 block->nra = block->index[block->nr];
766 printf("Removed group %d '%s'\n", nr+j, name);
767 sfree(name);
772 static void split_group(const t_atoms *atoms, int sel_nr, t_blocka *block, char ***gn,
773 gmx_bool bAtom)
775 char buf[STRLEN], *name;
776 int i, resind;
777 int a, n0, n1;
779 printf("Splitting group %d '%s' into %s\n", sel_nr, (*gn)[sel_nr],
780 bAtom ? "atoms" : "residues");
782 n0 = block->index[sel_nr];
783 n1 = block->index[sel_nr+1];
784 srenew(block->a, block->nra+n1-n0);
785 for (i = n0; i < n1; i++)
787 a = block->a[i];
788 resind = atoms->atom[a].resind;
789 name = *(atoms->resinfo[resind].name);
790 if (bAtom || (i == n0) || (atoms->atom[block->a[i-1]].resind != resind))
792 if (i > n0)
794 block->index[block->nr] = block->nra;
796 block->nr++;
797 srenew(block->index, block->nr+1);
798 srenew(*gn, block->nr);
799 if (bAtom)
801 sprintf(buf, "%s_%s_%d", (*gn)[sel_nr], *atoms->atomname[a], a+1);
803 else
805 sprintf(buf, "%s_%s_%d", (*gn)[sel_nr], name, atoms->resinfo[resind].nr);
807 (*gn)[block->nr-1] = gmx_strdup(buf);
809 block->a[block->nra] = a;
810 block->nra++;
812 block->index[block->nr] = block->nra;
815 static int split_chain(const t_atoms *atoms, const rvec *x,
816 int sel_nr, t_blocka *block, char ***gn)
818 char buf[STRLEN];
819 int j, nchain;
820 int i, a, natoms, *start = nullptr, *end = nullptr, ca_start, ca_end;
821 rvec vec;
823 natoms = atoms->nr;
824 nchain = 0;
825 ca_start = 0;
827 while (ca_start < natoms)
829 while ((ca_start < natoms) && std::strcmp(*atoms->atomname[ca_start], "CA") != 0)
831 ca_start++;
833 if (ca_start < natoms)
835 srenew(start, nchain+1);
836 srenew(end, nchain+1);
837 start[nchain] = ca_start;
838 while ((start[nchain] > 0) &&
839 (atoms->atom[start[nchain]-1].resind ==
840 atoms->atom[ca_start].resind))
842 start[nchain]--;
845 i = ca_start;
848 ca_end = i;
851 i++;
853 while ((i < natoms) && std::strcmp(*atoms->atomname[i], "CA") != 0);
854 if (i < natoms)
856 rvec_sub(x[ca_end], x[i], vec);
858 else
860 break;
863 while (norm(vec) < 0.45);
865 end[nchain] = ca_end;
866 while ((end[nchain]+1 < natoms) &&
867 (atoms->atom[end[nchain]+1].resind == atoms->atom[ca_end].resind))
869 end[nchain]++;
871 ca_start = end[nchain]+1;
872 nchain++;
875 if (nchain == 1)
877 printf("Found 1 chain, will not split\n");
879 else
881 printf("Found %d chains\n", nchain);
883 for (j = 0; j < nchain; j++)
885 printf("%d:%6d atoms (%d to %d)\n",
886 j+1, end[j]-start[j]+1, start[j]+1, end[j]+1);
889 if (nchain > 1)
891 srenew(block->a, block->nra+block->index[sel_nr+1]-block->index[sel_nr]);
892 for (j = 0; j < nchain; j++)
894 block->nr++;
895 srenew(block->index, block->nr+1);
896 srenew(*gn, block->nr);
897 sprintf(buf, "%s_chain%d", (*gn)[sel_nr], j+1);
898 (*gn)[block->nr-1] = gmx_strdup(buf);
899 for (i = block->index[sel_nr]; i < block->index[sel_nr+1]; i++)
901 a = block->a[i];
902 if ((a >= start[j]) && (a <= end[j]))
904 block->a[block->nra] = a;
905 block->nra++;
908 block->index[block->nr] = block->nra;
909 if (block->index[block->nr-1] == block->index[block->nr])
911 remove_group(block->nr-1, NOTSET, block, gn);
915 sfree(start);
916 sfree(end);
918 return nchain;
921 static gmx_bool check_have_atoms(const t_atoms *atoms, char *string)
923 if (atoms == nullptr)
925 printf("Can not process '%s' without atom info, use option -f\n", string);
926 return FALSE;
928 else
930 return TRUE;
934 static gmx_bool parse_entry(char **string, int natoms, const t_atoms *atoms,
935 t_blocka *block, char ***gn,
936 int *nr, int *index, char *gname)
938 static char **names, *ostring;
939 static gmx_bool bFirst = TRUE;
940 int j, n_names, sel_nr1;
941 int i, nr1, *index1;
942 char c;
943 gmx_bool bRet, bCompl;
945 if (bFirst)
947 bFirst = FALSE;
948 snew(names, MAXNAMES);
949 for (i = 0; i < MAXNAMES; i++)
951 snew(names[i], NAME_LEN+1);
955 bRet = FALSE;
956 sel_nr1 = NOTSET;
958 while (*string[0] == ' ')
960 (*string)++;
963 if ((*string)[0] == '!')
965 bCompl = TRUE;
966 (*string)++;
967 while (*string[0] == ' ')
969 (*string)++;
972 else
974 bCompl = FALSE;
977 ostring = *string;
979 if (parse_int(string, &sel_nr1) ||
980 parse_string(string, &sel_nr1, block->nr, *gn))
982 if ((sel_nr1 >= 0) && (sel_nr1 < block->nr))
984 copy_group(sel_nr1, block, nr, index);
985 std::strcpy(gname, (*gn)[sel_nr1]);
986 printf("Copied index group %d '%s'\n", sel_nr1, (*gn)[sel_nr1]);
987 bRet = TRUE;
989 else
991 printf("Group %d does not exist\n", sel_nr1);
994 else if ((*string)[0] == 'a')
996 (*string)++;
997 if (check_have_atoms(atoms, ostring))
999 if (parse_int(string, &sel_nr1))
1001 bRet = (select_atomnumbers(string, atoms, sel_nr1, nr, index, gname) != 0);
1003 else if (parse_names(string, &n_names, names))
1005 bRet = (select_atomnames(atoms, n_names, names, nr, index, FALSE) != 0);
1006 make_gname(n_names, names, gname);
1010 else if ((*string)[0] == 't')
1012 (*string)++;
1013 if (check_have_atoms(atoms, ostring) &&
1014 parse_names(string, &n_names, names))
1016 if (!(atoms->haveType))
1018 printf("Need a run input file to select atom types\n");
1020 else
1022 bRet = (select_atomnames(atoms, n_names, names, nr, index, TRUE) != 0);
1023 make_gname(n_names, names, gname);
1027 else if (std::strncmp(*string, "res", 3) == 0)
1029 (*string) += 3;
1030 if (check_have_atoms(atoms, ostring) &&
1031 parse_int(string, &sel_nr1) &&
1032 (sel_nr1 >= 0) && (sel_nr1 < block->nr) )
1034 bRet = atoms_from_residuenumbers(atoms,
1035 sel_nr1, block, nr, index, (*gn)[sel_nr1]);
1036 sprintf(gname, "atom_%s", (*gn)[sel_nr1]);
1039 else if (std::strncmp(*string, "ri", 2) == 0)
1041 (*string) += 2;
1042 if (check_have_atoms(atoms, ostring) &&
1043 parse_int_char(string, &sel_nr1, &c))
1045 bRet = (select_residueindices(string, atoms, sel_nr1, c, nr, index, gname) != 0);
1048 else if ((*string)[0] == 'r')
1050 (*string)++;
1051 if (check_have_atoms(atoms, ostring))
1053 if (parse_int_char(string, &sel_nr1, &c))
1055 bRet = (select_residuenumbers(string, atoms, sel_nr1, c, nr, index, gname) != 0);
1057 else if (parse_names(string, &n_names, names))
1059 bRet = (select_residuenames(atoms, n_names, names, nr, index) != 0);
1060 make_gname(n_names, names, gname);
1064 else if (std::strncmp(*string, "chain", 5) == 0)
1066 (*string) += 5;
1067 if (check_have_atoms(atoms, ostring) &&
1068 parse_names(string, &n_names, names))
1070 bRet = (select_chainnames(atoms, n_names, names, nr, index) != 0);
1071 sprintf(gname, "ch%s", names[0]);
1072 for (i = 1; i < n_names; i++)
1074 std::strcat(gname, names[i]);
1078 if (bRet && bCompl)
1080 snew(index1, natoms-*nr);
1081 nr1 = 0;
1082 for (i = 0; i < natoms; i++)
1084 j = 0;
1085 while ((j < *nr) && (index[j] != i))
1087 j++;
1089 if (j == *nr)
1091 if (nr1 >= natoms-*nr)
1093 printf("There are double atoms in your index group\n");
1094 break;
1096 index1[nr1] = i;
1097 nr1++;
1100 *nr = nr1;
1101 for (i = 0; i < nr1; i++)
1103 index[i] = index1[i];
1105 sfree(index1);
1107 for (i = std::strlen(gname)+1; i > 0; i--)
1109 gname[i] = gname[i-1];
1111 gname[0] = '!';
1112 printf("Complemented group: %d atoms\n", *nr);
1115 return bRet;
1118 static void list_residues(const t_atoms *atoms)
1120 int i, j, start, end, prev_resind, resind;
1121 gmx_bool bDiff;
1123 /* Print all the residues, assuming continuous resnr count */
1124 start = atoms->atom[0].resind;
1125 prev_resind = start;
1126 for (i = 0; i < atoms->nr; i++)
1128 resind = atoms->atom[i].resind;
1129 if ((resind != prev_resind) || (i == atoms->nr-1))
1131 if ((bDiff = (std::strcmp(*atoms->resinfo[resind].name,
1132 *atoms->resinfo[start].name) != 0)) ||
1133 (i == atoms->nr-1))
1135 if (bDiff)
1137 end = prev_resind;
1139 else
1141 end = resind;
1143 if (end < start+3)
1145 for (j = start; j <= end; j++)
1147 printf("%4d %-5s",
1148 j+1, *(atoms->resinfo[j].name));
1151 else
1153 printf(" %4d - %4d %-5s ",
1154 start+1, end+1, *(atoms->resinfo[start].name));
1156 start = resind;
1159 prev_resind = resind;
1161 printf("\n");
1164 static void edit_index(int natoms, const t_atoms *atoms, const rvec *x, t_blocka *block, char ***gn, gmx_bool bVerbose)
1166 static char **atnames, *ostring;
1167 static gmx_bool bFirst = TRUE;
1168 char inp_string[STRLEN], *string;
1169 char gname[STRLEN*3], gname1[STRLEN], gname2[STRLEN];
1170 int i, i0, i1, sel_nr, sel_nr2, newgroup;
1171 int nr, nr1, nr2, *index, *index1, *index2;
1172 gmx_bool bAnd, bOr, bPrintOnce;
1174 if (bFirst)
1176 bFirst = FALSE;
1177 snew(atnames, MAXNAMES);
1178 for (i = 0; i < MAXNAMES; i++)
1180 snew(atnames[i], NAME_LEN+1);
1184 string = nullptr;
1186 snew(index, natoms);
1187 snew(index1, natoms);
1188 snew(index2, natoms);
1190 newgroup = NOTSET;
1191 bPrintOnce = TRUE;
1194 gname1[0] = '\0';
1195 if (bVerbose || bPrintOnce || newgroup != NOTSET)
1197 printf("\n");
1198 if (bVerbose || bPrintOnce || newgroup == NOTSET)
1200 i0 = 0;
1201 i1 = block->nr;
1203 else
1205 i0 = newgroup;
1206 i1 = newgroup+1;
1208 for (i = i0; i < i1; i++)
1210 printf("%3d %-20s: %5d atoms\n", i, (*gn)[i],
1211 block->index[i+1]-block->index[i]);
1213 newgroup = NOTSET;
1215 if (bVerbose || bPrintOnce)
1217 printf("\n");
1218 printf(" nr : group '!': not 'name' nr name 'splitch' nr Enter: list groups\n");
1219 printf(" 'a': atom '&': and 'del' nr 'splitres' nr 'l': list residues\n");
1220 printf(" 't': atom type '|': or 'keep' nr 'splitat' nr 'h': help\n");
1221 printf(" 'r': residue 'res' nr 'chain' char\n");
1222 printf(" \"name\": group 'case': case %s 'q': save and quit\n",
1223 bCase ? "insensitive" : "sensitive ");
1224 printf(" 'ri': residue index\n");
1225 bPrintOnce = FALSE;
1227 printf("\n");
1228 printf("> ");
1229 if (nullptr == fgets(inp_string, STRLEN, stdin))
1231 gmx_fatal(FARGS, "Error reading user input");
1233 inp_string[std::strlen(inp_string)-1] = 0;
1234 printf("\n");
1235 string = inp_string;
1236 while (string[0] == ' ')
1238 string++;
1241 ostring = string;
1242 nr = 0;
1243 if (string[0] == 'h')
1245 printf(" nr : selects an index group by number or quoted string.\n");
1246 printf(" The string is first matched against the whole group name,\n");
1247 printf(" then against the beginning and finally against an\n");
1248 printf(" arbitrary substring. A multiple match is an error.\n");
1250 printf(" 'a' nr1 [nr2 ...] : selects atoms, atom numbering starts at 1.\n");
1251 printf(" 'a' nr1 - nr2 : selects atoms in the range from nr1 to nr2.\n");
1252 printf(" 'a' name1[*] [name2[*] ...] : selects atoms by name(s), '?' matches any char,\n");
1253 printf(" wildcard '*' allowed at the end of a name.\n");
1254 printf(" 't' type1[*] [type2[*] ...] : as 'a', but for type, run input file required.\n");
1255 printf(" 'r' nr1[ic1] [nr2[ic2] ...] : selects residues by number and insertion code.\n");
1256 printf(" 'r' nr1 - nr2 : selects residues in the range from nr1 to nr2.\n");
1257 printf(" 'r' name1[*] [name2[*] ...] : as 'a', but for residue names.\n");
1258 printf(" 'ri' nr1 - nr2 : selects residue indices, 1-indexed, (as opposed to numbers) in the range from nr1 to nr2.\n");
1259 printf(" 'chain' ch1 [ch2 ...] : selects atoms by chain identifier(s),\n");
1260 printf(" not available with a .gro file as input.\n");
1261 printf(" ! : takes the complement of a group with respect to all\n");
1262 printf(" the atoms in the input file.\n");
1263 printf(" & | : AND and OR, can be placed between any of the options\n");
1264 printf(" above, the input is processed from left to right.\n");
1265 printf(" 'name' nr name : rename group nr to name.\n");
1266 printf(" 'del' nr1 [- nr2] : deletes one group or groups in the range from nr1 to nr2.\n");
1267 printf(" 'keep' nr : deletes all groups except nr.\n");
1268 printf(" 'case' : make all name compares case (in)sensitive.\n");
1269 printf(" 'splitch' nr : split group into chains using CA distances.\n");
1270 printf(" 'splitres' nr : split group into residues.\n");
1271 printf(" 'splitat' nr : split group into atoms.\n");
1272 printf(" 'res' nr : interpret numbers in group as residue numbers\n");
1273 printf(" Enter : list the currently defined groups and commands\n");
1274 printf(" 'l' : list the residues.\n");
1275 printf(" 'h' : show this help.\n");
1276 printf(" 'q' : save and quit.\n");
1277 printf("\n");
1278 printf(" Examples:\n");
1279 printf(" > 2 | 4 & r 3-5\n");
1280 printf(" selects all atoms from group 2 and 4 that have residue numbers 3, 4 or 5\n");
1281 printf(" > a C* & !a C CA\n");
1282 printf(" selects all atoms starting with 'C' but not the atoms 'C' and 'CA'\n");
1283 printf(" > \"protein\" & ! \"backb\"\n");
1284 printf(" selects all atoms that are in group 'protein' and not in group 'backbone'\n");
1285 if (bVerbose)
1287 printf("\npress Enter ");
1288 getchar();
1291 else if (std::strncmp(string, "del", 3) == 0)
1293 string += 3;
1294 if (parse_int(&string, &sel_nr))
1296 while (string[0] == ' ')
1298 string++;
1300 if (string[0] == '-')
1302 string++;
1303 parse_int(&string, &sel_nr2);
1305 else
1307 sel_nr2 = NOTSET;
1309 while (string[0] == ' ')
1311 string++;
1313 if (string[0] == '\0')
1315 remove_group(sel_nr, sel_nr2, block, gn);
1317 else
1319 printf("\nSyntax error: \"%s\"\n", string);
1323 else if (std::strncmp(string, "keep", 4) == 0)
1325 string += 4;
1326 if (parse_int(&string, &sel_nr))
1328 remove_group(sel_nr+1, block->nr-1, block, gn);
1329 remove_group(0, sel_nr-1, block, gn);
1332 else if (std::strncmp(string, "name", 4) == 0)
1334 string += 4;
1335 if (parse_int(&string, &sel_nr))
1337 if ((sel_nr >= 0) && (sel_nr < block->nr))
1339 sscanf(string, "%s", gname);
1340 sfree((*gn)[sel_nr]);
1341 (*gn)[sel_nr] = gmx_strdup(gname);
1345 else if (std::strncmp(string, "case", 4) == 0)
1347 bCase = !bCase;
1348 printf("Switched to case %s\n", bCase ? "sensitive" : "insensitive");
1350 else if (string[0] == 'v')
1352 bVerbose = !bVerbose;
1353 printf("Turned verbose %s\n", bVerbose ? "on" : "off");
1355 else if (string[0] == 'l')
1357 if (check_have_atoms(atoms, ostring) )
1359 list_residues(atoms);
1362 else if (std::strncmp(string, "splitch", 7) == 0)
1364 string += 7;
1365 if (check_have_atoms(atoms, ostring) &&
1366 parse_int(&string, &sel_nr) &&
1367 (sel_nr >= 0) && (sel_nr < block->nr))
1369 split_chain(atoms, x, sel_nr, block, gn);
1372 else if (std::strncmp(string, "splitres", 8) == 0)
1374 string += 8;
1375 if (check_have_atoms(atoms, ostring) &&
1376 parse_int(&string, &sel_nr) &&
1377 (sel_nr >= 0) && (sel_nr < block->nr))
1379 split_group(atoms, sel_nr, block, gn, FALSE);
1382 else if (std::strncmp(string, "splitat", 7) == 0)
1384 string += 7;
1385 if (check_have_atoms(atoms, ostring) &&
1386 parse_int(&string, &sel_nr) &&
1387 (sel_nr >= 0) && (sel_nr < block->nr))
1389 split_group(atoms, sel_nr, block, gn, TRUE);
1392 else if (string[0] == '\0')
1394 bPrintOnce = TRUE;
1396 else if (string[0] != 'q')
1398 nr2 = -1;
1399 if (parse_entry(&string, natoms, atoms, block, gn, &nr, index, gname))
1403 while (string[0] == ' ')
1405 string++;
1408 bAnd = FALSE;
1409 bOr = FALSE;
1410 if (string[0] == '&')
1412 bAnd = TRUE;
1414 else if (string[0] == '|')
1416 bOr = TRUE;
1419 if (bAnd || bOr)
1421 string++;
1422 nr1 = nr;
1423 for (i = 0; i < nr; i++)
1425 index1[i] = index[i];
1427 std::strcpy(gname1, gname);
1428 if (parse_entry(&string, natoms, atoms, block, gn, &nr2, index2, gname2))
1430 if (bOr)
1432 or_groups(nr1, index1, nr2, index2, &nr, index);
1433 sprintf(gname, "%s_%s", gname1, gname2);
1435 else
1437 and_groups(nr1, index1, nr2, index2, &nr, index);
1438 sprintf(gname, "%s_&_%s", gname1, gname2);
1443 while (bAnd || bOr);
1445 while (string[0] == ' ')
1447 string++;
1449 if (string[0])
1451 printf("\nSyntax error: \"%s\"\n", string);
1453 else if (nr > 0)
1455 copy2block(nr, index, block);
1456 srenew(*gn, block->nr);
1457 newgroup = block->nr-1;
1458 (*gn)[newgroup] = gmx_strdup(gname);
1460 else
1462 printf("Group is empty\n");
1466 while (string[0] != 'q');
1468 sfree(index);
1469 sfree(index1);
1470 sfree(index2);
1473 static int block2natoms(t_blocka *block)
1475 int i, natoms;
1477 natoms = 0;
1478 for (i = 0; i < block->nra; i++)
1480 natoms = std::max(natoms, block->a[i]);
1482 natoms++;
1484 return natoms;
1487 static void merge_blocks(t_blocka *dest, t_blocka *source)
1489 int i, nra0, i0;
1491 /* count groups, srenew and fill */
1492 i0 = dest->nr;
1493 nra0 = dest->nra;
1494 dest->nr += source->nr;
1495 srenew(dest->index, dest->nr+1);
1496 for (i = 0; i < source->nr; i++)
1498 dest->index[i0+i] = nra0 + source->index[i];
1500 /* count atoms, srenew and fill */
1501 dest->nra += source->nra;
1502 srenew(dest->a, dest->nra);
1503 for (i = 0; i < source->nra; i++)
1505 dest->a[nra0+i] = source->a[i];
1508 /* terminate list */
1509 dest->index[dest->nr] = dest->nra;
1513 int gmx_make_ndx(int argc, char *argv[])
1515 const char *desc[] = {
1516 "Index groups are necessary for almost every GROMACS program.",
1517 "All these programs can generate default index groups. You ONLY",
1518 "have to use [THISMODULE] when you need SPECIAL index groups.",
1519 "There is a default index group for the whole system, 9 default",
1520 "index groups for proteins, and a default index group",
1521 "is generated for every other residue name.",
1523 "When no index file is supplied, also [THISMODULE] will generate the",
1524 "default groups.",
1525 "With the index editor you can select on atom, residue and chain names",
1526 "and numbers.",
1527 "When a run input file is supplied you can also select on atom type.",
1528 "You can use boolean operations, you can split groups",
1529 "into chains, residues or atoms. You can delete and rename groups.",
1530 "Type 'h' in the editor for more details.",
1532 "The atom numbering in the editor and the index file starts at 1.",
1534 "The [TT]-twin[tt] switch duplicates all index groups with an offset of",
1535 "[TT]-natoms[tt], which is useful for Computational Electrophysiology",
1536 "double-layer membrane setups.",
1538 "See also [gmx-select] [TT]-on[tt], which provides an alternative way",
1539 "for constructing index groups. It covers nearly all of [THISMODULE]",
1540 "functionality, and in many cases much more."
1543 static int natoms = 0;
1544 static gmx_bool bVerbose = FALSE;
1545 static gmx_bool bDuplicate = FALSE;
1546 t_pargs pa[] = {
1547 { "-natoms", FALSE, etINT, {&natoms},
1548 "set number of atoms (default: read from coordinate or index file)" },
1549 { "-twin", FALSE, etBOOL, {&bDuplicate},
1550 "Duplicate all index groups with an offset of -natoms" },
1551 { "-verbose", FALSE, etBOOL, {&bVerbose},
1552 "HIDDENVerbose output" }
1554 #define NPA asize(pa)
1556 gmx_output_env_t *oenv;
1557 const char *stxfile;
1558 const char *ndxoutfile;
1559 gmx_bool bNatoms;
1560 int j;
1561 t_atoms *atoms;
1562 rvec *x, *v;
1563 int ePBC;
1564 matrix box;
1565 t_blocka *block, *block2;
1566 char **gnames, **gnames2;
1567 t_filenm fnm[] = {
1568 { efSTX, "-f", nullptr, ffOPTRD },
1569 { efNDX, "-n", nullptr, ffOPTRDMULT },
1570 { efNDX, "-o", nullptr, ffWRITE }
1572 #define NFILE asize(fnm)
1574 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, NPA, pa, asize(desc), desc,
1575 0, nullptr, &oenv))
1577 return 0;
1580 stxfile = ftp2fn_null(efSTX, NFILE, fnm);
1581 gmx::ArrayRef<const std::string> ndxInFiles = opt2fnsIfOptionSet("-n", NFILE, fnm);
1582 ndxoutfile = opt2fn("-o", NFILE, fnm);
1583 bNatoms = opt2parg_bSet("-natoms", NPA, pa);
1585 if (!stxfile && ndxInFiles.empty())
1587 gmx_fatal(FARGS, "No input files (structure or index)");
1590 if (stxfile)
1592 t_topology *top;
1593 snew(top, 1);
1594 fprintf(stderr, "\nReading structure file\n");
1595 read_tps_conf(stxfile, top, &ePBC, &x, &v, box, FALSE);
1596 atoms = &top->atoms;
1597 if (atoms->pdbinfo == nullptr)
1599 snew(atoms->pdbinfo, atoms->nr);
1601 natoms = atoms->nr;
1602 bNatoms = TRUE;
1604 else
1606 atoms = nullptr;
1607 x = nullptr;
1610 /* read input file(s) */
1611 block = new_blocka();
1612 gnames = nullptr;
1613 printf("Going to read %td old index file(s)\n", ndxInFiles.ssize());
1614 if (!ndxInFiles.empty())
1616 for (const std::string &ndxInFile : ndxInFiles)
1618 block2 = init_index(ndxInFile.c_str(), &gnames2);
1619 srenew(gnames, block->nr+block2->nr);
1620 for (j = 0; j < block2->nr; j++)
1622 gnames[block->nr+j] = gnames2[j];
1624 sfree(gnames2);
1625 merge_blocks(block, block2);
1626 sfree(block2->a);
1627 sfree(block2->index);
1628 /* done_block(block2); */
1629 sfree(block2);
1632 else
1634 snew(gnames, 1);
1635 analyse(atoms, block, &gnames, FALSE, TRUE);
1638 if (!bNatoms)
1640 natoms = block2natoms(block);
1641 printf("Counted atom numbers up to %d in index file\n", natoms);
1644 edit_index(natoms, atoms, x, block, &gnames, bVerbose);
1646 write_index(ndxoutfile, block, gnames, bDuplicate, natoms);
1648 return 0;