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.
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/mtop_util.h"
52 #include "gromacs/topology/topology.h"
53 #include "gromacs/utility/arraysize.h"
54 #include "gromacs/utility/cstringutil.h"
55 #include "gromacs/utility/fatalerror.h"
56 #include "gromacs/utility/futil.h"
57 #include "gromacs/utility/smalloc.h"
59 /* It's not nice to have size limits, but we should not spend more time
60 * on this ancient tool, but instead use the new selection library.
64 static const int NOTSET = -92637;
66 static gmx_bool bCase = FALSE;
68 static int or_groups(int nr1, const int* at1, int nr2, const int* at2, int* nr, int* at)
76 for (i1 = 0; i1 < nr1; i1++)
78 if ((i1 > 0) && (at1[i1] <= max))
84 for (i1 = 0; i1 < nr2; i1++)
86 if ((i1 > 0) && (at2[i1] <= max))
95 printf("One of your groups is not ascending\n");
102 while ((i1 < nr1) || (i2 < nr2))
104 if ((i2 == nr2) || ((i1 < nr1) && (at1[i1] < at2[i2])))
112 if ((i2 < nr2) && ((i1 == nr1) || (at1[i1] > at2[i2])))
121 printf("Merged two groups with OR: %d %d -> %d\n", nr1, nr2, *nr);
127 static int and_groups(int nr1, const int* at1, int nr2, const int* at2, int* nr, int* at)
132 for (i1 = 0; i1 < nr1; i1++)
134 for (i2 = 0; i2 < nr2; i2++)
136 if (at1[i1] == at2[i2])
144 printf("Merged two groups with AND: %d %d -> %d\n", nr1, nr2, *nr);
149 static gmx_bool is_name_char(char c)
151 /* This string should contain all characters that can not be
152 * the first letter of a name due to the make_ndx syntax.
154 const char* spec = " !&|";
156 return (c != '\0' && std::strchr(spec, c) == nullptr);
159 static int parse_names(char** string, int* n_names, char** names)
164 while (is_name_char((*string)[0]) || (*string)[0] == ' ')
166 if (is_name_char((*string)[0]))
168 if (*n_names >= MAXNAMES)
170 gmx_fatal(FARGS, "To many names: %d\n", *n_names + 1);
173 while (is_name_char((*string)[i]))
175 names[*n_names][i] = (*string)[i];
179 printf("Name is too long, the maximum is %d characters\n", NAME_LEN);
183 names[*n_names][i] = '\0';
186 upstring(names[*n_names]);
200 static gmx_bool parse_int_char(char** string, int* nr, char* c)
207 while ((*string)[0] == ' ')
216 if (std::isdigit((*string)[0]))
218 *nr = (*string)[0] - '0';
220 while (std::isdigit((*string)[0]))
222 *nr = (*nr) * 10 + (*string)[0] - '0';
225 if (std::isalpha((*string)[0]))
230 /* Check if there is at most one non-digit character */
231 if (!std::isalnum((*string)[0]))
248 static gmx_bool parse_int(char** string, int* nr)
254 bRet = parse_int_char(string, nr, &c);
255 if (bRet && c != ' ')
264 static gmx_bool isquote(char c)
269 static gmx_bool parse_string(char** string, int* nr, int ngrps, char** grpname)
274 while ((*string)[0] == ' ')
280 if (isquote((*string)[0]))
284 s = gmx_strdup((*string));
285 sp = std::strchr(s, c);
288 (*string) += sp - s + 1;
290 (*nr) = find_group(s, ngrps, grpname);
294 return (*nr) != NOTSET;
297 static int select_atomnumbers(char** string, const t_atoms* atoms, int n1, int* nr, int* index, char* gname)
303 while ((*string)[0] == ' ')
307 if ((*string)[0] == '-')
310 parse_int(string, &up);
311 if ((n1 < 1) || (n1 > atoms->nr) || (up < 1) || (up > atoms->nr))
313 printf("Invalid atom range\n");
317 for (i = n1 - 1; i <= up - 1; i++)
322 printf("Found %d atom%s in range %d-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
325 sprintf(buf, "a_%d", n1);
329 sprintf(buf, "a_%d-%d", n1, up);
331 std::strcpy(gname, buf);
340 if ((i - 1 >= 0) && (i - 1 < atoms->nr))
344 sprintf(buf, "_%d", i);
345 std::strcat(gname, buf);
349 printf("Invalid atom number %d\n", i);
352 } while ((*nr != 0) && (parse_int(string, &i)));
358 static int select_residuenumbers(char** string, const t_atoms* atoms, int n1, char c, int* nr, int* index, char* gname)
365 while ((*string)[0] == ' ')
369 if ((*string)[0] == '-')
371 /* Residue number range selection */
374 printf("Error: residue insertion codes can not be used with residue range selection\n");
378 parse_int(string, &up);
380 for (i = 0; i < atoms->nr; i++)
382 ri = &atoms->resinfo[atoms->atom[i].resind];
383 for (j = n1; (j <= up); j++)
385 if (ri->nr == j && (c == ' ' || ri->ic == c))
392 printf("Found %d atom%s with res.nr. in range %d-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
395 sprintf(buf, "r_%d", n1);
399 sprintf(buf, "r_%d-%d", n1, up);
401 std::strcpy(gname, buf);
405 /* Individual residue number/insertion code selection */
410 for (i = 0; i < atoms->nr; i++)
412 ri = &atoms->resinfo[atoms->atom[i].resind];
413 if (ri->nr == j && ri->ic == c)
419 sprintf(buf, "_%d", j);
420 std::strcat(gname, buf);
421 } while (parse_int_char(string, &j, &c));
427 static int select_residueindices(char** string, const t_atoms* atoms, int n1, char c, int* nr, int* index, char* gname)
429 /*this should be similar to select_residuenumbers except select by index (sequential numbering in file)*/
430 /*resind+1 for 1-indexing*/
436 while ((*string)[0] == ' ')
440 if ((*string)[0] == '-')
442 /* Residue number range selection */
445 printf("Error: residue insertion codes can not be used with residue range selection\n");
449 parse_int(string, &up);
451 for (i = 0; i < atoms->nr; i++)
453 ri = &atoms->resinfo[atoms->atom[i].resind];
454 for (j = n1; (j <= up); j++)
456 if (atoms->atom[i].resind + 1 == j && (c == ' ' || ri->ic == c))
463 printf("Found %d atom%s with resind.+1 in range %d-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
466 sprintf(buf, "r_%d", n1);
470 sprintf(buf, "r_%d-%d", n1, up);
472 std::strcpy(gname, buf);
476 /* Individual residue number/insertion code selection */
481 for (i = 0; i < atoms->nr; i++)
483 ri = &atoms->resinfo[atoms->atom[i].resind];
484 if (atoms->atom[i].resind + 1 == j && ri->ic == c)
490 sprintf(buf, "_%d", j);
491 std::strcat(gname, buf);
492 } while (parse_int_char(string, &j, &c));
500 atoms_from_residuenumbers(const t_atoms* atoms, int group, t_blocka* block, int* nr, int* index, char* gname)
502 int i, j, j0, j1, resnr, nres;
504 j0 = block->index[group];
505 j1 = block->index[group + 1];
507 for (j = j0; j < j1; j++)
509 if (block->a[j] >= nres)
511 printf("Index %s contains number>nres (%d>%d)\n", gname, block->a[j] + 1, nres);
515 for (i = 0; i < atoms->nr; i++)
517 resnr = atoms->resinfo[atoms->atom[i].resind].nr;
518 for (j = j0; j < j1; j++)
520 if (block->a[j] + 1 == resnr)
528 printf("Found %d atom%s in %d residues from group %s\n", *nr, (*nr == 1) ? "" : "s", j1 - j0, gname);
532 static gmx_bool comp_name(const char* name, const char* search)
534 gmx_bool matches = TRUE;
536 // Loop while name and search are not end-of-string and matches is true
537 for (; *name && *search && matches; name++, search++)
541 // still matching, continue to next character
544 else if (*search == '*')
548 printf("WARNING: Currently '*' is only supported at the end of an expression\n");
550 // if * is the last char in search string, we have a match,
551 // otherwise we just failed. Return in either case, we're done.
552 return (*(search + 1) == '\0');
555 // Compare one character
558 matches = (*name == *search);
562 matches = (std::toupper(*name) == std::toupper(*search));
566 matches = matches && (*name == '\0' && (*search == '\0' || *search == '*'));
571 static int select_chainnames(const t_atoms* atoms, int n_names, char** names, int* nr, int* index)
579 for (i = 0; i < atoms->nr; i++)
581 name[0] = atoms->resinfo[atoms->atom[i].resind].chainid;
583 while (j < n_names && !comp_name(name, names[j]))
593 printf("Found %d atom%s with chain identifier%s", *nr, (*nr == 1) ? "" : "s", (n_names == 1) ? "" : "s");
594 for (j = 0; (j < n_names); j++)
596 printf(" %s", names[j]);
603 static int select_atomnames(const t_atoms* atoms, int n_names, char** names, int* nr, int* index, gmx_bool bType)
610 for (i = 0; i < atoms->nr; i++)
614 name = *(atoms->atomtype[i]);
618 name = *(atoms->atomname[i]);
621 while (j < n_names && !comp_name(name, names[j]))
631 printf("Found %d atoms with %s%s", *nr, bType ? "type" : "name", (n_names == 1) ? "" : "s");
632 for (j = 0; (j < n_names); j++)
634 printf(" %s", names[j]);
641 static int select_residuenames(const t_atoms* atoms, int n_names, char** names, int* nr, int* index)
648 for (i = 0; i < atoms->nr; i++)
650 name = *(atoms->resinfo[atoms->atom[i].resind].name);
652 while (j < n_names && !comp_name(name, names[j]))
662 printf("Found %d atoms with residue name%s", *nr, (n_names == 1) ? "" : "s");
663 for (j = 0; (j < n_names); j++)
665 printf(" %s", names[j]);
672 static void copy2block(int n, const int* index, t_blocka* block)
679 srenew(block->index, block->nr + 1);
680 block->index[block->nr] = n0 + n;
681 srenew(block->a, n0 + n);
682 for (i = 0; (i < n); i++)
684 block->a[n0 + i] = index[i];
688 static void make_gname(int n, char** names, char* gname)
692 std::strcpy(gname, names[0]);
693 for (i = 1; i < n; i++)
695 std::strcat(gname, "_");
696 std::strcat(gname, names[i]);
700 static void copy_group(int g, t_blocka* block, int* nr, int* index)
704 i0 = block->index[g];
705 *nr = block->index[g + 1] - i0;
706 for (i = 0; i < *nr; i++)
708 index[i] = block->a[i0 + i];
712 static void remove_group(int nr, int nr2, t_blocka* block, char*** gn)
722 for (j = 0; j <= nr2 - nr; j++)
724 if ((nr < 0) || (nr >= block->nr))
726 printf("Group %d does not exist\n", nr + j);
730 shift = block->index[nr + 1] - block->index[nr];
731 for (i = block->index[nr + 1]; i < block->nra; i++)
733 block->a[i - shift] = block->a[i];
736 for (i = nr; i < block->nr; i++)
738 block->index[i] = block->index[i + 1] - shift;
740 name = gmx_strdup((*gn)[nr]);
742 for (i = nr; i < block->nr - 1; i++)
744 (*gn)[i] = (*gn)[i + 1];
747 block->nra = block->index[block->nr];
748 printf("Removed group %d '%s'\n", nr + j, name);
754 static void split_group(const t_atoms* atoms, int sel_nr, t_blocka* block, char*** gn, gmx_bool bAtom)
756 char buf[STRLEN], *name;
760 printf("Splitting group %d '%s' into %s\n", sel_nr, (*gn)[sel_nr], bAtom ? "atoms" : "residues");
762 n0 = block->index[sel_nr];
763 n1 = block->index[sel_nr + 1];
764 srenew(block->a, block->nra + n1 - n0);
765 for (i = n0; i < n1; i++)
768 resind = atoms->atom[a].resind;
769 name = *(atoms->resinfo[resind].name);
770 if (bAtom || (i == n0) || (atoms->atom[block->a[i - 1]].resind != resind))
774 block->index[block->nr] = block->nra;
777 srenew(block->index, block->nr + 1);
778 srenew(*gn, block->nr);
781 sprintf(buf, "%s_%s_%d", (*gn)[sel_nr], *atoms->atomname[a], a + 1);
785 sprintf(buf, "%s_%s_%d", (*gn)[sel_nr], name, atoms->resinfo[resind].nr);
787 (*gn)[block->nr - 1] = gmx_strdup(buf);
789 block->a[block->nra] = a;
792 block->index[block->nr] = block->nra;
795 static int split_chain(const t_atoms* atoms, const rvec* x, int sel_nr, t_blocka* block, char*** gn)
799 int i, a, natoms, *start = nullptr, *end = nullptr, ca_start, ca_end;
806 while (ca_start < natoms)
808 while ((ca_start < natoms) && std::strcmp(*atoms->atomname[ca_start], "CA") != 0)
812 if (ca_start < natoms)
814 srenew(start, nchain + 1);
815 srenew(end, nchain + 1);
816 start[nchain] = ca_start;
817 while ((start[nchain] > 0)
818 && (atoms->atom[start[nchain] - 1].resind == atoms->atom[ca_start].resind))
830 } while ((i < natoms) && std::strcmp(*atoms->atomname[i], "CA") != 0);
833 rvec_sub(x[ca_end], x[i], vec);
839 } while (norm(vec) < 0.45);
841 end[nchain] = ca_end;
842 while ((end[nchain] + 1 < natoms)
843 && (atoms->atom[end[nchain] + 1].resind == atoms->atom[ca_end].resind))
847 ca_start = end[nchain] + 1;
853 printf("Found 1 chain, will not split\n");
857 printf("Found %d chains\n", nchain);
859 for (j = 0; j < nchain; j++)
861 printf("%d:%6d atoms (%d to %d)\n", j + 1, end[j] - start[j] + 1, start[j] + 1, end[j] + 1);
866 srenew(block->a, block->nra + block->index[sel_nr + 1] - block->index[sel_nr]);
867 for (j = 0; j < nchain; j++)
870 srenew(block->index, block->nr + 1);
871 srenew(*gn, block->nr);
872 sprintf(buf, "%s_chain%d", (*gn)[sel_nr], j + 1);
873 (*gn)[block->nr - 1] = gmx_strdup(buf);
874 for (i = block->index[sel_nr]; i < block->index[sel_nr + 1]; i++)
877 if ((a >= start[j]) && (a <= end[j]))
879 block->a[block->nra] = a;
883 block->index[block->nr] = block->nra;
884 if (block->index[block->nr - 1] == block->index[block->nr])
886 remove_group(block->nr - 1, NOTSET, block, gn);
896 static gmx_bool check_have_atoms(const t_atoms* atoms, char* string)
898 if (atoms == nullptr)
900 printf("Can not process '%s' without atom info, use option -f\n", string);
909 static gmx_bool parse_entry(char** string,
911 const t_atoms* atoms,
918 static char ** names, *ostring;
919 static gmx_bool bFirst = TRUE;
920 int j, n_names, sel_nr1;
923 gmx_bool bRet, bCompl;
928 snew(names, MAXNAMES);
929 for (i = 0; i < MAXNAMES; i++)
931 snew(names[i], NAME_LEN + 1);
938 while (*string[0] == ' ')
943 if ((*string)[0] == '!')
947 while (*string[0] == ' ')
959 if (parse_int(string, &sel_nr1) || parse_string(string, &sel_nr1, block->nr, *gn))
961 if ((sel_nr1 >= 0) && (sel_nr1 < block->nr))
963 copy_group(sel_nr1, block, nr, index);
964 std::strcpy(gname, (*gn)[sel_nr1]);
965 printf("Copied index group %d '%s'\n", sel_nr1, (*gn)[sel_nr1]);
970 printf("Group %d does not exist\n", sel_nr1);
973 else if ((*string)[0] == 'a')
976 if (check_have_atoms(atoms, ostring))
978 if (parse_int(string, &sel_nr1))
980 bRet = (select_atomnumbers(string, atoms, sel_nr1, nr, index, gname) != 0);
982 else if (parse_names(string, &n_names, names))
984 bRet = (select_atomnames(atoms, n_names, names, nr, index, FALSE) != 0);
985 make_gname(n_names, names, gname);
989 else if ((*string)[0] == 't')
992 if (check_have_atoms(atoms, ostring) && parse_names(string, &n_names, names))
994 if (!(atoms->haveType))
996 printf("Need a run input file to select atom types\n");
1000 bRet = (select_atomnames(atoms, n_names, names, nr, index, TRUE) != 0);
1001 make_gname(n_names, names, gname);
1005 else if (std::strncmp(*string, "res", 3) == 0)
1008 if (check_have_atoms(atoms, ostring) && parse_int(string, &sel_nr1) && (sel_nr1 >= 0)
1009 && (sel_nr1 < block->nr))
1011 bRet = atoms_from_residuenumbers(atoms, sel_nr1, block, nr, index, (*gn)[sel_nr1]);
1012 sprintf(gname, "atom_%s", (*gn)[sel_nr1]);
1015 else if (std::strncmp(*string, "ri", 2) == 0)
1018 if (check_have_atoms(atoms, ostring) && parse_int_char(string, &sel_nr1, &c))
1020 bRet = (select_residueindices(string, atoms, sel_nr1, c, nr, index, gname) != 0);
1023 else if ((*string)[0] == 'r')
1026 if (check_have_atoms(atoms, ostring))
1028 if (parse_int_char(string, &sel_nr1, &c))
1030 bRet = (select_residuenumbers(string, atoms, sel_nr1, c, nr, index, gname) != 0);
1032 else if (parse_names(string, &n_names, names))
1034 bRet = (select_residuenames(atoms, n_names, names, nr, index) != 0);
1035 make_gname(n_names, names, gname);
1039 else if (std::strncmp(*string, "chain", 5) == 0)
1042 if (check_have_atoms(atoms, ostring) && parse_names(string, &n_names, names))
1044 bRet = (select_chainnames(atoms, n_names, names, nr, index) != 0);
1045 sprintf(gname, "ch%s", names[0]);
1046 for (i = 1; i < n_names; i++)
1048 std::strcat(gname, names[i]);
1054 snew(index1, natoms - *nr);
1056 for (i = 0; i < natoms; i++)
1059 while ((j < *nr) && (index[j] != i))
1065 if (nr1 >= natoms - *nr)
1067 printf("There are double atoms in your index group\n");
1075 for (i = 0; i < nr1; i++)
1077 index[i] = index1[i];
1081 for (i = std::strlen(gname) + 1; i > 0; i--)
1083 gname[i] = gname[i - 1];
1086 printf("Complemented group: %d atoms\n", *nr);
1092 static void list_residues(const t_atoms* atoms)
1094 int i, j, start, end, prev_resind, resind;
1097 /* Print all the residues, assuming continuous resnr count */
1098 start = atoms->atom[0].resind;
1099 prev_resind = start;
1100 for (i = 0; i < atoms->nr; i++)
1102 resind = atoms->atom[i].resind;
1103 if ((resind != prev_resind) || (i == atoms->nr - 1))
1105 if ((bDiff = (std::strcmp(*atoms->resinfo[resind].name, *atoms->resinfo[start].name) != 0))
1106 || (i == atoms->nr - 1))
1116 if (end < start + 3)
1118 for (j = start; j <= end; j++)
1120 printf("%4d %-5s", j + 1, *(atoms->resinfo[j].name));
1125 printf(" %4d - %4d %-5s ", start + 1, end + 1, *(atoms->resinfo[start].name));
1130 prev_resind = resind;
1135 static void edit_index(int natoms, const t_atoms* atoms, const rvec* x, t_blocka* block, char*** gn, gmx_bool bVerbose)
1137 static char ** atnames, *ostring;
1138 static gmx_bool bFirst = TRUE;
1139 char inp_string[STRLEN], *string;
1140 char gname[STRLEN * 3], gname1[STRLEN], gname2[STRLEN];
1141 int i, i0, i1, sel_nr, sel_nr2, newgroup;
1142 int nr, nr1, nr2, *index, *index1, *index2;
1143 gmx_bool bAnd, bOr, bPrintOnce;
1148 snew(atnames, MAXNAMES);
1149 for (i = 0; i < MAXNAMES; i++)
1151 snew(atnames[i], NAME_LEN + 1);
1157 snew(index, natoms);
1158 snew(index1, natoms);
1159 snew(index2, natoms);
1166 if (bVerbose || bPrintOnce || newgroup != NOTSET)
1169 if (bVerbose || bPrintOnce || newgroup == NOTSET)
1179 for (i = i0; i < i1; i++)
1181 printf("%3d %-20s: %5d atoms\n", i, (*gn)[i], block->index[i + 1] - block->index[i]);
1185 if (bVerbose || bPrintOnce)
1188 printf(" nr : group '!': not 'name' nr name 'splitch' nr Enter: list "
1190 printf(" 'a': atom '&': and 'del' nr 'splitres' nr 'l': list "
1192 printf(" 't': atom type '|': or 'keep' nr 'splitat' nr 'h': help\n");
1193 printf(" 'r': residue 'res' nr 'chain' char\n");
1194 printf(" \"name\": group 'case': case %s 'q': save and quit\n",
1195 bCase ? "insensitive" : "sensitive ");
1196 printf(" 'ri': residue index\n");
1201 if (nullptr == fgets(inp_string, STRLEN, stdin))
1203 gmx_fatal(FARGS, "Error reading user input");
1205 inp_string[std::strlen(inp_string) - 1] = 0;
1207 string = inp_string;
1208 while (string[0] == ' ')
1215 if (string[0] == 'h')
1217 printf(" nr : selects an index group by number or quoted string.\n");
1218 printf(" The string is first matched against the whole group "
1220 printf(" then against the beginning and finally against an\n");
1221 printf(" arbitrary substring. A multiple match is an error.\n");
1223 printf(" 'a' nr1 [nr2 ...] : selects atoms, atom numbering starts at 1.\n");
1224 printf(" 'a' nr1 - nr2 : selects atoms in the range from nr1 to nr2.\n");
1225 printf(" 'a' name1[*] [name2[*] ...] : selects atoms by name(s), '?' matches any "
1227 printf(" wildcard '*' allowed at the end of a name.\n");
1228 printf(" 't' type1[*] [type2[*] ...] : as 'a', but for type, run input file "
1230 printf(" 'r' nr1[ic1] [nr2[ic2] ...] : selects residues by number and insertion "
1232 printf(" 'r' nr1 - nr2 : selects residues in the range from nr1 to "
1234 printf(" 'r' name1[*] [name2[*] ...] : as 'a', but for residue names.\n");
1235 printf(" 'ri' nr1 - nr2 : selects residue indices, 1-indexed, (as opposed "
1236 "to numbers) in the range from nr1 to nr2.\n");
1237 printf(" 'chain' ch1 [ch2 ...] : selects atoms by chain identifier(s),\n");
1238 printf(" not available with a .gro file as input.\n");
1239 printf(" ! : takes the complement of a group with respect to all\n");
1240 printf(" the atoms in the input file.\n");
1241 printf(" & | : AND and OR, can be placed between any of the options\n");
1242 printf(" above, the input is processed from left to right.\n");
1243 printf(" 'name' nr name : rename group nr to name.\n");
1244 printf(" 'del' nr1 [- nr2] : deletes one group or groups in the range from nr1 to "
1246 printf(" 'keep' nr : deletes all groups except nr.\n");
1247 printf(" 'case' : make all name compares case (in)sensitive.\n");
1248 printf(" 'splitch' nr : split group into chains using CA distances.\n");
1249 printf(" 'splitres' nr : split group into residues.\n");
1250 printf(" 'splitat' nr : split group into atoms.\n");
1251 printf(" 'res' nr : interpret numbers in group as residue numbers\n");
1252 printf(" Enter : list the currently defined groups and commands\n");
1253 printf(" 'l' : list the residues.\n");
1254 printf(" 'h' : show this help.\n");
1255 printf(" 'q' : save and quit.\n");
1257 printf(" Examples:\n");
1258 printf(" > 2 | 4 & r 3-5\n");
1259 printf(" selects all atoms from group 2 and 4 that have residue numbers 3, 4 or 5\n");
1260 printf(" > a C* & !a C CA\n");
1261 printf(" selects all atoms starting with 'C' but not the atoms 'C' and 'CA'\n");
1262 printf(" > \"protein\" & ! \"backb\"\n");
1263 printf(" selects all atoms that are in group 'protein' and not in group 'backbone'\n");
1266 printf("\npress Enter ");
1270 else if (std::strncmp(string, "del", 3) == 0)
1273 if (parse_int(&string, &sel_nr))
1275 while (string[0] == ' ')
1279 if (string[0] == '-')
1282 parse_int(&string, &sel_nr2);
1288 while (string[0] == ' ')
1292 if (string[0] == '\0')
1294 remove_group(sel_nr, sel_nr2, block, gn);
1298 printf("\nSyntax error: \"%s\"\n", string);
1302 else if (std::strncmp(string, "keep", 4) == 0)
1305 if (parse_int(&string, &sel_nr))
1307 remove_group(sel_nr + 1, block->nr - 1, block, gn);
1308 remove_group(0, sel_nr - 1, block, gn);
1311 else if (std::strncmp(string, "name", 4) == 0)
1314 if (parse_int(&string, &sel_nr))
1316 if ((sel_nr >= 0) && (sel_nr < block->nr))
1318 sscanf(string, "%s", gname);
1319 sfree((*gn)[sel_nr]);
1320 (*gn)[sel_nr] = gmx_strdup(gname);
1324 else if (std::strncmp(string, "case", 4) == 0)
1327 printf("Switched to case %s\n", bCase ? "sensitive" : "insensitive");
1329 else if (string[0] == 'v')
1331 bVerbose = !bVerbose;
1332 printf("Turned verbose %s\n", bVerbose ? "on" : "off");
1334 else if (string[0] == 'l')
1336 if (check_have_atoms(atoms, ostring))
1338 list_residues(atoms);
1341 else if (std::strncmp(string, "splitch", 7) == 0)
1344 if (check_have_atoms(atoms, ostring) && parse_int(&string, &sel_nr) && (sel_nr >= 0)
1345 && (sel_nr < block->nr))
1347 split_chain(atoms, x, sel_nr, block, gn);
1350 else if (std::strncmp(string, "splitres", 8) == 0)
1353 if (check_have_atoms(atoms, ostring) && parse_int(&string, &sel_nr) && (sel_nr >= 0)
1354 && (sel_nr < block->nr))
1356 split_group(atoms, sel_nr, block, gn, FALSE);
1359 else if (std::strncmp(string, "splitat", 7) == 0)
1362 if (check_have_atoms(atoms, ostring) && parse_int(&string, &sel_nr) && (sel_nr >= 0)
1363 && (sel_nr < block->nr))
1365 split_group(atoms, sel_nr, block, gn, TRUE);
1368 else if (string[0] == '\0')
1372 else if (string[0] != 'q')
1375 if (parse_entry(&string, natoms, atoms, block, gn, &nr, index, gname))
1379 while (string[0] == ' ')
1386 if (string[0] == '&')
1390 else if (string[0] == '|')
1399 for (i = 0; i < nr; i++)
1401 index1[i] = index[i];
1403 std::strcpy(gname1, gname);
1404 if (parse_entry(&string, natoms, atoms, block, gn, &nr2, index2, gname2))
1408 or_groups(nr1, index1, nr2, index2, &nr, index);
1409 sprintf(gname, "%s_%s", gname1, gname2);
1413 and_groups(nr1, index1, nr2, index2, &nr, index);
1414 sprintf(gname, "%s_&_%s", gname1, gname2);
1418 } while (bAnd || bOr);
1420 while (string[0] == ' ')
1426 printf("\nSyntax error: \"%s\"\n", string);
1430 copy2block(nr, index, block);
1431 srenew(*gn, block->nr);
1432 newgroup = block->nr - 1;
1433 (*gn)[newgroup] = gmx_strdup(gname);
1437 printf("Group is empty\n");
1440 } while (string[0] != 'q');
1447 static int block2natoms(t_blocka* block)
1452 for (i = 0; i < block->nra; i++)
1454 natoms = std::max(natoms, block->a[i]);
1461 static void merge_blocks(t_blocka* dest, t_blocka* source)
1465 /* count groups, srenew and fill */
1468 dest->nr += source->nr;
1469 srenew(dest->index, dest->nr + 1);
1470 for (i = 0; i < source->nr; i++)
1472 dest->index[i0 + i] = nra0 + source->index[i];
1474 /* count atoms, srenew and fill */
1475 dest->nra += source->nra;
1476 srenew(dest->a, dest->nra);
1477 for (i = 0; i < source->nra; i++)
1479 dest->a[nra0 + i] = source->a[i];
1482 /* terminate list */
1483 dest->index[dest->nr] = dest->nra;
1486 int gmx_make_ndx(int argc, char* argv[])
1488 const char* desc[] = { "Index groups are necessary for almost every GROMACS program.",
1489 "All these programs can generate default index groups. You ONLY",
1490 "have to use [THISMODULE] when you need SPECIAL index groups.",
1491 "There is a default index group for the whole system, 9 default",
1492 "index groups for proteins, and a default index group",
1493 "is generated for every other residue name.",
1495 "When no index file is supplied, also [THISMODULE] will generate the",
1497 "With the index editor you can select on atom, residue and chain names",
1499 "When a run input file is supplied you can also select on atom type.",
1500 "You can use boolean operations, you can split groups",
1501 "into chains, residues or atoms. You can delete and rename groups.",
1502 "Type 'h' in the editor for more details.",
1504 "The atom numbering in the editor and the index file starts at 1.",
1506 "The [TT]-twin[tt] switch duplicates all index groups with an offset of",
1507 "[TT]-natoms[tt], which is useful for Computational Electrophysiology",
1508 "double-layer membrane setups.",
1510 "See also [gmx-select] [TT]-on[tt], which provides an alternative way",
1511 "for constructing index groups. It covers nearly all of [THISMODULE]",
1512 "functionality, and in many cases much more." };
1514 static int natoms = 0;
1515 static gmx_bool bVerbose = FALSE;
1516 static gmx_bool bDuplicate = FALSE;
1517 t_pargs pa[] = { { "-natoms",
1521 "set number of atoms (default: read from coordinate or index file)" },
1526 "Duplicate all index groups with an offset of -natoms" },
1527 { "-verbose", FALSE, etBOOL, { &bVerbose }, "HIDDENVerbose output" } };
1528 #define NPA asize(pa)
1530 gmx_output_env_t* oenv;
1531 const char* stxfile;
1532 const char* ndxoutfile;
1539 t_blocka * block, *block2;
1540 char ** gnames, **gnames2;
1541 t_filenm fnm[] = { { efSTX, "-f", nullptr, ffOPTRD },
1542 { efNDX, "-n", nullptr, ffOPTRDMULT },
1543 { efNDX, "-o", nullptr, ffWRITE } };
1544 #define NFILE asize(fnm)
1546 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, NPA, pa, asize(desc), desc, 0, nullptr, &oenv))
1551 stxfile = ftp2fn_null(efSTX, NFILE, fnm);
1552 gmx::ArrayRef<const std::string> ndxInFiles = opt2fnsIfOptionSet("-n", NFILE, fnm);
1553 ndxoutfile = opt2fn("-o", NFILE, fnm);
1554 bNatoms = opt2parg_bSet("-natoms", NPA, pa);
1556 if (!stxfile && ndxInFiles.empty())
1558 gmx_fatal(FARGS, "No input files (structure or index)");
1564 bool haveFullTopology = false;
1565 fprintf(stderr, "\nReading structure file\n");
1566 readConfAndTopology(stxfile, &haveFullTopology, &mtop, &ePBC, &x, &v, box);
1567 atoms = gmx_mtop_global_atoms(&mtop);
1568 if (atoms.pdbinfo == nullptr)
1570 snew(atoms.pdbinfo, atoms.nr);
1580 /* read input file(s) */
1581 block = new_blocka();
1583 printf("Going to read %td old index file(s)\n", ndxInFiles.ssize());
1584 if (!ndxInFiles.empty())
1586 for (const std::string& ndxInFile : ndxInFiles)
1588 block2 = init_index(ndxInFile.c_str(), &gnames2);
1589 srenew(gnames, block->nr + block2->nr);
1590 for (j = 0; j < block2->nr; j++)
1592 gnames[block->nr + j] = gnames2[j];
1595 merge_blocks(block, block2);
1597 sfree(block2->index);
1598 /* done_block(block2); */
1605 analyse(&atoms, block, &gnames, FALSE, TRUE);
1610 natoms = block2natoms(block);
1611 printf("Counted atom numbers up to %d in index file\n", natoms);
1614 edit_index(natoms, &atoms, x, block, &gnames, bVerbose);
1616 write_index(ndxoutfile, block, gnames, bDuplicate, natoms);