PROGRAM ELLIPTIC_SOLVER
USE NRTYPE; USE NRUTIL, ONLY:NRERROR
IMPLICIT NONE

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!*************            VARIABLES AND PARAMETERS            *************!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

INTEGER(I4B)::i,j,k,l
INTEGER(I4B),PARAMETER::N=21,MAXITRS=20,maxw=501
REAL(DP)::DX,DY,w,ERROR
REAL(DP), PARAMETER::deltaw=0.002
REAL(DP),DIMENSION(:,:),ALLOCATABLE::X,Y,S,U0,UOLD,UNEW,R
ALLOCATE(X(N,N),Y(N,N),S(N,N),U0(N,N),UOLD(N,N),UNEW(N,N),R(N,N))

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!*************                   GRID SIZING                  *************!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

CALL GRID(X,Y,DX,DY,N)
    WRITE(*,*)'The Matrix X(i,j) is given as:'
    DO i=LBOUND(X,1),UBOUND(X,1)
        WRITE(*,*) (X(i,j), j=LBOUND(X,2),UBOUND(X,2))
    END DO
    
    WRITE(*,*)'The Matrix Y(i,j) is given as:'
    DO i=LBOUND(Y,1),UBOUND(Y,1)
        WRITE(*,*) (Y(i,j), j=LBOUND(Y,2),UBOUND(Y,2))
    END DO
    
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!*************               ANALYTICAL SOLUTION              *************!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

DO j=1,N
    DO i=1,N
        U0(i,j)=(X(i,j))**2-(Y(i,j))**2
    END DO
END DO

    WRITE(*,*)'The Analytical Solution of U0(i,j) is given as:'
    DO i=LBOUND(U0,1),UBOUND(U0,1)
        WRITE(*,*)(U0(i,j), j=LBOUND(U0,2),UBOUND(U0,2))
    END DO
    
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!*************         INITIAL AND BOUNDARY CONDITIONS        *************!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

DO i=1,N
    S(i,1)=U0(i,1)              !Boundary Condition from Analytical Solution
    S(i,N)=U0(i,N)              !Boundary Condition from Analytical Solution
END DO

DO j=1,N
    S(1,j)=U0(1,j)              !Boundary Condition from Analytical Solution
    S(N,j)=U0(N,j)              !Boundary Condition from Analytical Solution
END DO

DO i=2,N-1
    DO j=2,N-1
        S(i,j)=0.0                               !Initial-Value from Problem
    END DO
END DO

DO i=1,N
    DO j=1,N
        UOLD(i,j)=S(i,j)           !Equating Values from S(i,j) to UOLD(i,j)
    END DO
END DO

    WRITE(*,*)'The Initial Value Matrix UOLD(i,j) is given as:'
    DO i=LBOUND(UOLD,1),UBOUND(UOLD,1)
      WRITE(*,*) (UOLD(i,j), j=LBOUND(UOLD,2),UBOUND(UOLD,2))
    END DO
    
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!*************               NUMERICAL SOLUTION               *************!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

DO i=1,N                                !Boundary Conditions Remain Constant
    UNEW(i,1) = UOLD(i,1)
    UNEW(i,N) = UOLD(i,N)
END DO

DO j=1,N        
    UNEW(1,j) = UOLD(1,j)
    UNEW(N,j) = UOLD(N,j)
END DO

UNEW(i,j)=UOLD(i,j)
ERROR=0.0
w=1.0
DO l=1,maxw
    DO k=1,MAXITRS
        DO j=2,N-1
            DO i=2,N-1
                CALL PSOR(UOLD,UNEW)
                    CALL ERROR_CALCULATION(UOLD,UNEW,ERROR)
                        CALL RR(UNEW,U0,R)
                           
                UOLD(i,j)=UNEW(i,j)
            END DO
        END DO
         WRITE(*,*)'The Initial Value Matrix R(i,j) is given as:'
                            DO i=LBOUND(R,1),UBOUND(R,1)
                            WRITE(*,*) (R(i,j), j=LBOUND(R,2),UBOUND(R,2))
                            END DO
    END DO
    WRITE(*,*)w,ERROR
    ERROR=0.0
    w=w+deltaw
END DO

CONTAINS

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!*************                   SUBROUTINES                  *************!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

SUBROUTINE GRID(X,Y,DX,DY,N)
USE NRTYPE; USE NRUTIL, ONLY:NRERROR
IMPLICIT NONE
    INTEGER(I4B)::i,j
    REAL(DP), DIMENSION(N,N)::X,Y
    INTEGER(I4B),INTENT(IN)::N
    REAL(DP),INTENT(OUT)::DX,DY
    
    DX=1.0/(FLOAT(N)-1.0)
    DY=1.0/(FLOAT(N)-1.0)
    
    DO j=1,N
        DO i=1,N
            X(i,j)=(i-1)*DX
            Y(i,j)=(j-1)*DY
        END DO
    END DO
END SUBROUTINE GRID

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

SUBROUTINE PSOR(UOLD,UNEW)
USE NRTYPE; USE NRUTIL, ONLY:NRERROR
IMPLICIT NONE
    REAL(DP)::SUM1
    REAL(DP),DIMENSION(N,N),INTENT(IN)::UOLD
    REAL(DP),DIMENSION(N,N),INTENT(OUT)::UNEW

SUM1=0.25*(UOLD(i+1,j)+UNEW(i-1,j)+UOLD(i,j+1)+UNEW(i,j-1)-(4.0*UOLD(i,j)))
UNEW(i,j)=UOLD(i,j)+(w*SUM1)
END SUBROUTINE PSOR

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

SUBROUTINE ERROR_CALCULATION(UOLD,UNEW,ERROR)
USE NRTYPE; USE NRUTIL, ONLY:NRERROR
IMPLICIT NONE
    REAL(DP),DIMENSION(N,N),INTENT(IN)::UOLD,UNEW
    REAL(DP),INTENT(OUT)::ERROR

    ERROR=ERROR+ABS(UNEW(i,j)-UOLD(i,j))
END SUBROUTINE ERROR_CALCULATION

SUBROUTINE RR(UNEW,U0,R)
USE NRTYPE; USE NRUTIL, ONLY:NRERROR
IMPLICIT NONE
    REAL(DP),DIMENSION(N,N),INTENT(IN)::U0,UNEW
    REAL(DP),DIMENSION(N,N),INTENT(OUT)::R
R(i,j)=U0(i,j)-UNEW(i,j)
END SUBROUTINE RR

END PROGRAM ELLIPTIC_SOLVER