# 1 "../horiz_interp/horiz_interp_spherical.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_spherical_mod ! Matthew Harrison ! Zhi Liang ! ! ! Performs spatial interpolation between grids using inverse-distance-weighted scheme. ! ! ! This module can interpolate data from rectangular/tripolar grid ! to rectangular/tripolar grid. The interpolation scheme is inverse-distance-weighted ! scheme. There is an optional mask field for missing input data. ! An optional output mask field may be used in conjunction with ! the input mask to show where output data exists. ! use mpp_mod, only : mpp_error, FATAL, WARNING, stdout use mpp_mod, only : mpp_root_pe, mpp_pe use mpp_mod, only : input_nml_file use fms_mod, only : write_version_number, file_exist, close_file use fms_mod, only : check_nml_error, open_namelist_file use constants_mod, only : pi use horiz_interp_type_mod, only : horiz_interp_type, stats implicit none private public :: horiz_interp_spherical_new, horiz_interp_spherical, horiz_interp_spherical_del public :: horiz_interp_spherical_init, horiz_interp_spherical_wght integer, parameter :: max_neighbors = 400 real, parameter :: max_dist_default = 0.1 ! radians integer, parameter :: num_nbrs_default = 4 real, parameter :: large=1.e20 real, parameter :: epsln=1.e-10 integer :: pe, root_pe !--- namelist interface ! ! ! indicate the searching method to find the nearest neighbor points. Its value ! can be "radial_search" and "full_search", with default value "radial_search". ! when search_method is "radial_search", the search may be not quite accurate for some cases. ! Normally the search will be ok if you chose suitable max_dist. ! When search_method is "full_search", it will be always accurate, but will be slower ! comparing to "radial_search". Normally these two search algorithm will produce same ! results other than order of operation. "radial_search" are recommended to use. ! The purpose to add "full_search" is in case you think you interpolation results is ! not right, you have other option to verify. ! ! character(len=32) :: search_method = "radial_search" ! or "full_search" namelist /horiz_interp_spherical_nml/ search_method !----------------------------------------------------------------------- ! 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' # 83 "../horiz_interp/horiz_interp_spherical.F90" 2 logical :: module_is_initialized = .FALSE. contains !####################################################################### ! ! ! writes version number to logfile.out ! ! ! writes version number to logfile.out ! subroutine horiz_interp_spherical_init integer :: unit, ierr, io if(module_is_initialized) return call write_version_number("horiz_interp_spherical_mod", version) read (input_nml_file, horiz_interp_spherical_nml, iostat=io) ierr = check_nml_error(io,'horiz_interp_spherical_nml') # 114 module_is_initialized = .true. end subroutine horiz_interp_spherical_init ! !####################################################################### ! ! ! Initialization routine. ! ! ! Allocates space and initializes a derived-type variable ! that contains pre-computed interpolation indices and weights. ! ! ! ! ! Longitude (in radians) for source data grid. ! ! ! Latitude (in radians) for source data grid. ! ! ! Longitude (in radians) for source data grid. ! ! ! Latitude (in radians) for source data grid. ! ! ! Number of nearest neighbors for regridding. When number of neighbors within ! the radius max_dist ( namelist variable) is less than num_nbrs, All the neighbors ! will be used to interpolate onto destination grid. when number of neighbors within ! the radius max_dist ( namelist variable) is greater than num_nbrs, at least "num_nbrs" ! neighbors will be used to remap onto destination grid. ! ! ! Maximum region of influence around destination grid points. ! ! ! logical variable to indicate if the boundary condition along zonal boundary ! is cyclic or not. When true, the zonal boundary condition is cyclic. ! ! ! 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. ! subroutine horiz_interp_spherical_new(Interp, lon_in,lat_in,lon_out,lat_out, & num_nbrs, max_dist, src_modulo) type(horiz_interp_type), intent(inout) :: Interp real, intent(in), dimension(:,:) :: lon_in, lat_in, lon_out, lat_out integer, intent(in), optional :: num_nbrs real, optional, intent(in) :: max_dist logical, intent(in), optional :: src_modulo !------local variables --------------------------------------- integer :: i, j, n integer :: map_dst_xsize, map_dst_ysize, map_src_xsize, map_src_ysize integer :: map_src_size, num_neighbors real :: max_src_dist, tpi, hpi logical :: src_is_modulo real :: min_theta_dst, max_theta_dst, min_phi_dst, max_phi_dst real :: min_theta_src, max_theta_src, min_phi_src, max_phi_src integer :: map_src_add(size(lon_out,1),size(lon_out,2),max_neighbors) real :: map_src_dist(size(lon_out,1),size(lon_out,2),max_neighbors) integer :: num_found(size(lon_out,1),size(lon_out,2)) integer :: ilon(max_neighbors), jlat(max_neighbors) real, dimension(size(lon_out,1),size(lon_out,2)) :: theta_dst, phi_dst real, dimension(size(lon_in,1)*size(lon_in,2)) :: theta_src, phi_src !-------------------------------------------------------------- pe = mpp_pe() root_pe = mpp_root_pe() tpi = 2.0*PI; hpi = 0.5*PI num_neighbors = num_nbrs_default if(present(num_nbrs)) num_neighbors = num_nbrs if (num_neighbors <= 0) call mpp_error(FATAL,'horiz_interp_spherical_mod: num_neighbors must be > 0') max_src_dist = max_dist_default if (PRESENT(max_dist)) max_src_dist = max_dist Interp%max_src_dist = max_src_dist src_is_modulo = .true. if (PRESENT(src_modulo)) src_is_modulo = src_modulo !--- check the grid size comformable map_dst_xsize=size(lon_out,1);map_dst_ysize=size(lon_out,2) map_src_xsize=size(lon_in,1); map_src_ysize=size(lon_in,2) map_src_size = map_src_xsize*map_src_ysize if (map_dst_xsize /= size(lat_out,1) .or. map_dst_ysize /= size(lat_out,2)) & call mpp_error(FATAL,'horiz_interp_spherical_mod: destination grids not conformable') if (map_src_xsize /= size(lat_in,1) .or. map_src_ysize /= size(lat_in,2)) & call mpp_error(FATAL,'horiz_interp_spherical_mod: source grids not conformable') theta_src = reshape(lon_in,(/map_src_size/)) phi_src = reshape(lat_in,(/map_src_size/)) theta_dst(:,:) = lon_out(:,:) phi_dst(:,:) = lat_out(:,:) min_theta_dst=tpi;max_theta_dst=0.0;min_phi_dst=pi;max_phi_dst=-pi min_theta_src=tpi;max_theta_src=0.0;min_phi_src=pi;max_phi_src=-pi where(theta_dst<0.0) theta_dst = theta_dst+tpi where(theta_dst>tpi) theta_dst = theta_dst-tpi where(theta_src<0.0) theta_src = theta_src+tpi where(theta_src>tpi) theta_src = theta_src-tpi where(phi_dst < -hpi) phi_dst = -hpi where(phi_dst > hpi) phi_dst = hpi where(phi_src < -hpi) phi_src = -hpi where(phi_src > hpi) phi_src = hpi do j=1,map_dst_ysize do i=1,map_dst_xsize min_theta_dst = min(min_theta_dst,theta_dst(i,j)) max_theta_dst = max(max_theta_dst,theta_dst(i,j)) min_phi_dst = min(min_phi_dst,phi_dst(i,j)) max_phi_dst = max(max_phi_dst,phi_dst(i,j)) enddo enddo do i=1,map_src_size min_theta_src = min(min_theta_src,theta_src(i)) max_theta_src = max(max_theta_src,theta_src(i)) min_phi_src = min(min_phi_src,phi_src(i)) max_phi_src = max(max_phi_src,phi_src(i)) enddo if (min_phi_dst < min_phi_src) print *, '=> WARNING: latitute of dest grid exceeds src' if (max_phi_dst > max_phi_src) print *, '=> WARNING: latitute of dest grid exceeds src' ! when src is cyclic, no need to print out the following warning. if(.not. src_is_modulo) then if (min_theta_dst < min_theta_src) print *, '=> WARNING : longitude of dest grid exceeds src' if (max_theta_dst > max_theta_src) print *, '=> WARNING : longitude of dest grid exceeds src' endif ! allocate memory to data type if(ASSOCIATED(Interp%i_lon)) then if(size(Interp%i_lon,1) .NE. map_dst_xsize .OR. & size(Interp%i_lon,2) .NE. map_dst_ysize ) call mpp_error(FATAL, & 'horiz_interp_spherical_mod: size(Interp%i_lon(:),1) .NE. map_dst_xsize .OR. '// & 'size(Interp%i_lon(:),2) .NE. map_dst_ysize') else allocate(Interp%i_lon(map_dst_xsize,map_dst_ysize,max_neighbors), & Interp%j_lat(map_dst_xsize,map_dst_ysize,max_neighbors), & Interp%src_dist(map_dst_xsize,map_dst_ysize,max_neighbors), & Interp%num_found(map_dst_xsize,map_dst_ysize) ) endif map_src_add = 0 map_src_dist = large num_found = 0 !using radial_search to find the nearest points and corresponding distance. select case(trim(search_method)) case ("radial_search") ! will be efficient, but may be not so accurate for some cases call radial_search(theta_src, phi_src, theta_dst, phi_dst, map_src_xsize, map_src_ysize, & map_src_add, map_src_dist, num_found, num_neighbors,max_src_dist,src_is_modulo) case ("full_search") ! always accurate, but less efficient. call full_search(theta_src, phi_src, theta_dst, phi_dst, map_src_add, map_src_dist, & num_found, num_neighbors,max_src_dist ) case default call mpp_error(FATAL,"horiz_interp_spherical_new: nml search_method = "// & trim(search_method)//" is not a valid namelist option") end select do j=1,map_dst_ysize do i=1,map_dst_xsize do n=1,num_found(i,j) if(map_src_add(i,j,n) == 0) then jlat(n) = 0; ilon(n) = 0 else jlat(n) = map_src_add(i,j,n)/map_src_xsize + 1 ilon(n) = map_src_add(i,j,n) - (jlat(n)-1)*map_src_xsize if(ilon(n) == 0) then jlat(n) = jlat(n) - 1 ilon(n) = map_src_xsize endif endif enddo Interp%i_lon(i,j,:) = ilon(:) Interp%j_lat(i,j,:) = jlat(:) Interp%num_found(i,j) = num_found(i,j) Interp%src_dist(i,j,:) = map_src_dist(i,j,:) enddo enddo Interp%nlon_src = map_src_xsize; Interp%nlat_src = map_src_ysize Interp%nlon_dst = map_dst_xsize; Interp%nlat_dst = map_dst_ysize return end subroutine horiz_interp_spherical_new ! !####################################################################### ! ! ! Subroutine for performing the horizontal interpolation between two grids. ! ! ! Subroutine for performing the horizontal interpolation between two grids. ! horiz_interp_spherical_new must be called before calling this routine. ! ! ! ! ! Derived-type variable containing interpolation indices and weights. ! Returned by a previous call to horiz_interp_spherical_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. ! ! ! Use the missing_value to indicate missing data. ! ! ! Output data on destination grid. ! ! ! Output mask that specifies whether data was computed. ! subroutine horiz_interp_spherical( Interp, data_in, data_out, verbose, mask_in, mask_out, missing_value) 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 real, intent(in), optional :: missing_value !--- some local variables ---------------------------------------- real, dimension(Interp%nlon_dst, Interp%nlat_dst,size(Interp%src_dist,3)) :: wt real, dimension(Interp%nlon_src, Interp%nlat_src) :: mask_src real, dimension(Interp%nlon_dst, Interp%nlat_dst) :: mask_dst integer :: nlon_in, nlat_in, nlon_out, nlat_out, num_found integer :: m, n, i, j, k, miss_in, miss_out, i1, i2, j1, j2, iverbose real :: min_in, max_in, avg_in, min_out, max_out, avg_out, sum !----------------------------------------------------------------- iverbose = 0; if (present(verbose)) iverbose = verbose nlon_in = Interp%nlon_src; nlat_in = Interp%nlat_src nlon_out = Interp%nlon_dst; nlat_out = Interp%nlat_dst if(size(data_in,1) .ne. nlon_in .or. size(data_in,2) .ne. nlat_in ) & call mpp_error(FATAL,'horiz_interp_spherical_mod: size of input array incorrect') if(size(data_out,1) .ne. nlon_out .or. size(data_out,2) .ne. nlat_out ) & call mpp_error(FATAL,'horiz_interp_spherical_mod: size of output array incorrect') mask_src = 1.0; mask_dst = 1.0 if(present(mask_in)) mask_src = mask_in do n=1,nlat_out do m=1,nlon_out ! neighbors are sorted nearest to farthest ! check nearest to see if it is a land point num_found = Interp%num_found(m,n) if(num_found == 0 ) then mask_dst(m,n) = 0.0 else i1 = Interp%i_lon(m,n,1); j1 = Interp%j_lat(m,n,1) if (mask_src(i1,j1) .lt. 0.5) then mask_dst(m,n) = 0.0 endif if(num_found .gt. 1 ) then i2 = Interp%i_lon(m,n,2); j2 = Interp%j_lat(m,n,2) ! compare first 2 nearest neighbors -- if they are nearly ! equidistant then use this mask for robustness if(abs(Interp%src_dist(m,n,2)-Interp%src_dist(m,n,1)) .lt. epsln) then if((mask_src(i1,j1) .lt. 0.5)) mask_dst(m,n) = 0.0 endif endif sum=0.0 do k=1, num_found if(mask_src(Interp%i_lon(m,n,k),Interp%j_lat(m,n,k)) .lt. 0.5 ) then wt(m,n,k) = 0.0 else if (Interp%src_dist(m,n,k) <= epsln) then wt(m,n,k) = large sum = sum + large else if(Interp%src_dist(m,n,k) <= Interp%max_src_dist ) then wt(m,n,k) = 1.0/Interp%src_dist(m,n,k) sum = sum+wt(m,n,k) else wt(m,n,k) = 0.0 endif endif enddo if (sum > epsln) then do k = 1, num_found wt(m,n,k) = wt(m,n,k)/sum enddo else mask_dst(m,n) = 0.0 endif endif enddo enddo data_out = 0.0 do n=1,nlat_out do m=1,nlon_out if(mask_dst(m,n) .gt. 0.5) then do k=1, Interp%num_found(m,n) i = Interp%i_lon(m,n,k) j = Interp%j_lat(m,n,k) data_out(m,n) = data_out(m,n)+data_in(i,j)*wt(m,n,k) enddo else if(present(missing_value)) then data_out(m,n) = missing_value else data_out(m,n) = 0.0 endif endif enddo enddo if(present(mask_out)) mask_out = mask_dst !*********************************************************************** ! compute statistics: minimum, maximum, and mean !----------------------------------------------------------------------- if (iverbose > 0) then ! compute statistics of input data call stats (data_in, min_in, max_in, avg_in, miss_in, missing_value, mask=mask_src) ! compute statistics of output data call stats (data_out, min_out, max_out, avg_out, miss_out, missing_value, mask=mask_dst) !---- output statistics ---- ! root_pe have the information of global mean, min and max 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 return end subroutine horiz_interp_spherical ! !####################################################################### subroutine horiz_interp_spherical_wght( Interp, wt, verbose, mask_in, mask_out, missing_value) type (horiz_interp_type), intent(in) :: Interp real, intent(out), dimension(:,:,:) :: wt integer, intent(in), optional :: verbose real, intent(in), dimension(:,:), optional :: mask_in real, intent(inout), dimension(:,:), optional :: mask_out real, intent(in), optional :: missing_value !--- some local variables ---------------------------------------- real, dimension(Interp%nlon_src, Interp%nlat_src) :: mask_src real, dimension(Interp%nlon_dst, Interp%nlat_dst) :: mask_dst integer :: nlon_in, nlat_in, nlon_out, nlat_out, num_found integer :: m, n, i, j, k, miss_in, miss_out, i1, i2, j1, j2, iverbose real :: min_in, max_in, avg_in, min_out, max_out, avg_out, sum !----------------------------------------------------------------- iverbose = 0; if (present(verbose)) iverbose = verbose nlon_in = Interp%nlon_src; nlat_in = Interp%nlat_src nlon_out = Interp%nlon_dst; nlat_out = Interp%nlat_dst mask_src = 1.0; mask_dst = 1.0 if(present(mask_in)) mask_src = mask_in do n=1,nlat_out do m=1,nlon_out ! neighbors are sorted nearest to farthest ! check nearest to see if it is a land point num_found = Interp%num_found(m,n) if (num_found > num_nbrs_default) then print*,'pe=',mpp_pe(),'num_found=',num_found num_found = num_nbrs_default end if if(num_found == 0 ) then mask_dst(m,n) = 0.0 else i1 = Interp%i_lon(m,n,1); j1 = Interp%j_lat(m,n,1) if (mask_src(i1,j1) .lt. 0.5) then mask_dst(m,n) = 0.0 endif if(num_found .gt. 1 ) then i2 = Interp%i_lon(m,n,2); j2 = Interp%j_lat(m,n,2) ! compare first 2 nearest neighbors -- if they are nearly ! equidistant then use this mask for robustness if(abs(Interp%src_dist(m,n,2)-Interp%src_dist(m,n,1)) .lt. epsln) then if((mask_src(i1,j1) .lt. 0.5)) mask_dst(m,n) = 0.0 endif endif sum=0.0 do k=1, num_found if(mask_src(Interp%i_lon(m,n,k),Interp%j_lat(m,n,k)) .lt. 0.5 ) then wt(m,n,k) = 0.0 else if (Interp%src_dist(m,n,k) <= epsln) then wt(m,n,k) = large sum = sum + large else if(Interp%src_dist(m,n,k) <= Interp%max_src_dist ) then wt(m,n,k) = 1.0/Interp%src_dist(m,n,k) sum = sum+wt(m,n,k) else wt(m,n,k) = 0.0 endif endif enddo if (sum > epsln) then do k = 1, num_found wt(m,n,k) = wt(m,n,k)/sum enddo else mask_dst(m,n) = 0.0 endif endif enddo enddo return end subroutine horiz_interp_spherical_wght ! !####################################################################### ! ! ! Deallocates memory used by "horiz_interp_type" variables. ! Must be called before reinitializing with horiz_interp_spherical_new. ! ! ! Deallocates memory used by "horiz_interp_type" variables. ! Must be called before reinitializing with horiz_interp_spherical_new. ! ! ! ! A derived-type variable returned by previous call ! to horiz_interp_spherical_new. The input variable must have ! allocated arrays. The returned variable will contain ! deallocated arrays. ! subroutine horiz_interp_spherical_del( Interp ) type (horiz_interp_type), intent(inout) :: Interp if(associated(Interp%src_dist)) deallocate(Interp%src_dist) if(associated(Interp%num_found)) deallocate(Interp%num_found) if(associated(Interp%i_lon)) deallocate(Interp%i_lon) if(associated(Interp%j_lat)) deallocate(Interp%j_lat) end subroutine horiz_interp_spherical_del ! !####################################################################### subroutine radial_search(theta_src,phi_src,theta_dst,phi_dst, map_src_xsize, map_src_ysize, & map_src_add, map_src_dist, num_found, num_neighbors,max_src_dist,src_is_modulo) real, intent(in), dimension(:) :: theta_src, phi_src real, intent(in), dimension(:,:) :: theta_dst, phi_dst integer, intent(in) :: map_src_xsize, map_src_ysize integer, intent(out), dimension(:,:,:) :: map_src_add real, intent(out), dimension(:,:,:) :: map_src_dist integer, intent(inout), dimension(:,:) :: num_found integer, intent(in) :: num_neighbors real, intent(in) :: max_src_dist logical, intent(in) :: src_is_modulo !---------- local variables ---------------------------------------- integer, parameter :: max_nbrs = 50 integer :: i, j, jj, i0, j0, n, l,i_left, i_right integer :: map_dst_xsize, map_dst_ysize integer :: i_left1, i_left2, i_right1, i_right2 integer :: map_src_size, step, step_size, bound, bound_start, bound_end logical :: continue_search, result, continue_radial_search real :: d, res !------------------------------------------------------------------ map_dst_xsize=size(theta_dst,1);map_dst_ysize=size(theta_dst,2) map_src_size = map_src_xsize*map_src_ysize do j=1,map_dst_ysize do i=1,map_dst_xsize continue_search=.true. step = 1 step_size = sqrt(real(map_src_size) ) do while (continue_search .and. step_size > 0) do while (step <= map_src_size .and. continue_search) ! count land points as nearest neighbors d = spherical_distance(theta_dst(i,j),phi_dst(i,j),theta_src(step),phi_src(step)) if (d <= max_src_dist) then result = update_dest_neighbors(map_src_add(i,j,:),map_src_dist(i,j,:), & step,d, num_found(i,j), num_neighbors ) if (result) then n = 0 i0 = mod(step,map_src_xsize) if (i0 == 0) i0 = map_src_xsize res = float(step)/float(map_src_xsize) j0 = ceiling(res) continue_radial_search = .true. do while (continue_radial_search) continue_radial_search = .false. n = n+1 ! radial counter if(n > max_nbrs) exit ! ************** left boundary ******************************* i_left = i0-n if (i_left <= 0) then if (src_is_modulo) then i_left = map_src_xsize + i_left else i_left = 1 endif endif do l = 0, 2*n jj = j0 - n - 1 + l if( jj < 0) then bound = ( 1 - jj )*map_src_xsize - i_left else if ( jj >= map_src_ysize ) then bound = ( 2*map_src_ysize - jj ) * map_src_xsize - i_left else bound = jj * map_src_xsize + i_left endif d = spherical_distance(theta_dst(i,j),phi_dst(i,j),theta_src(bound),phi_src(bound)) if(d<=max_src_dist) then result = update_dest_neighbors(map_src_add(i,j,:),map_src_dist(i,j,:), & bound,d, num_found(i,j), num_neighbors) if (result) continue_radial_search = .true. endif enddo ! ***************************right boundary ******************************* i_right = i0+n if (i_right > map_src_xsize) then if (src_is_modulo) then i_right = i_right - map_src_xsize else i_right = map_src_xsize endif endif do l = 0, 2*n jj = j0 - n - 1 + l if( jj < 0) then bound = ( 1 - jj )*map_src_xsize - i_right else if ( jj >= map_src_ysize ) then bound = ( 2*map_src_ysize - jj) * map_src_xsize - i_right else bound = jj * map_src_xsize + i_right endif d = spherical_distance(theta_dst(i,j),phi_dst(i,j),theta_src(bound),phi_src(bound)) if(d<=max_src_dist) then result = update_dest_neighbors(map_src_add(i,j,:),map_src_dist(i,j,:), & bound,d, num_found(i,j), num_neighbors) if (result) continue_radial_search = .true. endif enddo ! ************************* bottom boundary ********************************** i_left2 = 0 if( i_left > i_right) then i_left1 = 1 i_right1 = i_right i_left2 = i_left i_right2 = map_src_xsize else i_left1 = i_left i_right1 = i_right endif jj = j0 - n - 1 if( jj < 0 ) then bound_start = ( 1 - jj)*map_src_xsize - i_right1 bound_end = ( 1 - jj)*map_src_xsize - i_left1 else bound_start = jj * map_src_xsize + i_left1 bound_end = jj * map_src_xsize + i_right1 endif bound = bound_start do while (bound <= bound_end) d = spherical_distance(theta_dst(i,j),phi_dst(i,j),theta_src(bound),phi_src(bound)) if(d<=max_src_dist) then result = update_dest_neighbors(map_src_add(i,j,:),map_src_dist(i,j,:), & bound,d, num_found(i,j), num_neighbors) if (result) continue_radial_search = .true. endif bound = bound + 1 enddo if(i_left2 > 0 ) then if( jj < 0 ) then bound_start = ( 1 - jj)*map_src_xsize - i_right2 bound_end = ( 1 - jj)*map_src_xsize - i_left2 else bound_start = jj * map_src_xsize + i_left2 bound_end = jj * map_src_xsize + i_right2 endif bound = bound_start do while (bound <= bound_end) d = spherical_distance(theta_dst(i,j),phi_dst(i,j),theta_src(bound),phi_src(bound)) if(d<=max_src_dist) then result = update_dest_neighbors(map_src_add(i,j,:),map_src_dist(i,j,:), & bound,d, num_found(i,j), num_neighbors) if (result) continue_radial_search = .true. endif bound = bound + 1 enddo endif ! ************************** top boundary ************************************ jj = j0 + n - 1 if( jj >= map_src_ysize) then bound_start = ( 2*map_src_ysize - jj ) * map_src_xsize - i_right1 bound_end = ( 2*map_src_ysize - jj ) * map_src_xsize - i_left1 else bound_start = jj * map_src_xsize + i_left1 bound_end = jj * map_src_xsize + i_right1 endif bound = bound_start do while (bound <= bound_end) d = spherical_distance(theta_dst(i,j),phi_dst(i,j),theta_src(bound),phi_src(bound)) if(d<=max_src_dist) then result = update_dest_neighbors(map_src_add(i,j,:),map_src_dist(i,j,:), & bound,d, num_found(i,j), num_neighbors) if (result) continue_radial_search = .true. endif bound = bound + 1 enddo if(i_left2 > 0) then if( jj >= map_src_ysize) then bound_start = ( 2*map_src_ysize - jj ) * map_src_xsize - i_right2 bound_end = ( 2*map_src_ysize - jj ) * map_src_xsize - i_left2 else bound_start = jj * map_src_xsize + i_left2 bound_end = jj * map_src_xsize + i_right2 endif bound = bound_start do while (bound <= bound_end) d = spherical_distance(theta_dst(i,j),phi_dst(i,j),theta_src(bound),phi_src(bound)) if(d<=max_src_dist) then result = update_dest_neighbors(map_src_add(i,j,:),map_src_dist(i,j,:), & bound,d, num_found(i,j), num_neighbors) if (result) continue_radial_search = .true. endif bound = bound + 1 enddo endif enddo continue_search = .false. ! stop looking endif endif step=step+step_size enddo ! search loop step = 1 step_size = step_size/2 enddo enddo enddo return end subroutine radial_search !##################################################################### function update_dest_neighbors(map_src_add, map_src_dist, src_add,d, num_found, min_nbrs) integer, intent(inout), dimension(:) :: map_src_add real, intent(inout), dimension(:) :: map_src_dist integer, intent(in) :: src_add real, intent(in) :: d integer, intent(inout) :: num_found integer, intent(in) :: min_nbrs logical :: update_dest_neighbors, already_exist = .false. integer :: n,m update_dest_neighbors = .false. n = 0 NLOOP : do while ( n .le. num_found ) n = n + 1 DIST_CHK : if (d .le. map_src_dist(n)) then do m=n,num_found if (src_add == map_src_add(m)) then already_exist = .true. exit NLOOP endif enddo if(num_found < max_neighbors) then num_found = num_found + 1 else call mpp_error(FATAL,'update_dest_neighbors: '// & 'number of neighbor points found is greated than maxium neighbor points' ) endif do m=num_found,n+1,-1 map_src_add(m) = map_src_add(m-1) map_src_dist(m) = map_src_dist(m-1) enddo map_src_add(n) = src_add map_src_dist(n) = d update_dest_neighbors = .true. if( num_found > min_nbrs ) then if( map_src_dist(num_found) > map_src_dist(num_found-1) ) then num_found = num_found - 1 endif if( map_src_dist(min_nbrs+1) > map_src_dist(min_nbrs) ) then num_found = min_nbrs endif endif exit NLOOP ! n loop endif DIST_CHK end do NLOOP if(already_exist) return if( .not. update_dest_neighbors ) then if( num_found < min_nbrs ) then num_found = num_found + 1 update_dest_neighbors = .true. map_src_add(num_found) = src_add map_src_dist(num_found) = d endif endif return end function update_dest_neighbors !######################################################################## ! function spherical_distance(theta1,phi1,theta2,phi2) ! real, intent(in) :: theta1, phi1, theta2, phi2 ! real :: spherical_distance ! real :: r1(3), r2(3), cross(3), s, dot, ang ! this is a simple, enough way to calculate distance on the sphere ! first, construct cartesian vectors r1 and r2 ! then calculate the cross-product which is proportional to the area ! between the 2 vectors. The angular distance is arcsin of the ! distancealong the sphere ! ! theta is longitude and phi is latitude ! ! r1(1) = cos(theta1)*cos(phi1);r1(2)=sin(theta1)*cos(phi1);r1(3)=sin(phi1) ! r2(1) = cos(theta2)*cos(phi2);r2(2)=sin(theta2)*cos(phi2);r2(3)=sin(phi2) ! cross(1) = r1(2)*r2(3)-r1(3)*r2(2) ! cross(2) = r1(3)*r2(1)-r1(1)*r2(3) ! cross(3) = r1(1)*r2(2)-r1(2)*r2(1) ! s = sqrt(cross(1)**2.+cross(2)**2.+cross(3)**2.) ! s = min(s,1.0-epsln) ! dot = r1(1)*r2(1) + r1(2)*r2(2) + r1(3)*r2(3) ! if (dot > 0) then ! ang = asin(s) ! else if (dot < 0) then ! ang = pi + asin(s) !? original is pi - asin(s) ! else ! ang = pi/2. ! endif ! spherical_distance = abs(ang) ! in radians ! return ! end function spherical_distance ! The great cycle distance function spherical_distance(theta1,phi1,theta2,phi2) real, intent(in) :: theta1, phi1, theta2, phi2 real :: spherical_distance, dot if(theta1 == theta2 .and. phi1 == phi2) then spherical_distance = 0.0 return endif dot = cos(phi1)*cos(phi2)*cos(theta1-theta2) + sin(phi1)*sin(phi2) if(dot > 1. ) dot = 1. if(dot < -1.) dot = -1. spherical_distance = acos(dot) return end function spherical_distance !####################################################################### subroutine full_search(theta_src,phi_src,theta_dst,phi_dst,map_src_add, map_src_dist,num_found, & num_neighbors,max_src_dist) real, intent(in), dimension(:) :: theta_src, phi_src real, intent(in), dimension(:,:) :: theta_dst, phi_dst integer, intent(out), dimension(:,:,:) :: map_src_add real, intent(out), dimension(:,:,:) :: map_src_dist integer, intent(out), dimension(:,:) :: num_found integer, intent(in) :: num_neighbors real, intent(in) :: max_src_dist integer :: i,j,map_src_size, step integer :: map_dst_xsize,map_dst_ysize real :: d logical :: found map_dst_xsize=size(theta_dst,1);map_dst_ysize=size(theta_dst,2) map_src_size =size(theta_src(:)) do j=1,map_dst_ysize do i=1,map_dst_xsize do step = 1, map_src_size d = spherical_distance(theta_dst(i,j),phi_dst(i,j),theta_src(step),phi_src(step)) if( d <= max_src_dist) then found = update_dest_neighbors(map_src_add(i,j,:),map_src_dist(i,j,:), & step,d,num_found(i,j), num_neighbors ) endif enddo enddo enddo end subroutine full_search !####################################################################### end module horiz_interp_spherical_mod