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
61 static void calc_dist(int nind
,atom_id index
[],rvec x
[],int ePBC
,matrix box
,
70 set_pbc(&pbc
,ePBC
,box
);
71 for(i
=0; (i
<nind
-1); i
++) {
73 for(j
=i
+1; (j
<nind
); j
++) {
74 pbc_dx(&pbc
,xi
,x
[index
[j
]],dx
);
81 static void calc_dist_tot(int nind
,atom_id index
[],rvec x
[],
83 real
**d
, real
**dtot
, real
**dtot2
,
84 gmx_bool bNMR
, real
**dtot1_3
, real
**dtot1_6
)
88 real temp
, temp2
, temp1_3
;
92 set_pbc(&pbc
,ePBC
,box
);
93 for(i
=0; (i
<nind
-1); i
++) {
95 for(j
=i
+1; (j
<nind
); j
++) {
96 pbc_dx(&pbc
,xi
,x
[index
[j
]],dx
);
103 temp1_3
= 1.0/(temp
*temp2
);
104 dtot1_3
[i
][j
] += temp1_3
;
105 dtot1_6
[i
][j
] += temp1_3
*temp1_3
;
111 static void calc_nmr(int nind
, int nframes
, real
**dtot1_3
, real
**dtot1_6
,
112 real
*max1_3
, real
*max1_6
)
115 real temp1_3
,temp1_6
;
117 for(i
=0; (i
<nind
-1); i
++) {
118 for(j
=i
+1; (j
<nind
); j
++) {
119 temp1_3
= pow(dtot1_3
[i
][j
]/nframes
,-1.0/3.0);
120 temp1_6
= pow(dtot1_6
[i
][j
]/nframes
,-1.0/6.0);
121 if (temp1_3
> *max1_3
) *max1_3
= temp1_3
;
122 if (temp1_6
> *max1_6
) *max1_6
= temp1_6
;
123 dtot1_3
[i
][j
] = temp1_3
;
124 dtot1_6
[i
][j
] = temp1_6
;
125 dtot1_3
[j
][i
] = temp1_3
;
126 dtot1_6
[j
][i
] = temp1_6
;
131 static char Hnum
[] = "123";
156 static int read_equiv(const char *eq_fn
, t_equiv
***equivptr
)
159 char line
[STRLEN
],resname
[10],atomname
[10],*lp
;
160 int neq
, na
, n
, resnr
;
163 fp
= ffopen(eq_fn
,"r");
166 while(get_a_line(fp
,line
,STRLEN
)) {
168 /* this is not efficient, but I'm lazy */
172 if (sscanf(lp
,"%s %n",atomname
,&n
)==1) {
175 equiv
[neq
][0].nname
=strdup(atomname
);
176 while (sscanf(lp
,"%d %s %s %n",&resnr
,resname
,atomname
,&n
)==3) {
177 /* this is not efficient, but I'm lazy (again) */
178 srenew(equiv
[neq
], na
+1);
179 equiv
[neq
][na
].rnr
=resnr
-1;
180 equiv
[neq
][na
].rname
=strdup(resname
);
181 equiv
[neq
][na
].aname
=strdup(atomname
);
182 if (na
>0) equiv
[neq
][na
].nname
=NULL
;
187 /* make empty element as flag for end of array */
188 srenew(equiv
[neq
], na
+1);
189 equiv
[neq
][na
].rnr
=NOTSET
;
190 equiv
[neq
][na
].rname
=NULL
;
191 equiv
[neq
][na
].aname
=NULL
;
203 static void dump_equiv(FILE *out
, int neq
, t_equiv
**equiv
)
207 fprintf(out
,"Dumping equivalent list\n");
208 for (i
=0; i
<neq
; i
++) {
209 fprintf(out
,"%s",equiv
[i
][0].nname
);
210 for(j
=0; equiv
[i
][j
].rnr
!=NOTSET
; j
++)
211 fprintf(out
," %d %s %s",
212 equiv
[i
][j
].rnr
,equiv
[i
][j
].rname
,equiv
[i
][j
].aname
);
217 static gmx_bool
is_equiv(int neq
, t_equiv
**equiv
, char **nname
,
218 int rnr1
, char *rname1
, char *aname1
,
219 int rnr2
, char *rname2
, char *aname2
)
225 /* we can terminate each loop when bFound is true! */
226 for (i
=0; i
<neq
&& !bFound
; i
++) {
227 /* find first atom */
228 for(j
=0; equiv
[i
][j
].rnr
!=NOTSET
&& !bFound
; j
++)
229 bFound
= ( equiv
[i
][j
].rnr
==rnr1
&&
230 strcmp(equiv
[i
][j
].rname
, rname1
)==0 &&
231 strcmp(equiv
[i
][j
].aname
, aname1
)==0 );
233 /* find second atom */
235 for(j
=0; equiv
[i
][j
].rnr
!=NOTSET
&& !bFound
; j
++)
236 bFound
= ( equiv
[i
][j
].rnr
==rnr2
&&
237 strcmp(equiv
[i
][j
].rname
, rname2
)==0 &&
238 strcmp(equiv
[i
][j
].aname
, aname2
)==0 );
242 *nname
= strdup(equiv
[i
-1][0].nname
);
247 static int analyze_noe_equivalent(const char *eq_fn
,
248 t_atoms
*atoms
, int isize
, atom_id
*index
,
250 atom_id
*noe_index
, t_noe_gr
*noe_gr
)
253 int i
, j
, anmil
, anmjl
, rnri
, rnrj
, gi
, groupnr
, neq
;
254 char *anmi
, *anmj
, **nnm
;
255 gmx_bool bMatch
,bEquiv
;
261 neq
=read_equiv(eq_fn
,&equiv
);
262 if (debug
) dump_equiv(debug
,neq
,equiv
);
269 for(i
=0; i
<isize
; i
++) {
270 if (equiv
&& i
<isize
-1) {
271 /* check explicit list of equivalent atoms */
274 rnri
=atoms
->atom
[index
[i
]].resind
;
275 rnrj
=atoms
->atom
[index
[j
]].resind
;
277 is_equiv(neq
, equiv
, &nnm
[i
],
278 rnri
,*atoms
->resinfo
[rnri
].name
,*atoms
->atomname
[index
[i
]],
279 rnrj
,*atoms
->resinfo
[rnrj
].name
,*atoms
->atomname
[index
[j
]]);
281 nnm
[j
]=strdup(nnm
[i
]);
283 /* set index for matching atom */
284 noe_index
[j
]=groupnr
;
285 /* skip matching atom */
288 } while ( bEquiv
&& i
<isize
-1 );
292 /* look for triplets of consecutive atoms with name XX?,
293 X are any number of letters or digits and ? goes from 1 to 3
294 This is supposed to cover all CH3 groups and the like */
295 anmi
= *atoms
->atomname
[index
[i
]];
296 anmil
= strlen(anmi
);
297 bMatch
= i
<isize
-3 && anmi
[anmil
-1]=='1';
300 anmj
= *atoms
->atomname
[index
[i
+j
]];
301 anmjl
= strlen(anmj
);
302 bMatch
= bMatch
&& ( anmil
==anmjl
&& anmj
[anmjl
-1]==Hnum
[j
] &&
303 strncmp(anmi
, anmj
, anmil
-1)==0 );
305 /* set index for this atom */
306 noe_index
[i
]=groupnr
;
308 /* set index for next two matching atoms */
310 noe_index
[i
+j
]=groupnr
;
311 /* skip matching atoms */
318 /* make index without looking for equivalent atoms */
319 for(i
=0; i
<isize
; i
++)
323 noe_index
[isize
]=groupnr
;
327 for(i
=0; i
<isize
; i
++) {
328 rnri
=atoms
->atom
[index
[i
]].resind
;
329 fprintf(debug
,"%s %s %d -> %s\n",*atoms
->atomname
[index
[i
]],
330 *atoms
->resinfo
[rnri
].name
,rnri
,nnm
[i
]?nnm
[i
]:"");
333 for(i
=0; i
<isize
; i
++) {
335 if (!noe_gr
[gi
].aname
) {
337 noe_gr
[gi
].anr
=index
[i
];
339 noe_gr
[gi
].aname
=strdup(nnm
[i
]);
341 noe_gr
[gi
].aname
=strdup(*atoms
->atomname
[index
[i
]]);
342 if ( noe_index
[i
]==noe_index
[i
+1] )
343 noe_gr
[gi
].aname
[strlen(noe_gr
[gi
].aname
)-1]='*';
345 noe_gr
[gi
].rnr
=atoms
->atom
[index
[i
]].resind
;
346 noe_gr
[gi
].rname
=strdup(*atoms
->resinfo
[noe_gr
[gi
].rnr
].name
);
347 /* dump group definitions */
348 if (debug
) fprintf(debug
,"%d %d %d %d %s %s %d\n",i
,gi
,
349 noe_gr
[gi
].ianr
,noe_gr
[gi
].anr
,noe_gr
[gi
].aname
,
350 noe_gr
[gi
].rname
,noe_gr
[gi
].rnr
);
353 for(i
=0; i
<isize
; i
++)
360 /* #define NSCALE 3 */
361 /* static char *noe_scale[NSCALE+1] = { */
362 /* "strong", "medium", "weak", "none" */
366 static char *noe2scale(real r3
, real r6
, real rmax
)
369 static char buf
[NSCALE
+1];
371 /* r goes from 0 to rmax
372 NSCALE*r/rmax goes from 0 to NSCALE
373 NSCALE - NSCALE*r/rmax goes from NSCALE to 0 */
374 s3
= NSCALE
- min(NSCALE
, (int)(NSCALE
*r3
/rmax
));
375 s6
= NSCALE
- min(NSCALE
, (int)(NSCALE
*r6
/rmax
));
386 static void calc_noe(int isize
, atom_id
*noe_index
,
387 real
**dtot1_3
, real
**dtot1_6
, int gnr
, t_noe
**noe
)
391 /* make half matrix of noe-group distances from atom distances */
392 for(i
=0; i
<isize
; i
++) {
394 for(j
=i
; j
<isize
; j
++) {
397 noe
[gi
][gj
].i_3
+=pow(dtot1_3
[i
][j
],-3);
398 noe
[gi
][gj
].i_6
+=pow(dtot1_6
[i
][j
],-6);
404 for(j
=i
+1; j
<gnr
; j
++) {
405 noe
[i
][j
].r_3
= pow(noe
[i
][j
].i_3
/noe
[i
][j
].nr
,-1.0/3.0);
406 noe
[i
][j
].r_6
= pow(noe
[i
][j
].i_6
/noe
[i
][j
].nr
,-1.0/6.0);
407 noe
[j
][i
] = noe
[i
][j
];
411 static void write_noe(FILE *fp
,int gnr
,t_noe
**noe
,t_noe_gr
*noe_gr
,real rmax
)
414 real r3
, r6
, min3
, min6
;
415 char buf
[10],b3
[10],b6
[10];
420 ";%4s %3s %4s %4s%3s %4s %4s %4s %4s%3s %5s %5s %8s %2s %2s %s\n",
421 "ianr","anr","anm","rnm","rnr","ianr","anr","anm","rnm","rnr",
422 "1/r^3","1/r^6","intnsty","Dr","Da","scale");
423 for(i
=0; i
<gnr
; i
++) {
425 for(j
=i
+1; j
<gnr
; j
++) {
431 if ( r3
< rmax
|| r6
< rmax
) {
432 if (grj
.rnr
== gri
.rnr
)
433 sprintf(buf
,"%2d", grj
.anr
-gri
.anr
);
437 sprintf(b3
,"%-5.3f",r3
);
441 sprintf(b6
,"%-5.3f",r6
);
445 "%4d %4d %4s %4s%3d %4d %4d %4s %4s%3d %5s %5s %8d %2d %2s %s\n",
446 gri
.ianr
+1, gri
.anr
+1, gri
.aname
, gri
.rname
, gri
.rnr
+1,
447 grj
.ianr
+1, grj
.anr
+1, grj
.aname
, grj
.rname
, grj
.rnr
+1,
448 b3
, b6
, (int)(noe
[i
][j
].i_6
+0.5), grj
.rnr
-gri
.rnr
, buf
,
449 noe2scale(r3
, r6
, rmax
));
453 #define MINI ((i==3)?min3:min6)
456 fprintf(stdout
,"NOTE: no 1/r^%d averaged distances found below %g, "
457 "smallest was %g\n", i
, rmax
, MINI
);
459 fprintf(stdout
,"Smallest 1/r^%d averaged distance was %g\n", i
, MINI
);
463 static void calc_rms(int nind
, int nframes
,
464 real
**dtot
, real
**dtot2
,
465 real
**rmsmat
, real
*rmsmax
,
466 real
**rmscmat
, real
*rmscmax
,
467 real
**meanmat
, real
*meanmax
)
470 real mean
, mean2
, rms
, rmsc
;
471 /* N.B. dtot and dtot2 contain the total distance and the total squared
472 * distance respectively, BUT they return RMS and the scaled RMS resp.
479 for(i
=0; (i
<nind
-1); i
++) {
480 for(j
=i
+1; (j
<nind
); j
++) {
481 mean
=dtot
[i
][j
]/nframes
;
482 mean2
=dtot2
[i
][j
]/nframes
;
483 rms
=sqrt(max(0,mean2
-mean
*mean
));
485 if (mean
> *meanmax
) *meanmax
=mean
;
486 if (rms
> *rmsmax
) *rmsmax
=rms
;
487 if (rmsc
> *rmscmax
) *rmscmax
=rmsc
;
488 meanmat
[i
][j
]=meanmat
[j
][i
]=mean
;
489 rmsmat
[i
][j
] =rmsmat
[j
][i
] =rms
;
490 rmscmat
[i
][j
]=rmscmat
[j
][i
]=rmsc
;
495 real
rms_diff(int natom
,real
**d
,real
**d_r
)
501 for(i
=0; (i
<natom
-1); i
++)
502 for(j
=i
+1; (j
<natom
); j
++) {
506 r2
/=(natom
*(natom
-1))/2;
511 int gmx_rmsdist (int argc
,char *argv
[])
513 const char *desc
[] = {
514 "[TT]g_rmsdist[tt] computes the root mean square deviation of atom distances,",
515 "which has the advantage that no fit is needed like in standard RMS",
516 "deviation as computed by [TT]g_rms[tt].",
517 "The reference structure is taken from the structure file.",
518 "The RMSD at time t is calculated as the RMS",
519 "of the differences in distance between atom-pairs in the reference",
520 "structure and the structure at time t.[PAR]",
521 "[TT]g_rmsdist[tt] can also produce matrices of the rms distances, rms distances",
522 "scaled with the mean distance and the mean distances and matrices with",
523 "NMR averaged distances (1/r^3 and 1/r^6 averaging). Finally, lists",
524 "of atom pairs with 1/r^3 and 1/r^6 averaged distance below the",
525 "maximum distance ([TT]-max[tt], which will default to 0.6 in this case)",
526 "can be generated, by default averaging over equivalent hydrogens",
527 "(all triplets of hydrogens named *[123]). Additionally a list of",
528 "equivalent atoms can be supplied ([TT]-equiv[tt]), each line containing",
529 "a set of equivalent atoms specified as residue number and name and",
530 "atom name; e.g.:[PAR]",
531 "[TT]3 SER HB1 3 SER HB2[tt][PAR]",
532 "Residue and atom names must exactly match those in the structure",
533 "file, including case. Specifying non-sequential atoms is undefined."
537 int natom
,i
,j
,teller
,gi
,gj
;
549 atom_id
*index
, *noe_index
;
551 real
**d_r
,**d
,**dtot
,**dtot2
,**mean
,**rms
,**rmsc
,*resnr
;
552 real
**dtot1_3
=NULL
,**dtot1_6
=NULL
;
553 real rmsnow
,meanmax
,rmsmax
,rmscmax
;
555 t_noe_gr
*noe_gr
=NULL
;
559 gmx_bool bRMS
, bScale
, bMean
, bNOE
, bNMR3
, bNMR6
, bNMR
;
561 static int nlevels
=40;
562 static real scalemax
=-1.0;
563 static gmx_bool bSumH
=TRUE
;
564 static gmx_bool bPBC
=TRUE
;
568 { "-nlevels", FALSE
, etINT
, {&nlevels
},
569 "Discretize RMS in this number of levels" },
570 { "-max", FALSE
, etREAL
, {&scalemax
},
571 "Maximum level in matrices" },
572 { "-sumh", FALSE
, etBOOL
, {&bSumH
},
573 "Average distance over equivalent hydrogens" },
574 { "-pbc", FALSE
, etBOOL
, {&bPBC
},
575 "Use periodic boundary conditions when computing distances" }
578 { efTRX
, "-f", NULL
, ffREAD
},
579 { efTPS
, NULL
, NULL
, ffREAD
},
580 { efNDX
, NULL
, NULL
, ffOPTRD
},
581 { efDAT
, "-equiv","equiv", ffOPTRD
},
582 { efXVG
, NULL
, "distrmsd", ffWRITE
},
583 { efXPM
, "-rms", "rmsdist", ffOPTWR
},
584 { efXPM
, "-scl", "rmsscale", ffOPTWR
},
585 { efXPM
, "-mean","rmsmean", ffOPTWR
},
586 { efXPM
, "-nmr3","nmr3", ffOPTWR
},
587 { efXPM
, "-nmr6","nmr6", ffOPTWR
},
588 { efDAT
, "-noe", "noe", ffOPTWR
},
590 #define NFILE asize(fnm)
592 CopyRight(stderr
,argv
[0]);
593 parse_common_args(&argc
,argv
,PCA_CAN_VIEW
| PCA_CAN_TIME
| PCA_BE_NICE
,
594 NFILE
,fnm
,asize(pa
),pa
,asize(desc
),desc
,0,NULL
,&oenv
);
596 bRMS
= opt2bSet("-rms", NFILE
,fnm
);
597 bScale
= opt2bSet("-scl", NFILE
,fnm
);
598 bMean
= opt2bSet("-mean",NFILE
,fnm
);
599 bNOE
= opt2bSet("-noe", NFILE
,fnm
);
600 bNMR3
= opt2bSet("-nmr3",NFILE
,fnm
);
601 bNMR6
= opt2bSet("-nmr6",NFILE
,fnm
);
602 bNMR
= bNMR3
|| bNMR6
|| bNOE
;
608 if (bNOE
&& scalemax
< 0) {
610 fprintf(stderr
,"WARNING: using -noe without -max "
611 "makes no sense, setting -max to %g\n\n",scalemax
);
614 /* get topology and index */
615 read_tps_conf(ftp2fn(efTPS
,NFILE
,fnm
),buf
,&top
,&ePBC
,&x
,NULL
,box
,FALSE
);
621 get_index(atoms
,ftp2fn_null(efNDX
,NFILE
,fnm
),1,&isize
,&index
,&grpname
);
623 /* initialize arrays */
636 for(i
=0; (i
<isize
); i
++) {
639 snew(dtot2
[i
],isize
);
641 snew(dtot1_3
[i
],isize
);
642 snew(dtot1_6
[i
],isize
);
652 calc_dist(isize
,index
,x
,ePBC
,box
,d_r
);
655 /*open output files*/
656 fp
=xvgropen(ftp2fn(efXVG
,NFILE
,fnm
),"RMS Deviation","Time (ps)","RMSD (nm)",
658 if (output_env_get_print_xvgr_codes(oenv
))
659 fprintf(fp
,"@ subtitle \"of distances between %s atoms\"\n",grpname
);
662 natom
=read_first_x(oenv
,&status
,ftp2fn(efTRX
,NFILE
,fnm
),&t
,&x
,box
);
665 calc_dist_tot(isize
,index
,x
,ePBC
,box
,d
,dtot
,dtot2
,bNMR
,dtot1_3
,dtot1_6
);
667 rmsnow
=rms_diff(isize
,d
,d_r
);
668 fprintf(fp
,"%g %g\n",t
,rmsnow
);
669 } while (read_next_x(oenv
,status
,&t
,natom
,x
,box
));
670 fprintf(stderr
, "\n");
674 teller
= nframes_read(status
);
678 calc_rms(isize
,teller
,dtot
,dtot2
,mean
,&meanmax
,rms
,&rmsmax
,rmsc
,&rmscmax
);
679 fprintf(stderr
,"rmsmax = %g, rmscmax = %g\n",rmsmax
,rmscmax
);
683 calc_nmr(isize
,teller
,dtot1_3
,dtot1_6
,&max1_3
,&max1_6
);
686 if (scalemax
> -1.0) {
695 /* make list of noe atom groups */
696 snew(noe_index
, isize
+1);
698 gnr
=analyze_noe_equivalent(opt2fn_null("-equiv",NFILE
,fnm
),
699 atoms
, isize
, index
, bSumH
, noe_index
, noe_gr
);
700 fprintf(stdout
, "Found %d non-equivalent atom-groups in %d atoms\n",
702 /* make half matrix of of noe-group distances from atom distances */
706 calc_noe(isize
, noe_index
, dtot1_3
, dtot1_6
, gnr
, noe
);
709 rlo
.r
=1.0, rlo
.g
=1.0, rlo
.b
=1.0;
710 rhi
.r
=0.0, rhi
.g
=0.0, rhi
.b
=0.0;
713 write_xpm(opt2FILE("-rms",NFILE
,fnm
,"w"),0,
714 "RMS of distance","RMS (nm)","Atom Index","Atom Index",
715 isize
,isize
,resnr
,resnr
,rms
,0.0,rmsmax
,rlo
,rhi
,&nlevels
);
718 write_xpm(opt2FILE("-scl",NFILE
,fnm
,"w"),0,
719 "Relative RMS","RMS","Atom Index","Atom Index",
720 isize
,isize
,resnr
,resnr
,rmsc
,0.0,rmscmax
,rlo
,rhi
,&nlevels
);
723 write_xpm(opt2FILE("-mean",NFILE
,fnm
,"w"),0,
724 "Mean Distance","Distance (nm)","Atom Index","Atom Index",
725 isize
,isize
,resnr
,resnr
,mean
,0.0,meanmax
,rlo
,rhi
,&nlevels
);
728 write_xpm(opt2FILE("-nmr3",NFILE
,fnm
,"w"),0,"1/r^3 averaged distances",
729 "Distance (nm)","Atom Index","Atom Index",
730 isize
,isize
,resnr
,resnr
,dtot1_3
,0.0,max1_3
,rlo
,rhi
,&nlevels
);
732 write_xpm(opt2FILE("-nmr6",NFILE
,fnm
,"w"),0,"1/r^6 averaged distances",
733 "Distance (nm)","Atom Index","Atom Index",
734 isize
,isize
,resnr
,resnr
,dtot1_6
,0.0,max1_6
,rlo
,rhi
,&nlevels
);
737 write_noe(opt2FILE("-noe",NFILE
,fnm
,"w"), gnr
, noe
, noe_gr
, scalemax
);
739 do_view(oenv
,ftp2fn(efXVG
,NFILE
,fnm
),NULL
);