# 1 "../horiz_interp/horiz_interp_conserve.F90"
!***********************************************************************
!* 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 .
!***********************************************************************
module horiz_interp_conserve_mod
! Bruce Wyman
! Zhi Liang
!
!
! Performs spatial interpolation between grids using conservative interpolation
!
!
! This module can conservatively interpolate data from any logically rectangular grid
! to any rectangular grid. The interpolation scheme is area-averaging
! conservative scheme. There is an optional mask field for missing input data in both
! horiz_interp__conserveinit and horiz_interp_conserve. For efficiency purpose, mask should only be
! kept in horiz_interp_init (will remove the mask in horiz_interp in the future).
! There are 1-D and 2-D version of horiz_interp_conserve_init for 1-D and 2-D grid.
! There is a optional argument mask in horiz_interp_conserve_init_2d and no mask should
! to passed into horiz_interp_conserv. optional argument mask will not be passed into
! horiz_interp_conserve_init_1d and optional argument mask may be passed into
! horiz_interp_conserve (For the purpose of reproduce Memphis??? results).
! An optional output mask field may be used in conjunction with the input mask to show
! where output data exists.
!
# 1 "../include/fms_platform.h" 1
! -*-f90-*-*
!***********************************************************************
!* 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 .
!***********************************************************************
!Set type kinds.
# 37
!These values are not necessarily portable.
!DEC$ MESSAGE:'Using 8-byte addressing'
!Control "pure" functions.
# 54
!DEC$ MESSAGE:'Using pure routines.'
!Control array members of derived types.
# 66
!DEC$ MESSAGE:'Using allocatable derived type array members.'
!Control use of cray pointers.
# 78
!DEC$ MESSAGE:'Using cray pointers.'
!Control size of integers that will hold address values.
!Appears for legacy reasons, but seems rather dangerous.
# 89
!If you do not want to use 64-bit integers.
# 95
!If you do not want to use 32-bit floats.
# 106
!If you want to use quad-precision.
# 115
# 45 "../horiz_interp/horiz_interp_conserve.F90" 2
use mpp_mod, only: mpp_send, mpp_recv, mpp_pe, mpp_root_pe, mpp_npes
use mpp_mod, only: mpp_error, FATAL, mpp_sync_self
use mpp_mod, only: COMM_TAG_1, COMM_TAG_2
use fms_mod, only: write_version_number
use fms_io_mod, only: get_great_circle_algorithm
use constants_mod, only: PI
use horiz_interp_type_mod, only: horiz_interp_type
implicit none
private
! public interface
!
!
! Allocates space and initializes a derived-type variable
! that contains pre-computed interpolation indices and weights.
!
!
! Allocates space and initializes a derived-type variable
! that contains pre-computed interpolation indices and weights
! for improved performance of multiple interpolations between
! the same grids.
!
!
! Longitude (in radians) for source data grid.
!
!
! Latitude (in radians) for source data grid.
!
!
! Longitude (in radians) for destination data grid.
!
!
! Latitude (in radians) for destination data grid.
!
!
! flag for the amount of print output.
!
!
! Input mask. must be the size (size(lon_in)-1, size(lon. The real value of
! mask_in must be in the range (0.,1.). Set mask_in=0.0 for data points
! that should not be used or have missing data.
!
!
! Output mask that specifies whether data was computed.
!
!
! A derived-type variable containing indices and weights used for subsequent
! interpolations. To reinitialize this variable for a different grid-to-grid
! interpolation you must first use the "horiz_interp_del" interface.
!
interface horiz_interp_conserve_new
module procedure horiz_interp_conserve_new_1dx1d
module procedure horiz_interp_conserve_new_1dx2d
module procedure horiz_interp_conserve_new_2dx1d
module procedure horiz_interp_conserve_new_2dx2d
end interface
!
public :: horiz_interp_conserve_init
public :: horiz_interp_conserve_new, horiz_interp_conserve, horiz_interp_conserve_del
integer :: pe, root_pe
!-----------------------------------------------------------------------
! Include variable "version" to be written to log file.
# 1 "../include/file_version.h" 1
! -*-f90-*-
!***********************************************************************
!* 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 .
!***********************************************************************
# 23
character(len=*), parameter :: version = 'unknown'
# 114 "../horiz_interp/horiz_interp_conserve.F90" 2
logical :: module_is_initialized = .FALSE.
logical :: great_circle_algorithm = .false.
contains
!#######################################################################
!
!
! writes version number to logfile.out
!
!
! writes version number to logfile.out
!
subroutine horiz_interp_conserve_init
if(module_is_initialized) return
call write_version_number("HORIZ_INTERP_CONSERVE_MOD", version)
great_circle_algorithm = get_great_circle_algorithm()
module_is_initialized = .true.
end subroutine horiz_interp_conserve_init
!
!#######################################################################
!
subroutine horiz_interp_conserve_new_1dx1d ( Interp, lon_in, lat_in, lon_out, lat_out, verbose)
type(horiz_interp_type), intent(inout) :: Interp
real, intent(in), dimension(:) :: lon_in , lat_in
real, intent(in), dimension(:) :: lon_out, lat_out
integer, intent(in), optional :: verbose
!
!-----------------------------------------------------------------------
real, dimension(size(lat_out(:))-1,2) :: sph
real, dimension(size(lon_out(:))-1,2) :: theta
real, dimension(size(lat_in(:))) :: slat_in
real, dimension(size(lon_in(:))-1) :: dlon_in
real, dimension(size(lat_in(:))-1) :: dsph_in
real, dimension(size(lon_out(:))-1) :: dlon_out
real, dimension(size(lat_out(:))-1) :: dsph_out
real :: blon, fac, hpi, tpi, eps
integer :: num_iters = 4
integer :: i, j, m, n, nlon_in, nlat_in, nlon_out, nlat_out, &
iverbose, m2, n2, iter
logical :: s2n
character(len=64) :: mesg
if(.not. module_is_initialized) call mpp_error(FATAL, &
'horiz_interp_conserve_new_1dx1d: horiz_interp_conserve_init is not called')
if(great_circle_algorithm) call mpp_error(FATAL, &
'horiz_interp_conserve_new_1dx1d: great_circle_algorithm is not implemented, contact developer')
!-----------------------------------------------------------------------
iverbose = 0; if (present(verbose)) iverbose = verbose
pe = mpp_pe()
root_pe = mpp_root_pe()
!-----------------------------------------------------------------------
hpi = 0.5*pi
tpi = 4.*hpi
Interp%version = 1
nlon_in = size(lon_in(:))-1; nlat_in = size(lat_in(:))-1
nlon_out = size(lon_out(:))-1; nlat_out = size(lat_out(:))-1
allocate ( Interp % facj (nlat_out,2), Interp % jlat (nlat_out,2), &
Interp % faci (nlon_out,2), Interp % ilon (nlon_out,2), &
Interp % area_src (nlon_in, nlat_in), &
Interp % area_dst (nlon_out, nlat_out) )
!-----------------------------------------------------------------------
! --- set-up for input grid boxes ---
do j = 1, nlat_in+1
slat_in(j) = sin(lat_in(j))
enddo
do j = 1, nlat_in
dsph_in(j) = abs(slat_in(j+1)-slat_in(j))
enddo
do i = 1,nlon_in
dlon_in(i) = abs(lon_in(i+1)-lon_in(i))
enddo
! set south to north flag
s2n = .true.
if (lat_in(1) > lat_in(nlat_in+1)) s2n = .false.
!-----------------------------------------------------------------------
! --- set-up for output grid boxes ---
do n = 1, nlat_out
dsph_out(n) = abs(sin(lat_out(n+1))-sin(lat_out(n)))
enddo
do m = 1,nlon_out
theta(m,1) = lon_out(m)
theta(m,2) = lon_out(m+1)
dlon_out(m) = abs(lon_out(m+1)-lon_out(m))
enddo
Interp%nlon_src = nlon_in; Interp%nlat_src = nlat_in
Interp%nlon_dst = nlon_out; Interp%nlat_dst = nlat_out
!***********************************************************************
!------ set up latitudinal indexing ------
!------ make sure output grid goes south to north ------
do n = 1, nlat_out
if (lat_out(n) < lat_out(n+1)) then
sph(n,1) = sin(lat_out(n))
sph(n,2) = sin(lat_out(n+1))
else
sph(n,1) = sin(lat_out(n+1))
sph(n,2) = sin(lat_out(n))
endif
enddo
Interp%jlat = 0
do n2 = 1, 2 ! looping on grid box edges
do n = 1, nlat_out ! looping on output latitudes
eps = 0.0
do iter=1,num_iters
! find indices from input latitudes
do j = 1, nlat_in
if ( (s2n .and. (slat_in(j)-sph(n,n2)) <= eps .and. &
(sph(n,n2)-slat_in(j+1)) <= eps) .or. &
(.not.s2n .and. (slat_in(j+1)-sph(n,n2)) <= eps .and. &
(sph(n,n2)-slat_in(j)) <= eps) ) then
Interp%jlat(n,n2) = j
! weight with sin(lat) to exactly conserve area-integral
fac = (sph(n,n2)-slat_in(j))/(slat_in(j+1)-slat_in(j))
if (s2n) then
if (n2 == 1) Interp%facj(n,n2) = 1.0 - fac
if (n2 == 2) Interp%facj(n,n2) = fac
else
if (n2 == 1) Interp%facj(n,n2) = fac
if (n2 == 2) Interp%facj(n,n2) = 1.0 - fac
endif
exit
endif
enddo
if ( Interp%jlat(n,n2) /= 0 ) exit
! did not find this output grid edge in the input grid
! increase tolerance for multiple passes
eps = epsilon(sph)*real(10**iter)
enddo
! no match
if ( Interp%jlat(n,n2) == 0 ) then
write (mesg,710) n,sph(n,n2)
710 format (': n,sph=',i3,f14.7,40x)
call mpp_error(FATAL, 'horiz_interp_conserve_mod:no latitude index found'//trim(mesg))
endif
enddo
enddo
!------ set up longitudinal indexing ------
Interp%ilon = 0
do m2 = 1, 2 ! looping on grid box edges
do m = 1, nlon_out ! looping on output longitudes
blon = theta(m,m2)
if ( blon < lon_in(1) ) blon = blon + tpi
if ( blon > lon_in(nlon_in+1) ) blon = blon - tpi
eps = 0.0
do iter=1,num_iters
! find indices from input longitudes
do i = 1, nlon_in
if ( (lon_in(i)-blon) <= eps .and. &
(blon-lon_in(i+1)) <= eps ) then
Interp%ilon(m,m2) = i
fac = (blon-lon_in(i))/(lon_in(i+1)-lon_in(i))
if (m2 == 1) Interp%faci(m,m2) = 1.0 - fac
if (m2 == 2) Interp%faci(m,m2) = fac
exit
endif
enddo
if ( Interp%ilon(m,m2) /= 0 ) exit
! did not find this output grid edge in the input grid
! increase tolerance for multiple passes
eps = epsilon(blon)*real(10**iter)
enddo
! no match
if ( Interp%ilon(m,m2) == 0 ) then
print *, 'lon_out,blon,blon_in,eps=', &
theta(m,m2),blon,lon_in(1),lon_in(nlon_in+1),eps
call mpp_error(FATAL, 'horiz_interp_conserve_mod: no longitude index found')
endif
enddo
enddo
! --- area of input grid boxes ---
do j = 1,nlat_in
do i = 1,nlon_in
Interp%area_src(i,j) = dlon_in(i) * dsph_in(j)
enddo
enddo
! --- area of output grid boxes ---
do n = 1, nlat_out
do m = 1, nlon_out
Interp%area_dst(m,n) = dlon_out(m) * dsph_out(n)
enddo
enddo
!-----------------------------------------------------------------------
! this output may be quite lengthy and is not recommended
! when using more than one processor
if (iverbose > 2) then
write (*,801) (i,Interp%ilon(i,1),Interp%ilon(i,2), &
Interp%faci(i,1),Interp%faci(i,2),i=1,nlon_out)
write (*,802) (j,Interp%jlat(j,1),Interp%jlat(j,2), &
Interp%facj(j,1),Interp%facj(j,2),j=1,nlat_out)
801 format (/,2x,'i',4x,'is',5x,'ie',4x,'facis',4x,'facie', &
/,(i4,2i7,2f10.5))
802 format (/,2x,'j',4x,'js',5x,'je',4x,'facjs',4x,'facje', &
/,(i4,2i7,2f10.5))
endif
!-----------------------------------------------------------------------
end subroutine horiz_interp_conserve_new_1dx1d
!#######################################################################
!
subroutine horiz_interp_conserve_new_1dx2d ( Interp, lon_in, lat_in, lon_out, lat_out, &
mask_in, mask_out, verbose)
type(horiz_interp_type), intent(inout) :: Interp
real, intent(in), dimension(:) :: lon_in , lat_in
real, intent(in), dimension(:,:) :: lon_out, lat_out
real, intent(in), optional, dimension(:,:) :: mask_in
real, intent(inout), optional, dimension(:,:) :: mask_out
integer, intent(in), optional :: verbose
!
integer :: create_xgrid_1DX2D_order1, get_maxxgrid, maxxgrid
integer :: create_xgrid_great_circle
integer :: nlon_in, nlat_in, nlon_out, nlat_out, nxgrid, i, j
real(8), dimension(size(lon_in(:))-1, size(lat_in(:))-1) :: mask_src
integer, allocatable, dimension(:) :: i_src, j_src, i_dst, j_dst
real(8), allocatable, dimension(:) :: xgrid_area, clon, clat
real(8), allocatable, dimension(:,:) :: dst_area, lon_src, lat_src
real(8), allocatable, dimension(:) :: lat_in_flip
real(8), allocatable, dimension(:,:) :: mask_src_flip
real(8), allocatable, dimension(:) :: lon_in_r8, lat_in_r8
real(8), allocatable, dimension(:,:) :: lon_out_r8, lat_out_r8
integer :: nincrease, ndecrease
logical :: flip_lat
integer :: wordsz
integer(kind=1) :: one_byte(8)
if(.not. module_is_initialized) call mpp_error(FATAL, &
'horiz_interp_conserve_new_1dx2d: horiz_interp_conserve_init is not called')
wordsz=size(transfer(lon_in(1), one_byte))
if(wordsz .NE. 4 .AND. wordsz .NE. 8) call mpp_error(FATAL, &
'horiz_interp_conserve_new_1dx2d: wordsz should be 4 or 8')
if( (size(lon_out,1) .NE. size(lat_out,1)) .OR. (size(lon_out,2) .NE. size(lat_out,2)) ) &
call mpp_error(FATAL, 'horiz_interp_conserve_mod: size mismatch between lon_out and lat_out')
nlon_in = size(lon_in(:)) - 1; nlat_in = size(lat_in(:)) - 1
nlon_out = size(lon_out,1) - 1; nlat_out = size(lon_out,2) - 1
mask_src = 1.
if(present(mask_in)) then
if( (size(mask_in,1) .NE. nlon_in) .OR. (size(mask_in,2) .NE. nlat_in)) call mpp_error(FATAL, &
'horiz_interp_conserve_mod: size mismatch between mask_in and lon_in/lat_in')
mask_src = mask_in
end if
maxxgrid = get_maxxgrid()
allocate(i_src(maxxgrid), j_src(maxxgrid), i_dst(maxxgrid), j_dst(maxxgrid) )
allocate( xgrid_area(maxxgrid), dst_area(nlon_out, nlat_out) )
!--- check if source latitude is flipped
nincrease = 0
ndecrease = 0
do j = 1, nlat_in
if( lat_in(j+1) > lat_in(j) ) then
nincrease = nincrease + 1
else if ( lat_in(j+1) < lat_in(j) ) then
ndecrease = ndecrease + 1
endif
enddo
if(nincrease == nlat_in) then
flip_lat = .false.
else if(ndecrease == nlat_in) then
flip_lat = .true.
else
call mpp_error(FATAL, 'horiz_interp_conserve_mod: nlat_in should be equal to nincreaase or ndecrease')
endif
if(wordsz==4) then
allocate(lon_out_r8(size(lon_out,1),size(lon_out,2)))
allocate(lat_out_r8(size(lat_out,1),size(lat_out,2)))
lon_out_r8 = lon_out
lat_out_r8 = lat_out
endif
if( .not. great_circle_algorithm ) then
if(flip_lat) then
allocate(lat_in_flip(nlat_in+1), mask_src_flip(nlon_in,nlat_in))
do j = 1, nlat_in+1
lat_in_flip(j) = lat_in(nlat_in+2-j)
enddo
do j = 1, nlat_in
mask_src_flip(:,j) = mask_src(:,nlat_in+1-j)
enddo
if(wordsz==4) then
allocate(lon_in_r8(size(lon_in)))
lon_in_r8 = lon_in
nxgrid = create_xgrid_1DX2D_order1(nlon_in, nlat_in, nlon_out, nlat_out, lon_in_r8, lat_in_flip, &
lon_out_r8, lat_out_r8, mask_src_flip, i_src, j_src, i_dst, j_dst, xgrid_area)
deallocate(lon_in_r8)
else
nxgrid = create_xgrid_1DX2D_order1(nlon_in, nlat_in, nlon_out, nlat_out, lon_in, lat_in_flip, lon_out, lat_out, &
mask_src_flip, i_src, j_src, i_dst, j_dst, xgrid_area)
endif
deallocate(lat_in_flip, mask_src_flip)
else
if(wordsz==4) then
allocate(lon_in_r8(size(lon_in)))
allocate(lat_in_r8(size(lat_in)))
lon_in_r8 = lon_in
lat_in_r8 = lat_in
nxgrid = create_xgrid_1DX2D_order1(nlon_in, nlat_in, nlon_out, nlat_out, lon_in_r8, lat_in_r8, lon_out_r8, lat_out_r8, &
mask_src, i_src, j_src, i_dst, j_dst, xgrid_area)
deallocate(lon_in_r8,lat_in_r8)
else
nxgrid = create_xgrid_1DX2D_order1(nlon_in, nlat_in, nlon_out, nlat_out, lon_in, lat_in, lon_out, lat_out, &
mask_src, i_src, j_src, i_dst, j_dst, xgrid_area)
endif
endif
else
allocate(lon_src(nlon_in+1,nlat_in+1), lat_src(nlon_in+1,nlat_in+1))
allocate(clon(maxxgrid), clat(maxxgrid))
if(flip_lat) then
allocate(mask_src_flip(nlon_in,nlat_in))
do j = 1, nlat_in+1
do i = 1, nlon_in+1
lon_src(i,j) = lon_in(i)
lat_src(i,j) = lat_in(nlat_in+2-j)
enddo
enddo
do j = 1, nlat_in
mask_src_flip(:,j) = mask_src(:,nlat_in+1-j)
enddo
if(wordsz==4) then
nxgrid = create_xgrid_great_circle(nlon_in, nlat_in, nlon_out, nlat_out, lon_src, lat_src, lon_out_r8, lat_out_r8, &
mask_src_flip, i_src, j_src, i_dst, j_dst, xgrid_area, clon, clat)
else !wordsz==8
nxgrid = create_xgrid_great_circle(nlon_in, nlat_in, nlon_out, nlat_out, lon_src, lat_src, lon_out, lat_out, &
mask_src_flip, i_src, j_src, i_dst, j_dst, xgrid_area, clon, clat)
endif
deallocate(mask_src_flip)
else
do j = 1, nlat_in+1
do i = 1, nlon_in+1
lon_src(i,j) = lon_in(i)
lat_src(i,j) = lat_in(j)
enddo
enddo
if(wordsz==4) then
nxgrid = create_xgrid_great_circle(nlon_in, nlat_in, nlon_out, nlat_out, lon_src, lat_src, lon_out_r8, lat_out_r8, &
mask_src, i_src, j_src, i_dst, j_dst, xgrid_area, clon, clat)
else
nxgrid = create_xgrid_great_circle(nlon_in, nlat_in, nlon_out, nlat_out, lon_src, lat_src, lon_out, lat_out, &
mask_src, i_src, j_src, i_dst, j_dst, xgrid_area, clon, clat)
endif
endif
deallocate(lon_src, lat_src, clon, clat)
endif
if(wordsz==4) deallocate(lon_out_r8, lat_out_r8)
allocate(Interp%i_src(nxgrid), Interp%j_src(nxgrid) )
allocate(Interp%i_dst(nxgrid), Interp%j_dst(nxgrid) )
allocate(Interp%area_frac_dst(nxgrid) )
Interp%version = 2
Interp%nxgrid = nxgrid
Interp%i_src = i_src(1:nxgrid)+1 ! in C, the starting index is 0
Interp%j_src = j_src(1:nxgrid)+1
if(flip_lat) Interp%j_src = nlat_in+1-Interp%j_src
Interp%i_dst = i_dst(1:nxgrid)+1
Interp%j_dst = j_dst(1:nxgrid)+1
! sum over exchange grid area to get destination grid area
dst_area = 0.
do i = 1, nxgrid
dst_area(Interp%i_dst(i), Interp%j_dst(i)) = dst_area(Interp%i_dst(i), Interp%j_dst(i)) + xgrid_area(i)
end do
do i = 1, nxgrid
Interp%area_frac_dst(i) = xgrid_area(i)/dst_area(Interp%i_dst(i), Interp%j_dst(i) )
end do
Interp%nlon_src = nlon_in; Interp%nlat_src = nlat_in
Interp%nlon_dst = nlon_out; Interp%nlat_dst = nlat_out
if(present(mask_out)) then
if( (size(mask_out,1) .NE. nlon_out) .OR. (size(mask_out,2) .NE. nlat_out) ) call mpp_error(FATAL, &
'horiz_interp_conserve_mod: size mismatch between mask_out and lon_out/lat_out')
mask_out = 0.0
do i = 1, nxgrid
mask_out(Interp%i_dst(i),Interp%j_dst(i)) = mask_out(Interp%i_dst(i),Interp%j_dst(i)) + Interp%area_frac_dst(i)
end do
end if
deallocate(i_src, j_src, i_dst, j_dst, xgrid_area, dst_area )
end subroutine horiz_interp_conserve_new_1dx2d
!#######################################################################
!
subroutine horiz_interp_conserve_new_2dx1d ( Interp, lon_in, lat_in, lon_out, lat_out, &
mask_in, mask_out, verbose)
type(horiz_interp_type), intent(inout) :: Interp
real, intent(in), dimension(:,:) :: lon_in , lat_in
real, intent(in), dimension(:) :: lon_out, lat_out
real, intent(in), optional, dimension(:,:) :: mask_in
real, intent(inout), optional, dimension(:,:) :: mask_out
integer, intent(in), optional :: verbose
!
integer :: create_xgrid_2DX1D_order1, get_maxxgrid, maxxgrid
integer :: create_xgrid_great_circle
integer :: nlon_in, nlat_in, nlon_out, nlat_out, nxgrid, i, j
real, dimension(size(lon_in,1)-1, size(lon_in,2)-1) :: mask_src
integer, allocatable, dimension(:) :: i_src, j_src, i_dst, j_dst
real, allocatable, dimension(:) :: xgrid_area, clon, clat
real, allocatable, dimension(:,:) :: dst_area, lon_dst, lat_dst
integer :: wordsz
integer(kind=1) :: one_byte(8)
if(.not. module_is_initialized) call mpp_error(FATAL, &
'horiz_interp_conserve_new_2dx1d: horiz_interp_conserve_init is not called')
wordsz=size(transfer(lon_in(1,1), one_byte))
if(wordsz .NE. 8) call mpp_error(FATAL, &
'horiz_interp_conserve_new_2dx1d: currently only support 64-bit real, contact developer')
if( (size(lon_in,1) .NE. size(lat_in,1)) .OR. (size(lon_in,2) .NE. size(lat_in,2)) ) &
call mpp_error(FATAL, 'horiz_interp_conserve_mod: size mismatch between lon_in and lat_in')
nlon_in = size(lon_in,1) - 1; nlat_in = size(lon_in,2) - 1
nlon_out = size(lon_out(:)) - 1; nlat_out = size(lat_out(:)) - 1
mask_src = 1.
if(present(mask_in)) then
if( (size(mask_in,1) .NE. nlon_in) .OR. (size(mask_in,2) .NE. nlat_in)) call mpp_error(FATAL, &
'horiz_interp_conserve_mod: size mismatch between mask_in and lon_in/lat_in')
mask_src = mask_in
end if
maxxgrid = get_maxxgrid()
allocate(i_src(maxxgrid), j_src(maxxgrid), i_dst(maxxgrid), j_dst(maxxgrid) )
allocate( xgrid_area(maxxgrid), dst_area(nlon_out, nlat_out) )
if( .not. great_circle_algorithm ) then
nxgrid = create_xgrid_2DX1D_order1(nlon_in, nlat_in, nlon_out, nlat_out, lon_in, lat_in, lon_out, lat_out, &
mask_src, i_src, j_src, i_dst, j_dst, xgrid_area)
else
allocate(lon_dst(nlon_out+1, nlat_out+1), lat_dst(nlon_out+1, nlat_out+1) )
allocate(clon(maxxgrid), clat(maxxgrid))
do j = 1, nlat_out+1
do i = 1, nlon_out+1
lon_dst(i,j) = lon_out(i)
lat_dst(i,j) = lat_out(j)
enddo
enddo
nxgrid = create_xgrid_great_circle(nlon_in, nlat_in, nlon_out, nlat_out, lon_in, lat_in, lon_dst, lat_dst, &
mask_src, i_src, j_src, i_dst, j_dst, xgrid_area, clon, clat)
deallocate(lon_dst, lat_dst, clon, clat)
endif
allocate(Interp%i_src(nxgrid), Interp%j_src(nxgrid) )
allocate(Interp%i_dst(nxgrid), Interp%j_dst(nxgrid) )
allocate(Interp%area_frac_dst(nxgrid) )
Interp%version = 2
Interp%nxgrid = nxgrid
Interp%i_src = i_src(1:nxgrid)+1 ! in C, the starting index is 0
Interp%j_src = j_src(1:nxgrid)+1
Interp%i_dst = i_dst(1:nxgrid)+1
Interp%j_dst = j_dst(1:nxgrid)+1
! sum over exchange grid area to get destination grid area
dst_area = 0.
do i = 1, nxgrid
dst_area(Interp%i_dst(i), Interp%j_dst(i)) = dst_area(Interp%i_dst(i), Interp%j_dst(i)) + xgrid_area(i)
end do
do i = 1, nxgrid
Interp%area_frac_dst(i) = xgrid_area(i)/dst_area(Interp%i_dst(i), Interp%j_dst(i) )
end do
Interp%nlon_src = nlon_in; Interp%nlat_src = nlat_in
Interp%nlon_dst = nlon_out; Interp%nlat_dst = nlat_out
if(present(mask_out)) then
if( (size(mask_out,1) .NE. nlon_out) .OR. (size(mask_out,2) .NE. nlat_out) ) call mpp_error(FATAL, &
'horiz_interp_conserve_mod: size mismatch between mask_out and lon_out/lat_out')
mask_out = 0.0
do i = 1, nxgrid
mask_out(Interp%i_dst(i),Interp%j_dst(i)) = mask_out(Interp%i_dst(i),Interp%j_dst(i)) + Interp%area_frac_dst(i)
end do
end if
deallocate(i_src, j_src, i_dst, j_dst, xgrid_area, dst_area)
end subroutine horiz_interp_conserve_new_2dx1d
!#######################################################################
!
subroutine horiz_interp_conserve_new_2dx2d ( Interp, lon_in, lat_in, lon_out, lat_out, &
mask_in, mask_out, verbose)
type(horiz_interp_type), intent(inout) :: Interp
real, intent(in), dimension(:,:) :: lon_in , lat_in
real, intent(in), dimension(:,:) :: lon_out, lat_out
real, intent(in), optional, dimension(:,:) :: mask_in
real, intent(inout), optional, dimension(:,:) :: mask_out
integer, intent(in), optional :: verbose
!
integer :: create_xgrid_2DX2D_order1, get_maxxgrid, maxxgrid
integer :: create_xgrid_great_circle
integer :: nlon_in, nlat_in, nlon_out, nlat_out, nxgrid, i
real(8), dimension(size(lon_in,1)-1, size(lon_in,2)-1) :: mask_src
integer, allocatable, dimension(:) :: i_src, j_src, i_dst, j_dst
real(8), allocatable, dimension(:) :: xgrid_area, clon, clat
real(8), allocatable, dimension(:,:) :: dst_area
real(8), allocatable, dimension(:,:) :: lon_in_r8, lat_in_r8
real(8), allocatable, dimension(:,:) :: lon_out_r8, lat_out_r8
integer :: wordsz
integer(kind=1) :: one_byte(8)
if(.not. module_is_initialized) call mpp_error(FATAL, &
'horiz_interp_conserve_new_2dx2d: horiz_interp_conserve_init is not called')
wordsz=size(transfer(lon_in(1,1), one_byte))
if(wordsz .NE. 4 .AND. wordsz .NE. 8) call mpp_error(FATAL, &
'horiz_interp_conserve_new_2dx2d: wordsz should be 4 or 8')
if( (size(lon_in,1) .NE. size(lat_in,1)) .OR. (size(lon_in,2) .NE. size(lat_in,2)) ) &
call mpp_error(FATAL, 'horiz_interp_conserve_mod: size mismatch between lon_in and lat_in')
if( (size(lon_out,1) .NE. size(lat_out,1)) .OR. (size(lon_out,2) .NE. size(lat_out,2)) ) &
call mpp_error(FATAL, 'horiz_interp_conserve_mod: size mismatch between lon_out and lat_out')
nlon_in = size(lon_in,1) - 1; nlat_in = size(lon_in,2) - 1
nlon_out = size(lon_out,1) - 1; nlat_out = size(lon_out,2) - 1
mask_src = 1.
if(present(mask_in)) then
if( (size(mask_in,1) .NE. nlon_in) .OR. (size(mask_in,2) .NE. nlat_in)) call mpp_error(FATAL, &
'horiz_interp_conserve_mod: size mismatch between mask_in and lon_in/lat_in')
mask_src = mask_in
end if
maxxgrid = get_maxxgrid()
allocate(i_src(maxxgrid), j_src(maxxgrid), i_dst(maxxgrid), j_dst(maxxgrid) )
allocate( xgrid_area(maxxgrid), dst_area(nlon_out, nlat_out) )
if(wordsz==4) then
allocate(lon_in_r8(size(lon_in,1),size(lon_in,2)))
allocate(lat_in_r8(size(lat_in,1),size(lat_in,2)))
allocate(lon_out_r8(size(lon_out,1),size(lon_out,2)))
allocate(lat_out_r8(size(lat_out,1),size(lat_out,2)))
lon_in_r8 = lon_in
lat_in_r8 = lat_in
lon_out_r8 = lon_out
lat_out_r8 = lat_out
endif
if( .not. great_circle_algorithm ) then
if(wordsz==4) then
nxgrid = create_xgrid_2DX2D_order1(nlon_in, nlat_in, nlon_out, nlat_out, lon_in_r8, lat_in_r8, lon_out_r8, lat_out_r8, &
mask_src, i_src, j_src, i_dst, j_dst, xgrid_area)
else
nxgrid = create_xgrid_2DX2D_order1(nlon_in, nlat_in, nlon_out, nlat_out, lon_in, lat_in, lon_out, lat_out, &
mask_src, i_src, j_src, i_dst, j_dst, xgrid_area)
endif
else
allocate(clon(maxxgrid), clat(maxxgrid))
if(wordsz==4) then
nxgrid = create_xgrid_great_circle(nlon_in, nlat_in, nlon_out, nlat_out, lon_in_r8, lat_in_r8, lon_out_r8, lat_out_r8, &
mask_src, i_src, j_src, i_dst, j_dst, xgrid_area, clon, clat)
else
nxgrid = create_xgrid_great_circle(nlon_in, nlat_in, nlon_out, nlat_out, lon_in, lat_in, lon_out, lat_out, &
mask_src, i_src, j_src, i_dst, j_dst, xgrid_area, clon, clat)
endif
deallocate(clon, clat)
endif
if(wordsz==4) deallocate(lon_in_r8, lat_in_r8, lon_out_r8, lat_out_r8)
allocate(Interp%i_src(nxgrid), Interp%j_src(nxgrid) )
allocate(Interp%i_dst(nxgrid), Interp%j_dst(nxgrid) )
allocate(Interp%area_frac_dst(nxgrid) )
Interp%version = 2
Interp%nxgrid = nxgrid
Interp%i_src = i_src(1:nxgrid)+1 ! in C, the starting index is 0
Interp%j_src = j_src(1:nxgrid)+1
Interp%i_dst = i_dst(1:nxgrid)+1
Interp%j_dst = j_dst(1:nxgrid)+1
! sum over exchange grid area to get destination grid area
dst_area = 0.
do i = 1, nxgrid
dst_area(Interp%i_dst(i), Interp%j_dst(i)) = dst_area(Interp%i_dst(i), Interp%j_dst(i)) + xgrid_area(i)
end do
do i = 1, nxgrid
Interp%area_frac_dst(i) = xgrid_area(i)/dst_area(Interp%i_dst(i), Interp%j_dst(i) )
end do
Interp%nlon_src = nlon_in; Interp%nlat_src = nlat_in
Interp%nlon_dst = nlon_out; Interp%nlat_dst = nlat_out
if(present(mask_out)) then
if( (size(mask_out,1) .NE. nlon_out) .OR. (size(mask_out,2) .NE. nlat_out) ) call mpp_error(FATAL, &
'horiz_interp_conserve_mod: size mismatch between mask_out and lon_out/lat_out')
mask_out = 0.0
do i = 1, nxgrid
mask_out(Interp%i_dst(i),Interp%j_dst(i)) = mask_out(Interp%i_dst(i),Interp%j_dst(i)) + Interp%area_frac_dst(i)
end do
end if
deallocate(i_src, j_src, i_dst, j_dst, xgrid_area, dst_area )
end subroutine horiz_interp_conserve_new_2dx2d
!########################################################################
!
!
! Subroutine for performing the horizontal interpolation between two grids.
!
!
! Subroutine for performing the horizontal interpolation between two grids.
! horiz_interp_conserve_new must be called before calling this routine.
!
!
! call horiz_interp_conserve ( Interp, data_in, data_out, verbose, mask_in, mask_out)
!
!
!
! Derived-type variable containing interpolation indices and weights.
! Returned by a previous call to horiz_interp_new.
!
!
! Input data on source grid.
!
!
! flag for the amount of print output.
! verbose = 0, no output; = 1, min,max,means; = 2, still more
!
!
! Input mask, must be the same size as the input data. The real value of
! mask_in must be in the range (0.,1.). Set mask_in=0.0 for data points
! that should not be used or have missing data. mask_in will be applied only
! when horiz_interp_conserve_new_1d is called. mask_in will be passed into
! horiz_interp_conserve_new_2d.
!
!
! Output data on destination grid.
!
!
! Output mask that specifies whether data was computed. mask_out will be computed only
! when horiz_interp_conserve_new_1d is called. mask_out will be computed in
! horiz_interp_conserve_new_2d.
!
subroutine horiz_interp_conserve ( Interp, data_in, data_out, verbose, &
mask_in, mask_out)
!-----------------------------------------------------------------------
type (horiz_interp_type), intent(in) :: Interp
real, intent(in), dimension(:,:) :: data_in
real, intent(out), dimension(:,:) :: data_out
integer, intent(in), optional :: verbose
real, intent(in), dimension(:,:), optional :: mask_in
real, intent(out), dimension(:,:), optional :: mask_out
! --- error checking ---
if (size(data_in,1) /= Interp%nlon_src .or. size(data_in,2) /= Interp%nlat_src) &
call mpp_error(FATAL, 'horiz_interp_conserve_mod: size of input array incorrect')
if (size(data_out,1) /= Interp%nlon_dst .or. size(data_out,2) /= Interp%nlat_dst) &
call mpp_error(FATAL, 'horiz_interp_conserve_mod: size of output array incorrect')
select case ( Interp%version)
case (1)
call horiz_interp_conserve_version1(Interp, data_in, data_out, verbose, mask_in, mask_out)
case (2)
if(present(mask_in) .OR. present(mask_out) ) call mpp_error(FATAL, &
'horiz_interp_conserve: for version 2, mask_in and mask_out must be passed in horiz_interp_new, not in horiz_interp')
call horiz_interp_conserve_version2(Interp, data_in, data_out, verbose)
end select
end subroutine horiz_interp_conserve
!
!##############################################################################
subroutine horiz_interp_conserve_version1 ( Interp, data_in, data_out, verbose, &
mask_in, mask_out)
!-----------------------------------------------------------------------
type (horiz_interp_type), intent(in) :: Interp
real, intent(in), dimension(:,:) :: data_in
real, intent(out), dimension(:,:) :: data_out
integer, intent(in), optional :: verbose
real, intent(in), dimension(:,:), optional :: mask_in
real, intent(out), dimension(:,:), optional :: mask_out
!----------local variables----------------------------------------------------
integer :: m, n, nlon_in, nlat_in, nlon_out, nlat_out, &
miss_in, miss_out, is, ie, js, je, &
np, npass, iverbose
real :: dsum, wsum, avg_in, min_in, max_in, &
avg_out, min_out, max_out, eps, asum, &
dwtsum, wtsum, arsum, fis, fie, fjs, fje
!-----------------------------------------------------------------------
iverbose = 0; if (present(verbose)) iverbose = verbose
eps = epsilon(wtsum)
nlon_in = Interp%nlon_src; nlat_in = Interp%nlat_src
nlon_out = Interp%nlon_dst; nlat_out = Interp%nlat_dst
if (present(mask_in)) then
if ( count(mask_in < -.0001 .or. mask_in > 1.0001) > 0 ) &
call mpp_error(FATAL, 'horiz_interp_conserve_mod: input mask not between 0,1')
endif
!-----------------------------------------------------------------------
!---- loop through output grid boxes ----
data_out = 0.0
do n = 1, nlat_out
! latitude window
! setup ascending latitude indices and weights
if (Interp%jlat(n,1) <= Interp%jlat(n,2)) then
js = Interp%jlat(n,1); je = Interp%jlat(n,2)
fjs = Interp%facj(n,1); fje = Interp%facj(n,2)
else
js = Interp%jlat(n,2); je = Interp%jlat(n,1)
fjs = Interp%facj(n,2); fje = Interp%facj(n,1)
endif
do m = 1, nlon_out
! longitude window
is = Interp%ilon(m,1); ie = Interp%ilon(m,2)
fis = Interp%faci(m,1); fie = Interp%faci(m,2)
npass = 1
dwtsum = 0.
wtsum = 0.
arsum = 0.
! wrap-around on input grid
! sum using 2 passes (pass 1: end of input grid)
if ( ie < is ) then
ie = nlon_in
fie = 1.0
npass = 2
endif
do np = 1, npass
! pass 2: beginning of input grid
if ( np == 2 ) then
is = 1
fis = 1.0
ie = Interp%ilon(m,2)
fie = Interp%faci(m,2)
endif
! summing data*weight and weight for single grid point
if (present(mask_in)) then
call data_sum ( data_in(is:ie,js:je), Interp%area_src(is:ie,js:je), &
fis, fie, fjs,fje, dwtsum, wtsum, arsum, mask_in(is:ie,js:je) )
else if( ASSOCIATED(Interp%mask_in) ) then
call data_sum ( data_in(is:ie,js:je), Interp%area_src(is:ie,js:je), &
fis, fie, fjs,fje, dwtsum, wtsum, arsum, Interp%mask_in(is:ie,js:je) )
else
call data_sum ( data_in(is:ie,js:je), Interp%area_src(is:ie,js:je), &
fis, fie, fjs,fje, dwtsum, wtsum, arsum )
endif
enddo
if (wtsum > eps) then
data_out(m,n) = dwtsum/wtsum
if (present(mask_out)) mask_out(m,n) = wtsum/arsum
else
data_out(m,n) = 0.
if (present(mask_out)) mask_out(m,n) = 0.0
endif
enddo
enddo
!***********************************************************************
! compute statistics: minimum, maximum, and mean
!-----------------------------------------------------------------------
if (iverbose > 0) then
! compute statistics of input data
call stats(data_in, Interp%area_src, asum, dsum, wsum, min_in, max_in, miss_in, mask_in)
! diagnostic messages
! on the root_pe, we can calculate the global mean, minimum and maximum.
if(pe == root_pe) then
if (wsum > 0.0) then
avg_in=dsum/wsum
else
print *, 'horiz_interp stats: input area equals zero '
avg_in=0.0
endif
if (iverbose > 1) print '(2f16.11)', 'global sum area_in = ', asum, wsum
endif
! compute statistics of output data
call stats(data_out, Interp%area_dst, asum, dsum, wsum, min_out, max_out, miss_out, mask_out)
! diagnostic messages
if(pe == root_pe) then
if (wsum > 0.0) then
avg_out=dsum/wsum
else
print *, 'horiz_interp stats: output area equals zero '
avg_out=0.0
endif
if (iverbose > 1) print '(2f16.11)', 'global sum area_out = ', asum, wsum
endif
!---- output statistics ----
! the global mean, min and max are calculated on the root pe.
if(pe == root_pe) then
write (*,900)
write (*,901) min_in ,max_in ,avg_in
if (present(mask_in)) write (*,903) miss_in
write (*,902) min_out,max_out,avg_out
if (present(mask_out)) write (*,903) miss_out
endif
900 format (/,1x,10('-'),' output from horiz_interp ',10('-'))
901 format (' input: min=',f16.9,' max=',f16.9,' avg=',f22.15)
902 format (' output: min=',f16.9,' max=',f16.9,' avg=',f22.15)
903 format (' number of missing points = ',i6)
endif
!-----------------------------------------------------------------------
end subroutine horiz_interp_conserve_version1
!#############################################################################
subroutine horiz_interp_conserve_version2 ( Interp, data_in, data_out, verbose )
!-----------------------------------------------------------------------
type (horiz_interp_type), intent(in) :: Interp
real, intent(in), dimension(:,:) :: data_in
real, intent(out), dimension(:,:) :: data_out
integer, intent(in), optional :: verbose
integer :: i, i_src, j_src, i_dst, j_dst
data_out = 0.0
do i = 1, Interp%nxgrid
i_src = Interp%i_src(i); j_src = Interp%j_src(i)
i_dst = Interp%i_dst(i); j_dst = Interp%j_dst(i)
data_out(i_dst, j_dst) = data_out(i_dst, j_dst) + data_in(i_src,j_src)*Interp%area_frac_dst(i)
end do
end subroutine horiz_interp_conserve_version2
!#######################################################################
!
!
! Deallocates memory used by "horiz_interp_type" variables.
! Must be called before reinitializing with horiz_interp_new.
!
!
! Deallocates memory used by "horiz_interp_type" variables.
! Must be called before reinitializing with horiz_interp_new.
!
!
! call horiz_interp_conserve_del ( Interp )
!
!
! A derived-type variable returned by previous call
! to horiz_interp_new. The input variable must have
! allocated arrays. The returned variable will contain
! deallocated arrays.
!
subroutine horiz_interp_conserve_del ( Interp )
type (horiz_interp_type), intent(inout) :: Interp
select case(Interp%version)
case (1)
if(associated(Interp%area_src)) deallocate(Interp%area_src)
if(associated(Interp%area_dst)) deallocate(Interp%area_dst)
if(associated(Interp%facj)) deallocate(Interp%facj)
if(associated(Interp%jlat)) deallocate(Interp%jlat)
if(associated(Interp%faci)) deallocate(Interp%faci)
if(associated(Interp%ilon)) deallocate(Interp%ilon)
case (2)
if(associated(Interp%i_src)) deallocate(Interp%i_src)
if(associated(Interp%j_src)) deallocate(Interp%j_src)
if(associated(Interp%i_dst)) deallocate(Interp%i_dst)
if(associated(Interp%j_dst)) deallocate(Interp%j_dst)
if(associated(Interp%area_frac_dst)) deallocate(Interp%area_frac_dst)
end select
end subroutine horiz_interp_conserve_del
!
!#######################################################################
!---This statistics is for conservative scheme
subroutine stats ( dat, area, asum, dsum, wsum, low, high, miss, mask )
real, intent(in) :: dat(:,:), area(:,:)
real, intent(out) :: asum, dsum, wsum, low, high
integer, intent(out) :: miss
real, intent(in), optional :: mask(:,:)
integer :: pe, root_pe, npes, p, buffer_int(1)
real :: buffer_real(5)
pe = mpp_pe()
root_pe = mpp_root_pe()
npes = mpp_npes()
! sum data, data*area; and find min,max on each pe.
if (present(mask)) then
asum = sum(area(:,:))
dsum = sum(area(:,:)*dat(:,:)*mask(:,:))
wsum = sum(area(:,:)*mask(:,:))
miss = count(mask(:,:) <= 0.5)
low = minval(dat(:,:),mask=mask(:,:) > 0.5)
high = maxval(dat(:,:),mask=mask(:,:) > 0.5)
else
asum = sum(area(:,:))
dsum = sum(area(:,:)*dat(:,:))
wsum = sum(area(:,:))
miss = 0
low = minval(dat(:,:))
high = maxval(dat(:,:))
endif
! other pe send local min, max, avg to the root pe and
! root pe receive these information
if(pe == root_pe) then
do p = 1, npes - 1
! Force use of "scalar", integer pointer mpp interface
call mpp_recv(buffer_real(1),glen=5,from_pe=root_pe+p, tag=COMM_TAG_1)
asum = asum + buffer_real(1)
dsum = dsum + buffer_real(2)
wsum = wsum + buffer_real(3)
low = min(low, buffer_real(4))
high = max(high, buffer_real(5))
call mpp_recv(buffer_int(1),glen=1,from_pe=root_pe+p, tag=COMM_TAG_2)
miss = miss + buffer_int(1)
enddo
else
buffer_real(1) = asum
buffer_real(2) = dsum
buffer_real(3) = wsum
buffer_real(4) = low
buffer_real(5) = high
! Force use of "scalar", integer pointer mpp interface
call mpp_send(buffer_real(1),plen=5,to_pe=root_pe, tag=COMM_TAG_1)
buffer_int(1) = miss
call mpp_send(buffer_int(1),plen=1,to_pe=root_pe, tag=COMM_TAG_2)
endif
call mpp_sync_self()
end subroutine stats
!#######################################################################
subroutine data_sum( data, area, facis, facie, facjs, facje, &
dwtsum, wtsum, arsum, mask )
! sums up the data and weights for a single output grid box
!-----------------------------------------------------------------------
real, intent(in), dimension(:,:) :: data, area
real, intent(in) :: facis, facie, facjs, facje
real, intent(inout) :: dwtsum, wtsum, arsum
real, intent(in), optional :: mask(:,:)
! fac__ = fractional portion of each boundary grid box included
! in the integral
! dwtsum = sum(data*area*mask)
! wtsum = sum(area*mask)
! arsum = sum(area)
!-----------------------------------------------------------------------
real, dimension(size(area,1),size(area,2)) :: wt
real :: asum
integer :: id, jd
!-----------------------------------------------------------------------
id=size(area,1); jd=size(area,2)
wt=area
wt( 1,:)=wt( 1,:)*facis
wt(id,:)=wt(id,:)*facie
wt(:, 1)=wt(:, 1)*facjs
wt(:,jd)=wt(:,jd)*facje
asum = sum(wt)
arsum = arsum + asum
if (present(mask)) then
wt = wt * mask
dwtsum = dwtsum + sum(wt*data)
wtsum = wtsum + sum(wt)
else
dwtsum = dwtsum + sum(wt*data)
wtsum = wtsum + asum
endif
!-----------------------------------------------------------------------
end subroutine data_sum
!#######################################################################
end module horiz_interp_conserve_mod