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 by the GROMACS development team.
7 * Copyright (c) 2017,2018,2019,2020,2021, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
49 #include "gromacs/commandline/pargs.h"
50 #include "gromacs/fileio/confio.h"
51 #include "gromacs/math/vec.h"
52 #include "gromacs/topology/block.h"
53 #include "gromacs/topology/index.h"
54 #include "gromacs/topology/mtop_util.h"
55 #include "gromacs/topology/topology.h"
56 #include "gromacs/utility/arrayref.h"
57 #include "gromacs/utility/arraysize.h"
58 #include "gromacs/utility/cstringutil.h"
59 #include "gromacs/utility/fatalerror.h"
60 #include "gromacs/utility/futil.h"
61 #include "gromacs/utility/smalloc.h"
63 /* It's not nice to have size limits, but we should not spend more time
64 * on this ancient tool, but instead use the new selection library.
68 static const int NOTSET = -92637;
70 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
71 static gmx_bool bCase = FALSE;
73 static int or_groups(int nr1, const int* at1, int nr2, const int* at2, int* nr, int* at)
81 for (i1 = 0; i1 < nr1; i1++)
83 if ((i1 > 0) && (at1[i1] <= max))
89 for (i1 = 0; i1 < nr2; i1++)
91 if ((i1 > 0) && (at2[i1] <= max))
100 printf("One of your groups is not ascending\n");
107 while ((i1 < nr1) || (i2 < nr2))
109 if ((i2 == nr2) || ((i1 < nr1) && (at1[i1] < at2[i2])))
117 if ((i2 < nr2) && ((i1 == nr1) || (at1[i1] > at2[i2])))
126 printf("Merged two groups with OR: %d %d -> %d\n", nr1, nr2, *nr);
132 static int and_groups(int nr1, const int* at1, int nr2, const int* at2, int* nr, int* at)
137 for (i1 = 0; i1 < nr1; i1++)
139 for (i2 = 0; i2 < nr2; i2++)
141 if (at1[i1] == at2[i2])
149 printf("Merged two groups with AND: %d %d -> %d\n", nr1, nr2, *nr);
154 static gmx_bool is_name_char(char c)
156 /* This string should contain all characters that can not be
157 * the first letter of a name due to the make_ndx syntax.
159 const char* spec = " !&|";
161 return (c != '\0' && std::strchr(spec, c) == nullptr);
164 static int parse_names(char** string, int* n_names, gmx::ArrayRef<char*> names)
169 while (is_name_char((*string)[0]) || (*string)[0] == ' ')
171 if (is_name_char((*string)[0]))
173 if (*n_names >= MAXNAMES)
175 gmx_fatal(FARGS, "To many names: %d\n", *n_names + 1);
178 while (is_name_char((*string)[i]))
180 names[*n_names][i] = (*string)[i];
184 printf("Name is too long, the maximum is %d characters\n", NAME_LEN);
188 names[*n_names][i] = '\0';
191 upstring(names[*n_names]);
205 static gmx_bool parse_int_char(char** string, int* nr, unsigned char* c)
212 while ((*string)[0] == ' ')
221 if (std::isdigit((*string)[0]))
223 *nr = (*string)[0] - '0';
225 while (std::isdigit((*string)[0]))
227 *nr = (*nr) * 10 + (*string)[0] - '0';
230 if (std::isalpha((*string)[0]))
235 /* Check if there is at most one non-digit character */
236 if (!std::isalnum((*string)[0]))
253 static gmx_bool parse_int(char** string, int* nr)
260 bRet = parse_int_char(string, nr, &c);
261 if (bRet && c != ' ')
270 static gmx_bool isquote(char c)
275 static gmx_bool parse_string(char** string, int* nr, int ngrps, char** grpname)
280 while ((*string)[0] == ' ')
286 if (isquote((*string)[0]))
290 s = gmx_strdup((*string));
291 sp = std::strchr(s, c);
294 (*string) += sp - s + 1;
296 (*nr) = find_group(s, ngrps, grpname);
300 return (*nr) != NOTSET;
303 static int select_atomnumbers(char** string, const t_atoms* atoms, int n1, int* nr, int* index, char* gname)
309 while ((*string)[0] == ' ')
313 if ((*string)[0] == '-')
316 parse_int(string, &up);
317 if ((n1 < 1) || (n1 > atoms->nr) || (up < 1) || (up > atoms->nr))
319 printf("Invalid atom range\n");
323 for (i = n1 - 1; i <= up - 1; i++)
328 printf("Found %d atom%s in range %d-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
331 sprintf(buf, "a_%d", n1);
335 sprintf(buf, "a_%d-%d", n1, up);
337 std::strcpy(gname, buf);
346 if ((i - 1 >= 0) && (i - 1 < atoms->nr))
350 sprintf(buf, "_%d", i);
351 std::strcat(gname, buf);
355 printf("Invalid atom number %d\n", i);
358 } while ((*nr != 0) && (parse_int(string, &i)));
365 select_residuenumbers(char** string, const t_atoms* atoms, int n1, unsigned char c, int* nr, int* index, char* gname)
372 while ((*string)[0] == ' ')
376 if ((*string)[0] == '-')
378 /* Residue number range selection */
381 printf("Error: residue insertion codes can not be used with residue range selection\n");
385 parse_int(string, &up);
387 for (i = 0; i < atoms->nr; i++)
389 ri = &atoms->resinfo[atoms->atom[i].resind];
390 for (j = n1; (j <= up); j++)
392 if (ri->nr == j && (c == ' ' || ri->ic == c))
399 printf("Found %d atom%s with res.nr. in range %d-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
402 sprintf(buf, "r_%d", n1);
406 sprintf(buf, "r_%d-%d", n1, up);
408 std::strcpy(gname, buf);
412 /* Individual residue number/insertion code selection */
417 for (i = 0; i < atoms->nr; i++)
419 ri = &atoms->resinfo[atoms->atom[i].resind];
420 if (ri->nr == j && ri->ic == c)
426 sprintf(buf, "_%d", j);
427 std::strcat(gname, buf);
428 } while (parse_int_char(string, &j, &c));
435 select_residueindices(char** string, const t_atoms* atoms, int n1, unsigned char c, int* nr, int* index, char* gname)
437 /*this should be similar to select_residuenumbers except select by index (sequential numbering in file)*/
438 /*resind+1 for 1-indexing*/
444 while ((*string)[0] == ' ')
448 if ((*string)[0] == '-')
450 /* Residue number range selection */
453 printf("Error: residue insertion codes can not be used with residue range selection\n");
457 parse_int(string, &up);
459 for (i = 0; i < atoms->nr; i++)
461 ri = &atoms->resinfo[atoms->atom[i].resind];
462 for (j = n1; (j <= up); j++)
464 if (atoms->atom[i].resind + 1 == j && (c == ' ' || ri->ic == c))
471 printf("Found %d atom%s with resind.+1 in range %d-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
474 sprintf(buf, "r_%d", n1);
478 sprintf(buf, "r_%d-%d", n1, up);
480 std::strcpy(gname, buf);
484 /* Individual residue number/insertion code selection */
489 for (i = 0; i < atoms->nr; i++)
491 ri = &atoms->resinfo[atoms->atom[i].resind];
492 if (atoms->atom[i].resind + 1 == j && ri->ic == c)
498 sprintf(buf, "_%d", j);
499 std::strcat(gname, buf);
500 } while (parse_int_char(string, &j, &c));
508 atoms_from_residuenumbers(const t_atoms* atoms, int group, t_blocka* block, int* nr, int* index, char* gname)
510 int i, j, j0, j1, resnr, nres;
512 j0 = block->index[group];
513 j1 = block->index[group + 1];
515 for (j = j0; j < j1; j++)
517 if (block->a[j] >= nres)
519 printf("Index %s contains number>nres (%d>%d)\n", gname, block->a[j] + 1, nres);
523 for (i = 0; i < atoms->nr; i++)
525 resnr = atoms->resinfo[atoms->atom[i].resind].nr;
526 for (j = j0; j < j1; j++)
528 if (block->a[j] + 1 == resnr)
536 printf("Found %d atom%s in %d residues from group %s\n", *nr, (*nr == 1) ? "" : "s", j1 - j0, gname);
540 static gmx_bool comp_name(const char* name, const char* search)
542 gmx_bool matches = TRUE;
544 // Loop while name and search are not end-of-string and matches is true
545 for (; *name && *search && matches; name++, search++)
549 // still matching, continue to next character
552 else if (*search == '*')
556 printf("WARNING: Currently '*' is only supported at the end of an expression\n");
558 // if * is the last char in search string, we have a match,
559 // otherwise we just failed. Return in either case, we're done.
560 return (*(search + 1) == '\0');
563 // Compare one character
566 matches = (*name == *search);
570 matches = (std::toupper(*name) == std::toupper(*search));
574 matches = matches && (*name == '\0' && (*search == '\0' || *search == '*'));
579 static int select_chainnames(const t_atoms* atoms, int n_names, gmx::ArrayRef<char*> names, int* nr, int* index)
587 for (i = 0; i < atoms->nr; i++)
589 name[0] = atoms->resinfo[atoms->atom[i].resind].chainid;
591 while (j < n_names && !comp_name(name, names[j]))
601 printf("Found %d atom%s with chain identifier%s", *nr, (*nr == 1) ? "" : "s", (n_names == 1) ? "" : "s");
602 for (j = 0; (j < n_names); j++)
604 printf(" %s", names[j]);
611 static int select_atomnames(const t_atoms* atoms, int n_names, gmx::ArrayRef<char*> names, int* nr, int* index, gmx_bool bType)
618 for (i = 0; i < atoms->nr; i++)
622 name = *(atoms->atomtype[i]);
626 name = *(atoms->atomname[i]);
629 while (j < n_names && !comp_name(name, names[j]))
639 printf("Found %d atoms with %s%s", *nr, bType ? "type" : "name", (n_names == 1) ? "" : "s");
640 for (j = 0; (j < n_names); j++)
642 printf(" %s", names[j]);
649 static int select_residuenames(const t_atoms* atoms, int n_names, gmx::ArrayRef<char*> names, int* nr, int* index)
656 for (i = 0; i < atoms->nr; i++)
658 name = *(atoms->resinfo[atoms->atom[i].resind].name);
660 while (j < n_names && !comp_name(name, names[j]))
670 printf("Found %d atoms with residue name%s", *nr, (n_names == 1) ? "" : "s");
671 for (j = 0; (j < n_names); j++)
673 printf(" %s", names[j]);
680 static void copy2block(int n, const int* index, t_blocka* block)
687 srenew(block->index, block->nr + 1);
688 block->index[block->nr] = n0 + n;
689 srenew(block->a, n0 + n);
690 for (i = 0; (i < n); i++)
692 block->a[n0 + i] = index[i];
696 static void make_gname(int n, gmx::ArrayRef<char*> names, char* gname)
700 std::strcpy(gname, names[0]);
701 for (i = 1; i < n; i++)
703 std::strcat(gname, "_");
704 std::strcat(gname, names[i]);
708 static void copy_group(int g, t_blocka* block, int* nr, int* index)
712 i0 = block->index[g];
713 *nr = block->index[g + 1] - i0;
714 for (i = 0; i < *nr; i++)
716 index[i] = block->a[i0 + i];
720 static void remove_group(int nr, int nr2, t_blocka* block, char*** gn)
730 for (j = 0; j <= nr2 - nr; j++)
732 if ((nr < 0) || (nr >= block->nr))
734 printf("Group %d does not exist\n", nr + j);
738 shift = block->index[nr + 1] - block->index[nr];
739 for (i = block->index[nr + 1]; i < block->nra; i++)
741 block->a[i - shift] = block->a[i];
744 for (i = nr; i < block->nr; i++)
746 block->index[i] = block->index[i + 1] - shift;
748 name = gmx_strdup((*gn)[nr]);
750 for (i = nr; i < block->nr - 1; i++)
752 (*gn)[i] = (*gn)[i + 1];
755 block->nra = block->index[block->nr];
756 printf("Removed group %d '%s'\n", nr + j, name);
762 static void split_group(const t_atoms* atoms, int sel_nr, t_blocka* block, char*** gn, gmx_bool bAtom)
764 char buf[STRLEN], *name;
768 printf("Splitting group %d '%s' into %s\n", sel_nr, (*gn)[sel_nr], bAtom ? "atoms" : "residues");
770 n0 = block->index[sel_nr];
771 n1 = block->index[sel_nr + 1];
772 srenew(block->a, block->nra + n1 - n0);
773 for (i = n0; i < n1; i++)
776 resind = atoms->atom[a].resind;
777 name = *(atoms->resinfo[resind].name);
778 if (bAtom || (i == n0) || (atoms->atom[block->a[i - 1]].resind != resind))
782 block->index[block->nr] = block->nra;
785 srenew(block->index, block->nr + 1);
786 srenew(*gn, block->nr);
789 sprintf(buf, "%s_%s_%d", (*gn)[sel_nr], *atoms->atomname[a], a + 1);
793 sprintf(buf, "%s_%s_%d", (*gn)[sel_nr], name, atoms->resinfo[resind].nr);
795 (*gn)[block->nr - 1] = gmx_strdup(buf);
797 block->a[block->nra] = a;
800 block->index[block->nr] = block->nra;
803 static int split_chain(const t_atoms* atoms, const rvec* x, int sel_nr, t_blocka* block, char*** gn)
807 int i, a, natoms, *start = nullptr, *end = nullptr, ca_start, ca_end;
814 while (ca_start < natoms)
816 while ((ca_start < natoms) && std::strcmp(*atoms->atomname[ca_start], "CA") != 0)
820 if (ca_start < natoms)
822 srenew(start, nchain + 1);
823 srenew(end, nchain + 1);
824 start[nchain] = ca_start;
825 while ((start[nchain] > 0)
826 && (atoms->atom[start[nchain] - 1].resind == atoms->atom[ca_start].resind))
838 } while ((i < natoms) && std::strcmp(*atoms->atomname[i], "CA") != 0);
841 rvec_sub(x[ca_end], x[i], vec);
847 } while (norm(vec) < 0.45);
849 end[nchain] = ca_end;
850 while ((end[nchain] + 1 < natoms)
851 && (atoms->atom[end[nchain] + 1].resind == atoms->atom[ca_end].resind))
855 ca_start = end[nchain] + 1;
861 printf("Found 1 chain, will not split\n");
865 printf("Found %d chains\n", nchain);
867 for (j = 0; j < nchain; j++)
869 printf("%d:%6d atoms (%d to %d)\n", j + 1, end[j] - start[j] + 1, start[j] + 1, end[j] + 1);
874 srenew(block->a, block->nra + block->index[sel_nr + 1] - block->index[sel_nr]);
875 for (j = 0; j < nchain; j++)
878 srenew(block->index, block->nr + 1);
879 srenew(*gn, block->nr);
880 sprintf(buf, "%s_chain%d", (*gn)[sel_nr], j + 1);
881 (*gn)[block->nr - 1] = gmx_strdup(buf);
882 for (i = block->index[sel_nr]; i < block->index[sel_nr + 1]; i++)
885 if ((a >= start[j]) && (a <= end[j]))
887 block->a[block->nra] = a;
891 block->index[block->nr] = block->nra;
892 if (block->index[block->nr - 1] == block->index[block->nr])
894 remove_group(block->nr - 1, NOTSET, block, gn);
904 static gmx_bool check_have_atoms(const t_atoms* atoms, char* string)
906 if (atoms == nullptr)
908 printf("Can not process '%s' without atom info, use option -f\n", string);
917 static gmx_bool parse_entry(char** string,
919 const t_atoms* atoms,
925 gmx::ArrayRef<char*> entryNames)
928 int j, n_names, sel_nr1;
931 gmx_bool bRet, bCompl;
936 while (*string[0] == ' ')
941 if ((*string)[0] == '!')
945 while (*string[0] == ' ')
957 if (parse_int(string, &sel_nr1) || parse_string(string, &sel_nr1, block->nr, *gn))
959 if ((sel_nr1 >= 0) && (sel_nr1 < block->nr))
961 copy_group(sel_nr1, block, nr, index);
962 std::strcpy(gname, (*gn)[sel_nr1]);
963 printf("Copied index group %d '%s'\n", sel_nr1, (*gn)[sel_nr1]);
968 printf("Group %d does not exist\n", sel_nr1);
971 else if ((*string)[0] == 'a')
974 if (check_have_atoms(atoms, ostring))
976 if (parse_int(string, &sel_nr1))
978 bRet = (select_atomnumbers(string, atoms, sel_nr1, nr, index, gname) != 0);
980 else if (parse_names(string, &n_names, entryNames))
982 bRet = (select_atomnames(atoms, n_names, entryNames, nr, index, FALSE) != 0);
983 make_gname(n_names, entryNames, gname);
987 else if ((*string)[0] == 't')
990 if (check_have_atoms(atoms, ostring) && parse_names(string, &n_names, entryNames))
992 if (!(atoms->haveType))
994 printf("Need a run input file to select atom types\n");
998 bRet = (select_atomnames(atoms, n_names, entryNames, nr, index, TRUE) != 0);
999 make_gname(n_names, entryNames, gname);
1003 else if (std::strncmp(*string, "res", 3) == 0)
1006 if (check_have_atoms(atoms, ostring) && parse_int(string, &sel_nr1) && (sel_nr1 >= 0)
1007 && (sel_nr1 < block->nr))
1009 bRet = atoms_from_residuenumbers(atoms, sel_nr1, block, nr, index, (*gn)[sel_nr1]);
1010 sprintf(gname, "atom_%s", (*gn)[sel_nr1]);
1013 else if (std::strncmp(*string, "ri", 2) == 0)
1016 if (check_have_atoms(atoms, ostring) && parse_int_char(string, &sel_nr1, &c))
1018 bRet = (select_residueindices(string, atoms, sel_nr1, c, nr, index, gname) != 0);
1021 else if ((*string)[0] == 'r')
1024 if (check_have_atoms(atoms, ostring))
1026 if (parse_int_char(string, &sel_nr1, &c))
1028 bRet = (select_residuenumbers(string, atoms, sel_nr1, c, nr, index, gname) != 0);
1030 else if (parse_names(string, &n_names, entryNames))
1032 bRet = (select_residuenames(atoms, n_names, entryNames, nr, index) != 0);
1033 make_gname(n_names, entryNames, gname);
1037 else if (std::strncmp(*string, "chain", 5) == 0)
1040 if (check_have_atoms(atoms, ostring) && parse_names(string, &n_names, entryNames))
1042 bRet = (select_chainnames(atoms, n_names, entryNames, nr, index) != 0);
1043 sprintf(gname, "ch%s", entryNames[0]);
1044 for (i = 1; i < n_names; i++)
1046 std::strcat(gname, entryNames[i]);
1052 snew(index1, natoms - *nr);
1054 for (i = 0; i < natoms; i++)
1057 while ((j < *nr) && (index[j] != i))
1063 if (nr1 >= natoms - *nr)
1065 printf("There are double atoms in your index group\n");
1073 for (i = 0; i < nr1; i++)
1075 index[i] = index1[i];
1079 for (i = std::strlen(gname) + 1; i > 0; i--)
1081 gname[i] = gname[i - 1];
1084 printf("Complemented group: %d atoms\n", *nr);
1090 static void list_residues(const t_atoms* atoms)
1092 int i, j, start, end, prev_resind, resind;
1095 /* Print all the residues, assuming continuous resnr count */
1096 start = atoms->atom[0].resind;
1097 prev_resind = start;
1098 for (i = 0; i < atoms->nr; i++)
1100 resind = atoms->atom[i].resind;
1101 if ((resind != prev_resind) || (i == atoms->nr - 1))
1103 if ((bDiff = (std::strcmp(*atoms->resinfo[resind].name, *atoms->resinfo[start].name) != 0))
1104 || (i == atoms->nr - 1))
1114 if (end < start + 3)
1116 for (j = start; j <= end; j++)
1118 printf("%4d %-5s", j + 1, *(atoms->resinfo[j].name));
1123 printf(" %4d - %4d %-5s ", start + 1, end + 1, *(atoms->resinfo[start].name));
1128 prev_resind = resind;
1133 static void edit_index(int natoms, const t_atoms* atoms, const rvec* x, t_blocka* block, char*** gn, gmx_bool bVerbose)
1136 char inp_string[STRLEN], *string;
1137 char gname[STRLEN * 3], gname1[STRLEN], gname2[STRLEN];
1138 int i, i0, i1, sel_nr, sel_nr2, newgroup;
1139 int nr, nr1, nr2, *index, *index1, *index2;
1140 gmx_bool bAnd, bOr, bPrintOnce;
1144 snew(index, natoms);
1145 snew(index1, natoms);
1146 snew(index2, natoms);
1150 std::array<char*, MAXNAMES> entryNames;
1151 for (auto& name : entryNames)
1153 snew(name, NAME_LEN + 1);
1159 if (bVerbose || bPrintOnce || newgroup != NOTSET)
1162 if (bVerbose || bPrintOnce || newgroup == NOTSET)
1172 for (i = i0; i < i1; i++)
1174 printf("%3d %-20s: %5d atoms\n", i, (*gn)[i], block->index[i + 1] - block->index[i]);
1178 if (bVerbose || bPrintOnce)
1181 printf(" nr : group '!': not 'name' nr name 'splitch' nr Enter: list "
1183 printf(" 'a': atom '&': and 'del' nr 'splitres' nr 'l': list "
1185 printf(" 't': atom type '|': or 'keep' nr 'splitat' nr 'h': help\n");
1186 printf(" 'r': residue 'res' nr 'chain' char\n");
1187 printf(" \"name\": group 'case': case %s 'q': save and quit\n",
1188 bCase ? "insensitive" : "sensitive ");
1189 printf(" 'ri': residue index\n");
1194 if (nullptr == fgets(inp_string, STRLEN, stdin))
1196 gmx_fatal(FARGS, "Error reading user input");
1198 inp_string[std::strlen(inp_string) - 1] = 0;
1200 string = inp_string;
1201 while (string[0] == ' ')
1208 if (string[0] == 'h')
1210 printf(" nr : selects an index group by number or quoted string.\n");
1211 printf(" The string is first matched against the whole group "
1213 printf(" then against the beginning and finally against an\n");
1214 printf(" arbitrary substring. A multiple match is an error.\n");
1216 printf(" 'a' nr1 [nr2 ...] : selects atoms, atom numbering starts at 1.\n");
1217 printf(" 'a' nr1 - nr2 : selects atoms in the range from nr1 to nr2.\n");
1218 printf(" 'a' name1[*] [name2[*] ...] : selects atoms by name(s), '?' matches any "
1220 printf(" wildcard '*' allowed at the end of a name.\n");
1221 printf(" 't' type1[*] [type2[*] ...] : as 'a', but for type, run input file "
1223 printf(" 'r' nr1[ic1] [nr2[ic2] ...] : selects residues by number and insertion "
1225 printf(" 'r' nr1 - nr2 : selects residues in the range from nr1 to "
1227 printf(" 'r' name1[*] [name2[*] ...] : as 'a', but for residue names.\n");
1228 printf(" 'ri' nr1 - nr2 : selects residue indices, 1-indexed, (as opposed "
1229 "to numbers) in the range from nr1 to nr2.\n");
1230 printf(" 'chain' ch1 [ch2 ...] : selects atoms by chain identifier(s),\n");
1231 printf(" not available with a .gro file as input.\n");
1232 printf(" ! : takes the complement of a group with respect to all\n");
1233 printf(" the atoms in the input file.\n");
1234 printf(" & | : AND and OR, can be placed between any of the options\n");
1235 printf(" above, the input is processed from left to right.\n");
1236 printf(" 'name' nr name : rename group nr to name.\n");
1237 printf(" 'del' nr1 [- nr2] : deletes one group or groups in the range from nr1 to "
1239 printf(" 'keep' nr : deletes all groups except nr.\n");
1240 printf(" 'case' : make all name compares case (in)sensitive.\n");
1241 printf(" 'splitch' nr : split group into chains using CA distances.\n");
1242 printf(" 'splitres' nr : split group into residues.\n");
1243 printf(" 'splitat' nr : split group into atoms.\n");
1244 printf(" 'res' nr : interpret numbers in group as residue numbers\n");
1245 printf(" Enter : list the currently defined groups and commands\n");
1246 printf(" 'l' : list the residues.\n");
1247 printf(" 'h' : show this help.\n");
1248 printf(" 'q' : save and quit.\n");
1250 printf(" Examples:\n");
1251 printf(" > 2 | 4 & r 3-5\n");
1252 printf(" selects all atoms from group 2 and 4 that have residue numbers 3, 4 or 5\n");
1253 printf(" > a C* & !a C CA\n");
1254 printf(" selects all atoms starting with 'C' but not the atoms 'C' and 'CA'\n");
1255 printf(" > \"protein\" & ! \"backb\"\n");
1256 printf(" selects all atoms that are in group 'protein' and not in group 'backbone'\n");
1259 printf("\npress Enter ");
1263 else if (std::strncmp(string, "del", 3) == 0)
1266 if (parse_int(&string, &sel_nr))
1268 while (string[0] == ' ')
1272 if (string[0] == '-')
1275 parse_int(&string, &sel_nr2);
1281 while (string[0] == ' ')
1285 if (string[0] == '\0')
1287 remove_group(sel_nr, sel_nr2, block, gn);
1291 printf("\nSyntax error: \"%s\"\n", string);
1295 else if (std::strncmp(string, "keep", 4) == 0)
1298 if (parse_int(&string, &sel_nr))
1300 remove_group(sel_nr + 1, block->nr - 1, block, gn);
1301 remove_group(0, sel_nr - 1, block, gn);
1304 else if (std::strncmp(string, "name", 4) == 0)
1307 if (parse_int(&string, &sel_nr))
1309 if ((sel_nr >= 0) && (sel_nr < block->nr))
1311 sscanf(string, "%s", gname);
1312 sfree((*gn)[sel_nr]);
1313 (*gn)[sel_nr] = gmx_strdup(gname);
1317 else if (std::strncmp(string, "case", 4) == 0)
1320 printf("Switched to case %s\n", bCase ? "sensitive" : "insensitive");
1322 else if (string[0] == 'v')
1324 bVerbose = !bVerbose;
1325 printf("Turned verbose %s\n", bVerbose ? "on" : "off");
1327 else if (string[0] == 'l')
1329 if (check_have_atoms(atoms, ostring))
1331 list_residues(atoms);
1334 else if (std::strncmp(string, "splitch", 7) == 0)
1337 if (check_have_atoms(atoms, ostring) && parse_int(&string, &sel_nr) && (sel_nr >= 0)
1338 && (sel_nr < block->nr))
1340 split_chain(atoms, x, sel_nr, block, gn);
1343 else if (std::strncmp(string, "splitres", 8) == 0)
1346 if (check_have_atoms(atoms, ostring) && parse_int(&string, &sel_nr) && (sel_nr >= 0)
1347 && (sel_nr < block->nr))
1349 split_group(atoms, sel_nr, block, gn, FALSE);
1352 else if (std::strncmp(string, "splitat", 7) == 0)
1355 if (check_have_atoms(atoms, ostring) && parse_int(&string, &sel_nr) && (sel_nr >= 0)
1356 && (sel_nr < block->nr))
1358 split_group(atoms, sel_nr, block, gn, TRUE);
1361 else if (string[0] == '\0')
1365 else if (string[0] != 'q')
1368 if (parse_entry(&string, natoms, atoms, block, gn, &nr, index, gname, entryNames))
1372 while (string[0] == ' ')
1379 if (string[0] == '&')
1383 else if (string[0] == '|')
1392 for (i = 0; i < nr; i++)
1394 index1[i] = index[i];
1396 std::strcpy(gname1, gname);
1397 if (parse_entry(&string, natoms, atoms, block, gn, &nr2, index2, gname2, entryNames))
1401 or_groups(nr1, index1, nr2, index2, &nr, index);
1402 sprintf(gname, "%s_%s", gname1, gname2);
1406 and_groups(nr1, index1, nr2, index2, &nr, index);
1407 sprintf(gname, "%s_&_%s", gname1, gname2);
1411 } while (bAnd || bOr);
1413 while (string[0] == ' ')
1419 printf("\nSyntax error: \"%s\"\n", string);
1423 copy2block(nr, index, block);
1424 srenew(*gn, block->nr);
1425 newgroup = block->nr - 1;
1426 (*gn)[newgroup] = gmx_strdup(gname);
1430 printf("Group is empty\n");
1433 } while (string[0] != 'q');
1435 for (auto& name : entryNames)
1444 static int block2natoms(t_blocka* block)
1449 for (i = 0; i < block->nra; i++)
1451 natoms = std::max(natoms, block->a[i]);
1458 static void merge_blocks(t_blocka* dest, t_blocka* source)
1462 /* count groups, srenew and fill */
1465 dest->nr += source->nr;
1466 srenew(dest->index, dest->nr + 1);
1467 for (i = 0; i < source->nr; i++)
1469 dest->index[i0 + i] = nra0 + source->index[i];
1471 /* count atoms, srenew and fill */
1472 dest->nra += source->nra;
1473 srenew(dest->a, dest->nra);
1474 for (i = 0; i < source->nra; i++)
1476 dest->a[nra0 + i] = source->a[i];
1479 /* terminate list */
1480 dest->index[dest->nr] = dest->nra;
1483 int gmx_make_ndx(int argc, char* argv[])
1485 const char* desc[] = { "Index groups are necessary for almost every GROMACS program.",
1486 "All these programs can generate default index groups. You ONLY",
1487 "have to use [THISMODULE] when you need SPECIAL index groups.",
1488 "There is a default index group for the whole system, 9 default",
1489 "index groups for proteins, and a default index group",
1490 "is generated for every other residue name.",
1492 "When no index file is supplied, also [THISMODULE] will generate the",
1494 "With the index editor you can select on atom, residue and chain names",
1496 "When a run input file is supplied you can also select on atom type.",
1497 "You can use boolean operations, you can split groups",
1498 "into chains, residues or atoms. You can delete and rename groups.",
1499 "Type 'h' in the editor for more details.",
1501 "The atom numbering in the editor and the index file starts at 1.",
1503 "The [TT]-twin[tt] switch duplicates all index groups with an offset of",
1504 "[TT]-natoms[tt], which is useful for Computational Electrophysiology",
1505 "double-layer membrane setups.",
1507 "See also [gmx-select] [TT]-on[tt], which provides an alternative way",
1508 "for constructing index groups. It covers nearly all of [THISMODULE]",
1509 "functionality, and in many cases much more." };
1511 static int natoms = 0;
1512 static gmx_bool bVerbose = FALSE;
1513 static gmx_bool bDuplicate = FALSE;
1514 t_pargs pa[] = { { "-natoms",
1518 "set number of atoms (default: read from coordinate or index file)" },
1523 "Duplicate all index groups with an offset of -natoms" },
1524 { "-verbose", FALSE, etBOOL, { &bVerbose }, "HIDDENVerbose output" } };
1525 #define NPA asize(pa)
1527 gmx_output_env_t* oenv;
1528 const char* stxfile;
1529 const char* ndxoutfile;
1536 t_blocka * block, *block2;
1537 char ** gnames, **gnames2;
1538 t_filenm fnm[] = { { efSTX, "-f", nullptr, ffOPTRD },
1539 { efNDX, "-n", nullptr, ffOPTRDMULT },
1540 { efNDX, "-o", nullptr, ffWRITE } };
1541 #define NFILE asize(fnm)
1543 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, NPA, pa, asize(desc), desc, 0, nullptr, &oenv))
1548 stxfile = ftp2fn_null(efSTX, NFILE, fnm);
1549 gmx::ArrayRef<const std::string> ndxInFiles = opt2fnsIfOptionSet("-n", NFILE, fnm);
1550 ndxoutfile = opt2fn("-o", NFILE, fnm);
1551 bNatoms = opt2parg_bSet("-natoms", NPA, pa);
1553 if (!stxfile && ndxInFiles.empty())
1555 gmx_fatal(FARGS, "No input files (structure or index)");
1561 bool haveFullTopology = false;
1562 fprintf(stderr, "\nReading structure file\n");
1563 readConfAndTopology(stxfile, &haveFullTopology, &mtop, &pbcType, &x, &v, box);
1564 atoms = gmx_mtop_global_atoms(mtop);
1565 if (atoms.pdbinfo == nullptr)
1567 snew(atoms.pdbinfo, atoms.nr);
1577 /* read input file(s) */
1578 block = new_blocka();
1580 printf("Going to read %td old index file(s)\n", ndxInFiles.ssize());
1581 if (!ndxInFiles.empty())
1583 for (const std::string& ndxInFile : ndxInFiles)
1585 block2 = init_index(ndxInFile.c_str(), &gnames2);
1586 srenew(gnames, block->nr + block2->nr);
1587 for (j = 0; j < block2->nr; j++)
1589 gnames[block->nr + j] = gnames2[j];
1592 merge_blocks(block, block2);
1594 sfree(block2->index);
1595 /* done_block(block2); */
1602 analyse(&atoms, block, &gnames, FALSE, TRUE);
1607 natoms = block2natoms(block);
1608 printf("Counted atom numbers up to %d in index file\n", natoms);
1610 edit_index(natoms, &atoms, x, block, &gnames, bVerbose);
1612 write_index(ndxoutfile, block, gnames, bDuplicate, natoms);