Split lines with many copyright years
[gromacs.git] / src / gromacs / linearalgebra / eigensolver.cpp
blobf0643b413bba54f1ea79ce05226b44987aaee720
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
7 * Copyright (c) 2017,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.
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.
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.
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.
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.
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.
38 #include "gmxpre.h"
40 #include "eigensolver.h"
42 #include "gromacs/linearalgebra/sparsematrix.h"
43 #include "gromacs/utility/fatalerror.h"
44 #include "gromacs/utility/real.h"
45 #include "gromacs/utility/smalloc.h"
47 #include "gmx_arpack.h"
48 #include "gmx_lapack.h"
50 void eigensolver(real* a, int n, int index_lower, int index_upper, real* eigenvalues, real* eigenvectors)
52 int* isuppz;
53 int lwork, liwork;
54 int m, iw0, info;
55 real w0, abstol;
56 int* iwork;
57 real* work;
58 real vl, vu;
59 const char* jobz;
61 if (index_lower < 0)
63 index_lower = 0;
66 if (index_upper >= n)
68 index_upper = n - 1;
71 /* Make jobz point to the character "V" if eigenvectors
72 * should be calculated, otherwise "N" (only eigenvalues).
74 jobz = (eigenvectors != nullptr) ? "V" : "N";
76 /* allocate lapack stuff */
77 snew(isuppz, 2 * n);
78 vl = vu = 0;
80 /* First time we ask the routine how much workspace it needs */
81 lwork = -1;
82 liwork = -1;
83 abstol = 0;
85 /* Convert indices to fortran standard */
86 index_lower++;
87 index_upper++;
89 /* Call LAPACK routine using fortran interface. Note that we use upper storage,
90 * but this corresponds to lower storage ("L") in Fortran.
92 #if GMX_DOUBLE
93 F77_FUNC(dsyevr, DSYEVR)
94 (jobz, "I", "L", &n, a, &n, &vl, &vu, &index_lower, &index_upper, &abstol, &m, eigenvalues,
95 eigenvectors, &n, isuppz, &w0, &lwork, &iw0, &liwork, &info);
96 #else
97 F77_FUNC(ssyevr, SSYEVR)
98 (jobz, "I", "L", &n, a, &n, &vl, &vu, &index_lower, &index_upper, &abstol, &m, eigenvalues,
99 eigenvectors, &n, isuppz, &w0, &lwork, &iw0, &liwork, &info);
100 #endif
102 if (info != 0)
104 sfree(isuppz);
105 gmx_fatal(FARGS, "Internal errror in LAPACK diagonalization.");
108 lwork = static_cast<int>(w0);
109 liwork = iw0;
111 snew(work, lwork);
112 snew(iwork, liwork);
114 abstol = 0;
116 #if GMX_DOUBLE
117 F77_FUNC(dsyevr, DSYEVR)
118 (jobz, "I", "L", &n, a, &n, &vl, &vu, &index_lower, &index_upper, &abstol, &m, eigenvalues,
119 eigenvectors, &n, isuppz, work, &lwork, iwork, &liwork, &info);
120 #else
121 F77_FUNC(ssyevr, SSYEVR)
122 (jobz, "I", "L", &n, a, &n, &vl, &vu, &index_lower, &index_upper, &abstol, &m, eigenvalues,
123 eigenvectors, &n, isuppz, work, &lwork, iwork, &liwork, &info);
124 #endif
126 sfree(isuppz);
127 sfree(work);
128 sfree(iwork);
130 if (info != 0)
132 gmx_fatal(FARGS, "Internal errror in LAPACK diagonalization.");
137 #ifdef GMX_MPI_NOT
138 void sparse_parallel_eigensolver(gmx_sparsematrix_t* A, int neig, real* eigenvalues, real* eigenvectors, int maxiter)
140 int iwork[80];
141 int iparam[11];
142 int ipntr[11];
143 real* resid;
144 real* workd;
145 real* workl;
146 real* v;
147 int n;
148 int ido, info, lworkl, i, ncv, dovec;
149 real abstol;
150 int* select;
151 int iter;
152 int nnodes, rank;
154 MPI_Comm_size(MPI_COMM_WORLD, &nnodes);
155 MPI_Comm_rank(MPI_COMM_WORLD, &rank);
157 if (eigenvectors != NULL)
159 dovec = 1;
161 else
163 dovec = 0;
166 n = A->nrow;
167 ncv = 2 * neig;
169 if (ncv > n)
171 ncv = n;
174 for (i = 0; i < 11; i++)
176 iparam[i] = ipntr[i] = 0;
179 iparam[0] = 1; /* Don't use explicit shifts */
180 iparam[2] = maxiter; /* Max number of iterations */
181 iparam[6] = 1; /* Standard symmetric eigenproblem */
183 lworkl = ncv * (8 + ncv);
184 snew(resid, n);
185 snew(workd, (3 * n + 4));
186 snew(workl, lworkl);
187 snew(select, ncv);
188 snew(v, n * ncv);
190 /* Use machine tolerance - roughly 1e-16 in double precision */
191 abstol = 0;
193 ido = info = 0;
194 fprintf(stderr, "Calculation Ritz values and Lanczos vectors, max %d iterations...\n", maxiter);
196 iter = 1;
199 # if GMX_DOUBLE
200 F77_FUNC(pdsaupd, PDSAUPD)
201 (&ido, "I", &n, "SA", &neig, &abstol, resid, &ncv, v, &n, iparam, ipntr, workd, iwork,
202 workl, &lworkl, &info);
203 # else
204 F77_FUNC(pssaupd, PSSAUPD)
205 (&ido, "I", &n, "SA", &neig, &abstol, resid, &ncv, v, &n, iparam, ipntr, workd, iwork,
206 workl, &lworkl, &info);
207 # endif
208 if (ido == -1 || ido == 1)
210 gmx_sparsematrix_vector_multiply(A, workd + ipntr[0] - 1, workd + ipntr[1] - 1);
213 fprintf(stderr, "\rIteration %4d: %3d out of %3d Ritz values converged.", iter++, iparam[4], neig);
214 fflush(stderr);
215 } while (info == 0 && (ido == -1 || ido == 1));
217 fprintf(stderr, "\n");
218 if (info == 1)
220 gmx_fatal(FARGS,
221 "Maximum number of iterations (%d) reached in Arnoldi\n"
222 "diagonalization, but only %d of %d eigenvectors converged.\n",
223 maxiter, iparam[4], neig);
225 else if (info != 0)
227 gmx_fatal(FARGS, "Unspecified error from Arnoldi diagonalization:%d\n", info);
230 info = 0;
231 /* Extract eigenvalues and vectors from data */
232 fprintf(stderr, "Calculating eigenvalues and eigenvectors...\n");
234 # if GMX_DOUBLE
235 F77_FUNC(pdseupd, PDSEUPD)
236 (&dovec, "A", select, eigenvalues, eigenvectors, &n, NULL, "I", &n, "SA", &neig, &abstol, resid,
237 &ncv, v, &n, iparam, ipntr, workd, workl, &lworkl, &info);
238 # else
239 F77_FUNC(psseupd, PSSEUPD)
240 (&dovec, "A", select, eigenvalues, eigenvectors, &n, NULL, "I", &n, "SA", &neig, &abstol, resid,
241 &ncv, v, &n, iparam, ipntr, workd, workl, &lworkl, &info);
242 # endif
244 sfree(v);
245 sfree(resid);
246 sfree(workd);
247 sfree(workl);
248 sfree(select);
250 #endif
253 void sparse_eigensolver(gmx_sparsematrix_t* A, int neig, real* eigenvalues, real* eigenvectors, int maxiter)
255 int iwork[80];
256 int iparam[11];
257 int ipntr[11];
258 real* resid;
259 real* workd;
260 real* workl;
261 real* v;
262 int n;
263 int ido, info, lworkl, i, ncv, dovec;
264 real abstol;
265 int* select;
266 int iter;
268 #ifdef GMX_MPI_NOT
269 MPI_Comm_size(MPI_COMM_WORLD, &n);
270 if (n > 1)
272 sparse_parallel_eigensolver(A, neig, eigenvalues, eigenvectors, maxiter);
273 return;
275 #endif
277 if (eigenvectors != nullptr)
279 dovec = 1;
281 else
283 dovec = 0;
286 n = A->nrow;
287 ncv = 2 * neig;
289 if (ncv > n)
291 ncv = n;
294 for (i = 0; i < 11; i++)
296 iparam[i] = ipntr[i] = 0;
299 iparam[0] = 1; /* Don't use explicit shifts */
300 iparam[2] = maxiter; /* Max number of iterations */
301 iparam[6] = 1; /* Standard symmetric eigenproblem */
303 lworkl = ncv * (8 + ncv);
304 snew(resid, n);
305 snew(workd, (3 * n + 4));
306 snew(workl, lworkl);
307 snew(select, ncv);
308 snew(v, n * ncv);
310 /* Use machine tolerance - roughly 1e-16 in double precision */
311 abstol = 0;
313 ido = info = 0;
314 fprintf(stderr, "Calculation Ritz values and Lanczos vectors, max %d iterations...\n", maxiter);
316 iter = 1;
319 #if GMX_DOUBLE
320 F77_FUNC(dsaupd, DSAUPD)
321 (&ido, "I", &n, "SA", &neig, &abstol, resid, &ncv, v, &n, iparam, ipntr, workd, iwork,
322 workl, &lworkl, &info);
323 #else
324 F77_FUNC(ssaupd, SSAUPD)
325 (&ido, "I", &n, "SA", &neig, &abstol, resid, &ncv, v, &n, iparam, ipntr, workd, iwork,
326 workl, &lworkl, &info);
327 #endif
328 if (ido == -1 || ido == 1)
330 gmx_sparsematrix_vector_multiply(A, workd + ipntr[0] - 1, workd + ipntr[1] - 1);
333 fprintf(stderr, "\rIteration %4d: %3d out of %3d Ritz values converged.", iter++, iparam[4], neig);
334 fflush(stderr);
335 } while (info == 0 && (ido == -1 || ido == 1));
337 fprintf(stderr, "\n");
338 if (info == 1)
340 gmx_fatal(FARGS,
341 "Maximum number of iterations (%d) reached in Arnoldi\n"
342 "diagonalization, but only %d of %d eigenvectors converged.\n",
343 maxiter, iparam[4], neig);
345 else if (info != 0)
347 gmx_fatal(FARGS, "Unspecified error from Arnoldi diagonalization:%d\n", info);
350 info = 0;
351 /* Extract eigenvalues and vectors from data */
352 fprintf(stderr, "Calculating eigenvalues and eigenvectors...\n");
354 #if GMX_DOUBLE
355 F77_FUNC(dseupd, DSEUPD)
356 (&dovec, "A", select, eigenvalues, eigenvectors, &n, nullptr, "I", &n, "SA", &neig, &abstol,
357 resid, &ncv, v, &n, iparam, ipntr, workd, workl, &lworkl, &info);
358 #else
359 F77_FUNC(sseupd, SSEUPD)
360 (&dovec, "A", select, eigenvalues, eigenvectors, &n, nullptr, "I", &n, "SA", &neig, &abstol,
361 resid, &ncv, v, &n, iparam, ipntr, workd, workl, &lworkl, &info);
362 #endif
364 sfree(v);
365 sfree(resid);
366 sfree(workd);
367 sfree(workl);
368 sfree(select);