3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
10 * Copyright (c) 1991-2001
11 * BIOSON Research Institute, Dept. of Biophysical Chemistry
12 * University of Groningen, The Netherlands
14 * 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 * Do check out http://www.gromacs.org , or mail us at gromacs@gromacs.org .
33 * Gyas ROwers Mature At Cryogenic Speed
35 * finished FD 09/07/08
51 #include "gmx_statistics.h"
54 #define SQR(x) (pow(x,2.0))
55 #define EPSI0 (EPSILON0*E_CHARGE*E_CHARGE*AVOGADRO/(KILO*NANO)) /* EPSILON0 in SI units */
58 static void index_atom2mol(int *n
,int *index
,t_block
*mols
)
67 while (index
[i
] > mols
->index
[mol
]) {
70 gmx_fatal(FARGS
,"Atom index out of range: %d",index
[i
]+1);
72 for(j
=mols
->index
[mol
]; j
<mols
->index
[mol
+1]; j
++) {
73 if (i
>= nat
|| index
[i
] != j
)
74 gmx_fatal(FARGS
,"The index group does not consist of whole molecules");
80 fprintf(stderr
,"\nSplit group of %d atoms into %d molecules\n",nat
,nmol
);
85 static bool precalc(t_topology top
,real mass2
[],real qmol
[]){
99 for(i
=0;i
<top
.mols
.nr
;i
++){
101 l
=top
.mols
.index
[i
+1];
106 mtot
+=top
.atoms
.atom
[j
].m
;
107 qtot
+=top
.atoms
.atom
[j
].q
;
111 top
.atoms
.atom
[j
].q
-=top
.atoms
.atom
[j
].m
*qtot
/mtot
;
112 mass2
[j
]=top
.atoms
.atom
[j
].m
/mtot
;
126 printf("\n\nSystem not neutral (q=%f) will not calculate translational part of the dipole moment.\n",qall
);
135 static void remove_jump(matrix box
,int natoms
,rvec xp
[],rvec x
[]){
141 hbox
[d
] = 0.5*box
[d
][d
];
142 for(i
=0; i
<natoms
; i
++)
143 for(m
=DIM
-1; m
>=0; m
--) {
144 while (x
[i
][m
]-xp
[i
][m
] <= -hbox
[m
])
146 x
[i
][d
] += box
[m
][d
];
147 while (x
[i
][m
]-xp
[i
][m
] > hbox
[m
])
149 x
[i
][d
] -= box
[m
][d
];
153 static void calc_mj(t_topology top
,int ePBC
,matrix box
,bool bNoJump
,int isize
,int index0
[],\
154 rvec fr
[],rvec mj
,real mass2
[],real qmol
[]){
163 calc_box_center(ecenterRECT
,box
,center
);
166 set_pbc(&pbc
,ePBC
,box
);
171 for(i
=0;i
<isize
;i
++){
174 k
=top
.mols
.index
[index0
[i
]];
175 l
=top
.mols
.index
[index0
[i
+1]];
177 svmul(mass2
[j
],fr
[j
],tmp
);
182 svmul(qmol
[k
],mt1
,mt1
);
184 pbc_dx(&pbc
,mt1
,center
,mt2
);
185 svmul(qmol
[k
],mt2
,mt1
);
195 static real
calceps(real prefactor
,real md2
,real mj2
,real cor
,real eps_rf
,bool bCOR
){
197 /* bCOR determines if the correlation is computed via
198 * static properties (FALSE) or the correlation integral (TRUE)
204 if (bCOR
) epsilon
=md2
-2.0*cor
+mj2
;
205 else epsilon
=md2
+mj2
+2.0*cor
;
208 epsilon
=1.0+prefactor
*epsilon
;
212 epsilon
=2.0*eps_rf
+1.0+2.0*eps_rf
*prefactor
*epsilon
;
213 epsilon
/=2.0*eps_rf
+1.0-prefactor
*epsilon
;
222 static real
calc_cacf(FILE *fcacf
,real prefactor
,real cacf
[],real time
[],int nfr
,int vfr
[],int ei
,int nshift
){
237 rfr
=(real
) (nfr
/nshift
-i
/nshift
);
240 if(time
[vfr
[i
]]<=time
[vfr
[ei
]])
243 fprintf(fcacf
,"%.3f\t%10.6g\t%10.6g\n",time
[vfr
[i
]],cacf
[i
],sigma
);
246 deltat
=(time
[vfr
[i
+1]]-time
[vfr
[i
]]);
248 corint
=2.0*deltat
*cacf
[i
]*prefactor
;
249 if(i
==0 || (i
+1)==nfr
)
257 printf("Too less points.\n");
263 static void calc_mjdsp(FILE *fmjdsp
,real vol
,real temp
,real prefactor
,rvec mjdsp
[],real dsp2
[],real time
[],int nfr
,real refr
[]){
270 fprintf(fmjdsp
,"#Prefactor fit E-H: 1 / 6.0*V*k_B*T: %g\n",prefactor
);
277 dsp2
[i
]*=prefactor
/refr
[i
];
278 fprintf(fmjdsp
,"%.3f\t%10.6g\n",time
[i
],dsp2
[i
]);
287 static void dielectric(FILE *fmj
,FILE *fmd
,FILE *outf
,FILE *fcur
,FILE *mcor
,
288 FILE *fmjdsp
, bool bNoJump
,bool bACF
,bool bINT
,
289 int ePBC
,t_topology top
, t_trxframe fr
,real temp
,
290 real trust
,real bfit
,real efit
,real bvit
,real evit
,
291 t_trxstatus
*status
,int isize
,int nmols
, int nshift
,
292 atom_id
*index0
,int indexm
[],real mass2
[],
293 real qmol
[], real eps_rf
, const output_env_t oenv
)
296 int valloc
,nalloc
,nfr
,nvfr
,m
,itrust
=0;
306 real prefactorav
=0.0;
337 * Variables for the least-squares fit for Einstein-Helfand and Green-Kubo
347 gmx_rmpbc_t gpbc
=NULL
;
366 /* This is the main loop over frames */
381 gpbc
= gmx_rmpbc_init(&top
.idef
,ePBC
,fr
.natoms
,fr
.box
);
391 srenew(mjdsp
,nalloc
);
393 srenew(mtrans
,nalloc
);
394 srenew(xshfr
,nalloc
);
396 for(i
=nfr
;i
<nalloc
;i
++){
397 clear_rvec(mjdsp
[i
]);
399 clear_rvec(mtrans
[i
]);
412 time
[nfr
]=fr
.time
-t0
;
422 remove_jump(fr
.box
,fr
.natoms
,xp
,fr
.x
);
426 for(i
=0; i
<fr
.natoms
;i
++)
427 copy_rvec(fr
.x
[i
],xp
[i
]);
431 gmx_rmpbc_trxfr(gpbc
,&fr
);
433 calc_mj(top
,ePBC
,fr
.box
,bNoJump
,nmols
,indexm
,fr
.x
,mtrans
[nfr
],mass2
,qmol
);
435 for(i
=0;i
<isize
;i
++){
437 svmul(top
.atoms
.atom
[j
].q
,fr
.x
[j
],fr
.x
[j
]);
438 rvec_inc(mu
[nfr
],fr
.x
[j
]);
441 /*if(mod(nfr,nshift)==0){*/
444 rvec_sub(mtrans
[nfr
],mtrans
[j
],tmp
);
445 dsp2
[nfr
-j
]+=norm2(tmp
);
465 clear_rvec(v0
[nvfr
]);
470 for(i
=0;i
<isize
;i
++){
472 svmul(mass2
[j
],fr
.v
[j
],fr
.v
[j
]);
473 svmul(qmol
[j
],fr
.v
[j
],fr
.v
[j
]);
474 rvec_inc(v0
[nvfr
],fr
.v
[j
]);
477 fprintf(fcur
,"%.3f\t%.6f\t%.6f\t%.6f\n",time
[nfr
],v0
[nfr
][XX
],v0
[nfr
][YY
],v0
[nfr
][ZZ
]);
479 /*if(mod(nvfr,nshift)==0){*/
481 for(j
=nvfr
;j
>=0;j
--){
483 cacf
[nvfr
-j
]+=iprod(v0
[nvfr
],v0
[j
]);
485 djc
[nvfr
-j
]+=iprod(mu
[vfr
[j
]],v0
[nvfr
]);
492 volume
= det(fr
.box
);
495 rvec_inc(mja_tmp
,mtrans
[nfr
]);
496 mjd
+=iprod(mu
[nfr
],mtrans
[nfr
]);
497 rvec_inc(mdvec
,mu
[nfr
]);
499 mj2
+=iprod(mtrans
[nfr
],mtrans
[nfr
]);
500 md2
+=iprod(mu
[nfr
],mu
[nfr
]);
502 fprintf(fmj
,"%.3f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\n",time
[nfr
],mtrans
[nfr
][XX
],mtrans
[nfr
][YY
],mtrans
[nfr
][ZZ
],mj2
/refr
,norm(mja_tmp
)/refr
);
503 fprintf(fmd
,"%.3f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\n", \
504 time
[nfr
],mu
[nfr
][XX
],mu
[nfr
][YY
],mu
[nfr
][ZZ
],md2
/refr
,norm(mdvec
)/refr
);
508 }while(read_next_frame(oenv
,status
,&fr
));
510 gmx_rmpbc_done(gpbc
);
515 prefactor
/=3.0*EPSILON0
*volume_av
*BOLTZ
*temp
;
518 prefactorav
=E_CHARGE
*E_CHARGE
;
519 prefactorav
/=volume_av
*BOLTZMANN
*temp
*NANO
*6.0;
521 fprintf(stderr
,"Prefactor fit E-H: 1 / 6.0*V*k_B*T: %g\n",prefactorav
);
523 calc_mjdsp(fmjdsp
,volume_av
,temp
,prefactorav
,mjdsp
,dsp2
,time
,nfr
,xshfr
);
526 * Now we can average and calculate the correlation functions
534 svmul(1.0/refr
,mdvec
,mdvec
);
535 svmul(1.0/refr
,mja_tmp
,mja_tmp
);
539 mjdav
=iprod(mdvec
,mja_tmp
);
542 printf("\n\nAverage translational dipole moment M_J [enm] after %d frames (|M|^2): %f %f %f (%f)\n",nfr
,mja_tmp
[XX
],mja_tmp
[YY
],mja_tmp
[ZZ
],mj2
);
543 printf("\n\nAverage molecular dipole moment M_D [enm] after %d frames (|M|^2): %f %f %f (%f)\n",nfr
,mdvec
[XX
],mdvec
[YY
],mdvec
[ZZ
],md2
);
551 printf("\nCalculating M_D - current correlation integral ... \n");
552 corint
=calc_cacf(mcor
,prefactorav
/EPSI0
,djc
,time
,nvfr
,vfr
,ie
,nshift
);
558 printf("\nCalculating current autocorrelation ... \n");
559 sgk
=calc_cacf(outf
,prefactorav
/PICO
,cacf
,time
,nvfr
,vfr
,ie
,nshift
);
568 xfit
[i
-ii
]=log(time
[vfr
[i
]]);
570 yfit
[i
-ii
]=log(rtmp
);
574 lsq_y_ax_b(ie
-ii
,xfit
,yfit
,&sigma
,&malt
,&err
,&rest
);
580 malt
*=prefactorav
*2.0e12
/sigma
;
590 /* Calculation of the dielectric constant */
592 fprintf(stderr
,"\n********************************************\n");
593 dk_s
=calceps(prefactor
,md2
,mj2
,mjd
,eps_rf
,FALSE
);
594 fprintf(stderr
,"\nAbsolute values:\n epsilon=%f\n",dk_s
);
595 fprintf(stderr
," <M_D^2> , <M_J^2>, <(M_J*M_D)^2>: (%f, %f, %f)\n\n",md2
,mj2
,mjd
);
596 fprintf(stderr
,"********************************************\n");
599 dk_f
=calceps(prefactor
,md2
-mdav2
,mj2
-mj
,mjd
-mjdav
,eps_rf
,FALSE
);
600 fprintf(stderr
,"\n\nFluctuations:\n epsilon=%f\n\n",dk_f
);
601 fprintf(stderr
,"\n deltaM_D , deltaM_J, deltaM_JD: (%f, %f, %f)\n",md2
-mdav2
,mj2
-mj
,mjd
-mjdav
);
602 fprintf(stderr
,"\n********************************************\n");
604 dk_d
=calceps(prefactor
,md2
-mdav2
,mj2
-mj
,corint
,eps_rf
,TRUE
);
605 fprintf(stderr
,"\nStatic dielectric constant using integral and fluctuations: %f\n",dk_d
);
606 fprintf(stderr
,"\n < M_JM_D > via integral: %.3f\n",-1.0*corint
);
609 fprintf(stderr
,"\n***************************************************");
610 fprintf(stderr
,"\n\nAverage volume V=%f nm^3 at T=%f K\n",volume_av
,temp
);
611 fprintf(stderr
,"and corresponding refactor 1.0 / 3.0*V*k_B*T*EPSILON_0: %f \n",prefactor
);
616 fprintf(stderr
,"Integral and integrated fit to the current acf yields at t=%f:\n",time
[vfr
[ii
]]);
617 fprintf(stderr
,"sigma=%8.3f (pure integral: %.3f)\n",sgk
-malt
*pow(time
[vfr
[ii
]],sigma
),sgk
);
621 fprintf(stderr
,"\nStart fit at %f ps (%f).\n",time
[bi
],bfit
);
622 fprintf(stderr
,"End fit at %f ps (%f).\n\n",time
[ei
],efit
);
632 lsq_y_ax_b(ei
-bi
,xfit
,yfit
,&sigma
,&malt
,&err
,&rest
);
635 dk_d
=calceps(prefactor
,md2
,0.5*malt
/prefactorav
,corint
,eps_rf
,TRUE
);
637 fprintf(stderr
,"Einstein-Helfand fit to the MSD of the translational dipole moment yields:\n\n");
638 fprintf(stderr
,"sigma=%.4f\n",sigma
);
639 fprintf(stderr
,"translational fraction of M^2: %.4f\n",0.5*malt
/prefactorav
);
640 fprintf(stderr
,"Dielectric constant using EH: %.4f\n",dk_d
);
646 fprintf(stderr
,"Too less points for a fit.\n");
666 int gmx_current(int argc
,char *argv
[])
669 static int nshift
=1000;
670 static real temp
=300.0;
671 static real trust
=0.25;
672 static real eps_rf
=0.0;
673 static bool bNoJump
=TRUE
;
674 static real bfit
=100.0;
675 static real bvit
=0.5;
676 static real efit
=400.0;
677 static real evit
=5.0;
679 { "-sh", FALSE
, etINT
, {&nshift
},
680 "Shift of the frames for averaging the correlation functions and the mean-square displacement."},
681 { "-nojump", FALSE
, etBOOL
, {&bNoJump
},
682 "Removes jumps of atoms across the box."},
683 { "-eps", FALSE
, etREAL
, {&eps_rf
},
684 "Dielectric constant of the surrounding medium. eps=0.0 corresponds to eps=infinity (thinfoil boundary conditions)."},
685 { "-bfit", FALSE
, etREAL
, {&bfit
},
686 "Begin of the fit of the straight line to the MSD of the translational fraction of the dipole moment."},
687 { "-efit", FALSE
, etREAL
, {&efit
},
688 "End of the fit of the straight line to the MSD of the translational fraction of the dipole moment."},
689 { "-bvit", FALSE
, etREAL
, {&bvit
},
690 "Begin of the fit of the current autocorrelation function to a*t^b."},
691 { "-evit", FALSE
, etREAL
, {&evit
},
692 "End of the fit of the current autocorrelation function to a*t^b."},
693 { "-tr", FALSE
, etREAL
, {&trust
},
694 "Fraction of the trajectory taken into account for the integral."},
695 { "-temp", FALSE
, etREAL
, {&temp
},
696 "Temperature for calculating epsilon."
709 atom_id
*index0
=NULL
;
735 { efTPS
, NULL
, NULL
, ffREAD
}, /* this is for the topology */
736 { efNDX
, NULL
, NULL
, ffOPTRD
},
737 { efTRX
, "-f", NULL
, ffREAD
}, /* and this for the trajectory */
738 { efXVG
, "-o", "current.xvg", ffWRITE
},
739 { efXVG
, "-caf", "caf.xvg", ffOPTWR
},
740 { efXVG
, "-dsp", "dsp.xvg", ffWRITE
},
741 { efXVG
, "-md", "md.xvg", ffWRITE
},
742 { efXVG
, "-mj", "mj.xvg", ffWRITE
},
743 { efXVG
, "-mc", "mc.xvg", ffOPTWR
}
746 #define NFILE asize(fnm)
749 const char *desc
[] = {
750 "This is a tool for calculating the current autocorrelation function, the correlation",
751 "of the rotational and translational dipole moment of the system, and the resulting static",
752 "dielectric constant. To obtain a reasonable result the index group has to be neutral.",
753 "Furthermore the routine is capable of extracting the static conductivity from the current ",
754 "autocorrelation function, if velocities are given. Additionally an Einstein-Helfand fit also",
755 "allows to get the static conductivity."
757 "The flag [TT]-caf[tt] is for the output of the current autocorrelation function and [TT]-mc[tt] writes the",
758 "correlation of the rotational and translational part of the dipole moment in the corresponding",
759 "file. However this option is only available for trajectories containing velocities.",
760 "Options [TT]-sh[tt] and [TT]-tr[tt] are responsible for the averaging and integration of the",
761 "autocorrelation functions. Since averaging proceeds by shifting the starting point",
762 "through the trajectory, the shift can be modified with [TT]-sh[tt] to enable the choice of uncorrelated",
763 "starting points. Towards the end, statistical inaccuracy grows and integrating the",
764 "correlation function only yields reliable values until a certain point, depending on",
765 "the number of frames. The option [TT]-tr[tt] controls the region of the integral taken into account",
766 "for calculating the static dielectric constant.",
768 "Option [TT]-temp[tt] sets the temperature required for the computation of the static dielectric constant.",
770 "Option [TT]-eps[tt] controls the dielectric constant of the surrounding medium for simulations using",
771 "a Reaction Field or dipole corrections of the Ewald summation (eps=0 corresponds to",
772 "tin-foil boundary conditions).",
774 "[TT]-[no]nojump[tt] unfolds the coordinates to allow free diffusion. This is required to get a continuous",
775 "translational dipole moment, required for the Einstein-Helfand fit. The resuls from the fit allow to",
776 "determine the dielectric constant for system of charged molecules. However it is also possible to extract",
777 "the dielectric constant from the fluctuations of the total dipole moment in folded coordinates. But this",
778 "options has to be used with care, since only very short time spans fulfill the approximation, that the density",
779 "of the molecules is approximately constant and the averages are already converged. To be on the safe side,",
780 "the dielectric constant should be calculated with the help of the Einstein-Helfand method for",
781 "the translational part of the dielectric constant."
785 /* At first the arguments will be parsed and the system information processed */
787 CopyRight(stderr
,argv
[0]);
789 parse_common_args(&argc
,argv
,PCA_CAN_TIME
| PCA_CAN_VIEW
,
790 NFILE
,fnm
,asize(pa
),pa
,asize(desc
),desc
,0,NULL
,&oenv
);
792 bACF
= opt2bSet("-caf",NFILE
,fnm
);
793 bINT
= opt2bSet("-mc",NFILE
,fnm
);
795 bTop
=read_tps_conf(ftp2fn(efTPS
,NFILE
,fnm
),title
,&top
,&ePBC
,&xtop
,&vtop
,box
,TRUE
);
801 indexfn
= ftp2fn_null(efNDX
,NFILE
,fnm
);
804 get_index(&(top
.atoms
),indexfn
,1,&isize
,&index0
,grpname
);
806 flags
= flags
| TRX_READ_X
| TRX_READ_V
;
808 read_first_frame(oenv
,&status
,ftp2fn(efTRX
,NFILE
,fnm
),&fr
,flags
);
810 snew(mass2
,top
.atoms
.nr
);
811 snew(qmol
,top
.atoms
.nr
);
813 bNEU
=precalc(top
,mass2
,qmol
);
824 index_atom2mol(&nmols
,indexm
,&top
.mols
);
828 outf
=xvgropen(opt2fn("-caf",NFILE
,fnm
),
829 "Current autocorrelation function",output_env_get_xvgr_tlabel(oenv
),
830 "ACF (e nm/ps)\\S2",oenv
);
831 fprintf(outf
,"# time\t acf\t average \t std.dev\n");
833 fcur
=xvgropen(opt2fn("-o",NFILE
,fnm
),
834 "Current",output_env_get_xvgr_tlabel(oenv
),"J(t) (e nm/ps)",oenv
);
835 fprintf(fcur
,"# time\t Jx\t Jy \t J_z \n");
837 mcor
= xvgropen(opt2fn("-mc",NFILE
,fnm
),
838 "M\\sD\\N - current autocorrelation function",
839 output_env_get_xvgr_tlabel(oenv
),
840 "< M\\sD\\N (0)\\c7\\CJ(t) > (e nm/ps)\\S2",oenv
);
841 fprintf(mcor
,"# time\t M_D(0) J(t) acf \t Integral acf\n");
845 fmj
= xvgropen(opt2fn("-mj",NFILE
,fnm
),
846 "Averaged translational part of M",output_env_get_xvgr_tlabel(oenv
),
847 "< M\\sJ\\N > (enm)",oenv
);
848 fprintf(fmj
,"# time\t x\t y \t z \t average of M_J^2 \t std.dev\n");
849 fmd
= xvgropen(opt2fn("-md",NFILE
,fnm
),
850 "Averaged rotational part of M",output_env_get_xvgr_tlabel(oenv
),
851 "< M\\sD\\N > (enm)",oenv
);
852 fprintf(fmd
,"# time\t x\t y \t z \t average of M_D^2 \t std.dev\n");
854 fmjdsp
= xvgropen(opt2fn("-dsp",NFILE
,fnm
),
855 "MSD of the squared translational dipole moment M",
856 output_env_get_xvgr_tlabel(oenv
),
857 "<|M\\sJ\\N(t)-M\\sJ\\N(0)|\\S2\\N > / 6.0*V*k\\sB\\N*T / Sm\\S-1\\Nps\\S-1\\N",
861 /* System information is read and prepared, dielectric() processes the frames
862 * and calculates the requested quantities */
864 dielectric(fmj
,fmd
,outf
,fcur
,mcor
,fmjdsp
,bNoJump
,bACF
,bINT
,ePBC
,top
,fr
,
865 temp
,trust
, bfit
,efit
,bvit
,evit
,status
,isize
,nmols
,nshift
,
866 index0
,indexm
,mass2
,qmol
,eps_rf
,oenv
);