2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2008,2009,2010,2011,2012 by the GROMACS development team.
5 * Copyright (c) 2013,2014,2015,2017,2018 by the GROMACS development team.
6 * Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
42 #include "gromacs/commandline/pargs.h"
43 #include "gromacs/fileio/confio.h"
44 #include "gromacs/fileio/trxio.h"
45 #include "gromacs/fileio/xvgr.h"
46 #include "gromacs/gmxana/gmx_ana.h"
47 #include "gromacs/math/units.h"
48 #include "gromacs/math/vec.h"
49 #include "gromacs/pbcutil/pbc.h"
50 #include "gromacs/pbcutil/rmpbc.h"
51 #include "gromacs/statistics/statistics.h"
52 #include "gromacs/topology/index.h"
53 #include "gromacs/topology/topology.h"
54 #include "gromacs/trajectory/trajectoryframe.h"
55 #include "gromacs/utility/arraysize.h"
56 #include "gromacs/utility/cstringutil.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/gmxassert.h"
59 #include "gromacs/utility/smalloc.h"
61 constexpr double EPSI0 = (gmx::c_epsilon0 * gmx::c_electronCharge * gmx::c_electronCharge * gmx::c_avogadro
62 / (gmx::c_kilo * gmx::c_nano)); /* c_epsilon0 in SI units */
64 static void index_atom2mol(int* n, int* index, t_block* mols)
66 int nat, i, nmol, mol, j;
74 while (index[i] > mols->index[mol])
79 gmx_fatal(FARGS, "Atom index out of range: %d", index[i] + 1);
82 for (j = mols->index[mol]; j < mols->index[mol + 1]; j++)
84 if (i >= nat || index[i] != j)
86 gmx_fatal(FARGS, "The index group does not consist of whole molecules");
93 fprintf(stderr, "\nSplit group of %d atoms into %d molecules\n", nat, nmol);
98 static gmx_bool precalc(t_topology top, real mass2[], real qmol[])
112 for (i = 0; i < top.mols.nr; i++)
114 k = top.mols.index[i];
115 l = top.mols.index[i + 1];
119 for (j = k; j < l; j++)
121 mtot += top.atoms.atom[j].m;
122 qtot += top.atoms.atom[j].q;
125 for (j = k; j < l; j++)
127 top.atoms.atom[j].q -= top.atoms.atom[j].m * qtot / mtot;
128 mass2[j] = top.atoms.atom[j].m / mtot;
145 if (std::abs(qall) > 0.01)
147 printf("\n\nSystem not neutral (q=%f) will not calculate translational part of the dipole "
160 static void remove_jump(matrix box, int natoms, rvec xp[], rvec x[])
166 for (d = 0; d < DIM; d++)
168 hbox[d] = 0.5 * box[d][d];
170 for (i = 0; i < natoms; i++)
172 for (m = DIM - 1; m >= 0; m--)
174 while (x[i][m] - xp[i][m] <= -hbox[m])
176 for (d = 0; d <= m; d++)
178 x[i][d] += box[m][d];
181 while (x[i][m] - xp[i][m] > hbox[m])
183 for (d = 0; d <= m; d++)
185 x[i][d] -= box[m][d];
192 static void calc_mj(t_topology top,
211 calc_box_center(ecenterRECT, box, center);
215 set_pbc(&pbc, pbcType, box);
221 for (i = 0; i < isize; i++)
225 k = top.mols.index[index0[i]];
226 l = top.mols.index[index0[i + 1]];
227 for (j = k; j < l; j++)
229 svmul(mass2[j], fr[j], tmp);
235 svmul(qmol[k], mt1, mt1);
239 pbc_dx(&pbc, mt1, center, mt2);
240 svmul(qmol[k], mt2, mt1);
248 static real calceps(real prefactor, real md2, real mj2, real cor, real eps_rf, gmx_bool bCOR)
251 /* bCOR determines if the correlation is computed via
252 * static properties (FALSE) or the correlation integral (TRUE)
260 epsilon = md2 - 2.0 * cor + mj2;
264 epsilon = md2 + mj2 + 2.0 * cor;
269 epsilon = 1.0 + prefactor * epsilon;
273 epsilon = 2.0 * eps_rf + 1.0 + 2.0 * eps_rf * prefactor * epsilon;
274 epsilon /= 2.0 * eps_rf + 1.0 - prefactor * epsilon;
282 static real calc_cacf(FILE* fcacf, real prefactor, real cacf[], real time[], int nfr, const int vfr[], int ei, int nshift)
289 real sigma_ret = 0.0;
299 rfr = static_cast<real>(nfr + i) / nshift;
302 if (time[vfr[i]] <= time[vfr[ei]])
307 fprintf(fcacf, "%.3f\t%10.6g\t%10.6g\n", time[vfr[i]], cacf[i], sigma);
311 deltat = (time[vfr[i + 1]] - time[vfr[i]]);
313 corint = 2.0 * deltat * cacf[i] * prefactor;
314 if (i == 0 || (i + 1) == nfr)
325 printf("Too less points.\n");
331 static void calc_mjdsp(FILE* fmjdsp, real prefactor, real dsp2[], real time[], int nfr, const real refr[])
336 fprintf(fmjdsp, "#Prefactor fit E-H: 1 / 6.0*V*k_B*T: %g\n", prefactor);
338 for (i = 0; i < nfr; i++)
343 dsp2[i] *= prefactor / refr[i];
344 fprintf(fmjdsp, "%.3f\t%10.6g\n", time[i], dsp2[i]);
350 static void dielectric(FILE* fmj,
376 const gmx_output_env_t* oenv)
379 int valloc, nalloc, nfr, nvfr;
381 real* xshfr = nullptr;
384 real* cacf = nullptr;
385 real* time = nullptr;
388 real prefactorav = 0.0;
389 real prefactor = 0.0;
391 real volume_av = 0.0;
392 real dk_s, dk_d, dk_f;
406 rvec* mjdsp = nullptr;
407 real* dsp2 = nullptr;
412 rvec* mtrans = nullptr;
415 * Variables for the least-squares fit for Einstein-Helfand and Green-Kubo
425 gmx_rmpbc_t gpbc = nullptr;
444 /* This is the main loop over frames */
459 gpbc = gmx_rmpbc_init(&top.idef, pbcType, fr.natoms);
469 srenew(time, nalloc);
471 srenew(mjdsp, nalloc);
472 srenew(dsp2, nalloc);
473 srenew(mtrans, nalloc);
474 srenew(xshfr, nalloc);
476 for (i = nfr; i < nalloc; i++)
478 clear_rvec(mjdsp[i]);
480 clear_rvec(mtrans[i]);
485 GMX_RELEASE_ASSERT(time != nullptr, "Memory not allocated correctly - time array is NULL");
492 time[nfr] = fr.time - t0;
494 if (time[nfr] <= bfit)
498 if (time[nfr] <= efit)
508 remove_jump(fr.box, fr.natoms, xp, fr.x);
515 for (i = 0; i < fr.natoms; i++)
517 copy_rvec(fr.x[i], xp[i]);
521 gmx_rmpbc_trxfr(gpbc, &fr);
523 calc_mj(top, pbcType, fr.box, bNoJump, nmols, indexm, fr.x, mtrans[nfr], mass2, qmol);
525 for (i = 0; i < isize; i++)
528 svmul(top.atoms.atom[j].q, fr.x[j], fr.x[j]);
529 rvec_inc(mu[nfr], fr.x[j]);
532 /*if(mod(nfr,nshift)==0){*/
533 if (nfr % nshift == 0)
535 for (j = nfr; j >= 0; j--)
537 rvec_sub(mtrans[nfr], mtrans[j], tmp);
538 dsp2[nfr - j] += norm2(tmp);
539 xshfr[nfr - j] += 1.0;
556 srenew(cacf, valloc);
559 if (time[nfr] <= bvit)
563 if (time[nfr] <= evit)
568 clear_rvec(v0[nvfr]);
577 for (i = 0; i < isize; i++)
580 svmul(mass2[j], fr.v[j], fr.v[j]);
581 svmul(qmol[j], fr.v[j], fr.v[j]);
582 rvec_inc(v0[nvfr], fr.v[j]);
585 fprintf(fcur, "%.3f\t%.6f\t%.6f\t%.6f\n", time[nfr], v0[nfr][XX], v0[nfr][YY], v0[nfr][ZZ]);
588 /*if(mod(nvfr,nshift)==0){*/
589 if (nvfr % nshift == 0)
591 for (j = nvfr; j >= 0; j--)
595 cacf[nvfr - j] += iprod(v0[nvfr], v0[j]);
599 djc[nvfr - j] += iprod(mu[vfr[j]], v0[nvfr]);
608 volume = det(fr.box);
611 rvec_inc(mja_tmp, mtrans[nfr]);
612 mjd += iprod(mu[nfr], mtrans[nfr]);
613 rvec_inc(mdvec, mu[nfr]);
615 mj2 += iprod(mtrans[nfr], mtrans[nfr]);
616 md2 += iprod(mu[nfr], mu[nfr]);
619 "%.3f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\n",
625 norm(mja_tmp) / refr);
627 "%.3f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\t%8.5f\n",
637 } while (read_next_frame(oenv, status, &fr));
639 gmx_rmpbc_done(gpbc);
644 prefactor /= 3.0 * gmx::c_epsilon0 * volume_av * gmx::c_boltz * temp;
647 prefactorav = gmx::c_electronCharge * gmx::c_electronCharge;
648 prefactorav /= volume_av * gmx::c_boltzmann * temp * gmx::c_nano * 6.0;
650 fprintf(stderr, "Prefactor fit E-H: 1 / 6.0*V*k_B*T: %g\n", prefactorav);
652 calc_mjdsp(fmjdsp, prefactorav, dsp2, time, nfr, xshfr);
655 * Now we can average and calculate the correlation functions
663 svmul(1.0 / refr, mdvec, mdvec);
664 svmul(1.0 / refr, mja_tmp, mja_tmp);
666 mdav2 = norm2(mdvec);
668 mjdav = iprod(mdvec, mja_tmp);
671 printf("\n\nAverage translational dipole moment M_J [enm] after %d frames (|M|^2): %f %f %f "
678 printf("\n\nAverage molecular dipole moment M_D [enm] after %d frames (|M|^2): %f %f %f (%f)\n",
690 printf("\nCalculating M_D - current correlation integral ... \n");
691 corint = calc_cacf(mcor, prefactorav / EPSI0, djc, time, nvfr, vfr, ie, nshift);
697 printf("\nCalculating current autocorrelation ... \n");
698 sgk = calc_cacf(outf, prefactorav / gmx::c_pico, cacf, time, nvfr, vfr, ie, nshift);
703 snew(xfit, ie - ii + 1);
704 snew(yfit, ie - ii + 1);
706 for (i = ii; i <= ie; i++)
709 xfit[i - ii] = std::log(time[vfr[i]]);
710 rtmp = std::abs(cacf[i]);
711 yfit[i - ii] = std::log(rtmp);
714 lsq_y_ax_b(ie - ii, xfit, yfit, &sigma, &malt, &err, &rest);
716 malt = std::exp(malt);
720 malt *= prefactorav * 2.0e12 / sigma;
729 /* Calculation of the dielectric constant */
731 fprintf(stderr, "\n********************************************\n");
732 dk_s = calceps(prefactor, md2, mj2, mjd, eps_rf, FALSE);
733 fprintf(stderr, "\nAbsolute values:\n epsilon=%f\n", dk_s);
734 fprintf(stderr, " <M_D^2> , <M_J^2>, <(M_J*M_D)^2>: (%f, %f, %f)\n\n", md2, mj2, mjd);
735 fprintf(stderr, "********************************************\n");
738 dk_f = calceps(prefactor, md2 - mdav2, mj2 - mj, mjd - mjdav, eps_rf, FALSE);
739 fprintf(stderr, "\n\nFluctuations:\n epsilon=%f\n\n", dk_f);
740 fprintf(stderr, "\n deltaM_D , deltaM_J, deltaM_JD: (%f, %f, %f)\n", md2 - mdav2, mj2 - mj, mjd - mjdav);
741 fprintf(stderr, "\n********************************************\n");
744 dk_d = calceps(prefactor, md2 - mdav2, mj2 - mj, corint, eps_rf, TRUE);
745 fprintf(stderr, "\nStatic dielectric constant using integral and fluctuations: %f\n", dk_d);
746 fprintf(stderr, "\n < M_JM_D > via integral: %.3f\n", -1.0 * corint);
749 fprintf(stderr, "\n***************************************************");
750 fprintf(stderr, "\n\nAverage volume V=%f nm^3 at T=%f K\n", volume_av, temp);
751 fprintf(stderr, "and corresponding refactor 1.0 / 3.0*V*k_B*T*EPSILON_0: %f \n", prefactor);
754 if (bACF && (ii < nvfr))
756 fprintf(stderr, "Integral and integrated fit to the current acf yields at t=%f:\n", time[vfr[ii]]);
757 fprintf(stderr, "sigma=%8.3f (pure integral: %.3f)\n", sgk - malt * std::pow(time[vfr[ii]], sigma), sgk);
762 fprintf(stderr, "\nStart fit at %f ps (%f).\n", time[bi], bfit);
763 fprintf(stderr, "End fit at %f ps (%f).\n\n", time[ei], efit);
765 snew(xfit, ei - bi + 1);
766 snew(yfit, ei - bi + 1);
768 for (i = bi; i <= ei; i++)
770 xfit[i - bi] = time[i];
771 yfit[i - bi] = dsp2[i];
774 lsq_y_ax_b(ei - bi, xfit, yfit, &sigma, &malt, &err, &rest);
777 dk_d = calceps(prefactor, md2, 0.5 * malt / prefactorav, corint, eps_rf, TRUE);
780 "Einstein-Helfand fit to the MSD of the translational dipole moment yields:\n\n");
781 fprintf(stderr, "sigma=%.4f\n", sigma);
782 fprintf(stderr, "translational fraction of M^2: %.4f\n", 0.5 * malt / prefactorav);
783 fprintf(stderr, "Dielectric constant using EH: %.4f\n", dk_d);
790 fprintf(stderr, "Too few points for a fit.\n");
815 int gmx_current(int argc, char* argv[])
818 static int nshift = 1000;
819 static real temp = 300.0;
820 static real eps_rf = 0.0;
821 static gmx_bool bNoJump = TRUE;
822 static real bfit = 100.0;
823 static real bvit = 0.5;
824 static real efit = 400.0;
825 static real evit = 5.0;
831 "Shift of the frames for averaging the correlation functions and the mean-square "
833 { "-nojump", FALSE, etBOOL, { &bNoJump }, "Removes jumps of atoms across the box." },
838 "Dielectric constant of the surrounding medium. The value zero corresponds to "
839 "infinity (tin-foil boundary conditions)." },
844 "Begin of the fit of the straight line to the MSD of the translational fraction "
845 "of the dipole moment." },
850 "End of the fit of the straight line to the MSD of the translational fraction of "
851 "the dipole moment." },
856 "Begin of the fit of the current autocorrelation function to a*t^b." },
861 "End of the fit of the current autocorrelation function to a*t^b." },
862 { "-temp", FALSE, etREAL, { &temp }, "Temperature for calculating epsilon." }
865 gmx_output_env_t* oenv;
867 char** grpname = nullptr;
870 real* mass2 = nullptr;
873 int* indexm = nullptr;
879 PbcType pbcType = PbcType::Unset;
883 FILE* outf = nullptr;
884 FILE* mcor = nullptr;
887 FILE* fmjdsp = nullptr;
888 FILE* fcur = nullptr;
889 t_filenm fnm[] = { { efTPS, nullptr, nullptr, ffREAD }, /* this is for the topology */
890 { efNDX, nullptr, nullptr, ffOPTRD },
891 { efTRX, "-f", nullptr, ffREAD }, /* and this for the trajectory */
892 { efXVG, "-o", "current", ffWRITE },
893 { efXVG, "-caf", "caf", ffOPTWR },
894 { efXVG, "-dsp", "dsp", ffWRITE },
895 { efXVG, "-md", "md", ffWRITE },
896 { efXVG, "-mj", "mj", ffWRITE },
897 { efXVG, "-mc", "mc", ffOPTWR } };
899 #define NFILE asize(fnm)
902 const char* desc[] = {
903 "[THISMODULE] is a tool for calculating the current autocorrelation function, the "
905 "of the rotational and translational dipole moment of the system, and the resulting static",
906 "dielectric constant. To obtain a reasonable result, the index group has to be neutral.",
907 "Furthermore, the routine is capable of extracting the static conductivity from the "
909 "autocorrelation function, if velocities are given. Additionally, an Einstein-Helfand fit ",
910 "can be used to obtain the static conductivity.",
912 "The flag [TT]-caf[tt] is for the output of the current autocorrelation function and "
913 "[TT]-mc[tt] writes the",
914 "correlation of the rotational and translational part of the dipole moment in the "
916 "file. However, this option is only available for trajectories containing velocities.",
917 "Options [TT]-sh[tt] and [TT]-tr[tt] are responsible for the averaging and integration of "
919 "autocorrelation functions. Since averaging proceeds by shifting the starting point",
920 "through the trajectory, the shift can be modified with [TT]-sh[tt] to enable the choice "
922 "starting points. Towards the end, statistical inaccuracy grows and integrating the",
923 "correlation function only yields reliable values until a certain point, depending on",
924 "the number of frames. The option [TT]-tr[tt] controls the region of the integral taken "
926 "for calculating the static dielectric constant.",
928 "Option [TT]-temp[tt] sets the temperature required for the computation of the static "
929 "dielectric constant.",
931 "Option [TT]-eps[tt] controls the dielectric constant of the surrounding medium for "
933 "a Reaction Field or dipole corrections of the Ewald summation ([TT]-eps[tt]\\=0 "
935 "tin-foil boundary conditions).",
937 "[TT]-[no]nojump[tt] unfolds the coordinates to allow free diffusion. This is required to "
939 "translational dipole moment, required for the Einstein-Helfand fit. The results from the "
941 "the determination of the dielectric constant for system of charged molecules. However, it "
942 "is also possible to extract",
943 "the dielectric constant from the fluctuations of the total dipole moment in folded "
944 "coordinates. But this",
945 "option has to be used with care, since only very short time spans fulfill the "
946 "approximation that the density",
947 "of the molecules is approximately constant and the averages are already converged. To be "
949 "the dielectric constant should be calculated with the help of the Einstein-Helfand method "
951 "the translational part of the dielectric constant."
955 /* At first the arguments will be parsed and the system information processed */
956 if (!parse_common_args(
957 &argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW, NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, nullptr, &oenv))
962 bACF = opt2bSet("-caf", NFILE, fnm);
963 bINT = opt2bSet("-mc", NFILE, fnm);
965 read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &pbcType, nullptr, nullptr, box, TRUE);
967 indexfn = ftp2fn_null(efNDX, NFILE, fnm);
970 get_index(&(top.atoms), indexfn, 1, &isize, &index0, grpname);
972 flags = flags | TRX_READ_X | TRX_READ_V;
974 read_first_frame(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &fr, flags);
976 snew(mass2, top.atoms.nr);
977 snew(qmol, top.atoms.nr);
979 precalc(top, mass2, qmol);
984 for (i = 0; i < isize; i++)
986 indexm[i] = index0[i];
992 index_atom2mol(&nmols, indexm, &top.mols);
998 outf = xvgropen(opt2fn("-caf", NFILE, fnm),
999 "Current autocorrelation function",
1000 output_env_get_xvgr_tlabel(oenv),
1001 "ACF (e nm/ps)\\S2",
1003 fprintf(outf, "# time\t acf\t average \t std.dev\n");
1005 fcur = xvgropen(opt2fn("-o", NFILE, fnm),
1007 output_env_get_xvgr_tlabel(oenv),
1010 fprintf(fcur, "# time\t Jx\t Jy \t J_z \n");
1013 mcor = xvgropen(opt2fn("-mc", NFILE, fnm),
1014 "M\\sD\\N - current autocorrelation function",
1015 output_env_get_xvgr_tlabel(oenv),
1016 "< M\\sD\\N (0)\\c7\\CJ(t) > (e nm/ps)\\S2",
1018 fprintf(mcor, "# time\t M_D(0) J(t) acf \t Integral acf\n");
1022 fmj = xvgropen(opt2fn("-mj", NFILE, fnm),
1023 "Averaged translational part of M",
1024 output_env_get_xvgr_tlabel(oenv),
1025 "< M\\sJ\\N > (enm)",
1027 fprintf(fmj, "# time\t x\t y \t z \t average of M_J^2 \t std.dev\n");
1028 fmd = xvgropen(opt2fn("-md", NFILE, fnm),
1029 "Averaged rotational part of M",
1030 output_env_get_xvgr_tlabel(oenv),
1031 "< M\\sD\\N > (enm)",
1033 fprintf(fmd, "# time\t x\t y \t z \t average of M_D^2 \t std.dev\n");
1036 opt2fn("-dsp", NFILE, fnm),
1037 "MSD of the squared translational dipole moment M",
1038 output_env_get_xvgr_tlabel(oenv),
1039 "<|M\\sJ\\N(t)-M\\sJ\\N(0)|\\S2\\N > / 6.0*V*k\\sB\\N*T / Sm\\S-1\\Nps\\S-1\\N",
1043 /* System information is read and prepared, dielectric() processes the frames
1044 * and calculates the requested quantities */