| moomba |
February 17, 2010 03:37 |
Problem solve, here are the sources if it can help someone.
It is a simple conjugate gradient with neumann, periodic and dirichlet boundary conditions.
Code:
! Neumann at face 1.1
BoundCG(1,1) = 1
! Dirichlet at face 1.2
BoundCG(1,2) = -1
PRES(Grid%Nx(1),:,:) = 0.0d0
! periodic on axe y
BoundCG(2,1) = 12
BoundCG(2,2) = 12
CALL CG_CORE(Grid,PRES,BoundCG)
Code:
module CG
use typegrid
contains
subroutine CG_CORE(Grid,b,BoundCG)
use commonGC
implicit none
type (grid_type) :: Grid
real(8), dimension(1:Grid%Nx(1),1:Grid%Nx(2),1:Grid%Nx(3)), intent(inout) :: b
integer, dimension(1:3,1:2), intent(in) :: BoundCG
real(8), dimension(1:Grid%Nx(1),1:Grid%Nx(2),1:Grid%Nx(3)) :: r,p,q,w
real(8), dimension(1:3) :: SDx
real(8) :: rho,rhop1,beta,alpha,res,a
integer :: i,n
! BoundCG
! 10 = simple com
! 12 = periodic com
! 1 = inflow (dP/dn = 0)
! -1 = outflow (P = fixed)
! Dirichlet Boundary Conditions, set fixed value in initial guess, this values will not be overwritten during the CG process
if(BoundCG(1,1) == -1) x(1,:,:) = b(1,:,:)
if(BoundCG(1,2) == -1) x(Grid%Nx(1),:,:) = b(Grid%Nx(1),:,:)
if(Grid%ndim > 1) then
if(BoundCG(2,1) == -1) x(:,1,:) = b(:,1,:)
if(BoundCG(2,2) == -1) x(:,Grid%Nx(2),:) = b(:,Grid%Nx(2),:)
end if
if(Grid%ndim > 2) then
if(BoundCG(3,1) == -1) x(:,:,1) = b(:,:,1)
if(BoundCG(3,2) == -1) x(:,:,Grid%Nx(3)) = b(:,:,Grid%Nx(3))
end if
CALL Matrix_A_Multiply(Grid,x,r,BoundCG) ! (x_in,x_out) --> x_out = A * x_in
r(:,:,:) = b(:,:,:) - r(:,:,:)
! Dirichlet Boundary Conditions, set residut at 0.0 (value is already correct)
if(BoundCG(1,1) == -1) r(1,:,:) = 0.0d0
if(BoundCG(1,2) == -1) r(Grid%Nx(1),:,:) = 0.0d0
if(Grid%ndim > 1) then
if(BoundCG(2,1) == -1) r(:,1,:) = 0.0d0
if(BoundCG(2,2) == -1) r(:,Grid%Nx(2),:) = 0.0d0
end if
if(Grid%ndim > 2) then
if(BoundCG(3,1) == -1) r(:,:,1) = 0.0d0
if(BoundCG(3,2) == -1) r(:,:,Grid%Nx(3)) = 0.0d0
end if
p(:,:,:) = 0.0d0
beta = 0.0d0
! PRECONDITIONNEMENT
do n=1,Grid%ndim
SDx(n)=(1.0d0/(Grid%h(n)*Grid%h(n)))
end do
a=1.0d0/(-2.0d0*SDx(1)-2.0d0*SDx(2))
w(:,:,:) = a * r(:,:,:)
rho = Scalar_Product(Grid,r,w) ! return v(1)*r(1) + v(2)*r(2) + v(3)*r(3) + ... + v(n)*r(n)
do i=1,2000
p(:,:,:) = w(:,:,:) + beta * p(:,:,:)
CALL Matrix_A_Multiply(Grid,p,q,BoundCG)
alpha = rho / Scalar_Product(Grid,p,q)
x(:,:,:) = x(:,:,:) + alpha * p(:,:,:)
r(:,:,:) = r(:,:,:) - alpha * q(:,:,:)
res = Residut(Grid,r)
if( res < 1e-6 ) then
print *, "GC converged in",i,"IT with a residut of",res
b(:,:,:) = x(:,:,:)
return
end if
! print *,res
w(:,:,:) = a * r(:,:,:)
rhop1 = Scalar_Product(Grid,r,w)
beta = rhop1 / rho
rho = rhop1
end do
print *,"ERROR, CG not converged"
end subroutine CG_CORE
subroutine Matrix_A_Multiply(Grid,p,q,BoundCG)
implicit none
type (grid_type) :: Grid
real(8), dimension(1:Grid%Nx(1),1:Grid%Nx(2),1:Grid%Nx(3)), intent(in) :: p
real(8), dimension(1:Grid%Nx(1),1:Grid%Nx(2),1:Grid%Nx(3)), intent(out) :: q
integer, dimension(1:3,1:2), intent(in) :: BoundCG
real(8), dimension(0:Grid%Nx(1)+1,0:Grid%Nx(2)+1,0:Grid%Nx(3)+1) :: pl ! copy with ghost, not optimized
real(8), dimension(1:3) :: SDx
integer :: i,j,n
pl=77.0
pl(1:Grid%Nx(1),1:Grid%Nx(2),1) = p(1:Grid%Nx(1),1:Grid%Nx(2),1)
! Neumann Boundaries, dP/dn = 0 , not optimized (should not copy, but simply not compute)
if(BoundCG(1,1) == 1) pl(0,:,:) = pl(2,:,:)
if(BoundCG(1,2) == 1) pl(Grid%Nx(1)+1,:,:) = pl(Grid%Nx(1)-1,:,:)
if(Grid%ndim > 1) then
if(BoundCG(2,1) == 1) pl(:,0,:) = pl(:,2,:)
if(BoundCG(2,2) == 1) pl(:,Grid%Nx(2)+1,:) = pl(:,Grid%Nx(2)-1,:)
end if
if(Grid%ndim > 2) then
if(BoundCG(3,1) == 1) pl(:,:,0) = pl(:,:,2)
if(BoundCG(3,2) == 1) pl(:,:,Grid%Nx(3)+1) = pl(:,:,Grid%Nx(3)-1)
end if
! Periodic Boundaries
if(BoundCG(1,1) == 12) pl(0,:,:) = pl(Grid%Nx(1)-1,:,:)
if(BoundCG(1,2) == 12) pl(Grid%Nx(1)+1,:,:) = pl(2,:,:)
if(Grid%ndim > 1) then
if(BoundCG(2,1) == 12) pl(:,0,:) = pl(:,Grid%Nx(2)-1,:)
if(BoundCG(2,2) == 12) pl(:,Grid%Nx(2)+1,:) = pl(:,2,:)
end if
if(Grid%ndim > 2) then
if(BoundCG(3,1) == 12) pl(:,:,0) = pl(:,:,Grid%Nx(3)-1)
if(BoundCG(3,2) == 12) pl(:,:,Grid%Nx(3)+1) = pl(:,:,2)
end if
do n=1,2
SDx(n)=(1.0/(Grid%h(n)*Grid%h(n)))
end do
do j=1,Grid%Nx(2)
do i=1,Grid%Nx(1)
q(i,j,1)=SDx(1)*(pl(i-1,j,1)+pl(i+1,j,1)-2.0*pl(i,j,1)) + &
& SDx(2)*(pl(i,j-1,1)+pl(i,j+1,1)-2.0*pl(i,j,1))
end do
end do
! Dirichelt boundaries, set q at 0 (to not alter the x fixed value)
if(BoundCG(1,1) == -1) q(1,:,:) = 0.0d0
if(BoundCG(1,2) == -1) q(Grid%Nx(1),:,:) = 0.0d0
if(Grid%ndim > 1) then
if(BoundCG(2,1) == -1) q(:,1,:) = 0.0d0
if(BoundCG(2,2) == -1) q(:,Grid%Nx(2),:) = 0.0d0
end if
if(Grid%ndim > 2) then
if(BoundCG(3,1) == -1) q(:,:,1) = 0.0d0
if(BoundCG(3,2) == -1) q(:,:,Grid%Nx(3)) = 0.0d0
end if
return
end subroutine Matrix_A_Multiply
function Residut(Grid,r)
implicit none
type (grid_type) :: Grid
real(8), dimension(1:Grid%Nx(1),1:Grid%Nx(2),1:Grid%Nx(3)), intent(in) :: r
real(8) :: Residut
integer :: i,j,k
Residut=0.0d0
do k=1,Grid%Nx(3)
do j=1,Grid%Nx(2)
do i=1,Grid%Nx(1)
Residut = max ( Residut, ABS(r(i,j,k)) )
end do
end do
end do
return
end function Residut
function Scalar_Product(Grid,x1,x2)
implicit none
type (grid_type) :: Grid
real(8), dimension(1:Grid%Nx(1),1:Grid%Nx(2),1:Grid%Nx(3)), intent(in) :: x1,x2
real(8) :: Scalar_Product
integer :: i,j,k
Scalar_Product=0.0d0
do k=1,Grid%Nx(3)
do j=1,Grid%Nx(2)
do i=1,Grid%Nx(1)
Scalar_Product = Scalar_Product + x1(i,j,k) * x2(i,j,k)
end do
end do
end do
return
end function Scalar_Product
end module CG
Code:
module commonGC
implicit none
real(8), allocatable, dimension(:,:,:), save :: x
end module commonGC
If used, you need to allocate x, and set it to 0.0 before the first time you use the routine, because the CG use the last solution calculated at the previous time as initial guess.
This program is not very efficient, but it is simple and a may help to start with CG.
With my best regards
|