!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! !!
!! 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 !!
!! !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
program regrid_3d
!-----------------------------------------------------------------------
! GNU General Public License
!
! This program 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; either version 2 of
! the License, or (at your option) any later version.
!
! MOM 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.
!
! For the full text of the GNU General Public License,
! write to: Free Software Foundation, Inc.,
! 675 Mass Ave, Cambridge, MA 02139, USA.
! or see: http://www.gnu.org/licenses/gpl.html
!-----------------------------------------------------------------------
!
! Bonnie Samuels
! Zhi Liang
! M.J. Harrison
!
!
! regrid 3-d lat-lon gridded data to logically rectangular grid
! described by grid descriptor file. Applies only to scalar fields
! No missing points allowed on input grid.
!
!
use mpp_mod, only : mpp_error, mpp_pe, mpp_npes, mpp_root_pe
use mpp_mod, only : FATAL, WARNING, stdout, stdlog, mpp_chksum
use mpp_io_mod, only : mpp_open, mpp_close, mpp_read, mpp_write, mpp_write_meta
use mpp_io_mod, only : mpp_copy_meta, axistype, fieldtype, atttype
use mpp_io_mod, only : mpp_get_atts, mpp_get_info, mpp_get_fields, mpp_get_times
use mpp_io_mod, only : mpp_get_axes, mpp_get_axis_data
use mpp_io_mod, only : MPP_RDONLY, MPP_NETCDF, MPP_MULTI, MPP_SINGLE, MPP_OVERWR
use mpp_domains_mod, only : mpp_update_domains, mpp_define_domains, mpp_global_field
use mpp_domains_mod, only : domain2d, mpp_define_layout, mpp_get_compute_domain
use mpp_domains_mod, only : mpp_domains_set_stack_size
use horiz_interp_mod, only : horiz_interp_new, horiz_interp, horiz_interp_end, horiz_interp_type
use axis_utils_mod, only : get_axis_cart, interp_1d
use fms_mod, only : fms_init, fms_end, open_namelist_file, close_file, file_exist
use fms_mod, only : check_nml_error, write_version_number, lowercase
use constants_mod, only : constants_init, PI
implicit none
integer, parameter :: max_fields = 10
integer, parameter :: max_ntimes_saved = 12
!--- namelist interface
!
!
! Name of input file containing grid and data to be regridded.
!
!
! Number of fields.
!
!
! Name of input field(s). default is (/'temp', 'salt'/)
!
!
! Name of output field(s). If it is not specified in the namelist, it will
! get the value from src_field_name
!
!
! Name of grid descriptor file containing target grid information.
!
!
! Name of output file.
!
!
! Number of nearest neighbors for regridding
!
!
! Maximum radial influence for regridding.
!
!
! scaling factor for data (e.g. -1 to flip sign or 0.01 to convert from centimeters)
!
!
! The stopping criteria when extrapping data onto missing points.
!
!
! when use_source_vertical_grid is set to true, the destination data will
! have the same vertical level as the source data. When use_source_vertical_grid
! is false, the vertical grid of destination data will come from dest_grid.
! A linear vertical interpolation will be done when the source vertical is different
! from destination vertical grid.
!
!
! flag to indicate if the land/sea mask of source/destination grid will be applied
! on the output dest_file. When apply_mask is false, the destination data will be
! global data, i.e. no missing value in the destination data file. When apply_mask
! is true, mask will be applied to the destination data. The mask can be either
! source grid or destination grid determined by nml use_source_vertical_grid.
! When use_source_vertical_grid is true, source grid mask will be applied, otherwise
! destination grid mask will be applied.
!
!
! specifying the remapping method when remampping data onto current grid.
! Its value can be "spherical" or " bilinear". "spherical" interpolation is a
! inverse distance weighted interpolation algorithm. Default value is "bilinear".
! "bilinear" interpolation is recommanded, since bilinear interpolation will provide
! more smooth results than "spherical" interpolation (especially when interpolating
! from coarse grid to fine grid). Plus bilinear interpolation is much more efficiency
! than "spherical interpolation".
!
!
! number of time levels to be saved. Its value has to be less than or equal to the number
! of time levels in the source data file.
!
!
! specify the selection of time levels to be saved. The number of elements to be specified
! should be equal to ntimes_saved.
!
!
! For Debugging. Set true to print out chksum information for debugging reproducing ability
! accross processors. default is false.
!
!
character(len=128) :: src_file = 'src_file.nc'
character(len=128) :: dest_grid = 'dest_grid.nc'
character(len=128) :: dest_file = 'dest_file.nc'
integer :: numfields = 2
character(len=128) :: src_field_name(max_fields) = ''
character(len=128) :: dest_field_name(max_fields) = ''
real :: stop_crit(max_fields) = 0.005
integer :: num_nbrs = 5
real :: max_dist = 0.1
real :: scale_factor(max_fields) = 1.0
logical :: use_source_vertical_grid = .FALSE.
logical :: apply_mask = .TRUE.
character(len=32) :: interp_method = "bilinear"
logical :: debug = .FALSE.
integer :: ntimes_saved = 0
integer :: timelevel_saved(max_ntimes_saved) = 0
namelist /regrid_3d_nml/ src_file, src_field_name, dest_field_name, numfields, dest_file, &
dest_grid, scale_factor, num_nbrs, max_dist, stop_crit, &
interp_method, apply_mask, use_source_vertical_grid, debug, &
ntimes_saved, timelevel_saved
!---------------------------------------------------------------------
integer :: ni_src, nj_src, nk_src, ni_dst, nj_dst, nk_dst, ntime_src
type(axistype) :: depth_axis, time_axis, axes_dst(2)
type(fieldtype) :: field_lon_dst, field_lat_dst
type(fieldtype) :: dest_field(max_fields)
integer :: dest_unit
logical :: time_axis_exists = .false.
real, parameter :: tol = 1.e-10 ! tolerance for detecting missing values
real, parameter :: max_val=1.e20
real, parameter :: rel_coef = 0.9
integer, parameter :: max_iter = 2000
logical :: is_cyclic = .true. ! we suppose the source data is always global data.
real :: missing(max_fields)
real :: D2R
type(fieldtype), dimension(:), allocatable :: src_field
integer :: src_unit
real, dimension(:), allocatable :: time_in
real, dimension(:), allocatable :: depth_src, depth_dst
real, dimension(:), allocatable :: lon_src, lat_src
real, dimension(:,:), allocatable :: lon_dst, lat_dst
real, dimension(:,:,:), allocatable :: mask_dst
!--- version information variables
character(len=128) :: version='CVS $Id: regrid_3d.f90,v 14.0.2.1 2009/10/15 16:12:00 z1l Exp $'
character(len=128) :: tagname='Tag $Name: mom4p1_pubrel_dec2009_nnz $'
! --- Begin of the program
! --- call fms_init, which will call mpp_init, mpp_io_init, mpp_domains_init
call fms_init
call constants_init
!--- call regrid_3d initialization routine
call regrid_3d_init
!--- read the dest_grid file
call read_dst_grid
!--- read src_file
call read_src_file
!--- set up metadata of output file
call setup_meta()
!--- remap data from src grid to dest grid and write out data to output file
call process_data()
call regrid_3d_end
call fms_end
contains
!#####################################################################
! --- read the namelist and write the version and namelist to logfile. Also
! --- write the namelist to standard output
subroutine regrid_3d_init
integer :: io_status, unit, ierr, n
D2R = PI/180.0
! --- the default src_field_name is 'temp and 'salt'
src_field_name(1) = 'temp'
src_field_name(2) = 'salt'
! --- read namelist ------------------------------------------------
if(file_exist('input.nml')) then
unit = open_namelist_file()
read (unit,regrid_3d_nml,IOSTAT=io_status)
write (stdout(),'(/)')
write (stdout(),regrid_3d_nml)
ierr = check_nml_error(io_status, 'regrid_3d_nml')
call close_file(unit)
else
call mpp_error(FATAL, 'regrid_3d: file input.nml does not exist' )
endif
if(ntimes_saved > 0) then
if(ntimes_saved > max_ntimes_saved) call mpp_error(FATAL, 'regrid_3d: nml ntimes_saved is greater than ' &
//'max_ntimes_saved, increase max_ntimes_saved or decrease ntimes_saved')
do n = 1, ntimes_saved
if(timelevel_saved(n) .le. 0) call mpp_error(FATAL, 'regrid_3d: nml timelevel_saved is not ' &
//'specified properly, modify timelevel_saved')
enddo
endif
if(numfields .gt. max_fields) call mpp_error(FATAL, &
'regrid_3d: numfields should be less than max_fields')
if (numfields .le. 0) call mpp_error(FATAL,'regrid_3d: No field specified')
!--- if dest_field_name is not defined in the namelist, get it from src_field_name
do n = 1, numfields
if(trim(dest_field_name(n)) == '') dest_field_name(n) = trim(src_field_name(n))
enddo
!--- write version information
call write_version_number(version, tagname)
end subroutine regrid_3d_init
!#####################################################################
!--- open grid file and store grid info
subroutine read_dst_grid
integer :: unit, ndim, nvar, natt, ntime, i, j, k
integer :: len1, siz_in(3)
logical :: found_xt, found_yt, found_kmt
character(len=32) :: name
real, allocatable, dimension(:,:) :: kmt
type(axistype), allocatable, dimension(:) :: axes
type(fieldtype), allocatable, dimension(:) :: fields
if(.not. file_exist(trim(dest_grid)) ) &
call mpp_error(FATAL, 'regrid_3d: file '//trim(dest_grid)//' does not exist')
call mpp_open(unit, trim(dest_grid),&
action=MPP_RDONLY, form=MPP_NETCDF, threading=MPP_MULTI, fileset=MPP_SINGLE)
call mpp_get_info(unit, ndim, nvar, natt, ntime)
allocate(fields(nvar), axes(ndim) )
call mpp_get_axes(unit, axes)
call mpp_get_fields(unit,fields)
!--------------------------------------------------------------------
! get output grid information
!--------------------------------------------------------------------
ni_dst=0; nj_dst=0; nk_dst=0
do i=1,ndim
call mpp_get_atts(axes(i),name=name,len=len1)
select case (trim(name))
case ('grid_x_T')
ni_dst = len1
case ('grid_y_T')
nj_dst = len1
case ('zt')
nk_dst = len1
allocate(depth_dst(nk_dst))
call mpp_get_axis_data(axes(i),depth_dst)
if(.not.use_source_vertical_grid) depth_axis = axes(i)
end select
enddo
if(ni_dst==0) call mpp_error(FATAL,'regrid_3d: file '//trim(dest_grid)//' does not contain axis grid_x_T')
if(nj_dst==0) call mpp_error(FATAL,'regrid_3d: file '//trim(dest_grid)//' does not contain axis grid_y_T')
if(nk_dst==0) call mpp_error(FATAL,'regrid_3d: file '//trim(dest_grid)//' does not contain axis zt')
allocate(lon_dst(ni_dst,nj_dst), lat_dst(ni_dst,nj_dst), kmt(ni_dst,nj_dst) )
found_xt = .FALSE.; found_yt = .FALSE.; found_kmt = .false.
do i=1,nvar
call mpp_get_atts(fields(i),name=name,ndim=ndim)
select case (trim(name))
case ('x_T')
found_xt = .true.
call mpp_read(unit,fields(i),lon_dst)
field_lon_dst = fields(i)
call mpp_get_atts(fields(i),axes=axes_dst)
case ('y_T')
found_yt = .true.
call mpp_read(unit,fields(i),lat_dst)
field_lat_dst = fields(i)
case ('num_levels')
found_kmt = .true.
call mpp_read(unit,fields(i),kmt )
end select
enddo
if(.not.found_kmt) call mpp_error(FATAL,'regrid_3d: field num_levels is not in the file '//trim(dest_grid) )
if(.not.found_xt) call mpp_error(FATAL,'regrid_3d: field x_T is not in the file '//trim(dest_grid) )
if(.not.found_yt) call mpp_error(FATAL,'regrid_3d: field y_T is not in the file '//trim(dest_grid) )
call mpp_close(unit)
allocate(mask_dst(ni_dst,nj_dst,nk_dst) )
do k = 1, nk_dst
do j = 1, nj_dst
do i = 1, ni_dst
if(kmt(i,j) .ge. k) then
mask_dst(i,j,k) = 1.0
else
mask_dst(i,j,k) = 0.0
endif
enddo
enddo
enddo
deallocate(fields, axes, kmt)
end subroutine read_dst_grid
!#####################################################################
!--- read source grid informationfrom src_file
subroutine read_src_file
integer :: unit, ndim, nvar, natt, n
integer :: nt, i, j, k, jj, len1
logical :: found_src_field(numfields)
character(len=1) :: cart
character(len=32) :: name, units
type(axistype), allocatable, dimension(:) :: axes
type(fieldtype), allocatable, dimension(:) :: fields
real :: scale, add
if(.not. file_exist(trim(src_file)) ) &
call mpp_error(FATAL, 'regrid_3d: file '//trim(src_file)//' does not exist')
call mpp_open(src_unit, trim(src_file),&
action=MPP_RDONLY, form=MPP_NETCDF, threading=MPP_MULTI, fileset=MPP_SINGLE)
call mpp_get_info(src_unit, ndim, nvar, natt, ntime_src)
allocate(fields(nvar))
call mpp_get_fields(src_unit, fields)
allocate(src_field(numfields))
found_src_field = .FALSE.
do n=1,numfields
do i=1,nvar
call mpp_get_atts(fields(i),name=name)
if (lowercase(trim(src_field_name(n))) == lowercase(trim(name))) then
src_field(n) = fields(i)
found_src_field(n) = .TRUE.
write(stdout(),*) 'Interpolating src field : ',trim(name), ' grid ', trim(dest_grid)
endif
end do
enddo
do n=1,numfields
if(.not. found_src_field(n)) call mpp_error(FATAL, 'regrid_3d: field '&
//trim(src_field_name(n))//' is not in the file '//trim(src_file) )
enddo
!--- get the src grid
call mpp_get_atts(src_field(1),ndim=ndim)
allocate(axes(ndim))
call mpp_get_atts(src_field(1),axes=axes)
ni_src=0; nj_src=0; nk_src=0; ntime_src = 1
do j=1,ndim
call mpp_get_atts(axes(j),len=len1,units=units)
call get_axis_cart(axes(j),cart)
select case (cart)
case ('X')
ni_src = len1
allocate(lon_src(ni_src))
call mpp_get_axis_data(axes(j),lon_src)
case('Y')
nj_src = len1
allocate(lat_src(nj_src))
call mpp_get_axis_data(axes(j),lat_src)
case('Z')
nk_src = len1
allocate(depth_src(nk_src))
if(use_source_vertical_grid) depth_axis = axes(j)
call mpp_get_axis_data(axes(j),depth_src)
if (trim(units) == 'cm') then
depth_src(:) = depth_src(:)*.01
endif
case ('T')
ntime_src = len1
time_axis_exists = .true.
allocate(time_in(ntime_src))
call mpp_get_times(src_unit, time_in)
time_axis = axes(j)
end select
enddo
if(ni_src==0) call mpp_error(FATAL,'regrid_3d: file ' &
//trim(src_file)//' does not contain axis with cartesian attributes = "X" ')
if(nj_src==0) call mpp_error(FATAL,'regrid_3d: file '&
//trim(src_file)//' does not contain axis with cartesian attributes = "Y" ')
if(nk_src==0) call mpp_error(FATAL,'regrid_3d: file '&
//trim(src_file)//' does not contain axis with cartesian attributes = "Z" ')
!--- if use selective time level, make sure the time level selection is suitable.
if(ntimes_saved > ntime_src) call mpp_error(FATAL, 'regrid_3d: nml ntime_src is greater than ' &
//'number of time levels in the file '// trim(src_file) )
do n = 1, ntimes_saved
if(timelevel_saved(n) > ntime_src) call mpp_error(FATAL, 'regrid_3d: some entry in nml timelevel_saved ' &
// 'is greater than number of time levels in the file '// trim(src_file) )
enddo
!--- get the missing value
do n=1, numfields
call mpp_get_atts(src_field(n),missing=missing(n),scale=scale, add=add )
if(scale .NE. 1 .OR. add .NE. 0) missing(n) = missing(n)*scale + add
enddo
deallocate(fields, axes)
end subroutine read_src_file
!#####################################################################
!--- setup metadata of output file
subroutine setup_meta
integer :: i, j, n, nt
character(len=32) :: units
character(len=128) :: longname
!--------------------------------------------------------------------
! write output file metadata
!--------------------------------------------------------------------
call mpp_open(dest_unit, trim(dest_file),MPP_OVERWR,MPP_NETCDF,threading=MPP_SINGLE,&
fileset=MPP_SINGLE)
!
! write axis metadata
!
do i=1,size(axes_dst(:))
call mpp_copy_meta(dest_unit,axes_dst(i))
end do
call mpp_copy_meta(dest_unit,depth_axis)
if (time_axis_exists) then
call mpp_copy_meta(dest_unit, time_axis)
endif
!
! write variable metadata
!
call mpp_copy_meta(dest_unit,field_lon_dst,axes=axes_dst)
call mpp_copy_meta(dest_unit,field_lat_dst,axes=axes_dst)
do n = 1, numfields
call mpp_get_atts(src_field(n),units=units,longname=longname)
if (time_axis_exists) then
call mpp_write_meta(dest_unit, dest_field(n), (/axes_dst(1),axes_dst(2), depth_axis,time_axis/), &
trim(dest_field_name(n)), units,longname, missing=missing(n), pack=1)
else
call mpp_write_meta(dest_unit, dest_field(n), (/axes_dst(1),axes_dst(2), depth_axis/), &
trim(dest_field_name(n)), units,longname, missing=missing(n), pack=1)
endif
enddo
! write axis data
do i=1,size(axes_dst(:))
call mpp_write(dest_unit,axes_dst(i))
end do
call mpp_write(dest_unit,depth_axis)
! write variable data
call mpp_write(dest_unit, field_lon_dst,lon_dst)
call mpp_write(dest_unit, field_lat_dst,lat_dst)
end subroutine setup_meta
!#####################################################################
!--- remap data from src grid to destination grid.
subroutine process_data
integer :: ndivs, i, j, k, n, nt, stat, ntimelevel, l
integer :: isc, iec, jsc, jec, layout(2) = (/1,0/)
real :: tmp_x, tmp_y
type(domain2d) :: Domain
type(horiz_interp_type) :: Interp
real, dimension(:,:,:), allocatable :: tmp1, tmp2, tmp, mask_src, data_dst, data_src
real, dimension(:,:,:), allocatable :: depth_src_3d, depth_dst_3d
!--- decompose model grid points
!--- mapping can get expensive so we distribute the task at this level
ndivs = mpp_npes()
call mpp_define_layout ((/1,ni_dst,1,nj_dst/), ndivs, layout)
call mpp_define_domains((/1,ni_dst,1,nj_dst/),layout, Domain,xhalo=0,yhalo=0)
call mpp_get_compute_domain (Domain, isc, iec, jsc, jec)
call horiz_interp_new(Interp, lon_src*D2R, lat_src*D2R, lon_dst(isc:iec,jsc:jec)*D2R, &
lat_dst(isc:iec,jsc:jec)*D2R, interp_method = trim(interp_method), &
num_nbrs = num_nbrs, max_dist=max_dist, grid_at_center = .true. )
allocate(tmp1(ni_src,nj_src, nk_src))
allocate(data_src(ni_src,nj_src, nk_src))
if( use_source_vertical_grid) then
allocate(data_dst(ni_dst,nj_dst,nk_src), tmp(isc:iec,jsc:jec, nk_src) )
call mpp_domains_set_stack_size(2*ni_dst*nj_dst*nk_src)
else
call mpp_domains_set_stack_size(2*ni_dst*nj_dst*nk_dst)
allocate(tmp2(isc:iec,jsc:jec, nk_src) )
allocate(data_dst(ni_dst,nj_dst,nk_dst), tmp(isc:iec,jsc:jec, nk_dst) )
allocate(depth_src_3d(isc:iec,jsc:jec, nk_src))
allocate(depth_dst_3d(isc:iec,jsc:jec,nk_dst))
do k=1,nk_src
depth_src_3d(:,:,k) = depth_src(k)
enddo
do k=1,nk_dst
depth_dst_3d(:,:,k) = depth_dst(k)
enddo
endif
if(ntimes_saved > 0) then
ntimelevel = ntimes_saved
else
ntimelevel = ntime_src
endif
write(stdout(),*)' There are ', ntimelevel, ' time steps to be saved to output file.'
do l = 1, ntimelevel
if(ntimes_saved > 0) then
nt = timelevel_saved(l)
else
nt = l
endif
write(stdout(),*)'**************At time step ', l
do n = 1, numfields
!--- read source data
call mpp_read(src_unit,src_field(n),data_src,nt)
if( n==1 .and. nt == 1) then
allocate(mask_src(ni_src,nj_src,nk_src))
mask_src = 1.0
do k = 1, nk_src
do j = 1, nj_src
do i = 1, ni_src
if (abs(data_src(i,j,k) - missing(1)) <= tol) mask_src(i,j,k) = 0.
if (abs(data_src(i,j,k)) > max_val) mask_src(i,j,k) = 0.0
enddo
enddo
enddo
endif
!--- if scale_factor is not 1, multiple scale_factor to each data
if(scale_factor(n) .ne. 1) then
do k =1, nk_src
do j =1, nj_src
do i =1, ni_src
if(mask_src(i,j,k) >0.5) data_src(i,j,k) = data_src(i,j,k)*scale_factor(n)
enddo
enddo
enddo
endif
!--- begin to regrid data
if( use_source_vertical_grid) then
if(apply_mask) then
do k = 1, nk_src
call horiz_interp(Interp, data_src(:,:,k), tmp(:,:,k), &
mask_in = mask_src(:,:,k), missing_value=missing(n) )
enddo
else
if(any(mask_src == 0.0) ) then ! do laplace extrap if needed.
call extrap(data_src(:,:,:), tmp1(:,:,:), stop_crit(n), &
missing(n)*scale_factor(n), is_cyclic )
do k = 1, nk_src
call horiz_interp(Interp, tmp1(:,:,k), tmp(:,:,k))
enddo
else
do k = 1, nk_src
call horiz_interp(Interp, data_src(:,:,k), tmp(:,:,k) )
enddo
endif
endif
else
if(any(mask_src == 0.0) ) then ! do laplace extrap if needed.
call extrap(data_src(:,:,:), tmp1(:,:,:), stop_crit(n), &
missing(n)*scale_factor(n), is_cyclic )
do k = 1, nk_src
call horiz_interp(Interp, tmp1(:,:,k), tmp2(:,:,k) )
enddo
else
do k = 1, nk_src
call horiz_interp(Interp, data_src(:,:,k), tmp2(:,:,k) )
enddo
endif
call interp_1d(depth_src_3d,depth_dst_3d,tmp2,tmp)
do k = 1, nk_dst
if(apply_mask) then
do j = jsc, jec
do i = isc, iec
if(mask_dst(i,j,k) < 0.5) tmp(i,j,k) = missing(n)
enddo
enddo
endif
enddo
endif
!--- get global data
call mpp_global_field(Domain,tmp, data_dst(:,:,:) )
if(mpp_pe()==mpp_root_pe()) then ! --- write out data from root pe
if (time_axis_exists) then
call mpp_write(dest_unit, dest_field(n), data_dst,time_in(nt))
else
call mpp_write(dest_unit, dest_field(n), data_dst)
endif
if(debug) then !--- the chksum is for debugging purpose
write(stdout(),*)'NOTE: At time level ',nt,', Chksum for ',trim(src_field_name(n)), &
' after-regrid data ', mpp_chksum(data_dst, (/mpp_root_pe()/) )
endif
endif
enddo
enddo
deallocate(tmp, tmp1, data_src, data_dst, mask_src)
if(.not. use_source_vertical_grid) deallocate(depth_src_3d, depth_dst_3d, tmp2 )
!--- write out chksum for parallel checking
call mpp_close(dest_unit)
call mpp_close(src_unit)
call horiz_interp_end
end subroutine process_data
!#####################################################################
!--- release the memory
subroutine regrid_3d_end
deallocate(depth_src, depth_dst, lon_src, lat_src, lon_dst, lat_dst, mask_dst)
if(time_axis_exists) deallocate(time_in)
end subroutine regrid_3d_end
!#####################################################################
subroutine extrap(data_in, data_out, crit, missing_value, is_cyclic )
real, dimension(:,:,:), intent(in) :: data_in
real, dimension(:,:,:), intent(out) :: data_out
real, intent(in) :: crit, missing_value
logical, intent(in) :: is_cyclic
real :: resmax, initial_guess = 0.0
integer :: ni, nj, nk, i, j, k, n
real, dimension(0:size(data_in,1)+1, 0:size(data_in,2)+1) :: tmp
real, dimension(size(data_in,1), size(data_in,2) ) :: sor, res
real, dimension(size(data_in,1), size(data_in,2) ) :: cfn, cfs, cfe, cfw, cfc
real :: latp, latm, cstr, csm, csj
real, dimension(size(data_in,1)) :: dxu, dxt
real, dimension(size(data_in,2)) :: dyu, dyt
ni = size(data_in,1)
nj = size(data_in,2)
nk = size(data_in,3)
! construct grid factors for a sphere
do j=1,nj-1
dyu(j) = lat_src(j+1)-lat_src(j)
enddo
dyu(nj) = dyu(nj-1)
do j=2,nj
dyt(j) = 0.5*(dyu(j)+dyu(j-1))
enddo
dyt(1) = dyt(2)
do i=1,ni-1
dxu(i) = lon_src(i+1)-lon_src(i)
enddo
dxu(ni) = dxu(ni-1)
do i=2,ni
dxt(i) = 0.5*(dxu(i)+dxu(i-1))
enddo
dxt(1) = dxt(2)
do j= 1, nj
if (j == nj) then
latp = lat_src(j) + 0.5*(lat_src(j) - lat_src(j-1))
else
latp = 0.5*(lat_src(j) + lat_src(j+1))
endif
if (j == 1) then
latm = lat_src(j) - 0.5*(lat_src(j+1) - lat_src(j))
else
latm = 0.5*(lat_src(j) + lat_src(j-1))
endif
csj = cos(latp*pi/180.0)
csm = cos(latm*pi/180.0)
cstr = 1.0/cos(lat_src(j)*pi/180.)
do i= 1,ni
cfn(i,j) = csj*cstr/(dyt(j)*dyu(j))
cfs(i,j) = csm*cstr/(dyt(j)*dyu(max(j-1,1)))
cfe(i,j) = cstr**2/(dxu(i)*dxt(i))
cfw(i,j) = cstr**2/(dxu(max(i-1,1))*dxt(i))
cfc(i,j) = 1.0/(cfn(i,j)+cfs(i,j)+cfe(i,j)+cfw(i,j))
cfn(i,j) = cfn(i,j)*cfc(i,j)
cfs(i,j) = cfs(i,j)*cfc(i,j)
cfe(i,j) = cfe(i,j)*cfc(i,j)
cfw(i,j) = cfw(i,j)*cfc(i,j)
enddo
enddo
tmp = 0.0
do j= 1, nj
do i= 1, ni
if(abs(data_in(i,j,1) - missing_value) <= tol) then
tmp(i,j) = initial_guess
endif
enddo
enddo
do k = 1, nk
do j= 1, nj
do i= 1, ni
if(abs(data_in(i,j,k) - missing_value) <= tol ) then
sor(i,j) = rel_coef
else
tmp(i,j) = data_in(i,j,k)
sor(i,j) = 0.0
endif
enddo
enddo
call fill_boundaries(tmp, is_cyclic)
! iterate
n=1
do
resmax=0.0
do j= 1, nj
do i= 1, ni
res(i,j) = cfw(i,j)*tmp(i-1,j) + cfe(i,j)*tmp(i+1,j) + cfs(i,j)*tmp(i,j-1) + cfn(i,j)*tmp(i,j+1) - tmp(i,j)
enddo
enddo
do j= 1, nj
do i= 1, ni
res(i,j) = res(i,j)*sor(i,j)
tmp(i,j)=tmp(i,j)+res(i,j)
resmax = max(abs(res(i,j)),resmax)
enddo
enddo
if(resmax .le. crit .or. n > max_iter) then
data_out(:,:,k) = tmp(1:ni,1:nj)
exit
endif
!--- update boundaries
call fill_boundaries(tmp, is_cyclic)
n=n+1
enddo
if(mpp_pe() == mpp_root_pe() ) write(stdout(),'(a,i6,a)') 'Stopped after ',n,' iterations'
if(mpp_pe() == mpp_root_pe() ) write(stdout(),'(a,f10.4)') 'maxres= ',resmax
enddo
end subroutine extrap
!#####################################################################
subroutine fill_boundaries(data, is_cyclic)
real, dimension(0:,0:), intent(inout) :: data
logical, intent(in) :: is_cyclic
integer :: i,j, ni, nj
ni = size(data,1) - 2
nj = size(data,2) - 2
if(is_cyclic) then
data(0,1:nj) = data(ni,1:nj)
data(ni+1,1:nj) = data(1,1:nj)
endif
return
end subroutine fill_boundaries
!#####################################################################
end program regrid_3d