Merge branch release-4-6
[alexxy/gromacs.git] / src / gromacs / gmxana / gmx_sorient.c
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  *                        VERSION 3.2.0
10  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12  * Copyright (c) 2001-2004, The GROMACS development team,
13  * check out http://www.gromacs.org for more information.
14
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * If you want to redistribute modifications, please consider that
21  * scientific software is very special. Version control is crucial -
22  * bugs must be traceable. We will be happy to consider code for
23  * inclusion in the official distribution, but derived work must not
24  * be called official GROMACS. Details are found in the README & COPYING
25  * files - if they are missing, get the official version at www.gromacs.org.
26  *
27  * To help us fund GROMACS development, we humbly ask that you cite
28  * the papers on the package - you can find them in the top README file.
29  *
30  * For more info, check our website at http://www.gromacs.org
31  *
32  * And Hey:
33  * Green Red Orange Magenta Azure Cyan Skyblue
34  */
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include "macros.h"
40 #include "statutil.h"
41 #include "smalloc.h"
42 #include "gstat.h"
43 #include "vec.h"
44 #include "xvgr.h"
45 #include "pbc.h"
46 #include "index.h"
47 #include "gromacs/fileio/tpxio.h"
48 #include "gromacs/fileio/trxio.h"
49 #include "gmx_ana.h"
50
51
52 static void calc_com_pbc(int nrefat, t_topology *top, rvec x[], t_pbc *pbc,
53                          atom_id index[], rvec xref, gmx_bool bPBC)
54 {
55     const real tol = 1e-4;
56     gmx_bool   bChanged;
57     int        m, j, ai, iter;
58     real       mass, mtot;
59     rvec       dx, xtest;
60
61     /* First simple calculation */
62     clear_rvec(xref);
63     mtot = 0;
64     for (m = 0; (m < nrefat); m++)
65     {
66         ai   = index[m];
67         mass = top->atoms.atom[ai].m;
68         for (j = 0; (j < DIM); j++)
69         {
70             xref[j] += mass*x[ai][j];
71         }
72         mtot += mass;
73     }
74     svmul(1/mtot, xref, xref);
75     /* Now check if any atom is more than half the box from the COM */
76     if (bPBC)
77     {
78         iter = 0;
79         do
80         {
81             bChanged = FALSE;
82             for (m = 0; (m < nrefat); m++)
83             {
84                 ai   = index[m];
85                 mass = top->atoms.atom[ai].m/mtot;
86                 pbc_dx(pbc, x[ai], xref, dx);
87                 rvec_add(xref, dx, xtest);
88                 for (j = 0; (j < DIM); j++)
89                 {
90                     if (fabs(xtest[j]-x[ai][j]) > tol)
91                     {
92                         /* Here we have used the wrong image for contributing to the COM */
93                         xref[j] += mass*(xtest[j]-x[ai][j]);
94                         x[ai][j] = xtest[j];
95                         bChanged = TRUE;
96                     }
97                 }
98             }
99             if (bChanged)
100             {
101                 printf("COM: %8.3f  %8.3f  %8.3f  iter = %d\n", xref[XX], xref[YY], xref[ZZ], iter);
102             }
103             iter++;
104         }
105         while (bChanged);
106     }
107 }
108
109 int gmx_sorient(int argc, char *argv[])
110 {
111     t_topology      top;
112     int             ePBC = -1;
113     char            title[STRLEN];
114     t_trxstatus    *status;
115     int             natoms;
116     real            t;
117     rvec           *xtop, *x;
118     matrix          box;
119
120     FILE           *fp;
121     int             i, j, p, sa0, sa1, sa2, n, ntot, nf, m, *hist1, *hist2, *histn, nbin1, nbin2, nrbin;
122     real           *histi1, *histi2, invbw, invrbw;
123     double          sum1, sum2;
124     int            *isize, nrefgrp, nrefat;
125     atom_id       **index;
126     char          **grpname;
127     real            inp, outp, two_pi, nav, normfac, rmin2, rmax2, rcut, rcut2, r2, r, mass, mtot;
128     real            c1, c2;
129     char            str[STRLEN];
130     gmx_bool        bTPS;
131     rvec            xref, dx, dxh1, dxh2, outer;
132     gmx_rmpbc_t     gpbc = NULL;
133     t_pbc           pbc;
134     const char     *legr[] = {
135         "<cos(\\8q\\4\\s1\\N)>",
136         "<3cos\\S2\\N(\\8q\\4\\s2\\N)-1>"
137     };
138     const char     *legc[] = {
139         "cos(\\8q\\4\\s1\\N)",
140         "3cos\\S2\\N(\\8q\\4\\s2\\N)-1"
141     };
142
143     const char     *desc[] = {
144         "[TT]g_sorient[tt] analyzes solvent orientation around solutes.",
145         "It calculates two angles between the vector from one or more",
146         "reference positions to the first atom of each solvent molecule:[PAR]",
147         "[GRK]theta[grk][SUB]1[sub]: the angle with the vector from the first atom of the solvent",
148         "molecule to the midpoint between atoms 2 and 3.[BR]",
149         "[GRK]theta[grk][SUB]2[sub]: the angle with the normal of the solvent plane, defined by the",
150         "same three atoms, or, when the option [TT]-v23[tt] is set, ",
151         "the angle with the vector between atoms 2 and 3.[PAR]",
152         "The reference can be a set of atoms or",
153         "the center of mass of a set of atoms. The group of solvent atoms should",
154         "consist of 3 atoms per solvent molecule.",
155         "Only solvent molecules between [TT]-rmin[tt] and [TT]-rmax[tt] are",
156         "considered for [TT]-o[tt] and [TT]-no[tt] each frame.[PAR]",
157         "[TT]-o[tt]: distribtion of [MATH][COS][GRK]theta[grk][SUB]1[sub][cos][math] for rmin<=r<=rmax.[PAR]",
158         "[TT]-no[tt]: distribution of [MATH][COS][GRK]theta[grk][SUB]2[sub][cos][math] for rmin<=r<=rmax.[PAR]",
159         "[TT]-ro[tt]: [MATH][CHEVRON][COS][GRK]theta[grk][SUB]1[sub][cos][chevron][math] and [MATH][CHEVRON]3[COS]^2[GRK]theta[grk][SUB]2[sub][cos]-1[chevron][math] as a function of the",
160         "distance.[PAR]",
161         "[TT]-co[tt]: the sum over all solvent molecules within distance r",
162         "of [MATH][COS][GRK]theta[grk][SUB]1[sub][cos][math] and [MATH]3[COS]^2([GRK]theta[grk][SUB]2[sub])-1[cos][math] as a function of r.[PAR]",
163         "[TT]-rc[tt]: the distribution of the solvent molecules as a function of r"
164     };
165
166     output_env_t    oenv;
167     static gmx_bool bCom = FALSE, bVec23 = FALSE, bPBC = FALSE;
168     static real     rmin = 0.0, rmax = 0.5, binwidth = 0.02, rbinw = 0.02;
169     t_pargs         pa[] = {
170         { "-com",  FALSE, etBOOL,  {&bCom},
171           "Use the center of mass as the reference postion" },
172         { "-v23",  FALSE, etBOOL,  {&bVec23},
173           "Use the vector between atoms 2 and 3" },
174         { "-rmin",  FALSE, etREAL, {&rmin}, "Minimum distance (nm)" },
175         { "-rmax",  FALSE, etREAL, {&rmax}, "Maximum distance (nm)" },
176         { "-cbin",  FALSE, etREAL, {&binwidth}, "Binwidth for the cosine" },
177         { "-rbin",  FALSE, etREAL, {&rbinw}, "Binwidth for r (nm)" },
178         { "-pbc",   FALSE, etBOOL, {&bPBC}, "Check PBC for the center of mass calculation. Only necessary when your reference group consists of several molecules." }
179     };
180
181     t_filenm        fnm[] = {
182         { efTRX, NULL,  NULL,  ffREAD },
183         { efTPS, NULL,  NULL,  ffREAD },
184         { efNDX, NULL,  NULL,  ffOPTRD },
185         { efXVG, NULL,  "sori.xvg",  ffWRITE },
186         { efXVG, "-no", "snor.xvg",  ffWRITE },
187         { efXVG, "-ro", "sord.xvg",  ffWRITE },
188         { efXVG, "-co", "scum.xvg",  ffWRITE },
189         { efXVG, "-rc", "scount.xvg",  ffWRITE }
190     };
191 #define NFILE asize(fnm)
192
193     if (!parse_common_args(&argc, argv, PCA_CAN_TIME | PCA_CAN_VIEW | PCA_BE_NICE,
194                            NFILE, fnm, asize(pa), pa, asize(desc), desc, 0, NULL, &oenv))
195     {
196         return 0;
197     }
198
199     two_pi = 2/M_PI;
200
201     bTPS = (opt2bSet("-s", NFILE, fnm) || !opt2bSet("-n", NFILE, fnm) || bCom);
202     if (bTPS)
203     {
204         read_tps_conf(ftp2fn(efTPS, NFILE, fnm), title, &top, &ePBC, &xtop, NULL, box,
205                       bCom);
206     }
207
208     /* get index groups */
209     printf("Select a group of reference particles and a solvent group:\n");
210     snew(grpname, 2);
211     snew(index, 2);
212     snew(isize, 2);
213     if (bTPS)
214     {
215         get_index(&top.atoms, ftp2fn_null(efNDX, NFILE, fnm), 2, isize, index, grpname);
216     }
217     else
218     {
219         get_index(NULL, ftp2fn(efNDX, NFILE, fnm), 2, isize, index, grpname);
220     }
221
222     if (bCom)
223     {
224         nrefgrp = 1;
225         nrefat  = isize[0];
226     }
227     else
228     {
229         nrefgrp = isize[0];
230         nrefat  = 1;
231     }
232
233     if (isize[1] % 3)
234     {
235         gmx_fatal(FARGS, "The number of solvent atoms (%d) is not a multiple of 3",
236                   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 = sqr(rmin);
243     rmax2 = sqr(rmax);
244     rcut  = 0.99*sqrt(max_cutoff2(guess_ePBC(box), box));
245     if (rcut == 0)
246     {
247         rcut = 10*rmax;
248     }
249     rcut2 = sqr(rcut);
250
251     invbw = 1/binwidth;
252     nbin1 = 1+(int)(2*invbw + 0.5);
253     nbin2 = 1+(int)(invbw + 0.5);
254
255     invrbw = 1/rbinw;
256
257     snew(hist1, nbin1);
258     snew(hist2, nbin2);
259     nrbin = 1+(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         outp = 0;
291         for (p = 0; (p < nrefgrp); p++)
292         {
293             if (bCom)
294             {
295                 calc_com_pbc(nrefat, &top, x, &pbc, index[0], xref, bPBC);
296             }
297             else
298             {
299                 copy_rvec(x[index[0][p]], xref);
300             }
301
302             for (m = 0; m < isize[1]; m += 3)
303             {
304                 sa0 = index[1][m];
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);
311                 r2  = norm2(dx);
312                 if (r2 < rcut2)
313                 {
314                     r = sqrt(r2);
315                     if (!bVec23)
316                     {
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);
322                         unitv(dxh1, dxh1);
323                         inp = iprod(dx, dxh1);
324                         cprod(dxh1, dxh2, outer);
325                         unitv(outer, outer);
326                         outp = iprod(dx, outer);
327                     }
328                     else
329                     {
330                         /* Use the vector between the 2nd and 3rd atom */
331                         rvec_sub(x[sa2], x[sa1], dxh2);
332                         unitv(dxh2, dxh2);
333                         outp = iprod(dx, dxh2)/r;
334                     }
335                     {
336                         int ii = (int)(invrbw*r);
337                         range_check(ii, 0, nrbin);
338                         histi1[ii] += inp;
339                         histi2[ii] += 3*sqr(outp) - 1;
340                         histn[ii]++;
341                     }
342                     if ((r2 >= rmin2) && (r2 < rmax2))
343                     {
344                         int ii1 = (int)(invbw*(inp + 1));
345                         int ii2 = (int)(invbw*fabs(outp));
346
347                         range_check(ii1, 0, nbin1);
348                         range_check(ii2, 0, nbin2);
349                         hist1[ii1]++;
350                         hist2[ii2]++;
351                         sum1 += inp;
352                         sum2 += outp;
353                         n++;
354                     }
355                 }
356             }
357         }
358         ntot += n;
359         nf++;
360
361     }
362     while (read_next_x(oenv, status, &t, x, box));
363
364     /* clean up */
365     sfree(x);
366     close_trj(status);
367     gmx_rmpbc_done(gpbc);
368
369     /* Add the bin for the exact maximum to the previous bin */
370     hist1[nbin1-1] += hist1[nbin1];
371     hist2[nbin2-1] += hist2[nbin2];
372
373     nav     = (real)ntot/(nrefgrp*nf);
374     normfac = invbw/ntot;
375
376     fprintf(stderr,  "Average nr of molecules between %g and %g nm: %.1f\n",
377             rmin, rmax, nav);
378     if (ntot > 0)
379     {
380         sum1 /= ntot;
381         sum2 /= ntot;
382         fprintf(stderr, "Average cos(theta1)     between %g and %g nm: %6.3f\n",
383                 rmin, rmax, sum1);
384         fprintf(stderr, "Average 3cos2(theta2)-1 between %g and %g nm: %6.3f\n",
385                 rmin, rmax, sum2);
386     }
387
388     sprintf(str, "Solvent orientation between %g and %g nm", rmin, rmax);
389     fp = xvgropen(opt2fn("-o", NFILE, fnm), str, "cos(\\8q\\4\\s1\\N)", "", oenv);
390     if (output_env_get_print_xvgr_codes(oenv))
391     {
392         fprintf(fp, "@ subtitle \"average shell size %.1f molecules\"\n", nav);
393     }
394     for (i = 0; i < nbin1; i++)
395     {
396         fprintf(fp, "%g %g\n", (i+0.5)*binwidth-1, 2*normfac*hist1[i]);
397     }
398     ffclose(fp);
399
400     sprintf(str, "Solvent normal orientation between %g and %g nm", rmin, rmax);
401     fp = xvgropen(opt2fn("-no", NFILE, fnm), str, "cos(\\8q\\4\\s2\\N)", "", oenv);
402     if (output_env_get_print_xvgr_codes(oenv))
403     {
404         fprintf(fp, "@ subtitle \"average shell size %.1f molecules\"\n", nav);
405     }
406     for (i = 0; i < nbin2; i++)
407     {
408         fprintf(fp, "%g %g\n", (i+0.5)*binwidth, normfac*hist2[i]);
409     }
410     ffclose(fp);
411
412
413     sprintf(str, "Solvent orientation");
414     fp = xvgropen(opt2fn("-ro", NFILE, fnm), str, "r (nm)", "", oenv);
415     if (output_env_get_print_xvgr_codes(oenv))
416     {
417         fprintf(fp, "@ subtitle \"as a function of distance\"\n");
418     }
419     xvgr_legend(fp, 2, legr, oenv);
420     for (i = 0; i < nrbin; i++)
421     {
422         fprintf(fp, "%g %g %g\n", (i+0.5)*rbinw,
423                 histn[i] ? histi1[i]/histn[i] : 0,
424                 histn[i] ? histi2[i]/histn[i] : 0);
425     }
426     ffclose(fp);
427
428     sprintf(str, "Cumulative solvent orientation");
429     fp = xvgropen(opt2fn("-co", NFILE, fnm), str, "r (nm)", "", oenv);
430     if (output_env_get_print_xvgr_codes(oenv))
431     {
432         fprintf(fp, "@ subtitle \"as a function of distance\"\n");
433     }
434     xvgr_legend(fp, 2, legc, oenv);
435     normfac = 1.0/(nrefgrp*nf);
436     c1      = 0;
437     c2      = 0;
438     fprintf(fp, "%g %g %g\n", 0.0, c1, c2);
439     for (i = 0; i < nrbin; i++)
440     {
441         c1 += histi1[i]*normfac;
442         c2 += histi2[i]*normfac;
443         fprintf(fp, "%g %g %g\n", (i+1)*rbinw, c1, c2);
444     }
445     ffclose(fp);
446
447     sprintf(str, "Solvent distribution");
448     fp = xvgropen(opt2fn("-rc", NFILE, fnm), str, "r (nm)", "molecules/nm", oenv);
449     if (output_env_get_print_xvgr_codes(oenv))
450     {
451         fprintf(fp, "@ subtitle \"as a function of distance\"\n");
452     }
453     normfac = 1.0/(rbinw*nf);
454     for (i = 0; i < nrbin; i++)
455     {
456         fprintf(fp, "%g %g\n", (i+0.5)*rbinw, histn[i]*normfac);
457     }
458     ffclose(fp);
459
460     do_view(oenv, opt2fn("-o", NFILE, fnm), NULL);
461     do_view(oenv, opt2fn("-no", NFILE, fnm), NULL);
462     do_view(oenv, opt2fn("-ro", NFILE, fnm), "-nxy");
463     do_view(oenv, opt2fn("-co", NFILE, fnm), "-nxy");
464
465     return 0;
466 }