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.
42 #include "gromacs/commandline/pargs.h"
43 #include "gromacs/commandline/viewit.h"
44 #include "gromacs/fileio/confio.h"
45 #include "gromacs/fileio/trxio.h"
46 #include "gromacs/fileio/xvgr.h"
47 #include "gromacs/gmxana/gmx_ana.h"
48 #include "gromacs/gmxana/gstat.h"
49 #include "gromacs/math/functions.h"
50 #include "gromacs/math/vec.h"
51 #include "gromacs/pbcutil/pbc.h"
52 #include "gromacs/pbcutil/rmpbc.h"
53 #include "gromacs/topology/index.h"
54 #include "gromacs/topology/topology.h"
55 #include "gromacs/utility/arraysize.h"
56 #include "gromacs/utility/cstringutil.h"
57 #include "gromacs/utility/fatalerror.h"
58 #include "gromacs/utility/smalloc.h"
60 static void calc_com_pbc(int nrefat
, t_topology
* top
, rvec x
[], t_pbc
* pbc
, const int index
[], rvec xref
, gmx_bool bPBC
)
62 const real tol
= 1e-4;
68 /* First simple calculation */
71 for (m
= 0; (m
< nrefat
); m
++)
74 mass
= top
->atoms
.atom
[ai
].m
;
75 for (j
= 0; (j
< DIM
); j
++)
77 xref
[j
] += mass
* x
[ai
][j
];
81 svmul(1 / mtot
, xref
, xref
);
82 /* Now check if any atom is more than half the box from the COM */
89 for (m
= 0; (m
< nrefat
); m
++)
92 mass
= top
->atoms
.atom
[ai
].m
/ mtot
;
93 pbc_dx(pbc
, x
[ai
], xref
, dx
);
94 rvec_add(xref
, dx
, xtest
);
95 for (j
= 0; (j
< DIM
); j
++)
97 if (std::abs(xtest
[j
] - x
[ai
][j
]) > tol
)
99 /* Here we have used the wrong image for contributing to the COM */
100 xref
[j
] += mass
* (xtest
[j
] - x
[ai
][j
]);
108 printf("COM: %8.3f %8.3f %8.3f iter = %d\n", xref
[XX
], xref
[YY
], xref
[ZZ
], iter
);
115 int gmx_sorient(int argc
, char* argv
[])
118 PbcType pbcType
= PbcType::Unset
;
126 int i
, p
, sa0
, sa1
, sa2
, n
, ntot
, nf
, m
, *hist1
, *hist2
, *histn
, nbin1
, nbin2
, nrbin
;
127 real
* histi1
, *histi2
, invbw
, invrbw
;
129 int * isize
, nrefgrp
, nrefat
;
132 real inp
, outp
, nav
, normfac
, rmin2
, rmax2
, rcut
, rcut2
, r2
, r
;
136 rvec xref
, dx
, dxh1
, dxh2
, outer
;
137 gmx_rmpbc_t gpbc
= nullptr;
139 const char* legr
[] = { "<cos(\\8q\\4\\s1\\N)>", "<3cos\\S2\\N(\\8q\\4\\s2\\N)-1>" };
140 const char* legc
[] = { "cos(\\8q\\4\\s1\\N)", "3cos\\S2\\N(\\8q\\4\\s2\\N)-1" };
142 const char* desc
[] = {
143 "[THISMODULE] analyzes solvent orientation around solutes.",
144 "It calculates two angles between the vector from one or more",
145 "reference positions to the first atom of each solvent molecule:",
147 " * [GRK]theta[grk][SUB]1[sub]: the angle with the vector from the first atom of "
149 " molecule to the midpoint between atoms 2 and 3.",
150 " * [GRK]theta[grk][SUB]2[sub]: the angle with the normal of the solvent plane, "
152 " same three atoms, or, when the option [TT]-v23[tt] is set, ",
153 " the angle with the vector between atoms 2 and 3.",
155 "The reference can be a set of atoms or",
156 "the center of mass of a set of atoms. The group of solvent atoms should",
157 "consist of 3 atoms per solvent molecule.",
158 "Only solvent molecules between [TT]-rmin[tt] and [TT]-rmax[tt] are",
159 "considered for [TT]-o[tt] and [TT]-no[tt] each frame.[PAR]",
160 "[TT]-o[tt]: distribution of [MATH][COS][GRK]theta[grk][SUB]1[sub][cos][math] for "
161 "rmin<=r<=rmax.[PAR]",
162 "[TT]-no[tt]: distribution of [MATH][COS][GRK]theta[grk][SUB]2[sub][cos][math] for "
163 "rmin<=r<=rmax.[PAR]",
164 "[TT]-ro[tt]: [MATH][CHEVRON][COS][GRK]theta[grk][SUB]1[sub][cos][chevron][math] "
165 "and [MATH][CHEVRON]3[COS]^2[GRK]theta[grk][SUB]2[sub][cos]-1[chevron][math] as a "
168 "[TT]-co[tt]: the sum over all solvent molecules within distance r",
169 "of [MATH][COS][GRK]theta[grk][SUB]1[sub][cos][math] and "
170 "[MATH]3[COS]^2([GRK]theta[grk][SUB]2[sub])-1[cos][math] as a function of r.[PAR]",
171 "[TT]-rc[tt]: the distribution of the solvent molecules as a function of r"
174 gmx_output_env_t
* oenv
;
175 static gmx_bool bCom
= FALSE
, bVec23
= FALSE
, bPBC
= FALSE
;
176 static real rmin
= 0.0, rmax
= 0.5, binwidth
= 0.02, rbinw
= 0.02;
178 { "-com", FALSE
, etBOOL
, { &bCom
}, "Use the center of mass as the reference position" },
179 { "-v23", FALSE
, etBOOL
, { &bVec23
}, "Use the vector between atoms 2 and 3" },
180 { "-rmin", FALSE
, etREAL
, { &rmin
}, "Minimum distance (nm)" },
181 { "-rmax", FALSE
, etREAL
, { &rmax
}, "Maximum distance (nm)" },
182 { "-cbin", FALSE
, etREAL
, { &binwidth
}, "Binwidth for the cosine" },
183 { "-rbin", FALSE
, etREAL
, { &rbinw
}, "Binwidth for r (nm)" },
188 "Check PBC for the center of mass calculation. Only necessary when your reference group "
189 "consists of several molecules." }
192 t_filenm fnm
[] = { { efTRX
, nullptr, nullptr, ffREAD
}, { efTPS
, nullptr, nullptr, ffREAD
},
193 { efNDX
, nullptr, nullptr, ffOPTRD
}, { efXVG
, nullptr, "sori", ffWRITE
},
194 { efXVG
, "-no", "snor", ffWRITE
}, { efXVG
, "-ro", "sord", ffWRITE
},
195 { efXVG
, "-co", "scum", ffWRITE
}, { efXVG
, "-rc", "scount", ffWRITE
} };
196 #define NFILE asize(fnm)
198 if (!parse_common_args(&argc
, argv
, PCA_CAN_TIME
| PCA_CAN_VIEW
, NFILE
, fnm
, asize(pa
), pa
,
199 asize(desc
), desc
, 0, nullptr, &oenv
))
204 bTPS
= (opt2bSet("-s", NFILE
, fnm
) || !opt2bSet("-n", NFILE
, fnm
) || bCom
);
207 read_tps_conf(ftp2fn(efTPS
, NFILE
, fnm
), &top
, &pbcType
, &xtop
, nullptr, box
, bCom
);
210 /* get index groups */
211 printf("Select a group of reference particles and a solvent group:\n");
217 get_index(&top
.atoms
, ftp2fn_null(efNDX
, NFILE
, fnm
), 2, isize
, index
, grpname
);
221 get_index(nullptr, ftp2fn(efNDX
, NFILE
, fnm
), 2, isize
, index
, grpname
);
237 gmx_fatal(FARGS
, "The number of solvent atoms (%d) is not a multiple of 3", isize
[1]);
240 /* initialize reading trajectory: */
241 natoms
= read_first_x(oenv
, &status
, ftp2fn(efTRX
, NFILE
, fnm
), &t
, &x
, box
);
243 rmin2
= gmx::square(rmin
);
244 rmax2
= gmx::square(rmax
);
245 rcut
= 0.99 * std::sqrt(max_cutoff2(guessPbcType(box
), box
));
250 rcut2
= gmx::square(rcut
);
252 invbw
= 1 / binwidth
;
253 nbin1
= 1 + gmx::roundToInt(2 * invbw
);
254 nbin2
= 1 + gmx::roundToInt(invbw
);
260 nrbin
= 1 + static_cast<int>(rcut
/ rbinw
);
276 /* make molecules whole again */
277 gpbc
= gmx_rmpbc_init(&top
.idef
, pbcType
, natoms
);
279 /* start analysis of trajectory */
284 /* make molecules whole again */
285 gmx_rmpbc(gpbc
, natoms
, box
, x
);
288 set_pbc(&pbc
, pbcType
, box
);
291 for (p
= 0; (p
< nrefgrp
); p
++)
295 calc_com_pbc(nrefat
, &top
, x
, &pbc
, index
[0], xref
, bPBC
);
299 copy_rvec(x
[index
[0][p
]], xref
);
302 for (m
= 0; m
< isize
[1]; m
+= 3)
305 sa1
= index
[1][m
+ 1];
306 sa2
= index
[1][m
+ 2];
307 range_check(sa0
, 0, natoms
);
308 range_check(sa1
, 0, natoms
);
309 range_check(sa2
, 0, natoms
);
310 pbc_dx(&pbc
, x
[sa0
], xref
, dx
);
317 /* Determine the normal to the plain */
318 rvec_sub(x
[sa1
], x
[sa0
], dxh1
);
319 rvec_sub(x
[sa2
], x
[sa0
], dxh2
);
320 rvec_inc(dxh1
, dxh2
);
321 svmul(1 / r
, dx
, dx
);
323 inp
= iprod(dx
, dxh1
);
324 cprod(dxh1
, dxh2
, outer
);
326 outp
= iprod(dx
, outer
);
330 /* Use the vector between the 2nd and 3rd atom */
331 rvec_sub(x
[sa2
], x
[sa1
], dxh2
);
333 outp
= iprod(dx
, dxh2
) / r
;
336 int ii
= static_cast<int>(invrbw
* r
);
337 range_check(ii
, 0, nrbin
);
339 histi2
[ii
] += 3 * gmx::square(outp
) - 1;
342 if ((r2
>= rmin2
) && (r2
< rmax2
))
344 int ii1
= static_cast<int>(invbw
* (inp
+ 1));
345 int ii2
= static_cast<int>(invbw
* std::abs(outp
));
347 range_check(ii1
, 0, nbin1
);
348 range_check(ii2
, 0, nbin2
);
361 } while (read_next_x(oenv
, status
, &t
, x
, box
));
366 gmx_rmpbc_done(gpbc
);
368 /* Add the bin for the exact maximum to the previous bin */
369 hist1
[nbin1
- 1] += hist1
[nbin1
];
370 hist2
[nbin2
- 1] += hist2
[nbin2
];
372 nav
= static_cast<real
>(ntot
) / (nrefgrp
* nf
);
373 normfac
= invbw
/ ntot
;
375 fprintf(stderr
, "Average nr of molecules between %g and %g nm: %.1f\n", rmin
, rmax
, nav
);
380 fprintf(stderr
, "Average cos(theta1) between %g and %g nm: %6.3f\n", rmin
, rmax
, sum1
);
381 fprintf(stderr
, "Average 3cos2(theta2)-1 between %g and %g nm: %6.3f\n", rmin
, rmax
, sum2
);
384 sprintf(str
, "Solvent orientation between %g and %g nm", rmin
, rmax
);
385 fp
= xvgropen(opt2fn("-o", NFILE
, fnm
), str
, "cos(\\8q\\4\\s1\\N)", "", oenv
);
386 if (output_env_get_print_xvgr_codes(oenv
))
388 fprintf(fp
, "@ subtitle \"average shell size %.1f molecules\"\n", nav
);
390 for (i
= 0; i
< nbin1
; i
++)
392 fprintf(fp
, "%g %g\n", (i
+ 0.5) * binwidth
- 1, 2 * normfac
* hist1
[i
]);
396 sprintf(str
, "Solvent normal orientation between %g and %g nm", rmin
, rmax
);
397 fp
= xvgropen(opt2fn("-no", NFILE
, fnm
), str
, "cos(\\8q\\4\\s2\\N)", "", oenv
);
398 if (output_env_get_print_xvgr_codes(oenv
))
400 fprintf(fp
, "@ subtitle \"average shell size %.1f molecules\"\n", nav
);
402 for (i
= 0; i
< nbin2
; i
++)
404 fprintf(fp
, "%g %g\n", (i
+ 0.5) * binwidth
, normfac
* hist2
[i
]);
409 sprintf(str
, "Solvent orientation");
410 fp
= xvgropen(opt2fn("-ro", NFILE
, fnm
), str
, "r (nm)", "", oenv
);
411 if (output_env_get_print_xvgr_codes(oenv
))
413 fprintf(fp
, "@ subtitle \"as a function of distance\"\n");
415 xvgr_legend(fp
, 2, legr
, oenv
);
416 for (i
= 0; i
< nrbin
; i
++)
418 fprintf(fp
, "%g %g %g\n", (i
+ 0.5) * rbinw
, histn
[i
] ? histi1
[i
] / histn
[i
] : 0,
419 histn
[i
] ? histi2
[i
] / histn
[i
] : 0);
423 sprintf(str
, "Cumulative solvent orientation");
424 fp
= xvgropen(opt2fn("-co", NFILE
, fnm
), str
, "r (nm)", "", oenv
);
425 if (output_env_get_print_xvgr_codes(oenv
))
427 fprintf(fp
, "@ subtitle \"as a function of distance\"\n");
429 xvgr_legend(fp
, 2, legc
, oenv
);
430 normfac
= 1.0 / (nrefgrp
* nf
);
433 fprintf(fp
, "%g %g %g\n", 0.0, c1
, c2
);
434 for (i
= 0; i
< nrbin
; i
++)
436 c1
+= histi1
[i
] * normfac
;
437 c2
+= histi2
[i
] * normfac
;
438 fprintf(fp
, "%g %g %g\n", (i
+ 1) * rbinw
, c1
, c2
);
442 sprintf(str
, "Solvent distribution");
443 fp
= xvgropen(opt2fn("-rc", NFILE
, fnm
), str
, "r (nm)", "molecules/nm", oenv
);
444 if (output_env_get_print_xvgr_codes(oenv
))
446 fprintf(fp
, "@ subtitle \"as a function of distance\"\n");
448 normfac
= 1.0 / (rbinw
* nf
);
449 for (i
= 0; i
< nrbin
; i
++)
451 fprintf(fp
, "%g %g\n", (i
+ 0.5) * rbinw
, histn
[i
] * normfac
);
455 do_view(oenv
, opt2fn("-o", NFILE
, fnm
), nullptr);
456 do_view(oenv
, opt2fn("-no", NFILE
, fnm
), nullptr);
457 do_view(oenv
, opt2fn("-ro", NFILE
, fnm
), "-nxy");
458 do_view(oenv
, opt2fn("-co", NFILE
, fnm
), "-nxy");