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