Merge release-5-0 into master
[alexxy/gromacs.git] / src / gromacs / math / do_fit.c
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) 2012,2014, 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 "do_fit.h"
40
41 #include "gromacs/linearalgebra/nrjac.h"
42 #include "gromacs/math/utilities.h"
43 #include "gromacs/math/vec.h"
44 #include "gromacs/utility/fatalerror.h"
45 #include "gromacs/utility/smalloc.h"
46
47 real calc_similar_ind(gmx_bool bRho, int nind, atom_id *index, real mass[],
48                       rvec x[], rvec xp[])
49 {
50     int  i, j, d;
51     real m, tm, xs, xd, rs, rd;
52
53     tm = 0;
54     rs = 0;
55     rd = 0;
56     for (j = 0; j < nind; j++)
57     {
58         if (index)
59         {
60             i = index[j];
61         }
62         else
63         {
64             i = j;
65         }
66         m   = mass[i];
67         tm += m;
68         for (d = 0; d < DIM; d++)
69         {
70             xd  = x[i][d] - xp[i][d];
71             rd += m * sqr(xd);
72             if (bRho)
73             {
74                 xs  = x[i][d] + xp[i][d];
75                 rs += m * sqr(xs);
76             }
77         }
78     }
79     if (bRho)
80     {
81         return 2*sqrt(rd/rs);
82     }
83     else
84     {
85         return sqrt(rd/tm);
86     }
87 }
88
89 real rmsdev_ind(int nind, atom_id index[], real mass[], rvec x[], rvec xp[])
90 {
91     return calc_similar_ind(FALSE, nind, index, mass, x, xp);
92 }
93
94 real rmsdev(int natoms, real mass[], rvec x[], rvec xp[])
95 {
96     return calc_similar_ind(FALSE, natoms, NULL, mass, x, xp);
97 }
98
99 real rhodev_ind(int nind, atom_id index[], real mass[], rvec x[], rvec xp[])
100 {
101     return calc_similar_ind(TRUE, nind, index, mass, x, xp);
102 }
103
104 real rhodev(int natoms, real mass[], rvec x[], rvec xp[])
105 {
106     return calc_similar_ind(TRUE, natoms, NULL, mass, x, xp);
107 }
108
109 void calc_fit_R(int ndim, int natoms, real *w_rls, rvec *xp, rvec *x, matrix R)
110 {
111     int      c, r, n, j, m, i, irot, s;
112     double **omega, **om;
113     double   d[2*DIM], xnr, xpc;
114     matrix   vh, vk, u;
115     real     mn;
116     int      index;
117     real     max_d;
118
119     if (ndim != 3 && ndim != 2)
120     {
121         gmx_fatal(FARGS, "calc_fit_R called with ndim=%d instead of 3 or 2", ndim);
122     }
123
124     snew(omega, 2*ndim);
125     snew(om, 2*ndim);
126     for (i = 0; i < 2*ndim; i++)
127     {
128         snew(omega[i], 2*ndim);
129         snew(om[i], 2*ndim);
130     }
131
132     for (i = 0; i < 2*ndim; i++)
133     {
134         d[i] = 0;
135         for (j = 0; j < 2*ndim; j++)
136         {
137             omega[i][j] = 0;
138             om[i][j]    = 0;
139         }
140     }
141
142     /*calculate the matrix U*/
143     clear_mat(u);
144     for (n = 0; (n < natoms); n++)
145     {
146         if ((mn = w_rls[n]) != 0.0)
147         {
148             for (c = 0; (c < ndim); c++)
149             {
150                 xpc = xp[n][c];
151                 for (r = 0; (r < ndim); r++)
152                 {
153                     xnr      = x[n][r];
154                     u[c][r] += mn*xnr*xpc;
155                 }
156             }
157         }
158     }
159
160     /*construct omega*/
161     /*omega is symmetric -> omega==omega' */
162     for (r = 0; r < 2*ndim; r++)
163     {
164         for (c = 0; c <= r; c++)
165         {
166             if (r >= ndim && c < ndim)
167             {
168                 omega[r][c] = u[r-ndim][c];
169                 omega[c][r] = u[r-ndim][c];
170             }
171             else
172             {
173                 omega[r][c] = 0;
174                 omega[c][r] = 0;
175             }
176         }
177     }
178
179     /*determine h and k*/
180     jacobi(omega, 2*ndim, d, om, &irot);
181     /*real   **omega = input matrix a[0..n-1][0..n-1] must be symmetric
182      * int     natoms = number of rows and columns
183      * real      NULL = d[0]..d[n-1] are the eigenvalues of a[][]
184      * real       **v = v[0..n-1][0..n-1] contains the vectors in columns
185      * int      *irot = number of jacobi rotations
186      */
187
188     if (debug && irot == 0)
189     {
190         fprintf(debug, "IROT=0\n");
191     }
192
193     index = 0; /* For the compiler only */
194
195     /* Copy only the first ndim-1 eigenvectors */
196     for (j = 0; j < ndim-1; j++)
197     {
198         max_d = -1000;
199         for (i = 0; i < 2*ndim; i++)
200         {
201             if (d[i] > max_d)
202             {
203                 max_d = d[i];
204                 index = i;
205             }
206         }
207         d[index] = -10000;
208         for (i = 0; i < ndim; i++)
209         {
210             vh[j][i] = M_SQRT2*om[i][index];
211             vk[j][i] = M_SQRT2*om[i+ndim][index];
212         }
213     }
214     if (ndim == 3)
215     {
216         /* Calculate the last eigenvector as the outer-product of the first two.
217          * This insures that the conformation is not mirrored and
218          * prevents problems with completely flat reference structures.
219          */
220         cprod(vh[0], vh[1], vh[2]);
221         cprod(vk[0], vk[1], vk[2]);
222     }
223     else if (ndim == 2)
224     {
225         /* Calculate the last eigenvector from the first one */
226         vh[1][XX] = -vh[0][YY];
227         vh[1][YY] =  vh[0][XX];
228         vk[1][XX] = -vk[0][YY];
229         vk[1][YY] =  vk[0][XX];
230     }
231
232     /* determine R */
233     clear_mat(R);
234     for (r = 0; r < ndim; r++)
235     {
236         for (c = 0; c < ndim; c++)
237         {
238             for (s = 0; s < ndim; s++)
239             {
240                 R[r][c] += vk[s][r]*vh[s][c];
241             }
242         }
243     }
244     for (r = ndim; r < DIM; r++)
245     {
246         R[r][r] = 1;
247     }
248
249     for (i = 0; i < 2*ndim; i++)
250     {
251         sfree(omega[i]);
252         sfree(om[i]);
253     }
254     sfree(omega);
255     sfree(om);
256 }
257
258 void do_fit_ndim(int ndim, int natoms, real *w_rls, rvec *xp, rvec *x)
259 {
260     int    i, j, m, r, c;
261     matrix R;
262     rvec   x_old;
263
264     /* Calculate the rotation matrix R */
265     calc_fit_R(ndim, natoms, w_rls, xp, x, R);
266
267     /*rotate X*/
268     for (j = 0; j < natoms; j++)
269     {
270         for (m = 0; m < DIM; m++)
271         {
272             x_old[m] = x[j][m];
273         }
274         for (r = 0; r < DIM; r++)
275         {
276             x[j][r] = 0;
277             for (c = 0; c < DIM; c++)
278             {
279                 x[j][r] += R[r][c]*x_old[c];
280             }
281         }
282     }
283 }
284
285 void do_fit(int natoms, real *w_rls, rvec *xp, rvec *x)
286 {
287     do_fit_ndim(3, natoms, w_rls, xp, x);
288 }
289
290 void reset_x_ndim(int ndim, int ncm, const atom_id *ind_cm,
291                   int nreset, const atom_id *ind_reset,
292                   rvec x[], const real mass[])
293 {
294     int  i, m, ai;
295     rvec xcm;
296     real tm, mm;
297
298     if (ndim > DIM)
299     {
300         gmx_incons("More than 3 dimensions not supported.");
301     }
302     tm = 0.0;
303     clear_rvec(xcm);
304     if (ind_cm != NULL)
305     {
306         for (i = 0; i < ncm; i++)
307         {
308             ai = ind_cm[i];
309             mm = mass[ai];
310             for (m = 0; m < ndim; m++)
311             {
312                 xcm[m] += mm*x[ai][m];
313             }
314             tm += mm;
315         }
316     }
317     else
318     {
319         for (i = 0; i < ncm; i++)
320         {
321             mm = mass[i];
322             for (m = 0; m < ndim; m++)
323             {
324                 xcm[m] += mm*x[i][m];
325             }
326             tm += mm;
327         }
328     }
329     for (m = 0; m < ndim; m++)
330     {
331         xcm[m] /= tm;
332     }
333
334     if (ind_reset != NULL)
335     {
336         for (i = 0; i < nreset; i++)
337         {
338             rvec_dec(x[ind_reset[i]], xcm);
339         }
340     }
341     else
342     {
343         for (i = 0; i < nreset; i++)
344         {
345             rvec_dec(x[i], xcm);
346         }
347     }
348 }
349
350 void reset_x(int ncm, const atom_id *ind_cm,
351              int nreset, const atom_id *ind_reset,
352              rvec x[], const real mass[])
353 {
354     reset_x_ndim(3, ncm, ind_cm, nreset, ind_reset, x, mass);
355 }