!***********************************************************************
!* GNU Lesser General Public License
!*
!* This file is part of the GFDL Flexible Modeling System (FMS).
!*
!* FMS is free software: you can redistribute it and/or modify it under
!* the terms of the GNU Lesser General Public License as published by
!* the Free Software Foundation, either version 3 of the License, or (at
!* your option) any later version.
!*
!* FMS is distributed in the hope that it will be useful, but WITHOUT
!* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
!* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
!* for more details.
!*
!* You should have received a copy of the GNU Lesser General Public
!* License along with FMS. If not, see .
!***********************************************************************
#ifdef SYSTEM_CLOCK
#undef SYSTEM_CLOCK
#endif
program test !test various aspects of mpp_mod
#include
#ifdef sgi_mipspro
use shmem_interface
#endif
use mpp_mod, only : mpp_init, mpp_exit, mpp_pe, mpp_npes, mpp_root_pe, stdout
use mpp_mod, only : mpp_clock_id, mpp_clock_begin, mpp_clock_end, mpp_sync, mpp_malloc
use mpp_mod, only : mpp_declare_pelist, mpp_set_current_pelist, mpp_set_stack_size
use mpp_mod, only : mpp_broadcast, mpp_transmit, mpp_sum, mpp_max, mpp_chksum, ALL_PES
use mpp_mod, only : mpp_gather, mpp_error, FATAL, mpp_sync_self
use mpp_io_mod, only: mpp_io_init, mpp_flush
#ifdef use_MPI_GSM
use mpp_mod, only : mpp_gsm_malloc, mpp_gsm_free
#endif
implicit none
integer, parameter :: n=1048576
real, allocatable, dimension(:) :: a, b, c
#ifdef use_MPI_GSM
real :: d(n)
pointer (locd, d)
#else
real, allocatable, dimension(:) :: d
integer(LONG_KIND) :: locd
#endif
integer :: tick, tick0, ticks_per_sec, id
integer :: pe, npes, root, i, j, k, l, m, n2, istat
integer :: out_unit
real :: dt
call mpp_init()
call mpp_io_init()
call mpp_set_stack_size(3145746)
pe = mpp_pe()
npes = mpp_npes()
root = mpp_root_pe()
out_unit = stdout()
if( pe.EQ.root ) print *, '------------------> Calling test_gather <------------------'
call test_gather(npes,pe,root,out_unit)
call test_gatherV(npes,pe,root,out_unit)
call test_gather2DV(npes,pe,root,out_unit)
if( pe.EQ.root ) print *, '------------------> Finished test_gather <------------------'
if( pe.EQ.root ) print *, '------------------> Calling test_broadcast <------------------'
call test_broadcast_2D()
call test_broadcast_char()
if( pe.EQ.root ) print *, '------------------> Finished test_broadcast <------------------'
call SYSTEM_CLOCK( count_rate=ticks_per_sec )
if( pe.EQ.root ) print *, '------------------> Calling test_time_transmit <------------------'
call test_time_transmit()
if( pe.EQ.root ) print *, '------------------> Finished test_time_transmit <------------------'
if( pe.EQ.root ) print *, '------------------> Calling test_mpp_sum <------------------'
call test_mpp_sum()
if( pe.EQ.root ) print *, '------------------> Finished test_mpp_sum <------------------'
if( pe.EQ.root ) print *, '------------------> Calling test_mpp_max <------------------'
call test_mpp_max()
if( pe.EQ.root ) print *, '------------------> Finished test_mpp_max <------------------'
if( pe.EQ.root ) print *, '------------------> Calling test_mpp_chksum <------------------'
call test_mpp_chksum()
if( pe.EQ.root ) print *, '------------------> Finished test_mpp_chksum <------------------'
!test of pointer sharing
#ifdef use_MPI_GSM
call mpp_gsm_malloc( locd, sizeof(d) )
#else
if( pe.EQ.root )then
allocate( d(n) )
locd = LOC(d)
end if
call mpp_broadcast(locd,root)
#endif
if( pe.EQ.root )then
call random_number(d)
end if
call mpp_sync()
! call test_shared_pointers(locd,n)
#ifdef use_MPI_GSM
call mpp_gsm_free( locd )
#else
if( pe.EQ.root )then
deallocate( d )
end if
#endif
call mpp_exit()
contains
!***********************************************
!currently only test the mpp_broadcast_char
subroutine test_broadcast_char()
integer, parameter :: ARRAYSIZE = 3
integer, parameter :: STRINGSIZE = 256
character(len=STRINGSIZE), dimension(ARRAYSIZE) :: textA, textB
integer :: n
textA(1) = "This is line 1 "
textA(2) = "Here comes the line 2 "
textA(3) = "Finally is line 3 "
do n = 1, ARRAYSIZE
textB(n) = TextA(n)
enddo
if(mpp_pe() .NE. mpp_root_pe()) then
do n =1, ARRAYSIZE
textA(n) = ""
enddo
endif
!--- comparing textA and textB. textA and textB are supposed to be different on pe other than root_pe
if(mpp_pe() == mpp_root_pe()) then
do n = 1, ARRAYSIZE
if(textA(n) .NE. textB(n)) call mpp_error(FATAL, "test_broadcast: on root_pe, textA should equal textB")
enddo
else
do n = 1, ARRAYSIZE
if(textA(n) == textB(n)) call mpp_error(FATAL, "test_broadcast: on root_pe, textA should not equal textB")
enddo
endif
call mpp_broadcast(textA, STRINGSIZE, mpp_root_pe())
!--- after broadcast, textA and textB should be the same
do n = 1, ARRAYSIZE
if(textA(n) .NE. textB(n)) call mpp_error(FATAL, "test_broadcast: after broadcast, textA should equal textB")
enddo
write(out_unit,*) "==> NOTE from test_broadcast_char: The test is succesful"
end subroutine test_broadcast_char
subroutine test_broadcast_2D()
integer, parameter :: ARRAYSIZE = 3
integer :: n, m, p
real :: r(3,3), k(3,3)
p=0;
do n = 1, ARRAYSIZE
do m = 1, ARRAYSIZE
p = p + 1
k(n, m) = p
r(n, m) = k(n, m)
enddo
enddo
if(mpp_pe() .NE. mpp_root_pe()) then
do n =1, ARRAYSIZE
r(:, n) = 0
enddo
endif
!--- comparing array m and n. m and n are supposed to be different on pe other than root_pe
if(mpp_pe() == mpp_root_pe()) then
do n = 1, ARRAYSIZE
do m = 1, ARRAYSIZE
if(r(n, m) .NE. k(n, m)) call mpp_error(FATAL, "test_broadcast: on root_pe, m should equal n")
enddo
enddo
else
do n = 1, ARRAYSIZE
do m = 1, ARRAYSIZE
if(r(n, m) == k(n, m)) call mpp_error(FATAL, "test_broadcast: on root_pe, m should not equal n")
enddo
enddo
endif
call mpp_broadcast(r, ARRAYSIZE*ARRAYSIZE, mpp_root_pe())
!--- after broadcast, m and n should be the same
do n = 1, ARRAYSIZE
do m =1, ARRAYSIZE
if(r(n, m) .NE. k(n, m)) call mpp_error(FATAL, "test_broadcast: after broadcast, m should equal n")
enddo
enddo
write(out_unit,*) "==> NOTE from test_broadcast_2D: The test is succesful"
end subroutine test_broadcast_2D
subroutine test_gather(npes,pe,root,out_unit)
integer, intent(in) :: npes,pe,root,out_unit
integer :: pelist(npes)
integer :: i
real :: rdata(npes)
real :: val
if(npes < 3)then
call mpp_error(FATAL, "Test_gather: minimum of 3 ranks required. Not testing gather; too few ranks.")
endif
write(out_unit,*)
val = pe
rdata = -1.0
do i=1,npes
pelist(i) = i-1
enddo
call mpp_gather((/val/),rdata)
if(pe == root)then
do i=1,npes
if(INT(rdata(i)) /= pelist(i))then
write(6,*) "Gathered data ",INT(rdata(i)), " NE reference ",pelist(i), "at i=",i
call mpp_error(FATAL, "Test gather uniform vector with global pelist failed")
endif
enddo
endif
call mpp_sync()
write(out_unit,*) "Test gather uniform vector with global pelist successful"
rdata = -1.0
if(ANY(pe == pelist(2:npes)))call mpp_gather((/val/),rdata(2:npes),pelist(2:npes))
if(pe == pelist(2))then
do i=2,npes
if(INT(rdata(i)) /= pelist(i))then
write(6,*) "Gathered data ",INT(rdata(i)), " NE reference ",pelist(i), "at i=",i
call mpp_error(FATAL, "Test gather uniform vector with reduced pelist failed")
endif
enddo
endif
call mpp_sync()
write(out_unit,*) "Test gather uniform vector with reduced pelist successful"
end subroutine test_gather
subroutine test_gatherV(npes,pe,root,out_unit)
implicit none
integer, intent(in) :: npes,pe,root,out_unit
integer :: pelist(npes),rsize(npes)
integer :: i,j,k,dsize,ssize
real,allocatable :: sdata(:), rdata(:), ref(:)
if(npes < 3)then
call mpp_error(FATAL, "Test_gatherV: minimum of 3 ranks required. Not testing gather; too few ranks.")
elseif(npes > 9999)then
call mpp_error(FATAL, "Test_gatherV: maximum of 9999 ranks supported. Not testing gatherV; too many ranks.")
endif
write(out_unit,*)
ssize = pe+1
allocate(sdata(ssize))
do i=1,ssize
sdata(i) = pe + 0.0001*i
enddo
do i=1,npes
pelist(i) = i-1
rsize(i) = i
enddo
dsize = sum(rsize)
allocate(rdata(dsize),ref(dsize))
rdata = -1.0
k=1
do j=1,npes
do i=1,rsize(j)
ref(k) = pelist(j) + 0.0001*i
k = k+1
enddo;enddo
call mpp_gather(sdata,ssize,rdata,rsize)
if(pe == root)then
k = 1
do j=1,npes
do i=1,rsize(j)
if(rdata(k) /= ref(k))then
write(6,*) "Gathered data ",rdata(k), " NE reference ",ref(k), "at k=",k
call mpp_error(FATAL, "Test gatherV global pelist failed")
endif
k = k+1
enddo;enddo
endif
call mpp_sync()
write(out_unit,*) "Test gatherV with global pelist successful"
rdata = -1.0
ref(1) = -1.0
if(ANY(pe == pelist(2:npes)))call mpp_gather(sdata,ssize,rdata(2:),rsize(2:),pelist(2:npes))
if(pe == pelist(2))then
k = 1
do j=1,npes
do i=1,rsize(j)
if(rdata(k) /= ref(k))then
write(6,*) "Gathered data ",rdata(k), " NE reference ",ref(k), "at k=",k
call mpp_error(FATAL, "Test gatherV with reduced pelist failed")
endif
k = k+1
enddo;enddo
endif
call mpp_sync()
write(out_unit,*) "Test gatherV with reduced pelist successful"
deallocate(sdata,rdata,ref)
end subroutine test_gatherV
subroutine test_gather2DV(npes,pe,root,out_unit)
implicit none
integer, intent(in) :: npes,pe,root,out_unit
integer :: pelist(npes),rsize(npes)
integer :: pelist2(npes),rsize2(npes)
integer :: i,j,k,l,nz,ssize,nelems
real,allocatable,dimension(:,:) :: data, cdata, sbuff,rbuff
real,allocatable :: ref(:,:)
integer, parameter :: KSIZE=10
real :: sbuff1D(size(sbuff))
real :: rbuff1D(size(rbuff))
pointer(sptr,sbuff1D); pointer(rptr,rbuff1D)
if(npes < 3)then
call mpp_error(FATAL, "Test_gather2DV: minimum of 3 ranks required. Not testing gather; too few ranks.")
elseif(npes > 9999)then
call mpp_error(FATAL, "Test_gather2DV: maximum of 9999 ranks supported. Not testing gather2DV; too many ranks.")
return
endif
write(out_unit,*)
ssize = pe+1
allocate(data(ssize,KSIZE))
do k=1,KSIZE; do i=1,ssize
data(i,k) = 10000.0*k + pe + 0.0001*i
enddo; enddo
do i=1,npes
pelist(i) = i-1
rsize(i) = i
enddo
nz = KSIZE
nelems = sum(rsize(:))
allocate(rbuff(nz,nelems)); rbuff = -1.0
allocate(ref(nelems,nz),cdata(nelems,nz))
ref = 0.0; cdata = 0.0
if(pe == root)then
do k=1,KSIZE
l=1
do j=1,npes
do i=1,rsize(j)
ref(l,k) = 10000.0*k + pelist(j) + 0.0001*i
l = l+1
enddo; enddo;enddo
endif
allocate(sbuff(nz,ssize))
! this matrix inversion makes for easy gather to the IO root
! and a clear, concise unpack
do j=1,ssize
do i=1,nz
sbuff(i,j) = data(j,i)
enddo; enddo
! Note that the gatherV implied here is asymmetric; only root needs to know the vector of recv size
sptr = LOC(sbuff); rptr = LOC(rbuff)
call mpp_gather(sbuff1D,size(sbuff),rbuff1D,nz*rsize(:))
if(pe == root)then
do j=1,nz
do i=1,nelems
cdata(i,j) = rbuff(j,i)
enddo; enddo
do j=1,nz
do i=1,nelems
if(cdata(i,j) /= ref(i,j))then
write(6,*) "Gathered data ",cdata(i,j), " NE reference ",ref(i,j), "at i,j=",i,j
call mpp_error(FATAL, "Test gather2DV global pelist failed")
endif
enddo;enddo
endif
call mpp_sync()
write(out_unit,*) "Test gather2DV with global pelist successful"
do i=1,npes
pelist2(i) = pelist(npes-i+1)
rsize2(i) = rsize(npes-i+1)
enddo
rbuff = -1.0
ref = 0.0; cdata = 0.0
if(pe == pelist2(1))then
do k=1,KSIZE
l=1
do j=1,npes
do i=1,rsize2(j)
ref(l,k) = 10000.0*k + pelist2(j) + 0.0001*i
l = l+1
enddo; enddo;enddo
endif
call mpp_gather(sbuff1D,size(sbuff),rbuff1D,nz*rsize2(:),pelist2)
if(pe == pelist2(1))then
do j=1,nz
do i=1,nelems
cdata(i,j) = rbuff(j,i)
enddo; enddo
do j=1,nz
do i=1,nelems
if(cdata(i,j) /= ref(i,j))then
write(6,*) "Gathered data ",cdata(i,j), " NE reference ",ref(i,j), "at i,j=",i,j
call mpp_error(FATAL, "Test gather2DV with reversed pelist failed")
endif
enddo;enddo
endif
call mpp_sync()
write(out_unit,*) "Test gather2DV with reversed pelist successful"
deallocate(data,sbuff,rbuff,cdata,ref)
end subroutine test_gather2DV
subroutine test_time_transmit()
allocate( a(n), b(n) )
id = mpp_clock_id( 'Random number' )
call mpp_clock_begin(id)
call random_number(a)
call mpp_clock_end (id)
id = mpp_clock_id( 'mpp_transmit' )
call mpp_clock_begin(id)
!timing is done for cyclical pass (more useful than ping-pong etc)
l = n
do while( l.GT.0 )
!--- mpp_transmit -------------------------------------------------
call mpp_sync()
call SYSTEM_CLOCK(tick0)
do i = 1,npes
call mpp_transmit( put_data=a(1), plen=l, to_pe=modulo(pe+npes-i,npes), &
get_data=b(1), glen=l, from_pe=modulo(pe+i,npes) )
call mpp_sync_self()
end do
call mpp_sync()
call SYSTEM_CLOCK(tick)
dt = real(tick-tick0)/(npes*ticks_per_sec)
dt = max( dt, epsilon(dt) )
if( pe.EQ.root ) print *, 'MPP_TRANSMIT length, time, bw(Mb/s)=', l, dt, l*8e-6/dt
l = l/2
end do
end subroutine test_time_transmit
subroutine test_mpp_sum()
a = real(pe+1)
call mpp_sync()
call SYSTEM_CLOCK(tick0)
call mpp_sum(a(1:1000),1000)
call SYSTEM_CLOCK(tick)
dt = real(tick-tick0)/ticks_per_sec
dt = max( dt, epsilon(dt) )
if( pe.EQ.root )write( out_unit,'(a,2i6,f9.1,i8,f13.6,f8.2/)' ) &
'mpp_sum: pe, npes, sum(pe+1), length, time, bw(Mb/s)=', pe, npes, a(1), n, dt, n*8e-6/dt
call mpp_clock_end(id)
end subroutine test_mpp_sum
subroutine test_mpp_max
a = real(pe+1)
print *, 'pe, pe+1 =', pe, a(1)
call mpp_max( a(1) )
print *, 'pe, max(pe+1)=', pe, a(1)
!pelist check
call mpp_sync()
call flush(out_unit)
if( npes.GE.2 )then
if( pe.EQ.root )print *, 'Test of pelists: bcast, sum and max using PEs 0...npes-2 (excluding last PE)'
call mpp_declare_pelist( (/(i,i=0,npes-2)/) )
a = real(pe+1)
if( pe.NE.npes-1 )call mpp_broadcast( a, n, npes-2, (/(i,i=0,npes-2)/) )
print *, 'bcast(npes-1) from 0 to npes-2=', pe, a(1)
a = real(pe+1)
if( pe.NE.npes-1 )then
call mpp_set_current_pelist( (/(i,i=0,npes-2)/) )
id = mpp_clock_id( 'Partial mpp_sum' )
call mpp_clock_begin(id)
call mpp_sum( a(1:1000), 1000, (/(i,i=0,npes-2)/) )
call mpp_clock_end (id)
end if
if( pe.EQ.root )print *, 'sum(pe+1) from 0 to npes-2=', a(1)
a = real(pe+1)
if( pe.NE.npes-1 )call mpp_max( a(1), (/(i,i=0,npes-2)/) )
if( pe.EQ.root )print *, 'max(pe+1) from 0 to npes-2=', a(1)
end if
call mpp_set_current_pelist()
end subroutine test_mpp_max
subroutine test_mpp_chksum()
if( modulo(n,npes).EQ.0 )then !only set up for even division
n2 = 1024
a = 0.d0
if( pe.EQ.root )call random_number(a(1:n2))
call mpp_sync()
call mpp_transmit( put_data=a(1), plen=n2, to_pe=ALL_PES, &
get_data=a(1), glen=n2, from_pe=root )
call mpp_sync_self ()
m= n2/npes
allocate( c(m) )
c = a(pe*m+1:pe*m+m)
if( pe.EQ.root )then
print *
print *, '------------------ > Test mpp_chksum <------------------ '
print *, 'This test shows that a whole array and a distributed array give identical checksums.'
end if
if ( mpp_chksum(a(1:n2),(/pe/)) .NE. mpp_chksum(c) ) then
call mpp_error(FATAL, 'Test mpp_chksum fails: a whole array and a distributed array did not give identical checksums')
else
print *, 'For pe=', pe, ' chksum(a(1:1024))=chksum(c(1:1024))='
endif
else
call mpp_error(FATAL, 'Test mpp_chksum: cannot run this test since n cannot be evenly by npes')
end if
end subroutine test_mpp_chksum
subroutine test_shared_pointers(locd,n)
integer(LONG_KIND), intent(in) :: locd
integer :: n
real :: dd(n)
pointer( p, dd )
p = locd
print *, 'TEST_SHARED_POINTERS: pe, locd=', pe, locd
! print *, 'TEST_SHARED_POINTERS: pe, chksum(d)=', pe, mpp_chksum(dd,(/pe/))
print *, 'TEST_SHARED_POINTERS: pe, sum(d)=', pe, sum(dd)
return
end subroutine test_shared_pointers
end program test