Re-organize BlueGene toolchain files
[gromacs.git] / src / tools / gmx_potential.c
bloba1cb2e9fa7d07dda6b9bea164ee9f36b88f8da19
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 * check out http://www.gromacs.org for more information.
7 * Copyright (c) 2012, by the GROMACS development team, led by
8 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
9 * others, as listed in the AUTHORS file in the top-level source
10 * 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 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
41 #include "gmx_header_config.h"
43 #include <math.h>
44 #include <ctype.h>
46 #include "sysstuff.h"
47 #include <string.h>
48 #include "typedefs.h"
49 #include "smalloc.h"
50 #include "macros.h"
51 #include "princ.h"
52 #include "rmpbc.h"
53 #include "vec.h"
54 #include "xvgr.h"
55 #include "pbc.h"
56 #include "copyrite.h"
57 #include "futil.h"
58 #include "statutil.h"
59 #include "index.h"
60 #include "gmx_ana.h"
62 /* Suppress Cygwin compiler warnings from using newlib version of
63 * ctype.h */
64 #ifdef GMX_CYGWIN
65 #undef toupper
66 #endif
68 #define EPS0 8.85419E-12
69 #define ELC 1.60219E-19
71 /****************************************************************************/
72 /* This program calculates the electrostatic potential across the box by */
73 /* determining the charge density in slices of the box and integrating these*/
74 /* twice. */
75 /* Peter Tieleman, April 1995 */
76 /* It now also calculates electrostatic potential in spherical micelles, */
77 /* using \frac{1}{r}\frac{d^2r\Psi}{r^2} = - \frac{\rho}{\epsilon_0} */
78 /* This probably sucks but it seems to work. */
79 /****************************************************************************/
81 static int ce=0, cb=0;
83 /* this routine integrates the array data and returns the resulting array */
84 /* routine uses simple trapezoid rule */
85 void p_integrate(double *result, double data[], int ndata, double slWidth)
87 int i, slice;
88 double sum;
90 if (ndata <= 2)
91 fprintf(stderr,"Warning: nr of slices very small. This will result"
92 "in nonsense.\n");
94 fprintf(stderr,"Integrating from slice %d to slice %d\n",cb, ndata-ce);
96 for (slice = cb; slice < (ndata-ce); slice ++)
98 sum = 0;
99 for (i = cb; i < slice; i++)
100 sum += slWidth * (data[i] + 0.5 * (data[i+1] - data[i]));
101 result[slice] = sum;
103 return;
106 void calc_potential(const char *fn, atom_id **index, int gnx[],
107 double ***slPotential, double ***slCharge,
108 double ***slField, int *nslices,
109 t_topology *top, int ePBC,
110 int axis, int nr_grps, double *slWidth,
111 double fudge_z, gmx_bool bSpherical, gmx_bool bCorrect,
112 const output_env_t oenv)
114 rvec *x0; /* coordinates without pbc */
115 matrix box; /* box (3x3) */
116 int natoms; /* nr. atoms in trj */
117 t_trxstatus *status;
118 int **slCount, /* nr. of atoms in one slice for a group */
119 i,j,n, /* loop indices */
120 teller = 0,
121 ax1=0, ax2=0,
122 nr_frames = 0, /* number of frames */
123 slice; /* current slice */
124 double slVolume; /* volume of slice for spherical averaging */
125 double qsum,nn;
126 real t;
127 double z;
128 rvec xcm;
129 gmx_rmpbc_t gpbc=NULL;
131 switch(axis)
133 case 0:
134 ax1 = 1; ax2 = 2;
135 break;
136 case 1:
137 ax1 = 0; ax2 = 2;
138 break;
139 case 2:
140 ax1 = 0; ax2 = 1;
141 break;
142 default:
143 gmx_fatal(FARGS,"Invalid axes. Terminating\n");
146 if ((natoms = read_first_x(oenv,&status,fn,&t,&x0,box)) == 0)
147 gmx_fatal(FARGS,"Could not read coordinates from statusfile\n");
149 if (! *nslices)
150 *nslices = (int)(box[axis][axis] * 10); /* default value */
152 fprintf(stderr,"\nDividing the box in %d slices\n",*nslices);
154 snew(*slField, nr_grps);
155 snew(*slCharge, nr_grps);
156 snew(*slPotential, nr_grps);
158 for (i = 0; i < nr_grps; i++)
160 snew((*slField)[i], *nslices);
161 snew((*slCharge)[i], *nslices);
162 snew((*slPotential)[i], *nslices);
166 gpbc = gmx_rmpbc_init(&top->idef,ePBC,natoms,box);
168 /*********** Start processing trajectory ***********/
171 *slWidth = box[axis][axis]/(*nslices);
172 teller++;
173 gmx_rmpbc(gpbc,natoms,box,x0);
175 /* calculate position of center of mass based on group 1 */
176 calc_xcm(x0, gnx[0], index[0], top->atoms.atom, xcm, FALSE);
177 svmul(-1,xcm,xcm);
179 for (n = 0; n < nr_grps; n++)
181 /* Check whether we actually have all positions of the requested index
182 * group in the trajectory file */
183 if (gnx[n] > natoms)
185 gmx_fatal(FARGS, "You selected a group with %d atoms, but only %d atoms\n"
186 "were found in the trajectory.\n", gnx[n], natoms);
188 for (i = 0; i < gnx[n]; i++) /* loop over all atoms in index file */
190 if (bSpherical)
192 rvec_add(x0[index[n][i]], xcm, x0[index[n][i]]);
193 /* only distance from origin counts, not sign */
194 slice = norm(x0[index[n][i]])/(*slWidth);
196 /* this is a nice check for spherical groups but not for
197 all water in a cubic box since a lot will fall outside
198 the sphere
199 if (slice > (*nslices))
201 fprintf(stderr,"Warning: slice = %d\n",slice);
204 (*slCharge)[n][slice] += top->atoms.atom[index[n][i]].q;
206 else
208 z = x0[index[n][i]][axis];
209 z = z + fudge_z;
210 if (z < 0)
211 z += box[axis][axis];
212 if (z > box[axis][axis])
213 z -= box[axis][axis];
214 /* determine which slice atom is in */
215 slice = (z / (*slWidth));
216 (*slCharge)[n][slice] += top->atoms.atom[index[n][i]].q;
220 nr_frames++;
221 } while (read_next_x(oenv,status,&t,natoms,x0,box));
223 gmx_rmpbc_done(gpbc);
225 /*********** done with status file **********/
226 close_trj(status);
228 /* slCharge now contains the total charge per slice, summed over all
229 frames. Now divide by nr_frames and integrate twice
233 if (bSpherical)
234 fprintf(stderr,"\n\nRead %d frames from trajectory. Calculating potential"
235 "in spherical coordinates\n", nr_frames);
236 else
237 fprintf(stderr,"\n\nRead %d frames from trajectory. Calculating potential\n",
238 nr_frames);
240 for (n =0; n < nr_grps; n++)
242 for (i = 0; i < *nslices; i++)
244 if (bSpherical)
246 /* charge per volume is now the summed charge, divided by the nr
247 of frames and by the volume of the slice it's in, 4pi r^2 dr
249 slVolume = 4*M_PI * sqr(i) * sqr(*slWidth) * *slWidth;
250 if (slVolume == 0)
252 (*slCharge)[n][i] = 0;
254 else
256 (*slCharge)[n][i] = (*slCharge)[n][i] / (nr_frames * slVolume);
259 else
261 /* get charge per volume */
262 (*slCharge)[n][i] = (*slCharge)[n][i] * (*nslices) /
263 (nr_frames * box[axis][axis] * box[ax1][ax1] * box[ax2][ax2]);
266 /* Now we have charge densities */
269 if(bCorrect && !bSpherical)
271 for(n =0; n < nr_grps; n++)
273 nn = 0;
274 qsum = 0;
275 for (i = 0; i < *nslices; i++)
277 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
279 nn++;
280 qsum += (*slCharge)[n][i];
283 qsum /= nn;
284 for (i = 0; i < *nslices; i++)
286 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
288 (*slCharge)[n][i] -= qsum;
294 for(n =0; n < nr_grps; n++)
296 /* integrate twice to get field and potential */
297 p_integrate((*slField)[n], (*slCharge)[n], *nslices, *slWidth);
301 if(bCorrect && !bSpherical)
303 for(n =0; n < nr_grps; n++)
305 nn = 0;
306 qsum = 0;
307 for (i = 0; i < *nslices; i++)
309 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
311 nn++;
312 qsum += (*slField)[n][i];
315 qsum /= nn;
316 for (i = 0; i < *nslices; i++)
318 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
320 (*slField)[n][i] -= qsum;
326 for(n =0; n < nr_grps; n++)
328 p_integrate((*slPotential)[n],(*slField)[n], *nslices, *slWidth);
331 /* Now correct for eps0 and in spherical case for r*/
332 for (n = 0; n < nr_grps; n++)
333 for (i = 0; i < *nslices; i++)
335 if (bSpherical)
337 (*slPotential)[n][i] = ELC * (*slPotential)[n][i] * -1.0E9 /
338 (EPS0 * i * (*slWidth));
339 (*slField)[n][i] = ELC * (*slField)[n][i] * 1E18 /
340 (EPS0 * i * (*slWidth));
342 else
344 (*slPotential)[n][i] = ELC * (*slPotential)[n][i] * -1.0E9 / EPS0 ;
345 (*slField)[n][i] = ELC * (*slField)[n][i] * 1E18 / EPS0;
349 sfree(x0); /* free memory used by coordinate array */
352 void plot_potential(double *potential[], double *charge[], double *field[],
353 const char *afile, const char *bfile, const char *cfile,
354 int nslices, int nr_grps, const char *grpname[], double slWidth,
355 const output_env_t oenv)
357 FILE *pot, /* xvgr file with potential */
358 *cha, /* xvgr file with charges */
359 *fie; /* xvgr files with fields */
360 char buf[256]; /* for xvgr title */
361 int slice, n;
363 sprintf(buf,"Electrostatic Potential");
364 pot = xvgropen(afile, buf, "Box (nm)","Potential (V)",oenv);
365 xvgr_legend(pot,nr_grps,grpname,oenv);
367 sprintf(buf,"Charge Distribution");
368 cha = xvgropen(bfile, buf, "Box (nm)", "Charge density (q/nm\\S3\\N)",oenv);
369 xvgr_legend(cha,nr_grps,grpname,oenv);
371 sprintf(buf, "Electric Field");
372 fie = xvgropen(cfile, buf, "Box (nm)", "Field (V/nm)",oenv);
373 xvgr_legend(fie,nr_grps,grpname,oenv);
375 for (slice = cb; slice < (nslices - ce); slice++)
377 fprintf(pot,"%20.16g ", slice * slWidth);
378 fprintf(cha,"%20.16g ", slice * slWidth);
379 fprintf(fie,"%20.16g ", slice * slWidth);
380 for (n = 0; n < nr_grps; n++)
382 fprintf(pot," %20.16g", potential[n][slice]);
383 fprintf(fie," %20.16g", field[n][slice]/1e9); /* convert to V/nm */
384 fprintf(cha," %20.16g", charge[n][slice]);
386 fprintf(pot,"\n");
387 fprintf(cha,"\n");
388 fprintf(fie,"\n");
391 ffclose(pot);
392 ffclose(cha);
393 ffclose(fie);
396 int gmx_potential(int argc,char *argv[])
398 const char *desc[] = {
399 "[TT]g_potential[tt] computes the electrostatical potential across the box. The potential is",
400 "calculated by first summing the charges per slice and then integrating",
401 "twice of this charge distribution. Periodic boundaries are not taken",
402 "into account. Reference of potential is taken to be the left side of",
403 "the box. It is also possible to calculate the potential in spherical",
404 "coordinates as function of r by calculating a charge distribution in",
405 "spherical slices and twice integrating them. epsilon_r is taken as 1,",
406 "but 2 is more appropriate in many cases."
408 output_env_t oenv;
409 static int axis = 2; /* normal to memb. default z */
410 static const char *axtitle="Z";
411 static int nslices = 10; /* nr of slices defined */
412 static int ngrps = 1;
413 static gmx_bool bSpherical = FALSE; /* default is bilayer types */
414 static real fudge_z = 0; /* translate coordinates */
415 static gmx_bool bCorrect = 0;
416 t_pargs pa [] = {
417 { "-d", FALSE, etSTR, {&axtitle},
418 "Take the normal on the membrane in direction X, Y or Z." },
419 { "-sl", FALSE, etINT, {&nslices},
420 "Calculate potential as function of boxlength, dividing the box"
421 " in this number of slices." } ,
422 { "-cb", FALSE, etINT, {&cb},
423 "Discard this number of first slices of box for integration" },
424 { "-ce", FALSE, etINT, {&ce},
425 "Discard this number of last slices of box for integration" },
426 { "-tz", FALSE, etREAL, {&fudge_z},
427 "Translate all coordinates by this distance in the direction of the box" },
428 { "-spherical", FALSE, etBOOL, {&bSpherical},
429 "Calculate spherical thingie" },
430 { "-ng", FALSE, etINT, {&ngrps},
431 "Number of groups to consider" },
432 { "-correct", FALSE, etBOOL, {&bCorrect},
433 "Assume net zero charge of groups to improve accuracy" }
435 const char *bugs[] = {
436 "Discarding slices for integration should not be necessary."
439 double **potential, /* potential per slice */
440 **charge, /* total charge per slice */
441 **field, /* field per slice */
442 slWidth; /* width of one slice */
443 char **grpname; /* groupnames */
444 int *ngx; /* sizes of groups */
445 t_topology *top; /* topology */
446 int ePBC;
447 atom_id **index; /* indices for all groups */
448 t_filenm fnm[] = { /* files for g_order */
449 { efTRX, "-f", NULL, ffREAD }, /* trajectory file */
450 { efNDX, NULL, NULL, ffREAD }, /* index file */
451 { efTPX, NULL, NULL, ffREAD }, /* topology file */
452 { efXVG,"-o","potential", ffWRITE }, /* xvgr output file */
453 { efXVG,"-oc","charge", ffWRITE }, /* xvgr output file */
454 { efXVG,"-of","field", ffWRITE }, /* xvgr output file */
457 #define NFILE asize(fnm)
459 CopyRight(stderr,argv[0]);
460 parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
461 NFILE,fnm,asize(pa),pa,asize(desc),desc,asize(bugs),bugs,
462 &oenv);
464 /* Calculate axis */
465 axis = toupper(axtitle[0]) - 'X';
467 top = read_top(ftp2fn(efTPX,NFILE,fnm),&ePBC); /* read topology file */
469 snew(grpname,ngrps);
470 snew(index,ngrps);
471 snew(ngx,ngrps);
473 rd_index(ftp2fn(efNDX,NFILE,fnm),ngrps,ngx,index,grpname);
476 calc_potential(ftp2fn(efTRX,NFILE,fnm), index, ngx,
477 &potential, &charge, &field,
478 &nslices, top, ePBC, axis, ngrps, &slWidth, fudge_z,
479 bSpherical, bCorrect,oenv);
481 plot_potential(potential, charge, field, opt2fn("-o",NFILE,fnm),
482 opt2fn("-oc",NFILE,fnm), opt2fn("-of",NFILE,fnm),
483 nslices, ngrps, (const char**)grpname, slWidth,oenv);
485 do_view(oenv,opt2fn("-o",NFILE,fnm), NULL); /* view xvgr file */
486 do_view(oenv,opt2fn("-oc",NFILE,fnm), NULL); /* view xvgr file */
487 do_view(oenv,opt2fn("-of",NFILE,fnm), NULL); /* view xvgr file */
489 thanx(stderr);
490 return 0;