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 * check out http://www.gromacs.org for more information.
7 * Copyright (c) 2012,2013, by the GROMACS development team, led by
8 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
9 * others, as listed in the AUTHORS file in the top-level source
10 * 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.
63 static void periodic_dist(matrix box,rvec x[],int n,atom_id index[],
64 real *rmin,real *rmax,int *min_ind)
68 real sqr_box,r2min,r2max,r2;
69 rvec shift[NSHIFT],d0,d;
71 sqr_box = sqr(min(box[XX][XX],min(box[YY][YY],box[ZZ][ZZ])));
74 for(sz=-1; sz<=1; sz++)
75 for(sy=-1; sy<=1; sy++)
76 for(sx=-1; sx<=1; sx++)
77 if (sx!=0 || sy!=0 || sz!=0) {
79 shift[s][i] = sx*box[XX][i]+sy*box[YY][i]+sz*box[ZZ][i];
87 for(j=i+1; j<n; j++) {
88 rvec_sub(x[index[i]],x[index[j]],d0);
92 for(s=0; s<NSHIFT; s++) {
93 rvec_add(d0,shift[s],d);
107 static void periodic_mindist_plot(const char *trxfn,const char *outfn,
108 t_topology *top,int ePBC,
109 int n,atom_id index[],gmx_bool bSplit,
110 const output_env_t oenv)
113 const char *leg[5] = { "min per.","max int.","box1","box2","box3" };
118 int natoms,ind_min[2]={0,0},ind_mini=0,ind_minj=0;
119 real r,rmin,rmax,rmint,tmint;
121 gmx_rmpbc_t gpbc=NULL;
123 natoms=read_first_x(oenv,&status,trxfn,&t,&x,box);
125 check_index(NULL,n,index,NULL,natoms);
127 out = xvgropen(outfn,"Minimum distance to periodic image",
128 output_env_get_time_label(oenv),"Distance (nm)",oenv);
129 if (output_env_get_print_xvgr_codes(oenv))
130 fprintf(out,"@ subtitle \"and maximum internal distance\"\n");
131 xvgr_legend(out,5,leg,oenv);
137 gpbc = gmx_rmpbc_init(&top->idef,ePBC,natoms,box);
142 gmx_rmpbc(gpbc,natoms,box,x);
144 periodic_dist(box,x,n,index,&rmin,&rmax,ind_min);
148 ind_mini = ind_min[0];
149 ind_minj = ind_min[1];
151 if ( bSplit && !bFirst && abs(t/output_env_get_time_factor(oenv))<1e-5 )
153 fprintf(out,"\t%g\t%6.3f %6.3f %6.3f %6.3f %6.3f\n",
154 output_env_conv_time(oenv,t),rmin,rmax,norm(box[0]),norm(box[1]),norm(box[2]));
156 } while(read_next_x(oenv,status,&t,natoms,x,box));
159 gmx_rmpbc_done(gpbc);
164 "\nThe shortest periodic distance is %g (nm) at time %g (%s),\n"
165 "between atoms %d and %d\n",
166 rmint,output_env_conv_time(oenv,tmint),output_env_get_time_unit(oenv),
167 index[ind_mini]+1,index[ind_minj]+1);
170 static void calc_dist(real rcut, gmx_bool bPBC, int ePBC, matrix box, rvec x[],
171 int nx1,int nx2, atom_id index1[], atom_id index2[],
173 real *rmin, real *rmax, int *nmin, int *nmax,
174 int *ixmin, int *jxmin, int *ixmax, int *jxmax)
180 real r2,rmin2,rmax2,rcut2;
193 /* Must init pbc every step because of pressure coupling */
195 set_pbc(&pbc,ePBC,box);
208 for(j=0; (j < j1); j++) {
210 if (index2 == NULL) {
215 for(i=i0; (i < nx1); i++) {
219 pbc_dx(&pbc,x[ix],x[jx],dx);
221 rvec_sub(x[ix],x[jx],dx);
235 } else if (r2 > rcut2) {
256 void dist_plot(const char *fn,const char *afile,const char *dfile,
257 const char *nfile,const char *rfile,const char *xfile,
258 real rcut,gmx_bool bMat,t_atoms *atoms,
259 int ng,atom_id *index[],int gnx[],char *grpn[],gmx_bool bSplit,
260 gmx_bool bMin, int nres, atom_id *residue,gmx_bool bPBC,int ePBC,
261 gmx_bool bGroup,gmx_bool bEachResEachTime, gmx_bool bPrintResName,
262 const output_env_t oenv)
264 FILE *atm,*dist,*num;
268 real t,dmin,dmax,**mindres=NULL,**maxdres=NULL;
272 int min1,min2,max1,max2,min1r,min2r,max1r,max2r;
278 FILE *respertime=NULL;
280 if ((natoms=read_first_x(oenv,&status,fn,&t,&x0,box))==0)
281 gmx_fatal(FARGS,"Could not read coordinates from statusfile\n");
283 sprintf(buf,"%simum Distance",bMin ? "Min" : "Max");
284 dist= xvgropen(dfile,buf,output_env_get_time_label(oenv),"Distance (nm)",oenv);
285 sprintf(buf,"Number of Contacts %s %g nm",bMin ? "<" : ">",rcut);
286 num = nfile ? xvgropen(nfile,buf,output_env_get_time_label(oenv),"Number",oenv) : NULL;
287 atm = afile ? ffopen(afile,"w") : NULL;
288 trxout = xfile ? open_trx(xfile,"w") : NULL;
293 sprintf(buf,"Internal in %s",grpn[0]);
295 xvgr_legend(dist,0,(const char**)leg,oenv);
296 if (num) xvgr_legend(num,0,(const char**)leg,oenv);
299 snew(leg,(ng*(ng-1))/2);
300 for(i=j=0; (i<ng-1); i++) {
301 for(k=i+1; (k<ng); k++,j++) {
302 sprintf(buf,"%s-%s",grpn[i],grpn[k]);
306 xvgr_legend(dist,j,(const char**)leg,oenv);
307 if (num) xvgr_legend(num,j,(const char**)leg,oenv);
312 for(i=0; (i<ng-1); i++) {
313 sprintf(buf,"%s-%s",grpn[0],grpn[i+1]);
316 xvgr_legend(dist,ng-1,(const char**)leg,oenv);
317 if (num) xvgr_legend(num,ng-1,(const char**)leg,oenv);
320 if (bEachResEachTime)
322 sprintf(buf,"%simum Distance",bMin ? "Min" : "Max");
323 respertime=xvgropen(rfile,buf,output_env_get_time_label(oenv),"Distance (nm)",oenv);
324 xvgr_legend(respertime,ng-1,(const char**)leg,oenv);
326 fprintf(respertime,"# ");
327 for (j=0; j<nres; j++)
328 fprintf(respertime,"%s%d ",*(atoms->resinfo[atoms->atom[index[0][residue[j]]].resind].name),atoms->atom[index[0][residue[j]]].resind);
329 fprintf(respertime,"\n");
337 for(i=1; i<ng; i++) {
338 snew(mindres[i-1], nres);
339 snew(maxdres[i-1], nres);
340 for(j=0; j<nres; j++)
342 /* maxdres[*][*] is already 0 */
347 if ( bSplit && !bFirst && abs(t/output_env_get_time_factor(oenv))<1e-5 ) {
348 fprintf(dist, "&\n");
349 if (num) fprintf(num, "&\n");
350 if (atm) fprintf(atm, "&\n");
352 fprintf(dist,"%12e",output_env_conv_time(oenv,t));
353 if (num) fprintf(num,"%12e",output_env_conv_time(oenv,t));
357 calc_dist(rcut,bPBC,ePBC,box,x0,gnx[0],gnx[0],index[0],index[0],bGroup,
358 &dmin,&dmax,&nmin,&nmax,&min1,&min2,&max1,&max2);
359 fprintf(dist," %12e",bMin?dmin:dmax);
360 if (num) fprintf(num," %8d",bMin?nmin:nmax);
363 for(i=0; (i<ng-1); i++) {
364 for(k=i+1; (k<ng); k++) {
365 calc_dist(rcut,bPBC,ePBC,box,x0,gnx[i],gnx[k],index[i],index[k],
366 bGroup,&dmin,&dmax,&nmin,&nmax,&min1,&min2,&max1,&max2);
367 fprintf(dist," %12e",bMin?dmin:dmax);
368 if (num) fprintf(num," %8d",bMin?nmin:nmax);
374 for(i=1; (i<ng); i++) {
375 calc_dist(rcut,bPBC,ePBC,box,x0,gnx[0],gnx[i],index[0],index[i],bGroup,
376 &dmin,&dmax,&nmin,&nmax,&min1,&min2,&max1,&max2);
377 fprintf(dist," %12e",bMin?dmin:dmax);
378 if (num) fprintf(num," %8d",bMin?nmin:nmax);
380 for(j=0; j<nres; j++) {
381 calc_dist(rcut,bPBC,ePBC,box,x0,residue[j+1]-residue[j],gnx[i],
382 &(index[0][residue[j]]),index[i],bGroup,
383 &dmin,&dmax,&nmin,&nmax,&min1r,&min2r,&max1r,&max2r);
384 mindres[i-1][j] = min(mindres[i-1][j],dmin);
385 maxdres[i-1][j] = max(maxdres[i-1][j],dmax);
393 if ( (bMin?min1:max1) != -1 )
395 fprintf(atm,"%12e %12d %12d\n",
396 output_env_conv_time(oenv,t),1+(bMin ? min1 : max1),
397 1+(bMin ? min2 : max2));
400 oindex[0]=bMin?min1:max1;
401 oindex[1]=bMin?min2:max2;
402 write_trx(trxout,2,oindex,atoms,i,t,box,x0,NULL,NULL);
405 /*dmin should be minimum distance for residue and group*/
406 if (bEachResEachTime)
408 fprintf(respertime,"%12e",t);
410 for(j=0; j<nres; j++)
412 fprintf(respertime, " %7g", bMin ? mindres[i-1][j] : maxdres[i-1][j]);
413 /*reset distances for next time point*/
417 fprintf(respertime, "\n");
419 } while (read_next_x(oenv,status,&t,natoms,x0,box));
423 if (num) ffclose(num);
424 if (atm) ffclose(atm);
425 if (trxout) close_trx(trxout);
427 if(nres && !bEachResEachTime) {
430 sprintf(buf,"%simum Distance",bMin ? "Min" : "Max");
431 res=xvgropen(rfile,buf,"Residue (#)","Distance (nm)",oenv);
432 xvgr_legend(res,ng-1,(const char**)leg,oenv);
433 for(j=0; j<nres; j++) {
434 fprintf(res, "%4d", j+1);
435 for(i=1; i<ng; i++) {
436 fprintf(res, " %7g", bMin ? mindres[i-1][j] : maxdres[i-1][j]);
445 int find_residues(t_atoms *atoms, int n, atom_id index[], atom_id **resindex)
448 int nres=0,resnr, presnr;
451 /* build index of first atom numbers for each residue */
453 snew(residx, atoms->nres+1);
455 resnr = atoms->atom[index[i]].resind;
456 if (resnr != presnr) {
462 if (debug) printf("Found %d residues out of %d (%d/%d atoms)\n",
463 nres, atoms->nres, atoms->nr, n);
464 srenew(residx, nres+1);
465 /* mark end of last residue */
471 void dump_res(FILE *out, int nres, atom_id *resindex, int n, atom_id index[])
475 for(i=0; i<nres-1; i++) {
476 fprintf(out,"Res %d (%d):", i, resindex[i+1]-resindex[i]);
477 for(j=resindex[i]; j<resindex[i+1]; j++)
478 fprintf(out," %d(%d)", j, index[j]);
483 int gmx_mindist(int argc,char *argv[])
485 const char *desc[] = {
486 "[TT]g_mindist[tt] computes the distance between one group and a number of",
487 "other groups. Both the minimum distance",
488 "(between any pair of atoms from the respective groups)",
489 "and the number of contacts within a given",
490 "distance are written to two separate output files.",
491 "With the [TT]-group[tt] option a contact of an atom in another group",
492 "with multiple atoms in the first group is counted as one contact",
493 "instead of as multiple contacts.",
494 "With [TT]-or[tt], minimum distances to each residue in the first",
495 "group are determined and plotted as a function of residue number.[PAR]",
496 "With option [TT]-pi[tt] the minimum distance of a group to its",
497 "periodic image is plotted. This is useful for checking if a protein",
498 "has seen its periodic image during a simulation. Only one shift in",
499 "each direction is considered, giving a total of 26 shifts.",
500 "It also plots the maximum distance within the group and the lengths",
501 "of the three box vectors.[PAR]",
502 "Other programs that calculate distances are [TT]g_dist[tt]",
503 "and [TT]g_bond[tt]."
505 const char *bugs[] = {
506 "The [TT]-pi[tt] option is very slow."
509 static gmx_bool bMat=FALSE,bPI=FALSE,bSplit=FALSE,bMax=FALSE,bPBC=TRUE;
510 static gmx_bool bGroup=FALSE;
511 static real rcutoff=0.6;
513 static gmx_bool bEachResEachTime=FALSE,bPrintResName=FALSE;
515 { "-matrix", FALSE, etBOOL, {&bMat},
516 "Calculate half a matrix of group-group distances" },
517 { "-max", FALSE, etBOOL, {&bMax},
518 "Calculate *maximum* distance instead of minimum" },
519 { "-d", FALSE, etREAL, {&rcutoff},
520 "Distance for contacts" },
521 { "-group", FALSE, etBOOL, {&bGroup},
522 "Count contacts with multiple atoms in the first group as one" },
523 { "-pi", FALSE, etBOOL, {&bPI},
524 "Calculate minimum distance with periodic images" },
525 { "-split", FALSE, etBOOL, {&bSplit},
526 "Split graph where time is zero" },
527 { "-ng", FALSE, etINT, {&ng},
528 "Number of secondary groups to compute distance to a central group" },
529 { "-pbc", FALSE, etBOOL, {&bPBC},
530 "Take periodic boundary conditions into account" },
531 { "-respertime", FALSE, etBOOL, {&bEachResEachTime},
532 "When writing per-residue distances, write distance for each time point" },
533 { "-printresname", FALSE, etBOOL, {&bPrintResName},
534 "Write residue names" }
537 t_topology *top=NULL;
547 const char *trxfnm,*tpsfnm,*ndxfnm,*distfnm,*numfnm,*atmfnm,*oxfnm,*resfnm;
550 atom_id **index, *residues=NULL;
552 { efTRX, "-f", NULL, ffREAD },
553 { efTPS, NULL, NULL, ffOPTRD },
554 { efNDX, NULL, NULL, ffOPTRD },
555 { efXVG, "-od","mindist", ffWRITE },
556 { efXVG, "-on","numcont", ffOPTWR },
557 { efOUT, "-o", "atm-pair", ffOPTWR },
558 { efTRO, "-ox","mindist", ffOPTWR },
559 { efXVG, "-or","mindistres", ffOPTWR }
561 #define NFILE asize(fnm)
563 CopyRight(stderr,argv[0]);
564 parse_common_args(&argc,argv,
565 PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
566 NFILE,fnm,asize(pa),pa,asize(desc),desc,0,NULL, &oenv);
568 trxfnm = ftp2fn(efTRX,NFILE,fnm);
569 ndxfnm = ftp2fn_null(efNDX,NFILE,fnm);
570 distfnm= opt2fn("-od",NFILE,fnm);
571 numfnm = opt2fn_null("-on",NFILE,fnm);
572 atmfnm = ftp2fn_null(efOUT,NFILE,fnm);
573 oxfnm = opt2fn_null("-ox",NFILE,fnm);
574 resfnm = opt2fn_null("-or",NFILE,fnm);
575 if (bPI || resfnm != NULL) {
576 /* We need a tps file */
577 tpsfnm = ftp2fn(efTPS,NFILE,fnm);
579 tpsfnm = ftp2fn_null(efTPS,NFILE,fnm);
582 if (!tpsfnm && !ndxfnm)
583 gmx_fatal(FARGS,"You have to specify either the index file or a tpr file");
587 fprintf(stderr,"Choose a group for distance calculation\n");
596 if (tpsfnm || resfnm || !ndxfnm) {
598 bTop = read_tps_conf(tpsfnm,title,top,&ePBC,&x,NULL,box,FALSE);
600 printf("\nWARNING: Without a run input file a trajectory with broken molecules will not give the correct periodic image distance\n\n");
602 get_index(top ? &(top->atoms) : NULL,ndxfnm,ng,gnx,index,grpname);
604 if (bMat && (ng == 1)) {
606 printf("Special case: making distance matrix between all atoms in group %s\n",
611 for(i=1; (i<ng); i++) {
613 grpname[i] = grpname[0];
615 index[i][0] = index[0][i];
621 nres=find_residues(top ? &(top->atoms) : NULL,
622 gnx[0], index[0], &residues);
623 if (debug) dump_res(debug, nres, residues, gnx[0], index[0]);
627 periodic_mindist_plot(trxfnm,distfnm,top,ePBC,gnx[0],index[0],bSplit,oenv);
629 dist_plot(trxfnm,atmfnm,distfnm,numfnm,resfnm,oxfnm,
630 rcutoff,bMat,top ? &(top->atoms) : NULL,
631 ng,index,gnx,grpname,bSplit,!bMax, nres, residues,bPBC,ePBC,
632 bGroup,bEachResEachTime,bPrintResName,oenv);
635 do_view(oenv,distfnm,"-nxy");
637 do_view(oenv,numfnm,"-nxy");