3 * This source code is NOT part of
7 * GROningen MAchine for Chemical Simulations
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
15 * Gyas ROwers Mature At Cryogenic Speed
30 #include "gromacs/fileio/futil.h"
31 #include "gromacs/commandline/pargs.h"
32 #include "gromacs/fileio/tpxio.h"
34 #include "gromacs/utility/smalloc.h"
37 #include "gromacs/fileio/matio.h"
38 #include "gmx_fatal.h"
50 static void i_write(FILE *output, int value)
52 if(fwrite(&value,sizeof(int),1,output) != 1)
54 gmx_fatal(FARGS,"Error writing to output file");
59 static void f_write(FILE *output,float value)
61 if(fwrite(&value,sizeof(float),1,output) != 1)
63 gmx_fatal(FARGS,"Error writing to output file");
68 static void do_sdf(const char *fnNDX,const char *fnTPS,const char *fnTRX,
69 const char *fnSDF, const char *fnREF, gmx_bool bRef,
70 rvec cutoff, real binwidth, int mode, rvec triangle,
71 rvec dtri, const output_env_t oenv)
75 int ng,natoms,i,j,k,l,X,Y,Z,lc,dest;
79 real sdf,min_sdf=1e10,max_sdf=0;
84 int ref_resind[3]={0};
87 atom_id *index_cg=NULL;
88 atom_id *index_ref=NULL;
89 real t,boxmin,hbox,normfac;
91 rvec tri_upper,tri_lower;
92 rvec *x,xcog,dx,*x_i1,xi,*x_refmol;
94 matrix rot; /* rotation matrix := unit vectors for the molecule frame */
95 rvec k_mol,i1_mol,i2_mol,dx_mol;
99 gmx_rmpbc_t gpbc=NULL;
102 gmx_bool bTop=FALSE,bRefDone=FALSE,bInGroup=FALSE;
108 bTop=read_tps_conf(fnTPS,title,&top,&ePBC,&x,NULL,box,TRUE);
114 fprintf(stderr,"\nNeed tpr-file to make a reference structure.\n");
115 fprintf(stderr,"Option -r will be ignored!\n");
120 /* Allocate memory for 4 groups, 3 dummy groups and a group for the ref
121 structure if needed */
124 /* the dummy groups are used to dynamically store triples of atoms */
125 /* for molecular coordinate systems */
138 /* Read the index groups */
139 fprintf(stderr,"\nSelect the 3 reference groups and the SDF group:\n");
141 get_index(&top.atoms,fnNDX,ng,isize,index,grpname);
143 rd_index(fnNDX,ng,isize,index,grpname);
146 isize[NDX_REF1]=isize[G_REF1];
147 for (i=NDX_REF1; i<=NDX_REF3; i++)
148 snew(index[i],isize[NDX_REF1]);
151 /* Read first frame and check it */
152 natoms=read_first_x(oenv,&status,fnTRX,&t,&x,box);
154 gmx_fatal(FARGS,"Could not read coordinates from statusfile!\n");
157 /* check with topology */
159 if ( natoms > top.atoms.nr )
161 "Trajectory (%d atoms) does not match topology (%d atoms)!\n",
162 natoms,top.atoms.nr);
165 /* check with index groups */
167 for (j=0; j<isize[i]; j++)
168 if ( index[i][j] >= natoms )
169 gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) larger "
170 "than number of atoms in trajectory (%d atoms)!\n",
171 index[i][j],grpname[i],isize[i],natoms);
174 /* check reference groups */
177 if ( isize[G_REF1] != isize[G_REF2] || isize[G_REF1] != isize[G_REF3] ||
178 isize[G_REF2] != isize[G_REF3] )
179 gmx_fatal(FARGS,"For single particle SDF, all reference groups"
180 "must have the same size.\n");
183 /* for single particle SDF dynamic triples are not needed */
184 /* so we build them right here */
187 /* copy all triples from G_REFx to NDX_REFx */
188 for (i=0; i<isize[G_REF1]; i++)
190 /* check if all three atoms come from the same molecule */
191 for (j=G_REF1; j<=G_REF3; j++)
192 ref_resind[j] = top.atoms.atom[index[j][i]].resind;
195 if ( ref_resind[G_REF1] != ref_resind[G_REF2] ||
196 ref_resind[G_REF2] != ref_resind[G_REF3] ||
197 ref_resind[G_REF3] != ref_resind[G_REF1] )
199 fprintf(stderr,"\nWarning: reference triple (%d) will be skipped.\n",i);
200 fprintf(stderr, " resnr[1]: %d, resnr[2]: %d, resnr[3]: %d\n",
201 ref_resind[G_REF1],ref_resind[G_REF2], ref_resind[G_REF3]);
203 for (j=NDX_REF1; j<=NDX_REF3; j++)
204 srenew(index[j],isize[NDX_REF1]);
208 /* check if all entries are unique*/
209 if ( index[G_REF1][i] == index[G_REF2][i] ||
210 index[G_REF2][i] == index[G_REF3][i] ||
211 index[G_REF3][i] == index[G_REF1][i] )
213 fprintf(stderr,"Warning: reference triple (%d) will be skipped.\n",i);
214 fprintf(stderr, " index[1]: %d, index[2]: %d, index[3]: %d\n",
215 index[G_REF1][i],index[G_REF2][i],index[G_REF3][i]);
217 for (j=NDX_REF1; j<=NDX_REF3; j++)
218 srenew(index[j],isize[NDX_REF1]);
221 else /* everythings fine, copy that one */
222 for (j=G_REF1; j<=G_REF3; j++)
223 index[j+4][i] = index[j][i];
226 else if ( mode == 2 )
228 if ( isize[G_REF1] != isize[G_REF2] )
229 gmx_fatal(FARGS,"For two particle SDF, reference groups 1 and 2"
230 "must have the same size.\n");
233 for (i=0; i<isize[G_REF1]; i++)
235 /* check consistency for atoms 1 and 2 */
236 for (j=G_REF1; j<=G_REF2; j++)
237 ref_resind[j] = top.atoms.atom[index[j][i]].resind;
240 if ( ref_resind[G_REF1] != ref_resind[G_REF2] ||
241 index[G_REF1][i] == index[G_REF2][i] )
243 if ( ref_resind[G_REF1] != ref_resind[G_REF2] )
245 fprintf(stderr,"\nWarning: bond (%d) not from one molecule."
246 "Will not be used for SDF.\n",i);
247 fprintf(stderr, " resnr[1]: %d, resnr[2]: %d\n",
248 ref_resind[G_REF1],ref_resind[G_REF2]);
252 fprintf(stderr,"\nWarning: atom1 and atom2 are identical."
253 "Bond (%d) will not be used for SDF.\n",i);
254 fprintf(stderr, " index[1]: %d, index[2]: %d\n",
255 index[G_REF1][i],index[G_REF2][i]);
257 for (j=NDX_REF1; j<=NDX_REF2; j++)
259 for (k=i; k<isize[G_REF1]-1; k++)
260 index[j][k]=index[j][k+1];
262 srenew(index[j],isize[j]);
269 /* Read Atoms for refmol group */
272 snew(index[G_REFMOL],1);
275 for (i=G_REF1; i<=G_REF3; i++)
276 ref_resind[i] = top.atoms.atom[index[i][0]].resind;
279 for (i=0; i<natoms; i++)
281 if ( ref_resind[G_REF1] == top.atoms.atom[i].resind ||
282 ref_resind[G_REF2] == top.atoms.atom[i].resind ||
283 ref_resind[G_REF3] == top.atoms.atom[i].resind )
286 srenew(index[G_REFMOL],nrefmol);
287 isize[G_REFMOL] = nrefmol;
291 for (i=0; i<natoms; i++)
293 if ( ref_resind[G_REF1] == top.atoms.atom[i].resind ||
294 ref_resind[G_REF2] == top.atoms.atom[i].resind ||
295 ref_resind[G_REF3] == top.atoms.atom[i].resind )
297 index[G_REFMOL][nrefmol] = i;
304 /* initialize some stuff */
305 boxmin = min( norm(box[XX]), min( norm(box[YY]), norm(box[ZZ]) ) );
309 for (i=0; i<DIM; i++)
311 cutoff[i] = cutoff[i] / 2;
312 nbin[i] = (int)(2 * cutoff[i] / binwidth) + 1;
313 invbinw = 1.0 / binwidth;
314 tri_upper[i] = triangle[i] + dtri[i];
315 tri_upper[i] = sqr(tri_upper[i]);
316 tri_lower[i] = triangle[i] - dtri[i];
317 tri_lower[i] = sqr(tri_lower[i]);
321 /* Allocate the array's for sdf */
322 snew(count,nbin[0]+1);
323 for(i=0; i<nbin[0]+1; i++)
325 snew(count[i],nbin[1]+1);
326 for (j=0; j<nbin[1]+1; j++)
327 snew(count[i][j],nbin[2]+1);
331 /* Allocate space for the coordinates */
332 snew(x_i1,isize[G_SDF]);
333 snew(x_refmol,isize[G_REFMOL]);
334 for (i=0; i<isize[G_REFMOL]; i++)
335 for (j=XX; j<=ZZ; j++)
341 gpbc = gmx_rmpbc_init(&top.idef,ePBC,natoms,box);
344 /* Must init pbc every step because of pressure coupling */
345 set_pbc(&pbc,ePBC,box);
346 gmx_rmpbc(gpbc,natoms,box,x);
348 /* Dynamically build the ref triples */
352 for (j=NDX_REF1; j<=NDX_REF3; j++)
353 srenew(index[j],isize[NDX_REF1]+1);
356 /* consistancy of G_REF[1,2] has already been check */
357 /* hence we can look for the third atom right away */
360 for (i=0; i<isize[G_REF1]; i++)
362 for (j=0; j<isize[G_REF3]; j++)
364 /* Avoid expensive stuff if possible */
365 if ( top.atoms.atom[index[G_REF1][i]].resind !=
366 top.atoms.atom[index[G_REF3][j]].resind &&
367 index[G_REF1][i] != index[G_REF3][j] &&
368 index[G_REF2][i] != index[G_REF3][j] )
370 pbc_dx(&pbc,x[index[G_REF1][i]],x[index[G_REF3][j]],dx);
372 if ( delta < tri_upper[G_REF1] &&
373 delta > tri_lower[G_REF1] )
375 pbc_dx(&pbc,x[index[G_REF2][i]],x[index[G_REF3][j]],dx);
377 if ( delta < tri_upper[G_REF2] &&
378 delta > tri_lower[G_REF2] )
381 index[NDX_REF1][isize[NDX_REF1]]=index[G_REF1][i];
382 index[NDX_REF2][isize[NDX_REF1]]=index[G_REF2][i];
383 index[NDX_REF3][isize[NDX_REF1]]=index[G_REF3][j];
388 for (k=NDX_REF1; k<=NDX_REF3; k++)
389 srenew(index[k],isize[NDX_REF1]+1);
399 for (j=NDX_REF1; j<=NDX_REF3; j++)
400 srenew(index[j],isize[NDX_REF1]+1);
402 /* consistancy will be checked while searching */
405 for (i=0; i<isize[G_REF1]; i++)
407 for (j=0; j<isize[G_REF2]; j++)
409 /* Avoid expensive stuff if possible */
410 if ( top.atoms.atom[index[G_REF1][i]].resind !=
411 top.atoms.atom[index[G_REF2][j]].resind &&
412 index[G_REF1][i] != index[G_REF2][j] )
414 pbc_dx(&pbc,x[index[G_REF1][i]],x[index[G_REF2][j]],dx);
416 if ( delta < tri_upper[G_REF3] &&
417 delta > tri_lower[G_REF3] )
419 for (k=0; k<isize[G_REF3]; k++)
421 if ( top.atoms.atom[index[G_REF1][i]].resind !=
422 top.atoms.atom[index[G_REF3][k]].resind &&
423 top.atoms.atom[index[G_REF2][j]].resind !=
424 top.atoms.atom[index[G_REF3][k]].resind &&
425 index[G_REF1][i] != index[G_REF3][k] &&
426 index[G_REF2][j] != index[G_REF3][k])
428 pbc_dx(&pbc,x[index[G_REF1][i]],x[index[G_REF3][k]],dx);
430 if ( delta < tri_upper[G_REF1] &&
431 delta > tri_lower[G_REF1] )
433 pbc_dx(&pbc,x[index[G_REF2][j]],x[index[G_REF3][k]],dx);
435 if ( delta < tri_upper[G_REF2] &&
436 delta > tri_lower[G_REF2] )
439 index[NDX_REF1][isize[NDX_REF1]]=index[G_REF1][i];
440 index[NDX_REF2][isize[NDX_REF1]]=index[G_REF2][j];
441 index[NDX_REF3][isize[NDX_REF1]]=index[G_REF3][k];
445 for (l=NDX_REF1; l<=NDX_REF3; l++)
446 srenew(index[l],isize[NDX_REF1]+1);
457 for (i=0; i<isize[NDX_REF1]; i++)
459 /* setup the molecular coordinate system (i',j',k') */
460 /* because the coodinate system of the box forms a unit matrix */
461 /* (i',j',k') is identical with the rotation matrix */
465 /* k' = unitv(r(atom0) - r(atom1)) */
466 pbc_dx(&pbc,x[index[NDX_REF1][i]],x[index[NDX_REF2][i]],k_mol);
469 /* i' = unitv(k' x (r(atom2) - r(atom1))) */
470 pbc_dx(&pbc,x[index[NDX_REF3][i]],x[index[NDX_REF2][i]],i1_mol);
471 cprod(i1_mol,rot[2],i2_mol);
472 unitv(i2_mol,rot[0]);
475 cprod(rot[2],rot[0],rot[1]);
478 /* set the point of reference */
480 copy_rvec(x[index[NDX_REF3][i]],xi);
482 copy_rvec(x[index[NDX_REF1][i]],xi);
485 /* make the reference */
488 for (j=0; j<isize[G_REFMOL]; j++)
490 pbc_dx(&pbc,xi,x[index[G_REFMOL][j]],dx);
491 mvmul(rot,dx,dx_mol);
492 rvec_inc(x_refmol[j],dx_mol);
493 for(k=XX; k<=ZZ; k++)
494 x_refmol[j][k] = x_refmol[j][k] / 2;
499 /* Copy the indexed coordinates to a continuous array */
500 for(j=0; j<isize[G_SDF]; j++)
501 copy_rvec(x[index[G_SDF][j]],x_i1[j]);
504 for(j=0; j<isize[G_SDF]; j++)
506 /* Exclude peaks from the reference set */
508 for (k=NDX_REF1; k<=NDX_REF3; k++)
509 if ( index[G_SDF][j] == index[k][i] )
515 pbc_dx(&pbc,xi,x_i1[j],dx);
517 /* transfer dx to the molecular coordinate system */
518 mvmul(rot,dx,dx_mol);
521 /* check cutoff's and count */
522 if ( dx_mol[XX] > -cutoff[XX] && dx_mol[XX] < cutoff[XX] )
523 if ( dx_mol[YY] > -cutoff[YY] && dx_mol[YY] < cutoff[YY] )
524 if ( dx_mol[ZZ] > -cutoff[ZZ] && dx_mol[ZZ] < cutoff[ZZ] )
526 X = (int)(floor(dx_mol[XX]*invbinw)) + (nbin[XX]-1)/2
528 Y = (int)(floor(dx_mol[YY]*invbinw)) + (nbin[YY]-1)/2
530 Z = (int)(floor(dx_mol[ZZ]*invbinw)) + (nbin[ZZ]-1)/2
538 } while (read_next_x(oenv,status,&t,natoms,x,box));
539 fprintf(stderr,"\n");
541 gmx_rmpbc_done(gpbc);
549 /* write the reference strcture*/
552 fp=gmx_ffopen(fnREF,"w");
553 fprintf(fp,"%s\n",title);
554 fprintf(fp," %d\n",isize[G_REFMOL]);
557 for (i=0; i<isize[G_REFMOL]; i++)
558 fprintf(fp,"%5d%5s%5s%5d%8.3f%8.3f%8.3f\n",
559 top.atoms.resinfo[top.atoms.atom[index[G_REFMOL][i]].resind].nr,
560 *(top.atoms.resinfo[top.atoms.atom[index[G_REFMOL][i]].resind].name),
561 *(top.atoms.atomname[index[G_REFMOL][i]]),i+1,
562 -1*x_refmol[i][XX],-1*x_refmol[i][YY],-1*x_refmol[i][ZZ]);
563 /* Inserted -1* on the line above three times */
564 fprintf(fp," 10.00000 10.00000 10.00000\n");
566 fprintf(stderr,"\nWrote reference structure. (%d Atoms)\n",isize[G_REFMOL]);
570 /* Calculate the mean probability density */
571 fprintf(stderr,"\nNumber of configurations used for SDF: %d\n",(int)normfac);
574 normfac = nbin[0]*nbin[1]*nbin[2] / normfac;
577 fprintf(stderr,"\nMean probability density: %f\n",1/normfac);
580 /* normalize the SDF and write output */
581 /* see http://www.csc.fi/gopenmol/index.phtml for documentation */
582 fp=gmx_ffopen(fnSDF,"wb");
589 /* Type of surface */
593 /* Zdim, Ydim, Xdim */
594 for (i=ZZ; i>=XX; i--)
598 /* [Z,Y,X][min,max] (box corners in Angstroem)*/
599 for (i=ZZ; i>=XX; i--)
601 f_write(fp,-cutoff[i]*10);
602 f_write(fp,cutoff[i]*10);
607 for (i=1; i<nbin[2]+1; i++)
608 for (j=1; j<nbin[1]+1; j++)
609 for (k=1; k<nbin[0]+1; k++)
611 sdf = normfac * count[k][j][i];
612 if ( sdf < min_sdf ) min_sdf = sdf;
613 if ( sdf > max_sdf ) max_sdf = sdf;
616 /* Changed Code to Mirror SDF to correct coordinates */
617 for (i=nbin[2]; i>0; i--)
618 for (j=nbin[1]; j>0; j--)
619 for (k=nbin[0]; k>0; k--)
621 sdf = normfac * count[k][j][i];
622 if ( sdf < min_sdf ) min_sdf = sdf;
623 if ( sdf > max_sdf ) max_sdf = sdf;
627 fprintf(stderr,"\nMin: %f Max: %f\n",min_sdf,max_sdf);
633 /* Give back the mem */
634 for(i=0; i<nbin[0]+1; i++)
636 for (j=0; j<nbin[1]+1; j++)
645 int gmx_sdf(int argc,char *argv[])
647 const char *desc[] = {
648 "[TT]g_sdf[tt] calculates the spatial distribution function (SDF) of a set of atoms",
649 "within a coordinate system defined by three atoms. There is single body, ",
650 "two body and three body SDF implemented (select with option [TT]-mode[tt]). ",
651 "In the single body case the local coordinate system is defined by using",
652 "a triple of atoms from one single molecule, for the two and three body case",
653 "the configurations are dynamically searched complexes of two or three",
654 "molecules (or residues) meeting certain distance consitions (see below).[PAR]",
655 "The program needs a trajectory, a GROMACS run input file and an index ",
657 "You have to setup 4 groups in the index file before using g_sdf: [PAR]",
658 "The first three groups are used to define the SDF coordinate system.",
659 "The program will dynamically generate the atom triples according to ",
660 "the selected [TT]-mode[tt]: ",
661 "In [TT]-mode[tt] 1 the triples will be just the 1st, 2nd, 3rd, ... atoms from ",
662 "groups 1, 2 and 3. Hence the nth entries in groups 1, 2 and 3 must be from the",
663 "same residue. In [TT]-mode[tt] 2 the triples will be 1st, 2nd, 3rd, ... atoms from",
664 "groups 1 and 2 (with the nth entries in groups 1 and 2 having the same res-id).",
665 "For each pair from groups 1 and 2 group 3 is searched for an atom meeting the",
666 "distance conditions set with [TT]-triangle[tt] and [TT]-dtri[tt] relative to atoms 1 and 2. In",
667 "[TT]-mode[tt] 3 for each atom in group 1 group 2 is searched for an atom meeting the",
668 "distance condition and if a pair is found group 3 is searched for an atom",
669 "meeting the further conditions. The triple will only be used if all three atoms",
670 "have different res-id's.[PAR]",
671 "The local coordinate system is always defined using the following scheme:",
672 "Atom 1 will be used as the point of origin for the SDF. ",
673 "Atom 1 and 2 will define the principle axis (Z) of the coordinate system.",
674 "The other two axis will be defined inplane (Y) and normal (X) to the plane through",
675 "Atoms 1, 2 and 3. ",
677 "contains the atoms for which the SDF will be evaluated.[PAR]",
678 "For [TT]-mode[tt] 2 and 3 you have to define the distance conditions for the ",
679 "2 resp. 3 molecule complexes to be searched for using [TT]-triangle[tt] and [TT]-dtri[tt].[PAR]",
680 "The SDF will be sampled in cartesian coordinates.",
681 "Use [TT]-grid x y z[tt] to define the size of the SDF grid around the ",
682 "reference molecule. ",
683 "The Volume of the SDF grid will be V=x*y*z (nm^3). ",
684 "Use [TT]-bin[tt] to set the binwidth for grid.[PAR]",
685 "The output will be a binary 3D-grid file ([TT]gom_plt.dat[tt]) in the [TT].plt[tt] format that can be be",
686 "read directly by gOpenMol. ",
687 "The option [TT]-r[tt] will generate a [TT].gro[tt] file with the reference molecule(s) transferred to",
688 "the SDF coordinate system. Load this file into gOpenMol and display the",
689 "SDF as a contour plot (see http://www.csc.fi/gopenmol/index.phtml for ",
690 "further documentation). [PAR]",
691 "For further information about SDF's have a look at: A. Vishnyakov, JPC A, 105,",
692 "2001, 1702 and the references cited within."
695 static gmx_bool bRef=FALSE;
697 static rvec triangle={0.0,0.0,0.0};
698 static rvec dtri={0.03,0.03,0.03};
699 static rvec cutoff={1,1,1};
700 static real binwidth=0.05;
702 { "-mode", FALSE, etINT, {&mode},
703 "SDF in [1,2,3] particle mode"},
704 { "-triangle", FALSE, etRVEC, {&triangle},
705 "r(1,3), r(2,3), r(1,2)"},
706 { "-dtri", FALSE, etRVEC, {&dtri},
707 "dr(1,3), dr(2,3), dr(1,2)"},
708 { "-bin", FALSE, etREAL, {&binwidth},
709 "Binwidth for the 3D-grid (nm)" },
710 { "-grid", FALSE, etRVEC, {&cutoff},
711 "Size of the 3D-grid (nm,nm,nm)"}
713 #define NPA asize(pa)
714 const char *fnTPS,*fnNDX,*fnREF;
717 { efTRX, "-f", NULL, ffREAD },
718 { efNDX, NULL, NULL, ffREAD },
719 { efTPS, NULL, NULL, ffOPTRD },
720 { efDAT, "-o", "gom_plt", ffWRITE },
721 { efSTO, "-r", "refmol", ffOPTWR },
723 #define NFILE asize(fnm)
725 CopyRight(stderr,argv[0]);
726 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_BE_NICE,
727 NFILE,fnm,NPA,pa,asize(desc),desc,0,NULL,&oenv);
730 fnTPS = ftp2fn_null(efTPS,NFILE,fnm);
731 fnNDX = ftp2fn_null(efNDX,NFILE,fnm);
732 fnREF = opt2fn_null("-r",NFILE,fnm);
733 bRef = opt2bSet("-r",NFILE,fnm);
738 gmx_fatal(FARGS,"No index file specified\n"
743 gmx_fatal(FARGS,"No topology file specified\n"
747 if ( bRef && (fn2ftp(fnREF) != efGRO))
749 fprintf(stderr,"\nOnly GROMACS format is supported for reference structures.\n");
750 fprintf(stderr,"Option -r will be ignored!\n");
755 if ((mode < 1) || (mode > 3))
756 gmx_fatal(FARGS,"Wrong -mode selection. Chose 1-, 2- oder 3-particle mode.\n");
759 do_sdf(fnNDX,fnTPS,ftp2fn(efTRX,NFILE,fnm),opt2fn("-o",NFILE,fnm),
760 fnREF,bRef,cutoff,binwidth,mode,triangle,dtri,oenv);
769 main(int argc, char *argv[])