![]() |
Gowdy solver
|
00001 /** 00002 * @file bandSol.h 00003 * @brief Solving a linear equation `A * x = b` for a band-diagonal matrix `A`. 00004 * @authors Mikica Kocic, Wilkinson & Reinsch, Press et al 00005 * @copyright GNU General Public License (GPLv3). 00006 */ 00007 00008 ///////////////////////////////////////////////////////////////////////////////////////// 00009 /** @defgroup g11 Numerical Methods */ 00010 ///////////////////////////////////////////////////////////////////////////////////////// 00011 /*@{*/ 00012 /** BandLUDecomposition implements a method for solving linear equations 00013 * `A * x = b` for a band-diagonal matrix `A` using LU decomposition. 00014 * 00015 * `A` is band-diagonal with `m1` rows below the diagonal and `m2` rows above. 00016 * The input vector is `x[0..n-1]` and the output vector is `b[0..n-1]`. 00017 * 00018 * The LU factorization refers to the factorization of `A`, with proper row and/or 00019 * column orderings or permutations, into two factors -- a lower triangular matrix 00020 * `L` and an upper triangular matrix `U` such that `A = L U`. 00021 * 00022 * The array `A[0..n-1][0..m1+m2]` stores `A` as follows: The diagonal elements are in 00023 * `A[0..n-1][m1]`. Subdiagonal elements are in `A[j ..n-1][0..m1-1]` with `j > 0` 00024 * appropriate to the number of elements on each subdiagonal. Superdiagonal elements 00025 * are in `A[0..j ][m1+1..m1+m2]` with `j < n-1` appropriate to the number of elements 00026 * on each superdiagonal. 00027 * 00028 * The implementation is based on the routines `bandet1` and `bansol1` in 00029 * Wilkinson, J.H., and Reinsch, C. 1971, Linear Algebra, vol. II of Handbook for 00030 * Automatic Computation (New York: Springer), Chapter I/6. The C++ code is based on 00031 * `bandec.h` @cite Press:2007nrec, Section 2.4.2 Band-Diagonal Systems (pp. 58--61). 00032 */ 00033 class BandLUDecomposition 00034 { 00035 Int nn; //!< The full matrix size. 00036 Int m1; //!< The size of subdiagonal. 00037 Int m2; //!< The size of superdiagonal. 00038 MatReal L; //!< The lower triangular matrix (stored compactly). 00039 MatReal U; //!< The upper triangular matrix (stored compactly). 00040 VecInt indx; //!< The row permutation index effected by the partial pivoting 00041 Real d; //!< `+/-1` whether the number of row interchanges was even or odd 00042 00043 static inline void SWAP( Real& x, Real& y ) { 00044 Real temp = x; x = y; y = temp; 00045 } 00046 00047 public: 00048 00049 /** The constructor which does the LU decomposition. It takes as arguments the 00050 * compactly stored matrix `A`, and the integers `m1 >= 0` (subdiagonal size) 00051 * and `m2 >= 0` (superdiagonal size). The upper and lower triangular matrices 00052 * are stored in `U` and `L`, respectively. 00053 */ 00054 BandLUDecomposition( MatReal_I& A, const Int mm1, const Int mm2 ); 00055 00056 /** Gets the lower triangular matrix. 00057 */ 00058 MatReal& getL () { 00059 return L; 00060 } 00061 00062 /** Gets the upper triangular matrix. 00063 */ 00064 MatReal& getU () { 00065 return U; 00066 } 00067 00068 /** Given a right-hand side vector `b[0..n-1]`, solves the band-diagonal 00069 * linear equation `A * x = b`. 00070 */ 00071 void solve( VecReal_I& b, VecReal_O& x ); 00072 00073 /** Performs a sanity check test with sample data. 00074 */ 00075 static void sanityCheck (); 00076 00077 /** Returns the determinant of the matrix A. 00078 */ 00079 Real det () const 00080 { 00081 Real dd = d; 00082 for( Int i = 0; i < nn; ++i ) { 00083 dd *= U[i][0]; 00084 } 00085 return dd; 00086 } 00087 }; 00088 00089 /** Given an `n*n` band-diagonal matrix `A` with `m1` subdiagonal rows and `m2` 00090 * superdiagonal rows, compactly stored in the array `A[0..n-1][0..m1+m2]`, 00091 * an LU decomposition of a rowwise permutation of A is constructed. The upper 00092 * and lower triangular matrices are stored in `U` and `L`, respectively. 00093 * The stored vector `indx[0..n-1]` records the row permutation effected by the partial 00094 * pivoting; `d` is `+/-1` depending on whether the number of row interchanges was even 00095 * or odd, respectively. 00096 * 00097 * Some pivoting is possible within the storage limitations, and the routine 00098 * does take advantage of the opportunity. Also, when `TINY` is returned as 00099 * a diagonal element of `U`, then the original matrix (perhaps as modified by roundoff 00100 * error) is in fact singular. 00101 */ 00102 BandLUDecomposition::BandLUDecomposition( 00103 MatReal_I& A, //!< Compactly stored diagonal-band matrix 00104 const Int mm1, //!< Size of subdiagonal 00105 const Int mm2 //!< Size of superdiagonal 00106 ) 00107 : nn(A.nrows()), m1(mm1), m2(mm2), L(nn,m1), U(A), indx(nn) 00108 { 00109 const Real TINY = 1e-40; 00110 00111 Int mm = m1 + m2 + 1; 00112 Int l = m1; 00113 00114 for( Int i = 0; i < m1; ++i ) { 00115 for( Int j = m1 - i; j < mm; ++j ) { 00116 U[i][j-l] = U[i][j]; 00117 } 00118 l--; 00119 for( Int j = mm - l - 1; j < mm; ++j ) { 00120 U[i][j] = 0; 00121 } 00122 } 00123 00124 Real d = 1; 00125 l = m1; 00126 00127 for( Int k = 0; k < nn; ++k ) 00128 { 00129 Real dum = U[k][0]; 00130 Int i = k; 00131 if( l < nn ) { 00132 l++; 00133 } 00134 for( Int j = k + 1; j < l; ++j ) { 00135 if( abs( U[j][0] ) > abs( dum ) ) { 00136 dum = U[j][0]; 00137 i = j; 00138 } 00139 } 00140 indx[k] = i + 1; 00141 if( dum == 0 ) { 00142 U[k][0]=TINY; 00143 } 00144 if (i != k) { 00145 d = -d; 00146 for( Int j = 0; j < mm; ++j ) { 00147 SWAP( U[k][j], U[i][j] ); 00148 } 00149 } 00150 for( i = k + 1; i < l; ++i ) 00151 { 00152 dum = U[i][0] / U[k][0]; 00153 L[k][i-k-1] = dum; 00154 for( Int j = 1; j < mm; ++j ) { 00155 U[i][j-1] = U[i][j] - dum * U[k][j]; 00156 } 00157 U[i][mm-1] = 0; 00158 } 00159 } 00160 } 00161 00162 /** Given a right-hand side vector `b[0..n-1]`, solves the band-diagonal 00163 * linear equation `A * x = b`. 00164 */ 00165 void BandLUDecomposition::solve( VecReal_I& b, VecReal_O& x ) 00166 { 00167 Int mm = m1 + m2 + 1; 00168 Int l = m1; 00169 00170 for( Int k = 0; k < nn; ++k ) { 00171 x[k] = b[k]; 00172 } 00173 00174 for( Int k = 0; k < nn; ++k ) 00175 { 00176 Int j = indx[k] - 1; 00177 if( j != k ) { 00178 SWAP( x[k], x[j] ); 00179 } 00180 if( l < nn ) { 00181 l++; 00182 } 00183 for( j = k + 1; j < l; ++j ) { 00184 x[j] -= L[k][j-k-1] * x[k]; 00185 } 00186 } 00187 00188 l = 1; 00189 00190 for( Int i = nn - 1; i >= 0; --i ) 00191 { 00192 Real dum = x[i]; 00193 for( Int k = 1; k < l; ++k ) { 00194 dum -= U[i][k]*x[k+i]; 00195 } 00196 00197 x[i] = dum / U[i][0]; 00198 if( l < mm ) { 00199 l++; 00200 } 00201 } 00202 } 00203 00204 /** Sanity check for BandLUDecomposition. 00205 */ 00206 void BandLUDecomposition::sanityCheck () 00207 { 00208 const Real A_values[] = { 00209 // -2 -1 0 +1 <--- the matrix diagonal lies at [0] 00210 0, 0, 3, 1, 00211 0, 4, 1, 5, 00212 9, 2, 6, 5, 00213 3, 5, 8, 9, 00214 7, 9, 3, 2, 00215 3, 8, 4, 6, 00216 2, 4, 4, 0 00217 }; 00218 const Real b_values[] = { 00219 7, 6, 5, 4, 3, 2, 1 00220 }; 00221 00222 MatReal_I A( 7, 4, A_values ); 00223 VecReal_I b( 7, b_values ); 00224 VecReal_O x( 7 ); 00225 00226 BandLUDecomposition( A, 2, 1 ).solve( b, x ); 00227 00228 for( Int i = 0; i < 7; ++i ) { 00229 std::cout << x[i] << ", "; 00230 } 00231 00232 std::cout << std::endl << std::endl << "Compare to:" << std::endl << std::endl 00233 << "4.66912, -7.00737, -1.13382, -3.24088, 6.29092, 10.616, -13.5114" 00234 << std::endl << std::endl; 00235 } 00236 /*@}*/ 00237 /////////////////////////////////////////////////////////////////////////////////////////