Rename ffopen and ffclose to gmx_ff*.
[gromacs/AngularHB.git] / src / gromacs / gmxana / gmx_potential.c
blob3ccb728231983ac22965afc4641ddc5e858eab22
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, 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 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
41 #include <math.h>
42 #include <ctype.h>
44 #include "sysstuff.h"
45 #include <string.h>
46 #include "typedefs.h"
47 #include "smalloc.h"
48 #include "macros.h"
49 #include "princ.h"
50 #include "rmpbc.h"
51 #include "vec.h"
52 #include "xvgr.h"
53 #include "pbc.h"
54 #include "gromacs/fileio/futil.h"
55 #include "gromacs/commandline/pargs.h"
56 #include "index.h"
57 #include "gmx_ana.h"
58 #include "string2.h"
59 #include "gromacs/fileio/trxio.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 void p_integrate(double *result, 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;
100 return;
103 void calc_potential(const char *fn, atom_id **index, int gnx[],
104 double ***slPotential, double ***slCharge,
105 double ***slField, int *nslices,
106 t_topology *top, int ePBC,
107 int axis, int nr_grps, double *slWidth,
108 double fudge_z, gmx_bool bSpherical, gmx_bool bCorrect,
109 const output_env_t oenv)
111 rvec *x0; /* coordinates without pbc */
112 matrix box; /* box (3x3) */
113 int natoms; /* nr. atoms in trj */
114 t_trxstatus *status;
115 int **slCount, /* nr. of atoms in one slice for a group */
116 i, j, n, /* loop indices */
117 teller = 0,
118 ax1 = 0, ax2 = 0,
119 nr_frames = 0, /* number of frames */
120 slice; /* current slice */
121 double slVolume; /* volume of slice for spherical averaging */
122 double qsum, nn;
123 real t;
124 double z;
125 rvec xcm;
126 gmx_rmpbc_t gpbc = NULL;
128 switch (axis)
130 case 0:
131 ax1 = 1; ax2 = 2;
132 break;
133 case 1:
134 ax1 = 0; ax2 = 2;
135 break;
136 case 2:
137 ax1 = 0; ax2 = 1;
138 break;
139 default:
140 gmx_fatal(FARGS, "Invalid axes. Terminating\n");
143 if ((natoms = read_first_x(oenv, &status, fn, &t, &x0, box)) == 0)
145 gmx_fatal(FARGS, "Could not read coordinates from statusfile\n");
148 if (!*nslices)
150 *nslices = (int)(box[axis][axis] * 10); /* default value */
153 fprintf(stderr, "\nDividing the box in %d slices\n", *nslices);
155 snew(*slField, nr_grps);
156 snew(*slCharge, nr_grps);
157 snew(*slPotential, nr_grps);
159 for (i = 0; i < nr_grps; i++)
161 snew((*slField)[i], *nslices);
162 snew((*slCharge)[i], *nslices);
163 snew((*slPotential)[i], *nslices);
167 gpbc = gmx_rmpbc_init(&top->idef, ePBC, natoms);
169 /*********** Start processing trajectory ***********/
172 *slWidth = box[axis][axis]/(*nslices);
173 teller++;
174 gmx_rmpbc(gpbc, natoms, box, x0);
176 /* calculate position of center of mass based on group 1 */
177 calc_xcm(x0, gnx[0], index[0], top->atoms.atom, xcm, FALSE);
178 svmul(-1, xcm, xcm);
180 for (n = 0; n < nr_grps; n++)
182 /* Check whether we actually have all positions of the requested index
183 * group in the trajectory file */
184 if (gnx[n] > natoms)
186 gmx_fatal(FARGS, "You selected a group with %d atoms, but only %d atoms\n"
187 "were found in the trajectory.\n", gnx[n], natoms);
189 for (i = 0; i < gnx[n]; i++) /* loop over all atoms in index file */
191 if (bSpherical)
193 rvec_add(x0[index[n][i]], xcm, x0[index[n][i]]);
194 /* only distance from origin counts, not sign */
195 slice = norm(x0[index[n][i]])/(*slWidth);
197 /* this is a nice check for spherical groups but not for
198 all water in a cubic box since a lot will fall outside
199 the sphere
200 if (slice > (*nslices))
202 fprintf(stderr,"Warning: slice = %d\n",slice);
205 (*slCharge)[n][slice] += top->atoms.atom[index[n][i]].q;
207 else
209 z = x0[index[n][i]][axis];
210 z = z + fudge_z;
211 if (z < 0)
213 z += box[axis][axis];
215 if (z > box[axis][axis])
217 z -= box[axis][axis];
219 /* determine which slice atom is in */
220 slice = (z / (*slWidth));
221 (*slCharge)[n][slice] += top->atoms.atom[index[n][i]].q;
225 nr_frames++;
227 while (read_next_x(oenv, status, &t, x0, box));
229 gmx_rmpbc_done(gpbc);
231 /*********** done with status file **********/
232 close_trj(status);
234 /* slCharge now contains the total charge per slice, summed over all
235 frames. Now divide by nr_frames and integrate twice
239 if (bSpherical)
241 fprintf(stderr, "\n\nRead %d frames from trajectory. Calculating potential"
242 "in spherical coordinates\n", nr_frames);
244 else
246 fprintf(stderr, "\n\nRead %d frames from trajectory. Calculating potential\n",
247 nr_frames);
250 for (n = 0; n < nr_grps; n++)
252 for (i = 0; i < *nslices; i++)
254 if (bSpherical)
256 /* charge per volume is now the summed charge, divided by the nr
257 of frames and by the volume of the slice it's in, 4pi r^2 dr
259 slVolume = 4*M_PI * sqr(i) * sqr(*slWidth) * *slWidth;
260 if (slVolume == 0)
262 (*slCharge)[n][i] = 0;
264 else
266 (*slCharge)[n][i] = (*slCharge)[n][i] / (nr_frames * slVolume);
269 else
271 /* get charge per volume */
272 (*slCharge)[n][i] = (*slCharge)[n][i] * (*nslices) /
273 (nr_frames * box[axis][axis] * box[ax1][ax1] * box[ax2][ax2]);
276 /* Now we have charge densities */
279 if (bCorrect && !bSpherical)
281 for (n = 0; n < nr_grps; n++)
283 nn = 0;
284 qsum = 0;
285 for (i = 0; i < *nslices; i++)
287 if (fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN)
289 nn++;
290 qsum += (*slCharge)[n][i];
293 qsum /= nn;
294 for (i = 0; i < *nslices; i++)
296 if (fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN)
298 (*slCharge)[n][i] -= qsum;
304 for (n = 0; n < nr_grps; n++)
306 /* integrate twice to get field and potential */
307 p_integrate((*slField)[n], (*slCharge)[n], *nslices, *slWidth);
311 if (bCorrect && !bSpherical)
313 for (n = 0; n < nr_grps; n++)
315 nn = 0;
316 qsum = 0;
317 for (i = 0; i < *nslices; i++)
319 if (fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN)
321 nn++;
322 qsum += (*slField)[n][i];
325 qsum /= nn;
326 for (i = 0; i < *nslices; i++)
328 if (fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN)
330 (*slField)[n][i] -= qsum;
336 for (n = 0; n < nr_grps; n++)
338 p_integrate((*slPotential)[n], (*slField)[n], *nslices, *slWidth);
341 /* Now correct for eps0 and in spherical case for r*/
342 for (n = 0; n < nr_grps; n++)
344 for (i = 0; i < *nslices; i++)
346 if (bSpherical)
348 (*slPotential)[n][i] = ELC * (*slPotential)[n][i] * -1.0E9 /
349 (EPS0 * i * (*slWidth));
350 (*slField)[n][i] = ELC * (*slField)[n][i] * 1E18 /
351 (EPS0 * i * (*slWidth));
353 else
355 (*slPotential)[n][i] = ELC * (*slPotential)[n][i] * -1.0E9 / EPS0;
356 (*slField)[n][i] = ELC * (*slField)[n][i] * 1E18 / EPS0;
361 sfree(x0); /* free memory used by coordinate array */
364 void plot_potential(double *potential[], double *charge[], double *field[],
365 const char *afile, const char *bfile, const char *cfile,
366 int nslices, int nr_grps, const char *grpname[], double slWidth,
367 const output_env_t oenv)
369 FILE *pot, /* xvgr file with potential */
370 *cha, /* xvgr file with charges */
371 *fie; /* xvgr files with fields */
372 char buf[256]; /* for xvgr title */
373 int slice, n;
375 sprintf(buf, "Electrostatic Potential");
376 pot = xvgropen(afile, buf, "Box (nm)", "Potential (V)", oenv);
377 xvgr_legend(pot, nr_grps, grpname, oenv);
379 sprintf(buf, "Charge Distribution");
380 cha = xvgropen(bfile, buf, "Box (nm)", "Charge density (q/nm\\S3\\N)", oenv);
381 xvgr_legend(cha, nr_grps, grpname, oenv);
383 sprintf(buf, "Electric Field");
384 fie = xvgropen(cfile, buf, "Box (nm)", "Field (V/nm)", oenv);
385 xvgr_legend(fie, nr_grps, grpname, oenv);
387 for (slice = cb; slice < (nslices - ce); slice++)
389 fprintf(pot, "%20.16g ", slice * slWidth);
390 fprintf(cha, "%20.16g ", slice * slWidth);
391 fprintf(fie, "%20.16g ", slice * slWidth);
392 for (n = 0; n < nr_grps; n++)
394 fprintf(pot, " %20.16g", potential[n][slice]);
395 fprintf(fie, " %20.16g", field[n][slice]/1e9); /* convert to V/nm */
396 fprintf(cha, " %20.16g", charge[n][slice]);
398 fprintf(pot, "\n");
399 fprintf(cha, "\n");
400 fprintf(fie, "\n");
403 gmx_ffclose(pot);
404 gmx_ffclose(cha);
405 gmx_ffclose(fie);
408 int gmx_potential(int argc, char *argv[])
410 const char *desc[] = {
411 "[THISMODULE] computes the electrostatical potential across the box. The potential is",
412 "calculated by first summing the charges per slice and then integrating",
413 "twice of this charge distribution. Periodic boundaries are not taken",
414 "into account. Reference of potential is taken to be the left side of",
415 "the box. It is also possible to calculate the potential in spherical",
416 "coordinates as function of r by calculating a charge distribution in",
417 "spherical slices and twice integrating them. epsilon_r is taken as 1,",
418 "but 2 is more appropriate in many cases."
420 output_env_t oenv;
421 static int axis = 2; /* normal to memb. default z */
422 static const char *axtitle = "Z";
423 static int nslices = 10; /* nr of slices defined */
424 static int ngrps = 1;
425 static gmx_bool bSpherical = FALSE; /* default is bilayer types */
426 static real fudge_z = 0; /* translate coordinates */
427 static gmx_bool bCorrect = 0;
428 t_pargs pa [] = {
429 { "-d", FALSE, etSTR, {&axtitle},
430 "Take the normal on the membrane in direction X, Y or Z." },
431 { "-sl", FALSE, etINT, {&nslices},
432 "Calculate potential as function of boxlength, dividing the box"
433 " in this number of slices." },
434 { "-cb", FALSE, etINT, {&cb},
435 "Discard this number of first slices of box for integration" },
436 { "-ce", FALSE, etINT, {&ce},
437 "Discard this number of last slices of box for integration" },
438 { "-tz", FALSE, etREAL, {&fudge_z},
439 "Translate all coordinates by this distance in the direction of the box" },
440 { "-spherical", FALSE, etBOOL, {&bSpherical},
441 "Calculate spherical thingie" },
442 { "-ng", FALSE, etINT, {&ngrps},
443 "Number of groups to consider" },
444 { "-correct", FALSE, etBOOL, {&bCorrect},
445 "Assume net zero charge of groups to improve accuracy" }
447 const char *bugs[] = {
448 "Discarding slices for integration should not be necessary."
451 double **potential, /* potential per slice */
452 **charge, /* total charge per slice */
453 **field, /* field per slice */
454 slWidth; /* width of one slice */
455 char **grpname; /* groupnames */
456 int *ngx; /* sizes of groups */
457 t_topology *top; /* topology */
458 int ePBC;
459 atom_id **index; /* indices for all groups */
460 t_filenm fnm[] = { /* files for g_order */
461 { efTRX, "-f", NULL, ffREAD }, /* trajectory file */
462 { efNDX, NULL, NULL, ffREAD }, /* index file */
463 { efTPX, NULL, NULL, ffREAD }, /* topology file */
464 { efXVG, "-o", "potential", ffWRITE }, /* xvgr output file */
465 { efXVG, "-oc", "charge", ffWRITE }, /* xvgr output file */
466 { efXVG, "-of", "field", ffWRITE }, /* xvgr output file */
469 #define NFILE asize(fnm)
471 if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
472 NFILE, fnm, asize(pa), pa, asize(desc), desc, asize(bugs), bugs,
473 &oenv))
475 return 0;
478 /* Calculate axis */
479 axis = toupper(axtitle[0]) - 'X';
481 top = read_top(ftp2fn(efTPX, NFILE, fnm), &ePBC); /* read topology file */
483 snew(grpname, ngrps);
484 snew(index, ngrps);
485 snew(ngx, ngrps);
487 rd_index(ftp2fn(efNDX, NFILE, fnm), ngrps, ngx, index, grpname);
490 calc_potential(ftp2fn(efTRX, NFILE, fnm), index, ngx,
491 &potential, &charge, &field,
492 &nslices, top, ePBC, axis, ngrps, &slWidth, fudge_z,
493 bSpherical, bCorrect, oenv);
495 plot_potential(potential, charge, field, opt2fn("-o", NFILE, fnm),
496 opt2fn("-oc", NFILE, fnm), opt2fn("-of", NFILE, fnm),
497 nslices, ngrps, (const char**)grpname, slWidth, oenv);
499 do_view(oenv, opt2fn("-o", NFILE, fnm), NULL); /* view xvgr file */
500 do_view(oenv, opt2fn("-oc", NFILE, fnm), NULL); /* view xvgr file */
501 do_view(oenv, opt2fn("-of", NFILE, fnm), NULL); /* view xvgr file */
503 return 0;