Re-organize BlueGene toolchain files
[gromacs.git] / src / tools / gmx_order.c
blobaae2d11db09439099b279cc8df09d745d692e5b2
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team,
6 * check out http://www.gromacs.org for more information.
7 * Copyright (c) 2012, by the GROMACS development team, led by
8 * David van der Spoel, Berk Hess, Erik Lindahl, and including many
9 * others, as listed in the AUTHORS file in the top-level source
10 * directory and at http://www.gromacs.org.
12 * GROMACS is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public License
14 * as published by the Free Software Foundation; either version 2.1
15 * of the License, or (at your option) any later version.
17 * GROMACS is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with GROMACS; if not, see
24 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 * If you want to redistribute modifications to GROMACS, please
28 * consider that scientific software is very special. Version
29 * control is crucial - bugs must be traceable. We will be happy to
30 * consider code for inclusion in the official distribution, but
31 * derived work must not be called official GROMACS. Details are found
32 * in the README & COPYING files - if they are missing, get the
33 * official version at http://www.gromacs.org.
35 * To help us fund GROMACS development, we humbly ask that you cite
36 * the research papers on the package. Check out http://www.gromacs.org.
38 #ifdef HAVE_CONFIG_H
39 #include <config.h>
40 #endif
42 #include <math.h>
43 #include <ctype.h>
45 #include "sysstuff.h"
46 #include <string.h>
47 #include "typedefs.h"
48 #include "smalloc.h"
49 #include "macros.h"
50 #include "gstat.h"
51 #include "vec.h"
52 #include "xvgr.h"
53 #include "pbc.h"
54 #include "copyrite.h"
55 #include "futil.h"
56 #include "statutil.h"
57 #include "index.h"
58 #include "tpxio.h"
59 #include "confio.h"
60 #include "cmat.h"
61 #include "gmx_ana.h"
64 /****************************************************************************/
65 /* This program calculates the order parameter per atom for an interface or */
66 /* bilayer, averaged over time. */
67 /* S = 1/2 * (3 * cos(i)cos(j) - delta(ij)) */
68 /* It is assumed that the order parameter with respect to a box-axis */
69 /* is calculated. In that case i = j = axis, and delta(ij) = 1. */
70 /* */
71 /* Peter Tieleman, April 1995 */
72 /* P.J. van Maaren, November 2005 Added tetrahedral stuff */
73 /****************************************************************************/
75 static void find_nearest_neighbours(t_topology top, int ePBC,
76 int natoms, matrix box,
77 rvec x[],int maxidx,atom_id index[],
78 real time,
79 real *sgmean, real *skmean,
80 int nslice,int slice_dim,
81 real sgslice[],real skslice[],
82 gmx_rmpbc_t gpbc)
84 FILE *fpoutdist;
85 char fnsgdist[32];
86 int ix,jx,nsgbin, *sgbin;
87 int i1,i2,i,ibin,j,k,l,n,*nn[4];
88 rvec dx,dx1,dx2,rj,rk,urk,urj;
89 real cost,cost2,*sgmol,*skmol,rmean,rmean2,r2,box2,*r_nn[4];
90 t_pbc pbc;
91 t_mat *dmat;
92 t_dist *d;
93 int m1,mm,sl_index;
94 int **nnb,*sl_count;
95 real onethird=1.0/3.0;
96 /* dmat = init_mat(maxidx, FALSE); */
97 box2 = box[XX][XX] * box[XX][XX];
98 snew(sl_count,nslice);
99 for (i=0; (i<4); i++) {
100 snew(r_nn[i],natoms);
101 snew(nn[i],natoms);
103 for (j=0; (j<natoms); j++) {
104 r_nn[i][j] = box2;
108 snew(sgmol,maxidx);
109 snew(skmol,maxidx);
111 /* Must init pbc every step because of pressure coupling */
112 set_pbc(&pbc,ePBC,box);
114 gmx_rmpbc(gpbc,natoms,box,x);
116 nsgbin = 1 + 1/0.0005;
117 snew(sgbin,nsgbin);
119 *sgmean = 0.0;
120 *skmean = 0.0;
121 l=0;
122 for (i=0; (i<maxidx); i++) { /* loop over index file */
123 ix = index[i];
124 for (j=0; (j<maxidx); j++) {
125 if (i == j) continue;
127 jx = index[j];
129 pbc_dx(&pbc,x[ix],x[jx],dx);
130 r2=iprod(dx,dx);
132 /* set_mat_entry(dmat,i,j,r2); */
134 /* determine the nearest neighbours */
135 if (r2 < r_nn[0][i]) {
136 r_nn[3][i] = r_nn[2][i]; nn[3][i] = nn[2][i];
137 r_nn[2][i] = r_nn[1][i]; nn[2][i] = nn[1][i];
138 r_nn[1][i] = r_nn[0][i]; nn[1][i] = nn[0][i];
139 r_nn[0][i] = r2; nn[0][i] = j;
140 } else if (r2 < r_nn[1][i]) {
141 r_nn[3][i] = r_nn[2][i]; nn[3][i] = nn[2][i];
142 r_nn[2][i] = r_nn[1][i]; nn[2][i] = nn[1][i];
143 r_nn[1][i] = r2; nn[1][i] = j;
144 } else if (r2 < r_nn[2][i]) {
145 r_nn[3][i] = r_nn[2][i]; nn[3][i] = nn[2][i];
146 r_nn[2][i] = r2; nn[2][i] = j;
147 } else if (r2 < r_nn[3][i]) {
148 r_nn[3][i] = r2; nn[3][i] = j;
153 /* calculate mean distance between nearest neighbours */
154 rmean = 0;
155 for (j=0; (j<4); j++) {
156 r_nn[j][i] = sqrt(r_nn[j][i]);
157 rmean += r_nn[j][i];
159 rmean /= 4;
161 n = 0;
162 sgmol[i] = 0.0;
163 skmol[i] = 0.0;
165 /* Chau1998a eqn 3 */
166 /* angular part tetrahedrality order parameter per atom */
167 for (j=0; (j<3); j++) {
168 for (k=j+1; (k<4); k++) {
169 pbc_dx(&pbc,x[ix],x[index[nn[k][i]]],rk);
170 pbc_dx(&pbc,x[ix],x[index[nn[j][i]]],rj);
172 unitv(rk,urk);
173 unitv(rj,urj);
175 cost = iprod(urk,urj) + onethird;
176 cost2 = cost * cost;
178 /* sgmol[i] += 3*cost2/32; */
179 sgmol[i] += cost2;
181 /* determine distribution */
182 ibin = nsgbin * cost2;
183 if (ibin < nsgbin)
184 sgbin[ibin]++;
185 /* printf("%d %d %f %d %d\n", j, k, cost * cost, ibin, sgbin[ibin]);*/
186 l++;
187 n++;
191 /* normalize sgmol between 0.0 and 1.0 */
192 sgmol[i] = 3*sgmol[i]/32;
193 *sgmean += sgmol[i];
195 /* distance part tetrahedrality order parameter per atom */
196 rmean2 = 4 * 3 * rmean * rmean;
197 for (j=0; (j<4); j++) {
198 skmol[i] += (rmean - r_nn[j][i]) * (rmean - r_nn[j][i]) / rmean2;
199 /* printf("%d %f (%f %f %f %f) \n",
200 i, skmol[i], rmean, rmean2, r_nn[j][i], (rmean - r_nn[j][i]) );
204 *skmean += skmol[i];
206 /* Compute sliced stuff */
207 sl_index = gmx_nint((1+x[i][slice_dim]/box[slice_dim][slice_dim])*nslice) % nslice;
208 sgslice[sl_index] += sgmol[i];
209 skslice[sl_index] += skmol[i];
210 sl_count[sl_index]++;
211 } /* loop over entries in index file */
213 *sgmean /= maxidx;
214 *skmean /= maxidx;
216 for(i=0; (i<nslice); i++) {
217 if (sl_count[i] > 0) {
218 sgslice[i] /= sl_count[i];
219 skslice[i] /= sl_count[i];
222 sfree(sl_count);
223 sfree(sgbin);
224 sfree(sgmol);
225 sfree(skmol);
226 for (i=0; (i<4); i++) {
227 sfree(r_nn[i]);
228 sfree(nn[i]);
233 static void calc_tetra_order_parm(const char *fnNDX,const char *fnTPS,
234 const char *fnTRX, const char *sgfn,
235 const char *skfn,
236 int nslice,int slice_dim,
237 const char *sgslfn,const char *skslfn,
238 const output_env_t oenv)
240 FILE *fpsg=NULL,*fpsk=NULL;
241 t_topology top;
242 int ePBC;
243 char title[STRLEN],fn[STRLEN],subtitle[STRLEN];
244 t_trxstatus *status;
245 int natoms;
246 real t;
247 rvec *xtop,*x;
248 matrix box;
249 real sg,sk;
250 atom_id **index;
251 char **grpname;
252 int i,*isize,ng,nframes;
253 real *sg_slice,*sg_slice_tot,*sk_slice,*sk_slice_tot;
254 gmx_rmpbc_t gpbc=NULL;
257 read_tps_conf(fnTPS,title,&top,&ePBC,&xtop,NULL,box,FALSE);
259 snew(sg_slice,nslice);
260 snew(sk_slice,nslice);
261 snew(sg_slice_tot,nslice);
262 snew(sk_slice_tot,nslice);
263 ng = 1;
264 /* get index groups */
265 printf("Select the group that contains the atoms you want to use for the tetrahedrality order parameter calculation:\n");
266 snew(grpname,ng);
267 snew(index,ng);
268 snew(isize,ng);
269 get_index(&top.atoms,fnNDX,ng,isize,index,grpname);
271 /* Analyze trajectory */
272 natoms=read_first_x(oenv,&status,fnTRX,&t,&x,box);
273 if ( natoms > top.atoms.nr )
274 gmx_fatal(FARGS,"Topology (%d atoms) does not match trajectory (%d atoms)",
275 top.atoms.nr,natoms);
276 check_index(NULL,ng,index[0],NULL,natoms);
278 fpsg=xvgropen(sgfn,"S\\sg\\N Angle Order Parameter","Time (ps)","S\\sg\\N",
279 oenv);
280 fpsk=xvgropen(skfn,"S\\sk\\N Distance Order Parameter","Time (ps)","S\\sk\\N",
281 oenv);
283 /* loop over frames */
284 gpbc = gmx_rmpbc_init(&top.idef,ePBC,natoms,box);
285 nframes = 0;
286 do {
287 find_nearest_neighbours(top,ePBC,natoms,box,x,isize[0],index[0],t,
288 &sg,&sk,nslice,slice_dim,sg_slice,sk_slice,gpbc);
289 for(i=0; (i<nslice); i++) {
290 sg_slice_tot[i] += sg_slice[i];
291 sk_slice_tot[i] += sk_slice[i];
293 fprintf(fpsg,"%f %f\n", t, sg);
294 fprintf(fpsk,"%f %f\n", t, sk);
295 nframes++;
296 } while (read_next_x(oenv,status,&t,natoms,x,box));
297 close_trj(status);
298 gmx_rmpbc_done(gpbc);
300 sfree(grpname);
301 sfree(index);
302 sfree(isize);
304 ffclose(fpsg);
305 ffclose(fpsk);
307 fpsg = xvgropen(sgslfn,
308 "S\\sg\\N Angle Order Parameter / Slab","(nm)","S\\sg\\N",
309 oenv);
310 fpsk = xvgropen(skslfn,
311 "S\\sk\\N Distance Order Parameter / Slab","(nm)","S\\sk\\N",
312 oenv);
313 for(i=0; (i<nslice); i++) {
314 fprintf(fpsg,"%10g %10g\n",(i+0.5)*box[slice_dim][slice_dim]/nslice,
315 sg_slice_tot[i]/nframes);
316 fprintf(fpsk,"%10g %10g\n",(i+0.5)*box[slice_dim][slice_dim]/nslice,
317 sk_slice_tot[i]/nframes);
319 ffclose(fpsg);
320 ffclose(fpsk);
324 /* Print name of first atom in all groups in index file */
325 static void print_types(atom_id index[], atom_id a[], int ngrps,
326 char *groups[], t_topology *top)
328 int i;
330 fprintf(stderr,"Using following groups: \n");
331 for(i = 0; i < ngrps; i++)
332 fprintf(stderr,"Groupname: %s First atomname: %s First atomnr %u\n",
333 groups[i], *(top->atoms.atomname[a[index[i]]]), a[index[i]]);
334 fprintf(stderr,"\n");
337 static void check_length(real length, int a, int b)
339 if (length > 0.3)
340 fprintf(stderr,"WARNING: distance between atoms %d and "
341 "%d > 0.3 nm (%f). Index file might be corrupt.\n",
342 a, b, length);
345 void calc_order(const char *fn, atom_id *index, atom_id *a, rvec **order,
346 real ***slOrder, real *slWidth, int nslices, gmx_bool bSliced,
347 gmx_bool bUnsat, t_topology *top, int ePBC, int ngrps, int axis,
348 gmx_bool permolecule, gmx_bool radial, gmx_bool distcalc, const char *radfn,
349 real ***distvals,
350 const output_env_t oenv)
352 /* if permolecule = TRUE, order parameters will be calculed per molecule
353 * and stored in slOrder with #slices = # molecules */
354 rvec *x0, /* coordinates with pbc */
355 *x1, /* coordinates without pbc */
356 dist; /* vector between two atoms */
357 matrix box; /* box (3x3) */
358 t_trxstatus *status;
359 rvec cossum, /* sum of vector angles for three axes */
360 Sx, Sy, Sz, /* the three molecular axes */
361 tmp1, tmp2, /* temp. rvecs for calculating dot products */
362 frameorder; /* order parameters for one frame */
363 real *slFrameorder; /* order parameter for one frame, per slice */
364 real length, /* total distance between two atoms */
365 t, /* time from trajectory */
366 z_ave,z1,z2; /* average z, used to det. which slice atom is in */
367 int natoms, /* nr. atoms in trj */
368 nr_tails, /* nr tails, to check if index file is correct */
369 size=0, /* nr. of atoms in group. same as nr_tails */
370 i,j,m,k,l,teller = 0,
371 slice, /* current slice number */
372 nr_frames = 0,
373 *slCount; /* nr. of atoms in one slice */
374 real dbangle = 0, /* angle between double bond and axis */
375 sdbangle = 0;/* sum of these angles */
376 gmx_bool use_unitvector = FALSE; /* use a specified unit vector instead of axis to specify unit normal*/
377 rvec direction, com,dref,dvec;
378 int comsize, distsize;
379 atom_id *comidx=NULL, *distidx=NULL;
380 char *grpname=NULL;
381 t_pbc pbc;
382 real arcdist;
383 gmx_rmpbc_t gpbc=NULL;
385 /* PBC added for center-of-mass vector*/
386 /* Initiate the pbc structure */
387 memset(&pbc,0,sizeof(pbc));
389 if ((natoms = read_first_x(oenv,&status,fn,&t,&x0,box)) == 0)
390 gmx_fatal(FARGS,"Could not read coordinates from statusfile\n");
392 nr_tails = index[1] - index[0];
393 fprintf(stderr,"Number of elements in first group: %d\n",nr_tails);
394 /* take first group as standard. Not rocksolid, but might catch error in index*/
396 if (permolecule)
398 nslices=nr_tails;
399 bSliced=FALSE; /*force slices off */
400 fprintf(stderr,"Calculating order parameters for each of %d molecules\n",
401 nslices);
404 if (radial)
406 use_unitvector=TRUE;
407 fprintf(stderr,"Select an index group to calculate the radial membrane normal\n");
408 get_index(&top->atoms,radfn,1,&comsize,&comidx,&grpname);
409 if (distcalc)
411 if (grpname!=NULL)
412 sfree(grpname);
413 fprintf(stderr,"Select an index group to use as distance reference\n");
414 get_index(&top->atoms,radfn,1,&distsize,&distidx,&grpname);
415 bSliced=FALSE; /*force slices off*/
419 if (use_unitvector && bSliced)
420 fprintf(stderr,"Warning: slicing and specified unit vectors are not currently compatible\n");
422 snew(slCount, nslices);
423 snew(*slOrder, nslices);
424 for(i = 0; i < nslices; i++)
425 snew((*slOrder)[i],ngrps);
426 if (distcalc)
428 snew(*distvals, nslices);
429 for(i = 0; i < nslices; i++)
430 snew((*distvals)[i],ngrps);
432 snew(*order,ngrps);
433 snew(slFrameorder, nslices);
434 snew(x1, natoms);
436 if (bSliced) {
437 *slWidth = box[axis][axis]/nslices;
438 fprintf(stderr,"Box divided in %d slices. Initial width of slice: %f\n",
439 nslices, *slWidth);
442 #if 0
443 nr_tails = index[1] - index[0];
444 fprintf(stderr,"Number of elements in first group: %d\n",nr_tails);
445 /* take first group as standard. Not rocksolid, but might catch error
446 in index*/
447 #endif
449 teller = 0;
451 gpbc = gmx_rmpbc_init(&top->idef,ePBC,natoms,box);
452 /*********** Start processing trajectory ***********/
453 do {
454 if (bSliced)
455 *slWidth = box[axis][axis]/nslices;
456 teller++;
458 set_pbc(&pbc,ePBC,box);
459 gmx_rmpbc_copy(gpbc,natoms,box,x0,x1);
461 /* Now loop over all groups. There are ngrps groups, the order parameter can
462 be calculated for grp 1 to grp ngrps - 1. For each group, loop over all
463 atoms in group, which is index[i] to (index[i+1] - 1) See block.h. Of
464 course, in this case index[i+1] -index[i] has to be the same for all
465 groups, namely the number of tails. i just runs over all atoms in a tail,
466 so for DPPC ngrps = 16 and i runs from 1 to 14, including 14
469 if (radial)
471 /*center-of-mass determination*/
472 com[XX]=0.0; com[YY]=0.0; com[ZZ]=0.0;
473 for (j=0;j<comsize;j++)
474 rvec_inc(com,x1[comidx[j]]);
475 svmul(1.0/comsize,com,com);
477 if (distcalc)
479 dref[XX]=0.0; dref[YY]=0.0;dref[ZZ]=0.0;
480 for (j=0;j<distsize;j++)
481 rvec_inc(dist,x1[distidx[j]]);
482 svmul(1.0/distsize,dref,dref);
483 pbc_dx(&pbc,dref,com,dvec);
484 unitv(dvec,dvec);
487 for (i = 1; i < ngrps - 1; i++) {
488 clear_rvec(frameorder);
490 size = index[i+1] - index[i];
491 if (size != nr_tails)
492 gmx_fatal(FARGS,"grp %d does not have same number of"
493 " elements as grp 1\n",i);
495 for (j = 0; j < size; j++) {
496 if (radial)
497 /*create unit vector*/
499 pbc_dx(&pbc,x1[a[index[i]+j]],com,direction);
500 unitv(direction,direction);
501 /*DEBUG*/
502 /*if (j==0)
503 fprintf(stderr,"X %f %f %f\tcom %f %f %f\tdirection %f %f %f\n",x1[a[index[i]+j]][0],x1[a[index[i]+j]][1],x1[a[index[i]+j]][2],com[0],com[1],com[2],
504 direction[0],direction[1],direction[2]);*/
507 if (bUnsat) {
508 /* Using convention for unsaturated carbons */
509 /* first get Sz, the vector from Cn to Cn+1 */
510 rvec_sub(x1[a[index[i+1]+j]], x1[a[index[i]+j]], dist);
511 length = norm(dist);
512 check_length(length, a[index[i]+j], a[index[i+1]+j]);
513 svmul(1/length, dist, Sz);
515 /* this is actually the cosine of the angle between the double bond
516 and axis, because Sz is normalized and the two other components of
517 the axis on the bilayer are zero */
518 if (use_unitvector)
520 sdbangle += acos(iprod(direction,Sz)); /*this can probably be optimized*/
522 else
523 sdbangle += acos(Sz[axis]);
524 } else {
525 /* get vector dist(Cn-1,Cn+1) for tail atoms */
526 rvec_sub(x1[a[index[i+1]+j]], x1[a[index[i-1]+j]], dist);
527 length = norm(dist); /* determine distance between two atoms */
528 check_length(length, a[index[i-1]+j], a[index[i+1]+j]);
530 svmul(1/length, dist, Sz);
531 /* Sz is now the molecular axis Sz, normalized and all that */
534 /* now get Sx. Sx is normal to the plane of Cn-1, Cn and Cn+1 so
535 we can use the outer product of Cn-1->Cn and Cn+1->Cn, I hope */
536 rvec_sub(x1[a[index[i+1]+j]], x1[a[index[i]+j]], tmp1);
537 rvec_sub(x1[a[index[i-1]+j]], x1[a[index[i]+j]], tmp2);
538 cprod(tmp1, tmp2, Sx);
539 svmul(1/norm(Sx), Sx, Sx);
541 /* now we can get Sy from the outer product of Sx and Sz */
542 cprod(Sz, Sx, Sy);
543 svmul(1/norm(Sy), Sy, Sy);
545 /* the square of cosine of the angle between dist and the axis.
546 Using the innerproduct, but two of the three elements are zero
547 Determine the sum of the orderparameter of all atoms in group
549 if (use_unitvector)
551 cossum[XX] = sqr(iprod(Sx,direction)); /* this is allowed, since Sa is normalized */
552 cossum[YY] = sqr(iprod(Sy,direction));
553 cossum[ZZ] = sqr(iprod(Sz,direction));
555 else
557 cossum[XX] = sqr(Sx[axis]); /* this is allowed, since Sa is normalized */
558 cossum[YY] = sqr(Sy[axis]);
559 cossum[ZZ] = sqr(Sz[axis]);
562 for (m = 0; m < DIM; m++)
563 frameorder[m] += 0.5 * (3 * cossum[m] - 1);
565 if (bSliced) {
566 /* get average coordinate in box length for slicing,
567 determine which slice atom is in, increase count for that
568 slice. slFrameorder and slOrder are reals, not
569 rvecs. Only the component [axis] of the order tensor is
570 kept, until I find it necessary to know the others too
573 z1 = x1[a[index[i-1]+j]][axis];
574 z2 = x1[a[index[i+1]+j]][axis];
575 z_ave = 0.5 * (z1 + z2);
576 if (z_ave < 0)
577 z_ave += box[axis][axis];
578 if (z_ave > box[axis][axis])
579 z_ave -= box[axis][axis];
581 slice = (int)(0.5 + (z_ave / (*slWidth))) - 1;
582 slCount[slice]++; /* determine slice, increase count */
584 slFrameorder[slice] += 0.5 * (3 * cossum[axis] - 1);
586 else if (permolecule)
588 /* store per-molecule order parameter
589 * To just track single-axis order: (*slOrder)[j][i] += 0.5 * (3 * iprod(cossum,direction) - 1);
590 * following is for Scd order: */
591 (*slOrder)[j][i] += -1* (0.3333 * (3 * cossum[XX] - 1) + 0.3333 * 0.5 * (3 * cossum[YY] - 1));
593 if (distcalc)
595 /* bin order parameter by arc distance from reference group*/
596 arcdist=acos(iprod(dvec,direction));
597 (*distvals)[j][i]+=arcdist;
599 } /* end loop j, over all atoms in group */
601 for (m = 0; m < DIM; m++)
602 (*order)[i][m] += (frameorder[m]/size);
604 if (!permolecule)
605 { /*Skip following if doing per-molecule*/
606 for (k = 0; k < nslices; k++) {
607 if (slCount[k]) { /* if no elements, nothing has to be added */
608 (*slOrder)[k][i] += slFrameorder[k]/slCount[k];
609 slFrameorder[k] = 0; slCount[k] = 0;
612 } /* end loop i, over all groups in indexfile */
614 nr_frames++;
616 } while (read_next_x(oenv,status,&t,natoms,x0,box));
617 /*********** done with status file **********/
619 fprintf(stderr,"\nRead trajectory. Printing parameters to file\n");
620 gmx_rmpbc_done(gpbc);
622 /* average over frames */
623 for (i = 1; i < ngrps - 1; i++) {
624 svmul(1.0/nr_frames, (*order)[i], (*order)[i]);
625 fprintf(stderr,"Atom %d Tensor: x=%g , y=%g, z=%g\n",i,(*order)[i][XX],
626 (*order)[i][YY], (*order)[i][ZZ]);
627 if (bSliced || permolecule) {
628 for (k = 0; k < nslices; k++)
629 (*slOrder)[k][i] /= nr_frames;
631 if (distcalc)
632 for (k = 0; k < nslices; k++)
633 (*distvals)[k][i] /= nr_frames;
636 if (bUnsat)
637 fprintf(stderr,"Average angle between double bond and normal: %f\n",
638 180*sdbangle/(nr_frames * size*M_PI));
640 sfree(x0); /* free memory used by coordinate arrays */
641 sfree(x1);
642 if (comidx!=NULL)
643 sfree(comidx);
644 if (distidx!=NULL)
645 sfree(distidx);
646 if (grpname!=NULL)
647 sfree(grpname);
651 void order_plot(rvec order[], real *slOrder[], const char *afile, const char *bfile,
652 const char *cfile, int ngrps, int nslices, real slWidth, gmx_bool bSzonly,
653 gmx_bool permolecule, real **distvals, const output_env_t oenv)
655 FILE *ord, *slOrd; /* xvgr files with order parameters */
656 int atom, slice; /* atom corresponding to order para.*/
657 char buf[256]; /* for xvgr title */
658 real S; /* order parameter averaged over all atoms */
660 if (permolecule)
662 sprintf(buf,"Scd order parameters");
663 ord = xvgropen(afile,buf,"Atom","S",oenv);
664 sprintf(buf, "Orderparameters per atom per slice");
665 slOrd = xvgropen(bfile, buf, "Molecule", "S",oenv);
666 for (atom = 1; atom < ngrps - 1; atom++) {
667 fprintf(ord,"%12d %12g\n", atom, -1 * (0.6667 * order[atom][XX] +
668 0.333 * order[atom][YY]));
671 for (slice = 0; slice < nslices; slice++) {
672 fprintf(slOrd,"%12d\t",slice);
673 if (distvals)
674 fprintf(slOrd,"%12g\t", distvals[slice][1]); /*use distance value at second carbon*/
675 for (atom = 1; atom < ngrps - 1; atom++)
676 fprintf(slOrd,"%12g\t", slOrder[slice][atom]);
677 fprintf(slOrd,"\n");
681 else if (bSzonly) {
682 sprintf(buf,"Orderparameters Sz per atom");
683 ord = xvgropen(afile,buf,"Atom","S",oenv);
684 fprintf(stderr,"ngrps = %d, nslices = %d",ngrps, nslices);
686 sprintf(buf, "Orderparameters per atom per slice");
687 slOrd = xvgropen(bfile, buf, "Slice", "S",oenv);
689 for (atom = 1; atom < ngrps - 1; atom++)
690 fprintf(ord,"%12d %12g\n", atom, order[atom][ZZ]);
692 for (slice = 0; slice < nslices; slice++) {
693 S = 0;
694 for (atom = 1; atom < ngrps - 1; atom++)
695 S += slOrder[slice][atom];
696 fprintf(slOrd,"%12g %12g\n", slice*slWidth, S/atom);
699 } else {
700 sprintf(buf,"Order tensor diagonal elements");
701 ord = xvgropen(afile,buf,"Atom","S",oenv);
702 sprintf(buf,"Deuterium order parameters");
703 slOrd = xvgropen(cfile,buf, "Atom", "Scd",oenv);
705 for (atom = 1; atom < ngrps - 1; atom++) {
706 fprintf(ord,"%12d %12g %12g %12g\n", atom, order[atom][XX],
707 order[atom][YY], order[atom][ZZ]);
708 fprintf(slOrd,"%12d %12g\n", atom, -1 * (0.6667 * order[atom][XX] +
709 0.333 * order[atom][YY]));
712 ffclose(ord);
713 ffclose(slOrd);
717 void write_bfactors(t_filenm *fnm, int nfile, atom_id *index, atom_id *a, int nslices, int ngrps, real **order, t_topology *top, real **distvals,output_env_t oenv)
719 /*function to write order parameters as B factors in PDB file using
720 first frame of trajectory*/
721 t_trxstatus *status;
722 int natoms;
723 t_trxframe fr, frout;
724 t_atoms useatoms;
725 int i,j,ctr,nout;
727 ngrps-=2; /*we don't have an order parameter for the first or
728 last atom in each chain*/
729 nout=nslices*ngrps;
730 natoms=read_first_frame(oenv,&status,ftp2fn(efTRX,nfile,fnm),&fr,
731 TRX_NEED_X);
732 close_trj(status);
733 frout = fr;
734 frout.natoms=nout;
735 frout.bF=FALSE;
736 frout.bV=FALSE;
737 frout.x=0;
738 snew(frout.x,nout);
740 init_t_atoms(&useatoms,nout,TRUE);
741 useatoms.nr=nout;
743 /*initialize PDBinfo*/
744 for (i=0;i<useatoms.nr;++i)
746 useatoms.pdbinfo[i].type=0;
747 useatoms.pdbinfo[i].occup=0.0;
748 useatoms.pdbinfo[i].bfac=0.0;
749 useatoms.pdbinfo[i].bAnisotropic=FALSE;
752 for (j=0,ctr=0;j<nslices;j++)
753 for (i=0;i<ngrps;i++,ctr++)
755 /*iterate along each chain*/
756 useatoms.pdbinfo[ctr].bfac=order[j][i+1];
757 if (distvals)
758 useatoms.pdbinfo[ctr].occup=distvals[j][i+1];
759 copy_rvec(fr.x[a[index[i+1]+j]],frout.x[ctr]);
760 useatoms.atomname[ctr]=top->atoms.atomname[a[index[i+1]+j]];
761 useatoms.atom[ctr]=top->atoms.atom[a[index[i+1]+j]];
762 useatoms.nres=max(useatoms.nres,useatoms.atom[ctr].resind+1);
763 useatoms.resinfo[useatoms.atom[ctr].resind]=top->atoms.resinfo[useatoms.atom[ctr].resind]; /*copy resinfo*/
766 write_sto_conf(opt2fn("-ob",nfile,fnm),"Order parameters",&useatoms,frout.x,NULL,frout.ePBC,frout.box);
768 sfree(frout.x);
769 free_t_atoms(&useatoms,FALSE);
772 int gmx_order(int argc,char *argv[])
774 const char *desc[] = {
775 "Compute the order parameter per atom for carbon tails. For atom i the",
776 "vector i-1, i+1 is used together with an axis. ",
777 "The index file should contain only the groups to be used for calculations,",
778 "with each group of equivalent carbons along the relevant acyl chain in its own",
779 "group. There should not be any generic groups (like System, Protein) in the index",
780 "file to avoid confusing the program (this is not relevant to tetrahedral order",
781 "parameters however, which only work for water anyway).[PAR]",
782 "The program can also give all",
783 "diagonal elements of the order tensor and even calculate the deuterium",
784 "order parameter Scd (default). If the option [TT]-szonly[tt] is given, only one",
785 "order tensor component (specified by the [TT]-d[tt] option) is given and the",
786 "order parameter per slice is calculated as well. If [TT]-szonly[tt] is not",
787 "selected, all diagonal elements and the deuterium order parameter is",
788 "given.[PAR]"
789 "The tetrahedrality order parameters can be determined",
790 "around an atom. Both angle an distance order parameters are calculated. See",
791 "P.-L. Chau and A.J. Hardwick, Mol. Phys., 93, (1998), 511-518.",
792 "for more details.[BR]",
796 static int nslices = 1; /* nr of slices defined */
797 static gmx_bool bSzonly = FALSE; /* True if only Sz is wanted */
798 static gmx_bool bUnsat = FALSE; /* True if carbons are unsat. */
799 static const char *normal_axis[] = { NULL, "z", "x", "y", NULL };
800 static gmx_bool permolecule = FALSE; /*compute on a per-molecule basis */
801 static gmx_bool radial = FALSE; /*compute a radial membrane normal */
802 static gmx_bool distcalc = FALSE; /*calculate distance from a reference group */
803 t_pargs pa[] = {
804 { "-d", FALSE, etENUM, {normal_axis},
805 "Direction of the normal on the membrane" },
806 { "-sl", FALSE, etINT, {&nslices},
807 "Calculate order parameter as function of box length, dividing the box"
808 " into this number of slices." },
809 { "-szonly", FALSE, etBOOL,{&bSzonly},
810 "Only give Sz element of order tensor. (axis can be specified with [TT]-d[tt])" },
811 { "-unsat", FALSE, etBOOL,{&bUnsat},
812 "Calculate order parameters for unsaturated carbons. Note that this can"
813 "not be mixed with normal order parameters." },
814 { "-permolecule", FALSE, etBOOL,{&permolecule},
815 "Compute per-molecule Scd order parameters" },
816 { "-radial", FALSE, etBOOL,{&radial},
817 "Compute a radial membrane normal" },
818 { "-calcdist", FALSE, etBOOL,{&distcalc},
819 "Compute distance from a reference (currently defined only for radial and permolecule)" },
822 rvec *order; /* order par. for each atom */
823 real **slOrder; /* same, per slice */
824 real slWidth = 0.0; /* width of a slice */
825 char **grpname; /* groupnames */
826 int ngrps, /* nr. of groups */
828 axis=0; /* normal axis */
829 t_topology *top; /* topology */
830 int ePBC;
831 atom_id *index, /* indices for a */
832 *a; /* atom numbers in each group */
833 t_blocka *block; /* data from index file */
834 t_filenm fnm[] = { /* files for g_order */
835 { efTRX, "-f", NULL, ffREAD }, /* trajectory file */
836 { efNDX, "-n", NULL, ffREAD }, /* index file */
837 { efNDX, "-nr", NULL, ffREAD }, /* index for radial axis calculation */
838 { efTPX, NULL, NULL, ffREAD }, /* topology file */
839 { efXVG,"-o","order", ffWRITE }, /* xvgr output file */
840 { efXVG,"-od","deuter", ffWRITE }, /* xvgr output file */
841 { efPDB,"-ob",NULL, ffWRITE }, /* write Scd as B factors to PDB if permolecule */
842 { efXVG,"-os","sliced", ffWRITE }, /* xvgr output file */
843 { efXVG,"-Sg","sg-ang", ffOPTWR }, /* xvgr output file */
844 { efXVG,"-Sk","sk-dist", ffOPTWR }, /* xvgr output file */
845 { efXVG,"-Sgsl","sg-ang-slice", ffOPTWR }, /* xvgr output file */
846 { efXVG,"-Sksl","sk-dist-slice", ffOPTWR }, /* xvgr output file */
848 gmx_bool bSliced = FALSE; /* True if box is sliced */
849 #define NFILE asize(fnm)
850 real **distvals=NULL;
851 const char *sgfnm,*skfnm,*ndxfnm,*tpsfnm,*trxfnm;
852 output_env_t oenv;
854 CopyRight(stderr,argv[0]);
856 parse_common_args(&argc,argv,PCA_CAN_VIEW | PCA_CAN_TIME | PCA_BE_NICE,
857 NFILE,fnm,asize(pa),pa,asize(desc),desc,0, NULL,&oenv);
858 if (nslices < 1)
859 gmx_fatal(FARGS,"Can not have nslices < 1");
860 sgfnm = opt2fn_null("-Sg",NFILE,fnm);
861 skfnm = opt2fn_null("-Sk",NFILE,fnm);
862 ndxfnm = opt2fn_null("-n",NFILE,fnm);
863 tpsfnm = ftp2fn(efTPX,NFILE,fnm);
864 trxfnm = ftp2fn(efTRX,NFILE,fnm);
866 /* Calculate axis */
867 if (strcmp(normal_axis[0],"x") == 0) axis = XX;
868 else if (strcmp(normal_axis[0],"y") == 0) axis = YY;
869 else if (strcmp(normal_axis[0],"z") == 0) axis = ZZ;
870 else gmx_fatal(FARGS,"Invalid axis, use x, y or z");
872 switch (axis) {
873 case 0:
874 fprintf(stderr,"Taking x axis as normal to the membrane\n");
875 break;
876 case 1:
877 fprintf(stderr,"Taking y axis as normal to the membrane\n");
878 break;
879 case 2:
880 fprintf(stderr,"Taking z axis as normal to the membrane\n");
881 break;
884 /* tetraheder order parameter */
885 if (skfnm || sgfnm) {
886 /* If either of theoptions is set we compute both */
887 sgfnm = opt2fn("-Sg",NFILE,fnm);
888 skfnm = opt2fn("-Sk",NFILE,fnm);
889 calc_tetra_order_parm(ndxfnm,tpsfnm,trxfnm,sgfnm,skfnm,nslices,axis,
890 opt2fn("-Sgsl",NFILE,fnm),opt2fn("-Sksl",NFILE,fnm),
891 oenv);
892 /* view xvgr files */
893 do_view(oenv,opt2fn("-Sg",NFILE,fnm), NULL);
894 do_view(oenv,opt2fn("-Sk",NFILE,fnm), NULL);
895 if (nslices > 1) {
896 do_view(oenv,opt2fn("-Sgsl",NFILE,fnm), NULL);
897 do_view(oenv,opt2fn("-Sksl",NFILE,fnm), NULL);
900 else {
901 /* tail order parameter */
903 if (nslices > 1) {
904 bSliced = TRUE;
905 fprintf(stderr,"Dividing box in %d slices.\n\n", nslices);
908 if (bSzonly)
909 fprintf(stderr,"Only calculating Sz\n");
910 if (bUnsat)
911 fprintf(stderr,"Taking carbons as unsaturated!\n");
913 top = read_top(ftp2fn(efTPX,NFILE,fnm),&ePBC); /* read topology file */
915 block = init_index(ftp2fn(efNDX,NFILE,fnm),&grpname);
916 index = block->index; /* get indices from t_block block */
917 a = block->a; /* see block.h */
918 ngrps = block->nr;
920 if (permolecule)
922 nslices = index[1] - index[0]; /*I think this assumes contiguous lipids in topology*/
923 fprintf(stderr,"Calculating Scd order parameters for each of %d molecules\n",nslices);
926 if (distcalc)
928 radial=TRUE;
929 fprintf(stderr,"Calculating radial distances\n");
930 if (!permolecule)
931 gmx_fatal(FARGS,"Cannot yet output radial distances without permolecule\n");
934 /* show atomtypes, to check if index file is correct */
935 print_types(index, a, ngrps, grpname, top);
937 calc_order(ftp2fn(efTRX,NFILE,fnm), index, a, &order,
938 &slOrder, &slWidth, nslices, bSliced, bUnsat,
939 top, ePBC, ngrps, axis,permolecule,radial,distcalc,opt2fn_null("-nr",NFILE,fnm),&distvals, oenv);
941 if (radial)
942 ngrps--; /*don't print the last group--was used for
943 center-of-mass determination*/
945 order_plot(order, slOrder, opt2fn("-o",NFILE,fnm), opt2fn("-os",NFILE,fnm),
946 opt2fn("-od",NFILE,fnm), ngrps, nslices, slWidth, bSzonly,permolecule,distvals,oenv);
948 if (opt2bSet("-ob",NFILE,fnm))
950 if (!permolecule)
951 fprintf(stderr,
952 "Won't write B-factors with averaged order parameters; use -permolecule\n");
953 else
954 write_bfactors(fnm,NFILE,index,a,nslices,ngrps,slOrder,top,distvals,oenv);
958 do_view(oenv,opt2fn("-o",NFILE,fnm), NULL); /* view xvgr file */
959 do_view(oenv,opt2fn("-os",NFILE,fnm), NULL); /* view xvgr file */
960 do_view(oenv,opt2fn("-od",NFILE,fnm), NULL); /* view xvgr file */
963 thanx(stderr);
964 if (distvals!=NULL)
966 for (i=0;i<nslices;++i)
967 sfree(distvals[i]);
968 sfree(distvals);
971 return 0;