Merge branch 'master' of git@git.gromacs.org:gromacs
[gromacs/rigid-bodies.git] / src / tools / gmx_rdf.c
blob37cb094f0fcc4199de6507e5c340f1da031ee934
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.
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.
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.
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.
30 * For more info, check our website at http://www.gromacs.org
32 * And Hey:
33 * Green Red Orange Magenta Azure Cyan Skyblue
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif
40 #include <math.h>
41 #include <ctype.h>
42 #include "string2.h"
43 #include "sysstuff.h"
44 #include "typedefs.h"
45 #include "macros.h"
46 #include "vec.h"
47 #include "pbc.h"
48 #include "xvgr.h"
49 #include "copyrite.h"
50 #include "futil.h"
51 #include "statutil.h"
52 #include "tpxio.h"
53 #include "physics.h"
54 #include "index.h"
55 #include "smalloc.h"
56 #include "fftgrid.h"
57 #include "calcgrid.h"
58 #include "nrnb.h"
59 #include "coulomb.h"
60 #include "gstat.h"
61 #include "matio.h"
62 #include "gmx_ana.h"
63 #include "names.h"
66 typedef struct
68 const char *label;
69 int elem,mass;
70 real a[4], b[4], c;
71 } t_CM_table;
75 * f0[k] = c + [SUM a_i*EXP(-b_i*(k^2)) ]
76 * i=1,4
79 const t_CM_table CM_t[] =
82 { "H", 1, 1, { 0.489918, 0.262003, 0.196767, 0.049879 },
83 { 20.6593, 7.74039, 49.5519, 2.20159 },
84 0.001305 },
85 { "C", 6, 12, { 2.26069, 1.56165, 1.05075, 0.839259 },
86 { 22.6907, 0.656665, 9.75618, 55.5949 },
87 0.286977 },
88 { "N", 7, 14, { 12.2126, 3.13220, 2.01250, 1.16630 },
89 { 0.005700, 9.89330, 28.9975, 0.582600 },
90 -11.529 },
91 { "O", 8, 16, { 3.04850, 2.28680, 1.54630, 0.867000 },
92 { 13.2771, 5.70110, 0.323900, 32.9089 },
93 0.250800 },
94 { "Na", 11, 23, { 3.25650, 3.93620, 1.39980, 1.00320 }, /* Na 1+ */
95 { 2.66710, 6.11530, 0.200100, 14.0390 },
96 0.404000 }
99 #define NCMT asize(CM_t)
101 typedef struct
103 int n_angles;
104 int n_groups;
105 double lambda;
106 double energy;
107 double momentum;
108 double ref_k;
109 double **F;
110 int nSteps;
111 int total_n_atoms;
112 } structure_factor;
114 typedef struct
116 rvec x;
117 int t;
118 } reduced_atom;
120 static void check_box_c(matrix box)
122 if (fabs(box[ZZ][XX]) > GMX_REAL_EPS*box[ZZ][ZZ] ||
123 fabs(box[ZZ][YY]) > GMX_REAL_EPS*box[ZZ][ZZ])
124 gmx_fatal(FARGS,
125 "The last box vector is not parallel to the z-axis: %f %f %f",
126 box[ZZ][XX],box[ZZ][YY],box[ZZ][ZZ]);
129 static void calc_comg(int is,int *coi,int *index,bool bMass,t_atom *atom,
130 rvec *x,rvec *x_comg)
132 int c,i,d;
133 rvec xc;
134 real mtot,m;
136 if (bMass && atom==NULL)
137 gmx_fatal(FARGS,"No masses available while mass weighting was requested");
139 for(c=0; c<is; c++) {
140 clear_rvec(xc);
141 mtot = 0;
142 for(i=coi[c]; i<coi[c+1]; i++) {
143 if (bMass) {
144 m = atom[index[i]].m;
145 for(d=0; d<DIM; d++)
146 xc[d] += m*x[index[i]][d];
147 mtot += m;
148 } else {
149 rvec_inc(xc,x[index[i]]);
150 mtot += 1.0;
153 svmul(1/mtot,xc,x_comg[c]);
157 static void split_group(int isize,int *index,char *grpname,
158 t_topology *top,char type,
159 int *is_out,int **coi_out)
161 t_block *mols=NULL;
162 t_atom *atom=NULL;
163 int is,*coi;
164 int cur,mol,res,i,a,i1;
166 /* Split up the group in molecules or residues */
167 switch (type) {
168 case 'm':
169 mols = &top->mols;
170 break;
171 case 'r':
172 atom = top->atoms.atom;
173 break;
174 default:
175 gmx_fatal(FARGS,"Unknown rdf option '%s'",type);
177 snew(coi,isize+1);
178 is = 0;
179 cur = -1;
180 mol = 0;
181 for(i=0; i<isize; i++) {
182 a = index[i];
183 if (type == 'm') {
184 /* Check if the molecule number has changed */
185 i1 = mols->index[mol+1];
186 while(a >= i1) {
187 mol++;
188 i1 = mols->index[mol+1];
190 if (mol != cur) {
191 coi[is++] = i;
192 cur = mol;
194 } else if (type == 'r') {
195 /* Check if the residue index has changed */
196 res = atom[a].resind;
197 if (res != cur) {
198 coi[is++] = i;
199 cur = res;
203 coi[is] = i;
204 srenew(coi,is+1);
205 printf("Group '%s' of %d atoms consists of %d %s\n",
206 grpname,isize,is,
207 (type=='m' ? "molecules" : "residues"));
209 *is_out = is;
210 *coi_out = coi;
213 static void do_rdf(const char *fnNDX,const char *fnTPS,const char *fnTRX,
214 const char *fnRDF,const char *fnCNRDF, const char *fnHQ,
215 bool bCM,const char *close,
216 const char **rdft,bool bXY,bool bPBC,bool bNormalize,
217 real cutoff,real binwidth,real fade,int ng,
218 const output_env_t oenv)
220 FILE *fp;
221 int status;
222 char outf1[STRLEN],outf2[STRLEN];
223 char title[STRLEN],gtitle[STRLEN],refgt[30];
224 int g,natoms,i,ii,j,k,nbin,j0,j1,n,nframes;
225 int **count;
226 char **grpname;
227 int *isize,isize_cm=0,nrdf=0,max_i,isize0,isize_g;
228 atom_id **index,*index_cm=NULL;
229 #if (defined SIZEOF_LONG_LONG_INT) && (SIZEOF_LONG_LONG_INT >= 8)
230 long long int *sum;
231 #else
232 double *sum;
233 #endif
234 real t,rmax2,cut2,r,r2,r2ii,invhbinw,normfac;
235 real segvol,spherevol,prev_spherevol,**rdf;
236 rvec *x,dx,*x0=NULL,*x_i1,xi;
237 real *inv_segvol,invvol,invvol_sum,rho;
238 bool bClose,*bExcl,bTop,bNonSelfExcl;
239 matrix box,box_pbc;
240 int **npairs;
241 atom_id ix,jx,***pairs;
242 t_topology *top=NULL;
243 int ePBC=-1,ePBCrdf=-1;
244 t_block *mols=NULL;
245 t_blocka *excl;
246 t_atom *atom=NULL;
247 t_pbc pbc;
249 int *is=NULL,**coi=NULL,cur,mol,i1,res,a;
251 excl=NULL;
253 bClose = (close[0] != 'n');
255 if (fnTPS) {
256 snew(top,1);
257 bTop=read_tps_conf(fnTPS,title,top,&ePBC,&x,NULL,box,TRUE);
258 if (bTop && !(bCM || bClose))
259 /* get exclusions from topology */
260 excl = &(top->excls);
262 snew(grpname,ng+1);
263 snew(isize,ng+1);
264 snew(index,ng+1);
265 fprintf(stderr,"\nSelect a reference group and %d group%s\n",
266 ng,ng==1?"":"s");
267 if (fnTPS) {
268 get_index(&(top->atoms),fnNDX,ng+1,isize,index,grpname);
269 atom = top->atoms.atom;
270 } else {
271 rd_index(fnNDX,ng+1,isize,index,grpname);
274 if (bCM || bClose) {
275 snew(is,1);
276 snew(coi,1);
277 if (bClose) {
278 split_group(isize[0],index[0],grpname[0],top,close[0],&is[0],&coi[0]);
281 if (rdft[0][0] != 'a') {
282 /* Split up all the groups in molecules or residues */
283 srenew(is,ng+1);
284 srenew(coi,ng+1);
285 for(g=((bCM || bClose) ? 1 : 0); g<ng+1; g++) {
286 split_group(isize[g],index[g],grpname[g],top,rdft[0][0],&is[g],&coi[g]);
290 if (bCM) {
291 is[0] = 1;
292 snew(coi[0],is[0]+1);
293 coi[0][0] = 0;
294 coi[0][1] = isize[0];
295 isize0 = is[0];
296 snew(x0,isize0);
297 } else if (bClose || rdft[0][0] != 'a') {
298 isize0 = is[0];
299 snew(x0,isize0);
300 } else {
301 isize0 = isize[0];
304 natoms=read_first_x(oenv,&status,fnTRX,&t,&x,box);
305 if ( !natoms )
306 gmx_fatal(FARGS,"Could not read coordinates from statusfile\n");
307 if (fnTPS)
308 /* check with topology */
309 if ( natoms > top->atoms.nr )
310 gmx_fatal(FARGS,"Trajectory (%d atoms) does not match topology (%d atoms)",
311 natoms,top->atoms.nr);
312 /* check with index groups */
313 for (i=0; i<ng+1; i++)
314 for (j=0; j<isize[i]; j++)
315 if ( index[i][j] >= natoms )
316 gmx_fatal(FARGS,"Atom index (%d) in index group %s (%d atoms) larger "
317 "than number of atoms in trajectory (%d atoms)",
318 index[i][j],grpname[i],isize[i],natoms);
320 /* initialize some handy things */
321 if (ePBC == -1) {
322 ePBC = guess_ePBC(box);
324 copy_mat(box,box_pbc);
325 if (bXY) {
326 check_box_c(box);
327 switch (ePBC) {
328 case epbcXYZ:
329 case epbcXY: ePBCrdf = epbcXY; break;
330 case epbcNONE: ePBCrdf = epbcNONE; break;
331 default:
332 gmx_fatal(FARGS,"xy-rdf's are not supported for pbc type'%s'",
333 EPBC(ePBC));
334 break;
336 /* Make sure the z-height does not influence the cut-off */
337 box_pbc[ZZ][ZZ] = 2*max(box[XX][XX],box[YY][YY]);
338 } else {
339 ePBCrdf = ePBC;
341 if (bPBC)
342 rmax2 = 0.99*0.99*max_cutoff2(bXY ? epbcXY : epbcXYZ,box_pbc);
343 else
344 rmax2 = sqr(3*max(box[XX][XX],max(box[YY][YY],box[ZZ][ZZ])));
345 if (debug)
346 fprintf(debug,"rmax2 = %g\n",rmax2);
348 /* We use the double amount of bins, so we can correctly
349 * write the rdf and rdf_cn output at i*binwidth values.
351 nbin = (int)(sqrt(rmax2) * 2 / binwidth);
352 invhbinw = 2.0 / binwidth;
353 cut2 = sqr(cutoff);
355 snew(count,ng);
356 snew(pairs,ng);
357 snew(npairs,ng);
359 snew(bExcl,natoms);
360 max_i = 0;
361 for(g=0; g<ng; g++) {
362 if (isize[g+1] > max_i)
363 max_i = isize[g+1];
365 /* this is THE array */
366 snew(count[g],nbin+1);
368 /* make pairlist array for groups and exclusions */
369 snew(pairs[g],isize[0]);
370 snew(npairs[g],isize[0]);
371 for(i=0; i<isize[0]; i++) {
372 /* We can only have exclusions with atomic rdfs */
373 if (!(bCM || bClose || rdft[0][0] != 'a')) {
374 ix = index[0][i];
375 for(j=0; j < natoms; j++)
376 bExcl[j] = FALSE;
377 /* exclusions? */
378 if (excl)
379 for( j = excl->index[ix]; j < excl->index[ix+1]; j++)
380 bExcl[excl->a[j]]=TRUE;
381 k = 0;
382 snew(pairs[g][i], isize[g+1]);
383 bNonSelfExcl = FALSE;
384 for(j=0; j<isize[g+1]; j++) {
385 jx = index[g+1][j];
386 if (!bExcl[jx])
387 pairs[g][i][k++]=jx;
388 else if (ix != jx)
389 /* Check if we have exclusions other than self exclusions */
390 bNonSelfExcl = TRUE;
392 if (bNonSelfExcl) {
393 npairs[g][i]=k;
394 srenew(pairs[g][i],npairs[g][i]);
395 } else {
396 /* Save a LOT of memory and some cpu cycles */
397 npairs[g][i]=-1;
398 sfree(pairs[g][i]);
400 } else {
401 npairs[g][i]=-1;
405 sfree(bExcl);
407 snew(x_i1,max_i);
408 nframes = 0;
409 invvol_sum = 0;
410 do {
411 /* Must init pbc every step because of pressure coupling */
412 copy_mat(box,box_pbc);
413 if (bPBC) {
414 if (top != NULL)
415 rm_pbc(&top->idef,ePBC,natoms,box,x,x);
416 if (bXY) {
417 check_box_c(box);
418 clear_rvec(box_pbc[ZZ]);
420 set_pbc(&pbc,ePBCrdf,box_pbc);
422 if (bXY)
423 /* Set z-size to 1 so we get the surface iso the volume */
424 box_pbc[ZZ][ZZ] = 1;
426 invvol = 1/det(box_pbc);
427 invvol_sum += invvol;
429 if (bCM) {
430 /* Calculate center of mass of the whole group */
431 calc_comg(is[0],coi[0],index[0],TRUE ,atom,x,x0);
432 } else if (!bClose && rdft[0][0] != 'a') {
433 calc_comg(is[0],coi[0],index[0],rdft[0][6]=='m',atom,x,x0);
436 for(g=0; g<ng; g++) {
437 if (rdft[0][0] == 'a') {
438 /* Copy the indexed coordinates to a continuous array */
439 for(i=0; i<isize[g+1]; i++)
440 copy_rvec(x[index[g+1][i]],x_i1[i]);
441 } else {
442 /* Calculate the COMs/COGs and store in x_i1 */
443 calc_comg(is[g+1],coi[g+1],index[g+1],rdft[0][6]=='m',atom,x,x_i1);
446 for(i=0; i<isize0; i++) {
447 if (bClose) {
448 /* Special loop, since we need to determine the minimum distance
449 * over all selected atoms in the reference molecule/residue.
451 if (rdft[0][0] == 'a')
452 isize_g = isize[g+1];
453 else
454 isize_g = is[g+1];
455 for(j=0; j<isize_g; j++) {
456 r2 = 1e30;
457 /* Loop over the selected atoms in the reference molecule */
458 for(ii=coi[0][i]; ii<coi[0][i+1]; ii++) {
459 if (bPBC)
460 pbc_dx(&pbc,x[index[0][ii]],x_i1[j],dx);
461 else
462 rvec_sub(x[index[0][ii]],x_i1[j],dx);
463 if (bXY)
464 r2ii = dx[XX]*dx[XX] + dx[YY]*dx[YY];
465 else
466 r2ii = iprod(dx,dx);
467 if (r2ii < r2)
468 r2 = r2ii;
470 if (r2>cut2 && r2<=rmax2)
471 count[g][(int)(sqrt(r2)*invhbinw)]++;
473 } else {
474 /* Real rdf between points in space */
475 if (bCM || rdft[0][0] != 'a') {
476 copy_rvec(x0[i],xi);
477 } else {
478 copy_rvec(x[index[0][i]],xi);
480 if (rdft[0][0] == 'a' && npairs[g][i] >= 0) {
481 /* Expensive loop, because of indexing */
482 for(j=0; j<npairs[g][i]; j++) {
483 jx=pairs[g][i][j];
484 if (bPBC)
485 pbc_dx(&pbc,xi,x[jx],dx);
486 else
487 rvec_sub(xi,x[jx],dx);
489 if (bXY)
490 r2 = dx[XX]*dx[XX] + dx[YY]*dx[YY];
491 else
492 r2=iprod(dx,dx);
493 if (r2>cut2 && r2<=rmax2)
494 count[g][(int)(sqrt(r2)*invhbinw)]++;
496 } else {
497 /* Cheaper loop, no exclusions */
498 if (rdft[0][0] == 'a')
499 isize_g = isize[g+1];
500 else
501 isize_g = is[g+1];
502 for(j=0; j<isize_g; j++) {
503 if (bPBC)
504 pbc_dx(&pbc,xi,x_i1[j],dx);
505 else
506 rvec_sub(xi,x_i1[j],dx);
507 if (bXY)
508 r2 = dx[XX]*dx[XX] + dx[YY]*dx[YY];
509 else
510 r2=iprod(dx,dx);
511 if (r2>cut2 && r2<=rmax2)
512 count[g][(int)(sqrt(r2)*invhbinw)]++;
518 nframes++;
519 } while (read_next_x(oenv,status,&t,natoms,x,box));
520 fprintf(stderr,"\n");
522 close_trj(status);
524 sfree(x);
526 /* Average volume */
527 invvol = invvol_sum/nframes;
529 /* Calculate volume of sphere segments or length of circle segments */
530 snew(inv_segvol,(nbin+1)/2);
531 prev_spherevol=0;
532 for(i=0; (i<(nbin+1)/2); i++) {
533 r = (i + 0.5)*binwidth;
534 if (bXY) {
535 spherevol=M_PI*r*r;
536 } else {
537 spherevol=(4.0/3.0)*M_PI*r*r*r;
539 segvol=spherevol-prev_spherevol;
540 inv_segvol[i]=1.0/segvol;
541 prev_spherevol=spherevol;
544 snew(rdf,ng);
545 for(g=0; g<ng; g++) {
546 /* We have to normalize by dividing by the number of frames */
547 if (rdft[0][0] == 'a')
548 normfac = 1.0/(nframes*invvol*isize0*isize[g+1]);
549 else
550 normfac = 1.0/(nframes*invvol*isize0*is[g+1]);
552 /* Do the normalization */
553 nrdf = max((nbin+1)/2,1+2*fade/binwidth);
554 snew(rdf[g],nrdf);
555 for(i=0; i<(nbin+1)/2; i++) {
556 r = i*binwidth;
557 if (i == 0)
558 j = count[g][0];
559 else
560 j = count[g][i*2-1] + count[g][i*2];
561 if ((fade > 0) && (r >= fade))
562 rdf[g][i] = 1 + (j*inv_segvol[i]*normfac-1)*exp(-16*sqr(r/fade-1));
563 else {
564 if (bNormalize)
565 rdf[g][i] = j*inv_segvol[i]*normfac;
566 else
567 rdf[g][i] = j/(binwidth*isize0*nframes);
570 for( ; (i<nrdf); i++)
571 rdf[g][i] = 1.0;
574 if (rdft[0][0] == 'a') {
575 sprintf(gtitle,"Radial distribution");
576 } else {
577 sprintf(gtitle,"Radial distribution of %s %s",
578 rdft[0][0]=='m' ? "molecule" : "residue",
579 rdft[0][6]=='m' ? "COM" : "COG");
581 fp=xvgropen(fnRDF,gtitle,"r","",oenv);
582 if (bCM) {
583 sprintf(refgt," %s","COM");
584 } else if (bClose) {
585 sprintf(refgt," closest atom in %s.",close);
586 } else {
587 sprintf(refgt,"%s","");
589 if (ng==1) {
590 if (output_env_get_print_xvgr_codes(oenv))
591 fprintf(fp,"@ subtitle \"%s%s - %s\"\n",grpname[0],refgt,grpname[1]);
593 else {
594 if (output_env_get_print_xvgr_codes(oenv))
595 fprintf(fp,"@ subtitle \"reference %s%s\"\n",grpname[0],refgt);
596 xvgr_legend(fp,ng,grpname+1,oenv);
598 for(i=0; (i<nrdf); i++) {
599 fprintf(fp,"%10g",i*binwidth);
600 for(g=0; g<ng; g++)
601 fprintf(fp," %10g",rdf[g][i]);
602 fprintf(fp,"\n");
604 ffclose(fp);
606 do_view(oenv,fnRDF,NULL);
608 /* h(Q) function: fourier transform of rdf */
609 if (fnHQ) {
610 int nhq = 401;
611 real *hq,*integrand,Q;
613 /* Get a better number density later! */
614 rho = isize[1]*invvol;
615 snew(hq,nhq);
616 snew(integrand,nrdf);
617 for(i=0; (i<nhq); i++) {
618 Q = i*0.5;
619 integrand[0] = 0;
620 for(j=1; (j<nrdf); j++) {
621 r = j*binwidth;
622 integrand[j] = (Q == 0) ? 1.0 : sin(Q*r)/(Q*r);
623 integrand[j] *= 4.0*M_PI*rho*r*r*(rdf[0][j]-1.0);
625 hq[i] = print_and_integrate(debug,nrdf,binwidth,integrand,NULL,0);
627 fp=xvgropen(fnHQ,"h(Q)","Q(/nm)","h(Q)",oenv);
628 for(i=0; (i<nhq); i++)
629 fprintf(fp,"%10g %10g\n",i*0.5,hq[i]);
630 ffclose(fp);
631 do_view(oenv,fnHQ,NULL);
632 sfree(hq);
633 sfree(integrand);
636 if (fnCNRDF) {
637 normfac = 1.0/(isize0*nframes);
638 fp=xvgropen(fnCNRDF,"Cumulative Number RDF","r","number",oenv);
639 if (ng==1) {
640 if (output_env_get_print_xvgr_codes(oenv))
641 fprintf(fp,"@ subtitle \"%s-%s\"\n",grpname[0],grpname[1]);
643 else {
644 if (output_env_get_print_xvgr_codes(oenv))
645 fprintf(fp,"@ subtitle \"reference %s\"\n",grpname[0]);
646 xvgr_legend(fp,ng,grpname+1,oenv);
648 snew(sum,ng);
649 for(i=0; (i<=nbin/2); i++) {
650 fprintf(fp,"%10g",i*binwidth);
651 for(g=0; g<ng; g++) {
652 fprintf(fp," %10g",(real)((double)sum[g]*normfac));
653 if (i*2+1 < nbin)
654 sum[g] += count[g][i*2] + count[g][i*2+1];
656 fprintf(fp,"\n");
658 ffclose(fp);
659 sfree(sum);
661 do_view(oenv,fnCNRDF,NULL);
664 for(g=0; g<ng; g++)
665 sfree(rdf[g]);
666 sfree(rdf);
669 t_complex *** rc_tensor_allocation(int x, int y, int z)
671 t_complex ***t;
672 int i,j;
674 snew(t,x);
675 t = (t_complex ***)calloc(x,sizeof(t_complex**));
676 if(!t) exit(fprintf(stderr,"\nallocation error"));
677 t[0] = (t_complex **)calloc(x*y,sizeof(t_complex*));
678 if(!t[0]) exit(fprintf(stderr,"\nallocation error"));
679 t[0][0] = (t_complex *)calloc(x*y*z,sizeof(t_complex));
680 if(!t[0][0]) exit(fprintf(stderr,"\nallocation error"));
682 for( j = 1 ; j < y ; j++)
683 t[0][j] = t[0][j-1] + z;
684 for( i = 1 ; i < x ; i++) {
685 t[i] = t[i-1] + y;
686 t[i][0] = t[i-1][0] + y*z;
687 for( j = 1 ; j < y ; j++)
688 t[i][j] = t[i][j-1] + z;
690 return t;
693 int return_atom_type (const char *name)
695 typedef struct {
696 const char *name;
697 int nh;
698 } t_united_h;
699 t_united_h uh[] = {
700 { "CH1", 1 }, { "CH2", 2 }, { "CH3", 3 },
701 { "CS1", 1 }, { "CS2", 2 }, { "CS3", 3 },
702 { "CP1", 1 }, { "CP2", 2 }, { "CP3", 3 }
704 int i;
706 for(i=0; (i<asize(uh)); i++)
707 if (strcmp(name,uh[i].name) == 0)
708 return NCMT-1+uh[i].nh;
710 for(i=0; (i<NCMT); i++)
711 if (strncmp (name, CM_t[i].label,strlen(CM_t[i].label)) == 0)
712 return i;
713 gmx_fatal(FARGS,"\nError: atom (%s) not in list (%d types checked)!\n",
714 name,i);
716 return 0;
719 double CMSF (int type,int nh,double lambda, double sin_theta)
721 * return Cromer-Mann fit for the atomic scattering factor:
722 * sin_theta is the sine of half the angle between incoming and scattered
723 * vectors. See g_sq.h for a short description of CM fit.
726 int i;
727 double tmp = 0.0, k2;
730 * united atoms case
731 * CH2 / CH3 groups
733 if (nh > 0) {
734 tmp = (CMSF (return_atom_type ("C"),0,lambda, sin_theta) +
735 nh*CMSF (return_atom_type ("H"),0,lambda, sin_theta));
737 /* all atom case */
738 else {
739 k2 = (sqr (sin_theta) / sqr (10.0 * lambda));
740 tmp = CM_t[type].c;
741 for (i = 0; (i < 4); i++)
742 tmp += CM_t[type].a[i] * exp (-CM_t[type].b[i] * k2);
744 return tmp;
747 real **compute_scattering_factor_table (structure_factor * sf,int *nsftable)
750 * this function build up a table of scattering factors for every atom
751 * type and for every scattering angle.
753 int i, j;
754 double sin_theta,q,hc=1239.842;
755 real ** sf_table;
757 /* \hbar \omega \lambda = hc = 1239.842 eV * nm */
758 sf->momentum = ((double) (2. * 1000.0 * M_PI * sf->energy) / hc);
759 sf->lambda = hc / (1000.0 * sf->energy);
760 fprintf (stderr, "\nwavelenght = %f nm\n", sf->lambda);
761 *nsftable = NCMT+3;
762 snew (sf_table,*nsftable);
763 for (i = 0; (i < *nsftable); i++) {
764 snew (sf_table[i], sf->n_angles);
765 for (j = 0; j < sf->n_angles; j++) {
766 q = ((double) j * sf->ref_k);
767 /* theta is half the angle between incoming
768 and scattered wavevectors. */
769 sin_theta = q / (2.0 * sf->momentum);
770 if (i < NCMT)
771 sf_table[i][j] = CMSF (i,0,sf->lambda, sin_theta);
772 else
773 sf_table[i][j] = CMSF (i,i-NCMT+1,sf->lambda, sin_theta);
776 return sf_table;
779 int * create_indexed_atom_type (reduced_atom * atm, int size)
782 * create an index of the atom types found in a group
783 * i.e.: for water index_atp[0]=type_number_of_O and
784 * index_atp[1]=type_number_of_H
786 * the last element is set to 0
788 int *index_atp, i, i_tmp, j;
790 snew (index_atp, 1);
791 i_tmp = 1;
792 index_atp[0] = atm[0].t;
793 for (i = 1; i < size; i++) {
794 for (j = 0; j < i_tmp; j++)
795 if (atm[i].t == index_atp[j])
796 break;
797 if (j == i_tmp) { /* i.e. no indexed atom type is == to atm[i].t */
798 i_tmp++;
799 srenew (index_atp, i_tmp * sizeof (int));
800 index_atp[i_tmp - 1] = atm[i].t;
803 i_tmp++;
804 srenew (index_atp, i_tmp * sizeof (int));
805 index_atp[i_tmp - 1] = 0;
806 return index_atp;
809 void rearrange_atoms (reduced_atom * positions, t_trxframe *fr, atom_id * index,
810 int isize, t_topology * top, bool flag)
811 /* given the group's index, return the (continuous) array of atoms */
813 int i;
815 if (flag)
816 for (i = 0; i < isize; i++)
817 positions[i].t =
818 return_atom_type (*(top->atoms.atomname[index[i]]));
819 for (i = 0; i < isize; i++)
820 copy_rvec (fr->x[index[i]], positions[i].x);
824 int atp_size (int *index_atp)
826 int i = 0;
828 while (index_atp[i])
829 i++;
830 return i;
833 void compute_structure_factor (structure_factor * sf, matrix box,
834 reduced_atom * red, int isize, real start_q,
835 real end_q, int group,real **sf_table)
837 t_complex ***tmpSF;
838 rvec k_factor;
839 real kdotx, asf, kx, ky, kz, krr;
840 int kr, maxkx, maxky, maxkz, i, j, k, p, *counter;
843 k_factor[XX] = 2 * M_PI / box[XX][XX];
844 k_factor[YY] = 2 * M_PI / box[YY][YY];
845 k_factor[ZZ] = 2 * M_PI / box[ZZ][ZZ];
847 maxkx = (int) (end_q / k_factor[XX] + 0.5);
848 maxky = (int) (end_q / k_factor[YY] + 0.5);
849 maxkz = (int) (end_q / k_factor[ZZ] + 0.5);
851 snew (counter, sf->n_angles);
853 tmpSF = rc_tensor_allocation(maxkx,maxky,maxkz);
855 * The big loop...
856 * compute real and imaginary part of the structure factor for every
857 * (kx,ky,kz))
859 fprintf(stderr,"\n");
860 for (i = 0; i < maxkx; i++) {
861 fprintf (stderr,"\rdone %3.1f%% ", (double)(100.0*(i+1))/maxkx);
862 kx = i * k_factor[XX];
863 for (j = 0; j < maxky; j++) {
864 ky = j * k_factor[YY];
865 for (k = 0; k < maxkz; k++)
866 if (i != 0 || j != 0 || k != 0) {
867 kz = k * k_factor[ZZ];
868 krr = sqrt (sqr (kx) + sqr (ky) + sqr (kz));
869 if (krr >= start_q && krr <= end_q) {
870 kr = (int) (krr/sf->ref_k + 0.5);
871 if (kr < sf->n_angles) {
872 counter[kr]++; /* will be used for the copmutation
873 of the average*/
874 for (p = 0; p < isize; p++) {
876 asf = sf_table[red[p].t][kr];
878 kdotx = kx * red[p].x[XX] +
879 ky * red[p].x[YY] + kz * red[p].x[ZZ];
881 tmpSF[i][j][k].re += cos (kdotx) * asf;
882 tmpSF[i][j][k].im += sin (kdotx) * asf;
888 } /* end loop on i */
890 * compute the square modulus of the structure factor, averaging on the surface
891 * kx*kx + ky*ky + kz*kz = krr*krr
892 * note that this is correct only for a (on the macroscopic scale)
893 * isotropic system.
895 for (i = 0; i < maxkx; i++) {
896 kx = i * k_factor[XX]; for (j = 0; j < maxky; j++) {
897 ky = j * k_factor[YY]; for (k = 0; k < maxkz; k++) {
898 kz = k * k_factor[ZZ]; krr = sqrt (sqr (kx) + sqr (ky)
899 + sqr (kz)); if (krr >= start_q && krr <= end_q) {
900 kr = (int) (krr / sf->ref_k + 0.5);
901 if (kr < sf->n_angles && counter[kr] != 0)
902 sf->F[group][kr] +=
903 (sqr (tmpSF[i][j][k].re) +
904 sqr (tmpSF[i][j][k].im))/ counter[kr];
908 } sfree (counter); free(tmpSF[0][0]); free(tmpSF[0]); free(tmpSF);
911 void save_data (structure_factor * sf, const char *file, int ngrps,
912 real start_q, real end_q, const output_env_t oenv)
915 FILE *fp;
916 int i, g = 0;
917 double *tmp, polarization_factor, A;
919 fp = xvgropen (file, "Scattering Intensity", "q (1/nm)",
920 "Intensity (a.u.)",oenv);
922 snew (tmp, ngrps);
924 for (g = 0; g < ngrps; g++)
925 for (i = 0; i < sf->n_angles; i++) {
927 * theta is half the angle between incoming and scattered vectors.
929 * polar. fact. = 0.5*(1+cos^2(2*theta)) = 1 - 0.5 * sin^2(2*theta)
931 * sin(theta) = q/(2k) := A -> sin^2(theta) = 4*A^2 (1-A^2) ->
932 * -> 0.5*(1+cos^2(2*theta)) = 1 - 2 A^2 (1-A^2)
934 A = (double) (i * sf->ref_k) / (2.0 * sf->momentum);
935 polarization_factor = 1 - 2.0 * sqr (A) * (1 - sqr (A));
936 sf->F[g][i] *= polarization_factor;
938 for (i = 0; i < sf->n_angles; i++) {
939 if (i * sf->ref_k >= start_q && i * sf->ref_k <= end_q) {
940 fprintf (fp, "%10.5f ", i * sf->ref_k);
941 for (g = 0; g < ngrps; g++)
942 fprintf (fp, " %10.5f ", (sf->F[g][i]) /( sf->total_n_atoms*
943 sf->nSteps));
944 fprintf (fp, "\n");
947 ffclose (fp);
950 int do_scattering_intensity (const char* fnTPS, const char* fnNDX,
951 const char* fnXVG, const char *fnTRX,
952 real start_q,real end_q,
953 real energy,int ng,const output_env_t oenv)
955 int i,*isize,status,flags = TRX_READ_X,**index_atp;
956 char **grpname,title[STRLEN];
957 atom_id **index;
958 t_topology top;
959 int ePBC;
960 t_trxframe fr;
961 reduced_atom **red;
962 structure_factor *sf;
963 rvec *xtop;
964 real **sf_table;
965 int nsftable;
966 matrix box;
967 double r_tmp;
969 snew (sf, 1);
970 sf->energy = energy;
972 /* Read the topology informations */
973 read_tps_conf (fnTPS, title, &top, &ePBC, &xtop, NULL, box, TRUE);
974 sfree (xtop);
976 /* groups stuff... */
977 snew (isize, ng);
978 snew (index, ng);
979 snew (grpname, ng);
981 fprintf (stderr, "\nSelect %d group%s\n", ng,
982 ng == 1 ? "" : "s");
983 if (fnTPS)
984 get_index (&top.atoms, fnNDX, ng, isize, index, grpname);
985 else
986 rd_index (fnNDX, ng, isize, index, grpname);
988 /* The first time we read data is a little special */
989 read_first_frame (oenv,&status, fnTRX, &fr, flags);
991 sf->total_n_atoms = fr.natoms;
993 snew (red, ng);
994 snew (index_atp, ng);
996 r_tmp = max (box[XX][XX], box[YY][YY]);
997 r_tmp = (double) max (box[ZZ][ZZ], r_tmp);
999 sf->ref_k = (2.0 * M_PI) / (r_tmp);
1000 /* ref_k will be the reference momentum unit */
1001 sf->n_angles = (int) (end_q / sf->ref_k + 0.5);
1003 snew (sf->F, ng);
1004 for (i = 0; i < ng; i++)
1005 snew (sf->F[i], sf->n_angles);
1006 for (i = 0; i < ng; i++) {
1007 snew (red[i], isize[i]);
1008 rearrange_atoms (red[i], &fr, index[i], isize[i], &top, TRUE);
1009 index_atp[i] = create_indexed_atom_type (red[i], isize[i]);
1011 sf_table = compute_scattering_factor_table (sf,&nsftable);
1012 /* This is the main loop over frames */
1014 do {
1015 sf->nSteps++;
1016 for (i = 0; i < ng; i++) {
1017 rearrange_atoms (red[i], &fr, index[i], isize[i], &top,FALSE);
1019 compute_structure_factor (sf, box, red[i], isize[i],
1020 start_q, end_q, i, sf_table);
1023 while (read_next_frame (oenv,status, &fr));
1025 save_data (sf, fnXVG, ng, start_q, end_q,oenv);
1027 return 0;
1030 int gmx_rdf(int argc,char *argv[])
1032 const char *desc[] = {
1033 "The structure of liquids can be studied by either neutron or X-ray",
1034 "scattering. The most common way to describe liquid structure is by a",
1035 "radial distribution function. However, this is not easy to obtain from",
1036 "a scattering experiment.[PAR]",
1037 "g_rdf calculates radial distribution functions in different ways.",
1038 "The normal method is around a (set of) particle(s), the other methods",
1039 "are around the center of mass of a set of particles ([TT]-com[tt])",
1040 "or to the closest particle in a set ([TT]-surf[tt]).",
1041 "With all methods rdf's can also be calculated around axes parallel",
1042 "to the z-axis with option [TT]-xy[tt].",
1043 "With option [TT]-surf[tt] normalization can not be used.[PAR]"
1044 "The option [TT]-rdf[tt] sets the type of rdf to be computed.",
1045 "Default is for atoms or particles, but one can also select center",
1046 "of mass or geometry of molecules or residues. In all cases only",
1047 "the atoms in the index groups are taken into account.",
1048 "For molecules and/or the center of mass option a run input file",
1049 "is required.",
1050 "Other weighting than COM or COG can currently only be achieved",
1051 "by providing a run input file with different masses.",
1052 "Options [TT]-com[tt] and [TT]-surf[tt] also work in conjunction",
1053 "with [TT]-rdf[tt].[PAR]",
1054 "If a run input file is supplied ([TT]-s[tt]) and [TT]-rdf[tt] is set",
1055 "to [TT]atom[tt], exclusions defined",
1056 "in that file are taken into account when calculating the rdf.",
1057 "The option [TT]-cut[tt] is meant as an alternative way to avoid",
1058 "intramolecular peaks in the rdf plot.",
1059 "It is however better to supply a run input file with a higher number of",
1060 "exclusions. For eg. benzene a topology with nrexcl set to 5",
1061 "would eliminate all intramolecular contributions to the rdf.",
1062 "Note that all atoms in the selected groups are used, also the ones",
1063 "that don't have Lennard-Jones interactions.[PAR]",
1064 "Option [TT]-cn[tt] produces the cumulative number rdf,",
1065 "i.e. the average number of particles within a distance r.[PAR]",
1066 "To bridge the gap between theory and experiment structure factors can",
1067 "be computed (option [TT]-sq[tt]). The algorithm uses FFT, the grid"
1068 "spacing of which is determined by option [TT]-grid[tt]."
1070 static bool bCM=FALSE,bXY=FALSE,bPBC=TRUE,bNormalize=TRUE;
1071 static real cutoff=0,binwidth=0.002,grid=0.05,fade=0.0,lambda=0.1,distance=10;
1072 static int npixel=256,nlevel=20,ngroups=1;
1073 static real start_q=0.0, end_q=60.0, energy=12.0;
1075 static const char *closet[]= { NULL, "no", "mol", "res", NULL };
1076 static const char *rdft[]={ NULL, "atom", "mol_com", "mol_cog", "res_com", "res_cog", NULL };
1078 t_pargs pa[] = {
1079 { "-bin", FALSE, etREAL, {&binwidth},
1080 "Binwidth (nm)" },
1081 { "-com", FALSE, etBOOL, {&bCM},
1082 "RDF with respect to the center of mass of first group" },
1083 { "-surf", FALSE, etENUM, {closet},
1084 "RDF with respect to the surface of the first group" },
1085 { "-rdf", FALSE, etENUM, {rdft},
1086 "RDF type" },
1087 { "-pbc", FALSE, etBOOL, {&bPBC},
1088 "Use periodic boundary conditions for computing distances. Without PBC the maximum range will be three times the largest box edge." },
1089 { "-norm", FALSE, etBOOL, {&bNormalize},
1090 "Normalize for volume and density" },
1091 { "-xy", FALSE, etBOOL, {&bXY},
1092 "Use only the x and y components of the distance" },
1093 { "-cut", FALSE, etREAL, {&cutoff},
1094 "Shortest distance (nm) to be considered"},
1095 { "-ng", FALSE, etINT, {&ngroups},
1096 "Number of secondary groups to compute RDFs around a central group" },
1097 { "-fade", FALSE, etREAL, {&fade},
1098 "From this distance onwards the RDF is tranformed by g'(r) = 1 + [g(r)-1] exp(-(r/fade-1)^2 to make it go to 1 smoothly. If fade is 0.0 nothing is done." },
1099 { "-grid", FALSE, etREAL, {&grid},
1100 "[HIDDEN]Grid spacing (in nm) for FFTs when computing structure factors" },
1101 { "-npixel", FALSE, etINT, {&npixel},
1102 "[HIDDEN]# pixels per edge of the square detector plate" },
1103 { "-nlevel", FALSE, etINT, {&nlevel},
1104 "Number of different colors in the diffraction image" },
1105 { "-distance", FALSE, etREAL, {&distance},
1106 "[HIDDEN]Distance (in cm) from the sample to the detector" },
1107 { "-wave", FALSE, etREAL, {&lambda},
1108 "[HIDDEN]Wavelength for X-rays/Neutrons for scattering. 0.1 nm corresponds to roughly 12 keV" },
1110 {"-startq", FALSE, etREAL, {&start_q},
1111 "Starting q (1/nm) "},
1112 {"-endq", FALSE, etREAL, {&end_q},
1113 "Ending q (1/nm)"},
1114 {"-energy", FALSE, etREAL, {&energy},
1115 "Energy of the incoming X-ray (keV) "}
1117 #define NPA asize(pa)
1118 const char *fnTPS,*fnNDX;
1119 bool bSQ,bRDF;
1120 output_env_t oenv;
1122 t_filenm fnm[] = {
1123 { efTRX, "-f", NULL, ffREAD },
1124 { efTPS, NULL, NULL, ffOPTRD },
1125 { efNDX, NULL, NULL, ffOPTRD },
1126 { efXVG, "-o", "rdf", ffOPTWR },
1127 { efXVG, "-sq", "sq", ffOPTWR },
1128 { efXVG, "-cn", "rdf_cn", ffOPTWR },
1129 { efXVG, "-hq", "hq", ffOPTWR },
1130 /* { efXPM, "-image", "sq", ffOPTWR }*/
1132 #define NFILE asize(fnm)
1134 CopyRight(stderr,argv[0]);
1135 parse_common_args(&argc,argv,PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
1136 NFILE,fnm,NPA,pa,asize(desc),desc,0,NULL,&oenv);
1138 bSQ = opt2bSet("-sq",NFILE,fnm);
1139 bRDF = opt2bSet("-o",NFILE,fnm) || !bSQ;
1140 if (bSQ || bCM || closet[0][0]!='n' || rdft[0][0]=='m' || rdft[0][6]=='m') {
1141 fnTPS = ftp2fn(efTPS,NFILE,fnm);
1142 } else {
1143 fnTPS = ftp2fn_null(efTPS,NFILE,fnm);
1145 fnNDX = ftp2fn_null(efNDX,NFILE,fnm);
1147 if (closet[0][0] != 'n') {
1148 if (bCM) {
1149 gmx_fatal(FARGS,"Can not have both -com and -surf");
1151 if (bNormalize) {
1152 fprintf(stderr,"Turning of normalization because of option -surf\n");
1153 bNormalize = FALSE;
1157 if (!bSQ && (!fnTPS && !fnNDX))
1158 gmx_fatal(FARGS,"Neither index file nor topology file specified\n"
1159 "Nothing to do!");
1161 if (bSQ)
1162 do_scattering_intensity(fnTPS,fnNDX,opt2fn("-sq",NFILE,fnm),
1163 ftp2fn(efTRX,NFILE,fnm),
1164 start_q, end_q, energy, ngroups,oenv);
1166 if (bRDF)
1167 do_rdf(fnNDX,fnTPS,ftp2fn(efTRX,NFILE,fnm),
1168 opt2fn("-o",NFILE,fnm),opt2fn_null("-cn",NFILE,fnm),
1169 opt2fn_null("-hq",NFILE,fnm),
1170 bCM,closet[0],rdft,bXY,bPBC,bNormalize,cutoff,binwidth,fade,ngroups,
1171 oenv);
1173 thanx(stderr);
1175 return 0;