Introduce SimulatorBuilder
[gromacs.git] / src / gromacs / gmxana / gmx_dipoles.cpp
blob984d9287ce7a9ee1af1ee49996ea3255a29d305d
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/smalloc.h"
75 #define e2d(x) ENM2DEBYE*(x)
76 #define EANG2CM (E_CHARGE*1.0e-10) /* e Angstrom to Coulomb meter */
77 #define CM2D (SPEED_OF_LIGHT*1.0e+24) /* Coulomb meter to Debye */
79 typedef struct {
80 int nelem;
81 real spacing, radius;
82 real *elem;
83 int *count;
84 gmx_bool bPhi;
85 int nx, ny;
86 real **cmap;
87 } t_gkrbin;
89 static t_gkrbin *mk_gkrbin(real radius, real rcmax, gmx_bool bPhi, int ndegrees)
91 t_gkrbin *gb;
92 char *ptr;
93 int i;
95 snew(gb, 1);
97 if ((ptr = getenv("GMX_DIPOLE_SPACING")) != nullptr)
99 double bw = strtod(ptr, nullptr);
100 gb->spacing = bw;
102 else
104 gb->spacing = 0.01; /* nm */
106 gb->nelem = 1 + static_cast<int>(radius/gb->spacing);
107 if (rcmax == 0)
109 gb->nx = gb->nelem;
111 else
113 gb->nx = 1 + static_cast<int>(rcmax/gb->spacing);
115 gb->radius = radius;
116 snew(gb->elem, gb->nelem);
117 snew(gb->count, gb->nelem);
119 snew(gb->cmap, gb->nx);
120 gb->ny = std::max(2, ndegrees);
121 for (i = 0; (i < gb->nx); i++)
123 snew(gb->cmap[i], gb->ny);
125 gb->bPhi = bPhi;
127 return gb;
130 static void done_gkrbin(t_gkrbin **gb)
132 sfree((*gb)->elem);
133 sfree((*gb)->count);
134 sfree((*gb));
135 *gb = nullptr;
138 static void add2gkr(t_gkrbin *gb, real r, real cosa, real phi)
140 int cy, index = gmx::roundToInt(r/gb->spacing);
141 real alpha;
143 if (index < gb->nelem)
145 gb->elem[index] += cosa;
146 gb->count[index]++;
148 if (index < gb->nx)
150 alpha = std::acos(cosa);
151 if (gb->bPhi)
153 cy = static_cast<int>((M_PI+phi)*gb->ny/(2*M_PI));
155 else
157 cy = static_cast<int>((alpha*gb->ny)/M_PI); /*((1+cosa)*0.5*(gb->ny));*/
159 cy = std::min(gb->ny-1, std::max(0, cy));
160 if (debug)
162 fprintf(debug, "CY: %10f %5d\n", alpha, cy);
164 gb->cmap[index][cy] += 1;
168 static void rvec2sprvec(rvec dipcart, rvec dipsp)
170 clear_rvec(dipsp);
171 dipsp[0] = std::sqrt(dipcart[XX]*dipcart[XX]+dipcart[YY]*dipcart[YY]+dipcart[ZZ]*dipcart[ZZ]); /* R */
172 dipsp[1] = std::atan2(dipcart[YY], dipcart[XX]); /* Theta */
173 dipsp[2] = std::atan2(std::sqrt(dipcart[XX]*dipcart[XX]+dipcart[YY]*dipcart[YY]), dipcart[ZZ]); /* Phi */
178 static void do_gkr(t_gkrbin *gb, int ncos, int *ngrp, int *molindex[],
179 const int mindex[], rvec x[], rvec mu[],
180 int ePBC, const matrix box, const t_atom *atom, const int *nAtom)
182 static rvec *xcm[2] = { nullptr, nullptr};
183 int gi, gj, j0, j1, i, j, k, n, grp0, grp1;
184 real qtot, q, cosa, rr, phi;
185 rvec dx;
186 t_pbc pbc;
188 for (n = 0; (n < ncos); n++)
190 if (!xcm[n])
192 snew(xcm[n], ngrp[n]);
194 for (i = 0; (i < ngrp[n]); i++)
196 /* Calculate center of mass of molecule */
197 gi = molindex[n][i];
198 j0 = mindex[gi];
200 if (nAtom[n] > 0)
202 copy_rvec(x[j0+nAtom[n]-1], xcm[n][i]);
204 else
206 j1 = mindex[gi+1];
207 clear_rvec(xcm[n][i]);
208 qtot = 0;
209 for (j = j0; j < j1; j++)
211 q = std::abs(atom[j].q);
212 qtot += q;
213 for (k = 0; k < DIM; k++)
215 xcm[n][i][k] += q*x[j][k];
218 svmul(1/qtot, xcm[n][i], xcm[n][i]);
222 set_pbc(&pbc, ePBC, box);
223 grp0 = 0;
224 grp1 = ncos-1;
225 for (i = 0; i < ngrp[grp0]; i++)
227 gi = molindex[grp0][i];
228 j0 = (ncos == 2) ? 0 : i+1;
229 for (j = j0; j < ngrp[grp1]; j++)
231 gj = molindex[grp1][j];
232 if ((iprod(mu[gi], mu[gi]) > 0) && (iprod(mu[gj], mu[gj]) > 0))
234 /* Compute distance between molecules including PBC */
235 pbc_dx(&pbc, xcm[grp0][i], xcm[grp1][j], dx);
236 rr = norm(dx);
238 if (gb->bPhi)
240 rvec xi, xj, xk, xl;
241 rvec r_ij, r_kj, r_kl, mm, nn;
242 int t1, t2, t3;
244 copy_rvec(xcm[grp0][i], xj);
245 copy_rvec(xcm[grp1][j], xk);
246 rvec_add(xj, mu[gi], xi);
247 rvec_add(xk, mu[gj], xl);
248 phi = dih_angle(xi, xj, xk, xl, &pbc,
249 r_ij, r_kj, r_kl, mm, nn, /* out */
250 &t1, &t2, &t3);
251 cosa = std::cos(phi);
253 else
255 cosa = cos_angle(mu[gi], mu[gj]);
256 phi = 0;
258 if (debug || std::isnan(cosa))
260 fprintf(debug ? debug : stderr,
261 "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",
262 gi, mu[gi][XX], mu[gi][YY], mu[gi][ZZ], norm(mu[gi]),
263 gj, mu[gj][XX], mu[gj][YY], mu[gj][ZZ], norm(mu[gj]),
264 rr, cosa);
267 add2gkr(gb, rr, cosa, phi);
273 static real normalize_cmap(t_gkrbin *gb)
275 int i, j;
276 real hi;
277 double vol;
279 hi = 0;
280 for (i = 0; (i < gb->nx); i++)
282 vol = 4*M_PI*gmx::square(gb->spacing*i)*gb->spacing;
283 for (j = 0; (j < gb->ny); j++)
285 gb->cmap[i][j] /= vol;
286 hi = std::max(hi, gb->cmap[i][j]);
289 if (hi <= 0)
291 gmx_fatal(FARGS, "No data in the cmap");
293 return hi;
296 static void print_cmap(const char *cmap, t_gkrbin *gb, int *nlevels)
298 FILE *out;
299 int i, j;
300 real hi;
302 real *xaxis, *yaxis;
303 t_rgb rlo = { 1, 1, 1 };
304 t_rgb rhi = { 0, 0, 0 };
306 hi = normalize_cmap(gb);
307 snew(xaxis, gb->nx+1);
308 for (i = 0; (i < gb->nx+1); i++)
310 xaxis[i] = i*gb->spacing;
312 snew(yaxis, gb->ny);
313 for (j = 0; (j < gb->ny); j++)
315 if (gb->bPhi)
317 yaxis[j] = (360.0*j)/(gb->ny-1.0)-180;
319 else
321 yaxis[j] = (180.0*j)/(gb->ny-1.0);
323 /*2.0*j/(gb->ny-1.0)-1.0;*/
325 out = gmx_ffopen(cmap, "w");
326 write_xpm(out, 0,
327 "Dipole Orientation Distribution", "Fraction", "r (nm)",
328 gb->bPhi ? "Phi" : "Alpha",
329 gb->nx, gb->ny, xaxis, yaxis,
330 gb->cmap, 0, hi, rlo, rhi, nlevels);
331 gmx_ffclose(out);
332 sfree(xaxis);
333 sfree(yaxis);
336 static void print_gkrbin(const char *fn, t_gkrbin *gb,
337 int ngrp, int nframes, real volume,
338 const gmx_output_env_t *oenv)
340 /* We compute Gk(r), gOO and hOO according to
341 * Nymand & Linse, JCP 112 (2000) pp 6386-6395.
342 * In this implementation the angle between dipoles is stored
343 * rather than their inner product. This allows to take polarizible
344 * models into account. The RDF is calculated as well, almost for free!
346 FILE *fp;
347 const char *leg[] = { "G\\sk\\N(r)", "< cos >", "h\\sOO\\N", "g\\sOO\\N", "Energy" };
348 int i, last;
349 real x0, x1, ggg, Gkr, vol_s, rho, gOO, hOO, cosav, ener;
350 double fac;
352 fp = xvgropen(fn, "Distance dependent Gk", "r (nm)", "G\\sk\\N(r)", oenv);
353 xvgr_legend(fp, asize(leg), leg, oenv);
355 Gkr = 1; /* Self-dipole inproduct = 1 */
356 rho = ngrp/volume;
358 if (debug)
360 fprintf(debug, "Number density is %g molecules / nm^3\n", rho);
361 fprintf(debug, "ngrp = %d, nframes = %d\n", ngrp, nframes);
364 last = gb->nelem-1;
365 while (last > 1 && gb->elem[last-1] == 0)
367 last--;
370 /* Divide by dipole squared, by number of frames, by number of origins.
371 * Multiply by 2 because we only take half the matrix of interactions
372 * into account.
374 fac = 2.0/(ngrp * nframes);
376 x0 = 0;
377 for (i = 0; i < last; i++)
379 /* Centre of the coordinate in the spherical layer */
380 x1 = x0+gb->spacing;
382 /* Volume of the layer */
383 vol_s = (4.0/3.0)*M_PI*(x1*x1*x1-x0*x0*x0);
385 /* gOO */
386 gOO = gb->count[i]*fac/(rho*vol_s);
388 /* Dipole correlation hOO, normalized by the relative number density, like
389 * in a Radial distribution function.
391 ggg = gb->elem[i]*fac;
392 hOO = 3.0*ggg/(rho*vol_s);
393 Gkr += ggg;
394 if (gb->count[i])
396 cosav = gb->elem[i]/gb->count[i];
398 else
400 cosav = 0;
402 ener = -0.5*cosav*ONE_4PI_EPS0/(x1*x1*x1);
404 fprintf(fp, "%10.5e %12.5e %12.5e %12.5e %12.5e %12.5e\n",
405 x1, Gkr, cosav, hOO, gOO, ener);
407 /* Swap x0 and x1 */
408 x0 = x1;
410 xvgrclose(fp);
413 static gmx_bool read_mu_from_enx(ener_file_t fmu, int Vol, const ivec iMu, rvec mu, real *vol,
414 real *t, int nre, t_enxframe *fr)
416 int i;
417 gmx_bool bCont;
418 char buf[22];
420 bCont = do_enx(fmu, fr);
421 if (fr->nre != nre)
423 fprintf(stderr, "Something strange: expected %d entries in energy file at step %s\n(time %g) but found %d entries\n",
424 nre, gmx_step_str(fr->step, buf), fr->t, fr->nre);
427 if (bCont)
429 if (Vol != -1) /* we've got Volume in the energy file */
431 *vol = fr->ener[Vol].e;
433 for (i = 0; i < DIM; i++)
435 mu[i] = fr->ener[iMu[i]].e;
437 *t = fr->t;
440 return bCont;
443 static void neutralize_mols(int n, const int *index, const t_block *mols, t_atom *atom)
445 double mtot, qtot;
446 int ncharged, m, a0, a1, a;
448 ncharged = 0;
449 for (m = 0; m < n; m++)
451 a0 = mols->index[index[m]];
452 a1 = mols->index[index[m]+1];
453 mtot = 0;
454 qtot = 0;
455 for (a = a0; a < a1; a++)
457 mtot += atom[a].m;
458 qtot += atom[a].q;
460 /* This check is only for the count print */
461 if (std::abs(qtot) > 0.01)
463 ncharged++;
465 if (mtot > 0)
467 /* Remove the net charge at the center of mass */
468 for (a = a0; a < a1; a++)
470 atom[a].q -= qtot*atom[a].m/mtot;
475 if (ncharged)
477 printf("There are %d charged molecules in the selection,\n"
478 "will subtract their charge at their center of mass\n", ncharged);
482 static void mol_dip(int k0, int k1, rvec x[], const t_atom atom[], rvec mu)
484 int k, m;
485 real q;
487 clear_rvec(mu);
488 for (k = k0; (k < k1); k++)
490 q = e2d(atom[k].q);
491 for (m = 0; (m < DIM); m++)
493 mu[m] += q*x[k][m];
498 static void mol_quad(int k0, int k1, rvec x[], const t_atom atom[], rvec quad)
500 int i, k, m, n, niter;
501 real q, r2, mass, masstot;
502 rvec com; /* center of mass */
503 rvec r; /* distance of atoms to center of mass */
504 double **inten;
505 double dd[DIM], **ev;
507 snew(inten, DIM);
508 snew(ev, DIM);
509 for (i = 0; (i < DIM); i++)
511 snew(inten[i], DIM);
512 snew(ev[i], DIM);
513 dd[i] = 0.0;
516 /* Compute center of mass */
517 clear_rvec(com);
518 masstot = 0.0;
519 for (k = k0; (k < k1); k++)
521 mass = atom[k].m;
522 masstot += mass;
523 for (i = 0; (i < DIM); i++)
525 com[i] += mass*x[k][i];
528 svmul((1.0/masstot), com, com);
530 /* We want traceless quadrupole moments, so let us calculate the complete
531 * quadrupole moment tensor and diagonalize this tensor to get
532 * the individual components on the diagonal.
535 #define delta(a, b) (( (a) == (b) ) ? 1.0 : 0.0)
537 for (m = 0; (m < DIM); m++)
539 for (n = 0; (n < DIM); n++)
541 inten[m][n] = 0;
544 for (k = k0; (k < k1); k++) /* loop over atoms in a molecule */
546 q = (atom[k].q)*100.0;
547 rvec_sub(x[k], com, r);
548 r2 = iprod(r, r);
549 for (m = 0; (m < DIM); m++)
551 for (n = 0; (n < DIM); n++)
553 inten[m][n] += 0.5*q*(3.0*r[m]*r[n] - r2*delta(m, n))*EANG2CM*CM2D;
557 if (debug)
559 for (i = 0; (i < DIM); i++)
561 fprintf(debug, "Q[%d] = %8.3f %8.3f %8.3f\n",
562 i, inten[i][XX], inten[i][YY], inten[i][ZZ]);
566 /* We've got the quadrupole tensor, now diagonalize the sucker */
567 jacobi(inten, 3, dd, ev, &niter);
569 if (debug)
571 for (i = 0; (i < DIM); i++)
573 fprintf(debug, "ev[%d] = %8.3f %8.3f %8.3f\n",
574 i, ev[i][XX], ev[i][YY], ev[i][ZZ]);
576 for (i = 0; (i < DIM); i++)
578 fprintf(debug, "Q'[%d] = %8.3f %8.3f %8.3f\n",
579 i, inten[i][XX], inten[i][YY], inten[i][ZZ]);
582 /* Sort the eigenvalues, for water we know that the order is as follows:
584 * Q_yy, Q_zz, Q_xx
586 * At the moment I have no idea how this will work out for other molecules...
589 if (dd[1] > dd[0])
591 std::swap(dd[0], dd[1]);
593 if (dd[2] > dd[1])
595 std::swap(dd[1], dd[2]);
597 if (dd[1] > dd[0])
599 std::swap(dd[0], dd[1]);
602 for (m = 0; (m < DIM); m++)
604 quad[0] = dd[2]; /* yy */
605 quad[1] = dd[0]; /* zz */
606 quad[2] = dd[1]; /* xx */
609 if (debug)
611 pr_rvec(debug, 0, "Quadrupole", quad, DIM, TRUE);
614 /* clean-up */
615 for (i = 0; (i < DIM); i++)
617 sfree(inten[i]);
618 sfree(ev[i]);
620 sfree(inten);
621 sfree(ev);
625 * Calculates epsilon according to M. Neumann, Mol. Phys. 50, 841 (1983)
627 static real calc_eps(double M_diff, double volume, double epsRF, double temp)
629 double eps, A, teller, noemer;
630 double eps_0 = 8.854187817e-12; /* epsilon_0 in C^2 J^-1 m^-1 */
631 double fac = 1.112650021e-59; /* converts Debye^2 to C^2 m^2 */
633 A = M_diff*fac/(3*eps_0*volume*NANO*NANO*NANO*BOLTZMANN*temp);
635 if (epsRF == 0.0)
637 teller = 1 + A;
638 noemer = 1;
640 else
642 teller = 1 + (A*2*epsRF/(2*epsRF+1));
643 noemer = 1 - (A/(2*epsRF+1));
645 eps = teller / noemer;
647 return eps;
650 static void update_slab_dipoles(int k0, int k1, rvec x[], rvec mu,
651 int idim, int nslice, rvec slab_dipole[],
652 matrix box)
654 int k;
655 real xdim = 0;
657 for (k = k0; (k < k1); k++)
659 xdim += x[k][idim];
661 xdim /= (k1-k0);
662 k = (static_cast<int>(xdim*nslice/box[idim][idim] + nslice)) % nslice;
663 rvec_inc(slab_dipole[k], mu);
666 static void dump_slab_dipoles(const char *fn, int idim, int nslice,
667 rvec slab_dipole[], matrix box, int nframes,
668 const gmx_output_env_t *oenv)
670 FILE *fp;
671 char buf[STRLEN];
672 int i;
673 real mutot;
674 const char *leg_dim[4] = {
675 "\\f{12}m\\f{4}\\sX\\N",
676 "\\f{12}m\\f{4}\\sY\\N",
677 "\\f{12}m\\f{4}\\sZ\\N",
678 "\\f{12}m\\f{4}\\stot\\N"
681 sprintf(buf, "Box-%c (nm)", 'X'+idim);
682 fp = xvgropen(fn, "Average dipole moment per slab", buf, "\\f{12}m\\f{4} (D)",
683 oenv);
684 xvgr_legend(fp, DIM, leg_dim, oenv);
685 for (i = 0; (i < nslice); i++)
687 mutot = norm(slab_dipole[i])/nframes;
688 fprintf(fp, "%10.3f %10.3f %10.3f %10.3f %10.3f\n",
689 ((i+0.5)*box[idim][idim])/nslice,
690 slab_dipole[i][XX]/nframes,
691 slab_dipole[i][YY]/nframes,
692 slab_dipole[i][ZZ]/nframes,
693 mutot);
695 xvgrclose(fp);
696 do_view(oenv, fn, "-autoscale xy -nxy");
699 static void compute_avercos(int n, rvec dip[], real *dd, rvec axis, gmx_bool bPairs)
701 int i, j, k;
702 double dc, d, ddc1, ddc2, ddc3;
703 rvec xxx = { 1, 0, 0 };
704 rvec yyy = { 0, 1, 0 };
705 rvec zzz = { 0, 0, 1 };
707 d = 0;
708 ddc1 = ddc2 = ddc3 = 0;
709 for (i = k = 0; (i < n); i++)
711 ddc1 += std::abs(cos_angle(dip[i], xxx));
712 ddc2 += std::abs(cos_angle(dip[i], yyy));
713 ddc3 += std::abs(cos_angle(dip[i], zzz));
714 if (bPairs)
716 for (j = i+1; (j < n); j++, k++)
718 dc = cos_angle(dip[i], dip[j]);
719 d += std::abs(dc);
723 *dd = d/k;
724 axis[XX] = ddc1/n;
725 axis[YY] = ddc2/n;
726 axis[ZZ] = ddc3/n;
729 static void do_dip(const t_topology *top, int ePBC, real volume,
730 const char *fn,
731 const char *out_mtot, const char *out_eps,
732 const char *out_aver, const char *dipdist,
733 const char *cosaver, const char *fndip3d,
734 const char *fnadip, gmx_bool bPairs,
735 const char *corrtype, const char *corf,
736 gmx_bool bGkr, const char *gkrfn,
737 gmx_bool bPhi, int *nlevels, int ndegrees,
738 int ncos,
739 const char *cmap, real rcmax,
740 gmx_bool bQuad,
741 gmx_bool bMU, const char *mufn,
742 int *gnx, int *molindex[],
743 real mu_max, real mu_aver,
744 real epsilonRF, real temp,
745 int *gkatom, int skip,
746 gmx_bool bSlab, int nslices,
747 const char *axtitle, const char *slabfn,
748 const gmx_output_env_t *oenv)
750 const char *leg_mtot[] = {
751 "M\\sx \\N",
752 "M\\sy \\N",
753 "M\\sz \\N",
754 "|M\\stot \\N|"
756 #define NLEGMTOT asize(leg_mtot)
757 const char *leg_eps[] = {
758 "epsilon",
759 "G\\sk",
760 "g\\sk"
762 #define NLEGEPS asize(leg_eps)
763 const char *leg_aver[] = {
764 "< |M|\\S2\\N >",
765 "< |M| >\\S2\\N",
766 "< |M|\\S2\\N > - < |M| >\\S2\\N",
767 "< |M| >\\S2\\N / < |M|\\S2\\N >"
769 #define NLEGAVER asize(leg_aver)
770 const char *leg_cosaver[] = {
771 "\\f{4}<|cos\\f{12}q\\f{4}\\sij\\N|>",
772 "RMSD cos",
773 "\\f{4}<|cos\\f{12}q\\f{4}\\siX\\N|>",
774 "\\f{4}<|cos\\f{12}q\\f{4}\\siY\\N|>",
775 "\\f{4}<|cos\\f{12}q\\f{4}\\siZ\\N|>"
777 #define NLEGCOSAVER asize(leg_cosaver)
778 const char *leg_adip[] = {
779 "<mu>",
780 "Std. Dev.",
781 "Error"
783 #define NLEGADIP asize(leg_adip)
785 FILE *outdd, *outmtot, *outaver, *outeps, *caver = nullptr;
786 FILE *dip3d = nullptr, *adip = nullptr;
787 rvec *x, *dipole = nullptr, mu_t, quad, *dipsp = nullptr;
788 t_gkrbin *gkrbin = nullptr;
789 gmx_enxnm_t *enm = nullptr;
790 t_enxframe *fr;
791 int nframes = 1000, nre, timecheck = 0, ncolour = 0;
792 ener_file_t fmu = nullptr;
793 int i, n, m, natom = 0, gnx_tot, teller, tel3;
794 t_trxstatus *status;
795 int *dipole_bin, ndipbin, ibin, iVol, idim = -1;
796 unsigned long mode;
797 real rcut = 0, t, t0, t1, dt, dd, rms_cos;
798 rvec dipaxis;
799 matrix box;
800 gmx_bool bCorr, bTotal, bCont;
801 double M_diff = 0, epsilon, invtel, vol_aver;
802 double mu_ave, mu_mol, M2_ave = 0, M_ave2 = 0, M_av[DIM], M_av2[DIM];
803 double M[3], M2[3], M4[3], Gk = 0, g_k = 0;
804 gmx_stats_t *Qlsq, mulsq, muframelsq = nullptr;
805 ivec iMu;
806 real **muall = nullptr;
807 rvec *slab_dipoles = nullptr;
808 const t_atom *atom = nullptr;
809 const t_block *mols = nullptr;
810 gmx_rmpbc_t gpbc = nullptr;
812 gnx_tot = gnx[0];
813 if (ncos > 1)
815 gnx_tot += gnx[1];
818 vol_aver = 0.0;
820 iVol = -1;
822 /* initialize to a negative value so we can see that it wasn't picked up */
823 iMu[XX] = iMu[YY] = iMu[ZZ] = -1;
824 if (bMU)
826 fmu = open_enx(mufn, "r");
827 do_enxnms(fmu, &nre, &enm);
829 /* Determine the indexes of the energy grps we need */
830 for (i = 0; (i < nre); i++)
832 if (std::strstr(enm[i].name, "Volume"))
834 iVol = i;
836 else if (std::strstr(enm[i].name, "Mu-X"))
838 iMu[XX] = i;
840 else if (std::strstr(enm[i].name, "Mu-Y"))
842 iMu[YY] = i;
844 else if (std::strstr(enm[i].name, "Mu-Z"))
846 iMu[ZZ] = i;
849 if (iMu[XX] < 0 || iMu[YY] < 0 || iMu[ZZ] < 0)
851 gmx_fatal(FARGS, "No index for Mu-X, Mu-Y or Mu-Z energy group.");
854 else
856 atom = top->atoms.atom;
857 mols = &(top->mols);
859 if ((iVol == -1) && bMU)
861 printf("Using Volume from topology: %g nm^3\n", volume);
864 /* Correlation stuff */
865 bCorr = (corrtype[0] != 'n');
866 bTotal = (corrtype[0] == 't');
867 if (bCorr)
869 if (bTotal)
871 snew(muall, 1);
872 snew(muall[0], nframes*DIM);
874 else
876 snew(muall, gnx[0]);
877 for (i = 0; (i < gnx[0]); i++)
879 snew(muall[i], nframes*DIM);
884 /* Allocate array which contains for every molecule in a frame the
885 * dipole moment.
887 if (!bMU)
889 snew(dipole, gnx_tot);
892 /* Statistics */
893 snew(Qlsq, DIM);
894 for (i = 0; (i < DIM); i++)
896 Qlsq[i] = gmx_stats_init();
898 mulsq = gmx_stats_init();
900 /* Open all the files */
901 outmtot = xvgropen(out_mtot,
902 "Total dipole moment of the simulation box vs. time",
903 "Time (ps)", "Total Dipole Moment (Debye)", oenv);
904 outeps = xvgropen(out_eps, "Epsilon and Kirkwood factors",
905 "Time (ps)", "", oenv);
906 outaver = xvgropen(out_aver, "Total dipole moment",
907 "Time (ps)", "D", oenv);
908 if (bSlab)
910 idim = axtitle[0] - 'X';
911 if ((idim < 0) || (idim >= DIM))
913 idim = axtitle[0] - 'x';
915 if ((idim < 0) || (idim >= DIM))
917 bSlab = FALSE;
919 if (nslices < 2)
921 bSlab = FALSE;
923 fprintf(stderr, "axtitle = %s, nslices = %d, idim = %d\n",
924 axtitle, nslices, idim);
925 if (bSlab)
927 snew(slab_dipoles, nslices);
928 fprintf(stderr, "Doing slab analysis\n");
932 if (fnadip)
934 adip = xvgropen(fnadip, "Average molecular dipole", "Dipole (D)", "", oenv);
935 xvgr_legend(adip, NLEGADIP, leg_adip, oenv);
938 if (cosaver)
940 caver = xvgropen(cosaver, bPairs ? "Average pair orientation" :
941 "Average absolute dipole orientation", "Time (ps)", "", oenv);
942 xvgr_legend(caver, NLEGCOSAVER, bPairs ? leg_cosaver : &(leg_cosaver[1]),
943 oenv);
946 if (fndip3d)
948 snew(dipsp, gnx_tot);
950 /* we need a dummy file for gnuplot */
951 dip3d = gmx_ffopen("dummy.dat", "w");
952 fprintf(dip3d, "%f %f %f", 0.0, 0.0, 0.0);
953 gmx_ffclose(dip3d);
955 dip3d = gmx_ffopen(fndip3d, "w");
958 gmx::BinaryInformationSettings settings;
959 settings.generatedByHeader(true);
960 settings.linePrefix("# ");
961 gmx::printBinaryInformation(dip3d, output_env_get_program_context(oenv),
962 settings);
964 GMX_CATCH_ALL_AND_EXIT_WITH_FATAL_ERROR;
967 /* Write legends to all the files */
968 xvgr_legend(outmtot, NLEGMTOT, leg_mtot, oenv);
969 xvgr_legend(outaver, NLEGAVER, leg_aver, oenv);
971 if (bMU && (mu_aver == -1))
973 xvgr_legend(outeps, NLEGEPS-2, leg_eps, oenv);
975 else
977 xvgr_legend(outeps, NLEGEPS, leg_eps, oenv);
980 snew(fr, 1);
981 clear_rvec(mu_t);
982 teller = 0;
983 /* Read the first frame from energy or traj file */
984 if (bMU)
988 bCont = read_mu_from_enx(fmu, iVol, iMu, mu_t, &volume, &t, nre, fr);
989 if (bCont)
991 timecheck = check_times(t);
992 if (timecheck < 0)
994 teller++;
996 if ((teller % 10) == 0)
998 fprintf(stderr, "\r Skipping Frame %6d, time: %8.3f", teller, t);
999 fflush(stderr);
1002 else
1004 printf("End of %s reached\n", mufn);
1005 break;
1008 while (bCont && (timecheck < 0));
1010 else
1012 natom = read_first_x(oenv, &status, fn, &t, &x, box);
1015 /* Calculate spacing for dipole bin (simple histogram) */
1016 ndipbin = 1 + static_cast<int>(mu_max/0.01);
1017 snew(dipole_bin, ndipbin);
1018 mu_ave = 0.0;
1019 for (m = 0; (m < DIM); m++)
1021 M[m] = M2[m] = M4[m] = 0.0;
1024 if (bGkr)
1026 /* Use 0.7 iso 0.5 to account for pressure scaling */
1027 /* rcut = 0.7*sqrt(max_cutoff2(box)); */
1028 rcut = 0.7*std::sqrt(gmx::square(box[XX][XX])+gmx::square(box[YY][YY])+gmx::square(box[ZZ][ZZ]));
1030 gkrbin = mk_gkrbin(rcut, rcmax, bPhi, ndegrees);
1032 gpbc = gmx_rmpbc_init(&top->idef, ePBC, natom);
1034 /* Start while loop over frames */
1035 t0 = t;
1036 teller = 0;
1039 if (bCorr && (teller >= nframes))
1041 nframes += 1000;
1042 if (bTotal)
1044 srenew(muall[0], nframes*DIM);
1046 else
1048 for (i = 0; (i < gnx_tot); i++)
1050 srenew(muall[i], nframes*DIM);
1054 t1 = t;
1056 muframelsq = gmx_stats_init();
1058 /* Initialise */
1059 for (m = 0; (m < DIM); m++)
1061 M_av2[m] = 0;
1064 if (bMU)
1066 /* Copy rvec into double precision local variable */
1067 for (m = 0; (m < DIM); m++)
1069 M_av[m] = mu_t[m];
1072 else
1074 /* Initialise */
1075 for (m = 0; (m < DIM); m++)
1077 M_av[m] = 0;
1080 gmx_rmpbc(gpbc, natom, box, x);
1082 /* Begin loop of all molecules in frame */
1083 for (n = 0; (n < ncos); n++)
1085 for (i = 0; (i < gnx[n]); i++)
1087 int ind0, ind1;
1089 ind0 = mols->index[molindex[n][i]];
1090 ind1 = mols->index[molindex[n][i]+1];
1092 mol_dip(ind0, ind1, x, atom, dipole[i]);
1093 gmx_stats_add_point(mulsq, 0, norm(dipole[i]), 0, 0);
1094 gmx_stats_add_point(muframelsq, 0, norm(dipole[i]), 0, 0);
1095 if (bSlab)
1097 update_slab_dipoles(ind0, ind1, x,
1098 dipole[i], idim, nslices, slab_dipoles, box);
1100 if (bQuad)
1102 mol_quad(ind0, ind1, x, atom, quad);
1103 for (m = 0; (m < DIM); m++)
1105 gmx_stats_add_point(Qlsq[m], 0, quad[m], 0, 0);
1108 if (bCorr && !bTotal)
1110 tel3 = DIM*teller;
1111 muall[i][tel3+XX] = dipole[i][XX];
1112 muall[i][tel3+YY] = dipole[i][YY];
1113 muall[i][tel3+ZZ] = dipole[i][ZZ];
1115 mu_mol = 0.0;
1116 for (m = 0; (m < DIM); m++)
1118 M_av[m] += dipole[i][m]; /* M per frame */
1119 mu_mol += dipole[i][m]*dipole[i][m]; /* calc. mu for distribution */
1121 mu_mol = std::sqrt(mu_mol);
1123 mu_ave += mu_mol; /* calc. the average mu */
1125 /* Update the dipole distribution */
1126 ibin = gmx::roundToInt(ndipbin*mu_mol/mu_max);
1127 if (ibin < ndipbin)
1129 dipole_bin[ibin]++;
1132 if (fndip3d)
1134 rvec2sprvec(dipole[i], dipsp[i]);
1136 if (dipsp[i][YY] > -M_PI && dipsp[i][YY] < -0.5*M_PI)
1138 if (dipsp[i][ZZ] < 0.5 * M_PI)
1140 ncolour = 1;
1142 else
1144 ncolour = 2;
1147 else if (dipsp[i][YY] > -0.5*M_PI && dipsp[i][YY] < 0.0*M_PI)
1149 if (dipsp[i][ZZ] < 0.5 * M_PI)
1151 ncolour = 3;
1153 else
1155 ncolour = 4;
1158 else if (dipsp[i][YY] > 0.0 && dipsp[i][YY] < 0.5*M_PI)
1160 if (dipsp[i][ZZ] < 0.5 * M_PI)
1162 ncolour = 5;
1164 else
1166 ncolour = 6;
1169 else if (dipsp[i][YY] > 0.5*M_PI && dipsp[i][YY] < M_PI)
1171 if (dipsp[i][ZZ] < 0.5 * M_PI)
1173 ncolour = 7;
1175 else
1177 ncolour = 8;
1180 if (dip3d)
1182 fprintf(dip3d, "set arrow %d from %f, %f, %f to %f, %f, %f lt %d # %d %d\n",
1183 i+1,
1184 x[ind0][XX],
1185 x[ind0][YY],
1186 x[ind0][ZZ],
1187 x[ind0][XX]+dipole[i][XX]/25,
1188 x[ind0][YY]+dipole[i][YY]/25,
1189 x[ind0][ZZ]+dipole[i][ZZ]/25,
1190 ncolour, ind0, i);
1193 } /* End loop of all molecules in frame */
1195 if (dip3d)
1197 fprintf(dip3d, "set title \"t = %4.3f\"\n", t);
1198 fprintf(dip3d, "set xrange [0.0:%4.2f]\n", box[XX][XX]);
1199 fprintf(dip3d, "set yrange [0.0:%4.2f]\n", box[YY][YY]);
1200 fprintf(dip3d, "set zrange [0.0:%4.2f]\n\n", box[ZZ][ZZ]);
1201 fprintf(dip3d, "splot 'dummy.dat' using 1:2:3 w vec\n");
1202 fprintf(dip3d, "pause -1 'Hit return to continue'\n");
1206 /* Compute square of total dipole */
1207 for (m = 0; (m < DIM); m++)
1209 M_av2[m] = M_av[m]*M_av[m];
1212 if (cosaver)
1214 compute_avercos(gnx_tot, dipole, &dd, dipaxis, bPairs);
1215 rms_cos = std::sqrt(gmx::square(dipaxis[XX]-0.5)+
1216 gmx::square(dipaxis[YY]-0.5)+
1217 gmx::square(dipaxis[ZZ]-0.5));
1218 if (bPairs)
1220 fprintf(caver, "%10.3e %10.3e %10.3e %10.3e %10.3e %10.3e\n",
1221 t, dd, rms_cos, dipaxis[XX], dipaxis[YY], dipaxis[ZZ]);
1223 else
1225 fprintf(caver, "%10.3e %10.3e %10.3e %10.3e %10.3e\n",
1226 t, rms_cos, dipaxis[XX], dipaxis[YY], dipaxis[ZZ]);
1230 if (bGkr)
1232 do_gkr(gkrbin, ncos, gnx, molindex, mols->index, x, dipole, ePBC, box,
1233 atom, gkatom);
1236 if (bTotal)
1238 tel3 = DIM*teller;
1239 muall[0][tel3+XX] = M_av[XX];
1240 muall[0][tel3+YY] = M_av[YY];
1241 muall[0][tel3+ZZ] = M_av[ZZ];
1244 /* Write to file the total dipole moment of the box, and its components
1245 * for this frame.
1247 if ((skip == 0) || ((teller % skip) == 0))
1249 fprintf(outmtot, "%10g %12.8e %12.8e %12.8e %12.8e\n",
1250 t, M_av[XX], M_av[YY], M_av[ZZ],
1251 std::sqrt(M_av2[XX]+M_av2[YY]+M_av2[ZZ]));
1254 for (m = 0; (m < DIM); m++)
1256 M[m] += M_av[m];
1257 M2[m] += M_av2[m];
1258 M4[m] += gmx::square(M_av2[m]);
1260 /* Increment loop counter */
1261 teller++;
1263 /* Calculate for output the running averages */
1264 invtel = 1.0/teller;
1265 M2_ave = (M2[XX]+M2[YY]+M2[ZZ])*invtel;
1266 M_ave2 = invtel*(invtel*(M[XX]*M[XX] + M[YY]*M[YY] + M[ZZ]*M[ZZ]));
1267 M_diff = M2_ave - M_ave2;
1269 /* Compute volume from box in traj, else we use the one from above */
1270 if (!bMU)
1272 volume = det(box);
1274 vol_aver += volume;
1276 epsilon = calc_eps(M_diff, (vol_aver/teller), epsilonRF, temp);
1278 /* Calculate running average for dipole */
1279 if (mu_ave != 0)
1281 mu_aver = (mu_ave/gnx_tot)*invtel;
1284 if ((skip == 0) || ((teller % skip) == 0))
1286 /* Write to file < |M|^2 >, |< M >|^2. And the difference between
1287 * the two. Here M is sum mu_i. Further write the finite system
1288 * Kirkwood G factor and epsilon.
1290 fprintf(outaver, "%10g %10.3e %10.3e %10.3e %10.3e\n",
1291 t, M2_ave, M_ave2, M_diff, M_ave2/M2_ave);
1293 if (fnadip)
1295 real aver;
1296 gmx_stats_get_average(muframelsq, &aver);
1297 fprintf(adip, "%10g %f \n", t, aver);
1299 /*if (dipole)
1300 printf("%f %f\n", norm(dipole[0]), norm(dipole[1]));
1302 if (!bMU || (mu_aver != -1))
1304 /* Finite system Kirkwood G-factor */
1305 Gk = M_diff/(gnx_tot*mu_aver*mu_aver);
1306 /* Infinite system Kirkwood G-factor */
1307 if (epsilonRF == 0.0)
1309 g_k = ((2*epsilon+1)*Gk/(3*epsilon));
1311 else
1313 g_k = ((2*epsilonRF+epsilon)*(2*epsilon+1)*
1314 Gk/(3*epsilon*(2*epsilonRF+1)));
1317 fprintf(outeps, "%10g %10.3e %10.3e %10.3e\n", t, epsilon, Gk, g_k);
1320 else
1322 fprintf(outeps, "%10g %12.8e\n", t, epsilon);
1325 gmx_stats_free(muframelsq);
1327 if (bMU)
1329 bCont = read_mu_from_enx(fmu, iVol, iMu, mu_t, &volume, &t, nre, fr);
1331 else
1333 bCont = read_next_x(oenv, status, &t, x, box);
1335 timecheck = check_times(t);
1337 while (bCont && (timecheck == 0) );
1339 gmx_rmpbc_done(gpbc);
1341 if (!bMU)
1343 close_trx(status);
1346 xvgrclose(outmtot);
1347 xvgrclose(outaver);
1348 xvgrclose(outeps);
1350 if (fnadip)
1352 xvgrclose(adip);
1355 if (cosaver)
1357 xvgrclose(caver);
1360 if (dip3d)
1362 fprintf(dip3d, "set xrange [0.0:%4.2f]\n", box[XX][XX]);
1363 fprintf(dip3d, "set yrange [0.0:%4.2f]\n", box[YY][YY]);
1364 fprintf(dip3d, "set zrange [0.0:%4.2f]\n\n", box[ZZ][ZZ]);
1365 fprintf(dip3d, "splot 'dummy.dat' using 1:2:3 w vec\n");
1366 fprintf(dip3d, "pause -1 'Hit return to continue'\n");
1367 gmx_ffclose(dip3d);
1370 if (bSlab)
1372 dump_slab_dipoles(slabfn, idim, nslices, slab_dipoles, box, teller, oenv);
1373 sfree(slab_dipoles);
1376 vol_aver /= teller;
1377 printf("Average volume over run is %g\n", vol_aver);
1378 if (bGkr)
1380 print_gkrbin(gkrfn, gkrbin, gnx[0], teller, vol_aver, oenv);
1381 print_cmap(cmap, gkrbin, nlevels);
1383 /* Autocorrelation function */
1384 if (bCorr)
1386 if (teller < 2)
1388 printf("Not enough frames for autocorrelation\n");
1390 else
1392 dt = (t1 - t0)/(teller-1);
1393 printf("t0 %g, t %g, teller %d\n", t0, t, teller);
1395 mode = eacVector;
1397 if (bTotal)
1399 do_autocorr(corf, oenv, "Autocorrelation Function of Total Dipole",
1400 teller, 1, muall, dt, mode, TRUE);
1402 else
1404 do_autocorr(corf, oenv, "Dipole Autocorrelation Function",
1405 teller, gnx_tot, muall, dt,
1406 mode, std::strcmp(corrtype, "molsep") != 0);
1410 if (!bMU)
1412 real aver, sigma, error;
1414 gmx_stats_get_ase(mulsq, &aver, &sigma, &error);
1415 printf("\nDipole moment (Debye)\n");
1416 printf("---------------------\n");
1417 printf("Average = %8.4f Std. Dev. = %8.4f Error = %8.4f\n",
1418 aver, sigma, error);
1419 if (bQuad)
1421 rvec a, s, e;
1422 for (m = 0; (m < DIM); m++)
1424 gmx_stats_get_ase(mulsq, &(a[m]), &(s[m]), &(e[m]));
1427 printf("\nQuadrupole moment (Debye-Ang)\n");
1428 printf("-----------------------------\n");
1429 printf("Averages = %8.4f %8.4f %8.4f\n", a[XX], a[YY], a[ZZ]);
1430 printf("Std. Dev. = %8.4f %8.4f %8.4f\n", s[XX], s[YY], s[ZZ]);
1431 printf("Error = %8.4f %8.4f %8.4f\n", e[XX], e[YY], e[ZZ]);
1433 printf("\n");
1435 printf("The following averages for the complete trajectory have been calculated:\n\n");
1436 printf(" Total < M_x > = %g Debye\n", M[XX]/teller);
1437 printf(" Total < M_y > = %g Debye\n", M[YY]/teller);
1438 printf(" Total < M_z > = %g Debye\n\n", M[ZZ]/teller);
1440 printf(" Total < M_x^2 > = %g Debye^2\n", M2[XX]/teller);
1441 printf(" Total < M_y^2 > = %g Debye^2\n", M2[YY]/teller);
1442 printf(" Total < M_z^2 > = %g Debye^2\n\n", M2[ZZ]/teller);
1444 printf(" Total < |M|^2 > = %g Debye^2\n", M2_ave);
1445 printf(" Total |< M >|^2 = %g Debye^2\n\n", M_ave2);
1447 printf(" < |M|^2 > - |< M >|^2 = %g Debye^2\n\n", M_diff);
1449 if (!bMU || (mu_aver != -1))
1451 printf("Finite system Kirkwood g factor G_k = %g\n", Gk);
1452 printf("Infinite system Kirkwood g factor g_k = %g\n\n", g_k);
1454 printf("Epsilon = %g\n", epsilon);
1456 if (!bMU)
1458 /* Write to file the dipole moment distibution during the simulation.
1460 outdd = xvgropen(dipdist, "Dipole Moment Distribution", "mu (Debye)", "", oenv);
1461 for (i = 0; (i < ndipbin); i++)
1463 fprintf(outdd, "%10g %10f\n",
1464 (i*mu_max)/ndipbin, static_cast<real>(dipole_bin[i])/teller);
1466 xvgrclose(outdd);
1467 sfree(dipole_bin);
1469 if (bGkr)
1471 done_gkrbin(&gkrbin);
1475 static void dipole_atom2molindex(int *n, int *index, const t_block *mols)
1477 int nmol, i, j, m;
1479 nmol = 0;
1480 i = 0;
1481 while (i < *n)
1483 m = 0;
1484 while (m < mols->nr && index[i] != mols->index[m])
1486 m++;
1488 if (m == mols->nr)
1490 gmx_fatal(FARGS, "index[%d]=%d does not correspond to the first atom of a molecule", i+1, index[i]+1);
1492 for (j = mols->index[m]; j < mols->index[m+1]; j++)
1494 if (i >= *n || index[i] != j)
1496 gmx_fatal(FARGS, "The index group is not a set of whole molecules");
1498 i++;
1500 /* Modify the index in place */
1501 index[nmol++] = m;
1503 printf("There are %d molecules in the selection\n", nmol);
1505 *n = nmol;
1507 int gmx_dipoles(int argc, char *argv[])
1509 const char *desc[] = {
1510 "[THISMODULE] computes the total dipole plus fluctuations of a simulation",
1511 "system. From this you can compute e.g. the dielectric constant for",
1512 "low-dielectric media.",
1513 "For molecules with a net charge, the net charge is subtracted at",
1514 "center of mass of the molecule.[PAR]",
1515 "The file [TT]Mtot.xvg[tt] contains the total dipole moment of a frame, the",
1516 "components as well as the norm of the vector.",
1517 "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",
1518 "simulation.",
1519 "The file [TT]dipdist.xvg[tt] contains the distribution of dipole moments during",
1520 "the simulation",
1521 "The value of [TT]-mumax[tt] is used as the highest value in the distribution graph.[PAR]",
1522 "Furthermore, the dipole autocorrelation function will be computed when",
1523 "option [TT]-corr[tt] is used. The output file name is given with the [TT]-c[tt]",
1524 "option.",
1525 "The correlation functions can be averaged over all molecules",
1526 "([TT]mol[tt]), plotted per molecule separately ([TT]molsep[tt])",
1527 "or it can be computed over the total dipole moment of the simulation box",
1528 "([TT]total[tt]).[PAR]",
1529 "Option [TT]-g[tt] produces a plot of the distance dependent Kirkwood",
1530 "G-factor, as well as the average cosine of the angle between the dipoles",
1531 "as a function of the distance. The plot also includes gOO and hOO",
1532 "according to Nymand & Linse, J. Chem. Phys. 112 (2000) pp 6386-6395. In the same plot, ",
1533 "we also include the energy per scale computed by taking the inner product of",
1534 "the dipoles divided by the distance to the third power.[PAR]",
1535 "[PAR]",
1536 "EXAMPLES[PAR]",
1537 "[TT]gmx dipoles -corr mol -P 1 -o dip_sqr -mu 2.273 -mumax 5.0[tt][PAR]",
1538 "This will calculate the autocorrelation function of the molecular",
1539 "dipoles using a first order Legendre polynomial of the angle of the",
1540 "dipole vector and itself a time t later. For this calculation 1001",
1541 "frames will be used. Further, the dielectric constant will be calculated",
1542 "using an [TT]-epsilonRF[tt] of infinity (default), temperature of 300 K (default) and",
1543 "an average dipole moment of the molecule of 2.273 (SPC). For the",
1544 "distribution function a maximum of 5.0 will be used."
1546 real mu_max = 5, mu_aver = -1, rcmax = 0;
1547 real epsilonRF = 0.0, temp = 300;
1548 gmx_bool bPairs = TRUE, bPhi = FALSE, bQuad = FALSE;
1549 const char *corrtype[] = {nullptr, "none", "mol", "molsep", "total", nullptr};
1550 const char *axtitle = "Z";
1551 int nslices = 10; /* nr of slices defined */
1552 int skip = 0, nFA = 0, nFB = 0, ncos = 1;
1553 int nlevels = 20, ndegrees = 90;
1554 gmx_output_env_t *oenv;
1555 t_pargs pa[] = {
1556 { "-mu", FALSE, etREAL, {&mu_aver},
1557 "dipole of a single molecule (in Debye)" },
1558 { "-mumax", FALSE, etREAL, {&mu_max},
1559 "max dipole in Debye (for histogram)" },
1560 { "-epsilonRF", FALSE, etREAL, {&epsilonRF},
1561 "[GRK]epsilon[grk] of the reaction field used during the simulation, needed for dielectric constant calculation. WARNING: 0.0 means infinity (default)" },
1562 { "-skip", FALSE, etINT, {&skip},
1563 "Skip steps in the output (but not in the computations)" },
1564 { "-temp", FALSE, etREAL, {&temp},
1565 "Average temperature of the simulation (needed for dielectric constant calculation)" },
1566 { "-corr", FALSE, etENUM, {corrtype},
1567 "Correlation function to calculate" },
1568 { "-pairs", FALSE, etBOOL, {&bPairs},
1569 "Calculate [MAG][COS][GRK]theta[grk][cos][mag] between all pairs of molecules. May be slow" },
1570 { "-quad", FALSE, etBOOL, {&bQuad},
1571 "Take quadrupole into account"},
1572 { "-ncos", FALSE, etINT, {&ncos},
1573 "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." },
1574 { "-axis", FALSE, etSTR, {&axtitle},
1575 "Take the normal on the computational box in direction X, Y or Z." },
1576 { "-sl", FALSE, etINT, {&nslices},
1577 "Divide the box into this number of slices." },
1578 { "-gkratom", FALSE, etINT, {&nFA},
1579 "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" },
1580 { "-gkratom2", FALSE, etINT, {&nFB},
1581 "Same as previous option in case ncos = 2, i.e. dipole interaction between two groups of molecules" },
1582 { "-rcmax", FALSE, etREAL, {&rcmax},
1583 "Maximum distance to use in the dipole orientation distribution (with ncos == 2). If zero, a criterion based on the box length will be used." },
1584 { "-phi", FALSE, etBOOL, {&bPhi},
1585 "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." },
1586 { "-nlevels", FALSE, etINT, {&nlevels},
1587 "Number of colors in the cmap output" },
1588 { "-ndegrees", FALSE, etINT, {&ndegrees},
1589 "Number of divisions on the [IT]y[it]-axis in the cmap output (for 180 degrees)" }
1591 int *gnx;
1592 int nFF[2];
1593 int **grpindex;
1594 char **grpname = nullptr;
1595 gmx_bool bGkr, bMU, bSlab;
1596 t_filenm fnm[] = {
1597 { efEDR, "-en", nullptr, ffOPTRD },
1598 { efTRX, "-f", nullptr, ffREAD },
1599 { efTPR, nullptr, nullptr, ffREAD },
1600 { efNDX, nullptr, nullptr, ffOPTRD },
1601 { efXVG, "-o", "Mtot", ffWRITE },
1602 { efXVG, "-eps", "epsilon", ffWRITE },
1603 { efXVG, "-a", "aver", ffWRITE },
1604 { efXVG, "-d", "dipdist", ffWRITE },
1605 { efXVG, "-c", "dipcorr", ffOPTWR },
1606 { efXVG, "-g", "gkr", ffOPTWR },
1607 { efXVG, "-adip", "adip", ffOPTWR },
1608 { efXVG, "-dip3d", "dip3d", ffOPTWR },
1609 { efXVG, "-cos", "cosaver", ffOPTWR },
1610 { efXPM, "-cmap", "cmap", ffOPTWR },
1611 { efXVG, "-slab", "slab", ffOPTWR }
1613 #define NFILE asize(fnm)
1614 int npargs;
1615 t_pargs *ppa;
1616 t_topology *top;
1617 int ePBC;
1618 int k, natoms;
1619 matrix box;
1621 npargs = asize(pa);
1622 ppa = add_acf_pargs(&npargs, pa);
1623 if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW,
1624 NFILE, fnm, npargs, ppa, asize(desc), desc, 0, nullptr, &oenv))
1626 sfree(ppa);
1627 return 0;
1630 printf("Using %g as mu_max and %g as the dipole moment.\n",
1631 mu_max, mu_aver);
1632 if (epsilonRF == 0.0)
1634 printf("WARNING: EpsilonRF = 0.0, this really means EpsilonRF = infinity\n");
1637 bMU = opt2bSet("-en", NFILE, fnm);
1638 if (bMU)
1640 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.");
1642 bGkr = opt2bSet("-g", NFILE, fnm);
1643 if (opt2parg_bSet("-ncos", asize(pa), pa))
1645 if ((ncos != 1) && (ncos != 2))
1647 gmx_fatal(FARGS, "ncos has to be either 1 or 2");
1649 bGkr = TRUE;
1651 bSlab = (opt2bSet("-slab", NFILE, fnm) || opt2parg_bSet("-sl", asize(pa), pa) ||
1652 opt2parg_bSet("-axis", asize(pa), pa));
1653 if (bMU)
1655 if (bQuad)
1657 printf("WARNING: Can not determine quadrupoles from energy file\n");
1658 bQuad = FALSE;
1660 if (bGkr)
1662 printf("WARNING: Can not determine Gk(r) from energy file\n");
1663 bGkr = FALSE;
1664 ncos = 1;
1666 if (mu_aver == -1)
1668 printf("WARNING: Can not calculate Gk and gk, since you did\n"
1669 " not enter a valid dipole for the molecules\n");
1673 snew(top, 1);
1674 ePBC = read_tpx_top(ftp2fn(efTPR, NFILE, fnm), nullptr, box,
1675 &natoms, nullptr, nullptr, top);
1677 snew(gnx, ncos);
1678 snew(grpname, ncos);
1679 snew(grpindex, ncos);
1680 get_index(&top->atoms, ftp2fn_null(efNDX, NFILE, fnm),
1681 ncos, gnx, grpindex, grpname);
1682 for (k = 0; (k < ncos); k++)
1684 dipole_atom2molindex(&gnx[k], grpindex[k], &(top->mols));
1685 neutralize_mols(gnx[k], grpindex[k], &(top->mols), top->atoms.atom);
1687 nFF[0] = nFA;
1688 nFF[1] = nFB;
1689 do_dip(top, ePBC, det(box), ftp2fn(efTRX, NFILE, fnm),
1690 opt2fn("-o", NFILE, fnm), opt2fn("-eps", NFILE, fnm),
1691 opt2fn("-a", NFILE, fnm), opt2fn("-d", NFILE, fnm),
1692 opt2fn_null("-cos", NFILE, fnm),
1693 opt2fn_null("-dip3d", NFILE, fnm), opt2fn_null("-adip", NFILE, fnm),
1694 bPairs, corrtype[0],
1695 opt2fn("-c", NFILE, fnm),
1696 bGkr, opt2fn("-g", NFILE, fnm),
1697 bPhi, &nlevels, ndegrees,
1698 ncos,
1699 opt2fn("-cmap", NFILE, fnm), rcmax,
1700 bQuad, bMU, opt2fn("-en", NFILE, fnm),
1701 gnx, grpindex, mu_max, mu_aver, epsilonRF, temp, nFF, skip,
1702 bSlab, nslices, axtitle, opt2fn("-slab", NFILE, fnm), oenv);
1704 do_view(oenv, opt2fn("-o", NFILE, fnm), "-autoscale xy -nxy");
1705 do_view(oenv, opt2fn("-eps", NFILE, fnm), "-autoscale xy -nxy");
1706 do_view(oenv, opt2fn("-a", NFILE, fnm), "-autoscale xy -nxy");
1707 do_view(oenv, opt2fn("-d", NFILE, fnm), "-autoscale xy");
1708 do_view(oenv, opt2fn("-c", NFILE, fnm), "-autoscale xy");
1710 return 0;