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.
40 #include "gromacs/utility/arrayref.h"
51 #include "gromacs/commandline/pargs.h"
52 #include "gromacs/fileio/confio.h"
53 #include "gromacs/math/vec.h"
54 #include "gromacs/topology/block.h"
55 #include "gromacs/topology/index.h"
56 #include "gromacs/topology/mtop_util.h"
57 #include "gromacs/topology/topology.h"
58 #include "gromacs/utility/arrayref.h"
59 #include "gromacs/utility/arraysize.h"
60 #include "gromacs/utility/cstringutil.h"
61 #include "gromacs/utility/fatalerror.h"
62 #include "gromacs/utility/futil.h"
63 #include "gromacs/utility/smalloc.h"
65 /* It's not nice to have size limits, but we should not spend more time
66 * on this ancient tool, but instead use the new selection library.
70 static const int NOTSET = -92637;
72 // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
73 static gmx_bool bCase = FALSE;
75 static int or_groups(int nr1, const int* at1, int nr2, const int* at2, int* nr, int* at)
83 for (i1 = 0; i1 < nr1; i1++)
85 if ((i1 > 0) && (at1[i1] <= max))
91 for (i1 = 0; i1 < nr2; i1++)
93 if ((i1 > 0) && (at2[i1] <= max))
102 printf("One of your groups is not ascending\n");
109 while ((i1 < nr1) || (i2 < nr2))
111 if ((i2 == nr2) || ((i1 < nr1) && (at1[i1] < at2[i2])))
119 if ((i2 < nr2) && ((i1 == nr1) || (at1[i1] > at2[i2])))
128 printf("Merged two groups with OR: %d %d -> %d\n", nr1, nr2, *nr);
134 static int and_groups(int nr1, const int* at1, int nr2, const int* at2, int* nr, int* at)
139 for (i1 = 0; i1 < nr1; i1++)
141 for (i2 = 0; i2 < nr2; i2++)
143 if (at1[i1] == at2[i2])
151 printf("Merged two groups with AND: %d %d -> %d\n", nr1, nr2, *nr);
156 static gmx_bool is_name_char(char c)
158 /* This string should contain all characters that can not be
159 * the first letter of a name due to the make_ndx syntax.
161 const char* spec = " !&|";
163 return (c != '\0' && std::strchr(spec, c) == nullptr);
166 static int parse_names(char** string, int* n_names, gmx::ArrayRef<char*> names)
171 while (is_name_char((*string)[0]) || (*string)[0] == ' ')
173 if (is_name_char((*string)[0]))
175 if (*n_names >= MAXNAMES)
177 gmx_fatal(FARGS, "To many names: %d\n", *n_names + 1);
180 while (is_name_char((*string)[i]))
182 names[*n_names][i] = (*string)[i];
186 printf("Name is too long, the maximum is %d characters\n", NAME_LEN);
190 names[*n_names][i] = '\0';
193 upstring(names[*n_names]);
207 static gmx_bool parse_int_char(char** string, int* nr, unsigned char* c)
214 while ((*string)[0] == ' ')
223 if (std::isdigit((*string)[0]))
225 *nr = (*string)[0] - '0';
227 while (std::isdigit((*string)[0]))
229 *nr = (*nr) * 10 + (*string)[0] - '0';
232 if (std::isalpha((*string)[0]))
237 /* Check if there is at most one non-digit character */
238 if (!std::isalnum((*string)[0]))
255 static gmx_bool parse_int(char** string, int* nr)
262 bRet = parse_int_char(string, nr, &c);
263 if (bRet && c != ' ')
272 static gmx_bool isquote(char c)
277 static gmx_bool parse_string(char** string, int* nr, int ngrps, char** grpname)
282 while ((*string)[0] == ' ')
288 if (isquote((*string)[0]))
292 s = gmx_strdup((*string));
293 sp = std::strchr(s, c);
296 (*string) += sp - s + 1;
298 (*nr) = find_group(s, ngrps, grpname);
302 return (*nr) != NOTSET;
305 static int select_atomnumbers(char** string, const t_atoms* atoms, int n1, int* nr, int* index, char* gname)
311 while ((*string)[0] == ' ')
315 if ((*string)[0] == '-')
318 parse_int(string, &up);
319 if ((n1 < 1) || (n1 > atoms->nr) || (up < 1) || (up > atoms->nr))
321 printf("Invalid atom range\n");
325 for (i = n1 - 1; i <= up - 1; i++)
330 printf("Found %d atom%s in range %d-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
333 sprintf(buf, "a_%d", n1);
337 sprintf(buf, "a_%d-%d", n1, up);
339 std::strcpy(gname, buf);
348 if ((i - 1 >= 0) && (i - 1 < atoms->nr))
352 sprintf(buf, "_%d", i);
353 std::strcat(gname, buf);
357 printf("Invalid atom number %d\n", i);
360 } while ((*nr != 0) && (parse_int(string, &i)));
367 select_residuenumbers(char** string, const t_atoms* atoms, int n1, unsigned char c, int* nr, int* index, char* gname)
374 while ((*string)[0] == ' ')
378 if ((*string)[0] == '-')
380 /* Residue number range selection */
383 printf("Error: residue insertion codes can not be used with residue range selection\n");
387 parse_int(string, &up);
389 for (i = 0; i < atoms->nr; i++)
391 ri = &atoms->resinfo[atoms->atom[i].resind];
392 for (j = n1; (j <= up); j++)
394 if (ri->nr == j && (c == ' ' || ri->ic == c))
401 printf("Found %d atom%s with res.nr. in range %d-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
404 sprintf(buf, "r_%d", n1);
408 sprintf(buf, "r_%d-%d", n1, up);
410 std::strcpy(gname, buf);
414 /* Individual residue number/insertion code selection */
419 for (i = 0; i < atoms->nr; i++)
421 ri = &atoms->resinfo[atoms->atom[i].resind];
422 if (ri->nr == j && ri->ic == c)
428 sprintf(buf, "_%d", j);
429 std::strcat(gname, buf);
430 } while (parse_int_char(string, &j, &c));
437 select_residueindices(char** string, const t_atoms* atoms, int n1, unsigned char c, int* nr, int* index, char* gname)
439 /*this should be similar to select_residuenumbers except select by index (sequential numbering in file)*/
440 /*resind+1 for 1-indexing*/
446 while ((*string)[0] == ' ')
450 if ((*string)[0] == '-')
452 /* Residue number range selection */
455 printf("Error: residue insertion codes can not be used with residue range selection\n");
459 parse_int(string, &up);
461 for (i = 0; i < atoms->nr; i++)
463 ri = &atoms->resinfo[atoms->atom[i].resind];
464 for (j = n1; (j <= up); j++)
466 if (atoms->atom[i].resind + 1 == j && (c == ' ' || ri->ic == c))
473 printf("Found %d atom%s with resind.+1 in range %d-%d\n", *nr, (*nr == 1) ? "" : "s", n1, up);
476 sprintf(buf, "r_%d", n1);
480 sprintf(buf, "r_%d-%d", n1, up);
482 std::strcpy(gname, buf);
486 /* Individual residue number/insertion code selection */
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)
500 sprintf(buf, "_%d", j);
501 std::strcat(gname, buf);
502 } while (parse_int_char(string, &j, &c));
510 atoms_from_residuenumbers(const t_atoms* atoms, int group, t_blocka* block, int* nr, int* index, char* gname)
512 int i, j, j0, j1, resnr, nres;
514 j0 = block->index[group];
515 j1 = block->index[group + 1];
517 for (j = j0; j < j1; j++)
519 if (block->a[j] >= nres)
521 printf("Index %s contains number>nres (%d>%d)\n", gname, block->a[j] + 1, nres);
525 for (i = 0; i < atoms->nr; i++)
527 resnr = atoms->resinfo[atoms->atom[i].resind].nr;
528 for (j = j0; j < j1; j++)
530 if (block->a[j] + 1 == resnr)
538 printf("Found %d atom%s in %d residues from group %s\n", *nr, (*nr == 1) ? "" : "s", j1 - j0, gname);
542 static gmx_bool comp_name(const char* name, const char* search)
544 gmx_bool matches = TRUE;
546 // Loop while name and search are not end-of-string and matches is true
547 for (; *name && *search && matches; name++, search++)
551 // still matching, continue to next character
554 else if (*search == '*')
558 printf("WARNING: Currently '*' is only supported at the end of an expression\n");
560 // if * is the last char in search string, we have a match,
561 // otherwise we just failed. Return in either case, we're done.
562 return (*(search + 1) == '\0');
565 // Compare one character
568 matches = (*name == *search);
572 matches = (std::toupper(*name) == std::toupper(*search));
576 matches = matches && (*name == '\0' && (*search == '\0' || *search == '*'));
581 static int select_chainnames(const t_atoms* atoms, int n_names, gmx::ArrayRef<char*> names, int* nr, int* index)
589 for (i = 0; i < atoms->nr; i++)
591 name[0] = atoms->resinfo[atoms->atom[i].resind].chainid;
593 while (j < n_names && !comp_name(name, names[j]))
603 printf("Found %d atom%s with chain identifier%s", *nr, (*nr == 1) ? "" : "s", (n_names == 1) ? "" : "s");
604 for (j = 0; (j < n_names); j++)
606 printf(" %s", names[j]);
613 static int select_atomnames(const t_atoms* atoms, int n_names, gmx::ArrayRef<char*> names, int* nr, int* index, gmx_bool bType)
620 for (i = 0; i < atoms->nr; i++)
624 name = *(atoms->atomtype[i]);
628 name = *(atoms->atomname[i]);
631 while (j < n_names && !comp_name(name, names[j]))
641 printf("Found %d atoms with %s%s", *nr, bType ? "type" : "name", (n_names == 1) ? "" : "s");
642 for (j = 0; (j < n_names); j++)
644 printf(" %s", names[j]);
651 static int select_residuenames(const t_atoms* atoms, int n_names, gmx::ArrayRef<char*> names, int* nr, int* index)
658 for (i = 0; i < atoms->nr; i++)
660 name = *(atoms->resinfo[atoms->atom[i].resind].name);
662 while (j < n_names && !comp_name(name, names[j]))
672 printf("Found %d atoms with residue name%s", *nr, (n_names == 1) ? "" : "s");
673 for (j = 0; (j < n_names); j++)
675 printf(" %s", names[j]);
682 static void copy2block(int n, const int* index, t_blocka* block)
689 srenew(block->index, block->nr + 1);
690 block->index[block->nr] = n0 + n;
691 srenew(block->a, n0 + n);
692 for (i = 0; (i < n); i++)
694 block->a[n0 + i] = index[i];
698 static void make_gname(int n, gmx::ArrayRef<char*> names, char* gname)
702 std::strcpy(gname, names[0]);
703 for (i = 1; i < n; i++)
705 std::strcat(gname, "_");
706 std::strcat(gname, names[i]);
710 static void copy_group(int g, t_blocka* block, int* nr, int* index)
714 i0 = block->index[g];
715 *nr = block->index[g + 1] - i0;
716 for (i = 0; i < *nr; i++)
718 index[i] = block->a[i0 + i];
722 static void remove_group(int nr, int nr2, t_blocka* block, char*** gn)
732 for (j = 0; j <= nr2 - nr; j++)
734 if ((nr < 0) || (nr >= block->nr))
736 printf("Group %d does not exist\n", nr + j);
740 shift = block->index[nr + 1] - block->index[nr];
741 for (i = block->index[nr + 1]; i < block->nra; i++)
743 block->a[i - shift] = block->a[i];
746 for (i = nr; i < block->nr; i++)
748 block->index[i] = block->index[i + 1] - shift;
750 name = gmx_strdup((*gn)[nr]);
752 for (i = nr; i < block->nr - 1; i++)
754 (*gn)[i] = (*gn)[i + 1];
757 block->nra = block->index[block->nr];
758 printf("Removed group %d '%s'\n", nr + j, name);
764 static void split_group(const t_atoms* atoms, int sel_nr, t_blocka* block, char*** gn, gmx_bool bAtom)
766 char buf[STRLEN], *name;
770 printf("Splitting group %d '%s' into %s\n", sel_nr, (*gn)[sel_nr], bAtom ? "atoms" : "residues");
772 n0 = block->index[sel_nr];
773 n1 = block->index[sel_nr + 1];
774 srenew(block->a, block->nra + n1 - n0);
775 for (i = n0; i < n1; i++)
778 resind = atoms->atom[a].resind;
779 name = *(atoms->resinfo[resind].name);
780 if (bAtom || (i == n0) || (atoms->atom[block->a[i - 1]].resind != resind))
784 block->index[block->nr] = block->nra;
787 srenew(block->index, block->nr + 1);
788 srenew(*gn, block->nr);
791 sprintf(buf, "%s_%s_%d", (*gn)[sel_nr], *atoms->atomname[a], a + 1);
795 sprintf(buf, "%s_%s_%d", (*gn)[sel_nr], name, atoms->resinfo[resind].nr);
797 (*gn)[block->nr - 1] = gmx_strdup(buf);
799 block->a[block->nra] = a;
802 block->index[block->nr] = block->nra;
805 static int split_chain(const t_atoms* atoms, const rvec* x, int sel_nr, t_blocka* block, char*** gn)
809 int i, a, natoms, *start = nullptr, *end = nullptr, ca_start, ca_end;
816 while (ca_start < natoms)
818 while ((ca_start < natoms) && std::strcmp(*atoms->atomname[ca_start], "CA") != 0)
822 if (ca_start < natoms)
824 srenew(start, nchain + 1);
825 srenew(end, nchain + 1);
826 start[nchain] = ca_start;
827 while ((start[nchain] > 0)
828 && (atoms->atom[start[nchain] - 1].resind == atoms->atom[ca_start].resind))
840 } while ((i < natoms) && std::strcmp(*atoms->atomname[i], "CA") != 0);
843 rvec_sub(x[ca_end], x[i], vec);
849 } while (norm(vec) < 0.45);
851 end[nchain] = ca_end;
852 while ((end[nchain] + 1 < natoms)
853 && (atoms->atom[end[nchain] + 1].resind == atoms->atom[ca_end].resind))
857 ca_start = end[nchain] + 1;
863 printf("Found 1 chain, will not split\n");
867 printf("Found %d chains\n", nchain);
869 for (j = 0; j < nchain; j++)
871 printf("%d:%6d atoms (%d to %d)\n", j + 1, end[j] - start[j] + 1, start[j] + 1, end[j] + 1);
876 srenew(block->a, block->nra + block->index[sel_nr + 1] - block->index[sel_nr]);
877 for (j = 0; j < nchain; j++)
880 srenew(block->index, block->nr + 1);
881 srenew(*gn, block->nr);
882 sprintf(buf, "%s_chain%d", (*gn)[sel_nr], j + 1);
883 (*gn)[block->nr - 1] = gmx_strdup(buf);
884 for (i = block->index[sel_nr]; i < block->index[sel_nr + 1]; i++)
887 if ((a >= start[j]) && (a <= end[j]))
889 block->a[block->nra] = a;
893 block->index[block->nr] = block->nra;
894 if (block->index[block->nr - 1] == block->index[block->nr])
896 remove_group(block->nr - 1, NOTSET, block, gn);
906 static gmx_bool check_have_atoms(const t_atoms* atoms, char* string)
908 if (atoms == nullptr)
910 printf("Can not process '%s' without atom info, use option -f\n", string);
919 static gmx_bool parse_entry(char** string,
921 const t_atoms* atoms,
927 gmx::ArrayRef<char*> entryNames)
930 int j, n_names, sel_nr1;
933 gmx_bool bRet, bCompl;
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, entryNames))
984 bRet = (select_atomnames(atoms, n_names, entryNames, nr, index, FALSE) != 0);
985 make_gname(n_names, entryNames, gname);
989 else if ((*string)[0] == 't')
992 if (check_have_atoms(atoms, ostring) && parse_names(string, &n_names, entryNames))
994 if (!(atoms->haveType))
996 printf("Need a run input file to select atom types\n");
1000 bRet = (select_atomnames(atoms, n_names, entryNames, nr, index, TRUE) != 0);
1001 make_gname(n_names, entryNames, 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, entryNames))
1034 bRet = (select_residuenames(atoms, n_names, entryNames, nr, index) != 0);
1035 make_gname(n_names, entryNames, gname);
1039 else if (std::strncmp(*string, "chain", 5) == 0)
1042 if (check_have_atoms(atoms, ostring) && parse_names(string, &n_names, entryNames))
1044 bRet = (select_chainnames(atoms, n_names, entryNames, nr, index) != 0);
1045 sprintf(gname, "ch%s", entryNames[0]);
1046 for (i = 1; i < n_names; i++)
1048 std::strcat(gname, entryNames[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)
1138 char inp_string[STRLEN], *string;
1139 char gname[STRLEN * 3], gname1[STRLEN], gname2[STRLEN];
1140 int i, i0, i1, sel_nr, sel_nr2, newgroup;
1141 int nr, nr1, nr2, *index, *index1, *index2;
1142 gmx_bool bAnd, bOr, bPrintOnce;
1146 snew(index, natoms);
1147 snew(index1, natoms);
1148 snew(index2, natoms);
1152 std::array<char*, MAXNAMES> entryNames;
1153 for (auto& name : entryNames)
1155 snew(name, NAME_LEN + 1);
1161 if (bVerbose || bPrintOnce || newgroup != NOTSET)
1164 if (bVerbose || bPrintOnce || newgroup == NOTSET)
1174 for (i = i0; i < i1; i++)
1176 printf("%3d %-20s: %5d atoms\n", i, (*gn)[i], block->index[i + 1] - block->index[i]);
1180 if (bVerbose || bPrintOnce)
1183 printf(" nr : group '!': not 'name' nr name 'splitch' nr Enter: list "
1185 printf(" 'a': atom '&': and 'del' nr 'splitres' nr 'l': list "
1187 printf(" 't': atom type '|': or 'keep' nr 'splitat' nr 'h': help\n");
1188 printf(" 'r': residue 'res' nr 'chain' char\n");
1189 printf(" \"name\": group 'case': case %s 'q': save and quit\n",
1190 bCase ? "insensitive" : "sensitive ");
1191 printf(" 'ri': residue index\n");
1196 if (nullptr == fgets(inp_string, STRLEN, stdin))
1198 gmx_fatal(FARGS, "Error reading user input");
1200 inp_string[std::strlen(inp_string) - 1] = 0;
1202 string = inp_string;
1203 while (string[0] == ' ')
1210 if (string[0] == 'h')
1212 printf(" nr : selects an index group by number or quoted string.\n");
1213 printf(" The string is first matched against the whole group "
1215 printf(" then against the beginning and finally against an\n");
1216 printf(" arbitrary substring. A multiple match is an error.\n");
1218 printf(" 'a' nr1 [nr2 ...] : selects atoms, atom numbering starts at 1.\n");
1219 printf(" 'a' nr1 - nr2 : selects atoms in the range from nr1 to nr2.\n");
1220 printf(" 'a' name1[*] [name2[*] ...] : selects atoms by name(s), '?' matches any "
1222 printf(" wildcard '*' allowed at the end of a name.\n");
1223 printf(" 't' type1[*] [type2[*] ...] : as 'a', but for type, run input file "
1225 printf(" 'r' nr1[ic1] [nr2[ic2] ...] : selects residues by number and insertion "
1227 printf(" 'r' nr1 - nr2 : selects residues in the range from nr1 to "
1229 printf(" 'r' name1[*] [name2[*] ...] : as 'a', but for residue names.\n");
1230 printf(" 'ri' nr1 - nr2 : selects residue indices, 1-indexed, (as opposed "
1231 "to numbers) in the range from nr1 to nr2.\n");
1232 printf(" 'chain' ch1 [ch2 ...] : selects atoms by chain identifier(s),\n");
1233 printf(" not available with a .gro file as input.\n");
1234 printf(" ! : takes the complement of a group with respect to all\n");
1235 printf(" the atoms in the input file.\n");
1236 printf(" & | : AND and OR, can be placed between any of the options\n");
1237 printf(" above, the input is processed from left to right.\n");
1238 printf(" 'name' nr name : rename group nr to name.\n");
1239 printf(" 'del' nr1 [- nr2] : deletes one group or groups in the range from nr1 to "
1241 printf(" 'keep' nr : deletes all groups except nr.\n");
1242 printf(" 'case' : make all name compares case (in)sensitive.\n");
1243 printf(" 'splitch' nr : split group into chains using CA distances.\n");
1244 printf(" 'splitres' nr : split group into residues.\n");
1245 printf(" 'splitat' nr : split group into atoms.\n");
1246 printf(" 'res' nr : interpret numbers in group as residue numbers\n");
1247 printf(" Enter : list the currently defined groups and commands\n");
1248 printf(" 'l' : list the residues.\n");
1249 printf(" 'h' : show this help.\n");
1250 printf(" 'q' : save and quit.\n");
1252 printf(" Examples:\n");
1253 printf(" > 2 | 4 & r 3-5\n");
1254 printf(" selects all atoms from group 2 and 4 that have residue numbers 3, 4 or 5\n");
1255 printf(" > a C* & !a C CA\n");
1256 printf(" selects all atoms starting with 'C' but not the atoms 'C' and 'CA'\n");
1257 printf(" > \"protein\" & ! \"backb\"\n");
1258 printf(" selects all atoms that are in group 'protein' and not in group 'backbone'\n");
1261 printf("\npress Enter ");
1265 else if (std::strncmp(string, "del", 3) == 0)
1268 if (parse_int(&string, &sel_nr))
1270 while (string[0] == ' ')
1274 if (string[0] == '-')
1277 parse_int(&string, &sel_nr2);
1283 while (string[0] == ' ')
1287 if (string[0] == '\0')
1289 remove_group(sel_nr, sel_nr2, block, gn);
1293 printf("\nSyntax error: \"%s\"\n", string);
1297 else if (std::strncmp(string, "keep", 4) == 0)
1300 if (parse_int(&string, &sel_nr))
1302 remove_group(sel_nr + 1, block->nr - 1, block, gn);
1303 remove_group(0, sel_nr - 1, block, gn);
1306 else if (std::strncmp(string, "name", 4) == 0)
1309 if (parse_int(&string, &sel_nr))
1311 if ((sel_nr >= 0) && (sel_nr < block->nr))
1313 sscanf(string, "%s", gname);
1314 sfree((*gn)[sel_nr]);
1315 (*gn)[sel_nr] = gmx_strdup(gname);
1319 else if (std::strncmp(string, "case", 4) == 0)
1322 printf("Switched to case %s\n", bCase ? "sensitive" : "insensitive");
1324 else if (string[0] == 'v')
1326 bVerbose = !bVerbose;
1327 printf("Turned verbose %s\n", bVerbose ? "on" : "off");
1329 else if (string[0] == 'l')
1331 if (check_have_atoms(atoms, ostring))
1333 list_residues(atoms);
1336 else if (std::strncmp(string, "splitch", 7) == 0)
1339 if (check_have_atoms(atoms, ostring) && parse_int(&string, &sel_nr) && (sel_nr >= 0)
1340 && (sel_nr < block->nr))
1342 split_chain(atoms, x, sel_nr, block, gn);
1345 else if (std::strncmp(string, "splitres", 8) == 0)
1348 if (check_have_atoms(atoms, ostring) && parse_int(&string, &sel_nr) && (sel_nr >= 0)
1349 && (sel_nr < block->nr))
1351 split_group(atoms, sel_nr, block, gn, FALSE);
1354 else if (std::strncmp(string, "splitat", 7) == 0)
1357 if (check_have_atoms(atoms, ostring) && parse_int(&string, &sel_nr) && (sel_nr >= 0)
1358 && (sel_nr < block->nr))
1360 split_group(atoms, sel_nr, block, gn, TRUE);
1363 else if (string[0] == '\0')
1367 else if (string[0] != 'q')
1370 if (parse_entry(&string, natoms, atoms, block, gn, &nr, index, gname, entryNames))
1374 while (string[0] == ' ')
1381 if (string[0] == '&')
1385 else if (string[0] == '|')
1394 for (i = 0; i < nr; i++)
1396 index1[i] = index[i];
1398 std::strcpy(gname1, gname);
1399 if (parse_entry(&string, natoms, atoms, block, gn, &nr2, index2, gname2, entryNames))
1403 or_groups(nr1, index1, nr2, index2, &nr, index);
1404 sprintf(gname, "%s_%s", gname1, gname2);
1408 and_groups(nr1, index1, nr2, index2, &nr, index);
1409 sprintf(gname, "%s_&_%s", gname1, gname2);
1413 } while (bAnd || bOr);
1415 while (string[0] == ' ')
1421 printf("\nSyntax error: \"%s\"\n", string);
1425 copy2block(nr, index, block);
1426 srenew(*gn, block->nr);
1427 newgroup = block->nr - 1;
1428 (*gn)[newgroup] = gmx_strdup(gname);
1432 printf("Group is empty\n");
1435 } while (string[0] != 'q');
1437 for (auto& name : entryNames)
1446 static int block2natoms(t_blocka* block)
1451 for (i = 0; i < block->nra; i++)
1453 natoms = std::max(natoms, block->a[i]);
1460 static void merge_blocks(t_blocka* dest, t_blocka* source)
1464 /* count groups, srenew and fill */
1467 dest->nr += source->nr;
1468 srenew(dest->index, dest->nr + 1);
1469 for (i = 0; i < source->nr; i++)
1471 dest->index[i0 + i] = nra0 + source->index[i];
1473 /* count atoms, srenew and fill */
1474 dest->nra += source->nra;
1475 srenew(dest->a, dest->nra);
1476 for (i = 0; i < source->nra; i++)
1478 dest->a[nra0 + i] = source->a[i];
1481 /* terminate list */
1482 dest->index[dest->nr] = dest->nra;
1485 int gmx_make_ndx(int argc, char* argv[])
1487 const char* desc[] = { "Index groups are necessary for almost every GROMACS program.",
1488 "All these programs can generate default index groups. You ONLY",
1489 "have to use [THISMODULE] when you need SPECIAL index groups.",
1490 "There is a default index group for the whole system, 9 default",
1491 "index groups for proteins, and a default index group",
1492 "is generated for every other residue name.",
1494 "When no index file is supplied, also [THISMODULE] will generate the",
1496 "With the index editor you can select on atom, residue and chain names",
1498 "When a run input file is supplied you can also select on atom type.",
1499 "You can use boolean operations, you can split groups",
1500 "into chains, residues or atoms. You can delete and rename groups.",
1501 "Type 'h' in the editor for more details.",
1503 "The atom numbering in the editor and the index file starts at 1.",
1505 "The [TT]-twin[tt] switch duplicates all index groups with an offset of",
1506 "[TT]-natoms[tt], which is useful for Computational Electrophysiology",
1507 "double-layer membrane setups.",
1509 "See also [gmx-select] [TT]-on[tt], which provides an alternative way",
1510 "for constructing index groups. It covers nearly all of [THISMODULE]",
1511 "functionality, and in many cases much more." };
1513 static int natoms = 0;
1514 static gmx_bool bVerbose = FALSE;
1515 static gmx_bool bDuplicate = FALSE;
1516 t_pargs pa[] = { { "-natoms",
1520 "set number of atoms (default: read from coordinate or index file)" },
1525 "Duplicate all index groups with an offset of -natoms" },
1526 { "-verbose", FALSE, etBOOL, { &bVerbose }, "HIDDENVerbose output" } };
1527 #define NPA asize(pa)
1529 gmx_output_env_t* oenv;
1530 const char* stxfile;
1531 const char* ndxoutfile;
1538 t_blocka * block, *block2;
1539 char ** gnames, **gnames2;
1540 t_filenm fnm[] = { { efSTX, "-f", nullptr, ffOPTRD },
1541 { efNDX, "-n", nullptr, ffOPTRDMULT },
1542 { efNDX, "-o", nullptr, ffWRITE } };
1543 #define NFILE asize(fnm)
1545 if (!parse_common_args(&argc, argv, 0, NFILE, fnm, NPA, pa, asize(desc), desc, 0, nullptr, &oenv))
1550 stxfile = ftp2fn_null(efSTX, NFILE, fnm);
1551 gmx::ArrayRef<const std::string> ndxInFiles = opt2fnsIfOptionSet("-n", NFILE, fnm);
1552 ndxoutfile = opt2fn("-o", NFILE, fnm);
1553 bNatoms = opt2parg_bSet("-natoms", NPA, pa);
1555 if (!stxfile && ndxInFiles.empty())
1557 gmx_fatal(FARGS, "No input files (structure or index)");
1563 bool haveFullTopology = false;
1564 fprintf(stderr, "\nReading structure file\n");
1565 readConfAndTopology(stxfile, &haveFullTopology, &mtop, &pbcType, &x, &v, box);
1566 atoms = gmx_mtop_global_atoms(mtop);
1567 if (atoms.pdbinfo == nullptr)
1569 snew(atoms.pdbinfo, atoms.nr);
1579 /* read input file(s) */
1580 block = new_blocka();
1582 printf("Going to read %td old index file(s)\n", ndxInFiles.ssize());
1583 if (!ndxInFiles.empty())
1585 for (const std::string& ndxInFile : ndxInFiles)
1587 block2 = init_index(ndxInFile.c_str(), &gnames2);
1588 srenew(gnames, block->nr + block2->nr);
1589 for (j = 0; j < block2->nr; j++)
1591 gnames[block->nr + j] = gnames2[j];
1594 merge_blocks(block, block2);
1596 sfree(block2->index);
1597 /* done_block(block2); */
1604 analyse(&atoms, block, &gnames, FALSE, TRUE);
1609 natoms = block2natoms(block);
1610 printf("Counted atom numbers up to %d in index file\n", natoms);
1612 edit_index(natoms, &atoms, x, block, &gnames, bVerbose);
1614 write_index(ndxoutfile, block, gnames, bDuplicate, natoms);