3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
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
33 * Green Red Orange Magenta Azure Cyan Skyblue
75 * f0[k] = c + [SUM a_i*EXP(-b_i*(k^2)) ]
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 },
85 { "C", 6, 12, { 2.26069, 1.56165, 1.05075, 0.839259 },
86 { 22.6907, 0.656665, 9.75618, 55.5949 },
88 { "N", 7, 14, { 12.2126, 3.13220, 2.01250, 1.16630 },
89 { 0.005700, 9.89330, 28.9975, 0.582600 },
91 { "O", 8, 16, { 3.04850, 2.28680, 1.54630, 0.867000 },
92 { 13.2771, 5.70110, 0.323900, 32.9089 },
94 { "Na", 11, 23, { 3.25650, 3.93620, 1.39980, 1.00320 }, /* Na 1+ */
95 { 2.66710, 6.11530, 0.200100, 14.0390 },
99 #define NCMT asize(CM_t)
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
])
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
)
136 if (bMass
&& atom
==NULL
)
137 gmx_fatal(FARGS
,"No masses available while mass weighting was requested");
139 for(c
=0; c
<is
; c
++) {
142 for(i
=coi
[c
]; i
<coi
[c
+1]; i
++) {
144 m
= atom
[index
[i
]].m
;
146 xc
[d
] += m
*x
[index
[i
]][d
];
149 rvec_inc(xc
,x
[index
[i
]]);
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
)
164 int cur
,mol
,res
,i
,a
,i1
;
166 /* Split up the group in molecules or residues */
172 atom
= top
->atoms
.atom
;
175 gmx_fatal(FARGS
,"Unknown rdf option '%s'",type
);
181 for(i
=0; i
<isize
; i
++) {
184 /* Check if the molecule number has changed */
185 i1
= mols
->index
[mol
+1];
188 i1
= mols
->index
[mol
+1];
194 } else if (type
== 'r') {
195 /* Check if the residue index has changed */
196 res
= atom
[a
].resind
;
205 printf("Group '%s' of %d atoms consists of %d %s\n",
207 (type
=='m' ? "molecules" : "residues"));
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
)
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
;
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)
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
;
241 atom_id ix
,jx
,***pairs
;
242 t_topology
*top
=NULL
;
243 int ePBC
=-1,ePBCrdf
=-1;
249 int *is
=NULL
,**coi
=NULL
,cur
,mol
,i1
,res
,a
;
253 bClose
= (close
[0] != 'n');
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
);
265 fprintf(stderr
,"\nSelect a reference group and %d group%s\n",
268 get_index(&(top
->atoms
),fnNDX
,ng
+1,isize
,index
,grpname
);
269 atom
= top
->atoms
.atom
;
271 rd_index(fnNDX
,ng
+1,isize
,index
,grpname
);
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 */
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
]);
292 snew(coi
[0],is
[0]+1);
294 coi
[0][1] = isize
[0];
297 } else if (bClose
|| rdft
[0][0] != 'a') {
304 natoms
=read_first_x(oenv
,&status
,fnTRX
,&t
,&x
,box
);
306 gmx_fatal(FARGS
,"Could not read coordinates from statusfile\n");
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 */
322 ePBC
= guess_ePBC(box
);
324 copy_mat(box
,box_pbc
);
329 case epbcXY
: ePBCrdf
= epbcXY
; break;
330 case epbcNONE
: ePBCrdf
= epbcNONE
; break;
332 gmx_fatal(FARGS
,"xy-rdf's are not supported for pbc type'%s'",
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
]);
342 rmax2
= 0.99*0.99*max_cutoff2(bXY
? epbcXY
: epbcXYZ
,box_pbc
);
344 rmax2
= sqr(3*max(box
[XX
][XX
],max(box
[YY
][YY
],box
[ZZ
][ZZ
])));
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
;
361 for(g
=0; g
<ng
; g
++) {
362 if (isize
[g
+1] > max_i
)
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')) {
375 for(j
=0; j
< natoms
; j
++)
379 for( j
= excl
->index
[ix
]; j
< excl
->index
[ix
+1]; j
++)
380 bExcl
[excl
->a
[j
]]=TRUE
;
382 snew(pairs
[g
][i
], isize
[g
+1]);
383 bNonSelfExcl
= FALSE
;
384 for(j
=0; j
<isize
[g
+1]; j
++) {
389 /* Check if we have exclusions other than self exclusions */
394 srenew(pairs
[g
][i
],npairs
[g
][i
]);
396 /* Save a LOT of memory and some cpu cycles */
411 /* Must init pbc every step because of pressure coupling */
412 copy_mat(box
,box_pbc
);
415 rm_pbc(&top
->idef
,ePBC
,natoms
,box
,x
,x
);
418 clear_rvec(box_pbc
[ZZ
]);
420 set_pbc(&pbc
,ePBCrdf
,box_pbc
);
423 /* Set z-size to 1 so we get the surface iso the volume */
426 invvol
= 1/det(box_pbc
);
427 invvol_sum
+= invvol
;
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
]);
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
++) {
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];
455 for(j
=0; j
<isize_g
; j
++) {
457 /* Loop over the selected atoms in the reference molecule */
458 for(ii
=coi
[0][i
]; ii
<coi
[0][i
+1]; ii
++) {
460 pbc_dx(&pbc
,x
[index
[0][ii
]],x_i1
[j
],dx
);
462 rvec_sub(x
[index
[0][ii
]],x_i1
[j
],dx
);
464 r2ii
= dx
[XX
]*dx
[XX
] + dx
[YY
]*dx
[YY
];
470 if (r2
>cut2
&& r2
<=rmax2
)
471 count
[g
][(int)(sqrt(r2
)*invhbinw
)]++;
474 /* Real rdf between points in space */
475 if (bCM
|| rdft
[0][0] != 'a') {
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
++) {
485 pbc_dx(&pbc
,xi
,x
[jx
],dx
);
487 rvec_sub(xi
,x
[jx
],dx
);
490 r2
= dx
[XX
]*dx
[XX
] + dx
[YY
]*dx
[YY
];
493 if (r2
>cut2
&& r2
<=rmax2
)
494 count
[g
][(int)(sqrt(r2
)*invhbinw
)]++;
497 /* Cheaper loop, no exclusions */
498 if (rdft
[0][0] == 'a')
499 isize_g
= isize
[g
+1];
502 for(j
=0; j
<isize_g
; j
++) {
504 pbc_dx(&pbc
,xi
,x_i1
[j
],dx
);
506 rvec_sub(xi
,x_i1
[j
],dx
);
508 r2
= dx
[XX
]*dx
[XX
] + dx
[YY
]*dx
[YY
];
511 if (r2
>cut2
&& r2
<=rmax2
)
512 count
[g
][(int)(sqrt(r2
)*invhbinw
)]++;
519 } while (read_next_x(oenv
,status
,&t
,natoms
,x
,box
));
520 fprintf(stderr
,"\n");
527 invvol
= invvol_sum
/nframes
;
529 /* Calculate volume of sphere segments or length of circle segments */
530 snew(inv_segvol
,(nbin
+1)/2);
532 for(i
=0; (i
<(nbin
+1)/2); i
++) {
533 r
= (i
+ 0.5)*binwidth
;
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
;
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]);
550 normfac
= 1.0/(nframes
*invvol
*isize0
*is
[g
+1]);
552 /* Do the normalization */
553 nrdf
= max((nbin
+1)/2,1+2*fade
/binwidth
);
555 for(i
=0; i
<(nbin
+1)/2; i
++) {
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));
565 rdf
[g
][i
] = j
*inv_segvol
[i
]*normfac
;
567 rdf
[g
][i
] = j
/(binwidth
*isize0
*nframes
);
570 for( ; (i
<nrdf
); i
++)
574 if (rdft
[0][0] == 'a') {
575 sprintf(gtitle
,"Radial distribution");
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
);
583 sprintf(refgt
," %s","COM");
585 sprintf(refgt
," closest atom in %s.",close
);
587 sprintf(refgt
,"%s","");
590 if (output_env_get_print_xvgr_codes(oenv
))
591 fprintf(fp
,"@ subtitle \"%s%s - %s\"\n",grpname
[0],refgt
,grpname
[1]);
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
);
601 fprintf(fp
," %10g",rdf
[g
][i
]);
606 do_view(oenv
,fnRDF
,NULL
);
608 /* h(Q) function: fourier transform of rdf */
611 real
*hq
,*integrand
,Q
;
613 /* Get a better number density later! */
614 rho
= isize
[1]*invvol
;
616 snew(integrand
,nrdf
);
617 for(i
=0; (i
<nhq
); i
++) {
620 for(j
=1; (j
<nrdf
); j
++) {
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
]);
631 do_view(oenv
,fnHQ
,NULL
);
637 normfac
= 1.0/(isize0
*nframes
);
638 fp
=xvgropen(fnCNRDF
,"Cumulative Number RDF","r","number",oenv
);
640 if (output_env_get_print_xvgr_codes(oenv
))
641 fprintf(fp
,"@ subtitle \"%s-%s\"\n",grpname
[0],grpname
[1]);
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
);
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
));
654 sum
[g
] += count
[g
][i
*2] + count
[g
][i
*2+1];
661 do_view(oenv
,fnCNRDF
,NULL
);
669 t_complex
*** rc_tensor_allocation(int x
, int y
, int z
)
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
++) {
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
;
693 int return_atom_type (const char *name
)
700 { "CH1", 1 }, { "CH2", 2 }, { "CH3", 3 },
701 { "CS1", 1 }, { "CS2", 2 }, { "CS3", 3 },
702 { "CP1", 1 }, { "CP2", 2 }, { "CP3", 3 }
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)
713 gmx_fatal(FARGS
,"\nError: atom (%s) not in list (%d types checked)!\n",
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.
727 double tmp
= 0.0, k2
;
734 tmp
= (CMSF (return_atom_type ("C"),0,lambda
, sin_theta
) +
735 nh
*CMSF (return_atom_type ("H"),0,lambda
, sin_theta
));
739 k2
= (sqr (sin_theta
) / sqr (10.0 * lambda
));
741 for (i
= 0; (i
< 4); i
++)
742 tmp
+= CM_t
[type
].a
[i
] * exp (-CM_t
[type
].b
[i
] * k2
);
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.
754 double sin_theta
,q
,hc
=1239.842;
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
);
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
);
771 sf_table
[i
][j
] = CMSF (i
,0,sf
->lambda
, sin_theta
);
773 sf_table
[i
][j
] = CMSF (i
,i
-NCMT
+1,sf
->lambda
, sin_theta
);
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
;
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
])
797 if (j
== i_tmp
) { /* i.e. no indexed atom type is == to atm[i].t */
799 srenew (index_atp
, i_tmp
* sizeof (int));
800 index_atp
[i_tmp
- 1] = atm
[i
].t
;
804 srenew (index_atp
, i_tmp
* sizeof (int));
805 index_atp
[i_tmp
- 1] = 0;
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 */
816 for (i
= 0; i
< isize
; i
++)
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
)
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
)
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
);
856 * compute real and imaginary part of the structure factor for every
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
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)
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)
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
)
917 double *tmp
, polarization_factor
, A
;
919 fp
= xvgropen (file
, "Scattering Intensity", "q (1/nm)",
920 "Intensity (a.u.)",oenv
);
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
*
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
];
962 structure_factor
*sf
;
972 /* Read the topology informations */
973 read_tps_conf (fnTPS
, title
, &top
, &ePBC
, &xtop
, NULL
, box
, TRUE
);
976 /* groups stuff... */
981 fprintf (stderr
, "\nSelect %d group%s\n", ng
,
984 get_index (&top
.atoms
, fnNDX
, ng
, isize
, index
, grpname
);
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
;
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);
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 */
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
);
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",
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
};
1079 { "-bin", FALSE
, etREAL
, {&binwidth
},
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
},
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
},
1114 {"-energy", FALSE
, etREAL
, {&energy
},
1115 "Energy of the incoming X-ray (keV) "}
1117 #define NPA asize(pa)
1118 const char *fnTPS
,*fnNDX
;
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
);
1143 fnTPS
= ftp2fn_null(efTPS
,NFILE
,fnm
);
1145 fnNDX
= ftp2fn_null(efNDX
,NFILE
,fnm
);
1147 if (closet
[0][0] != 'n') {
1149 gmx_fatal(FARGS
,"Can not have both -com and -surf");
1152 fprintf(stderr
,"Turning of normalization because of option -surf\n");
1157 if (!bSQ
&& (!fnTPS
&& !fnNDX
))
1158 gmx_fatal(FARGS
,"Neither index file nor topology file specified\n"
1162 do_scattering_intensity(fnTPS
,fnNDX
,opt2fn("-sq",NFILE
,fnm
),
1163 ftp2fn(efTRX
,NFILE
,fnm
),
1164 start_q
, end_q
, energy
, ngroups
,oenv
);
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
,