MODFLOW 6  version 6.5.0.dev2
MODFLOW 6 Code Documentation
ilut.f90 File Reference

Go to the source code of this file.

Functions/Subroutines

subroutine ilut (n, a, ja, ia, lfil, droptol, alu, jlu, ju, iwk, w, jw, ierr, relax, izero, delta)
 
subroutine lusol (n, y, x, alu, jlu, ju)
 
subroutine qsplit (n, a, ind, ncut)
 

Function/Subroutine Documentation

◆ ilut()

subroutine ilut ( integer(kind=4)  n,
real(kind=8), dimension(*)  a,
integer(kind=4), dimension(*)  ja,
integer(kind=4), dimension(n+1)  ia,
integer(kind=4)  lfil,
real(kind=8)  droptol,
real(kind=8), dimension(*)  alu,
integer(kind=4), dimension(*)  jlu,
integer(kind=4), dimension(n)  ju,
integer(kind=4)  iwk,
real(kind=8), dimension(n+1)  w,
integer(kind=4), dimension(2*n)  jw,
integer(kind=4)  ierr,
real(kind=8)  relax,
integer(kind=4)  izero,
real(kind=8)  delta 
)

Definition at line 48 of file ilut.f90.

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  ! ----------------------------------------------------------------------
subroutine qsplit(n, a, ind, ncut)
Definition: ilut.f90:520
Here is the call graph for this function:
Here is the caller graph for this function:

◆ lusol()

subroutine lusol ( integer(kind=4)  n,
real(kind=8), dimension(n)  y,
real(kind=8), dimension(n)  x,
real(kind=8), dimension(*)  alu,
integer(kind=4), dimension(*)  jlu,
integer(kind=4), dimension(*)  ju 
)

Definition at line 465 of file ilut.f90.

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  ! ----------------------------------------------------------------------
Here is the caller graph for this function:

◆ qsplit()

subroutine qsplit ( integer(kind=4)  n,
real(kind=8), dimension(n)  a,
integer(kind=4), dimension(n)  ind,
integer(kind=4)  ncut 
)

Definition at line 519 of file ilut.f90.

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  ! ----------------------------------------------------------------------
Here is the caller graph for this function: