!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! !! !! 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_2d !----------------------------------------------------------------------- ! 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 !----------------------------------------------------------------------- ! ! M.J. Harrison ! Zhi Liang ! Bonnie Samuels ! ! ! regrid 2-d lat-lon gridded data to logically rectangular grid ! described by grid descriptor file. If two fields are specified ! for regridding, it is assumed that they represent vector components. ! Rotation to the local grid direction on the target grid will be performed. ! ! ! use mpp_mod, only : mpp_error, mpp_pe, mpp_npes, mpp_root_pe, mpp_chksum use mpp_mod, only : FATAL, WARNING, stdout, stdlog 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_io_mod, only : mpp_get_att_name, mpp_get_att_char, mpp_get_att_type, mpp_get_att_real 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 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 !--- namelist interface ! ! ! Name of input file containing grid and data to be regridded. ! ! ! Name of input field(s). ! ! ! 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 fields (1 or 2). If numfields=2, then the fields are assumed ! to represent vector components. ! ! ! Name of target grid type. Valid choices are (T)racer and (C)orner ! ! ! The stopping criteria when extrapping data onto missing points. ! ! ! flag to indicate if the land/sea mask of destination grid will be applied ! on the output dest_file. when true, land/sea mask of destination grid will ! be applied on the output dest_file. When false, the output data will be ! global data. Default is true. ! ! ! True if fields are vector components. ! ! ! Vertical level from the source grid if one exists. ! ! ! 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) ! ! ! 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". ! ! character(len=128) :: src_file = 'src_file.nc' character(len=128) :: dest_grid = 'dest_grid.nc' character(len=128) :: dest_file = 'dest_file.nc' character(len=128) :: src_field_name(2) character(len=128) :: dest_field_name(2) = '' real :: stop_crit(2) = 0.005 character(len=1) :: dest_grid_type = 'T' integer :: numfields = 1 integer :: level = 1 integer :: num_nbrs = 5 real :: max_dist = 0.1 real :: scale_factor(2) = 1.0 logical :: vector_field=.false. logical :: apply_dest_mask = .true. character(len=20) :: interp_method = "bilinear" namelist /regrid_2d_nml/ src_file, src_field_name, dest_field_name, dest_file, dest_grid, & dest_grid_type, numfields, scale_factor, vector_field,level, num_nbrs, max_dist, & apply_dest_mask, stop_crit, interp_method !--------------------------------------------------------------------- integer :: ni_src, nj_src, ni_dst, nj_dst, ntime_src type(axistype) :: time_axis, axes_dst(2) type(fieldtype) :: field_lon_dst, field_lat_dst, src_field(2) character(len=16) :: y_boundary_type character(len=128) :: title 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(2) = -1e20 real :: D2R logical :: do_extrap type(atttype), dimension(:), allocatable :: global_atts real, dimension(:), allocatable :: time_in real, dimension(:), allocatable :: lon_src, lat_src real, dimension(:,:), allocatable :: lon_dst, lat_dst real, dimension(:,:), allocatable :: sin_rot, cos_rot real, dimension(:,:), allocatable :: mask_dst real, dimension(:,:,:,:), allocatable :: data_dst, data_src !--- version information variables character(len=128) :: version='CVS $Id: regrid_2d.f90,v 14.0 2007/03/15 22:47:20 fms 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_2d initialization routine call regrid_2d_init !--- read the dest_grid file call read_dst_grid !--- read src_file call read_src_file !--- remap data from src grid to dest grid call remap_data !--- write out metadata and data to output file dest_file if(mpp_pe() == mpp_root_pe() ) call write_dst_file call regrid_2d_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_2d_init integer :: io_status, unit, ierr, n D2R = PI/180.0 ! --- read namelist ------------------------------------------------ if(file_exist('input.nml')) then unit = open_namelist_file() read (unit,regrid_2d_nml,IOSTAT=io_status) write (stdout(),'(/)') write (stdout(),regrid_2d_nml) ierr = check_nml_error(io_status, 'regrid_2d_nml') call close_file(unit) else call mpp_error(FATAL, 'regrid_2d: file input.nml does not exist' ) endif if (numfields.ne.2.and.vector_field) & call mpp_error(FATAL,'regrid_2d: 2 components of vector field not specified') if (numfields == 2.and..not.vector_field) & call mpp_error(FATAL,'regrid_2d: only 1 scalar at a time') if (numfields .gt. 2) call mpp_error(FATAL,'regrid_2d: more than 2 fields specified') if (numfields .le. 0) call mpp_error(FATAL,'regrid_2d: No field specified') if (dest_grid_type .ne. 'T' .and. dest_grid_type .ne. 'C') & call mpp_error(FATAL,'regrid_2d: nml dest_grid_type = '//dest_grid_type//', should be either T or C') !--- 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_2d_init !##################################################################### !--- open grid file and store grid info subroutine read_dst_grid integer :: unit, ndim, nvar, natt, ntime, i integer :: len1, siz_in(3) logical :: found_xt, found_yt, found_wet logical :: found_xc, found_yc, found_angle character(len=32) :: name real, dimension(:,:), allocatable :: angle type(axistype), allocatable, dimension(:) :: axes type(fieldtype), allocatable, dimension(:) :: fields if(.not. file_exist(trim(dest_grid)) ) & call mpp_error(FATAL, 'regrid_2d: 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), global_atts(natt), axes(ndim) ) call mpp_get_axes(unit, axes) call mpp_get_atts(unit, global_atts) call mpp_get_fields(unit,fields) do i=1,natt if (trim(mpp_get_att_name(global_atts(i))) == 'y_boundary_type') & y_boundary_type = trim(mpp_get_att_char(global_atts(i))) if (trim(mpp_get_att_name(global_atts(i))) == 'filename') & title = trim(mpp_get_att_char(global_atts(i))) enddo write(stdout(),*) 'Retrieving input grid information ...' write(stdout(),*) 'Input grid name: ',trim(title) write(stdout(),*) 'y_boundary_type: ',trim(y_boundary_type) !-------------------------------------------------------------------- ! get output grid information !-------------------------------------------------------------------- ni_dst=0; nj_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 end select enddo if(ni_dst==0) call mpp_error(FATAL,'regrid_2d: file '//trim(dest_grid)//' does not contain axis grid_x_T') if(nj_dst==0) call mpp_error(FATAL,'regrid_2d: file '//trim(dest_grid)//' does not contain axis grid_y_T') allocate(lon_dst(ni_dst,nj_dst), lat_dst(ni_dst,nj_dst) ) allocate(mask_dst(ni_dst,nj_dst) ) found_xt = .FALSE.; found_yt = .FALSE.; found_wet = .FALSE. found_xc = .FALSE.; found_yc = .FALSE.; found_angle = .FALSE. do i=1,nvar call mpp_get_atts(fields(i),name=name,ndim=ndim) !--- get the land/sea mask if(trim(name) == 'wet') then found_wet = .true. call mpp_read(unit,fields(i),mask_dst) endif if(dest_grid_type == 'T') then 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) end select else if ( dest_grid_type == 'C' ) then select case (trim(name)) case ('x_C') found_xc = .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_C') found_yc = .true. call mpp_read(unit,fields(i),lat_dst) field_lat_dst = fields(i) case('angle_C') found_angle = .true. allocate(angle(ni_dst,nj_dst), sin_rot(ni_dst,nj_dst), cos_rot(ni_dst,nj_dst) ) call mpp_read(unit,fields(i),angle) sin_rot = sin(angle*D2R) cos_rot = cos(angle*D2R) deallocate(angle) end select endif enddo if(dest_grid_type == 'T') then if(.not.found_xt) call mpp_error(FATAL,'regrid_2d: field x_T is not in the file '//trim(dest_grid) ) if(.not.found_yt) call mpp_error(FATAL,'regrid_2d: field y_T is not in the file '//trim(dest_grid) ) else if(.not.found_xc) call mpp_error(FATAL,'regrid_2d: field x_C is not in the file '//trim(dest_grid) ) if(.not.found_yc) call mpp_error(FATAL,'regrid_2d: field y_C is not in the file '//trim(dest_grid) ) if(.not.found_angle) call mpp_error(FATAL,'regrid_2d: field angle_C is not in the file '//trim(dest_grid) ) endif if(.not.found_wet) call mpp_error(FATAL,'regrid_2d: field wet is not in the file '//trim(dest_grid) ) if(.not. apply_dest_mask) mask_dst = 1.0 ! will get global data call mpp_close(unit) deallocate(fields, axes) end subroutine read_dst_grid !##################################################################### !--- read source grid and source data from src_file subroutine read_src_file integer :: unit, ndim, nvar, natt, n integer :: nt, i, j, k, jj, len1, nk_src logical :: flip_y, found_src_field(2) character(len=1) :: cart character(len=32) :: name real, dimension(:), allocatable :: tmp1d real, dimension(:,:), allocatable :: tmp real, dimension(:,:,:), allocatable :: tmp3d type(axistype), allocatable, dimension(:) :: axes type(fieldtype), allocatable, dimension(:) :: fields if(.not. file_exist(trim(src_file)) ) & call mpp_error(FATAL, 'regrid_2d: file '//trim(src_file)//' does not exist') call mpp_open(unit, trim(src_file),& action=MPP_RDONLY, form=MPP_NETCDF, threading=MPP_MULTI, fileset=MPP_SINGLE) call mpp_get_info(unit, ndim, nvar, natt, ntime_src) allocate(fields(nvar)) call mpp_get_fields(unit, fields) if (numfields > nvar) call mpp_error(FATAL,'not enough fields in file') 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 found_src_field(n) = .true. src_field(n) = fields(i) write(stdout(),*) 'Interpolating src field : ',trim(name), ' to ocean model grid' endif end do enddo do n=1,numfields if(.not. found_src_field(n)) call mpp_error(FATAL, 'regrid_2d: 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) flip_y = .false. ni_src=0; nj_src=0; nk_src=1; ntime_src = 1 do j=1,ndim call mpp_get_atts(axes(j),len=len1) 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) if (lat_src(2) < lat_src(1)) then flip_y = .true. allocate(tmp1d(nj_src)) tmp1d=lat_src do jj=1,nj_src lat_src(jj)=tmp1d(nj_src-jj+1) enddo deallocate(tmp1d) endif case('Z') nk_src = len1 case ('T') ntime_src = len1 time_axis_exists = .true. allocate(time_in(ntime_src)) call mpp_get_times(unit, time_in) time_axis = axes(j) end select enddo if(ni_src==0) call mpp_error(FATAL,'regrid_2d: file ' & //trim(src_file)//' does not contain axis with cartesian attributes = "X" ') if(nj_src==0) call mpp_error(FATAL,'regrid_2d: file '& //trim(src_file)//' does not contain axis with cartesian attributes = "Y" ') allocate(data_src(ni_src,nj_src,2,ntime_src) ) allocate(tmp3d(ni_src,nj_src,nk_src), tmp(ni_src,nj_src)) !-------------------------------------------------------------------- ! loop through source fields !-------------------------------------------------------------------- do n=1, numfields !--- get the missing value call mpp_get_atts(src_field(n),missing=missing(n) ) if (level > nk_src) call mpp_error(FATAL,'selected level exceeds size of input array') if (nk_src > 1) write(*,*) 'warning: selecting level ',level,' from 3d array' do nt=1,ntime_src call mpp_read(unit,src_field(n), tmp3d,nt) tmp=tmp3d(:,:,level) !--- set up the mask of source data if(n==1 .and. nt == 1) then do_extrap = .false. do j = 1, nj_src do i = 1, ni_src if( abs(tmp(i,j) - missing(n)) <= tol ) then do_extrap = .true. endif enddo enddo endif if (flip_y) then do j =1, nj_src data_src(:,j,n,nt)=tmp(:,nj_src-j+1)* scale_factor(n) enddo else data_src(:,:,n,nt) = tmp(:,:)* scale_factor(n) endif enddo enddo call mpp_close(unit) deallocate(fields, axes, tmp, tmp3d) end subroutine read_src_file !##################################################################### !--- remap data from src grid to destination grid. subroutine remap_data integer :: ndivs, i, j, n, nt, 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 :: tmp_src, tmp_dst !--- 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(data_dst(ni_dst,nj_dst,numfields,ntime_src)) allocate(tmp_src(ni_src, nj_src), tmp_dst(isc:iec,jsc:jec) ) call mpp_domains_set_stack_size(2*ni_dst*nj_dst) do n=1, numfields do l=1,ntime_src if(do_extrap) then call extrap(data_src(:,:,n,l), tmp_src(:,:), stop_crit(n), missing(n)*scale_factor(n), is_cyclic ) call horiz_interp(Interp, tmp_src(:,:), tmp_dst ) else call horiz_interp(Interp, data_src(:,:,n,l), tmp_dst) endif !--- apply the mask to destination data do j = jsc, jec do i = isc, iec if(mask_dst(i,j) < 0.5) tmp_dst(i,j) = missing(n) enddo enddo call mpp_global_field(Domain,tmp_dst, data_dst(:,:,n,l) ) enddo enddo deallocate(tmp_src, tmp_dst) call horiz_interp_end if (vector_field) then do nt=1,ntime_src do j=1,nj_dst do i=1,ni_dst if(mask_dst(i,j) >= 0.5) then tmp_x = cos_rot(i,j)*data_dst(i,j,1,nt)+sin_rot(i,j)*data_dst(i,j,2,nt) tmp_y = -1.*sin_rot(i,j)*data_dst(i,j,1,nt)+cos_rot(i,j)*data_dst(i,j,2,nt) data_dst(i,j,1,nt) = tmp_x data_dst(i,j,2,nt) = tmp_y endif enddo enddo if (y_boundary_type == 'fold_north_edge' .and. dest_grid == 'c') then do i=1,ni_dst/2 if(mask_dst(i,nj_dst) >= 0.5) then data_dst(i,nj_dst,1,nt) = -1.*data_dst(ni_dst-i,nj_dst,1,nt) data_dst(i,nj_dst,2,nt) = -1.*data_dst(ni_dst-i,nj_dst,2,nt) endif enddo endif enddo endif !--- apply the destination mask to the !--- write out chksum for parallel checking if(mpp_pe()==mpp_root_pe()) then write(stdout(),*)'NOTE: Chksum for after-regrid data ', mpp_chksum(data_dst, (/mpp_root_pe()/) ) endif end subroutine remap_data !##################################################################### !--- write remapped data to file dest_file subroutine write_dst_file type(fieldtype) :: dest_field(2) integer :: unit, i, j, n character(len=32) :: units character(len=128) :: longname !-------------------------------------------------------------------- ! write output file metadata !-------------------------------------------------------------------- call mpp_open(unit, trim(dest_file),MPP_OVERWR,MPP_NETCDF,threading=MPP_SINGLE,& fileset=MPP_SINGLE) ! ! write global atts, will be changed in the future. The reason is we ! need to rename regrid_2d.f90 to regrid_2d.F90 ! !!$ do i=1,size(global_atts(:)) !!$ if (global_atts(i)%name == 'title') then !!$ global_atts(i)%catt = 'SBC field interpolated to ocean grid' !!$ global_atts(i)%len = 36 !!$ endif !!$ end do ! ! write axis metadata ! do i=1,size(axes_dst(:)) call mpp_copy_meta(unit,axes_dst(i)) end do if (time_axis_exists) then call mpp_copy_meta(unit, time_axis) endif ! ! write variable metadata ! call mpp_copy_meta(unit,field_lon_dst,axes=axes_dst) call mpp_copy_meta(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(unit, dest_field(n), (/axes_dst(1),axes_dst(2), time_axis/), & trim(dest_field_name(n)), units,longname, missing=missing(n), pack=1) else call mpp_write_meta(unit, dest_field(n), (/axes_dst(1),axes_dst(2)/), & 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(unit,axes_dst(i)) end do ! write variable data call mpp_write(unit, field_lon_dst,lon_dst) call mpp_write(unit, field_lat_dst,lat_dst) do j=1, ntime_src do n=1,numfields if (time_axis_exists) then call mpp_write(unit, dest_field(n), data_dst(:,:,n,j),time_in(j)) else call mpp_write(unit, dest_field(n), data_dst(:,:,n,1)) endif enddo enddo call mpp_close(unit) end subroutine write_dst_file !##################################################################### !--- release the memory subroutine regrid_2d_end deallocate( lon_src, lat_src, lon_dst, lat_dst) deallocate(data_dst, data_src, mask_dst) if(time_axis_exists) deallocate(time_in) if(dest_grid_type == 'C') deallocate(sin_rot, cos_rot) end subroutine regrid_2d_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, i, j, 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) ! 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) - missing_value) <= tol ) then tmp(i,j) = initial_guess sor(i,j) = rel_coef else tmp(i,j)=data_in(i,j) 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(:,:) = 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 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_2d