3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
10 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12 * Copyright (c) 2001-2004, The GROMACS development team,
13 * check out http://www.gromacs.org for more information.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * If you want to redistribute modifications, please consider that
21 * scientific software is very special. Version control is crucial -
22 * bugs must be traceable. We will be happy to consider code for
23 * inclusion in the official distribution, but derived work must not
24 * be called official GROMACS. Details are found in the README & COPYING
25 * files - if they are missing, get the official version at www.gromacs.org.
27 * To help us fund GROMACS development, we humbly ask that you cite
28 * the papers on the package - you can find them in the top README file.
30 * For more info, check our website at http://www.gromacs.org
33 * Green Red Orange Magenta Azure Cyan Skyblue
52 #include "gmx_fatal.h"
63 void calc_rm_cm(int isize
, atom_id index
[], t_atoms
*atoms
, rvec x
[], rvec xcm
)
68 /* calculate and remove center of mass of reference structure */
71 for(i
=0; i
<isize
; i
++) {
72 m
= atoms
->atom
[index
[i
]].m
;
74 xcm
[d
] += m
*x
[index
[i
]][d
];
78 for(i
=0; i
<atoms
->nr
; i
++)
82 int build_res_index(int isize
, atom_id index
[], t_atom atom
[], int rindex
[])
87 rindex
[r
] = atom
[index
[0]].resind
;
89 for(i
=1; i
<isize
; i
++)
90 if (atom
[index
[i
]].resind
!= rindex
[r
-1]) {
91 rindex
[r
] = atom
[index
[i
]].resind
;
98 int find_res_end(int i
, int isize
, atom_id index
[], t_atoms
*atoms
)
102 rnr
= atoms
->atom
[index
[i
]].resind
;
103 while(i
<isize
&& atoms
->atom
[index
[i
]].resind
==rnr
)
108 int debug_strcmp(char s1
[], char s2
[])
110 if (debug
) fprintf(debug
," %s-%s",s1
,s2
);
111 return strcmp(s1
, s2
);
114 int find_next_match_atoms_in_res(int *i1
,int isize1
,atom_id index1
[],
115 int m1
,char **atnms1
[],
116 int *i2
,int isize2
,atom_id index2
[],
117 int m2
,char **atnms2
[])
119 int dx
, dy
, dmax
, cmp
;
124 dmax
= max(m1
-*i1
, m2
-*i2
);
125 for(dx
=0; dx
<dmax
&& cmp
!=0; dx
++) {
126 for(dy
=dx
; dy
<dmax
&& cmp
!=0; dy
++) {
128 if (debug
) fprintf(debug
, ".");
130 if ( *i1
+dx
<m1
&& *i2
+dy
<m2
) {
132 cmp
= debug_strcmp(*atnms1
[index1
[*i1
+dx
]],*atnms2
[index2
[*i2
+dy
]]);
133 if (debug
) fprintf(debug
,"(%d %d)", *i1
+dx
, *i2
+dy
);
135 if ( cmp
!=0 && *i1
+dy
<m1
&& *i2
+dx
<m2
) {
137 cmp
= debug_strcmp(*atnms1
[index1
[*i1
+dy
]],*atnms2
[index2
[*i2
+dx
]]);
138 if (debug
) fprintf(debug
,"(%d %d)", *i1
+dy
, *i2
+dx
);
143 /* apparently, dx and dy are incremented one more time
144 as the loops terminate: we correct this here */
148 if (debug
) fprintf(debug
, "{%d %d}", *i1
+bFW
?dx
:dy
, *i2
+bFW
?dy
:dx
);
161 static int find_next_match_res(int *rnr1
, int isize1
,
162 int index1
[], t_resinfo
*resinfo1
,
163 int *rnr2
, int isize2
,
164 int index2
[], t_resinfo
*resinfo2
)
166 int dx
, dy
, dmax
, cmp
, rr1
, rr2
;
167 bool bFW
=FALSE
,bFF
=FALSE
;
171 while(rr1
<isize1
&& *rnr1
!= index1
[rr1
])
174 while(rr2
<isize2
&& *rnr2
!= index2
[rr2
])
178 dmax
= max(isize1
-rr1
, isize2
-rr2
);
179 if (debug
) fprintf(debug
," R:%d-%d:%d-%d:%d ",
180 rr1
, isize1
, rr2
, isize2
, dmax
);
181 for(dx
=0; dx
<dmax
&& cmp
!=0; dx
++)
182 for(dy
=0; dy
<=dx
&& cmp
!=0; dy
++)
185 if ( rr1
+dx
<isize1
&& rr2
+dy
<isize2
) {
187 cmp
=debug_strcmp(*resinfo1
[index1
[rr1
+dx
]].name
,
188 *resinfo2
[index2
[rr2
+dy
]].name
);
189 if (debug
) fprintf(debug
,"(%d %d)", rr1
+dx
, rr2
+dy
);
191 if ( cmp
!=0 && rr1
+dy
<isize1
&& rr2
+dx
<isize2
) {
193 cmp
=debug_strcmp(*resinfo1
[index1
[rr1
+dy
]].name
,
194 *resinfo2
[index2
[rr2
+dx
]].name
);
195 if (debug
) fprintf(debug
,"(%d %d)", rr1
+dy
, rr2
+dx
);
197 if ( dx
!=0 && cmp
!=0 && rr1
+dx
<isize1
&& rr2
+dx
<isize2
) {
199 cmp
=debug_strcmp(*resinfo1
[index1
[rr1
+dx
]].name
,
200 *resinfo2
[index2
[rr2
+dx
]].name
);
201 if (debug
) fprintf(debug
,"(%d %d)", rr1
+dx
, rr2
+dx
);
205 /* apparently, dx and dy are incremented one more time
206 as the loops terminate: we correct this here */
209 /* if we skipped equal on both sides, only skip one residue
210 to allow for single mutations of residues... */
212 if (debug
) fprintf(debug
, "%d.%d.%dX%sX%s",dx
,rr1
,rr2
,
213 *resinfo1
[index1
[rr1
+1]].name
,
214 *resinfo2
[index2
[rr2
+1]].name
);
218 if (debug
) fprintf(debug
, "!");
237 int find_first_atom_in_res(int rnr
, int isize
, atom_id index
[], t_atom atom
[])
242 while(i
<isize
&& atom
[index
[i
]].resind
!=rnr
)
245 if (atom
[index
[i
]].resind
==rnr
)
251 void find_matching_names(int *isize1
, atom_id index1
[], t_atoms
*atoms1
,
252 int *isize2
, atom_id index2
[], t_atoms
*atoms2
)
254 int i
, i1
, i2
, ii1
, ii2
, m1
, m2
;
256 int r
,rnr1
, rnr2
, prnr1
, prnr2
;
258 int *rindex1
, *rindex2
;
259 char *resnm1
, *resnm2
, *atnm1
, *atnm2
;
260 char ***atnms1
,***atnms2
;
261 t_resinfo
*resinfo1
,*resinfo2
;
263 /* set some handy shortcuts */
264 resinfo1
= atoms1
->resinfo
;
265 atnms1
= atoms1
->atomname
;
266 resinfo2
= atoms2
->resinfo
;
267 atnms2
= atoms2
->atomname
;
269 /* build indexes of selected residues */
270 snew(rindex1
,atoms1
->nres
);
271 rsize1
=build_res_index(*isize1
, index1
, atoms1
->atom
, rindex1
);
272 snew(rindex2
,atoms2
->nres
);
273 rsize2
=build_res_index(*isize2
, index2
, atoms2
->atom
, rindex2
);
279 if (debug
) fprintf(debug
, "Find matching names: %d, %d\n", *isize1
, *isize2
);
280 while ( atcmp
==0 && i1
<*isize1
&& i2
<*isize2
) {
282 rnr1
= atoms1
->atom
[index1
[i1
]].resind
;
283 rnr2
= atoms2
->atom
[index2
[i2
]].resind
;
284 if (rnr1
!=prnr1
|| rnr2
!=prnr2
) {
285 if (debug
) fprintf(debug
, "R: %s%d %s%d\n",
286 *resinfo1
[rnr1
].name
,rnr1
,*resinfo2
[rnr2
].name
,rnr2
);
287 rescmp
=strcmp(*resinfo1
[rnr1
].name
, *resinfo2
[rnr2
].name
);
289 if (debug
) fprintf(debug
, "comparing %d %d", i1
, i2
);
290 atcmp
=debug_strcmp(*atnms1
[index1
[i1
]], *atnms2
[index2
[i2
]]);
293 if (atcmp
!=0) { /* no match -> find match within residues */
294 m1
= find_res_end(i1
, *isize1
, index1
, atoms1
);
295 m2
= find_res_end(i2
, *isize2
, index2
, atoms2
);
296 if (debug
) fprintf(debug
, " [%d<%d %d<%d]", i1
,m1
, i2
,m2
);
297 atcmp
= find_next_match_atoms_in_res(&i1
,*isize1
,index1
,m1
,atnms1
,
298 &i2
,*isize2
,index2
,m2
,atnms2
);
299 if (debug
) fprintf(debug
, " -> %d %d %s-%s", i1
, i2
,
300 *atnms1
[index1
[i1
]],*atnms2
[index2
[i2
]]);
303 if (atcmp
!=0) { /* still no match -> next residue(s) */
306 rescmp
= find_next_match_res(&rnr1
,rsize1
,rindex1
,resinfo1
,
307 &rnr2
,rsize2
,rindex2
,resinfo2
);
309 i1
= find_first_atom_in_res(rnr1
, *isize1
, index1
, atoms1
->atom
);
311 i2
= find_first_atom_in_res(rnr2
, *isize2
, index2
, atoms2
->atom
);
312 if (debug
) fprintf(debug
, " -> %s%d-%s%d %s%d-%s%d",
313 *resinfo1
[rnr1
].name
,rnr1
,
314 *atnms1
[index1
[i1
]],index1
[i1
],
315 *resinfo2
[rnr2
].name
,rnr2
,
316 *atnms2
[index2
[i2
]],index2
[i2
]);
317 m1
= find_res_end(i1
, *isize1
, index1
, atoms1
);
318 m2
= find_res_end(i2
, *isize2
, index2
, atoms2
);
319 if (debug
) fprintf(debug
, " [%d<%d %d<%d]", i1
,m1
, i2
,m2
);
320 atcmp
= find_next_match_atoms_in_res(&i1
,*isize1
,index1
,m1
,atnms1
,
321 &i2
,*isize2
,index2
,m2
,atnms2
);
322 if (debug
) fprintf(debug
, " -> %d %d %s-%s", i1
, i2
,
323 *atnms1
[index1
[i1
]],*atnms2
[index2
[i2
]]);
326 fprintf(debug
, "(%d %d): %d %d\n", ii1
, ii2
, atcmp
, rescmp
);
327 if (atcmp
==0) { /* if match -> store indices */
328 index1
[ii1
++]=index1
[i1
];
329 index2
[ii2
++]=index2
[i2
];
340 gmx_fatal(FARGS
, "DEATH HORROR: non-equal number of matching atoms!\n");
341 if ( ii1
==i1
&& ii2
==i2
)
342 printf("All atoms in index groups 1 and 2 match\n");
344 if ( i1
==i2
&& ii1
==ii2
)
345 printf("Both index groups modified from %d to %d atoms\n", i1
, ii1
);
348 printf("Index group 1 modified from %d to %d atoms\n",i1
, ii1
);
350 printf("Index group 2 modified from %d to %d atoms\n",i2
, ii2
);
358 int gmx_confrms(int argc
,char *argv
[])
360 const char *desc
[] = {
361 "g_confrms computes the root mean square deviation (RMSD) of two",
362 "structures after LSQ fitting the second structure on the first one.",
363 "The two structures do NOT need to have the same number of atoms,",
364 "only the two index groups used for the fit need to be identical.",
365 "With [TT]-name[tt] only matching atom names from the selected groups",
366 "will be used for the fit and RMSD calculation. This can be useful ",
367 "when comparing mutants of a protein."
369 "The superimposed structures are written to file. In a [TT].pdb[tt] file",
370 "the two structures will be written as separate models",
371 "(use [TT]rasmol -nmrpdb[tt]). Also in a [TT].pdb[tt] file, B-factors",
372 "calculated from the atomic MSD values can be written with [TT]-bfac[tt].",
374 static bool bOne
=FALSE
,bRmpbc
=FALSE
,bMW
=TRUE
,bName
=FALSE
,
375 bBfac
=FALSE
,bFit
=TRUE
,bLabel
=FALSE
;
378 { "-one", FALSE
, etBOOL
, {&bOne
}, "Only write the fitted structure to file" },
379 { "-mw", FALSE
, etBOOL
, {&bMW
}, "Mass-weighted fitting and RMSD" },
380 { "-pbc", FALSE
, etBOOL
, {&bRmpbc
}, "Try to make molecules whole again" },
381 { "-fit", FALSE
, etBOOL
, {&bFit
},
382 "Do least squares superposition of the target structure to the reference" },
383 { "-name", FALSE
,etBOOL
,{&bName
},
384 "Only compare matching atom names" },
385 { "-label",FALSE
,etBOOL
,{&bLabel
},
386 "Added chain labels A for first and B for second structure"},
387 { "-bfac", FALSE
,etBOOL
,{&bBfac
},
388 "Output B-factors from atomic MSD values" }
391 { efTPS
, "-f1", "conf1.gro", ffREAD
},
392 { efSTX
, "-f2", "conf2", ffREAD
},
393 { efSTO
, "-o", "fit.pdb", ffWRITE
},
394 { efNDX
, "-n1" , "fit1.ndx", ffOPTRD
},
395 { efNDX
, "-n2", "fit2.ndx", ffOPTRD
},
396 { efNDX
, "-no", "match.ndx", ffOPTWR
}
398 #define NFILE asize(fnm)
400 /* the two structure files */
401 const char *conf1file
, *conf2file
, *matchndxfile
, *outfile
;
403 char title1
[STRLEN
],title2
[STRLEN
],*name1
,*name2
;
404 t_topology
*top1
,*top2
;
406 t_atoms
*atoms1
,*atoms2
;
409 real
*w_rls
,mass
,totmass
;
410 rvec
*x1
,*v1
,*x2
,*v2
,*fit_x
;
418 /* center of mass calculation */
422 /* variables for fit */
423 char *groupnames1
,*groupnames2
;
425 atom_id
*index1
,*index2
;
426 real rms
,msd
,minmsd
,maxmsd
;
430 CopyRight(stderr
,argv
[0]);
431 parse_common_args(&argc
,argv
,PCA_BE_NICE
| PCA_CAN_VIEW
,
432 NFILE
,fnm
,asize(pa
),pa
,asize(desc
),desc
,0,NULL
,&oenv
);
433 matchndxfile
= opt2fn_null("-no",NFILE
,fnm
);
434 conf1file
= ftp2fn(efTPS
,NFILE
,fnm
);
435 conf2file
= ftp2fn(efSTX
,NFILE
,fnm
);
437 /* reading reference structure from first structure file */
438 fprintf(stderr
,"\nReading first structure file\n");
440 read_tps_conf(conf1file
,title1
,top1
,&ePBC1
,&x1
,&v1
,box1
,TRUE
);
441 atoms1
= &(top1
->atoms
);
442 fprintf(stderr
,"%s\nContaining %d atoms in %d residues\n",
443 title1
,atoms1
->nr
,atoms1
->nres
);
446 rm_gropbc(atoms1
,x1
,box1
);
448 fprintf(stderr
,"Select group from first structure\n");
449 get_index(atoms1
,opt2fn_null("-n1",NFILE
,fnm
),
450 1,&isize1
,&index1
,&groupnames1
);
453 if (bFit
&& (isize1
< 3))
454 gmx_fatal(FARGS
,"Need >= 3 points to fit!\n");
456 /* reading second structure file */
457 fprintf(stderr
,"\nReading second structure file\n");
459 read_tps_conf(conf2file
,title2
,top2
,&ePBC2
,&x2
,&v2
,box2
,TRUE
);
460 atoms2
= &(top2
->atoms
);
461 fprintf(stderr
,"%s\nContaining %d atoms in %d residues\n",
462 title2
,atoms2
->nr
,atoms2
->nres
);
465 rm_gropbc(atoms2
,x2
,box2
);
467 fprintf(stderr
,"Select group from second structure\n");
468 get_index(atoms2
,opt2fn_null("-n2",NFILE
,fnm
),
469 1,&isize2
,&index2
,&groupnames2
);
472 find_matching_names(&isize1
, index1
, atoms1
, &isize2
, index2
, atoms2
);
474 fp
= ffopen(matchndxfile
,"w");
475 fprintf(fp
, "; Matching atoms between %s from %s and %s from %s\n",
476 groupnames1
, conf1file
, groupnames2
, conf2file
);
477 fprintf(fp
, "[ Match_%s_%s ]\n", conf1file
, groupnames1
);
478 for(i
=0; i
<isize1
; i
++)
479 fprintf(fp
, "%4u%s", index1
[i
]+1, (i
%15==14 || i
==isize1
-1)?"\n":" ");
480 fprintf(fp
, "[ Match_%s_%s ]\n", conf2file
, groupnames2
);
481 for(i
=0; i
<isize2
; i
++)
482 fprintf(fp
, "%4u%s", index2
[i
]+1, (i
%15==14 || i
==isize2
-1)?"\n":" ");
486 /* check isizes, must be equal */
487 if ( isize2
!= isize1
)
488 gmx_fatal(FARGS
,"You selected groups with differen number of atoms.\n");
490 for(i
=0; i
<isize1
; i
++) {
491 name1
=*atoms1
->atomname
[index1
[i
]];
492 name2
=*atoms2
->atomname
[index2
[i
]];
493 if (strcmp(name1
,name2
)) {
496 "Warning: atomnames at index %d don't match: %u %s, %u %s\n",
497 i
+1,index1
[i
]+1,name1
,index2
[i
]+1,name2
);
501 atoms1
->atom
[index1
[i
]].m
= 1;
502 atoms2
->atom
[index2
[i
]].m
= 1;
506 fprintf(stderr
,"%d atomname%s did not match\n",warn
,(warn
==1) ? "":"s");
509 /* calculate and remove center of mass of structures */
510 calc_rm_cm(isize1
, index1
, atoms1
, x1
, xcm1
);
511 calc_rm_cm(isize2
, index2
, atoms2
, x2
, xcm2
);
513 snew(w_rls
,atoms2
->nr
);
514 snew(fit_x
,atoms2
->nr
);
515 for(at
=0; (at
<isize1
); at
++) {
516 w_rls
[index2
[at
]] = atoms1
->atom
[index1
[at
]].m
;
517 copy_rvec(x1
[index1
[at
]],fit_x
[index2
[at
]]);
520 /* do the least squares fit to the reference structure */
521 do_fit(atoms2
->nr
,w_rls
,fit_x
,x2
);
533 /* calculate the rms deviation */
539 for(at
=0; at
<isize1
; at
++) {
540 mass
= atoms1
->atom
[index1
[at
]].m
;
541 for(m
=0; m
<DIM
; m
++) {
542 msd
= sqr(x1
[index1
[at
]][m
] - x2
[index2
[at
]][m
]);
546 maxmsd
= max(maxmsd
, msds
[at
]);
547 minmsd
= min(minmsd
, msds
[at
]);
550 rms
= sqrt(rms
/totmass
);
552 printf("Root mean square deviation after lsq fit = %g\n",rms
);
554 printf("Atomic MSD's range from %g to %g\n",minmsd
, maxmsd
);
557 /* reset coordinates of reference and fitted structure */
558 for(i
=0; i
<atoms1
->nr
; i
++)
561 for(i
=0; i
<atoms2
->nr
; i
++)
566 outfile
= ftp2fn(efSTO
,NFILE
,fnm
);
567 switch (fn2ftp(outfile
)) {
571 if (bBfac
|| bLabel
) {
572 snew(atoms1
->pdbinfo
, atoms1
->nr
);
573 snew(atoms1
->atom
, atoms1
->nr
);
574 for(i
=0; i
<isize1
; i
++) {
575 atoms1
->pdbinfo
[index1
[i
]].type
= eptAtom
;
576 atoms1
->pdbinfo
[index1
[i
]].bAnisotropic
= FALSE
;
578 atoms1
->pdbinfo
[index1
[i
]].bfac
= 800*M_PI
*M_PI
/3.0*msds
[i
]/100;
580 atoms1
->resinfo
[atoms1
->atom
[index1
[i
]].resind
].chain
= 'A';
582 snew(atoms2
->pdbinfo
, atoms2
->nr
);
583 snew(atoms2
->atom
, atoms2
->nr
);
584 for(i
=0; i
<isize2
; i
++) {
585 atoms2
->pdbinfo
[index2
[i
]].type
= eptAtom
;
586 atoms2
->pdbinfo
[index2
[i
]].bAnisotropic
= FALSE
;
588 atoms2
->pdbinfo
[index2
[i
]].bfac
= 800*M_PI
*M_PI
/3.0*msds
[i
]/100;
590 atoms2
->resinfo
[atoms2
->atom
[index2
[i
]].resind
].chain
= 'B';
593 fp
=ffopen(outfile
,"w");
595 write_pdbfile(fp
,title1
,atoms1
,x1
,ePBC1
,box1
,0,1,NULL
);
596 write_pdbfile(fp
,title2
,atoms2
,x2
,ePBC2
,box2
,0,bOne
? -1 : 2,NULL
);
601 fprintf(stderr
,"WARNING: cannot write B-factor values to gro file\n");
602 fp
=ffopen(outfile
,"w");
604 write_hconf_p(fp
,title1
,atoms1
,3,x1
,v1
,box1
);
605 write_hconf_p(fp
,title2
,atoms2
,3,x2
,v2
,box2
);
610 fprintf(stderr
,"WARNING: cannot write B-factor values to %s file\n",
611 ftp2ext(fn2ftp(outfile
)));
614 "WARNING: cannot write the reference structure to %s file\n",
615 ftp2ext(fn2ftp(outfile
)));
616 write_sto_conf(outfile
,title2
,atoms2
,x2
,v2
,ePBC2
,box2
);
620 view_all(oenv
,NFILE
, fnm
);