4 * This source code is part of
8 * GROningen MAchine for Chemical Simulations
12 * Copyright (c) 1991-1999
13 * BIOSON Research Institute, Dept. of Biophysical Chemistry
14 * University of Groningen, The Netherlands
17 * GROMACS: A message-passing parallel molecular dynamics implementation
18 * H.J.C. Berendsen, D. van der Spoel and R. van Drunen
19 * Comp. Phys. Comm. 91, 43-56 (1995)
21 * Also check out our WWW page:
22 * http://md.chem.rug.nl/~gmx
27 * GRowing Old MAkes el Chrono Sweat
29 static char *SRCID_g_order_c
= "$Id$";
47 /****************************************************************************/
48 /* This program calculates the order parameter per atom for an interface or */
49 /* bilayer, averaged over time. */
50 /* S = 1/2 * (3 * cos(i)cos(j) - delta(ij)) */
51 /* It is assumed that the order parameter with respect to a box-axis */
52 /* is calculated. In that case i = j = axis, and delta(ij) = 1. */
54 /* Peter Tieleman, April 1995 */
55 /****************************************************************************/
58 /* Print name of first atom in all groups in index file */
59 static void print_types(atom_id index
[], atom_id a
[], int ngrps
,
60 char *groups
[], t_topology
*top
)
64 fprintf(stderr
,"Using following groups: \n");
65 for(i
= 0; i
< ngrps
; i
++)
66 fprintf(stderr
,"Groupname: %s First atomname: %s First atomnr %u\n",
67 groups
[i
], *(top
->atoms
.atomname
[a
[index
[i
]]]), a
[index
[i
]]);
71 static void check_length(real length
, int a
, int b
)
74 fprintf(stderr
,"WARNING: distance between atoms %d and "
75 "%d > 0.3 nm (%f). Index file might be corrupt.\n",
79 void calc_order(char *fn
, atom_id
*index
, atom_id
*a
, rvec
**order
,
80 real
***slOrder
, real
*slWidth
, int nslices
, bool bSliced
,
81 bool bUnsat
, t_topology
*top
, int ngrps
, int axis
)
83 rvec
*x0
, /* coordinates with pbc */
84 *x1
, /* coordinates without pbc */
85 dist
; /* vector between two atoms */
86 matrix box
; /* box (3x3) */
88 rvec cossum
, /* sum of vector angles for three axes */
89 Sx
, Sy
, Sz
, /* the three molecular axes */
90 tmp1
, tmp2
, /* temp. rvecs for calculating dot products */
91 frameorder
; /* order parameters for one frame */
92 real
*slFrameorder
; /* order parameter for one frame, per slice */
93 real length
, /* total distance between two atoms */
94 t
, /* time from trajectory */
95 z_ave
,z1
,z2
; /* average z, used to det. which slice atom is in */
96 int natoms
, /* nr. atoms in trj */
97 nr_tails
, /* nr tails, to check if index file is correct */
98 size
=0, /* nr. of atoms in group. same as nr_tails */
100 slice
, /* current slice number */
102 *slCount
; /* nr. of atoms in one slice */
103 real dbangle
= 0, /* angle between double bond and axis */
104 sdbangle
= 0;/* sum of these angles */
106 if ((natoms
= read_first_x(&status
,fn
,&t
,&x0
,box
)) == 0)
107 fatal_error(0,"Could not read coordinates from statusfile\n");
109 snew(slCount
, nslices
);
110 snew(*slOrder
, nslices
);
111 for(i
= 0; i
< nslices
; i
++)
112 snew((*slOrder
)[i
],ngrps
);
114 snew(slFrameorder
, nslices
);
118 *slWidth
= box
[axis
][axis
]/nslices
;
119 fprintf(stderr
,"Box divided in %d slices. Initial width of slice: %f\n",
123 nr_tails
= index
[1] - index
[0];
124 fprintf(stderr
,"Number of elements in first group: %d\n",nr_tails
);
125 /* take first group as standard. Not rocksolid, but might catch error in index*/
129 /*********** Start processing trajectory ***********/
132 *slWidth
= box
[axis
][axis
]/nslices
;
135 rm_pbc(&(top
->idef
),top
->atoms
.nr
,box
,x0
,x1
);
137 /* Now loop over all groups. There are ngrps groups, the order parameter can
138 be calculated for grp 1 to grp ngrps - 1. For each group, loop over all
139 atoms in group, which is index[i] to (index[i+1] - 1) See block.h. Of
140 course, in this case index[i+1] -index[i] has to be the same for all
141 groups, namely the number of tails. i just runs over all atoms in a tail,
142 so for DPPC ngrps = 16 and i runs from 1 to 14, including 14
145 for (i
= 1; i
< ngrps
- 1; i
++) {
146 clear_rvec(frameorder
);
148 size
= index
[i
+1] - index
[i
];
149 if (size
!= nr_tails
)
150 fatal_error(0,"grp %d does not have same number of"
151 " elements as grp 1\n",i
);
153 for (j
= 0; j
< size
; j
++) {
155 /* Using convention for unsaturated carbons */
156 /* first get Sz, the vector from Cn to Cn+1 */
157 rvec_sub(x1
[a
[index
[i
+1]+j
]], x1
[a
[index
[i
]+j
]], dist
);
159 check_length(length
, a
[index
[i
]+j
], a
[index
[i
+1]+j
]);
160 svmul(1/length
, dist
, Sz
);
162 /* this is actually the cosine of the angle between the double bond
163 and axis, because Sz is normalized and the two other components of
164 the axis on the bilayer are zero */
165 sdbangle
+= acos(Sz
[axis
]);
167 /* get vector dist(Cn-1,Cn+1) for tail atoms */
168 rvec_sub(x1
[a
[index
[i
+1]+j
]], x1
[a
[index
[i
-1]+j
]], dist
);
169 length
= norm(dist
); /* determine distance between two atoms */
170 check_length(length
, a
[index
[i
-1]+j
], a
[index
[i
+1]+j
]);
172 svmul(1/length
, dist
, Sz
);
173 /* Sz is now the molecular axis Sz, normalized and all that */
176 /* now get Sx. Sx is normal to the plane of Cn-1, Cn and Cn+1 so
177 we can use the outer product of Cn-1->Cn and Cn+1->Cn, I hope */
178 rvec_sub(x1
[a
[index
[i
+1]+j
]], x1
[a
[index
[i
]+j
]], tmp1
);
179 rvec_sub(x1
[a
[index
[i
-1]+j
]], x1
[a
[index
[i
]+j
]], tmp2
);
180 oprod(tmp1
, tmp2
, Sx
);
181 svmul(1/norm(Sx
), Sx
, Sx
);
183 /* now we can get Sy from the outer product of Sx and Sz */
185 svmul(1/norm(Sy
), Sy
, Sy
);
187 /* the square of cosine of the angle between dist and the axis.
188 Using the innerproduct, but two of the three elements are zero
189 Determine the sum of the orderparameter of all atoms in group
191 cossum
[XX
] = sqr(Sx
[axis
]); /* this is allowed, since Sa is normalized */
192 cossum
[YY
] = sqr(Sy
[axis
]);
193 cossum
[ZZ
] = sqr(Sz
[axis
]);
195 for (m
= 0; m
< DIM
; m
++)
196 frameorder
[m
] += 0.5 * (3 * cossum
[m
] - 1);
199 /* get average coordinate in box length for slicing,
200 determine which slice atom is in, increase count for that
201 slice. slFrameorder and slOrder are reals, not
202 rvecs. Only the component [axis] of the order tensor is
203 kept, until I find it necessary to know the others too
206 z1
= x1
[a
[index
[i
-1]+j
]][axis
];
207 z2
= x1
[a
[index
[i
+1]+j
]][axis
];
208 z_ave
= 0.5 * (z1
+ z2
);
210 z_ave
+= box
[axis
][axis
];
211 if (z_ave
> box
[axis
][axis
])
212 z_ave
-= box
[axis
][axis
];
214 slice
= (int)(0.5 + (z_ave
/ (*slWidth
))) - 1;
215 slCount
[slice
]++; /* determine slice, increase count */
217 slFrameorder
[slice
] += 0.5 * (3 * cossum
[axis
] - 1);
219 } /* end loop j, over all atoms in group */
221 for (m
= 0; m
< DIM
; m
++)
222 (*order
)[i
][m
] += (frameorder
[m
]/size
);
224 for (k
= 0; k
< nslices
; k
++) {
225 if (slCount
[k
]) { /* if no elements, nothing has to be added */
226 (*slOrder
)[k
][i
] += slFrameorder
[k
]/slCount
[k
];
227 slFrameorder
[k
] = 0; slCount
[k
] = 0;
229 } /* end loop i, over all groups in indexfile */
233 } while (read_next_x(status
,&t
,natoms
,x0
,box
));
234 /*********** done with status file **********/
236 fprintf(stderr
,"\nRead trajectory. Printing parameters to file\n");
238 /* average over frames */
239 for (i
= 1; i
< ngrps
- 1; i
++) {
240 svmul(1.0/nr_frames
, (*order
)[i
], (*order
)[i
]);
241 fprintf(stderr
,"Atom %d Tensor: x=%g , y=%g, z=%g\n",i
,(*order
)[i
][XX
],
242 (*order
)[i
][YY
], (*order
)[i
][ZZ
]);
244 for (k
= 0; k
< nslices
; k
++)
245 (*slOrder
)[k
][i
] /= nr_frames
;
250 fprintf(stderr
,"Average angle between double bond and normal: %f\n",
251 180*sdbangle
/(nr_frames
* size
*M_PI
));
253 sfree(x0
); /* free memory used by coordinate arrays */
258 void order_plot(rvec order
[], real
*slOrder
[], char *afile
, char *bfile
,
259 char *cfile
, int ngrps
, int nslices
, real slWidth
, bool bSzonly
)
261 FILE *ord
, *slOrd
; /* xvgr files with order parameters */
262 int atom
, slice
; /* atom corresponding to order para.*/
263 char buf
[256]; /* for xvgr title */
264 real S
; /* order parameter averaged over all atoms */
267 sprintf(buf
,"Orderparameters Sz per atom");
268 ord
= xvgropen(afile
,buf
,"Atom","S");
269 fprintf(stderr
,"ngrps = %d, nslices = %d",ngrps
, nslices
);
271 sprintf(buf
, "Orderparameters per atom per slice");
272 slOrd
= xvgropen(bfile
, buf
, "Slice", "S");
274 for (atom
= 1; atom
< ngrps
- 1; atom
++)
275 fprintf(ord
,"%12d %12g\n", atom
, order
[atom
][ZZ
]);
277 for (slice
= 0; slice
< nslices
; slice
++) {
279 for (atom
= 1; atom
< ngrps
- 1; atom
++)
280 S
+= slOrder
[slice
][atom
];
281 fprintf(slOrd
,"%12g %12g\n", slice
*slWidth
, S
/atom
);
285 sprintf(buf
,"Order tensor diagonal elements");
286 ord
= xvgropen(afile
,buf
,"Atom","S");
287 sprintf(buf
,"Deuterium order parameters");
288 slOrd
= xvgropen(cfile
,buf
, "Atom", "Scd");
290 for (atom
= 1; atom
< ngrps
- 1; atom
++) {
291 fprintf(ord
,"%12d %12g %12g %12g\n", atom
, order
[atom
][XX
],
292 order
[atom
][YY
], order
[atom
][ZZ
]);
293 fprintf(slOrd
,"%12d %12g\n", atom
, -1 * (0.6667 * order
[atom
][XX
] +
294 0.333 * order
[atom
][YY
]));
302 int main(int argc
,char *argv
[])
304 static char *desc
[] = {
305 "Compute the order parameter per atom for carbon tails. For atom i the",
306 "vector i-1, i+1 is used together with an axis. The index file has to contain",
307 "a group with all equivalent atoms in all tails for each atom the",
308 "order parameter has to be calculated for. The program can also give all",
309 "diagonal elements of the order tensor and even calculate the deuterium",
310 "order parameter Scd (default). If the option -szonly is given, only one",
311 "order tensor component (specified by the -d option) is given and the",
312 "order parameter per slice is calculated as well. If -szonly is not",
313 "selected, all diagonal elements and the deuterium order parameter is",
317 static int nslices
= 1; /* nr of slices defined */
318 static bool bSzonly
= FALSE
; /* True if only Sz is wanted */
319 static bool bUnsat
= FALSE
; /* True if carbons are unsat. */
320 static char *normal_axis
[] = { NULL
, "z", "x", "y", NULL
};
322 { "-d", FALSE
, etENUM
, {normal_axis
},
323 "Direction of the normal on the membrane" },
324 { "-sl", FALSE
, etINT
, {&nslices
},
325 "Calculate order parameter as function of boxlength, dividing the box"
327 { "-szonly", FALSE
, etBOOL
,{&bSzonly
},
328 "Only give Sz element of order tensor. (axis can be specified with -d)" },
329 { "-unsat", FALSE
, etBOOL
,{&bUnsat
},
330 "Calculate order parameters for unsaturated carbons. Note that this can"
331 "not be mixed with normal order parameters." }
334 rvec
*order
; /* order par. for each atom */
335 real
**slOrder
; /* same, per slice */
336 real slWidth
= 0.0; /* width of a slice */
337 char **grpname
; /* groupnames */
338 int ngrps
, /* nr. of groups */
340 axis
; /* normal axis */
341 t_topology
*top
; /* topology */
342 atom_id
*index
, /* indices for a */
343 *a
; /* atom numbers in each group */
344 t_block
*block
; /* data from index file */
345 t_filenm fnm
[] = { /* files for g_order */
346 { efTRX
, "-f", NULL
, ffREAD
}, /* trajectory file */
347 { efNDX
, NULL
, NULL
, ffREAD
}, /* index file */
348 { efTPX
, NULL
, NULL
, ffREAD
}, /* topology file */
349 { efXVG
,"-o","order", ffWRITE
}, /* xvgr output file */
350 { efXVG
,"-od","deuter", ffWRITE
}, /* xvgr output file */
351 { efXVG
,"-os","sliced", ffWRITE
}, /* xvgr output file */
353 bool bSliced
= FALSE
; /* True if box is sliced */
354 #define NFILE asize(fnm)
356 CopyRight(stderr
,argv
[0]);
358 parse_common_args(&argc
,argv
,PCA_CAN_VIEW
| PCA_CAN_TIME
,TRUE
,
359 NFILE
,fnm
,asize(pa
),pa
,asize(desc
),desc
,0, NULL
);
362 if (strcmp(normal_axis
[0],"x") == 0) axis
= XX
;
363 else if (strcmp(normal_axis
[0],"y") == 0) axis
= YY
;
364 else if (strcmp(normal_axis
[0],"z") == 0) axis
= ZZ
;
365 else fatal_error(0,"Invalid axis, use x, y or z");
369 fprintf(stderr
,"Taking x axis as normal to the membrane\n");
372 fprintf(stderr
,"Taking y axis as normal to the membrane\n");
375 fprintf(stderr
,"Taking z axis as normal to the membrane\n");
381 fprintf(stderr
,"Dividing box in %d slices.\n\n", nslices
);
385 fprintf(stderr
,"Only calculating Sz\n");
387 fprintf(stderr
,"Taking carbons as unsaturated!\n");
389 top
= read_top(ftp2fn(efTPX
,NFILE
,fnm
)); /* read topology file */
391 block
= init_index(ftp2fn(efNDX
,NFILE
,fnm
),&grpname
);
392 index
= block
->index
; /* get indices from t_block block */
393 a
= block
->a
; /* see block.h */
396 /* show atomtypes, to check if index file is correct */
397 print_types(index
, a
, ngrps
, grpname
, top
);
399 calc_order(ftp2fn(efTRX
,NFILE
,fnm
), index
, a
, &order
,
400 &slOrder
, &slWidth
, nslices
, bSliced
, bUnsat
,
403 order_plot(order
, slOrder
, opt2fn("-o",NFILE
,fnm
), opt2fn("-os",NFILE
,fnm
),
404 opt2fn("-od",NFILE
,fnm
), ngrps
, nslices
, slWidth
, bSzonly
);
406 xvgr_file(opt2fn("-o",NFILE
,fnm
), NULL
); /* view xvgr file */
407 xvgr_file(opt2fn("-os",NFILE
,fnm
), NULL
); /* view xvgr file */
408 xvgr_file(opt2fn("-od",NFILE
,fnm
), NULL
); /* view xvgr file */