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 * Copyright (c) 2013,2014,2015,2016,2017 by the GROMACS development team.
7 * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
8 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9 * and including many others, as listed in the AUTHORS file in the
10 * top-level source 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.
45 #include "gromacs/commandline/pargs.h"
46 #include "gromacs/commandline/viewit.h"
47 #include "gromacs/fileio/confio.h"
48 #include "gromacs/fileio/trxio.h"
49 #include "gromacs/fileio/xvgr.h"
50 #include "gromacs/gmxana/cmat.h"
51 #include "gromacs/gmxana/gmx_ana.h"
52 #include "gromacs/gmxana/gstat.h"
53 #include "gromacs/math/functions.h"
54 #include "gromacs/math/utilities.h"
55 #include "gromacs/math/vec.h"
56 #include "gromacs/pbcutil/pbc.h"
57 #include "gromacs/pbcutil/rmpbc.h"
58 #include "gromacs/topology/index.h"
59 #include "gromacs/topology/topology.h"
60 #include "gromacs/trajectory/trajectoryframe.h"
61 #include "gromacs/utility/arraysize.h"
62 #include "gromacs/utility/cstringutil.h"
63 #include "gromacs/utility/fatalerror.h"
64 #include "gromacs/utility/futil.h"
65 #include "gromacs/utility/gmxassert.h"
66 #include "gromacs/utility/smalloc.h"
68 /****************************************************************************/
69 /* This program calculates the order parameter per atom for an interface or */
70 /* bilayer, averaged over time. */
71 /* S = 1/2 * (3 * cos(i)cos(j) - delta(ij)) */
72 /* It is assumed that the order parameter with respect to a box-axis */
73 /* is calculated. In that case i = j = axis, and delta(ij) = 1. */
75 /* Peter Tieleman, April 1995 */
76 /* P.J. van Maaren, November 2005 Added tetrahedral stuff */
77 /****************************************************************************/
79 static void find_nearest_neighbours(PbcType pbcType
,
93 int ix
, jx
, nsgbin
, *sgbin
;
94 int i
, ibin
, j
, k
, l
, n
, *nn
[4];
95 rvec dx
, rj
, rk
, urk
, urj
;
96 real cost
, cost2
, *sgmol
, *skmol
, rmean
, rmean2
, r2
, box2
, *r_nn
[4];
100 real onethird
= 1.0 / 3.0;
101 /* dmat = init_mat(maxidx, FALSE); */
102 box2
= box
[XX
][XX
] * box
[XX
][XX
];
103 snew(sl_count
, nslice
);
104 for (i
= 0; (i
< 4); i
++)
106 snew(r_nn
[i
], natoms
);
109 for (j
= 0; (j
< natoms
); j
++)
118 /* Must init pbc every step because of pressure coupling */
119 set_pbc(&pbc
, pbcType
, box
);
121 gmx_rmpbc(gpbc
, natoms
, box
, x
);
123 nsgbin
= 2001; // Calculated as (1 + 1/0.0005)
129 for (i
= 0; (i
< maxidx
); i
++) /* loop over index file */
132 for (j
= 0; (j
< maxidx
); j
++)
141 pbc_dx(&pbc
, x
[ix
], x
[jx
], dx
);
144 /* set_mat_entry(dmat,i,j,r2); */
146 /* determine the nearest neighbours */
149 r_nn
[3][i
] = r_nn
[2][i
];
151 r_nn
[2][i
] = r_nn
[1][i
];
153 r_nn
[1][i
] = r_nn
[0][i
];
158 else if (r2
< r_nn
[1][i
])
160 r_nn
[3][i
] = r_nn
[2][i
];
162 r_nn
[2][i
] = r_nn
[1][i
];
167 else if (r2
< r_nn
[2][i
])
169 r_nn
[3][i
] = r_nn
[2][i
];
174 else if (r2
< r_nn
[3][i
])
182 /* calculate mean distance between nearest neighbours */
184 for (j
= 0; (j
< 4); j
++)
186 r_nn
[j
][i
] = std::sqrt(r_nn
[j
][i
]);
195 /* Chau1998a eqn 3 */
196 /* angular part tetrahedrality order parameter per atom */
197 for (j
= 0; (j
< 3); j
++)
199 for (k
= j
+ 1; (k
< 4); k
++)
201 pbc_dx(&pbc
, x
[ix
], x
[index
[nn
[k
][i
]]], rk
);
202 pbc_dx(&pbc
, x
[ix
], x
[index
[nn
[j
][i
]]], rj
);
207 cost
= iprod(urk
, urj
) + onethird
;
210 /* sgmol[i] += 3*cost2/32; */
213 /* determine distribution */
214 ibin
= static_cast<int>(static_cast<real
>(nsgbin
) * cost2
);
219 /* printf("%d %d %f %d %d\n", j, k, cost * cost, ibin, sgbin[ibin]);*/
225 /* normalize sgmol between 0.0 and 1.0 */
226 sgmol
[i
] = 3 * sgmol
[i
] / 32;
229 /* distance part tetrahedrality order parameter per atom */
230 rmean2
= 4 * 3 * rmean
* rmean
;
231 for (j
= 0; (j
< 4); j
++)
233 skmol
[i
] += (rmean
- r_nn
[j
][i
]) * (rmean
- r_nn
[j
][i
]) / rmean2
;
234 /* printf("%d %f (%f %f %f %f) \n",
235 i, skmol[i], rmean, rmean2, r_nn[j][i], (rmean - r_nn[j][i]) );
241 /* Compute sliced stuff */
242 sl_index
= static_cast<int>(std::round((1 + x
[i
][slice_dim
] / box
[slice_dim
][slice_dim
])
243 * static_cast<real
>(nslice
)))
245 sgslice
[sl_index
] += sgmol
[i
];
246 skslice
[sl_index
] += skmol
[i
];
247 sl_count
[sl_index
]++;
248 } /* loop over entries in index file */
250 *sgmean
/= static_cast<real
>(maxidx
);
251 *skmean
/= static_cast<real
>(maxidx
);
253 for (i
= 0; (i
< nslice
); i
++)
257 sgslice
[i
] /= sl_count
[i
];
258 skslice
[i
] /= sl_count
[i
];
265 for (i
= 0; (i
< 4); i
++)
273 static void calc_tetra_order_parm(const char* fnNDX
,
282 const gmx_output_env_t
* oenv
)
284 FILE * fpsg
= nullptr, *fpsk
= nullptr;
295 int i
, *isize
, ng
, nframes
;
296 real
* sg_slice
, *sg_slice_tot
, *sk_slice
, *sk_slice_tot
;
297 gmx_rmpbc_t gpbc
= nullptr;
300 read_tps_conf(fnTPS
, &top
, &pbcType
, &xtop
, nullptr, box
, FALSE
);
302 snew(sg_slice
, nslice
);
303 snew(sk_slice
, nslice
);
304 snew(sg_slice_tot
, nslice
);
305 snew(sk_slice_tot
, nslice
);
307 /* get index groups */
308 printf("Select the group that contains the atoms you want to use for the tetrahedrality order "
309 "parameter calculation:\n");
313 get_index(&top
.atoms
, fnNDX
, ng
, isize
, index
, grpname
);
315 /* Analyze trajectory */
316 natoms
= read_first_x(oenv
, &status
, fnTRX
, &t
, &x
, box
);
317 if (natoms
> top
.atoms
.nr
)
319 gmx_fatal(FARGS
, "Topology (%d atoms) does not match trajectory (%d atoms)", top
.atoms
.nr
, natoms
);
321 check_index(nullptr, ng
, index
[0], nullptr, natoms
);
323 fpsg
= xvgropen(sgfn
, "S\\sg\\N Angle Order Parameter", "Time (ps)", "S\\sg\\N", oenv
);
324 fpsk
= xvgropen(skfn
, "S\\sk\\N Distance Order Parameter", "Time (ps)", "S\\sk\\N", oenv
);
326 /* loop over frames */
327 gpbc
= gmx_rmpbc_init(&top
.idef
, pbcType
, natoms
);
331 find_nearest_neighbours(pbcType
, natoms
, box
, x
, isize
[0], index
[0], &sg
, &sk
, nslice
,
332 slice_dim
, sg_slice
, sk_slice
, gpbc
);
333 for (i
= 0; (i
< nslice
); i
++)
335 sg_slice_tot
[i
] += sg_slice
[i
];
336 sk_slice_tot
[i
] += sk_slice
[i
];
338 fprintf(fpsg
, "%f %f\n", t
, sg
);
339 fprintf(fpsk
, "%f %f\n", t
, sk
);
341 } while (read_next_x(oenv
, status
, &t
, x
, box
));
343 gmx_rmpbc_done(gpbc
);
352 fpsg
= xvgropen(sgslfn
, "S\\sg\\N Angle Order Parameter / Slab", "(nm)", "S\\sg\\N", oenv
);
353 fpsk
= xvgropen(skslfn
, "S\\sk\\N Distance Order Parameter / Slab", "(nm)", "S\\sk\\N", oenv
);
354 for (i
= 0; (i
< nslice
); i
++)
356 fprintf(fpsg
, "%10g %10g\n", (i
+ 0.5) * box
[slice_dim
][slice_dim
] / nslice
,
357 sg_slice_tot
[i
] / static_cast<real
>(nframes
));
358 fprintf(fpsk
, "%10g %10g\n", (i
+ 0.5) * box
[slice_dim
][slice_dim
] / nslice
,
359 sk_slice_tot
[i
] / static_cast<real
>(nframes
));
366 /* Print name of first atom in all groups in index file */
367 static void print_types(const int index
[], int a
[], int ngrps
, char* groups
[], const t_topology
* top
)
371 fprintf(stderr
, "Using following groups: \n");
372 for (i
= 0; i
< ngrps
; i
++)
374 fprintf(stderr
, "Groupname: %s First atomname: %s First atomnr %d\n", groups
[i
],
375 *(top
->atoms
.atomname
[a
[index
[i
]]]), a
[index
[i
]]);
377 fprintf(stderr
, "\n");
380 static void check_length(real length
, int a
, int b
)
385 "WARNING: distance between atoms %d and "
386 "%d > 0.3 nm (%f). Index file might be corrupt.\n",
391 static void calc_order(const char* fn
,
400 const t_topology
* top
,
404 gmx_bool permolecule
,
409 const gmx_output_env_t
* oenv
)
411 /* if permolecule = TRUE, order parameters will be calculed per molecule
412 * and stored in slOrder with #slices = # molecules */
413 rvec
*x0
, /* coordinates with pbc */
414 *x1
; /* coordinates without pbc */
415 matrix box
; /* box (3x3) */
417 rvec cossum
, /* sum of vector angles for three axes */
418 Sx
, Sy
, Sz
, /* the three molecular axes */
419 tmp1
, tmp2
, /* temp. rvecs for calculating dot products */
420 frameorder
; /* order parameters for one frame */
421 real
* slFrameorder
; /* order parameter for one frame, per slice */
422 real length
, /* total distance between two atoms */
423 t
, /* time from trajectory */
424 z_ave
, z1
, z2
; /* average z, used to det. which slice atom is in */
425 int natoms
, /* nr. atoms in trj */
426 nr_tails
, /* nr tails, to check if index file is correct */
427 size
= 0, /* nr. of atoms in group. same as nr_tails */
428 i
, j
, m
, k
, teller
= 0, slice
; /* current slice number */
430 int* slCount
; /* nr. of atoms in one slice */
431 real sdbangle
= 0; /* sum of these angles */
432 gmx_bool use_unitvector
= FALSE
; /* use a specified unit vector instead of axis to specify unit normal*/
434 int comsize
, distsize
;
435 int * comidx
= nullptr, *distidx
= nullptr;
436 char* grpname
= nullptr;
438 real arcdist
, tmpdist
;
439 gmx_rmpbc_t gpbc
= nullptr;
441 /* PBC added for center-of-mass vector*/
442 /* Initiate the pbc structure */
443 std::memset(&pbc
, 0, sizeof(pbc
));
445 if ((natoms
= read_first_x(oenv
, &status
, fn
, &t
, &x0
, box
)) == 0)
447 gmx_fatal(FARGS
, "Could not read coordinates from statusfile\n");
450 nr_tails
= index
[1] - index
[0];
451 fprintf(stderr
, "Number of elements in first group: %d\n", nr_tails
);
452 /* take first group as standard. Not rocksolid, but might catch error in index*/
457 bSliced
= FALSE
; /*force slices off */
458 fprintf(stderr
, "Calculating order parameters for each of %d molecules\n", nslices
);
463 use_unitvector
= TRUE
;
464 fprintf(stderr
, "Select an index group to calculate the radial membrane normal\n");
465 get_index(&top
->atoms
, radfn
, 1, &comsize
, &comidx
, &grpname
);
469 if (grpname
!= nullptr)
473 fprintf(stderr
, "Select an index group to use as distance reference\n");
474 get_index(&top
->atoms
, radfn
, 1, &distsize
, &distidx
, &grpname
);
475 bSliced
= FALSE
; /*force slices off*/
478 if (use_unitvector
&& bSliced
)
481 "Warning: slicing and specified unit vectors are not currently compatible\n");
484 snew(slCount
, nslices
);
485 snew(*slOrder
, nslices
);
486 for (i
= 0; i
< nslices
; i
++)
488 snew((*slOrder
)[i
], ngrps
);
492 snew(*distvals
, nslices
);
493 for (i
= 0; i
< nslices
; i
++)
495 snew((*distvals
)[i
], ngrps
);
499 snew(slFrameorder
, nslices
);
504 *slWidth
= box
[axis
][axis
] / static_cast<real
>(nslices
);
505 fprintf(stderr
, "Box divided in %d slices. Initial width of slice: %f\n", nslices
, *slWidth
);
510 nr_tails
= index
[1] - index
[0];
511 fprintf(stderr
, "Number of elements in first group: %d\n", nr_tails
);
512 /* take first group as standard. Not rocksolid, but might catch error
518 gpbc
= gmx_rmpbc_init(&top
->idef
, pbcType
, natoms
);
519 /*********** Start processing trajectory ***********/
524 *slWidth
= box
[axis
][axis
] / static_cast<real
>(nslices
);
528 set_pbc(&pbc
, pbcType
, box
);
529 gmx_rmpbc_copy(gpbc
, natoms
, box
, x0
, x1
);
531 /* Now loop over all groups. There are ngrps groups, the order parameter can
532 be calculated for grp 1 to grp ngrps - 1. For each group, loop over all
533 atoms in group, which is index[i] to (index[i+1] - 1) See block.h. Of
534 course, in this case index[i+1] -index[i] has to be the same for all
535 groups, namely the number of tails. i just runs over all atoms in a tail,
536 so for DPPC ngrps = 16 and i runs from 1 to 14, including 14
542 /*center-of-mass determination*/
546 for (j
= 0; j
< comsize
; j
++)
548 rvec_inc(com
, x1
[comidx
[j
]]);
550 svmul(1.0 / comsize
, com
, com
);
552 rvec displacementFromReference
;
555 rvec dref
= { 0.0, 0.0, 0.0 };
556 for (j
= 0; j
< distsize
; j
++)
558 rvec_inc(dref
, x1
[distidx
[j
]]);
560 svmul(1.0 / distsize
, dref
, dref
);
563 pbc_dx(&pbc
, dref
, com
, displacementFromReference
);
564 unitv(displacementFromReference
, displacementFromReference
);
568 for (i
= 1; i
< ngrps
- 1; i
++)
570 clear_rvec(frameorder
);
572 size
= index
[i
+ 1] - index
[i
];
573 if (size
!= nr_tails
)
576 "grp %d does not have same number of"
577 " elements as grp 1\n",
581 for (j
= 0; j
< size
; j
++)
584 /*create unit vector*/
586 pbc_dx(&pbc
, x1
[a
[index
[i
] + j
]], com
, direction
);
587 unitv(direction
, direction
);
590 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],
591 direction[0],direction[1],direction[2]);*/
597 /* Using convention for unsaturated carbons */
598 /* first get Sz, the vector from Cn to Cn+1 */
599 rvec_sub(x1
[a
[index
[i
+ 1] + j
]], x1
[a
[index
[i
] + j
]], dist
);
601 check_length(length
, a
[index
[i
] + j
], a
[index
[i
+ 1] + j
]);
602 svmul(1.0 / length
, dist
, Sz
);
604 /* this is actually the cosine of the angle between the double bond
605 and axis, because Sz is normalized and the two other components of
606 the axis on the bilayer are zero */
609 sdbangle
+= gmx_angle(direction
, Sz
); /*this can probably be optimized*/
613 sdbangle
+= std::acos(Sz
[axis
]);
619 /* get vector dist(Cn-1,Cn+1) for tail atoms */
620 rvec_sub(x1
[a
[index
[i
+ 1] + j
]], x1
[a
[index
[i
- 1] + j
]], dist
);
621 length
= norm(dist
); /* determine distance between two atoms */
622 check_length(length
, a
[index
[i
- 1] + j
], a
[index
[i
+ 1] + j
]);
624 svmul(1.0 / length
, dist
, Sz
);
625 /* Sz is now the molecular axis Sz, normalized and all that */
628 /* now get Sx. Sx is normal to the plane of Cn-1, Cn and Cn+1 so
629 we can use the outer product of Cn-1->Cn and Cn+1->Cn, I hope */
630 rvec_sub(x1
[a
[index
[i
+ 1] + j
]], x1
[a
[index
[i
] + j
]], tmp1
);
631 rvec_sub(x1
[a
[index
[i
- 1] + j
]], x1
[a
[index
[i
] + j
]], tmp2
);
632 cprod(tmp1
, tmp2
, Sx
);
633 svmul(1.0 / norm(Sx
), Sx
, Sx
);
635 /* now we can get Sy from the outer product of Sx and Sz */
637 svmul(1.0 / norm(Sy
), Sy
, Sy
);
639 /* the square of cosine of the angle between dist and the axis.
640 Using the innerproduct, but two of the three elements are zero
641 Determine the sum of the orderparameter of all atoms in group
645 cossum
[XX
] = gmx::square(iprod(Sx
, direction
)); /* this is allowed, since Sa is normalized */
646 cossum
[YY
] = gmx::square(iprod(Sy
, direction
));
647 cossum
[ZZ
] = gmx::square(iprod(Sz
, direction
));
651 cossum
[XX
] = gmx::square(Sx
[axis
]); /* this is allowed, since Sa is normalized */
652 cossum
[YY
] = gmx::square(Sy
[axis
]);
653 cossum
[ZZ
] = gmx::square(Sz
[axis
]);
656 for (m
= 0; m
< DIM
; m
++)
658 frameorder
[m
] += 0.5 * (3.0 * cossum
[m
] - 1.0);
663 /* get average coordinate in box length for slicing,
664 determine which slice atom is in, increase count for that
665 slice. slFrameorder and slOrder are reals, not
666 rvecs. Only the component [axis] of the order tensor is
667 kept, until I find it necessary to know the others too
670 z1
= x1
[a
[index
[i
- 1] + j
]][axis
];
671 z2
= x1
[a
[index
[i
+ 1] + j
]][axis
];
672 z_ave
= 0.5 * (z1
+ z2
);
673 slice
= static_cast<int>((static_cast<real
>(nslices
) * z_ave
) / box
[axis
][axis
]);
676 slice
+= static_cast<real
>(nslices
);
678 slice
= slice
% nslices
;
680 slCount
[slice
]++; /* determine slice, increase count */
682 slFrameorder
[slice
] += 0.5 * (3 * cossum
[axis
] - 1);
684 else if (permolecule
)
686 /* store per-molecule order parameter
687 * To just track single-axis order: (*slOrder)[j][i] += 0.5 * (3 *
688 * iprod(cossum,direction) - 1); following is for Scd order: */
689 (*slOrder
)[j
][i
] += -1
690 * (1.0 / 3.0 * (3 * cossum
[XX
] - 1)
691 + 1.0 / 3.0 * 0.5 * (3.0 * cossum
[YY
] - 1));
697 /* bin order parameter by arc distance from reference group*/
698 arcdist
= gmx_angle(displacementFromReference
, direction
);
699 (*distvals
)[j
][i
] += arcdist
;
703 /* Want minimum lateral distance to first group calculated */
704 tmpdist
= trace(box
); /* should be max value */
705 for (k
= 0; k
< distsize
; k
++)
708 pbc_dx(&pbc
, x1
[distidx
[k
]], x1
[a
[index
[i
] + j
]], displacement
);
709 /* at the moment, just remove displacement[axis] */
710 displacement
[axis
] = 0;
711 tmpdist
= std::min(tmpdist
, norm2(displacement
));
713 // fprintf(stderr, "Min dist %f; trace %f\n", tmpdist, trace(box));
714 (*distvals
)[j
][i
] += std::sqrt(tmpdist
);
717 } /* end loop j, over all atoms in group */
719 for (m
= 0; m
< DIM
; m
++)
721 (*order
)[i
][m
] += (frameorder
[m
] / static_cast<real
>(size
));
725 { /*Skip following if doing per-molecule*/
726 for (k
= 0; k
< nslices
; k
++)
728 if (slCount
[k
]) /* if no elements, nothing has to be added */
730 (*slOrder
)[k
][i
] += slFrameorder
[k
] / static_cast<real
>(slCount
[k
]);
735 } /* end loop i, over all groups in indexfile */
739 } while (read_next_x(oenv
, status
, &t
, x0
, box
));
740 /*********** done with status file **********/
742 fprintf(stderr
, "\nRead trajectory. Printing parameters to file\n");
743 gmx_rmpbc_done(gpbc
);
745 /* average over frames */
746 for (i
= 1; i
< ngrps
- 1; i
++)
748 svmul(1.0 / nr_frames
, (*order
)[i
], (*order
)[i
]);
749 fprintf(stderr
, "Atom %d Tensor: x=%g , y=%g, z=%g\n", i
, (*order
)[i
][XX
], (*order
)[i
][YY
],
751 if (bSliced
|| permolecule
)
753 for (k
= 0; k
< nslices
; k
++)
755 (*slOrder
)[k
][i
] /= nr_frames
;
760 for (k
= 0; k
< nslices
; k
++)
762 (*distvals
)[k
][i
] /= nr_frames
;
769 fprintf(stderr
, "Average angle between double bond and normal: %f\n",
770 180 * sdbangle
/ (nr_frames
* static_cast<real
>(size
) * M_PI
));
773 sfree(x0
); /* free memory used by coordinate arrays */
775 if (comidx
!= nullptr)
779 if (distidx
!= nullptr)
783 if (grpname
!= nullptr)
790 static void order_plot(rvec order
[],
799 gmx_bool permolecule
,
801 const gmx_output_env_t
* oenv
)
803 FILE *ord
, *slOrd
; /* xvgr files with order parameters */
804 int atom
, slice
; /* atom corresponding to order para.*/
805 char buf
[256]; /* for xvgr title */
806 real S
; /* order parameter averaged over all atoms */
810 sprintf(buf
, "Scd order parameters");
811 ord
= xvgropen(afile
, buf
, "Atom", "S", oenv
);
812 sprintf(buf
, "Orderparameters per atom per slice");
813 slOrd
= xvgropen(bfile
, buf
, "Molecule", "S", oenv
);
814 for (atom
= 1; atom
< ngrps
- 1; atom
++)
816 fprintf(ord
, "%12d %12g\n", atom
,
817 -1.0 * (2.0 / 3.0 * order
[atom
][XX
] + 1.0 / 3.0 * order
[atom
][YY
]));
820 for (slice
= 0; slice
< nslices
; slice
++)
822 fprintf(slOrd
, "%12d\t", slice
);
825 fprintf(slOrd
, "%12g\t", distvals
[slice
][1]); /*use distance value at second carbon*/
827 for (atom
= 1; atom
< ngrps
- 1; atom
++)
829 fprintf(slOrd
, "%12g\t", slOrder
[slice
][atom
]);
831 fprintf(slOrd
, "\n");
836 sprintf(buf
, "Orderparameters Sz per atom");
837 ord
= xvgropen(afile
, buf
, "Atom", "S", oenv
);
838 fprintf(stderr
, "ngrps = %d, nslices = %d", ngrps
, nslices
);
840 sprintf(buf
, "Orderparameters per atom per slice");
841 slOrd
= xvgropen(bfile
, buf
, "Slice", "S", oenv
);
843 for (atom
= 1; atom
< ngrps
- 1; atom
++)
845 fprintf(ord
, "%12d %12g\n", atom
, order
[atom
][ZZ
]);
848 for (slice
= 0; slice
< nslices
; slice
++)
851 for (atom
= 1; atom
< ngrps
- 1; atom
++)
853 S
+= slOrder
[slice
][atom
];
855 fprintf(slOrd
, "%12g %12g\n", static_cast<real
>(slice
) * slWidth
,
856 S
/ static_cast<real
>(atom
));
861 sprintf(buf
, "Order tensor diagonal elements");
862 ord
= xvgropen(afile
, buf
, "Atom", "S", oenv
);
863 sprintf(buf
, "Deuterium order parameters");
864 slOrd
= xvgropen(cfile
, buf
, "Atom", "Scd", oenv
);
866 for (atom
= 1; atom
< ngrps
- 1; atom
++)
868 fprintf(ord
, "%12d %12g %12g %12g\n", atom
, order
[atom
][XX
], order
[atom
][YY
],
870 fprintf(slOrd
, "%12d %12g\n", atom
,
871 -1.0 * (2.0 / 3.0 * order
[atom
][XX
] + 1.0 / 3.0 * order
[atom
][YY
]));
878 static void write_bfactors(t_filenm
* fnm
,
885 const t_topology
* top
,
887 gmx_output_env_t
* oenv
)
889 /*function to write order parameters as B factors in PDB file using
890 first frame of trajectory*/
892 t_trxframe fr
, frout
;
896 ngrps
-= 2; /*we don't have an order parameter for the first or
897 last atom in each chain*/
898 nout
= nslices
* ngrps
;
899 read_first_frame(oenv
, &status
, ftp2fn(efTRX
, nfile
, fnm
), &fr
, TRX_NEED_X
);
909 init_t_atoms(&useatoms
, nout
, TRUE
);
912 /*initialize PDBinfo*/
913 for (i
= 0; i
< useatoms
.nr
; ++i
)
915 useatoms
.pdbinfo
[i
].type
= 0;
916 useatoms
.pdbinfo
[i
].occup
= 0.0;
917 useatoms
.pdbinfo
[i
].bfac
= 0.0;
918 useatoms
.pdbinfo
[i
].bAnisotropic
= FALSE
;
921 for (j
= 0, ctr
= 0; j
< nslices
; j
++)
923 for (i
= 0; i
< ngrps
; i
++, ctr
++)
925 /*iterate along each chain*/
926 useatoms
.pdbinfo
[ctr
].bfac
= order
[j
][i
+ 1];
929 useatoms
.pdbinfo
[ctr
].occup
= distvals
[j
][i
+ 1];
931 copy_rvec(fr
.x
[a
[index
[i
+ 1] + j
]], frout
.x
[ctr
]);
932 useatoms
.atomname
[ctr
] = top
->atoms
.atomname
[a
[index
[i
+ 1] + j
]];
933 useatoms
.atom
[ctr
] = top
->atoms
.atom
[a
[index
[i
+ 1] + j
]];
934 useatoms
.nres
= std::max(useatoms
.nres
, useatoms
.atom
[ctr
].resind
+ 1);
935 useatoms
.resinfo
[useatoms
.atom
[ctr
].resind
] =
936 top
->atoms
.resinfo
[useatoms
.atom
[ctr
].resind
]; /*copy resinfo*/
940 write_sto_conf(opt2fn("-ob", nfile
, fnm
), "Order parameters", &useatoms
, frout
.x
, nullptr,
941 frout
.pbcType
, frout
.box
);
944 done_atom(&useatoms
);
947 int gmx_order(int argc
, char* argv
[])
949 const char* desc
[] = {
950 "[THISMODULE] computes the order parameter per atom for carbon tails. For atom i the",
951 "vector i-1, i+1 is used together with an axis. ",
952 "The index file should contain only the groups to be used for calculations,",
953 "with each group of equivalent carbons along the relevant acyl chain in its own",
954 "group. There should not be any generic groups (like System, Protein) in the index",
955 "file to avoid confusing the program (this is not relevant to tetrahedral order",
956 "parameters however, which only work for water anyway).[PAR]",
957 "[THISMODULE] can also give all",
958 "diagonal elements of the order tensor and even calculate the deuterium",
959 "order parameter Scd (default). If the option [TT]-szonly[tt] is given, only one",
960 "order tensor component (specified by the [TT]-d[tt] option) is given and the",
961 "order parameter per slice is calculated as well. If [TT]-szonly[tt] is not",
962 "selected, all diagonal elements and the deuterium order parameter is",
964 "The tetrahedrality order parameters can be determined",
965 "around an atom. Both angle an distance order parameters are calculated. See",
966 "P.-L. Chau and A.J. Hardwick, Mol. Phys., 93, (1998), 511-518.",
970 static int nslices
= 1; /* nr of slices defined */
971 static gmx_bool bSzonly
= FALSE
; /* True if only Sz is wanted */
972 static gmx_bool bUnsat
= FALSE
; /* True if carbons are unsat. */
973 static const char* normal_axis
[] = { nullptr, "z", "x", "y", nullptr };
974 static gmx_bool permolecule
= FALSE
; /*compute on a per-molecule basis */
975 static gmx_bool radial
= FALSE
; /*compute a radial membrane normal */
976 static gmx_bool distcalc
= FALSE
; /*calculate distance from a reference group */
978 { "-d", FALSE
, etENUM
, { normal_axis
}, "Direction of the normal on the membrane" },
983 "Calculate order parameter as function of box length, dividing the box"
984 " into this number of slices." },
989 "Only give Sz element of order tensor. (axis can be specified with [TT]-d[tt])" },
994 "Calculate order parameters for unsaturated carbons. Note that this can"
995 "not be mixed with normal order parameters." },
1000 "Compute per-molecule Scd order parameters" },
1001 { "-radial", FALSE
, etBOOL
, { &radial
}, "Compute a radial membrane normal" },
1002 { "-calcdist", FALSE
, etBOOL
, { &distcalc
}, "Compute distance from a reference" },
1005 rvec
* order
; /* order par. for each atom */
1006 real
** slOrder
; /* same, per slice */
1007 real slWidth
= 0.0; /* width of a slice */
1008 char** grpname
; /* groupnames */
1009 int ngrps
, /* nr. of groups */
1010 i
, axis
= 0; /* normal axis */
1011 t_topology
* top
; /* topology */
1012 PbcType pbcType
; /* type of periodic boundary conditions */
1013 int * index
, /* indices for a */
1014 *a
; /* atom numbers in each group */
1015 t_blocka
* block
; /* data from index file */
1017 /* files for g_order */
1018 { efTRX
, "-f", nullptr, ffREAD
}, /* trajectory file */
1019 { efNDX
, "-n", nullptr, ffREAD
}, /* index file */
1020 { efNDX
, "-nr", nullptr, ffOPTRD
}, /* index for radial axis calculation */
1021 { efTPR
, nullptr, nullptr, ffREAD
}, /* topology file */
1022 { efXVG
, "-o", "order", ffWRITE
}, /* xvgr output file */
1023 { efXVG
, "-od", "deuter", ffWRITE
}, /* xvgr output file */
1024 { efPDB
, "-ob", nullptr, ffOPTWR
}, /* write Scd as B factors to PDB if permolecule */
1025 { efXVG
, "-os", "sliced", ffWRITE
}, /* xvgr output file */
1026 { efXVG
, "-Sg", "sg-ang", ffOPTWR
}, /* xvgr output file */
1027 { efXVG
, "-Sk", "sk-dist", ffOPTWR
}, /* xvgr output file */
1028 { efXVG
, "-Sgsl", "sg-ang-slice", ffOPTWR
}, /* xvgr output file */
1029 { efXVG
, "-Sksl", "sk-dist-slice", ffOPTWR
}, /* xvgr output file */
1031 gmx_bool bSliced
= FALSE
; /* True if box is sliced */
1032 #define NFILE asize(fnm)
1033 real
** distvals
= nullptr;
1034 const char * sgfnm
, *skfnm
, *ndxfnm
, *tpsfnm
, *trxfnm
;
1035 gmx_output_env_t
* oenv
;
1037 if (!parse_common_args(&argc
, argv
, PCA_CAN_VIEW
| PCA_CAN_TIME
, NFILE
, fnm
, asize(pa
), pa
,
1038 asize(desc
), desc
, 0, nullptr, &oenv
))
1044 gmx_fatal(FARGS
, "Can not have nslices < 1");
1046 sgfnm
= opt2fn_null("-Sg", NFILE
, fnm
);
1047 skfnm
= opt2fn_null("-Sk", NFILE
, fnm
);
1048 ndxfnm
= opt2fn_null("-n", NFILE
, fnm
);
1049 tpsfnm
= ftp2fn(efTPR
, NFILE
, fnm
);
1050 trxfnm
= ftp2fn(efTRX
, NFILE
, fnm
);
1052 /* Calculate axis */
1053 GMX_RELEASE_ASSERT(normal_axis
[0] != nullptr, "Options inconsistency; normal_axis[0] is NULL");
1054 if (std::strcmp(normal_axis
[0], "x") == 0)
1058 else if (std::strcmp(normal_axis
[0], "y") == 0)
1062 else if (std::strcmp(normal_axis
[0], "z") == 0)
1068 gmx_fatal(FARGS
, "Invalid axis, use x, y or z");
1073 case 0: fprintf(stderr
, "Taking x axis as normal to the membrane\n"); break;
1074 case 1: fprintf(stderr
, "Taking y axis as normal to the membrane\n"); break;
1075 case 2: fprintf(stderr
, "Taking z axis as normal to the membrane\n"); break;
1078 /* tetraheder order parameter */
1081 /* If either of theoptions is set we compute both */
1082 sgfnm
= opt2fn("-Sg", NFILE
, fnm
);
1083 skfnm
= opt2fn("-Sk", NFILE
, fnm
);
1084 calc_tetra_order_parm(ndxfnm
, tpsfnm
, trxfnm
, sgfnm
, skfnm
, nslices
, axis
,
1085 opt2fn("-Sgsl", NFILE
, fnm
), opt2fn("-Sksl", NFILE
, fnm
), oenv
);
1086 /* view xvgr files */
1087 do_view(oenv
, opt2fn("-Sg", NFILE
, fnm
), nullptr);
1088 do_view(oenv
, opt2fn("-Sk", NFILE
, fnm
), nullptr);
1091 do_view(oenv
, opt2fn("-Sgsl", NFILE
, fnm
), nullptr);
1092 do_view(oenv
, opt2fn("-Sksl", NFILE
, fnm
), nullptr);
1097 /* tail order parameter */
1102 fprintf(stderr
, "Dividing box in %d slices.\n\n", nslices
);
1107 fprintf(stderr
, "Only calculating Sz\n");
1111 fprintf(stderr
, "Taking carbons as unsaturated!\n");
1114 top
= read_top(ftp2fn(efTPR
, NFILE
, fnm
), &pbcType
); /* read topology file */
1116 block
= init_index(ftp2fn(efNDX
, NFILE
, fnm
), &grpname
);
1117 index
= block
->index
; /* get indices from t_block block */
1118 a
= block
->a
; /* see block.h */
1123 nslices
= index
[1] - index
[0]; /*I think this assumes contiguous lipids in topology*/
1124 fprintf(stderr
, "Calculating Scd order parameters for each of %d molecules\n", nslices
);
1129 fprintf(stderr
, "Calculating radial distances\n");
1132 gmx_fatal(FARGS
, "Cannot yet output radial distances without permolecule\n");
1136 /* show atomtypes, to check if index file is correct */
1137 print_types(index
, a
, ngrps
, grpname
, top
);
1139 calc_order(ftp2fn(efTRX
, NFILE
, fnm
), index
, a
, &order
, &slOrder
, &slWidth
, nslices
,
1140 bSliced
, bUnsat
, top
, pbcType
, ngrps
, axis
, permolecule
, radial
, distcalc
,
1141 opt2fn_null("-nr", NFILE
, fnm
), &distvals
, oenv
);
1145 ngrps
--; /*don't print the last group--was used for
1146 center-of-mass determination*/
1148 order_plot(order
, slOrder
, opt2fn("-o", NFILE
, fnm
), opt2fn("-os", NFILE
, fnm
),
1149 opt2fn("-od", NFILE
, fnm
), ngrps
, nslices
, slWidth
, bSzonly
, permolecule
,
1152 if (opt2bSet("-ob", NFILE
, fnm
))
1157 "Won't write B-factors with averaged order parameters; use -permolecule\n");
1161 write_bfactors(fnm
, NFILE
, index
, a
, nslices
, ngrps
, slOrder
, top
, distvals
, oenv
);
1166 do_view(oenv
, opt2fn("-o", NFILE
, fnm
), nullptr); /* view xvgr file */
1167 do_view(oenv
, opt2fn("-os", NFILE
, fnm
), nullptr); /* view xvgr file */
1168 do_view(oenv
, opt2fn("-od", NFILE
, fnm
), nullptr); /* view xvgr file */
1171 if (distvals
!= nullptr)
1173 for (i
= 0; i
< nslices
; ++i
)