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
25 #include "gromacs/math/vec.h"
29 #include "gromacs/utility/futil.h"
30 #include "gromacs/commandline/pargs.h"
31 #include "gromacs/fileio/tpxio.h"
33 #include "gromacs/utility/smalloc.h"
36 #include "gromacs/fileio/matio.h"
37 #include "gromacs/utility/fatalerror.h"
49 static void i_write(FILE *output, int value)
51 if(fwrite(&value,sizeof(int),1,output) != 1)
53 gmx_fatal(FARGS,"Error writing to output file");
58 static void f_write(FILE *output,float value)
60 if(fwrite(&value,sizeof(float),1,output) != 1)
62 gmx_fatal(FARGS,"Error writing to output file");
67 static void do_sdf(const char *fnNDX,const char *fnTPS,const char *fnTRX,
68 const char *fnSDF, const char *fnREF, gmx_bool bRef,
69 rvec cutoff, real binwidth, int mode, rvec triangle,
70 rvec dtri, const output_env_t oenv)
74 int ng,natoms,i,j,k,l,X,Y,Z,lc,dest;
78 real sdf,min_sdf=1e10,max_sdf=0;
83 int ref_resind[3]={0};
86 atom_id *index_cg=NULL;
87 atom_id *index_ref=NULL;
88 real t,boxmin,hbox,normfac;
90 rvec tri_upper,tri_lower;
91 rvec *x,xcog,dx,*x_i1,xi,*x_refmol;
93 matrix rot; /* rotation matrix := unit vectors for the molecule frame */
94 rvec k_mol,i1_mol,i2_mol,dx_mol;
98 gmx_rmpbc_t gpbc=NULL;
101 gmx_bool bTop=FALSE,bRefDone=FALSE,bInGroup=FALSE;
107 bTop=read_tps_conf(fnTPS,title,&top,&ePBC,&x,NULL,box,TRUE);
113 fprintf(stderr,"\nNeed tpr-file to make a reference structure.\n");
114 fprintf(stderr,"Option -r will be ignored!\n");
119 /* Allocate memory for 4 groups, 3 dummy groups and a group for the ref
120 structure if needed */
123 /* the dummy groups are used to dynamically store triples of atoms */
124 /* for molecular coordinate systems */
137 /* Read the index groups */
138 fprintf(stderr,"\nSelect the 3 reference groups and the SDF group:\n");
140 get_index(&top.atoms,fnNDX,ng,isize,index,grpname);
142 rd_index(fnNDX,ng,isize,index,grpname);
145 isize[NDX_REF1]=isize[G_REF1];
146 for (i=NDX_REF1; i<=NDX_REF3; i++)
147 snew(index[i],isize[NDX_REF1]);
150 /* Read first frame and check it */
151 natoms=read_first_x(oenv,&status,fnTRX,&t,&x,box);
153 gmx_fatal(FARGS,"Could not read coordinates from statusfile!\n");
156 /* check with topology */
158 if ( natoms > top.atoms.nr )
160 "Trajectory (%d atoms) does not match topology (%d atoms)!\n",
161 natoms,top.atoms.nr);
164 /* check with index groups */
166 for (j=0; j<isize[i]; j++)
167 if ( index[i][j] >= natoms )
168 gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) larger "
169 "than number of atoms in trajectory (%d atoms)!\n",
170 index[i][j],grpname[i],isize[i],natoms);
173 /* check reference groups */
176 if ( isize[G_REF1] != isize[G_REF2] || isize[G_REF1] != isize[G_REF3] ||
177 isize[G_REF2] != isize[G_REF3] )
178 gmx_fatal(FARGS,"For single particle SDF, all reference groups"
179 "must have the same size.\n");
182 /* for single particle SDF dynamic triples are not needed */
183 /* so we build them right here */
186 /* copy all triples from G_REFx to NDX_REFx */
187 for (i=0; i<isize[G_REF1]; i++)
189 /* check if all three atoms come from the same molecule */
190 for (j=G_REF1; j<=G_REF3; j++)
191 ref_resind[j] = top.atoms.atom[index[j][i]].resind;
194 if ( ref_resind[G_REF1] != ref_resind[G_REF2] ||
195 ref_resind[G_REF2] != ref_resind[G_REF3] ||
196 ref_resind[G_REF3] != ref_resind[G_REF1] )
198 fprintf(stderr,"\nWarning: reference triple (%d) will be skipped.\n",i);
199 fprintf(stderr, " resnr[1]: %d, resnr[2]: %d, resnr[3]: %d\n",
200 ref_resind[G_REF1],ref_resind[G_REF2], ref_resind[G_REF3]);
202 for (j=NDX_REF1; j<=NDX_REF3; j++)
203 srenew(index[j],isize[NDX_REF1]);
207 /* check if all entries are unique*/
208 if ( index[G_REF1][i] == index[G_REF2][i] ||
209 index[G_REF2][i] == index[G_REF3][i] ||
210 index[G_REF3][i] == index[G_REF1][i] )
212 fprintf(stderr,"Warning: reference triple (%d) will be skipped.\n",i);
213 fprintf(stderr, " index[1]: %d, index[2]: %d, index[3]: %d\n",
214 index[G_REF1][i],index[G_REF2][i],index[G_REF3][i]);
216 for (j=NDX_REF1; j<=NDX_REF3; j++)
217 srenew(index[j],isize[NDX_REF1]);
220 else /* everythings fine, copy that one */
221 for (j=G_REF1; j<=G_REF3; j++)
222 index[j+4][i] = index[j][i];
225 else if ( mode == 2 )
227 if ( isize[G_REF1] != isize[G_REF2] )
228 gmx_fatal(FARGS,"For two particle SDF, reference groups 1 and 2"
229 "must have the same size.\n");
232 for (i=0; i<isize[G_REF1]; i++)
234 /* check consistency for atoms 1 and 2 */
235 for (j=G_REF1; j<=G_REF2; j++)
236 ref_resind[j] = top.atoms.atom[index[j][i]].resind;
239 if ( ref_resind[G_REF1] != ref_resind[G_REF2] ||
240 index[G_REF1][i] == index[G_REF2][i] )
242 if ( ref_resind[G_REF1] != ref_resind[G_REF2] )
244 fprintf(stderr,"\nWarning: bond (%d) not from one molecule."
245 "Will not be used for SDF.\n",i);
246 fprintf(stderr, " resnr[1]: %d, resnr[2]: %d\n",
247 ref_resind[G_REF1],ref_resind[G_REF2]);
251 fprintf(stderr,"\nWarning: atom1 and atom2 are identical."
252 "Bond (%d) will not be used for SDF.\n",i);
253 fprintf(stderr, " index[1]: %d, index[2]: %d\n",
254 index[G_REF1][i],index[G_REF2][i]);
256 for (j=NDX_REF1; j<=NDX_REF2; j++)
258 for (k=i; k<isize[G_REF1]-1; k++)
259 index[j][k]=index[j][k+1];
261 srenew(index[j],isize[j]);
268 /* Read Atoms for refmol group */
271 snew(index[G_REFMOL],1);
274 for (i=G_REF1; i<=G_REF3; i++)
275 ref_resind[i] = top.atoms.atom[index[i][0]].resind;
278 for (i=0; i<natoms; i++)
280 if ( ref_resind[G_REF1] == top.atoms.atom[i].resind ||
281 ref_resind[G_REF2] == top.atoms.atom[i].resind ||
282 ref_resind[G_REF3] == top.atoms.atom[i].resind )
285 srenew(index[G_REFMOL],nrefmol);
286 isize[G_REFMOL] = nrefmol;
290 for (i=0; i<natoms; i++)
292 if ( ref_resind[G_REF1] == top.atoms.atom[i].resind ||
293 ref_resind[G_REF2] == top.atoms.atom[i].resind ||
294 ref_resind[G_REF3] == top.atoms.atom[i].resind )
296 index[G_REFMOL][nrefmol] = i;
303 /* initialize some stuff */
304 boxmin = min( norm(box[XX]), min( norm(box[YY]), norm(box[ZZ]) ) );
308 for (i=0; i<DIM; i++)
310 cutoff[i] = cutoff[i] / 2;
311 nbin[i] = (int)(2 * cutoff[i] / binwidth) + 1;
312 invbinw = 1.0 / binwidth;
313 tri_upper[i] = triangle[i] + dtri[i];
314 tri_upper[i] = sqr(tri_upper[i]);
315 tri_lower[i] = triangle[i] - dtri[i];
316 tri_lower[i] = sqr(tri_lower[i]);
320 /* Allocate the array's for sdf */
321 snew(count,nbin[0]+1);
322 for(i=0; i<nbin[0]+1; i++)
324 snew(count[i],nbin[1]+1);
325 for (j=0; j<nbin[1]+1; j++)
326 snew(count[i][j],nbin[2]+1);
330 /* Allocate space for the coordinates */
331 snew(x_i1,isize[G_SDF]);
332 snew(x_refmol,isize[G_REFMOL]);
333 for (i=0; i<isize[G_REFMOL]; i++)
334 for (j=XX; j<=ZZ; j++)
340 gpbc = gmx_rmpbc_init(&top.idef,ePBC,natoms,box);
343 /* Must init pbc every step because of pressure coupling */
344 set_pbc(&pbc,ePBC,box);
345 gmx_rmpbc(gpbc,natoms,box,x);
347 /* Dynamically build the ref triples */
351 for (j=NDX_REF1; j<=NDX_REF3; j++)
352 srenew(index[j],isize[NDX_REF1]+1);
355 /* consistancy of G_REF[1,2] has already been check */
356 /* hence we can look for the third atom right away */
359 for (i=0; i<isize[G_REF1]; i++)
361 for (j=0; j<isize[G_REF3]; j++)
363 /* Avoid expensive stuff if possible */
364 if ( top.atoms.atom[index[G_REF1][i]].resind !=
365 top.atoms.atom[index[G_REF3][j]].resind &&
366 index[G_REF1][i] != index[G_REF3][j] &&
367 index[G_REF2][i] != index[G_REF3][j] )
369 pbc_dx(&pbc,x[index[G_REF1][i]],x[index[G_REF3][j]],dx);
371 if ( delta < tri_upper[G_REF1] &&
372 delta > tri_lower[G_REF1] )
374 pbc_dx(&pbc,x[index[G_REF2][i]],x[index[G_REF3][j]],dx);
376 if ( delta < tri_upper[G_REF2] &&
377 delta > tri_lower[G_REF2] )
380 index[NDX_REF1][isize[NDX_REF1]]=index[G_REF1][i];
381 index[NDX_REF2][isize[NDX_REF1]]=index[G_REF2][i];
382 index[NDX_REF3][isize[NDX_REF1]]=index[G_REF3][j];
387 for (k=NDX_REF1; k<=NDX_REF3; k++)
388 srenew(index[k],isize[NDX_REF1]+1);
398 for (j=NDX_REF1; j<=NDX_REF3; j++)
399 srenew(index[j],isize[NDX_REF1]+1);
401 /* consistancy will be checked while searching */
404 for (i=0; i<isize[G_REF1]; i++)
406 for (j=0; j<isize[G_REF2]; j++)
408 /* Avoid expensive stuff if possible */
409 if ( top.atoms.atom[index[G_REF1][i]].resind !=
410 top.atoms.atom[index[G_REF2][j]].resind &&
411 index[G_REF1][i] != index[G_REF2][j] )
413 pbc_dx(&pbc,x[index[G_REF1][i]],x[index[G_REF2][j]],dx);
415 if ( delta < tri_upper[G_REF3] &&
416 delta > tri_lower[G_REF3] )
418 for (k=0; k<isize[G_REF3]; k++)
420 if ( top.atoms.atom[index[G_REF1][i]].resind !=
421 top.atoms.atom[index[G_REF3][k]].resind &&
422 top.atoms.atom[index[G_REF2][j]].resind !=
423 top.atoms.atom[index[G_REF3][k]].resind &&
424 index[G_REF1][i] != index[G_REF3][k] &&
425 index[G_REF2][j] != index[G_REF3][k])
427 pbc_dx(&pbc,x[index[G_REF1][i]],x[index[G_REF3][k]],dx);
429 if ( delta < tri_upper[G_REF1] &&
430 delta > tri_lower[G_REF1] )
432 pbc_dx(&pbc,x[index[G_REF2][j]],x[index[G_REF3][k]],dx);
434 if ( delta < tri_upper[G_REF2] &&
435 delta > tri_lower[G_REF2] )
438 index[NDX_REF1][isize[NDX_REF1]]=index[G_REF1][i];
439 index[NDX_REF2][isize[NDX_REF1]]=index[G_REF2][j];
440 index[NDX_REF3][isize[NDX_REF1]]=index[G_REF3][k];
444 for (l=NDX_REF1; l<=NDX_REF3; l++)
445 srenew(index[l],isize[NDX_REF1]+1);
456 for (i=0; i<isize[NDX_REF1]; i++)
458 /* setup the molecular coordinate system (i',j',k') */
459 /* because the coodinate system of the box forms a unit matrix */
460 /* (i',j',k') is identical with the rotation matrix */
464 /* k' = unitv(r(atom0) - r(atom1)) */
465 pbc_dx(&pbc,x[index[NDX_REF1][i]],x[index[NDX_REF2][i]],k_mol);
468 /* i' = unitv(k' x (r(atom2) - r(atom1))) */
469 pbc_dx(&pbc,x[index[NDX_REF3][i]],x[index[NDX_REF2][i]],i1_mol);
470 cprod(i1_mol,rot[2],i2_mol);
471 unitv(i2_mol,rot[0]);
474 cprod(rot[2],rot[0],rot[1]);
477 /* set the point of reference */
479 copy_rvec(x[index[NDX_REF3][i]],xi);
481 copy_rvec(x[index[NDX_REF1][i]],xi);
484 /* make the reference */
487 for (j=0; j<isize[G_REFMOL]; j++)
489 pbc_dx(&pbc,xi,x[index[G_REFMOL][j]],dx);
490 mvmul(rot,dx,dx_mol);
491 rvec_inc(x_refmol[j],dx_mol);
492 for(k=XX; k<=ZZ; k++)
493 x_refmol[j][k] = x_refmol[j][k] / 2;
498 /* Copy the indexed coordinates to a continuous array */
499 for(j=0; j<isize[G_SDF]; j++)
500 copy_rvec(x[index[G_SDF][j]],x_i1[j]);
503 for(j=0; j<isize[G_SDF]; j++)
505 /* Exclude peaks from the reference set */
507 for (k=NDX_REF1; k<=NDX_REF3; k++)
508 if ( index[G_SDF][j] == index[k][i] )
514 pbc_dx(&pbc,xi,x_i1[j],dx);
516 /* transfer dx to the molecular coordinate system */
517 mvmul(rot,dx,dx_mol);
520 /* check cutoff's and count */
521 if ( dx_mol[XX] > -cutoff[XX] && dx_mol[XX] < cutoff[XX] )
522 if ( dx_mol[YY] > -cutoff[YY] && dx_mol[YY] < cutoff[YY] )
523 if ( dx_mol[ZZ] > -cutoff[ZZ] && dx_mol[ZZ] < cutoff[ZZ] )
525 X = (int)(floor(dx_mol[XX]*invbinw)) + (nbin[XX]-1)/2
527 Y = (int)(floor(dx_mol[YY]*invbinw)) + (nbin[YY]-1)/2
529 Z = (int)(floor(dx_mol[ZZ]*invbinw)) + (nbin[ZZ]-1)/2
537 } while (read_next_x(oenv,status,&t,natoms,x,box));
538 fprintf(stderr,"\n");
540 gmx_rmpbc_done(gpbc);
548 /* write the reference strcture*/
551 fp=gmx_ffopen(fnREF,"w");
552 fprintf(fp,"%s\n",title);
553 fprintf(fp," %d\n",isize[G_REFMOL]);
556 for (i=0; i<isize[G_REFMOL]; i++)
557 fprintf(fp,"%5d%5s%5s%5d%8.3f%8.3f%8.3f\n",
558 top.atoms.resinfo[top.atoms.atom[index[G_REFMOL][i]].resind].nr,
559 *(top.atoms.resinfo[top.atoms.atom[index[G_REFMOL][i]].resind].name),
560 *(top.atoms.atomname[index[G_REFMOL][i]]),i+1,
561 -1*x_refmol[i][XX],-1*x_refmol[i][YY],-1*x_refmol[i][ZZ]);
562 /* Inserted -1* on the line above three times */
563 fprintf(fp," 10.00000 10.00000 10.00000\n");
565 fprintf(stderr,"\nWrote reference structure. (%d Atoms)\n",isize[G_REFMOL]);
569 /* Calculate the mean probability density */
570 fprintf(stderr,"\nNumber of configurations used for SDF: %d\n",(int)normfac);
573 normfac = nbin[0]*nbin[1]*nbin[2] / normfac;
576 fprintf(stderr,"\nMean probability density: %f\n",1/normfac);
579 /* normalize the SDF and write output */
580 /* see http://www.csc.fi/gopenmol/index.phtml for documentation */
581 fp=gmx_ffopen(fnSDF,"wb");
588 /* Type of surface */
592 /* Zdim, Ydim, Xdim */
593 for (i=ZZ; i>=XX; i--)
597 /* [Z,Y,X][min,max] (box corners in Angstroem)*/
598 for (i=ZZ; i>=XX; i--)
600 f_write(fp,-cutoff[i]*10);
601 f_write(fp,cutoff[i]*10);
606 for (i=1; i<nbin[2]+1; i++)
607 for (j=1; j<nbin[1]+1; j++)
608 for (k=1; k<nbin[0]+1; k++)
610 sdf = normfac * count[k][j][i];
611 if ( sdf < min_sdf ) min_sdf = sdf;
612 if ( sdf > max_sdf ) max_sdf = sdf;
615 /* Changed Code to Mirror SDF to correct coordinates */
616 for (i=nbin[2]; i>0; i--)
617 for (j=nbin[1]; j>0; j--)
618 for (k=nbin[0]; k>0; k--)
620 sdf = normfac * count[k][j][i];
621 if ( sdf < min_sdf ) min_sdf = sdf;
622 if ( sdf > max_sdf ) max_sdf = sdf;
626 fprintf(stderr,"\nMin: %f Max: %f\n",min_sdf,max_sdf);
632 /* Give back the mem */
633 for(i=0; i<nbin[0]+1; i++)
635 for (j=0; j<nbin[1]+1; j++)
644 int gmx_sdf(int argc,char *argv[])
646 const char *desc[] = {
647 "[TT]g_sdf[tt] calculates the spatial distribution function (SDF) of a set of atoms",
648 "within a coordinate system defined by three atoms. There is single body, ",
649 "two body and three body SDF implemented (select with option [TT]-mode[tt]). ",
650 "In the single body case the local coordinate system is defined by using",
651 "a triple of atoms from one single molecule, for the two and three body case",
652 "the configurations are dynamically searched complexes of two or three",
653 "molecules (or residues) meeting certain distance consitions (see below).[PAR]",
654 "The program needs a trajectory, a GROMACS run input file and an index ",
656 "You have to setup 4 groups in the index file before using g_sdf: [PAR]",
657 "The first three groups are used to define the SDF coordinate system.",
658 "The program will dynamically generate the atom triples according to ",
659 "the selected [TT]-mode[tt]: ",
660 "In [TT]-mode[tt] 1 the triples will be just the 1st, 2nd, 3rd, ... atoms from ",
661 "groups 1, 2 and 3. Hence the nth entries in groups 1, 2 and 3 must be from the",
662 "same residue. In [TT]-mode[tt] 2 the triples will be 1st, 2nd, 3rd, ... atoms from",
663 "groups 1 and 2 (with the nth entries in groups 1 and 2 having the same res-id).",
664 "For each pair from groups 1 and 2 group 3 is searched for an atom meeting the",
665 "distance conditions set with [TT]-triangle[tt] and [TT]-dtri[tt] relative to atoms 1 and 2. In",
666 "[TT]-mode[tt] 3 for each atom in group 1 group 2 is searched for an atom meeting the",
667 "distance condition and if a pair is found group 3 is searched for an atom",
668 "meeting the further conditions. The triple will only be used if all three atoms",
669 "have different res-id's.[PAR]",
670 "The local coordinate system is always defined using the following scheme:",
671 "Atom 1 will be used as the point of origin for the SDF. ",
672 "Atom 1 and 2 will define the principle axis (Z) of the coordinate system.",
673 "The other two axis will be defined inplane (Y) and normal (X) to the plane through",
674 "Atoms 1, 2 and 3. ",
676 "contains the atoms for which the SDF will be evaluated.[PAR]",
677 "For [TT]-mode[tt] 2 and 3 you have to define the distance conditions for the ",
678 "2 resp. 3 molecule complexes to be searched for using [TT]-triangle[tt] and [TT]-dtri[tt].[PAR]",
679 "The SDF will be sampled in cartesian coordinates.",
680 "Use [TT]-grid x y z[tt] to define the size of the SDF grid around the ",
681 "reference molecule. ",
682 "The Volume of the SDF grid will be V=x*y*z (nm^3). ",
683 "Use [TT]-bin[tt] to set the binwidth for grid.[PAR]",
684 "The output will be a binary 3D-grid file ([TT]gom_plt.dat[tt]) in the [TT].plt[tt] format that can be be",
685 "read directly by gOpenMol. ",
686 "The option [TT]-r[tt] will generate a [TT].gro[tt] file with the reference molecule(s) transferred to",
687 "the SDF coordinate system. Load this file into gOpenMol and display the",
688 "SDF as a contour plot (see http://www.csc.fi/gopenmol/index.phtml for ",
689 "further documentation). [PAR]",
690 "For further information about SDF's have a look at: A. Vishnyakov, JPC A, 105,",
691 "2001, 1702 and the references cited within."
694 static gmx_bool bRef=FALSE;
696 static rvec triangle={0.0,0.0,0.0};
697 static rvec dtri={0.03,0.03,0.03};
698 static rvec cutoff={1,1,1};
699 static real binwidth=0.05;
701 { "-mode", FALSE, etINT, {&mode},
702 "SDF in [1,2,3] particle mode"},
703 { "-triangle", FALSE, etRVEC, {&triangle},
704 "r(1,3), r(2,3), r(1,2)"},
705 { "-dtri", FALSE, etRVEC, {&dtri},
706 "dr(1,3), dr(2,3), dr(1,2)"},
707 { "-bin", FALSE, etREAL, {&binwidth},
708 "Binwidth for the 3D-grid (nm)" },
709 { "-grid", FALSE, etRVEC, {&cutoff},
710 "Size of the 3D-grid (nm,nm,nm)"}
712 #define NPA asize(pa)
713 const char *fnTPS,*fnNDX,*fnREF;
716 { efTRX, "-f", NULL, ffREAD },
717 { efNDX, NULL, NULL, ffREAD },
718 { efTPS, NULL, NULL, ffOPTRD },
719 { efDAT, "-o", "gom_plt", ffWRITE },
720 { efSTO, "-r", "refmol", ffOPTWR },
722 #define NFILE asize(fnm)
724 CopyRight(stderr,argv[0]);
725 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_BE_NICE,
726 NFILE,fnm,NPA,pa,asize(desc),desc,0,NULL,&oenv);
729 fnTPS = ftp2fn_null(efTPS,NFILE,fnm);
730 fnNDX = ftp2fn_null(efNDX,NFILE,fnm);
731 fnREF = opt2fn_null("-r",NFILE,fnm);
732 bRef = opt2bSet("-r",NFILE,fnm);
737 gmx_fatal(FARGS,"No index file specified\n"
742 gmx_fatal(FARGS,"No topology file specified\n"
746 if ( bRef && (fn2ftp(fnREF) != efGRO))
748 fprintf(stderr,"\nOnly GROMACS format is supported for reference structures.\n");
749 fprintf(stderr,"Option -r will be ignored!\n");
754 if ((mode < 1) || (mode > 3))
755 gmx_fatal(FARGS,"Wrong -mode selection. Chose 1-, 2- oder 3-particle mode.\n");
758 do_sdf(fnNDX,fnTPS,ftp2fn(efTRX,NFILE,fnm),opt2fn("-o",NFILE,fnm),
759 fnREF,bRef,cutoff,binwidth,mode,triangle,dtri,oenv);
768 main(int argc, char *argv[])