2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2011,2012,2013,2014,2015,2016, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
42 #include "gromacs/commandline/pargs.h"
43 #include "gromacs/correlationfunctions/autocorr.h"
44 #include "gromacs/correlationfunctions/integrate.h"
45 #include "gromacs/fft/fft.h"
46 #include "gromacs/fileio/confio.h"
47 #include "gromacs/fileio/trxio.h"
48 #include "gromacs/fileio/xvgr.h"
49 #include "gromacs/gmxana/gmx_ana.h"
50 #include "gromacs/legacyheaders/copyrite.h"
51 #include "gromacs/legacyheaders/macros.h"
52 #include "gromacs/legacyheaders/txtdump.h"
53 #include "gromacs/legacyheaders/typedefs.h"
54 #include "gromacs/legacyheaders/viewit.h"
55 #include "gromacs/math/units.h"
56 #include "gromacs/math/utilities.h"
57 #include "gromacs/math/vec.h"
58 #include "gromacs/topology/index.h"
59 #include "gromacs/utility/fatalerror.h"
60 #include "gromacs/utility/futil.h"
61 #include "gromacs/utility/smalloc.h"
65 VACF
, MVACF
, DOS
, DOS_SOLID
, DOS_DIFF
, DOS_CP
, DOS_S
, DOS_A
, DOS_E
, DOS_NR
68 static int calcMoleculesInIndexGroup(t_block
*mols
, int natoms
, atom_id
*index
, int nindex
)
77 while (index
[i
] > mols
->index
[mol
])
82 gmx_fatal(FARGS
, "Atom index out of range: %d", index
[i
]+1);
85 for (j
= mols
->index
[mol
]; j
< mols
->index
[mol
+1]; j
++)
89 gmx_fatal(FARGS
, "The index group does not consist of whole molecules");
94 gmx_fatal(FARGS
, "Index contains atom numbers larger than the topology");
102 static double FD(double Delta
, double f
)
104 return (2*pow(Delta
, -4.5)*pow(f
, 7.5) -
105 6*pow(Delta
, -3)*pow(f
, 5) -
106 pow(Delta
, -1.5)*pow(f
, 3.5) +
107 6*pow(Delta
, -1.5)*pow(f
, 2.5) +
111 static double YYY(double f
, double y
)
113 return (2*pow(y
*f
, 3) - sqr(f
)*y
*(1+6*y
) +
117 static double calc_compress(double y
)
123 return ((1+y
+sqr(y
)-pow(y
, 3))/(pow(1-y
, 3)));
126 static double bisector(double Delta
, double tol
,
127 double ff0
, double ff1
,
128 double ff(double, double))
130 double fd0
, fd
, fd1
, f
, f0
, f1
;
131 double tolmin
= 1e-8;
137 fprintf(stderr
, "Unrealistic tolerance %g for bisector. Setting it to %g\n", tol
, tolmin
);
160 while ((f1
-f0
) > tol
);
165 static double calc_fluidicity(double Delta
, double tol
)
167 return bisector(Delta
, tol
, 0, 1, FD
);
170 static double calc_y(double f
, double Delta
, double toler
)
174 y1
= pow(f
/Delta
, 1.5);
175 y2
= bisector(f
, toler
, 0, 10000, YYY
);
176 if (fabs((y1
-y2
)/(y1
+y2
)) > 100*toler
)
178 fprintf(stderr
, "Inconsistency computing y: y1 = %f, y2 = %f, using y1.\n",
185 static double calc_Shs(double f
, double y
)
189 return BOLTZ
*(log(calc_compress(fy
)) + fy
*(3*fy
-4)/sqr(1-fy
));
192 static real
wCsolid(real nu
, real beta
)
194 real bhn
= beta
*PLANCK
*nu
;
205 return sqr(bhn
)*ebn
/koko
;
209 static real
wSsolid(real nu
, real beta
)
211 real bhn
= beta
*PLANCK
*nu
;
219 return bhn
/gmx_expm1(bhn
) - gmx_log1p(-exp(-bhn
));
223 static real
wAsolid(real nu
, real beta
)
225 real bhn
= beta
*PLANCK
*nu
;
233 return log((1-exp(-bhn
))/(exp(-bhn
/2))) - log(bhn
);
237 static real
wEsolid(real nu
, real beta
)
239 real bhn
= beta
*PLANCK
*nu
;
247 return bhn
/2 + bhn
/gmx_expm1(bhn
)-1;
251 int gmx_dos(int argc
, char *argv
[])
253 const char *desc
[] = {
254 "[THISMODULE] computes the Density of States from a simulations.",
255 "In order for this to be meaningful the velocities must be saved",
256 "in the trajecotry with sufficiently high frequency such as to cover",
257 "all vibrations. For flexible systems that would be around a few fs",
258 "between saving. Properties based on the DoS are printed on the",
260 "Note that the density of states is calculated from the mass-weighted",
261 "autocorrelation, and by default only from the square of the real",
262 "component rather than absolute value. This means the shape can differ",
263 "substantially from the plain vibrational power spectrum you can",
264 "calculate with gmx velacc."
266 const char *bugs
[] = {
267 "This program needs a lot of memory: total usage equals the number of atoms times 3 times number of frames times 4 (or 8 when run in double precision)."
278 int nV
, nframes
, n_alloc
, i
, j
, k
, l
, fftcode
, Nmol
, Natom
;
279 double rho
, dt
, V2sum
, Vsum
, V
, tmass
, dostot
, dos2
, dosabs
;
280 real
**c1
, **dos
, mi
, beta
, bfac
, *nu
, *tt
, stddev
, c1j
;
283 double cP
, S
, A
, E
, DiffCoeff
, Delta
, f
, y
, z
, sigHS
, Shs
, Sig
, DoS0
, recip_fac
;
284 double wCdiff
, wSdiff
, wAdiff
, wEdiff
;
289 gmx_bool normalizeAutocorrelation
;
291 static gmx_bool bVerbose
= TRUE
, bAbsolute
= FALSE
, bNormalizeDos
= FALSE
;
292 static gmx_bool bRecip
= FALSE
;
293 static real Temp
= 298.15, toler
= 1e-6;
296 { "-v", FALSE
, etBOOL
, {&bVerbose
},
297 "Be loud and noisy." },
298 { "-recip", FALSE
, etBOOL
, {&bRecip
},
299 "Use cm^-1 on X-axis instead of 1/ps for DoS plots." },
300 { "-abs", FALSE
, etBOOL
, {&bAbsolute
},
301 "Use the absolute value of the Fourier transform of the VACF as the Density of States. Default is to use the real component only" },
302 { "-normdos", FALSE
, etBOOL
, {&bNormalizeDos
},
303 "Normalize the DoS such that it adds up to 3N. This should usually not be necessary." },
304 { "-T", FALSE
, etREAL
, {&Temp
},
305 "Temperature in the simulation" },
306 { "-toler", FALSE
, etREAL
, {&toler
},
307 "[HIDDEN]Tolerance when computing the fluidicity using bisection algorithm" }
311 { efTRN
, "-f", NULL
, ffREAD
},
312 { efTPR
, "-s", NULL
, ffREAD
},
313 { efNDX
, NULL
, NULL
, ffOPTRD
},
314 { efXVG
, "-vacf", "vacf", ffWRITE
},
315 { efXVG
, "-mvacf", "mvacf", ffWRITE
},
316 { efXVG
, "-dos", "dos", ffWRITE
},
317 { efLOG
, "-g", "dos", ffWRITE
},
319 #define NFILE asize(fnm)
322 const char *DoSlegend
[] = {
323 "DoS(v)", "DoS(v)[Solid]", "DoS(v)[Diff]"
327 ppa
= add_acf_pargs(&npargs
, pa
);
328 if (!parse_common_args(&argc
, argv
, PCA_CAN_VIEW
| PCA_CAN_TIME
,
329 NFILE
, fnm
, npargs
, ppa
, asize(desc
), desc
,
330 asize(bugs
), bugs
, &oenv
))
335 beta
= 1/(Temp
*BOLTZ
);
337 fplog
= gmx_fio_fopen(ftp2fn(efLOG
, NFILE
, fnm
), "w");
338 fprintf(fplog
, "Doing density of states analysis based on trajectory.\n");
339 please_cite(fplog
, "Pascal2011a");
340 please_cite(fplog
, "Caleman2011b");
342 read_tps_conf(ftp2fn(efTPR
, NFILE
, fnm
), title
, &top
, &ePBC
, NULL
, NULL
, box
, TRUE
);
344 /* Handle index groups */
345 get_index(&top
.atoms
, ftp2fn_null(efNDX
, NFILE
, fnm
), 1, &grpNatoms
, &index
, &grpname
);
349 for (i
= 0; i
< grpNatoms
; i
++)
351 tmass
+= top
.atoms
.atom
[index
[i
]].m
;
355 Nmol
= calcMoleculesInIndexGroup(&top
.mols
, top
.atoms
.nr
, index
, grpNatoms
);
358 /* Correlation stuff */
360 for (i
= 0; (i
< gnx
); i
++)
365 read_first_frame(oenv
, &status
, ftp2fn(efTRN
, NFILE
, fnm
), &fr
, TRX_NEED_V
);
381 if (nframes
>= n_alloc
)
384 for (i
= 0; i
< gnx
; i
++)
386 srenew(c1
[i
], n_alloc
);
389 for (i
= 0; i
< gnx
; i
+= DIM
)
391 c1
[i
+XX
][nframes
] = fr
.v
[index
[i
/DIM
]][XX
];
392 c1
[i
+YY
][nframes
] = fr
.v
[index
[i
/DIM
]][YY
];
393 c1
[i
+ZZ
][nframes
] = fr
.v
[index
[i
/DIM
]][ZZ
];
400 while (read_next_frame(oenv
, status
, &fr
));
404 dt
= (t1
-t0
)/(nframes
-1);
411 printf("Going to do %d fourier transforms of length %d. Hang on.\n",
414 /* Unfortunately the -normalize program option for the autocorrelation
415 * function calculation is added as a hack with a static variable in the
416 * autocorrelation.c source. That would work if we called the normal
417 * do_autocorr(), but this routine overrides that by directly calling
418 * the low-level functionality. That unfortunately leads to ignoring the
419 * default value for the option (which is to normalize).
420 * Since the absolute value seems to be important for the subsequent
421 * analysis below, we detect the value directly from the option, calculate
422 * the autocorrelation without normalization, and then apply the
423 * normalization just to the autocorrelation output
424 * (or not, if the user asked for a non-normalized autocorrelation).
426 normalizeAutocorrelation
= opt2parg_bool("-normalize", npargs
, ppa
);
428 /* Note that we always disable normalization here, regardless of user settings */
429 low_do_autocorr(NULL
, oenv
, NULL
, nframes
, gnx
, nframes
, c1
, dt
, eacNormal
, 0, FALSE
,
430 FALSE
, FALSE
, -1, -1, 0);
432 for (j
= 0; (j
< DOS_NR
); j
++)
434 snew(dos
[j
], nframes
+4);
439 printf("Going to merge the ACFs into the mass-weighted and plain ACF\n");
441 for (i
= 0; (i
< gnx
); i
+= DIM
)
443 mi
= top
.atoms
.atom
[index
[i
/DIM
]].m
;
444 for (j
= 0; (j
< nframes
/2); j
++)
446 c1j
= (c1
[i
+XX
][j
] + c1
[i
+YY
][j
] + c1
[i
+ZZ
][j
]);
447 dos
[VACF
][j
] += c1j
/Natom
;
448 dos
[MVACF
][j
] += mi
*c1j
;
452 fp
= xvgropen(opt2fn("-vacf", NFILE
, fnm
), "Velocity autocorrelation function",
453 "Time (ps)", "C(t)", oenv
);
456 invNormalize
= normalizeAutocorrelation
? 1.0/dos
[VACF
][0] : 1.0;
458 for (j
= 0; (j
< nframes
/2); j
++)
461 fprintf(fp
, "%10g %10g\n", tt
[j
], dos
[VACF
][j
] * invNormalize
);
465 fp
= xvgropen(opt2fn("-mvacf", NFILE
, fnm
), "Mass-weighted velocity autocorrelation function",
466 "Time (ps)", "C(t)", oenv
);
468 invNormalize
= normalizeAutocorrelation
? 1.0/dos
[VACF
][0] : 1.0;
470 for (j
= 0; (j
< nframes
/2); j
++)
472 fprintf(fp
, "%10g %10g\n", tt
[j
], dos
[MVACF
][j
] * invNormalize
);
476 if ((fftcode
= gmx_fft_init_1d_real(&fft
, nframes
/2,
477 GMX_FFT_FLAG_NONE
)) != 0)
479 gmx_fatal(FARGS
, "gmx_fft_init_1d_real returned %d", fftcode
);
481 if ((fftcode
= gmx_fft_1d_real(fft
, GMX_FFT_REAL_TO_COMPLEX
,
482 (void *)dos
[MVACF
], (void *)dos
[DOS
])) != 0)
484 gmx_fatal(FARGS
, "gmx_fft_1d_real returned %d", fftcode
);
487 /* First compute the DoS */
488 /* Magic factor of 8 included now. */
492 for (j
= 0; (j
< nframes
/4); j
++)
495 dos2
+= sqr(dos
[DOS
][2*j
]) + sqr(dos
[DOS
][2*j
+1]);
498 dos
[DOS
][j
] = bfac
*sqrt(sqr(dos
[DOS
][2*j
]) + sqr(dos
[DOS
][2*j
+1]));
502 dos
[DOS
][j
] = bfac
*dos
[DOS
][2*j
];
506 dostot
= evaluate_integral(nframes
/4, nu
, dos
[DOS
], NULL
, nframes
/4, &stddev
);
509 for (j
= 0; (j
< nframes
/4); j
++)
511 dos
[DOS
][j
] *= 3*Natom
/dostot
;
518 /* Note this eqn. is incorrect in Pascal2011a! */
519 Delta
= ((2*DoS0
/(9*Natom
))*sqrt(M_PI
*BOLTZ
*Temp
*Natom
/tmass
)*
520 pow((Natom
/V
), 1.0/3.0)*pow(6/M_PI
, 2.0/3.0));
521 f
= calc_fluidicity(Delta
, toler
);
522 y
= calc_y(f
, Delta
, toler
);
523 z
= calc_compress(y
);
524 Sig
= BOLTZ
*(5.0/2.0+log(2*M_PI
*BOLTZ
*Temp
/(sqr(PLANCK
))*V
/(f
*Natom
)));
525 Shs
= Sig
+calc_Shs(f
, y
);
526 rho
= (tmass
*AMU
)/(V
*NANO
*NANO
*NANO
);
527 sigHS
= pow(6*y
*V
/(M_PI
*Natom
), 1.0/3.0);
529 fprintf(fplog
, "System = \"%s\"\n", title
);
530 fprintf(fplog
, "Nmol = %d\n", Nmol
);
531 fprintf(fplog
, "Natom = %d\n", Natom
);
532 fprintf(fplog
, "dt = %g ps\n", dt
);
533 fprintf(fplog
, "tmass = %g amu\n", tmass
);
534 fprintf(fplog
, "V = %g nm^3\n", V
);
535 fprintf(fplog
, "rho = %g g/l\n", rho
);
536 fprintf(fplog
, "T = %g K\n", Temp
);
537 fprintf(fplog
, "beta = %g mol/kJ\n", beta
);
539 fprintf(fplog
, "\nDoS parameters\n");
540 fprintf(fplog
, "Delta = %g\n", Delta
);
541 fprintf(fplog
, "fluidicity = %g\n", f
);
542 fprintf(fplog
, "hard sphere packing fraction = %g\n", y
);
543 fprintf(fplog
, "hard sphere compressibility = %g\n", z
);
544 fprintf(fplog
, "ideal gas entropy = %g\n", Sig
);
545 fprintf(fplog
, "hard sphere entropy = %g\n", Shs
);
546 fprintf(fplog
, "sigma_HS = %g nm\n", sigHS
);
547 fprintf(fplog
, "DoS0 = %g\n", DoS0
);
548 fprintf(fplog
, "Dos2 = %g\n", dos2
);
549 fprintf(fplog
, "DoSTot = %g\n", dostot
);
551 /* Now compute solid (2) and diffusive (3) components */
552 fp
= xvgropen(opt2fn("-dos", NFILE
, fnm
), "Density of states",
553 bRecip
? "E (cm\\S-1\\N)" : "\\f{12}n\\f{4} (1/ps)",
554 "\\f{4}S(\\f{12}n\\f{4})", oenv
);
555 xvgr_legend(fp
, asize(DoSlegend
), DoSlegend
, oenv
);
556 recip_fac
= bRecip
? (1e7
/SPEED_OF_LIGHT
) : 1.0;
557 for (j
= 0; (j
< nframes
/4); j
++)
559 dos
[DOS_DIFF
][j
] = DoS0
/(1+sqr(DoS0
*M_PI
*nu
[j
]/(6*f
*Natom
)));
560 dos
[DOS_SOLID
][j
] = dos
[DOS
][j
]-dos
[DOS_DIFF
][j
];
561 fprintf(fp
, "%10g %10g %10g %10g\n",
563 dos
[DOS
][j
]/recip_fac
,
564 dos
[DOS_SOLID
][j
]/recip_fac
,
565 dos
[DOS_DIFF
][j
]/recip_fac
);
569 /* Finally analyze the results! */
571 wSdiff
= Shs
/(3*BOLTZ
); /* Is this correct? */
573 wAdiff
= wEdiff
-wSdiff
;
574 for (j
= 0; (j
< nframes
/4); j
++)
576 dos
[DOS_CP
][j
] = (dos
[DOS_DIFF
][j
]*wCdiff
+
577 dos
[DOS_SOLID
][j
]*wCsolid(nu
[j
], beta
));
578 dos
[DOS_S
][j
] = (dos
[DOS_DIFF
][j
]*wSdiff
+
579 dos
[DOS_SOLID
][j
]*wSsolid(nu
[j
], beta
));
580 dos
[DOS_A
][j
] = (dos
[DOS_DIFF
][j
]*wAdiff
+
581 dos
[DOS_SOLID
][j
]*wAsolid(nu
[j
], beta
));
582 dos
[DOS_E
][j
] = (dos
[DOS_DIFF
][j
]*wEdiff
+
583 dos
[DOS_SOLID
][j
]*wEsolid(nu
[j
], beta
));
585 DiffCoeff
= evaluate_integral(nframes
/2, tt
, dos
[VACF
], NULL
, nframes
/2, &stddev
);
586 DiffCoeff
= 1000*DiffCoeff
/3.0;
587 fprintf(fplog
, "Diffusion coefficient from VACF %g 10^-5 cm^2/s\n",
589 fprintf(fplog
, "Diffusion coefficient from DoS %g 10^-5 cm^2/s\n",
590 1000*DoS0
/(12*tmass
*beta
));
592 cP
= BOLTZ
* evaluate_integral(nframes
/4, nu
, dos
[DOS_CP
], NULL
,
594 fprintf(fplog
, "Heat capacity %g J/mol K\n", 1000*cP
/Nmol
);
595 fprintf(fplog
, "\nArrivederci!\n");
596 gmx_fio_fclose(fplog
);
598 do_view(oenv
, ftp2fn(efXVG
, NFILE
, fnm
), "-nxy");