OpenMD 3.0
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
RealSymmetricTridiagonal.hpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the appropriate papers when you publish your
33 * work. Good starting points are:
34 *
35 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
36 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
37 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
38 * [4] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
39 * [5] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
40 * [6] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
41 * [7] Lamichhane, Newman & Gezelter, J. Chem. Phys. 141, 134110 (2014).
42 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
43 */
44
45#ifndef MATH_REALSYMMETRICTRIDIAGONAL_HPP
46#define MATH_REALSYMMETRICTRIDIAGONAL_HPP
47
48#include <config.h>
49
50#include <algorithm>
51
53// for min(), max() below
54#include <cmath>
55// for abs() below
56
57namespace OpenMD {
58
59 /**
60
61 Computes eigenvalues and eigenvectors of a real (non-complex)
62 symmetric tridiagonal matrix by the QL method.
63 **/
64
65 template<typename Real>
67 /** Row and column dimension (square matrix). */
68 int n;
69
72
73 /** Array for internal storage of eigenvectors. */
75
76 // Symmetric tridiagonal QL algorithm.
77
78 void tql2() {
79 // This is derived from the Algol procedures tql2, by
80 // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
81 // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
82 // Fortran subroutine in EISPACK.
83
84 for (int i = 1; i < n; i++) {
85 e(i - 1) = e(i);
86 }
87 e(n - 1) = 0.0;
88
89 Real f = 0.0;
90 Real tst1 = 0.0;
91 Real eps = pow(2.0, -52.0);
92 for (int l = 0; l < n; l++) {
93 // Find small subdiagonal element
94
95 tst1 = max(tst1, abs(d(l)) + abs(e(l)));
96 int m = l;
97
98 // Original while-loop from Java code
99 while (m < n) {
100 if (abs(e(m)) <= eps * tst1) { break; }
101 m++;
102 }
103
104 // If m == l, d(l) is an eigenvalue,
105 // otherwise, iterate.
106
107 if (m > l) {
108 int iter = 0;
109 do {
110 iter = iter + 1; // (Could check iteration count here.)
111
112 // Compute implicit shift
113
114 Real g = d(l);
115 Real p = (d(l + 1) - g) / (2.0 * e(l));
116 Real r = hypot(p, 1.0);
117 if (p < 0) { r = -r; }
118 d(l) = e(l) / (p + r);
119 d(l + 1) = e(l) * (p + r);
120 Real dl1 = d(l + 1);
121 Real h = g - d(l);
122 for (int i = l + 2; i < n; i++) {
123 d(i) -= h;
124 }
125 f = f + h;
126
127 // Implicit QL transformation.
128
129 p = d(m);
130 Real c = 1.0;
131 Real c2 = c;
132 Real c3 = c;
133 Real el1 = e(l + 1);
134 Real s = 0.0;
135 Real s2 = 0.0;
136 for (int i = m - 1; i >= l; i--) {
137 c3 = c2;
138 c2 = c;
139 s2 = s;
140 g = c * e(i);
141 h = c * p;
142 r = hypot(p, e(i));
143 e(i + 1) = s * r;
144 s = e(i) / r;
145 c = p / r;
146 p = c * d(i) - s * g;
147 d(i + 1) = h + s * (c * g + s * d(i));
148
149 // Accumulate transformation.
150
151 for (int k = 0; k < n; k++) {
152 h = V(k, i + 1);
153 V(k, i + 1) = s * V(k, i) + c * h;
154 V(k, i) = c * V(k, i) - s * h;
155 }
156 }
157 p = -s * s2 * c3 * el1 * e(l) / dl1;
158 e(l) = s * p;
159 d(l) = c * p;
160
161 // Check for convergence.
162
163 } while (abs(e(l)) > eps * tst1);
164 }
165 d(l) = d(l) + f;
166 e(l) = 0.0;
167 }
168
169 // Sort eigenvalues and corresponding vectors.
170
171 for (int i = 0; i < n - 1; i++) {
172 int k = i;
173 Real p = d(i);
174 for (int j = i + 1; j < n; j++) {
175 if (d(j) < p) {
176 k = j;
177 p = d(j);
178 }
179 }
180 if (k != i) {
181 d(k) = d(i);
182 d(i) = p;
183 for (int j = 0; j < n; j++) {
184 p = V(j, i);
185 V(j, i) = V(j, k);
186 V(j, k) = p;
187 }
188 }
189 }
190 }
191
192 public:
193 /** Construct the eigenvalue decomposition
194
195 @param diagonals the diagonal elements of the input matrix.
196 @param subdiagonals the subdiagonal elements of the input matrix in its
197 last n-1 positions. subdiagonals[0] is arbitrary.
198 */
200 const DynamicVector<Real>& subdiagonals) {
201 n = diagonals.size();
202 V = DynamicRectMatrix<Real>(n, n, 0.0);
203 d = diagonals;
204 e = subdiagonals;
205
206 for (int i = 0; i < n; i++) {
207 V(i, i) = 1.0;
208 }
209 // Diagonalize.
210 tql2();
211 }
212
213 /** Return the eigenvector matrix
214 @return V
215 */
217 V_ = V;
218 return;
219 }
220
221 /** Return the real parts of the eigenvalues
222 @return real(diag(D))
223 */
225 d_ = d;
226 return;
227 }
228 };
229} // namespace OpenMD
230
231#endif // MATH_REALSYMMETRICTRIDIAGONAL_HPP
rectangular matrix class
Dynamically-sized vector class.
Computes eigenvalues and eigenvectors of a real (non-complex) symmetric tridiagonal matrix by the QL ...
void getEigenvectors(DynamicRectMatrix< Real > &V_)
Return the eigenvector matrix.
RealSymmetricTridiagonal(const DynamicVector< Real > &diagonals, const DynamicVector< Real > &subdiagonals)
Construct the eigenvalue decomposition.
void getEigenvalues(DynamicVector< Real > &d_)
Return the real parts of the eigenvalues.
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.