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
52 #include "gmx_statistics.h"
65 #define e2d(x) ENM2DEBYE*(x)
66 #define EANG2CM E_CHARGE*1.0e-10 /* e Angstrom to Coulomb meter */
67 #define CM2D SPEED_OF_LIGHT*1.0e+24 /* Coulomb meter to Debye */
79 static t_gkrbin *mk_gkrbin(real radius,real rcmax,gmx_bool bPhi,int ndegrees)
87 if ((ptr = getenv("GKRWIDTH")) != NULL) {
90 sscanf(ptr,"%lf",&bw);
94 gb->spacing = 0.01; /* nm */
95 gb->nelem = 1 + radius/gb->spacing;
99 gb->nx = 1 + rcmax/gb->spacing;
101 snew(gb->elem,gb->nelem);
102 snew(gb->count,gb->nelem);
104 snew(gb->cmap,gb->nx);
105 gb->ny = max(2,ndegrees);
106 for(i=0; (i<gb->nx); i++)
107 snew(gb->cmap[i],gb->ny);
113 static void done_gkrbin(t_gkrbin **gb)
121 static void add2gkr(t_gkrbin *gb,real r,real cosa,real phi)
123 int cy,index = gmx_nint(r/gb->spacing);
126 if (index < gb->nelem) {
127 gb->elem[index] += cosa;
130 if (index < gb->nx) {
133 cy = (M_PI+phi)*gb->ny/(2*M_PI);
135 cy = (alpha*gb->ny)/M_PI;/*((1+cosa)*0.5*(gb->ny));*/
136 cy = min(gb->ny-1,max(0,cy));
138 fprintf(debug,"CY: %10f %5d\n",alpha,cy);
139 gb->cmap[index][cy] += 1;
143 static void rvec2sprvec(rvec dipcart,rvec dipsp)
146 dipsp[0] = sqrt(dipcart[XX]*dipcart[XX]+dipcart[YY]*dipcart[YY]+dipcart[ZZ]*dipcart[ZZ]); /* R */
147 dipsp[1] = atan2(dipcart[YY],dipcart[XX]); /* Theta */
148 dipsp[2] = atan2(sqrt(dipcart[XX]*dipcart[XX]+dipcart[YY]*dipcart[YY]),dipcart[ZZ]); /* Phi */
153 void do_gkr(t_gkrbin *gb,int ncos,int *ngrp,int *molindex[],
154 int mindex[],rvec x[],rvec mu[],
155 int ePBC,matrix box,t_atom *atom,int *nAtom)
157 static rvec *xcm[2] = { NULL, NULL};
158 int gi,gj,j0,j1,i,j,k,n,index,grp0,grp1;
159 real qtot,q,r2,cosa,rr,phi;
163 for(n=0; (n<ncos); n++) {
165 snew(xcm[n],ngrp[n]);
166 for(i=0; (i<ngrp[n]); i++) {
167 /* Calculate center of mass of molecule */
172 copy_rvec(x[j0+nAtom[n]-1],xcm[n][i]);
175 clear_rvec(xcm[n][i]);
177 for(j=j0; j<j1; j++) {
181 xcm[n][i][k] += q*x[j][k];
183 svmul(1/qtot,xcm[n][i],xcm[n][i]);
187 set_pbc(&pbc,ePBC,box);
190 for(i=0; i<ngrp[grp0]; i++) {
191 gi = molindex[grp0][i];
192 j0 = (ncos == 2) ? 0 : i+1;
193 for(j=j0; j<ngrp[grp1]; j++) {
194 gj = molindex[grp1][j];
195 if ((iprod(mu[gi],mu[gi]) > 0) && (iprod(mu[gj],mu[gj]) > 0)) {
196 /* Compute distance between molecules including PBC */
197 pbc_dx(&pbc,xcm[grp0][i],xcm[grp1][j],dx);
202 rvec r_ij,r_kj,r_kl,mm,nn;
206 copy_rvec(xcm[grp0][i],xj);
207 copy_rvec(xcm[grp1][j],xk);
208 rvec_add(xj,mu[gi],xi);
209 rvec_add(xk,mu[gj],xl);
210 phi = dih_angle(xi,xj,xk,xl,&pbc,
211 r_ij,r_kj,r_kl,mm,nn, /* out */
216 cosa = cos_angle(mu[gi],mu[gj]);
219 if (debug || (cosa != cosa)) {
220 fprintf(debug ? debug : stderr,
221 "mu[%d] = %5.2f %5.2f %5.2f |mi| = %5.2f, mu[%d] = %5.2f %5.2f %5.2f |mj| = %5.2f rr = %5.2f cosa = %5.2f\n",
222 gi,mu[gi][XX],mu[gi][YY],mu[gi][ZZ],norm(mu[gi]),
223 gj,mu[gj][XX],mu[gj][YY],mu[gj][ZZ],norm(mu[gj]),
227 add2gkr(gb,rr,cosa,phi);
233 static real normalize_cmap(t_gkrbin *gb)
239 for(i=0; (i<gb->nx); i++) {
240 vol = 4*M_PI*sqr(gb->spacing*i)*gb->spacing;
241 for(j=0; (j<gb->ny); j++) {
242 gb->cmap[i][j] /= vol;
243 hi = max(hi,gb->cmap[i][j]);
247 gmx_fatal(FARGS,"No data in the cmap");
251 static void print_cmap(const char *cmap,t_gkrbin *gb,int *nlevels)
258 t_rgb rlo = { 1, 1, 1 };
259 t_rgb rhi = { 0, 0, 0 };
261 hi = normalize_cmap(gb);
262 snew(xaxis,gb->nx+1);
263 for(i=0; (i<gb->nx+1); i++)
264 xaxis[i] = i*gb->spacing;
266 for(j=0; (j<gb->ny); j++) {
268 yaxis[j] = (360.0*j)/(gb->ny-1.0)-180;
270 yaxis[j] = (180.0*j)/(gb->ny-1.0);
271 /*2.0*j/(gb->ny-1.0)-1.0;*/
273 out = ffopen(cmap,"w");
275 "Dipole Orientation Distribution","Fraction","r (nm)",
276 gb->bPhi ? "Phi" : "Alpha",
277 gb->nx,gb->ny,xaxis,yaxis,
278 gb->cmap,0,hi,rlo,rhi,nlevels);
284 static void print_gkrbin(const char *fn,t_gkrbin *gb,
285 int ngrp,int nframes,real volume,
286 const output_env_t oenv)
288 /* We compute Gk(r), gOO and hOO according to
289 * Nymand & Linse, JCP 112 (2000) pp 6386-6395.
290 * In this implementation the angle between dipoles is stored
291 * rather than their inner product. This allows to take polarizible
292 * models into account. The RDF is calculated as well, almost for free!
295 const char *leg[] = { "G\\sk\\N(r)", "< cos >", "h\\sOO\\N", "g\\sOO\\N", "Energy" };
297 real x0,x1,ggg,Gkr,vol_s,rho,gOO,hOO,cosav,ener;
300 fp=xvgropen(fn,"Distance dependent Gk","r (nm)","G\\sk\\N(r)",oenv);
301 xvgr_legend(fp,asize(leg),leg,oenv);
303 Gkr = 1; /* Self-dipole inproduct = 1 */
307 fprintf(debug,"Number density is %g molecules / nm^3\n",rho);
308 fprintf(debug,"ngrp = %d, nframes = %d\n",ngrp,nframes);
312 while(last>1 && gb->elem[last-1]==0)
315 /* Divide by dipole squared, by number of frames, by number of origins.
316 * Multiply by 2 because we only take half the matrix of interactions
319 fac = 2.0/((double) ngrp * (double) nframes);
322 for(i=0; i<last; i++) {
323 /* Centre of the coordinate in the spherical layer */
326 /* Volume of the layer */
327 vol_s = (4.0/3.0)*M_PI*(x1*x1*x1-x0*x0*x0);
330 gOO = gb->count[i]*fac/(rho*vol_s);
332 /* Dipole correlation hOO, normalized by the relative number density, like
333 * in a Radial distribution function.
335 ggg = gb->elem[i]*fac;
336 hOO = 3.0*ggg/(rho*vol_s);
339 cosav = gb->elem[i]/gb->count[i];
342 ener = -0.5*cosav*ONE_4PI_EPS0/(x1*x1*x1);
344 fprintf(fp,"%10.5e %12.5e %12.5e %12.5e %12.5e %12.5e\n",
345 x1,Gkr,cosav,hOO,gOO,ener);
353 gmx_bool read_mu_from_enx(ener_file_t fmu,int Vol,ivec iMu,rvec mu,real *vol,
354 real *t, int nre,t_enxframe *fr)
360 bCont = do_enx(fmu,fr);
362 fprintf(stderr,"Something strange: expected %d entries in energy file at step %s\n(time %g) but found %d entries\n",
363 nre,gmx_step_str(fr->step,buf),fr->t,fr->nre);
366 if (Vol != -1) /* we've got Volume in the energy file */
367 *vol = fr->ener[Vol].e;
368 for (i=0; i<DIM; i++)
369 mu[i] = fr->ener[iMu[i]].e;
376 static void neutralize_mols(int n,int *index,t_block *mols,t_atom *atom)
379 int ncharged,m,a0,a1,a;
383 a0 = mols->index[index[m]];
384 a1 = mols->index[index[m]+1];
387 for(a=a0; a<a1; a++) {
391 /* This check is only for the count print */
392 if (fabs(qtot) > 0.01)
395 /* Remove the net charge at the center of mass */
397 atom[a].q -= qtot*atom[a].m/mtot;
402 printf("There are %d charged molecules in the selection,\n"
403 "will subtract their charge at their center of mass\n",ncharged);
406 static void mol_dip(int k0,int k1,rvec x[],t_atom atom[],rvec mu)
412 for(k=k0; (k<k1); k++) {
414 for(m=0; (m<DIM); m++)
419 static void mol_quad(int k0,int k1,rvec x[],t_atom atom[],rvec quad)
422 real q,r2,mass,masstot;
423 rvec com; /* center of mass */
424 rvec r; /* distance of atoms to center of mass */
427 double dd[DIM],**ev,tmp;
431 for(i=0; (i<DIM); i++) {
437 /* Compute center of mass */
440 for(k=k0; (k<k1); k++) {
443 for(i=0; (i<DIM); i++)
444 com[i] += mass*x[k][i];
446 svmul((1.0/masstot),com,com);
448 /* We want traceless quadrupole moments, so let us calculate the complete
449 * quadrupole moment tensor and diagonalize this tensor to get
450 * the individual components on the diagonal.
453 #define delta(a,b) (( a == b ) ? 1.0 : 0.0)
455 for(m=0; (m<DIM); m++)
456 for(n=0; (n<DIM); n++)
458 for(k=k0; (k<k1); k++) { /* loop over atoms in a molecule */
459 q = (atom[k].q)*100.0;
460 rvec_sub(x[k],com,r);
462 for(m=0; (m<DIM); m++)
463 for(n=0; (n<DIM); n++)
464 inten[m][n] += 0.5*q*(3.0*r[m]*r[n] - r2*delta(m,n))*EANG2CM*CM2D;
467 for(i=0; (i<DIM); i++)
468 fprintf(debug,"Q[%d] = %8.3f %8.3f %8.3f\n",
469 i,inten[i][XX],inten[i][YY],inten[i][ZZ]);
471 /* We've got the quadrupole tensor, now diagonalize the sucker */
472 jacobi(inten,3,dd,ev,&niter);
475 for(i=0; (i<DIM); i++)
476 fprintf(debug,"ev[%d] = %8.3f %8.3f %8.3f\n",
477 i,ev[i][XX],ev[i][YY],ev[i][ZZ]);
478 for(i=0; (i<DIM); i++)
479 fprintf(debug,"Q'[%d] = %8.3f %8.3f %8.3f\n",
480 i,inten[i][XX],inten[i][YY],inten[i][ZZ]);
482 /* Sort the eigenvalues, for water we know that the order is as follows:
486 * At the moment I have no idea how this will work out for other molecules...
490 if (dd[i+1] > dd[i]) { \
499 for(m=0; (m<DIM); m++) {
500 quad[0]=dd[2]; /* yy */
501 quad[1]=dd[0]; /* zz */
502 quad[2]=dd[1]; /* xx */
506 pr_rvec(debug,0,"Quadrupole",quad,DIM,TRUE);
509 for(i=0; (i<DIM); i++) {
518 * Calculates epsilon according to M. Neumann, Mol. Phys. 50, 841 (1983)
520 real calc_eps(double M_diff,double volume,double epsRF,double temp)
522 double eps,A,teller,noemer;
523 double eps_0=8.854187817e-12; /* epsilon_0 in C^2 J^-1 m^-1 */
524 double fac=1.112650021e-59; /* converts Debye^2 to C^2 m^2 */
526 A = M_diff*fac/(3*eps_0*volume*NANO*NANO*NANO*BOLTZMANN*temp);
532 teller = 1 + (A*2*epsRF/(2*epsRF+1));
533 noemer = 1 - (A/(2*epsRF+1));
535 eps = teller / noemer;
540 static void update_slab_dipoles(int k0,int k1,rvec x[],rvec mu,
541 int idim,int nslice,rvec slab_dipole[],
547 for(k=k0; (k<k1); k++)
550 k = ((int)(xdim*nslice/box[idim][idim] + nslice)) % nslice;
551 rvec_inc(slab_dipole[k],mu);
554 static void dump_slab_dipoles(const char *fn,int idim,int nslice,
555 rvec slab_dipole[], matrix box,int nframes,
556 const output_env_t oenv)
562 const char *leg_dim[4] = {
563 "\\f{12}m\\f{4}\\sX\\N",
564 "\\f{12}m\\f{4}\\sY\\N",
565 "\\f{12}m\\f{4}\\sZ\\N",
566 "\\f{12}m\\f{4}\\stot\\N"
569 sprintf(buf,"Box-%c (nm)",'X'+idim);
570 fp = xvgropen(fn,"Average dipole moment per slab",buf,"\\f{12}m\\f{4} (D)",
572 xvgr_legend(fp,DIM,leg_dim,oenv);
573 for(i=0; (i<nslice); i++) {
574 mutot = norm(slab_dipole[i])/nframes;
575 fprintf(fp,"%10.3f %10.3f %10.3f %10.3f %10.3f\n",
576 ((i+0.5)*box[idim][idim])/nslice,
577 slab_dipole[i][XX]/nframes,
578 slab_dipole[i][YY]/nframes,
579 slab_dipole[i][ZZ]/nframes,
583 do_view(oenv,fn,"-autoscale xy -nxy");
586 static void compute_avercos(int n,rvec dip[],real *dd,rvec axis,gmx_bool bPairs)
589 double dc,dc1,d,n5,ddc1,ddc2,ddc3;
590 rvec xxx = { 1, 0, 0 };
591 rvec yyy = { 0, 1, 0 };
592 rvec zzz = { 0, 0, 1 };
595 ddc1 = ddc2 = ddc3 = 0;
596 for(i=k=0; (i<n); i++) {
597 ddc1 += fabs(cos_angle(dip[i],xxx));
598 ddc2 += fabs(cos_angle(dip[i],yyy));
599 ddc3 += fabs(cos_angle(dip[i],zzz));
601 for(j=i+1; (j<n); j++,k++) {
602 dc = cos_angle(dip[i],dip[j]);
612 static void do_dip(t_topology *top,int ePBC,real volume,
614 const char *out_mtot,const char *out_eps,
615 const char *out_aver, const char *dipdist,
616 const char *cosaver, const char *fndip3d,
617 const char *fnadip, gmx_bool bPairs,
618 const char *corrtype,const char *corf,
619 gmx_bool bGkr, const char *gkrfn,
620 gmx_bool bPhi, int *nlevels, int ndegrees,
622 const char *cmap, real rcmax,
623 gmx_bool bQuad, const char *quadfn,
624 gmx_bool bMU, const char *mufn,
625 int *gnx, int *molindex[],
626 real mu_max, real mu_aver,
627 real epsilonRF,real temp,
628 int *gkatom, int skip,
629 gmx_bool bSlab, int nslices,
630 const char *axtitle, const char *slabfn,
631 const output_env_t oenv)
633 const char *leg_mtot[] = {
639 #define NLEGMTOT asize(leg_mtot)
640 const char *leg_eps[] = {
645 #define NLEGEPS asize(leg_eps)
646 const char *leg_aver[] = {
649 "< |M|\\S2\\N > - < |M| >\\S2\\N",
650 "< |M| >\\S2\\N / < |M|\\S2\\N >"
652 #define NLEGAVER asize(leg_aver)
653 const char *leg_cosaver[] = {
654 "\\f{4}<|cos\\f{12}q\\f{4}\\sij\\N|>",
656 "\\f{4}<|cos\\f{12}q\\f{4}\\siX\\N|>",
657 "\\f{4}<|cos\\f{12}q\\f{4}\\siY\\N|>",
658 "\\f{4}<|cos\\f{12}q\\f{4}\\siZ\\N|>"
660 #define NLEGCOSAVER asize(leg_cosaver)
661 const char *leg_adip[] = {
666 #define NLEGADIP asize(leg_adip)
668 FILE *outdd,*outmtot,*outaver,*outeps,*caver=NULL;
669 FILE *dip3d=NULL,*adip=NULL;
670 rvec *x,*dipole=NULL,mu_t,quad,*dipsp=NULL;
671 t_gkrbin *gkrbin = NULL;
672 gmx_enxnm_t *enm=NULL;
674 int nframes=1000,nre,timecheck=0,ncolour=0;
675 ener_file_t fmu=NULL;
676 int i,j,k,n,m,natom=0,nmol,gnx_tot,teller,tel3;
678 int *dipole_bin,ndipbin,ibin,iVol,step,idim=-1;
681 real rcut=0,t,t0,t1,dt,lambda,dd,rms_cos;
684 gmx_bool bCorr,bTotal,bCont;
685 double M_diff=0,epsilon,invtel,vol_aver;
686 double mu_ave,mu_mol,M2_ave=0,M_ave2=0,M_av[DIM],M_av2[DIM];
687 double M[3],M2[3],M4[3],Gk=0,g_k=0;
688 gmx_stats_t Mx,My,Mz,Msq,Vol,*Qlsq,mulsq,muframelsq=NULL;
691 rvec *slab_dipoles=NULL;
694 gmx_rmpbc_t gpbc=NULL;
705 fmu = open_enx(mufn,"r");
706 do_enxnms(fmu,&nre,&enm);
708 /* Determine the indexes of the energy grps we need */
709 for (i=0; (i<nre); i++) {
710 if (strstr(enm[i].name,"Volume"))
712 else if (strstr(enm[i].name,"Mu-X"))
714 else if (strstr(enm[i].name,"Mu-Y"))
716 else if (strstr(enm[i].name,"Mu-Z"))
721 atom = top->atoms.atom;
726 printf("Using Volume from topology: %g nm^3\n",volume);
728 /* Correlation stuff */
729 bCorr = (corrtype[0] != 'n');
730 bTotal = (corrtype[0] == 't');
734 snew(muall[0],nframes*DIM);
738 for(i=0; (i<gnx[0]); i++)
739 snew(muall[i],nframes*DIM);
743 /* Allocate array which contains for every molecule in a frame the
747 snew(dipole,gnx_tot);
751 for(i=0; (i<DIM); i++)
752 Qlsq[i] = gmx_stats_init();
753 mulsq = gmx_stats_init();
755 /* Open all the files */
756 outmtot = xvgropen(out_mtot,
757 "Total dipole moment of the simulation box vs. time",
758 "Time (ps)","Total Dipole Moment (Debye)",oenv);
759 outeps = xvgropen(out_eps,"Epsilon and Kirkwood factors",
760 "Time (ps)","",oenv);
761 outaver = xvgropen(out_aver,"Total dipole moment",
762 "Time (ps)","D",oenv);
764 idim = axtitle[0] - 'X';
765 if ((idim < 0) || (idim >= DIM))
766 idim = axtitle[0] - 'x';
767 if ((idim < 0) || (idim >= DIM))
771 fprintf(stderr,"axtitle = %s, nslices = %d, idim = %d\n",
772 axtitle,nslices,idim);
774 snew(slab_dipoles,nslices);
775 fprintf(stderr,"Doing slab analysis\n");
780 adip = xvgropen(fnadip, "Average molecular dipole","Dipole (D)","",oenv);
781 xvgr_legend(adip,NLEGADIP,leg_adip, oenv);
785 caver = xvgropen(cosaver,bPairs ? "Average pair orientation" :
786 "Average absolute dipole orientation","Time (ps)","",oenv);
787 xvgr_legend(caver,NLEGCOSAVER,bPairs ? leg_cosaver : &(leg_cosaver[1]),
794 /* we need a dummy file for gnuplot */
795 dip3d = (FILE *)ffopen("dummy.dat","w");
796 fprintf(dip3d,"%f %f %f", 0.0,0.0,0.0);
799 dip3d = (FILE *)ffopen(fndip3d,"w");
800 fprintf(dip3d,"# This file was created by %s\n",Program());
801 fprintf(dip3d,"# which is part of G R O M A C S:\n");
802 fprintf(dip3d,"#\n");
805 /* Write legends to all the files */
806 xvgr_legend(outmtot,NLEGMTOT,leg_mtot,oenv);
807 xvgr_legend(outaver,NLEGAVER,leg_aver,oenv);
809 if (bMU && (mu_aver == -1))
810 xvgr_legend(outeps,NLEGEPS-2,leg_eps,oenv);
812 xvgr_legend(outeps,NLEGEPS,leg_eps,oenv);
817 /* Read the first frame from energy or traj file */
820 bCont = read_mu_from_enx(fmu,iVol,iMu,mu_t,&volume,&t,nre,fr);
822 timecheck=check_times(t);
825 if ((teller % 10) == 0)
826 fprintf(stderr,"\r Skipping Frame %6d, time: %8.3f", teller, t);
829 printf("End of %s reached\n",mufn);
832 } while (bCont && (timecheck < 0));
834 natom = read_first_x(oenv,&status,fn,&t,&x,box);
836 /* Calculate spacing for dipole bin (simple histogram) */
837 ndipbin = 1+(mu_max/0.01);
838 snew(dipole_bin, ndipbin);
841 for(m=0; (m<DIM); m++) {
842 M[m] = M2[m] = M4[m] = 0.0;
846 /* Use 0.7 iso 0.5 to account for pressure scaling */
847 /* rcut = 0.7*sqrt(max_cutoff2(box)); */
848 rcut = 0.7*sqrt(sqr(box[XX][XX])+sqr(box[YY][YY])+sqr(box[ZZ][ZZ]));
850 gkrbin = mk_gkrbin(rcut,rcmax,bPhi,ndegrees);
852 gpbc = gmx_rmpbc_init(&top->idef,ePBC,natom,box);
854 /* Start while loop over frames */
858 if (bCorr && (teller >= nframes)) {
861 srenew(muall[0],nframes*DIM);
864 for(i=0; (i<gnx_tot); i++)
865 srenew(muall[i],nframes*DIM);
871 /* Copy rvec into double precision local variable */
872 for(m=0; (m<DIM); m++)
877 for(m=0; (m<DIM); m++) {
881 gmx_rmpbc(gpbc,natom,box,x);
883 muframelsq = gmx_stats_init();
884 /* Begin loop of all molecules in frame */
885 for(n=0; (n<ncos); n++) {
886 for(i=0; (i<gnx[n]); i++) {
889 ind0 = mols->index[molindex[n][i]];
890 ind1 = mols->index[molindex[n][i]+1];
892 mol_dip(ind0,ind1,x,atom,dipole[i]);
893 gmx_stats_add_point(mulsq,0,norm(dipole[i]),0,0);
894 gmx_stats_add_point(muframelsq,0,norm(dipole[i]),0,0);
896 update_slab_dipoles(ind0,ind1,x,
897 dipole[i],idim,nslices,slab_dipoles,box);
899 mol_quad(ind0,ind1,x,atom,quad);
900 for(m=0; (m<DIM); m++)
901 gmx_stats_add_point(Qlsq[m],0,quad[m],0,0);
903 if (bCorr && !bTotal) {
905 muall[i][tel3+XX] = dipole[i][XX];
906 muall[i][tel3+YY] = dipole[i][YY];
907 muall[i][tel3+ZZ] = dipole[i][ZZ];
910 for(m=0; (m<DIM); m++) {
911 M_av[m] += dipole[i][m]; /* M per frame */
912 mu_mol += dipole[i][m]*dipole[i][m]; /* calc. mu for distribution */
914 mu_mol = sqrt(mu_mol);
916 mu_ave += mu_mol; /* calc. the average mu */
918 /* Update the dipole distribution */
919 ibin = (int)(ndipbin*mu_mol/mu_max + 0.5);
924 rvec2sprvec(dipole[i],dipsp[i]);
926 if (dipsp[i][YY] > -M_PI && dipsp[i][YY] < -0.5*M_PI) {
927 if (dipsp[i][ZZ] < 0.5 * M_PI) {
932 }else if (dipsp[i][YY] > -0.5*M_PI && dipsp[i][YY] < 0.0*M_PI) {
933 if (dipsp[i][ZZ] < 0.5 * M_PI) {
938 }else if (dipsp[i][YY] > 0.0 && dipsp[i][YY] < 0.5*M_PI) {
939 if (dipsp[i][ZZ] < 0.5 * M_PI) {
944 }else if (dipsp[i][YY] > 0.5*M_PI && dipsp[i][YY] < M_PI) {
945 if (dipsp[i][ZZ] < 0.5 * M_PI) {
952 fprintf(dip3d,"set arrow %d from %f, %f, %f to %f, %f, %f lt %d # %d %d\n",
957 x[ind0][XX]+dipole[i][XX]/25,
958 x[ind0][YY]+dipole[i][YY]/25,
959 x[ind0][ZZ]+dipole[i][ZZ]/25,
962 } /* End loop of all molecules in frame */
965 fprintf(dip3d,"set title \"t = %4.3f\"\n",t);
966 fprintf(dip3d,"set xrange [0.0:%4.2f]\n",box[XX][XX]);
967 fprintf(dip3d,"set yrange [0.0:%4.2f]\n",box[YY][YY]);
968 fprintf(dip3d,"set zrange [0.0:%4.2f]\n\n",box[ZZ][ZZ]);
969 fprintf(dip3d,"splot 'dummy.dat' using 1:2:3 w vec\n");
970 fprintf(dip3d,"pause -1 'Hit return to continue'\n");
974 /* Compute square of total dipole */
975 for(m=0; (m<DIM); m++)
976 M_av2[m] = M_av[m]*M_av[m];
979 compute_avercos(gnx_tot,dipole,&dd,dipaxis,bPairs);
980 rms_cos = sqrt(sqr(dipaxis[XX]-0.5)+
981 sqr(dipaxis[YY]-0.5)+
982 sqr(dipaxis[ZZ]-0.5));
984 fprintf(caver,"%10.3e %10.3e %10.3e %10.3e %10.3e %10.3e\n",
985 t,dd,rms_cos,dipaxis[XX],dipaxis[YY],dipaxis[ZZ]);
987 fprintf(caver,"%10.3e %10.3e %10.3e %10.3e %10.3e\n",
988 t,rms_cos,dipaxis[XX],dipaxis[YY],dipaxis[ZZ]);
992 do_gkr(gkrbin,ncos,gnx,molindex,mols->index,x,dipole,ePBC,box,
998 muall[0][tel3+XX] = M_av[XX];
999 muall[0][tel3+YY] = M_av[YY];
1000 muall[0][tel3+ZZ] = M_av[ZZ];
1003 /* Write to file the total dipole moment of the box, and its components
1006 if ((skip == 0) || ((teller % skip) == 0))
1007 fprintf(outmtot,"%10g %12.8e %12.8e %12.8e %12.8e\n",
1008 t,M_av[XX],M_av[YY],M_av[ZZ],
1009 sqrt(M_av2[XX]+M_av2[YY]+M_av2[ZZ]));
1011 for(m=0; (m<DIM); m++) {
1014 M4[m] += sqr(M_av2[m]);
1016 /* Increment loop counter */
1019 /* Calculate for output the running averages */
1020 invtel = 1.0/teller;
1021 M2_ave = (M2[XX]+M2[YY]+M2[ZZ])*invtel;
1022 M_ave2 = invtel*(invtel*(M[XX]*M[XX] + M[YY]*M[YY] + M[ZZ]*M[ZZ]));
1023 M_diff = M2_ave - M_ave2;
1025 /* Compute volume from box in traj, else we use the one from above */
1030 epsilon = calc_eps(M_diff,(vol_aver/teller),epsilonRF,temp);
1032 /* Calculate running average for dipole */
1034 mu_aver = (mu_ave/gnx_tot)*invtel;
1036 if ((skip == 0) || ((teller % skip) == 0)) {
1037 /* Write to file < |M|^2 >, |< M >|^2. And the difference between
1038 * the two. Here M is sum mu_i. Further write the finite system
1039 * Kirkwood G factor and epsilon.
1041 fprintf(outaver,"%10g %10.3e %10.3e %10.3e %10.3e\n",
1042 t,M2_ave,M_ave2,M_diff,M_ave2/M2_ave);
1046 gmx_stats_get_average(muframelsq,&aver);
1047 fprintf(adip, "%10g %f \n", t,aver);
1050 printf("%f %f\n", norm(dipole[0]), norm(dipole[1]));
1052 if (!bMU || (mu_aver != -1)) {
1053 /* Finite system Kirkwood G-factor */
1054 Gk = M_diff/(gnx_tot*mu_aver*mu_aver);
1055 /* Infinite system Kirkwood G-factor */
1056 if (epsilonRF == 0.0)
1057 g_k = ((2*epsilon+1)*Gk/(3*epsilon));
1059 g_k = ((2*epsilonRF+epsilon)*(2*epsilon+1)*
1060 Gk/(3*epsilon*(2*epsilonRF+1)));
1062 fprintf(outeps,"%10g %10.3e %10.3e %10.3e\n",t,epsilon,Gk,g_k);
1066 fprintf(outeps,"%10g %12.8e\n",t,epsilon);
1070 bCont = read_mu_from_enx(fmu,iVol,iMu,mu_t,&volume,&t,nre,fr);
1072 bCont = read_next_x(oenv,status,&t,natom,x,box);
1075 gmx_rmpbc_done(gpbc);
1091 fprintf(dip3d,"set xrange [0.0:%4.2f]\n",box[XX][XX]);
1092 fprintf(dip3d,"set yrange [0.0:%4.2f]\n",box[YY][YY]);
1093 fprintf(dip3d,"set zrange [0.0:%4.2f]\n\n",box[ZZ][ZZ]);
1094 fprintf(dip3d,"splot 'dummy.dat' using 1:2:3 w vec\n");
1095 fprintf(dip3d,"pause -1 'Hit return to continue'\n");
1100 dump_slab_dipoles(slabfn,idim,nslices,slab_dipoles,box,teller,oenv);
1101 sfree(slab_dipoles);
1105 printf("Average volume over run is %g\n",vol_aver);
1107 print_gkrbin(gkrfn,gkrbin,gnx[0],teller,vol_aver,oenv);
1108 print_cmap(cmap,gkrbin,nlevels);
1110 /* Autocorrelation function */
1113 printf("Not enough frames for autocorrelation\n");
1116 dt=(t1 - t0)/(teller-1);
1117 printf("t0 %g, t %g, teller %d\n", t0,t,teller);
1122 do_autocorr(corf,oenv,"Autocorrelation Function of Total Dipole",
1123 teller,1,muall,dt,mode,TRUE);
1125 do_autocorr(corf,oenv,"Dipole Autocorrelation Function",
1126 teller,gnx_tot,muall,dt,
1127 mode,strcmp(corrtype,"molsep"));
1131 real aver,sigma,error,lsq;
1133 gmx_stats_get_ase(mulsq,&aver,&sigma,&error);
1134 printf("\nDipole moment (Debye)\n");
1135 printf("---------------------\n");
1136 printf("Average = %8.4f Std. Dev. = %8.4f Error = %8.4f\n",
1141 for(m=0; (m<DIM); m++)
1142 gmx_stats_get_ase(mulsq,&(a[m]),&(s[m]),&(e[m]));
1144 printf("\nQuadrupole moment (Debye-Ang)\n");
1145 printf("-----------------------------\n");
1146 printf("Averages = %8.4f %8.4f %8.4f\n",a[XX],a[YY],a[ZZ]);
1147 printf("Std. Dev. = %8.4f %8.4f %8.4f\n",s[XX],s[YY],s[ZZ]);
1148 printf("Error = %8.4f %8.4f %8.4f\n",e[XX],e[YY],e[ZZ]);
1152 printf("The following averages for the complete trajectory have been calculated:\n\n");
1153 printf(" Total < M_x > = %g Debye\n", M[XX]/teller);
1154 printf(" Total < M_y > = %g Debye\n", M[YY]/teller);
1155 printf(" Total < M_z > = %g Debye\n\n", M[ZZ]/teller);
1157 printf(" Total < M_x^2 > = %g Debye^2\n", M2[XX]/teller);
1158 printf(" Total < M_y^2 > = %g Debye^2\n", M2[YY]/teller);
1159 printf(" Total < M_z^2 > = %g Debye^2\n\n", M2[ZZ]/teller);
1161 printf(" Total < |M|^2 > = %g Debye^2\n", M2_ave);
1162 printf(" Total |< M >|^2 = %g Debye^2\n\n", M_ave2);
1164 printf(" < |M|^2 > - |< M >|^2 = %g Debye^2\n\n", M_diff);
1166 if (!bMU || (mu_aver != -1)) {
1167 printf("Finite system Kirkwood g factor G_k = %g\n", Gk);
1168 printf("Infinite system Kirkwood g factor g_k = %g\n\n", g_k);
1170 printf("Epsilon = %g\n", epsilon);
1173 /* Write to file the dipole moment distibution during the simulation.
1175 outdd=xvgropen(dipdist,"Dipole Moment Distribution","mu (Debye)","",oenv);
1176 for(i=0; (i<ndipbin); i++)
1177 fprintf(outdd,"%10g %10f\n",
1178 (i*mu_max)/ndipbin,dipole_bin[i]/(double)teller);
1183 done_gkrbin(&gkrbin);
1186 void dipole_atom2molindex(int *n,int *index,t_block *mols)
1194 while(m < mols->nr && index[i] != mols->index[m])
1197 gmx_fatal(FARGS,"index[%d]=%d does not correspond to the first atom of a molecule",i+1,index[i]+1);
1198 for(j=mols->index[m]; j<mols->index[m+1]; j++) {
1199 if (i >= *n || index[i] != j)
1200 gmx_fatal(FARGS,"The index group is not a set of whole molecules");
1203 /* Modify the index in place */
1206 printf("There are %d molecules in the selection\n",nmol);
1210 int gmx_dipoles(int argc,char *argv[])
1212 const char *desc[] = {
1213 "g_dipoles computes the total dipole plus fluctuations of a simulation",
1214 "system. From this you can compute e.g. the dielectric constant for",
1215 "low dielectric media.",
1216 "For molecules with a net charge, the net charge is subtracted at",
1217 "center of mass of the molecule.[PAR]",
1218 "The file Mtot.xvg contains the total dipole moment of a frame, the",
1219 "components as well as the norm of the vector.",
1220 "The file aver.xvg contains < |Mu|^2 > and |< Mu >|^2 during the",
1222 "The file dipdist.xvg contains the distribution of dipole moments during",
1224 "The mu_max is used as the highest value in the distribution graph.[PAR]",
1225 "Furthermore the dipole autocorrelation function will be computed when",
1226 "option -corr is used. The output file name is given with the [TT]-c[tt]",
1228 "The correlation functions can be averaged over all molecules",
1229 "([TT]mol[tt]), plotted per molecule separately ([TT]molsep[tt])",
1230 "or it can be computed over the total dipole moment of the simulation box",
1231 "([TT]total[tt]).[PAR]",
1232 "Option [TT]-g[tt] produces a plot of the distance dependent Kirkwood",
1233 "G-factor, as well as the average cosine of the angle between the dipoles",
1234 "as a function of the distance. The plot also includes gOO and hOO",
1235 "according to Nymand & Linse, JCP 112 (2000) pp 6386-6395. In the same plot",
1236 "we also include the energy per scale computed by taking the inner product of",
1237 "the dipoles divided by the distance to the third power.[PAR]",
1240 "g_dipoles -corr mol -P1 -o dip_sqr -mu 2.273 -mumax 5.0 -nofft[PAR]",
1241 "This will calculate the autocorrelation function of the molecular",
1242 "dipoles using a first order Legendre polynomial of the angle of the",
1243 "dipole vector and itself a time t later. For this calculation 1001",
1244 "frames will be used. Further the dielectric constant will be calculated",
1245 "using an epsilonRF of infinity (default), temperature of 300 K (default) and",
1246 "an average dipole moment of the molecule of 2.273 (SPC). For the",
1247 "distribution function a maximum of 5.0 will be used."
1249 real mu_max=5, mu_aver=-1,rcmax=0;
1250 real epsilonRF=0.0, temp=300;
1251 gmx_bool bAverCorr=FALSE,bMolCorr=FALSE,bPairs=TRUE,bPhi=FALSE;
1252 const char *corrtype[]={NULL, "none", "mol", "molsep", "total", NULL};
1253 const char *axtitle="Z";
1254 int nslices = 10; /* nr of slices defined */
1255 int skip=0,nFA=0,nFB=0,ncos=1;
1256 int nlevels=20,ndegrees=90;
1259 { "-mu", FALSE, etREAL, {&mu_aver},
1260 "dipole of a single molecule (in Debye)" },
1261 { "-mumax", FALSE, etREAL, {&mu_max},
1262 "max dipole in Debye (for histrogram)" },
1263 { "-epsilonRF",FALSE, etREAL, {&epsilonRF},
1264 "epsilon of the reaction field used during the simulation, needed for dielectric constant calculation. WARNING: 0.0 means infinity (default)" },
1265 { "-skip", FALSE, etINT, {&skip},
1266 "Skip steps in the output (but not in the computations)" },
1267 { "-temp", FALSE, etREAL, {&temp},
1268 "Average temperature of the simulation (needed for dielectric constant calculation)" },
1269 { "-corr", FALSE, etENUM, {corrtype},
1270 "Correlation function to calculate" },
1271 { "-pairs", FALSE, etBOOL, {&bPairs},
1272 "Calculate |cos theta| between all pairs of molecules. May be slow" },
1273 { "-ncos", FALSE, etINT, {&ncos},
1274 "Must be 1 or 2. Determines whether the <cos> is computed between all mole cules in one group, or between molecules in two different groups. This turns on the -gkr flag." },
1275 { "-axis", FALSE, etSTR, {&axtitle},
1276 "Take the normal on the computational box in direction X, Y or Z." },
1277 { "-sl", FALSE, etINT, {&nslices},
1278 "Divide the box in #nr slices." },
1279 { "-gkratom", FALSE, etINT, {&nFA},
1280 "Use the n-th atom of a molecule (starting from 1) to calculate the distance between molecules rather than the center of charge (when 0) in the calculation of distance dependent Kirkwood factors" },
1281 { "-gkratom2", FALSE, etINT, {&nFB},
1282 "Same as previous option in case ncos = 2, i.e. dipole interaction between two groups of molecules" },
1283 { "-rcmax", FALSE, etREAL, {&rcmax},
1284 "Maximum distance to use in the dipole orientation distribution (with ncos == 2). If zero, a criterium based on the box length will be used." },
1285 { "-phi", FALSE, etBOOL, {&bPhi},
1286 "Plot the 'torsion angle' defined as the rotation of the two dipole vectors around the distance vector between the two molecules in the xpm file from the -cmap option. By default the cosine of the angle between the dipoles is plotted." },
1287 { "-nlevels", FALSE, etINT, {&nlevels},
1288 "Number of colors in the cmap output" },
1289 { "-ndegrees", FALSE, etINT, {&ndegrees},
1290 "Number of divisions on the y-axis in the camp output (for 180 degrees)" }
1295 char **grpname=NULL;
1296 gmx_bool bCorr,bQuad,bGkr,bMU,bSlab;
1298 { efEDR, "-en", NULL, ffOPTRD },
1299 { efTRX, "-f", NULL, ffREAD },
1300 { efTPX, NULL, NULL, ffREAD },
1301 { efNDX, NULL, NULL, ffOPTRD },
1302 { efXVG, "-o", "Mtot", ffWRITE },
1303 { efXVG, "-eps", "epsilon", ffWRITE },
1304 { efXVG, "-a", "aver", ffWRITE },
1305 { efXVG, "-d", "dipdist", ffWRITE },
1306 { efXVG, "-c", "dipcorr", ffOPTWR },
1307 { efXVG, "-g", "gkr", ffOPTWR },
1308 { efXVG, "-adip","adip", ffOPTWR },
1309 { efXVG, "-dip3d", "dip3d", ffOPTWR },
1310 { efXVG, "-cos", "cosaver", ffOPTWR },
1311 { efXPM, "-cmap","cmap", ffOPTWR },
1312 { efXVG, "-q", "quadrupole", ffOPTWR },
1313 { efXVG, "-slab","slab", ffOPTWR }
1315 #define NFILE asize(fnm)
1323 CopyRight(stderr,argv[0]);
1325 ppa = add_acf_pargs(&npargs,pa);
1326 parse_common_args(&argc,argv,PCA_CAN_TIME | PCA_CAN_VIEW | PCA_BE_NICE,
1327 NFILE,fnm,npargs,ppa,asize(desc),desc,0,NULL,&oenv);
1329 printf("Using %g as mu_max and %g as the dipole moment.\n",
1331 if (epsilonRF == 0.0)
1332 printf("WARNING: EpsilonRF = 0.0, this really means EpsilonRF = infinity\n");
1334 bMU = opt2bSet("-en",NFILE,fnm);
1335 bQuad = opt2bSet("-q",NFILE,fnm);
1336 bGkr = opt2bSet("-g",NFILE,fnm);
1337 if (opt2parg_bSet("-ncos",asize(pa),pa)) {
1338 if ((ncos != 1) && (ncos != 2))
1339 gmx_fatal(FARGS,"ncos has to be either 1 or 2");
1342 bSlab = (opt2bSet("-slab",NFILE,fnm) || opt2parg_bSet("-sl",asize(pa),pa) ||
1343 opt2parg_bSet("-axis",asize(pa),pa));
1347 printf("WARNING: Can not determine quadrupoles from energy file\n");
1351 printf("WARNING: Can not determine Gk(r) from energy file\n");
1356 printf("WARNING: Can not calculate Gk and gk, since you did\n"
1357 " not enter a valid dipole for the molecules\n");
1361 ePBC = read_tpx_top(ftp2fn(efTPX,NFILE,fnm),NULL,box,
1362 &natoms,NULL,NULL,NULL,top);
1366 snew(grpindex,ncos);
1367 get_index(&top->atoms,ftp2fn_null(efNDX,NFILE,fnm),
1368 ncos,gnx,grpindex,grpname);
1369 for(k=0; (k<ncos); k++) {
1370 dipole_atom2molindex(&gnx[k],grpindex[k],&(top->mols));
1371 neutralize_mols(gnx[k],grpindex[k],&(top->mols),top->atoms.atom);
1375 do_dip(top,ePBC,det(box),ftp2fn(efTRX,NFILE,fnm),
1376 opt2fn("-o",NFILE,fnm),opt2fn("-eps",NFILE,fnm),
1377 opt2fn("-a",NFILE,fnm),opt2fn("-d",NFILE,fnm),
1378 opt2fn_null("-cos",NFILE,fnm),
1379 opt2fn_null("-dip3d",NFILE,fnm),opt2fn_null("-adip",NFILE,fnm),
1381 opt2fn("-c",NFILE,fnm),
1382 bGkr, opt2fn("-g",NFILE,fnm),
1383 bPhi, &nlevels, ndegrees,
1385 opt2fn("-cmap",NFILE,fnm),rcmax,
1386 bQuad, opt2fn("-q",NFILE,fnm),
1387 bMU, opt2fn("-en",NFILE,fnm),
1388 gnx,grpindex,mu_max,mu_aver,epsilonRF,temp,nFF,skip,
1389 bSlab,nslices,axtitle,opt2fn("-slab",NFILE,fnm),oenv);
1391 do_view(oenv,opt2fn("-o",NFILE,fnm),"-autoscale xy -nxy");
1392 do_view(oenv,opt2fn("-eps",NFILE,fnm),"-autoscale xy -nxy");
1393 do_view(oenv,opt2fn("-a",NFILE,fnm),"-autoscale xy -nxy");
1394 do_view(oenv,opt2fn("-d",NFILE,fnm),"-autoscale xy");
1395 do_view(oenv,opt2fn("-c",NFILE,fnm),"-autoscale xy");