Partial commit of the project to remove all static variables.
[gromacs.git] / src / tools / g_msd.c
blob3ccc8609e4109e7105611ee7dee7cc7e7aae5791
1 /*
2 * $Id$
3 *
4 * This source code is part of
5 *
6 * G R O M A C S
7 *
8 * GROningen MAchine for Chemical Simulations
9 *
10 * VERSION 3.1
11 * Copyright (c) 1991-2001, University of Groningen, The Netherlands
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * If you want to redistribute modifications, please consider that
18 * scientific software is very special. Version control is crucial -
19 * bugs must be traceable. We will be happy to consider code for
20 * inclusion in the official distribution, but derived work must not
21 * be called official GROMACS. Details are found in the README & COPYING
22 * files - if they are missing, get the official version at www.gromacs.org.
24 * To help us fund GROMACS development, we humbly ask that you cite
25 * the papers on the package - you can find them in the top README file.
27 * For more info, check our website at http://www.gromacs.org
29 * And Hey:
30 * Gromacs Runs One Microsecond At Cannonball Speeds
34 #include <string.h>
35 #include <ctype.h>
36 #include <math.h>
37 #include "sysstuff.h"
38 #include "smalloc.h"
39 #include "macros.h"
40 #include "statutil.h"
41 #include "maths.h"
42 #include "futil.h"
43 #include "rdgroup.h"
44 #include "copyrite.h"
45 #include "typedefs.h"
46 #include "xvgr.h"
47 #include "gstat.h"
48 #include "tpxio.h"
49 #include "pbc.h"
50 #include "vec.h"
52 #define FACTOR 1000.0 /* Convert nm^2/ps to 10e-5 cm^2/s */
53 /* NORMAL = total diffusion coefficient (default). X,Y,Z is diffusion
54 coefficient in X,Y,Z direction. LATERAL is diffusion coefficient in
55 plane perpendicular to axis
57 enum { NOT_USED, NORMAL, X, Y, Z, LATERAL };
59 typedef struct {
60 real t0,delta_t,dim_factor;
61 real **data,*time,*data_x,*data_y,*data_z,*data_xy,*mass;
62 rvec **x0;
63 t_block *mols;
64 t_lsq **lsq;
65 int type,axis,natoms,nrestart,nnx,nframes,nlast,ngrp;
66 int *n_offs;
67 int **ndata;
68 } t_corr;
70 typedef real t_calc_func(t_corr *,int,atom_id[],int,rvec[]);
71 typedef void t_prep_data_func(t_corr *this,int gnx,atom_id index[],
72 rvec xcur[],rvec xprev[],matrix box);
74 static real thistime(t_corr *this)
76 return this->time[this->nframes];
79 static bool in_data(t_corr *this,int nx00)
81 return this->nframes-this->n_offs[nx00];
84 t_corr *init_corr(int nrgrp,int type,int axis,real dim_factor,
85 bool bMass,bool bMol,real dt,t_topology *top)
87 t_corr *this;
88 t_atoms *atoms;
89 int i;
91 snew(this,1);
92 this->type = type;
93 this->axis = axis;
94 this->ngrp = nrgrp;
95 this->nrestart = 0;
96 this->delta_t = dt;
97 this->x0 = NULL;
98 this->n_offs = NULL;
99 this->nframes = 0;
100 this->nlast = 0;
101 this->dim_factor = dim_factor;
103 snew(this->ndata,nrgrp);
104 snew(this->data,nrgrp);
105 for(i=0; (i<nrgrp); i++) {
106 this->ndata[i] = NULL;
107 this->data[i] = NULL;
109 this->time = NULL;
110 this->lsq = NULL;
111 if (bMass) {
112 atoms=&top->atoms;
113 snew(this->mass,atoms->nr);
114 for(i=0; (i<atoms->nr); i++) {
115 this->mass[i]=atoms->atom[i].m;
118 if (bMol)
119 this->mols = &(top->blocks[ebMOLS]);
121 return this;
124 static void done_corr(t_corr *this)
126 int i;
128 sfree(this->n_offs);
129 for(i=0; (i<this->nrestart); i++)
130 sfree(this->x0[i]);
131 sfree(this->x0);
134 static void init_restart(t_corr *this)
136 int i;
138 this->nrestart++;
140 srenew(this->x0,this->nrestart);
141 snew(this->x0[this->nrestart-1],this->natoms);
142 srenew(this->n_offs,this->nrestart);
145 static void corr_print(t_corr *this,char *fn,char *title,char *yaxis,
146 real beginfit,real endfit,
147 real *DD,real *SigmaD,char *grpname[])
149 FILE *out;
150 int i,j;
152 out=xvgropen(fn,title,xvgr_tlabel(),yaxis);
153 if (DD) {
154 fprintf(out,"# Diffusion constants fitted from time %g to %g (%s)\n",
155 beginfit,endfit,time_unit());
156 for(i=0; i<this->ngrp; i++)
157 fprintf(out,"# D[%10s] = %.3f (+/- %.3f) (1e-5 cm^2/s)\n",
158 grpname[i],DD[i],SigmaD[i]);
160 for(i=0; i<this->nframes; i++) {
161 fprintf(out,"%10g",convert_time(this->time[i]));
162 for(j=0; j<this->ngrp; j++)
163 fprintf(out," %10g",this->data[j][i]);
164 fprintf(out,"\n");
166 fclose(out);
169 /* called from corr_loop, to do the main calculations */
170 static void calc_corr(t_corr *this,int nr,int nx,atom_id index[],rvec xc[],
171 t_calc_func *calc1)
173 int nx0;
174 real g;
176 /* Check for new starting point */
177 if (this->nlast < this->nrestart) {
178 if ((thistime(this) >= (this->nlast*this->delta_t)) && (nr==0)) {
179 memcpy(this->x0[this->nlast],xc,this->natoms*sizeof(xc[0]));
180 this->n_offs[this->nlast]=this->nframes;
181 this->nlast++;
185 /* nx0 appears to be the number of new starting points,
186 * so for all starting points, call calc1.
188 for(nx0=0; (nx0<this->nlast); nx0++) {
189 g = calc1(this,nx,index,nx0,xc);
190 #ifdef DEBUG2
191 printf("g[%d]=%g\n",nx0,g);
192 #endif
193 this->data [nr][in_data(this,nx0)]+=g;
194 this->ndata[nr][in_data(this,nx0)]++;
198 static real calc1_norm(t_corr *this,int nx,atom_id index[],int nx0,rvec xc[])
200 int i,ix,m;
201 real g,r,r2;
203 g=0.0;
205 for(i=0; (i<nx); i++) {
206 ix=index[i];
207 r2=0.0;
208 switch (this->type) {
209 case NORMAL:
210 for(m=0; (m<DIM); m++) {
211 r = this->x0[nx0][ix][m]-xc[ix][m];
212 r2 += r*r;
214 break;
215 case X:
216 case Y:
217 case Z:
218 r = this->x0[nx0][ix][this->type-X]-xc[ix][this->type-X];
219 break;
220 case LATERAL:
221 for(m=0; (m<DIM); m++) {
222 if (m != this->axis) {
223 r = this->x0[nx0][ix][m]-xc[ix][m];
224 r2 += r*r;
227 break;
228 default:
229 fatal_error(0,"Error: did not expect option value %d",this->type);
231 g+=r2;
233 g/=nx;
235 return g;
238 /* this is the mean loop for the correlation type functions
239 * fx and nx are file pointers to things like read_first_x and
240 * read_next_x
242 void corr_loop(t_corr *this,char *fn,int gnx[],atom_id *index[],
243 t_calc_func *calc1,t_prep_data_func *prep1,real dt)
245 rvec *x[2];
246 real t;
247 int i,j,status,cur=0,maxframes=0;
248 #define prev (1-cur)
249 matrix box;
251 this->natoms=read_first_x(&status,fn,&this->t0,&(x[prev]),box);
252 #ifdef DEBUG
253 fprintf(stderr,"Read %d atoms for first frame\n",this->natoms);
254 #endif
256 snew(x[cur],this->natoms);
258 /* init_restart(this,nrestart,dt); */
259 memcpy(x[cur],x[prev],this->natoms*sizeof(x[prev][0]));
260 t=this->t0;
261 do {
262 if (bRmod(t-this->t0,dt))
263 init_restart(this);
264 if (this->nframes >= maxframes-1) {
265 if (maxframes==0) {
266 for(i=0; (i<this->ngrp); i++) {
267 this->ndata[i] = NULL;
268 this->data[i] = NULL;
270 this->time = NULL;
272 maxframes+=10;
273 for(i=0; (i<this->ngrp); i++) {
274 srenew(this->ndata[i],maxframes);
275 srenew(this->data[i],maxframes);
276 for(j=maxframes-10; j<maxframes; j++) {
277 this->ndata[i][j]=0;
278 this->data[i][j]=0;
281 srenew(this->time,maxframes);
284 this->time[this->nframes] = t - this->t0;
286 /* loop over all groups in index file */
287 for(i=0; (i<this->ngrp); i++) {
288 /* nice for putting things in boxes and such */
289 prep1(this,gnx[i],index[i],x[cur],x[prev],box);
290 /* calculate something useful, like mean square displacements */
291 calc_corr(this,i,gnx[i],index[i],x[cur],calc1);
293 cur=prev;
295 this->nframes++;
296 } while (read_next_x(status,&t,this->natoms,x[cur],box));
297 fprintf(stderr,"\nUsed %d restart points spaced %g %s over %g %s\n\n",
298 this->nrestart,
299 convert_time(dt), time_unit(),
300 convert_time(this->time[this->nframes-1]), time_unit() );
302 close_trj(status);
305 static void prep_data_mol(t_corr *this,int gnx,atom_id index[],
306 rvec xcur[],rvec xprev[],matrix box)
308 int i,j,k,m,d,ind;
309 rvec hbox;
311 /* Remove periodicity */
312 for(m=0; (m<DIM); m++)
313 hbox[m]=0.5*box[m][m];
314 for(i=0; i<gnx; i++) {
315 ind=index[i];
316 for(j=this->mols->index[ind]; j<this->mols->index[ind+1]; j++) {
317 k=this->mols->a[j];
318 for(m=DIM-1; m>=0; m--) {
319 while (xcur[k][m]-xprev[k][m] <= -hbox[m])
320 for(d=0; d<=m; d++)
321 xcur[k][d] += box[m][d];
322 while (xcur[k][m]-xprev[k][m] > hbox[m])
323 for(d=0; d<=m; d++)
324 xcur[k][d] -= box[m][d];
330 static real calc_one_mw(t_corr *this,int ix,int nx0,rvec xc[],real *tm)
332 real r2,r,mm;
333 int m;
335 mm=this->mass[ix];
336 if (mm < 1)
337 return 0;
338 (*tm)+=this->mass[ix];
339 r2=0.0;
340 switch (this->type) {
341 case NORMAL:
342 for(m=0; (m<DIM); m++) {
343 r = this->x0[nx0][ix][m]-xc[ix][m];
344 r2 += this->mass[ix]*r*r;
346 break;
347 case X:
348 case Y:
349 case Z:
350 r = this->x0[nx0][ix][this->type-X]-xc[ix][this->type-X];
351 r2 = this->mass[ix]*r*r;
352 break;
353 case LATERAL:
354 for(m=0; (m<DIM); m++) {
355 if (m != this->axis) {
356 r = this->x0[nx0][ix][m]-xc[ix][m];
357 r2 += this->mass[ix]*r*r;
360 break;
361 default:
362 fatal_error(0,"Options got screwed. Did not expect value %d\n",this->type);
363 } /* end switch */
364 return r2;
367 static real calc1_mw(t_corr *this,int nx,atom_id index[],int nx0,rvec xc[])
369 int i;
370 real g,tm;
372 g=tm=0.0;
373 for(i=0; (i<nx); i++)
374 g+=calc_one_mw(this,index[i],nx0,xc,&tm);
376 g/=tm;
378 return g;
381 static void prep_data_norm(t_corr *this,int gnx,atom_id index[],
382 rvec xcur[],rvec xprev[],matrix box)
384 int i,m,ind;
385 rvec hbox;
387 /* Remove periodicity */
388 for(m=0; (m<DIM); m++)
389 hbox[m]=0.5*box[m][m];
390 for(i=0; (i<gnx); i++) {
391 ind=index[i];
392 for(m=DIM-1; m>=0; m--) {
393 while(xcur[ind][m]-xprev[ind][m] <= hbox[m])
394 rvec_inc(xcur[ind],box[m]);
395 while(xcur[ind][m]-xprev[ind][m] > hbox[m])
396 rvec_dec(xcur[ind],box[m]);
401 static real calc1_mol(t_corr *this,int nx,atom_id index[],int nx0,rvec xc[])
403 int i,ii,j;
404 real g,mm,gtot,tt;
406 if (this->lsq == NULL) {
407 this->nnx = nx;
408 snew(this->lsq,this->nrestart);
409 for(i=0; (i<this->nrestart); i++)
410 snew(this->lsq[i],nx);
412 tt=this->time[in_data(this,nx0)];
413 gtot=0;
414 for(i=0; (i<nx); i++) {
415 ii=index[i];
416 g=mm=0;
417 for(j=this->mols->index[ii]; (j<this->mols->index[ii+1]); j++)
418 g += calc_one_mw(this,this->mols->a[j],nx0,xc,&mm);
420 g/=mm;
421 gtot+=g;
422 add_lsq(&(this->lsq[nx0][i]),tt,g);
424 return gtot/nx;
427 void printmol(t_corr *this,char *fn)
429 #define NDIST 100
430 FILE *out;
431 t_lsq lsq1;
432 int i,j;
433 real a,b,*D,Dav,D2av,VarD;
435 out=xvgropen(fn,"Diffusion Coefficients / Molecule","Molecule","D");
437 snew(D,this->nnx);
438 Dav = D2av = 0;
439 for(i=0; (i<this->nnx); i++) {
440 init_lsq(&lsq1);
441 for(j=0; (j<this->nrestart); j++) {
442 lsq1.sx+=this->lsq[j][i].sx;
443 lsq1.sy+=this->lsq[j][i].sy;
444 lsq1.xx+=this->lsq[j][i].xx;
445 lsq1.yx+=this->lsq[j][i].yx;
446 lsq1.np+=this->lsq[j][i].np;
448 get_lsq_ab(&lsq1,&a,&b);
449 D[i] = a*FACTOR/this->dim_factor;
450 Dav += D[i];
451 D2av += sqr(D[i]);
452 fprintf(out,"%10d %10g\n",i,D[i]);
454 fclose(out);
455 do_view(fn,"-graphtype bar");
457 /* Compute variance, stddev and error */
458 Dav /= this->nnx;
459 D2av /= this->nnx;
460 VarD = D2av - sqr(Dav);
461 printf("<D> = %.3f Std. Dev. = %.3f Error = %.3f\n",
462 Dav,sqrt(VarD),sqrt(VarD/this->nnx));
464 sfree(D);
467 void do_corr(char *trx_file, char *ndx_file, char *msd_file, char *mol_file,
468 int nrgrp, t_topology *top,bool bMW,
469 int type,real dim_factor,int axis,
470 real dt,real beginfit,real endfit)
472 t_corr *msd;
473 int *gnx;
474 atom_id **index;
475 char **grpname;
476 int i,i0,i1,j,N;
477 real *DD,*SigmaD,a,a2,b;
479 snew(gnx,nrgrp);
480 snew(index,nrgrp);
481 snew(grpname,nrgrp);
483 if (mol_file && !ndx_file) {
484 gnx[0] = top->blocks[ebMOLS].nr;
485 snew(index[0],gnx[0]);
486 grpname[0] = "Molecules";
487 for(i=0; (i<gnx[0]); i++)
488 index[0][i] = i;
490 else
491 get_index(&top->atoms,ndx_file,nrgrp,gnx,index,grpname);
493 msd = init_corr(nrgrp,type,axis,dim_factor,bMW,(mol_file!=NULL),dt,top);
495 corr_loop(msd,trx_file,gnx,index,
496 (mol_file!=NULL) ? calc1_mol : (bMW ? calc1_mw : calc1_norm),
497 (mol_file!=NULL) ? prep_data_mol : prep_data_norm,dt);
499 /* Correct for the number of points */
500 for(j=0; (j<msd->ngrp); j++)
501 for(i=0; (i<msd->nframes); i++)
502 msd->data[j][i] /= msd->ndata[j][i];
504 if (mol_file)
505 printmol(msd,mol_file);
507 DD = NULL;
508 SigmaD = NULL;
509 for(i0=0; i0<msd->nframes && msd->time[i0]<beginfit; i0++)
511 if (endfit == -1) {
512 i1 = msd->nframes;
513 endfit = msd->time[i1-1];
514 } else
515 for(i1=i0; i1<msd->nframes && msd->time[i1]<=endfit; i1++)
517 N = i1-i0;
518 if (N <= 2) {
519 fprintf(stdout,"Not enough points for fitting (%d).\n"
520 "Can not determine the diffusion constant.\n",N);
521 } else {
522 snew(DD,msd->ngrp);
523 snew(SigmaD,msd->ngrp);
524 for(j=0; j<msd->ngrp; j++) {
525 if (N >= 4) {
526 lsq_y_ax_b(N/2,&(msd->time[i0]),&(msd->data[j][i0]),&a,&b);
527 lsq_y_ax_b(N/2,&(msd->time[i0+N/2]),&(msd->data[j][i0+N/2]),&a2,&b);
528 SigmaD[j] = fabs(a-a2);
529 } else
530 SigmaD[j] = 0;
531 lsq_y_ax_b(N,&(msd->time[i0]),&(msd->data[j][i0]),&(DD[j]),&b);
532 DD[j] *= FACTOR/msd->dim_factor;
533 SigmaD[j] *= FACTOR/msd->dim_factor;
534 fprintf(stdout,"D[%10s] %.3f (+/- %.3f) 1e-5 cm^2/s\n",
535 grpname[j],DD[j],SigmaD[j]);
538 /* Print mean square displacement */
539 corr_print(msd,msd_file,
540 "Mean Square Displacement",
541 "MSD (nm\\S2\\N)",
542 beginfit,endfit,DD,SigmaD,grpname);
545 int main(int argc,char *argv[])
547 static char *desc[] = {
548 "g_msd computes the mean square displacement (MSD) of atoms from",
549 "their initial positions. This provides an easy way to compute",
550 "the diffusion constant using the Einstein relation.",
551 "The diffusion constant is calculated by least squares fitting a",
552 "straight line through the MSD from [TT]-beginfit[tt] to",
553 "[TT]-endfit[tt]. An error estimate given, which is the difference",
554 "of the diffusion coefficients obtained from fits over the two halfs",
555 "of the fit interval.[PAR]",
556 "Option [TT]-mol[tt] plots the MSD for molecules, this implies",
557 "[TT]-mw[tt], i.e. for each inidividual molecule an diffusion constant",
558 "is computed. When using an index file, it should contain molecule",
559 "numbers instead of atom numbers.",
560 "Using this option one also gets an accurate error estimate",
561 "based on the statistics between individual molecules. Since one usually",
562 "is interested in self-diffusion at infinite dilution this is probably",
563 "the most useful number.[PAR]",
565 static char *normtype[]= { NULL,"no","x","y","z",NULL };
566 static char *axtitle[] = { NULL,"no","x","y","z",NULL };
567 static int ngroup = 1;
568 static real dt = 0;
569 static real beginfit = 0;
570 static real endfit = -1;
571 static bool bMW = TRUE;
572 t_pargs pa[] = {
573 { "-type", FALSE, etENUM, {normtype},
574 "Compute diffusion coefficient in one direction" },
575 { "-lateral", FALSE, etENUM, {axtitle},
576 "Calculate the lateral diffusion in a plane perpendicular to" },
577 { "-ngroup", FALSE, etINT, {&ngroup},
578 "Number of groups to calculate MSD for" },
579 { "-mw", FALSE, etBOOL, {&bMW},
580 "Mass weighted MSD" },
581 { "-trestart",FALSE, etTIME, {&dt},
582 "Time between restarting points in trajectory (%t)" },
583 { "-beginfit",FALSE, etTIME, {&beginfit},
584 "Start time for fitting the MSD (%t)" },
585 { "-endfit",FALSE, etTIME, {&endfit},
586 "End time for fitting the MSD (%t), -1 is till end" }
589 t_filenm fnm[] = {
590 { efTRX, NULL, NULL, ffREAD },
591 { efTPS, NULL, NULL, ffREAD },
592 { efNDX, NULL, NULL, ffOPTRD },
593 { efXVG, NULL, "msd", ffWRITE },
594 { efXVG, "-mol", "diff_mol",ffOPTWR }
596 #define NFILE asize(fnm)
598 t_topology top;
599 matrix box;
600 char title[256];
601 char *trx_file, *tps_file, *ndx_file, *msd_file, *mol_file;
602 rvec *xdum;
603 bool bTop;
604 int axis,type;
605 real dim_factor;
607 CopyRight(stderr,argv[0]);
609 parse_common_args(&argc,argv,PCA_CAN_VIEW | PCA_CAN_TIME | PCA_TIME_UNIT | PCA_BE_NICE,
610 NFILE,fnm,asize(pa),pa,asize(desc),desc,0,NULL);
611 trx_file = ftp2fn_null(efTRX,NFILE,fnm);
612 tps_file = ftp2fn_null(efTPS,NFILE,fnm);
613 ndx_file = ftp2fn_null(efNDX,NFILE,fnm);
614 msd_file = ftp2fn_null(efXVG,NFILE,fnm);
615 mol_file = opt2fn_null("-mol",NFILE,fnm);
617 if (ngroup < 1)
618 fatal_error(0,"Must have at least 1 group (now %d)",ngroup);
620 if (mol_file) {
621 bMW = TRUE;
622 fprintf(stderr,"Calculating diffusion coefficients for molecules.\n");
625 if (normtype[0][0]!='n') {
626 type = normtype[0][0] - 'x' + X; /* See defines above */
627 dim_factor = 2.0;
629 else {
630 type = NORMAL;
631 dim_factor = 6.0;
633 if ((type==NORMAL) && (axtitle[0][0]!='n')) {
634 type=LATERAL;
635 dim_factor = 4.0;
636 axis = (axtitle[0][0] - 'x'); /* See defines above */
638 else
639 axis = 0;
640 bTop=read_tps_conf(tps_file,title,&top,&xdum,NULL,box,bMW);
641 if (mol_file && !bTop)
642 fatal_error(0,"Could not read a topology from %s. Try a tpr file instead.",
643 tps_file);
645 do_corr(trx_file,ndx_file,msd_file,mol_file,ngroup,
646 &top,bMW,type,dim_factor,axis,dt,beginfit,endfit);
648 view_all(NFILE, fnm);
650 thanx(stderr);
652 return 0;