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-2007, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
43 #include "gromacs/commandline/pargs.h"
44 #include "gromacs/fileio/matio.h"
45 #include "gromacs/fileio/tpxio.h"
46 #include "gromacs/fileio/trxio.h"
47 #include "gromacs/fileio/xvgr.h"
48 #include "gromacs/gmxana/gmx_ana.h"
49 #include "gromacs/gmxana/gstat.h"
50 #include "gromacs/gmxlib/nrnb.h"
51 #include "gromacs/math/units.h"
52 #include "gromacs/math/vec.h"
53 #include "gromacs/pbcutil/pbc.h"
54 #include "gromacs/topology/index.h"
55 #include "gromacs/topology/mtop_lookup.h"
56 #include "gromacs/topology/topology.h"
57 #include "gromacs/trajectory/trajectoryframe.h"
58 #include "gromacs/utility/arraysize.h"
59 #include "gromacs/utility/cstringutil.h"
60 #include "gromacs/utility/fatalerror.h"
61 #include "gromacs/utility/futil.h"
62 #include "gromacs/utility/gmxassert.h"
63 #include "gromacs/utility/smalloc.h"
65 static void clust_size(const char *ndx, const char *trx, const char *xpm,
66 const char *xpmw, const char *ncl, const char *acl,
67 const char *mcl, const char *histo, const char *tempf,
68 const char *mcn, gmx_bool bMol, gmx_bool bPBC, const char *tpr,
69 real cut, int nskip, int nlevels,
70 t_rgb rmid, t_rgb rhi, int ndf,
71 const gmx_output_env_t *oenv)
73 FILE *fp, *gp, *hp, *tp;
77 rvec *x = NULL, *v = NULL, dx;
81 gmx_bool bSame, bTPRwarn = TRUE;
85 gmx_mtop_t *mtop = NULL;
90 /* Cluster size distribution (matrix) */
91 real **cs_dist = NULL;
92 real tf, dx2, cut2, *t_x = NULL, *t_y, cmid, cmax, cav, ekin;
93 int i, j, k, ai, aj, ci, cj, nframe, nclust, n_x, max_size = 0;
94 int *clust_index, *clust_size, max_clust_size, max_clust_ind, nav, nhisto;
95 t_rgb rlo = { 1.0, 1.0, 1.0 };
97 clear_trxframe(&fr, TRUE);
98 sprintf(timebuf, "Time (%s)", output_env_get_time_unit(oenv));
99 tf = output_env_get_time_factor(oenv);
100 fp = xvgropen(ncl, "Number of clusters", timebuf, "N", oenv);
101 gp = xvgropen(acl, "Average cluster size", timebuf, "#molecules", oenv);
102 hp = xvgropen(mcl, "Max cluster size", timebuf, "#molecules", oenv);
103 tp = xvgropen(tempf, "Temperature of largest cluster", timebuf, "T (K)",
106 if (!read_first_frame(oenv, &status, trx, &fr, TRX_NEED_X | TRX_READ_V))
117 read_tpxheader(tpr, &tpxh, TRUE);
118 if (tpxh.natoms != natoms)
120 gmx_fatal(FARGS, "tpr (%d atoms) and trajectory (%d atoms) do not match!",
121 tpxh.natoms, natoms);
123 ePBC = read_tpx(tpr, NULL, NULL, &natoms, NULL, NULL, mtop);
131 tfac = ndf/(3.0*natoms);
138 printf("Using molecules rather than atoms. Not reading index file %s\n",
141 GMX_RELEASE_ASSERT(mtop != NULL, "Trying to access mtop->mols from NULL mtop pointer");
142 mols = &(mtop->mols);
144 /* Make dummy index */
147 for (i = 0; (i < nindex); i++)
151 gname = gmx_strdup("mols");
155 rd_index(ndx, 1, &nindex, &index, &gname);
158 snew(clust_index, nindex);
159 snew(clust_size, nindex);
164 for (i = 0; (i < nindex); i++)
173 if ((nskip == 0) || ((nskip > 0) && ((nframe % nskip) == 0)))
177 set_pbc(&pbc, ePBC, fr.box);
182 /* Put all atoms/molecules in their own cluster, with size 1 */
183 for (i = 0; (i < nindex); i++)
185 /* Cluster index is indexed with atom index number */
187 /* Cluster size is indexed with cluster number */
191 /* Loop over atoms */
192 for (i = 0; (i < nindex); i++)
197 /* Loop over atoms (only half a matrix) */
198 for (j = i+1; (j < nindex); j++)
202 /* If they are not in the same cluster already */
207 /* Compute distance */
210 GMX_RELEASE_ASSERT(mols != NULL, "Cannot access index[] from NULL mols pointer");
212 for (ii = mols->index[ai]; !bSame && (ii < mols->index[ai+1]); ii++)
214 for (jj = mols->index[aj]; !bSame && (jj < mols->index[aj+1]); jj++)
218 pbc_dx(&pbc, x[ii], x[jj], dx);
222 rvec_sub(x[ii], x[jj], dx);
225 bSame = (dx2 < cut2);
233 pbc_dx(&pbc, x[ai], x[aj], dx);
237 rvec_sub(x[ai], x[aj], dx);
240 bSame = (dx2 < cut2);
242 /* If distance less than cut-off */
245 /* Merge clusters: check for all atoms whether they are in
246 * cluster cj and if so, put them in ci
248 for (k = 0; (k < nindex); k++)
250 if (clust_index[k] == cj)
252 if (clust_size[cj] <= 0)
254 gmx_fatal(FARGS, "negative cluster size %d for element %d",
268 t_x[n_x-1] = fr.time*tf;
269 srenew(cs_dist, n_x);
270 snew(cs_dist[n_x-1], nindex);
274 for (i = 0; (i < nindex); i++)
277 if (ci > max_clust_size)
285 cs_dist[n_x-1][ci-1] += 1.0;
286 max_size = std::max(max_size, ci);
294 fprintf(fp, "%14.6e %10d\n", fr.time, nclust);
297 fprintf(gp, "%14.6e %10.3f\n", fr.time, cav/nav);
299 fprintf(hp, "%14.6e %10d\n", fr.time, max_clust_size);
301 /* Analyse velocities, if present */
308 printf("You need a [REF].tpr[ref] file to analyse temperatures\n");
315 /* Loop over clusters and for each cluster compute 1/2 m v^2 */
316 if (max_clust_ind >= 0)
319 for (i = 0; (i < nindex); i++)
321 if (clust_index[i] == max_clust_ind)
324 real m = mtopGetAtomMass(mtop, ai, &molb);
325 ekin += 0.5*m*iprod(v[ai], v[ai]);
328 temp = (ekin*2.0)/(3.0*tfac*max_clust_size*BOLTZ);
329 fprintf(tp, "%10.3f %10.3f\n", fr.time, temp);
335 while (read_next_frame(oenv, status, &fr));
342 if (max_clust_ind >= 0)
344 fp = gmx_ffopen(mcn, "w");
345 fprintf(fp, "[ max_clust ]\n");
346 for (i = 0; (i < nindex); i++)
348 if (clust_index[i] == max_clust_ind)
352 GMX_RELEASE_ASSERT(mols != NULL, "Cannot access index[] from NULL mols pointer");
353 for (j = mols->index[i]; (j < mols->index[i+1]); j++)
355 fprintf(fp, "%d\n", j+1);
360 fprintf(fp, "%d\n", index[i]+1);
367 /* Print the real distribution cluster-size/numer, averaged over the trajectory. */
368 fp = xvgropen(histo, "Cluster size distribution", "Cluster size", "()", oenv);
370 fprintf(fp, "%5d %8.3f\n", 0, 0.0);
371 for (j = 0; (j < max_size); j++)
374 for (i = 0; (i < n_x); i++)
376 nelem += cs_dist[i][j];
378 fprintf(fp, "%5d %8.3f\n", j+1, nelem/n_x);
379 nhisto += static_cast<int>((j+1)*nelem/n_x);
381 fprintf(fp, "%5d %8.3f\n", j+1, 0.0);
384 fprintf(stderr, "Total number of atoms in clusters = %d\n", nhisto);
386 /* Look for the smallest entry that is not zero
387 * This will make that zero is white, and not zero is coloured.
391 for (i = 0; (i < n_x); i++)
393 for (j = 0; (j < max_size); j++)
395 if ((cs_dist[i][j] > 0) && (cs_dist[i][j] < cmid))
397 cmid = cs_dist[i][j];
399 cmax = std::max(cs_dist[i][j], cmax);
402 fprintf(stderr, "cmid: %g, cmax: %g, max_size: %d\n", cmid, cmax, max_size);
404 fp = gmx_ffopen(xpm, "w");
405 write_xpm3(fp, 0, "Cluster size distribution", "# clusters", timebuf, "Size",
406 n_x, max_size, t_x, t_y, cs_dist, 0, cmid, cmax,
407 rlo, rmid, rhi, &nlevels);
411 for (i = 0; (i < n_x); i++)
413 for (j = 0; (j < max_size); j++)
415 cs_dist[i][j] *= (j+1);
416 if ((cs_dist[i][j] > 0) && (cs_dist[i][j] < cmid))
418 cmid = cs_dist[i][j];
420 cmax = std::max(cs_dist[i][j], cmax);
423 fprintf(stderr, "cmid: %g, cmax: %g, max_size: %d\n", cmid, cmax, max_size);
424 fp = gmx_ffopen(xpmw, "w");
425 write_xpm3(fp, 0, "Weighted cluster size distribution", "Fraction", timebuf,
426 "Size", n_x, max_size, t_x, t_y, cs_dist, 0, cmid, cmax,
427 rlo, rmid, rhi, &nlevels);
435 int gmx_clustsize(int argc, char *argv[])
437 const char *desc[] = {
438 "[THISMODULE] computes the size distributions of molecular/atomic clusters in",
439 "the gas phase. The output is given in the form of an [REF].xpm[ref] file.",
440 "The total number of clusters is written to an [REF].xvg[ref] file.[PAR]",
441 "When the [TT]-mol[tt] option is given clusters will be made out of",
442 "molecules rather than atoms, which allows clustering of large molecules.",
443 "In this case an index file would still contain atom numbers",
444 "or your calculation will die with a SEGV.[PAR]",
445 "When velocities are present in your trajectory, the temperature of",
446 "the largest cluster will be printed in a separate [REF].xvg[ref] file assuming",
447 "that the particles are free to move. If you are using constraints,",
448 "please correct the temperature. For instance water simulated with SHAKE",
449 "or SETTLE will yield a temperature that is 1.5 times too low. You can",
450 "compensate for this with the [TT]-ndf[tt] option. Remember to take the removal",
451 "of center of mass motion into account.[PAR]",
452 "The [TT]-mc[tt] option will produce an index file containing the",
453 "atom numbers of the largest cluster."
456 static real cutoff = 0.35;
457 static int nskip = 0;
458 static int nlevels = 20;
460 static gmx_bool bMol = FALSE;
461 static gmx_bool bPBC = TRUE;
462 static rvec rlo = { 1.0, 1.0, 0.0 };
463 static rvec rhi = { 0.0, 0.0, 1.0 };
465 gmx_output_env_t *oenv;
468 { "-cut", FALSE, etREAL, {&cutoff},
469 "Largest distance (nm) to be considered in a cluster" },
470 { "-mol", FALSE, etBOOL, {&bMol},
471 "Cluster molecules rather than atoms (needs [REF].tpr[ref] file)" },
472 { "-pbc", FALSE, etBOOL, {&bPBC},
473 "Use periodic boundary conditions" },
474 { "-nskip", FALSE, etINT, {&nskip},
475 "Number of frames to skip between writing" },
476 { "-nlevels", FALSE, etINT, {&nlevels},
477 "Number of levels of grey in [REF].xpm[ref] output" },
478 { "-ndf", FALSE, etINT, {&ndf},
479 "Number of degrees of freedom of the entire system for temperature calculation. If not set, the number of atoms times three is used." },
480 { "-rgblo", FALSE, etRVEC, {rlo},
481 "RGB values for the color of the lowest occupied cluster size" },
482 { "-rgbhi", FALSE, etRVEC, {rhi},
483 "RGB values for the color of the highest occupied cluster size" }
485 #define NPA asize(pa)
486 const char *fnNDX, *fnTPR;
490 { efTRX, "-f", NULL, ffREAD },
491 { efTPR, NULL, NULL, ffOPTRD },
492 { efNDX, NULL, NULL, ffOPTRD },
493 { efXPM, "-o", "csize", ffWRITE },
494 { efXPM, "-ow", "csizew", ffWRITE },
495 { efXVG, "-nc", "nclust", ffWRITE },
496 { efXVG, "-mc", "maxclust", ffWRITE },
497 { efXVG, "-ac", "avclust", ffWRITE },
498 { efXVG, "-hc", "histo-clust", ffWRITE },
499 { efXVG, "-temp", "temp", ffOPTWR },
500 { efNDX, "-mcn", "maxclust", ffOPTWR }
502 #define NFILE asize(fnm)
504 if (!parse_common_args(&argc, argv,
505 PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT,
506 NFILE, fnm, NPA, pa, asize(desc), desc, 0, NULL, &oenv))
511 fnNDX = ftp2fn_null(efNDX, NFILE, fnm);
512 rgblo.r = rlo[XX], rgblo.g = rlo[YY], rgblo.b = rlo[ZZ];
513 rgbhi.r = rhi[XX], rgbhi.g = rhi[YY], rgbhi.b = rhi[ZZ];
515 fnTPR = ftp2fn_null(efTPR, NFILE, fnm);
518 gmx_fatal(FARGS, "You need a tpr file for the -mol option");
521 clust_size(fnNDX, ftp2fn(efTRX, NFILE, fnm), opt2fn("-o", NFILE, fnm),
522 opt2fn("-ow", NFILE, fnm),
523 opt2fn("-nc", NFILE, fnm), opt2fn("-ac", NFILE, fnm),
524 opt2fn("-mc", NFILE, fnm), opt2fn("-hc", NFILE, fnm),
525 opt2fn("-temp", NFILE, fnm), opt2fn("-mcn", NFILE, fnm),
527 cutoff, nskip, nlevels, rgblo, rgbhi, ndf, oenv);