Merge branch release-4-6 into release-5-0
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_velacc.c
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5  * Copyright (c) 2001-2004, The GROMACS development team.
6  * Copyright (c) 2013,2014, 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.
10  *
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.
15  *
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.
20  *
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.
25  *
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.
33  *
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.
36  */
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40 #include <stdio.h>
41 #include <math.h>
42
43 #include "gromacs/fileio/confio.h"
44 #include "gmx_fatal.h"
45 #include "gromacs/fileio/futil.h"
46 #include "gstat.h"
47 #include "macros.h"
48 #include "gromacs/math/utilities.h"
49 #include "physics.h"
50 #include "index.h"
51 #include "gromacs/utility/smalloc.h"
52 #include "gromacs/commandline/pargs.h"
53 #include <string.h>
54 #include "sysstuff.h"
55 #include "txtdump.h"
56 #include "typedefs.h"
57 #include "vec.h"
58 #include "xvgr.h"
59 #include "gmx_ana.h"
60 #include "gromacs/fft/fft.h"
61 #include "gromacs/fileio/trxio.h"
62
63 static void index_atom2mol(int *n, atom_id *index, t_block *mols)
64 {
65     int nat, i, nmol, mol, j;
66
67     nat  = *n;
68     i    = 0;
69     nmol = 0;
70     mol  = 0;
71     while (i < nat)
72     {
73         while (index[i] > mols->index[mol])
74         {
75             mol++;
76             if (mol >= mols->nr)
77             {
78                 gmx_fatal(FARGS, "Atom index out of range: %d", index[i]+1);
79             }
80         }
81         for (j = mols->index[mol]; j < mols->index[mol+1]; j++)
82         {
83             if (i >= nat || index[i] != j)
84             {
85                 gmx_fatal(FARGS, "The index group does not consist of whole molecules");
86             }
87             i++;
88         }
89         index[nmol++] = mol;
90     }
91
92     fprintf(stderr, "\nSplit group of %d atoms into %d molecules\n", nat, nmol);
93
94     *n = nmol;
95 }
96
97 static void precalc(t_topology top, real normm[])
98 {
99
100     real mtot;
101     int  i, j, k, l;
102
103     for (i = 0; i < top.mols.nr; i++)
104     {
105         k    = top.mols.index[i];
106         l    = top.mols.index[i+1];
107         mtot = 0.0;
108
109         for (j = k; j < l; j++)
110         {
111             mtot += top.atoms.atom[j].m;
112         }
113
114         for (j = k; j < l; j++)
115         {
116             normm[j] = top.atoms.atom[j].m/mtot;
117         }
118
119     }
120
121 }
122
123 static void calc_spectrum(int n, real c[], real dt, const char *fn,
124                           output_env_t oenv, gmx_bool bRecip)
125 {
126     FILE     *fp;
127     gmx_fft_t fft;
128     int       i, status;
129     real     *data;
130     real      nu, omega, recip_fac;
131
132     snew(data, n*2);
133     for (i = 0; (i < n); i++)
134     {
135         data[i] = c[i];
136     }
137
138     if ((status = gmx_fft_init_1d_real(&fft, n, GMX_FFT_FLAG_NONE)) != 0)
139     {
140         gmx_fatal(FARGS, "Invalid fft return status %d", status);
141     }
142     if ((status = gmx_fft_1d_real(fft, GMX_FFT_REAL_TO_COMPLEX, data, data)) != 0)
143     {
144         gmx_fatal(FARGS, "Invalid fft return status %d", status);
145     }
146     fp = xvgropen(fn, "Vibrational Power Spectrum",
147                   bRecip ? "\\f{12}w\\f{4} (cm\\S-1\\N)" :
148                   "\\f{12}n\\f{4} (ps\\S-1\\N)",
149                   "a.u.", oenv);
150     /* This is difficult.
151      * The length of the ACF is dt (as passed to this routine).
152      * We pass the vacf with N time steps from 0 to dt.
153      * That means that after FFT we have lowest frequency = 1/dt
154      * then 1/(2 dt) etc. (this is the X-axis of the data after FFT).
155      * To convert to 1/cm we need to have to realize that
156      * E = hbar w = h nu = h c/lambda. We want to have reciprokal cm
157      * on the x-axis, that is 1/lambda, so we then have
158      * 1/lambda = nu/c. Since nu has units of 1/ps and c has gromacs units
159      * of nm/ps, we need to multiply by 1e7.
160      * The timestep between saving the trajectory is
161      * 1e7 is to convert nanometer to cm
162      */
163     recip_fac = bRecip ? (1e7/SPEED_OF_LIGHT) : 1.0;
164     for (i = 0; (i < n); i += 2)
165     {
166         nu    = i/(2*dt);
167         omega = nu*recip_fac;
168         /* Computing the square magnitude of a complex number, since this is a power
169          * spectrum.
170          */
171         fprintf(fp, "%10g  %10g\n", omega, sqr(data[i])+sqr(data[i+1]));
172     }
173     xvgrclose(fp);
174     gmx_fft_destroy(fft);
175     sfree(data);
176 }
177
178 int gmx_velacc(int argc, char *argv[])
179 {
180     const char     *desc[] = {
181         "[THISMODULE] computes the velocity autocorrelation function.",
182         "When the [TT]-m[tt] option is used, the momentum autocorrelation",
183         "function is calculated.[PAR]",
184         "With option [TT]-mol[tt] the velocity autocorrelation function of",
185         "molecules is calculated. In this case the index group should consist",
186         "of molecule numbers instead of atom numbers.[PAR]",
187         "Be sure that your trajectory contains frames with velocity information",
188         "(i.e. [TT]nstvout[tt] was set in your original [TT].mdp[tt] file),",
189         "and that the time interval between data collection points is",
190         "much shorter than the time scale of the autocorrelation."
191     };
192
193     static gmx_bool bMass = FALSE, bMol = FALSE, bRecip = TRUE;
194     t_pargs         pa[]  = {
195         { "-m", FALSE, etBOOL, {&bMass},
196           "Calculate the momentum autocorrelation function" },
197         { "-recip", FALSE, etBOOL, {&bRecip},
198           "Use cm^-1 on X-axis instead of 1/ps for spectra." },
199         { "-mol", FALSE, etBOOL, {&bMol},
200           "Calculate the velocity acf of molecules" }
201     };
202
203     t_topology      top;
204     int             ePBC = -1;
205     t_trxframe      fr;
206     matrix          box;
207     gmx_bool        bTPS = FALSE, bTop = FALSE;
208     int             gnx;
209     atom_id        *index;
210     char           *grpname;
211     char            title[256];
212     /* t0, t1 are the beginning and end time respectively.
213      * dt is the time step, mass is temp variable for atomic mass.
214      */
215     real              t0, t1, dt, mass;
216     t_trxstatus      *status;
217     int               counter, n_alloc, i, j, counter_dim, k, l;
218     rvec              mv_mol;
219     /* Array for the correlation function */
220     real            **c1;
221     real             *normm = NULL;
222     output_env_t      oenv;
223
224 #define NHISTO 360
225
226     t_filenm  fnm[] = {
227         { efTRN, "-f",    NULL,   ffREAD  },
228         { efTPS, NULL,    NULL,   ffOPTRD },
229         { efNDX, NULL,    NULL,   ffOPTRD },
230         { efXVG, "-o",    "vac",  ffWRITE },
231         { efXVG, "-os",   "spectrum", ffOPTWR }
232     };
233 #define NFILE asize(fnm)
234     int       npargs;
235     t_pargs  *ppa;
236
237     npargs = asize(pa);
238     ppa    = add_acf_pargs(&npargs, pa);
239     if (!parse_common_args(&argc, argv, PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
240                            NFILE, fnm, npargs, ppa, asize(desc), desc, 0, NULL, &oenv))
241     {
242         return 0;
243     }
244
245     if (bMol || bMass)
246     {
247         bTPS = ftp2bSet(efTPS, NFILE, fnm) || !ftp2bSet(efNDX, NFILE, fnm);
248     }
249
250     if (bTPS)
251     {
252         bTop = read_tps_conf(ftp2fn(efTPS, NFILE, fnm), title, &top, &ePBC, NULL, NULL, box,
253                              TRUE);
254         get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 1, &gnx, &index, &grpname);
255     }
256     else
257     {
258         rd_index(ftp2fn(efNDX, NFILE, fnm), 1, &gnx, &index, &grpname);
259     }
260
261     if (bMol)
262     {
263         if (!bTop)
264         {
265             gmx_fatal(FARGS, "Need a topology to determine the molecules");
266         }
267         snew(normm, top.atoms.nr);
268         precalc(top, normm);
269         index_atom2mol(&gnx, index, &top.mols);
270     }
271
272     /* Correlation stuff */
273     snew(c1, gnx);
274     for (i = 0; (i < gnx); i++)
275     {
276         c1[i] = NULL;
277     }
278
279     read_first_frame(oenv, &status, ftp2fn(efTRN, NFILE, fnm), &fr, TRX_NEED_V);
280     t0 = fr.time;
281
282     n_alloc = 0;
283     counter = 0;
284     do
285     {
286         if (counter >= n_alloc)
287         {
288             n_alloc += 100;
289             for (i = 0; i < gnx; i++)
290             {
291                 srenew(c1[i], DIM*n_alloc);
292             }
293         }
294         counter_dim = DIM*counter;
295         if (bMol)
296         {
297             for (i = 0; i < gnx; i++)
298             {
299                 clear_rvec(mv_mol);
300                 k = top.mols.index[index[i]];
301                 l = top.mols.index[index[i]+1];
302                 for (j = k; j < l; j++)
303                 {
304                     if (bMass)
305                     {
306                         mass = top.atoms.atom[j].m;
307                     }
308                     else
309                     {
310                         mass = normm[j];
311                     }
312                     mv_mol[XX] += mass*fr.v[j][XX];
313                     mv_mol[YY] += mass*fr.v[j][YY];
314                     mv_mol[ZZ] += mass*fr.v[j][ZZ];
315                 }
316                 c1[i][counter_dim+XX] = mv_mol[XX];
317                 c1[i][counter_dim+YY] = mv_mol[YY];
318                 c1[i][counter_dim+ZZ] = mv_mol[ZZ];
319             }
320         }
321         else
322         {
323             for (i = 0; i < gnx; i++)
324             {
325                 if (bMass)
326                 {
327                     mass = top.atoms.atom[index[i]].m;
328                 }
329                 else
330                 {
331                     mass = 1;
332                 }
333                 c1[i][counter_dim+XX] = mass*fr.v[index[i]][XX];
334                 c1[i][counter_dim+YY] = mass*fr.v[index[i]][YY];
335                 c1[i][counter_dim+ZZ] = mass*fr.v[index[i]][ZZ];
336             }
337         }
338
339         t1 = fr.time;
340
341         counter++;
342     }
343     while (read_next_frame(oenv, status, &fr));
344
345     close_trj(status);
346
347     if (counter >= 4)
348     {
349         /* Compute time step between frames */
350         dt = (t1-t0)/(counter-1);
351         do_autocorr(opt2fn("-o", NFILE, fnm), oenv,
352                     bMass ?
353                     "Momentum Autocorrelation Function" :
354                     "Velocity Autocorrelation Function",
355                     counter, gnx, c1, dt, eacVector, TRUE);
356
357         do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
358
359         if (opt2bSet("-os", NFILE, fnm))
360         {
361             calc_spectrum(counter/2, (real *) (c1[0]), (t1-t0)/2, opt2fn("-os", NFILE, fnm),
362                           oenv, bRecip);
363             do_view(oenv, opt2fn("-os", NFILE, fnm), "-nxy");
364         }
365     }
366     else
367     {
368         fprintf(stderr, "Not enough frames in trajectory - no output generated.\n");
369     }
370
371     return 0;
372 }