3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
10 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12 * Copyright (c) 2001-2004, The GROMACS development team,
13 * check out http://www.gromacs.org for more information.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * If you want to redistribute modifications, please consider that
21 * scientific software is very special. Version control is crucial -
22 * bugs must be traceable. We will be happy to consider code for
23 * inclusion in the official distribution, but derived work must not
24 * be called official GROMACS. Details are found in the README & COPYING
25 * files - if they are missing, get the official version at www.gromacs.org.
27 * To help us fund GROMACS development, we humbly ask that you cite
28 * the papers on the package - you can find them in the top README file.
30 * For more info, check our website at http://www.gromacs.org
33 * Green Red Orange Magenta Azure Cyan Skyblue
63 static void check_box_c(matrix box)
65 if (fabs(box[ZZ][XX]) > GMX_REAL_EPS*box[ZZ][ZZ] ||
66 fabs(box[ZZ][YY]) > GMX_REAL_EPS*box[ZZ][ZZ])
69 "The last box vector is not parallel to the z-axis: %f %f %f",
70 box[ZZ][XX], box[ZZ][YY], box[ZZ][ZZ]);
74 static void calc_comg(int is, int *coi, int *index, gmx_bool bMass, t_atom *atom,
75 rvec *x, rvec *x_comg)
81 if (bMass && atom == NULL)
83 gmx_fatal(FARGS, "No masses available while mass weighting was requested");
86 for (c = 0; c < is; c++)
90 for (i = coi[c]; i < coi[c+1]; i++)
95 for (d = 0; d < DIM; d++)
97 xc[d] += m*x[index[i]][d];
103 rvec_inc(xc, x[index[i]]);
107 svmul(1/mtot, xc, x_comg[c]);
111 static void split_group(int isize, int *index, char *grpname,
112 t_topology *top, char type,
113 int *is_out, int **coi_out)
115 t_block *mols = NULL;
118 int cur, mol, res, i, a, i1;
120 /* Split up the group in molecules or residues */
127 atom = top->atoms.atom;
130 gmx_fatal(FARGS, "Unknown rdf option '%s'", type);
136 for (i = 0; i < isize; i++)
141 /* Check if the molecule number has changed */
142 i1 = mols->index[mol+1];
146 i1 = mols->index[mol+1];
154 else if (type == 'r')
156 /* Check if the residue index has changed */
157 res = atom[a].resind;
167 printf("Group '%s' of %d atoms consists of %d %s\n",
169 (type == 'm' ? "molecules" : "residues"));
175 static void do_rdf(const char *fnNDX, const char *fnTPS, const char *fnTRX,
176 const char *fnRDF, const char *fnCNRDF, const char *fnHQ,
177 gmx_bool bCM, const char *close,
178 const char **rdft, gmx_bool bXY, gmx_bool bPBC, gmx_bool bNormalize,
179 real cutoff, real binwidth, real fade, int ng,
180 const output_env_t oenv)
184 char outf1[STRLEN], outf2[STRLEN];
185 char title[STRLEN], gtitle[STRLEN], refgt[30];
186 int g, natoms, i, ii, j, k, nbin, j0, j1, n, nframes;
189 int *isize, isize_cm = 0, nrdf = 0, max_i, isize0, isize_g;
190 atom_id **index, *index_cm = NULL;
191 #if (defined SIZEOF_LONG_LONG_INT) && (SIZEOF_LONG_LONG_INT >= 8)
196 real t, rmax2, cut2, r, r2, r2ii, invhbinw, normfac;
197 real segvol, spherevol, prev_spherevol, **rdf;
198 rvec *x, dx, *x0 = NULL, *x_i1, xi;
199 real *inv_segvol, invvol, invvol_sum, rho;
200 gmx_bool bClose, *bExcl, bTop, bNonSelfExcl;
203 atom_id ix, jx, ***pairs;
204 t_topology *top = NULL;
205 int ePBC = -1, ePBCrdf = -1;
206 t_block *mols = NULL;
210 gmx_rmpbc_t gpbc = NULL;
211 int *is = NULL, **coi = NULL, cur, mol, i1, res, a;
215 bClose = (close[0] != 'n');
220 bTop = read_tps_conf(fnTPS, title, top, &ePBC, &x, NULL, box, TRUE);
221 if (bTop && !(bCM || bClose))
223 /* get exclusions from topology */
224 excl = &(top->excls);
230 fprintf(stderr, "\nSelect a reference group and %d group%s\n",
231 ng, ng == 1 ? "" : "s");
234 get_index(&(top->atoms), fnNDX, ng+1, isize, index, grpname);
235 atom = top->atoms.atom;
239 rd_index(fnNDX, ng+1, isize, index, grpname);
248 split_group(isize[0], index[0], grpname[0], top, close[0], &is[0], &coi[0]);
251 if (rdft[0][0] != 'a')
253 /* Split up all the groups in molecules or residues */
256 for (g = ((bCM || bClose) ? 1 : 0); g < ng+1; g++)
258 split_group(isize[g], index[g], grpname[g], top, rdft[0][0], &is[g], &coi[g]);
265 snew(coi[0], is[0]+1);
267 coi[0][1] = isize[0];
271 else if (bClose || rdft[0][0] != 'a')
281 natoms = read_first_x(oenv, &status, fnTRX, &t, &x, box);
284 gmx_fatal(FARGS, "Could not read coordinates from statusfile\n");
288 /* check with topology */
289 if (natoms > top->atoms.nr)
291 gmx_fatal(FARGS, "Trajectory (%d atoms) does not match topology (%d atoms)",
292 natoms, top->atoms.nr);
295 /* check with index groups */
296 for (i = 0; i < ng+1; i++)
298 for (j = 0; j < isize[i]; j++)
300 if (index[i][j] >= natoms)
302 gmx_fatal(FARGS, "Atom index (%d) in index group %s (%d atoms) larger "
303 "than number of atoms in trajectory (%d atoms)",
304 index[i][j], grpname[i], isize[i], natoms);
309 /* initialize some handy things */
312 ePBC = guess_ePBC(box);
314 copy_mat(box, box_pbc);
321 case epbcXY: ePBCrdf = epbcXY; break;
322 case epbcNONE: ePBCrdf = epbcNONE; break;
324 gmx_fatal(FARGS, "xy-rdf's are not supported for pbc type'%s'",
328 /* Make sure the z-height does not influence the cut-off */
329 box_pbc[ZZ][ZZ] = 2*max(box[XX][XX], box[YY][YY]);
337 rmax2 = 0.99*0.99*max_cutoff2(bXY ? epbcXY : epbcXYZ, box_pbc);
341 rmax2 = sqr(3*max(box[XX][XX], max(box[YY][YY], box[ZZ][ZZ])));
345 fprintf(debug, "rmax2 = %g\n", rmax2);
348 /* We use the double amount of bins, so we can correctly
349 * write the rdf and rdf_cn output at i*binwidth values.
351 nbin = (int)(sqrt(rmax2) * 2 / binwidth);
352 invhbinw = 2.0 / binwidth;
361 for (g = 0; g < ng; g++)
363 if (isize[g+1] > max_i)
368 /* this is THE array */
369 snew(count[g], nbin+1);
371 /* make pairlist array for groups and exclusions */
372 snew(pairs[g], isize[0]);
373 snew(npairs[g], isize[0]);
374 for (i = 0; i < isize[0]; i++)
376 /* We can only have exclusions with atomic rdfs */
377 if (!(bCM || bClose || rdft[0][0] != 'a'))
380 for (j = 0; j < natoms; j++)
387 for (j = excl->index[ix]; j < excl->index[ix+1]; j++)
389 bExcl[excl->a[j]] = TRUE;
393 snew(pairs[g][i], isize[g+1]);
394 bNonSelfExcl = FALSE;
395 for (j = 0; j < isize[g+1]; j++)
400 pairs[g][i][k++] = jx;
404 /* Check if we have exclusions other than self exclusions */
411 srenew(pairs[g][i], npairs[g][i]);
415 /* Save a LOT of memory and some cpu cycles */
431 if (bPBC && (NULL != top))
433 gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms, box);
438 /* Must init pbc every step because of pressure coupling */
439 copy_mat(box, box_pbc);
444 gmx_rmpbc(gpbc, natoms, box, x);
449 clear_rvec(box_pbc[ZZ]);
451 set_pbc(&pbc, ePBCrdf, box_pbc);
455 /* Set z-size to 1 so we get the surface iso the volume */
459 invvol = 1/det(box_pbc);
460 invvol_sum += invvol;
464 /* Calculate center of mass of the whole group */
465 calc_comg(is[0], coi[0], index[0], TRUE, atom, x, x0);
467 else if (!bClose && rdft[0][0] != 'a')
469 calc_comg(is[0], coi[0], index[0], rdft[0][6] == 'm', atom, x, x0);
472 for (g = 0; g < ng; g++)
474 if (rdft[0][0] == 'a')
476 /* Copy the indexed coordinates to a continuous array */
477 for (i = 0; i < isize[g+1]; i++)
479 copy_rvec(x[index[g+1][i]], x_i1[i]);
484 /* Calculate the COMs/COGs and store in x_i1 */
485 calc_comg(is[g+1], coi[g+1], index[g+1], rdft[0][6] == 'm', atom, x, x_i1);
488 for (i = 0; i < isize0; i++)
492 /* Special loop, since we need to determine the minimum distance
493 * over all selected atoms in the reference molecule/residue.
495 if (rdft[0][0] == 'a')
497 isize_g = isize[g+1];
503 for (j = 0; j < isize_g; j++)
506 /* Loop over the selected atoms in the reference molecule */
507 for (ii = coi[0][i]; ii < coi[0][i+1]; ii++)
511 pbc_dx(&pbc, x[index[0][ii]], x_i1[j], dx);
515 rvec_sub(x[index[0][ii]], x_i1[j], dx);
519 r2ii = dx[XX]*dx[XX] + dx[YY]*dx[YY];
523 r2ii = iprod(dx, dx);
530 if (r2 > cut2 && r2 <= rmax2)
532 count[g][(int)(sqrt(r2)*invhbinw)]++;
538 /* Real rdf between points in space */
539 if (bCM || rdft[0][0] != 'a')
541 copy_rvec(x0[i], xi);
545 copy_rvec(x[index[0][i]], xi);
547 if (rdft[0][0] == 'a' && npairs[g][i] >= 0)
549 /* Expensive loop, because of indexing */
550 for (j = 0; j < npairs[g][i]; j++)
555 pbc_dx(&pbc, xi, x[jx], dx);
559 rvec_sub(xi, x[jx], dx);
564 r2 = dx[XX]*dx[XX] + dx[YY]*dx[YY];
570 if (r2 > cut2 && r2 <= rmax2)
572 count[g][(int)(sqrt(r2)*invhbinw)]++;
578 /* Cheaper loop, no exclusions */
579 if (rdft[0][0] == 'a')
581 isize_g = isize[g+1];
587 for (j = 0; j < isize_g; j++)
591 pbc_dx(&pbc, xi, x_i1[j], dx);
595 rvec_sub(xi, x_i1[j], dx);
599 r2 = dx[XX]*dx[XX] + dx[YY]*dx[YY];
605 if (r2 > cut2 && r2 <= rmax2)
607 count[g][(int)(sqrt(r2)*invhbinw)]++;
616 while (read_next_x(oenv, status, &t, natoms, x, box));
617 fprintf(stderr, "\n");
619 if (bPBC && (NULL != top))
621 gmx_rmpbc_done(gpbc);
629 invvol = invvol_sum/nframes;
631 /* Calculate volume of sphere segments or length of circle segments */
632 snew(inv_segvol, (nbin+1)/2);
634 for (i = 0; (i < (nbin+1)/2); i++)
636 r = (i + 0.5)*binwidth;
639 spherevol = M_PI*r*r;
643 spherevol = (4.0/3.0)*M_PI*r*r*r;
645 segvol = spherevol-prev_spherevol;
646 inv_segvol[i] = 1.0/segvol;
647 prev_spherevol = spherevol;
651 for (g = 0; g < ng; g++)
653 /* We have to normalize by dividing by the number of frames */
654 if (rdft[0][0] == 'a')
656 normfac = 1.0/(nframes*invvol*isize0*isize[g+1]);
660 normfac = 1.0/(nframes*invvol*isize0*is[g+1]);
663 /* Do the normalization */
664 nrdf = max((nbin+1)/2, 1+2*fade/binwidth);
666 for (i = 0; i < (nbin+1)/2; i++)
675 j = count[g][i*2-1] + count[g][i*2];
677 if ((fade > 0) && (r >= fade))
679 rdf[g][i] = 1 + (j*inv_segvol[i]*normfac-1)*exp(-16*sqr(r/fade-1));
685 rdf[g][i] = j*inv_segvol[i]*normfac;
689 rdf[g][i] = j/(binwidth*isize0*nframes);
693 for (; (i < nrdf); i++)
699 if (rdft[0][0] == 'a')
701 sprintf(gtitle, "Radial distribution");
705 sprintf(gtitle, "Radial distribution of %s %s",
706 rdft[0][0] == 'm' ? "molecule" : "residue",
707 rdft[0][6] == 'm' ? "COM" : "COG");
709 fp = xvgropen(fnRDF, gtitle, "r", "", oenv);
712 sprintf(refgt, " %s", "COM");
716 sprintf(refgt, " closest atom in %s.", close);
720 sprintf(refgt, "%s", "");
724 if (output_env_get_print_xvgr_codes(oenv))
726 fprintf(fp, "@ subtitle \"%s%s - %s\"\n", grpname[0], refgt, grpname[1]);
731 if (output_env_get_print_xvgr_codes(oenv))
733 fprintf(fp, "@ subtitle \"reference %s%s\"\n", grpname[0], refgt);
735 xvgr_legend(fp, ng, (const char**)(grpname+1), oenv);
737 for (i = 0; (i < nrdf); i++)
739 fprintf(fp, "%10g", i*binwidth);
740 for (g = 0; g < ng; g++)
742 fprintf(fp, " %10g", rdf[g][i]);
748 do_view(oenv, fnRDF, NULL);
750 /* h(Q) function: fourier transform of rdf */
754 real *hq, *integrand, Q;
756 /* Get a better number density later! */
757 rho = isize[1]*invvol;
759 snew(integrand, nrdf);
760 for (i = 0; (i < nhq); i++)
764 for (j = 1; (j < nrdf); j++)
767 integrand[j] = (Q == 0) ? 1.0 : sin(Q*r)/(Q*r);
768 integrand[j] *= 4.0*M_PI*rho*r*r*(rdf[0][j]-1.0);
770 hq[i] = print_and_integrate(debug, nrdf, binwidth, integrand, NULL, 0);
772 fp = xvgropen(fnHQ, "h(Q)", "Q(/nm)", "h(Q)", oenv);
773 for (i = 0; (i < nhq); i++)
775 fprintf(fp, "%10g %10g\n", i*0.5, hq[i]);
778 do_view(oenv, fnHQ, NULL);
785 normfac = 1.0/(isize0*nframes);
786 fp = xvgropen(fnCNRDF, "Cumulative Number RDF", "r", "number", oenv);
789 if (output_env_get_print_xvgr_codes(oenv))
791 fprintf(fp, "@ subtitle \"%s-%s\"\n", grpname[0], grpname[1]);
796 if (output_env_get_print_xvgr_codes(oenv))
798 fprintf(fp, "@ subtitle \"reference %s\"\n", grpname[0]);
800 xvgr_legend(fp, ng, (const char**)(grpname+1), oenv);
803 for (i = 0; (i <= nbin/2); i++)
805 fprintf(fp, "%10g", i*binwidth);
806 for (g = 0; g < ng; g++)
808 fprintf(fp, " %10g", (real)((double)sum[g]*normfac));
811 sum[g] += count[g][i*2] + count[g][i*2+1];
819 do_view(oenv, fnCNRDF, NULL);
822 for (g = 0; g < ng; g++)
830 int gmx_rdf(int argc, char *argv[])
832 const char *desc[] = {
833 "The structure of liquids can be studied by either neutron or X-ray",
834 "scattering. The most common way to describe liquid structure is by a",
835 "radial distribution function. However, this is not easy to obtain from",
836 "a scattering experiment.[PAR]",
837 "[TT]g_rdf[tt] calculates radial distribution functions in different ways.",
838 "The normal method is around a (set of) particle(s), the other methods",
839 "are around the center of mass of a set of particles ([TT]-com[tt])",
840 "or to the closest particle in a set ([TT]-surf[tt]).",
841 "With all methods, the RDF can also be calculated around axes parallel",
842 "to the [IT]z[it]-axis with option [TT]-xy[tt].",
843 "With option [TT]-surf[tt] normalization can not be used.[PAR]",
844 "The option [TT]-rdf[tt] sets the type of RDF to be computed.",
845 "Default is for atoms or particles, but one can also select center",
846 "of mass or geometry of molecules or residues. In all cases, only",
847 "the atoms in the index groups are taken into account.",
848 "For molecules and/or the center of mass option, a run input file",
850 "Weighting other than COM or COG can currently only be achieved",
851 "by providing a run input file with different masses.",
852 "Options [TT]-com[tt] and [TT]-surf[tt] also work in conjunction",
853 "with [TT]-rdf[tt].[PAR]",
854 "If a run input file is supplied ([TT]-s[tt]) and [TT]-rdf[tt] is set",
855 "to [TT]atom[tt], exclusions defined",
856 "in that file are taken into account when calculating the RDF.",
857 "The option [TT]-cut[tt] is meant as an alternative way to avoid",
858 "intramolecular peaks in the RDF plot.",
859 "It is however better to supply a run input file with a higher number of",
860 "exclusions. For e.g. benzene a topology, setting nrexcl to 5",
861 "would eliminate all intramolecular contributions to the RDF.",
862 "Note that all atoms in the selected groups are used, also the ones",
863 "that don't have Lennard-Jones interactions.[PAR]",
864 "Option [TT]-cn[tt] produces the cumulative number RDF,",
865 "i.e. the average number of particles within a distance r.[PAR]"
867 static gmx_bool bCM = FALSE, bXY = FALSE, bPBC = TRUE, bNormalize = TRUE;
868 static real cutoff = 0, binwidth = 0.002, fade = 0.0;
869 static int ngroups = 1;
871 static const char *closet[] = { NULL, "no", "mol", "res", NULL };
872 static const char *rdft[] = { NULL, "atom", "mol_com", "mol_cog", "res_com", "res_cog", NULL };
875 { "-bin", FALSE, etREAL, {&binwidth},
877 { "-com", FALSE, etBOOL, {&bCM},
878 "RDF with respect to the center of mass of first group" },
879 { "-surf", FALSE, etENUM, {closet},
880 "RDF with respect to the surface of the first group" },
881 { "-rdf", FALSE, etENUM, {rdft},
883 { "-pbc", FALSE, etBOOL, {&bPBC},
884 "Use periodic boundary conditions for computing distances. Without PBC the maximum range will be three times the largest box edge." },
885 { "-norm", FALSE, etBOOL, {&bNormalize},
886 "Normalize for volume and density" },
887 { "-xy", FALSE, etBOOL, {&bXY},
888 "Use only the x and y components of the distance" },
889 { "-cut", FALSE, etREAL, {&cutoff},
890 "Shortest distance (nm) to be considered"},
891 { "-ng", FALSE, etINT, {&ngroups},
892 "Number of secondary groups to compute RDFs around a central group" },
893 { "-fade", FALSE, etREAL, {&fade},
894 "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." }
896 #define NPA asize(pa)
897 const char *fnTPS, *fnNDX;
901 { efTRX, "-f", NULL, ffREAD },
902 { efTPS, NULL, NULL, ffOPTRD },
903 { efNDX, NULL, NULL, ffOPTRD },
904 { efXVG, "-o", "rdf", ffWRITE },
905 { efXVG, "-cn", "rdf_cn", ffOPTWR },
906 { efXVG, "-hq", "hq", ffOPTWR },
908 #define NFILE asize(fnm)
909 parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
910 NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv);
912 if (bCM || closet[0][0] != 'n' || rdft[0][0] == 'm' || rdft[0][6] == 'm')
914 fnTPS = ftp2fn(efTPS, NFILE, fnm);
918 fnTPS = ftp2fn_null(efTPS, NFILE, fnm);
920 fnNDX = ftp2fn_null(efNDX, NFILE, fnm);
922 if (!fnTPS && !fnNDX)
924 gmx_fatal(FARGS, "Neither index file nor topology file specified\n"
928 if (closet[0][0] != 'n')
932 gmx_fatal(FARGS, "Can not have both -com and -surf");
936 fprintf(stderr, "Turning of normalization because of option -surf\n");
941 do_rdf(fnNDX, fnTPS, ftp2fn(efTRX, NFILE, fnm),
942 opt2fn("-o", NFILE, fnm), opt2fn_null("-cn", NFILE, fnm),
943 opt2fn_null("-hq", NFILE, fnm),
944 bCM, closet[0], rdft, bXY, bPBC, bNormalize, cutoff, binwidth, fade, ngroups,