Introduce SimulatorBuilder
[gromacs.git] / src / gromacs / gmxana / gmx_potential.cpp
blob4c39aa2e41d05036344d19aca395480d4b00ba4a
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,2018, 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.
37 #include "gmxpre.h"
39 #include <cctype>
40 #include <cmath>
41 #include <cstring>
43 #include "gromacs/commandline/pargs.h"
44 #include "gromacs/commandline/viewit.h"
45 #include "gromacs/fileio/trxio.h"
46 #include "gromacs/fileio/xvgr.h"
47 #include "gromacs/gmxana/gmx_ana.h"
48 #include "gromacs/gmxana/princ.h"
49 #include "gromacs/math/functions.h"
50 #include "gromacs/math/utilities.h"
51 #include "gromacs/math/vec.h"
52 #include "gromacs/pbcutil/rmpbc.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/smalloc.h"
61 #define EPS0 8.85419E-12
62 #define ELC 1.60219E-19
64 /****************************************************************************/
65 /* This program calculates the electrostatic potential across the box by */
66 /* determining the charge density in slices of the box and integrating these*/
67 /* twice. */
68 /* Peter Tieleman, April 1995 */
69 /* It now also calculates electrostatic potential in spherical micelles, */
70 /* using \frac{1}{r}\frac{d^2r\Psi}{r^2} = - \frac{\rho}{\epsilon_0} */
71 /* This probably sucks but it seems to work. */
72 /****************************************************************************/
74 static int ce = 0, cb = 0;
76 /* this routine integrates the array data and returns the resulting array */
77 /* routine uses simple trapezoid rule */
78 static void p_integrate(double *result, const double data[], int ndata, double slWidth)
80 int i, slice;
81 double sum;
83 if (ndata <= 2)
85 fprintf(stderr, "Warning: nr of slices very small. This will result"
86 "in nonsense.\n");
89 fprintf(stderr, "Integrating from slice %d to slice %d\n", cb, ndata-ce);
91 for (slice = cb; slice < (ndata-ce); slice++)
93 sum = 0;
94 for (i = cb; i < slice; i++)
96 sum += slWidth * (data[i] + 0.5 * (data[i+1] - data[i]));
98 result[slice] = sum;
102 static void calc_potential(const char *fn, int **index, int gnx[],
103 double ***slPotential, double ***slCharge,
104 double ***slField, int *nslices,
105 const t_topology *top, int ePBC,
106 int axis, int nr_grps, double *slWidth,
107 double fudge_z, gmx_bool bSpherical, gmx_bool bCorrect,
108 const gmx_output_env_t *oenv)
110 rvec *x0; /* coordinates without pbc */
111 matrix box; /* box (3x3) */
112 int natoms; /* nr. atoms in trj */
113 t_trxstatus *status;
114 int i, n, /* loop indices */
115 teller = 0,
116 ax1 = 0, ax2 = 0,
117 nr_frames = 0, /* number of frames */
118 slice; /* current slice */
119 double slVolume; /* volume of slice for spherical averaging */
120 double qsum, nn;
121 real t;
122 double z;
123 rvec xcm;
124 gmx_rmpbc_t gpbc = nullptr;
126 switch (axis)
128 case 0:
129 ax1 = 1; ax2 = 2;
130 break;
131 case 1:
132 ax1 = 0; ax2 = 2;
133 break;
134 case 2:
135 ax1 = 0; ax2 = 1;
136 break;
137 default:
138 gmx_fatal(FARGS, "Invalid axes. Terminating\n");
141 if ((natoms = read_first_x(oenv, &status, fn, &t, &x0, box)) == 0)
143 gmx_fatal(FARGS, "Could not read coordinates from statusfile\n");
146 if (!*nslices)
148 *nslices = static_cast<int>(box[axis][axis] * 10.0); /* default value */
151 fprintf(stderr, "\nDividing the box in %d slices\n", *nslices);
153 snew(*slField, nr_grps);
154 snew(*slCharge, nr_grps);
155 snew(*slPotential, nr_grps);
157 for (i = 0; i < nr_grps; i++)
159 snew((*slField)[i], *nslices);
160 snew((*slCharge)[i], *nslices);
161 snew((*slPotential)[i], *nslices);
165 gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
167 /*********** Start processing trajectory ***********/
170 *slWidth = box[axis][axis]/(*nslices);
171 teller++;
172 gmx_rmpbc(gpbc, natoms, box, x0);
174 /* calculate position of center of mass based on group 1 */
175 calc_xcm(x0, gnx[0], index[0], top->atoms.atom, xcm, FALSE);
176 svmul(-1, xcm, xcm);
178 for (n = 0; n < nr_grps; n++)
180 /* Check whether we actually have all positions of the requested index
181 * group in the trajectory file */
182 if (gnx[n] > natoms)
184 gmx_fatal(FARGS, "You selected a group with %d atoms, but only %d atoms\n"
185 "were found in the trajectory.\n", gnx[n], natoms);
187 for (i = 0; i < gnx[n]; i++) /* loop over all atoms in index file */
189 if (bSpherical)
191 rvec_add(x0[index[n][i]], xcm, x0[index[n][i]]);
192 /* only distance from origin counts, not sign */
193 slice = static_cast<int>(norm(x0[index[n][i]])/(*slWidth));
195 /* this is a nice check for spherical groups but not for
196 all water in a cubic box since a lot will fall outside
197 the sphere
198 if (slice > (*nslices))
200 fprintf(stderr,"Warning: slice = %d\n",slice);
203 (*slCharge)[n][slice] += top->atoms.atom[index[n][i]].q;
205 else
207 z = x0[index[n][i]][axis];
208 z = z + fudge_z;
209 if (z < 0)
211 z += box[axis][axis];
213 if (z > box[axis][axis])
215 z -= box[axis][axis];
217 /* determine which slice atom is in */
218 slice = static_cast<int>((z / (*slWidth)));
219 (*slCharge)[n][slice] += top->atoms.atom[index[n][i]].q;
223 nr_frames++;
225 while (read_next_x(oenv, status, &t, x0, box));
227 gmx_rmpbc_done(gpbc);
229 /*********** done with status file **********/
230 close_trx(status);
232 /* slCharge now contains the total charge per slice, summed over all
233 frames. Now divide by nr_frames and integrate twice
237 if (bSpherical)
239 fprintf(stderr, "\n\nRead %d frames from trajectory. Calculating potential"
240 "in spherical coordinates\n", nr_frames);
242 else
244 fprintf(stderr, "\n\nRead %d frames from trajectory. Calculating potential\n",
245 nr_frames);
248 for (n = 0; n < nr_grps; n++)
250 for (i = 0; i < *nslices; i++)
252 if (bSpherical)
254 /* charge per volume is now the summed charge, divided by the nr
255 of frames and by the volume of the slice it's in, 4pi r^2 dr
257 slVolume = 4*M_PI * gmx::square(i) * gmx::square(*slWidth) * *slWidth;
258 if (slVolume == 0)
260 (*slCharge)[n][i] = 0;
262 else
264 (*slCharge)[n][i] = (*slCharge)[n][i] / (nr_frames * slVolume);
267 else
269 /* get charge per volume */
270 (*slCharge)[n][i] = (*slCharge)[n][i] * (*nslices) /
271 (nr_frames * box[axis][axis] * box[ax1][ax1] * box[ax2][ax2]);
274 /* Now we have charge densities */
277 if (bCorrect && !bSpherical)
279 for (n = 0; n < nr_grps; n++)
281 nn = 0;
282 qsum = 0;
283 for (i = 0; i < *nslices; i++)
285 if (std::abs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN)
287 nn++;
288 qsum += (*slCharge)[n][i];
291 qsum /= nn;
292 for (i = 0; i < *nslices; i++)
294 if (std::abs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN)
296 (*slCharge)[n][i] -= qsum;
302 for (n = 0; n < nr_grps; n++)
304 /* integrate twice to get field and potential */
305 p_integrate((*slField)[n], (*slCharge)[n], *nslices, *slWidth);
309 if (bCorrect && !bSpherical)
311 for (n = 0; n < nr_grps; n++)
313 nn = 0;
314 qsum = 0;
315 for (i = 0; i < *nslices; i++)
317 if (std::abs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN)
319 nn++;
320 qsum += (*slField)[n][i];
323 qsum /= nn;
324 for (i = 0; i < *nslices; i++)
326 if (std::abs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN)
328 (*slField)[n][i] -= qsum;
334 for (n = 0; n < nr_grps; n++)
336 p_integrate((*slPotential)[n], (*slField)[n], *nslices, *slWidth);
339 /* Now correct for eps0 and in spherical case for r*/
340 for (n = 0; n < nr_grps; n++)
342 for (i = 0; i < *nslices; i++)
344 if (bSpherical)
346 (*slPotential)[n][i] = ELC * (*slPotential)[n][i] * -1.0E9 /
347 (EPS0 * i * (*slWidth));
348 (*slField)[n][i] = ELC * (*slField)[n][i] * 1E18 /
349 (EPS0 * i * (*slWidth));
351 else
353 (*slPotential)[n][i] = ELC * (*slPotential)[n][i] * -1.0E9 / EPS0;
354 (*slField)[n][i] = ELC * (*slField)[n][i] * 1E18 / EPS0;
359 sfree(x0); /* free memory used by coordinate array */
362 static void plot_potential(double *potential[], double *charge[], double *field[],
363 const char *afile, const char *bfile, const char *cfile,
364 int nslices, int nr_grps, const char *const grpname[], double slWidth,
365 const gmx_output_env_t *oenv)
367 FILE *pot, /* xvgr file with potential */
368 *cha, /* xvgr file with charges */
369 *fie; /* xvgr files with fields */
370 char buf[256]; /* for xvgr title */
371 int slice, n;
373 sprintf(buf, "Electrostatic Potential");
374 pot = xvgropen(afile, buf, "Box (nm)", "Potential (V)", oenv);
375 xvgr_legend(pot, nr_grps, grpname, oenv);
377 sprintf(buf, "Charge Distribution");
378 cha = xvgropen(bfile, buf, "Box (nm)", "Charge density (q/nm\\S3\\N)", oenv);
379 xvgr_legend(cha, nr_grps, grpname, oenv);
381 sprintf(buf, "Electric Field");
382 fie = xvgropen(cfile, buf, "Box (nm)", "Field (V/nm)", oenv);
383 xvgr_legend(fie, nr_grps, grpname, oenv);
385 for (slice = cb; slice < (nslices - ce); slice++)
387 fprintf(pot, "%20.16g ", slice * slWidth);
388 fprintf(cha, "%20.16g ", slice * slWidth);
389 fprintf(fie, "%20.16g ", slice * slWidth);
390 for (n = 0; n < nr_grps; n++)
392 fprintf(pot, " %20.16g", potential[n][slice]);
393 fprintf(fie, " %20.16g", field[n][slice]/1e9); /* convert to V/nm */
394 fprintf(cha, " %20.16g", charge[n][slice]);
396 fprintf(pot, "\n");
397 fprintf(cha, "\n");
398 fprintf(fie, "\n");
401 xvgrclose(pot);
402 xvgrclose(cha);
403 xvgrclose(fie);
406 int gmx_potential(int argc, char *argv[])
408 const char *desc[] = {
409 "[THISMODULE] computes the electrostatical potential across the box. The potential is",
410 "calculated by first summing the charges per slice and then integrating",
411 "twice of this charge distribution. Periodic boundaries are not taken",
412 "into account. Reference of potential is taken to be the left side of",
413 "the box. It is also possible to calculate the potential in spherical",
414 "coordinates as function of r by calculating a charge distribution in",
415 "spherical slices and twice integrating them. epsilon_r is taken as 1,",
416 "but 2 is more appropriate in many cases."
418 gmx_output_env_t *oenv;
419 static int axis = 2; /* normal to memb. default z */
420 static const char *axtitle = "Z";
421 static int nslices = 10; /* nr of slices defined */
422 static int ngrps = 1;
423 static gmx_bool bSpherical = FALSE; /* default is bilayer types */
424 static real fudge_z = 0; /* translate coordinates */
425 static gmx_bool bCorrect = false;
426 t_pargs pa [] = {
427 { "-d", FALSE, etSTR, {&axtitle},
428 "Take the normal on the membrane in direction X, Y or Z." },
429 { "-sl", FALSE, etINT, {&nslices},
430 "Calculate potential as function of boxlength, dividing the box"
431 " in this number of slices." },
432 { "-cb", FALSE, etINT, {&cb},
433 "Discard this number of first slices of box for integration" },
434 { "-ce", FALSE, etINT, {&ce},
435 "Discard this number of last slices of box for integration" },
436 { "-tz", FALSE, etREAL, {&fudge_z},
437 "Translate all coordinates by this distance in the direction of the box" },
438 { "-spherical", FALSE, etBOOL, {&bSpherical},
439 "Calculate in spherical coordinates" },
440 { "-ng", FALSE, etINT, {&ngrps},
441 "Number of groups to consider" },
442 { "-correct", FALSE, etBOOL, {&bCorrect},
443 "Assume net zero charge of groups to improve accuracy" }
445 const char *bugs[] = {
446 "Discarding slices for integration should not be necessary."
449 double **potential, /* potential per slice */
450 **charge, /* total charge per slice */
451 **field, /* field per slice */
452 slWidth; /* width of one slice */
453 char **grpname; /* groupnames */
454 int *ngx; /* sizes of groups */
455 t_topology *top; /* topology */
456 int ePBC;
457 int **index; /* indices for all groups */
458 t_filenm fnm[] = { /* files for g_order */
459 { efTRX, "-f", nullptr, ffREAD }, /* trajectory file */
460 { efNDX, nullptr, nullptr, ffREAD }, /* index file */
461 { efTPR, nullptr, nullptr, ffREAD }, /* topology file */
462 { efXVG, "-o", "potential", ffWRITE }, /* xvgr output file */
463 { efXVG, "-oc", "charge", ffWRITE }, /* xvgr output file */
464 { efXVG, "-of", "field", ffWRITE }, /* xvgr output file */
467 #define NFILE asize(fnm)
469 if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME,
470 NFILE, fnm, asize(pa), pa, asize(desc), desc, asize(bugs), bugs,
471 &oenv))
473 return 0;
476 /* Calculate axis */
477 axis = toupper(axtitle[0]) - 'X';
479 top = read_top(ftp2fn(efTPR, NFILE, fnm), &ePBC); /* read topology file */
481 snew(grpname, ngrps);
482 snew(index, ngrps);
483 snew(ngx, ngrps);
485 rd_index(ftp2fn(efNDX, NFILE, fnm), ngrps, ngx, index, grpname);
488 calc_potential(ftp2fn(efTRX, NFILE, fnm), index, ngx,
489 &potential, &charge, &field,
490 &nslices, top, ePBC, axis, ngrps, &slWidth, fudge_z,
491 bSpherical, bCorrect, oenv);
493 plot_potential(potential, charge, field, opt2fn("-o", NFILE, fnm),
494 opt2fn("-oc", NFILE, fnm), opt2fn("-of", NFILE, fnm),
495 nslices, ngrps, grpname, slWidth, oenv);
497 do_view(oenv, opt2fn("-o", NFILE, fnm), nullptr); /* view xvgr file */
498 do_view(oenv, opt2fn("-oc", NFILE, fnm), nullptr); /* view xvgr file */
499 do_view(oenv, opt2fn("-of", NFILE, fnm), nullptr); /* view xvgr file */
501 return 0;