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 * 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.
42 #include "gromacs/commandline/pargs.h"
43 #include "gromacs/commandline/viewit.h"
44 #include "gromacs/fileio/confio.h"
45 #include "gromacs/fileio/matio.h"
46 #include "gromacs/fileio/trxio.h"
47 #include "gromacs/gmxana/gmx_ana.h"
48 #include "gromacs/gmxana/gstat.h"
49 #include "gromacs/math/utilities.h"
50 #include "gromacs/math/vec.h"
51 #include "gromacs/pbcutil/pbc.h"
52 #include "gromacs/topology/index.h"
53 #include "gromacs/topology/topology.h"
54 #include "gromacs/utility/arraysize.h"
55 #include "gromacs/utility/cstringutil.h"
56 #include "gromacs/utility/fatalerror.h"
57 #include "gromacs/utility/futil.h"
58 #include "gromacs/utility/gmxassert.h"
59 #include "gromacs/utility/smalloc.h"
61 int gmx_densmap(int argc, char* argv[])
63 const char* desc[] = {
64 "[THISMODULE] computes 2D number-density maps.",
65 "It can make planar and axial-radial density maps.",
66 "The output [REF].xpm[ref] file can be visualized with for instance xv",
67 "and can be converted to postscript with [TT]xpm2ps[tt].",
68 "Optionally, output can be in text form to a [REF].dat[ref] file with [TT]-od[tt], ",
69 "instead of the usual [REF].xpm[ref] file with [TT]-o[tt].",
71 "The default analysis is a 2-D number-density map for a selected",
72 "group of atoms in the x-y plane.",
73 "The averaging direction can be changed with the option [TT]-aver[tt].",
74 "When [TT]-xmin[tt] and/or [TT]-xmax[tt] are set only atoms that are",
75 "within the limit(s) in the averaging direction are taken into account.",
76 "The grid spacing is set with the option [TT]-bin[tt].",
77 "When [TT]-n1[tt] or [TT]-n2[tt] is non-zero, the grid",
78 "size is set by this option.",
79 "Box size fluctuations are properly taken into account.",
81 "When options [TT]-amax[tt] and [TT]-rmax[tt] are set, an axial-radial",
82 "number-density map is made. Three groups should be supplied, the centers",
83 "of mass of the first two groups define the axis, the third defines the",
84 "analysis group. The axial direction goes from -amax to +amax, where",
85 "the center is defined as the midpoint between the centers of mass and",
86 "the positive direction goes from the first to the second center of mass.",
87 "The radial direction goes from 0 to rmax or from -rmax to +rmax",
88 "when the [TT]-mirror[tt] option has been set.",
90 "The normalization of the output is set with the [TT]-unit[tt] option.",
91 "The default produces a true number density. Unit [TT]nm-2[tt] leaves out",
92 "the normalization for the averaging or the angular direction.",
93 "Option [TT]count[tt] produces the count for each grid cell.",
94 "When you do not want the scale in the output to go",
95 "from zero to the maximum density, you can set the maximum",
96 "with the option [TT]-dmax[tt]."
98 static int n1 = 0, n2 = 0;
99 static real xmin = -1, xmax = -1, bin = 0.02, dmin = 0, dmax = 0, amax = 0, rmax = 0;
100 static gmx_bool bMirror = FALSE, bSums = FALSE;
101 static const char* eaver[] = { nullptr, "z", "y", "x", nullptr };
102 static const char* eunit[] = { nullptr, "nm-3", "nm-2", "count", nullptr };
105 { "-bin", FALSE, etREAL, { &bin }, "Grid size (nm)" },
106 { "-aver", FALSE, etENUM, { eaver }, "The direction to average over" },
107 { "-xmin", FALSE, etREAL, { &xmin }, "Minimum coordinate for averaging" },
108 { "-xmax", FALSE, etREAL, { &xmax }, "Maximum coordinate for averaging" },
109 { "-n1", FALSE, etINT, { &n1 }, "Number of grid cells in the first direction" },
110 { "-n2", FALSE, etINT, { &n2 }, "Number of grid cells in the second direction" },
111 { "-amax", FALSE, etREAL, { &amax }, "Maximum axial distance from the center" },
112 { "-rmax", FALSE, etREAL, { &rmax }, "Maximum radial distance" },
113 { "-mirror", FALSE, etBOOL, { &bMirror }, "Add the mirror image below the axial axis" },
114 { "-sums", FALSE, etBOOL, { &bSums }, "Print density sums (1D map) to stdout" },
115 { "-unit", FALSE, etENUM, { eunit }, "Unit for the output" },
116 { "-dmin", FALSE, etREAL, { &dmin }, "Minimum density in output" },
117 { "-dmax", FALSE, etREAL, { &dmax }, "Maximum density in output (0 means calculate it)" },
119 gmx_bool bXmin, bXmax, bRadial;
124 rvec * x, xcom[2], direction, center, dx;
128 int cav = 0, c1 = 0, c2 = 0;
129 char ** grpname, buf[STRLEN];
131 int i, j, k, l, ngrps, anagrp, *gnx = nullptr, nindex, nradial = 0, nfr, nmpower;
132 int ** ind = nullptr, *index;
133 real ** grid, maxgrid, m1, m2, box1, box2, *tickx, *tickz, invcellvol;
134 real invspa = 0, invspz = 0, axial, r, vol_old, vol, rowsum;
136 t_rgb rlo = { 1, 1, 1 }, rhi = { 0, 0, 0 };
137 gmx_output_env_t* oenv;
138 const char* label[] = { "x (nm)", "y (nm)", "z (nm)" };
139 t_filenm fnm[] = { { efTRX, "-f", nullptr, ffREAD },
140 { efTPS, nullptr, nullptr, ffOPTRD },
141 { efNDX, nullptr, nullptr, ffOPTRD },
142 { efDAT, "-od", "densmap", ffOPTWR },
143 { efXPM, "-o", "densmap", ffWRITE } };
144 #define NFILE asize(fnm)
149 if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW, NFILE, fnm, npargs, pa,
150 asize(desc), desc, 0, nullptr, &oenv))
155 bXmin = opt2parg_bSet("-xmin", npargs, pa);
156 bXmax = opt2parg_bSet("-xmax", npargs, pa);
157 bRadial = (amax > 0 || rmax > 0);
160 if (amax <= 0 || rmax <= 0)
162 gmx_fatal(FARGS, "Both amax and rmax should be larger than zero");
166 GMX_RELEASE_ASSERT(eunit[0] != nullptr, "Option setting inconsistency; eunit[0] is NULL");
168 if (std::strcmp(eunit[0], "nm-3") == 0)
173 else if (std::strcmp(eunit[0], "nm-2") == 0)
184 if (ftp2bSet(efTPS, NFILE, fnm) || !ftp2bSet(efNDX, NFILE, fnm))
186 read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &x, nullptr, box, bRadial);
191 fprintf(stderr, "\nSelect an analysis group\n");
196 fprintf(stderr, "\nSelect two groups to define the axis and an analysis group\n");
199 snew(grpname, ngrps);
201 get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), ngrps, gnx, ind, grpname);
203 nindex = gnx[anagrp];
207 if ((gnx[0] > 1 || gnx[1] > 1) && !ftp2bSet(efTPS, NFILE, fnm))
210 "No run input file was supplied (option -s), this is required for the center "
211 "of mass calculation");
215 GMX_RELEASE_ASSERT(eaver[0] != nullptr, "Option setting inconsistency; eaver[0] is NULL");
236 read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
242 n1 = gmx::roundToInt(box[c1][c1] / bin);
246 n2 = gmx::roundToInt(box[c2][c2] / bin);
251 n1 = gmx::roundToInt(2 * amax / bin);
252 nradial = gmx::roundToInt(rmax / bin);
253 invspa = n1 / (2 * amax);
254 invspz = nradial / rmax;
266 for (i = 0; i < n1; i++)
280 invcellvol = n1 * n2;
283 invcellvol /= det(box);
285 else if (nmpower == -2)
287 invcellvol /= box[c1][c1] * box[c2][c2];
289 for (i = 0; i < nindex; i++)
292 if ((!bXmin || x[j][cav] >= xmin) && (!bXmax || x[j][cav] <= xmax))
294 m1 = x[j][c1] / box[c1][c1];
303 m2 = x[j][c2] / box[c2][c2];
312 grid[static_cast<int>(m1 * n1)][static_cast<int>(m2 * n2)] += invcellvol;
318 set_pbc(&pbc, ePBC, box);
319 for (i = 0; i < 2; i++)
323 /* One atom, just copy the coordinates */
324 copy_rvec(x[ind[i][0]], xcom[i]);
328 /* Calculate the center of mass */
331 for (j = 0; j < gnx[i]; j++)
334 m = top.atoms.atom[k].m;
335 for (l = 0; l < DIM; l++)
337 xcom[i][l] += m * x[k][l];
341 svmul(1 / mtot, xcom[i], xcom[i]);
344 pbc_dx(&pbc, xcom[1], xcom[0], direction);
345 for (i = 0; i < DIM; i++)
347 center[i] = xcom[0][i] + 0.5 * direction[i];
349 unitv(direction, direction);
350 for (i = 0; i < nindex; i++)
353 pbc_dx(&pbc, x[j], center, dx);
354 axial = iprod(dx, direction);
355 r = std::sqrt(norm2(dx) - axial * axial);
356 if (axial >= -amax && axial < amax && r < rmax)
362 grid[static_cast<int>((axial + amax) * invspa)][static_cast<int>(r * invspz)] += 1;
367 } while (read_next_x(oenv, status, &t, x, box));
370 /* normalize gridpoints */
374 for (i = 0; i < n1; i++)
376 for (j = 0; j < n2; j++)
379 if (grid[i][j] > maxgrid)
381 maxgrid = grid[i][j];
388 for (i = 0; i < n1; i++)
391 for (j = 0; j < nradial; j++)
395 case -3: vol = M_PI * (j + 1) * (j + 1) / (invspz * invspz * invspa); break;
396 case -2: vol = (j + 1) / (invspz * invspa); break;
397 default: vol = j + 1; break;
407 grid[i][k] /= nfr * (vol - vol_old);
410 grid[i][nradial - 1 - j] = grid[i][k];
413 if (grid[i][k] > maxgrid)
415 maxgrid = grid[i][k];
420 fprintf(stdout, "\n The maximum density is %f %s\n", maxgrid, unit);
430 /* normalize box-axes */
433 for (i = 0; i <= n1; i++)
435 tickx[i] = i * box1 / n1;
437 for (i = 0; i <= n2; i++)
439 tickz[i] = i * box2 / n2;
444 for (i = 0; i <= n1; i++)
446 tickx[i] = i / invspa - amax;
450 for (i = 0; i <= n2; i++)
452 tickz[i] = i / invspz - rmax;
457 for (i = 0; i <= n2; i++)
459 tickz[i] = i / invspz;
466 for (i = 0; i < n1; ++i)
468 fprintf(stdout, "Density sums:\n");
470 for (j = 0; j < n2; ++j)
472 rowsum += grid[i][j];
474 fprintf(stdout, "%g\t", rowsum);
476 fprintf(stdout, "\n");
479 sprintf(buf, "%s number density", grpname[anagrp]);
480 if (!bRadial && (bXmin || bXmax))
484 sprintf(buf + std::strlen(buf), ", %c > %g nm", eaver[0][0], xmin);
488 sprintf(buf + std::strlen(buf), ", %c < %g nm", eaver[0][0], xmax);
492 sprintf(buf + std::strlen(buf), ", %c: %g - %g nm", eaver[0][0], xmin, xmax);
495 if (ftp2bSet(efDAT, NFILE, fnm))
497 fp = gmx_ffopen(ftp2fn(efDAT, NFILE, fnm), "w");
498 /*optional text form output: first row is tickz; first col is tickx */
500 for (j = 0; j < n2; ++j)
502 fprintf(fp, "%g\t", tickz[j]);
506 for (i = 0; i < n1; ++i)
508 fprintf(fp, "%g\t", tickx[i]);
509 for (j = 0; j < n2; ++j)
511 fprintf(fp, "%g\t", grid[i][j]);
519 fp = gmx_ffopen(ftp2fn(efXPM, NFILE, fnm), "w");
520 write_xpm(fp, MAT_SPATIAL_X | MAT_SPATIAL_Y, buf, unit, bRadial ? "axial (nm)" : label[c1],
521 bRadial ? "r (nm)" : label[c2], n1, n2, tickx, tickz, grid, dmin, maxgrid, rlo,
526 do_view(oenv, opt2fn("-o", NFILE, fnm), nullptr);