4 * This source code is part of
8 * GROningen MAchine for Chemical Simulations
12 * Copyright (c) 1991-1999
13 * BIOSON Research Institute, Dept. of Biophysical Chemistry
14 * University of Groningen, The Netherlands
17 * GROMACS: A message-passing parallel molecular dynamics implementation
18 * H.J.C. Berendsen, D. van der Spoel and R. van Drunen
19 * Comp. Phys. Comm. 91, 43-56 (1995)
21 * Also check out our WWW page:
22 * http://md.chem.rug.nl/~gmx
27 * Great Red Oystrich Makes All Chemists Sane
29 static char *SRCID_make_ndx_c
= "$Id$";
51 static int or_groups(atom_id nr1
,atom_id
*at1
,atom_id nr2
,atom_id
*at2
,
52 atom_id
*nr
,atom_id
*at
)
60 for(i1
=0; i1
<nr1
; i1
++) {
61 if ((i1
>0) && (at1
[i1
] <= max
))
65 for(i1
=0; i1
<nr2
; i1
++) {
66 if ((i1
>0) && (at2
[i1
] <= max
))
72 printf("One of your groups is not ascending\n");
77 while ((i1
< nr1
) || (i2
< nr2
)) {
78 if ((i2
== nr2
) || ((i1
<nr1
) && (at1
[i1
] < at2
[i2
]))) {
84 if ((i2
<nr2
) && ((i1
==nr1
) || (at1
[i1
] > at2
[i2
]))) {
92 printf("Merged two groups with OR: %u %u -> %u\n",nr1
,nr2
,*nr
);
98 static int and_groups(atom_id nr1
,atom_id
*at1
,atom_id nr2
,atom_id
*at2
,
99 atom_id
*nr
,atom_id
*at
)
104 for (i1
=0; i1
<nr1
; i1
++) {
105 for (i2
=0; i2
<nr2
; i2
++) {
106 if (at1
[i1
]==at2
[i2
]) {
113 printf("Merged two groups with AND: %u %u -> %u\n",nr1
,nr2
,*nr
);
118 static bool isalnum_star(char c
)
120 return (isalnum(c
) || (c
=='*'));
123 static int parse_names(char **string
,int *n_names
,char **names
)
128 while ((isalnum_star((*string
)[0]) || ((*string
)[0]==' '))) {
129 if (isalnum_star((*string
)[0])) {
130 if (*n_names
>= MAXNAMES
)
131 fatal_error(0,"To many names: %d\n",*n_names
+1);
133 while (isalnum_star((*string
)[i
])) {
134 names
[*n_names
][i
]=(*string
)[i
];
137 printf("Name to long: %d characters\n",i
);
141 names
[*n_names
][i
]='\0';
143 upstring(names
[*n_names
]);
154 static bool parse_int(char **string
,int *nr
)
158 while ((*string
)[0]==' ')
163 if (isdigit((*string
)[0])) {
164 *nr
=(*string
)[0]-'0';
166 while (isdigit((*string
)[0])) {
167 *nr
= (*nr
)*10+(*string
)[0]-'0';
178 static int select_atomnumbers(char **string
,t_atoms
*atoms
,atom_id n1
,
179 atom_id
*nr
,atom_id
*index
,char *gname
)
185 while ((*string
)[0]==' ')
187 if ((*string
)[0]=='-') {
189 parse_int(string
,&up
);
190 if ((n1
<1) || (n1
>atoms
->nr
) || (up
<1) || (up
>atoms
->nr
))
191 printf("Invalid atom range\n");
193 for(i
=n1
-1; i
<=up
-1; i
++) {
197 printf("Found %u atom%s in range %u-%d\n",*nr
,(*nr
==1)?"":"s",n1
,up
);
199 sprintf(buf
,"a_%u",n1
);
201 sprintf(buf
,"a_%u-%d",n1
,up
);
209 if ((i
-1>=0) && (i
-1<atoms
->nr
)) {
212 sprintf(buf
,"_%d",i
);
215 printf("Invalid atom number %d\n",i
);
218 } while ((*nr
!=0) && (parse_int(string
,&i
)));
224 static int select_residuenumbers(char **string
,t_atoms
*atoms
,atom_id n1
,
225 atom_id
*nr
,atom_id
*index
,char *gname
)
232 while ((*string
)[0]==' ')
234 if ((*string
)[0]=='-') {
236 parse_int(string
,&up
);
238 for(i
=0; i
<atoms
->nr
; i
++) {
239 resnr
=atoms
->atom
[i
].resnr
;
240 for(j
=n1
; (j
<=up
); j
++) {
247 printf("Found %u atom%s with res.nr. in range %u-%d\n",
248 *nr
,(*nr
==1)?"":"s",n1
,up
);
250 sprintf(buf
,"r_%u",n1
);
252 sprintf(buf
,"r_%u-%d",n1
,up
);
259 for(i
=0; i
<atoms
->nr
; i
++) {
260 if (atoms
->atom
[i
].resnr
==j
-1) {
265 sprintf(buf
,"_%d",j
);
267 } while (parse_int(string
,&j
));
273 static bool comp_name(char *name
,char *search
)
279 return (((search
[n
]!='*') &&
280 (bCase
? strcmp(name
,search
) : strcasecmp(name
,search
))) ||
282 (bCase
? strncmp(name
,search
,n
) : strncasecmp(name
,search
,n
))));
285 static int select_chainnames(t_atoms
*atoms
,int n_names
,char **names
,
286 atom_id
*nr
,atom_id
*index
)
294 for(i
=0; i
<atoms
->nr
; i
++) {
295 name
[0]=atoms
->atom
[i
].chain
;
297 while (j
<n_names
&& comp_name(name
,names
[j
]))
304 printf("Found %u atom%s with chain identifier%s",
305 *nr
,(*nr
==1)?"":"s",(n_names
==1)?"":"s");
306 for(j
=0; (j
<n_names
); j
++)
307 printf(" %s",names
[j
]);
313 static int select_atomnames(t_atoms
*atoms
,int n_names
,char **names
,
314 atom_id
*nr
,atom_id
*index
)
321 for(i
=0; i
<atoms
->nr
; i
++) {
322 name
=*(atoms
->atomname
[i
]);
324 while (j
<n_names
&& comp_name(name
,names
[j
]))
331 printf("Found %u atoms with name%s",*nr
,(n_names
==1)?"":"s");
332 for(j
=0; (j
<n_names
); j
++)
333 printf(" %s",names
[j
]);
339 static int select_residuenames(t_atoms
*atoms
,int n_names
,char **names
,
340 atom_id
*nr
,atom_id
*index
)
347 for(i
=0; i
<atoms
->nr
; i
++) {
348 name
=*(atoms
->resname
[atoms
->atom
[i
].resnr
]);
350 while (j
<n_names
&& comp_name(name
,names
[j
]))
357 printf("Found %u atoms with residue name%s",*nr
,(n_names
==1)?"":"s");
358 for(j
=0; (j
<n_names
); j
++)
359 printf(" %s",names
[j
]);
365 static void copy2block(int n
,atom_id
*index
,t_block
*block
)
372 srenew(block
->index
,block
->nr
+1);
373 block
->index
[block
->nr
]=n0
+n
;
374 srenew(block
->a
,n0
+n
);
376 block
->a
[n0
+i
]=index
[i
];
379 static void make_gname(int n
,char **names
,char *gname
)
383 strcpy(gname
,names
[0]);
384 for (i
=1; i
<n
; i
++) {
386 strcat(gname
,names
[i
]);
390 static void copy_group(int g
,t_block
*block
,atom_id
*nr
,atom_id
*index
)
395 *nr
=block
->index
[g
+1]-i0
;
396 for (i
=0; i
<=*nr
; i
++)
397 index
[i
]=block
->a
[i0
+i
];
400 static void remove_group(int nr
,int nr2
,t_block
*block
,char ***gn
)
407 for(j
=0; j
<=nr2
-nr
; j
++) {
408 if ((nr
<0) || (nr
>=block
->nr
))
409 printf("Group %d does not exist\n",nr
+j
);
411 shift
=block
->index
[nr
+1]-block
->index
[nr
];
412 for(i
=block
->index
[nr
+1]; i
<block
->nra
; i
++)
413 block
->a
[i
-shift
]=block
->a
[i
];
415 for(i
=nr
; i
<block
->nr
; i
++)
416 block
->index
[i
]=block
->index
[i
+1]-shift
;
418 for(i
=nr
; i
<block
->nr
-1; i
++) {
422 block
->nra
=block
->index
[block
->nr
];
423 printf("Removed group %d\n",nr
+j
);
428 static void split_group(t_atoms
*atoms
,int sel_nr
,t_block
*block
,char ***gn
,
431 char buf
[STRLEN
],*name
;
436 printf("Splitting group %d into residues\n",sel_nr
);
438 printf("Splitting group %d into atoms\n",sel_nr
);
441 n0
=block
->index
[sel_nr
];
442 n1
=block
->index
[sel_nr
+1];
443 for (i
=n0
; i
<n1
; i
++) {
445 nr
=atoms
->atom
[a
].resnr
;
446 name
=*(atoms
->resname
[nr
]);
447 if (bAtom
|| (i
==n0
) || (atoms
->atom
[block
->a
[i
-1]].resnr
!=nr
)) {
449 block
->index
[block
->nr
]=block
->nra
;
451 srenew(block
->index
,block
->nr
+1);
452 srenew(*gn
,block
->nr
);
454 sprintf(buf
,"%s_%s_%u",(*gn
)[sel_nr
],*atoms
->atomname
[a
],a
+1);
456 sprintf(buf
,"%s_%s_%d",(*gn
)[sel_nr
],name
,nr
+1);
457 (*gn
)[block
->nr
-1]=strdup(buf
);
459 if (block
->nra
== max
) {
461 srenew(block
->a
,max
);
463 block
->a
[block
->nra
]=a
;
466 block
->index
[block
->nr
]=block
->nra
;
469 static int split_chain(t_atoms
*atoms
,rvec
*x
,
470 int sel_nr
,t_block
*block
,char ***gn
)
474 atom_id i
,a
,max
,natoms
,*start
=NULL
,*end
=NULL
,ca_start
,ca_end
;
481 while (ca_start
<natoms
) {
482 while((ca_start
<natoms
) && strcmp(*atoms
->atomname
[ca_start
],"CA"))
484 if (ca_start
<natoms
) {
485 srenew(start
,nchain
+1);
486 srenew(end
,nchain
+1);
487 start
[nchain
]=ca_start
;
488 while ((start
[nchain
]>0) &&
489 (atoms
->atom
[start
[nchain
]-1].resnr
==atoms
->atom
[ca_start
].resnr
))
497 } while ((i
<natoms
) && strcmp(*atoms
->atomname
[i
],"CA"));
499 rvec_sub(x
[ca_end
],x
[i
],vec
);
500 } while ((i
<natoms
) && (norm(vec
)<0.45));
503 while ((end
[nchain
]+1<natoms
) &&
504 (atoms
->atom
[end
[nchain
]+1].resnr
==atoms
->atom
[ca_end
].resnr
))
506 ca_start
=end
[nchain
]+1;
511 printf("Found 1 chain, will not split\n");
513 printf("Found %d chains\n",nchain
);
514 for (j
=0; j
<nchain
; j
++)
515 printf("%d:%6u atoms (%u to %u)\n",
516 j
+1,end
[j
]-start
[j
]+1,start
[j
]+1,end
[j
]+1);
519 for (j
=0; j
<nchain
; j
++) {
521 srenew(block
->index
,block
->nr
+1);
522 srenew(*gn
,block
->nr
);
523 sprintf(buf
,"%s_chain%d",(*gn
)[sel_nr
],j
+1);
524 (*gn
)[block
->nr
-1]=strdup(buf
);
526 for (i
=block
->index
[sel_nr
]; i
<block
->index
[sel_nr
+1]; i
++) {
528 if ((a
>=start
[j
]) && (a
<=end
[j
])) {
529 if (block
->nra
==max
) {
531 srenew(block
->a
,max
);
533 block
->a
[block
->nra
]=a
;
537 block
->index
[block
->nr
]=block
->nra
;
538 if (block
->index
[block
->nr
-1]==block
->index
[block
->nr
])
539 remove_group(block
->nr
-1,NOTSET
,block
,gn
);
548 static bool parse_entry(char **string
,t_atoms
*atoms
,
549 t_block
*block
,char ***gn
,
550 atom_id
*nr
,atom_id
*index
,char *gname
)
553 static int namelen
=5;
554 static bool bFirst
=TRUE
;
555 int j
,n_names
,sel_nr1
;
556 atom_id i
,nr1
,*index1
;
561 snew(names
,MAXNAMES
);
562 for (i
=0; i
<MAXNAMES
; i
++)
563 snew(names
[i
],namelen
);
569 while(*string
[0]==' ')
572 if ((*string
)[0]=='!') {
575 while(*string
[0]==' ')
580 if (parse_int(string
,&sel_nr1
)) {
581 if ((sel_nr1
>=0) && (sel_nr1
<block
->nr
)) {
582 copy_group(sel_nr1
,block
,nr
,index
);
583 strcpy(gname
,(*gn
)[sel_nr1
]);
584 printf("Copied index group %d\n",sel_nr1
);
587 printf("Group %d does not exist\n",sel_nr1
);
589 else if ((*string
)[0]=='a') {
591 if (parse_int(string
,&sel_nr1
)) {
592 bRet
=select_atomnumbers(string
,atoms
,sel_nr1
,nr
,index
,gname
);
594 else if (parse_names(string
,&n_names
,names
)) {
595 bRet
=select_atomnames(atoms
,n_names
,names
,nr
,index
);
596 make_gname(n_names
,names
,gname
);
599 else if ((*string
)[0]=='r') {
601 if (parse_int(string
,&sel_nr1
)) {
602 bRet
=select_residuenumbers(string
,atoms
,sel_nr1
,nr
,index
,gname
);
604 else if (parse_names(string
,&n_names
,names
)) {
605 bRet
=select_residuenames(atoms
,n_names
,names
,nr
,index
);
606 make_gname(n_names
,names
,gname
);
609 else if (!strncmp(*string
,"chain",5)) {
611 if (parse_names(string
,&n_names
,names
)) {
612 bRet
=select_chainnames(atoms
,n_names
,names
,nr
,index
);
613 sprintf(gname
,"ch%s",names
[0]);
614 for (i
=1; i
<n_names
; i
++)
615 strcat(gname
,names
[i
]);
618 if (bRet
&& bCompl
) {
619 snew(index1
,atoms
->nr
-*nr
);
621 for(i
=0; i
<atoms
->nr
; i
++) {
623 while ((j
<*nr
) && (index
[j
] != i
))
626 if (nr1
>= atoms
->nr
-*nr
) {
627 printf("There are double atoms in your index group\n");
639 for (i
=strlen(gname
); i
>0; i
--)
642 printf("Complemented group: %u atoms\n",*nr
);
648 static void list_residues(t_atoms
*atoms
)
650 int i
,j
,start
,end
,prev_resnr
,resnr
;
653 /* Print all the residues, assuming continuous resnr count */
654 start
= atoms
->atom
[0].resnr
;
656 for(i
=0; i
<atoms
->nr
; i
++) {
657 resnr
= atoms
->atom
[i
].resnr
;
658 if ((resnr
!= prev_resnr
) || (i
==atoms
->nr
-1)) {
659 if ((bDiff
=strcmp(*atoms
->resname
[resnr
],*atoms
->resname
[start
])) ||
666 for(j
=start
; j
<=end
; j
++)
667 printf("%4d %-5s",j
+1,*(atoms
->resname
[j
]));
669 printf(" %4d - %4d %-5s ",start
+1,end
+1,*(atoms
->resname
[start
]));
678 static void edit_index(t_atoms
*atoms
,rvec
*x
,t_block
*block
, char ***gn
)
680 static char **atnames
;
681 static bool bFirst
=TRUE
;
682 static int namelen
=5;
683 char inp_string
[STRLEN
],*string
,*atom_name
;
684 char gname
[STRLEN
],gname1
[STRLEN
],gname2
[STRLEN
];
685 int i
,sel_nr
,sel_nr2
;
686 atom_id nr
,nr1
,nr2
,*index
,*index1
,*index2
;
687 bool bCompl
,bAnd
,bOr
;
691 snew(atnames
,MAXNAMES
);
692 for (i
=0; i
<MAXNAMES
; i
++)
693 snew(atnames
[i
],namelen
);
698 snew(index
,atoms
->nr
);
699 snew(index1
,atoms
->nr
);
700 snew(index2
,atoms
->nr
);
705 for(i
=0; (i
<block
->nr
); i
++)
706 printf("%3d %-20s: %5u atoms\n",i
,(*gn
)[i
],
707 block
->index
[i
+1]-block
->index
[i
]);
710 printf(" nr : group ! 'name' nr name 'splitch' nr 'l': list residues\n");
711 printf(" 'a': atom & 'del' nr 'splitres' nr 'h': help\n");
712 printf(" 'r': residue | 'keep' nr 'splitat' nr\n");
713 printf(" 'chain' char 'case': %s 'q': save and quit\n",
714 bCase
? "case insensitive" : "case sensitive ");
716 fgets(inp_string
,STRLEN
,stdin
);
717 inp_string
[strlen(inp_string
)-1]=0;
720 while (string
[0]==' ')
724 if (string
[0] == 'h') {
725 printf(" nr : selects an index group.\n");
726 printf(" 'a' nr1 [nr2 ...] : selects atoms, atom numbering starts at 1.\n");
727 printf(" 'a' nr1 - nr2 : selects atoms in the range from nr1 to nr2.\n");
728 printf(" 'a' name1[*] [name2[*] ...] : selects atoms by name(s), wildcard allowed\n");
729 printf(" at the end of a name.\n");
730 printf(" 'r' : analogous to 'a', but for residues.\n");
731 printf(" 'chain' ch1 [ch2 ...] : selects atoms by chain identifier(s),\n");
732 printf(" not available with a .gro file as input.\n");
733 printf(" ! : takes the complement of a group with respect to all\n");
734 printf(" the atoms in the input file.\n");
735 printf(" & | : AND and OR, can be placed between any of the options\n");
736 printf(" above, the input is processed from left to right.\n");
737 printf(" 'name' nr name : rename group nr to name.\n");
738 printf(" 'del' nr1 [- nr2] : deletes one group or groups in the range from nr1 to nr2.\n");
739 printf(" 'keep' nr : deletes all groups except nr.\n");
740 printf(" 'case' : make all name compares case (in)sensitive.\n");
741 printf(" 'splitch' nr : split group into chains using CA distances.\n");
742 printf(" 'splitres' nr : split group into residues.\n");
743 printf(" 'splitat' nr : split group into atoms.\n");
744 printf(" 'l' : list the residues.\n");
745 printf(" 'h' : show this help.\n");
746 printf(" 'q' : save and quit.\n");
748 printf(" Examples:\n");
749 printf(" > 2 | 4 & r 3-5\n");
750 printf(" will select all atoms from group 2 and 4 which have residue numbers\n 3, 4 or 5\n");
751 printf(" > a C* & !a C CA\n");
752 printf(" will select all atoms starting with 'C' but not the atoms 'C' and 'CA'\n");
753 printf("\npress Enter");
756 else if (!strncmp(string
,"del",3)) {
758 if (parse_int(&string
,&sel_nr
)) {
759 while(string
[0]==' ')
761 if (string
[0]=='-') {
763 parse_int(&string
,&sel_nr2
);
766 while(string
[0]==' ')
769 remove_group(sel_nr
,sel_nr2
,block
,gn
);
771 printf("\nSyntax error: \"%s\"\n",string
);
773 } else if (!strncmp(string
,"keep",4)) {
775 if (parse_int(&string
,&sel_nr
)) {
776 remove_group(sel_nr
+1,block
->nr
-1,block
,gn
);
777 remove_group(0,sel_nr
-1,block
,gn
);
779 } else if (!strncmp(string
,"name",4)) {
781 if (parse_int(&string
,&sel_nr
)) {
782 if ((sel_nr
>=0) && (sel_nr
<block
->nr
)) {
783 sscanf(string
,"%s",gname
);
784 sfree((*gn
)[sel_nr
]);
785 (*gn
)[sel_nr
]=strdup(gname
);
788 } else if (!strncmp(string
,"case",4)) {
790 } else if (string
[0] == 'l')
791 list_residues(atoms
);
792 else if (!strncmp(string
,"splitch",7)) {
794 if (parse_int(&string
,&sel_nr
))
795 if ((sel_nr
>=0) && (sel_nr
<block
->nr
))
796 split_chain(atoms
,x
,sel_nr
,block
,gn
);
797 } else if (!strncmp(string
,"splitres",8)) {
799 if (parse_int(&string
,&sel_nr
))
800 if ((sel_nr
>=0) && (sel_nr
<block
->nr
))
801 split_group(atoms
,sel_nr
,block
,gn
,FALSE
);
802 } else if (!strncmp(string
,"splitat",7)) {
804 if (parse_int(&string
,&sel_nr
))
805 if ((sel_nr
>=0) && (sel_nr
<block
->nr
))
806 split_group(atoms
,sel_nr
,block
,gn
,TRUE
);
807 } else if (string
[0] != 'q') {
810 if (parse_entry(&string
,atoms
,block
,gn
,&nr
,index
,gname
)) {
812 while (string
[0]==' ')
819 else if (string
[0]=='|')
827 strcpy(gname1
,gname
);
828 if (parse_entry(&string
,atoms
,block
,gn
,&nr2
,index2
,gname2
)) {
830 or_groups(nr1
,index1
,nr2
,index2
,&nr
,index
);
831 sprintf(gname
,"%s_%s",gname1
,gname2
);
834 and_groups(nr1
,index1
,nr2
,index2
,&nr
,index
);
835 sprintf(gname
,"%s_&_%s",gname1
,gname2
);
839 } while (bAnd
|| bOr
);
841 while(string
[0]==' ')
844 printf("\nSyntax error: \"%s\"\n",string
);
846 copy2block(nr
,index
,block
);
847 srenew(*gn
,block
->nr
);
848 (*gn
)[block
->nr
-1]=strdup(gname
);
851 printf("Group is empty\n");
853 } while (string
[0]!='q');
860 int main(int argc
,char *argv
[])
862 static char *desc
[] = {
863 "Index groups are necessary for almost every gromacs program.",
864 "All these programs can generate default index groups. You ONLY",
865 "have to use make_ndx when you need SPECIAL index groups.",
866 "There is a default index group for the whole system, 9 default",
867 "index groups are generated for proteins, a default index group",
868 "is generated for every other residue name.[PAR]"
869 "When no index file is supplied, also make_ndx will generate the",
871 "With the index editor you can select on atom, residue and chain names",
872 "and numbers, you can use NOT, AND and OR, you can split groups",
873 "into chains, residues or atoms. You can delete and rename groups.[PAR]",
874 "The atom numbering in the editor and the index file starts at 1."
884 { efSTX
, "-f", NULL
, ffREAD
},
885 { efNDX
, "-n", "in", ffOPTRD
},
886 { efNDX
, "-o", NULL
, ffWRITE
}
888 #define NFILE asize(fnm)
890 CopyRight(stdout
,argv
[0]);
892 parse_common_args(&argc
,argv
,0,FALSE
,NFILE
,fnm
,0,NULL
,asize(desc
),
895 get_stx_coordnum(ftp2fn(efSTX
,NFILE
,fnm
),&(atoms
.nr
));
896 init_t_atoms(&atoms
,atoms
.nr
,TRUE
);
899 fprintf(stderr
,"\nReading structure file\n");
900 read_stx_conf(ftp2fn(efSTX
,NFILE
,fnm
),title
,&atoms
,x
,v
,box
);
902 if (opt2bSet("-n",NFILE
,fnm
))
903 block
= init_index(opt2fn("-n",NFILE
,fnm
),&gnames
);
907 analyse(&atoms
,block
,&gnames
,FALSE
,TRUE
);
910 edit_index(&atoms
,x
,block
,&gnames
);
912 write_index(opt2fn("-o",NFILE
,fnm
),block
,gnames
);