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