MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
ilut.f90
Go to the documentation of this file.
1  ! SUBSET OF SPARSKIT VERSION 2 SOURCE CODE
2  !
3  ! SPARSKIT VERSION 2 SUBROUTINES INCLUDED INCLUDE:
4  !
5  ! 1 - ilut
6  ! 2 - luson
7  ! 3 - qsplit
8  !
9  ! ----------------------------------------------------------------------
10  ! S P A R S K I T V E R S I O N 2.
11  ! ----------------------------------------------------------------------
12  !
13  !Latest update : Tue Mar 8 11:01:12 CST 2005
14  !
15  ! ----------------------------------------------------------------------
16  !
17  !Welcome to SPARSKIT VERSION 2. SPARSKIT is a package of FORTRAN
18  !subroutines for working with sparse matrices. It includes general
19  !sparse matrix manipulation routines as well as a few iterative
20  !solvers, see detailed description of contents below.
21  !
22  ! Copyright (C) 2005, the Regents of the University of Minnesota
23  !
24  !SPARSKIT is free software; you can redistribute it and/or modify it
25  !under the terms of the GNU Lesser General Public License as published
26  !by the Free Software Foundation [version 2.1 of the License, or any
27  !later version.]
28  !
29  !A copy of the licencing agreement is attached in the file LGPL. For
30  !additional information contact the Free Software Foundation Inc., 59
31  !Temple Place - Suite 330, Boston, MA 02111, USA or visit the web-site
32  !
33  ! http://www.gnu.org/copyleft/lesser.html
34  !
35  !
36  !DISCLAIMER
37  ! ---------
38  !
39  !SPARSKIT is distributed in the hope that it will be useful, but
40  !WITHOUT ANY WARRANTY; without even the implied warranty of
41  !MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
42  !Lesser General Public License for more details.
43  !
44  !For more information contact saad@cs.umn.edu
45  !
46  !
47 
48  subroutine ilut(n, a, ja, ia, lfil, droptol, &
49  alu, jlu, ju, iwk, w, jw, ierr, &
50  relax, izero, delta)
51  ! ----------------------------------------------------------------------
52  implicit none
53  integer(kind=4) :: n
54  real(kind=8) :: a(*),alu(*),w(n+1),droptol
55  integer(kind=4) :: ja(*),ia(n+1),jlu(*),ju(n),jw(2*n),lfil,iwk,ierr
56  real(kind=8) :: relax
57  integer(kind=4) :: izero
58  real(kind=8) :: delta
59  ! ---------------------------------------------------------------------*
60  ! *** ILUT preconditioner *** *
61  ! incomplete LU factorization with dual truncation mechanism *
62  ! ---------------------------------------------------------------------*
63  ! Author: Yousef Saad *May, 5, 1990, Latest revision, August 1996 *
64  ! Modified: Joseph Hughes *August, 27, 2017 *
65  ! 1) FORTRAN90 version *
66  ! 2) Removed implict typing *
67  ! 3) Added relaxation for dropped terms (MILUT) *
68  ! 4) Added diagonal scaling for zero pivots based on *
69  ! Hill (1990) https://doi.org/10.3133/wri904048 *
70  ! ---------------------------------------------------------------------*
71  ! PARAMETERS
72  ! ----------
73  !
74  ! on entry:
75  !==========
76  ! n = integer. The row dimension of the matrix A. The matrix
77  !
78  ! a,ja,ia = matrix stored in Compressed Sparse Row format.
79  !
80  ! lfil = integer. The fill-in parameter. Each row of L and each row
81  ! of U will have a maximum of lfil elements (excluding the
82  ! diagonal element). lfil must be .ge. 0.
83  ! ** WARNING: THE MEANING OF LFIL HAS CHANGED WITH RESPECT TO
84  ! EARLIER VERSIONS.
85  !
86  ! droptol = real. Sets the threshold for dropping small terms
87  ! in the factorization. See below for details on dropping
88  ! strategy.
89  !
90  !
91  ! iwk = integer. The lengths of arrays alu and jlu. If the arrays
92  ! are not big enough to store the ILU factorizations, ilut
93  ! will stop with an error message.
94  !
95  ! relax = real. Relaxation factor for dropped terms (dropsum). If
96  ! relax .eq. 0 then standard ILUT. If relax .eq. 1 then
97  ! MILUT.
98  !
99  ! delta = real. Diagonal scaling term.
100  !
101  ! On return:
102  !===========
103  !
104  ! alu,jlu = matrix stored in Modified Sparse Row (MSR) format containing
105  ! the L and U factors together. The diagonal (stored in
106  ! alu(1:n) ) is inverted. Each i-th row of the alu,jlu matrix
107  ! contains the i-th row of L (excluding the diagonal entry=1)
108  ! followed by the i-th row of U.
109  !
110  ! ju = integer array of length n containing the pointers to
111  ! the beginning of each row of U in the matrix alu,jlu.
112  !
113  ! ierr = integer. Error message with the following meaning.
114  ! ierr = 0 --> successful return.
115  ! ierr .gt. 0 --> zero pivot encountered at step number ierr.
116  ! ierr = -1 --> Error. input matrix may be wrong.
117  ! (The elimination process has generated a
118  ! row in L or U whose length is .gt. n.)
119  ! ierr = -2 --> The matrix L overflows the array al.
120  ! ierr = -3 --> The matrix U overflows the array alu.
121  ! ierr = -4 --> Illegal value for lfil.
122  ! ierr = -5 --> zero row encountered.
123  !
124  ! izero = integer. Counter for diagonal scaling increment. If izero
125  ! is zero then this is the first call to ILUT and diagonal
126  ! scaling is not applied and izero is set to 1. If izero
127  ! is greater than 1 then diagonal scaling is applied when
128  ! the diagonal is .eq. 0. This is continued until all of
129  ! the diagonals are .ne. 0. Protection is all provided so
130  ! that the sign of the diagonal does not change with diagonal
131  ! scaling.
132  !
133  !
134  ! work arrays:
135  !=============
136  ! jw = integer work array of length 2*n.
137  ! w = real work array of length n+1.
138  !
139  ! ---------------------------------------------------------------------
140  ! w, ju (1:n) store the working array [1:ii-1 = L-part, ii:n = u]
141  ! jw(n+1:2n) stores nonzero indicators
142  !
143  ! Notes:
144  ! ------
145  ! The diagonal elements of the input matrix must be nonzero (at least
146  ! 'structurally').
147  !
148  ! ---------------------------------------------------------------------*
149  !---- Dual drop strategy works as follows. *
150  ! *
151  ! 1) Thresholding in L and U as set by droptol. Any element whose *
152  ! magnitude is less than some tolerance (relative to the abs *
153  ! value of diagonal element in u) is dropped. *
154  ! *
155  ! 2) Keeping only the largest lfil elements in the i-th row of L *
156  ! and the largest lfil elements in the i-th row of U (excluding *
157  ! diagonal elements). *
158  ! *
159  ! Flexibility: one can use droptol=0 to get a strategy based on *
160  ! keeping the largest elements in each row of L and U. Taking *
161  ! droptol .ne. 0 but lfil=n will give the usual threshold strategy *
162  ! (however, fill-in is then unpredictable). *
163  ! ---------------------------------------------------------------------*
164  ! locals
165  integer(kind=4) :: ju0,k,j1,j2,j,ii,i,lenl,lenu,jj,jrow,jpos,ilen
166  real(kind=8) :: tnorm, t, abs, s, fact
167  real(kind=8) :: dropsum, diag, sign_check, diag_working
168  ! format
169  character(len=*), parameter :: fmterr = "(//,1x,a)"
170  ! code
171  if (lfil .lt. 0) goto 998
172  ! ----------------------------------------------------------------------
173  ! initialize ju0 (points to next element to be added to alu,jlu)
174  ! and pointer array.
175  ! ----------------------------------------------------------------------
176  ju0 = n+2
177  jlu(1) = ju0
178  !
179  ! initialize nonzero indicator array.
180  !
181  do j = 1, n
182  jw(n+j) = 0
183  end do
184  ! ----------------------------------------------------------------------
185  ! beginning of main loop.
186  ! ----------------------------------------------------------------------
187  main: do ii = 1, n
188  j1 = ia(ii)
189  j2 = ia(ii+1) - 1
190  dropsum = 0.0d0
191  tnorm = 0.0d0
192  do k = j1, j2
193  tnorm = tnorm+abs(a(k))
194  end do
195  if (tnorm .eq. 0.0d0) goto 999
196  tnorm = tnorm/real(j2-j1+1)
197  !
198  ! unpack L-part and U-part of row of A in arrays w
199  !
200  lenu = 1
201  lenl = 0
202  jw(ii) = ii
203  w(ii) = 0.0d0
204  jw(n+ii) = ii
205  !
206  do j = j1, j2
207  k = ja(j)
208  t = a(j)
209  if (k .lt. ii) then
210  lenl = lenl+1
211  jw(lenl) = k
212  w(lenl) = t
213  jw(n+k) = lenl
214  else if (k .eq. ii) then
215  w(ii) = t
216  else
217  lenu = lenu+1
218  jpos = ii+lenu-1
219  jw(jpos) = k
220  w(jpos) = t
221  jw(n+k) = jpos
222  end if
223  end do
224  jj = 0
225  ilen = 0
226  !
227  ! eliminate previous rows
228  !
229 150 jj = jj+1
230  if (jj .gt. lenl) goto 160
231  ! ----------------------------------------------------------------------
232  ! in order to do the elimination in the correct order we must select
233  ! the smallest column index among jw(k), k=jj+1, ..., lenl.
234  ! ----------------------------------------------------------------------
235  jrow = jw(jj)
236  k = jj
237  !
238  ! determine smallest column index
239  !
240  do j = jj+1, lenl
241  if (jw(j) .lt. jrow) then
242  jrow = jw(j)
243  k = j
244  end if
245  end do
246  !
247  if (k .ne. jj) then
248  ! exchange in jw
249  j = jw(jj)
250  jw(jj) = jw(k)
251  jw(k) = j
252  ! exchange in jr
253  jw(n+jrow) = jj
254  jw(n+j) = k
255  ! exchange in w
256  s = w(jj)
257  w(jj) = w(k)
258  w(k) = s
259  end if
260  !
261  ! zero out element in row by setting jw(n+jrow) to zero.
262  !
263  jw(n+jrow) = 0
264  !
265  ! get the multiplier for row to be eliminated (jrow).
266  !
267  fact = w(jj)*alu(jrow)
268  if (abs(fact) .le. droptol) then
269  dropsum = dropsum + w(jj)
270  goto 150
271  end if
272  !
273  ! combine current row and row jrow
274  !
275  do k = ju(jrow), jlu(jrow+1)-1
276  s = fact*alu(k)
277  j = jlu(k)
278  jpos = jw(n+j)
279  if (j .ge. ii) then
280  !
281  ! dealing with upper part.
282  !
283  if (jpos .eq. 0) then
284  !
285  ! this is a fill-in element
286  !
287  lenu = lenu+1
288  if (lenu .gt. n) goto 995
289  i = ii+lenu-1
290  jw(i) = j
291  jw(n+j) = i
292  w(i) = - s
293  else
294  !
295  ! this is not a fill-in element
296  !
297  w(jpos) = w(jpos) - s
298 
299  end if
300  else
301  !
302  ! dealing with lower part.
303  !
304  if (jpos .eq. 0) then
305  !
306  ! this is a fill-in element
307  !
308  lenl = lenl+1
309  if (lenl .gt. n) goto 995
310  jw(lenl) = j
311  jw(n+j) = lenl
312  w(lenl) = - s
313  else
314  !
315  ! this is not a fill-in element
316  !
317  w(jpos) = w(jpos) - s
318  end if
319  end if
320  end do
321  !
322  ! store this pivot element -- (from left to right -- no danger of
323  ! overlap with the working elements in L (pivots).
324  !
325  ilen = ilen+1
326  w(ilen) = fact
327  jw(ilen) = jrow
328  goto 150
329 160 continue
330  !
331  ! reset double-pointer to zero (U-part)
332  !
333  do k = 1, lenu
334  jw(n+jw(ii+k-1)) = 0
335  end do
336  !
337  ! update L-matrix
338  !
339  lenl = ilen
340  ilen = min0(lenl,lfil)
341  !
342  ! sort by quick-split
343  !
344  call qsplit(lenl, w, jw, ilen)
345  !
346  ! store L-part
347  !
348  do k = 1, ilen
349  if (ju0 .gt. iwk) goto 996
350  alu(ju0) = w(k)
351  jlu(ju0) = jw(k)
352  ju0 = ju0+1
353  end do
354  !
355  ! save pointer to beginning of row ii of U
356  !
357  ju(ii) = ju0
358  !
359  ! update U-matrix -- first apply dropping strategy
360  !
361  ilen = 0
362  do k = 1, lenu-1
363  if (abs(w(ii+k)) .gt. droptol*tnorm) then
364  ilen = ilen+1
365  w(ii+ilen) = w(ii+k)
366  jw(ii+ilen) = jw(ii+k)
367  else
368  dropsum = dropsum + w(ii+k)
369  end if
370  end do
371  lenu = ilen+1
372  ilen = min0(lenu,lfil)
373  !
374  call qsplit(lenu-1, w(ii+1), jw(ii+1), ilen)
375  !
376  ! copy
377  !
378  t = abs(w(ii))
379  if (ilen + ju0 .gt. iwk) goto 997
380  do k = ii+1, ii+ilen-1
381  jlu(ju0) = jw(k)
382  alu(ju0) = w(k)
383  t = t + abs(w(k) )
384  ju0 = ju0+1
385  end do
386  !
387  ! diagonal - calculate inverse of diagonal for solution
388  !
389  diag = w(ii)
390  diag_working = ( 1.0d0 + delta ) * diag + ( relax * dropsum )
391  !
392  ! ensure that the sign of the diagonal has not changed
393  !
394  sign_check = sign(diag, diag_working)
395  if (sign_check .ne. diag) then
396  ! use small value if diagonal scaling is not effective for
397  ! pivots that change the sign of the diagonal
398  if (izero > 1) then
399  diag_working = sign(1.0d0, diag) * (1.0d-4 + droptol) * tnorm
400  ! diagonal scaling continues to be effective
401  else
402  izero = 1
403  exit main
404  end if
405  end if
406  !
407  ! ensure that the diagonal is not zero
408  !
409  IF (abs(diag_working) == 0.0d0) THEN
410  ! use small value if diagonal scaling is not effective
411  ! zero pivots
412  IF (izero > 1) THEN
413  diag_working = sign(1.0d0, diag) * (1.0d-4 + droptol) * tnorm
414  ! diagonal scaling continues to be effective
415  else
416  izero = 1
417  exit main
418  end if
419  end if
420  w(ii) = diag_working
421  !
422  ! store inverse of diagonal element of u
423  !
424  alu(ii) = 1.0d0 / w(ii)
425  !
426  ! update pointer to beginning of next row of U.
427  !
428  jlu(ii+1) = ju0
429  ! ----------------------------------------------------------------------
430  ! end main loop
431  ! ----------------------------------------------------------------------
432  end do main
433  ierr = 0
434  return
435  !
436  ! incomprehensible error. Matrix must be wrong.
437  !
438 995 ierr = -1
439  return
440  !
441  ! insufficient storage in L.
442  !
443 996 ierr = -2
444  return
445  !
446  ! insufficient storage in U.
447  !
448 997 ierr = -3
449  return
450  !
451  ! illegal lfil entered.
452  !
453 998 ierr = -4
454  return
455  !
456  ! zero row encountered
457  !
458 999 ierr = -5
459  return
460  ! ---------------end-of-ilut--------------------------------------------
461  ! ----------------------------------------------------------------------
462  end subroutine ilut
463 
464  ! ----------------------------------------------------------------------
465  subroutine lusol(n, y, x, alu, jlu, ju)
466  implicit none
467  integer(kind=4) :: n
468  real(kind=8) :: x(n), y(n), alu(*)
469  integer(kind=4) :: jlu(*), ju(*)
470  ! ----------------------------------------------------------------------
471  !
472  ! This routine solves the system (LU) x = y,
473  ! given an LU decomposition of a matrix stored in (alu, jlu, ju)
474  ! modified sparse row format
475  !
476  ! ----------------------------------------------------------------------
477  ! on entry:
478  ! n = dimension of system
479  ! y = the right-hand-side vector
480  ! alu, jlu, ju
481  ! = the LU matrix as provided from the ILU routines.
482  !
483  ! on return
484  ! x = solution of LU x = y.
485  ! ----------------------------------------------------------------------
486  !
487  ! Note: routine is in place: call IMSLINEARSUB_PCMILUT_LUSOL (n, x, x, alu, jlu, ju)
488  ! will solve the system with rhs x and overwrite the result on x .
489  !
490  ! ----------------------------------------------------------------------
491  ! ------local
492  !
493  integer(kind=4) :: i, k
494  !
495  ! forward solve
496  !
497  do i = 1, n
498  x(i) = y(i)
499  do k = jlu(i), ju(i)-1
500  x(i) = x(i) - alu(k)* x(jlu(k))
501  end do
502  end do
503  !
504  ! backward solve.
505  !
506  do i = n, 1, -1
507  do k = ju(i), jlu(i+1)-1
508  x(i) = x(i) - alu(k)*x(jlu(k))
509  end do
510  x(i) = alu(i)*x(i)
511  end do
512  !
513  return
514  ! ---------------end of lusol ------------------------------------------
515  ! ----------------------------------------------------------------------
516  end subroutine lusol
517 
518  ! ----------------------------------------------------------------------
519  subroutine qsplit(n, a, ind, ncut)
520  implicit none
521  integer(kind=4) :: n
522  real(kind=8) :: a(n)
523  integer(kind=4) :: ind(n), ncut
524  ! ----------------------------------------------------------------------
525  ! does a quick-sort split of a real array.
526  ! on input a(1:n). is a real array
527  ! on output a(1:n) is permuted such that its elements satisfy:
528  !
529  ! abs(a(i)) .ge. abs(a(ncut)) for i .lt. ncut and
530  ! abs(a(i)) .le. abs(a(ncut)) for i .gt. ncut
531  !
532  ! ind(1:n) is an integer array which permuted in the same way as a(*
533  ! ----------------------------------------------------------------------
534  real(kind=8) :: tmp, abskey
535  integer(kind=4) :: itmp, first, last
536  integer(kind=4) :: mid
537  integer(kind=4) :: j
538  !-----
539  first = 1
540  last = n
541  if (ncut .lt. first .or. ncut .gt. last) return
542  !
543  ! outer loop -- while mid .ne. ncut do
544  !
545 00001 mid = first
546  abskey = abs(a(mid))
547  do j = first+1, last
548  if (abs(a(j)) .gt. abskey) then
549  mid = mid+1
550  ! interchange
551  tmp = a(mid)
552  itmp = ind(mid)
553  a(mid) = a(j)
554  ind(mid) = ind(j)
555  a(j) = tmp
556  ind(j) = itmp
557  end if
558  end do
559  !
560  ! interchange
561  !
562  tmp = a(mid)
563  a(mid) = a(first)
564  a(first) = tmp
565  !
566  itmp = ind(mid)
567  ind(mid) = ind(first)
568  ind(first) = itmp
569  !
570  ! test for while loop
571  !
572  if (mid .eq. ncut) return
573  if (mid .gt. ncut) then
574  last = mid-1
575  else
576  first = mid+1
577  end if
578  goto 1
579  ! ---------------end-of-qsplit------------------------------------------
580  ! ----------------------------------------------------------------------
581  end subroutine qsplit
subroutine ilut(n, a, ja, ia, lfil, droptol, alu, jlu, ju, iwk, w, jw, ierr, relax, izero, delta)
Definition: ilut.f90:51
subroutine qsplit(n, a, ind, ncut)
Definition: ilut.f90:520
subroutine lusol(n, y, x, alu, jlu, ju)
Definition: ilut.f90:466