module fill_module 1 !----------------------------------------------------------------------- ! $Id$ !BOP ! ! !MODULE: fill_module --- utilities for filling in "bad" data use shr_kind_mod, only: r8 => shr_kind_r8 use cam_logfile, only: iulog #ifdef NO_R16 integer,parameter :: r16= selected_real_kind(12) ! 8 byte real #else integer,parameter :: r16= selected_real_kind(24) ! 16 byte real #endif ! ! !PUBLIC MEMBER FUNCTIONS: public filew, fillxy, fillz, filns, pfix ! ! !DESCRIPTION: ! ! This module provides the basic utilities to fill in regions ! with bad "data", for example slightly negative values in fields ! which must be positive, like mixing ratios. Generally this ! means borrowing positive values from neighboring cells. ! ! !REVISION HISTORY: ! 99.03.01 Lin Creation ! 01.02.14 Lin Routines coalesced into this module ! 01.03.26 Sawyer Added ProTeX documentation ! 05.05.25 Sawyer Merged CAM and GEOS5 versions ! !EOP !----------------------------------------------------------------------- private real(r8), parameter :: D0_0 = 0.0_r8 real(r8), parameter :: D0_5 = 0.5_r8 real(r8), parameter :: D1_0 = 1.0_r8 real(r8), parameter :: D1_5 = 1.5_r8 contains !----------------------------------------------------------------------- !BOP ! !IROUTINE: filew --- Fill from east and west neighbors; essentially ! performing local flux adjustment ! ! !INTERFACE: subroutine filew(q, im, jm, jfirst, jlast, acap, ipx, tiny, cosp2) 1,4 ! !USES: implicit none ! !INPUT PARAMETERS: integer im ! Longitudes integer jm ! Total latitudes integer jfirst ! Starting latitude integer jlast ! Finishing latitude real(r8) tiny ! A small number to pump up value real(r8) acap ! 1/(polar cap area) real(r8) cosp2 ! cosine(lat) at j=2 ! !INPUT/OUTPUT PARAMETERS: real(r8) q(im,jfirst:jlast) ! Field to adjust ! !OUTPUT PARAMETERS: integer ipx ! Flag: 0 if Q not change, 1 if changed ! !DESCRIPTION: ! Check for "bad" data and fill from east and west neighbors ! ! !REVISION HISTORY: ! 01.99.10 Lin Creation ! 01.07.30 Lin Improvement ! !EOP !----------------------------------------------------------------------- !BOC ! !LOCAL VARIABLES: real(r8) d0, d1, d2 real(r8) qtmp(jfirst:jlast,im) real(r8) tinyl ! local tiny mixing ratio real(r8) qmin integer i, j, jm1, ip2 integer j1, j2 integer imin, jmin j1 = max( jfirst, 2 ) j2 = min( jlast, jm-1 ) jm1 = jm-1 ipx = 0 ! Copy & swap direction for vectorization. do j=j1,j2 do i=1,im qtmp(j,i) = q(i,j) enddo enddo do i=2,im-1 do j=j1,j2 if(qtmp(j,i) < D0_0) then tinyl = max(D0_0,qtmp(j,i-1),qtmp(j,i+1))*tiny ipx = 1 ! west d0 = max(D0_0,qtmp(j,i-1)) d1 = min(-qtmp(j,i),d0) qtmp(j,i-1) = qtmp(j,i-1) - d1 qtmp(j,i) = qtmp(j,i) + d1 ! east d0 = max(D0_0,qtmp(j,i+1)) d2 = min(-qtmp(j,i),d0) qtmp(j,i+1) = qtmp(j,i+1) - d2 qtmp(j,i) = qtmp(j,i) + d2 + tinyl endif enddo enddo i=1 do j=j1,j2 if(qtmp(j,i) < D0_0) then ipx = 1 tinyl = max(D0_0,qtmp(j,im),qtmp(j,i+1))*tiny ! west d0 = max(D0_0,qtmp(j,im)) d1 = min(-qtmp(j,i),d0) qtmp(j,im) = qtmp(j,im) - d1 qtmp(j,i) = qtmp(j,i) + d1 ! east d0 = max(D0_0,qtmp(j,i+1)) d2 = min(-qtmp(j,i),d0) qtmp(j,i+1) = qtmp(j,i+1) - d2 qtmp(j,i) = qtmp(j,i) + d2 + tinyl endif enddo i=im do j=j1,j2 if(qtmp(j,i) < D0_0) then ipx = 1 tinyl = max(D0_0,qtmp(j,i-1),qtmp(j,1))*tiny ! west d0 = max(D0_0,qtmp(j,i-1)) d1 = min(-qtmp(j,i),d0) qtmp(j,i-1) = qtmp(j,i-1) - d1 qtmp(j,i) = qtmp(j,i) + d1 ! east d0 = max(D0_0,qtmp(j,1)) d2 = min(-qtmp(j,i),d0) qtmp(j,1) = qtmp(j,1) - d2 qtmp(j,i) = qtmp(j,i) + d2 + tinyl endif enddo if(ipx .ne. 0) then !----------- ! Final pass !----------- do i=1,im-1 do j=j1,j2 if (qtmp(j,i) < D0_0 ) then ! Take mass from east (essentially adjusting fx(i+1,j)) qtmp(j,i+1) = qtmp(j,i+1) + qtmp(j,i) qtmp(j,i) = D0_0 endif enddo enddo do i=im,2,-1 do j=j1,j2 if (qtmp(j,i) < D0_0 ) then ! Take mass from west (essentially adjusting fx(i,j)) qtmp(j,i-1) = qtmp(j,i-1) + qtmp(j,i) qtmp(j,i) = D0_0 endif enddo enddo do j=j1,j2 qmin = D0_0 do i=1, im if (qtmp(j,i) < qmin) then qmin = qtmp(j,i) imin = i jmin = j endif enddo if ( qmin < D0_0 ) then write(iulog,*) ' filew failed, worst i, j, qtmp, q = ', imin, jmin, qtmp(jmin,imin), q(imin,jmin) end if do i=1,im q(i,j) = qtmp(j,i) enddo enddo endif ! Check Poles. if ( jfirst == 1 ) then if(q(1,1) < D0_0) then call pfix(q(1,2),q(1,1),im,ipx,acap,cosp2) else ! Check j=2 ip2 = 0 do i=1,im if(q(i,2).lt.D0_0) then ip2 = 1 go to 322 endif enddo 322 continue if(ip2.ne.0) call pfix(q(1,2),q(1,1),im,ipx,acap,cosp2) endif endif if ( jlast == jm ) then if(q(1,jm) < D0_0) then call pfix(q(1,jm1),q(1,jm),im,ipx,acap,cosp2) else ! Check j=jm1 ip2 = 0 do i=1,im if(q(i,jm1) < D0_0) then ip2 = 1 go to 323 endif enddo 323 continue if(ip2.ne.0) call pfix(q(1,jm1),q(1,jm),im,ipx,acap,cosp2) endif endif !EOC end subroutine filew !----------------------------------------------------------------------- !----------------------------------------------------------------------- !BOP ! !IROUTINE: fillxy --- Fill from east, west, north and south neighbors ! ! !INTERFACE: subroutine fillxy(q, im, jm, jfirst, jlast, acap, cosp, acosp) 1,1 ! !USES: implicit none integer im ! Longitudes integer jm ! Total latitudes integer jfirst ! Starting latitude integer jlast ! Finishing latitude real(r8) acap ! ??? real(r8) cosp(jm) ! ??? real(r8) acosp(jm) ! ??? ! ! !INPUT/OUTPUT PARAMETERS: real(r8) q(im,jfirst:jlast) ! Field to adjust ! !DESCRIPTION: ! Check for "bad" data and fill from east and west neighbors ! ! !BUGS: ! Currently this routine only performs the east-west fill algorithm. ! This is because the N-S fill is very hard to do in a reproducible ! fashion when the problem is decomposed by latitudes. ! ! !REVISION HISTORY: ! 99.03.01 Lin Creation ! !EOP !----------------------------------------------------------------------- !BOC ! ! !LOCAL VARIABLES: integer ipx, ipy, j1, j2 real(r8) tiny parameter( tiny = 1.e-20_r8 ) call filew(q,im,jm,jfirst,jlast,acap,ipx,tiny,cosp(2)) ! WS 99.08.03 : S.-J. can you clean up the j1, j2 stuff here? if(ipx.ne.0) then j1 = max( 2, jfirst ) j2 = min( jm-1, jlast ) ! ! WS 99.08.03 : see comments in "BUGS" above !!! call filns(q,im,jm,j1,j2,cosp,acosp,ipy,tiny) ! if(ipy .ne. 0) then ! do fill zonally ! xfx is problematic ! call xfix(q,IM,JM,tiny,qt) ! endif endif !EOC end subroutine fillxy !----------------------------------------------------------------------- !----------------------------------------------------------------------- !BOP ! !IROUTINE: fillz --- Fill from neighbors below and above ! ! !INTERFACE: subroutine fillz(im, i1, i2, km, nq, q, dp) ! !USES: implicit none ! !INPUT PARAMETERS: integer, intent(in) :: im ! No. of longitudes integer, intent(in) :: km ! No. of levels integer, intent(in) :: i1 ! Starting longitude integer, intent(in) :: i2 ! Finishing longitude integer, intent(in) :: nq ! Total number of tracers real(r8), intent(in) :: dp(im,km) ! pressure thickness ! !INPUT/OUTPUT PARAMETERS: real(r8), intent(inout) :: q(im,km,nq) ! tracer mixing ratio ! !DESCRIPTION: ! Check for "bad" data and fill from east and west neighbors ! ! !BUGS: ! Currently this routine only performs the east-west fill algorithm. ! This is because the N-S fill is very hard to do in a reproducible ! fashion when the problem is decomposed by latitudes. ! ! !REVISION HISTORY: ! 00.04.01 Lin Creation ! !EOP !----------------------------------------------------------------------- !BOC ! ! !LOCAL VARIABLES: integer i, k, ic real(r8) qup, qly, dup do ic=1,nq ! Top layer do i=i1,i2 if( q(i,1,ic) < D0_0) then q(i,2,ic) = q(i,2,ic) + q(i,1,ic)*dp(i,1)/dp(i,2) q(i,1,ic) = D0_0 endif enddo ! Interior do k=2,km-1 do i=i1,i2 if( q(i,k,ic) < D0_0 ) then ! Borrow from above qup = q(i,k-1,ic)*dp(i,k-1) qly = -q(i,k ,ic)*dp(i,k ) dup = min( D0_5*qly, qup ) !borrow no more than 50% q(i,k-1,ic) = q(i,k-1,ic) - dup/dp(i,k-1) ! Borrow from below: q(i,k,ic) is still negative at this stage q(i,k+1,ic) = q(i,k+1,ic) + (dup-qly)/dp(i,k+1) q(i,k ,ic) = D0_0 endif enddo enddo ! Bottom layer k = km do i=i1,i2 if( q(i,k,ic) < D0_0) then ! Borrow from above qup = q(i,k-1,ic)*dp(i,k-1) qly = -q(i,k ,ic)*dp(i,k ) dup = min( qly, qup ) q(i,k-1,ic) = q(i,k-1,ic) - dup/dp(i,k-1) q(i,k,ic) = D0_0 endif enddo enddo !EOC end subroutine fillz !----------------------------------------------------------------------- !----------------------------------------------------------------------- !BOP ! !IROUTINE: filns --- Fill from north and south neighbors ! ! !INTERFACE: subroutine filns(q,im,jm,j1,j2,cosp,acosp,ipy,tiny) ! !USES: implicit none ! !INPUT PARAMETERS: integer im ! Longitudes integer jm ! Total latitudes integer j1 ! Starting latitude integer j2 ! Finishing latitude real(r8) tiny ! A small number to pump up value real(r8) cosp(*) ! ??? real(r8) acosp(*) ! ??? ! !INPUT/OUTPUT PARAMETERS: real(r8) q(im,*) ! Field to adjust ! !OUTPUT PARAMETERS: integer ipy ! Flag: 0 if no fill-in, 1 if fill-in ! !DESCRIPTION: ! Check for "bad" data and fill from north and south neighbors ! ! !BUGS: ! Currently this routine can only be used performs when the ! problem is *not* distributed in latitude (i.e. j1=1, j2=jm). ! This is because the N-S fill is very hard to do in a reproducible ! fashion when the problem is decomposed by latitudes. ! ! !REVISION HISTORY: ! 99.03.01 Lin Creation ! 05.06.30 Sawyer Removed SAVE attribute for cap1 (recalculated) ! !EOP !----------------------------------------------------------------------- !BOC ! ! !LOCAL VARIABLES: integer i, j ! This definition of PI as opposed to 4._r16*atan(1._r16) does not ! appear to generate non-zero differences in GEOS5 checkpoint files real(R16),parameter :: pi = 3.1415926535897932384626433832795028841971_R16 real(r8) :: dp, cap1, dq, dn, ds, d0, d1, d2 dp = pi/real(jm-1,r16) cap1 = im*(D1_0-cos((j1-D1_5)*dp))/dp ipy = 0 do j=j1+1,j2-1 do i=1,im if(q(i,j).lt.D0_0) then ipy = 1 dq = - q(i,j)*cosp(j) ! North dn = q(i,j+1)*cosp(j+1) d0 = max(D0_0,dn) d1 = min(dq,d0) q(i,j+1) = (dn - d1)*acosp(j+1) dq = dq - d1 ! South ds = q(i,j-1)*cosp(j-1) d0 = max(D0_0,ds) d2 = min(dq,d0) q(i,j-1) = (ds - d2)*acosp(j-1) q(i,j) = (d2 - dq)*acosp(j) + tiny endif enddo enddo do i=1,im if(q(i,j1).lt.D0_0) then ipy = 1 dq = - q(i,j1)*cosp(j1) ! North dn = q(i,j1+1)*cosp(j1+1) d0 = max(D0_0,dn) d1 = min(dq,d0) q(i,j1+1) = (dn - d1)*acosp(j1+1) q(i,j1) = (d1 - dq)*acosp(j1) + tiny endif enddo j = j2 do i=1,im if(q(i,j).lt.D0_0) then ipy = 1 dq = - q(i,j)*cosp(j) ! South ds = q(i,j-1)*cosp(j-1) d0 = max(D0_0,ds) d2 = min(dq,d0) q(i,j-1) = (ds - d2)*acosp(j-1) q(i,j) = (d2 - dq)*acosp(j) + tiny endif enddo ! Check Poles. if(q(1,1).lt.D0_0) then dq = q(1,1)*cap1/real(im,r8)*acosp(j1) do i=1,im q(i,1) = tiny q(i,j1) = q(i,j1) + dq q(i,j1) = max(tiny, q(i,j1) + dq ) enddo endif if(q(1,jm).lt.D0_0) then dq = q(1,jm)*cap1/real(im,r8)*acosp(j2) do i=1,im q(i,jm) = tiny q(i,j2) = max(tiny, q(i,j2) + dq ) enddo endif !EOC end subroutine filns !----------------------------------------------------------------------- !----------------------------------------------------------------------- !BOP ! !IROUTINE: pfix --- fix an individual latitude-level ! ! !INTERFACE: subroutine pfix(q, qp, im, ipx, acap, cosp2) 4 ! !USES: implicit none ! !INPUT PARAMETERS: integer im ! Longitudes real(r8) acap ! ??? real(r8) cosp2 ! ??? ! !INPUT/OUTPUT PARAMETERS: real(r8) q(im) ! Latitude-level field to adjust real(r8) qp(im) ! Second latitude-level field to adjust (usually pole) ! !OUTPUT PARAMETERS: integer ipx ! Flag: 0 if Q not change, 1 if changed ! !DESCRIPTION: ! Fill one latitude-level from east and west neighbors ! ! !REVISION HISTORY: ! 99.03.01 Lin Creation ! !EOP !----------------------------------------------------------------------- !BOC ! ! !LOCAL VARIABLES: integer i real(r8) summ, sump, pmean summ = D0_0 sump = D0_0 do i=1,im summ = summ + q(i) sump = sump + qp(i) enddo sump = sump/im pmean = (sump*acap + summ*cosp2) / (acap + cosp2*im) do i=1,im q(i) = pmean qp(i) = pmean enddo if( qp(1) < D0_0 ) then ipx = 1 endif !EOC end subroutine pfix !----------------------------------------------------------------------- end module fill_module