From 7574bdaac81a7cf7abb5138794af6fb6e59cf1f2 Mon Sep 17 00:00:00 2001 From: shion Date: Fri, 17 May 2024 19:25:18 +0900 Subject: [PATCH 1/2] Updates in Lapack Utility - adding GE_EigenValueMethods --- .../Lapack/src/GE_EigenValueMethods.F90 | 81 +++++++- .../src/GE_EigenValueMethods@Methods.F90 | 190 ++++++++++++++++-- 2 files changed, 256 insertions(+), 15 deletions(-) diff --git a/src/modules/Lapack/src/GE_EigenValueMethods.F90 b/src/modules/Lapack/src/GE_EigenValueMethods.F90 index 01de14a1..c84dea44 100644 --- a/src/modules/Lapack/src/GE_EigenValueMethods.F90 +++ b/src/modules/Lapack/src/GE_EigenValueMethods.F90 @@ -16,8 +16,85 @@ ! MODULE GE_EigenValueMethods -! USE GlobalData, ONLY: DFP, I4B, LGT -! IMPLICIT NONE +USE GlobalData, ONLY: DFP, I4B, LGT +IMPLICIT NONE +CHARACTER(*), PARAMETER :: modName = "GE_EigenValueMethods" +PRIVATE + +PUBLIC :: GetEigVals +PUBLIC :: GetEig + +!---------------------------------------------------------------------------- +! getEigVals +!---------------------------------------------------------------------------- + +!> author: Shion Shimizu +! date: 2024-05-17 +! summary: calculate eigenvalues for real matrix + +INTERFACE GetEigVals + MODULE SUBROUTINE deigvals(A, lam) + REAL(DFP), INTENT(IN) :: A(:, :) + COMPLEX(DFP), INTENT(OUT) :: lam(:) + END SUBROUTINE deigvals +END INTERFACE GetEigVals + +!---------------------------------------------------------------------------- +! getEigVals +!---------------------------------------------------------------------------- + +!> author: Shion Shimizu +! date: 2024-05-17 +! summary: calculate eigenvalues for complex matrix + +INTERFACE GetEigVals + MODULE SUBROUTINE zeigvals(A, lam) + COMPLEX(DFP), INTENT(IN) :: A(:, :) + COMPLEX(DFP), INTENT(OUT) :: lam(:) + END SUBROUTINE zeigvals +END INTERFACE GetEigVals + +!---------------------------------------------------------------------------- +! getEig +!---------------------------------------------------------------------------- + +!> author: Shion Shimizu +! date: 2024-05-17 +! summary: calculate eigenvalues and eigenvectors for real matrix + +INTERFACE GetEig + MODULE SUBROUTINE deig(A, lam, c) + REAL(DFP), INTENT(IN) :: A(:, :) + COMPLEX(DFP), INTENT(OUT) :: lam(:) + ! eigenvalues + ! should be allocated + COMPLEX(DFP), INTENT(OUT) :: c(:, :) + ! eigenvectors + ! c(i,j) = ith component of jth eigenvec. + ! should be allocated + END SUBROUTINE deig +END INTERFACE GetEig + +!---------------------------------------------------------------------------- +! getEig +!---------------------------------------------------------------------------- + +!> author: Shion Shimizu +! date: 2024-05-17 +! summary: calculate eigenvalues and eigenvectors for complex matrix + +INTERFACE GetEig + MODULE SUBROUTINE zeig(A, lam, c) + COMPLEX(DFP), INTENT(IN) :: A(:, :) + COMPLEX(DFP), INTENT(OUT) :: lam(:) + ! eigenvalues + ! should be allocated + COMPLEX(DFP), INTENT(OUT) :: c(:, :) + ! eigenvectors + ! c(i,j) = ith component of jth eigenvec. + ! should be allocated + END SUBROUTINE zeig +END INTERFACE GetEig !---------------------------------------------------------------------------- ! DGEES@EigenValue diff --git a/src/submodules/Lapack/src/GE_EigenValueMethods@Methods.F90 b/src/submodules/Lapack/src/GE_EigenValueMethods@Methods.F90 index 3fd0066d..16b75a69 100644 --- a/src/submodules/Lapack/src/GE_EigenValueMethods@Methods.F90 +++ b/src/submodules/Lapack/src/GE_EigenValueMethods@Methods.F90 @@ -13,17 +13,181 @@ ! ! You should have received a copy of the GNU General Public License ! along with this program. If not, see -! -! SUBMODULE(GE_EigenValueMethods) Methods -! ! USE BaseMethod -! ! IMPLICIT NONE -! ! CONTAINS -! -! !---------------------------------------------------------------------------- -! ! DGEES -! !---------------------------------------------------------------------------- -! -! ! MODULE PROCEDURE dgees_1 -! ! END PROCEDURE dgees_1 -! END SUBMODULE Methods +! the implementation of deig, zeig, deigvals, and zeigvals are copied from +! linalg.f90 available in https://github.com/certik/fortran-utils +! and are modified to suit the needs of EASIFEM library + +SUBMODULE(GE_EigenValueMethods) Methods +USE BaseMethod, ONLY: ErrorMsg, LA_GEEV, stderr, tostring, & + display +USE AssertUtility +IMPLICIT NONE +COMPLEX(DFP), PARAMETER :: i_ = (0, 1) +CONTAINS + +!---------------------------------------------------------------------------- +! getEig +!---------------------------------------------------------------------------- + +MODULE PROCEDURE deig +! LAPACK variables for DGEEV: +REAL(DFP), ALLOCATABLE :: At(:, :), vl(:, :), vr(:, :), wi(:), & + work(:), wr(:) +INTEGER(I4B) :: info, lda, ldvl, ldvr, lwork, n, i + +CHARACTER(*), PARAMETER :: myName = "deig" + +lda = SIZE(A(:, 1)) +n = SIZE(A(1, :)) +! CALL Assert(Mat=A, s=[n, n], msg="[ARG ERROR] :: A should be square", & +! file=__FILE__, line=__LINE__, routine=myName) +! CALL Assert(n1=SIZE(lam), n2=n, msg="[ARG ERROR] :: size of lam should be "// & +! "equal to "//tostring(n), file=__FILE__, line=__LINE__, & +! routine=myName) +! CALL Assert(mat=c, s=[n, n], msg="[ARG ERROR] :: shape of c should be"// & +! "the same as one of A", file=__FILE__, line=__LINE__, & +! routine=myName) +ldvl = n +ldvr = n +lwork = 8 * n ! TODO: can this size be optimized? query first? +ALLOCATE (At(lda, n), wr(n), wi(n), vl(ldvl, n), vr(ldvr, n), & + work(lwork)) +At = A + +CALL LA_GEEV('N', 'V', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & + work, lwork, info) +IF (info .NE. 0) CALL geevErrorMsg(info, n) + +lam = wr + i_ * wi +! as DGEEV has a rather complicated way of returning the eigenvectors, +! it is necessary to build the complex array of eigenvectors from +! two real arrays: +DO i = 1, n + IF (wi(i) > 0.0) THEN ! first of two conjugate eigenvalues + c(:, i) = vr(:, i) + i_ * vr(:, i + 1) + ELSEIF (wi(i) < 0.0_DFP) THEN ! second of two conjugate eigenvalues + c(:, i) = vr(:, i - 1) - i_ * vr(:, i) + ELSE + c(:, i) = vr(:, i) + END IF +END DO + +END PROCEDURE deig + +!---------------------------------------------------------------------------- +! getEig +!---------------------------------------------------------------------------- + +MODULE PROCEDURE zeig +! LAPACK variables: +INTEGER(I4B) :: info, lda, ldvl, ldvr, lwork, n, lrwork +REAL(DFP), ALLOCATABLE :: rwork(:) +COMPLEX(DFP), ALLOCATABLE :: vl(:, :), vr(:, :), work(:) + +CHARACTER(*), PARAMETER :: myName = "zeig" + +lda = SIZE(A(:, 1)) +n = SIZE(A(1, :)) +! CALL Assert(Mat=A, s=[n, n], msg="[ARG ERROR] :: A should be square", & +! file=__FILE__, line=__LINE__, routine=myName) +! CALL Assert(n1=SIZE(lam), n2=n, msg="[ARG ERROR] :: size of lam should be "// & +! "equal to "//tostring(n), file=__FILE__, line=__LINE__, & +! routine=myName) +! CALL Assert(mat=c, s=[n, n], msg="[ARG ERROR] :: shape of c should be"// & +! "the same as one of A", file=__FILE__, line=__LINE__, & +! routine=myName) +ldvl = n +ldvr = n +lwork = 8 * n ! TODO: can this size be optimized? query first? +lrwork = 2 * n +ALLOCATE (vl(ldvl, n), vr(ldvr, n), work(lwork), rwork(lrwork)) +c = A +CALL LA_GEEV('N', 'V', n, c, lda, lam, vl, ldvl, vr, ldvr, work, & + lwork, rwork, info) +IF (info .NE. 0) CALL geevErrorMsg(info, n) +c = vr + +END PROCEDURE zeig + +!---------------------------------------------------------------------------- +! getEigVals +!---------------------------------------------------------------------------- + +MODULE PROCEDURE deigvals +! LAPACK variables for DGEEV: +REAL(DFP), ALLOCATABLE :: At(:, :), vl(:, :), vr(:, :), wi(:), work(:), wr(:) +INTEGER :: info, lda, ldvl, ldvr, lwork, n + +CHARACTER(*), PARAMETER :: myName = "deigvals" + +lda = SIZE(A(:, 1)) +n = SIZE(A(1, :)) +! CALL assert_shape(A, [n, n], "solve", "A") +ldvl = n +ldvr = n +lwork = 8 * n ! TODO: can this size be optimized? query first? +ALLOCATE (At(lda, n), wr(n), wi(n), vl(ldvl, n), & + vr(ldvr, n), work(lwork)) +At = A + +CALL LA_GEEV('N', 'N', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & + work, lwork, info) +IF (info .NE. 0) CALL geevErrorMsg(info, n) + +lam = wr + i_ * wi +END PROCEDURE deigvals + +!---------------------------------------------------------------------------- +! getEigVals_2 +!---------------------------------------------------------------------------- + +MODULE PROCEDURE zeigvals +! LAPACK variables: +INTEGER :: info, lda, ldvl, ldvr, lwork, n, lrwork +REAL(DFP), ALLOCATABLE :: rwork(:) +COMPLEX(DFP), ALLOCATABLE :: At(:, :), vl(:, :), vr(:, :), work(:) + +lda = SIZE(A(:, 1)) +n = SIZE(A(1, :)) +! CALL assert_shape(A, [n, n], "solve", "A") +ldvl = n +ldvr = n +lwork = 8 * n ! TODO: can this size be optimized? query first? +lrwork = 2 * n +ALLOCATE (At(lda, n), vl(ldvl, n), vr(ldvr, n), & + work(lwork), rwork(lrwork)) +At = A +CALL LA_GEEV('N', 'N', n, At, lda, lam, vl, ldvl, vr, ldvr, work, & + lwork, rwork, info) +IF (info .NE. 0) CALL geevErrorMsg(info, n) + +END PROCEDURE zeigvals + +!---------------------------------------------------------------------------- +! geevErrorMsg +!---------------------------------------------------------------------------- + +SUBROUTINE geevErrorMsg(info, n) + INTEGER(I4B), INTENT(IN) :: info, n + + CALL Display(info, "LA_GEEV returned info = ") + IF (info .LT. 0) THEN + CALL display("The "//tostring(-info)//"-th argument "// & + "had an illegal value.") + ELSE + CALL display("The QR algorithm failed to compute all the") + CALL display("eigenvalues, and no eigenvectors have been computed;") + CALL display("elements "//tostring(info + 1)//":"//tostring(n)// & + " of WR and WI contain eigenvalues which converged.") + END IF + CALL ErrorMsg( & + & msg="ERROR IN LA_GEEV", & + & file=__FILE__, & + & line=__LINE__, & + & routine="zeigvals", & + & unitno=stderr) + STOP +END SUBROUTINE geevErrorMsg + +END SUBMODULE Methods From 2fff4c699566614bcae694a1afdd82b864c8fb53 Mon Sep 17 00:00:00 2001 From: shion Date: Fri, 17 May 2024 19:58:10 +0900 Subject: [PATCH 2/2] Updates in Lapack - minor changes --- .../Lapack/src/GE_EigenValueMethods.F90 | 18 +++++------ .../src/GE_EigenValueMethods@Methods.F90 | 30 ++++++++++--------- 2 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/modules/Lapack/src/GE_EigenValueMethods.F90 b/src/modules/Lapack/src/GE_EigenValueMethods.F90 index c84dea44..4c4086a3 100644 --- a/src/modules/Lapack/src/GE_EigenValueMethods.F90 +++ b/src/modules/Lapack/src/GE_EigenValueMethods.F90 @@ -16,7 +16,7 @@ ! MODULE GE_EigenValueMethods -USE GlobalData, ONLY: DFP, I4B, LGT +USE GlobalData, ONLY: DFP, DFPC, I4B, LGT IMPLICIT NONE CHARACTER(*), PARAMETER :: modName = "GE_EigenValueMethods" PRIVATE @@ -35,7 +35,7 @@ MODULE GE_EigenValueMethods INTERFACE GetEigVals MODULE SUBROUTINE deigvals(A, lam) REAL(DFP), INTENT(IN) :: A(:, :) - COMPLEX(DFP), INTENT(OUT) :: lam(:) + COMPLEX(DFPC), INTENT(INOUT) :: lam(:) END SUBROUTINE deigvals END INTERFACE GetEigVals @@ -49,8 +49,8 @@ END SUBROUTINE deigvals INTERFACE GetEigVals MODULE SUBROUTINE zeigvals(A, lam) - COMPLEX(DFP), INTENT(IN) :: A(:, :) - COMPLEX(DFP), INTENT(OUT) :: lam(:) + COMPLEX(DFPC), INTENT(IN) :: A(:, :) + COMPLEX(DFPC), INTENT(INOUT) :: lam(:) END SUBROUTINE zeigvals END INTERFACE GetEigVals @@ -65,10 +65,10 @@ END SUBROUTINE zeigvals INTERFACE GetEig MODULE SUBROUTINE deig(A, lam, c) REAL(DFP), INTENT(IN) :: A(:, :) - COMPLEX(DFP), INTENT(OUT) :: lam(:) + COMPLEX(DFPC), INTENT(INOUT) :: lam(:) ! eigenvalues ! should be allocated - COMPLEX(DFP), INTENT(OUT) :: c(:, :) + COMPLEX(DFPC), INTENT(INOUT) :: c(:, :) ! eigenvectors ! c(i,j) = ith component of jth eigenvec. ! should be allocated @@ -85,11 +85,11 @@ END SUBROUTINE deig INTERFACE GetEig MODULE SUBROUTINE zeig(A, lam, c) - COMPLEX(DFP), INTENT(IN) :: A(:, :) - COMPLEX(DFP), INTENT(OUT) :: lam(:) + COMPLEX(DFPC), INTENT(IN) :: A(:, :) + COMPLEX(DFPC), INTENT(INOUT) :: lam(:) ! eigenvalues ! should be allocated - COMPLEX(DFP), INTENT(OUT) :: c(:, :) + COMPLEX(DFPC), INTENT(INOUT) :: c(:, :) ! eigenvectors ! c(i,j) = ith component of jth eigenvec. ! should be allocated diff --git a/src/submodules/Lapack/src/GE_EigenValueMethods@Methods.F90 b/src/submodules/Lapack/src/GE_EigenValueMethods@Methods.F90 index 16b75a69..ffd75d3a 100644 --- a/src/submodules/Lapack/src/GE_EigenValueMethods@Methods.F90 +++ b/src/submodules/Lapack/src/GE_EigenValueMethods@Methods.F90 @@ -20,10 +20,10 @@ SUBMODULE(GE_EigenValueMethods) Methods USE BaseMethod, ONLY: ErrorMsg, LA_GEEV, stderr, tostring, & - display + Display USE AssertUtility IMPLICIT NONE -COMPLEX(DFP), PARAMETER :: i_ = (0, 1) +COMPLEX(DFPC), PARAMETER :: i_ = (0.0_DFP, 1.0_DFP) CONTAINS !---------------------------------------------------------------------------- @@ -57,7 +57,7 @@ CALL LA_GEEV('N', 'V', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & work, lwork, info) -IF (info .NE. 0) CALL geevErrorMsg(info, n) +IF (info .NE. 0) CALL GeevErrorMsg(info, n) lam = wr + i_ * wi ! as DGEEV has a rather complicated way of returning the eigenvectors, @@ -83,7 +83,7 @@ ! LAPACK variables: INTEGER(I4B) :: info, lda, ldvl, ldvr, lwork, n, lrwork REAL(DFP), ALLOCATABLE :: rwork(:) -COMPLEX(DFP), ALLOCATABLE :: vl(:, :), vr(:, :), work(:) +COMPLEX(DFPC), ALLOCATABLE :: vl(:, :), vr(:, :), work(:) CHARACTER(*), PARAMETER :: myName = "zeig" @@ -105,7 +105,7 @@ c = A CALL LA_GEEV('N', 'V', n, c, lda, lam, vl, ldvl, vr, ldvr, work, & lwork, rwork, info) -IF (info .NE. 0) CALL geevErrorMsg(info, n) +IF (info .NE. 0) CALL GeevErrorMsg(info, n) c = vr END PROCEDURE zeig @@ -133,7 +133,7 @@ CALL LA_GEEV('N', 'N', n, At, lda, wr, wi, vl, ldvl, vr, ldvr, & work, lwork, info) -IF (info .NE. 0) CALL geevErrorMsg(info, n) +IF (info .NE. 0) CALL GeevErrorMsg(info, n) lam = wr + i_ * wi END PROCEDURE deigvals @@ -146,7 +146,9 @@ ! LAPACK variables: INTEGER :: info, lda, ldvl, ldvr, lwork, n, lrwork REAL(DFP), ALLOCATABLE :: rwork(:) -COMPLEX(DFP), ALLOCATABLE :: At(:, :), vl(:, :), vr(:, :), work(:) +COMPLEX(DFPC), ALLOCATABLE :: At(:, :), vl(:, :), vr(:, :), work(:) + +CHARACTER(*), PARAMETER :: myName = "zeigvals" lda = SIZE(A(:, 1)) n = SIZE(A(1, :)) @@ -160,7 +162,7 @@ At = A CALL LA_GEEV('N', 'N', n, At, lda, lam, vl, ldvl, vr, ldvr, work, & lwork, rwork, info) -IF (info .NE. 0) CALL geevErrorMsg(info, n) +IF (info .NE. 0) CALL GeevErrorMsg(info, n) END PROCEDURE zeigvals @@ -168,17 +170,17 @@ ! geevErrorMsg !---------------------------------------------------------------------------- -SUBROUTINE geevErrorMsg(info, n) +SUBROUTINE GeevErrorMsg(info, n) INTEGER(I4B), INTENT(IN) :: info, n CALL Display(info, "LA_GEEV returned info = ") IF (info .LT. 0) THEN - CALL display("The "//tostring(-info)//"-th argument "// & + CALL Display("The "//tostring(-info)//"-th argument "// & "had an illegal value.") ELSE - CALL display("The QR algorithm failed to compute all the") - CALL display("eigenvalues, and no eigenvectors have been computed;") - CALL display("elements "//tostring(info + 1)//":"//tostring(n)// & + CALL Display("The QR algorithm failed to compute all the") + CALL Display("eigenvalues, and no eigenvectors have been computed;") + CALL Display("elements "//tostring(info + 1)//":"//tostring(n)// & " of WR and WI contain eigenvalues which converged.") END IF CALL ErrorMsg( & @@ -188,6 +190,6 @@ SUBROUTINE geevErrorMsg(info, n) & routine="zeigvals", & & unitno=stderr) STOP -END SUBROUTINE geevErrorMsg +END SUBROUTINE GeevErrorMsg END SUBMODULE Methods