module aniso_ens_util !$$$ subprogram documentation block ! . . . . ! module: aniso_ens_util ! prgmmr: sato org: np22 date: 2008-10-03 ! ! abstract: some utilities for ensemble based anisotropy, ! extracted from anisofilter. ! ! program history log: ! 2008-10-03 sato ! ! subroutines included: ! ! ens_uv_to_psichi - convert uv field to psichi for regional mode ! set_grdparm212 - projection parameter settings for 212 grid ! set_grdparm221 - projection parameter settings for 221 grid ! ens_intpcoeffs_reg - prepare interpolation coefficient ! fillanlgrd - fill analysis grid with ensemble input ! ens_mirror - mirroring for the out of the input domain ! pges_minmax ! check_32primes ! intp_spl ! ens_fill ! ens_unfill ! ! variable definitions: ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ end documentatio block implicit none ! set default to private private ! define subroutines as public public :: ens_uv_to_psichi public :: set_grdparm212 public :: set_grdparm221 public :: ens_intpcoeffs_reg public :: fillanlgrd public :: ens_mirror public :: pges_minmax public :: check_32primes public :: intp_spl public :: ens_fill public :: ens_unfill contains !======================================================================= subroutine ens_uv_to_psichi(u,v,truewind) !$$$ subprogram documentation block ! . . . . ! subprogram: ens_uv_to_psichi ! prgmmr: pondeca org: np22 date: 2007-03-08 ! ! abstract: given earth u and v on analysis grid, rotate wind to get ! grid-relative u and v components, compute divergence and ! vorticity and call subroutine that converts into ! streamfunction and velocity potential. ! ! program history log: ! 2008-08-21 pondeca ! 2020-05-04 wu - no rotate_wind for fv3_regional ! ! input argument list: ! u(nlat,nlon) - earth relative u-field on analysis grid ! v(nlat,nlon) - earth relative v-field on analysis grid ! truewind ! output argument list: ! u(nlat,nlon) - streamfunction on analysis grid ! v(nlat,nlon) - velocity potential on analysis grid ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ end documentation block use kinds, only: i_kind,r_kind,r_single use constants, only: one,two use gridmod, only: nlon,nlat,region_lon,region_lat,region_dx,region_dy, & rotate_wind_ll2xy,fv3_regional use wind_fft, only: divvort_to_psichi implicit none ! Declare passed variables real(r_single),intent(inout) :: u(nlat,nlon),v(nlat,nlon) logical ,intent(in ) :: truewind ! Declare local variables integer(i_kind) i,j,im,ip,jm,jp real(r_kind) rlon,rlat,dlon,dlat real(r_kind) ue,ve,ug,vg real(r_kind) dxi,dyi real(r_single),allocatable,dimension(:,:)::div,vort real(r_single),allocatable,dimension(:,:)::divb,vortb real(r_single),allocatable,dimension(:,:,:)::tqg real(r_single),allocatable,dimension(:,:)::dxy,dxyb,tdxyb integer(i_kind) ijext,n0,n1,nxb,nyb integer(i_kind) nxs,nxe,nys,nye integer(i_kind) mmaxb,nmaxb logical lprime, no_wgt !========================================================================== !==>rotation to get analysis grid-relative wind !========================================================================== ! print*,'in ens_uv_to_psichi: region_lon,min,max=', & ! minval(region_lon),maxval(region_lon) ! print*,'in ens_uv_to_psichi: region_lat,min,max=', & ! minval(region_lat),maxval(region_lat) if(.not.fv3_regional)then do i=1,nlat do j=1,nlon rlon=region_lon(i,j) rlat=region_lat(i,j) dlon=float(j)*one dlat=float(i)*one ue=u(i,j) ve=v(i,j) call rotate_wind_ll2xy(ue,ve,ug,vg,rlon,dlon,dlat) u(i,j)=ug v(i,j)=vg enddo enddo endif if (truewind) return !========================================================================== !==>divergence and vorticity computation !========================================================================== allocate(div(nlat,nlon)) allocate(vort(nlat,nlon)) do i=1,nlat im=max(1,i-1) ip=min(nlat,i+1) do j=1,nlon jm=max(1,j-1) jp=min(nlon,j+1) dxi=one/((jp-jm)*region_dx(i,j)) dyi=one/((ip-im)*region_dy(i,j)) div(i,j)= (u(i,jp)-u(i,jm))*dxi + (v(ip,j)-v(im,j))*dyi vort(i,j)=(v(i,jp)-v(i,jm))*dxi - (u(ip,j)-u(im,j))*dyi enddo enddo allocate(dxy(nlat,nlon)) dxy=(region_dx+region_dy)/two !========================================================================== !==>expand domain in preparation for fft use !========================================================================== n0=max(nlat,nlon) ijext=4 prime_loop: do n1=n0+2*ijext call check_32primes(n1,lprime) if (lprime) exit prime_loop ijext=ijext+1 end do prime_loop nxs=ijext+1 nxe=ijext+nlon nys=ijext+1 nye=ijext+nlat nxb=n1 nyb=n1 allocate(divb(1:nyb,1:nxb)) allocate(vortb(1:nyb,1:nxb)) allocate(dxyb(1:nyb,1:nxb)) no_wgt=.false. call ens_fill(divb ,nxb,nyb,div ,nlat,nlon,ijext,no_wgt) call ens_fill(vortb,nxb,nyb,vort,nlat,nlon,ijext,no_wgt) no_wgt=.true. call ens_fill(dxyb ,nxb,nyb,dxy ,nlat,nlon,ijext,no_wgt) allocate(tqg(1:nxb,1:nyb,2)) !note reverse order of indices allocate(tdxyb(1:nxb,1:nyb)) !note reverse order of indices do i=1,nyb do j=1,nxb tqg(j,i,1)=divb(i,j) tqg(j,i,2)=vortb(i,j) tdxyb(j,i)=dxyb(i,j) enddo enddo mmaxb=nxb/3-1 nmaxb=nyb/3-1 call divvort_to_psichi(nxb,nyb,mmaxb,nmaxb,tdxyb,tqg) do i=1,nyb do j=1,nxb divb(i,j)=tqg(j,i,1) !vel potential vortb(i,j)=tqg(j,i,2) !streamfunction enddo enddo call ens_unfill(vortb,nxb,nyb,u,nlat,nlon) call ens_unfill(divb ,nxb,nyb,v,nlat,nlon) !in future may add call here to get unbalanced part of chi deallocate(div) deallocate(vort) deallocate(divb) deallocate(vortb) deallocate(tqg) deallocate(dxy) deallocate(dxyb) deallocate(tdxyb) return end subroutine ens_uv_to_psichi ! -------------------------------------------------------------- !======================================================================= !======================================================================= subroutine set_grdparm212(iy,jx,jxp,alat1,elon1,ds,elonv,alatan) !$$$ subprogram documentation block ! . . . . ! subprogram: set_grdparm212 ! prgmmr: ! ! abstract: ! ! program history log: ! 2009-09-15 lueken - added subprogram doc block ! ! input argument list: ! ! output argument list: ! iy,ix,jxp ! alat1,elon1,ds,elonv,alatan ! ! attributes: ! language: f90 ! machine: ! !$$$ end documentation block use kinds, only: i_kind, r_kind implicit none integer(i_kind),intent( out) :: iy,jx,jxp real(r_kind) ,intent( out) :: alat1,elon1,ds,elonv,alatan iy=129 jx=185 jxp=jx ds=40635.25_r_kind alat1=12.190_r_kind elon1=226.541_r_kind elonv=265.000_r_kind alatan=25.000_r_kind return end subroutine set_grdparm212 !======================================================================= !======================================================================= subroutine set_grdparm221(iy,jx,jxp,alat1,elon1,ds,elonv,alatan) !$$$ subprogram documentation block ! . . . . ! subprogram: set_grdparm221 ! prgmmr: ! ! abstract: ! ! program history log: ! 2009-09-15 lueken - added subprogram doc block ! ! input argument list: ! ! output argument list: ! iy,ix,jxp ! alat1,elon1,ds,elonv,alatan ! ! attributes: ! language: f90 ! machine: ! !$$$ end documentation block use kinds, only: i_kind, r_kind use constants, only: one implicit none integer(i_kind),intent( out) :: iy,jx,jxp real(r_kind) ,intent( out) :: alat1,elon1,ds,elonv,alatan iy=277 jx=349 jxp=jx ds=32463.41_r_kind alat1=one elon1=214.500_r_kind elonv=253.000_r_kind alatan=50.000_r_kind end subroutine set_grdparm221 !======================================================================= !======================================================================= subroutine ens_intpcoeffs_reg(ngrds,igbox,iref,jref,igbox0f,ensmask,enscoeff,gblend,mype) ! !$$$ subprogram documentation block ! . . . . ! subprogram: ens_intpcoeffs_reg ! prgmmr: pondeca org: np22 date: 2006-07-06 ! ! abstract: compute coefficients of horizontal bilinear ! interpolation of ensemble field to analysis grid. ! also load mask fields with a value of 1 if grid point ! of analysis grid is within bounds of the ensemble ! grid and -1 otherwise. supported ensemble grids are ! awips 212, 221, and 3 (1dg x 1dg global grid) ! ! (pt#2) i+1,j | | i+1,j+1 (pt#4) ! --+------------+--- ! | | dyg1 ! | * + - ! | X,Y | dyg ! | | ! --+-----+------+--- ! (pt#1) i,j||| i,j+1 (pt#3) ! ! note: fields are assumed to be ! dimensioned as field(nlatdirection,nlondirection) ! ! program history log: ! 2007-02-24 pondeca ! ! input argument list: ! mype - mpi task id ! ngrds ! ! output argument list: ! enscoeff(:,:,:,1) - interpolation coeffs for grid 212 ! enscoeff(:,:,:,2) - interpolation coeffs for grid 221 ! enscoeff(:,:,:,3) - interpolation coeffs for global grid ! igbox(4,3) - first indice is associated with the corner i and j ! values of the largest rectangular portion of the ! analysis grid that falls completely inside the ! ensemble grid. second index is for grids 212, 221 and ! global grid. ! igbox0f(4,3) - same as igbox but values valid for filter grid ! gblend(nlatf,nlonf,2) - blending functions for grids 212 and 221 ! iref ! jref ! ensmask ! ! attributes: ! language: f90 ! machine: ibm RS/6000 SP ! !$$$ use kinds, only: i_kind, r_kind use constants, only: zero, one, half, rad2deg use mpimod, only: npe, mpi_sum, mpi_itype, mpi_comm_world use gridmod, only: nlat,nlon,tll2xy, & region_lon, region_lat, & rlon_min_dd,rlon_max_dd, & rlat_min_dd,rlat_max_dd use anberror, only: pf2aP1 implicit none ! Declare passed variables integer(i_kind),intent(in ) :: mype,ngrds integer(i_kind),intent( out) :: igbox(4,ngrds),igbox0f(4,ngrds) integer(i_kind),intent( out) :: iref(nlat,nlon,ngrds) integer(i_kind),intent( out) :: jref(nlat,nlon,ngrds) real(r_kind), intent( out) :: ensmask(nlat,nlon,ngrds) real(r_kind) ,intent( out) :: enscoeff(4,nlat,nlon,ngrds) real(r_kind), intent( out) :: gblend(pf2aP1%nlatf,pf2aP1%nlonf,2) ! Declare local variables integer(i_kind),parameter::ijadjust=4 integer(i_kind) iy,jx,jxp integer(i_kind) i,j,kg,ierr8,ierror integer(i_kind) iarea(npe),iarea2(npe),iprod,ilateral,jlateral integer(i_kind) ilower,iupper,jleft,jright integer(i_kind) ilower_prev,iupper_prev,jleft_prev,jright_prev integer(i_kind) i1,i2,j1,j2,ij,mype1 integer(i_kind) iimin(npe),iimax(npe),jjmin(npe),jjmax(npe) integer(i_kind) iimin2(npe),iimax2(npe),jjmin2(npe),jjmax2(npe) integer(i_kind) iimin0(3),iimax0(3),jjmin0(3),jjmax0(3) real(r_kind) rlat,rlon,alat1,elon1,ds,elonv,alatan,xg,yg real(r_kind) dxg,dyg,dxg1,dyg1 real(r_kind) dlon,dlat real(r_kind) dlonmin,dlonmax,dlatmin,dlatmax real(r_kind) dist1,dist2 real(r_kind) gblend_l(pf2aP1%nlonf,2) real(r_kind) gblend_r(pf2aP1%nlonf,2) real(r_kind) gblend_b(pf2aP1%nlatf,2) real(r_kind) gblend_t(pf2aP1%nlatf,2) real(r_kind) r360 logical outside logical ltest,ltest1,ltest2,ltest3,ltest4 !***************************************************************** r360=360._r_kind !================================================================= enscoeff(:,:,:,:)=-1.e+20_r_kind ensmask(:,:,1:2)=-one ensmask(:,:,3) = one !model grid is always fully contained in global ! ensemble grid do kg=1,3 if (kg==1) then !grid #212 call set_grdparm212(iy,jx,jxp,alat1,elon1,ds,elonv,alatan) else if (kg==2) then !grid #221 call set_grdparm221(iy,jx,jxp,alat1,elon1,ds,elonv,alatan) else if (kg==3) then !1dg x 1dg global grid iy=181 jx=360 jxp=jx+1 end if do j=1,nlon do i=1,nlat ! latlon for the analysing grid rlat=region_lat(i,j)*rad2deg rlon=region_lon(i,j)*rad2deg if (rlon=r360) xg=rlon+one-r360 yg=rlat+90._r_kind+one end if dxg=xg-float(floor(xg)) dyg=yg-float(floor(yg)) dxg1=one-dxg dyg1=one-dyg if (xg>=one .and. xg<=float(jxp) .and. & yg>=one .and. yg<=float(iy) ) then enscoeff(1,i,j,kg)=dxg1*dyg1 enscoeff(2,i,j,kg)=dxg1*dyg enscoeff(3,i,j,kg)=dxg*dyg1 enscoeff(4,i,j,kg)=dxg*dyg iref(i,j,kg)=floor(yg) jref(i,j,kg)=floor(xg) if (kg==1 .or. kg==2) ensmask(i,j,kg)=one end if end do end do end do !==>determine rectangle of analysis grid that falls inside the ensemble grid do kg=1,2 dlonmin=+huge(dlonmin) dlonmax=-huge(dlonmax) dlatmin=+huge(dlatmin) dlatmax=-huge(dlatmax) if (kg==1) then !grid #212 call set_grdparm212(iy,jx,jxp,alat1,elon1,ds,elonv,alatan) else if (kg==2) then !grid #221 call set_grdparm221(iy,jx,jxp,alat1,elon1,ds,elonv,alatan) endif do j=1,iy yg=float(j)*one do i=1,jx xg=float(i)*one call w3fb12(xg,yg,alat1,elon1,ds,elonv,alatan,rlat,rlon,ierr8) rlon=rlon/rad2deg rlat=rlat/rad2deg call tll2xy(rlon,rlat,dlon,dlat,outside) dlon=min(rlon_max_dd,max(rlon_min_dd,dlon)) dlat=min(rlat_max_dd,max(rlat_min_dd,dlat)) dlonmin=min(dlonmin,dlon) dlonmax=max(dlonmax,dlon) dlatmin=min(dlatmin,dlat) dlatmax=max(dlatmax,dlat) end do end do i1=ceiling(dlatmin) i2=floor (dlatmax) j1=ceiling(dlonmin) j2=floor (dlonmax) if (mype==0) print*,'in ens_intpcoeffs_reg: kg, i1,i2,j1,j2=',kg, i1,i2,j1,j2 !==>adjust limits by trial and error ilateral=2 jlateral=2 loop1: do ilower=i1+ilateral iupper=i2-ilateral jleft=j1+jlateral jright=j2-jlateral ltest=any(ensmask(ilower:iupper,jleft:jright,kg)