!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! !! !! 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 runoff_regrid !----------------------------------------------------------------------- ! 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 !----------------------------------------------------------------------- ! ! Sergey Malyshev ! Zhi Liang ! ! ! This program works together with program make_xgrids, create_grid to ! remap runoff data from a spherical grid onto any grid (spherical or tripolar) ! using conservative scheme. ! ! ! This program expects to read runoff data from a netcdf file, which is specfied ! by the namelist variable "src_data". The name of the runoff field in input file is specified ! by the namelist variable "src_fld_name". The output file is a netcdf file specified ! by the namelist variable "dst_data". The name of the runoff field in output file is specified ! by the namelist variable "src_fld_name". Both the source grid and destination grid ! are read from netcdf file "grid_spec.nc". The grid_spec.nc is expected contains ! field 'x_T' and 'y_T', which is the new name convention used as a prototype for ESMF. ! The source grid read from variable "xta" and "yta" of "grid_spec.nc" should match ! the grid in "src_data". "grid_spec.nc" is generated by ! " make_xgrid -a atmos_grid.nc -l atmos_grid.nc -o dst_grid.nc ", ! where dst_grid is the grid which you want remap runoff data on and atmos_grid.nc ! is generated through program "create_grid", which is located at the same directory ! as this program. make_xgrid is a c program that generates exchange grid between ! atmos, ocean and land grid. make_xgrid source code is located at ! "../generate_grids/make_xgrids". If there is any land point has runoff data ! after remapping runoff data onto destination grid, the runoff value of that land point ! will be moved to the nearest ocean point. ! ! ! implicit none #include "netcdf.inc" !--- namelist interface ---------------------------------------------- ! ! ! Name of input file containing runoff data. ! ! ! Name of runoff field in input file. ! ! ! Name of runoff field in output file. ! ! ! Name of output file containing runoff data. ! ! character(len=128) :: src_fld_name = 'runoff' ! name of the input runoff field character(len=128) :: dst_fld_name = 'runoff' ! name of the output runoff field character(len=128) :: src_data = 'src_data.nc' ! name of the runoff source data file character(len=128) :: dst_data = 'dst_data.nc' ! name of the runoff output file namelist /runoff_regrid_nml/ src_fld_name, dst_fld_name, src_data, dst_data !--- version information --------------------------------------------- character(len=128) :: version= '$ID$' character(len=128) :: tagname= '$Name: mom4p1_pubrel_dec2009_nnz $' !--- other variables ------------------------------------------------- real, allocatable :: time_value(:) real, allocatable :: runoff(:,:,:), runoff_src(:,:,:) logical, allocatable :: mask(:,:) real, allocatable :: lon(:), lat(:), x_t_dst(:,:), y_t_dst(:,:), grid_x_t(:), grid_y_t(:) integer, allocatable :: i_atm_atmxocn(:), j_atm_atmxocn(:), i_ocn_atmxocn(:), j_ocn_atmxocn(:) integer, allocatable :: i_atm_atmxlnd(:), j_atm_atmxlnd(:), i_lnd_atmxlnd(:), j_lnd_atmxlnd(:) real, allocatable :: area_atmxocn(:), area_atmxlnd(:) integer :: ni_src, nj_src, ni_dst, nj_dst, ntime real, parameter :: PI = 3.141592658979 integer :: stdout = 6 !--- initialize routine call runoff_regrid_init() !--- read source data file ------------------------------------------- call read_src_data() !--- read exchange grid information ---------------------------------- call read_xgrid() !--- read the runoff data and remap onto destination grid. call remap_data() !--- write runoff data to destination file call write_dst_data() !--- release memory call runoff_regrid_end contains !####################################################################### !--- initialization routine: read namelist, write namelist and version information subroutine runoff_regrid_init integer :: unit=7, iostat logical :: opened !--- read namelist do inquire( unit, opened = opened ) if( .not. opened ) exit unit = unit + 1 if( unit.EQ.100 )call error_handler( 'Error: unable to locate unit number.' ) enddo open (unit, file='input.nml', iostat = iostat ) if(iostat .ne. 0) call error_handler('error in open input.nml file') read (unit, runoff_regrid_nml, iostat=iostat) if(iostat .ne. 0) call error_handler('error in while reading runoff_regrid_nml ') close(unit) !--- write out namelist and version information write( stdout,'(/a)' )'runoff_regrid '//trim(version)//trim(tagname) write( stdout, runoff_regrid_nml) end subroutine runoff_regrid_init !####################################################################### ! read runoff data from src_data subroutine read_src_data integer :: dims(4), start(4), nread(4) integer :: rcode, ncid, ndims, nvar, id_time, id_runoff, i real :: missing_value rcode = nf_open(trim(src_data), NF_NOWRITE, ncid) rcode = nf_inq_varid(ncid, trim(src_fld_name), id_runoff ) rcode = nf_inq_varndims(ncid, id_runoff, ndims) rcode = nf_inq_vardimid(ncid, id_runoff, dims) rcode = nf_inq_dimlen(ncid, dims(1), ni_src) rcode = nf_inq_dimlen(ncid, dims(2), nj_src) ntime = 1 if(ndims .gt. 2) then rcode = nf_inq_dimlen(ncid, dims(3), ntime) endif !--- get the time values if there are more than one time level if(ndims .gt. 2) then allocate(time_value(ntime) ) rcode = nf_inq_varid(ncid, 'Time', id_time) rcode = nf_get_var_double(ncid, id_time, time_value) endif allocate(runoff_src(ni_src, nj_src, ntime) ) !--- read the source runoff data start = 1; nread = 1 nread(1) = ni_src; nread(2) = nj_src; nread(3) = ntime rcode = nf_get_vara_double(ncid, id_runoff, start, nread, runoff_src) !--- get the missing value ------------------------------------------- missing_value = -1.0e20 rcode = nf_get_att_double(ncid, id_runoff, 'missing_value', missing_value) if(rcode == 0) then where(runoff_src == missing_value) runoff_src = 0.0 endif rcode = nf_close(ncid) end subroutine read_src_data !####################################################################### ! read component grid and exchange grid from grid_spec.nc subroutine read_xgrid integer :: rcode, ncid, ncells, ni_lon, nj_lat, i, j real, allocatable :: wet(:,:) logical, allocatable :: mask0(:,:) !--- read xgrid from the grid_spec.nc file -------------------------- rcode = nf_open('grid_spec.nc', NF_NOWRITE, ncid) call error_handler('error in open file grid_spec.nc', rcode) ni_dst = get_dimlen(ncid, 'grid_x_T') nj_dst = get_dimlen(ncid, 'grid_y_T') allocate(grid_x_t(ni_dst), grid_y_t(nj_dst) ) allocate(wet(ni_dst,nj_dst), x_t_dst(ni_dst,nj_dst), y_t_dst(ni_dst,nj_dst) ) allocate(mask0(ni_dst,nj_dst),mask(ni_dst,nj_dst)) !--- get grid axis data ---------------------------------------------- call get_var_real_1d(ncid,'grid_x_T', grid_x_t) call get_var_real_1d(ncid,'grid_y_T', grid_y_t) call get_var_real_2d(ncid, 'wet', wet) ! calculate coastal mask, on ocn grid mask0 = (wet > 0.5) do j = 1, nj_dst do i = 1, ni_dst mask(i,j) = coastal(mask0,i,j) enddo enddo deallocate(mask0) call get_var_real_2d(ncid, 'x_T', x_t_dst) call get_var_real_2d(ncid, 'y_T', y_t_dst) !--- get exchange grid information ncells = get_dimlen(ncid, 'I_ATM_ATMxOCN') allocate(i_atm_atmxocn(ncells), j_atm_atmxocn(ncells), & i_ocn_atmxocn(ncells), j_ocn_atmxocn(ncells), & area_atmxocn(ncells) ) call get_var_int_1d(ncid, 'I_ATM_ATMxOCN', i_atm_atmxocn) call get_var_int_1d(ncid, 'J_ATM_ATMxOCN',j_atm_atmxocn ) call get_var_int_1d(ncid, 'I_OCN_ATMxOCN',i_ocn_atmxocn ) call get_var_int_1d(ncid, 'J_OCN_ATMxOCN',j_ocn_atmxocn ) call get_var_real_1d(ncid, 'AREA_ATMxOCN',area_atmxocn ) ncells = get_dimlen(ncid, 'I_ATM_ATMxLND') allocate(i_atm_atmxlnd(ncells), j_atm_atmxlnd(ncells), & i_lnd_atmxlnd(ncells), j_lnd_atmxlnd(ncells), & area_atmxlnd(ncells) ) call get_var_int_1d(ncid, 'I_ATM_ATMxLND', i_atm_atmxlnd) call get_var_int_1d(ncid, 'J_ATM_ATMxLND',j_atm_atmxlnd ) call get_var_int_1d(ncid, 'I_LND_ATMxLND',i_lnd_atmxlnd ) call get_var_int_1d(ncid, 'J_LND_ATMxLND',j_lnd_atmxlnd ) call get_var_real_1d(ncid, 'AREA_ATMxLND',area_atmxlnd ) !--- get the longitude and latitude of simple lon lat grid !--- it should has the same size as the runoff data ni_src, nj_src ni_lon = get_dimlen(ncid, 'xta') nj_lat = get_dimlen(ncid, 'yta') if(ni_lon .ne. ni_src .or. nj_lat .ne. nj_src) & call error_handler('Error: size mismatch between file grid_spec.nc and file '//trim(src_data) ) allocate(lon(ni_src), lat(nj_src)) call get_var_real_1d(ncid, 'xta', lon) call get_var_real_1d(ncid, 'yta', lat) rcode = nf_close(ncid) end subroutine read_xgrid !####################################################################### ! remapping runoff data onto destination grid subroutine remap_data integer :: i, j, k, ncells_axo, ncells_axl real, allocatable :: area_ocn(:,:), area_lnd(:,:), runoff_lnd(:,:), x(:), xl(:) integer, allocatable :: imap(:,:), jmap(:,:) real :: D2R D2R = PI/180. ncells_axo = size(i_atm_atmxocn(:)) ncells_axl = size(i_atm_atmxlnd(:)) allocate(area_ocn(ni_dst, nj_dst), area_lnd(ni_src, nj_src)) allocate(runoff_lnd(ni_src, nj_src), runoff(ni_dst, nj_dst, ntime) ) allocate(x(ncells_axo), xl(ncells_axl) ) allocate(imap(ni_src, nj_src), jmap(ni_src, nj_src) ) imap = 0 jmap = 0 runoff = 0 do k = 1, ntime ! put input runoff to ocean grid do i = 1, ncells_axo x(i) = runoff_src(i_atm_atmxocn(i),j_atm_atmxocn(i),k) enddo area_ocn = 0 do i = 1, ncells_axo runoff(i_ocn_atmxocn(i),j_ocn_atmxocn(i), k) = & runoff(i_ocn_atmxocn(i),j_ocn_atmxocn(i), k)+area_atmxocn(i)*x(i) area_ocn(i_ocn_atmxocn(i),j_ocn_atmxocn(i)) = & area_ocn(i_ocn_atmxocn(i),j_ocn_atmxocn(i))+area_atmxocn(i) enddo where (area_ocn(:,:) > 0) runoff(:,:,k) = runoff(:,:,k)/area_ocn(:,:) ! put input runoff to land grid -- it will be mostly zero, but in places where ! the input data grid and model ocean grid do not coincide do i = 1, ncells_axl xl(i) = runoff_src(i_atm_atmxlnd(i),j_atm_atmxlnd(i),k) enddo runoff_lnd = 0 area_lnd = 0 do i = 1, ncells_axl runoff_lnd(i_lnd_atmxlnd(i),j_lnd_atmxlnd(i)) = & runoff_lnd(i_lnd_atmxlnd(i),j_lnd_atmxlnd(i))+area_atmxlnd(i)*xl(i) area_lnd(i_lnd_atmxlnd(i),j_lnd_atmxlnd(i)) = & area_lnd(i_lnd_atmxlnd(i),j_lnd_atmxlnd(i))+area_atmxlnd(i) enddo where (area_lnd > 0) runoff_lnd = runoff_lnd/area_lnd write (stdout,*) 'At time level ', k,' There are ',count(runoff_lnd/=0), 'land points that has runoff value' ! calculate remapping do j = 1,nj_src do i = 1,ni_src if(runoff_lnd(i,j)>0) then if(imap(i,j)==0) & call nearest(mask,x_t_dst*D2R,y_t_dst*D2R,lon(i)*D2R,lat(j)*D2R,imap(i,j),jmap(i,j)) runoff(imap(i,j),jmap(i,j),k) = runoff(imap(i,j),jmap(i,j),k) + & runoff_lnd(i,j)*area_lnd(i,j)/area_ocn(imap(i,j),jmap(i,j)) endif enddo enddo enddo end subroutine remap_data !##################################################################### ! write out output subroutine write_dst_data integer :: id_lon, id_lat, id_time, id_time_src, id_runoff_src, id_runoff integer :: type, ndims, natts, rcode, ncid, ncid_src, i, natts_time integer :: dims(4), start(4), nwrite(4) character(len=64) :: name rcode = nf_open(trim(src_data), NF_NOWRITE, ncid_src) call error_handler('error in opening file '//trim(src_data) , rcode) rcode = nf_inq_varid(ncid_src, trim(src_fld_name), id_runoff_src ) call error_handler('error in inquring varible id of variable'//trim(src_fld_name) , rcode) rcode = nf_inq_varndims(ncid_src, id_runoff_src, ndims ) call error_handler('error in inquring ndims of variable'//trim(src_fld_name) , rcode) rcode = nf_inq_varnatts(ncid_src, id_runoff_src, natts ) call error_handler('error in inquring natts of variable'//trim(src_fld_name) , rcode) rcode = nf_create(trim(dst_data),NF_WRITE, ncid) call error_handler('error in creating file '//trim(dst_data), rcode) rcode = nf_def_dim(ncid, 'grid_x_T', ni_dst, dims(1)) call error_handler('error in defining x dimension' , rcode) rcode = nf_def_dim(ncid, 'grid_y_T', nj_dst, dims(2)) call error_handler('error in defining y dimension' , rcode) if(ndims > 2) then rcode = nf_def_dim(ncid, 'Time', NF_UNLIMITED, dims(3)) call error_handler('error in defining time dimension' , rcode) endif rcode = nf_def_var(ncid, 'grid_x_T', NF_DOUBLE, 1, dims(1), id_lon) call error_handler('error in defining variable grid_x_T' , rcode) rcode = nf_put_att_text(ncid,id_lon, 'long_name', 34,'Nominal Longitude of T-cell center') call error_handler('error in putting longnmae of variable grid_x_T' , rcode) rcode = nf_put_att_text(ncid,id_lon, 'cartesian_axis', 1,'X') call error_handler('error in putting cartesian_axis of variable grid_x_T' , rcode) rcode = nf_put_att_text(ncid,id_lon, 'units', 11,'degree_east') call error_handler('error in putting units of variable grid_x_T' , rcode) rcode = nf_def_var(ncid, 'grid_y_T', NF_DOUBLE, 1, dims(2), id_lat) call error_handler('error in defining variable grid_y_T' , rcode) rcode = nf_put_att_text(ncid,id_lat, 'long_name', 33,'Nominal latitude of T-cell center') call error_handler('error in putting longnmae of variable grid_x_T' , rcode) rcode = nf_put_att_text(ncid,id_lat, 'cartesian_axis', 1,'Y') call error_handler('error in putting cartesian_axis of variable grid_x_T' , rcode) rcode = nf_put_att_text(ncid,id_lat, 'units', 12,'degree_north') call error_handler('error in putting units of variable grid_x_T' , rcode) if(ndims > 2) then rcode = nf_def_var(ncid, 'Time', NF_DOUBLE, 1, dims(3), id_time) call error_handler('error in defining variable Time' , rcode) rcode = nf_inq_varid(ncid_src,'Time',id_time_src) call error_handler('error in inquiring variable id of Time of file'//trim(src_data) , rcode) rcode = nf_inq_varnatts(ncid_src, id_time_src, natts_time ) call error_handler('error in inquring natts of variable Time', rcode) do i = 1, natts_time rcode = nf_inq_attname(ncid_src, id_time_src, i, name) call error_handler('error in inquring attribute '//trim(name), rcode) rcode = nf_copy_att(ncid_src,id_time_src, trim(name), ncid, id_time) call error_handler('error in copy attribute '//trim(name)//' of Time', rcode) enddo endif if(ndims .le. 2) then rcode = nf_def_var(ncid, trim(dst_fld_name), NF_DOUBLE, 2, dims(1:2), id_runoff) else rcode = nf_def_var(ncid, trim(dst_fld_name), NF_DOUBLE, 3, dims(1:3), id_runoff) endif call error_handler('error in defining variable '//trim(dst_fld_name) , rcode) !--- write the field attribute --------------------------------------- do i = 1, natts rcode = nf_inq_attname(ncid_src, id_runoff_src, i, name) call error_handler('error in inquring attribute '//trim(name), rcode) if(trim(name) .ne. '_FillValue') then rcode = nf_copy_att(ncid_src,id_runoff_src, trim(name), ncid, id_runoff) call error_handler('error in copy attribute '//trim(name)//' of variable '//trim(src_fld_name), rcode) endif enddo rcode = nf_enddef(ncid) !--- write out data -------------------------------------------------- start = 1; nwrite = 1; nwrite(1) = ni_dst rcode = nf_put_vara_double(ncid, id_lon, start, nwrite, grid_x_t) call error_handler('error in putting grid_x_T data', rcode) nwrite(1) = nj_dst rcode = nf_put_vara_double(ncid, id_lat, start, nwrite, grid_y_t) call error_handler('error in putting grid_y_T data', rcode) do i =1, ntime if(ndims>2) then rcode = nf_put_var1_double(ncid, id_time, i, time_value(i)) call error_handler('error in putting time data', rcode) endif nwrite(1) = ni_dst; nwrite(2) = nj_dst start(3) = i rcode = nf_put_vara_double(ncid, id_runoff, start, nwrite, runoff(:,:,i)) call error_handler('error in putting data of '//trim(dst_fld_name), rcode) enddo rcode = nf_close(ncid_src) rcode = nf_close(ncid) end subroutine write_dst_data !##################################################################### ! get the dimension length of any one dimensional variable function get_dimlen(ncid, name) integer, intent(in) :: ncid character(len=*), intent(in) :: name integer :: get_dimlen integer :: varid, rcode, dims(1) rcode = nf_inq_varid(ncid, trim(name), varid) call error_handler('error in inquiring variable id of '//trim(name), rcode) rcode = nf_inq_vardimid(ncid, varid, dims) call error_handler('error in inquiring dimension id of '//trim(name), rcode) rcode = nf_inq_dimlen(ncid, dims(1), get_dimlen) call error_handler('error in inquiring dimension length of '//trim(name), rcode) end function get_dimlen !##################################################################### ! read the 1d real data from netcdf file. subroutine get_var_real_1d(ncid, name, data) integer, intent(in) :: ncid character(len=*), intent(in) :: name real, dimension(:), intent(out) :: data integer :: rcode, varid rcode = nf_inq_varid(ncid, name, varid) call error_handler('error in inquiring variable id of '//trim(name), rcode) rcode = nf_get_var_double(ncid, varid, data) call error_handler('error in reading data of '//trim(name), rcode) end subroutine get_var_real_1d !##################################################################### ! read the 2d real data from netcdf file. subroutine get_var_real_2d(ncid, name, data) integer, intent(in) :: ncid character(len=*), intent(in) :: name real, dimension(:,:), intent(out) :: data integer :: rcode, varid rcode = nf_inq_varid(ncid, name, varid) call error_handler('error in inquiring variable id of '//trim(name), rcode) rcode = nf_get_var_double(ncid, varid, data) call error_handler('error in reading data of '//trim(name), rcode) end subroutine get_var_real_2d !##################################################################### ! read the 1d integer data from netcdf file. subroutine get_var_int_1d(ncid, name, data) integer, intent(in) :: ncid character(len=*), intent(in) :: name integer, dimension(:), intent(out) :: data integer :: rcode, varid rcode = nf_inq_varid(ncid, name, varid) call error_handler('error in inquiring variable id of '//trim(name), rcode) rcode = nf_get_var_int(ncid, varid, data) call error_handler('error in reading data of '//trim(name), rcode) end subroutine get_var_int_1d !##################################################################### ! find the nearest ocean point of a given land point. subroutine nearest(mask, lon, lat, plon, plat, iout, jout) ! finde nearest point that is not masked out; brute force approach logical, intent(in) :: mask(:,:) ! mask of valid input points real, intent(in) :: lon(:,:) ! longitudes of input grd central points real, intent(in) :: lat(:,:) ! latitudes of input grd central points real, intent(in) :: plon, plat ! coordinates of destination point integer, intent(out):: iout, jout ! output map values integer :: i,j real :: r, r1 r = distance(0.0,PI/2,0.0,-PI/2) ! some big value do j = 1, size(mask,2) do i = 1, size(mask,1) if (.not.mask(i,j)) cycle r1 = distance(plon,plat,lon(i,j),lat(i,j)) if ( r1 < r ) then iout = i jout = j r = r1 endif enddo enddo end subroutine nearest !##################################################################### ! returns true if (i,j) is a coastal point of ocean function coastal(wet, i,j) logical, intent(in) :: wet(:,:) integer, intent(in) :: i,j logical :: coastal type delta integer :: i,j end type delta integer :: i0,j0,k type(delta):: stencil(4) data stencil/delta(-1,0),delta(1,0),delta(0,-1),delta(0,1)/ coastal = .false. if(.not.wet(i,j)) return do k = 1, size(stencil(:)) i0 = lbound(wet,1)+modulo(i+stencil(k)%i-lbound(wet,1),size(wet,1)); j0 = min( max(j+stencil(k)%j,lbound(wet,2)), ubound(wet,2) ) coastal = coastal.or..not.wet(i0,j0) enddo end function coastal !##################################################################### ! calculate a distance in 3-d between points on unit square function distance(lon1, lat1, lon2, lat2) real, intent(in) :: lon1,lat1,lon2,lat2 real :: distance real :: x1,y1,z1, x2,y2,z2 z1 = sin(lat1) ; z2 = sin(lat2) x1 = cos(lat1)*cos(lon1) ; x2 = cos(lat2)*cos(lon2) y1 = cos(lat1)*sin(lon1) ; y2 = cos(lat2)*sin(lon2) distance = sqrt((z1-z2)**2+(x1-x2)**2+(y1-y2)**2) end function distance !##################################################################### ! error handling routine. subroutine error_handler(mesg, status) character(len=*), intent(in) :: mesg integer, optional, intent(in) :: status character(len=256) :: msg if(present(status)) then if(status == 0) return msg = nf_strerror(status) msg = trim(mesg)//': '// trim(msg) else msg = trim(mesg) endif write(stdout,*) msg call ABORT() end subroutine error_handler !####################################################################### ! destructor: release memory. subroutine runoff_regrid_end deallocate(runoff, runoff_src, mask, lon, lat, x_t_dst, y_t_dst) deallocate(i_atm_atmxocn, j_atm_atmxocn, i_ocn_atmxocn, j_ocn_atmxocn) deallocate(i_atm_atmxlnd, j_atm_atmxlnd, i_lnd_atmxlnd, j_lnd_atmxlnd) deallocate(area_atmxocn, area_atmxlnd) end subroutine runoff_regrid_end !####################################################################### end program runoff_regrid