! +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ! + + ! + glint_interp.f90 - part of the Glimmer-CISM ice model + ! + + ! +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ! ! Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010 ! Glimmer-CISM contributors - see AUTHORS file for list of contributors ! ! This file is part of Glimmer-CISM. ! ! Glimmer-CISM is free software: you can redistribute it and/or modify ! it under 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. ! ! Glimmer-CISM 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 Glimmer-CISM. If not, see . ! ! Glimmer-CISM is hosted on BerliOS.de: ! https://developer.berlios.de/projects/glimmer-cism/ ! ! +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #ifdef HAVE_CONFIG_H #include "config.inc" #endif module glint_interp !*FD Downscaling and upscaling routines for use in GLIMMER. use glimmer_global use glimmer_map_types use glint_mpinterp use glimmer_paramets, only: stdout, itest, jtest, jjtest, itest_local, & jtest_local, GLC_DEBUG implicit none type downscale !*FD Derived type containing indexing !*FD information for downscaling. This type was !*FD included for speed. Four of the arrays contained in it !*FD are arrays of the indices of the corners !*FD of the global grid-boxes within which the given !*FD local grid point lies. real(rk),dimension(:,:),pointer :: llats => null() !*FD The latitude of each point in x-y space. real(rk),dimension(:,:),pointer :: llons => null() !*FD The longitude of each point in x-y space. integer, dimension(:,:,:),pointer :: xloc => null() !*FD The x-locations of the corner points of the !*FD interpolation domain. integer, dimension(:,:,:),pointer :: yloc => null() !*FD The y-locations of the corner points of the !*FD interpolation domain. integer, dimension(:,:), pointer :: xin => null() !*FD x-locations of global cell the point is in integer, dimension(:,:), pointer :: yin => null() !*FD y-locations of global cell the point is in real(rk),dimension(:,:), pointer :: xfrac => null() real(rk),dimension(:,:), pointer :: yfrac => null() real(rk),dimension(:,:),pointer :: sintheta => NULL() !*FD sines of grid angle relative to north. real(rk),dimension(:,:),pointer :: costheta => NULL() !*FD coses of grid angle relative to north. type(mpinterp) :: mpint !*FD Parameters for mean-preserving interpolation logical :: use_mpint = .false. !*FD set true if we're using mean-preserving interpolation integer,dimension(:,:),pointer :: lmask => null() !*FD mask = 1 where downscaling is valid !*FD mask = 0 elsewhere end type downscale type upscale !*FD Derived type containing indexing information !*FD for upscaling by areal averaging. integer, dimension(:,:),pointer :: gboxx => null() !*FD $x$-indicies of global grid-box !*FD containing given local grid-box. integer, dimension(:,:),pointer :: gboxy => null() !*FD $y$-indicies of global grid-box !*FD containing given local grid-box. integer, dimension(:,:),pointer :: gboxn => null() !*FD Number of local grid-boxes !*FD contained in each global box. logical :: set = .false. !*FD Set if the type has been initialised. end type upscale interface mean_to_global module procedure mean_to_global_sp,mean_to_global_dp end interface contains subroutine new_downscale(downs,proj,ggrid,lgrid,mpint) use glint_global_grid use glimmer_map_trans use glimmer_map_types use glimmer_coordinates !*FD Initialises a downscale variable, !*FD according to given projected and global grids ! Arguments type(downscale),intent(out) :: downs !*FD Downscaling variable to be set type(glimmap_proj),intent(in) :: proj !*FD Projection to use type(global_grid),intent(in) :: ggrid !*FD Global grid to use type(coordsystem_type),intent(in) :: lgrid !*FD Local (ice) grid logical,optional :: mpint !*FD Set true if we're using mean-preserving interp ! Internal variables real(rk) :: llat,llon integer :: i,j type(upscale) :: ups integer,dimension(:,:),pointer :: upsm upsm => null() ! Allocate arrays allocate(downs%xloc(lgrid%size%pt(1),lgrid%size%pt(2),4)) allocate(downs%yloc(lgrid%size%pt(1),lgrid%size%pt(2),4)) call coordsystem_allocate(lgrid,downs%xfrac) call coordsystem_allocate(lgrid,downs%yfrac) call coordsystem_allocate(lgrid,downs%llons) call coordsystem_allocate(lgrid,downs%llats) call coordsystem_allocate(lgrid,downs%sintheta) call coordsystem_allocate(lgrid,downs%costheta) call coordsystem_allocate(lgrid,downs%xin) call coordsystem_allocate(lgrid,downs%yin) call coordsystem_allocate(lgrid,upsm) call coordsystem_allocate(lgrid,downs%lmask) ! index local boxes call index_local_boxes(downs%xloc, downs%yloc, & downs%xfrac, downs%yfrac, & ggrid, proj, lgrid, & downs%lmask ) ! Calculate grid angle call calc_grid_angle(downs,proj,lgrid) ! Find lats and lons do i=1,lgrid%size%pt(1) do j=1,lgrid%size%pt(2) call glimmap_xy_to_ll(llon,llat,real(i,rk),real(j,rk),proj,lgrid) downs%llons(i,j)=llon downs%llats(i,j)=llat end do end do ! Initialise mean-preserving interpolation if necessary if (present(mpint)) then if (mpint) then call new_mpinterp(downs%mpint,ggrid) downs%use_mpint = .true. end if end if call new_upscale(ups,ggrid,proj,upsm,lgrid) downs%xin = ups%gboxx downs%yin = ups%gboxy deallocate(upsm) end subroutine new_downscale !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine interp_wind_to_local(lgrid,zonwind,merwind,downs,xwind,ywind) !*FD Interpolates a global wind field !*FD (or any vector field) onto a given projected grid. use glimmer_utils use glimmer_coordinates ! Argument declarations type(coordsystem_type), intent(in) :: lgrid !*FD Target grid real(rk),dimension(:,:),intent(in) :: zonwind !*FD Zonal component (input) real(rk),dimension(:,:),intent(in) :: merwind !*FD Meridional components (input) type(downscale), intent(inout) :: downs !*FD Downscaling parameters real(rk),dimension(:,:),intent(out) :: xwind,ywind !*FD x and y components on the projected grid (output) ! Declare two temporary arrays to hold the interpolated zonal and meridional winds real(dp),dimension(size(xwind,1),size(xwind,2)) :: tempzw,tempmw ! Check input arrays are conformal to one another call check_conformal(zonwind,merwind,'interp_wind 1') call check_conformal(xwind,ywind,'interp_wind 2') ! Interpolate onto the projected grid call interp_to_local(lgrid,zonwind,downs,localdp=tempzw) call interp_to_local(lgrid,merwind,downs,localdp=tempmw) ! Apply rotation xwind=tempzw*downs%costheta-tempmw*downs%sintheta ywind=tempzw*downs%sintheta+tempmw*downs%costheta end subroutine interp_wind_to_local !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine interp_to_local (lgrid, global, downs, & localsp, localdp, localrk, & global_fn, z_constrain, & gmask, maskval) !*FD Interpolate a global scalar field onto a projected grid. !*FD !*FD This uses a simple bilinear interpolation, which assumes !*FD that the global grid boxes are rectangular - i.e. it works !*FD in lat-lon space. !*FD !*FD Either localsp or localdp must be present (or both), depending !*FD which precision output is required. ! Cell indexing for (xloc,yloc) is as follows: ! ! 4---------3 ! | | ! | | ! | | ! 1---------2 ! use glimmer_utils use glimmer_coordinates use glimmer_log ! Argument declarations type(coordsystem_type), intent(in) :: lgrid !*FD Local grid real(rk), dimension(:,:),intent(in) :: global !*FD Global field (input) type(downscale), intent(inout) :: downs !*FD Downscaling parameters real(sp),dimension(:,:), intent(out),optional :: localsp !*FD Local field on projected grid (output) sp real(dp),dimension(:,:), intent(out),optional :: localdp !*FD Local field on projected grid (output) dp real(rk),dimension(:,:), intent(out),optional :: localrk !*FD Local field on projected grid (output) rk real(sp),optional,external :: global_fn !*FD Function returning values in global field. This !*FD may be used as an alternative to passing the !*FD whole array in \texttt{global} if, for instance the !*FD data-set is in a large file, being accessed point by point. !*FD In these circumstances, \texttt{global} !*FD may be of any size, and its contents are irrelevant. logical,optional :: z_constrain integer, dimension(:,:), intent(in),optional :: gmask !*FD = 1 where global data are valid, else = 0 real(dp), intent(in), optional :: maskval !*FD Value to write for masked-out cells ! Local variable declarations integer :: i,j ! Counter variables for main loop real(rk),dimension(4) :: f ! Temporary array holding the four points in the ! interpolation domain. real(rk), dimension(size(global,1),size(global,2)) :: g_loc logical, dimension(size(global,1),size(global,2)) :: zeros logical :: zc integer :: x1, x2, x3, x4 integer :: y1, y2, y3, y4 if (present(z_constrain)) then zc=z_constrain else zc=.false. end if ! check we have one output at least... if (.not.(present(localsp).or.present(localdp).or.present(localrk))) then call write_log('Interp_to_local has no output',GM_WARNING,__FILE__,__LINE__) endif ! Do stuff for mean-preserving interpolation if (downs%use_mpint) then call mean_preserve_interp(downs%mpint,global,g_loc,zeros) end if ! Main interpolation loop do i=1,lgrid%size%pt(1) do j=1,lgrid%size%pt(2) ! Compile the temporary array f from adjacent points !lipscomb - TO DO - This could be handled more efficiently by precomputing arrays that specify ! which neighbor gridcell supplies values in each masked-out global gridcell. if (present(gmask) .and. present(maskval)) then if (downs%lmask(i,j) == 0) then f(1) = maskval f(2) = maskval f(3) = maskval f(4) = maskval else x1 = downs%xloc(i,j,1); y1 = downs%yloc(i,j,1) x2 = downs%xloc(i,j,2); y2 = downs%yloc(i,j,2) x3 = downs%xloc(i,j,3); y3 = downs%yloc(i,j,3) x4 = downs%xloc(i,j,4); y4 = downs%yloc(i,j,4) ! if a gridcell is masked out, try to assign a value from a ! neighbor that is not masked out if (gmask(x1,y1) /= 0) then f(1) = global(x1,y1) elseif (gmask(x2,y2) /= 0) then f(1) = global(x2,y2) elseif (gmask(x4,y4) /= 0) then f(1) = global(x4,y4) elseif (gmask(x3,y3) /= 0) then f(1) = global(x3,y3) else f(1) = maskval endif if (gmask(x2,y2) /= 0) then f(2) = global(x2,y2) elseif (gmask(x1,y1) /= 0) then f(2) = global(x1,y1) elseif (gmask(x3,y3) /= 0) then f(2) = global(x3,y3) elseif (gmask(x4,y4) /= 0) then f(2) = global(x4,y4) else f(2) = maskval endif if (gmask(x3,y3) /= 0) then f(3) = global(x3,y3) elseif (gmask(x4,y4) /= 0) then f(3) = global(x4,y4) elseif (gmask(x2,y2) /= 0) then f(3) = global(x2,y2) elseif (gmask(x1,y1) /= 0) then f(3) = global(x1,y1) else f(3) = maskval endif if (gmask(x4,y4) /= 0) then f(4) = global(x4,y4) elseif (gmask(x3,y3) /= 0) then f(4) = global(x3,y3) elseif (gmask(x1,y1) /= 0) then f(4) = global(x1,y1) elseif (gmask(x2,y2) /= 0) then f(4) = global(x2,y2) else f(4) = maskval endif endif ! lmask = 0 else ! gmask and maskval not present if (present(global_fn)) then f(1)=global_fn(downs%xloc(i,j,1),downs%yloc(i,j,1)) f(2)=global_fn(downs%xloc(i,j,2),downs%yloc(i,j,2)) f(3)=global_fn(downs%xloc(i,j,3),downs%yloc(i,j,3)) f(4)=global_fn(downs%xloc(i,j,4),downs%yloc(i,j,4)) else if (downs%use_mpint) then f(1)=g_loc(downs%xloc(i,j,1),downs%yloc(i,j,1)) f(2)=g_loc(downs%xloc(i,j,2),downs%yloc(i,j,2)) f(3)=g_loc(downs%xloc(i,j,3),downs%yloc(i,j,3)) f(4)=g_loc(downs%xloc(i,j,4),downs%yloc(i,j,4)) else f(1)=global(downs%xloc(i,j,1),downs%yloc(i,j,1)) f(2)=global(downs%xloc(i,j,2),downs%yloc(i,j,2)) f(3)=global(downs%xloc(i,j,3),downs%yloc(i,j,3)) f(4)=global(downs%xloc(i,j,4),downs%yloc(i,j,4)) end if end if endif ! gmask and maskval present ! Apply the bilinear interpolation if (zc.and.zeros(downs%xin(i,j),downs%yin(i,j)).and.downs%use_mpint) then if (present(localsp)) localsp(i,j)=0.0_sp if (present(localdp)) localdp(i,j)=0.0_dp if (present(localrk)) localrk(i,j)=0.0_rk else if (present(localsp)) localsp(i,j)=bilinear_interp(downs%xfrac(i,j),downs%yfrac(i,j),f) if (present(localdp)) localdp(i,j)=bilinear_interp(downs%xfrac(i,j),downs%yfrac(i,j),f) if (present(localrk)) localrk(i,j)=bilinear_interp(downs%xfrac(i,j),downs%yfrac(i,j),f) end if enddo enddo end subroutine interp_to_local !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine mean_to_local(proj,lgrid,ggrid,global,localsp,localdp,global_fn) !*FD Average a high-resolution global field onto the projected grid !*FD This assumes that the global field is sufficiently high-resolution !*FD compared with the local grid - it just averages the points contained !*FD in each local grid-box. use glimmer_map_types use glimmer_map_trans use glimmer_coordinates use glimmer_utils use glimmer_log use glint_global_grid ! Argument declarations type(glimmap_proj), intent(in) :: proj !*FD Target map projection type(coordsystem_type), intent(in) :: lgrid !*FD Local grid information type(global_grid), intent(in) :: ggrid !*FD Global grid information real(rk),dimension(:,:), intent(in) :: global !*FD Global field (input) real(sp),dimension(:,:),optional,intent(out) :: localsp !*FD Local field on projected grid (output) sp real(dp),dimension(:,:),optional,intent(out) :: localdp !*FD Local field on projected grid (output) dp real(sp),optional, external :: global_fn !*FD Function returning values in global field. This !*FD may be used as an alternative to passing the !*FD whole array in \texttt{global} if, for instance the !*FD data-set is in a large file, being accessed point by point. !*FD In these circumstances, \texttt{global} !*FD may be of any size, and its contents are irrelevant. integer :: i,j,xbox,ybox real(rk) :: lat,lon,x,y real(dp),dimension(lgrid%size%pt(1),lgrid%size%pt(2)) :: temp_out real(rk),dimension(lgrid%size%pt(1),lgrid%size%pt(2)) :: mean_count if (.not.present(global_fn)) then if ((lgrid%size%pt(1)/=size(ggrid%lons)).or.(lgrid%size%pt(2)/=size(ggrid%lats))) then call write_log('Size mismatch in interp_to_local',GM_FATAL,__FILE__,__LINE__) end if end if ! check we have one output at least... if (.not.(present(localsp).or.present(localdp))) then call write_log('mean_to_local has no output',GM_WARNING,__FILE__,__LINE__) endif ! Zero some things mean_count=0 temp_out=0.0 ! Loop over all global points do i=1,lgrid%size%pt(1) lon=ggrid%lons(i) do j=1,lgrid%size%pt(2) ! Find location in local coordinates lat=ggrid%lats(j) ! (Have already found lat above) call glimmap_ll_to_xy(lon,lat,x,y,proj,lgrid) xbox=nint(x) ybox=nint(y) ! Add to appropriate location and update count if (xbox.ge.1.and.xbox.le.lgrid%size%pt(1).and. & ybox.ge.1.and.ybox.le.lgrid%size%pt(2)) then if (present(global_fn)) then temp_out(xbox,ybox)=temp_out(xbox,ybox)+global_fn(i,j)*ggrid%box_areas(xbox,ybox) else temp_out(xbox,ybox)=temp_out(xbox,ybox)+global(i,j)*ggrid%box_areas(xbox,ybox) end if mean_count(xbox,ybox)=mean_count(xbox,ybox)+ggrid%box_areas(xbox,ybox) end if end do end do ! Divide by number of contributing points and copy to output if (present(localsp)) localsp=temp_out/real(mean_count,dp) if (present(localdp)) localdp=temp_out/real(mean_count,dp) end subroutine mean_to_local !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine pointwise_to_global(proj,lgrid,local,lons,lats,global) !*FD Upscale to global domain by !*FD pointwise sampling. !*FD !*FD Note that this is the mathematically inverse process of the !*FD \texttt{interp\_to\_local} routine. use glimmer_coordinates use glimmer_map_trans ! Arguments type(glimmap_proj), intent(in) :: proj !*FD Projection to use type(coordsystem_type), intent(in) :: lgrid !*FD Local grid real(rk),dimension(:,:),intent(in) :: local !*FD Local field (input) real(rk),dimension(:,:),intent(out) :: global !*FD Global field (output) real(rk),dimension(:), intent(in) :: lats !*FD Latitudes of grid-points (degrees) real(rk),dimension(:), intent(in) :: lons !*FD Longitudes of grid-points (degrees) ! Internal variables real(rk),dimension(2,2) :: f integer :: nxg,nyg,nxl,nyl,i,j,xx,yy real(rk) :: x,y real(rk),dimension(size(local,1),size(local,2)) :: tempmask nxg=size(global,1) ; nyg=size(global,2) nxl=size(local,1) ; nyl=size(local,2) do i=1,nxg do j=1,nyg call glimmap_ll_to_xy(lons(i),lats(j),x,y,proj,lgrid) xx=int(x) ; yy=int(y) if (nint(x)<=1.or.nint(x)>nxl-1.or.nint(y)<=1.or.nint(y)>nyl-1) then global(i,j)=0.0 else f=local(xx:xx+1,yy:yy+1)*tempmask(xx:xx+1,yy:yy+1) global(i,j)=bilinear_interp((x-real(xx))/real(1.0,rk),(y-real(yy))/real(1.0,rk),f) endif enddo enddo end subroutine pointwise_to_global !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine mean_to_global_sp(ups,local,global,mask) !*FD Upscale to global domain by !*FD areal averaging. !*FD !*FD Note that: !*FD \begin{itemize} !*FD \item \texttt{gboxx} and \texttt{gboxy} are the same size as \texttt{local} !*FD \item \texttt{gboxn} is the same size as \texttt{global} !*FD \item This method is \emph{not} the mathematical inverse of the !*FD \texttt{interp\_to\_local} routine. !*FD \end{itemize} ! Arguments type(upscale), intent(in) :: ups !*FD Upscaling indexing data. real(sp),dimension(:,:),intent(in) :: local !*FD Data on projected grid (input). real(rk),dimension(:,:),intent(out) :: global !*FD Data on global grid (output). integer, dimension(:,:),intent(in),optional :: mask !*FD Mask for upscaling ! Internal variables integer :: nxl,nyl,i,j real(rk),dimension(size(local,1),size(local,2)) :: tempmask ! Beginning of code nxl=size(local,1) ; nyl=size(local,2) global=0.0 if (present(mask)) then tempmask=mask else tempmask=1 endif do i=1,nxl do j=1,nyl global(ups%gboxx(i,j),ups%gboxy(i,j))= & global(ups%gboxx(i,j),ups%gboxy(i,j))+local(i,j)*tempmask(i,j) enddo enddo where (ups%gboxn.ne.0) global=global/ups%gboxn elsewhere global=0.0 endwhere end subroutine mean_to_global_sp !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine mean_to_global_dp(ups,local,global,mask) !*FD Upscale to global domain by !*FD areal averaging. !*FD !*FD Note that: !*FD \begin{itemize} !*FD \item \texttt{gboxx} and \texttt{gboxy} are the same size as \texttt{local} !*FD \item \texttt{gboxn} is the same size as \texttt{global} !*FD \item This method is \emph{not} the mathematical inverse of the !*FD \texttt{interp\_to\_local} routine. !*FD \end{itemize} ! Arguments type(upscale), intent(in) :: ups !*FD Upscaling indexing data. real(dp),dimension(:,:),intent(in) :: local !*FD Data on projected grid (input). real(rk),dimension(:,:),intent(out) :: global !*FD Data on global grid (output). integer, dimension(:,:),intent(in),optional :: mask !*FD Mask for upscaling ! Internal variables integer :: nxl,nyl,i,j real(rk),dimension(size(local,1),size(local,2)) :: tempmask ! Beginning of code nxl=size(local,1) ; nyl=size(local,2) global=0.0 if (present(mask)) then tempmask=mask else tempmask=1 endif do i=1,nxl do j=1,nyl global(ups%gboxx(i,j),ups%gboxy(i,j))= & global(ups%gboxx(i,j),ups%gboxy(i,j))+local(i,j)*tempmask(i,j) enddo enddo where (ups%gboxn.ne.0) global=global/ups%gboxn elsewhere global=0.0 endwhere end subroutine mean_to_global_dp !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine mean_to_global_mec(ups, & nxl, nyl, & nxg, nyg, & nec, topomax, & local, global, & ltopo, mask) ! Upscale from the local domain to a global domain with multiple elevation classes ! by areal averaging. ! ! This subroutine is adapted from subroutine mean_to_global in GLIMMER. ! The difference is that local topography is upscaled to multiple elevation classes ! in each global grid cell. ! ! Note: This method is not the inverse of the interp_to_local routine. ! Also note that each local grid cell is weighted equally. ! In the future we may want to use the CESM coupler for upscaling. use glimmer_log !jw check use glimmer_paramets, only: itest, jtest, jjtest, itest_local, jtest_local, stdout ! Arguments type(upscale), intent(in) :: ups ! upscaling indexing data integer, intent(in) :: nxl,nyl ! local grid dimensions integer, intent(in) :: nxg,nyg ! global grid dimensions integer, intent(in) :: nec ! number of elevation classes real(dp),dimension(0:nec),intent(in) :: topomax ! max elevation in each class real(dp),dimension(nxl,nyl), intent(in) :: local ! data on local grid real(dp),dimension(nxg,nyg,nec),intent(out) :: global ! data on global grid real(dp),dimension(nxl,nyl), intent(in) :: ltopo ! surface elevation on local grid (m) integer, dimension(nxl,nyl),intent(in),optional :: mask ! mask for upscaling ! Internal variables integer :: & i, j, n, &! indices ig, jg ! indices integer, dimension(nxl,nyl) :: & tempmask, &! temporary mask gboxec ! elevation class into which local data is upscaled integer, dimension(nxg,nyg,nec) :: & gnumloc ! no. of local cells within each global cell in each elevation class integer :: il, jl real(dp) :: lsum, gsum if (present(mask)) then tempmask(:,:) = mask(:,:) else tempmask(:,:) = 1 endif ! Compute global elevation class for each local grid cell ! Also compute number of local cells within each global cell in each elevation class gboxec(:,:) = 0 gnumloc(:,:,:) = 0 do n = 1, nec do j = 1, nyl do i = 1, nxl if (ltopo(i,j) >= topomax(n-1) .and. ltopo(i,j) < topomax(n)) then gboxec(i,j) = n if (tempmask(i,j)==1) then ig = ups%gboxx(i,j) jg = ups%gboxy(i,j) gnumloc(ig,jg,n) = gnumloc(ig,jg,n) + 1 endif endif enddo enddo enddo global(:,:,:) = 0._dp do j = 1, nyl do i = 1, nxl ig = ups%gboxx(i,j) jg = ups%gboxy(i,j) n = gboxec(i,j) if (n==0) then if (GLC_DEBUG) then write(stdout,*) 'Upscaling error: local topography out of bounds' write(stdout,*) 'i, j, ltopo:', i, j, ltopo(i,j) write(stdout,*) 'topomax =', topomax(:) endif call write_log('Upscaling error: local topography out of bounds', & GM_FATAL,__FILE__,__LINE__) endif global(ig,jg,n) = global(ig,jg,n) + local(i,j)*tempmask(i,j) enddo enddo do n = 1, nec do j = 1, nyg do i = 1, nxg if (gnumloc(i,j,n) /= 0) then global(i,j,n) = global(i,j,n) / gnumloc(i,j,n) else global(i,j,n) = 0._dp endif enddo enddo enddo ! conservation check lsum = 0._dp do j = 1, nyl do i = 1, nxl lsum = lsum + local(i,j)*tempmask(i,j) enddo enddo gsum = 0._dp do n = 1, nec do j = 1, nyg do i = 1, nxg gsum = gsum + global(i,j,n)*gnumloc(i,j,n) enddo enddo enddo if (abs(lsum) > 1.e-10_dp) then if (abs(gsum-lsum)/abs(lsum) > 1.e-10_dp) then if (GLC_DEBUG) then write(stdout,*) 'local and global sums disagree' write (stdout,*) 'lsum, gsum =', lsum, gsum endif call write_log('Upscaling error: local and glocal sums disagree', & GM_FATAL,__FILE__,__LINE__) endif else ! lsum is close to zero if (abs(gsum-lsum) > 1.e-10_dp) then if (GLC_DEBUG) then write(stdout,*) 'local and global sums disagree' write (stdout,*) 'lsum, gsum =', lsum, gsum endif call write_log('Upscaling error: local and glocal sums disagree', & GM_FATAL,__FILE__,__LINE__) endif endif end subroutine mean_to_global_mec !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ real(rk) function bilinear_interp(xp,yp,f) !*FD Performs bilinear interpolation !*FD in a rectangular domain. Note that the bilinear interpolation formula is: !*FD \[f_{\mathtt{x},\mathtt{y}}=(1-X')(1-Y')f_{1}+X'(1-Y')f_{2}+X'Y'f_{3}+(1-X')Y'f_{4}\] !*FD where $X'$ and $Y'$ are the fractional displacements of the target point within the domain. !*RV The value of \texttt{f} at \texttt{x,y} ! Argument declarations real(rk), intent(in) :: xp !*FD The fractional $x$-displacement of the target. real(rk), intent(in) :: yp !*FD The fractional $y$-displacement of the target. real(rk),dimension(4),intent(in) :: f !*FD The interpolation domain; !*FD i.e. the four points surrounding the !*FD target, presented anticlockwise from bottom- !*FD left ! Apply bilinear interpolation formula bilinear_interp=(1-xp)*(1-yp)*f(1)+xp*(1-yp)*f(2)+xp*yp*f(3)+(1-xp)*yp*f(4) end function bilinear_interp !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine find_ll_index(il,jl,lon,lat,lons,lats) !*FD Find the global gridpoint at the first corner of the box surrounding !*FD a given location in lat-lon space. use glimmer_utils ! Arguments real(rk), intent(in) :: lon !*FD Longitude of location to be indexed (input) real(rk), intent(in) :: lat !*FD Latitude of location to be indexed (input) real(rk),dimension(:),intent(in) :: lats !*FD Latitudes of global grid points real(rk),dimension(:),intent(in) :: lons !*FD Longitudes of global grid points integer, intent(out) :: il !*FD $x$-gridpoint index (output) integer, intent(out) :: jl !*FD $y$-gridpoint index (output) ! Internal variables integer :: nx,ny,i nx=size(lons) ; ny=size(lats) il=nx do i=1,nx-1 if (lon_between(lons(i),lons(i+1),lon)) then il=i exit endif enddo if ((lat-90.0)) then jl=ny return endif if ((lat>lats(1)).and.(lat<90.0)) then jl=1 return endif jl=1 do if (lat>lats(jl)) exit jl=jl+1 enddo end subroutine find_ll_index !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine index_local_boxes (xloc, yloc, xfrac, yfrac, ggrid, proj, lgrid, lmask) !*FD Indexes the corners of the !*FD global grid box in which each local grid box sits. use glimmer_utils use glint_global_grid use glimmer_coordinates use glimmer_map_trans ! Arguments integer, dimension(:,:,:),intent(out) :: xloc,yloc !*FD Array of indicies (see \texttt{downscale} type) real(rk),dimension(:,:), intent(out) :: xfrac,yfrac !*FD Fractional off-sets of grid points type(global_grid), intent(in) :: ggrid !*FD Global grid to be used type(glimmap_proj), intent(in) :: proj !*FD Projection to be used type(coordsystem_type), intent(in) :: lgrid !*FD Local grid integer, dimension(:,:), intent(out) :: lmask !*FD Mask of local cells for which interpolation is valid ! Internal variables integer :: i,j,il,jl,temp real(rk) :: ilon,jlat,xa,ya,xb,yb,xc,yc,xd,yd integer :: nx, ny, nxg, nyg, n if (GLC_DEBUG) then nx = lgrid%size%pt(1) ny = lgrid%size%pt(2) nxg = size(ggrid%mask,1) nyg = size(ggrid%mask,2) write(stdout,*) ' ' write(stdout,*) 'nx, ny =', nx, ny write(stdout,*) 'nxg, nyg =', nxg, nyg write(stdout,*) 'Indexing local boxes' endif do i=1,lgrid%size%pt(1) do j=1,lgrid%size%pt(2) ! Find out where point i,j is in lat-lon space call glimmap_xy_to_ll(ilon,jlat,real(i,rk),real(j,rk),proj,lgrid) ! Index that location onto the global grid call find_ll_index(il,jl,ilon,jlat,ggrid%lons,ggrid%lats) xloc(i,j,1)=il ! This is the starting point - we now need to find yloc(i,j,1)=jl ! three other points that enclose the interpolation target if (jlat>ggrid%lats(ggrid%ny)) then ! For all points except on the bottom row xloc(i,j,2)=il+1 yloc(i,j,2)=jl xloc(i,j,3)=il+1 yloc(i,j,3)=jl-1 xloc(i,j,4)=il yloc(i,j,4)=jl-1 call fix_bcs2d(xloc(i,j,2) ,yloc(i,j,2),ggrid%nx,ggrid%ny) call fix_bcs2d(xloc(i,j,3) ,yloc(i,j,3),ggrid%nx,ggrid%ny) call fix_bcs2d(xloc(i,j,4) ,yloc(i,j,4),ggrid%nx,ggrid%ny) if (jl==1) then temp=xloc(i,j,3) xloc(i,j,3)=xloc(i,j,4) xloc(i,j,4)=temp endif else ! The bottom row xloc(i,j,2)=il-1 yloc(i,j,2)=jl xloc(i,j,3)=il-1 yloc(i,j,3)=jl+1 xloc(i,j,4)=il yloc(i,j,4)=jl+1 call fix_bcs2d(xloc(i,j,2) ,yloc(i,j,2),ggrid%nx,ggrid%ny) call fix_bcs2d(xloc(i,j,3) ,yloc(i,j,3),ggrid%nx,ggrid%ny) call fix_bcs2d(xloc(i,j,4) ,yloc(i,j,4),ggrid%nx,ggrid%ny) temp=xloc(i,j,3) xloc(i,j,3)=xloc(i,j,4) xloc(i,j,4)=temp endif ! Now, find out where each of those points is on the projected ! grid, and calculate fractional displacements accordingly call glimmap_ll_to_xy(ggrid%lons(xloc(i,j,1)),ggrid%lats(yloc(i,j,1)),xa,ya,proj,lgrid) call glimmap_ll_to_xy(ggrid%lons(xloc(i,j,2)),ggrid%lats(yloc(i,j,2)),xb,yb,proj,lgrid) call glimmap_ll_to_xy(ggrid%lons(xloc(i,j,3)),ggrid%lats(yloc(i,j,3)),xc,yc,proj,lgrid) call glimmap_ll_to_xy(ggrid%lons(xloc(i,j,4)),ggrid%lats(yloc(i,j,4)),xd,yd,proj,lgrid) call calc_fractional(xfrac(i,j),yfrac(i,j),real(i,rk),real(j,rk), & xa,ya,xb,yb,xc,yc,xd,yd) ! If all four global gridcells surrounding this local gridcell ! are masked out, then mask out the local gridcell if ( (ggrid%mask(xloc(i,j,1),yloc(i,j,1)) == 0) .and. & (ggrid%mask(xloc(i,j,2),yloc(i,j,2)) == 0) .and. & (ggrid%mask(xloc(i,j,3),yloc(i,j,3)) == 0) .and. & (ggrid%mask(xloc(i,j,4),yloc(i,j,4)) == 0) ) then lmask(i,j) = 0 else lmask(i,j) = 1 endif enddo enddo if (GLC_DEBUG) then write(stdout,*) ' ' write(stdout,*) 'Mask in neighborhood of i, j = ', itest_local, jtest_local do j = jtest_local-1, jtest_local+1 write(stdout,*) lmask(itest_local-1:itest_local+1,j) enddo write(stdout,*) ' ' write(stdout,*) 'Global mask near Greenland' do j = 1, 20 write(stdout,150) ggrid%mask(nxg-29:nxg,j) enddo write(stdout,*) ' ' write(stdout,*) 'Local mask' do j = ny, 1, -1 write(stdout,200) lmask(1:nx,j) enddo 100 format(144i2) 150 format(30i2) 200 format(76i2) endif end subroutine index_local_boxes !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine calc_grid_angle(downs,proj,lgrid) !*FD Calculates the angle the projected !*FD grid makes with north at each point and stores the cos !*FD and sin of that angle in the relevant arrays in \texttt{proj}. use glimmer_coordinates use glimmer_map_trans type(downscale),intent(inout) :: downs !*FD The projection to be used type(glimmap_proj),intent(in) :: proj type(coordsystem_type),intent(in) :: lgrid integer :: i,j real(rk) :: latn,lonn,lats,lons,lat,lon,dlat,dlon,temp do i=1,lgrid%size%pt(1) ! Main, central block do j=2,lgrid%size%pt(2)-1 call glimmap_xy_to_ll(lonn,latn,real(i,rk),real(j+1,rk),proj,lgrid) call glimmap_xy_to_ll(lon,lat,real(i,rk),real(j,rk),proj,lgrid) call glimmap_xy_to_ll(lons,lats,real(i,rk),real(j-1,rk),proj,lgrid) dlat=latn-lats dlon=lonn-lons if (dlon<-90) dlon=dlon+360 temp=atan(dlon/dlat) downs%sintheta(i,j)=sin(temp) downs%costheta(i,j)=cos(temp) enddo ! bottom row call glimmap_xy_to_ll(lonn,latn,real(i,rk),real(2,rk),proj,lgrid) call glimmap_xy_to_ll(lon,lat,real(i,rk),real(1,rk),proj,lgrid) dlat=latn-lat dlon=lonn-lon if (dlon<-90) dlon=dlon+360 temp=atan(dlon/dlat) downs%sintheta(i,1)=sin(temp) downs%costheta(i,1)=cos(temp) ! top row call glimmap_xy_to_ll(lon,lat,real(i,rk),real(lgrid%size%pt(2),rk),proj,lgrid) call glimmap_xy_to_ll(lons,lats,real(i,rk),real(lgrid%size%pt(2)-1,rk),proj,lgrid) dlat=lat-lats dlon=lon-lons if (dlon<-90) dlon=dlon+360 temp=atan(dlon/dlat) downs%sintheta(i,lgrid%size%pt(2))=sin(temp) downs%costheta(i,lgrid%size%pt(2))=cos(temp) enddo end subroutine calc_grid_angle !++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine new_upscale(ups,ggrid,proj,mask,lgrid) use glint_global_grid use glimmer_log use glimmer_map_trans use glimmer_coordinates !*FD Compiles an index of which global grid box contains a given !*FD grid box on the projected grid, and sets derived type \texttt{ups} !*FD accordingly. ! Arguments type(upscale), intent(out) :: ups !*FD Upscaling type to be set type(global_grid), intent(in) :: ggrid !*FD Global grid to be used type(glimmap_proj), intent(in) :: proj !*FD Projection being used integer,dimension(:,:),intent(in) :: mask !*FD Upscaling mask to be used type(coordsystem_type),intent(in) :: lgrid !*FD local grid ! Internal variables integer :: i,j,ii,jj,nx,ny,gnx,gny real(rk) :: plon,plat ! Beginning of code if (associated(ups%gboxx)) deallocate(ups%gboxx) if (associated(ups%gboxy)) deallocate(ups%gboxy) if (associated(ups%gboxn)) deallocate(ups%gboxn) allocate(ups%gboxx(lgrid%size%pt(1),lgrid%size%pt(2))) allocate(ups%gboxy(lgrid%size%pt(1),lgrid%size%pt(2))) allocate(ups%gboxn(ggrid%nx,ggrid%ny)) gnx=ggrid%nx ; gny=ggrid%ny nx =lgrid%size%pt(1) ; ny =lgrid%size%pt(2) ups%gboxx=0 ; ups%gboxy=0 do i=1,nx do j=1,ny call glimmap_xy_to_ll(plon,plat,real(i,rk),real(j,rk),proj,lgrid) ii=1 ; jj=1 do ups%gboxx(i,j)=ii if (ii>gnx) then call write_log('global index failure',GM_FATAL,__FILE__,__LINE__) endif if (lon_between(ggrid%lon_bound(ii),ggrid%lon_bound(ii+1),plon)) exit ii=ii+1 enddo jj=1 do ups%gboxy(i,j)=jj if (jj>gny) then call write_log('global index failure',GM_FATAL,__FILE__,__LINE__) endif if ((ggrid%lat_bound(jj)>=plat).and.(plat>ggrid%lat_bound(jj+1))) exit jj=jj+1 enddo enddo enddo ups%gboxn=0 do i=1,nx do j=1,ny ups%gboxn(ups%gboxx(i,j),ups%gboxy(i,j))=ups%gboxn(ups%gboxx(i,j),ups%gboxy(i,j))+mask(i,j) enddo enddo ups%set=.true. end subroutine new_upscale !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ subroutine copy_upscale(in,out) use glimmer_log type(upscale),intent(in) :: in type(upscale),intent(out) :: out if (.not.in%set) then call write_log('Attempt to copy un-initialised upscale type',GM_FATAL,& __FILE__,__LINE__) endif if (associated(out%gboxx)) deallocate(out%gboxx) if (associated(out%gboxy)) deallocate(out%gboxy) if (associated(out%gboxn)) deallocate(out%gboxn) allocate(out%gboxx(size(in%gboxx,1),size(in%gboxx,2))) allocate(out%gboxy(size(in%gboxy,1),size(in%gboxy,2))) allocate(out%gboxn(size(in%gboxn,1),size(in%gboxn,2))) out%gboxx=in%gboxx out%gboxy=in%gboxy out%gboxn=in%gboxn out%set=.true. end subroutine copy_upscale !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ logical function lon_between(a,b,x) !*FD Checks to see whether a !*FD longitudinal coordinate is between two bounds, !*FD taking into account the periodic boundary conditions. !*RV Returns \texttt{.true.} if $\mathtt{x}\geq \mathtt{a}$ and $\mathtt{x}<\mathtt{b}$. ! Arguments real(rk),intent(in) :: a !*FD Lower bound on interval for checking real(rk),intent(in) :: b !*FD Upper bound on interval for checking real(rk),intent(in) :: x !*FD Test value (degrees) ! Internal variables real(rk) :: ta,tb ! Beginning of code if (a=a).and.(x=ta).and.(xsmall) then x=(-b-sqrt(b**2-4*a*c))/(2*a) else x=-c/b endif y=(yp-ya-x*(yb-ya))/(yd+x*(yc-yd-yb+ya)-ya) if (GLC_DEBUG) then ! Could use the following code if points are degenerate (a=b, c=d, etc.) ! if (abs(a) > small) then ! x=(-b-sqrt(b**2-4*a*c))/(2*a) ! elseif (abs(b) > small) then ! x=-c/b ! else ! x=0._rk ! endif ! ! if (abs(yd+x*(yc-yd-yb+ya)-ya) > small) then ! y=(yp-ya-x*(yb-ya))/(yd+x*(yc-yd-yb+ya)-ya) ! else ! y=0._rk ! endif endif end subroutine calc_fractional !+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ end module glint_interp