Merge "Fix bug in selection subexpression handling."
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_dielectric.c
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  *                        VERSION 3.2.0
10  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12  * Copyright (c) 2001-2004, The GROMACS development team,
13  * check out http://www.gromacs.org for more information.
14
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * If you want to redistribute modifications, please consider that
21  * scientific software is very special. Version control is crucial -
22  * bugs must be traceable. We will be happy to consider code for
23  * inclusion in the official distribution, but derived work must not
24  * be called official GROMACS. Details are found in the README & COPYING
25  * files - if they are missing, get the official version at www.gromacs.org.
26  *
27  * To help us fund GROMACS development, we humbly ask that you cite
28  * the papers on the package - you can find them in the top README file.
29  *
30  * For more info, check our website at http://www.gromacs.org
31  *
32  * And Hey:
33  * Green Red Orange Magenta Azure Cyan Skyblue
34  */
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <ctype.h>
41 #include <math.h>
42
43 #include "copyrite.h"
44 #include "typedefs.h"
45 #include "string2.h"
46 #include "gstat.h"
47 #include "smalloc.h"
48 #include "futil.h"
49 #include "macros.h"
50 #include "maths.h"
51 #include "xvgr.h"
52 #include "gmxcomplex.h"
53 #include "correl.h"
54 #include "gmx_ana.h"
55 #include "gmx_fatal.h"
56
57
58 /* Determines at which point in the array the fit should start */
59 int calc_nbegin(int nx, real x[], real tbegin)
60 {
61     int  nbegin;
62     real dt, dtt;
63
64     /* Assume input x is sorted */
65     for (nbegin = 0; (nbegin < nx) && (x[nbegin] <= tbegin); nbegin++)
66     {
67         ;
68     }
69     if ((nbegin == nx) || (nbegin == 0))
70     {
71         gmx_fatal(FARGS, "Begin time %f not in x-domain [%f through %f]\n",
72                   tbegin, x[0], x[nx-1]);
73     }
74
75     /* Take the one closest to tbegin */
76     if (fabs(x[nbegin]-tbegin) > fabs(x[nbegin-1]-tbegin))
77     {
78         nbegin--;
79     }
80
81     printf("nbegin = %d, x[nbegin] = %g, tbegin = %g\n",
82            nbegin, x[nbegin], tbegin);
83
84     return nbegin;
85 }
86
87 real numerical_deriv(int nx, real x[], real y[], real fity[], real combined[], real dy[],
88                      real tendInt, int nsmooth)
89 {
90     FILE *tmpfp;
91     int   i, nbegin, i0, i1;
92     real  fac, fx, fy, integralSmth;
93
94     nbegin = calc_nbegin(nx, x, tendInt);
95     if (nsmooth == 0)
96     {
97         for (i = 0; (i < nbegin); i++)
98         {
99             combined[i] = y[i];
100         }
101         fac = y[nbegin]/fity[nbegin];
102         printf("scaling fitted curve by %g\n", fac);
103         for (i = nbegin; (i < nx); i++)
104         {
105             combined[i] = fity[i]*fac;
106         }
107     }
108     else
109     {
110         i0 = max(0, nbegin);
111         i1 = min(nx-1, nbegin+nsmooth);
112         printf("Making smooth transition from %d through %d\n", i0, i1);
113         for (i = 0; (i < i0); i++)
114         {
115             combined[i] = y[i];
116         }
117         for (i = i0; (i <= i1); i++)
118         {
119             fx = (i1-i)/(real)(i1-i0);
120             fy = (i-i0)/(real)(i1-i0);
121             if (debug)
122             {
123                 fprintf(debug, "x: %g factors for smoothing: %10g %10g\n", x[i], fx, fy);
124             }
125             combined[i] = fx*y[i] + fy*fity[i];
126         }
127         for (i = i1+1; (i < nx); i++)
128         {
129             combined[i] = fity[i];
130         }
131     }
132
133     tmpfp        = ffopen("integral_smth.xvg", "w");
134     integralSmth = print_and_integrate(tmpfp, nx, x[1]-x[0], combined, NULL, 1);
135     printf("SMOOTH integral = %10.5e\n", integralSmth);
136
137     dy[0] = (combined[1]-combined[0])/(x[1]-x[0]);
138     for (i = 1; (i < nx-1); i++)
139     {
140         dy[i] = (combined[i+1]-combined[i-1])/(x[i+1]-x[i-1]);
141     }
142     dy[nx-1] = (combined[nx-1]-combined[nx-2])/(x[nx-1]-x[nx-2]);
143
144     for (i = 0; (i < nx); i++)
145     {
146         dy[i] *= -1;
147     }
148
149     return integralSmth;
150 }
151
152 void do_four(const char *fn, const char *cn, int nx, real x[], real dy[],
153              real eps0, real epsRF, const output_env_t oenv)
154 {
155     FILE      *fp, *cp;
156     t_complex *tmp, gw, hw, kw;
157     int        i, nnx, nxsav;
158     real       fac, nu, dt, *ptr, maxeps, numax;
159
160     nxsav = nx;
161     /*while ((dy[nx-1] == 0.0) && (nx > 0))
162        nx--;*/
163     if (nx == 0)
164     {
165         gmx_fatal(FARGS, "Empty dataset in %s, line %d!", __FILE__, __LINE__);
166     }
167
168     nnx = 1;
169     while (nnx < 2*nx)
170     {
171         nnx *= 2;
172     }
173     snew(tmp, 2*nnx);
174     printf("Doing FFT of %d points\n", nnx);
175     for (i = 0; (i < nx); i++)
176     {
177         tmp[i].re = dy[i];
178     }
179     ptr = &tmp[0].re;
180     four1(ptr-1, nnx, -1);
181
182     dt = x[1]-x[0];
183     if (epsRF == 0)
184     {
185         fac = (eps0-1)/tmp[0].re;
186     }
187     else
188     {
189         fac = ((eps0-1)/(2*epsRF+eps0))/tmp[0].re;
190     }
191     fp     = xvgropen(fn, "Epsilon(\\8w\\4)", "Freq. (GHz)", "eps", oenv);
192     cp     = xvgropen(cn, "Cole-Cole plot", "Eps'", "Eps''", oenv);
193     maxeps = 0;
194     numax  = 0;
195     for (i = 0; (i < nxsav); i++)
196     {
197         if (epsRF == 0)
198         {
199             kw.re = 1+fac*tmp[i].re;
200             kw.im = 1+fac*tmp[i].im;
201         }
202         else
203         {
204             gw     = rcmul(fac, tmp[i]);
205             hw     = rcmul(2*epsRF, gw);
206             hw.re += 1.0;
207             gw.re  = 1.0 - gw.re;
208             gw.im  = -gw.im;
209             kw     = cdiv(hw, gw);
210         }
211         kw.im *= -1;
212
213         nu     = (i+1)*1000.0/(nnx*dt);
214         if (kw.im > maxeps)
215         {
216             maxeps = kw.im;
217             numax  = nu;
218         }
219
220         fprintf(fp, "%10.5e  %10.5e  %10.5e\n", nu, kw.re, kw.im);
221         fprintf(cp, "%10.5e  %10.5e\n", kw.re, kw.im);
222     }
223     printf("MAXEPS = %10.5e at frequency %10.5e GHz (tauD = %8.1f ps)\n",
224            maxeps, numax, 1000/(2*M_PI*numax));
225     ffclose(fp);
226     ffclose(cp);
227     sfree(tmp);
228 }
229
230 int gmx_dielectric(int argc, char *argv[])
231 {
232     const char  *desc[] = {
233         "[TT]g_dielectric[tt] calculates frequency dependent dielectric constants",
234         "from the autocorrelation function of the total dipole moment in",
235         "your simulation. This ACF can be generated by [TT]g_dipoles[tt].",
236         "The functional forms of the available functions are:[PAR]",
237         "One parameter:    y = [EXP]-a[SUB]1[sub] x[exp],[BR]",
238         "Two parameters:   y = a[SUB]2[sub] [EXP]-a[SUB]1[sub] x[exp],[BR]",
239         "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].[BR]",
240         "Start values for the fit procedure can be given on the command line.",
241         "It is also possible to fix parameters at their start value, use [TT]-fix[tt]",
242         "with the number of the parameter you want to fix.",
243         "[PAR]",
244         "Three output files are generated, the first contains the ACF,",
245         "an exponential fit to it with 1, 2 or 3 parameters, and the",
246         "numerical derivative of the combination data/fit.",
247         "The second file contains the real and imaginary parts of the",
248         "frequency-dependent dielectric constant, the last gives a plot",
249         "known as the Cole-Cole plot, in which the imaginary",
250         "component is plotted as a function of the real component.",
251         "For a pure exponential relaxation (Debye relaxation) the latter",
252         "plot should be one half of a circle."
253     };
254     t_filenm     fnm[] = {
255         { efXVG, "-f", "dipcorr", ffREAD  },
256         { efXVG, "-d", "deriv",  ffWRITE },
257         { efXVG, "-o", "epsw",   ffWRITE },
258         { efXVG, "-c", "cole",   ffWRITE }
259     };
260 #define NFILE asize(fnm)
261     output_env_t oenv;
262     int          i, j, nx, ny, nxtail, eFitFn, nfitparm;
263     real         dt, integral, fitintegral, *fitparms, fac, rffac;
264     double     **yd;
265     real       **y;
266     const char  *legend[] = { "Correlation", "Std. Dev.", "Fit", "Combined", "Derivative" };
267     static int   fix      = 0, bFour = 0, bX = 1, nsmooth = 3;
268     static real  tendInt  = 5.0, tbegin = 5.0, tend = 500.0;
269     static real  A        = 0.5, tau1 = 10.0, tau2 = 1.0, eps0 = 80, epsRF = 78.5, tail = 500.0;
270     real         lambda;
271     t_pargs      pa[] = {
272         { "-fft", FALSE, etBOOL, {&bFour},
273           "use fast fourier transform for correlation function" },
274         { "-x1",  FALSE, etBOOL, {&bX},
275           "use first column as [IT]x[it]-axis rather than first data set" },
276         { "-eint", FALSE, etREAL, {&tendInt},
277           "Time to end the integration of the data and start to use the fit"},
278         { "-bfit", FALSE, etREAL, {&tbegin},
279           "Begin time of fit" },
280         { "-efit", FALSE, etREAL, {&tend},
281           "End time of fit" },
282         { "-tail", FALSE, etREAL, {&tail},
283           "Length of function including data and tail from fit" },
284         { "-A", FALSE, etREAL, {&A},
285           "Start value for fit parameter A" },
286         { "-tau1", FALSE, etREAL, {&tau1},
287           "Start value for fit parameter [GRK]tau[grk]1" },
288         { "-tau2", FALSE, etREAL, {&tau2},
289           "Start value for fit parameter [GRK]tau[grk]2" },
290         { "-eps0", FALSE, etREAL, {&eps0},
291           "[GRK]epsilon[grk]0 of your liquid" },
292         { "-epsRF", FALSE, etREAL, {&epsRF},
293           "[GRK]epsilon[grk] of the reaction field used in your simulation. A value of 0 means infinity." },
294         { "-fix", FALSE, etINT,  {&fix},
295           "Fix parameters at their start values, A (2), tau1 (1), or tau2 (4)" },
296         { "-ffn",    FALSE, etENUM, {s_ffn},
297           "Fit function" },
298         { "-nsmooth", FALSE, etINT, {&nsmooth},
299           "Number of points for smoothing" }
300     };
301
302     parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW | PCA_BE_NICE,
303                       NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv);
304     please_cite(stdout, "Spoel98a");
305     printf("WARNING: non-polarizable models can never yield an infinite\n"
306            "dielectric constant that is different from 1. This is incorrect\n"
307            "in the reference given above (Spoel98a).\n\n");
308
309
310     nx     = read_xvg(opt2fn("-f", NFILE, fnm), &yd, &ny);
311     dt     = yd[0][1] - yd[0][0];
312     nxtail = min(tail/dt, nx);
313
314     printf("Read data set containing %d colums and %d rows\n", ny, nx);
315     printf("Assuming (from data) that timestep is %g, nxtail = %d\n",
316            dt, nxtail);
317     snew(y, 6);
318     for (i = 0; (i < ny); i++)
319     {
320         snew(y[i], max(nx, nxtail));
321     }
322     for (i = 0; (i < nx); i++)
323     {
324         y[0][i] = yd[0][i];
325         for (j = 1; (j < ny); j++)
326         {
327             y[j][i] = yd[j][i];
328         }
329     }
330     if (nxtail > nx)
331     {
332         for (i = nx; (i < nxtail); i++)
333         {
334             y[0][i] = dt*i+y[0][0];
335             for (j = 1; (j < ny); j++)
336             {
337                 y[j][i] = 0.0;
338             }
339         }
340         nx = nxtail;
341     }
342
343
344     /* We have read a file WITHOUT standard deviations, so we make our own... */
345     if (ny == 2)
346     {
347         printf("Creating standard deviation numbers ...\n");
348         srenew(y, 3);
349         snew(y[2], nx);
350
351         fac = 1.0/((real)nx);
352         for (i = 0; (i < nx); i++)
353         {
354             y[2][i] = fac;
355         }
356     }
357
358     eFitFn   = sffn2effn(s_ffn);
359     nfitparm = nfp_ffn[eFitFn];
360     snew(fitparms, 4);
361     fitparms[0] = tau1;
362     if (nfitparm > 1)
363     {
364         fitparms[1] = A;
365     }
366     if (nfitparm > 2)
367     {
368         fitparms[2] = tau2;
369     }
370
371
372     snew(y[3], nx);
373     snew(y[4], nx);
374     snew(y[5], nx);
375
376     integral = print_and_integrate(NULL, calc_nbegin(nx, y[0], tbegin),
377                                    dt, y[1], NULL, 1);
378     integral += do_lmfit(nx, y[1], y[2], dt, y[0], tbegin, tend,
379                          oenv, TRUE, eFitFn, fitparms, fix);
380     for (i = 0; i < nx; i++)
381     {
382         y[3][i] = fit_function(eFitFn, fitparms, y[0][i]);
383     }
384
385     if (epsRF == 0)
386     {
387         /* This means infinity! */
388         lambda = 0;
389         rffac  = 1;
390     }
391     else
392     {
393         lambda = (eps0 - 1.0)/(2*epsRF - 1.0);
394         rffac  = (2*epsRF+eps0)/(2*epsRF+1);
395     }
396     printf("DATA INTEGRAL: %5.1f, tauD(old) = %5.1f ps, "
397            "tau_slope = %5.1f, tau_slope,D = %5.1f ps\n",
398            integral, integral*rffac, fitparms[0], fitparms[0]*rffac);
399
400     printf("tau_D from tau1 = %8.3g , eps(Infty) = %8.3f\n",
401            fitparms[0]*(1 + fitparms[1]*lambda),
402            1 + ((1 - fitparms[1])*(eps0 - 1))/(1 + fitparms[1]*lambda));
403
404     fitintegral = numerical_deriv(nx, y[0], y[1], y[3], y[4], y[5], tendInt, nsmooth);
405     printf("FIT INTEGRAL (tau_M): %5.1f, tau_D = %5.1f\n",
406            fitintegral, fitintegral*rffac);
407
408     /* Now we have the negative gradient of <Phi(0) Phi(t)> */
409     write_xvg(opt2fn("-d", NFILE, fnm), "Data", nx-1, 6, y, legend, oenv);
410
411     /* Do FFT and analysis */
412     do_four(opt2fn("-o", NFILE, fnm), opt2fn("-c", NFILE, fnm),
413             nx-1, y[0], y[5], eps0, epsRF, oenv);
414
415     do_view(oenv, opt2fn("-o", NFILE, fnm), "-nxy");
416     do_view(oenv, opt2fn("-c", NFILE, fnm), NULL);
417     do_view(oenv, opt2fn("-d", NFILE, fnm), "-nxy");
418
419     thanx(stderr);
420
421     return 0;
422 }