2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2008,2009,2010,2011,2012,2013,2014,2015,2017,2018,2019, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
40 #include "gromacs/commandline/pargs.h"
41 #include "gromacs/fileio/confio.h"
42 #include "gromacs/fileio/trxio.h"
43 #include "gromacs/fileio/xvgr.h"
44 #include "gromacs/gmxana/gmx_ana.h"
45 #include "gromacs/math/units.h"
46 #include "gromacs/math/vec.h"
47 #include "gromacs/pbcutil/pbc.h"
48 #include "gromacs/pbcutil/rmpbc.h"
49 #include "gromacs/statistics/statistics.h"
50 #include "gromacs/topology/index.h"
51 #include "gromacs/topology/topology.h"
52 #include "gromacs/trajectory/trajectoryframe.h"
53 #include "gromacs/utility/arraysize.h"
54 #include "gromacs/utility/cstringutil.h"
55 #include "gromacs/utility/fatalerror.h"
56 #include "gromacs/utility/gmxassert.h"
57 #include "gromacs/utility/smalloc.h"
59 #define EPSI0 (EPSILON0 * E_CHARGE * E_CHARGE * AVOGADRO / (KILO * NANO)) /* EPSILON0 in SI units */
61 static void index_atom2mol(int* n, int* index, t_block* mols)
63 int nat, i, nmol, mol, j;
71 while (index[i] > mols->index[mol])
76 gmx_fatal(FARGS, "Atom index out of range: %d", index[i] + 1);
79 for (j = mols->index[mol]; j < mols->index[mol + 1]; j++)
81 if (i >= nat || index[i] != j)
83 gmx_fatal(FARGS, "The index group does not consist of whole molecules");
90 fprintf(stderr, "\nSplit group of %d atoms into %d molecules\n", nat, nmol);
95 static gmx_bool precalc(t_topology top, real mass2[], real qmol[])
109 for (i = 0; i < top.mols.nr; i++)
111 k = top.mols.index[i];
112 l = top.mols.index[i + 1];
116 for (j = k; j < l; j++)
118 mtot += top.atoms.atom[j].m;
119 qtot += top.atoms.atom[j].q;
122 for (j = k; j < l; j++)
124 top.atoms.atom[j].q -= top.atoms.atom[j].m * qtot / mtot;
125 mass2[j] = top.atoms.atom[j].m / mtot;
142 if (std::abs(qall) > 0.01)
144 printf("\n\nSystem not neutral (q=%f) will not calculate translational part of the dipole "
157 static void remove_jump(matrix box, int natoms, rvec xp[], rvec x[])
163 for (d = 0; d < DIM; d++)
165 hbox[d] = 0.5 * box[d][d];
167 for (i = 0; i < natoms; i++)
169 for (m = DIM - 1; m >= 0; m--)
171 while (x[i][m] - xp[i][m] <= -hbox[m])
173 for (d = 0; d <= m; d++)
175 x[i][d] += box[m][d];
178 while (x[i][m] - xp[i][m] > hbox[m])
180 for (d = 0; d <= m; d++)
182 x[i][d] -= box[m][d];
189 static void calc_mj(t_topology top,
208 calc_box_center(ecenterRECT, box, center);
212 set_pbc(&pbc, ePBC, box);
218 for (i = 0; i < isize; i++)
222 k = top.mols.index[index0[i]];
223 l = top.mols.index[index0[i + 1]];
224 for (j = k; j < l; j++)
226 svmul(mass2[j], fr[j], tmp);
232 svmul(qmol[k], mt1, mt1);
236 pbc_dx(&pbc, mt1, center, mt2);
237 svmul(qmol[k], mt2, mt1);
245 static real calceps(real prefactor, real md2, real mj2, real cor, real eps_rf, gmx_bool bCOR)
248 /* bCOR determines if the correlation is computed via
249 * static properties (FALSE) or the correlation integral (TRUE)
257 epsilon = md2 - 2.0 * cor + mj2;
261 epsilon = md2 + mj2 + 2.0 * cor;
266 epsilon = 1.0 + prefactor * epsilon;
270 epsilon = 2.0 * eps_rf + 1.0 + 2.0 * eps_rf * prefactor * epsilon;
271 epsilon /= 2.0 * eps_rf + 1.0 - prefactor * epsilon;
279 static real calc_cacf(FILE* fcacf, real prefactor, real cacf[], real time[], int nfr, const int vfr[], int ei, int nshift)
286 real sigma_ret = 0.0;
296 rfr = static_cast<real>(nfr + i) / nshift;
299 if (time[vfr[i]] <= time[vfr[ei]])
304 fprintf(fcacf, "%.3f\t%10.6g\t%10.6g\n", time[vfr[i]], cacf[i], sigma);
308 deltat = (time[vfr[i + 1]] - time[vfr[i]]);
310 corint = 2.0 * deltat * cacf[i] * prefactor;
311 if (i == 0 || (i + 1) == nfr)
322 printf("Too less points.\n");
328 static void calc_mjdsp(FILE* fmjdsp, real prefactor, real dsp2[], real time[], int nfr, const real refr[])
333 fprintf(fmjdsp, "#Prefactor fit E-H: 1 / 6.0*V*k_B*T: %g\n", prefactor);
335 for (i = 0; i < nfr; i++)
340 dsp2[i] *= prefactor / refr[i];
341 fprintf(fmjdsp, "%.3f\t%10.6g\n", time[i], dsp2[i]);
347 static void dielectric(FILE* fmj,
373 const gmx_output_env_t* oenv)
376 int valloc, nalloc, nfr, nvfr;
378 real* xshfr = nullptr;
381 real* cacf = nullptr;
382 real* time = nullptr;
385 real prefactorav = 0.0;
386 real prefactor = 0.0;
388 real volume_av = 0.0;
389 real dk_s, dk_d, dk_f;
403 rvec* mjdsp = nullptr;
404 real* dsp2 = nullptr;
409 rvec* mtrans = nullptr;
412 * Variables for the least-squares fit for Einstein-Helfand and Green-Kubo
422 gmx_rmpbc_t gpbc = nullptr;
441 /* This is the main loop over frames */
456 gpbc = gmx_rmpbc_init(&top.idef, ePBC, fr.natoms);
466 srenew(time, nalloc);
468 srenew(mjdsp, nalloc);
469 srenew(dsp2, nalloc);
470 srenew(mtrans, nalloc);
471 srenew(xshfr, nalloc);
473 for (i = nfr; i < nalloc; i++)
475 clear_rvec(mjdsp[i]);
477 clear_rvec(mtrans[i]);
482 GMX_RELEASE_ASSERT(time != nullptr, "Memory not allocated correctly - time array is NULL");
489 time[nfr] = fr.time - t0;
491 if (time[nfr] <= bfit)
495 if (time[nfr] <= efit)
505 remove_jump(fr.box, fr.natoms, xp, fr.x);
512 for (i = 0; i < fr.natoms; i++)
514 copy_rvec(fr.x[i], xp[i]);
518 gmx_rmpbc_trxfr(gpbc, &fr);
520 calc_mj(top, ePBC, fr.box, bNoJump, nmols, indexm, fr.x, mtrans[nfr], mass2, qmol);
522 for (i = 0; i < isize; i++)
525 svmul(top.atoms.atom[j].q, fr.x[j], fr.x[j]);
526 rvec_inc(mu[nfr], fr.x[j]);
529 /*if(mod(nfr,nshift)==0){*/
530 if (nfr % nshift == 0)
532 for (j = nfr; j >= 0; j--)
534 rvec_sub(mtrans[nfr], mtrans[j], tmp);
535 dsp2[nfr - j] += norm2(tmp);
536 xshfr[nfr - j] += 1.0;
553 srenew(cacf, valloc);
556 if (time[nfr] <= bvit)
560 if (time[nfr] <= evit)
565 clear_rvec(v0[nvfr]);
574 for (i = 0; i < isize; i++)
577 svmul(mass2[j], fr.v[j], fr.v[j]);
578 svmul(qmol[j], fr.v[j], fr.v[j]);
579 rvec_inc(v0[nvfr], fr.v[j]);
582 fprintf(fcur, "%.3f\t%.6f\t%.6f\t%.6f\n", time[nfr], v0[nfr][XX], v0[nfr][YY], v0[nfr][ZZ]);
585 /*if(mod(nvfr,nshift)==0){*/
586 if (nvfr % nshift == 0)
588 for (j = nvfr; j >= 0; j--)
592 cacf[nvfr - j] += iprod(v0[nvfr], v0[j]);
596 djc[nvfr - j] += iprod(mu[vfr[j]], v0[nvfr]);
605 volume = det(fr.box);
608 rvec_inc(mja_tmp, mtrans[nfr]);
609 mjd += iprod(mu[nfr], mtrans[nfr]);
610 rvec_inc(mdvec, mu[nfr]);
612 mj2 += iprod(mtrans[nfr], mtrans[nfr]);
613 md2 += iprod(mu[nfr], mu[nfr]);
615 fprintf(fmj, "%.3f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\n", time[nfr], mtrans[nfr][XX],
616 mtrans[nfr][YY], mtrans[nfr][ZZ], mj2 / refr, norm(mja_tmp) / refr);
617 fprintf(fmd, "%.3f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\n", time[nfr], mu[nfr][XX],
618 mu[nfr][YY], mu[nfr][ZZ], md2 / refr, norm(mdvec) / refr);
622 } while (read_next_frame(oenv, status, &fr));
624 gmx_rmpbc_done(gpbc);
629 prefactor /= 3.0 * EPSILON0 * volume_av * BOLTZ * temp;
632 prefactorav = E_CHARGE * E_CHARGE;
633 prefactorav /= volume_av * BOLTZMANN * temp * NANO * 6.0;
635 fprintf(stderr, "Prefactor fit E-H: 1 / 6.0*V*k_B*T: %g\n", prefactorav);
637 calc_mjdsp(fmjdsp, prefactorav, dsp2, time, nfr, xshfr);
640 * Now we can average and calculate the correlation functions
648 svmul(1.0 / refr, mdvec, mdvec);
649 svmul(1.0 / refr, mja_tmp, mja_tmp);
651 mdav2 = norm2(mdvec);
653 mjdav = iprod(mdvec, mja_tmp);
656 printf("\n\nAverage translational dipole moment M_J [enm] after %d frames (|M|^2): %f %f %f "
658 nfr, mja_tmp[XX], mja_tmp[YY], mja_tmp[ZZ], mj2);
659 printf("\n\nAverage molecular dipole moment M_D [enm] after %d frames (|M|^2): %f %f %f (%f)\n",
660 nfr, mdvec[XX], mdvec[YY], mdvec[ZZ], md2);
667 printf("\nCalculating M_D - current correlation integral ... \n");
668 corint = calc_cacf(mcor, prefactorav / EPSI0, djc, time, nvfr, vfr, ie, nshift);
674 printf("\nCalculating current autocorrelation ... \n");
675 sgk = calc_cacf(outf, prefactorav / PICO, cacf, time, nvfr, vfr, ie, nshift);
680 snew(xfit, ie - ii + 1);
681 snew(yfit, ie - ii + 1);
683 for (i = ii; i <= ie; i++)
686 xfit[i - ii] = std::log(time[vfr[i]]);
687 rtmp = std::abs(cacf[i]);
688 yfit[i - ii] = std::log(rtmp);
691 lsq_y_ax_b(ie - ii, xfit, yfit, &sigma, &malt, &err, &rest);
693 malt = std::exp(malt);
697 malt *= prefactorav * 2.0e12 / sigma;
706 /* Calculation of the dielectric constant */
708 fprintf(stderr, "\n********************************************\n");
709 dk_s = calceps(prefactor, md2, mj2, mjd, eps_rf, FALSE);
710 fprintf(stderr, "\nAbsolute values:\n epsilon=%f\n", dk_s);
711 fprintf(stderr, " <M_D^2> , <M_J^2>, <(M_J*M_D)^2>: (%f, %f, %f)\n\n", md2, mj2, mjd);
712 fprintf(stderr, "********************************************\n");
715 dk_f = calceps(prefactor, md2 - mdav2, mj2 - mj, mjd - mjdav, eps_rf, FALSE);
716 fprintf(stderr, "\n\nFluctuations:\n epsilon=%f\n\n", dk_f);
717 fprintf(stderr, "\n deltaM_D , deltaM_J, deltaM_JD: (%f, %f, %f)\n", md2 - mdav2, mj2 - mj,
719 fprintf(stderr, "\n********************************************\n");
722 dk_d = calceps(prefactor, md2 - mdav2, mj2 - mj, corint, eps_rf, TRUE);
723 fprintf(stderr, "\nStatic dielectric constant using integral and fluctuations: %f\n", dk_d);
724 fprintf(stderr, "\n < M_JM_D > via integral: %.3f\n", -1.0 * corint);
727 fprintf(stderr, "\n***************************************************");
728 fprintf(stderr, "\n\nAverage volume V=%f nm^3 at T=%f K\n", volume_av, temp);
729 fprintf(stderr, "and corresponding refactor 1.0 / 3.0*V*k_B*T*EPSILON_0: %f \n", prefactor);
732 if (bACF && (ii < nvfr))
734 fprintf(stderr, "Integral and integrated fit to the current acf yields at t=%f:\n", time[vfr[ii]]);
735 fprintf(stderr, "sigma=%8.3f (pure integral: %.3f)\n",
736 sgk - malt * std::pow(time[vfr[ii]], sigma), sgk);
741 fprintf(stderr, "\nStart fit at %f ps (%f).\n", time[bi], bfit);
742 fprintf(stderr, "End fit at %f ps (%f).\n\n", time[ei], efit);
744 snew(xfit, ei - bi + 1);
745 snew(yfit, ei - bi + 1);
747 for (i = bi; i <= ei; i++)
749 xfit[i - bi] = time[i];
750 yfit[i - bi] = dsp2[i];
753 lsq_y_ax_b(ei - bi, xfit, yfit, &sigma, &malt, &err, &rest);
756 dk_d = calceps(prefactor, md2, 0.5 * malt / prefactorav, corint, eps_rf, TRUE);
759 "Einstein-Helfand fit to the MSD of the translational dipole moment yields:\n\n");
760 fprintf(stderr, "sigma=%.4f\n", sigma);
761 fprintf(stderr, "translational fraction of M^2: %.4f\n", 0.5 * malt / prefactorav);
762 fprintf(stderr, "Dielectric constant using EH: %.4f\n", dk_d);
769 fprintf(stderr, "Too few points for a fit.\n");
794 int gmx_current(int argc, char* argv[])
797 static int nshift = 1000;
798 static real temp = 300.0;
799 static real eps_rf = 0.0;
800 static gmx_bool bNoJump = TRUE;
801 static real bfit = 100.0;
802 static real bvit = 0.5;
803 static real efit = 400.0;
804 static real evit = 5.0;
810 "Shift of the frames for averaging the correlation functions and the mean-square "
812 { "-nojump", FALSE, etBOOL, { &bNoJump }, "Removes jumps of atoms across the box." },
817 "Dielectric constant of the surrounding medium. The value zero corresponds to "
818 "infinity (tin-foil boundary conditions)." },
823 "Begin of the fit of the straight line to the MSD of the translational fraction "
824 "of the dipole moment." },
829 "End of the fit of the straight line to the MSD of the translational fraction of "
830 "the dipole moment." },
835 "Begin of the fit of the current autocorrelation function to a*t^b." },
840 "End of the fit of the current autocorrelation function to a*t^b." },
841 { "-temp", FALSE, etREAL, { &temp }, "Temperature for calculating epsilon." }
844 gmx_output_env_t* oenv;
846 char** grpname = nullptr;
849 real* mass2 = nullptr;
852 int* indexm = nullptr;
862 FILE* outf = nullptr;
863 FILE* mcor = nullptr;
866 FILE* fmjdsp = nullptr;
867 FILE* fcur = nullptr;
868 t_filenm fnm[] = { { efTPS, nullptr, nullptr, ffREAD }, /* this is for the topology */
869 { efNDX, nullptr, nullptr, ffOPTRD },
870 { efTRX, "-f", nullptr, ffREAD }, /* and this for the trajectory */
871 { efXVG, "-o", "current", ffWRITE },
872 { efXVG, "-caf", "caf", ffOPTWR },
873 { efXVG, "-dsp", "dsp", ffWRITE },
874 { efXVG, "-md", "md", ffWRITE },
875 { efXVG, "-mj", "mj", ffWRITE },
876 { efXVG, "-mc", "mc", ffOPTWR } };
878 #define NFILE asize(fnm)
881 const char* desc[] = {
882 "[THISMODULE] is a tool for calculating the current autocorrelation function, the "
884 "of the rotational and translational dipole moment of the system, and the resulting static",
885 "dielectric constant. To obtain a reasonable result, the index group has to be neutral.",
886 "Furthermore, the routine is capable of extracting the static conductivity from the "
888 "autocorrelation function, if velocities are given. Additionally, an Einstein-Helfand fit ",
889 "can be used to obtain the static conductivity.",
891 "The flag [TT]-caf[tt] is for the output of the current autocorrelation function and "
892 "[TT]-mc[tt] writes the",
893 "correlation of the rotational and translational part of the dipole moment in the "
895 "file. However, this option is only available for trajectories containing velocities.",
896 "Options [TT]-sh[tt] and [TT]-tr[tt] are responsible for the averaging and integration of "
898 "autocorrelation functions. Since averaging proceeds by shifting the starting point",
899 "through the trajectory, the shift can be modified with [TT]-sh[tt] to enable the choice "
901 "starting points. Towards the end, statistical inaccuracy grows and integrating the",
902 "correlation function only yields reliable values until a certain point, depending on",
903 "the number of frames. The option [TT]-tr[tt] controls the region of the integral taken "
905 "for calculating the static dielectric constant.",
907 "Option [TT]-temp[tt] sets the temperature required for the computation of the static "
908 "dielectric constant.",
910 "Option [TT]-eps[tt] controls the dielectric constant of the surrounding medium for "
912 "a Reaction Field or dipole corrections of the Ewald summation ([TT]-eps[tt]\\=0 "
914 "tin-foil boundary conditions).",
916 "[TT]-[no]nojump[tt] unfolds the coordinates to allow free diffusion. This is required to "
918 "translational dipole moment, required for the Einstein-Helfand fit. The results from the "
920 "the determination of the dielectric constant for system of charged molecules. However, it "
921 "is also possible to extract",
922 "the dielectric constant from the fluctuations of the total dipole moment in folded "
923 "coordinates. But this",
924 "option has to be used with care, since only very short time spans fulfill the "
925 "approximation that the density",
926 "of the molecules is approximately constant and the averages are already converged. To be "
928 "the dielectric constant should be calculated with the help of the Einstein-Helfand method "
930 "the translational part of the dielectric constant."
934 /* At first the arguments will be parsed and the system information processed */
935 if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW, NFILE, fnm, asize(pa), pa,
936 asize(desc), desc, 0, nullptr, &oenv))
941 bACF = opt2bSet("-caf", NFILE, fnm);
942 bINT = opt2bSet("-mc", NFILE, fnm);
944 read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, nullptr, nullptr, box, TRUE);
946 indexfn = ftp2fn_null(efNDX, NFILE, fnm);
949 get_index(&(top.atoms), indexfn, 1, &isize, &index0, grpname);
951 flags = flags | TRX_READ_X | TRX_READ_V;
953 read_first_frame(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &fr, flags);
955 snew(mass2, top.atoms.nr);
956 snew(qmol, top.atoms.nr);
958 precalc(top, mass2, qmol);
963 for (i = 0; i < isize; i++)
965 indexm[i] = index0[i];
971 index_atom2mol(&nmols, indexm, &top.mols);
977 outf = xvgropen(opt2fn("-caf", NFILE, fnm), "Current autocorrelation function",
978 output_env_get_xvgr_tlabel(oenv), "ACF (e nm/ps)\\S2", oenv);
979 fprintf(outf, "# time\t acf\t average \t std.dev\n");
981 fcur = xvgropen(opt2fn("-o", NFILE, fnm), "Current", output_env_get_xvgr_tlabel(oenv),
982 "J(t) (e nm/ps)", oenv);
983 fprintf(fcur, "# time\t Jx\t Jy \t J_z \n");
986 mcor = xvgropen(opt2fn("-mc", NFILE, fnm),
987 "M\\sD\\N - current autocorrelation function",
988 output_env_get_xvgr_tlabel(oenv),
989 "< M\\sD\\N (0)\\c7\\CJ(t) > (e nm/ps)\\S2", oenv);
990 fprintf(mcor, "# time\t M_D(0) J(t) acf \t Integral acf\n");
994 fmj = xvgropen(opt2fn("-mj", NFILE, fnm), "Averaged translational part of M",
995 output_env_get_xvgr_tlabel(oenv), "< M\\sJ\\N > (enm)", oenv);
996 fprintf(fmj, "# time\t x\t y \t z \t average of M_J^2 \t std.dev\n");
997 fmd = xvgropen(opt2fn("-md", NFILE, fnm), "Averaged rotational part of M",
998 output_env_get_xvgr_tlabel(oenv), "< M\\sD\\N > (enm)", oenv);
999 fprintf(fmd, "# time\t x\t y \t z \t average of M_D^2 \t std.dev\n");
1002 opt2fn("-dsp", NFILE, fnm), "MSD of the squared translational dipole moment M",
1003 output_env_get_xvgr_tlabel(oenv),
1004 "<|M\\sJ\\N(t)-M\\sJ\\N(0)|\\S2\\N > / 6.0*V*k\\sB\\N*T / Sm\\S-1\\Nps\\S-1\\N", oenv);
1007 /* System information is read and prepared, dielectric() processes the frames
1008 * and calculates the requested quantities */
1010 dielectric(fmj, fmd, outf, fcur, mcor, fmjdsp, bNoJump, bACF, bINT, ePBC, top, fr, temp, bfit, efit,
1011 bvit, evit, status, isize, nmols, nshift, index0, indexm, mass2, qmol, eps_rf, oenv);