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
41 #include "gmx_fatal.h"
53 static void i_write(FILE *output, int value)
55 if(fwrite(&value,sizeof(int),1,output) != 1)
57 gmx_fatal(FARGS,"Error writing to output file");
62 static void f_write(FILE *output,float value)
64 if(fwrite(&value,sizeof(float),1,output) != 1)
66 gmx_fatal(FARGS,"Error writing to output file");
71 static void do_sdf(const char *fnNDX,const char *fnTPS,const char *fnTRX,
72 const char *fnSDF, const char *fnREF, gmx_bool bRef,
73 rvec cutoff, real binwidth, int mode, rvec triangle,
74 rvec dtri, const output_env_t oenv)
78 int ng,natoms,i,j,k,l,X,Y,Z,lc,dest;
82 real sdf,min_sdf=1e10,max_sdf=0;
87 int ref_resind[3]={0};
90 atom_id *index_cg=NULL;
91 atom_id *index_ref=NULL;
92 real t,boxmin,hbox,normfac;
94 rvec tri_upper,tri_lower;
95 rvec *x,xcog,dx,*x_i1,xi,*x_refmol;
97 matrix rot; /* rotation matrix := unit vectors for the molecule frame */
98 rvec k_mol,i1_mol,i2_mol,dx_mol;
102 gmx_rmpbc_t gpbc=NULL;
105 gmx_bool bTop=FALSE,bRefDone=FALSE,bInGroup=FALSE;
111 bTop=read_tps_conf(fnTPS,title,&top,&ePBC,&x,NULL,box,TRUE);
117 fprintf(stderr,"\nNeed tpr-file to make a reference structure.\n");
118 fprintf(stderr,"Option -r will be ignored!\n");
123 /* Allocate memory for 4 groups, 3 dummy groups and a group for the ref
124 structure if needed */
127 /* the dummy groups are used to dynamically store triples of atoms */
128 /* for molecular coordinate systems */
141 /* Read the index groups */
142 fprintf(stderr,"\nSelect the 3 reference groups and the SDF group:\n");
144 get_index(&top.atoms,fnNDX,ng,isize,index,grpname);
146 rd_index(fnNDX,ng,isize,index,grpname);
149 isize[NDX_REF1]=isize[G_REF1];
150 for (i=NDX_REF1; i<=NDX_REF3; i++)
151 snew(index[i],isize[NDX_REF1]);
154 /* Read first frame and check it */
155 natoms=read_first_x(oenv,&status,fnTRX,&t,&x,box);
157 gmx_fatal(FARGS,"Could not read coordinates from statusfile!\n");
160 /* check with topology */
162 if ( natoms > top.atoms.nr )
164 "Trajectory (%d atoms) does not match topology (%d atoms)!\n",
165 natoms,top.atoms.nr);
168 /* check with index groups */
170 for (j=0; j<isize[i]; j++)
171 if ( index[i][j] >= natoms )
172 gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) larger "
173 "than number of atoms in trajectory (%d atoms)!\n",
174 index[i][j],grpname[i],isize[i],natoms);
177 /* check reference groups */
180 if ( isize[G_REF1] != isize[G_REF2] || isize[G_REF1] != isize[G_REF3] ||
181 isize[G_REF2] != isize[G_REF3] )
182 gmx_fatal(FARGS,"For single particle SDF, all reference groups"
183 "must have the same size.\n");
186 /* for single particle SDF dynamic triples are not needed */
187 /* so we build them right here */
190 /* copy all triples from G_REFx to NDX_REFx */
191 for (i=0; i<isize[G_REF1]; i++)
193 /* check if all three atoms come from the same molecule */
194 for (j=G_REF1; j<=G_REF3; j++)
195 ref_resind[j] = top.atoms.atom[index[j][i]].resind;
198 if ( ref_resind[G_REF1] != ref_resind[G_REF2] ||
199 ref_resind[G_REF2] != ref_resind[G_REF3] ||
200 ref_resind[G_REF3] != ref_resind[G_REF1] )
202 fprintf(stderr,"\nWarning: reference triple (%d) will be skipped.\n",i);
203 fprintf(stderr, " resnr[1]: %d, resnr[2]: %d, resnr[3]: %d\n",
204 ref_resind[G_REF1],ref_resind[G_REF2], ref_resind[G_REF3]);
206 for (j=NDX_REF1; j<=NDX_REF3; j++)
207 srenew(index[j],isize[NDX_REF1]);
211 /* check if all entries are unique*/
212 if ( index[G_REF1][i] == index[G_REF2][i] ||
213 index[G_REF2][i] == index[G_REF3][i] ||
214 index[G_REF3][i] == index[G_REF1][i] )
216 fprintf(stderr,"Warning: reference triple (%d) will be skipped.\n",i);
217 fprintf(stderr, " index[1]: %d, index[2]: %d, index[3]: %d\n",
218 index[G_REF1][i],index[G_REF2][i],index[G_REF3][i]);
220 for (j=NDX_REF1; j<=NDX_REF3; j++)
221 srenew(index[j],isize[NDX_REF1]);
224 else /* everythings fine, copy that one */
225 for (j=G_REF1; j<=G_REF3; j++)
226 index[j+4][i] = index[j][i];
229 else if ( mode == 2 )
231 if ( isize[G_REF1] != isize[G_REF2] )
232 gmx_fatal(FARGS,"For two particle SDF, reference groups 1 and 2"
233 "must have the same size.\n");
236 for (i=0; i<isize[G_REF1]; i++)
238 /* check consistency for atoms 1 and 2 */
239 for (j=G_REF1; j<=G_REF2; j++)
240 ref_resind[j] = top.atoms.atom[index[j][i]].resind;
243 if ( ref_resind[G_REF1] != ref_resind[G_REF2] ||
244 index[G_REF1][i] == index[G_REF2][i] )
246 if ( ref_resind[G_REF1] != ref_resind[G_REF2] )
248 fprintf(stderr,"\nWarning: bond (%d) not from one molecule."
249 "Will not be used for SDF.\n",i);
250 fprintf(stderr, " resnr[1]: %d, resnr[2]: %d\n",
251 ref_resind[G_REF1],ref_resind[G_REF2]);
255 fprintf(stderr,"\nWarning: atom1 and atom2 are identical."
256 "Bond (%d) will not be used for SDF.\n",i);
257 fprintf(stderr, " index[1]: %d, index[2]: %d\n",
258 index[G_REF1][i],index[G_REF2][i]);
260 for (j=NDX_REF1; j<=NDX_REF2; j++)
262 for (k=i; k<isize[G_REF1]-1; k++)
263 index[j][k]=index[j][k+1];
265 srenew(index[j],isize[j]);
272 /* Read Atoms for refmol group */
275 snew(index[G_REFMOL],1);
278 for (i=G_REF1; i<=G_REF3; i++)
279 ref_resind[i] = top.atoms.atom[index[i][0]].resind;
282 for (i=0; i<natoms; i++)
284 if ( ref_resind[G_REF1] == top.atoms.atom[i].resind ||
285 ref_resind[G_REF2] == top.atoms.atom[i].resind ||
286 ref_resind[G_REF3] == top.atoms.atom[i].resind )
289 srenew(index[G_REFMOL],nrefmol);
290 isize[G_REFMOL] = nrefmol;
294 for (i=0; i<natoms; i++)
296 if ( ref_resind[G_REF1] == top.atoms.atom[i].resind ||
297 ref_resind[G_REF2] == top.atoms.atom[i].resind ||
298 ref_resind[G_REF3] == top.atoms.atom[i].resind )
300 index[G_REFMOL][nrefmol] = i;
307 /* initialize some stuff */
308 boxmin = min( norm(box[XX]), min( norm(box[YY]), norm(box[ZZ]) ) );
312 for (i=0; i<DIM; i++)
314 cutoff[i] = cutoff[i] / 2;
315 nbin[i] = (int)(2 * cutoff[i] / binwidth) + 1;
316 invbinw = 1.0 / binwidth;
317 tri_upper[i] = triangle[i] + dtri[i];
318 tri_upper[i] = sqr(tri_upper[i]);
319 tri_lower[i] = triangle[i] - dtri[i];
320 tri_lower[i] = sqr(tri_lower[i]);
324 /* Allocate the array's for sdf */
325 snew(count,nbin[0]+1);
326 for(i=0; i<nbin[0]+1; i++)
328 snew(count[i],nbin[1]+1);
329 for (j=0; j<nbin[1]+1; j++)
330 snew(count[i][j],nbin[2]+1);
334 /* Allocate space for the coordinates */
335 snew(x_i1,isize[G_SDF]);
336 snew(x_refmol,isize[G_REFMOL]);
337 for (i=0; i<isize[G_REFMOL]; i++)
338 for (j=XX; j<=ZZ; j++)
344 gpbc = gmx_rmpbc_init(&top.idef,ePBC,natoms,box);
347 /* Must init pbc every step because of pressure coupling */
348 set_pbc(&pbc,ePBC,box);
349 gmx_rmpbc(gpbc,natoms,box,x);
351 /* Dynamically build the ref triples */
355 for (j=NDX_REF1; j<=NDX_REF3; j++)
356 srenew(index[j],isize[NDX_REF1]+1);
359 /* consistancy of G_REF[1,2] has already been check */
360 /* hence we can look for the third atom right away */
363 for (i=0; i<isize[G_REF1]; i++)
365 for (j=0; j<isize[G_REF3]; j++)
367 /* Avoid expensive stuff if possible */
368 if ( top.atoms.atom[index[G_REF1][i]].resind !=
369 top.atoms.atom[index[G_REF3][j]].resind &&
370 index[G_REF1][i] != index[G_REF3][j] &&
371 index[G_REF2][i] != index[G_REF3][j] )
373 pbc_dx(&pbc,x[index[G_REF1][i]],x[index[G_REF3][j]],dx);
375 if ( delta < tri_upper[G_REF1] &&
376 delta > tri_lower[G_REF1] )
378 pbc_dx(&pbc,x[index[G_REF2][i]],x[index[G_REF3][j]],dx);
380 if ( delta < tri_upper[G_REF2] &&
381 delta > tri_lower[G_REF2] )
384 index[NDX_REF1][isize[NDX_REF1]]=index[G_REF1][i];
385 index[NDX_REF2][isize[NDX_REF1]]=index[G_REF2][i];
386 index[NDX_REF3][isize[NDX_REF1]]=index[G_REF3][j];
391 for (k=NDX_REF1; k<=NDX_REF3; k++)
392 srenew(index[k],isize[NDX_REF1]+1);
402 for (j=NDX_REF1; j<=NDX_REF3; j++)
403 srenew(index[j],isize[NDX_REF1]+1);
405 /* consistancy will be checked while searching */
408 for (i=0; i<isize[G_REF1]; i++)
410 for (j=0; j<isize[G_REF2]; j++)
412 /* Avoid expensive stuff if possible */
413 if ( top.atoms.atom[index[G_REF1][i]].resind !=
414 top.atoms.atom[index[G_REF2][j]].resind &&
415 index[G_REF1][i] != index[G_REF2][j] )
417 pbc_dx(&pbc,x[index[G_REF1][i]],x[index[G_REF2][j]],dx);
419 if ( delta < tri_upper[G_REF3] &&
420 delta > tri_lower[G_REF3] )
422 for (k=0; k<isize[G_REF3]; k++)
424 if ( top.atoms.atom[index[G_REF1][i]].resind !=
425 top.atoms.atom[index[G_REF3][k]].resind &&
426 top.atoms.atom[index[G_REF2][j]].resind !=
427 top.atoms.atom[index[G_REF3][k]].resind &&
428 index[G_REF1][i] != index[G_REF3][k] &&
429 index[G_REF2][j] != index[G_REF3][k])
431 pbc_dx(&pbc,x[index[G_REF1][i]],x[index[G_REF3][k]],dx);
433 if ( delta < tri_upper[G_REF1] &&
434 delta > tri_lower[G_REF1] )
436 pbc_dx(&pbc,x[index[G_REF2][j]],x[index[G_REF3][k]],dx);
438 if ( delta < tri_upper[G_REF2] &&
439 delta > tri_lower[G_REF2] )
442 index[NDX_REF1][isize[NDX_REF1]]=index[G_REF1][i];
443 index[NDX_REF2][isize[NDX_REF1]]=index[G_REF2][j];
444 index[NDX_REF3][isize[NDX_REF1]]=index[G_REF3][k];
448 for (l=NDX_REF1; l<=NDX_REF3; l++)
449 srenew(index[l],isize[NDX_REF1]+1);
460 for (i=0; i<isize[NDX_REF1]; i++)
462 /* setup the molecular coordinate system (i',j',k') */
463 /* because the coodinate system of the box forms a unit matrix */
464 /* (i',j',k') is identical with the rotation matrix */
468 /* k' = unitv(r(atom0) - r(atom1)) */
469 pbc_dx(&pbc,x[index[NDX_REF1][i]],x[index[NDX_REF2][i]],k_mol);
472 /* i' = unitv(k' x (r(atom2) - r(atom1))) */
473 pbc_dx(&pbc,x[index[NDX_REF3][i]],x[index[NDX_REF2][i]],i1_mol);
474 cprod(i1_mol,rot[2],i2_mol);
475 unitv(i2_mol,rot[0]);
478 cprod(rot[2],rot[0],rot[1]);
481 /* set the point of reference */
483 copy_rvec(x[index[NDX_REF3][i]],xi);
485 copy_rvec(x[index[NDX_REF1][i]],xi);
488 /* make the reference */
491 for (j=0; j<isize[G_REFMOL]; j++)
493 pbc_dx(&pbc,xi,x[index[G_REFMOL][j]],dx);
494 mvmul(rot,dx,dx_mol);
495 rvec_inc(x_refmol[j],dx_mol);
496 for(k=XX; k<=ZZ; k++)
497 x_refmol[j][k] = x_refmol[j][k] / 2;
502 /* Copy the indexed coordinates to a continuous array */
503 for(j=0; j<isize[G_SDF]; j++)
504 copy_rvec(x[index[G_SDF][j]],x_i1[j]);
507 for(j=0; j<isize[G_SDF]; j++)
509 /* Exclude peaks from the reference set */
511 for (k=NDX_REF1; k<=NDX_REF3; k++)
512 if ( index[G_SDF][j] == index[k][i] )
518 pbc_dx(&pbc,xi,x_i1[j],dx);
520 /* transfer dx to the molecular coordinate system */
521 mvmul(rot,dx,dx_mol);
524 /* check cutoff's and count */
525 if ( dx_mol[XX] > -cutoff[XX] && dx_mol[XX] < cutoff[XX] )
526 if ( dx_mol[YY] > -cutoff[YY] && dx_mol[YY] < cutoff[YY] )
527 if ( dx_mol[ZZ] > -cutoff[ZZ] && dx_mol[ZZ] < cutoff[ZZ] )
529 X = (int)(floor(dx_mol[XX]*invbinw)) + (nbin[XX]-1)/2
531 Y = (int)(floor(dx_mol[YY]*invbinw)) + (nbin[YY]-1)/2
533 Z = (int)(floor(dx_mol[ZZ]*invbinw)) + (nbin[ZZ]-1)/2
541 } while (read_next_x(oenv,status,&t,natoms,x,box));
542 fprintf(stderr,"\n");
544 gmx_rmpbc_done(gpbc);
552 /* write the reference strcture*/
555 fp=ffopen(fnREF,"w");
556 fprintf(fp,"%s\n",title);
557 fprintf(fp," %d\n",isize[G_REFMOL]);
560 for (i=0; i<isize[G_REFMOL]; i++)
561 fprintf(fp,"%5d%5s%5s%5d%8.3f%8.3f%8.3f\n",
562 top.atoms.resinfo[top.atoms.atom[index[G_REFMOL][i]].resind].nr,
563 *(top.atoms.resinfo[top.atoms.atom[index[G_REFMOL][i]].resind].name),
564 *(top.atoms.atomname[index[G_REFMOL][i]]),i+1,
565 -1*x_refmol[i][XX],-1*x_refmol[i][YY],-1*x_refmol[i][ZZ]);
566 /* Inserted -1* on the line above three times */
567 fprintf(fp," 10.00000 10.00000 10.00000\n");
569 fprintf(stderr,"\nWrote reference structure. (%d Atoms)\n",isize[G_REFMOL]);
573 /* Calculate the mean probability density */
574 fprintf(stderr,"\nNumber of configurations used for SDF: %d\n",(int)normfac);
577 normfac = nbin[0]*nbin[1]*nbin[2] / normfac;
580 fprintf(stderr,"\nMean probability density: %f\n",1/normfac);
583 /* normalize the SDF and write output */
584 /* see http://www.csc.fi/gopenmol/index.phtml for documentation */
585 fp=ffopen(fnSDF,"wb");
592 /* Type of surface */
596 /* Zdim, Ydim, Xdim */
597 for (i=ZZ; i>=XX; i--)
601 /* [Z,Y,X][min,max] (box corners in Angstroem)*/
602 for (i=ZZ; i>=XX; i--)
604 f_write(fp,-cutoff[i]*10);
605 f_write(fp,cutoff[i]*10);
610 for (i=1; i<nbin[2]+1; i++)
611 for (j=1; j<nbin[1]+1; j++)
612 for (k=1; k<nbin[0]+1; k++)
614 sdf = normfac * count[k][j][i];
615 if ( sdf < min_sdf ) min_sdf = sdf;
616 if ( sdf > max_sdf ) max_sdf = sdf;
619 /* Changed Code to Mirror SDF to correct coordinates */
620 for (i=nbin[2]; i>0; i--)
621 for (j=nbin[1]; j>0; j--)
622 for (k=nbin[0]; k>0; k--)
624 sdf = normfac * count[k][j][i];
625 if ( sdf < min_sdf ) min_sdf = sdf;
626 if ( sdf > max_sdf ) max_sdf = sdf;
630 fprintf(stderr,"\nMin: %f Max: %f\n",min_sdf,max_sdf);
636 /* Give back the mem */
637 for(i=0; i<nbin[0]+1; i++)
639 for (j=0; j<nbin[1]+1; j++)
648 int gmx_sdf(int argc,char *argv[])
650 const char *desc[] = {
651 "[TT]g_sdf[tt] calculates the spatial distribution function (SDF) of a set of atoms",
652 "within a coordinate system defined by three atoms. There is single body, ",
653 "two body and three body SDF implemented (select with option [TT]-mode[tt]). ",
654 "In the single body case the local coordinate system is defined by using",
655 "a triple of atoms from one single molecule, for the two and three body case",
656 "the configurations are dynamically searched complexes of two or three",
657 "molecules (or residues) meeting certain distance consitions (see below).[PAR]",
658 "The program needs a trajectory, a GROMACS run input file and an index ",
660 "You have to setup 4 groups in the index file before using g_sdf: [PAR]",
661 "The first three groups are used to define the SDF coordinate system.",
662 "The program will dynamically generate the atom triples according to ",
663 "the selected [TT]-mode[tt]: ",
664 "In [TT]-mode[tt] 1 the triples will be just the 1st, 2nd, 3rd, ... atoms from ",
665 "groups 1, 2 and 3. Hence the nth entries in groups 1, 2 and 3 must be from the",
666 "same residue. In [TT]-mode[tt] 2 the triples will be 1st, 2nd, 3rd, ... atoms from",
667 "groups 1 and 2 (with the nth entries in groups 1 and 2 having the same res-id).",
668 "For each pair from groups 1 and 2 group 3 is searched for an atom meeting the",
669 "distance conditions set with [TT]-triangle[tt] and [TT]-dtri[tt] relative to atoms 1 and 2. In",
670 "[TT]-mode[tt] 3 for each atom in group 1 group 2 is searched for an atom meeting the",
671 "distance condition and if a pair is found group 3 is searched for an atom",
672 "meeting the further conditions. The triple will only be used if all three atoms",
673 "have different res-id's.[PAR]",
674 "The local coordinate system is always defined using the following scheme:",
675 "Atom 1 will be used as the point of origin for the SDF. ",
676 "Atom 1 and 2 will define the principle axis (Z) of the coordinate system.",
677 "The other two axis will be defined inplane (Y) and normal (X) to the plane through",
678 "Atoms 1, 2 and 3. ",
680 "contains the atoms for which the SDF will be evaluated.[PAR]",
681 "For [TT]-mode[tt] 2 and 3 you have to define the distance conditions for the ",
682 "2 resp. 3 molecule complexes to be searched for using [TT]-triangle[tt] and [TT]-dtri[tt].[PAR]",
683 "The SDF will be sampled in cartesian coordinates.",
684 "Use [TT]-grid x y z[tt] to define the size of the SDF grid around the ",
685 "reference molecule. ",
686 "The Volume of the SDF grid will be V=x*y*z (nm^3). ",
687 "Use [TT]-bin[tt] to set the binwidth for grid.[PAR]",
688 "The output will be a binary 3D-grid file ([TT]gom_plt.dat[tt]) in the [TT].plt[tt] format that can be be",
689 "read directly by gOpenMol. ",
690 "The option [TT]-r[tt] will generate a [TT].gro[tt] file with the reference molecule(s) transferred to",
691 "the SDF coordinate system. Load this file into gOpenMol and display the",
692 "SDF as a contour plot (see http://www.csc.fi/gopenmol/index.phtml for ",
693 "further documentation). [PAR]",
694 "For further information about SDF's have a look at: A. Vishnyakov, JPC A, 105,",
695 "2001, 1702 and the references cited within."
698 static gmx_bool bRef=FALSE;
700 static rvec triangle={0.0,0.0,0.0};
701 static rvec dtri={0.03,0.03,0.03};
702 static rvec cutoff={1,1,1};
703 static real binwidth=0.05;
705 { "-mode", FALSE, etINT, {&mode},
706 "SDF in [1,2,3] particle mode"},
707 { "-triangle", FALSE, etRVEC, {&triangle},
708 "r(1,3), r(2,3), r(1,2)"},
709 { "-dtri", FALSE, etRVEC, {&dtri},
710 "dr(1,3), dr(2,3), dr(1,2)"},
711 { "-bin", FALSE, etREAL, {&binwidth},
712 "Binwidth for the 3D-grid (nm)" },
713 { "-grid", FALSE, etRVEC, {&cutoff},
714 "Size of the 3D-grid (nm,nm,nm)"}
716 #define NPA asize(pa)
717 const char *fnTPS,*fnNDX,*fnREF;
720 { efTRX, "-f", NULL, ffREAD },
721 { efNDX, NULL, NULL, ffREAD },
722 { efTPS, NULL, NULL, ffOPTRD },
723 { efDAT, "-o", "gom_plt", ffWRITE },
724 { efSTO, "-r", "refmol", ffOPTWR },
726 #define NFILE asize(fnm)
728 CopyRight(stderr,argv[0]);
729 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_BE_NICE,
730 NFILE,fnm,NPA,pa,asize(desc),desc,0,NULL,&oenv);
733 fnTPS = ftp2fn_null(efTPS,NFILE,fnm);
734 fnNDX = ftp2fn_null(efNDX,NFILE,fnm);
735 fnREF = opt2fn_null("-r",NFILE,fnm);
736 bRef = opt2bSet("-r",NFILE,fnm);
741 gmx_fatal(FARGS,"No index file specified\n"
746 gmx_fatal(FARGS,"No topology file specified\n"
750 if ( bRef && (fn2ftp(fnREF) != efGRO))
752 fprintf(stderr,"\nOnly GROMACS format is supported for reference structures.\n");
753 fprintf(stderr,"Option -r will be ignored!\n");
758 if ((mode < 1) || (mode > 3))
759 gmx_fatal(FARGS,"Wrong -mode selection. Chose 1-, 2- oder 3-particle mode.\n");
762 do_sdf(fnNDX,fnTPS,ftp2fn(efTRX,NFILE,fnm),opt2fn("-o",NFILE,fnm),
763 fnREF,bRef,cutoff,binwidth,mode,triangle,dtri,oenv);
772 main(int argc, char *argv[])