Fixed make_edi.c
[gromacs/rigid-bodies.git] / src / tools / gmx_potential.c
blob89d54af82f57a7ced60ab80f1810a13f786f9738
1 /*
2 *
3 * This source code is part of
4 *
5 * G R O M A C S
6 *
7 * GROningen MAchine for Chemical Simulations
8 *
9 * VERSION 3.2.0
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
32 * And Hey:
33 * Green Red Orange Magenta Azure Cyan Skyblue
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
39 #include <math.h>
40 #include <ctype.h>
42 #include "sysstuff.h"
43 #include "string.h"
44 #include "typedefs.h"
45 #include "smalloc.h"
46 #include "macros.h"
47 #include "princ.h"
48 #include "rmpbc.h"
49 #include "vec.h"
50 #include "xvgr.h"
51 #include "pbc.h"
52 #include "copyrite.h"
53 #include "futil.h"
54 #include "statutil.h"
55 #include "index.h"
56 #include "gmx_ana.h"
59 #define EPS0 8.85419E-12
60 #define ELC 1.60219E-19
62 /****************************************************************************/
63 /* This program calculates the electrostatic potential across the box by */
64 /* determining the charge density in slices of the box and integrating these*/
65 /* twice. */
66 /* Peter Tieleman, April 1995 */
67 /* It now also calculates electrostatic potential in spherical micelles, */
68 /* using \frac{1}{r}\frac{d^2r\Psi}{r^2} = - \frac{\rho}{\epsilon_0} */
69 /* This probably sucks but it seems to work. */
70 /****************************************************************************/
72 static int ce=0, cb=0;
74 /* this routine integrates the array data and returns the resulting array */
75 /* routine uses simple trapezoid rule */
76 void p_integrate(double *result, double data[], int ndata, double slWidth)
78 int i, slice;
79 double sum;
81 if (ndata <= 2)
82 fprintf(stderr,"Warning: nr of slices very small. This will result"
83 "in nonsense.\n");
85 fprintf(stderr,"Integrating from slice %d to slice %d\n",cb, ndata-ce);
87 for (slice = cb; slice < (ndata-ce); slice ++)
89 sum = 0;
90 for (i = cb; i < slice; i++)
91 sum += slWidth * (data[i] + 0.5 * (data[i+1] - data[i]));
92 result[slice] = sum;
94 return;
97 void calc_potential(const char *fn, atom_id **index, int gnx[],
98 double ***slPotential, double ***slCharge,
99 double ***slField, int *nslices,
100 t_topology *top, int ePBC,
101 int axis, int nr_grps, double *slWidth,
102 double fudge_z, bool bSpherical, bool bCorrect,
103 const output_env_t oenv)
105 rvec *x0; /* coordinates without pbc */
106 matrix box; /* box (3x3) */
107 int natoms, /* nr. atoms in trj */
108 status,
109 **slCount, /* nr. of atoms in one slice for a group */
110 i,j,n, /* loop indices */
111 teller = 0,
112 ax1=0, ax2=0,
113 nr_frames = 0, /* number of frames */
114 slice; /* current slice */
115 double slVolume; /* volume of slice for spherical averaging */
116 double qsum,nn;
117 real t;
118 double z;
119 rvec xcm;
121 switch(axis)
123 case 0:
124 ax1 = 1; ax2 = 2;
125 break;
126 case 1:
127 ax1 = 0; ax2 = 2;
128 break;
129 case 2:
130 ax1 = 0; ax2 = 1;
131 break;
132 default:
133 gmx_fatal(FARGS,"Invalid axes. Terminating\n");
136 if ((natoms = read_first_x(oenv,&status,fn,&t,&x0,box)) == 0)
137 gmx_fatal(FARGS,"Could not read coordinates from statusfile\n");
139 if (! *nslices)
140 *nslices = (int)(box[axis][axis] * 10); /* default value */
142 fprintf(stderr,"\nDividing the box in %d slices\n",*nslices);
144 snew(*slField, nr_grps);
145 snew(*slCharge, nr_grps);
146 snew(*slPotential, nr_grps);
148 for (i = 0; i < nr_grps; i++)
150 snew((*slField)[i], *nslices);
151 snew((*slCharge)[i], *nslices);
152 snew((*slPotential)[i], *nslices);
155 /*********** Start processing trajectory ***********/
158 *slWidth = box[axis][axis]/(*nslices);
159 teller++;
161 rm_pbc(&(top->idef),ePBC,top->atoms.nr,box,x0,x0);
163 /* calculate position of center of mass based on group 1 */
164 calc_xcm(x0, gnx[0], index[0], top->atoms.atom, xcm, FALSE);
165 svmul(-1,xcm,xcm);
167 for (n = 0; n < nr_grps; n++)
169 for (i = 0; i < gnx[n]; i++) /* loop over all atoms in index file */
171 if (bSpherical)
173 rvec_add(x0[index[n][i]], xcm, x0[index[n][i]]);
174 /* only distance from origin counts, not sign */
175 slice = norm(x0[index[n][i]])/(*slWidth);
177 /* this is a nice check for spherical groups but not for
178 all water in a cubic box since a lot will fall outside
179 the sphere
180 if (slice > (*nslices))
182 fprintf(stderr,"Warning: slice = %d\n",slice);
185 (*slCharge)[n][slice] += top->atoms.atom[index[n][i]].q;
187 else
189 z = x0[index[n][i]][axis];
190 z = z + fudge_z;
191 if (z < 0)
192 z += box[axis][axis];
193 if (z > box[axis][axis])
194 z -= box[axis][axis];
195 /* determine which slice atom is in */
196 slice = (z / (*slWidth));
197 (*slCharge)[n][slice] += top->atoms.atom[index[n][i]].q;
201 nr_frames++;
202 } while (read_next_x(oenv,status,&t,natoms,x0,box));
204 /*********** done with status file **********/
205 close_trj(status);
207 /* slCharge now contains the total charge per slice, summed over all
208 frames. Now divide by nr_frames and integrate twice
212 if (bSpherical)
213 fprintf(stderr,"\n\nRead %d frames from trajectory. Calculating potential"
214 "in spherical coordinates\n", nr_frames);
215 else
216 fprintf(stderr,"\n\nRead %d frames from trajectory. Calculating potential\n",
217 nr_frames);
219 for (n =0; n < nr_grps; n++)
221 for (i = 0; i < *nslices; i++)
223 if (bSpherical)
225 /* charge per volume is now the summed charge, divided by the nr
226 of frames and by the volume of the slice it's in, 4pi r^2 dr
228 slVolume = 4*M_PI * sqr(i) * sqr(*slWidth) * *slWidth;
229 if (slVolume == 0)
231 (*slCharge)[n][i] = 0;
233 else
235 (*slCharge)[n][i] = (*slCharge)[n][i] / (nr_frames * slVolume);
238 else
240 /* get charge per volume */
241 (*slCharge)[n][i] = (*slCharge)[n][i] * (*nslices) /
242 (nr_frames * box[axis][axis] * box[ax1][ax1] * box[ax2][ax2]);
245 /* Now we have charge densities */
248 if(bCorrect && !bSpherical)
250 for(n =0; n < nr_grps; n++)
252 nn = 0;
253 qsum = 0;
254 for (i = 0; i < *nslices; i++)
256 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
258 nn++;
259 qsum += (*slCharge)[n][i];
262 qsum /= nn;
263 for (i = 0; i < *nslices; i++)
265 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
267 (*slCharge)[n][i] -= qsum;
273 for(n =0; n < nr_grps; n++)
275 /* integrate twice to get field and potential */
276 p_integrate((*slField)[n], (*slCharge)[n], *nslices, *slWidth);
280 if(bCorrect && !bSpherical)
282 for(n =0; n < nr_grps; n++)
284 nn = 0;
285 qsum = 0;
286 for (i = 0; i < *nslices; i++)
288 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
290 nn++;
291 qsum += (*slField)[n][i];
294 qsum /= nn;
295 for (i = 0; i < *nslices; i++)
297 if( fabs((*slCharge)[n][i]) >= GMX_DOUBLE_MIN )
299 (*slField)[n][i] -= qsum;
305 for(n =0; n < nr_grps; n++)
307 p_integrate((*slPotential)[n],(*slField)[n], *nslices, *slWidth);
310 /* Now correct for eps0 and in spherical case for r*/
311 for (n = 0; n < nr_grps; n++)
312 for (i = 0; i < *nslices; i++)
314 if (bSpherical)
316 (*slPotential)[n][i] = ELC * (*slPotential)[n][i] * -1.0E9 /
317 (EPS0 * i * (*slWidth));
318 (*slField)[n][i] = ELC * (*slField)[n][i] * 1E18 /
319 (EPS0 * i * (*slWidth));
321 else
323 (*slPotential)[n][i] = ELC * (*slPotential)[n][i] * -1.0E9 / EPS0 ;
324 (*slField)[n][i] = ELC * (*slField)[n][i] * 1E18 / EPS0;
328 sfree(x0); /* free memory used by coordinate array */
331 void plot_potential(double *potential[], double *charge[], double *field[],
332 const char *afile, const char *bfile, const char *cfile,
333 int nslices, int nr_grps, char *grpname[], double slWidth,
334 const output_env_t oenv)
336 FILE *pot, /* xvgr file with potential */
337 *cha, /* xvgr file with charges */
338 *fie; /* xvgr files with fields */
339 char buf[256]; /* for xvgr title */
340 int slice, n;
342 sprintf(buf,"Electrostatic Potential");
343 pot = xvgropen(afile, buf, "Box (nm)","Potential (V)",oenv);
344 xvgr_legend(pot,nr_grps,grpname,oenv);
346 sprintf(buf,"Charge Distribution");
347 cha = xvgropen(bfile, buf, "Box (nm)", "Charge density (q/nm\\S3\\N)",oenv);
348 xvgr_legend(cha,nr_grps,grpname,oenv);
350 sprintf(buf, "Electric Field");
351 fie = xvgropen(cfile, buf, "Box (nm)", "Field (V/nm)",oenv);
352 xvgr_legend(fie,nr_grps,grpname,oenv);
354 for (slice = cb; slice < (nslices - ce); slice++)
356 fprintf(pot,"%20.16g ", slice * slWidth);
357 fprintf(cha,"%20.16g ", slice * slWidth);
358 fprintf(fie,"%20.16g ", slice * slWidth);
359 for (n = 0; n < nr_grps; n++)
361 fprintf(pot," %20.16g", potential[n][slice]);
362 fprintf(fie," %20.16g", field[n][slice]);
363 fprintf(cha," %20.16g", charge[n][slice]);
365 fprintf(pot,"\n");
366 fprintf(cha,"\n");
367 fprintf(fie,"\n");
370 ffclose(pot);
371 ffclose(cha);
372 ffclose(fie);
375 int gmx_potential(int argc,char *argv[])
377 const char *desc[] = {
378 "Compute the electrostatical potential across the box. The potential is",
379 "calculated by first summing the charges per slice and then integrating",
380 "twice of this charge distribution. Periodic boundaries are not taken",
381 "into account. Reference of potential is taken to be the left side of",
382 "the box. It's also possible to calculate the potential in spherical",
383 "coordinates as function of r by calculating a charge distribution in",
384 "spherical slices and twice integrating them. epsilon_r is taken as 1,",
385 "2 is more appropriate in many cases."
387 output_env_t oenv;
388 static int axis = 2; /* normal to memb. default z */
389 static const char *axtitle="Z";
390 static int nslices = 10; /* nr of slices defined */
391 static int ngrps = 1;
392 static bool bSpherical = FALSE; /* default is bilayer types */
393 static real fudge_z = 0; /* translate coordinates */
394 static bool bCorrect = 0;
395 t_pargs pa [] = {
396 { "-d", FALSE, etSTR, {&axtitle},
397 "Take the normal on the membrane in direction X, Y or Z." },
398 { "-sl", FALSE, etINT, {&nslices},
399 "Calculate potential as function of boxlength, dividing the box"
400 " in #nr slices." } ,
401 { "-cb", FALSE, etINT, {&cb},
402 "Discard first #nr slices of box for integration" },
403 { "-ce", FALSE, etINT, {&ce},
404 "Discard last #nr slices of box for integration" },
405 { "-tz", FALSE, etREAL, {&fudge_z},
406 "Translate all coordinates <distance> in the direction of the box" },
407 { "-spherical", FALSE, etBOOL, {&bSpherical},
408 "Calculate spherical thingie" },
409 { "-ng", FALSE, etINT, {&ngrps},
410 "Number of groups to consider" },
411 { "-correct", FALSE, etBOOL, {&bCorrect},
412 "Assume net zero charge of groups to improve accuracy" }
414 const char *bugs[] = {
415 "Discarding slices for integration should not be necessary."
418 double **potential, /* potential per slice */
419 **charge, /* total charge per slice */
420 **field, /* field per slice */
421 slWidth; /* width of one slice */
422 char **grpname; /* groupnames */
423 int *ngx; /* sizes of groups */
424 t_topology *top; /* topology */
425 int ePBC;
426 atom_id **index; /* indices for all groups */
427 t_filenm fnm[] = { /* files for g_order */
428 { efTRX, "-f", NULL, ffREAD }, /* trajectory file */
429 { efNDX, NULL, NULL, ffREAD }, /* index file */
430 { efTPX, NULL, NULL, ffREAD }, /* topology file */
431 { efXVG,"-o","potential", ffWRITE }, /* xvgr output file */
432 { efXVG,"-oc","charge", ffWRITE }, /* xvgr output file */
433 { efXVG,"-of","field", ffWRITE }, /* xvgr output file */
436 #define NFILE asize(fnm)
438 CopyRight(stderr,argv[0]);
439 parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
440 NFILE,fnm,asize(pa),pa,asize(desc),desc,asize(bugs),bugs,
441 &oenv);
443 /* Calculate axis */
444 axis = toupper(axtitle[0]) - 'X';
446 top = read_top(ftp2fn(efTPX,NFILE,fnm),&ePBC); /* read topology file */
448 snew(grpname,ngrps);
449 snew(index,ngrps);
450 snew(ngx,ngrps);
452 rd_index(ftp2fn(efNDX,NFILE,fnm),ngrps,ngx,index,grpname);
455 calc_potential(ftp2fn(efTRX,NFILE,fnm), index, ngx,
456 &potential, &charge, &field,
457 &nslices, top, ePBC, axis, ngrps, &slWidth, fudge_z,
458 bSpherical, bCorrect,oenv);
460 plot_potential(potential, charge, field, opt2fn("-o",NFILE,fnm),
461 opt2fn("-oc",NFILE,fnm), opt2fn("-of",NFILE,fnm),
462 nslices, ngrps, grpname, slWidth,oenv);
464 do_view(oenv,opt2fn("-o",NFILE,fnm), NULL); /* view xvgr file */
465 do_view(oenv,opt2fn("-oc",NFILE,fnm), NULL); /* view xvgr file */
466 do_view(oenv,opt2fn("-of",NFILE,fnm), NULL); /* view xvgr file */
468 thanx(stderr);
469 return 0;