!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
!! GNU General Public License !!
!! !!
!! This file is part of the Flexible Modeling System (FMS). !!
!! !!
!! FMS is free software; you can redistribute it and/or modify !!
!! it and are expected to follow the terms of the GNU General Public !!
!! License as published by the Free Software Foundation. !!
!! !!
!! 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 General Public License !!
!! along with FMS; if not, write to: !!
!! Free Software Foundation, Inc. !!
!! 59 Temple Place, Suite 330 !!
!! Boston, MA 02111-1307 USA !!
!! or see: !!
!! http://www.gnu.org/licenses/gpl.txt !!
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
module ocean_util_mod
!
! Ron Pacanowski
!
!
! Matt Harrison
!
!
! S. M. Griffies
!
!
!
! This module contains many routines of use for mom4.
!
!
!
! A utility module for mom4.
!
!
use constants_mod, only: epsln
use mpp_domains_mod, only: mpp_update_domains, mpp_global_field
use mpp_mod, only: stdout, stdlog, FATAL
use mpp_mod, only: mpp_error, mpp_pe, mpp_root_pe, mpp_min, mpp_max
use time_manager_mod, only: time_type, get_date
use ocean_domains_mod, only: get_local_indices
use ocean_types_mod, only: ocean_domain_type, ocean_grid_type
use ocean_types_mod, only: ocean_thickness_type, ocean_time_type
implicit none
private
character(len=256) :: version='CVS $Id'
character(len=256) :: tagname='Tag $Name'
! for output
integer :: unit=6
#include
type(ocean_domain_type), pointer :: Dom =>NULL()
public iplot
public matrix
public ocean_util_init
public invtri
public invtri_bmf
public write_timestamp
private invtri_bmf_v1
contains
!#######################################################################
!
!
!
! Initialize mom4 utilities.
!
!
subroutine ocean_util_init (Domain)
type(ocean_domain_type), intent(in), target :: Domain
integer :: stdlogunit
stdlogunit=stdlog()
write( stdlogunit,'(/a/)') trim(version)
#ifndef MOM4_STATIC_ARRAYS
call get_local_indices(Domain, isd, ied, jsd, jed, isc, iec, jsc, jec)
#endif
Dom => Domain
end subroutine ocean_util_init
! NAME="ocean_util_init">
!#######################################################################
!
!
!
! Solve the vertical diffusion equation implicitly using the
! method of inverting a tridiagonal matrix as described in
! Numerical Recipes in Fortran, The art of Scientific Computing,
! Second Edition, Press, Teukolsky, Vetterling, Flannery, 1992
! pages 42,43.
!
! This routine assumes that the variables are defined at grid points,
! and the top and bottom b.c. are flux conditions.
!
! inputs:
!
! z = right hand side terms
!
! nk = number of vertical levels
!
! topbc = top boundary condition
!
! botbc = bottom boundary condition
!
! dcb = vertical mixing coeff at base of cell
!
! tdt = timestep over which do implicit update
!
! kmz = level indicator
!
! mask = land/sea mask
!
! outputs:
!
! z = returned solution
!
!
!
subroutine invtri (z, topbc, botbc, dcb, tdt, kmz, mask, dh, dhw, aidif, nk)
integer, intent(in) :: nk
real, intent(inout), dimension(isd:,jsd:,:) :: z
real, intent(in), dimension(isd:,jsd:,:) :: dcb
real, intent(in), dimension(isd:,jsd:,:) :: dh
real, intent(in), dimension(isd:,jsd:,:) :: mask
real, intent(in), dimension(isd:,jsd:,0:) :: dhw
real, intent(in), dimension(isd:,jsd:) :: topbc
real, intent(in), dimension(isd:,jsd:) :: botbc
real, intent(in) :: tdt
integer, intent(in), dimension(isd:ied,jsd:jed) :: kmz
real, intent(in) :: aidif
real, dimension(isd:ied,0:nk) :: a, b, c, e, f
real, dimension(isd:ied) :: bet
integer :: i, j, k, km1, kp1
real :: eps, factu, factl, tdt_aidif
eps = 1.e-30
tdt_aidif = tdt*aidif
do j=jsc,jec
do k=1,nk
km1 = max(1,k-1)
kp1 = min(k+1,nk)
do i=isc,iec
factu = tdt_aidif/(dhw(i,j,k-1)*dh(i,j,k))
factl = tdt_aidif/(dhw(i,j,k)*dh(i,j,k))
a(i,k) = -dcb(i,j,km1)*factu*mask(i,j,k)
c(i,k) = -dcb(i,j,k)*factl*mask(i,j,kp1)
f(i,k) = z(i,j,k)*mask(i,j,k)
b(i,k) = 1.0 - a(i,k) - c(i,k)
enddo
enddo
do i=isc,iec
a(i,1) = 0.0
c(i,nk) = 0.0
b(i,1) = 1.0 - a(i,1) - c(i,1)
b(i,nk) = 1.0 - a(i,nk) - c(i,nk)
! top and bottom b.c.
f(i,1) = z(i,j,1) + topbc(i,j)*tdt_aidif*mask(i,j,1)/dh(i,j,1)
k = max(2,kmz(i,j))
f(i,k) = z(i,j,k) - botbc(i,j)*tdt_aidif*mask(i,j,k)/dh(i,j,k)
enddo
! decomposition and forward substitution
do i=isc,iec
bet(i) = mask(i,j,1)/(b(i,1) + eps)
z(i,j,1) = f(i,1)*bet(i)
enddo
do k=2,nk
do i=isc,iec
e(i,k) = c(i,k-1)*bet(i)
bet(i) = mask(i,j,k)/(b(i,k) - a(i,k)*e(i,k) + eps)
z(i,j,k) = (f(i,k) - a(i,k)*z(i,j,k-1))*bet(i)
enddo
enddo
! back substitution
do k=nk-1,1,-1
do i=isc,iec
z(i,j,k) = z(i,j,k) - e(i,k+1)*z(i,j,k+1)
enddo
enddo
enddo
end subroutine invtri
! NAME="invtri">
!#######################################################################
!
!
!
! Solve the vertical diffusion equation implicitly using the
! method of inverting a tridiagonal matrix as described in
! Numerical Recipes in Fortran, The art of Scientific Computing,
! Second Edition, Press, Teukolsky, Vetterling, Flannery, 1992
! pages 42,43.
!
! This routine assumes that the variables are defined at grid points,
! and the top b.c. is a flux condition. The bottom b.c. is assumed
! to be a bottom drag which is implemented implicitly, thus allowing
! for large values of the bottom drag coefficient.
!
! NOTE: This routine is generally only called when doing the bmf
! implicitly in time. The original invtri is used for explicit
! bmf. It differs from invtri only in the presence of a mask(i,j,kp1)
! multiplying unity in the definition of the coefficient b(i,k).
!
! NOTE: This routine is under development (SMG. Nov 2008)
!
! inputs:
!
! z = right hand side terms
!
! nk = number of vertical levels
!
! topbc = top boundary condition
!
! botbc = bottom boundary condition
!
! dcb = vertical mixing coeff at base of cell
!
! tdt = timestep over which do implicit update
!
! kmz = level indicator
!
! mask = land/sea mask
!
! outputs:
!
! z = returned solution
!
!
!
subroutine invtri_bmf (z, topbc, botbc, dcb, tdt, kmz, mask, dh, dhw, aidif, nk)
integer, intent(in) :: nk
real, intent(inout), dimension(isd:,jsd:,:) :: z
real, intent(in), dimension(isd:,jsd:,:) :: dcb
real, intent(in), dimension(isd:,jsd:,:) :: dh
real, intent(in), dimension(isd:,jsd:,:) :: mask
real, intent(in), dimension(isd:,jsd:,0:) :: dhw
real, intent(in), dimension(isd:,jsd:) :: topbc
real, intent(in), dimension(isd:,jsd:) :: botbc
real, intent(in) :: tdt
integer, intent(in), dimension(isd:ied,jsd:jed) :: kmz
real, intent(in) :: aidif
real, dimension(isd:ied,0:nk) :: a, b, c, e, f
real, dimension(isd:ied) :: bet
integer :: i, j, k, km1, kp1
real :: eps, factu, factl, tdt_aidif
eps = 1.e-30
tdt_aidif = tdt*aidif
do j=jsc,jec
do k=1,nk
km1 = max(1,k-1)
kp1 = min(k+1,nk)
do i=isc,iec
factu = tdt_aidif/(dhw(i,j,k-1)*dh(i,j,k))
factl = tdt_aidif/(dhw(i,j,k)*dh(i,j,k))
a(i,k) = -dcb(i,j,km1)*factu*mask(i,j,k)
c(i,k) = -dcb(i,j,k)*factl*mask(i,j,kp1)
f(i,k) = z(i,j,k)*mask(i,j,k)
b(i,k) = 1.0*mask(i,j,kp1) - a(i,k) - c(i,k)
enddo
enddo
do i=isc,iec
a(i,1) = 0.0
c(i,nk) = 0.0
b(i,1) = 1.0 - a(i,1) - c(i,1)
b(i,nk) = - a(i,nk) - c(i,nk)
! top and bottom b.c.
f(i,1) = z(i,j,1) + topbc(i,j)*tdt_aidif*mask(i,j,1)/dh(i,j,1)
k = max(2,kmz(i,j))
f(i,k) = z(i,j,k) - botbc(i,j)*tdt_aidif*mask(i,j,k)/dh(i,j,k)
enddo
! decomposition and forward substitution
do i=isc,iec
bet(i) = mask(i,j,1)/(b(i,1) + eps)
z(i,j,1) = f(i,1)*bet(i)
enddo
do k=2,nk
do i=isc,iec
e(i,k) = c(i,k-1)*bet(i)
bet(i) = mask(i,j,k)/(b(i,k) - a(i,k)*e(i,k) + eps)
z(i,j,k) = (f(i,k) - a(i,k)*z(i,j,k-1))*bet(i)
enddo
enddo
! back substitution
do k=nk-1,1,-1
do i=isc,iec
z(i,j,k) = z(i,j,k) - e(i,k+1)*z(i,j,k+1)
enddo
enddo
enddo
end subroutine invtri_bmf
! NAME="invtri_bmf">
!#######################################################################
!
!
!
! Solve the vertical friction equation implicitly using the
! method of inverting a tridiagonal matrix as described in
! Numerical Recipes in Fortran, The art of Scientific Computing,
! Second Edition, Press, Teukolsky, Vetterling, Flannery, 1992
! pages 42,43.
!
! This routine assumes that the variables are defined at grid points,
! and the top b.c. is a flux condition. The bottom b.c. is assumed
! to be a bottom drag which is implemented implicitly, thus allowing
! for large values of the bottom drag coefficient.
!
! NOTE: This routine is generally only called when doing the bmf
! implicitly in time. The original invtri is used for explicit
! bmf.
!
! NOTE: This routine is under development, and is presently not
! used. (SMG. Nov 2008)
!
! inputs:
!
! z = right hand side terms
!
! nk = number of vertical levels
!
! topbc = top boundary condition
!
! botbc = time explicit bottom boundary condition (zero in this routine, since bmf is implicit)
!
! gamma = botttom drag factor scaling the u(taup1) contribution to bottom drag
!
! dcb = vertical mixing coeff at base of cell
!
! tdt = timestep over which do implicit update
!
! kmz = level indicator
!
! mask = land/sea mask
!
! outputs:
!
! z = returned solution
!
!
!
subroutine invtri_bmf_v1 (z, topbc, gamma, dcb, tdt, kmz, mask, dh, dhw, aidif, nk)
integer, intent(in) :: nk
real, intent(inout), dimension(isd:,jsd:,:) :: z
real, intent(in), dimension(isd:,jsd:) :: gamma
real, intent(in), dimension(isd:,jsd:,:) :: dcb
real, intent(in), dimension(isd:,jsd:,:) :: dh
real, intent(in), dimension(isd:,jsd:,:) :: mask
real, intent(in), dimension(isd:,jsd:,0:) :: dhw
real, intent(in), dimension(isd:,jsd:) :: topbc
real, intent(in) :: tdt
integer, intent(in), dimension(isd:ied,jsd:jed) :: kmz
real, intent(in) :: aidif
real, dimension(isd:ied,0:nk) :: a, b, c, e, f
real, dimension(isd:ied) :: bet
integer :: i, j, k, km1, kp1
real :: eps, factu, factl, tdt_aidif
eps = 1.e-30
tdt_aidif = tdt*aidif
do j=jsc,jec
do k=1,nk
km1 = max(1,k-1)
kp1 = min(k+1,nk)
do i=isc,iec
factu = tdt_aidif/(dhw(i,j,k-1)*dh(i,j,k))
factl = tdt_aidif/(dhw(i,j,k)*dh(i,j,k))
a(i,k) = -dcb(i,j,km1)*factu*mask(i,j,k)
c(i,k) = -dcb(i,j,k)*factl*mask(i,j,kp1)
f(i,k) = z(i,j,k)*mask(i,j,k)
b(i,k) = 1.0 - a(i,k) - c(i,k)
enddo
enddo
do i=isc,iec
a(i,1) = 0.0
c(i,nk) = 0.0
b(i,1) = 1.0 - a(i,1) - c(i,1)
b(i,nk) = 1.0 - a(i,nk) - c(i,nk)
! top and bottom b.c.
f(i,1) = z(i,j,1) + topbc(i,j)*tdt_aidif*mask(i,j,1)/dh(i,j,1)
k = max(2,kmz(i,j))
f(i,k) = z(i,j,k)
b(i,k) = 1.0 - a(i,k) + gamma(i,j)*tdt_aidif*mask(i,j,k)/dh(i,j,k)
enddo
! decomposition and forward substitution
do i=isc,iec
bet(i) = mask(i,j,1)/(b(i,1) + eps)
z(i,j,1) = f(i,1)*bet(i)
enddo
do k=2,nk
do i=isc,iec
e(i,k) = c(i,k-1)*bet(i)
bet(i) = mask(i,j,k)/(b(i,k) - a(i,k)*e(i,k) + eps)
z(i,j,k) = (f(i,k) - a(i,k)*z(i,j,k-1))*bet(i)
enddo
enddo
! back substitution
do k=nk-1,1,-1
do i=isc,iec
z(i,j,k) = z(i,j,k) - e(i,k+1)*z(i,j,k+1)
enddo
enddo
enddo
end subroutine invtri_bmf_v1
! NAME="invtri_bmf_v1">
!#######################################################################
!
!
!
!
! map integer array "iarray" into characters for printing with
! format (a1) to provide a contour map of the integer field.
! note: max number of unique characters = 80
!
! inputs:
!
! iarray = integer array to be plotted
!
! is = starting index along inner dimension of "iarray"
!
! ie = ending index along inner dimension of "iarray"
!
! js = starting index along outer dimension of "iarray"
!
! je = ending index along outer dimension of "iarray"
!
! output: prints contour map of "iarray"
!
!
!
subroutine iplot (iarray, is, ie, js, je, ni, nj)
integer, intent(in) :: is, ie, js, je, ni, nj
integer, intent(in), dimension(ni,nj) :: iarray
character*80 levels
character*80 lev1
integer :: i, j, il, jl, l, incr, ii, jj, inc, last, jinc, maxint, minint
save levels
integer :: stdoutunit
stdoutunit=stdout()
write (stdoutunit,*) ' '
! set character markers
lev1(1:51) = '.+*ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv'
levels = lev1(1:51)//'wxyz0123456789-=!@#$%<>[]{}()'
! find range of integers
maxint = iarray(is,js)
minint = iarray(is,js)
do j=js,je
do i=is,ie
maxint = max(maxint,iarray(i,j))
minint = min(minint,iarray(i,j))
enddo
enddo
! show mapping of integers into characters
write (stdoutunit,*) ' '
write (stdoutunit,*) ' "iplot" mapping of integers to characters is as follows:'
inc = 3
last = min(minint+80-1,maxint)
do i=minint,last,inc
ii = i-minint+1
if (i+inc <= last) then
jinc = inc
else
jinc = last-i+1
endif
write (stdoutunit,'(6(1x,i6,a,a,3x))') (j+minint-1,' is printed as ',levels(j:j),j=ii,ii+jinc-1)
enddo
write (stdoutunit,*) ' '
if (maxint - minint + 1 > 80) then
write (stdoutunit,*) ' Note: there are ',maxint-minint+1,' integers in the field'
write (stdoutunit,*) ' "iplot" cannot uniquely assign more than 80 characters for plotting symbols.'
write (stdoutunit,*) ' therefore integers are represented by cyclicly reusing the list of plotting symbols'
write (stdoutunit,*) ' '
endif
! print character representation of integers
inc=124
il = ie-is+1
jl = je-js+1
do l=0,il,inc
incr = min(inc,il-l)
write (stdoutunit,8800) (l+i,i=1,incr,4)
do jj=1,jl
j = jl+1-jj
write (stdoutunit,8900) j, (levels(mod(iarray(l+i+is-1,j+js-1)-minint+1-1,80)+1:&
mod(iarray(l+i+is-1,j+js-1)-minint+1-1,80)+1),i=1,incr)
enddo
enddo
8800 format (/, 2x, 31i4)
8900 format (1x,i3,1x, 124a1)
end subroutine iplot
! NAME="iplot">
!#######################################################################
!
!
!
! matrix is a general two-dimensional array printing routine,
! input:
!
! array = the array to be printed
!
! istrt = the 1st element of the 1st dimension to be printed
!
! im = the last element of the 1st dimension to be printed
!
! jstrt = the 1st element of the 2nd dimension to be printed
!
! jm = the last element of the 2nd dimension to be printed
! the 2nd dimension is printed in reverse order if both
! jstrt and jm are negative
!
! scale = a scaling factor by which array is divided before
! printing. (if this is zero, no scaling is done.)
! if scale=0, 10 columns are printed across in e format
! if scale>0, 20 columns are printed across in f format
!
! output: print "array" as a matrix
!
!
!
subroutine matrix (array, istrt, im, jstrt, jm, scale)
integer :: i, l, istrt, im, jstrt, jm, is, ie, js, je, jinc, unit
real, dimension(istrt:im,abs(jstrt):abs(jm)) :: array
real :: scale, scaler
unit = 6
if (jstrt*jm < 0) then
write (unit,999) jstrt, jm
call mpp_error(FATAL,'==>Error in ocean_util_mod (matrix): jstrt*jm < 0 found in matrix')
endif
! allow for inversion of 2nd dimension
if (jm < 0) then
js = -jm
je = -jstrt
jinc = -1
else
js = jstrt
je = jm
jinc = 1
endif
if (scale == 0.0) then
do is=istrt,im,10
ie = min(is + 9,im)
write (unit,9001) (i, i=is,ie)
do l=js,je,jinc
write (unit,9002) l, (array(i,l),i=is,ie)
enddo
write (unit,'(/)')
enddo
else
scaler = 1.0/scale
do is=istrt,im,20
ie = min(is + 19,im)
write (unit,9003) (i, i=is,ie)
do l=js,je,jinc
write (unit,9004) l, (array(i,l)*scaler,i=is,ie)
enddo
write (unit,'(/)')
enddo
endif
999 format (1x,'jstrt=',i5,' jm=',i5,' in matrix')
9001 format(10i13)
9002 format(i3,10(1pe13.5))
9003 format(3x,20i6)
9004 format(1x,i3,1x,20f6.2)
end subroutine matrix
! NAME="matrix">
!#######################################################################
!
!
!
! Write the time stamp.
!
!
subroutine write_timestamp(Time)
type(time_type), intent(in) :: Time
integer :: yr, mon, day, hr, min, sec
integer :: stdoutunit
stdoutunit=stdout()
call get_date(Time, yr, mon, day, hr, min, sec)
write(stdoutunit,'(a,i4,a,i2,a,i2,1x,i2,a,i2,a,i2)' ) &
'yyyy/mm/dd hh:mm:ss = ', yr, '/',mon,'/',day, hr, ':',min,':', sec
end subroutine write_timestamp
! NAME="min_max">
end module ocean_util_mod