!> @author Ignis !> @brief High-order compact finite difference scheme (generalized Padé scheme) solver with specific order of accuracy. !! !! This module handles the generation of tridiagonal/pentadiagonal matrices, !! LU decomposition calculations, and tridiagonal solver operations (such as stdlu, ptdlu, etc.) !! for periodic and non-periodic boundary conditions. Since compact finite difference schemes !! are implicit, these solvers are implemented internally to perform numerical differentiation !! by solving the implicit relations efficiently. MODULE Compact use, intrinsic :: iso_fortran_env, only: real64 IMPLICIT NONE real(real64), DIMENSION(:), ALLOCATABLE :: lxf,lxs,wxf,wxs, & !< x-방향 LU Decomposition 밴드 계수 lyf,lys,wyf,wys, & !< y-방향 LU Decomposition 밴드 계수 lzf,lzs,wzf,wzs !< z-방향 LU Decomposition 밴드 계수 INTEGER :: nxc,nyc,nzc !< 각 방향의 실제 격자 사이즈 수치 (x, y, z) real(real64), PARAMETER :: ezero = 1.0e-14 CONTAINS !> Entry point for LU decomposition calculations. !! !! Prepares the workspace allocations and runs the decomposition calculation for all three directions. !! !! @param nx Grid size in x-direction. !! @param ny Grid size in y-direction. !! @param nz Grid size in z-direction. !! @param xp Periodic flag for x-direction (0 = periodic, other = non-periodic). !! @param yp Periodic flag for y-direction (0 = periodic, other = non-periodic). !! @param zp Periodic flag for z-direction (0 = periodic, other = non-periodic). SUBROUTINE ludcmp(nx,ny,nz,xp,yp,zp) INTEGER, INTENT(IN) :: nx,ny,nz INTEGER, INTENT(IN) :: xp,yp,zp INTEGER :: ierr nxc=nx nyc=ny nzc=nz CALL ludcmp_allocate(nx,ny,nz,xp,yp,zp) CALL ludcmp_calculate(nx,ny,nz,xp,yp,zp) END SUBROUTINE ludcmp !> LU 분해를 위한 포트란 workspace 배열 메모리를 동적 할당하는 서브루틴입니다. !! !! 경계 조건(xp, yp, zp = 0 주기적 경계 조건, 1 비주기적 경계 조건)에 따라 배열 크기와 !! 할당 여부를 결정하며, 할당에 실패하면 에러를 출력하고 즉시 프로그램을 안전하게 종료(STOP)시킵니다. !! !! @param nx Grid size in x-direction. !! @param ny Grid size in y-direction. !! @param nz Grid size in z-direction. !! @param xp Periodic flag for x-direction (0 = periodic, other = non-periodic). !! @param yp Periodic flag for y-direction (0 = periodic, other = non-periodic). !! @param zp Periodic flag for z-direction (0 = periodic, other = non-periodic). SUBROUTINE ludcmp_allocate(nx,ny,nz,xp,yp,zp) INTEGER, INTENT(IN) :: nx,ny,nz INTEGER, INTENT(IN) :: xp,yp,zp INTEGER :: ierr nxc=nx nyc=ny nzc=nz ! IF(nyc /= nzc) PRINT*,'ny should be equal nz' ! xp, yp, zp = 0 : periodic ALLOCATE(lxf(nxc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF ALLOCATE(lxs(nxc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF IF(xp.eq.0) THEN ALLOCATE(wxf(nxc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF ALLOCATE(wxs(nxc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF ENDIF ALLOCATE(lyf(nyc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF ALLOCATE(lys(nyc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF IF(yp.eq.0) THEN ALLOCATE(wyf(nyc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF ALLOCATE(wys(nyc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF ENDIF ALLOCATE(lzf(nzc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF ALLOCATE(lzs(nzc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF IF(zp.eq.0) THEN ALLOCATE(wzf(nzc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF ALLOCATE(wzs(nzc),STAT=ierr) IF(ierr /= 0) THEN PRINT*, 'work array for lud allocation failed' STOP 1 ENDIF ENDIF END SUBROUTINE ludcmp_allocate SUBROUTINE ludcmp_deallocate(xp,yp,zp) INTEGER, INTENT(IN) :: xp,yp,zp ! IF(nyc /= nzc) PRINT*,'ny should be equal nz' ! xp, yp, zp = 0 : periodic DEALLOCATE(lxf) DEALLOCATE(lxs) IF(xp.eq.0) THEN DEALLOCATE(wxf) DEALLOCATE(wxs) ENDIF DEALLOCATE(lyf) DEALLOCATE(lys) IF(yp.eq.0) THEN DEALLOCATE(wyf) DEALLOCATE(wys) ENDIF DEALLOCATE(lzf) DEALLOCATE(lzs) IF(zp.eq.0) THEN DEALLOCATE(wzf) DEALLOCATE(wzs) ENDIF END SUBROUTINE ludcmp_deallocate SUBROUTINE ludcmp_testalloc IF (.not. ALLOCATED(lxf)) print *, "lxf not allocated" IF (.not. ALLOCATED(lxs)) print *, "lxs not allocated" IF (.not. ALLOCATED(wxf)) print *, "wxf not allocated" IF (.not. ALLOCATED(wxs)) print *, "wxs not allocated" IF (.not. ALLOCATED(lyf)) print *, "lyf not allocated" IF (.not. ALLOCATED(lys)) print *, "lys not allocated" IF (.not. ALLOCATED(wyf)) print *, "wyf not allocated" IF (.not. ALLOCATED(wys)) print *, "wys not allocated" IF (.not. ALLOCATED(lzf)) print *, "lzf not allocated" IF (.not. ALLOCATED(lzs)) print *, "lzs not allocated" IF (.not. ALLOCATED(wzf)) print *, "wzf not allocated" IF (.not. ALLOCATED(wzs)) print *, "wzs not allocated" END SUBROUTINE ludcmp_testalloc SUBROUTINE ludcmp_calculate(nx,ny,nz,xp,yp,zp) INTEGER, INTENT(IN) :: nx,ny,nz INTEGER, INTENT(IN) :: xp,yp,zp INTEGER :: ierr nxc=nx nyc=ny nzc=nz ! CALL ludcmp_testalloc ! IF(nyc /= nzc) PRINT*,'ny should be equal nz' ! xp, yp, zp = 0 : periodic IF(xp.eq.0) THEN CALL p_lud(1,nxc) ELSE CALL nonp_lud(1,nxc) ENDIF IF(yp.eq.0) THEN CALL p_lud(2,nyc) ELSE call nonp_lud(2,nyc) ENDIF IF(zp.eq.0) THEN CALL p_lud(3,nzc) ELSE call nonp_lud(3,nzc) ENDIF END SUBROUTINE ludcmp_calculate SUBROUTINE test_nonp_lud1(xx, coef) INTEGER :: xx real(real64), DIMENSION(xx) :: aa real(real64), DIMENSION(xx), INTENT(OUT) :: coef aa=3. aa(1)=0.5 ; aa(2)=4. aa(xx-1)=4. ; aa(xx)=0.5 CALL stdlu(aa,xx,coef) END SUBROUTINE test_nonp_lud1 SUBROUTINE test_nonp_lud2(xx, coef) INTEGER :: xx real(real64), DIMENSION(xx) :: aa real(real64), DIMENSION(xx), INTENT(OUT) :: coef aa=5.5 aa(1)=2./11. ; aa(2)=10. aa(xx-1)=10. ; aa(xx)=2./11. CALL stdlu(aa,xx,coef) END SUBROUTINE test_nonp_lud2 SUBROUTINE test_p_lud1(xx, coef1, coef2) INTEGER :: xx real(real64) :: a real(real64), DIMENSION(xx), INTENT(OUT) :: coef1, coef2 a=3. ! first derivative CALL ptdlu(a,xx,coef1,coef2) ! x-direction END SUBROUTINE test_p_lud1 SUBROUTINE test_p_lud2(xx, coef1, coef2) INTEGER :: xx real(real64) :: a real(real64), DIMENSION(xx), INTENT(OUT) :: coef1, coef2 a=11./2. ! second derivative CALL ptdlu(a,xx,coef1,coef2) ! x-direction END SUBROUTINE test_p_lud2 SUBROUTINE nonp_lud(xyz,xx) INTEGER :: xyz,xx real(real64), DIMENSION(xx) :: aa aa=3. aa(1)=0.5 ; aa(2)=4. aa(xx-1)=4. ; aa(xx)=0.5 ! first derivative IF (xyz.eq.1) CALL stdlu(aa,xx,lxf) ! x-direction IF (xyz.eq.2) CALL stdlu(aa,xx,lyf) ! y-direction IF (xyz.eq.3) CALL stdlu(aa,xx,lzf) ! z-direction aa=5.5 aa(1)=2./11. ; aa(2)=10. aa(xx-1)=10. ; aa(xx)=2./11. ! second derivative IF (xyz.eq.1) CALL stdlu(aa,xx,lxs) ! x-direction IF (xyz.eq.2) CALL stdlu(aa,xx,lys) ! y-direction IF (xyz.eq.3) CALL stdlu(aa,xx,lzs) ! z-direction END SUBROUTINE nonp_lud SUBROUTINE p_lud(xyz,xx) INTEGER :: xyz,xx real(real64) :: a a=3. ! first derivative IF (xyz.eq.1) CALL ptdlu(a,xx,lxf,wxf) ! x-direction IF (xyz.eq.2) CALL ptdlu(a,xx,lyf,wyf) ! y-direction IF (xyz.eq.3) CALL ptdlu(a,xx,lzf,wzf) ! z-direction a=11./2. ! second derivative IF (xyz.eq.1) CALL ptdlu(a,xx,lxs,wxs) ! x-direction IF (xyz.eq.2) CALL ptdlu(a,xx,lys,wys) ! y-direction IF (xyz.eq.3) CALL ptdlu(a,xx,lzs,wzs) ! z-direction END SUBROUTINE p_lud SUBROUTINE stdlu(a,n,l) INTEGER :: n real(real64), INTENT(IN) :: a(n) real(real64), INTENT(OUT) :: l(n) real(real64) :: d INTEGER :: i l(1)=1.0d0/a(1) DO i=2,n d=a(i)-l(i-1) l(i)=1.0d0/d ENDDO END SUBROUTINE stdlu SUBROUTINE ptdlu(a,n,l,w) INTEGER :: n real(real64), INTENT(IN) :: a real(real64), INTENT(OUT) :: l(n),w(n) INTEGER :: i real(real64) :: aa(n),d DO i=1,n-1 aa(i)=a ENDDO i=n-1 call stdlu(aa,i,l) w(1)=1.0 DO i=2,n-2 w(i)=-l(i-1)*w(i-1) ENDDO w(n-1)=1.0-l(n-2)*w(n-2) DO i=1,n-1 w(i)=w(i)*l(i) ENDDO d=a DO i=1,n-1 d=d-w(i)*w(i)/l(i) ENDDO l(n)=1./d END SUBROUTINE ptdlu SUBROUTINE rhs1np(n,h,x,dx,nd) INTEGER,INTENT(IN) :: n,nd real(real64),INTENT(IN) :: h real(real64),INTENT(IN),DIMENSION(nd,n) :: x real(real64),INTENT(OUT),DIMENSION(nd,n) :: dx INTEGER :: i,j real(real64) :: r1,r2,r3,a,b,c,h1,t1,t2,t3,t4 h1=1.d0/h r1=7.d0/3.d0 r2=1.d0/12.d0 r3=3. a=-1.25 b=1. c=0.25 DO j=1,nd dx(j,n-1)=x(j,n)-x(j,n-2) dx(j,n)=-(a*x(j,n)+b*x(j,n-1)+c*x(j,n-2)) dx(j,1)=(a*x(j,1)+b*x(j,2)+c*x(j,3)) dx(j,2)=x(j,3)-x(j,1) IF (x(j,n).eq.x(j,n-1).and.x(j,n-1).eq.x(j,n-2)) dx(j,n)=0. IF (x(j,1).eq.x(j,2).and.x(j,2).eq.x(j,3)) dx(j,1)=0. dx(j,n-1)=dx(j,n-1)*h1*r3 dx(j,n)=dx(j,n)*h1 dx(j,1)=dx(j,1)*h1 dx(j,2)=dx(j,2)*h1*r3 ENDDO DO i=3,n-2 DO j=1,nd t1=x(j,i+1)-x(j,i-1) t2=x(j,i+2)-x(j,i-2) dx(j,i)=h1*(r1*t1+r2*t2) ENDDO ENDDO END SUBROUTINE rhs1np SUBROUTINE dfnonp(n,h,x,dx,nd,dir) INTEGER,INTENT(IN) :: n,nd,dir real(real64),INTENT(IN) :: h real(real64),INTENT(IN),DIMENSION(nd,n) :: x real(real64),INTENT(OUT),DIMENSION(nd,n) :: dx INTEGER :: i,j real(real64) :: r1,r2,r3,a,b,c,h1,t1,t2,t3,t4 CALL rhs1np (n,h,x,dx,nd) IF (dir.eq.1) CALL tdslv(dx,n,lxf,nd) ! x-direction IF (dir.eq.2) CALL tdslv(dx,n,lyf,nd) ! y-direction IF (dir.eq.3) CALL tdslv(dx,n,lzf,nd) ! z-direction END SUBROUTINE dfnonp SUBROUTINE dfp(n,h,x,dx,nd,dir) INTEGER,INTENT(IN) :: n,nd,dir real(real64),INTENT(IN) :: h real(real64),INTENT(IN),DIMENSION(nd,n) :: x real(real64),INTENT(OUT),DIMENSION(nd,n) :: dx INTEGER :: i,j real(real64) :: r1,r2,h1 ! print *, "dfnonp received (nd,n)", nd, n h1=1./h r1=7./3. r2=1./12. DO j=1,nd dx(j,n-1)=(r1*(x(j,n)-x(j,n-2))+r2*(x(j,1)-x(j,n-3))) dx(j,n)=(r1*(x(j,1)-x(j,n-1))+r2*(x(j,2)-x(j,n-2))) dx(j,1)=(r1*(x(j,2)-x(j,n))+r2*(x(j,3)-x(j,n-1))) dx(j,2)=(r1*(x(j,3)-x(j,1))+r2*(x(j,4)-x(j,n))) dx(j,n-1)=dx(j,n-1)*h1 dx(j,n)=dx(j,n)*h1 dx(j,1)=dx(j,1)*h1 dx(j,2)=dx(j,2)*h1 ENDDO DO i=3,n-2 DO j=1,nd dx(j,i)=(r1*(x(j,i+1)-x(j,i-1))+r2*(x(j,i+2)-x(j,i-2))) dx(j,i)=dx(j,i)*h1 ENDDO ENDDO IF (dir.eq.1) CALL ptdslv(dx,n,lxf,wxf,nd) ! x-direction IF (dir.eq.2) CALL ptdslv(dx,n,lyf,wyf,nd) ! y-direction IF (dir.eq.3) CALL ptdslv(dx,n,lzf,wzf,nd) ! z-direction END SUBROUTINE dfp SUBROUTINE ptdslv(r,n,l,w,nd) INTEGER,INTENT(IN) :: n,nd real(real64),INTENT(INOUT),DIMENSION(nd,n) :: r real(real64),INTENT(IN),DIMENSION(n) :: l,w INTEGER i,j real(real64), DIMENSION(nd) :: sum DO j=1,nd sum(j)=w(1)*r(j,1) r(j,1)=r(j,1)*l(1) ENDDO DO i=2,n-1 DO j=1,nd r(j,i)=r(j,i)-r(j,i-1) sum(j)=sum(j)+w(i)*r(j,i) r(j,i)=r(j,i)*l(i) ENDDO ENDDO DO j=1,nd r(j,n)=l(n)*(r(j,n)-sum(j)) r(j,n-1)=r(j,n-1)-w(n-1)*r(j,n) ENDDO DO i=n-2,1,-1 DO j=1,nd r(j,i)=r(j,i)-l(i)*r(j,i+1)-w(i)*r(j,n) ENDDO ENDDO END SUBROUTINE ptdslv SUBROUTINE d2fp(n,h,x,dx,nd,dir) INTEGER,INTENT(IN) :: n,nd,dir real(real64),INTENT(IN) :: h real(real64),INTENT(IN),DIMENSION(nd,n) :: x real(real64),INTENT(OUT),DIMENSION(nd,n) :: dx INTEGER :: i,j real(real64) :: h2,r1,r2,t1,t2 h2=1./(h*h) r1=6. r2=3./8. DO j=1,nd t1 = (x(j,n)-2.*x(j,n-1)+x(j,n-2)) t2 = (x(j,1)-2.*x(j,n-1)+x(j,n-3)) IF (x(j,n).eq.x(j,n-1).and.x(j,n-1).eq.x(j,n-2)) t1=0. IF (x(j,1).eq.x(j,n-1).and.x(j,n-1).eq.x(j,n-3)) t2=0. dx(j,n-1)=(r1*t1+r2*t2) t1 = (x(j,1)-2.*x(j,n)+x(j,n-1)) t2 = (x(j,2)-2.*x(j,n)+x(j,n-2)) IF (x(j,1).eq.x(j,n).and.x(j,n).eq.x(j,n-1)) t1=0. IF (x(j,2).eq.x(j,n).and.x(j,n).eq.x(j,n-2)) t2=0. ! dx(j,n)=(r1*(x(j,1)-2.*x(j,n)+x(j,n-1)) & ! +r2*(x(j,2)-2.*x(j,n)+x(j,n-2))) dx(j,n)=(r1*t1+r2*t2) t1 = (x(j,2)-2.*x(j,1)+x(j,n)) t2 = (x(j,3)-2.*x(j,1)+x(j,n-1)) IF (x(j,2).eq.x(j,1).and.x(j,1).eq.x(j,n)) t1=0. IF (x(j,3).eq.x(j,1).and.x(j,1).eq.x(j,n-1)) t2=0. ! dx(j,1)=(r1*(x(j,2)-2.*x(j,1)+x(j,n)) & ! +r2*(x(j,3)-2.*x(j,1)+x(j,n-1))) dx(j,1)=(r1*t1+r2*t2) t1 = (x(j,3)-2.*x(j,2)+x(j,1)) t2 = (x(j,4)-2.*x(j,2)+x(j,n)) IF (x(j,3).eq.x(j,2).and.x(j,2).eq.x(j,1)) t1=0. IF (x(j,4).eq.x(j,2).and.x(j,2).eq.x(j,n)) t2=0. ! dx(j,2)=(r1*(x(j,3)-2.*x(j,2)+x(j,1)) & ! +r2*(x(j,4)-2.*x(j,2)+x(j,n))) dx(j,2)=(r1*t1+r2*t2) dx(j,n-1)=dx(j,n-1)*h2 dx(j,n)=dx(j,n)*h2 dx(j,1)=dx(j,1)*h2 dx(j,2)=dx(j,2)*h2 ENDDO DO i=3,n-2 DO j=1,nd t1 = (x(j,i+1)-2.*x(j,i)+x(j,i-1)) t2 = (x(j,i+2)-2.*x(j,i)+x(j,i-2)) IF (x(j,i+1).eq.x(j,i).and.x(j,i).eq.x(j,i-1)) t1=0. IF (x(j,i+2).eq.x(j,i).and.x(j,i).eq.x(j,i-2)) t2=0. ! dx(j,i)=(r1*(x(j,i+1)-2.*x(j,i)+x(j,i-1)) & ! +r2*(x(j,i+2)-2.*x(j,i)+x(j,i-2))) dx(j,i)=(r1*t1+r2*t2) dx(j,i)=dx(j,i)*h2 ENDDO ENDDO IF (dir.eq.1) CALL ptdslv(dx,n,lxs,wxs,nd) ! x-direction IF (dir.eq.2) CALL ptdslv(dx,n,lys,wys,nd) ! y-direction IF (dir.eq.3) CALL ptdslv(dx,n,lzs,wzs,nd) ! z-direction END SUBROUTINE d2fp SUBROUTINE tdslv(r,n,l,nd) INTEGER,INTENT(IN) :: n,nd real(real64),INTENT(INOUT),DIMENSION(nd,n) :: r real(real64),INTENT(IN),DIMENSION(n) :: l INTEGER i,j real(real64) t1 DO j=1,nd r(j,1)=r(j,1)*l(1) ENDDO DO i=2,n DO j=1,nd t1=r(j,i)-r(j,i-1) r(j,i)=l(i)*t1 ENDDO ENDDO DO i=n-1,1,-1 DO j=1,nd r(j,i)=r(j,i)-l(i)*r(j,i+1) ENDDO ENDDO END SUBROUTINE tdslv SUBROUTINE d2fnonp(n,h,x,dx,nd,dir) INTEGER,INTENT(IN) :: n,nd,dir real(real64),INTENT(IN) :: h real(real64),INTENT(IN),DIMENSION(nd,n) :: x real(real64),INTENT(OUT),DIMENSION(nd,n) :: dx INTEGER :: i,j real(real64) :: h2,r1,r2,r3,a,b,c,e,t1,t2 h2=1./(h*h) r1=6. r2=3./8. r3=12. a=13./11. b=-27./11. c=15./11. e=-1./11. DO j=1,nd dx(j,1)=(a*x(j,1)+b*x(j,2)+c*x(j,3)+e*x(j,4)) dx(j,2)=(x(j,3)-2.*x(j,2)+x(j,1)) dx(j,n-1)=(x(j,n)-2.*x(j,n-1)+x(j,n-2)) dx(j,n)=(a*x(j,n)+b*x(j,n-1)+c*x(j,n-2)+e*x(j,n-3)) IF (x(j,1).eq.x(j,2).and.x(j,2).eq.x(j,3).and.x(j,3).eq.x(j,4)) dx(j,1)=0. IF (x(j,3).eq.x(j,2).and.x(j,2).eq.x(j,1)) dx(j,2)=0. IF (x(j,n).eq.x(j,n-1).and.x(j,n-1).eq.x(j,n-2).and.x(j,n-2).eq.x(j,n-3)) dx(j,n)=0. IF (x(j,n).eq.x(j,n-1).and.x(j,n-1).eq.x(j,n-2)) dx(j,n-1)=0. dx(j,1)=dx(j,1)*h2 dx(j,2)=dx(j,2)*h2*r3 dx(j,n-1)=dx(j,n-1)*h2*r3 dx(j,n)=dx(j,n)*h2 ENDDO DO i=3,n-2 DO j=1,nd t1 = (x(j,i+1)-2.*x(j,i)+x(j,i-1)) t2 = (x(j,i+2)-2.*x(j,i)+x(j,i-2)) IF (x(j,i+1).eq.x(j,i).and.x(j,i).eq.x(j,i-1)) t1=0. IF (x(j,i+2).eq.x(j,i).and.x(j,i).eq.x(j,i-2)) t2=0. ! dx(j,i)=(r1*(x(j,i+1)-2.*x(j,i)+x(j,i-1)) & ! +r2*(x(j,i+2)-2.*x(j,i)+x(j,i-2))) dx(j,i)=(r1*t1+r2*t2) dx(j,i)=dx(j,i)*h2 ENDDO ENDDO IF (dir.eq.1) CALL tdslv(dx,n,lxs,nd) ! x-direction IF (dir.eq.2) CALL tdslv(dx,n,lys,nd) ! y-direction IF (dir.eq.3) CALL tdslv(dx,n,lzs,nd) ! z-direction END SUBROUTINE d2fnonp END MODULE Compact