d8cd57f75c33af92c34b67c1b0ca72a3ca747651
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_dielectric.cpp
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,2015, 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 #include "gmxpre.h"
38
39 #include <cmath>
40 #include <cstdio>
41 #include <cstdlib>
42 #include <cstring>
43
44 #include <algorithm>
45
46 #include "gromacs/correlationfunctions/expfit.h"
47 #include "gromacs/correlationfunctions/integrate.h"
48 #include "gromacs/fft/fft.h"
49 #include "gromacs/fileio/xvgr.h"
50 #include "gromacs/gmxana/gmx_ana.h"
51 #include "gromacs/gmxana/gstat.h"
52 #include "gromacs/legacyheaders/copyrite.h"
53 #include "gromacs/legacyheaders/macros.h"
54 #include "gromacs/legacyheaders/typedefs.h"
55 #include "gromacs/legacyheaders/viewit.h"
56 #include "gromacs/math/gmxcomplex.h"
57 #include "gromacs/math/utilities.h"
58 #include "gromacs/utility/fatalerror.h"
59 #include "gromacs/utility/futil.h"
60 #include "gromacs/utility/smalloc.h"
61 #include "gromacs/utility/fatalerror.h"
62 #include "gromacs/math/gmxcomplex.h"
63 #include "gromacs/math/utilities.h"
64 #include "gmx_ana.h"
65
66 /* Determines at which point in the array the fit should start */
67 int calc_nbegin(int nx, real x[], real tbegin)
68 {
69     int  nbegin;
70
71     /* Assume input x is sorted */
72     for (nbegin = 0; (nbegin < nx) && (x[nbegin] <= tbegin); nbegin++)
73     {
74         ;
75     }
76     if ((nbegin == nx) || (nbegin == 0))
77     {
78         gmx_fatal(FARGS, "Begin time %f not in x-domain [%f through %f]\n",
79                   tbegin, x[0], x[nx-1]);
80     }
81
82     /* Take the one closest to tbegin */
83     if (std::abs(x[nbegin]-tbegin) > std::abs(x[nbegin-1]-tbegin))
84     {
85         nbegin--;
86     }
87
88     printf("nbegin = %d, x[nbegin] = %g, tbegin = %g\n",
89            nbegin, x[nbegin], tbegin);
90
91     return nbegin;
92 }
93
94 real numerical_deriv(int nx, real x[], real y[], real fity[], real combined[], real dy[],
95                      real tendInt, int nsmooth)
96 {
97     FILE *tmpfp;
98     int   i, nbegin, i0, i1;
99     real  fac, fx, fy, integralSmth;
100
101     nbegin = calc_nbegin(nx, x, tendInt);
102     if (nsmooth == 0)
103     {
104         for (i = 0; (i < nbegin); i++)
105         {
106             combined[i] = y[i];
107         }
108         fac = y[nbegin]/fity[nbegin];
109         printf("scaling fitted curve by %g\n", fac);
110         for (i = nbegin; (i < nx); i++)
111         {
112             combined[i] = fity[i]*fac;
113         }
114     }
115     else
116     {
117         i0 = std::max(0, nbegin);
118         i1 = std::min(nx-1, nbegin+nsmooth);
119         printf("Making smooth transition from %d through %d\n", i0, i1);
120         for (i = 0; (i < i0); i++)
121         {
122             combined[i] = y[i];
123         }
124         for (i = i0; (i <= i1); i++)
125         {
126             fx = static_cast<real>(i1-i)/(i1-i0);
127             fy = static_cast<real>(i-i0)/(i1-i0);
128             if (debug)
129             {
130                 fprintf(debug, "x: %g factors for smoothing: %10g %10g\n", x[i], fx, fy);
131             }
132             combined[i] = fx*y[i] + fy*fity[i];
133         }
134         for (i = i1+1; (i < nx); i++)
135         {
136             combined[i] = fity[i];
137         }
138     }
139
140     tmpfp        = gmx_ffopen("integral_smth.xvg", "w");
141     integralSmth = print_and_integrate(tmpfp, nx, x[1]-x[0], combined, NULL, 1);
142     printf("SMOOTH integral = %10.5e\n", integralSmth);
143
144     dy[0] = (combined[1]-combined[0])/(x[1]-x[0]);
145     for (i = 1; (i < nx-1); i++)
146     {
147         dy[i] = (combined[i+1]-combined[i-1])/(x[i+1]-x[i-1]);
148     }
149     dy[nx-1] = (combined[nx-1]-combined[nx-2])/(x[nx-1]-x[nx-2]);
150
151     for (i = 0; (i < nx); i++)
152     {
153         dy[i] *= -1;
154     }
155
156     return integralSmth;
157 }
158
159 void do_four(const char *fn, const char *cn, int nx, real x[], real dy[],
160              real eps0, real epsRF, const output_env_t oenv)
161 {
162     FILE      *fp, *cp;
163     t_complex *tmp, gw, hw, kw;
164     int        i, nnx, nxsav;
165     real       fac, nu, dt, maxeps, numax;
166     gmx_fft_t  fft;
167     int        fftcode;
168
169     nxsav = nx;
170     /*while ((dy[nx-1] == 0.0) && (nx > 0))
171        nx--;*/
172     if (nx == 0)
173     {
174         gmx_fatal(FARGS, "Empty dataset in %s, line %d!", __FILE__, __LINE__);
175     }
176
177     nnx = 1;
178     while (nnx < 2*nx)
179     {
180         nnx *= 2;
181     }
182
183     snew(tmp, 2*nnx);
184     printf("Doing FFT of %d points\n", nnx);
185     for (i = 0; (i < nx); i++)
186     {
187         tmp[i].re = dy[i];
188     }
189     if ((fftcode = gmx_fft_init_1d_real(&fft, nnx,
190                                         GMX_FFT_FLAG_NONE)) != 0)
191     {
192         gmx_fatal(FARGS, "gmx_fft_init_1d_real returned %d", fftcode);
193     }
194     if ((fftcode = gmx_fft_1d_real(fft, GMX_FFT_COMPLEX_TO_REAL,
195                                    (void *)tmp, (void *)tmp)) != 0)
196     {
197         gmx_fatal(FARGS, "gmx_fft_1d_real returned %d", fftcode);
198     }
199     gmx_fft_destroy(fft);
200     dt = x[1]-x[0];
201     if (epsRF == 0)
202     {
203         fac = (eps0-1)/tmp[0].re;
204     }
205     else
206     {
207         fac = ((eps0-1)/(2*epsRF+eps0))/tmp[0].re;
208     }
209     fp     = xvgropen(fn, "Epsilon(\\8w\\4)", "Freq. (GHz)", "eps", oenv);
210     cp     = xvgropen(cn, "Cole-Cole plot", "Eps'", "Eps''", oenv);
211     maxeps = 0;
212     numax  = 0;
213     for (i = 0; (i < nxsav); i++)
214     {
215         if (epsRF == 0)
216         {
217             kw.re = 1+fac*tmp[i].re;
218             kw.im = 1+fac*tmp[i].im;
219         }
220         else
221         {
222             gw     = rcmul(fac, tmp[i]);
223             hw     = rcmul(2*epsRF, gw);
224             hw.re += 1.0;
225             gw.re  = 1.0 - gw.re;
226             gw.im  = -gw.im;
227             kw     = cdiv(hw, gw);
228         }
229         kw.im *= -1;
230
231         nu     = (i+1)*1000.0/(nnx*dt);
232         if (kw.im > maxeps)
233         {
234             maxeps = kw.im;
235             numax  = nu;
236         }
237
238         fprintf(fp, "%10.5e  %10.5e  %10.5e\n", nu, kw.re, kw.im);
239         fprintf(cp, "%10.5e  %10.5e\n", kw.re, kw.im);
240     }
241     printf("MAXEPS = %10.5e at frequency %10.5e GHz (tauD = %8.1f ps)\n",
242            maxeps, numax, 1000/(2*M_PI*numax));
243     xvgrclose(fp);
244     xvgrclose(cp);
245     sfree(tmp);
246 }
247
248 int gmx_dielectric(int argc, char *argv[])
249 {
250     const char  *desc[] = {
251         "[THISMODULE] calculates frequency dependent dielectric constants",
252         "from the autocorrelation function of the total dipole moment in",
253         "your simulation. This ACF can be generated by [gmx-dipoles].",
254         "The functional forms of the available functions are:",
255         "",
256         " * One parameter:    y = [EXP]-a[SUB]1[sub] x[exp],",
257         " * Two parameters:   y = a[SUB]2[sub] [EXP]-a[SUB]1[sub] x[exp],",
258         " * Three parameters: y = a[SUB]2[sub] [EXP]-a[SUB]1[sub] x[exp] + (1 - a[SUB]2[sub]) [EXP]-a[SUB]3[sub] x[exp].",
259         "",
260         "Start values for the fit procedure can be given on the command line.",
261         "It is also possible to fix parameters at their start value, use [TT]-fix[tt]",
262         "with the number of the parameter you want to fix.",
263         "[PAR]",
264         "Three output files are generated, the first contains the ACF,",
265         "an exponential fit to it with 1, 2 or 3 parameters, and the",
266         "numerical derivative of the combination data/fit.",
267         "The second file contains the real and imaginary parts of the",
268         "frequency-dependent dielectric constant, the last gives a plot",
269         "known as the Cole-Cole plot, in which the imaginary",
270         "component is plotted as a function of the real component.",
271         "For a pure exponential relaxation (Debye relaxation) the latter",
272         "plot should be one half of a circle."
273     };
274     t_filenm     fnm[] = {
275         { efXVG, "-f", "dipcorr", ffREAD  },
276         { efXVG, "-d", "deriv",  ffWRITE },
277         { efXVG, "-o", "epsw",   ffWRITE },
278         { efXVG, "-c", "cole",   ffWRITE }
279     };
280 #define NFILE asize(fnm)
281     output_env_t oenv;
282     int          i, j, nx, ny, nxtail, eFitFn, nfitparm;
283     real         dt, integral, fitintegral, fac, rffac;
284     double      *fitparms;
285     double     **yd;
286     real       **y;
287     const char  *legend[] = { "Correlation", "Std. Dev.", "Fit", "Combined", "Derivative" };
288     static int   fix      = 0, bX = 1, nsmooth = 3;
289     static real  tendInt  = 5.0, tbegin = 5.0, tend = 500.0;
290     static real  A        = 0.5, tau1 = 10.0, tau2 = 1.0, eps0 = 80, epsRF = 78.5, tail = 500.0;
291     real         lambda;
292     t_pargs      pa[] = {
293         { "-x1",  FALSE, etBOOL, {&bX},
294           "use first column as [IT]x[it]-axis rather than first data set" },
295         { "-eint", FALSE, etREAL, {&tendInt},
296           "Time to end the integration of the data and start to use the fit"},
297         { "-bfit", FALSE, etREAL, {&tbegin},
298           "Begin time of fit" },
299         { "-efit", FALSE, etREAL, {&tend},
300           "End time of fit" },
301         { "-tail", FALSE, etREAL, {&tail},
302           "Length of function including data and tail from fit" },
303         { "-A", FALSE, etREAL, {&A},
304           "Start value for fit parameter A" },
305         { "-tau1", FALSE, etREAL, {&tau1},
306           "Start value for fit parameter [GRK]tau[grk]1" },
307         { "-tau2", FALSE, etREAL, {&tau2},
308           "Start value for fit parameter [GRK]tau[grk]2" },
309         { "-eps0", FALSE, etREAL, {&eps0},
310           "[GRK]epsilon[grk]0 of your liquid" },
311         { "-epsRF", FALSE, etREAL, {&epsRF},
312           "[GRK]epsilon[grk] of the reaction field used in your simulation. A value of 0 means infinity." },
313         { "-fix", FALSE, etINT,  {&fix},
314           "Fix parameters at their start values, A (2), tau1 (1), or tau2 (4)" },
315         { "-ffn",    FALSE, etENUM, {s_ffn},
316           "Fit function" },
317         { "-nsmooth", FALSE, etINT, {&nsmooth},
318           "Number of points for smoothing" }
319     };
320
321     if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW,
322                            NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
323     {
324         return 0;
325     }
326     please_cite(stdout, "Spoel98a");
327     printf("WARNING: non-polarizable models can never yield an infinite\n"
328            "dielectric constant that is different from 1. This is incorrect\n"
329            "in the reference given above (Spoel98a).\n\n");
330
331
332     nx     = read_xvg(opt2fn("-f", NFILE, fnm), &yd, &ny);
333     dt     = yd[0][1] - yd[0][0];
334     nxtail = std::min(static_cast<int>(tail/dt), nx);
335
336     printf("Read data set containing %d colums and %d rows\n", ny, nx);
337     printf("Assuming (from data) that timestep is %g, nxtail = %d\n",
338            dt, nxtail);
339     snew(y, 6);
340     for (i = 0; (i < ny); i++)
341     {
342         snew(y[i], std::max(nx, nxtail));
343     }
344     for (i = 0; (i < nx); i++)
345     {
346         y[0][i] = yd[0][i];
347         for (j = 1; (j < ny); j++)
348         {
349             y[j][i] = yd[j][i];
350         }
351     }
352     if (nxtail > nx)
353     {
354         for (i = nx; (i < nxtail); i++)
355         {
356             y[0][i] = dt*i+y[0][0];
357             for (j = 1; (j < ny); j++)
358             {
359                 y[j][i] = 0.0;
360             }
361         }
362         nx = nxtail;
363     }
364
365
366     /* We have read a file WITHOUT standard deviations, so we make our own... */
367     if (ny == 2)
368     {
369         printf("Creating standard deviation numbers ...\n");
370         srenew(y, 3);
371         snew(y[2], nx);
372
373         fac = 1.0/nx;
374         for (i = 0; (i < nx); i++)
375         {
376             y[2][i] = fac;
377         }
378     }
379
380     eFitFn   = sffn2effn(s_ffn);
381     nfitparm = effnNparams(eFitFn);
382     snew(fitparms, 4);
383     fitparms[0] = tau1;
384     if (nfitparm > 1)
385     {
386         fitparms[1] = A;
387     }
388     if (nfitparm > 2)
389     {
390         fitparms[2] = tau2;
391     }
392
393
394     snew(y[3], nx);
395     snew(y[4], nx);
396     snew(y[5], nx);
397
398     integral = print_and_integrate(NULL, calc_nbegin(nx, y[0], tbegin),
399                                    dt, y[1], NULL, 1);
400     integral += do_lmfit(nx, y[1], y[2], dt, y[0], tbegin, tend,
401                          oenv, TRUE, eFitFn, fitparms, fix, NULL);
402     for (i = 0; i < nx; i++)
403     {
404         y[3][i] = fit_function(eFitFn, fitparms, y[0][i]);
405     }
406
407     if (epsRF == 0)
408     {
409         /* This means infinity! */
410         lambda = 0;
411         rffac  = 1;
412     }
413     else
414     {
415         lambda = (eps0 - 1.0)/(2*epsRF - 1.0);
416         rffac  = (2*epsRF+eps0)/(2*epsRF+1);
417     }
418     printf("DATA INTEGRAL: %5.1f, tauD(old) = %5.1f ps, "
419            "tau_slope = %5.1f, tau_slope,D = %5.1f ps\n",
420            integral, integral*rffac, fitparms[0], fitparms[0]*rffac);
421
422     printf("tau_D from tau1 = %8.3g , eps(Infty) = %8.3f\n",
423            fitparms[0]*(1 + fitparms[1]*lambda),
424            1 + ((1 - fitparms[1])*(eps0 - 1))/(1 + fitparms[1]*lambda));
425
426     fitintegral = numerical_deriv(nx, y[0], y[1], y[3], y[4], y[5], tendInt, nsmooth);
427     printf("FIT INTEGRAL (tau_M): %5.1f, tau_D = %5.1f\n",
428            fitintegral, fitintegral*rffac);
429
430     /* Now we have the negative gradient of <Phi(0) Phi(t)> */
431     write_xvg(opt2fn("-d", NFILE, fnm), "Data", nx-1, 6, y, legend, oenv);
432
433     /* Do FFT and analysis */
434     do_four(opt2fn("-o", NFILE, fnm), opt2fn("-c", NFILE, fnm),
435             nx-1, y[0], y[5], eps0, epsRF, oenv);
436
437     do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
438     do_view(oenv, opt2fn("-c", NFILE, fnm), NULL);
439     do_view(oenv, opt2fn("-d", NFILE, fnm), "-nxy");
440
441     return 0;
442 }