!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! !! !! 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 !! !! !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! module axis_utils_mod ! !M.J. Harrison ! !Bruce Wyman ! ! ! A set of utilities for manipulating axes and extracting axis ! attributes ! ! ! ! subroutine get_axis_cart(axis,cart) : Returns X,Y,Z or T cartesian attribute ! subroutine get_axis_bounds(axis,axis_bound,axes) : Return axis_bound either from an array of ! available axes, or defined based on axis mid-points ! function get_axis_modulo : Returns true if axis has the modulo attribute ! function get_axis_fold : Returns is axis is folded at a boundary (non-standard meta-data) ! function lon_in_range : Returns lon_strt <= longitude <= lon_strt+360 ! subroutine tranlon : Returns monotonic array of longitudes s.t., lon_strt <= lon(:) <= lon_strt+360. ! subroutine nearest_index : Return index of nearest point along axis ! ! ! use mpp_io_mod, only: axistype, atttype, default_axis, default_att, & mpp_get_atts, mpp_get_axis_data, mpp_modify_meta, & mpp_get_att_name, mpp_get_att_type, mpp_get_att_char, & mpp_get_att_length use mpp_mod, only: mpp_error, FATAL, stdout use fms_mod, only: lowercase, string_array_index implicit none # include public get_axis_cart, get_axis_bounds, get_axis_modulo, get_axis_fold, lon_in_range, & tranlon, frac_index, nearest_index, interp_1d, get_axis_modulo_times private integer, parameter :: maxatts = 100 real, parameter :: epsln= 1.e-10 real, parameter :: fp5 = 0.5, f360 = 360.0 character(len=256) :: version = '$Id: axis_utils.F90,v 16.0 2008/07/30 22:44:47 fms Exp $' character(len=256) :: tagname = '$Name: mom4p1_pubrel_dec2009_nnz $' interface interp_1d module procedure interp_1d_1d module procedure interp_1d_2d module procedure interp_1d_3d end interface contains subroutine get_axis_cart(axis, cart) type(axistype), intent(in) :: axis character(len=1), intent(out) :: cart character(len=1) :: axis_cart character(len=16), dimension(2) :: lon_names, lat_names character(len=16), dimension(3) :: z_names character(len=16), dimension(2) :: t_names character(len=16), dimension(3) :: lon_units, lat_units character(len=8) , dimension(4) :: z_units character(len=3) , dimension(6) :: t_units character(len=32) :: name integer :: i,j lon_names = (/'lon','x '/) lat_names = (/'lat','y '/) z_names = (/'depth ','height','z '/) t_names = (/'time','t '/) lon_units = (/'degrees_e ', 'degrees_east', 'degreese '/) lat_units = (/'degrees_n ', 'degrees_north', 'degreesn '/) z_units = (/'cm ','m ','pa ','hpa'/) t_units = (/'sec', 'min','hou','day','mon','yea'/) call mpp_get_atts(axis,cartesian=axis_cart) cart = 'N' if ( lowercase(axis_cart) == 'x' ) cart = 'X' if ( lowercase(axis_cart) == 'y' ) cart = 'Y' if ( lowercase(axis_cart) == 'z' ) cart = 'Z' if ( lowercase(axis_cart) == 't' ) cart = 'T' if (cart /= 'X' .and. cart /= 'Y' .and. cart /= 'Z' .and. cart /= 'T') then call mpp_get_atts(axis,name=name) name = lowercase(name) do i=1,size(lon_names(:)) if (trim(name(1:3)) == trim(lon_names(i))) cart = 'X' enddo do i=1,size(lat_names(:)) if (trim(name(1:3)) == trim(lat_names(i))) cart = 'Y' enddo do i=1,size(z_names(:)) if (trim(name) == trim(z_names(i))) cart = 'Z' enddo do i=1,size(t_names(:)) if (trim(name) == t_names(i)) cart = 'T' enddo end if if (cart /= 'X' .and. cart /= 'Y' .and. cart /= 'Z' .and. cart /= 'T') then call mpp_get_atts(axis,units=name) name = lowercase(name) do i=1,size(lon_units(:)) if (trim(name) == trim(lon_units(i))) cart = 'X' enddo do i=1,size(lat_units(:)) if (trim(name) == trim(lat_units(i))) cart = 'Y' enddo do i=1,size(z_units(:)) if (trim(name) == trim(z_units(i))) cart = 'Z' enddo do i=1,size(t_units(:)) if (name(1:3) == trim(t_units(i))) cart = 'T' enddo end if return end subroutine get_axis_cart subroutine get_axis_bounds(axis,axis_bound,axes) type(axistype), intent(in) :: axis type(axistype), intent(inout) :: axis_bound type(axistype), intent(in), dimension(:) :: axes type(atttype), dimension(:), allocatable :: att real, dimension(:), allocatable :: data, tmp integer :: i, len character(len=128) :: bounds_name = 'none', name, units character(len=256) :: longname character(len=1) :: cartesian axis_bound = default_axis allocate(att(maxatts)) att = default_att call mpp_get_atts(axis,atts=att) do i=1,maxatts if (mpp_get_att_type(att(i)) == NF_CHAR) then ! if (str_contains(att(i)%name,'bounds') .or. str_contains(att(i)%name,'edge')) then if (string_array_index('bounds',(/mpp_get_att_name(att(i))/)) .or. & string_array_index('edge',(/mpp_get_att_name(att(i))/))) then bounds_name = mpp_get_att_char(att(i)) endif endif enddo if (trim(bounds_name) /= 'none') then do i=1,size(axes(:)) call mpp_get_atts(axes(i),name=name) if (lowercase(trim(name)) == lowercase(trim(bounds_name))) then axis_bound = axes(i) endif enddo call mpp_get_atts(axis_bound,len=len) if (len < 1) call mpp_error(FATAL,'error locating boundary axis for '//bounds_name) else call mpp_get_atts(axis,name=name,units=units,longname=longname,& cartesian=cartesian,len=len) name = trim(name)//'_bounds' longname = trim(longname)//' bounds' allocate(tmp(len)) call mpp_get_axis_data(axis,tmp) allocate(data(len+1)) do i=2,len data(i)= tmp(i-1)+fp5*(tmp(i)-tmp(i-1)) enddo data(1)= tmp(1)- fp5*(tmp(2)-tmp(1)) if (abs(data(1)) < epsln) data(1) = 0.0 data(len+1)= tmp(len)+ fp5*(tmp(len)-tmp(len-1)) if (data(1) == 0.0) then if (abs(data(len+1)-360.) > epsln) data(len+1)=360.0 endif call mpp_modify_meta(axis_bound,name=name,units=units,longname=& longname,cartesian=cartesian,data=data) deallocate(tmp) deallocate(data) endif return end subroutine get_axis_bounds function get_axis_modulo(axis) type(axistype) :: axis logical :: get_axis_modulo integer :: natt, i type(atttype), dimension(:), allocatable :: atts call mpp_get_atts(axis,natts=natt) allocate(atts(natt)) call mpp_get_atts(axis,atts=atts) get_axis_modulo=.false. do i = 1,natt if (lowercase(trim(mpp_get_att_name(atts(i)))) == 'modulo') get_axis_modulo = .true. enddo deallocate(atts) return end function get_axis_modulo function get_axis_modulo_times(axis, tbeg, tend) logical :: get_axis_modulo_times type(axistype), intent(in) :: axis character(len=*), intent(out) :: tbeg, tend integer :: natt, i type(atttype), dimension(:), allocatable :: atts logical :: found_tbeg, found_tend call mpp_get_atts(axis,natts=natt) allocate(atts(natt)) call mpp_get_atts(axis,atts=atts) found_tbeg = .false. found_tend = .false. do i = 1,natt if(lowercase(trim(mpp_get_att_name(atts(i)))) == 'modulo_beg') then if(mpp_get_att_length(atts(i)) > len(tbeg)) then call mpp_error(FATAL,'error in get: len(tbeg) too small to hold attribute') endif tbeg = trim(mpp_get_att_char(atts(i))) found_tbeg = .true. endif if(lowercase(trim(mpp_get_att_name(atts(i)))) == 'modulo_end') then if(mpp_get_att_length(atts(i)) > len(tend)) then call mpp_error(FATAL,'error in get: len(tend) too small to hold attribute') endif tend = trim(mpp_get_att_char(atts(i))) found_tend = .true. endif enddo if(found_tbeg .and. .not.found_tend) then call mpp_error(FATAL,'error in get: Found modulo_beg but not modulo_end') endif if(.not.found_tbeg .and. found_tend) then call mpp_error(FATAL,'error in get: Found modulo_end but not modulo_beg') endif get_axis_modulo_times = found_tbeg end function get_axis_modulo_times function get_axis_fold(axis) type(axistype) :: axis logical :: get_axis_fold integer :: natt, i type(atttype), dimension(:), allocatable :: atts call mpp_get_atts(axis,natts=natt) allocate(atts(natt)) call mpp_get_atts(axis,atts=atts) get_axis_fold=.false. do i = 1,natt if (mpp_get_att_char(atts(i)) == 'fold_top') get_axis_fold = .true. enddo deallocate(atts) return end function get_axis_fold function lon_in_range(lon, l_strt) real :: lon, l_strt, lon_in_range, l_end lon_in_range = lon l_end = l_strt+360. if (abs(lon_in_range - l_strt) < 1.e-4) then lon_in_range = l_strt return endif if (abs(lon_in_range - l_end) < 1.e-4) then lon_in_range = l_strt return endif do if (lon_in_range < l_strt) then lon_in_range = lon_in_range + f360; else if (lon_in_range > l_end) then lon_in_range = lon_in_range - f360; else exit end if end do end function lon_in_range subroutine tranlon(lon, lon_start, istrt) ! returns array of longitudes s.t. lon_strt <= lon < lon_strt+360. ! also, the first istrt-1 entries are moved to the end of the array ! ! e.g. ! lon = 0 1 2 3 4 5 ... 358 359; lon_strt = 3 ==> ! tranlon = 3 4 5 6 7 8 ... 359 360 361 362; istrt = 4 real, intent(inout), dimension(:) :: lon real, intent(in) :: lon_start integer, intent(out) :: istrt integer :: len, i real :: lon_strt, tmp(size(lon(:))-1) len = size(lon(:)) do i=1,len lon(i) = lon_in_range(lon(i),lon_start) enddo istrt=0 do i=1,len-1 if (lon(i+1) < lon(i)) then istrt=i+1 exit endif enddo if (istrt>1) then ! grid is not monotonic if (abs(lon(len)-lon(1)) < epsln) then tmp = cshift(lon(1:len-1),istrt-1) lon(1:len-1) = tmp lon(len) = lon(1) else lon = cshift(lon,istrt-1) endif lon_strt = lon(1) do i=2,len+1 lon(i) = lon_in_range(lon(i),lon_strt) lon_strt = lon(i) enddo endif return end subroutine tranlon function frac_index (value, array) !======================================================================= ! ! nearest_index = index of nearest data point within "array" corresponding to ! "value". ! ! inputs: ! ! value = arbitrary data...same units as elements in "array" ! array = array of data points (must be monotonically increasing) ! ! output: ! ! nearest_index = index of nearest data point to "value" ! if "value" is outside the domain of "array" then nearest_index = 1 ! or "ia" depending on whether array(1) or array(ia) is ! closest to "value" ! ! note: if "array" is dimensioned array(0:ia) in the calling ! program, then the returned index should be reduced ! by one to account for the zero base. ! ! example: ! ! let model depths be defined by the following: ! parameter (km=5) ! dimension z(km) ! data z /5.0, 10.0, 50.0, 100.0, 250.0/ ! ! k1 = nearest_index (12.5, z, km) ! k2 = nearest_index (0.0, z, km) ! ! k1 would be set to 2, and k2 would be set to 1 so that ! z(k1) would be the nearest data point to 12.5 and z(k2) would ! be the nearest data point to 0.0 ! !======================================================================= integer :: ia, i, ii, unit real :: value, frac_index real, dimension(:) :: array logical keep_going ia = size(array(:)) do i=2,ia if (array(i) < array(i-1)) then unit = stdout() write (unit,*) '=> Error: "frac_index" array must be monotonically increasing when searching for nearest value to ',& value write (unit,*) ' array(i) < array(i-1) for i=',i write (unit,*) ' array(i) for i=1..ia follows:' do ii=1,ia write (unit,*) 'i=',ii, ' array(i)=',array(ii) enddo call mpp_error(FATAL,' "frac_index" array must be monotonically increasing.') endif enddo if (value < array(1) .or. value > array(ia)) then ! if (value < array(1)) frac_index = 1. ! if (value > array(ia)) frac_index = float(ia) frac_index = -1.0 else i=1 keep_going = .true. do while (i <= ia .and. keep_going) i = i+1 if (value <= array(i)) then frac_index = float(i-1) + (value-array(i-1))/(array(i)-array(i-1)) keep_going = .false. endif enddo endif end function frac_index function nearest_index (value, array) !======================================================================= ! ! nearest_index = index of nearest data point within "array" corresponding to ! "value". ! ! inputs: ! ! value = arbitrary data...same units as elements in "array" ! array = array of data points (must be monotonically increasing) ! ia = dimension of "array" ! ! output: ! ! nearest_index = index of nearest data point to "value" ! if "value" is outside the domain of "array" then nearest_index = 1 ! or "ia" depending on whether array(1) or array(ia) is ! closest to "value" ! ! note: if "array" is dimensioned array(0:ia) in the calling ! program, then the returned index should be reduced ! by one to account for the zero base. ! ! example: ! ! let model depths be defined by the following: ! parameter (km=5) ! dimension z(km) ! data z /5.0, 10.0, 50.0, 100.0, 250.0/ ! ! k1 = nearest_index (12.5, z, km) ! k2 = nearest_index (0.0, z, km) ! ! k1 would be set to 2, and k2 would be set to 1 so that ! z(k1) would be the nearest data point to 12.5 and z(k2) would ! be the nearest data point to 0.0 ! !======================================================================= integer :: nearest_index, ia, i, ii, unit real :: value real, dimension(:) :: array logical keep_going ia = size(array(:)) do i=2,ia if (array(i) < array(i-1)) then unit = stdout() write (unit,*) '=> Error: "nearest_index" array must be monotonically increasing & &when searching for nearest value to ',value write (unit,*) ' array(i) < array(i-1) for i=',i write (unit,*) ' array(i) for i=1..ia follows:' do ii=1,ia write (unit,*) 'i=',ii, ' array(i)=',array(ii) enddo call mpp_error(FATAL,' "nearest_index" array must be monotonically increasing.') endif enddo if (value < array(1) .or. value > array(ia)) then if (value < array(1)) nearest_index = 1 if (value > array(ia)) nearest_index = ia else i=1 keep_going = .true. do while (i <= ia .and. keep_going) i = i+1 if (value <= array(i)) then nearest_index = i if (array(i)-value > value-array(i-1)) nearest_index = i-1 keep_going = .false. endif enddo endif end function nearest_index !############################################################################# subroutine interp_1d_linear(grid1,grid2,data1,data2) real, dimension(:), intent(in) :: grid1, data1, grid2 real, dimension(:), intent(inout) :: data2 integer :: n1, n2, i, n, ext real :: w n1 = size(grid1(:)) n2 = size(grid2(:)) do i=2,n1 if (grid1(i) <= grid1(i-1)) call mpp_error(FATAL, 'grid1 not monotonic') enddo do i=2,n2 if (grid2(i) <= grid2(i-1)) call mpp_error(FATAL, 'grid2 not monotonic') enddo if (grid1(1) > grid2(1) ) call mpp_error(FATAL, 'grid2 lies outside grid1') if (grid1(n1) < grid2(n2) ) call mpp_error(FATAL, 'grid2 lies outside grid1') do i=1,n2 n = nearest_index(grid2(i),grid1) if (grid1(n) < grid2(i)) then w = (grid2(i)-grid1(n))/(grid1(n+1)-grid1(n)) data2(i) = (1.-w)*data1(n) + w*data1(n+1) else if(n==1) then data2(i) = data1(n) else w = (grid2(i)-grid1(n-1))/(grid1(n)-grid1(n-1)) data2(i) = (1.-w)*data1(n-1) + w*data1(n) endif endif enddo return end subroutine interp_1d_linear !################################################################### subroutine interp_1d_cubic_spline(grid1, grid2, data1, data2, yp1, ypn) real, dimension(:), intent(in) :: grid1, grid2, data1 real, dimension(:), intent(inout) :: data2 real, intent(in) :: yp1, ypn real, dimension(size(grid1)) :: y2, u real :: sig, p, qn, un, h, a ,b integer :: n, m, i, k, klo, khi n = size(grid1(:)) m = size(grid2(:)) do i=2,n if (grid1(i) <= grid1(i-1)) call mpp_error(FATAL, 'grid1 not monotonic') enddo do i=2,m if (grid2(i) <= grid2(i-1)) call mpp_error(FATAL, 'grid2 not monotonic') enddo if (grid1(1) > grid2(1) ) call mpp_error(FATAL, 'grid2 lies outside grid1') if (grid1(n) < grid2(m) ) call mpp_error(FATAL, 'grid2 lies outside grid1') if (yp1 >.99e30) then y2(1)=0. u(1)=0. else y2(1)=-0.5 u(1)=(3./(grid1(2)-grid1(1)))*((data1(2)-data1(1))/(grid1(2)-grid1(1))-yp1) endif do i=2,n-1 sig=(grid1(i)-grid1(i-1))/(grid1(i+1)-grid1(i-1)) p=sig*y2(i-1)+2. y2(i)=(sig-1.)/p u(i)=(6.*((data1(i+1)-data1(i))/(grid1(i+1)-grid1(i))-(data1(i)-data1(i-1)) & /(grid1(i)-grid1(i-1)))/(grid1(i+1)-grid1(i-1))-sig*u(i-1))/p enddo if (ypn > .99e30) then qn=0. un=0. else qn=0.5 un=(3./(grid1(n)-grid1(n-1)))*(ypn-(data1(n)-data1(n-1))/(grid1(n)-grid1(n-1))) endif y2(n)=(un-qn*u(n-1))/(qn*y2(n-1)+1.) do k=n-1,1,-1 y2(k)=y2(k)*y2(k+1)+u(k) enddo do k = 1, m n = nearest_index(grid2(k),grid1) if (grid1(n) < grid2(k)) then klo = n else if(n==1) then klo = n else klo = n -1 endif endif khi = klo+1 h = grid1(khi)-grid1(klo) a = (grid1(khi) - grid2(k))/h b = (grid2(k) - grid1(klo))/h data2(k) = a*data1(klo) + b*data1(khi)+ ((a**3-a)*y2(klo) + (b**3-b)*y2(khi))*(h**2)/6 enddo end subroutine interp_1d_cubic_spline !################################################################### subroutine interp_1d_1d(grid1,grid2,data1,data2, method, yp1, yp2) real, dimension(:), intent(in) :: grid1, data1, grid2 real, dimension(:), intent(inout) :: data2 character(len=*), optional, intent(in) :: method real, optional, intent(in) :: yp1, yp2 real :: y1, y2 character(len=32) :: interp_method integer :: k2, ks, ke k2 = size(grid2(:)) interp_method = "linear" if(present(method)) interp_method = method y1 = 1.0e30 if(present(yp1)) y1 = yp1 y2 = 1.0e30 if(present(yp2)) y2 = yp2 call find_index(grid1, grid2(1), grid2(k2), ks, ke) select case(trim(interp_method)) case("linear") call interp_1d_linear(grid1(ks:ke),grid2,data1(ks:ke),data2) case("cubic_spline") call interp_1d_cubic_spline(grid1(ks:ke),grid2,data1(ks:ke),data2, y1, y2) case default call mpp_error(FATAL,"axis_utils: interp_method should be linear or cubic_spline") end select return end subroutine interp_1d_1d !################################################################### subroutine interp_1d_2d(grid1,grid2,data1,data2) real, dimension(:,:), intent(in) :: grid1, data1, grid2 real, dimension(:,:), intent(inout) :: data2 integer :: n1, n2, i, n, k2, ks, ke real :: w n1 = size(grid1,1) n2 = size(grid2,1) k2 = size(grid2,2) if (n1 /= n2) call mpp_error(FATAL,'grid size mismatch') do n=1,n1 call find_index(grid1(n,:), grid2(n,1), grid2(n,k2), ks, ke) call interp_1d_linear(grid1(n,ks:ke),grid2(n,:),data1(n,ks:ke),data2(n,:)) enddo return end subroutine interp_1d_2d !################################################################### subroutine interp_1d_3d(grid1,grid2,data1,data2, method, yp1, yp2) real, dimension(:,:,:), intent(in) :: grid1, data1, grid2 real, dimension(:,:,:), intent(inout) :: data2 character(len=*), optional, intent(in) :: method real, optional, intent(in) :: yp1, yp2 integer :: n1, n2, m1, m2, k2, i, n, m real :: w, y1, y2 character(len=32) :: interp_method integer :: ks, ke n1 = size(grid1,1) n2 = size(grid2,1) m1 = size(grid1,2) m2 = size(grid2,2) k2 = size(grid2,3) interp_method = "linear" if(present(method)) interp_method = method y1 = 1.0e30 if(present(yp1)) y1 = yp1 y2 = 1.0e30 if(present(yp2)) y2 = yp2 if (n1 /= n2 .or. m1 /= m2) call mpp_error(FATAL,'grid size mismatch') select case(trim(interp_method)) case("linear") do m=1,m1 do n=1,n1 call find_index(grid1(n,m,:), grid2(n,m,1), grid2(n,m,k2), ks, ke) call interp_1d_linear(grid1(n,m,ks:ke),grid2(n,m,:),data1(n,m,ks:ke),data2(n,m,:)) enddo enddo case("cubic_spline") do m=1,m1 do n=1,n1 call find_index(grid1(n,m,:), grid2(n,m,1), grid2(n,m,k2), ks, ke) call interp_1d_cubic_spline(grid1(n,m,ks:ke),grid2(n,m,:), data1(n,m,ks:ke),data2(n,m,:), y1, y2) enddo enddo case default call mpp_error(FATAL,"axis_utils: interp_method should be linear or cubic_spline") end select return end subroutine interp_1d_3d !##################################################################### subroutine find_index(grid1, xs, xe, ks, ke) real, dimension(:), intent(in) :: grid1 real, intent(in) :: xs, xe integer, intent(out) :: ks, ke integer :: k, nk nk = size(grid1(:)) ks = 0; ke = 0 do k = 1, nk-1 if(grid1(k) <= xs .and. grid1(k+1) > xs ) then ks = k exit endif enddo do k = nk, 2, -1 if(grid1(k) >= xe .and. grid1(k-1) < xe ) then ke = k exit endif enddo if(ks == 0 ) call mpp_error(FATAL,' xs locate outside of grid1') if(ke == 0 ) call mpp_error(FATAL,' xe locate outside of grid1') end subroutine find_index end module axis_utils_mod #ifdef test_axis_utils program test use fms_mod, only : fms_init, file_exist, open_namelist_file, check_nml_error use fms_mod, only : close_file use mpp_mod, only : mpp_error, FATAL, stdout use axis_utils_mod, only: interp_1d implicit none integer, parameter :: maxsize = 100 integer :: n_src = 0 integer :: n_dst = 0 real, dimension(MAXSIZE) :: grid_src = 0 real, dimension(MAXSIZE) :: grid_dst = 0 real, dimension(MAXSIZE) :: data_src = 0 namelist / test_axis_utils_nml / n_src, n_dst, grid_src, grid_dst, data_src real, allocatable :: data_dst(:) integer :: unit, ierr, io call fms_init() !--- default option of data n_src = 31 n_dst = 40 grid_src(1:n_src) = (/ -63.6711465476916, -63.6711455476916, 166.564180735096, 401.25299580552, & 641.056493022762, 886.219516665347, 1137.35352761133, 1394.4936854079, & 1657.17893448689, 1925.64572676068, 2200.13183483549, 2480.9124139255, & 2768.35396680912, 3062.86513953019, 3675.47369643284, 4325.10564183322, & 5020.19039479527, 5769.85432323481, 6584.25101514851, 7475.94655633703, & 8462.01951335773, 9568.28246037887, 10178.3869413515, 10834.1425668942, & 11543.5265942777, 12317.3907407535, 13170.4562394288, 14125.6466646843, & 15225.8720618086, 16554.7859690842, 19697.1334102613 /) grid_dst(1:n_dst) = (/ 1002.9522552602, 1077.51144617887, 1163.37842788755, 1264.19848463606, & 1382.57557953916, 1521.56713587855, 1684.76300370633, 1876.37817787584, & 2101.36166220498, 2365.52429149707, 2675.68881278444, 3039.86610206727, & 3467.4620678435, 3969.52058529847, 4553.81573511231, 5159.54844211827, & 5765.28114912423, 6371.01385613019, 6976.74656313614, 7582.4792701421, & 8188.21197714806, 8793.94468415402, 9399.67739115997, 10005.4100981659, & 10611.1428051719, 11216.8755121778, 11822.6082191838, 12428.3409261898, & 13034.0736331957, 13639.8063402017, 14245.5390472076, 14851.2717542136, & 15457.0044612196, 16062.7371682255, 16668.4698752315, 17274.2025822374, & 17879.9352892434, 18485.6679962493, 19091.4007032553, 19697.1334102613 /) data_src(1:n_src) = (/ 309.895999643929, 309.991081541887, 309.971074746584, 310.873654697145, & 311.946530606618, 312.862249229647, 314.821236806913, 315.001269608758, & 315.092410930288, 315.19010999336, 315.122964496815, 315.057882573487, & 314.998796850493, 314.984586411292, 315.782246062002, 318.142544345795, & 321.553905292867, 325.247730854554, 329.151282227113, 332.835673638378, & 336.810414210932, 341.64530983048, 344.155248759994, 346.650476976385, & 349.106430095269, 351.915323032738, 354.709396583792, 359.68904432446, & 371.054289820675, 395.098187506342, 446.150726850039 /) !---reading namelist if(file_exist('input.nml')) then unit = open_namelist_file() ierr=1 do while (ierr /= 0) read (unit, nml=test_axis_utils_nml, iostat=io, end=10) ierr = check_nml_error(io,'test_axis_utils_nml') ! also initializes nml error codes enddo 10 call close_file(unit) endif if(n_src >MAXSIZE) call mpp_error(FATAL, 'test_axis_utils: nml n_src is greater than MAXSIZE') if(n_dst >MAXSIZE) call mpp_error(FATAL, 'test_axis_utils: nml n_dst is greater than MAXSIZE') allocate(data_dst(n_dst) ) !--- write out data unit = stdout() write(unit,*)' the source grid is ', grid_src(1:n_src) write(unit,*)' the destination grid is ', grid_dst(1:n_dst) write(unit,*)' the source data is ', data_src(1:n_src) call interp_1d(grid_src(1:n_src), grid_dst(1:n_dst), data_src(1:n_src), data_dst, "linear") write(unit,*)' the destination data using linear interpolation is ', data_dst(1:n_dst) call interp_1d(grid_src(1:n_src), grid_dst(1:n_dst), data_src(1:n_src), data_dst, "cubic_spline") write(unit,*)' the destination data using cublic spline interpolation is ', data_dst(1:n_dst) end program test #endif /* test_axis_utils */