OpenMD 3.2
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 following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47
48#ifndef MATH_REALSYMMETRICTRIDIAGONAL_HPP
49#define MATH_REALSYMMETRICTRIDIAGONAL_HPP
50
51#include <config.h>
52
53#include <algorithm>
54
56// for min(), max() below
57#include <cmath>
58// for abs() below
59
60namespace OpenMD {
61
62 /**
63
64 Computes eigenvalues and eigenvectors of a real (non-complex)
65 symmetric tridiagonal matrix by the QL method.
66 **/
67
68 template<typename Real>
70 /** Row and column dimension (square matrix). */
71 int n;
72
75
76 /** Array for internal storage of eigenvectors. */
78
79 // Symmetric tridiagonal QL algorithm.
80
81 void tql2() {
82 // This is derived from the Algol procedures tql2, by
83 // Bowdler, Martin, Reinsch, and Wilkinson, Handbook for
84 // Auto. Comp., Vol.ii-Linear Algebra, and the corresponding
85 // Fortran subroutine in EISPACK.
86
87 for (int i = 1; i < n; i++) {
88 e(i - 1) = e(i);
89 }
90 e(n - 1) = 0.0;
91
92 Real f = 0.0;
93 Real tst1 = 0.0;
94 Real eps = std::pow(2.0, -52.0);
95 for (int l = 0; l < n; l++) {
96 // Find small subdiagonal element
97
98 tst1 = std::max(tst1, std::abs(d(l)) + std::abs(e(l)));
99 int m = l;
100
101 // Original while-loop from Java code
102 while (m < n) {
103 if (std::abs(e(m)) <= eps * tst1) { break; }
104 m++;
105 }
106
107 // If m == l, d(l) is an eigenvalue,
108 // otherwise, iterate.
109
110 if (m > l) {
111 int iter = 0;
112 do {
113 iter = iter + 1; // (Could check iteration count here.)
114
115 // Compute implicit shift
116
117 Real g = d(l);
118 Real p = (d(l + 1) - g) / (2.0 * e(l));
119 Real r = std::hypot(p, 1.0);
120 if (p < 0) { r = -r; }
121 d(l) = e(l) / (p + r);
122 d(l + 1) = e(l) * (p + r);
123 Real dl1 = d(l + 1);
124 Real h = g - d(l);
125 for (int i = l + 2; i < n; i++) {
126 d(i) -= h;
127 }
128 f = f + h;
129
130 // Implicit QL transformation.
131
132 p = d(m);
133 Real c = 1.0;
134 Real c2 = c;
135 Real c3 = c;
136 Real el1 = e(l + 1);
137 Real s = 0.0;
138 Real s2 = 0.0;
139 for (int i = m - 1; i >= l; i--) {
140 c3 = c2;
141 c2 = c;
142 s2 = s;
143 g = c * e(i);
144 h = c * p;
145 r = std::hypot(p, e(i));
146 e(i + 1) = s * r;
147 s = e(i) / r;
148 c = p / r;
149 p = c * d(i) - s * g;
150 d(i + 1) = h + s * (c * g + s * d(i));
151
152 // Accumulate transformation.
153
154 for (int k = 0; k < n; k++) {
155 h = V(k, i + 1);
156 V(k, i + 1) = s * V(k, i) + c * h;
157 V(k, i) = c * V(k, i) - s * h;
158 }
159 }
160 p = -s * s2 * c3 * el1 * e(l) / dl1;
161 e(l) = s * p;
162 d(l) = c * p;
163
164 // Check for convergence.
165
166 } while (std::abs(e(l)) > eps * tst1);
167 }
168 d(l) = d(l) + f;
169 e(l) = 0.0;
170 }
171
172 // Sort eigenvalues and corresponding vectors.
173
174 for (int i = 0; i < n - 1; i++) {
175 int k = i;
176 Real p = d(i);
177 for (int j = i + 1; j < n; j++) {
178 if (d(j) < p) {
179 k = j;
180 p = d(j);
181 }
182 }
183 if (k != i) {
184 d(k) = d(i);
185 d(i) = p;
186 for (int j = 0; j < n; j++) {
187 p = V(j, i);
188 V(j, i) = V(j, k);
189 V(j, k) = p;
190 }
191 }
192 }
193 }
194
195 public:
196 /** Construct the eigenvalue decomposition
197
198 @param diagonals the diagonal elements of the input matrix.
199 @param subdiagonals the subdiagonal elements of the input matrix in its
200 last n-1 positions. subdiagonals[0] is arbitrary.
201 */
203 const DynamicVector<Real>& subdiagonals) {
204 n = diagonals.size();
205 V = DynamicRectMatrix<Real>(n, n, 0.0);
206 d = diagonals;
207 e = subdiagonals;
208
209 for (int i = 0; i < n; i++) {
210 V(i, i) = 1.0;
211 }
212 // Diagonalize.
213 tql2();
214 }
215
216 /** Return the eigenvector matrix
217 @return V
218 */
220 V_ = V;
221 return;
222 }
223
224 /** Return the real parts of the eigenvalues
225 @return real(diag(D))
226 */
228 d_ = d;
229 return;
230 }
231 };
232} // namespace OpenMD
233
234#endif // MATH_REALSYMMETRICTRIDIAGONAL_HPP
Rectangular matrix class with contiguous flat storage.
Dynamically-sized vector class.
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.