3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
10 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12 * Copyright (c) 2001-2004, The GROMACS development team,
13 * check out http://www.gromacs.org for more information.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * If you want to redistribute modifications, please consider that
21 * scientific software is very special. Version control is crucial -
22 * bugs must be traceable. We will be happy to consider code for
23 * inclusion in the official distribution, but derived work must not
24 * be called official GROMACS. Details are found in the README & COPYING
25 * files - if they are missing, get the official version at www.gromacs.org.
27 * To help us fund GROMACS development, we humbly ask that you cite
28 * the papers on the package - you can find them in the top README file.
30 * For more info, check our website at http://www.gromacs.org
33 * Green Red Orange Magenta Azure Cyan Skyblue
58 /* Suppress Cygwin compiler warnings from using newlib version of
64 #define EPS0 8.85419E-12
65 #define ELC 1.60219E-19
67 /****************************************************************************/
68 /* This program calculates the electrostatic potential across the box by */
69 /* determining the charge density in slices of the box and integrating these*/
71 /* Peter Tieleman, April 1995 */
72 /* It now also calculates electrostatic potential in spherical micelles, */
73 /* using \frac{1}{r}\frac{d^2r\Psi}{r^2} = - \frac{\rho}{\epsilon_0} */
74 /* This probably sucks but it seems to work. */
75 /****************************************************************************/
77 static int ce=0, cb=0;
79 /* this routine integrates the array data and returns the resulting array */
80 /* routine uses simple trapezoid rule */
81 void p_integrate(double *result, double data[], int ndata, double slWidth)
87 fprintf(stderr,"Warning: nr of slices very small. This will result"
90 fprintf(stderr,"Integrating from slice %d to slice %d\n",cb, ndata-ce);
92 for (slice = cb; slice < (ndata-ce); slice ++)
95 for (i = cb; i < slice; i++)
96 sum += slWidth * (data[i] + 0.5 * (data[i+1] - data[i]));
102 void calc_potential(const char *fn, atom_id **index, int gnx[],
103 double ***slPotential, double ***slCharge,
104 double ***slField, int *nslices,
105 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 output_env_t oenv)
110 rvec *x0; /* coordinates without pbc */
111 matrix box; /* box (3x3) */
112 int natoms; /* nr. atoms in trj */
114 int **slCount, /* nr. of atoms in one slice for a group */
115 i,j,n, /* loop indices */
118 nr_frames = 0, /* number of frames */
119 slice; /* current slice */
120 double slVolume; /* volume of slice for spherical averaging */
125 gmx_rmpbc_t gpbc=NULL;
139 gmx_fatal(FARGS,"Invalid axes. Terminating\n");
142 if ((natoms = read_first_x(oenv,&status,fn,&t,&x0,box)) == 0)
143 gmx_fatal(FARGS,"Could not read coordinates from statusfile\n");
146 *nslices = (int)(box[axis][axis] * 10); /* default value */
148 fprintf(stderr,"\nDividing the box in %d slices\n",*nslices);
150 snew(*slField, nr_grps);
151 snew(*slCharge, nr_grps);
152 snew(*slPotential, nr_grps);
154 for (i = 0; i < nr_grps; i++)
156 snew((*slField)[i], *nslices);
157 snew((*slCharge)[i], *nslices);
158 snew((*slPotential)[i], *nslices);
162 gpbc = gmx_rmpbc_init(&top->idef,ePBC,natoms,box);
164 /*********** Start processing trajectory ***********/
167 *slWidth = box[axis][axis]/(*nslices);
169 gmx_rmpbc(gpbc,natoms,box,x0);
171 /* calculate position of center of mass based on group 1 */
172 calc_xcm(x0, gnx[0], index[0], top->atoms.atom, xcm, FALSE);
175 for (n = 0; n < nr_grps; n++)
177 /* Check whether we actually have all positions of the requested index
178 * group in the trajectory file */
181 gmx_fatal(FARGS, "You selected a group with %d atoms, but only %d atoms\n"
182 "were found in the trajectory.\n", gnx[n], natoms);
184 for (i = 0; i < gnx[n]; i++) /* loop over all atoms in index file */
188 rvec_add(x0[index[n][i]], xcm, x0[index[n][i]]);
189 /* only distance from origin counts, not sign */
190 slice = norm(x0[index[n][i]])/(*slWidth);
192 /* this is a nice check for spherical groups but not for
193 all water in a cubic box since a lot will fall outside
195 if (slice > (*nslices))
197 fprintf(stderr,"Warning: slice = %d\n",slice);
200 (*slCharge)[n][slice] += top->atoms.atom[index[n][i]].q;
204 z = x0[index[n][i]][axis];
207 z += box[axis][axis];
208 if (z > box[axis][axis])
209 z -= box[axis][axis];
210 /* determine which slice atom is in */
211 slice = (z / (*slWidth));
212 (*slCharge)[n][slice] += top->atoms.atom[index[n][i]].q;
217 } while (read_next_x(oenv,status,&t,natoms,x0,box));
219 gmx_rmpbc_done(gpbc);
221 /*********** done with status file **********/
224 /* slCharge now contains the total charge per slice, summed over all
225 frames. Now divide by nr_frames and integrate twice
230 fprintf(stderr,"\n\nRead %d frames from trajectory. Calculating potential"
231 "in spherical coordinates\n", nr_frames);
233 fprintf(stderr,"\n\nRead %d frames from trajectory. Calculating potential\n",
236 for (n =0; n < nr_grps; n++)
238 for (i = 0; i < *nslices; i++)
242 /* charge per volume is now the summed charge, divided by the nr
243 of frames and by the volume of the slice it's in, 4pi r^2 dr
245 slVolume = 4*M_PI * sqr(i) * sqr(*slWidth) * *slWidth;
248 (*slCharge)[n][i] = 0;
252 (*slCharge)[n][i] = (*slCharge)[n][i] / (nr_frames * slVolume);
257 /* get charge per volume */
258 (*slCharge)[n][i] = (*slCharge)[n][i] * (*nslices) /
259 (nr_frames * box[axis][axis] * box[ax1][ax1] * box[ax2][ax2]);
262 /* Now we have charge densities */
265 if(bCorrect && !bSpherical)
267 for(n =0; n < nr_grps; n++)
271 for (i = 0; i < *nslices; i++)
273 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
276 qsum += (*slCharge)[n][i];
280 for (i = 0; i < *nslices; i++)
282 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
284 (*slCharge)[n][i] -= qsum;
290 for(n =0; n < nr_grps; n++)
292 /* integrate twice to get field and potential */
293 p_integrate((*slField)[n], (*slCharge)[n], *nslices, *slWidth);
297 if(bCorrect && !bSpherical)
299 for(n =0; n < nr_grps; n++)
303 for (i = 0; i < *nslices; i++)
305 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
308 qsum += (*slField)[n][i];
312 for (i = 0; i < *nslices; i++)
314 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
316 (*slField)[n][i] -= qsum;
322 for(n =0; n < nr_grps; n++)
324 p_integrate((*slPotential)[n],(*slField)[n], *nslices, *slWidth);
327 /* Now correct for eps0 and in spherical case for r*/
328 for (n = 0; n < nr_grps; n++)
329 for (i = 0; i < *nslices; i++)
333 (*slPotential)[n][i] = ELC * (*slPotential)[n][i] * -1.0E9 /
334 (EPS0 * i * (*slWidth));
335 (*slField)[n][i] = ELC * (*slField)[n][i] * 1E18 /
336 (EPS0 * i * (*slWidth));
340 (*slPotential)[n][i] = ELC * (*slPotential)[n][i] * -1.0E9 / EPS0 ;
341 (*slField)[n][i] = ELC * (*slField)[n][i] * 1E18 / EPS0;
345 sfree(x0); /* free memory used by coordinate array */
348 void plot_potential(double *potential[], double *charge[], double *field[],
349 const char *afile, const char *bfile, const char *cfile,
350 int nslices, int nr_grps, const char *grpname[], double slWidth,
351 const output_env_t oenv)
353 FILE *pot, /* xvgr file with potential */
354 *cha, /* xvgr file with charges */
355 *fie; /* xvgr files with fields */
356 char buf[256]; /* for xvgr title */
359 sprintf(buf,"Electrostatic Potential");
360 pot = xvgropen(afile, buf, "Box (nm)","Potential (V)",oenv);
361 xvgr_legend(pot,nr_grps,grpname,oenv);
363 sprintf(buf,"Charge Distribution");
364 cha = xvgropen(bfile, buf, "Box (nm)", "Charge density (q/nm\\S3\\N)",oenv);
365 xvgr_legend(cha,nr_grps,grpname,oenv);
367 sprintf(buf, "Electric Field");
368 fie = xvgropen(cfile, buf, "Box (nm)", "Field (V/nm)",oenv);
369 xvgr_legend(fie,nr_grps,grpname,oenv);
371 for (slice = cb; slice < (nslices - ce); slice++)
373 fprintf(pot,"%20.16g ", slice * slWidth);
374 fprintf(cha,"%20.16g ", slice * slWidth);
375 fprintf(fie,"%20.16g ", slice * slWidth);
376 for (n = 0; n < nr_grps; n++)
378 fprintf(pot," %20.16g", potential[n][slice]);
379 fprintf(fie," %20.16g", field[n][slice]/1e9); /* convert to V/nm */
380 fprintf(cha," %20.16g", charge[n][slice]);
392 int gmx_potential(int argc,char *argv[])
394 const char *desc[] = {
395 "[TT]g_potential[tt] computes the electrostatical potential across the box. The potential is",
396 "calculated by first summing the charges per slice and then integrating",
397 "twice of this charge distribution. Periodic boundaries are not taken",
398 "into account. Reference of potential is taken to be the left side of",
399 "the box. It is also possible to calculate the potential in spherical",
400 "coordinates as function of r by calculating a charge distribution in",
401 "spherical slices and twice integrating them. epsilon_r is taken as 1,",
402 "but 2 is more appropriate in many cases."
405 static int axis = 2; /* normal to memb. default z */
406 static const char *axtitle="Z";
407 static int nslices = 10; /* nr of slices defined */
408 static int ngrps = 1;
409 static gmx_bool bSpherical = FALSE; /* default is bilayer types */
410 static real fudge_z = 0; /* translate coordinates */
411 static gmx_bool bCorrect = 0;
413 { "-d", FALSE, etSTR, {&axtitle},
414 "Take the normal on the membrane in direction X, Y or Z." },
415 { "-sl", FALSE, etINT, {&nslices},
416 "Calculate potential as function of boxlength, dividing the box"
417 " in this number of slices." } ,
418 { "-cb", FALSE, etINT, {&cb},
419 "Discard this number of first slices of box for integration" },
420 { "-ce", FALSE, etINT, {&ce},
421 "Discard this number of last slices of box for integration" },
422 { "-tz", FALSE, etREAL, {&fudge_z},
423 "Translate all coordinates by this distance in the direction of the box" },
424 { "-spherical", FALSE, etBOOL, {&bSpherical},
425 "Calculate spherical thingie" },
426 { "-ng", FALSE, etINT, {&ngrps},
427 "Number of groups to consider" },
428 { "-correct", FALSE, etBOOL, {&bCorrect},
429 "Assume net zero charge of groups to improve accuracy" }
431 const char *bugs[] = {
432 "Discarding slices for integration should not be necessary."
435 double **potential, /* potential per slice */
436 **charge, /* total charge per slice */
437 **field, /* field per slice */
438 slWidth; /* width of one slice */
439 char **grpname; /* groupnames */
440 int *ngx; /* sizes of groups */
441 t_topology *top; /* topology */
443 atom_id **index; /* indices for all groups */
444 t_filenm fnm[] = { /* files for g_order */
445 { efTRX, "-f", NULL, ffREAD }, /* trajectory file */
446 { efNDX, NULL, NULL, ffREAD }, /* index file */
447 { efTPX, NULL, NULL, ffREAD }, /* topology file */
448 { efXVG,"-o","potential", ffWRITE }, /* xvgr output file */
449 { efXVG,"-oc","charge", ffWRITE }, /* xvgr output file */
450 { efXVG,"-of","field", ffWRITE }, /* xvgr output file */
453 #define NFILE asize(fnm)
455 CopyRight(stderr,argv[0]);
456 parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
457 NFILE,fnm,asize(pa),pa,asize(desc),desc,asize(bugs),bugs,
461 axis = toupper(axtitle[0]) - 'X';
463 top = read_top(ftp2fn(efTPX,NFILE,fnm),&ePBC); /* read topology file */
469 rd_index(ftp2fn(efNDX,NFILE,fnm),ngrps,ngx,index,grpname);
472 calc_potential(ftp2fn(efTRX,NFILE,fnm), index, ngx,
473 &potential, &charge, &field,
474 &nslices, top, ePBC, axis, ngrps, &slWidth, fudge_z,
475 bSpherical, bCorrect,oenv);
477 plot_potential(potential, charge, field, opt2fn("-o",NFILE,fnm),
478 opt2fn("-oc",NFILE,fnm), opt2fn("-of",NFILE,fnm),
479 nslices, ngrps, (const char**)grpname, slWidth,oenv);
481 do_view(oenv,opt2fn("-o",NFILE,fnm), NULL); /* view xvgr file */
482 do_view(oenv,opt2fn("-oc",NFILE,fnm), NULL); /* view xvgr file */
483 do_view(oenv,opt2fn("-of",NFILE,fnm), NULL); /* view xvgr file */