Update instructions in containers.rst
[gromacs.git] / src / gromacs / gmxana / gmx_densmap.cpp
blobf9c838b2a723f28db2e0c8245be4be1a8494c358
1 /*
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 by the GROMACS development team.
7 * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source 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.
38 #include "gmxpre.h"
40 #include <cmath>
41 #include <cstring>
43 #include "gromacs/commandline/pargs.h"
44 #include "gromacs/commandline/viewit.h"
45 #include "gromacs/fileio/confio.h"
46 #include "gromacs/fileio/matio.h"
47 #include "gromacs/fileio/trxio.h"
48 #include "gromacs/gmxana/gmx_ana.h"
49 #include "gromacs/gmxana/gstat.h"
50 #include "gromacs/math/utilities.h"
51 #include "gromacs/math/vec.h"
52 #include "gromacs/pbcutil/pbc.h"
53 #include "gromacs/topology/index.h"
54 #include "gromacs/topology/topology.h"
55 #include "gromacs/utility/arraysize.h"
56 #include "gromacs/utility/cstringutil.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/futil.h"
59 #include "gromacs/utility/gmxassert.h"
60 #include "gromacs/utility/smalloc.h"
62 int gmx_densmap(int argc, char* argv[])
64 const char* desc[] = {
65 "[THISMODULE] computes 2D number-density maps.",
66 "It can make planar and axial-radial density maps.",
67 "The output [REF].xpm[ref] file can be visualized with for instance xv",
68 "and can be converted to postscript with [TT]xpm2ps[tt].",
69 "Optionally, output can be in text form to a [REF].dat[ref] file with [TT]-od[tt], ",
70 "instead of the usual [REF].xpm[ref] file with [TT]-o[tt].",
71 "[PAR]",
72 "The default analysis is a 2-D number-density map for a selected",
73 "group of atoms in the x-y plane.",
74 "The averaging direction can be changed with the option [TT]-aver[tt].",
75 "When [TT]-xmin[tt] and/or [TT]-xmax[tt] are set only atoms that are",
76 "within the limit(s) in the averaging direction are taken into account.",
77 "The grid spacing is set with the option [TT]-bin[tt].",
78 "When [TT]-n1[tt] or [TT]-n2[tt] is non-zero, the grid",
79 "size is set by this option.",
80 "Box size fluctuations are properly taken into account.",
81 "[PAR]",
82 "When options [TT]-amax[tt] and [TT]-rmax[tt] are set, an axial-radial",
83 "number-density map is made. Three groups should be supplied, the centers",
84 "of mass of the first two groups define the axis, the third defines the",
85 "analysis group. The axial direction goes from -amax to +amax, where",
86 "the center is defined as the midpoint between the centers of mass and",
87 "the positive direction goes from the first to the second center of mass.",
88 "The radial direction goes from 0 to rmax or from -rmax to +rmax",
89 "when the [TT]-mirror[tt] option has been set.",
90 "[PAR]",
91 "The normalization of the output is set with the [TT]-unit[tt] option.",
92 "The default produces a true number density. Unit [TT]nm-2[tt] leaves out",
93 "the normalization for the averaging or the angular direction.",
94 "Option [TT]count[tt] produces the count for each grid cell.",
95 "When you do not want the scale in the output to go",
96 "from zero to the maximum density, you can set the maximum",
97 "with the option [TT]-dmax[tt]."
99 static int n1 = 0, n2 = 0;
100 static real xmin = -1, xmax = -1, bin = 0.02, dmin = 0, dmax = 0, amax = 0, rmax = 0;
101 static gmx_bool bMirror = FALSE, bSums = FALSE;
102 static const char* eaver[] = { nullptr, "z", "y", "x", nullptr };
103 static const char* eunit[] = { nullptr, "nm-3", "nm-2", "count", nullptr };
105 t_pargs pa[] = {
106 { "-bin", FALSE, etREAL, { &bin }, "Grid size (nm)" },
107 { "-aver", FALSE, etENUM, { eaver }, "The direction to average over" },
108 { "-xmin", FALSE, etREAL, { &xmin }, "Minimum coordinate for averaging" },
109 { "-xmax", FALSE, etREAL, { &xmax }, "Maximum coordinate for averaging" },
110 { "-n1", FALSE, etINT, { &n1 }, "Number of grid cells in the first direction" },
111 { "-n2", FALSE, etINT, { &n2 }, "Number of grid cells in the second direction" },
112 { "-amax", FALSE, etREAL, { &amax }, "Maximum axial distance from the center" },
113 { "-rmax", FALSE, etREAL, { &rmax }, "Maximum radial distance" },
114 { "-mirror", FALSE, etBOOL, { &bMirror }, "Add the mirror image below the axial axis" },
115 { "-sums", FALSE, etBOOL, { &bSums }, "Print density sums (1D map) to stdout" },
116 { "-unit", FALSE, etENUM, { eunit }, "Unit for the output" },
117 { "-dmin", FALSE, etREAL, { &dmin }, "Minimum density in output" },
118 { "-dmax", FALSE, etREAL, { &dmax }, "Maximum density in output (0 means calculate it)" },
120 gmx_bool bXmin, bXmax, bRadial;
121 FILE* fp;
122 t_trxstatus* status;
123 t_topology top;
124 PbcType pbcType = PbcType::Unset;
125 rvec * x, xcom[2], direction, center, dx;
126 matrix box;
127 real t, m, mtot;
128 t_pbc pbc;
129 int cav = 0, c1 = 0, c2 = 0;
130 char ** grpname, buf[STRLEN];
131 const char* unit;
132 int i, j, k, l, ngrps, anagrp, *gnx = nullptr, nindex, nradial = 0, nfr, nmpower;
133 int ** ind = nullptr, *index;
134 real ** grid, maxgrid, m1, m2, box1, box2, *tickx, *tickz, invcellvol;
135 real invspa = 0, invspz = 0, axial, r, vol_old, vol, rowsum;
136 int nlev = 51;
137 t_rgb rlo = { 1, 1, 1 }, rhi = { 0, 0, 0 };
138 gmx_output_env_t* oenv;
139 const char* label[] = { "x (nm)", "y (nm)", "z (nm)" };
140 t_filenm fnm[] = { { efTRX, "-f", nullptr, ffREAD },
141 { efTPS, nullptr, nullptr, ffOPTRD },
142 { efNDX, nullptr, nullptr, ffOPTRD },
143 { efDAT, "-od", "densmap", ffOPTWR },
144 { efXPM, "-o", "densmap", ffWRITE } };
145 #define NFILE asize(fnm)
146 int npargs;
148 npargs = asize(pa);
150 if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW, NFILE, fnm, npargs, pa,
151 asize(desc), desc, 0, nullptr, &oenv))
153 return 0;
156 bXmin = opt2parg_bSet("-xmin", npargs, pa);
157 bXmax = opt2parg_bSet("-xmax", npargs, pa);
158 bRadial = (amax > 0 || rmax > 0);
159 if (bRadial)
161 if (amax <= 0 || rmax <= 0)
163 gmx_fatal(FARGS, "Both amax and rmax should be larger than zero");
167 GMX_RELEASE_ASSERT(eunit[0] != nullptr, "Option setting inconsistency; eunit[0] is NULL");
169 if (std::strcmp(eunit[0], "nm-3") == 0)
171 nmpower = -3;
172 unit = "(nm^-3)";
174 else if (std::strcmp(eunit[0], "nm-2") == 0)
176 nmpower = -2;
177 unit = "(nm^-2)";
179 else
181 nmpower = 0;
182 unit = "count";
185 if (ftp2bSet(efTPS, NFILE, fnm) || !ftp2bSet(efNDX, NFILE, fnm))
187 read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, &x, nullptr, box, bRadial);
189 if (!bRadial)
191 ngrps = 1;
192 fprintf(stderr, "\nSelect an analysis group\n");
194 else
196 ngrps = 3;
197 fprintf(stderr, "\nSelect two groups to define the axis and an analysis group\n");
199 snew(gnx, ngrps);
200 snew(grpname, ngrps);
201 snew(ind, ngrps);
202 get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), ngrps, gnx, ind, grpname);
203 anagrp = ngrps - 1;
204 nindex = gnx[anagrp];
205 index = ind[anagrp];
206 if (bRadial)
208 if ((gnx[0] > 1 || gnx[1] > 1) && !ftp2bSet(efTPS, NFILE, fnm))
210 gmx_fatal(FARGS,
211 "No run input file was supplied (option -s), this is required for the center "
212 "of mass calculation");
216 GMX_RELEASE_ASSERT(eaver[0] != nullptr, "Option setting inconsistency; eaver[0] is NULL");
218 switch (eaver[0][0])
220 case 'x':
221 cav = XX;
222 c1 = YY;
223 c2 = ZZ;
224 break;
225 case 'y':
226 cav = YY;
227 c1 = XX;
228 c2 = ZZ;
229 break;
230 case 'z':
231 cav = ZZ;
232 c1 = XX;
233 c2 = YY;
234 break;
237 read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
239 if (!bRadial)
241 if (n1 == 0)
243 n1 = gmx::roundToInt(box[c1][c1] / bin);
245 if (n2 == 0)
247 n2 = gmx::roundToInt(box[c2][c2] / bin);
250 else
252 n1 = gmx::roundToInt(2 * amax / bin);
253 nradial = gmx::roundToInt(rmax / bin);
254 invspa = n1 / (2 * amax);
255 invspz = nradial / rmax;
256 if (bMirror)
258 n2 = 2 * nradial;
260 else
262 n2 = nradial;
266 snew(grid, n1);
267 for (i = 0; i < n1; i++)
269 snew(grid[i], n2);
272 box1 = 0;
273 box2 = 0;
274 nfr = 0;
277 if (!bRadial)
279 box1 += box[c1][c1];
280 box2 += box[c2][c2];
281 invcellvol = n1 * n2;
282 if (nmpower == -3)
284 invcellvol /= det(box);
286 else if (nmpower == -2)
288 invcellvol /= box[c1][c1] * box[c2][c2];
290 for (i = 0; i < nindex; i++)
292 j = index[i];
293 if ((!bXmin || x[j][cav] >= xmin) && (!bXmax || x[j][cav] <= xmax))
295 m1 = x[j][c1] / box[c1][c1];
296 if (m1 >= 1)
298 m1 -= 1;
300 if (m1 < 0)
302 m1 += 1;
304 m2 = x[j][c2] / box[c2][c2];
305 if (m2 >= 1)
307 m2 -= 1;
309 if (m2 < 0)
311 m2 += 1;
313 grid[static_cast<int>(m1 * n1)][static_cast<int>(m2 * n2)] += invcellvol;
317 else
319 set_pbc(&pbc, pbcType, box);
320 for (i = 0; i < 2; i++)
322 if (gnx[i] == 1)
324 /* One atom, just copy the coordinates */
325 copy_rvec(x[ind[i][0]], xcom[i]);
327 else
329 /* Calculate the center of mass */
330 clear_rvec(xcom[i]);
331 mtot = 0;
332 for (j = 0; j < gnx[i]; j++)
334 k = ind[i][j];
335 m = top.atoms.atom[k].m;
336 for (l = 0; l < DIM; l++)
338 xcom[i][l] += m * x[k][l];
340 mtot += m;
342 svmul(1 / mtot, xcom[i], xcom[i]);
345 pbc_dx(&pbc, xcom[1], xcom[0], direction);
346 for (i = 0; i < DIM; i++)
348 center[i] = xcom[0][i] + 0.5 * direction[i];
350 unitv(direction, direction);
351 for (i = 0; i < nindex; i++)
353 j = index[i];
354 pbc_dx(&pbc, x[j], center, dx);
355 axial = iprod(dx, direction);
356 r = std::sqrt(norm2(dx) - axial * axial);
357 if (axial >= -amax && axial < amax && r < rmax)
359 if (bMirror)
361 r += rmax;
363 grid[static_cast<int>((axial + amax) * invspa)][static_cast<int>(r * invspz)] += 1;
367 nfr++;
368 } while (read_next_x(oenv, status, &t, x, box));
369 close_trx(status);
371 /* normalize gridpoints */
372 maxgrid = 0;
373 if (!bRadial)
375 for (i = 0; i < n1; i++)
377 for (j = 0; j < n2; j++)
379 grid[i][j] /= nfr;
380 if (grid[i][j] > maxgrid)
382 maxgrid = grid[i][j];
387 else
389 for (i = 0; i < n1; i++)
391 vol_old = 0;
392 for (j = 0; j < nradial; j++)
394 switch (nmpower)
396 case -3: vol = M_PI * (j + 1) * (j + 1) / (invspz * invspz * invspa); break;
397 case -2: vol = (j + 1) / (invspz * invspa); break;
398 default: vol = j + 1; break;
400 if (bMirror)
402 k = j + nradial;
404 else
406 k = j;
408 grid[i][k] /= nfr * (vol - vol_old);
409 if (bMirror)
411 grid[i][nradial - 1 - j] = grid[i][k];
413 vol_old = vol;
414 if (grid[i][k] > maxgrid)
416 maxgrid = grid[i][k];
421 fprintf(stdout, "\n The maximum density is %f %s\n", maxgrid, unit);
422 if (dmax > 0)
424 maxgrid = dmax;
427 snew(tickx, n1 + 1);
428 snew(tickz, n2 + 1);
429 if (!bRadial)
431 /* normalize box-axes */
432 box1 /= nfr;
433 box2 /= nfr;
434 for (i = 0; i <= n1; i++)
436 tickx[i] = i * box1 / n1;
438 for (i = 0; i <= n2; i++)
440 tickz[i] = i * box2 / n2;
443 else
445 for (i = 0; i <= n1; i++)
447 tickx[i] = i / invspa - amax;
449 if (bMirror)
451 for (i = 0; i <= n2; i++)
453 tickz[i] = i / invspz - rmax;
456 else
458 for (i = 0; i <= n2; i++)
460 tickz[i] = i / invspz;
465 if (bSums)
467 for (i = 0; i < n1; ++i)
469 fprintf(stdout, "Density sums:\n");
470 rowsum = 0;
471 for (j = 0; j < n2; ++j)
473 rowsum += grid[i][j];
475 fprintf(stdout, "%g\t", rowsum);
477 fprintf(stdout, "\n");
480 sprintf(buf, "%s number density", grpname[anagrp]);
481 if (!bRadial && (bXmin || bXmax))
483 if (!bXmax)
485 sprintf(buf + std::strlen(buf), ", %c > %g nm", eaver[0][0], xmin);
487 else if (!bXmin)
489 sprintf(buf + std::strlen(buf), ", %c < %g nm", eaver[0][0], xmax);
491 else
493 sprintf(buf + std::strlen(buf), ", %c: %g - %g nm", eaver[0][0], xmin, xmax);
496 if (ftp2bSet(efDAT, NFILE, fnm))
498 fp = gmx_ffopen(ftp2fn(efDAT, NFILE, fnm), "w");
499 /*optional text form output: first row is tickz; first col is tickx */
500 fprintf(fp, "0\t");
501 for (j = 0; j < n2; ++j)
503 fprintf(fp, "%g\t", tickz[j]);
505 fprintf(fp, "\n");
507 for (i = 0; i < n1; ++i)
509 fprintf(fp, "%g\t", tickx[i]);
510 for (j = 0; j < n2; ++j)
512 fprintf(fp, "%g\t", grid[i][j]);
514 fprintf(fp, "\n");
516 gmx_ffclose(fp);
518 else
520 fp = gmx_ffopen(ftp2fn(efXPM, NFILE, fnm), "w");
521 write_xpm(fp, MAT_SPATIAL_X | MAT_SPATIAL_Y, buf, unit, bRadial ? "axial (nm)" : label[c1],
522 bRadial ? "r (nm)" : label[c2], n1, n2, tickx, tickz, grid, dmin, maxgrid, rlo,
523 rhi, &nlev);
524 gmx_ffclose(fp);
527 do_view(oenv, opt2fn("-o", NFILE, fnm), nullptr);
529 return 0;