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,2017,2018,2019, 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/filenm.h"
44 #include "gromacs/commandline/pargs.h"
45 #include "gromacs/fileio/matio.h"
46 #include "gromacs/fileio/tpxio.h"
47 #include "gromacs/fileio/trxio.h"
48 #include "gromacs/fileio/xvgr.h"
49 #include "gromacs/gmxana/gmx_ana.h"
50 #include "gromacs/gmxana/gstat.h"
51 #include "gromacs/gmxlib/nrnb.h"
52 #include "gromacs/math/units.h"
53 #include "gromacs/math/vec.h"
54 #include "gromacs/pbcutil/pbc.h"
55 #include "gromacs/topology/index.h"
56 #include "gromacs/topology/mtop_lookup.h"
57 #include "gromacs/topology/mtop_util.h"
58 #include "gromacs/topology/topology.h"
59 #include "gromacs/trajectory/trajectoryframe.h"
60 #include "gromacs/utility/arraysize.h"
61 #include "gromacs/utility/cstringutil.h"
62 #include "gromacs/utility/fatalerror.h"
63 #include "gromacs/utility/futil.h"
64 #include "gromacs/utility/gmxassert.h"
65 #include "gromacs/utility/smalloc.h"
67 static void clust_size(const char* ndx,
86 const gmx_output_env_t* oenv)
88 FILE * fp, *gp, *hp, *tp;
92 rvec * x = nullptr, *v = nullptr, dx;
94 gmx_bool bSame, bTPRwarn = TRUE;
98 gmx_mtop_t* mtop = nullptr;
102 /* Cluster size distribution (matrix) */
103 real** cs_dist = nullptr;
104 real tf, dx2, cut2, *t_x = nullptr, *t_y, cmid, cmax, cav, ekin;
105 int i, j, k, ai, aj, ci, cj, nframe, nclust, n_x, max_size = 0;
106 int * clust_index, *clust_size, max_clust_size, max_clust_ind, nav, nhisto;
107 t_rgb rlo = { 1.0, 1.0, 1.0 };
108 int frameCounter = 0;
111 clear_trxframe(&fr, TRUE);
112 auto timeLabel = output_env_get_time_label(oenv);
113 tf = output_env_get_time_factor(oenv);
114 fp = xvgropen(ncl, "Number of clusters", timeLabel, "N", oenv);
115 gp = xvgropen(acl, "Average cluster size", timeLabel, "#molecules", oenv);
116 hp = xvgropen(mcl, "Max cluster size", timeLabel, "#molecules", oenv);
117 tp = xvgropen(tempf, "Temperature of largest cluster", timeLabel, "T (K)", oenv);
119 if (!read_first_frame(oenv, &status, trx, &fr, TRX_NEED_X | TRX_READ_V))
129 mtop = new gmx_mtop_t;
130 tpxh = readTpxHeader(tpr, true);
131 if (tpxh.natoms != natoms)
133 gmx_fatal(FARGS, "tpr (%d atoms) and trajectory (%d atoms) do not match!", tpxh.natoms, natoms);
135 ePBC = read_tpx(tpr, nullptr, nullptr, &natoms, nullptr, nullptr, mtop);
143 tfac = ndf / (3.0 * natoms);
146 gmx::RangePartitioning mols;
151 printf("Using molecules rather than atoms. Not reading index file %s\n", ndx);
153 GMX_RELEASE_ASSERT(mtop != nullptr, "Trying to access mtop->mols from NULL mtop pointer");
154 mols = gmx_mtop_molecules(*mtop);
156 /* Make dummy index */
157 nindex = mols.numBlocks();
159 for (i = 0; (i < nindex); i++)
167 rd_index(ndx, 1, &nindex, &index, &gname);
171 snew(clust_index, nindex);
172 snew(clust_size, nindex);
177 for (i = 0; (i < nindex); i++)
186 if ((nskip == 0) || ((nskip > 0) && ((nframe % nskip) == 0)))
190 set_pbc(&pbc, ePBC, fr.box);
195 /* Put all atoms/molecules in their own cluster, with size 1 */
196 for (i = 0; (i < nindex); i++)
198 /* Cluster index is indexed with atom index number */
200 /* Cluster size is indexed with cluster number */
204 /* Loop over atoms */
205 for (i = 0; (i < nindex); i++)
210 /* Loop over atoms (only half a matrix) */
211 for (j = i + 1; (j < nindex); j++)
215 /* If they are not in the same cluster already */
220 /* Compute distance */
223 GMX_RELEASE_ASSERT(mols.numBlocks() > 0,
224 "Cannot access index[] from empty mols");
226 for (ii = mols.block(ai).begin(); !bSame && ii < mols.block(ai).end(); ii++)
228 for (jj = mols.block(aj).begin(); !bSame && jj < mols.block(aj).end(); jj++)
232 pbc_dx(&pbc, x[ii], x[jj], dx);
236 rvec_sub(x[ii], x[jj], dx);
239 bSame = (dx2 < cut2);
247 pbc_dx(&pbc, x[ai], x[aj], dx);
251 rvec_sub(x[ai], x[aj], dx);
254 bSame = (dx2 < cut2);
256 /* If distance less than cut-off */
259 /* Merge clusters: check for all atoms whether they are in
260 * cluster cj and if so, put them in ci
262 for (k = 0; (k < nindex); k++)
264 if (clust_index[k] == cj)
266 if (clust_size[cj] <= 0)
268 gmx_fatal(FARGS, "negative cluster size %d for element %d",
292 frameTime = ++frameCounter;
294 t_x[n_x - 1] = frameTime * tf;
295 srenew(cs_dist, n_x);
296 snew(cs_dist[n_x - 1], nindex);
300 for (i = 0; (i < nindex); i++)
303 if (ci > max_clust_size)
311 cs_dist[n_x - 1][ci - 1] += 1.0;
312 max_size = std::max(max_size, ci);
320 fprintf(fp, "%14.6e %10d\n", frameTime, nclust);
323 fprintf(gp, "%14.6e %10.3f\n", frameTime, cav / nav);
325 fprintf(hp, "%14.6e %10d\n", frameTime, max_clust_size);
327 /* Analyse velocities, if present */
334 printf("You need a [REF].tpr[ref] file to analyse temperatures\n");
341 /* Loop over clusters and for each cluster compute 1/2 m v^2 */
342 if (max_clust_ind >= 0)
345 for (i = 0; (i < nindex); i++)
347 if (clust_index[i] == max_clust_ind)
350 real m = mtopGetAtomMass(mtop, ai, &molb);
351 ekin += 0.5 * m * iprod(v[ai], v[ai]);
354 temp = (ekin * 2.0) / (3.0 * tfac * max_clust_size * BOLTZ);
355 fprintf(tp, "%10.3f %10.3f\n", frameTime, temp);
360 } while (read_next_frame(oenv, status, &fr));
368 if (max_clust_ind >= 0)
370 fp = gmx_ffopen(mcn, "w");
371 fprintf(fp, "[ max_clust ]\n");
372 for (i = 0; (i < nindex); i++)
374 if (clust_index[i] == max_clust_ind)
378 GMX_RELEASE_ASSERT(mols.numBlocks() > 0,
379 "Cannot access index[] from empty mols");
380 for (int j : mols.block(i))
382 fprintf(fp, "%d\n", j + 1);
387 fprintf(fp, "%d\n", index[i] + 1);
394 /* Print the real distribution cluster-size/numer, averaged over the trajectory. */
395 fp = xvgropen(histo, "Cluster size distribution", "Cluster size", "()", oenv);
397 fprintf(fp, "%5d %8.3f\n", 0, 0.0);
398 for (j = 0; (j < max_size); j++)
401 for (i = 0; (i < n_x); i++)
403 nelem += cs_dist[i][j];
405 fprintf(fp, "%5d %8.3f\n", j + 1, nelem / n_x);
406 nhisto += static_cast<int>((j + 1) * nelem / n_x);
408 fprintf(fp, "%5d %8.3f\n", j + 1, 0.0);
411 fprintf(stderr, "Total number of atoms in clusters = %d\n", nhisto);
413 /* Look for the smallest entry that is not zero
414 * This will make that zero is white, and not zero is coloured.
418 for (i = 0; (i < n_x); i++)
420 for (j = 0; (j < max_size); j++)
422 if ((cs_dist[i][j] > 0) && (cs_dist[i][j] < cmid))
424 cmid = cs_dist[i][j];
426 cmax = std::max(cs_dist[i][j], cmax);
429 fprintf(stderr, "cmid: %g, cmax: %g, max_size: %d\n", cmid, cmax, max_size);
431 fp = gmx_ffopen(xpm, "w");
432 write_xpm3(fp, 0, "Cluster size distribution", "# clusters", timeLabel, "Size", n_x, max_size,
433 t_x, t_y, cs_dist, 0, cmid, cmax, rlo, rmid, rhi, &nlevels);
437 for (i = 0; (i < n_x); i++)
439 for (j = 0; (j < max_size); j++)
441 cs_dist[i][j] *= (j + 1);
442 if ((cs_dist[i][j] > 0) && (cs_dist[i][j] < cmid))
444 cmid = cs_dist[i][j];
446 cmax = std::max(cs_dist[i][j], cmax);
449 fprintf(stderr, "cmid: %g, cmax: %g, max_size: %d\n", cmid, cmax, max_size);
450 fp = gmx_ffopen(xpmw, "w");
451 write_xpm3(fp, 0, "Weighted cluster size distribution", "Fraction", timeLabel, "Size", n_x,
452 max_size, t_x, t_y, cs_dist, 0, cmid, cmax, rlo, rmid, rhi, &nlevels);
457 for (i = 0; (i < n_x); i++)
467 int gmx_clustsize(int argc, char* argv[])
469 const char* desc[] = {
470 "[THISMODULE] computes the size distributions of molecular/atomic clusters in",
471 "the gas phase. The output is given in the form of an [REF].xpm[ref] file.",
472 "The total number of clusters is written to an [REF].xvg[ref] file.[PAR]",
473 "When the [TT]-mol[tt] option is given clusters will be made out of",
474 "molecules rather than atoms, which allows clustering of large molecules.",
475 "In this case an index file would still contain atom numbers",
476 "or your calculation will die with a SEGV.[PAR]",
477 "When velocities are present in your trajectory, the temperature of",
478 "the largest cluster will be printed in a separate [REF].xvg[ref] file assuming",
479 "that the particles are free to move. If you are using constraints,",
480 "please correct the temperature. For instance water simulated with SHAKE",
481 "or SETTLE will yield a temperature that is 1.5 times too low. You can",
482 "compensate for this with the [TT]-ndf[tt] option. Remember to take the removal",
483 "of center of mass motion into account.[PAR]",
484 "The [TT]-mc[tt] option will produce an index file containing the",
485 "atom numbers of the largest cluster."
492 gmx_bool bMol = FALSE;
493 gmx_bool bPBC = TRUE;
494 rvec rlo = { 1.0, 1.0, 0.0 };
495 rvec rhi = { 0.0, 0.0, 1.0 };
497 gmx_output_env_t* oenv;
504 "Largest distance (nm) to be considered in a cluster" },
509 "Cluster molecules rather than atoms (needs [REF].tpr[ref] file)" },
510 { "-pbc", FALSE, etBOOL, { &bPBC }, "Use periodic boundary conditions" },
511 { "-nskip", FALSE, etINT, { &nskip }, "Number of frames to skip between writing" },
516 "Number of levels of grey in [REF].xpm[ref] output" },
521 "Number of degrees of freedom of the entire system for temperature calculation. "
522 "If not set, the number of atoms times three is used." },
527 "RGB values for the color of the lowest occupied cluster size" },
532 "RGB values for the color of the highest occupied cluster size" }
534 #define NPA asize(pa)
535 const char *fnNDX, *fnTPR;
539 { efTRX, "-f", nullptr, ffREAD }, { efTPR, nullptr, nullptr, ffOPTRD },
540 { efNDX, nullptr, nullptr, ffOPTRD }, { efXPM, "-o", "csize", ffWRITE },
541 { efXPM, "-ow", "csizew", ffWRITE }, { efXVG, "-nc", "nclust", ffWRITE },
542 { efXVG, "-mc", "maxclust", ffWRITE }, { efXVG, "-ac", "avclust", ffWRITE },
543 { efXVG, "-hc", "histo-clust", ffWRITE }, { efXVG, "-temp", "temp", ffOPTWR },
544 { efNDX, "-mcn", "maxclust", ffOPTWR }
546 #define NFILE asize(fnm)
548 if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT, NFILE, fnm,
549 NPA, pa, asize(desc), desc, 0, nullptr, &oenv))
554 fnNDX = ftp2fn_null(efNDX, NFILE, fnm);
562 fnTPR = ftp2fn_null(efTPR, NFILE, fnm);
565 gmx_fatal(FARGS, "You need a tpr file for the -mol option");
568 clust_size(fnNDX, ftp2fn(efTRX, NFILE, fnm), opt2fn("-o", NFILE, fnm), opt2fn("-ow", NFILE, fnm),
569 opt2fn("-nc", NFILE, fnm), opt2fn("-ac", NFILE, fnm), opt2fn("-mc", NFILE, fnm),
570 opt2fn("-hc", NFILE, fnm), opt2fn("-temp", NFILE, fnm), opt2fn("-mcn", NFILE, fnm),
571 bMol, bPBC, fnTPR, cutoff, nskip, nlevels, rgblo, rgbhi, ndf, oenv);
573 output_env_done(oenv);