Apply clang-format to source tree
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_sorient.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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,2018,2019, by the GROMACS development team, led by
7  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8  * and including many others, as listed in the AUTHORS file in the
9  * top-level source directory and at http://www.gromacs.org.
10  *
11  * GROMACS is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public License
13  * as published by the Free Software Foundation; either version 2.1
14  * of the License, or (at your option) any later version.
15  *
16  * GROMACS is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with GROMACS; if not, see
23  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
25  *
26  * If you want to redistribute modifications to GROMACS, please
27  * consider that scientific software is very special. Version
28  * control is crucial - bugs must be traceable. We will be happy to
29  * consider code for inclusion in the official distribution, but
30  * derived work must not be called official GROMACS. Details are found
31  * in the README & COPYING files - if they are missing, get the
32  * official version at http://www.gromacs.org.
33  *
34  * To help us fund GROMACS development, we humbly ask that you cite
35  * the research papers on the package. Check out http://www.gromacs.org.
36  */
37 #include "gmxpre.h"
38
39 #include <cmath>
40
41 #include "gromacs/commandline/pargs.h"
42 #include "gromacs/commandline/viewit.h"
43 #include "gromacs/fileio/confio.h"
44 #include "gromacs/fileio/trxio.h"
45 #include "gromacs/fileio/xvgr.h"
46 #include "gromacs/gmxana/gmx_ana.h"
47 #include "gromacs/gmxana/gstat.h"
48 #include "gromacs/math/functions.h"
49 #include "gromacs/math/vec.h"
50 #include "gromacs/pbcutil/pbc.h"
51 #include "gromacs/pbcutil/rmpbc.h"
52 #include "gromacs/topology/index.h"
53 #include "gromacs/topology/topology.h"
54 #include "gromacs/utility/arraysize.h"
55 #include "gromacs/utility/cstringutil.h"
56 #include "gromacs/utility/fatalerror.h"
57 #include "gromacs/utility/smalloc.h"
58
59 static void calc_com_pbc(int nrefat, t_topology* top, rvec x[], t_pbc* pbc, const int index[], rvec xref, gmx_bool bPBC)
60 {
61     const real tol = 1e-4;
62     gmx_bool   bChanged;
63     int        m, j, ai, iter;
64     real       mass, mtot;
65     rvec       dx, xtest;
66
67     /* First simple calculation */
68     clear_rvec(xref);
69     mtot = 0;
70     for (m = 0; (m < nrefat); m++)
71     {
72         ai   = index[m];
73         mass = top->atoms.atom[ai].m;
74         for (j = 0; (j < DIM); j++)
75         {
76             xref[j] += mass * x[ai][j];
77         }
78         mtot += mass;
79     }
80     svmul(1 / mtot, xref, xref);
81     /* Now check if any atom is more than half the box from the COM */
82     if (bPBC)
83     {
84         iter = 0;
85         do
86         {
87             bChanged = FALSE;
88             for (m = 0; (m < nrefat); m++)
89             {
90                 ai   = index[m];
91                 mass = top->atoms.atom[ai].m / mtot;
92                 pbc_dx(pbc, x[ai], xref, dx);
93                 rvec_add(xref, dx, xtest);
94                 for (j = 0; (j < DIM); j++)
95                 {
96                     if (std::abs(xtest[j] - x[ai][j]) > tol)
97                     {
98                         /* Here we have used the wrong image for contributing to the COM */
99                         xref[j] += mass * (xtest[j] - x[ai][j]);
100                         x[ai][j] = xtest[j];
101                         bChanged = TRUE;
102                     }
103                 }
104             }
105             if (bChanged)
106             {
107                 printf("COM: %8.3f  %8.3f  %8.3f  iter = %d\n", xref[XX], xref[YY], xref[ZZ], iter);
108             }
109             iter++;
110         } while (bChanged);
111     }
112 }
113
114 int gmx_sorient(int argc, char* argv[])
115 {
116     t_topology   top;
117     int          ePBC = -1;
118     t_trxstatus* status;
119     int          natoms;
120     real         t;
121     rvec *       xtop, *x;
122     matrix       box;
123
124     FILE*       fp;
125     int         i, p, sa0, sa1, sa2, n, ntot, nf, m, *hist1, *hist2, *histn, nbin1, nbin2, nrbin;
126     real *      histi1, *histi2, invbw, invrbw;
127     double      sum1, sum2;
128     int *       isize, nrefgrp, nrefat;
129     int**       index;
130     char**      grpname;
131     real        inp, outp, nav, normfac, rmin2, rmax2, rcut, rcut2, r2, r;
132     real        c1, c2;
133     char        str[STRLEN];
134     gmx_bool    bTPS;
135     rvec        xref, dx, dxh1, dxh2, outer;
136     gmx_rmpbc_t gpbc = nullptr;
137     t_pbc       pbc;
138     const char* legr[] = { "<cos(\\8q\\4\\s1\\N)>", "<3cos\\S2\\N(\\8q\\4\\s2\\N)-1>" };
139     const char* legc[] = { "cos(\\8q\\4\\s1\\N)", "3cos\\S2\\N(\\8q\\4\\s2\\N)-1" };
140
141     const char* desc[] = {
142         "[THISMODULE] analyzes solvent orientation around solutes.",
143         "It calculates two angles between the vector from one or more",
144         "reference positions to the first atom of each solvent molecule:",
145         "",
146         " * [GRK]theta[grk][SUB]1[sub]: the angle with the vector from the first atom of "
147         "the solvent",
148         "   molecule to the midpoint between atoms 2 and 3.",
149         " * [GRK]theta[grk][SUB]2[sub]: the angle with the normal of the solvent plane, "
150         "defined by the",
151         "   same three atoms, or, when the option [TT]-v23[tt] is set, ",
152         "   the angle with the vector between atoms 2 and 3.",
153         "",
154         "The reference can be a set of atoms or",
155         "the center of mass of a set of atoms. The group of solvent atoms should",
156         "consist of 3 atoms per solvent molecule.",
157         "Only solvent molecules between [TT]-rmin[tt] and [TT]-rmax[tt] are",
158         "considered for [TT]-o[tt] and [TT]-no[tt] each frame.[PAR]",
159         "[TT]-o[tt]: distribution of [MATH][COS][GRK]theta[grk][SUB]1[sub][cos][math] for "
160         "rmin<=r<=rmax.[PAR]",
161         "[TT]-no[tt]: distribution of [MATH][COS][GRK]theta[grk][SUB]2[sub][cos][math] for "
162         "rmin<=r<=rmax.[PAR]",
163         "[TT]-ro[tt]: [MATH][CHEVRON][COS][GRK]theta[grk][SUB]1[sub][cos][chevron][math] "
164         "and [MATH][CHEVRON]3[COS]^2[GRK]theta[grk][SUB]2[sub][cos]-1[chevron][math] as a "
165         "function of the",
166         "distance.[PAR]",
167         "[TT]-co[tt]: the sum over all solvent molecules within distance r",
168         "of [MATH][COS][GRK]theta[grk][SUB]1[sub][cos][math] and "
169         "[MATH]3[COS]^2([GRK]theta[grk][SUB]2[sub])-1[cos][math] as a function of r.[PAR]",
170         "[TT]-rc[tt]: the distribution of the solvent molecules as a function of r"
171     };
172
173     gmx_output_env_t* oenv;
174     static gmx_bool   bCom = FALSE, bVec23 = FALSE, bPBC = FALSE;
175     static real       rmin = 0.0, rmax = 0.5, binwidth = 0.02, rbinw = 0.02;
176     t_pargs           pa[] = {
177         { "-com", FALSE, etBOOL, { &bCom }, "Use the center of mass as the reference position" },
178         { "-v23", FALSE, etBOOL, { &bVec23 }, "Use the vector between atoms 2 and 3" },
179         { "-rmin", FALSE, etREAL, { &rmin }, "Minimum distance (nm)" },
180         { "-rmax", FALSE, etREAL, { &rmax }, "Maximum distance (nm)" },
181         { "-cbin", FALSE, etREAL, { &binwidth }, "Binwidth for the cosine" },
182         { "-rbin", FALSE, etREAL, { &rbinw }, "Binwidth for r (nm)" },
183         { "-pbc",
184           FALSE,
185           etBOOL,
186           { &bPBC },
187           "Check PBC for the center of mass calculation. Only necessary when your reference group "
188           "consists of several molecules." }
189     };
190
191     t_filenm fnm[] = { { efTRX, nullptr, nullptr, ffREAD },  { efTPS, nullptr, nullptr, ffREAD },
192                        { efNDX, nullptr, nullptr, ffOPTRD }, { efXVG, nullptr, "sori", ffWRITE },
193                        { efXVG, "-no", "snor", ffWRITE },    { efXVG, "-ro", "sord", ffWRITE },
194                        { efXVG, "-co", "scum", ffWRITE },    { efXVG, "-rc", "scount", ffWRITE } };
195 #define NFILE asize(fnm)
196
197     if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW, NFILE, fnm, asize(pa), pa,
198                            asize(desc), desc, 0, nullptr, &oenv))
199     {
200         return 0;
201     }
202
203     bTPS = (opt2bSet("-s", NFILE, fnm) || !opt2bSet("-n", NFILE, fnm) || bCom);
204     if (bTPS)
205     {
206         read_tps_conf(ftp2fn(efTPS, NFILE, fnm), &top, &ePBC, &xtop, nullptr, box, bCom);
207     }
208
209     /* get index groups */
210     printf("Select a group of reference particles and a solvent group:\n");
211     snew(grpname, 2);
212     snew(index, 2);
213     snew(isize, 2);
214     if (bTPS)
215     {
216         get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 2, isize, index, grpname);
217     }
218     else
219     {
220         get_index(nullptr, ftp2fn(efNDX, NFILE, fnm), 2, isize, index, grpname);
221     }
222
223     if (bCom)
224     {
225         nrefgrp = 1;
226         nrefat  = isize[0];
227     }
228     else
229     {
230         nrefgrp = isize[0];
231         nrefat  = 1;
232     }
233
234     if (isize[1] % 3)
235     {
236         gmx_fatal(FARGS, "The number of solvent atoms (%d) is not a multiple of 3", isize[1]);
237     }
238
239     /* initialize reading trajectory:                         */
240     natoms = read_first_x(oenv, &status, ftp2fn(efTRX, NFILE, fnm), &t, &x, box);
241
242     rmin2 = gmx::square(rmin);
243     rmax2 = gmx::square(rmax);
244     rcut  = 0.99 * std::sqrt(max_cutoff2(guess_ePBC(box), box));
245     if (rcut == 0)
246     {
247         rcut = 10 * rmax;
248     }
249     rcut2 = gmx::square(rcut);
250
251     invbw = 1 / binwidth;
252     nbin1 = 1 + gmx::roundToInt(2 * invbw);
253     nbin2 = 1 + gmx::roundToInt(invbw);
254
255     invrbw = 1 / rbinw;
256
257     snew(hist1, nbin1);
258     snew(hist2, nbin2);
259     nrbin = 1 + static_cast<int>(rcut / rbinw);
260     if (nrbin == 0)
261     {
262         nrbin = 1;
263     }
264     snew(histi1, nrbin);
265     snew(histi2, nrbin);
266     snew(histn, nrbin);
267
268     ntot = 0;
269     nf   = 0;
270     sum1 = 0;
271     sum2 = 0;
272
273     if (bTPS)
274     {
275         /* make molecules whole again */
276         gpbc = gmx_rmpbc_init(&top.idef, ePBC, natoms);
277     }
278     /* start analysis of trajectory */
279     do
280     {
281         if (bTPS)
282         {
283             /* make molecules whole again */
284             gmx_rmpbc(gpbc, natoms, box, x);
285         }
286
287         set_pbc(&pbc, ePBC, box);
288         n   = 0;
289         inp = 0;
290         for (p = 0; (p < nrefgrp); p++)
291         {
292             if (bCom)
293             {
294                 calc_com_pbc(nrefat, &top, x, &pbc, index[0], xref, bPBC);
295             }
296             else
297             {
298                 copy_rvec(x[index[0][p]], xref);
299             }
300
301             for (m = 0; m < isize[1]; m += 3)
302             {
303                 sa0 = index[1][m];
304                 sa1 = index[1][m + 1];
305                 sa2 = index[1][m + 2];
306                 range_check(sa0, 0, natoms);
307                 range_check(sa1, 0, natoms);
308                 range_check(sa2, 0, natoms);
309                 pbc_dx(&pbc, x[sa0], xref, dx);
310                 r2 = norm2(dx);
311                 if (r2 < rcut2)
312                 {
313                     r = std::sqrt(r2);
314                     if (!bVec23)
315                     {
316                         /* Determine the normal to the plain */
317                         rvec_sub(x[sa1], x[sa0], dxh1);
318                         rvec_sub(x[sa2], x[sa0], dxh2);
319                         rvec_inc(dxh1, dxh2);
320                         svmul(1 / r, dx, dx);
321                         unitv(dxh1, dxh1);
322                         inp = iprod(dx, dxh1);
323                         cprod(dxh1, dxh2, outer);
324                         unitv(outer, outer);
325                         outp = iprod(dx, outer);
326                     }
327                     else
328                     {
329                         /* Use the vector between the 2nd and 3rd atom */
330                         rvec_sub(x[sa2], x[sa1], dxh2);
331                         unitv(dxh2, dxh2);
332                         outp = iprod(dx, dxh2) / r;
333                     }
334                     {
335                         int ii = static_cast<int>(invrbw * r);
336                         range_check(ii, 0, nrbin);
337                         histi1[ii] += inp;
338                         histi2[ii] += 3 * gmx::square(outp) - 1;
339                         histn[ii]++;
340                     }
341                     if ((r2 >= rmin2) && (r2 < rmax2))
342                     {
343                         int ii1 = static_cast<int>(invbw * (inp + 1));
344                         int ii2 = static_cast<int>(invbw * std::abs(outp));
345
346                         range_check(ii1, 0, nbin1);
347                         range_check(ii2, 0, nbin2);
348                         hist1[ii1]++;
349                         hist2[ii2]++;
350                         sum1 += inp;
351                         sum2 += outp;
352                         n++;
353                     }
354                 }
355             }
356         }
357         ntot += n;
358         nf++;
359
360     } while (read_next_x(oenv, status, &t, x, box));
361
362     /* clean up */
363     sfree(x);
364     close_trx(status);
365     gmx_rmpbc_done(gpbc);
366
367     /* Add the bin for the exact maximum to the previous bin */
368     hist1[nbin1 - 1] += hist1[nbin1];
369     hist2[nbin2 - 1] += hist2[nbin2];
370
371     nav     = static_cast<real>(ntot) / (nrefgrp * nf);
372     normfac = invbw / ntot;
373
374     fprintf(stderr, "Average nr of molecules between %g and %g nm: %.1f\n", rmin, rmax, nav);
375     if (ntot > 0)
376     {
377         sum1 /= ntot;
378         sum2 /= ntot;
379         fprintf(stderr, "Average cos(theta1)     between %g and %g nm: %6.3f\n", rmin, rmax, sum1);
380         fprintf(stderr, "Average 3cos2(theta2)-1 between %g and %g nm: %6.3f\n", rmin, rmax, sum2);
381     }
382
383     sprintf(str, "Solvent orientation between %g and %g nm", rmin, rmax);
384     fp = xvgropen(opt2fn("-o", NFILE, fnm), str, "cos(\\8q\\4\\s1\\N)", "", oenv);
385     if (output_env_get_print_xvgr_codes(oenv))
386     {
387         fprintf(fp, "@ subtitle \"average shell size %.1f molecules\"\n", nav);
388     }
389     for (i = 0; i < nbin1; i++)
390     {
391         fprintf(fp, "%g %g\n", (i + 0.5) * binwidth - 1, 2 * normfac * hist1[i]);
392     }
393     xvgrclose(fp);
394
395     sprintf(str, "Solvent normal orientation between %g and %g nm", rmin, rmax);
396     fp = xvgropen(opt2fn("-no", NFILE, fnm), str, "cos(\\8q\\4\\s2\\N)", "", oenv);
397     if (output_env_get_print_xvgr_codes(oenv))
398     {
399         fprintf(fp, "@ subtitle \"average shell size %.1f molecules\"\n", nav);
400     }
401     for (i = 0; i < nbin2; i++)
402     {
403         fprintf(fp, "%g %g\n", (i + 0.5) * binwidth, normfac * hist2[i]);
404     }
405     xvgrclose(fp);
406
407
408     sprintf(str, "Solvent orientation");
409     fp = xvgropen(opt2fn("-ro", NFILE, fnm), str, "r (nm)", "", oenv);
410     if (output_env_get_print_xvgr_codes(oenv))
411     {
412         fprintf(fp, "@ subtitle \"as a function of distance\"\n");
413     }
414     xvgr_legend(fp, 2, legr, oenv);
415     for (i = 0; i < nrbin; i++)
416     {
417         fprintf(fp, "%g %g %g\n", (i + 0.5) * rbinw, histn[i] ? histi1[i] / histn[i] : 0,
418                 histn[i] ? histi2[i] / histn[i] : 0);
419     }
420     xvgrclose(fp);
421
422     sprintf(str, "Cumulative solvent orientation");
423     fp = xvgropen(opt2fn("-co", NFILE, fnm), str, "r (nm)", "", oenv);
424     if (output_env_get_print_xvgr_codes(oenv))
425     {
426         fprintf(fp, "@ subtitle \"as a function of distance\"\n");
427     }
428     xvgr_legend(fp, 2, legc, oenv);
429     normfac = 1.0 / (nrefgrp * nf);
430     c1      = 0;
431     c2      = 0;
432     fprintf(fp, "%g %g %g\n", 0.0, c1, c2);
433     for (i = 0; i < nrbin; i++)
434     {
435         c1 += histi1[i] * normfac;
436         c2 += histi2[i] * normfac;
437         fprintf(fp, "%g %g %g\n", (i + 1) * rbinw, c1, c2);
438     }
439     xvgrclose(fp);
440
441     sprintf(str, "Solvent distribution");
442     fp = xvgropen(opt2fn("-rc", NFILE, fnm), str, "r (nm)", "molecules/nm", oenv);
443     if (output_env_get_print_xvgr_codes(oenv))
444     {
445         fprintf(fp, "@ subtitle \"as a function of distance\"\n");
446     }
447     normfac = 1.0 / (rbinw * nf);
448     for (i = 0; i < nrbin; i++)
449     {
450         fprintf(fp, "%g %g\n", (i + 0.5) * rbinw, histn[i] * normfac);
451     }
452     xvgrclose(fp);
453
454     do_view(oenv, opt2fn("-o", NFILE, fnm), nullptr);
455     do_view(oenv, opt2fn("-no", NFILE, fnm), nullptr);
456     do_view(oenv, opt2fn("-ro", NFILE, fnm), "-nxy");
457     do_view(oenv, opt2fn("-co", NFILE, fnm), "-nxy");
458
459     return 0;
460 }