FE-Project
Loading...
Searching...
No Matches
scale_linalgebra.F90
Go to the documentation of this file.
1!> Module common / Linear algebra
2!!
3!! @par Description
4!! A module to provide utilities for linear algebra
5!!
6!! @par Reference
7!!
8!! @author Yuta Kawai, Team SCALE
9!!
10#include "scaleFElib.h"
12 !-----------------------------------------------------------------------------
13 !
14 !++ used modules
15 !
16 use scale_precision
17 use scale_io
18 use scale_prc
19 use scale_sparsemat, only: sparsemat
20
21 !-----------------------------------------------------------------------------
22 implicit none
23 private
24
25 !-----------------------------------------------------------------------------
26 !
27 !++ Public procedure
28 !
29
30 public :: linalgebra_lu
31 public :: linalgebra_inv
32 !> Solve a linear equation Ax=b using a direct solver
34 module procedure linalgebra_solvelineq_b1d
35 module procedure linalgebra_solvelineq_b2d
36 end interface
37 public :: linalgebra_solvelineq
40
41
42 !-----------------------------------------------------------------------------
43 !
44 !++ Public parameters & variables
45 !
46
47 !-----------------------------------------------------------------------------
48 !
49 !++ Private procedure
50 !
51
52 !-----------------------------------------------------------------------------
53 !
54 !++ Private parameters & variables
55 !
56
57 !-----------------------------------------------------------------------------
58
59 ! Private procedure
60 !
61 private :: my_dger
62 private :: my_idamax
63 private :: my_swap
64
65 private :: precondstep_ilu0_constructmat
66 private :: precondstep_ilu0_solve
67 private :: precondstep_ptjacobi
68
69 ! Private variable
70 !
71
72contains
73
74!> Calculate a inversion of matrix A
75!OCL SERIAL
76 function linalgebra_inv(A) result(Ainv)
77 implicit none
78 real(rp), intent(in) :: a(:,:) !< Matrix A
79 real(rp) :: ainv(size(a,1),size(a,2)) !< Result of matrix inversion
80
81 real(rp) :: work(size(a,1))
82 integer :: ipiv(size(a,1))
83 integer :: n, info
84
85 !---------------------------------------------------------------------------
86
87 ainv(:,:) = a
88 n = size(a,1)
89
90 call dgetrf(n, n, ainv, n, ipiv, info)
91 if (info /=0 ) then
92 log_error("linalgebra_inv",*) "Matrix is singular"
93 call prc_abort
94 end if
95
96 call dgetri(n, ainv, n, ipiv, work, n, info)
97 if (info /=0 ) then
98 log_error("linalgebra_inv",*) "Matrix inversion is failed"
99 call prc_abort
100 end if
101
102 return
103 end function linalgebra_inv
104
105!> Solve a linear equation Ax=b using a direct solver
106!OCL SERIAL
107 subroutine linalgebra_solvelineq_b1d(A, b, x)
108 implicit none
109 real(RP), intent(in) :: A(:,:) !< Coefficient matrix
110 real(RP), intent(in) :: b(:) !< Vector in the right-hand side
111 real(RP), intent(out) :: x(size(b)) !< Solution vector of linear equation Ax=b
112
113 real(RP) :: A_lu(size(A,1),size(A,2))
114 integer :: ipiv(size(A,1))
115 integer :: n, info
116
117 !---------------------------------------------------------------------------
118
119 a_lu = a
120 n = size(a,1)
121
122 call dgetrf(n, n, a_lu, n, ipiv, info)
123 if (info /=0 ) then
124 log_error("linalgebra_SolveLinEq",*) "Matrix is singular"
125 call prc_abort
126 end if
127
128 x(:) = b
129 call dgetrs('N', n, 1, a_lu, n, ipiv, x, n, info)
130 if (info /=0 ) then
131 log_error("linalgebra_SolveLinEq",*) "Matrix inversion is failed"
132 call prc_abort
133 end if
134
135 return
136 end subroutine linalgebra_solvelineq_b1d
137
138!> Solve a linear equation Ax=b using a direct solver where b is a matrix
139!OCL SERIAL
140 subroutine linalgebra_solvelineq_b2d(A, b, x)
141 implicit none
142 real(RP), intent(in) :: A(:,:) !< Coefficient matrix
143 real(RP), intent(in) :: b(:,:) !< Matrix in the right-hand side
144 real(RP), intent(out) :: x(size(b,1),size(b,2)) !< Matrix storing solution of linear equation Ax=b
145
146 real(RP) :: A_lu(size(A,1),size(A,2))
147 integer :: ipiv(size(A,1))
148 integer :: n, info
149
150 !---------------------------------------------------------------------------
151
152 a_lu = a
153 n = size(a,1)
154
155 call dgetrf(n, n, a_lu, n, ipiv, info)
156 if (info /=0 ) then
157 log_error("linalgebra_SolveLinEq",*) "Matrix is singular"
158 call prc_abort
159 end if
160
161 x(:,:) = b
162 call dgetrs('N', n, size(b,2), a_lu, n, ipiv, x, n, info)
163 if (info /=0 ) then
164 log_error("linalgebra_SolveLinEq",*) "Matrix inversion is failed"
165 call prc_abort
166 end if
167
168 return
169 end subroutine linalgebra_solvelineq_b2d
170
171!> Perform LU factorization
172!OCL SERIAL
173 subroutine linalgebra_lu(A_lu, ipiv)
174 implicit none
175 real(rp), intent(inout) :: a_lu(:,:) !> Matrix performed LU factorization. Note that original matrix is overwritten.
176 integer, intent(out) :: ipiv(size(a_lu,1)) !> Array storing the pivoting information
177
178 integer :: n
179 integer :: info
180
181 !---------------------------------------------------------------------------
182 n = size(a_lu,1)
183
184 call dgetrf(n, n, a_lu, n, ipiv, info)
185 if (info /=0 ) then
186 log_error("linalgebra_LU",*) "Matrix is singular"
187 call prc_abort
188 end if
189
190 return
191 end subroutine linalgebra_lu
192
193
194!> Calculate the solution of linear equations with a band matrix
195!!
196!! In this module, we treat a linear equations system, A * X = B
197!! where A is a band matrix of order N with KL subdiagonals and KU superdiagonals,
198!! and X and B are matrices whose size is N * NRHS.
199!!
200!! @param A Matrix A in band storage same as in dgbsv of LAPACK
201!! @param b Right hand side matrix B with N-by-RHS
202!! @param ipiv Pivot indices that define the permutation matrix
203!! @param KL Number of subdiagonals within the band of A
204!! @param KU Number of superdiagonals within the band of A
205!! @param NRHS Number of right hand sides
206!! @param use_lapack Flag whether LAPACK is used
207!OCL SERIAL
208 subroutine linalgebra_solvelineq_bndmat(A, b, ipiv, &
209 N, KL, KU, NRHS, use_lapack )
210 implicit none
211 integer, intent(in) :: n
212 integer, intent(in) :: kl
213 integer, intent(in) :: ku
214 integer, intent(in) :: nrhs
215 real(rp), intent(inout) :: a(2*kl+ku+1,n)
216 real(rp), intent(inout) :: b(n,nrhs)
217 integer, intent(out) :: ipiv(n)
218 logical, intent(in), optional :: use_lapack
219
220 integer :: i, j, l, lm, p
221 integer :: km
222 integer :: ju, jp
223 integer :: kv
224 real(rp) :: tmp
225 integer :: lda, ldb
226 logical :: use_lapack_
227
228 integer :: info
229 !---------------------------------------------------------------------------
230
231 if (present(use_lapack)) then
232 use_lapack_ = use_lapack
233 else
234 use_lapack_ = .false.
235 end if
236
237 if ( use_lapack_ ) then
238 call dgbsv( n, kl, ku, nrhs, a, 2*kl+ku+1, ipiv, b, n, info)
239 return
240 end if
241
242 !--
243 kv = kl + ku
244 lda = size(a,1)
245 ldb = size(b,1)
246
247 do j=ku+2, min(kv, n)
248 do i=kv-j+2, kl
249 a(i,j) = 0.0_rp
250 end do
251 end do
252
253 ju = 1
254 do j=1, n
255 if ( j+kv <= n ) then
256 do i=1, kl
257 a(i,j+kv) = 0.0_rp
258 end do
259 end if
260
261 km = min(kl, n-j)
262 call my_idamax( km+1, a(kv+1,j), &
263 jp )
264 ipiv(j) = jp + j - 1
265
266 if ( a(kv+jp,j) /= 0.0_rp ) then
267 ju = max( ju, min(j+ku+jp-1,n) )
268
269 if ( jp /= 1 ) then
270 call my_swap( ju-j+1, a(kv+jp,j), lda-1, &
271 a(kv +1,j), lda-1 )
272 end if
273
274 if ( km > 0 ) then
275 tmp = 1.0_rp / a(kv+1,j)
276 do l=kv+2, kv+1+km
277 a(l,j) = tmp * a(l,j)
278 end do
279 call my_dger( km, ju-j, &
280 a(kv+2,j ), &
281 a(kv ,j+1), lda-1, &
282 a(kv+1,j+1), lda-1 )
283 end if
284 end if
285 end do
286
287 !--
288 ! Solve L*X = B, overwriting B with X
289
290 if ( kl > 0 ) then
291 do j=1, n-1
292 lm = min( kl, n-j )
293 l = ipiv(j)
294 if ( l /= j ) call my_swap( nrhs, b(l,1), ldb, b(j,1), ldb )
295 call my_dger( lm, nrhs, &
296 a(kv+2,j), &
297 b(j ,1), ldb, &
298 b(j+1,1), ldb )
299 end do
300 end if
301
302 !--
303 ! Solve U*X = B, overwriting B with X
304 do p=1, nrhs
305 do j=n, 1, -1
306 l = kv + 1 - j
307 b(j,p) = b(j,p) / a(kv+1,j)
308 tmp = b(j,p)
309 do i=j-1, max(1,j-kv),-1
310 b(i,p) = b(i,p) - tmp * a(l+i,j)
311 end do
312 end do
313 end do
314
315 return
316 end subroutine linalgebra_solvelineq_bndmat
317
318 !> Solve a linear equation Ax=b using the Generalized Minimal Residual solver (GMRES)
319 !!
320 subroutine linalgebra_solvelineq_gmres(A, b, x, m, restart_num, CONV_CRIT)
322 implicit none
323
324 type(sparsemat), intent(in) :: a !< Object managing sparse matrix A
325 real(rp), intent(in) :: b(:) !< Right hand side vector
326 real(rp), intent(inout) :: x(:) !< Solution vector
327 integer, intent(in) :: m !<
328 integer, intent(in) ::restart_num !< Number of times by which restarting is performed if convergence condition is not satisfied
329 real(rp), intent(in) :: conv_crit !< Threshold for finishing the iteration solver
330
331 real(rp) :: x0(size(x))
332 real(rp) :: w(size(x))
333 real(rp) :: v(size(x),m+1)
334 real(rp) :: z(size(x),m+1)
335 real(rp) :: av(size(x))
336 real(rp) :: g(size(x)+1)
337 real(rp) :: r0_l2, r0_l2_new
338 real(rp) :: h(m+1,m)
339 real(rp) :: r(m,m)
340 real(rp) :: c(m), s(m)
341 real(rp) :: y(m)
342 real(rp) :: tmp, tmp1, tmp2
343 integer :: i, j
344 integer :: restart_i
345
346 type(sparsemat) :: m_pc
347
348 !---------------------------------------------------------------------------
349
350 call precondstep_ilu0_constructmat(a, m_pc)
351 x0(:) = x
352
353 do restart_i=1, restart_num
354
355 if (restart_i == 1) then
356 call sparsemat_matmul(a, x, av)
357 v(:,1) = b - av
358 r0_l2 = sqrt(sum(v(:,1)**2))
359 end if
360 v(:,1) = v(:,1)/r0_l2
361 g(1) = r0_l2
362
363 do j=1, m
364
365 call precondstep_ilu0_solve(m_pc, v(:,j), z(:,j))
366
367 call sparsemat_matmul(a, z(:,j), w)
368
369 do i=1, j
370 h(i,j) = sum(w*v(:,i))
371 w(:) = w - h(i,j)*v(:,i)
372 end do
373 h(j+1,j) = sqrt(sum(w**2))
374 v(:,j+1) = w(:)/h(j+1,j)
375
376 r(1,j) = h(1,j)
377 do i=1, j-1
378 tmp1 = c(i)*r(i,j) + s(i)*h(i+1,j)
379 tmp2 = -s(i)*r(i,j) + c(i)*h(i+1,j)
380 r(i,j) = tmp1
381 r(i+1,j) = tmp2
382 end do
383
384 tmp = sqrt(r(j,j)**2 + h(j+1,j)**2)
385 c(j) = r(j,j)/tmp
386 s(j) = h(j+1,j)/tmp
387
388 g(j+1) = -s(j)*g(j)
389 g(j) = c(j)*g(j)
390 r(j,j) = c(j)*r(j,j) + s(j)*h(j+1,j)
391 end do
392
393 y(m) = g(m)/r(m,m)
394 do i=m-1,1,-1
395 y(i) = (g(i) - sum(r(i,i+1:m)*y(i+1:m)))/r(i,i)
396 end do
397
398 x(:) = x + matmul(z(:,1:m),y)
399
400 !write(*,*) "x:", x
401 !write(*,*) "x0:", x0
402
403 call sparsemat_matmul(a, x, av)
404 v(:,1) = b - av
405 r0_l2_new = sqrt(sum(v(:,1)**2))
406
407 !write(*,*) "r:", r0_l2, "->", r0_l2_new
408 if (r0_l2_new < conv_crit) exit
409 r0_l2 = r0_l2_new
410 end do
411
412 return
413 end subroutine linalgebra_solvelineq_gmres
414
415!- private ----------------------------------------------------------------
416
417!OCL SERIAL
418 subroutine my_idamax( N, DX, ind )
419 implicit none
420 integer, intent(in) :: n
421 real(rp), intent(in) :: dx(*)
422 integer, intent(out) :: ind
423
424 integer :: i
425 real(rp) :: dmax
426 !----------------------------
427 dmax = abs(dx(1))
428 ind = 1
429 do i=2, n
430 if ( abs(dx(i)) > dmax ) then
431 ind = i
432 dmax = abs(dx(i))
433 end if
434 end do
435 return
436 end subroutine my_idamax
437
438!> Calculate A_ij = A_ij - x_i y_j
439!OCL SERIAL
440 subroutine my_dger( m, n, x, y, incy, A, LDA)
441 implicit none
442 integer, intent(in) :: m
443 integer, intent(in) :: n
444 integer, intent(in) :: lda
445 real(rp), intent(inout) :: a(lda,*)
446 real(rp), intent(in) :: x(*)
447 real(rp), intent(in) :: y(*)
448 integer, intent(in) :: incy
449
450 integer :: i, j
451 integer :: jy
452 real(rp) :: tmp
453 !---------------------------------------------------
454 jy = 1
455 do j=1, n
456 tmp = y(jy)
457 if ( tmp /= 0.0_rp ) then
458 do i=1, m
459 a(i,j) = a(i,j) - tmp * x(i)
460 end do
461 end if
462 jy = jy + incy
463 end do
464 return
465 end subroutine my_dger
466
467!> Interchange two vectors
468!!
469!! Note that incx, incy should be > 0
470!OCL SERIAL
471 subroutine my_swap( n, dx, incx, dy, incy )
472 implicit none
473 integer, intent(in) :: n
474 real(rp), intent(inout) :: dx(*)
475 integer, intent(in) :: incx
476 real(rp), intent(inout) :: dy(*)
477 integer, intent(in) :: incy
478
479 integer :: i, ix, iy
480 integer :: m, mp1
481 real(rp) :: dtemp
482 !---------------------------------------------------
483 if ( incx==1 .and. incy==1 ) then
484 m = mod(n,3)
485 if (m /= 0) then
486 do i=1, m
487 dtemp = dx(i)
488 dx(i) = dy(i)
489 dy(i) = dtemp
490 end do
491 mp1 = m + 1
492 do i=mp1, n, 3
493 dtemp = dx(i)
494 dx(i) = dy(i)
495 dy(i) = dtemp
496 dtemp = dx(i+1)
497 dx(i+1) = dy(i+1)
498 dy(i+1) = dtemp
499 dtemp = dx(i+2)
500 dx(i+2) = dy(i+2)
501 dy(i+2) = dtemp
502 end do
503 end if
504 else
505 ix = 1; iy = 1
506 do i=1, n
507 dtemp = dx(ix)
508 dx(ix) = dy(iy)
509 dy(iy) = dtemp
510 ix = ix + incx
511 iy = iy + incy
512 end do
513 end if
514 return
515 end subroutine my_swap
516
517!OCL SERIAL
518 subroutine precondstep_ptjacobi(A, b, x)
519 implicit none
520 type(sparsemat), intent(in) :: a
521 real(rp), intent(in) :: b(:)
522 real(rp), intent(out) :: x(size(b))
523
524 integer :: n
525 !---------------------------------------------------------------------------
526
527 do n=1, size(x)
528 x(n) = b(n) / a%GetVal(n, n)
529 end do
530
531 return
532 end subroutine precondstep_ptjacobi
533
534!OCL SERIAL
535 subroutine precondstep_ilu0_constructmat(A, M)
536 use scale_const, only: &
537 eps => const_eps
538 implicit none
539
540 type(sparsemat), intent(in) :: a
541 type(sparsemat), intent(inout) :: m
542
543 integer :: i, j, k, n
544 real(rp) :: mij, m_ik, m_kk
545 !---------------------------------------------------------------------------
546
547 n = a%rowPtrSize-1
548
549 m = a
550 do i=2, n
551 do k=1, i-1
552 m_ik = m%GetVal(i,k)
553 m_kk = m%GetVal(k,k)
554 if ( abs(m_ik) < eps .and. abs(m_kk) < eps ) then
555 m_ik = m_ik / m_kk
556 call m%ReplaceVal( i, k, m_ik )
557
558 do j=k+1, n
559 mij = m%GetVal(i,j)
560 if ( abs(mij) < eps ) then
561 call m%ReplaceVal( i, j, mij - m%GetVal(i,k) * m%GetVal(k,j) )
562 end if
563 end do
564 end if
565 end do
566 end do
567
568 return
569 end subroutine precondstep_ilu0_constructmat
570
571!OCL SERIAL
572 subroutine precondstep_ilu0_solve(M, b, x)
573 use scale_sparsemat, only: &
575 implicit none
576
577 type(sparsemat), intent(in) :: m
578 real(rp), intent(in) :: b(:)
579 real(rp), intent(out) :: x(size(b))
580
581 integer :: i
582 integer :: j
583 integer :: n
584
585 integer :: j1, j2
586 !---------------------------------------------------------------------------
587
588 if ( m%GetStorageFormatId() /= sparsemat_storage_typeid_csr ) then
589 log_error("linalgebra_PreCondStep_ILU0_solve",*) "The strorge type of specified sparse matrix is not supported. Check!"
590 call prc_abort
591 end if
592
593 n = size(x)
594
595 x(1) = b(1)
596 do i=2, n
597 j1 = m%rowPtr(i)
598 j2 = m%rowPtr(i+1)-1
599 x(i) = b(i)
600 do j=j1, j2
601 if (m%colIdx(j) <= i-1) &
602 x(i) = x(i) - m%val(j)*x(m%colIdx(j))
603 end do
604 end do
605
606 x(n) = x(n) / m%GetVal(n,n)
607 do i=n-1, 1, -1
608 j1 = m%rowPtr(i)
609 j2 = m%rowPtr(i+1)-1
610 do j=j1, j2
611 if (m%colIdx(j) >= i+1) &
612 x(i) = x(i) - m%val(j)*x(m%colIdx(j))
613 end do
614 x(i) = x(i)/m%GetVal(i,i)
615 end do
616
617 return
618 end subroutine precondstep_ilu0_solve
619end module scale_linalgebra
Solve a linear equation Ax=b using a direct solver.
Module common / Linear algebra.
real(rp) function, dimension(size(a, 1), size(a, 2)), public linalgebra_inv(a)
Calculate a inversion of matrix A.
subroutine, public linalgebra_solvelineq_gmres(a, b, x, m, restart_num, conv_crit)
Solve a linear equation Ax=b using the Generalized Minimal Residual solver (GMRES)
subroutine, public linalgebra_solvelineq_bndmat(a, b, ipiv, n, kl, ku, nrhs, use_lapack)
Calculate the solution of linear equations with a band matrix.
subroutine, public linalgebra_lu(a_lu, ipiv)
Perform LU factorization.
Module common / sparsemat.
integer, parameter, public sparsemat_storage_typeid_csr
Derived type to manage a sparse matrix.