/******************************************************************* * * * File : iterativ.c * * Programmers : Scott D. Cohen and Alan C. Hindmarsh @ LLNL * * Version of : 26 June 2002 * *-----------------------------------------------------------------* * Copyright (c) 2002, The Regents of the University of California * * Produced at the Lawrence Livermore National Laboratory * * All rights reserved * * For details, see sundials/shared/LICENSE * *-----------------------------------------------------------------* * This is the implementation file for the iterativ.h header * * file. It contains the implementation of functions that may be * * useful for many different iterative solvers of A x = b. * * * *******************************************************************/ #include "iterativ.h" #include "sundialstypes.h" #include "nvector.h" #include "sundialsmath.h" #define FACTOR RCONST(1000.0) #define ZERO RCONST(0.0) #define ONE RCONST(1.0) /************************* ModifiedGS *********************************** This implementation of ModifiedGS is a slight modification of a previous modified Gram-Schmidt routine (called mgs) written by Milo Dorr. *************************************************************************/ int ModifiedGS(N_Vector *v, realtype **h, int k, int p, realtype *new_vk_norm) { int i, k_minus_1, i0; realtype new_norm_2, new_product, vk_norm, temp; vk_norm = RSqrt(N_VDotProd(v[k],v[k])); k_minus_1 = k - 1; i0 = MAX(k-p, 0); /* Perform modified Gram-Schmidt */ for (i=i0; i < k; i++) { h[i][k_minus_1] = N_VDotProd(v[i], v[k]); N_VLinearSum(ONE, v[k], -h[i][k_minus_1], v[i], v[k]); } /* Compute the norm of the new vector at v[k]. */ *new_vk_norm = RSqrt(N_VDotProd(v[k], v[k])); /* If the norm of the new vector at v[k] is less than FACTOR (== 1000) times unit roundoff times the norm of the input vector v[k], then the vector will be reorthogonalized in order to ensure that nonorthogonality is not being masked by a very small vector length. */ temp = FACTOR * vk_norm; if ((temp + (*new_vk_norm)) != temp) return(0); new_norm_2 = ZERO; for (i=i0; i < k; i++) { new_product = N_VDotProd(v[i], v[k]); temp = FACTOR * h[i][k_minus_1]; if ((temp + new_product) == temp) continue; h[i][k_minus_1] += new_product; N_VLinearSum(ONE, v[k],-new_product, v[i], v[k]); new_norm_2 += SQR(new_product); } if (new_norm_2 != ZERO) { new_product = SQR(*new_vk_norm) - new_norm_2; *new_vk_norm = (new_product > ZERO) ? RSqrt(new_product) : ZERO; } return(0); } /************************ ClassicalGS ******************************** This implementation of ClassicalGS was contributed to by Homer Walker and Peter Brown. **********************************************************************/ int ClassicalGS(N_Vector *v, realtype **h, int k, int p, realtype *new_vk_norm, N_Vector temp, realtype *s) { int i, k_minus_1, i0; realtype vk_norm; k_minus_1 = k - 1; /* Perform Classical Gram-Schmidt */ vk_norm = RSqrt(N_VDotProd(v[k], v[k])); i0 = MAX(k-p, 0); for (i=i0; i < k; i++) { h[i][k_minus_1] = N_VDotProd(v[i], v[k]); } for (i=i0; i < k; i++) { N_VLinearSum(ONE, v[k], -h[i][k_minus_1], v[i], v[k]); } /* Compute the norm of the new vector at v[k]. */ *new_vk_norm = RSqrt(N_VDotProd(v[k], v[k])); /* Reorthogonalize if necessary */ if ((FACTOR * (*new_vk_norm)) < vk_norm) { for (i=i0; i < k; i++) { s[i] = N_VDotProd(v[i], v[k]); } if (i0 < k) { N_VScale(s[i0], v[i0], temp); h[i0][k_minus_1] += s[i0]; } for (i=i0+1; i < k; i++) { N_VLinearSum(s[i], v[i], ONE, temp, temp); h[i][k_minus_1] += s[i]; } N_VLinearSum(ONE, v[k], -ONE, temp, v[k]); *new_vk_norm = RSqrt(N_VDotProd(v[k],v[k])); } return(0); } /*************** QRfact ********************************************** This implementation of QRfact is a slight modification of a previous routine (called qrfact) written by Milo Dorr. **********************************************************************/ int QRfact(int n, realtype **h, realtype *q, int job) { realtype c, s, temp1, temp2, temp3; int i, j, k, q_ptr, n_minus_1, code=0; switch (job) { case 0: /* Compute a new factorization of H. */ code = 0; for (k=0; k < n; k++) { /* Multiply column k by the previous k-1 Givens rotations. */ for (j=0; j < k-1; j++) { i = 2*j; temp1 = h[j][k]; temp2 = h[j+1][k]; c = q[i]; s = q[i+1]; h[j][k] = c*temp1 - s*temp2; h[j+1][k] = s*temp1 + c*temp2; } /* Compute the Givens rotation components c and s */ q_ptr = 2*k; temp1 = h[k][k]; temp2 = h[k+1][k]; if( temp2 == ZERO) { c = ONE; s = ZERO; } else if (ABS(temp2) >= ABS(temp1)) { temp3 = temp1/temp2; s = -ONE/RSqrt(ONE+SQR(temp3)); c = -s*temp3; } else { temp3 = temp2/temp1; c = ONE/RSqrt(ONE+SQR(temp3)); s = -c*temp3; } q[q_ptr] = c; q[q_ptr+1] = s; if( (h[k][k] = c*temp1 - s*temp2) == ZERO) code = k+1; } break; default: /* Update the factored H to which a new column has been added. */ n_minus_1 = n - 1; code = 0; /* Multiply the new column by the previous n-1 Givens rotations. */ for (k=0; k < n_minus_1; k++) { i = 2*k; temp1 = h[k][n_minus_1]; temp2 = h[k+1][n_minus_1]; c = q[i]; s = q[i+1]; h[k][n_minus_1] = c*temp1 - s*temp2; h[k+1][n_minus_1] = s*temp1 + c*temp2; } /* Compute new Givens rotation and multiply it times the last two entries in the new column of H. Note that the second entry of this product will be 0, so it is not necessary to compute it. */ temp1 = h[n_minus_1][n_minus_1]; temp2 = h[n][n_minus_1]; if (temp2 == ZERO) { c = ONE; s = ZERO; } else if (ABS(temp2) >= ABS(temp1)) { temp3 = temp1/temp2; s = -ONE/RSqrt(ONE+SQR(temp3)); c = -s*temp3; } else { temp3 = temp2/temp1; c = ONE/RSqrt(ONE+SQR(temp3)); s = -c*temp3; } q_ptr = 2*n_minus_1; q[q_ptr] = c; q[q_ptr+1] = s; if ((h[n_minus_1][n_minus_1] = c*temp1 - s*temp2) == ZERO) code = n; } return (code); } /*************** QRsol ************************************************ This implementation of QRsol is a slight modification of a previous routine (called qrsol) written by Milo Dorr. **********************************************************************/ int QRsol(int n, realtype **h, realtype *q, realtype *b) { realtype c, s, temp1, temp2; int i, k, q_ptr, code=0; /* Compute Q*b. */ for (k=0; k < n; k++) { q_ptr = 2*k; c = q[q_ptr]; s = q[q_ptr+1]; temp1 = b[k]; temp2 = b[k+1]; b[k] = c*temp1 - s*temp2; b[k+1] = s*temp1 + c*temp2; } /* Solve R*x = Q*b. */ for (k=n-1; k >= 0; k--) { if (h[k][k] == ZERO) { code = k + 1; break; } b[k] /= h[k][k]; for (i=0; i < k; i++) b[i] -= b[k]*h[i][k]; } return (code); }