module dyn_grid 28,4
!-----------------------------------------------------------------------
!
! Purpose: Definition of dynamics computational grid.
!
! Method: Variables are private; interface routines used to extract
! information for use in user code. Global column index range
! defined using full (unreduced) grid.
!
! Entry points:
! get_block_bounds_d get first and last indices in global
! block ordering
! get_block_gcol_d get column indices for given block
! get_block_gcol_cnt_d get number of columns in given block
! get_block_lvl_cnt_d get number of vertical levels in column
! get_block_levels_d get vertical levels in column
! get_gcol_block_d get global block indices and local columns
! index for given global column index
! get_gcol_block_cnt_d get number of blocks containing data
! from a given global column index
! get_block_owner_d get process "owning" given block
! get_horiz_grid_d get horizontal grid coordinates
! get_horiz_grid_dim_d get horizontal dimensions of dynamics grid
!
! Author: John Drake and Patrick Worley
!
!-----------------------------------------------------------------------
use shr_kind_mod
, only: r8 => shr_kind_r8
use pmgrid
, only: plev
use abortutils
, only: endrun
use cam_logfile
, only: iulog
integer, private :: ngcols_d = 0 ! number of dynamics columns
! WS 2006.04.12: moved ptimelevels here from prognostics, which is gone
integer, parameter :: ptimelevels = 2 ! number of time levels in the dycore
real(r8), parameter :: D0_5 = 0.5_r8
real(r8), parameter :: D90_0 = 90.0_r8
real(r8), parameter :: D360_0 = 360.0_r8
contains
!========================================================================
!
Subroutine get_block_bounds_d(block_first,block_last) 3,1
!-----------------------------------------------------------------------
!
!
! Purpose: Return first and last indices used in global block ordering
!
! Method:
!
! Author: Patrick Worley
!
!-----------------------------------------------------------------------
use pmgrid
, only: plat, spmd_on, nprxy_x, nprxy_y
implicit none
!------------------------------Arguments--------------------------------
integer, intent(out) :: block_first ! first (global) index used for blocks
integer, intent(out) :: block_last ! last (global) index used for blocks
!-----------------------------------------------------------------------
! latitude slice block
block_first = 1
if (spmd_on .eq. 1) then
! Assume 1 block per subdomain
block_last = nprxy_x*nprxy_y
else
block_last = plat
endif
return
end subroutine get_block_bounds_d
!
!========================================================================
!
subroutine get_block_gcol_d(blockid,size,cdex) 5,6
!-----------------------------------------------------------------------
!
!
! Purpose: Return list of dynamics column indices in given block
!
! Method:
!
! Author: Patrick Worley
!
!-----------------------------------------------------------------------
use rgrid
, only: nlon
use pmgrid
, only: spmd_on, plon, nprxy_x
use spmd_utils
, only: iam
#if ( defined SPMD )
use spmd_dyn
, only: lonrangexy, latrangexy, npes_xy
#endif
implicit none
!------------------------------Arguments--------------------------------
integer, intent(in) :: blockid ! global block id
integer, intent(in) :: size ! array size
integer, intent(out):: cdex(size) ! global column indices
!---------------------------Local workspace-----------------------------
!
integer i,j ! block coordinates
integer blksiz ! block size
integer k,l ! loop indices
integer n ! column index
!-----------------------------------------------------------------------
if (spmd_on .eq. 1) then
j = (blockid-1) / nprxy_x + 1
i = blockid - (j-1) * nprxy_x
#if ( defined SPMD )
blksiz = (lonrangexy(2,i)-lonrangexy(1,i)+1) * &
(latrangexy(2,j)-latrangexy(1,j)+1)
if (size < blksiz) then
write(iulog,*)'GET_BLOCK_GCOL_D: array not large enough (', &
size,' < ',blksiz,' ) '
call endrun
else
n = 0
do k=latrangexy(1,j),latrangexy(2,j)
do l=lonrangexy(1,i),lonrangexy(2,i)
n = n + 1
cdex(n) = l + (k-1)*plon
enddo
enddo
endif
#endif
else
if (size < nlon(blockid)) then
write(iulog,*)'GET_BLOCK_GCOL_D: array not large enough (', &
size,' < ',nlon(blockid),' ) '
call endrun
else
n = (blockid-1)*plon
do i = 1,nlon(blockid)
n = n + 1
cdex(i) = n
enddo
endif
endif
!
return
end subroutine get_block_gcol_d
!
!========================================================================
!
integer function get_block_gcol_cnt_d(blockid) 9,3
!-----------------------------------------------------------------------
!
!
! Purpose: Return number of dynamics columns in indicated block
!
! Method:
!
! Author: Patrick Worley
!
!-----------------------------------------------------------------------
use rgrid
, only: nlon
use pmgrid
, only: spmd_on, nprxy_x
#if ( defined SPMD )
use spmd_dyn
, only: lonrangexy, latrangexy
#endif
implicit none
!------------------------------Arguments--------------------------------
integer, intent(in) :: blockid ! global block id
!---------------------------Local workspace-----------------------------
integer i, j
!-----------------------------------------------------------------------
if (spmd_on .eq. 1) then
j = (blockid-1) / nprxy_x + 1
i = blockid - (j-1) * nprxy_x
#if ( defined SPMD )
get_block_gcol_cnt_d = (lonrangexy(2,i)-lonrangexy(1,i)+1) * &
(latrangexy(2,j)-latrangexy(1,j)+1)
#endif
else
get_block_gcol_cnt_d = nlon(blockid)
endif
return
end function get_block_gcol_cnt_d
!
!========================================================================
!
integer function get_block_lvl_cnt_d(blockid,bcid) 2
!-----------------------------------------------------------------------
!
!
! Purpose: Return number of levels in indicated column. If column
! includes surface fields, then it is defined to also
! include level 0.
!
! Method:
!
! Author: Patrick Worley
!
!-----------------------------------------------------------------------
implicit none
!------------------------------Arguments--------------------------------
integer, intent(in) :: blockid ! global block id
integer, intent(in) :: bcid ! column index within block
!-----------------------------------------------------------------------
! latitude slice block
get_block_lvl_cnt_d = plev + 1
return
end function get_block_lvl_cnt_d
!
!========================================================================
!
subroutine get_block_levels_d(blockid, bcid, lvlsiz, levels) 1,1
!-----------------------------------------------------------------------
!
!
! Purpose: Return level indices in indicated column. If column
! includes surface fields, then it is defined to also
! include level 0.
!
! Method:
!
! Author: Patrick Worley
!
!-----------------------------------------------------------------------
implicit none
!------------------------------Arguments--------------------------------
integer, intent(in) :: blockid ! global block id
integer, intent(in) :: bcid ! column index within block
integer, intent(in) :: lvlsiz ! dimension of levels array
integer, intent(out) :: levels(lvlsiz) ! levels indices for block
!---------------------------Local workspace-----------------------------
!
integer k ! loop index
!-----------------------------------------------------------------------
! latitude slice block
if (lvlsiz < plev + 1) then
write(iulog,*)'GET_BLOCK_LEVELS_D: levels array not large enough (', &
lvlsiz,' < ',plev + 1,' ) '
call endrun
else
do k=0,plev
levels(k+1) = k
enddo
do k=plev+2,lvlsiz
levels(k) = -1
enddo
endif
return
end subroutine get_block_levels_d
!
!========================================================================
!
subroutine get_gcol_block_d(gcol,cnt,blockid,bcid) 9,4
!-----------------------------------------------------------------------
!
!
! Purpose: Return global block index and local column index
! for global column index
!
! Method:
!
! Author: Patrick Worley
!
!-----------------------------------------------------------------------
use pmgrid
, only: plon, spmd_on, nprxy_x, nprxy_y
#if ( defined SPMD )
use spmd_dyn
, only: lonrangexy, latrangexy
#endif
implicit none
!------------------------------Arguments--------------------------------
integer, intent(in) :: gcol ! global column index
integer, intent(in) :: cnt ! size of blockid and bcid arrays
integer, intent(out) :: blockid(cnt) ! block index
integer, intent(out) :: bcid(cnt) ! column index within block
!---------------------------Local workspace-----------------------------
!
integer i,j,ii,jj ! loop indices
integer glon, glat ! global longitude and latitude
! indices
integer ddlon ! number of longitudes in block
!-----------------------------------------------------------------------
! lon/lat block
if (cnt < 1) then
write(iulog,*)'GET_GCOL_BLOCK_D: arrays not large enough (', cnt,' < ',1,' ) '
call endrun
else
if (spmd_on .eq. 1) then
! Determine global latitude and longitude coordinate indices from
! global column index
glat = (gcol-1)/plon + 1
glon = gcol - ((glat-1)*plon)
! Determine block coordinates (ii,jj), where ii ranges from 1 to
! nprxy_x and jj ranges from 1 to nprxy_y.
#if ( defined SPMD )
ii=0
do i=1,nprxy_x
if (lonrangexy(1,i) .le. glon .and. glon .le. lonrangexy(2,i)) ii=i
enddo
jj=0
do j=1,nprxy_y
if (latrangexy(1,j) .le. glat .and. glat .le. latrangexy(2,j)) jj=j
enddo
if (ii .eq. 0 .or. jj .eq. 0) then
write(iulog,*)'GET_GCOL_BLOCK_D: could not find block indices for (', &
glon,',',glat,' ) '
call endrun
endif
! Global block index
blockid(1) = (jj-1)*nprxy_x+ii
! Local coordinates in block
j = glat-latrangexy(1,jj)+1
i = glon-lonrangexy(1,ii)+1
ddlon = lonrangexy(2,ii)-lonrangexy(1,ii)+1
! Local column index in block
bcid(1) = (j-1)*ddlon+i
!
#endif
else
glat = (gcol-1)/plon + 1
glon = gcol - ((glat-1)*plon)
blockid(1) = glat
bcid(1) = glon
endif
!
do j=2,cnt
blockid(j) = -1
bcid(j) = -1
enddo
!
endif
!
return
end subroutine get_gcol_block_d
subroutine get_gcol_lat(gcid, lat),2
use pmgrid
, only: plat, plon
use commap
, only: clat
integer, intent(in) :: gcid(:)
real(r8), intent(out) :: lat(:)
integer :: k, glen, ilat
glen = size(gcid)
do k=1,glen
ilat = (gcid(k)-1)/plon + 1
lat(k) = CLAT(ilat)
end do
end subroutine get_gcol_lat
subroutine get_gcol_lon(gcid, lon),2
use pmgrid
, only: plat, plon
use commap
, only: clon
integer, intent(in) :: gcid(:)
real(r8), intent(out) :: lon(:)
integer :: k, glen, ilat
glen = size(gcid)
do k=1,glen
ilat = (gcid(k)-1)/plon + 1
lon(k) = CLON(gcid(k) - ((ilat-1)*plon),1)
end do
end subroutine get_gcol_lon
!
!========================================================================
!
integer function get_gcol_block_cnt_d(gcol) 7
!-----------------------------------------------------------------------
!
!
! Purpose: Return number of blocks contain data for the vertical column
! with the given global column index
!
! Method:
!
! Author: Patrick Worley
!
!-----------------------------------------------------------------------
implicit none
!------------------------------Arguments--------------------------------
integer, intent(in) :: gcol ! global column index
!-----------------------------------------------------------------------
! lon/lat block
get_gcol_block_cnt_d = 1
return
end function get_gcol_block_cnt_d
!
!========================================================================
!
integer function get_block_owner_d(blockid) 14,2
!-----------------------------------------------------------------------
!
!
! Purpose: Return id of processor that "owns" the indicated block
!
! Method:
!
! Author: Patrick Worley
!
!-----------------------------------------------------------------------
use pmgrid
, only: spmd_on
#if ( defined SPMD )
use spmd_dyn
, only: proc
#endif
implicit none
!------------------------------Arguments--------------------------------
integer, intent(in) :: blockid ! global block id
!-----------------------------------------------------------------------
! latitude slice block
#if (defined SPMD)
if (spmd_on .eq. 1) then
get_block_owner_d = blockid - 1
else
get_block_owner_d = proc(blockid)
endif
#else
get_block_owner_d = 0
#endif
return
end function get_block_owner_d
!
!========================================================================
!
subroutine get_horiz_grid_dim_d(hdim1_d,hdim2_d) 15,1
!-----------------------------------------------------------------------
!
!
! Purpose: Returns declared horizontal dimensions of computational grid.
! Note that global column ordering is assumed to be compatible
! with the first dimension major ordering of the 2D array.
!
! Method:
!
! Author: Patrick Worley
!
!-----------------------------------------------------------------------
use pmgrid
, only: plat, plon
!------------------------------Arguments--------------------------------
integer, intent(out) :: hdim1_d ! first horizontal dimension
integer, intent(out) :: hdim2_d ! second horizontal dimension
!-----------------------------------------------------------------------
if (ngcols_d == 0) then
ngcols_d = plat*plon
endif
hdim1_d = plon
hdim2_d = plat
return
end subroutine get_horiz_grid_dim_d
!
!========================================================================
!
subroutine get_horiz_grid_d(size,clat_d_out,clon_d_out, & 7,7
area_d_out, wght_d_out)
!-----------------------------------------------------------------------
!
!
! Purpose: Return latitude and longitude (in radians), column surface
! area (in radians squared) and surface integration weights
! for global column indices that will be passed to/from physics
!
! Method:
!
! Author: Patrick Worley
!
!-----------------------------------------------------------------------
use pmgrid
, only: plat, plon
use rgrid
, only: nlon
use commap
, only: clat, clon, latdeg, londeg, w
use physconst
, only: pi, spval
implicit none
!------------------------------Arguments--------------------------------
integer, intent(in) :: size ! array sizes
real(r8), intent(out), optional :: clat_d_out(size) ! column latitudes
real(r8), intent(out), optional :: clon_d_out(size) ! column longitudes
real(r8), intent(out), optional :: area_d_out(size) ! column surface
! area
real(r8), intent(out), optional :: wght_d_out(size) ! column integration
! weight
!---------------------------Local workspace-----------------------------
!
integer i,j ! loop indices
integer n ! column index
real(r8) :: ns_vert(2,plon) ! latitude grid vertices
real(r8) :: ew_vert(2,plon) ! longitude grid vertices
real(r8) :: del_theta ! difference in latitude at a grid point
real(r8) :: del_phi ! difference in longitude at a grid point
real(r8),parameter :: degtorad=pi/180.0_r8 ! convert degrees to radians
!-----------------------------------------------------------------------
! if (size < ngcols_d) then
! write(iulog,*)'GET_HORIZ_GRID_D: arrays not large enough (', &
! size,' < ',ngcols_d,' ) '
! call endrun
! else
if(present(clon_d_out)) then
if(size == ngcols_d) then
n = 0
do j = 1,plat
do i = 1,nlon(j)
n = n + 1
clon_d_out(n) = clon(i,j)
enddo
enddo
else if(size == plon) then
clon_d_out(:) = clon(:,1)
else
write(iulog,*)'GET_HORIZ_GRID_D: arrays not large enough (', &
size,' < ',ngcols_d,' ) '
call endrun
end if
end if
if(present(clat_d_out)) then
if(size == ngcols_d) then
n = 0
do j = 1,plat
do i = 1,nlon(j)
n = n + 1
clat_d_out(n) = clat(j)
enddo
enddo
else if(size == plat) then
clat_d_out(:) = clat(:)
else
write(iulog,*)'GET_HORIZ_GRID_D: arrays not large enough (', &
size,' < ',ngcols_d,' ) '
call endrun
end if
end if
if(size==plat .and. present(wght_d_out)) then
wght_d_out(:) = w(:)
return
endif
if ( ( present(area_d_out) ) .or. ( present(wght_d_out) ) ) then
if(size==plat .and.present(area_d_out)) then
call endrun
('size argument to get_horiz_grid_d too small')
end if
n = 0
do j = 1,plat
! First, determine vertices of each grid point.
! Verticies are ordered as follows:
! ns_vert: 1=lower left, 2 = upper left
! ew_vert: 1=lower left, 2 = lower right
! Latitude vertices
ns_vert(:,:) = spval
if (j .eq. 1) then
ns_vert(1,:nlon(1)) = -D90_0 + (latdeg(1) - latdeg(2))*D0_5
else
ns_vert(1,:nlon(j)) = (latdeg(j) + latdeg(j-1) )*D0_5
endif
if (j .eq. plat) then
ns_vert(2,:nlon(plat)) = D90_0 + (latdeg(plat) - latdeg(plat-1))*D0_5
else
ns_vert(2,:nlon(j)) = (latdeg(j) + latdeg(j+1) )*D0_5
endif
! Longitude vertices
ew_vert(:,:) = spval
ew_vert(1,1) = (londeg(1,j) - D360_0 + londeg(nlon(j),j))*D0_5
ew_vert(1,2:nlon(j)) = (londeg(1:nlon(j)-1,j)+ londeg(2:nlon(j),j))*D0_5
ew_vert(2,:nlon(j)-1) = ew_vert(1,2:nlon(j))
ew_vert(2,nlon(j)) = (londeg(nlon(j),j) + (D360_0 + londeg(1,j)))*D0_5
do i = 1,nlon(j)
n = n + 1
if (j .eq. 1) then
del_phi = -sin( latdeg(j)*degtorad ) + sin( ns_vert(2,i)*degtorad )
else if (j .eq. plat) then
del_phi = sin( latdeg(j)*degtorad ) - sin( ns_vert(1,i)*degtorad )
else
del_phi = sin( ns_vert(2,i)*degtorad ) - sin( ns_vert(1,i)*degtorad )
end if
del_theta = ( ew_vert(2,i) - ew_vert(1,i) )*degtorad
if ( present(area_d_out) ) area_d_out(n) = del_theta*del_phi
if (present(wght_d_out) ) wght_d_out(n) = del_theta*del_phi
enddo
enddo
endif
return
end subroutine get_horiz_grid_d
!#######################################################################
function get_dyn_grid_parm_real2d(name) result(rval) 5,1
use commap
, only : londeg, londeg_st, clon
character(len=*), intent(in) :: name
real(r8), pointer :: rval(:,:)
if(name.eq.'clon') then
rval => clon
else if(name.eq.'londeg') then
rval => londeg
else if(name.eq.'londeg_st') then
rval => londeg_st
else
nullify(rval)
end if
end function get_dyn_grid_parm_real2d
!#######################################################################
function get_dyn_grid_parm_real1d(name) result(rval) 8,1
use commap
, only : latdeg, latdeg_st, clat_staggered, w_staggered, clat, w
character(len=*), intent(in) :: name
real(r8), pointer :: rval(:)
if(name.eq.'clat') then
rval => clat
else if(name.eq.'latdeg') then
rval => latdeg
else if(name.eq.'latdeg_st') then
rval => latdeg_st
else if(name.eq.'clatdeg_staggered') then
rval => latdeg_st
else if(name.eq.'w') then
rval => w
else if(name.eq.'w_staggered') then
rval => w_staggered
else
nullify(rval)
end if
end function get_dyn_grid_parm_real1d
integer function get_dyn_grid_parm(name) result(ival) 51,1
use pmgrid
, only : beglat, endlat, plat, plon, plev, plevp, &
splon, beglev, endlev, endlevp, &
beglonxy, endlonxy, beglatxy, endlatxy
character(len=*), intent(in) :: name
if(name.eq.'splon') then
ival = splon
else if(name.eq.'beglev') then
ival = beglev
else if(name.eq.'endlev') then
ival = endlev
else if(name.eq.'endlevp') then
ival = endlevp
else if(name.eq.'beglonxy') then
ival = beglonxy
else if(name.eq.'endlonxy') then
ival = endlonxy
else if(name.eq.'beglatxy') then
ival = beglatxy
else if(name.eq.'endlatxy') then
ival = endlatxy
else if(name.eq.'beglat') then
ival = beglat
else if(name.eq.'endlat') then
ival = endlat
else if(name.eq.'plat') then
ival = plat
else if(name.eq.'plon') then
ival = plon
else if(name.eq.'plev') then
ival = plev
else if(name.eq.'plevp') then
ival = plevp
else
ival = -1
end if
return
end function get_dyn_grid_parm
end module dyn_grid