SUBROUTINE barnes_cntrl(nx,ny,mxstn,nvar,maxrng,nobs,x,y, & 2,4 obs,odiff,obanx,xsta,ysta,isrc,qobs,rely, & nm_var,npass,kappa,irngsel,range,rngobs, & wlim,klim,knt,rngwgt,wgtsum,zsum,istat, & anx,istatus) ! ! Routine to control Barnes analysis looping. ! For each pass it calls routines to analyze at grid points, ! analyze at obs points, computes new obs differences and ! reports rms statistics. ! ! Keith Brewster, CAPS ! March, 1994 ! IMPLICIT NONE INTEGER :: nx,ny,mxstn,nvar,maxrng INTEGER :: istat ! ! Grid arrays ! REAL :: x(nx),y(ny) REAL :: anx(nx,ny,nvar) ! ! Observation arrays ! INTEGER :: nobs REAL :: obs(mxstn,nvar) REAL :: odiff(mxstn,nvar) REAL :: obanx(mxstn,nvar) REAL :: xsta(mxstn),ysta(mxstn) REAL :: qobs(mxstn,nvar) REAL :: rngobs(mxstn,maxrng) INTEGER :: isrc(mxstn) INTEGER :: rely(mxstn,nvar) CHARACTER (LEN=6) :: nm_var(nvar) ! ! Barnes control variables ! INTEGER :: npass INTEGER :: irngsel(nvar) REAL :: kappa(npass) REAL :: range(nx,ny,maxrng) REAL :: wlim INTEGER :: klim(npass) ! ! Barnes scratch space ! INTEGER :: knt(nvar) REAL :: rngwgt(maxrng) REAL :: wgtsum(nvar) REAL :: zsum(nvar) ! INTEGER :: istatus ! ! Misc internal variables ! INTEGER :: i,j,k,ipass,ista,irng INTEGER :: iloc,jloc REAL :: delx,dely,rpass ! ! Initialize rngobs, the appropriate analysis scale range at ! each observation point. It is important that this be ! consistant with the ranges assigned at the grid points. ! Bilinear interpolation is used rather than an independent ! calculation. Obs outside the domain use the nearest grid ! point in the domain. ! delx=x(2)-x(1) dely=y(2)-y(1) DO ista=1,nobs iloc=INT((xsta(ista)-x(1))/delx) + 1 iloc=MIN(iloc,nx-1) iloc=MAX(iloc,1) jloc=INT((ysta(ista)-y(1))/dely) + 1 jloc=MIN(jloc,ny-1) jloc=MAX(jloc,1) DO irng=1,maxrng CALL bilin1(nx,ny,1,xsta(ista),ysta(ista),iloc,jloc, & x,y,range(1,1,irng),rngobs(ista,irng)) END DO END DO ! ! Establish initial condition. Analysis is zero and ! ob differences are equal to obs. ! DO k=1,nvar DO j=1,ny DO i=1,nx anx(i,j,k)=0. END DO END DO END DO ! DO k=1,nvar DO i=1,mxstn obanx(i,k)=0. odiff(i,k)=obs(i,k) END DO END DO ! DO ipass=1,npass ! rpass=kappa(ipass)*kappa(ipass) ! CALL bargrid(nx,ny,mxstn,nvar,maxrng,x,y, & odiff,xsta,ysta,qobs,isrc,nobs, & knt,rngwgt,wgtsum,zsum, & rpass,irngsel,range,wlim,klim(ipass), & anx,istatus) ! CALL barobs(mxstn,nvar,maxrng, & odiff,xsta,ysta,qobs,isrc,nobs, & knt,rngwgt,wgtsum,zsum, & rpass,irngsel,rngobs,wlim,klim(ipass), & obanx,istatus) ! CALL diffnstat(mxstn,nvar,nobs,obs,isrc,obanx,odiff, & knt,wgtsum,zsum) ! WRITE(6,820) ipass 820 FORMAT(' Statistics for Barnes pass ',i4,/ & ' ivar name knt bias rms') DO k=1,nvar WRITE(6,822) k,nm_var(k),knt(k),wgtsum(k),zsum(k) 822 FORMAT(i6,2X,a6,i8,f10.3,f10.3) END DO IF(istat > 0) THEN WRITE(istat,820) ipass DO k=1,nvar WRITE(istat,822) k,nm_var(k),knt(k),wgtsum(k),zsum(k) END DO END IF ! END DO istatus=0 RETURN END SUBROUTINE barnes_cntrl ! SUBROUTINE diffnstat(mxstn,nvar,nobs,obs,isrc,obanx,odiff, & 1 knt,dmean,drms) ! ! Routine to calculate new observation differences ! and report statistics on the differences. ! ! Keith Brewster, CAPS ! March, 1994 ! IMPLICIT NONE INTEGER :: mxstn,nvar ! REAL :: obs(mxstn,nvar) INTEGER :: isrc(mxstn) REAL :: obanx(mxstn,nvar) REAL :: odiff(mxstn,nvar) INTEGER :: nobs INTEGER :: knt(nvar) REAL :: dmean(nvar) REAL :: drms(nvar) ! ! Misc internal variables ! INTEGER :: i,k REAL :: flknt ! DO k=1,nvar knt(k)=0 dmean(k)=0. drms(k)=0. END DO ! DO i=1,nobs IF(isrc(i) /= 0) THEN DO k=1,nvar IF(obs(i,k) > -90.) THEN odiff(i,k)=obs(i,k)-obanx(i,k) knt(k)=knt(k)+1 dmean(k)=dmean(k)+odiff(i,k) drms(k)=drms(k)+odiff(i,k)*odiff(i,k) END IF END DO END IF END DO ! ! Calculate stats from the sums formed above ! DO k=1,nvar IF(knt(k) > 0) THEN flknt=FLOAT(knt(k)) dmean(k)=dmean(k)/flknt drms(k)=SQRT(drms(k)/flknt) ELSE dmean(k)=0. drms(k)=0. END IF END DO ! RETURN END SUBROUTINE diffnstat SUBROUTINE barqc(maxsta,nvar,nsrc,nsta,z,zdiff, & 1 obstime,xsta,ysta,isrc, & knt,wgtsum,zsum,sqsum, & range,wlim,klim,qclim,varnam,snam, & iqclist,iqcsave, & flagz,flagd) ! ! Routine to objectively quality control data using a ! Barnes analysis of nearby stations. ! ! If an observations is found to be bad it is set to missing flag ! flagz, and the corresponding difference variable is set to flagz. ! ! Keith Brewster, April, 1991 ! Based on Barnes analysis Keith Brewster, March, 1989 ! IMPLICIT NONE ! ! Observation Arguments ! INTEGER :: maxsta,nvar,nsrc REAL :: z(maxsta,nvar), & ! original variables zdiff(maxsta,nvar) ! variable to analyze INTEGER :: obstime(maxsta) REAL :: xsta(maxsta),ysta(maxsta) ! x and y locations of stations INTEGER :: isrc(maxsta) INTEGER :: nsta ! ! Scratch space ! INTEGER :: knt(nvar) REAL :: wgtsum(nvar),zsum(nvar),sqsum(nvar) ! ! File pointers ! INTEGER :: iqclist,iqcsave ! ! Analysis specification arguments ! ! INTEGER iwgt REAL :: range, & ! e-folding range km**2 of barnes weight wlim ! limit of weight to set max range INTEGER :: klim ! minimum # of stations to influence grid pt REAL :: qclim(nsrc,nvar) ! QC cutoffs ! ! Diagnostics and other argument variables ! CHARACTER (LEN=6) :: varnam(nvar) CHARACTER (LEN=5) :: snam(maxsta) REAL :: flagz,flagd ! ! Misc internal variables ! REAL :: rlimsq,dxsta,dysta,dist, & wgt,zanx,dob,sqanx,chklim INTEGER :: jsta,ksta,ivar LOGICAL :: listit,saveit ! ! Set printing logicals ! listit=(iqclist > 0) saveit=(iqcsave > 0) ! ! Based on the minimum weight to consider, set ! a maximum range ! rlimsq=-range*ALOG(wlim) ! ! print *, ' Analyzing...' ! print *, ' rlim= ', rlim ! print *, ' range = ',range ! ! Uses Barnes weighting function. see variable wgt. ! ! Ksta is location that is being examined ! DO ksta=nsta,1,-1 DO ivar=1,nvar zsum(ivar)=0. sqsum(ivar)=0. wgtsum(ivar)=0. knt(ivar)=0 END DO DO jsta=1,nsta IF(jsta /= ksta) THEN dxsta=xsta(ksta)-xsta(jsta) dysta=ysta(ksta)-ysta(jsta) dist=dxsta*dxsta + dysta*dysta IF(dist < rlimsq) THEN wgt=EXP(-dist/range) DO ivar=1,nvar IF(zdiff(jsta,ivar) > flagd) THEN knt(ivar)=knt(ivar)+1 wgtsum(ivar)=wgtsum(ivar)+wgt zsum(ivar)=zsum(ivar)+wgt*zdiff(jsta,ivar) sqsum(ivar)=sqsum(ivar)+ & wgt*zdiff(jsta,ivar)*zdiff(jsta,ivar) END IF END DO END IF END IF ! dont use current station END DO DO ivar=1,nvar IF(zdiff(ksta,ivar) > flagd) THEN IF(knt(ivar) > klim) THEN sqanx=sqsum(ivar)- & (zsum(ivar)*zsum(ivar)/wgtsum(ivar)) sqanx=AMAX1(sqanx,0.) sqanx=3.0*SQRT(sqanx/wgtsum(ivar)) zanx=zsum(ivar)/wgtsum(ivar) dob=zdiff(ksta,ivar)-zanx chklim=AMAX1(sqanx,qclim(isrc(ksta),ivar)) IF(ABS(dob) > chklim) THEN PRINT *, ' QC flagging data...sta=',snam(ksta) PRINT *, ' var, ob =', & varnam(ivar),z(ksta,ivar) PRINT *, ' delta ob =',dob PRINT *, ' 3.0*stdv,qclimit=',sqanx, & qclim(isrc(ksta),ivar) IF(listit) WRITE(iqclist,810) & snam(ksta),obstime(ksta),ivar,varnam(ivar), & z(ksta,ivar),dob, & sqanx,qclim(isrc(ksta),ivar) 810 FORMAT(2X,a5,i5,2X,i3,1X,a6,f11.2,f11.2,f11.2,f11.2) z(ksta,ivar)=flagz zdiff(ksta,ivar)=flagd END IF zsum(ivar)=dob ELSE ! print *, ' Not enuf data to check ivar= ', ! + ivar,' for ',snam(ksta) zsum(ivar)=-999. END IF ELSE zsum(ivar)=-999. END IF ! data missing, dont bother checking END DO IF(saveit) WRITE(iqcsave,820) snam(ksta),obstime(ksta), & (zdiff(ksta,ivar),ivar=1,nvar) 820 FORMAT(2X,a4,i6,2X,15(f7.2)) END DO RETURN END SUBROUTINE barqc SUBROUTINE barobs(mxstn,nvar,maxrng, & 1 odiff,xsta,ysta,qobs,isrc,nobs, & knt,rngwgt,wgtsum,zsum, & rpass,irngsel,rngobs,wlim,klim, & obanx,istatus) ! ! Routine to objectively analyze data at the observation locations ! Purpose is to create an analysis parallel to the gridded analysis ! for obtaining a new observation difference for the next pass. ! Barnes weight function is employed. ! ! Keith Brewster, March, 1989 ! Streamlined to remove unneeded options, kb, April, 1990 ! Modified for use in OLAPS Keith Brewster, March, 1994 ! IMPLICIT NONE ! ! Observation Arguments ! INTEGER :: mxstn,nvar,maxrng REAL :: odiff(mxstn,nvar), & ! variable to analyse xsta(mxstn),ysta(mxstn), & ! x and y locations of stations qobs(mxstn,nvar) INTEGER :: isrc(mxstn) INTEGER :: nobs ! ! Scratch space ! INTEGER :: knt(nvar) REAL :: rngwgt(maxrng) REAL :: wgtsum(nvar),zsum(nvar) ! ! Input/Output analysis at observation locations.Grid arguments ! REAL :: obanx(mxstn,nvar) ! analysed data -- Input and OUTPUT INTEGER :: istatus ! ! Analysis specification arguments ! ! integer iwgt INTEGER :: irngsel(nvar) REAL :: rpass, & rngobs(mxstn,maxrng), & ! e-folding range km**2 of barnes weight wlim ! limit in km of station influence INTEGER :: klim ! minimum # of stations to influence grid pt ! ! Misc internal variables ! INTEGER :: ista,jsta,ivar,irng REAL :: denom,rlimsq,dxsta,dysta,dist,wgt,rmax ! real wvar ! ! print *, ' Analyzing...' ! print *, ' rlim= ', rlim ! print *, ' range = ',range ! ! Uses Barnes weighting function. see variable wgt. ! DO ista=1,nobs IF(isrc(ista) /= 0) THEN rmax=rngobs(ista,1) DO irng=1,maxrng rmax=AMAX1(rmax,rngobs(ista,irng)) END DO denom=rpass*rmax rlimsq=-denom*ALOG(wlim) DO ivar=1,nvar zsum(ivar)=0. wgtsum(ivar)=0. knt(ivar)=0 END DO DO jsta=1,nobs IF(isrc(jsta) /= 0) THEN dxsta=xsta(ista)-xsta(jsta) dysta=ysta(ista)-ysta(jsta) dist=dxsta*dxsta + dysta*dysta IF(dist < rlimsq) THEN DO irng=1,maxrng rngwgt(irng)= & EXP(-dist/(rpass*rngobs(ista,irng))) END DO DO ivar=1,nvar wgt=rngwgt(irngsel(ivar)) IF(odiff(jsta,ivar) > -90.) THEN knt(ivar)=knt(ivar)+1 ! wvar=wgt/qobs(jsta,ivar) wgtsum(ivar)=wgtsum(ivar)+wgt zsum(ivar)=zsum(ivar)+wgt*odiff(jsta,ivar) END IF END DO END IF END IF END DO DO ivar=1,nvar IF(knt(ivar) > klim) & obanx(ista,ivar)=obanx(ista,ivar)+zsum(ivar)/wgtsum(ivar) END DO END IF ! valid station? END DO istatus=0 RETURN END SUBROUTINE barobs SUBROUTINE bargrid(nx,ny,mxstn,nvar,maxrng,x,y, & 1 obs,xsta,ysta,qobs,isrc,nobs, & knt,rngwgt,wgtsum,zsum, & rpass,irngsel,range,wlim,klim, & anx,istatus) ! ! Routine to objectively analyze data on a regular grid specified ! by x and y. Barnes weight function is employed. ! ! Keith Brewster, March, 1989 ! Streamlined to remove unneeded options, kb, April, 1990 ! Modified for use in OLAPS Keith Brewster, March, 1994 ! IMPLICIT NONE ! ! Observation Arguments ! INTEGER :: mxstn,nvar,maxrng REAL :: obs(mxstn,nvar), & ! variable to analyse xsta(mxstn),ysta(mxstn), & ! x and y locations of stations qobs(mxstn,nvar) INTEGER :: isrc(mxstn) INTEGER :: nobs ! ! Scratch space ! INTEGER :: knt(nvar) REAL :: rngwgt(maxrng) REAL :: wgtsum(nvar),zsum(nvar) ! ! Grid arguments ! INTEGER :: nx,ny REAL :: x(nx),y(ny) ! x and y locations of grid points REAL :: anx(nx,ny,nvar) ! analysed grid -- Input and OUTPUT INTEGER :: istatus ! ! Analysis specification arguments ! ! integer iwgt INTEGER :: irngsel(nvar) REAL :: rpass, & range(nx,ny,maxrng), & ! e-folding range km**2 of barnes weight wlim ! limit in km of station influence INTEGER :: klim ! minimum # of stations to influence grid pt ! ! Misc internal variables ! REAL :: rlimsq,denom,dxsta,dysta,dist,wgt,rmax INTEGER :: i,j,jsta,ivar,irng ! ! Uses Barnes weighting function. see variable wgt. ! DO j=1,ny DO i=1,nx rmax=range(i,j,1) DO irng=1,maxrng rmax=AMAX1(rmax,range(i,j,irng)) END DO denom=rpass*rmax rlimsq=-denom*ALOG(wlim) DO ivar=1,nvar zsum(ivar)=0. wgtsum(ivar)=0. knt(ivar)=0 END DO DO jsta=1,nobs IF(isrc(jsta) /= 0) THEN dxsta=x(i)-xsta(jsta) dysta=y(j)-ysta(jsta) dist=dxsta*dxsta + dysta*dysta IF(dist < rlimsq) THEN DO irng=1,maxrng rngwgt(irng)=EXP(-dist/(rpass*range(i,j,irng))) END DO DO ivar=1,nvar wgt=rngwgt(irngsel(ivar)) IF(obs(jsta,ivar) > -90.) THEN knt(ivar)=knt(ivar)+1 wgtsum(ivar)=wgtsum(ivar)+wgt zsum(ivar)=zsum(ivar)+wgt*obs(jsta,ivar) END IF END DO END IF END IF END DO DO ivar=1,nvar IF(knt(ivar) > klim) anx(i,j,ivar)=anx(i,j,ivar)+zsum(ivar)/wgtsum(ivar) END DO END DO END DO istatus=0 RETURN END SUBROUTINE bargrid SUBROUTINE bilin1(nx,ny,nvar,xpos,ypos,i,j,x,y,var,varint) 1 ! ! Does bilinear interpolation of variables (nvar variables) ! to position (xpos,ypos) given a grid of variables "var", their ! x and y positions of the grid (x,y) and the index of the ! grid point (i,j) which is to the "lower-left" of xpos and ypos. ! That means (xpos,ypos) is between (i,j) and (i+1,j+1). ! ! A saftey feature in case xpos and ypos are outside the grid ! is built-in. ! IMPLICIT NONE ! ! Arguments, input ! INTEGER :: nx,ny,nvar REAL :: var(nx,ny,nvar) REAL :: x(nx),y(ny),xpos,ypos INTEGER :: i,j ! ! Arguments output ! REAL :: varint(nvar) ! ! Misc Internal Variables ! REAL :: xtem,ytem,dxpos,dypos,dx,dy REAL :: w11,w12,w21,w22 INTEGER :: ivar,ip1,jp1 ! xtem=AMAX1(xpos,x(1)) xtem=AMIN1(xtem,x(nx)) ytem=AMAX1(ypos,y(1)) ytem=AMIN1(ytem,y(ny)) ip1=i+1 jp1=j+1 dx=x(ip1)-x(i) dy=y(jp1)-y(j) dxpos=xtem-x(i) dypos=ytem-y(j) IF((dx /= 0.) .AND. (dy /= 0.))THEN dx=dxpos/dx dy=dypos/dy w11=(1.-dx)*(1.-dy) w21=dx*(1.-dy) w12=dy*(1.-dx) w22=dx*dy ELSE IF(dx /= 0.) THEN dx=dxpos/dx w11=1.-dx w21=dx w12=0. w22=0. ELSE dy=dypos/dy w11=1.-dy w21=0. w12=dy w22=0. END IF ! DO ivar=1,nvar varint(ivar)=var( i, j,ivar)*w11 & +var(ip1, j,ivar)*w21 & +var( i,jp1,ivar)*w12 & +var(ip1,jp1,ivar)*w22 END DO RETURN END SUBROUTINE bilin1