Remove unused function generate_excls and make clean_excls static
[gromacs.git] / src / gromacs / gmxana / gmx_dipoles.cpp
blob6601111115daca05e689c475bc95876e4868fc05
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2016,2017,2018,2019, 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.
37 #include "gmxpre.h"
39 #include <cmath>
40 #include <cstring>
42 #include <algorithm>
44 #include "gromacs/commandline/pargs.h"
45 #include "gromacs/commandline/viewit.h"
46 #include "gromacs/correlationfunctions/autocorr.h"
47 #include "gromacs/fileio/confio.h"
48 #include "gromacs/fileio/enxio.h"
49 #include "gromacs/fileio/matio.h"
50 #include "gromacs/fileio/tpxio.h"
51 #include "gromacs/fileio/trxio.h"
52 #include "gromacs/fileio/xvgr.h"
53 #include "gromacs/gmxana/gmx_ana.h"
54 #include "gromacs/linearalgebra/nrjac.h"
55 #include "gromacs/listed_forces/bonded.h"
56 #include "gromacs/math/functions.h"
57 #include "gromacs/math/units.h"
58 #include "gromacs/math/vec.h"
59 #include "gromacs/math/vecdump.h"
60 #include "gromacs/mdtypes/md_enums.h"
61 #include "gromacs/pbcutil/pbc.h"
62 #include "gromacs/pbcutil/rmpbc.h"
63 #include "gromacs/statistics/statistics.h"
64 #include "gromacs/topology/index.h"
65 #include "gromacs/topology/topology.h"
66 #include "gromacs/trajectory/energyframe.h"
67 #include "gromacs/utility/arraysize.h"
68 #include "gromacs/utility/binaryinformation.h"
69 #include "gromacs/utility/cstringutil.h"
70 #include "gromacs/utility/exceptions.h"
71 #include "gromacs/utility/fatalerror.h"
72 #include "gromacs/utility/futil.h"
73 #include "gromacs/utility/gmxassert.h"
74 #include "gromacs/utility/smalloc.h"
76 #define e2d(x) ENM2DEBYE*(x)
77 #define EANG2CM (E_CHARGE*1.0e-10) /* e Angstrom to Coulomb meter */
78 #define CM2D (SPEED_OF_LIGHT*1.0e+24) /* Coulomb meter to Debye */
80 typedef struct {
81 int nelem;
82 real spacing, radius;
83 real *elem;
84 int *count;
85 gmx_bool bPhi;
86 int nx, ny;
87 real **cmap;
88 } t_gkrbin;
90 static t_gkrbin *mk_gkrbin(real radius, real rcmax, gmx_bool bPhi, int ndegrees)
92 t_gkrbin *gb;
93 char *ptr;
94 int i;
96 snew(gb, 1);
98 if ((ptr = getenv("GMX_DIPOLE_SPACING")) != nullptr)
100 double bw = strtod(ptr, nullptr);
101 gb->spacing = bw;
103 else
105 gb->spacing = 0.01; /* nm */
107 gb->nelem = 1 + static_cast<int>(radius/gb->spacing);
108 if (rcmax == 0)
110 gb->nx = gb->nelem;
112 else
114 gb->nx = 1 + static_cast<int>(rcmax/gb->spacing);
116 gb->radius = radius;
117 snew(gb->elem, gb->nelem);
118 snew(gb->count, gb->nelem);
120 snew(gb->cmap, gb->nx);
121 gb->ny = std::max(2, ndegrees);
122 for (i = 0; (i < gb->nx); i++)
124 snew(gb->cmap[i], gb->ny);
126 gb->bPhi = bPhi;
128 return gb;
131 static void done_gkrbin(t_gkrbin **gb)
133 sfree((*gb)->elem);
134 sfree((*gb)->count);
135 sfree((*gb));
136 *gb = nullptr;
139 static void add2gkr(t_gkrbin *gb, real r, real cosa, real phi)
141 int cy, index = gmx::roundToInt(r/gb->spacing);
142 real alpha;
144 if (index < gb->nelem)
146 gb->elem[index] += cosa;
147 gb->count[index]++;
149 if (index < gb->nx)
151 alpha = std::acos(cosa);
152 if (gb->bPhi)
154 cy = static_cast<int>((M_PI+phi)*gb->ny/(2*M_PI));
156 else
158 cy = static_cast<int>((alpha*gb->ny)/M_PI); /*((1+cosa)*0.5*(gb->ny));*/
160 cy = std::min(gb->ny-1, std::max(0, cy));
161 if (debug)
163 fprintf(debug, "CY: %10f %5d\n", alpha, cy);
165 gb->cmap[index][cy] += 1;
169 static void rvec2sprvec(rvec dipcart, rvec dipsp)
171 clear_rvec(dipsp);
172 dipsp[0] = std::sqrt(dipcart[XX]*dipcart[XX]+dipcart[YY]*dipcart[YY]+dipcart[ZZ]*dipcart[ZZ]); /* R */
173 dipsp[1] = std::atan2(dipcart[YY], dipcart[XX]); /* Theta */
174 dipsp[2] = std::atan2(std::sqrt(dipcart[XX]*dipcart[XX]+dipcart[YY]*dipcart[YY]), dipcart[ZZ]); /* Phi */
179 static void do_gkr(t_gkrbin *gb, int ncos, int *ngrp, int *molindex[],
180 const int mindex[], rvec x[], rvec mu[],
181 int ePBC, const matrix box, const t_atom *atom, const int *nAtom)
183 static rvec *xcm[2] = { nullptr, nullptr};
184 int gi, gj, j0, j1, i, j, k, n, grp0, grp1;
185 real qtot, q, cosa, rr, phi;
186 rvec dx;
187 t_pbc pbc;
189 for (n = 0; (n < ncos); n++)
191 if (!xcm[n])
193 snew(xcm[n], ngrp[n]);
195 for (i = 0; (i < ngrp[n]); i++)
197 /* Calculate center of mass of molecule */
198 gi = molindex[n][i];
199 j0 = mindex[gi];
201 if (nAtom[n] > 0)
203 copy_rvec(x[j0+nAtom[n]-1], xcm[n][i]);
205 else
207 j1 = mindex[gi+1];
208 clear_rvec(xcm[n][i]);
209 qtot = 0;
210 for (j = j0; j < j1; j++)
212 q = std::abs(atom[j].q);
213 qtot += q;
214 for (k = 0; k < DIM; k++)
216 xcm[n][i][k] += q*x[j][k];
219 svmul(1/qtot, xcm[n][i], xcm[n][i]);
223 set_pbc(&pbc, ePBC, box);
224 grp0 = 0;
225 grp1 = ncos-1;
226 for (i = 0; i < ngrp[grp0]; i++)
228 gi = molindex[grp0][i];
229 j0 = (ncos == 2) ? 0 : i+1;
230 for (j = j0; j < ngrp[grp1]; j++)
232 gj = molindex[grp1][j];
233 if ((iprod(mu[gi], mu[gi]) > 0) && (iprod(mu[gj], mu[gj]) > 0))
235 /* Compute distance between molecules including PBC */
236 pbc_dx(&pbc, xcm[grp0][i], xcm[grp1][j], dx);
237 rr = norm(dx);
239 if (gb->bPhi)
241 rvec xi, xj, xk, xl;
242 rvec r_ij, r_kj, r_kl, mm, nn;
243 int t1, t2, t3;
245 copy_rvec(xcm[grp0][i], xj);
246 copy_rvec(xcm[grp1][j], xk);
247 rvec_add(xj, mu[gi], xi);
248 rvec_add(xk, mu[gj], xl);
249 phi = dih_angle(xi, xj, xk, xl, &pbc,
250 r_ij, r_kj, r_kl, mm, nn, /* out */
251 &t1, &t2, &t3);
252 cosa = std::cos(phi);
254 else
256 cosa = cos_angle(mu[gi], mu[gj]);
257 phi = 0;
259 if (debug || std::isnan(cosa))
261 fprintf(debug ? debug : stderr,
262 "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",
263 gi, mu[gi][XX], mu[gi][YY], mu[gi][ZZ], norm(mu[gi]),
264 gj, mu[gj][XX], mu[gj][YY], mu[gj][ZZ], norm(mu[gj]),
265 rr, cosa);
268 add2gkr(gb, rr, cosa, phi);
274 static real normalize_cmap(t_gkrbin *gb)
276 int i, j;
277 real hi;
278 double vol;
280 hi = 0;
281 for (i = 0; (i < gb->nx); i++)
283 vol = 4*M_PI*gmx::square(gb->spacing*i)*gb->spacing;
284 for (j = 0; (j < gb->ny); j++)
286 gb->cmap[i][j] /= vol;
287 hi = std::max(hi, gb->cmap[i][j]);
290 if (hi <= 0)
292 gmx_fatal(FARGS, "No data in the cmap");
294 return hi;
297 static void print_cmap(const char *cmap, t_gkrbin *gb, int *nlevels)
299 FILE *out;
300 int i, j;
301 real hi;
303 real *xaxis, *yaxis;
304 t_rgb rlo = { 1, 1, 1 };
305 t_rgb rhi = { 0, 0, 0 };
307 hi = normalize_cmap(gb);
308 snew(xaxis, gb->nx+1);
309 for (i = 0; (i < gb->nx+1); i++)
311 xaxis[i] = i*gb->spacing;
313 snew(yaxis, gb->ny);
314 for (j = 0; (j < gb->ny); j++)
316 if (gb->bPhi)
318 yaxis[j] = (360.0*j)/(gb->ny-1.0)-180;
320 else
322 yaxis[j] = (180.0*j)/(gb->ny-1.0);
324 /*2.0*j/(gb->ny-1.0)-1.0;*/
326 out = gmx_ffopen(cmap, "w");
327 write_xpm(out, 0,
328 "Dipole Orientation Distribution", "Fraction", "r (nm)",
329 gb->bPhi ? "Phi" : "Alpha",
330 gb->nx, gb->ny, xaxis, yaxis,
331 gb->cmap, 0, hi, rlo, rhi, nlevels);
332 gmx_ffclose(out);
333 sfree(xaxis);
334 sfree(yaxis);
337 static void print_gkrbin(const char *fn, t_gkrbin *gb,
338 int ngrp, int nframes, real volume,
339 const gmx_output_env_t *oenv)
341 /* We compute Gk(r), gOO and hOO according to
342 * Nymand & Linse, JCP 112 (2000) pp 6386-6395.
343 * In this implementation the angle between dipoles is stored
344 * rather than their inner product. This allows to take polarizible
345 * models into account. The RDF is calculated as well, almost for free!
347 FILE *fp;
348 const char *leg[] = { "G\\sk\\N(r)", "< cos >", "h\\sOO\\N", "g\\sOO\\N", "Energy" };
349 int i, last;
350 real x0, x1, ggg, Gkr, vol_s, rho, gOO, hOO, cosav, ener;
351 double fac;
353 fp = xvgropen(fn, "Distance dependent Gk", "r (nm)", "G\\sk\\N(r)", oenv);
354 xvgr_legend(fp, asize(leg), leg, oenv);
356 Gkr = 1; /* Self-dipole inproduct = 1 */
357 rho = ngrp/volume;
359 if (debug)
361 fprintf(debug, "Number density is %g molecules / nm^3\n", rho);
362 fprintf(debug, "ngrp = %d, nframes = %d\n", ngrp, nframes);
365 last = gb->nelem-1;
366 while (last > 1 && gb->elem[last-1] == 0)
368 last--;
371 /* Divide by dipole squared, by number of frames, by number of origins.
372 * Multiply by 2 because we only take half the matrix of interactions
373 * into account.
375 fac = 2.0/(ngrp * nframes);
377 x0 = 0;
378 for (i = 0; i < last; i++)
380 /* Centre of the coordinate in the spherical layer */
381 x1 = x0+gb->spacing;
383 /* Volume of the layer */
384 vol_s = (4.0/3.0)*M_PI*(x1*x1*x1-x0*x0*x0);
386 /* gOO */
387 gOO = gb->count[i]*fac/(rho*vol_s);
389 /* Dipole correlation hOO, normalized by the relative number density, like
390 * in a Radial distribution function.
392 ggg = gb->elem[i]*fac;
393 hOO = 3.0*ggg/(rho*vol_s);
394 Gkr += ggg;
395 if (gb->count[i])
397 cosav = gb->elem[i]/gb->count[i];
399 else
401 cosav = 0;
403 ener = -0.5*cosav*ONE_4PI_EPS0/(x1*x1*x1);
405 fprintf(fp, "%10.5e %12.5e %12.5e %12.5e %12.5e %12.5e\n",
406 x1, Gkr, cosav, hOO, gOO, ener);
408 /* Swap x0 and x1 */
409 x0 = x1;
411 xvgrclose(fp);
414 static gmx_bool read_mu_from_enx(ener_file_t fmu, int Vol, const ivec iMu, rvec mu, real *vol,
415 real *t, int nre, t_enxframe *fr)
417 int i;
418 gmx_bool bCont;
419 char buf[22];
421 bCont = do_enx(fmu, fr);
422 if (fr->nre != nre)
424 fprintf(stderr, "Something strange: expected %d entries in energy file at step %s\n(time %g) but found %d entries\n",
425 nre, gmx_step_str(fr->step, buf), fr->t, fr->nre);
428 if (bCont)
430 if (Vol != -1) /* we've got Volume in the energy file */
432 *vol = fr->ener[Vol].e;
434 for (i = 0; i < DIM; i++)
436 mu[i] = fr->ener[iMu[i]].e;
438 *t = fr->t;
441 return bCont;
444 static void neutralize_mols(int n, const int *index, const t_block *mols, t_atom *atom)
446 double mtot, qtot;
447 int ncharged, m, a0, a1, a;
449 ncharged = 0;
450 for (m = 0; m < n; m++)
452 a0 = mols->index[index[m]];
453 a1 = mols->index[index[m]+1];
454 mtot = 0;
455 qtot = 0;
456 for (a = a0; a < a1; a++)
458 mtot += atom[a].m;
459 qtot += atom[a].q;
461 /* This check is only for the count print */
462 if (std::abs(qtot) > 0.01)
464 ncharged++;
466 if (mtot > 0)
468 /* Remove the net charge at the center of mass */
469 for (a = a0; a < a1; a++)
471 atom[a].q -= qtot*atom[a].m/mtot;
476 if (ncharged)
478 printf("There are %d charged molecules in the selection,\n"
479 "will subtract their charge at their center of mass\n", ncharged);
483 static void mol_dip(int k0, int k1, rvec x[], const t_atom atom[], rvec mu)
485 int k, m;
486 real q;
488 clear_rvec(mu);
489 for (k = k0; (k < k1); k++)
491 q = e2d(atom[k].q);
492 for (m = 0; (m < DIM); m++)
494 mu[m] += q*x[k][m];
499 static void mol_quad(int k0, int k1, rvec x[], const t_atom atom[], rvec quad)
501 int i, k, m, n, niter;
502 real q, r2, mass, masstot;
503 rvec com; /* center of mass */
504 rvec r; /* distance of atoms to center of mass */
505 double **inten;
506 double dd[DIM], **ev;
508 snew(inten, DIM);
509 snew(ev, DIM);
510 for (i = 0; (i < DIM); i++)
512 snew(inten[i], DIM);
513 snew(ev[i], DIM);
514 dd[i] = 0.0;
517 /* Compute center of mass */
518 clear_rvec(com);
519 masstot = 0.0;
520 for (k = k0; (k < k1); k++)
522 mass = atom[k].m;
523 masstot += mass;
524 for (i = 0; (i < DIM); i++)
526 com[i] += mass*x[k][i];
529 svmul((1.0/masstot), com, com);
531 /* We want traceless quadrupole moments, so let us calculate the complete
532 * quadrupole moment tensor and diagonalize this tensor to get
533 * the individual components on the diagonal.
536 #define delta(a, b) (( (a) == (b) ) ? 1.0 : 0.0)
538 for (m = 0; (m < DIM); m++)
540 for (n = 0; (n < DIM); n++)
542 inten[m][n] = 0;
545 for (k = k0; (k < k1); k++) /* loop over atoms in a molecule */
547 q = (atom[k].q)*100.0;
548 rvec_sub(x[k], com, r);
549 r2 = iprod(r, r);
550 for (m = 0; (m < DIM); m++)
552 for (n = 0; (n < DIM); n++)
554 inten[m][n] += 0.5*q*(3.0*r[m]*r[n] - r2*delta(m, n))*EANG2CM*CM2D;
558 if (debug)
560 for (i = 0; (i < DIM); i++)
562 fprintf(debug, "Q[%d] = %8.3f %8.3f %8.3f\n",
563 i, inten[i][XX], inten[i][YY], inten[i][ZZ]);
567 /* We've got the quadrupole tensor, now diagonalize the sucker */
568 jacobi(inten, 3, dd, ev, &niter);
570 if (debug)
572 for (i = 0; (i < DIM); i++)
574 fprintf(debug, "ev[%d] = %8.3f %8.3f %8.3f\n",
575 i, ev[i][XX], ev[i][YY], ev[i][ZZ]);
577 for (i = 0; (i < DIM); i++)
579 fprintf(debug, "Q'[%d] = %8.3f %8.3f %8.3f\n",
580 i, inten[i][XX], inten[i][YY], inten[i][ZZ]);
583 /* Sort the eigenvalues, for water we know that the order is as follows:
585 * Q_yy, Q_zz, Q_xx
587 * At the moment I have no idea how this will work out for other molecules...
590 if (dd[1] > dd[0])
592 std::swap(dd[0], dd[1]);
594 if (dd[2] > dd[1])
596 std::swap(dd[1], dd[2]);
598 if (dd[1] > dd[0])
600 std::swap(dd[0], dd[1]);
603 for (m = 0; (m < DIM); m++)
605 quad[0] = dd[2]; /* yy */
606 quad[1] = dd[0]; /* zz */
607 quad[2] = dd[1]; /* xx */
610 if (debug)
612 pr_rvec(debug, 0, "Quadrupole", quad, DIM, TRUE);
615 /* clean-up */
616 for (i = 0; (i < DIM); i++)
618 sfree(inten[i]);
619 sfree(ev[i]);
621 sfree(inten);
622 sfree(ev);
626 * Calculates epsilon according to M. Neumann, Mol. Phys. 50, 841 (1983)
628 static real calc_eps(double M_diff, double volume, double epsRF, double temp)
630 double eps, A, teller, noemer;
631 double eps_0 = 8.854187817e-12; /* epsilon_0 in C^2 J^-1 m^-1 */
632 double fac = 1.112650021e-59; /* converts Debye^2 to C^2 m^2 */
634 A = M_diff*fac/(3*eps_0*volume*NANO*NANO*NANO*BOLTZMANN*temp);
636 if (epsRF == 0.0)
638 teller = 1 + A;
639 noemer = 1;
641 else
643 teller = 1 + (A*2*epsRF/(2*epsRF+1));
644 noemer = 1 - (A/(2*epsRF+1));
646 eps = teller / noemer;
648 return eps;
651 static void update_slab_dipoles(int k0, int k1, rvec x[], rvec mu,
652 int idim, int nslice, rvec slab_dipole[],
653 matrix box)
655 int k;
656 real xdim = 0;
658 for (k = k0; (k < k1); k++)
660 xdim += x[k][idim];
662 xdim /= (k1-k0);
663 k = (static_cast<int>(xdim*nslice/box[idim][idim] + nslice)) % nslice;
664 rvec_inc(slab_dipole[k], mu);
667 static void dump_slab_dipoles(const char *fn, int idim, int nslice,
668 rvec slab_dipole[], matrix box, int nframes,
669 const gmx_output_env_t *oenv)
671 FILE *fp;
672 char buf[STRLEN];
673 int i;
674 real mutot;
675 const char *leg_dim[4] = {
676 "\\f{12}m\\f{4}\\sX\\N",
677 "\\f{12}m\\f{4}\\sY\\N",
678 "\\f{12}m\\f{4}\\sZ\\N",
679 "\\f{12}m\\f{4}\\stot\\N"
682 sprintf(buf, "Box-%c (nm)", 'X'+idim);
683 fp = xvgropen(fn, "Average dipole moment per slab", buf, "\\f{12}m\\f{4} (D)",
684 oenv);
685 xvgr_legend(fp, DIM, leg_dim, oenv);
686 for (i = 0; (i < nslice); i++)
688 mutot = norm(slab_dipole[i])/nframes;
689 fprintf(fp, "%10.3f %10.3f %10.3f %10.3f %10.3f\n",
690 ((i+0.5)*box[idim][idim])/nslice,
691 slab_dipole[i][XX]/nframes,
692 slab_dipole[i][YY]/nframes,
693 slab_dipole[i][ZZ]/nframes,
694 mutot);
696 xvgrclose(fp);
697 do_view(oenv, fn, "-autoscale xy -nxy");
700 static void compute_avercos(int n, rvec dip[], real *dd, rvec axis, gmx_bool bPairs)
702 int i, j, k;
703 double dc, d, ddc1, ddc2, ddc3;
704 rvec xxx = { 1, 0, 0 };
705 rvec yyy = { 0, 1, 0 };
706 rvec zzz = { 0, 0, 1 };
708 d = 0;
709 ddc1 = ddc2 = ddc3 = 0;
710 for (i = k = 0; (i < n); i++)
712 ddc1 += std::abs(cos_angle(dip[i], xxx));
713 ddc2 += std::abs(cos_angle(dip[i], yyy));
714 ddc3 += std::abs(cos_angle(dip[i], zzz));
715 if (bPairs)
717 for (j = i+1; (j < n); j++, k++)
719 dc = cos_angle(dip[i], dip[j]);
720 d += std::abs(dc);
724 *dd = d/k;
725 axis[XX] = ddc1/n;
726 axis[YY] = ddc2/n;
727 axis[ZZ] = ddc3/n;
730 static void do_dip(const t_topology *top, int ePBC, real volume,
731 const char *fn,
732 const char *out_mtot, const char *out_eps,
733 const char *out_aver, const char *dipdist,
734 const char *cosaver, const char *fndip3d,
735 const char *fnadip, gmx_bool bPairs,
736 const char *corrtype, const char *corf,
737 gmx_bool bGkr, const char *gkrfn,
738 gmx_bool bPhi, int *nlevels, int ndegrees,
739 int ncos,
740 const char *cmap, real rcmax,
741 gmx_bool bQuad,
742 gmx_bool bMU, const char *mufn,
743 int *gnx, int *molindex[],
744 real mu_max, real mu_aver,
745 real epsilonRF, real temp,
746 int *gkatom, int skip,
747 gmx_bool bSlab, int nslices,
748 const char *axtitle, const char *slabfn,
749 const gmx_output_env_t *oenv)
751 const char *leg_mtot[] = {
752 "M\\sx \\N",
753 "M\\sy \\N",
754 "M\\sz \\N",
755 "|M\\stot \\N|"
757 #define NLEGMTOT asize(leg_mtot)
758 const char *leg_eps[] = {
759 "epsilon",
760 "G\\sk",
761 "g\\sk"
763 #define NLEGEPS asize(leg_eps)
764 const char *leg_aver[] = {
765 "< |M|\\S2\\N >",
766 "< |M| >\\S2\\N",
767 "< |M|\\S2\\N > - < |M| >\\S2\\N",
768 "< |M| >\\S2\\N / < |M|\\S2\\N >"
770 #define NLEGAVER asize(leg_aver)
771 const char *leg_cosaver[] = {
772 "\\f{4}<|cos\\f{12}q\\f{4}\\sij\\N|>",
773 "RMSD cos",
774 "\\f{4}<|cos\\f{12}q\\f{4}\\siX\\N|>",
775 "\\f{4}<|cos\\f{12}q\\f{4}\\siY\\N|>",
776 "\\f{4}<|cos\\f{12}q\\f{4}\\siZ\\N|>"
778 #define NLEGCOSAVER asize(leg_cosaver)
779 const char *leg_adip[] = {
780 "<mu>",
781 "Std. Dev.",
782 "Error"
784 #define NLEGADIP asize(leg_adip)
786 FILE *outdd, *outmtot, *outaver, *outeps, *caver = nullptr;
787 FILE *dip3d = nullptr, *adip = nullptr;
788 rvec *x, *dipole = nullptr, mu_t, quad, *dipsp = nullptr;
789 t_gkrbin *gkrbin = nullptr;
790 gmx_enxnm_t *enm = nullptr;
791 t_enxframe *fr;
792 int nframes = 1000, nre, timecheck = 0, ncolour = 0;
793 ener_file_t fmu = nullptr;
794 int i, n, m, natom = 0, gnx_tot, teller, tel3;
795 t_trxstatus *status;
796 int *dipole_bin, ndipbin, ibin, iVol, idim = -1;
797 unsigned long mode;
798 real rcut = 0, t, t0, t1, dt, dd, rms_cos;
799 rvec dipaxis;
800 matrix box;
801 gmx_bool bCorr, bTotal, bCont;
802 double M_diff = 0, epsilon, invtel, vol_aver;
803 double mu_ave, mu_mol, M2_ave = 0, M_ave2 = 0, M_av[DIM], M_av2[DIM];
804 double M[3], M2[3], M4[3], Gk = 0, g_k = 0;
805 gmx_stats_t *Qlsq, mulsq, muframelsq = nullptr;
806 ivec iMu;
807 real **muall = nullptr;
808 rvec *slab_dipoles = nullptr;
809 const t_atom *atom = nullptr;
810 const t_block *mols = nullptr;
811 gmx_rmpbc_t gpbc = nullptr;
813 gnx_tot = gnx[0];
814 if (ncos == 2)
816 gnx_tot += gnx[1];
818 GMX_RELEASE_ASSERT(ncos == 1 || ncos == 2, "Invalid number of groups used with -ncos");
820 vol_aver = 0.0;
822 iVol = -1;
824 /* initialize to a negative value so we can see that it wasn't picked up */
825 iMu[XX] = iMu[YY] = iMu[ZZ] = -1;
826 if (bMU)
828 fmu = open_enx(mufn, "r");
829 do_enxnms(fmu, &nre, &enm);
831 /* Determine the indexes of the energy grps we need */
832 for (i = 0; (i < nre); i++)
834 if (std::strstr(enm[i].name, "Volume"))
836 iVol = i;
838 else if (std::strstr(enm[i].name, "Mu-X"))
840 iMu[XX] = i;
842 else if (std::strstr(enm[i].name, "Mu-Y"))
844 iMu[YY] = i;
846 else if (std::strstr(enm[i].name, "Mu-Z"))
848 iMu[ZZ] = i;
851 if (iMu[XX] < 0 || iMu[YY] < 0 || iMu[ZZ] < 0)
853 gmx_fatal(FARGS, "No index for Mu-X, Mu-Y or Mu-Z energy group.");
856 else
858 atom = top->atoms.atom;
859 mols = &(top->mols);
861 if ((iVol == -1) && bMU)
863 printf("Using Volume from topology: %g nm^3\n", volume);
866 /* Correlation stuff */
867 bCorr = (corrtype[0] != 'n');
868 bTotal = (corrtype[0] == 't');
869 if (bCorr)
871 if (bTotal)
873 snew(muall, 1);
874 snew(muall[0], nframes*DIM);
876 else
878 snew(muall, gnx[0]);
879 for (i = 0; (i < gnx[0]); i++)
881 snew(muall[i], nframes*DIM);
886 /* Allocate array which contains for every molecule in a frame the
887 * dipole moment.
889 if (!bMU)
891 snew(dipole, gnx_tot);
894 /* Statistics */
895 snew(Qlsq, DIM);
896 for (i = 0; (i < DIM); i++)
898 Qlsq[i] = gmx_stats_init();
900 mulsq = gmx_stats_init();
902 /* Open all the files */
903 outmtot = xvgropen(out_mtot,
904 "Total dipole moment of the simulation box vs. time",
905 "Time (ps)", "Total Dipole Moment (Debye)", oenv);
906 outeps = xvgropen(out_eps, "Epsilon and Kirkwood factors",
907 "Time (ps)", "", oenv);
908 outaver = xvgropen(out_aver, "Total dipole moment",
909 "Time (ps)", "D", oenv);
910 if (bSlab)
912 idim = axtitle[0] - 'X';
913 if ((idim < 0) || (idim >= DIM))
915 idim = axtitle[0] - 'x';
917 if ((idim < 0) || (idim >= DIM))
919 bSlab = FALSE;
921 if (nslices < 2)
923 bSlab = FALSE;
925 fprintf(stderr, "axtitle = %s, nslices = %d, idim = %d\n",
926 axtitle, nslices, idim);
927 if (bSlab)
929 snew(slab_dipoles, nslices);
930 fprintf(stderr, "Doing slab analysis\n");
934 if (fnadip)
936 adip = xvgropen(fnadip, "Average molecular dipole", "Dipole (D)", "", oenv);
937 xvgr_legend(adip, NLEGADIP, leg_adip, oenv);
940 if (cosaver)
942 caver = xvgropen(cosaver, bPairs ? "Average pair orientation" :
943 "Average absolute dipole orientation", "Time (ps)", "", oenv);
944 xvgr_legend(caver, NLEGCOSAVER, bPairs ? leg_cosaver : &(leg_cosaver[1]),
945 oenv);
948 if (fndip3d)
950 snew(dipsp, gnx_tot);
952 /* we need a dummy file for gnuplot */
953 dip3d = gmx_ffopen("dummy.dat", "w");
954 fprintf(dip3d, "%f %f %f", 0.0, 0.0, 0.0);
955 gmx_ffclose(dip3d);
957 dip3d = gmx_ffopen(fndip3d, "w");
960 gmx::BinaryInformationSettings settings;
961 settings.generatedByHeader(true);
962 settings.linePrefix("# ");
963 gmx::printBinaryInformation(dip3d, output_env_get_program_context(oenv),
964 settings);
966 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
969 /* Write legends to all the files */
970 xvgr_legend(outmtot, NLEGMTOT, leg_mtot, oenv);
971 xvgr_legend(outaver, NLEGAVER, leg_aver, oenv);
973 if (bMU && (mu_aver == -1))
975 xvgr_legend(outeps, NLEGEPS-2, leg_eps, oenv);
977 else
979 xvgr_legend(outeps, NLEGEPS, leg_eps, oenv);
982 snew(fr, 1);
983 clear_rvec(mu_t);
984 teller = 0;
985 /* Read the first frame from energy or traj file */
986 if (bMU)
990 bCont = read_mu_from_enx(fmu, iVol, iMu, mu_t, &volume, &t, nre, fr);
991 if (bCont)
993 timecheck = check_times(t);
994 if (timecheck < 0)
996 teller++;
998 if ((teller % 10) == 0)
1000 fprintf(stderr, "\r Skipping Frame %6d, time: %8.3f", teller, t);
1001 fflush(stderr);
1004 else
1006 printf("End of %s reached\n", mufn);
1007 break;
1010 while (bCont && (timecheck < 0));
1012 else
1014 natom = read_first_x(oenv, &status, fn, &t, &x, box);
1017 /* Calculate spacing for dipole bin (simple histogram) */
1018 ndipbin = 1 + static_cast<int>(mu_max/0.01);
1019 snew(dipole_bin, ndipbin);
1020 mu_ave = 0.0;
1021 for (m = 0; (m < DIM); m++)
1023 M[m] = M2[m] = M4[m] = 0.0;
1026 if (bGkr)
1028 /* Use 0.7 iso 0.5 to account for pressure scaling */
1029 /* rcut = 0.7*sqrt(max_cutoff2(box)); */
1030 rcut = 0.7*std::sqrt(gmx::square(box[XX][XX])+gmx::square(box[YY][YY])+gmx::square(box[ZZ][ZZ]));
1032 gkrbin = mk_gkrbin(rcut, rcmax, bPhi, ndegrees);
1034 gpbc = gmx_rmpbc_init(&top->idef, ePBC, natom);
1036 /* Start while loop over frames */
1037 t0 = t;
1038 teller = 0;
1041 if (bCorr && (teller >= nframes))
1043 nframes += 1000;
1044 if (bTotal)
1046 srenew(muall[0], nframes*DIM);
1048 else
1050 for (i = 0; (i < gnx_tot); i++)
1052 srenew(muall[i], nframes*DIM);
1056 t1 = t;
1058 muframelsq = gmx_stats_init();
1060 /* Initialise */
1061 for (m = 0; (m < DIM); m++)
1063 M_av2[m] = 0;
1066 if (bMU)
1068 /* Copy rvec into double precision local variable */
1069 for (m = 0; (m < DIM); m++)
1071 M_av[m] = mu_t[m];
1074 else
1076 /* Initialise */
1077 for (m = 0; (m < DIM); m++)
1079 M_av[m] = 0;
1082 gmx_rmpbc(gpbc, natom, box, x);
1084 /* Begin loop of all molecules in frame */
1085 for (n = 0; (n < ncos); n++)
1087 for (i = 0; (i < gnx[n]); i++)
1089 int ind0, ind1;
1091 ind0 = mols->index[molindex[n][i]];
1092 ind1 = mols->index[molindex[n][i]+1];
1094 mol_dip(ind0, ind1, x, atom, dipole[i]);
1095 gmx_stats_add_point(mulsq, 0, norm(dipole[i]), 0, 0);
1096 gmx_stats_add_point(muframelsq, 0, norm(dipole[i]), 0, 0);
1097 if (bSlab)
1099 update_slab_dipoles(ind0, ind1, x,
1100 dipole[i], idim, nslices, slab_dipoles, box);
1102 if (bQuad)
1104 mol_quad(ind0, ind1, x, atom, quad);
1105 for (m = 0; (m < DIM); m++)
1107 gmx_stats_add_point(Qlsq[m], 0, quad[m], 0, 0);
1110 if (bCorr && !bTotal)
1112 tel3 = DIM*teller;
1113 muall[i][tel3+XX] = dipole[i][XX];
1114 muall[i][tel3+YY] = dipole[i][YY];
1115 muall[i][tel3+ZZ] = dipole[i][ZZ];
1117 mu_mol = 0.0;
1118 for (m = 0; (m < DIM); m++)
1120 M_av[m] += dipole[i][m]; /* M per frame */
1121 mu_mol += dipole[i][m]*dipole[i][m]; /* calc. mu for distribution */
1123 mu_mol = std::sqrt(mu_mol);
1125 mu_ave += mu_mol; /* calc. the average mu */
1127 /* Update the dipole distribution */
1128 ibin = gmx::roundToInt(ndipbin*mu_mol/mu_max);
1129 if (ibin < ndipbin)
1131 dipole_bin[ibin]++;
1134 if (fndip3d)
1136 rvec2sprvec(dipole[i], dipsp[i]);
1138 if (dipsp[i][YY] > -M_PI && dipsp[i][YY] < -0.5*M_PI)
1140 if (dipsp[i][ZZ] < 0.5 * M_PI)
1142 ncolour = 1;
1144 else
1146 ncolour = 2;
1149 else if (dipsp[i][YY] > -0.5*M_PI && dipsp[i][YY] < 0.0*M_PI)
1151 if (dipsp[i][ZZ] < 0.5 * M_PI)
1153 ncolour = 3;
1155 else
1157 ncolour = 4;
1160 else if (dipsp[i][YY] > 0.0 && dipsp[i][YY] < 0.5*M_PI)
1162 if (dipsp[i][ZZ] < 0.5 * M_PI)
1164 ncolour = 5;
1166 else
1168 ncolour = 6;
1171 else if (dipsp[i][YY] > 0.5*M_PI && dipsp[i][YY] < M_PI)
1173 if (dipsp[i][ZZ] < 0.5 * M_PI)
1175 ncolour = 7;
1177 else
1179 ncolour = 8;
1182 if (dip3d)
1184 fprintf(dip3d, "set arrow %d from %f, %f, %f to %f, %f, %f lt %d # %d %d\n",
1185 i+1,
1186 x[ind0][XX],
1187 x[ind0][YY],
1188 x[ind0][ZZ],
1189 x[ind0][XX]+dipole[i][XX]/25,
1190 x[ind0][YY]+dipole[i][YY]/25,
1191 x[ind0][ZZ]+dipole[i][ZZ]/25,
1192 ncolour, ind0, i);
1195 } /* End loop of all molecules in frame */
1197 if (dip3d)
1199 fprintf(dip3d, "set title \"t = %4.3f\"\n", t);
1200 fprintf(dip3d, "set xrange [0.0:%4.2f]\n", box[XX][XX]);
1201 fprintf(dip3d, "set yrange [0.0:%4.2f]\n", box[YY][YY]);
1202 fprintf(dip3d, "set zrange [0.0:%4.2f]\n\n", box[ZZ][ZZ]);
1203 fprintf(dip3d, "splot 'dummy.dat' using 1:2:3 w vec\n");
1204 fprintf(dip3d, "pause -1 'Hit return to continue'\n");
1208 /* Compute square of total dipole */
1209 for (m = 0; (m < DIM); m++)
1211 M_av2[m] = M_av[m]*M_av[m];
1214 if (cosaver)
1216 compute_avercos(gnx_tot, dipole, &dd, dipaxis, bPairs);
1217 rms_cos = std::sqrt(gmx::square(dipaxis[XX]-0.5)+
1218 gmx::square(dipaxis[YY]-0.5)+
1219 gmx::square(dipaxis[ZZ]-0.5));
1220 if (bPairs)
1222 fprintf(caver, "%10.3e %10.3e %10.3e %10.3e %10.3e %10.3e\n",
1223 t, dd, rms_cos, dipaxis[XX], dipaxis[YY], dipaxis[ZZ]);
1225 else
1227 fprintf(caver, "%10.3e %10.3e %10.3e %10.3e %10.3e\n",
1228 t, rms_cos, dipaxis[XX], dipaxis[YY], dipaxis[ZZ]);
1232 if (bGkr)
1234 do_gkr(gkrbin, ncos, gnx, molindex, mols->index, x, dipole, ePBC, box,
1235 atom, gkatom);
1238 if (bTotal)
1240 tel3 = DIM*teller;
1241 muall[0][tel3+XX] = M_av[XX];
1242 muall[0][tel3+YY] = M_av[YY];
1243 muall[0][tel3+ZZ] = M_av[ZZ];
1246 /* Write to file the total dipole moment of the box, and its components
1247 * for this frame.
1249 if ((skip == 0) || ((teller % skip) == 0))
1251 fprintf(outmtot, "%10g %12.8e %12.8e %12.8e %12.8e\n",
1252 t, M_av[XX], M_av[YY], M_av[ZZ],
1253 std::sqrt(M_av2[XX]+M_av2[YY]+M_av2[ZZ]));
1256 for (m = 0; (m < DIM); m++)
1258 M[m] += M_av[m];
1259 M2[m] += M_av2[m];
1260 M4[m] += gmx::square(M_av2[m]);
1262 /* Increment loop counter */
1263 teller++;
1265 /* Calculate for output the running averages */
1266 invtel = 1.0/teller;
1267 M2_ave = (M2[XX]+M2[YY]+M2[ZZ])*invtel;
1268 M_ave2 = invtel*(invtel*(M[XX]*M[XX] + M[YY]*M[YY] + M[ZZ]*M[ZZ]));
1269 M_diff = M2_ave - M_ave2;
1271 /* Compute volume from box in traj, else we use the one from above */
1272 if (!bMU)
1274 volume = det(box);
1276 vol_aver += volume;
1278 epsilon = calc_eps(M_diff, (vol_aver/teller), epsilonRF, temp);
1280 /* Calculate running average for dipole */
1281 if (mu_ave != 0)
1283 mu_aver = (mu_ave/gnx_tot)*invtel;
1286 if ((skip == 0) || ((teller % skip) == 0))
1288 /* Write to file < |M|^2 >, |< M >|^2. And the difference between
1289 * the two. Here M is sum mu_i. Further write the finite system
1290 * Kirkwood G factor and epsilon.
1292 fprintf(outaver, "%10g %10.3e %10.3e %10.3e %10.3e\n",
1293 t, M2_ave, M_ave2, M_diff, M_ave2/M2_ave);
1295 if (fnadip)
1297 real aver;
1298 gmx_stats_get_average(muframelsq, &aver);
1299 fprintf(adip, "%10g %f \n", t, aver);
1301 /*if (dipole)
1302 printf("%f %f\n", norm(dipole[0]), norm(dipole[1]));
1304 if (!bMU || (mu_aver != -1))
1306 /* Finite system Kirkwood G-factor */
1307 Gk = M_diff/(gnx_tot*mu_aver*mu_aver);
1308 /* Infinite system Kirkwood G-factor */
1309 if (epsilonRF == 0.0)
1311 g_k = ((2*epsilon+1)*Gk/(3*epsilon));
1313 else
1315 g_k = ((2*epsilonRF+epsilon)*(2*epsilon+1)*
1316 Gk/(3*epsilon*(2*epsilonRF+1)));
1319 fprintf(outeps, "%10g %10.3e %10.3e %10.3e\n", t, epsilon, Gk, g_k);
1322 else
1324 fprintf(outeps, "%10g %12.8e\n", t, epsilon);
1327 gmx_stats_free(muframelsq);
1329 if (bMU)
1331 bCont = read_mu_from_enx(fmu, iVol, iMu, mu_t, &volume, &t, nre, fr);
1333 else
1335 bCont = read_next_x(oenv, status, &t, x, box);
1337 timecheck = check_times(t);
1339 while (bCont && (timecheck == 0) );
1341 gmx_rmpbc_done(gpbc);
1343 if (!bMU)
1345 close_trx(status);
1348 xvgrclose(outmtot);
1349 xvgrclose(outaver);
1350 xvgrclose(outeps);
1352 if (fnadip)
1354 xvgrclose(adip);
1357 if (cosaver)
1359 xvgrclose(caver);
1362 if (dip3d)
1364 fprintf(dip3d, "set xrange [0.0:%4.2f]\n", box[XX][XX]);
1365 fprintf(dip3d, "set yrange [0.0:%4.2f]\n", box[YY][YY]);
1366 fprintf(dip3d, "set zrange [0.0:%4.2f]\n\n", box[ZZ][ZZ]);
1367 fprintf(dip3d, "splot 'dummy.dat' using 1:2:3 w vec\n");
1368 fprintf(dip3d, "pause -1 'Hit return to continue'\n");
1369 gmx_ffclose(dip3d);
1372 if (bSlab)
1374 dump_slab_dipoles(slabfn, idim, nslices, slab_dipoles, box, teller, oenv);
1375 sfree(slab_dipoles);
1378 vol_aver /= teller;
1379 printf("Average volume over run is %g\n", vol_aver);
1380 if (bGkr)
1382 print_gkrbin(gkrfn, gkrbin, gnx[0], teller, vol_aver, oenv);
1383 print_cmap(cmap, gkrbin, nlevels);
1385 /* Autocorrelation function */
1386 if (bCorr)
1388 if (teller < 2)
1390 printf("Not enough frames for autocorrelation\n");
1392 else
1394 dt = (t1 - t0)/(teller-1);
1395 printf("t0 %g, t %g, teller %d\n", t0, t, teller);
1397 mode = eacVector;
1399 if (bTotal)
1401 do_autocorr(corf, oenv, "Autocorrelation Function of Total Dipole",
1402 teller, 1, muall, dt, mode, TRUE);
1404 else
1406 do_autocorr(corf, oenv, "Dipole Autocorrelation Function",
1407 teller, gnx_tot, muall, dt,
1408 mode, std::strcmp(corrtype, "molsep") != 0);
1412 if (!bMU)
1414 real aver, sigma, error;
1416 gmx_stats_get_ase(mulsq, &aver, &sigma, &error);
1417 printf("\nDipole moment (Debye)\n");
1418 printf("---------------------\n");
1419 printf("Average = %8.4f Std. Dev. = %8.4f Error = %8.4f\n",
1420 aver, sigma, error);
1421 if (bQuad)
1423 rvec a, s, e;
1424 for (m = 0; (m < DIM); m++)
1426 gmx_stats_get_ase(mulsq, &(a[m]), &(s[m]), &(e[m]));
1429 printf("\nQuadrupole moment (Debye-Ang)\n");
1430 printf("-----------------------------\n");
1431 printf("Averages = %8.4f %8.4f %8.4f\n", a[XX], a[YY], a[ZZ]);
1432 printf("Std. Dev. = %8.4f %8.4f %8.4f\n", s[XX], s[YY], s[ZZ]);
1433 printf("Error = %8.4f %8.4f %8.4f\n", e[XX], e[YY], e[ZZ]);
1435 printf("\n");
1437 printf("The following averages for the complete trajectory have been calculated:\n\n");
1438 printf(" Total < M_x > = %g Debye\n", M[XX]/teller);
1439 printf(" Total < M_y > = %g Debye\n", M[YY]/teller);
1440 printf(" Total < M_z > = %g Debye\n\n", M[ZZ]/teller);
1442 printf(" Total < M_x^2 > = %g Debye^2\n", M2[XX]/teller);
1443 printf(" Total < M_y^2 > = %g Debye^2\n", M2[YY]/teller);
1444 printf(" Total < M_z^2 > = %g Debye^2\n\n", M2[ZZ]/teller);
1446 printf(" Total < |M|^2 > = %g Debye^2\n", M2_ave);
1447 printf(" Total |< M >|^2 = %g Debye^2\n\n", M_ave2);
1449 printf(" < |M|^2 > - |< M >|^2 = %g Debye^2\n\n", M_diff);
1451 if (!bMU || (mu_aver != -1))
1453 printf("Finite system Kirkwood g factor G_k = %g\n", Gk);
1454 printf("Infinite system Kirkwood g factor g_k = %g\n\n", g_k);
1456 printf("Epsilon = %g\n", epsilon);
1458 if (!bMU)
1460 /* Write to file the dipole moment distibution during the simulation.
1462 outdd = xvgropen(dipdist, "Dipole Moment Distribution", "mu (Debye)", "", oenv);
1463 for (i = 0; (i < ndipbin); i++)
1465 fprintf(outdd, "%10g %10f\n",
1466 (i*mu_max)/ndipbin, static_cast<real>(dipole_bin[i])/teller);
1468 xvgrclose(outdd);
1469 sfree(dipole_bin);
1471 if (bGkr)
1473 done_gkrbin(&gkrbin);
1477 static void dipole_atom2molindex(int *n, int *index, const t_block *mols)
1479 int nmol, i, j, m;
1481 nmol = 0;
1482 i = 0;
1483 while (i < *n)
1485 m = 0;
1486 while (m < mols->nr && index[i] != mols->index[m])
1488 m++;
1490 if (m == mols->nr)
1492 gmx_fatal(FARGS, "index[%d]=%d does not correspond to the first atom of a molecule", i+1, index[i]+1);
1494 for (j = mols->index[m]; j < mols->index[m+1]; j++)
1496 if (i >= *n || index[i] != j)
1498 gmx_fatal(FARGS, "The index group is not a set of whole molecules");
1500 i++;
1502 /* Modify the index in place */
1503 index[nmol++] = m;
1505 printf("There are %d molecules in the selection\n", nmol);
1507 *n = nmol;
1509 int gmx_dipoles(int argc, char *argv[])
1511 const char *desc[] = {
1512 "[THISMODULE] computes the total dipole plus fluctuations of a simulation",
1513 "system. From this you can compute e.g. the dielectric constant for",
1514 "low-dielectric media.",
1515 "For molecules with a net charge, the net charge is subtracted at",
1516 "center of mass of the molecule.[PAR]",
1517 "The file [TT]Mtot.xvg[tt] contains the total dipole moment of a frame, the",
1518 "components as well as the norm of the vector.",
1519 "The file [TT]aver.xvg[tt] contains [CHEVRON][MAG][GRK]mu[grk][mag]^2[chevron] and [MAG][CHEVRON][GRK]mu[grk][chevron][mag]^2 during the",
1520 "simulation.",
1521 "The file [TT]dipdist.xvg[tt] contains the distribution of dipole moments during",
1522 "the simulation",
1523 "The value of [TT]-mumax[tt] is used as the highest value in the distribution graph.[PAR]",
1524 "Furthermore, the dipole autocorrelation function will be computed when",
1525 "option [TT]-corr[tt] is used. The output file name is given with the [TT]-c[tt]",
1526 "option.",
1527 "The correlation functions can be averaged over all molecules",
1528 "([TT]mol[tt]), plotted per molecule separately ([TT]molsep[tt])",
1529 "or it can be computed over the total dipole moment of the simulation box",
1530 "([TT]total[tt]).[PAR]",
1531 "Option [TT]-g[tt] produces a plot of the distance dependent Kirkwood",
1532 "G-factor, as well as the average cosine of the angle between the dipoles",
1533 "as a function of the distance. The plot also includes gOO and hOO",
1534 "according to Nymand & Linse, J. Chem. Phys. 112 (2000) pp 6386-6395. In the same plot, ",
1535 "we also include the energy per scale computed by taking the inner product of",
1536 "the dipoles divided by the distance to the third power.[PAR]",
1537 "[PAR]",
1538 "EXAMPLES[PAR]",
1539 "[TT]gmx dipoles -corr mol -P 1 -o dip_sqr -mu 2.273 -mumax 5.0[tt][PAR]",
1540 "This will calculate the autocorrelation function of the molecular",
1541 "dipoles using a first order Legendre polynomial of the angle of the",
1542 "dipole vector and itself a time t later. For this calculation 1001",
1543 "frames will be used. Further, the dielectric constant will be calculated",
1544 "using an [TT]-epsilonRF[tt] of infinity (default), temperature of 300 K (default) and",
1545 "an average dipole moment of the molecule of 2.273 (SPC). For the",
1546 "distribution function a maximum of 5.0 will be used."
1548 real mu_max = 5, mu_aver = -1, rcmax = 0;
1549 real epsilonRF = 0.0, temp = 300;
1550 gmx_bool bPairs = TRUE, bPhi = FALSE, bQuad = FALSE;
1551 const char *corrtype[] = {nullptr, "none", "mol", "molsep", "total", nullptr};
1552 const char *axtitle = "Z";
1553 int nslices = 10; /* nr of slices defined */
1554 int skip = 0, nFA = 0, nFB = 0, ncos = 1;
1555 int nlevels = 20, ndegrees = 90;
1556 gmx_output_env_t *oenv;
1557 t_pargs pa[] = {
1558 { "-mu", FALSE, etREAL, {&mu_aver},
1559 "dipole of a single molecule (in Debye)" },
1560 { "-mumax", FALSE, etREAL, {&mu_max},
1561 "max dipole in Debye (for histogram)" },
1562 { "-epsilonRF", FALSE, etREAL, {&epsilonRF},
1563 "[GRK]epsilon[grk] of the reaction field used during the simulation, needed for dielectric constant calculation. WARNING: 0.0 means infinity (default)" },
1564 { "-skip", FALSE, etINT, {&skip},
1565 "Skip steps in the output (but not in the computations)" },
1566 { "-temp", FALSE, etREAL, {&temp},
1567 "Average temperature of the simulation (needed for dielectric constant calculation)" },
1568 { "-corr", FALSE, etENUM, {corrtype},
1569 "Correlation function to calculate" },
1570 { "-pairs", FALSE, etBOOL, {&bPairs},
1571 "Calculate [MAG][COS][GRK]theta[grk][cos][mag] between all pairs of molecules. May be slow" },
1572 { "-quad", FALSE, etBOOL, {&bQuad},
1573 "Take quadrupole into account"},
1574 { "-ncos", FALSE, etINT, {&ncos},
1575 "Must be 1 or 2. Determines whether the [CHEVRON][COS][GRK]theta[grk][cos][chevron] is computed between all molecules in one group, or between molecules in two different groups. This turns on the [TT]-g[tt] flag." },
1576 { "-axis", FALSE, etSTR, {&axtitle},
1577 "Take the normal on the computational box in direction X, Y or Z." },
1578 { "-sl", FALSE, etINT, {&nslices},
1579 "Divide the box into this number of slices." },
1580 { "-gkratom", FALSE, etINT, {&nFA},
1581 "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" },
1582 { "-gkratom2", FALSE, etINT, {&nFB},
1583 "Same as previous option in case ncos = 2, i.e. dipole interaction between two groups of molecules" },
1584 { "-rcmax", FALSE, etREAL, {&rcmax},
1585 "Maximum distance to use in the dipole orientation distribution (with ncos == 2). If zero, a criterion based on the box length will be used." },
1586 { "-phi", FALSE, etBOOL, {&bPhi},
1587 "Plot the 'torsion angle' defined as the rotation of the two dipole vectors around the distance vector between the two molecules in the [REF].xpm[ref] file from the [TT]-cmap[tt] option. By default the cosine of the angle between the dipoles is plotted." },
1588 { "-nlevels", FALSE, etINT, {&nlevels},
1589 "Number of colors in the cmap output" },
1590 { "-ndegrees", FALSE, etINT, {&ndegrees},
1591 "Number of divisions on the [IT]y[it]-axis in the cmap output (for 180 degrees)" }
1593 int *gnx;
1594 int nFF[2];
1595 int **grpindex;
1596 char **grpname = nullptr;
1597 gmx_bool bGkr, bMU, bSlab;
1598 t_filenm fnm[] = {
1599 { efEDR, "-en", nullptr, ffOPTRD },
1600 { efTRX, "-f", nullptr, ffREAD },
1601 { efTPR, nullptr, nullptr, ffREAD },
1602 { efNDX, nullptr, nullptr, ffOPTRD },
1603 { efXVG, "-o", "Mtot", ffWRITE },
1604 { efXVG, "-eps", "epsilon", ffWRITE },
1605 { efXVG, "-a", "aver", ffWRITE },
1606 { efXVG, "-d", "dipdist", ffWRITE },
1607 { efXVG, "-c", "dipcorr", ffOPTWR },
1608 { efXVG, "-g", "gkr", ffOPTWR },
1609 { efXVG, "-adip", "adip", ffOPTWR },
1610 { efXVG, "-dip3d", "dip3d", ffOPTWR },
1611 { efXVG, "-cos", "cosaver", ffOPTWR },
1612 { efXPM, "-cmap", "cmap", ffOPTWR },
1613 { efXVG, "-slab", "slab", ffOPTWR }
1615 #define NFILE asize(fnm)
1616 int npargs;
1617 t_pargs *ppa;
1618 t_topology *top;
1619 int ePBC;
1620 int k, natoms;
1621 matrix box;
1623 npargs = asize(pa);
1624 ppa = add_acf_pargs(&npargs, pa);
1625 if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW,
1626 NFILE, fnm, npargs, ppa, asize(desc), desc, 0, nullptr, &oenv))
1628 sfree(ppa);
1629 return 0;
1632 printf("Using %g as mu_max and %g as the dipole moment.\n",
1633 mu_max, mu_aver);
1634 if (epsilonRF == 0.0)
1636 printf("WARNING: EpsilonRF = 0.0, this really means EpsilonRF = infinity\n");
1639 bMU = opt2bSet("-en", NFILE, fnm);
1640 if (bMU)
1642 gmx_fatal(FARGS, "Due to new ways of treating molecules in GROMACS the total dipole in the energy file may be incorrect, because molecules can be split over periodic boundary conditions before computing the dipole. Please use your trajectory file.");
1644 bGkr = opt2bSet("-g", NFILE, fnm);
1645 if (opt2parg_bSet("-ncos", asize(pa), pa))
1647 if ((ncos != 1) && (ncos != 2))
1649 gmx_fatal(FARGS, "ncos has to be either 1 or 2");
1651 bGkr = TRUE;
1653 bSlab = (opt2bSet("-slab", NFILE, fnm) || opt2parg_bSet("-sl", asize(pa), pa) ||
1654 opt2parg_bSet("-axis", asize(pa), pa));
1655 if (bMU)
1657 if (bQuad)
1659 printf("WARNING: Can not determine quadrupoles from energy file\n");
1660 bQuad = FALSE;
1662 if (bGkr)
1664 printf("WARNING: Can not determine Gk(r) from energy file\n");
1665 bGkr = FALSE;
1666 ncos = 1;
1668 if (mu_aver == -1)
1670 printf("WARNING: Can not calculate Gk and gk, since you did\n"
1671 " not enter a valid dipole for the molecules\n");
1675 snew(top, 1);
1676 ePBC = read_tpx_top(ftp2fn(efTPR, NFILE, fnm), nullptr, box,
1677 &natoms, nullptr, nullptr, top);
1679 snew(gnx, ncos);
1680 snew(grpname, ncos);
1681 snew(grpindex, ncos);
1682 get_index(&top->atoms, ftp2fn_null(efNDX, NFILE, fnm),
1683 ncos, gnx, grpindex, grpname);
1684 for (k = 0; (k < ncos); k++)
1686 dipole_atom2molindex(&gnx[k], grpindex[k], &(top->mols));
1687 neutralize_mols(gnx[k], grpindex[k], &(top->mols), top->atoms.atom);
1689 nFF[0] = nFA;
1690 nFF[1] = nFB;
1691 do_dip(top, ePBC, det(box), ftp2fn(efTRX, NFILE, fnm),
1692 opt2fn("-o", NFILE, fnm), opt2fn("-eps", NFILE, fnm),
1693 opt2fn("-a", NFILE, fnm), opt2fn("-d", NFILE, fnm),
1694 opt2fn_null("-cos", NFILE, fnm),
1695 opt2fn_null("-dip3d", NFILE, fnm), opt2fn_null("-adip", NFILE, fnm),
1696 bPairs, corrtype[0],
1697 opt2fn("-c", NFILE, fnm),
1698 bGkr, opt2fn("-g", NFILE, fnm),
1699 bPhi, &nlevels, ndegrees,
1700 ncos,
1701 opt2fn("-cmap", NFILE, fnm), rcmax,
1702 bQuad, bMU, opt2fn("-en", NFILE, fnm),
1703 gnx, grpindex, mu_max, mu_aver, epsilonRF, temp, nFF, skip,
1704 bSlab, nslices, axtitle, opt2fn("-slab", NFILE, fnm), oenv);
1706 do_view(oenv, opt2fn("-o", NFILE, fnm), "-autoscale xy -nxy");
1707 do_view(oenv, opt2fn("-eps", NFILE, fnm), "-autoscale xy -nxy");
1708 do_view(oenv, opt2fn("-a", NFILE, fnm), "-autoscale xy -nxy");
1709 do_view(oenv, opt2fn("-d", NFILE, fnm), "-autoscale xy");
1710 do_view(oenv, opt2fn("-c", NFILE, fnm), "-autoscale xy");
1712 return 0;