4 * This source code is part of
8 * GROningen MAchine for Chemical Simulations
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
30 * GROtesk MACabre and Sinister
52 #include "shift_util.h"
57 static void do_rdf(char *fnNDX
,char *fnTPS
,char *fnTRX
,
58 char *fnRDF
,char *fnCNRDF
, char *fnHQ
,
59 bool bCM
,real cutoff
,real binwidth
,real fade
)
63 char outf1
[STRLEN
],outf2
[STRLEN
];
65 int g
,ng
,natoms
,i
,j
,k
,nbin
,j0
,j1
,n
,nframes
;
68 int *isize
,isize_cm
=0,nrdf
=0,max_i
;
69 atom_id
**index
,*index_cm
=NULL
;
70 unsigned long int *sum
;
71 real t
,boxmin
,hbox
,hbox2
,cut2
,r
,r2
,invbinw
,normfac
;
72 real segvol
,spherevol
,prev_spherevol
,**rdf
;
73 rvec
*x
,xcom
,dx
,*x_i1
,xi
;
74 real
*inv_segvol
,vol
,vol_sum
,rho
;
75 bool *bExcl
,bTop
,bNonSelfExcl
;
78 atom_id ix
,jx
,***pairs
;
84 bTop
=read_tps_conf(fnTPS
,title
,&top
,&x
,NULL
,box
,TRUE
);
87 /* get exclusions from topology */
88 excl
=&(top
.atoms
.excl
);
90 fprintf(stderr
,"\nHow many groups do you want to calculate the RDF of?\n");
97 fprintf(stderr
,"\nSelect a reference group and %d group%s\n",
100 get_index(&top
.atoms
,fnNDX
,ng
+1,isize
,index
,grpname
);
102 rd_index(fnNDX
,ng
+1,isize
,index
,grpname
);
104 natoms
=read_first_x(&status
,fnTRX
,&t
,&x
,box
);
106 fatal_error(0,"Could not read coordinates from statusfile\n");
108 /* check with topology */
109 if ( natoms
> top
.atoms
.nr
)
110 fatal_error(0,"Trajectory (%d atoms) does not match topology (%d atoms)",
111 natoms
,top
.atoms
.nr
);
112 /* check with index groups */
113 for (i
=0; i
<=ng
; i
++)
114 for (j
=0; j
<isize
[i
]; j
++)
115 if ( index
[i
][j
] >= natoms
)
116 fatal_error(0,"Atom index (%d) in index group %s (%d atoms) larger "
117 "than number of atoms in trajectory (%d atoms)",
118 index
[i
][j
],grpname
[i
],isize
[i
],natoms
);
121 /* move index[0] to index_cm and make 'dummy' index[0] */
123 snew(index_cm
,isize_cm
);
124 for(i
=0; i
<isize
[0]; i
++)
125 index_cm
[i
]=index
[0][i
];
128 srenew(index
[0],isize
[0]);
129 /* make space for center of mass */
133 /* initialize some handy things */
134 boxmin
= min( norm(box
[XX
]), min( norm(box
[YY
]), norm(box
[ZZ
]) ) );
136 nbin
= (int)(hbox
/ binwidth
) + 1;
137 invbinw
= 1.0 / binwidth
;
147 for(g
=0; g
<ng
; g
++) {
148 if (isize
[g
+1] > max_i
)
151 /* this is THE array */
152 snew(count
[g
],nbin
+1);
154 /* make pairlist array for groups and exclusions */
155 snew(pairs
[g
],isize
[0]);
156 snew(npairs
[g
],isize
[0]);
157 for(i
=0; i
<isize
[0]; i
++) {
159 for(j
=0; j
< natoms
; j
++)
163 for( j
= excl
->index
[ix
]; j
< excl
->index
[ix
+1]; j
++)
164 bExcl
[excl
->a
[j
]]=TRUE
;
166 snew(pairs
[g
][i
], isize
[g
+1]);
167 bNonSelfExcl
= FALSE
;
168 for(j
=0; j
<isize
[g
+1]; j
++) {
173 /* Check if we have exclusions other than self exclusions */
174 bNonSelfExcl
= bNonSelfExcl
|| (ix
!= jx
);
178 srenew(pairs
[g
][i
],npairs
[g
][i
]);
180 /* Save a LOT of memory and some cpu cycles */
192 /* Must init pbc every step because of pressure coupling */
194 rm_pbc(&top
.idef
,natoms
,box
,x
,x
);
200 /* calculate centre of mass */
202 for(i
=0; (i
< isize_cm
); i
++) {
204 rvec_inc(xcom
,x
[ix
]);
206 /* store it in the first 'group' */
207 for(j
=0; (j
<DIM
); j
++)
208 x
[index
[0][0]][j
] = xcom
[j
] / isize_cm
;
211 for(g
=0; g
<ng
; g
++) {
212 /* Copy the indexed coordinates to a continuous array */
213 for(i
=0; i
<isize
[g
+1]; i
++)
214 copy_rvec(x
[index
[g
+1][i
]],x_i1
[i
]);
216 for(i
=0; i
<isize
[0]; i
++) {
217 copy_rvec(x
[index
[0][i
]],xi
);
218 if (npairs
[g
][i
] >= 0)
219 /* Expensive loop, because of indexing */
220 for(j
=0; j
<npairs
[g
][i
]; j
++) {
224 if (r2
>cut2
&& r2
<=hbox2
)
225 count
[g
][(int)(sqrt(r2
)*invbinw
)]++;
228 /* Cheaper loop, no exclusions */
229 for(j
=0; j
<isize
[g
+1]; j
++) {
230 pbc_dx(xi
,x_i1
[j
],dx
);
232 if (r2
>cut2
&& r2
<=hbox2
)
233 count
[g
][(int)(sqrt(r2
)*invbinw
)]++;
238 } while (read_next_x(status
,&t
,natoms
,x
,box
));
239 fprintf(stderr
,"\n");
246 vol
= vol_sum
/nframes
;
248 /* Calculate volume of sphere segments */
249 snew(inv_segvol
,nbin
);
251 for(i
=0; (i
<nbin
); i
++) {
253 spherevol
=(4.0/3.0)*M_PI
*r
*r
*r
;
254 segvol
=spherevol
-prev_spherevol
;
255 inv_segvol
[i
]=1.0/segvol
;
256 prev_spherevol
=spherevol
;
260 for(g
=0; g
<ng
; g
++) {
261 /* We have to normalize by dividing by the number of frames */
262 rho
= isize
[g
+1]/vol
;
263 normfac
= 1.0/((rho
*nframes
)*isize
[0]);
265 /* Do the normalization */
266 nrdf
= max(nbin
-1,1+(2*fade
/binwidth
));
268 for(i
=0; (i
<nbin
-1); i
++) {
269 r
= (i
+0.5)*binwidth
;
270 if ((fade
> 0) && (r
>= fade
))
271 rdf
[g
][i
] = 1+(count
[g
][i
]*inv_segvol
[i
]*normfac
-1)*exp(-16*sqr(r
/fade
-1));
273 rdf
[g
][i
] = count
[g
][i
]*inv_segvol
[i
]*normfac
;
275 for( ; (i
<nrdf
); i
++)
279 fp
=xvgropen(fnRDF
,"Radial Distribution","r","");
281 fprintf(fp
,"@ subtitle \"%s-%s\"\n",grpname
[0],grpname
[1]);
283 fprintf(fp
,"@ subtitle \"reference %s\"\n",grpname
[0]);
284 xvgr_legend(fp
,ng
,grpname
+1);
286 for(i
=0; (i
<nrdf
); i
++) {
287 fprintf(fp
,"%10g", (i
+0.5)*binwidth
);
289 fprintf(fp
," %10g",rdf
[g
][i
]);
296 /* h(Q) function: fourier transform of rdf */
299 real
*hq
,*integrand
,Q
;
301 /* Get a better number density later! */
304 snew(integrand
,nrdf
);
305 for(i
=0; (i
<nhq
); i
++) {
308 for(j
=1; (j
<nrdf
); j
++) {
309 r
= (j
+0.5)*binwidth
;
310 integrand
[j
] = (Q
== 0) ? 1.0 : sin(Q
*r
)/(Q
*r
);
311 integrand
[j
] *= 4.0*M_PI
*rho
*r
*r
*(rdf
[0][j
]-1.0);
313 hq
[i
] = print_and_integrate(debug
,nrdf
,binwidth
,integrand
,NULL
,0);
315 fp
=xvgropen(fnHQ
,"h(Q)","Q(/nm)","h(Q)");
316 for(i
=0; (i
<nhq
); i
++)
317 fprintf(fp
,"%10g %10g\n",i
*0.5,hq
[i
]);
325 normfac
= 1.0/(isize
[0]*nframes
);
326 fp
=xvgropen(fnCNRDF
,"Cumulative Number RDF","r","number");
328 fprintf(fp
,"@ subtitle \"%s-%s\"\n",grpname
[0],grpname
[1]);
330 fprintf(fp
,"@ subtitle \"reference %s\"\n",grpname
[0]);
331 xvgr_legend(fp
,ng
,grpname
+1);
334 for(i
=0; (i
<nbin
-1); i
++) {
335 fprintf(fp
,"%10g",i
*binwidth
);
336 for(g
=0; g
<ng
; g
++) {
337 fprintf(fp
," %10g",(real
)((double)sum
[g
]*normfac
));
338 sum
[g
] += count
[g
][i
];
345 do_view(fnCNRDF
,NULL
);
359 int comp_xdata(const void *a
,const void *b
)
369 else if (xb
->ndata
== 0)
372 tmp
= xa
->kkk
- xb
->kkk
;
381 static t_xdata
*init_xdata(int nx
,int ny
)
383 int ix
,iy
,i
,j
,maxkx
,maxky
;
389 for(i
=0; (i
<nx
); i
++) {
390 for(j
=0; (j
<ny
); j
++) {
391 ix
= abs((i
< maxkx
) ? i
: (i
- nx
));
392 iy
= abs((j
< maxky
) ? j
: (j
- ny
));
393 data
[ix
*ny
+iy
].kkk
= sqrt(ix
*ix
+iy
*iy
);
399 static void extract_sq(t_fftgrid
*fftgrid
,int nbin
,real k_max
,real lambda
,
400 real count
[],rvec box
,int npixel
,real
*map
[],
403 int nx
,ny
,nz
,nx2
,ny2
,nz2
,la2
,la12
;
405 int i
,j
,k
,maxkx
,maxky
,maxkz
,n
,ind
,ix
,iy
;
406 real k1
,kxy2
,kz2
,k2
,z
,kxy
,kxy_max
,cos_theta2
,ttt
,factor
;
411 kxy_max = k_max/sqrt(3);*/
412 unpack_fftgrid(fftgrid
,&nx
,&ny
,&nz
,&nx2
,&ny2
,&nz2
,
413 &la2
,&la12
,FALSE
,(t_fft_r
**)&ptr
);
414 /* This bit copied from pme.c */
419 for(i
=0; (i
<nx
); i
++) {
420 #define IDX(i,n) (i<=n/2) ? (i) : (i-n)
422 for(j
=0; (j
<ny
); j
++) {
426 for(k
=0; (k
<maxkz
); k
++,p0
++) {
427 if ((i
==0) && (j
==0) && (k
==0))
430 z
= sqrt(sqr(p0
->re
)+sqr(p0
->im
));
431 kxy2
= sqr(kk
[XX
]) + sqr(kk
[YY
]);
432 k2
= kxy2
+sqr(kk
[ZZ
]);
437 * R. W. James (1962),
438 * The Optical Principles of the Diffraction of X-Rays,
439 * Oxbow press, Woodbridge Connecticut
440 * the intensity is proportional to (eq. 9.10):
441 * I = C (1+cos^2 [2 theta])/2 FFT
443 * cos[2 theta] = cos^2[theta] - sin^2[theta] = 2 cos^2[theta] - 1
444 * we can compute the prefactor straight from cos[theta]
446 cos_theta2
= kxy2
/k2
;
447 /*ttt = z*0.5*(1+sqr(2*cos_theta2-1));*/
448 ttt
= z
*0.5*(1+cos_theta2
);
450 ix
= ((i
< maxkx
) ? i
: (i
- nx
));
451 iy
= ((j
< maxky
) ? j
: (j
- ny
));
452 map
[npixel
/2+ix
][npixel
/2+iy
] += ttt
;
453 data
[abs(ix
)*ny
+abs(iy
)].ndata
+= 1;
454 data
[abs(ix
)*ny
+abs(iy
)].intensity
+= ttt
;
457 fprintf(stderr
,"k (%g) > k_max (%g)\n",k1
,k_max
);
468 static void do_sq(char *fnNDX
,char *fnTPS
,char *fnTRX
,char *fnSQ
,
469 char *fnXPM
,real grid
,real lambda
,real distance
,
470 int npixel
,int nlevel
)
473 t_element elem
[] = { { "H", 1 }, { "C", 6 }, { "N", 7 }, { "O", 8 }, { "F", 9 }, { "S", 16 } };
474 #define NELEM asize(elem)
476 char title
[STRLEN
],*aname
;
477 int natoms
,i
,j
,k
,nbin
,j0
,j1
,n
,nframes
,pme_order
;
482 real I0
,C
,t
,k_max
,factor
,yfactor
,segvol
;
483 rvec
*x
,*xndx
,box_size
,kk
,lll
;
484 real fj0
,*fj
,max_spacing
,r
,lambda_1
;
487 int nx
,ny
,nz
,nelectron
;
488 atom_id ix
,jx
,**pairs
;
495 /* bTop=read_tps_conf(fnTPS,title,&top,&x,NULL,box,TRUE); */
497 fprintf(stderr
,"\nSelect group for structure factor computation:\n");
498 get_index(&top
.atoms
,fnNDX
,1,&isize
,&index
,&grpname
);
499 natoms
=read_first_x(&status
,fnTRX
,&t
,&x
,box
);
500 if (isize
< top
.atoms
.nr
)
504 fprintf(stderr
,"\n");
509 fatal_error(0,"Could not read coordinates from statusfile\n");
510 /* check with topology */
511 if ( natoms
> top
.atoms
.nr
)
512 fatal_error(0,"Trajectory (%d atoms) does not match topology (%d atoms)",
513 natoms
,top
.atoms
.nr
);
515 /* Atomic scattering factors */
519 for(i
=0; (i
<isize
); i
++) {
520 aname
= *(top
.atoms
.atomname
[index
[i
]]);
522 if (top
.atoms
.atom
[i
].ptype
== eptAtom
) {
523 for(j
=0; (j
<NELEM
); j
++)
524 if (aname
[0] == elem
[j
].name
[0]) {
529 fprintf(stderr
,"Warning: don't know number of electrons for atom %s\n",aname
);
531 /* Correct for partial charge */
532 fj
[i
] = fj0
- top
.atoms
.atom
[index
[i
]].q
;
539 /* Dump scattering factors */
540 for(i
=0; (i
<isize
); i
++)
541 fprintf(debug
,"Atom %3s-%5d q = %10.4f f = %10.4f\n",
542 *(top
.atoms
.atomname
[index
[i
]]),index
[i
],
543 top
.atoms
.atom
[index
[i
]].q
,fj
[i
]);
546 /* Constant for scattering */
547 C
= sqr(1.0/(ELECTRONMASS_keV
*KILO
*ELECTRONVOLT
*1e7
*distance
));
548 fprintf(stderr
,"C is %g\n",C
);
550 /* This bit is dimensionless */
552 max_spacing
= calc_grid(box
,grid
,&nx
,&ny
,&nz
,1);
553 pme_order
= max(4,1+(0.2/grid
));
555 data
= init_xdata(nx
,ny
);
557 fprintf(stderr
,"Largest grid spacing: %g nm, pme_order %d, %dx%d pixel on image\n",
558 max_spacing
,pme_order
,npixel
,npixel
);
559 init_pme(stdout
,NULL
,nx
,ny
,nz
,pme_order
,isize
,FALSE
,eewg3D
);
561 /* Determine largest k vector length. */
562 k_max
= 1+sqrt(sqr(1+nx
/2)+sqr(1+ny
/2)+sqr(1+nz
/2));
564 /* this is the S(q) array */
568 for(i
=0; (i
<npixel
); i
++)
573 /* Put the atoms with scattering factor on a grid. Misuses
574 * an old routine from the PPPM code.
576 for(j
=0; (j
<DIM
); j
++)
577 box_size
[j
] = box
[j
][j
];
579 /* Scale coordinates to the wavelength */
580 for(i
=0; (i
<isize
); i
++)
581 copy_rvec(x
[index
[i
]],xndx
[i
]);
583 /* put local atoms on grid. */
584 fftgrid
= spread_on_grid(stdout
,isize
,pme_order
,xndx
,fj
,box
,FALSE
);
586 /* FFT the density */
587 gmxfft3D(fftgrid
,FFTW_FORWARD
,NULL
);
589 /* Extract the Sq function and sum it into the average array */
590 extract_sq(fftgrid
,nbin
,k_max
,lambda
,count
,box_size
,npixel
,map
,data
);
593 } while (read_next_x(status
,&t
,natoms
,x
,box
));
594 fprintf(stderr
,"\n");
600 /* Normalize it ?? */
601 factor
= k_max
/(nbin
);
602 yfactor
= (1.0/nframes
)/*(1.0/fftgrid->nxyz)*/;
603 fp
=xvgropen(fnSQ
,"Structure Factor","q (1/nm)","S(q)");
604 fprintf(fp
,"@ subtitle \"Lambda = %g nm. Grid spacing = %g nm\"\n",
607 for(i
=0; i
<nbin
; i
++) {
608 r
= (i
+0.5)*factor
*2*M_PI
;
609 segvol
= 4*M_PI
*sqr(r
)*factor
;
610 fprintf(fp
,"%10g %10g\n",r
,count
[i
]*yfactor
/segvol
);
617 t_rgb rhi
= { 0,0,0 }, rlo
= { 1,1,1 };
618 real
*tx
,*ty
,hi
,inv_nframes
;
621 inv_nframes
= 1.0/nframes
;
624 for(i
=0; (i
<npixel
); i
++) {
628 for(j
=0; (j
<npixel
); j
++) {
629 map
[i
][j
] *= inv_nframes
;
630 hi
= max(hi
,map
[i
][j
]);
634 fp
= ffopen(fnXPM
,"w");
635 write_xpm(fp
,"Diffraction Image","Intensity","kx","ky",
636 nbin
,nbin
,tx
,ty
,map
,0,hi
,rlo
,rhi
,&nlevel
);
641 /* qsort(data,nx*ny,sizeof(data[0]),comp_xdata);
642 fp = ffopen("test.xvg","w");
643 for(i=0; (i<nx*ny); i++) {
644 if (data[i].ndata != 0) {
645 fprintf(fp,"%10.3f %10.3f\n",data[i].kkk,data[i].intensity/data[i].ndata);
653 int main(int argc
,char *argv
[])
655 static char *desc
[] = {
656 "The structure of liquids can be studied by either neutron or X-ray",
657 "scattering. The most common way to describe liquid structure is by a",
658 "radial distribution function. However, this is not easy to obtain from",
659 "a scattering experiment.[PAR]",
660 "g_rdf calculates radial distribution functions in different ways.",
661 "The normal method is around a (set of) particle(s), the other method",
662 "is around the center of mass of a set of particles.[PAR]",
663 "If a run input file is supplied ([TT]-s[tt]), exclusions defined",
664 "in that file are taken into account when calculating the rdf.",
665 "The option [TT]-cut[tt] is meant as an alternative way to avoid",
666 "intramolecular peaks in the rdf plot.",
667 "It is however better to supply a run input file with a higher number of",
668 "exclusions. For eg. benzene a topology with nrexcl set to 5",
669 "would eliminate all intramolecular contributions to the rdf.",
670 "Note that all atoms in the selected groups are used, also the ones",
671 "that don't have Lennard-Jones interactions.[PAR]",
672 "Option [TT]-cn[tt] produces the cumulative number rdf.[PAR]"
673 "To bridge the gap between theory and experiment structure factors can",
674 "be computed (option [TT]-sq[tt]). The algorithm uses FFT, the grid"
675 "spacing of which is determined by option [TT]-grid[tt]."
677 static bool bCM
=FALSE
;
678 static real cutoff
=0,binwidth
=0.001,grid
=0.05,fade
=0.0,lambda
=0.1,distance
=10;
679 static int npixel
=256,nlevel
=20;
681 { "-bin", FALSE
, etREAL
, {&binwidth
},
683 { "-com", FALSE
, etBOOL
, {&bCM
},
684 "RDF with respect to the center of mass of first group" },
685 { "-cut", FALSE
, etREAL
, {&cutoff
},
686 "Shortest distance (nm) to be considered"},
687 { "-fade", FALSE
, etREAL
, {&fade
},
688 "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." },
689 { "-grid", FALSE
, etREAL
, {&grid
},
690 "Grid spacing (in nm) for FFTs when computing structure factors" },
691 { "-npixel", FALSE
, etINT
, {&npixel
},
692 "[HIDDEN]# pixels per edge of the square detector plate" },
693 { "-nlevel", FALSE
, etINT
, {&nlevel
},
694 "Number of different colors in the diffraction image" },
695 { "-distance", FALSE
, etREAL
, {&distance
},
696 "[HIDDEN]Distance (in cm) from the sample to the detector" },
697 { "-wave", FALSE
, etREAL
, {&lambda
},
698 "Wavelength for X-rays/Neutrons for scattering. 0.1 nm corresponds to roughly 12 keV" }
700 #define NPA asize(pa)
705 { efTRX
, "-f", NULL
, ffREAD
},
706 { efTPS
, NULL
, NULL
, ffOPTRD
},
707 { efNDX
, NULL
, NULL
, ffOPTRD
},
708 { efXVG
, "-o", "rdf", ffOPTWR
},
709 { efXVG
, "-sq", "sq", ffOPTWR
},
710 { efXVG
, "-cn", "rdf_cn", ffOPTWR
},
711 { efXVG
, "-hq", "hq", ffOPTWR
},
712 { efXPM
, "-image", "sq", ffOPTWR
}
714 #define NFILE asize(fnm)
716 CopyRight(stderr
,argv
[0]);
717 parse_common_args(&argc
,argv
,PCA_CAN_VIEW
| PCA_CAN_TIME
| PCA_BE_NICE
,
718 NFILE
,fnm
,NPA
,pa
,asize(desc
),desc
,0,NULL
);
720 fnTPS
= ftp2fn_null(efTPS
,NFILE
,fnm
);
721 fnNDX
= ftp2fn_null(efNDX
,NFILE
,fnm
);
722 bSQ
= opt2bSet("-sq",NFILE
,fnm
) || opt2parg_bSet("-grid",NPA
,pa
);
723 bRDF
= opt2bSet("-o",NFILE
,fnm
) || !bSQ
;
727 fatal_error(0,"Need a tps file for calculating structure factors\n");
730 if (!fnTPS
&& !fnNDX
)
731 fatal_error(0,"Neither index file nor topology file specified\n"
736 do_sq(fnNDX
,fnTPS
,ftp2fn(efTRX
,NFILE
,fnm
),opt2fn("-sq",NFILE
,fnm
),
737 ftp2fn(efXPM
,NFILE
,fnm
),grid
,lambda
,distance
,npixel
,nlevel
);
740 do_rdf(fnNDX
,fnTPS
,ftp2fn(efTRX
,NFILE
,fnm
),
741 opt2fn("-o",NFILE
,fnm
),opt2fn_null("-cn",NFILE
,fnm
),
742 opt2fn_null("-hq",NFILE
,fnm
),
743 bCM
,cutoff
,binwidth
,fade
);