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, 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.
69 static void check_box_c(matrix box)
71 if (fabs(box[ZZ][XX]) > GMX_REAL_EPS*box[ZZ][ZZ] ||
72 fabs(box[ZZ][YY]) > GMX_REAL_EPS*box[ZZ][ZZ])
74 "The last box vector is not parallel to the z-axis: %f %f %f",
75 box[ZZ][XX],box[ZZ][YY],box[ZZ][ZZ]);
78 static void calc_comg(int is,int *coi,int *index,gmx_bool bMass,t_atom *atom,
85 if (bMass && atom==NULL)
86 gmx_fatal(FARGS,"No masses available while mass weighting was requested");
91 for(i=coi[c]; i<coi[c+1]; i++) {
95 xc[d] += m*x[index[i]][d];
98 rvec_inc(xc,x[index[i]]);
102 svmul(1/mtot,xc,x_comg[c]);
106 static void split_group(int isize,int *index,char *grpname,
107 t_topology *top,char type,
108 int *is_out,int **coi_out)
113 int cur,mol,res,i,a,i1;
115 /* Split up the group in molecules or residues */
121 atom = top->atoms.atom;
124 gmx_fatal(FARGS,"Unknown rdf option '%s'",type);
130 for(i=0; i<isize; i++) {
133 /* Check if the molecule number has changed */
134 i1 = mols->index[mol+1];
137 i1 = mols->index[mol+1];
143 } else if (type == 'r') {
144 /* Check if the residue index has changed */
145 res = atom[a].resind;
154 printf("Group '%s' of %d atoms consists of %d %s\n",
156 (type=='m' ? "molecules" : "residues"));
162 static void do_rdf(const char *fnNDX,const char *fnTPS,const char *fnTRX,
163 const char *fnRDF,const char *fnCNRDF, const char *fnHQ,
164 gmx_bool bCM,const char *close,
165 const char **rdft,gmx_bool bXY,gmx_bool bPBC,gmx_bool bNormalize,
166 real cutoff,real binwidth,real fade,int ng,
167 const output_env_t oenv)
171 char outf1[STRLEN],outf2[STRLEN];
172 char title[STRLEN],gtitle[STRLEN],refgt[30];
173 int g,natoms,i,ii,j,k,nbin,j0,j1,n,nframes;
176 int *isize,isize_cm=0,nrdf=0,max_i,isize0,isize_g;
177 atom_id **index,*index_cm=NULL;
178 #if (defined SIZEOF_LONG_LONG_INT) && (SIZEOF_LONG_LONG_INT >= 8)
183 real t,rmax2,cut2,r,r2,r2ii,invhbinw,normfac;
184 real segvol,spherevol,prev_spherevol,**rdf;
185 rvec *x,dx,*x0=NULL,*x_i1,xi;
186 real *inv_segvol,invvol,invvol_sum,rho;
187 gmx_bool bClose,*bExcl,bTop,bNonSelfExcl;
190 atom_id ix,jx,***pairs;
191 t_topology *top=NULL;
192 int ePBC=-1,ePBCrdf=-1;
197 gmx_rmpbc_t gpbc=NULL;
198 int *is=NULL,**coi=NULL,cur,mol,i1,res,a;
202 bClose = (close[0] != 'n');
206 bTop=read_tps_conf(fnTPS,title,top,&ePBC,&x,NULL,box,TRUE);
207 if (bTop && !(bCM || bClose))
208 /* get exclusions from topology */
209 excl = &(top->excls);
214 fprintf(stderr,"\nSelect a reference group and %d group%s\n",
217 get_index(&(top->atoms),fnNDX,ng+1,isize,index,grpname);
218 atom = top->atoms.atom;
220 rd_index(fnNDX,ng+1,isize,index,grpname);
227 split_group(isize[0],index[0],grpname[0],top,close[0],&is[0],&coi[0]);
230 if (rdft[0][0] != 'a') {
231 /* Split up all the groups in molecules or residues */
234 for(g=((bCM || bClose) ? 1 : 0); g<ng+1; g++) {
235 split_group(isize[g],index[g],grpname[g],top,rdft[0][0],&is[g],&coi[g]);
241 snew(coi[0],is[0]+1);
243 coi[0][1] = isize[0];
246 } else if (bClose || rdft[0][0] != 'a') {
253 natoms=read_first_x(oenv,&status,fnTRX,&t,&x,box);
255 gmx_fatal(FARGS,"Could not read coordinates from statusfile\n");
257 /* check with topology */
258 if ( natoms > top->atoms.nr )
259 gmx_fatal(FARGS,"Trajectory (%d atoms) does not match topology (%d atoms)",
260 natoms,top->atoms.nr);
261 /* check with index groups */
262 for (i=0; i<ng+1; i++)
263 for (j=0; j<isize[i]; j++)
264 if ( index[i][j] >= natoms )
265 gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) larger "
266 "than number of atoms in trajectory (%d atoms)",
267 index[i][j],grpname[i],isize[i],natoms);
269 /* initialize some handy things */
271 ePBC = guess_ePBC(box);
273 copy_mat(box,box_pbc);
278 case epbcXY: ePBCrdf = epbcXY; break;
279 case epbcNONE: ePBCrdf = epbcNONE; break;
281 gmx_fatal(FARGS,"xy-rdf's are not supported for pbc type'%s'",
285 /* Make sure the z-height does not influence the cut-off */
286 box_pbc[ZZ][ZZ] = 2*max(box[XX][XX],box[YY][YY]);
291 rmax2 = 0.99*0.99*max_cutoff2(bXY ? epbcXY : epbcXYZ,box_pbc);
293 rmax2 = sqr(3*max(box[XX][XX],max(box[YY][YY],box[ZZ][ZZ])));
295 fprintf(debug,"rmax2 = %g\n",rmax2);
297 /* We use the double amount of bins, so we can correctly
298 * write the rdf and rdf_cn output at i*binwidth values.
300 nbin = (int)(sqrt(rmax2) * 2 / binwidth);
301 invhbinw = 2.0 / binwidth;
310 for(g=0; g<ng; g++) {
311 if (isize[g+1] > max_i)
314 /* this is THE array */
315 snew(count[g],nbin+1);
317 /* make pairlist array for groups and exclusions */
318 snew(pairs[g],isize[0]);
319 snew(npairs[g],isize[0]);
320 for(i=0; i<isize[0]; i++) {
321 /* We can only have exclusions with atomic rdfs */
322 if (!(bCM || bClose || rdft[0][0] != 'a')) {
324 for(j=0; j < natoms; j++)
328 for( j = excl->index[ix]; j < excl->index[ix+1]; j++)
329 bExcl[excl->a[j]]=TRUE;
331 snew(pairs[g][i], isize[g+1]);
332 bNonSelfExcl = FALSE;
333 for(j=0; j<isize[g+1]; j++) {
338 /* Check if we have exclusions other than self exclusions */
343 srenew(pairs[g][i],npairs[g][i]);
345 /* Save a LOT of memory and some cpu cycles */
359 if (bPBC && (NULL != top))
360 gpbc = gmx_rmpbc_init(&top->idef,ePBC,natoms,box);
363 /* Must init pbc every step because of pressure coupling */
364 copy_mat(box,box_pbc);
367 gmx_rmpbc(gpbc,natoms,box,x);
370 clear_rvec(box_pbc[ZZ]);
372 set_pbc(&pbc,ePBCrdf,box_pbc);
375 /* Set z-size to 1 so we get the surface iso the volume */
378 invvol = 1/det(box_pbc);
379 invvol_sum += invvol;
382 /* Calculate center of mass of the whole group */
383 calc_comg(is[0],coi[0],index[0],TRUE ,atom,x,x0);
384 } else if (!bClose && rdft[0][0] != 'a') {
385 calc_comg(is[0],coi[0],index[0],rdft[0][6]=='m',atom,x,x0);
388 for(g=0; g<ng; g++) {
389 if (rdft[0][0] == 'a') {
390 /* Copy the indexed coordinates to a continuous array */
391 for(i=0; i<isize[g+1]; i++)
392 copy_rvec(x[index[g+1][i]],x_i1[i]);
394 /* Calculate the COMs/COGs and store in x_i1 */
395 calc_comg(is[g+1],coi[g+1],index[g+1],rdft[0][6]=='m',atom,x,x_i1);
398 for(i=0; i<isize0; i++) {
400 /* Special loop, since we need to determine the minimum distance
401 * over all selected atoms in the reference molecule/residue.
403 if (rdft[0][0] == 'a')
404 isize_g = isize[g+1];
407 for(j=0; j<isize_g; j++) {
409 /* Loop over the selected atoms in the reference molecule */
410 for(ii=coi[0][i]; ii<coi[0][i+1]; ii++) {
412 pbc_dx(&pbc,x[index[0][ii]],x_i1[j],dx);
414 rvec_sub(x[index[0][ii]],x_i1[j],dx);
416 r2ii = dx[XX]*dx[XX] + dx[YY]*dx[YY];
422 if (r2>cut2 && r2<=rmax2)
423 count[g][(int)(sqrt(r2)*invhbinw)]++;
426 /* Real rdf between points in space */
427 if (bCM || rdft[0][0] != 'a') {
430 copy_rvec(x[index[0][i]],xi);
432 if (rdft[0][0] == 'a' && npairs[g][i] >= 0) {
433 /* Expensive loop, because of indexing */
434 for(j=0; j<npairs[g][i]; j++) {
437 pbc_dx(&pbc,xi,x[jx],dx);
439 rvec_sub(xi,x[jx],dx);
442 r2 = dx[XX]*dx[XX] + dx[YY]*dx[YY];
445 if (r2>cut2 && r2<=rmax2)
446 count[g][(int)(sqrt(r2)*invhbinw)]++;
449 /* Cheaper loop, no exclusions */
450 if (rdft[0][0] == 'a')
451 isize_g = isize[g+1];
454 for(j=0; j<isize_g; j++) {
456 pbc_dx(&pbc,xi,x_i1[j],dx);
458 rvec_sub(xi,x_i1[j],dx);
460 r2 = dx[XX]*dx[XX] + dx[YY]*dx[YY];
463 if (r2>cut2 && r2<=rmax2)
464 count[g][(int)(sqrt(r2)*invhbinw)]++;
471 } while (read_next_x(oenv,status,&t,natoms,x,box));
472 fprintf(stderr,"\n");
474 if (bPBC && (NULL != top))
475 gmx_rmpbc_done(gpbc);
482 invvol = invvol_sum/nframes;
484 /* Calculate volume of sphere segments or length of circle segments */
485 snew(inv_segvol,(nbin+1)/2);
487 for(i=0; (i<(nbin+1)/2); i++) {
488 r = (i + 0.5)*binwidth;
492 spherevol=(4.0/3.0)*M_PI*r*r*r;
494 segvol=spherevol-prev_spherevol;
495 inv_segvol[i]=1.0/segvol;
496 prev_spherevol=spherevol;
500 for(g=0; g<ng; g++) {
501 /* We have to normalize by dividing by the number of frames */
502 if (rdft[0][0] == 'a')
503 normfac = 1.0/(nframes*invvol*isize0*isize[g+1]);
505 normfac = 1.0/(nframes*invvol*isize0*is[g+1]);
507 /* Do the normalization */
508 nrdf = max((nbin+1)/2,1+2*fade/binwidth);
510 for(i=0; i<(nbin+1)/2; i++) {
515 j = count[g][i*2-1] + count[g][i*2];
516 if ((fade > 0) && (r >= fade))
517 rdf[g][i] = 1 + (j*inv_segvol[i]*normfac-1)*exp(-16*sqr(r/fade-1));
520 rdf[g][i] = j*inv_segvol[i]*normfac;
522 rdf[g][i] = j/(binwidth*isize0*nframes);
525 for( ; (i<nrdf); i++)
529 if (rdft[0][0] == 'a') {
530 sprintf(gtitle,"Radial distribution");
532 sprintf(gtitle,"Radial distribution of %s %s",
533 rdft[0][0]=='m' ? "molecule" : "residue",
534 rdft[0][6]=='m' ? "COM" : "COG");
536 fp=xvgropen(fnRDF,gtitle,"r","",oenv);
538 sprintf(refgt," %s","COM");
540 sprintf(refgt," closest atom in %s.",close);
542 sprintf(refgt,"%s","");
545 if (output_env_get_print_xvgr_codes(oenv))
546 fprintf(fp,"@ subtitle \"%s%s - %s\"\n",grpname[0],refgt,grpname[1]);
549 if (output_env_get_print_xvgr_codes(oenv))
550 fprintf(fp,"@ subtitle \"reference %s%s\"\n",grpname[0],refgt);
551 xvgr_legend(fp,ng,(const char**)(grpname+1),oenv);
553 for(i=0; (i<nrdf); i++) {
554 fprintf(fp,"%10g",i*binwidth);
556 fprintf(fp," %10g",rdf[g][i]);
561 do_view(oenv,fnRDF,NULL);
563 /* h(Q) function: fourier transform of rdf */
566 real *hq,*integrand,Q;
568 /* Get a better number density later! */
569 rho = isize[1]*invvol;
571 snew(integrand,nrdf);
572 for(i=0; (i<nhq); i++) {
575 for(j=1; (j<nrdf); j++) {
577 integrand[j] = (Q == 0) ? 1.0 : sin(Q*r)/(Q*r);
578 integrand[j] *= 4.0*M_PI*rho*r*r*(rdf[0][j]-1.0);
580 hq[i] = print_and_integrate(debug,nrdf,binwidth,integrand,NULL,0);
582 fp=xvgropen(fnHQ,"h(Q)","Q(/nm)","h(Q)",oenv);
583 for(i=0; (i<nhq); i++)
584 fprintf(fp,"%10g %10g\n",i*0.5,hq[i]);
586 do_view(oenv,fnHQ,NULL);
592 normfac = 1.0/(isize0*nframes);
593 fp=xvgropen(fnCNRDF,"Cumulative Number RDF","r","number",oenv);
595 if (output_env_get_print_xvgr_codes(oenv))
596 fprintf(fp,"@ subtitle \"%s-%s\"\n",grpname[0],grpname[1]);
599 if (output_env_get_print_xvgr_codes(oenv))
600 fprintf(fp,"@ subtitle \"reference %s\"\n",grpname[0]);
601 xvgr_legend(fp,ng,(const char**)(grpname+1),oenv);
604 for(i=0; (i<=nbin/2); i++) {
605 fprintf(fp,"%10g",i*binwidth);
606 for(g=0; g<ng; g++) {
607 fprintf(fp," %10g",(real)((double)sum[g]*normfac));
609 sum[g] += count[g][i*2] + count[g][i*2+1];
616 do_view(oenv,fnCNRDF,NULL);
625 int gmx_rdf(int argc,char *argv[])
627 const char *desc[] = {
628 "The structure of liquids can be studied by either neutron or X-ray",
629 "scattering. The most common way to describe liquid structure is by a",
630 "radial distribution function. However, this is not easy to obtain from",
631 "a scattering experiment.[PAR]",
632 "[TT]g_rdf[tt] calculates radial distribution functions in different ways.",
633 "The normal method is around a (set of) particle(s), the other methods",
634 "are around the center of mass of a set of particles ([TT]-com[tt])",
635 "or to the closest particle in a set ([TT]-surf[tt]).",
636 "With all methods, the RDF can also be calculated around axes parallel",
637 "to the [IT]z[it]-axis with option [TT]-xy[tt].",
638 "With option [TT]-surf[tt] normalization can not be used.[PAR]",
639 "The option [TT]-rdf[tt] sets the type of RDF to be computed.",
640 "Default is for atoms or particles, but one can also select center",
641 "of mass or geometry of molecules or residues. In all cases, only",
642 "the atoms in the index groups are taken into account.",
643 "For molecules and/or the center of mass option, a run input file",
645 "Weighting other than COM or COG can currently only be achieved",
646 "by providing a run input file with different masses.",
647 "Options [TT]-com[tt] and [TT]-surf[tt] also work in conjunction",
648 "with [TT]-rdf[tt].[PAR]",
649 "If a run input file is supplied ([TT]-s[tt]) and [TT]-rdf[tt] is set",
650 "to [TT]atom[tt], exclusions defined",
651 "in that file are taken into account when calculating the RDF.",
652 "The option [TT]-cut[tt] is meant as an alternative way to avoid",
653 "intramolecular peaks in the RDF plot.",
654 "It is however better to supply a run input file with a higher number of",
655 "exclusions. For e.g. benzene a topology, setting nrexcl to 5",
656 "would eliminate all intramolecular contributions to the RDF.",
657 "Note that all atoms in the selected groups are used, also the ones",
658 "that don't have Lennard-Jones interactions.[PAR]",
659 "Option [TT]-cn[tt] produces the cumulative number RDF,",
660 "i.e. the average number of particles within a distance r.[PAR]",
661 "To bridge the gap between theory and experiment structure factors can",
662 "be computed (option [TT]-sq[tt]). The algorithm uses FFT, the grid",
663 "spacing of which is determined by option [TT]-grid[tt]."
665 static gmx_bool bCM=FALSE,bXY=FALSE,bPBC=TRUE,bNormalize=TRUE;
666 static real cutoff=0,binwidth=0.002,grid=0.05,fade=0.0,lambda=0.1,distance=10;
667 static int npixel=256,nlevel=20,ngroups=1;
668 static real start_q=0.0, end_q=60.0, energy=12.0;
670 static const char *closet[]= { NULL, "no", "mol", "res", NULL };
671 static const char *rdft[]={ NULL, "atom", "mol_com", "mol_cog", "res_com", "res_cog", NULL };
674 { "-bin", FALSE, etREAL, {&binwidth},
676 { "-com", FALSE, etBOOL, {&bCM},
677 "RDF with respect to the center of mass of first group" },
678 { "-surf", FALSE, etENUM, {closet},
679 "RDF with respect to the surface of the first group" },
680 { "-rdf", FALSE, etENUM, {rdft},
682 { "-pbc", FALSE, etBOOL, {&bPBC},
683 "Use periodic boundary conditions for computing distances. Without PBC the maximum range will be three times the largest box edge." },
684 { "-norm", FALSE, etBOOL, {&bNormalize},
685 "Normalize for volume and density" },
686 { "-xy", FALSE, etBOOL, {&bXY},
687 "Use only the x and y components of the distance" },
688 { "-cut", FALSE, etREAL, {&cutoff},
689 "Shortest distance (nm) to be considered"},
690 { "-ng", FALSE, etINT, {&ngroups},
691 "Number of secondary groups to compute RDFs around a central group" },
692 { "-fade", FALSE, etREAL, {&fade},
693 "From this distance onwards the RDF is tranformed by g'(r) = 1 + [g(r)-1] exp(-(r/fade-1)^2 to make it go to 1 smoothly. If fade is 0.0 nothing is done." },
694 { "-grid", FALSE, etREAL, {&grid},
695 "[HIDDEN]Grid spacing (in nm) for FFTs when computing structure factors" },
696 { "-npixel", FALSE, etINT, {&npixel},
697 "[HIDDEN]# pixels per edge of the square detector plate" },
698 { "-nlevel", FALSE, etINT, {&nlevel},
699 "Number of different colors in the diffraction image" },
700 { "-distance", FALSE, etREAL, {&distance},
701 "[HIDDEN]Distance (in cm) from the sample to the detector" },
702 { "-wave", FALSE, etREAL, {&lambda},
703 "[HIDDEN]Wavelength for X-rays/Neutrons for scattering. 0.1 nm corresponds to roughly 12 keV" },
705 {"-startq", FALSE, etREAL, {&start_q},
706 "Starting q (1/nm) "},
707 {"-endq", FALSE, etREAL, {&end_q},
709 {"-energy", FALSE, etREAL, {&energy},
710 "Energy of the incoming X-ray (keV) "}
712 #define NPA asize(pa)
713 const char *fnTPS,*fnNDX,*fnDAT=NULL;
718 { efTRX, "-f", NULL, ffREAD },
719 { efTPS, NULL, NULL, ffOPTRD },
720 { efNDX, NULL, NULL, ffOPTRD },
721 { efDAT, "-d", "sfactor", ffOPTRD },
722 { efXVG, "-o", "rdf", ffOPTWR },
723 { efXVG, "-sq", "sq", ffOPTWR },
724 { efXVG, "-cn", "rdf_cn", ffOPTWR },
725 { efXVG, "-hq", "hq", ffOPTWR },
726 /* { efXPM, "-image", "sq", ffOPTWR }*/
728 #define NFILE asize(fnm)
730 CopyRight(stderr,argv[0]);
731 parse_common_args(&argc,argv,PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
732 NFILE,fnm,NPA,pa,asize(desc),desc,0,NULL,&oenv);
734 bSQ = opt2bSet("-sq",NFILE,fnm);
736 please_cite(stdout,"Cromer1968a");
738 bRDF = opt2bSet("-o",NFILE,fnm) || !bSQ;
739 if (bSQ || bCM || closet[0][0]!='n' || rdft[0][0]=='m' || rdft[0][6]=='m') {
740 fnTPS = ftp2fn(efTPS,NFILE,fnm);
741 fnDAT = ftp2fn(efDAT,NFILE,fnm);
743 fnTPS = ftp2fn_null(efTPS,NFILE,fnm);
745 fnNDX = ftp2fn_null(efNDX,NFILE,fnm);
747 if (closet[0][0] != 'n') {
749 gmx_fatal(FARGS,"Can not have both -com and -surf");
752 fprintf(stderr,"Turning of normalization because of option -surf\n");
757 if (!bSQ && (!fnTPS && !fnNDX))
758 gmx_fatal(FARGS,"Neither index file nor topology file specified\n"
762 do_scattering_intensity(fnTPS,fnNDX,opt2fn("-sq",NFILE,fnm),
763 ftp2fn(efTRX,NFILE,fnm),fnDAT,
764 start_q, end_q, energy, ngroups,oenv);
767 do_rdf(fnNDX,fnTPS,ftp2fn(efTRX,NFILE,fnm),
768 opt2fn("-o",NFILE,fnm),opt2fn_null("-cn",NFILE,fnm),
769 opt2fn_null("-hq",NFILE,fnm),
770 bCM,closet[0],rdft,bXY,bPBC,bNormalize,cutoff,binwidth,fade,ngroups,