Split lines with many copyright years
[alexxy/gromacs.git] / src / gromacs / gmxana / princ.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.
7  * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
8  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
9  * and including many others, as listed in the AUTHORS file in the
10  * top-level source directory and at http://www.gromacs.org.
11  *
12  * GROMACS is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public License
14  * as published by the Free Software Foundation; either version 2.1
15  * of the License, or (at your option) any later version.
16  *
17  * GROMACS is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with GROMACS; if not, see
24  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
26  *
27  * If you want to redistribute modifications to GROMACS, please
28  * consider that scientific software is very special. Version
29  * control is crucial - bugs must be traceable. We will be happy to
30  * consider code for inclusion in the official distribution, but
31  * derived work must not be called official GROMACS. Details are found
32  * in the README & COPYING files - if they are missing, get the
33  * official version at http://www.gromacs.org.
34  *
35  * To help us fund GROMACS development, we humbly ask that you cite
36  * the research papers on the package. Check out http://www.gromacs.org.
37  */
38 /* This file is completely threadsafe - keep it that way! */
39 #include "gmxpre.h"
40
41 #include "princ.h"
42
43 #include <cmath>
44
45 #include "gromacs/linearalgebra/nrjac.h"
46 #include "gromacs/math/functions.h"
47 #include "gromacs/math/vec.h"
48 #include "gromacs/topology/topology.h"
49 #include "gromacs/utility/smalloc.h"
50
51 #define NDIM 4
52
53 #ifdef DEBUG
54 static void m_op(matrix mat, rvec x)
55 {
56     rvec xp;
57     int  m;
58
59     for (m = 0; (m < DIM); m++)
60     {
61         xp[m] = mat[m][XX] * x[XX] + mat[m][YY] * x[YY] + mat[m][ZZ] * x[ZZ];
62     }
63     fprintf(stderr, "x    %8.3f  %8.3f  %8.3f\n", x[XX], x[YY], x[ZZ]);
64     fprintf(stderr, "xp   %8.3f  %8.3f  %8.3f\n", xp[XX], xp[YY], xp[ZZ]);
65     fprintf(stderr, "fac  %8.3f  %8.3f  %8.3f\n", xp[XX] / x[XX], xp[YY] / x[YY], xp[ZZ] / x[ZZ]);
66 }
67
68 static void ptrans(char* s, real** inten, real d[], real e[])
69 {
70     int  m;
71     real n, x, y, z;
72     for (m = 1; (m < NDIM); m++)
73     {
74         x = inten[m][1];
75         y = inten[m][2];
76         z = inten[m][3];
77         n = x * x + y * y + z * z;
78         fprintf(stderr, "%8s %8.3f %8.3f %8.3f, norm:%8.3f, d:%8.3f, e:%8.3f\n", s, x, y, z,
79                 std::sqrt(n), d[m], e[m]);
80     }
81     fprintf(stderr, "\n");
82 }
83
84 void t_trans(matrix trans, real d[], real** ev)
85 {
86     rvec x;
87     int  j;
88     for (j = 0; (j < DIM); j++)
89     {
90         x[XX] = ev[1][j + 1];
91         x[YY] = ev[2][j + 1];
92         x[ZZ] = ev[3][j + 1];
93         m_op(trans, x);
94         fprintf(stderr, "d[%d]=%g\n", j, d[j + 1]);
95     }
96 }
97 #endif
98
99 void principal_comp(int n, const int index[], t_atom atom[], rvec x[], matrix trans, rvec d)
100 {
101     int      i, j, ai, m, nrot;
102     real     mm, rx, ry, rz;
103     double **inten, dd[NDIM], tvec[NDIM], **ev;
104 #ifdef DEBUG
105     real e[NDIM];
106 #endif
107     real temp;
108
109     snew(inten, NDIM);
110     snew(ev, NDIM);
111     for (i = 0; (i < NDIM); i++)
112     {
113         snew(inten[i], NDIM);
114         snew(ev[i], NDIM);
115         dd[i] = 0.0;
116 #ifdef DEBUG
117         e[i] = 0.0;
118 #endif
119     }
120
121     for (i = 0; (i < NDIM); i++)
122     {
123         for (m = 0; (m < NDIM); m++)
124         {
125             inten[i][m] = 0;
126         }
127     }
128     for (i = 0; (i < n); i++)
129     {
130         ai = index[i];
131         mm = atom[ai].m;
132         rx = x[ai][XX];
133         ry = x[ai][YY];
134         rz = x[ai][ZZ];
135         inten[0][0] += mm * (gmx::square(ry) + gmx::square(rz));
136         inten[1][1] += mm * (gmx::square(rx) + gmx::square(rz));
137         inten[2][2] += mm * (gmx::square(rx) + gmx::square(ry));
138         inten[1][0] -= mm * (ry * rx);
139         inten[2][0] -= mm * (rx * rz);
140         inten[2][1] -= mm * (rz * ry);
141     }
142     inten[0][1] = inten[1][0];
143     inten[0][2] = inten[2][0];
144     inten[1][2] = inten[2][1];
145 #ifdef DEBUG
146     ptrans("initial", inten, dd, e);
147 #endif
148
149     for (i = 0; (i < DIM); i++)
150     {
151         for (m = 0; (m < DIM); m++)
152         {
153             trans[i][m] = inten[i][m];
154         }
155     }
156
157     /* Call numerical recipe routines */
158     jacobi(inten, 3, dd, ev, &nrot);
159 #ifdef DEBUG
160     ptrans("jacobi", ev, dd, e);
161 #endif
162
163     /* Sort eigenvalues in ascending order */
164 #define SWAPPER(i)                               \
165     if (std::abs(dd[(i) + 1]) < std::abs(dd[i])) \
166     {                                            \
167         temp = dd[i];                            \
168         for (j = 0; (j < NDIM); j++)             \
169         {                                        \
170             tvec[j] = ev[j][i];                  \
171         }                                        \
172         dd[i] = dd[(i) + 1];                     \
173         for (j = 0; (j < NDIM); j++)             \
174         {                                        \
175             ev[j][i] = ev[j][(i) + 1];           \
176         }                                        \
177         dd[(i) + 1] = temp;                      \
178         for (j = 0; (j < NDIM); j++)             \
179         {                                        \
180             ev[j][(i) + 1] = tvec[j];            \
181         }                                        \
182     }
183     SWAPPER(0)
184     SWAPPER(1)
185     SWAPPER(0)
186 #ifdef DEBUG
187     ptrans("swap", ev, dd, e);
188     t_trans(trans, dd, ev);
189 #endif
190
191     for (i = 0; (i < DIM); i++)
192     {
193         d[i] = dd[i];
194         for (m = 0; (m < DIM); m++)
195         {
196             trans[i][m] = ev[m][i];
197         }
198     }
199
200     for (i = 0; (i < NDIM); i++)
201     {
202         sfree(inten[i]);
203         sfree(ev[i]);
204     }
205     sfree(inten);
206     sfree(ev);
207 }
208
209 void rotate_atoms(int gnx, const int* index, rvec x[], matrix trans)
210 {
211     real xt, yt, zt;
212     int  i, ii;
213
214     for (i = 0; (i < gnx); i++)
215     {
216         ii        = index ? index[i] : i;
217         xt        = x[ii][XX];
218         yt        = x[ii][YY];
219         zt        = x[ii][ZZ];
220         x[ii][XX] = trans[XX][XX] * xt + trans[XX][YY] * yt + trans[XX][ZZ] * zt;
221         x[ii][YY] = trans[YY][XX] * xt + trans[YY][YY] * yt + trans[YY][ZZ] * zt;
222         x[ii][ZZ] = trans[ZZ][XX] * xt + trans[ZZ][YY] * yt + trans[ZZ][ZZ] * zt;
223     }
224 }
225
226 real calc_xcm(const rvec x[], int gnx, const int* index, const t_atom* atom, rvec xcm, gmx_bool bQ)
227 {
228     int  i, ii, m;
229     real m0, tm;
230
231     clear_rvec(xcm);
232     tm = 0;
233     for (i = 0; (i < gnx); i++)
234     {
235         ii = index ? index[i] : i;
236         if (atom)
237         {
238             if (bQ)
239             {
240                 m0 = std::abs(atom[ii].q);
241             }
242             else
243             {
244                 m0 = atom[ii].m;
245             }
246         }
247         else
248         {
249             m0 = 1;
250         }
251         tm += m0;
252         for (m = 0; (m < DIM); m++)
253         {
254             xcm[m] += m0 * x[ii][m];
255         }
256     }
257     for (m = 0; (m < DIM); m++)
258     {
259         xcm[m] /= tm;
260     }
261
262     return tm;
263 }
264
265 real sub_xcm(rvec x[], int gnx, const int* index, const t_atom atom[], rvec xcm, gmx_bool bQ)
266 {
267     int  i, ii;
268     real tm;
269
270     tm = calc_xcm(x, gnx, index, atom, xcm, bQ);
271     for (i = 0; (i < gnx); i++)
272     {
273         ii = index ? index[i] : i;
274         rvec_dec(x[ii], xcm);
275     }
276     return tm;
277 }
278
279 void add_xcm(rvec x[], int gnx, const int* index, rvec xcm)
280 {
281     int i, ii;
282
283     for (i = 0; (i < gnx); i++)
284     {
285         ii = index ? index[i] : i;
286         rvec_inc(x[ii], xcm);
287     }
288 }
289
290 void orient_princ(const t_atoms* atoms, int isize, const int* index, int natoms, rvec x[], rvec* v, rvec d)
291 {
292     int    i, m;
293     rvec   xcm, prcomp;
294     matrix trans;
295
296     calc_xcm(x, isize, index, atoms->atom, xcm, FALSE);
297     for (i = 0; i < natoms; i++)
298     {
299         rvec_dec(x[i], xcm);
300     }
301     principal_comp(isize, index, atoms->atom, x, trans, prcomp);
302     if (d)
303     {
304         copy_rvec(prcomp, d);
305     }
306
307     /* Check whether this trans matrix mirrors the molecule */
308     if (det(trans) < 0)
309     {
310         for (m = 0; (m < DIM); m++)
311         {
312             trans[ZZ][m] = -trans[ZZ][m];
313         }
314     }
315     rotate_atoms(natoms, nullptr, x, trans);
316     if (v)
317     {
318         rotate_atoms(natoms, nullptr, v, trans);
319     }
320
321     for (i = 0; i < natoms; i++)
322     {
323         rvec_inc(x[i], xcm);
324     }
325 }