xref: /petsc/src/mat/utils/zerodiag.c (revision 89f0e13287d63c8e6d5ff1eb322fd524891f2c22) !
1 #ifdef PETSC_RCS_HEADER
2 static char vcid[] = "$Id: zerodiag.c,v 1.20 1998/04/13 17:43:46 bsmith Exp curfman $";
3 #endif
4 
5 /*
6     This file contains routines to reorder a matrix so that the diagonal
7     elements are nonzero.
8  */
9 
10 #include "src/mat/matimpl.h"       /*I  "mat.h"  I*/
11 #include <math.h>
12 
13 #define SWAP(a,b) {int _t; _t = a; a = b; b = _t; }
14 
15 #undef __FUNC__
16 #define __FUNC__ "MatZeroFindPre_Private"
17 /*
18    Given a current row and current permutation, find a column permutation
19    that removes a zero diagonal.
20 */
21 int MatZeroFindPre_Private(Mat mat,int prow,int* row,int* col,double repla,
22                            double atol,int* rc,double* rcv )
23 {
24   int      k, nz, repl, *j, kk, nnz, *jj,ierr;
25   Scalar   *v, *vv;
26 
27   PetscFunctionBegin;
28   ierr = MatGetRow( mat, row[prow], &nz, &j, &v ); CHKERRQ(ierr);
29 /*
30     Here one could sort the col[j[k]] to try to select the column closest
31   to the diagonal (in the new ordering) that satisfies the criteria
32 */
33   for (k=0; k<nz; k++) {
34     if (col[j[k]] < prow && PetscAbsScalar(v[k]) > repla) {
35       /* See if this one will work */
36       repl  = col[j[k]];
37       ierr = MatGetRow( mat, row[repl], &nnz, &jj, &vv ); CHKERRQ(ierr);
38       for (kk=0; kk<nnz; kk++) {
39 	if (col[jj[kk]] == prow && PetscAbsScalar(vv[kk]) > atol) {
40 	  *rcv = PetscAbsScalar(v[k]);
41 	  *rc  = repl;
42           ierr = MatRestoreRow( mat, row[repl], &nnz, &jj, &vv ); CHKERRQ(ierr);
43           ierr = MatRestoreRow( mat, row[prow], &nz, &j, &v ); CHKERRQ(ierr);
44 	  PetscFunctionReturn(1);
45 	}
46       }
47       ierr = MatRestoreRow( mat, row[repl], &nnz, &jj, &vv ); CHKERRQ(ierr);
48     }
49   }
50   ierr = MatRestoreRow( mat, row[prow], &nz, &j, &v ); CHKERRQ(ierr);
51   PetscFunctionReturn(0);
52 }
53 
54 #undef __FUNC__
55 #define __FUNC__ "MatReorderForNonzeroDiagonal"
56 /*@
57     MatReorderForNonzeroDiagonal - Changes matrix ordering to remove
58     zeros from diagonal. This may help in the LU factorization to
59     prevent a zero pivot.
60 
61     Input Parameters:
62 .   mat  - matrix to reorder
63 .   rmap,cmap - row and column permutations.  Usually obtained from
64 .               MatGetReordering().
65 
66     Collective on Mat
67 
68     Notes:
69     This is not intended as a replacement for pivoting for matrices that
70     have ``bad'' structure. It is only a stop-gap measure. Should be called
71     after a call to MatGetReordering(), this routine changes the column
72     ordering defined in cis.
73 
74     Options Database Keys (When using SLES):
75 $      -pc_ilu_nonzeros_along_diagonal
76 $      -pc_lu_nonzeros_along_diagonal
77 
78     Algorithm:
79     Column pivoting is used.  Choice of column is made by looking at the
80     non-zero elements in the row.  This algorithm is simple and fast but
81     does NOT guarantee that a non-singular or well conditioned
82     principle submatrix will be produced.
83 @*/
84 int MatReorderForNonzeroDiagonal(Mat mat,double atol,IS ris,IS cis )
85 {
86   int      ierr, prow, k, nz, n, repl, *j, *col, *row, m;
87   Scalar   *v;
88   double   repla;
89 
90   PetscFunctionBegin;
91   PetscValidHeaderSpecific(mat,MAT_COOKIE);
92   PetscValidHeaderSpecific(ris,IS_COOKIE);
93   PetscValidHeaderSpecific(cis,IS_COOKIE);
94 
95   ierr = ISGetIndices(ris,&row); CHKERRQ(ierr);
96   ierr = ISGetIndices(cis,&col); CHKERRQ(ierr);
97   ierr = MatGetSize(mat,&m,&n); CHKERRQ(ierr);
98 
99   for (prow=0; prow<n; prow++) {
100     ierr = MatGetRow( mat, row[prow], &nz, &j, &v ); CHKERRQ(ierr);
101     for (k=0; k<nz; k++) {if (col[j[k]] == prow) break;}
102     if (k >= nz || PetscAbsScalar(v[k]) <= atol) {
103       /* Element too small or zero; find the best candidate */
104       repl  = prow;
105       repla = (k >= nz) ? 0.0 : PetscAbsScalar(v[k]);
106 /*
107    Here one could sort the col[j[k]] list to try to select the
108   column closest to the diagonal in the new ordering. (Note have
109   to permute the v[k] values as well, and use a fixed bound on the
110   quality of repla rather then looking for the absolute largest.
111 */
112       for (k=0; k<nz; k++) {
113 	if (col[j[k]] > prow && PetscAbsScalar(v[k]) > repla) {
114 	  repl  = col[j[k]];
115 	  repla = PetscAbsScalar(v[k]);
116         }
117       }
118       if (prow == repl) {
119 	    /* Look for an element that allows us
120 	       to pivot with a previous column.  To do this, we need
121 	       to be sure that we don't introduce a zero in a previous
122 	       diagonal */
123         if (!MatZeroFindPre_Private(mat,prow,row,col,repla,atol,&repl,&repla)){
124 	  SETERRQ(PETSC_ERR_MAT_LU_ZRPVT,0,"Cannot reorder matrix to eliminate zero diagonal entry");
125 	}
126       }
127       SWAP(col[prow],col[repl]);
128     }
129     ierr = MatRestoreRow( mat, row[prow], &nz, &j, &v ); CHKERRQ(ierr);
130   }
131   ierr = ISRestoreIndices(ris,&row); CHKERRQ(ierr);
132   ierr = ISRestoreIndices(cis,&col); CHKERRQ(ierr);
133   PetscFunctionReturn(0);
134 }
135 
136 
137 
138